summaryrefslogtreecommitdiffstats
path: root/arch/xtensa/platforms
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 18:49:45 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 18:49:45 +0000
commit2c3c1048746a4622d8c89a29670120dc8fab93c4 (patch)
tree848558de17fb3008cdf4d861b01ac7781903ce39 /arch/xtensa/platforms
parentInitial commit. (diff)
downloadlinux-upstream.tar.xz
linux-upstream.zip
Adding upstream version 6.1.76.upstream/6.1.76upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'arch/xtensa/platforms')
-rw-r--r--arch/xtensa/platforms/Makefile4
-rw-r--r--arch/xtensa/platforms/iss/Makefile11
-rw-r--r--arch/xtensa/platforms/iss/console.c224
-rw-r--r--arch/xtensa/platforms/iss/include/platform/serial.h15
-rw-r--r--arch/xtensa/platforms/iss/include/platform/simcall-gdbio.h34
-rw-r--r--arch/xtensa/platforms/iss/include/platform/simcall-iss.h73
-rw-r--r--arch/xtensa/platforms/iss/include/platform/simcall.h110
-rw-r--r--arch/xtensa/platforms/iss/network.c640
-rw-r--r--arch/xtensa/platforms/iss/setup.c85
-rw-r--r--arch/xtensa/platforms/iss/simdisk.c366
-rw-r--r--arch/xtensa/platforms/xt2000/Makefile6
-rw-r--r--arch/xtensa/platforms/xt2000/include/platform/hardware.h43
-rw-r--r--arch/xtensa/platforms/xt2000/include/platform/serial.h28
-rw-r--r--arch/xtensa/platforms/xt2000/setup.c150
-rw-r--r--arch/xtensa/platforms/xtfpga/Makefile11
-rw-r--r--arch/xtensa/platforms/xtfpga/include/platform/hardware.h60
-rw-r--r--arch/xtensa/platforms/xtfpga/include/platform/lcd.h35
-rw-r--r--arch/xtensa/platforms/xtfpga/include/platform/serial.h18
-rw-r--r--arch/xtensa/platforms/xtfpga/lcd.c89
-rw-r--r--arch/xtensa/platforms/xtfpga/setup.c292
20 files changed, 2294 insertions, 0 deletions
diff --git a/arch/xtensa/platforms/Makefile b/arch/xtensa/platforms/Makefile
new file mode 100644
index 000000000..e2e7e0726
--- /dev/null
+++ b/arch/xtensa/platforms/Makefile
@@ -0,0 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0-only
+obj-$(CONFIG_XTENSA_PLATFORM_XT2000) += xt2000/
+obj-$(CONFIG_XTENSA_PLATFORM_ISS) += iss/
+obj-$(CONFIG_XTENSA_PLATFORM_XTFPGA) += xtfpga/
diff --git a/arch/xtensa/platforms/iss/Makefile b/arch/xtensa/platforms/iss/Makefile
new file mode 100644
index 000000000..f3dd5e72a
--- /dev/null
+++ b/arch/xtensa/platforms/iss/Makefile
@@ -0,0 +1,11 @@
+# SPDX-License-Identifier: GPL-2.0-only
+# $Id: Makefile,v 1.1.1.1 2002/08/28 16:10:14 aroll Exp $
+#
+# Makefile for the Xtensa Instruction Set Simulator (ISS)
+# "prom monitor" library routines under Linux.
+#
+
+obj-y = setup.o
+obj-$(CONFIG_TTY) += console.o
+obj-$(CONFIG_NET) += network.o
+obj-$(CONFIG_BLK_DEV_SIMDISK) += simdisk.o
diff --git a/arch/xtensa/platforms/iss/console.c b/arch/xtensa/platforms/iss/console.c
new file mode 100644
index 000000000..10b79d3c7
--- /dev/null
+++ b/arch/xtensa/platforms/iss/console.c
@@ -0,0 +1,224 @@
+/*
+ * arch/xtensa/platforms/iss/console.c
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * Copyright (C) 2001-2005 Tensilica Inc.
+ * Authors Christian Zankel, Joe Taylor
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/console.h>
+#include <linux/init.h>
+#include <linux/mm.h>
+#include <linux/major.h>
+#include <linux/param.h>
+#include <linux/seq_file.h>
+#include <linux/serial.h>
+
+#include <linux/uaccess.h>
+#include <asm/irq.h>
+
+#include <platform/simcall.h>
+
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+
+#define SERIAL_MAX_NUM_LINES 1
+#define SERIAL_TIMER_VALUE (HZ / 10)
+
+static void rs_poll(struct timer_list *);
+
+static struct tty_driver *serial_driver;
+static struct tty_port serial_port;
+static DEFINE_TIMER(serial_timer, rs_poll);
+
+static int rs_open(struct tty_struct *tty, struct file * filp)
+{
+ if (tty->count == 1)
+ mod_timer(&serial_timer, jiffies + SERIAL_TIMER_VALUE);
+
+ return 0;
+}
+
+static void rs_close(struct tty_struct *tty, struct file * filp)
+{
+ if (tty->count == 1)
+ del_timer_sync(&serial_timer);
+}
+
+
+static int rs_write(struct tty_struct * tty,
+ const unsigned char *buf, int count)
+{
+ /* see drivers/char/serialX.c to reference original version */
+
+ simc_write(1, buf, count);
+ return count;
+}
+
+static void rs_poll(struct timer_list *unused)
+{
+ struct tty_port *port = &serial_port;
+ int i = 0;
+ int rd = 1;
+ unsigned char c;
+
+ while (simc_poll(0)) {
+ rd = simc_read(0, &c, 1);
+ if (rd <= 0)
+ break;
+ tty_insert_flip_char(port, c, TTY_NORMAL);
+ i++;
+ }
+
+ if (i)
+ tty_flip_buffer_push(port);
+ if (rd)
+ mod_timer(&serial_timer, jiffies + SERIAL_TIMER_VALUE);
+}
+
+
+static int rs_put_char(struct tty_struct *tty, unsigned char ch)
+{
+ return rs_write(tty, &ch, 1);
+}
+
+static void rs_flush_chars(struct tty_struct *tty)
+{
+}
+
+static unsigned int rs_write_room(struct tty_struct *tty)
+{
+ /* Let's say iss can always accept 2K characters.. */
+ return 2 * 1024;
+}
+
+static void rs_hangup(struct tty_struct *tty)
+{
+ /* Stub, once again.. */
+}
+
+static void rs_wait_until_sent(struct tty_struct *tty, int timeout)
+{
+ /* Stub, once again.. */
+}
+
+static int rs_proc_show(struct seq_file *m, void *v)
+{
+ seq_printf(m, "serinfo:1.0 driver:0.1\n");
+ return 0;
+}
+
+static const struct tty_operations serial_ops = {
+ .open = rs_open,
+ .close = rs_close,
+ .write = rs_write,
+ .put_char = rs_put_char,
+ .flush_chars = rs_flush_chars,
+ .write_room = rs_write_room,
+ .hangup = rs_hangup,
+ .wait_until_sent = rs_wait_until_sent,
+ .proc_show = rs_proc_show,
+};
+
+static int __init rs_init(void)
+{
+ struct tty_driver *driver;
+ int ret;
+
+ driver = tty_alloc_driver(SERIAL_MAX_NUM_LINES, TTY_DRIVER_REAL_RAW);
+ if (IS_ERR(driver))
+ return PTR_ERR(driver);
+
+ tty_port_init(&serial_port);
+
+ /* Initialize the tty_driver structure */
+
+ driver->driver_name = "iss_serial";
+ driver->name = "ttyS";
+ driver->major = TTY_MAJOR;
+ driver->minor_start = 64;
+ driver->type = TTY_DRIVER_TYPE_SERIAL;
+ driver->subtype = SERIAL_TYPE_NORMAL;
+ driver->init_termios = tty_std_termios;
+ driver->init_termios.c_cflag =
+ B9600 | CS8 | CREAD | HUPCL | CLOCAL;
+
+ tty_set_operations(driver, &serial_ops);
+ tty_port_link_device(&serial_port, driver, 0);
+
+ ret = tty_register_driver(driver);
+ if (ret) {
+ pr_err("Couldn't register serial driver\n");
+ tty_driver_kref_put(driver);
+ tty_port_destroy(&serial_port);
+
+ return ret;
+ }
+
+ serial_driver = driver;
+
+ return 0;
+}
+
+
+static __exit void rs_exit(void)
+{
+ tty_unregister_driver(serial_driver);
+ tty_driver_kref_put(serial_driver);
+ tty_port_destroy(&serial_port);
+}
+
+
+/* We use `late_initcall' instead of just `__initcall' as a workaround for
+ * the fact that (1) simcons_tty_init can't be called before tty_init,
+ * (2) tty_init is called via `module_init', (3) if statically linked,
+ * module_init == device_init, and (4) there's no ordering of init lists.
+ * We can do this easily because simcons is always statically linked, but
+ * other tty drivers that depend on tty_init and which must use
+ * `module_init' to declare their init routines are likely to be broken.
+ */
+
+late_initcall(rs_init);
+
+
+#ifdef CONFIG_SERIAL_CONSOLE
+
+static void iss_console_write(struct console *co, const char *s, unsigned count)
+{
+ if (s && *s != 0) {
+ int len = strlen(s);
+ simc_write(1, s, count < len ? count : len);
+ }
+}
+
+static struct tty_driver* iss_console_device(struct console *c, int *index)
+{
+ *index = c->index;
+ return serial_driver;
+}
+
+
+static struct console sercons = {
+ .name = "ttyS",
+ .write = iss_console_write,
+ .device = iss_console_device,
+ .flags = CON_PRINTBUFFER,
+ .index = -1
+};
+
+static int __init iss_console_init(void)
+{
+ register_console(&sercons);
+ return 0;
+}
+
+console_initcall(iss_console_init);
+
+#endif /* CONFIG_SERIAL_CONSOLE */
+
diff --git a/arch/xtensa/platforms/iss/include/platform/serial.h b/arch/xtensa/platforms/iss/include/platform/serial.h
new file mode 100644
index 000000000..16aec542d
--- /dev/null
+++ b/arch/xtensa/platforms/iss/include/platform/serial.h
@@ -0,0 +1,15 @@
+/*
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * Copyright (C) 2012 Tensilica Inc.
+ */
+
+#ifndef __ASM_XTENSA_ISS_SERIAL_H
+#define __ASM_XTENSA_ISS_SERIAL_H
+
+/* Have no meaning on ISS, but needed for 8250_early.c */
+#define BASE_BAUD 0
+
+#endif /* __ASM_XTENSA_ISS_SERIAL_H */
diff --git a/arch/xtensa/platforms/iss/include/platform/simcall-gdbio.h b/arch/xtensa/platforms/iss/include/platform/simcall-gdbio.h
new file mode 100644
index 000000000..e642860e2
--- /dev/null
+++ b/arch/xtensa/platforms/iss/include/platform/simcall-gdbio.h
@@ -0,0 +1,34 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Copyright (C) 2021 Cadence Design Systems Inc. */
+
+#ifndef _XTENSA_PLATFORM_ISS_SIMCALL_GDBIO_H
+#define _XTENSA_PLATFORM_ISS_SIMCALL_GDBIO_H
+
+/*
+ * System call like services offered by the GDBIO host.
+ */
+
+#define SYS_open -2
+#define SYS_close -3
+#define SYS_read -4
+#define SYS_write -5
+#define SYS_lseek -6
+
+static int errno;
+
+static inline int __simc(int a, int b, int c, int d)
+{
+ register int a1 asm("a2") = a;
+ register int b1 asm("a6") = b;
+ register int c1 asm("a3") = c;
+ register int d1 asm("a4") = d;
+ __asm__ __volatile__ (
+ "break 1, 14\n"
+ : "+r"(a1), "+r"(c1)
+ : "r"(b1), "r"(d1)
+ : "memory");
+ errno = c1;
+ return a1;
+}
+
+#endif /* _XTENSA_PLATFORM_ISS_SIMCALL_GDBIO_H */
diff --git a/arch/xtensa/platforms/iss/include/platform/simcall-iss.h b/arch/xtensa/platforms/iss/include/platform/simcall-iss.h
new file mode 100644
index 000000000..5a1e7a1f1
--- /dev/null
+++ b/arch/xtensa/platforms/iss/include/platform/simcall-iss.h
@@ -0,0 +1,73 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Copyright (C) 2021 Cadence Design Systems Inc. */
+
+#ifndef _XTENSA_PLATFORM_ISS_SIMCALL_ISS_H
+#define _XTENSA_PLATFORM_ISS_SIMCALL_ISS_H
+
+/*
+ * System call like services offered by the simulator host.
+ */
+
+#define SYS_nop 0 /* unused */
+#define SYS_exit 1 /*x*/
+#define SYS_fork 2
+#define SYS_read 3 /*x*/
+#define SYS_write 4 /*x*/
+#define SYS_open 5 /*x*/
+#define SYS_close 6 /*x*/
+#define SYS_rename 7 /*x 38 - waitpid */
+#define SYS_creat 8 /*x*/
+#define SYS_link 9 /*x (not implemented on WIN32) */
+#define SYS_unlink 10 /*x*/
+#define SYS_execv 11 /* n/a - execve */
+#define SYS_execve 12 /* 11 - chdir */
+#define SYS_pipe 13 /* 42 - time */
+#define SYS_stat 14 /* 106 - mknod */
+#define SYS_chmod 15
+#define SYS_chown 16 /* 202 - lchown */
+#define SYS_utime 17 /* 30 - break */
+#define SYS_wait 18 /* n/a - oldstat */
+#define SYS_lseek 19 /*x*/
+#define SYS_getpid 20
+#define SYS_isatty 21 /* n/a - mount */
+#define SYS_fstat 22 /* 108 - oldumount */
+#define SYS_time 23 /* 13 - setuid */
+#define SYS_gettimeofday 24 /*x 78 - getuid (not implemented on WIN32) */
+#define SYS_times 25 /*X 43 - stime (Xtensa-specific implementation) */
+#define SYS_socket 26
+#define SYS_sendto 27
+#define SYS_recvfrom 28
+#define SYS_select_one 29 /* not compatible select, one file descriptor at the time */
+#define SYS_bind 30
+#define SYS_ioctl 31
+
+#define SYS_iss_argc 1000 /* returns value of argc */
+#define SYS_iss_argv_size 1001 /* bytes needed for argv & arg strings */
+#define SYS_iss_set_argv 1002 /* saves argv & arg strings at given addr */
+
+/*
+ * SYS_select_one specifiers
+ */
+
+#define XTISS_SELECT_ONE_READ 1
+#define XTISS_SELECT_ONE_WRITE 2
+#define XTISS_SELECT_ONE_EXCEPT 3
+
+static int errno;
+
+static inline int __simc(int a, int b, int c, int d)
+{
+ register int a1 asm("a2") = a;
+ register int b1 asm("a3") = b;
+ register int c1 asm("a4") = c;
+ register int d1 asm("a5") = d;
+ __asm__ __volatile__ (
+ "simcall\n"
+ : "+r"(a1), "+r"(b1)
+ : "r"(c1), "r"(d1)
+ : "memory");
+ errno = b1;
+ return a1;
+}
+
+#endif /* _XTENSA_PLATFORM_ISS_SIMCALL_ISS_H */
diff --git a/arch/xtensa/platforms/iss/include/platform/simcall.h b/arch/xtensa/platforms/iss/include/platform/simcall.h
new file mode 100644
index 000000000..e1ec50ce3
--- /dev/null
+++ b/arch/xtensa/platforms/iss/include/platform/simcall.h
@@ -0,0 +1,110 @@
+/*
+ * include/asm-xtensa/platform-iss/simcall.h
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * Copyright (C) 2001 Tensilica Inc.
+ * Copyright (C) 2017 - 2021 Cadence Design Systems Inc.
+ */
+
+#ifndef _XTENSA_PLATFORM_ISS_SIMCALL_H
+#define _XTENSA_PLATFORM_ISS_SIMCALL_H
+
+#include <linux/bug.h>
+
+#ifdef CONFIG_XTENSA_SIMCALL_ISS
+#include <platform/simcall-iss.h>
+#endif
+#ifdef CONFIG_XTENSA_SIMCALL_GDBIO
+#include <platform/simcall-gdbio.h>
+#endif
+
+static inline int simc_exit(int exit_code)
+{
+#ifdef SYS_exit
+ return __simc(SYS_exit, exit_code, 0, 0);
+#else
+ WARN_ONCE(1, "%s: not implemented\n", __func__);
+ return -1;
+#endif
+}
+
+static inline int simc_open(const char *file, int flags, int mode)
+{
+ return __simc(SYS_open, (int) file, flags, mode);
+}
+
+static inline int simc_close(int fd)
+{
+ return __simc(SYS_close, fd, 0, 0);
+}
+
+static inline int simc_ioctl(int fd, int request, void *arg)
+{
+#ifdef SYS_ioctl
+ return __simc(SYS_ioctl, fd, request, (int) arg);
+#else
+ WARN_ONCE(1, "%s: not implemented\n", __func__);
+ return -1;
+#endif
+}
+
+static inline int simc_read(int fd, void *buf, size_t count)
+{
+ return __simc(SYS_read, fd, (int) buf, count);
+}
+
+static inline int simc_write(int fd, const void *buf, size_t count)
+{
+ return __simc(SYS_write, fd, (int) buf, count);
+}
+
+static inline int simc_poll(int fd)
+{
+#ifdef SYS_select_one
+ long timeval[2] = { 0, 0 };
+
+ return __simc(SYS_select_one, fd, XTISS_SELECT_ONE_READ, (int)&timeval);
+#else
+ WARN_ONCE(1, "%s: not implemented\n", __func__);
+ return -1;
+#endif
+}
+
+static inline int simc_lseek(int fd, uint32_t off, int whence)
+{
+ return __simc(SYS_lseek, fd, off, whence);
+}
+
+static inline int simc_argc(void)
+{
+#ifdef SYS_iss_argc
+ return __simc(SYS_iss_argc, 0, 0, 0);
+#else
+ WARN_ONCE(1, "%s: not implemented\n", __func__);
+ return 0;
+#endif
+}
+
+static inline int simc_argv_size(void)
+{
+#ifdef SYS_iss_argv_size
+ return __simc(SYS_iss_argv_size, 0, 0, 0);
+#else
+ WARN_ONCE(1, "%s: not implemented\n", __func__);
+ return 0;
+#endif
+}
+
+static inline void simc_argv(void *buf)
+{
+#ifdef SYS_iss_set_argv
+ __simc(SYS_iss_set_argv, (int)buf, 0, 0);
+#else
+ WARN_ONCE(1, "%s: not implemented\n", __func__);
+#endif
+}
+
+#endif /* _XTENSA_PLATFORM_ISS_SIMCALL_H */
diff --git a/arch/xtensa/platforms/iss/network.c b/arch/xtensa/platforms/iss/network.c
new file mode 100644
index 000000000..bea539f90
--- /dev/null
+++ b/arch/xtensa/platforms/iss/network.c
@@ -0,0 +1,640 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ *
+ * arch/xtensa/platforms/iss/network.c
+ *
+ * Platform specific initialization.
+ *
+ * Authors: Chris Zankel <chris@zankel.net>
+ * Based on work form the UML team.
+ *
+ * Copyright 2005 Tensilica Inc.
+ */
+
+#define pr_fmt(fmt) "%s: " fmt, __func__
+
+#include <linux/list.h>
+#include <linux/irq.h>
+#include <linux/spinlock.h>
+#include <linux/slab.h>
+#include <linux/timer.h>
+#include <linux/if_ether.h>
+#include <linux/inetdevice.h>
+#include <linux/init.h>
+#include <linux/if_tun.h>
+#include <linux/etherdevice.h>
+#include <linux/interrupt.h>
+#include <linux/ioctl.h>
+#include <linux/memblock.h>
+#include <linux/ethtool.h>
+#include <linux/rtnetlink.h>
+#include <linux/platform_device.h>
+
+#include <platform/simcall.h>
+
+#define DRIVER_NAME "iss-netdev"
+#define ETH_MAX_PACKET 1500
+#define ETH_HEADER_OTHER 14
+#define ISS_NET_TIMER_VALUE (HZ / 10)
+
+/* ------------------------------------------------------------------------- */
+
+/* We currently only support the TUNTAP transport protocol. */
+
+#define TRANSPORT_TUNTAP_NAME "tuntap"
+#define TRANSPORT_TUNTAP_MTU ETH_MAX_PACKET
+
+struct tuntap_info {
+ char dev_name[IFNAMSIZ];
+ int fd;
+};
+
+/* ------------------------------------------------------------------------- */
+
+
+struct iss_net_private;
+
+struct iss_net_ops {
+ int (*open)(struct iss_net_private *lp);
+ void (*close)(struct iss_net_private *lp);
+ int (*read)(struct iss_net_private *lp, struct sk_buff **skb);
+ int (*write)(struct iss_net_private *lp, struct sk_buff **skb);
+ unsigned short (*protocol)(struct sk_buff *skb);
+ int (*poll)(struct iss_net_private *lp);
+};
+
+/* This structure contains out private information for the driver. */
+
+struct iss_net_private {
+ spinlock_t lock;
+ struct net_device *dev;
+ struct platform_device pdev;
+ struct timer_list tl;
+ struct rtnl_link_stats64 stats;
+
+ struct timer_list timer;
+ unsigned int timer_val;
+
+ int index;
+ int mtu;
+
+ struct {
+ union {
+ struct tuntap_info tuntap;
+ } info;
+
+ const struct iss_net_ops *net_ops;
+ } tp;
+
+};
+
+/* ================================ HELPERS ================================ */
+
+
+static char *split_if_spec(char *str, ...)
+{
+ char **arg, *end;
+ va_list ap;
+
+ va_start(ap, str);
+ while ((arg = va_arg(ap, char**)) != NULL) {
+ if (*str == '\0') {
+ va_end(ap);
+ return NULL;
+ }
+ end = strchr(str, ',');
+ if (end != str)
+ *arg = str;
+ if (end == NULL) {
+ va_end(ap);
+ return NULL;
+ }
+ *end++ = '\0';
+ str = end;
+ }
+ va_end(ap);
+ return str;
+}
+
+/* Set Ethernet address of the specified device. */
+
+static void setup_etheraddr(struct net_device *dev, char *str)
+{
+ u8 addr[ETH_ALEN];
+
+ if (str == NULL)
+ goto random;
+
+ if (!mac_pton(str, addr)) {
+ pr_err("%s: failed to parse '%s' as an ethernet address\n",
+ dev->name, str);
+ goto random;
+ }
+ if (is_multicast_ether_addr(addr)) {
+ pr_err("%s: attempt to assign a multicast ethernet address\n",
+ dev->name);
+ goto random;
+ }
+ if (!is_valid_ether_addr(addr)) {
+ pr_err("%s: attempt to assign an invalid ethernet address\n",
+ dev->name);
+ goto random;
+ }
+ if (!is_local_ether_addr(addr))
+ pr_warn("%s: assigning a globally valid ethernet address\n",
+ dev->name);
+ eth_hw_addr_set(dev, addr);
+ return;
+
+random:
+ pr_info("%s: choosing a random ethernet address\n",
+ dev->name);
+ eth_hw_addr_random(dev);
+}
+
+/* ======================= TUNTAP TRANSPORT INTERFACE ====================== */
+
+static int tuntap_open(struct iss_net_private *lp)
+{
+ struct ifreq ifr;
+ char *dev_name = lp->tp.info.tuntap.dev_name;
+ int err = -EINVAL;
+ int fd;
+
+ fd = simc_open("/dev/net/tun", 02, 0); /* O_RDWR */
+ if (fd < 0) {
+ pr_err("%s: failed to open /dev/net/tun, returned %d (errno = %d)\n",
+ lp->dev->name, fd, errno);
+ return fd;
+ }
+
+ memset(&ifr, 0, sizeof(ifr));
+ ifr.ifr_flags = IFF_TAP | IFF_NO_PI;
+ strscpy(ifr.ifr_name, dev_name, sizeof(ifr.ifr_name));
+
+ err = simc_ioctl(fd, TUNSETIFF, &ifr);
+ if (err < 0) {
+ pr_err("%s: failed to set interface %s, returned %d (errno = %d)\n",
+ lp->dev->name, dev_name, err, errno);
+ simc_close(fd);
+ return err;
+ }
+
+ lp->tp.info.tuntap.fd = fd;
+ return err;
+}
+
+static void tuntap_close(struct iss_net_private *lp)
+{
+ simc_close(lp->tp.info.tuntap.fd);
+ lp->tp.info.tuntap.fd = -1;
+}
+
+static int tuntap_read(struct iss_net_private *lp, struct sk_buff **skb)
+{
+ return simc_read(lp->tp.info.tuntap.fd,
+ (*skb)->data, (*skb)->dev->mtu + ETH_HEADER_OTHER);
+}
+
+static int tuntap_write(struct iss_net_private *lp, struct sk_buff **skb)
+{
+ return simc_write(lp->tp.info.tuntap.fd, (*skb)->data, (*skb)->len);
+}
+
+static unsigned short tuntap_protocol(struct sk_buff *skb)
+{
+ return eth_type_trans(skb, skb->dev);
+}
+
+static int tuntap_poll(struct iss_net_private *lp)
+{
+ return simc_poll(lp->tp.info.tuntap.fd);
+}
+
+static const struct iss_net_ops tuntap_ops = {
+ .open = tuntap_open,
+ .close = tuntap_close,
+ .read = tuntap_read,
+ .write = tuntap_write,
+ .protocol = tuntap_protocol,
+ .poll = tuntap_poll,
+};
+
+/*
+ * ethX=tuntap,[mac address],device name
+ */
+
+static int tuntap_probe(struct iss_net_private *lp, int index, char *init)
+{
+ struct net_device *dev = lp->dev;
+ char *dev_name = NULL, *mac_str = NULL, *rem = NULL;
+
+ /* Transport should be 'tuntap': ethX=tuntap,mac,dev_name */
+
+ if (strncmp(init, TRANSPORT_TUNTAP_NAME,
+ sizeof(TRANSPORT_TUNTAP_NAME) - 1))
+ return 0;
+
+ init += sizeof(TRANSPORT_TUNTAP_NAME) - 1;
+ if (*init == ',') {
+ rem = split_if_spec(init + 1, &mac_str, &dev_name, NULL);
+ if (rem != NULL) {
+ pr_err("%s: extra garbage on specification : '%s'\n",
+ dev->name, rem);
+ return 0;
+ }
+ } else if (*init != '\0') {
+ pr_err("%s: invalid argument: %s. Skipping device!\n",
+ dev->name, init);
+ return 0;
+ }
+
+ if (!dev_name) {
+ pr_err("%s: missing tuntap device name\n", dev->name);
+ return 0;
+ }
+
+ strscpy(lp->tp.info.tuntap.dev_name, dev_name,
+ sizeof(lp->tp.info.tuntap.dev_name));
+
+ setup_etheraddr(dev, mac_str);
+
+ lp->mtu = TRANSPORT_TUNTAP_MTU;
+
+ lp->tp.info.tuntap.fd = -1;
+ lp->tp.net_ops = &tuntap_ops;
+
+ return 1;
+}
+
+/* ================================ ISS NET ================================ */
+
+static int iss_net_rx(struct net_device *dev)
+{
+ struct iss_net_private *lp = netdev_priv(dev);
+ int pkt_len;
+ struct sk_buff *skb;
+
+ /* Check if there is any new data. */
+
+ if (lp->tp.net_ops->poll(lp) == 0)
+ return 0;
+
+ /* Try to allocate memory, if it fails, try again next round. */
+
+ skb = dev_alloc_skb(dev->mtu + 2 + ETH_HEADER_OTHER);
+ if (skb == NULL) {
+ spin_lock_bh(&lp->lock);
+ lp->stats.rx_dropped++;
+ spin_unlock_bh(&lp->lock);
+ return 0;
+ }
+
+ skb_reserve(skb, 2);
+
+ /* Setup skb */
+
+ skb->dev = dev;
+ skb_reset_mac_header(skb);
+ pkt_len = lp->tp.net_ops->read(lp, &skb);
+ skb_put(skb, pkt_len);
+
+ if (pkt_len > 0) {
+ skb_trim(skb, pkt_len);
+ skb->protocol = lp->tp.net_ops->protocol(skb);
+
+ spin_lock_bh(&lp->lock);
+ lp->stats.rx_bytes += skb->len;
+ lp->stats.rx_packets++;
+ spin_unlock_bh(&lp->lock);
+ netif_rx(skb);
+ return pkt_len;
+ }
+ kfree_skb(skb);
+ return pkt_len;
+}
+
+static int iss_net_poll(struct iss_net_private *lp)
+{
+ int err, ret = 0;
+
+ if (!netif_running(lp->dev))
+ return 0;
+
+ while ((err = iss_net_rx(lp->dev)) > 0)
+ ret++;
+
+ if (err < 0) {
+ pr_err("Device '%s' read returned %d, shutting it down\n",
+ lp->dev->name, err);
+ dev_close(lp->dev);
+ } else {
+ /* FIXME reactivate_fd(lp->fd, ISS_ETH_IRQ); */
+ }
+
+ return ret;
+}
+
+
+static void iss_net_timer(struct timer_list *t)
+{
+ struct iss_net_private *lp = from_timer(lp, t, timer);
+
+ iss_net_poll(lp);
+ mod_timer(&lp->timer, jiffies + lp->timer_val);
+}
+
+
+static int iss_net_open(struct net_device *dev)
+{
+ struct iss_net_private *lp = netdev_priv(dev);
+ int err;
+
+ err = lp->tp.net_ops->open(lp);
+ if (err < 0)
+ return err;
+
+ netif_start_queue(dev);
+
+ /* clear buffer - it can happen that the host side of the interface
+ * is full when we get here. In this case, new data is never queued,
+ * SIGIOs never arrive, and the net never works.
+ */
+ while ((err = iss_net_rx(dev)) > 0)
+ ;
+
+ timer_setup(&lp->timer, iss_net_timer, 0);
+ lp->timer_val = ISS_NET_TIMER_VALUE;
+ mod_timer(&lp->timer, jiffies + lp->timer_val);
+
+ return err;
+}
+
+static int iss_net_close(struct net_device *dev)
+{
+ struct iss_net_private *lp = netdev_priv(dev);
+
+ netif_stop_queue(dev);
+ del_timer_sync(&lp->timer);
+ lp->tp.net_ops->close(lp);
+
+ return 0;
+}
+
+static int iss_net_start_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+ struct iss_net_private *lp = netdev_priv(dev);
+ int len;
+
+ netif_stop_queue(dev);
+
+ len = lp->tp.net_ops->write(lp, &skb);
+
+ if (len == skb->len) {
+ spin_lock_bh(&lp->lock);
+ lp->stats.tx_packets++;
+ lp->stats.tx_bytes += skb->len;
+ spin_unlock_bh(&lp->lock);
+ netif_trans_update(dev);
+ netif_start_queue(dev);
+
+ /* this is normally done in the interrupt when tx finishes */
+ netif_wake_queue(dev);
+
+ } else if (len == 0) {
+ netif_start_queue(dev);
+ spin_lock_bh(&lp->lock);
+ lp->stats.tx_dropped++;
+ spin_unlock_bh(&lp->lock);
+
+ } else {
+ netif_start_queue(dev);
+ pr_err("%s: %s failed(%d)\n", dev->name, __func__, len);
+ }
+
+
+ dev_kfree_skb(skb);
+ return NETDEV_TX_OK;
+}
+
+
+static void iss_net_get_stats64(struct net_device *dev,
+ struct rtnl_link_stats64 *stats)
+{
+ struct iss_net_private *lp = netdev_priv(dev);
+
+ spin_lock_bh(&lp->lock);
+ *stats = lp->stats;
+ spin_unlock_bh(&lp->lock);
+}
+
+static void iss_net_set_multicast_list(struct net_device *dev)
+{
+}
+
+static void iss_net_tx_timeout(struct net_device *dev, unsigned int txqueue)
+{
+}
+
+static int iss_net_change_mtu(struct net_device *dev, int new_mtu)
+{
+ return -EINVAL;
+}
+
+static void iss_net_user_timer_expire(struct timer_list *unused)
+{
+}
+
+
+static struct platform_driver iss_net_driver = {
+ .driver = {
+ .name = DRIVER_NAME,
+ },
+};
+
+static int driver_registered;
+
+static const struct net_device_ops iss_netdev_ops = {
+ .ndo_open = iss_net_open,
+ .ndo_stop = iss_net_close,
+ .ndo_get_stats64 = iss_net_get_stats64,
+ .ndo_start_xmit = iss_net_start_xmit,
+ .ndo_validate_addr = eth_validate_addr,
+ .ndo_change_mtu = iss_net_change_mtu,
+ .ndo_set_mac_address = eth_mac_addr,
+ .ndo_tx_timeout = iss_net_tx_timeout,
+ .ndo_set_rx_mode = iss_net_set_multicast_list,
+};
+
+static void iss_net_pdev_release(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct iss_net_private *lp =
+ container_of(pdev, struct iss_net_private, pdev);
+
+ free_netdev(lp->dev);
+}
+
+static void iss_net_configure(int index, char *init)
+{
+ struct net_device *dev;
+ struct iss_net_private *lp;
+
+ dev = alloc_etherdev(sizeof(*lp));
+ if (dev == NULL) {
+ pr_err("eth_configure: failed to allocate device\n");
+ return;
+ }
+
+ /* Initialize private element. */
+
+ lp = netdev_priv(dev);
+ *lp = (struct iss_net_private) {
+ .dev = dev,
+ .index = index,
+ };
+
+ spin_lock_init(&lp->lock);
+ /*
+ * If this name ends up conflicting with an existing registered
+ * netdevice, that is OK, register_netdev{,ice}() will notice this
+ * and fail.
+ */
+ snprintf(dev->name, sizeof(dev->name), "eth%d", index);
+
+ /*
+ * Try all transport protocols.
+ * Note: more protocols can be added by adding '&& !X_init(lp, eth)'.
+ */
+
+ if (!tuntap_probe(lp, index, init)) {
+ pr_err("%s: invalid arguments. Skipping device!\n",
+ dev->name);
+ goto err_free_netdev;
+ }
+
+ pr_info("Netdevice %d (%pM)\n", index, dev->dev_addr);
+
+ /* sysfs register */
+
+ if (!driver_registered) {
+ if (platform_driver_register(&iss_net_driver))
+ goto err_free_netdev;
+ driver_registered = 1;
+ }
+
+ lp->pdev.id = index;
+ lp->pdev.name = DRIVER_NAME;
+ lp->pdev.dev.release = iss_net_pdev_release;
+ if (platform_device_register(&lp->pdev))
+ goto err_free_netdev;
+ SET_NETDEV_DEV(dev, &lp->pdev.dev);
+
+ dev->netdev_ops = &iss_netdev_ops;
+ dev->mtu = lp->mtu;
+ dev->watchdog_timeo = (HZ >> 1);
+ dev->irq = -1;
+
+ rtnl_lock();
+ if (register_netdevice(dev)) {
+ rtnl_unlock();
+ pr_err("%s: error registering net device!\n", dev->name);
+ platform_device_unregister(&lp->pdev);
+ return;
+ }
+ rtnl_unlock();
+
+ timer_setup(&lp->tl, iss_net_user_timer_expire, 0);
+
+ return;
+
+err_free_netdev:
+ free_netdev(dev);
+}
+
+/* ------------------------------------------------------------------------- */
+
+/* Filled in during early boot */
+
+struct list_head eth_cmd_line = LIST_HEAD_INIT(eth_cmd_line);
+
+struct iss_net_init {
+ struct list_head list;
+ char *init; /* init string */
+ int index;
+};
+
+/*
+ * Parse the command line and look for 'ethX=...' fields, and register all
+ * those fields. They will be later initialized in iss_net_init.
+ */
+
+static int __init iss_net_setup(char *str)
+{
+ struct iss_net_init *device = NULL;
+ struct iss_net_init *new;
+ struct list_head *ele;
+ char *end;
+ int rc;
+ unsigned n;
+
+ end = strchr(str, '=');
+ if (!end) {
+ pr_err("Expected '=' after device number\n");
+ return 1;
+ }
+ *end = 0;
+ rc = kstrtouint(str, 0, &n);
+ *end = '=';
+ if (rc < 0) {
+ pr_err("Failed to parse '%s'\n", str);
+ return 1;
+ }
+ str = end;
+
+ list_for_each(ele, &eth_cmd_line) {
+ device = list_entry(ele, struct iss_net_init, list);
+ if (device->index == n)
+ break;
+ }
+
+ if (device && device->index == n) {
+ pr_err("Device %u already configured\n", n);
+ return 1;
+ }
+
+ new = memblock_alloc(sizeof(*new), SMP_CACHE_BYTES);
+ if (new == NULL) {
+ pr_err("Alloc_bootmem failed\n");
+ return 1;
+ }
+
+ INIT_LIST_HEAD(&new->list);
+ new->index = n;
+ new->init = str + 1;
+
+ list_add_tail(&new->list, &eth_cmd_line);
+ return 1;
+}
+
+__setup("eth", iss_net_setup);
+
+/*
+ * Initialize all ISS Ethernet devices previously registered in iss_net_setup.
+ */
+
+static int iss_net_init(void)
+{
+ struct list_head *ele, *next;
+
+ /* Walk through all Ethernet devices specified in the command line. */
+
+ list_for_each_safe(ele, next, &eth_cmd_line) {
+ struct iss_net_init *eth;
+ eth = list_entry(ele, struct iss_net_init, list);
+ iss_net_configure(eth->index, eth->init);
+ }
+
+ return 1;
+}
+device_initcall(iss_net_init);
diff --git a/arch/xtensa/platforms/iss/setup.c b/arch/xtensa/platforms/iss/setup.c
new file mode 100644
index 000000000..d3433e1bb
--- /dev/null
+++ b/arch/xtensa/platforms/iss/setup.c
@@ -0,0 +1,85 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ *
+ * arch/xtensa/platform-iss/setup.c
+ *
+ * Platform specific initialization.
+ *
+ * Authors: Chris Zankel <chris@zankel.net>
+ * Joe Taylor <joe@tensilica.com>
+ *
+ * Copyright 2001 - 2005 Tensilica Inc.
+ * Copyright 2017 Cadence Design Systems Inc.
+ */
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/notifier.h>
+#include <linux/panic_notifier.h>
+#include <linux/printk.h>
+#include <linux/string.h>
+
+#include <asm/platform.h>
+#include <asm/setup.h>
+
+#include <platform/simcall.h>
+
+
+void platform_halt(void)
+{
+ pr_info(" ** Called platform_halt() **\n");
+ simc_exit(0);
+}
+
+void platform_power_off(void)
+{
+ pr_info(" ** Called platform_power_off() **\n");
+ simc_exit(0);
+}
+
+void platform_restart(void)
+{
+ /* Flush and reset the mmu, simulate a processor reset, and
+ * jump to the reset vector. */
+ cpu_reset();
+ /* control never gets here */
+}
+
+static int
+iss_panic_event(struct notifier_block *this, unsigned long event, void *ptr)
+{
+ simc_exit(1);
+ return NOTIFY_DONE;
+}
+
+static struct notifier_block iss_panic_block = {
+ .notifier_call = iss_panic_event,
+};
+
+void __init platform_setup(char **p_cmdline)
+{
+ static void *argv[COMMAND_LINE_SIZE / sizeof(void *)] __initdata;
+ static char cmdline[COMMAND_LINE_SIZE] __initdata;
+ int argc = simc_argc();
+ int argv_size = simc_argv_size();
+
+ if (argc > 1) {
+ if (argv_size > sizeof(argv)) {
+ pr_err("%s: command line too long: argv_size = %d\n",
+ __func__, argv_size);
+ } else {
+ int i;
+
+ cmdline[0] = 0;
+ simc_argv((void *)argv);
+
+ for (i = 1; i < argc; ++i) {
+ if (i > 1)
+ strcat(cmdline, " ");
+ strcat(cmdline, argv[i]);
+ }
+ *p_cmdline = cmdline;
+ }
+ }
+
+ atomic_notifier_chain_register(&panic_notifier_list, &iss_panic_block);
+}
diff --git a/arch/xtensa/platforms/iss/simdisk.c b/arch/xtensa/platforms/iss/simdisk.c
new file mode 100644
index 000000000..f50caaa1c
--- /dev/null
+++ b/arch/xtensa/platforms/iss/simdisk.c
@@ -0,0 +1,366 @@
+/*
+ * arch/xtensa/platforms/iss/simdisk.c
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * Copyright (C) 2001-2013 Tensilica Inc.
+ * Authors Victor Prupis
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/string.h>
+#include <linux/blkdev.h>
+#include <linux/bio.h>
+#include <linux/proc_fs.h>
+#include <linux/uaccess.h>
+#include <platform/simcall.h>
+
+#define SIMDISK_MAJOR 240
+#define SIMDISK_MINORS 1
+#define MAX_SIMDISK_COUNT 10
+
+struct simdisk {
+ const char *filename;
+ spinlock_t lock;
+ struct gendisk *gd;
+ struct proc_dir_entry *procfile;
+ int users;
+ unsigned long size;
+ int fd;
+};
+
+
+static int simdisk_count = CONFIG_BLK_DEV_SIMDISK_COUNT;
+module_param(simdisk_count, int, S_IRUGO);
+MODULE_PARM_DESC(simdisk_count, "Number of simdisk units.");
+
+static int n_files;
+static const char *filename[MAX_SIMDISK_COUNT] = {
+#ifdef CONFIG_SIMDISK0_FILENAME
+ CONFIG_SIMDISK0_FILENAME,
+#ifdef CONFIG_SIMDISK1_FILENAME
+ CONFIG_SIMDISK1_FILENAME,
+#endif
+#endif
+};
+
+static int simdisk_param_set_filename(const char *val,
+ const struct kernel_param *kp)
+{
+ if (n_files < ARRAY_SIZE(filename))
+ filename[n_files++] = val;
+ else
+ return -EINVAL;
+ return 0;
+}
+
+static const struct kernel_param_ops simdisk_param_ops_filename = {
+ .set = simdisk_param_set_filename,
+};
+module_param_cb(filename, &simdisk_param_ops_filename, &n_files, 0);
+MODULE_PARM_DESC(filename, "Backing storage filename.");
+
+static int simdisk_major = SIMDISK_MAJOR;
+
+static void simdisk_transfer(struct simdisk *dev, unsigned long sector,
+ unsigned long nsect, char *buffer, int write)
+{
+ unsigned long offset = sector << SECTOR_SHIFT;
+ unsigned long nbytes = nsect << SECTOR_SHIFT;
+
+ if (offset > dev->size || dev->size - offset < nbytes) {
+ pr_notice("Beyond-end %s (%ld %ld)\n",
+ write ? "write" : "read", offset, nbytes);
+ return;
+ }
+
+ spin_lock(&dev->lock);
+ while (nbytes > 0) {
+ unsigned long io;
+
+ simc_lseek(dev->fd, offset, SEEK_SET);
+ READ_ONCE(*buffer);
+ if (write)
+ io = simc_write(dev->fd, buffer, nbytes);
+ else
+ io = simc_read(dev->fd, buffer, nbytes);
+ if (io == -1) {
+ pr_err("SIMDISK: IO error %d\n", errno);
+ break;
+ }
+ buffer += io;
+ offset += io;
+ nbytes -= io;
+ }
+ spin_unlock(&dev->lock);
+}
+
+static void simdisk_submit_bio(struct bio *bio)
+{
+ struct simdisk *dev = bio->bi_bdev->bd_disk->private_data;
+ struct bio_vec bvec;
+ struct bvec_iter iter;
+ sector_t sector = bio->bi_iter.bi_sector;
+
+ bio_for_each_segment(bvec, bio, iter) {
+ char *buffer = bvec_kmap_local(&bvec);
+ unsigned len = bvec.bv_len >> SECTOR_SHIFT;
+
+ simdisk_transfer(dev, sector, len, buffer,
+ bio_data_dir(bio) == WRITE);
+ sector += len;
+ kunmap_local(buffer);
+ }
+
+ bio_endio(bio);
+}
+
+static int simdisk_open(struct block_device *bdev, fmode_t mode)
+{
+ struct simdisk *dev = bdev->bd_disk->private_data;
+
+ spin_lock(&dev->lock);
+ ++dev->users;
+ spin_unlock(&dev->lock);
+ return 0;
+}
+
+static void simdisk_release(struct gendisk *disk, fmode_t mode)
+{
+ struct simdisk *dev = disk->private_data;
+ spin_lock(&dev->lock);
+ --dev->users;
+ spin_unlock(&dev->lock);
+}
+
+static const struct block_device_operations simdisk_ops = {
+ .owner = THIS_MODULE,
+ .submit_bio = simdisk_submit_bio,
+ .open = simdisk_open,
+ .release = simdisk_release,
+};
+
+static struct simdisk *sddev;
+static struct proc_dir_entry *simdisk_procdir;
+
+static int simdisk_attach(struct simdisk *dev, const char *filename)
+{
+ int err = 0;
+
+ filename = kstrdup(filename, GFP_KERNEL);
+ if (filename == NULL)
+ return -ENOMEM;
+
+ spin_lock(&dev->lock);
+
+ if (dev->fd != -1) {
+ err = -EBUSY;
+ goto out;
+ }
+ dev->fd = simc_open(filename, O_RDWR, 0);
+ if (dev->fd == -1) {
+ pr_err("SIMDISK: Can't open %s: %d\n", filename, errno);
+ err = -ENODEV;
+ goto out;
+ }
+ dev->size = simc_lseek(dev->fd, 0, SEEK_END);
+ set_capacity(dev->gd, dev->size >> SECTOR_SHIFT);
+ dev->filename = filename;
+ pr_info("SIMDISK: %s=%s\n", dev->gd->disk_name, dev->filename);
+out:
+ if (err)
+ kfree(filename);
+ spin_unlock(&dev->lock);
+
+ return err;
+}
+
+static int simdisk_detach(struct simdisk *dev)
+{
+ int err = 0;
+
+ spin_lock(&dev->lock);
+
+ if (dev->users != 0) {
+ err = -EBUSY;
+ } else if (dev->fd != -1) {
+ if (simc_close(dev->fd)) {
+ pr_err("SIMDISK: error closing %s: %d\n",
+ dev->filename, errno);
+ err = -EIO;
+ } else {
+ pr_info("SIMDISK: %s detached from %s\n",
+ dev->gd->disk_name, dev->filename);
+ dev->fd = -1;
+ kfree(dev->filename);
+ dev->filename = NULL;
+ }
+ }
+ spin_unlock(&dev->lock);
+ return err;
+}
+
+static ssize_t proc_read_simdisk(struct file *file, char __user *buf,
+ size_t size, loff_t *ppos)
+{
+ struct simdisk *dev = pde_data(file_inode(file));
+ const char *s = dev->filename;
+ if (s) {
+ ssize_t len = strlen(s);
+ char *temp = kmalloc(len + 2, GFP_KERNEL);
+
+ if (!temp)
+ return -ENOMEM;
+
+ len = scnprintf(temp, len + 2, "%s\n", s);
+ len = simple_read_from_buffer(buf, size, ppos,
+ temp, len);
+
+ kfree(temp);
+ return len;
+ }
+ return simple_read_from_buffer(buf, size, ppos, "\n", 1);
+}
+
+static ssize_t proc_write_simdisk(struct file *file, const char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ char *tmp = memdup_user_nul(buf, count);
+ struct simdisk *dev = pde_data(file_inode(file));
+ int err;
+
+ if (IS_ERR(tmp))
+ return PTR_ERR(tmp);
+
+ err = simdisk_detach(dev);
+ if (err != 0)
+ goto out_free;
+
+ if (count > 0 && tmp[count - 1] == '\n')
+ tmp[count - 1] = 0;
+
+ if (tmp[0])
+ err = simdisk_attach(dev, tmp);
+
+ if (err == 0)
+ err = count;
+out_free:
+ kfree(tmp);
+ return err;
+}
+
+static const struct proc_ops simdisk_proc_ops = {
+ .proc_read = proc_read_simdisk,
+ .proc_write = proc_write_simdisk,
+ .proc_lseek = default_llseek,
+};
+
+static int __init simdisk_setup(struct simdisk *dev, int which,
+ struct proc_dir_entry *procdir)
+{
+ char tmp[2] = { '0' + which, 0 };
+ int err = -ENOMEM;
+
+ dev->fd = -1;
+ dev->filename = NULL;
+ spin_lock_init(&dev->lock);
+ dev->users = 0;
+
+ dev->gd = blk_alloc_disk(NUMA_NO_NODE);
+ if (!dev->gd)
+ goto out;
+ dev->gd->major = simdisk_major;
+ dev->gd->first_minor = which;
+ dev->gd->minors = SIMDISK_MINORS;
+ dev->gd->fops = &simdisk_ops;
+ dev->gd->private_data = dev;
+ snprintf(dev->gd->disk_name, 32, "simdisk%d", which);
+ set_capacity(dev->gd, 0);
+ err = add_disk(dev->gd);
+ if (err)
+ goto out_cleanup_disk;
+
+ dev->procfile = proc_create_data(tmp, 0644, procdir, &simdisk_proc_ops, dev);
+
+ return 0;
+
+out_cleanup_disk:
+ put_disk(dev->gd);
+out:
+ return err;
+}
+
+static int __init simdisk_init(void)
+{
+ int i;
+
+ if (register_blkdev(simdisk_major, "simdisk") < 0) {
+ pr_err("SIMDISK: register_blkdev: %d\n", simdisk_major);
+ return -EIO;
+ }
+ pr_info("SIMDISK: major: %d\n", simdisk_major);
+
+ if (n_files > simdisk_count)
+ simdisk_count = n_files;
+ if (simdisk_count > MAX_SIMDISK_COUNT)
+ simdisk_count = MAX_SIMDISK_COUNT;
+
+ sddev = kmalloc_array(simdisk_count, sizeof(*sddev), GFP_KERNEL);
+ if (sddev == NULL)
+ goto out_unregister;
+
+ simdisk_procdir = proc_mkdir("simdisk", 0);
+ if (simdisk_procdir == NULL)
+ goto out_free_unregister;
+
+ for (i = 0; i < simdisk_count; ++i) {
+ if (simdisk_setup(sddev + i, i, simdisk_procdir) == 0) {
+ if (filename[i] != NULL && filename[i][0] != 0 &&
+ (n_files == 0 || i < n_files))
+ simdisk_attach(sddev + i, filename[i]);
+ }
+ }
+
+ return 0;
+
+out_free_unregister:
+ kfree(sddev);
+out_unregister:
+ unregister_blkdev(simdisk_major, "simdisk");
+ return -ENOMEM;
+}
+module_init(simdisk_init);
+
+static void simdisk_teardown(struct simdisk *dev, int which,
+ struct proc_dir_entry *procdir)
+{
+ char tmp[2] = { '0' + which, 0 };
+
+ simdisk_detach(dev);
+ if (dev->gd) {
+ del_gendisk(dev->gd);
+ put_disk(dev->gd);
+ }
+ remove_proc_entry(tmp, procdir);
+}
+
+static void __exit simdisk_exit(void)
+{
+ int i;
+
+ for (i = 0; i < simdisk_count; ++i)
+ simdisk_teardown(sddev + i, i, simdisk_procdir);
+ remove_proc_entry("simdisk", 0);
+ kfree(sddev);
+ unregister_blkdev(simdisk_major, "simdisk");
+}
+module_exit(simdisk_exit);
+
+MODULE_ALIAS_BLOCKDEV_MAJOR(SIMDISK_MAJOR);
+
+MODULE_LICENSE("GPL");
diff --git a/arch/xtensa/platforms/xt2000/Makefile b/arch/xtensa/platforms/xt2000/Makefile
new file mode 100644
index 000000000..53eaeba5e
--- /dev/null
+++ b/arch/xtensa/platforms/xt2000/Makefile
@@ -0,0 +1,6 @@
+# SPDX-License-Identifier: GPL-2.0-only
+#
+# Makefile for the Tensilica XT2000 Emulation Board
+#
+
+obj-y = setup.o
diff --git a/arch/xtensa/platforms/xt2000/include/platform/hardware.h b/arch/xtensa/platforms/xt2000/include/platform/hardware.h
new file mode 100644
index 000000000..9f213f573
--- /dev/null
+++ b/arch/xtensa/platforms/xt2000/include/platform/hardware.h
@@ -0,0 +1,43 @@
+/*
+ * platform/hardware.h
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * Copyright (C) 2001 Tensilica Inc.
+ */
+
+/*
+ * This file contains the hardware configuration of the XT2000 board.
+ */
+
+#ifndef _XTENSA_XT2000_HARDWARE_H
+#define _XTENSA_XT2000_HARDWARE_H
+
+#include <asm/core.h>
+
+/*
+ * On-board components.
+ */
+
+#define SONIC83934_INTNUM XCHAL_EXTINT3_NUM
+#define SONIC83934_ADDR IOADDR(0x0d030000)
+
+/*
+ * V3-PCI
+ */
+
+/* The XT2000 uses the V3 as a cascaded interrupt controller for the PCI bus */
+
+#define IRQ_PCI_A (XCHAL_NUM_INTERRUPTS + 0)
+#define IRQ_PCI_B (XCHAL_NUM_INTERRUPTS + 1)
+#define IRQ_PCI_C (XCHAL_NUM_INTERRUPTS + 2)
+
+/*
+ * Various other components.
+ */
+
+#define XT2000_LED_ADDR IOADDR(0x0d040000)
+
+#endif /* _XTENSA_XT2000_HARDWARE_H */
diff --git a/arch/xtensa/platforms/xt2000/include/platform/serial.h b/arch/xtensa/platforms/xt2000/include/platform/serial.h
new file mode 100644
index 000000000..cde804827
--- /dev/null
+++ b/arch/xtensa/platforms/xt2000/include/platform/serial.h
@@ -0,0 +1,28 @@
+/*
+ * platform/serial.h
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * Copyright (C) 2001 Tensilica Inc.
+ */
+
+#ifndef _XTENSA_XT2000_SERIAL_H
+#define _XTENSA_XT2000_SERIAL_H
+
+#include <asm/core.h>
+#include <asm/io.h>
+
+/* National-Semi PC16552D DUART: */
+
+#define DUART16552_1_INTNUM XCHAL_EXTINT4_NUM
+#define DUART16552_2_INTNUM XCHAL_EXTINT5_NUM
+
+#define DUART16552_1_ADDR IOADDR(0x0d050020) /* channel 1 */
+#define DUART16552_2_ADDR IOADDR(0x0d050000) /* channel 2 */
+
+#define DUART16552_XTAL_FREQ 18432000 /* crystal frequency in Hz */
+#define BASE_BAUD ( DUART16552_XTAL_FREQ / 16 )
+
+#endif /* _XTENSA_XT2000_SERIAL_H */
diff --git a/arch/xtensa/platforms/xt2000/setup.c b/arch/xtensa/platforms/xt2000/setup.c
new file mode 100644
index 000000000..0dc22c371
--- /dev/null
+++ b/arch/xtensa/platforms/xt2000/setup.c
@@ -0,0 +1,150 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * arch/xtensa/platforms/xt2000/setup.c
+ *
+ * Platform specific functions for the XT2000 board.
+ *
+ * Authors: Chris Zankel <chris@zankel.net>
+ * Joe Taylor <joe@tensilica.com>
+ *
+ * Copyright 2001 - 2004 Tensilica Inc.
+ */
+#include <linux/stddef.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/errno.h>
+#include <linux/reboot.h>
+#include <linux/kdev_t.h>
+#include <linux/types.h>
+#include <linux/major.h>
+#include <linux/console.h>
+#include <linux/delay.h>
+#include <linux/stringify.h>
+#include <linux/platform_device.h>
+#include <linux/serial.h>
+#include <linux/serial_8250.h>
+
+#include <asm/processor.h>
+#include <asm/platform.h>
+#include <asm/bootparam.h>
+#include <platform/hardware.h>
+#include <platform/serial.h>
+
+/* Assumes s points to an 8-chr string. No checking for NULL. */
+
+static void led_print (int f, char *s)
+{
+ unsigned long* led_addr = (unsigned long*) (XT2000_LED_ADDR + 0xE0) + f;
+ int i;
+ for (i = f; i < 8; i++)
+ if ((*led_addr++ = *s++) == 0)
+ break;
+}
+
+void platform_halt(void)
+{
+ led_print (0, " HALT ");
+ local_irq_disable();
+ while (1);
+}
+
+void platform_power_off(void)
+{
+ led_print (0, "POWEROFF");
+ local_irq_disable();
+ while (1);
+}
+
+void platform_restart(void)
+{
+ /* Flush and reset the mmu, simulate a processor reset, and
+ * jump to the reset vector. */
+ cpu_reset();
+ /* control never gets here */
+}
+
+void __init platform_setup(char** cmdline)
+{
+ led_print (0, "LINUX ");
+}
+
+/* early initialization */
+
+void __init platform_init(bp_tag_t *first)
+{
+}
+
+/* Heartbeat. Let the LED blink. */
+
+void platform_heartbeat(void)
+{
+ static int i, t;
+
+ if (--t < 0)
+ {
+ t = 59;
+ led_print(7, i ? ".": " ");
+ i ^= 1;
+ }
+}
+
+//#define RS_TABLE_SIZE 2
+
+#define _SERIAL_PORT(_base,_irq) \
+{ \
+ .mapbase = (_base), \
+ .membase = (void*)(_base), \
+ .irq = (_irq), \
+ .uartclk = DUART16552_XTAL_FREQ, \
+ .iotype = UPIO_MEM, \
+ .flags = UPF_BOOT_AUTOCONF, \
+ .regshift = 2, \
+}
+
+static struct plat_serial8250_port xt2000_serial_data[] = {
+#if XCHAL_HAVE_BE
+ _SERIAL_PORT(DUART16552_1_ADDR + 3, DUART16552_1_INTNUM),
+ _SERIAL_PORT(DUART16552_2_ADDR + 3, DUART16552_2_INTNUM),
+#else
+ _SERIAL_PORT(DUART16552_1_ADDR, DUART16552_1_INTNUM),
+ _SERIAL_PORT(DUART16552_2_ADDR, DUART16552_2_INTNUM),
+#endif
+ { }
+};
+
+static struct platform_device xt2000_serial8250_device = {
+ .name = "serial8250",
+ .id = PLAT8250_DEV_PLATFORM,
+ .dev = {
+ .platform_data = xt2000_serial_data,
+ },
+};
+
+static struct resource xt2000_sonic_res[] = {
+ {
+ .start = SONIC83934_ADDR,
+ .end = SONIC83934_ADDR + 0xff,
+ .flags = IORESOURCE_MEM,
+ },
+ {
+ .start = SONIC83934_INTNUM,
+ .end = SONIC83934_INTNUM,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static struct platform_device xt2000_sonic_device = {
+ .name = "xtsonic",
+ .num_resources = ARRAY_SIZE(xt2000_sonic_res),
+ .resource = xt2000_sonic_res,
+};
+
+static int __init xt2000_setup_devinit(void)
+{
+ platform_device_register(&xt2000_serial8250_device);
+ platform_device_register(&xt2000_sonic_device);
+
+ return 0;
+}
+
+device_initcall(xt2000_setup_devinit);
diff --git a/arch/xtensa/platforms/xtfpga/Makefile b/arch/xtensa/platforms/xtfpga/Makefile
new file mode 100644
index 000000000..0600371b2
--- /dev/null
+++ b/arch/xtensa/platforms/xtfpga/Makefile
@@ -0,0 +1,11 @@
+# SPDX-License-Identifier: GPL-2.0-only
+# Makefile for the Tensilica xtavnet Emulation Board
+#
+# Note! Dependencies are done automagically by 'make dep', which also
+# removes any old dependencies. DON'T put your own dependencies here
+# unless it's something special (ie not a .c file).
+#
+# Note 2! The CFLAGS definitions are in the main makefile...
+
+obj-y += setup.o
+obj-$(CONFIG_XTFPGA_LCD) += lcd.o
diff --git a/arch/xtensa/platforms/xtfpga/include/platform/hardware.h b/arch/xtensa/platforms/xtfpga/include/platform/hardware.h
new file mode 100644
index 000000000..30d9cb6cf
--- /dev/null
+++ b/arch/xtensa/platforms/xtfpga/include/platform/hardware.h
@@ -0,0 +1,60 @@
+/*
+ * arch/xtensa/platform/xtavnet/include/platform/hardware.h
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * Copyright (C) 2006 Tensilica Inc.
+ */
+
+/*
+ * This file contains the hardware configuration of the XTAVNET boards.
+ */
+
+#include <asm/types.h>
+
+#ifndef __XTENSA_XTAVNET_HARDWARE_H
+#define __XTENSA_XTAVNET_HARDWARE_H
+
+/* Default assignment of LX60 devices to external interrupts. */
+
+#ifdef CONFIG_XTENSA_MX
+#define DUART16552_INTNUM XCHAL_EXTINT3_NUM
+#define OETH_IRQ XCHAL_EXTINT4_NUM
+#define C67X00_IRQ XCHAL_EXTINT8_NUM
+#else
+#define DUART16552_INTNUM XCHAL_EXTINT0_NUM
+#define OETH_IRQ XCHAL_EXTINT1_NUM
+#define C67X00_IRQ XCHAL_EXTINT5_NUM
+#endif
+
+/*
+ * Device addresses and parameters.
+ */
+
+/* UART */
+#define DUART16552_PADDR (XCHAL_KIO_PADDR + 0x0D050020)
+
+/* Misc. */
+#define XTFPGA_FPGAREGS_VADDR IOADDR(0x0D020000)
+/* Clock frequency in Hz (read-only): */
+#define XTFPGA_CLKFRQ_VADDR (XTFPGA_FPGAREGS_VADDR + 0x04)
+/* Setting of 8 DIP switches: */
+#define DIP_SWITCHES_VADDR (XTFPGA_FPGAREGS_VADDR + 0x0C)
+/* Software reset (write 0xdead): */
+#define XTFPGA_SWRST_VADDR (XTFPGA_FPGAREGS_VADDR + 0x10)
+
+/* OpenCores Ethernet controller: */
+ /* regs + RX/TX descriptors */
+#define OETH_REGS_PADDR (XCHAL_KIO_PADDR + 0x0D030000)
+#define OETH_REGS_SIZE 0x1000
+#define OETH_SRAMBUFF_PADDR (XCHAL_KIO_PADDR + 0x0D800000)
+
+ /* 5*rx buffs + 5*tx buffs */
+#define OETH_SRAMBUFF_SIZE (5 * 0x600 + 5 * 0x600)
+
+#define C67X00_PADDR (XCHAL_KIO_PADDR + 0x0D0D0000)
+#define C67X00_SIZE 0x10
+
+#endif /* __XTENSA_XTAVNET_HARDWARE_H */
diff --git a/arch/xtensa/platforms/xtfpga/include/platform/lcd.h b/arch/xtensa/platforms/xtfpga/include/platform/lcd.h
new file mode 100644
index 000000000..4c8541ed1
--- /dev/null
+++ b/arch/xtensa/platforms/xtfpga/include/platform/lcd.h
@@ -0,0 +1,35 @@
+/*
+ * arch/xtensa/platform/xtavnet/include/platform/lcd.h
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * Copyright (C) 2001, 2006 Tensilica Inc.
+ */
+
+#ifndef __XTENSA_XTAVNET_LCD_H
+#define __XTENSA_XTAVNET_LCD_H
+
+#ifdef CONFIG_XTFPGA_LCD
+/* Display string STR at position POS on the LCD. */
+void lcd_disp_at_pos(char *str, unsigned char pos);
+
+/* Shift the contents of the LCD display left or right. */
+void lcd_shiftleft(void);
+void lcd_shiftright(void);
+#else
+static inline void lcd_disp_at_pos(char *str, unsigned char pos)
+{
+}
+
+static inline void lcd_shiftleft(void)
+{
+}
+
+static inline void lcd_shiftright(void)
+{
+}
+#endif
+
+#endif
diff --git a/arch/xtensa/platforms/xtfpga/include/platform/serial.h b/arch/xtensa/platforms/xtfpga/include/platform/serial.h
new file mode 100644
index 000000000..14d8f7bee
--- /dev/null
+++ b/arch/xtensa/platforms/xtfpga/include/platform/serial.h
@@ -0,0 +1,18 @@
+/*
+ * arch/xtensa/platform/xtavnet/include/platform/serial.h
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * Copyright (C) 2001, 2006 Tensilica Inc.
+ */
+
+#ifndef __ASM_XTENSA_XTAVNET_SERIAL_H
+#define __ASM_XTENSA_XTAVNET_SERIAL_H
+
+#include <platform/hardware.h>
+
+#define BASE_BAUD (*(long *)XTFPGA_CLKFRQ_VADDR / 16)
+
+#endif /* __ASM_XTENSA_XTAVNET_SERIAL_H */
diff --git a/arch/xtensa/platforms/xtfpga/lcd.c b/arch/xtensa/platforms/xtfpga/lcd.c
new file mode 100644
index 000000000..2f7eb66c2
--- /dev/null
+++ b/arch/xtensa/platforms/xtfpga/lcd.c
@@ -0,0 +1,89 @@
+/*
+ * Driver for the LCD display on the Tensilica XTFPGA board family.
+ * http://www.mytechcorp.com/cfdata/productFile/File1/MOC-16216B-B-A0A04.pdf
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * Copyright (C) 2001, 2006 Tensilica Inc.
+ * Copyright (C) 2015 Cadence Design Systems Inc.
+ */
+
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/io.h>
+
+#include <platform/hardware.h>
+#include <platform/lcd.h>
+
+/* LCD instruction and data addresses. */
+#define LCD_INSTR_ADDR ((char *)IOADDR(CONFIG_XTFPGA_LCD_BASE_ADDR))
+#define LCD_DATA_ADDR (LCD_INSTR_ADDR + 4)
+
+#define LCD_CLEAR 0x1
+#define LCD_DISPLAY_ON 0xc
+
+/* 8bit and 2 lines display */
+#define LCD_DISPLAY_MODE8BIT 0x38
+#define LCD_DISPLAY_MODE4BIT 0x28
+#define LCD_DISPLAY_POS 0x80
+#define LCD_SHIFT_LEFT 0x18
+#define LCD_SHIFT_RIGHT 0x1c
+
+static void lcd_put_byte(u8 *addr, u8 data)
+{
+#ifdef CONFIG_XTFPGA_LCD_8BIT_ACCESS
+ WRITE_ONCE(*addr, data);
+#else
+ WRITE_ONCE(*addr, data & 0xf0);
+ WRITE_ONCE(*addr, (data << 4) & 0xf0);
+#endif
+}
+
+static int __init lcd_init(void)
+{
+ WRITE_ONCE(*LCD_INSTR_ADDR, LCD_DISPLAY_MODE8BIT);
+ mdelay(5);
+ WRITE_ONCE(*LCD_INSTR_ADDR, LCD_DISPLAY_MODE8BIT);
+ udelay(200);
+ WRITE_ONCE(*LCD_INSTR_ADDR, LCD_DISPLAY_MODE8BIT);
+ udelay(50);
+#ifndef CONFIG_XTFPGA_LCD_8BIT_ACCESS
+ WRITE_ONCE(*LCD_INSTR_ADDR, LCD_DISPLAY_MODE4BIT);
+ udelay(50);
+ lcd_put_byte(LCD_INSTR_ADDR, LCD_DISPLAY_MODE4BIT);
+ udelay(50);
+#endif
+ lcd_put_byte(LCD_INSTR_ADDR, LCD_DISPLAY_ON);
+ udelay(50);
+ lcd_put_byte(LCD_INSTR_ADDR, LCD_CLEAR);
+ mdelay(10);
+ lcd_disp_at_pos("XTENSA LINUX", 0);
+ return 0;
+}
+
+void lcd_disp_at_pos(char *str, unsigned char pos)
+{
+ lcd_put_byte(LCD_INSTR_ADDR, LCD_DISPLAY_POS | pos);
+ udelay(100);
+ while (*str != 0) {
+ lcd_put_byte(LCD_DATA_ADDR, *str);
+ udelay(200);
+ str++;
+ }
+}
+
+void lcd_shiftleft(void)
+{
+ lcd_put_byte(LCD_INSTR_ADDR, LCD_SHIFT_LEFT);
+ udelay(50);
+}
+
+void lcd_shiftright(void)
+{
+ lcd_put_byte(LCD_INSTR_ADDR, LCD_SHIFT_RIGHT);
+ udelay(50);
+}
+
+arch_initcall(lcd_init);
diff --git a/arch/xtensa/platforms/xtfpga/setup.c b/arch/xtensa/platforms/xtfpga/setup.c
new file mode 100644
index 000000000..c79c1d09e
--- /dev/null
+++ b/arch/xtensa/platforms/xtfpga/setup.c
@@ -0,0 +1,292 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ *
+ * arch/xtensa/platform/xtavnet/setup.c
+ *
+ * ...
+ *
+ * Authors: Chris Zankel <chris@zankel.net>
+ * Joe Taylor <joe@tensilica.com>
+ *
+ * Copyright 2001 - 2006 Tensilica Inc.
+ */
+#include <linux/stddef.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/errno.h>
+#include <linux/reboot.h>
+#include <linux/kdev_t.h>
+#include <linux/types.h>
+#include <linux/major.h>
+#include <linux/console.h>
+#include <linux/delay.h>
+#include <linux/of.h>
+#include <linux/clk-provider.h>
+#include <linux/of_address.h>
+#include <linux/slab.h>
+
+#include <asm/timex.h>
+#include <asm/processor.h>
+#include <asm/platform.h>
+#include <asm/bootparam.h>
+#include <platform/lcd.h>
+#include <platform/hardware.h>
+
+void platform_halt(void)
+{
+ lcd_disp_at_pos(" HALT ", 0);
+ local_irq_disable();
+ while (1)
+ cpu_relax();
+}
+
+void platform_power_off(void)
+{
+ lcd_disp_at_pos("POWEROFF", 0);
+ local_irq_disable();
+ while (1)
+ cpu_relax();
+}
+
+void platform_restart(void)
+{
+ /* Try software reset first. */
+ WRITE_ONCE(*(u32 *)XTFPGA_SWRST_VADDR, 0xdead);
+
+ /* If software reset did not work, flush and reset the mmu,
+ * simulate a processor reset, and jump to the reset vector.
+ */
+ cpu_reset();
+ /* control never gets here */
+}
+
+#ifdef CONFIG_XTENSA_CALIBRATE_CCOUNT
+
+void __init platform_calibrate_ccount(void)
+{
+ ccount_freq = *(long *)XTFPGA_CLKFRQ_VADDR;
+}
+
+#endif
+
+#ifdef CONFIG_USE_OF
+
+static void __init xtfpga_clk_setup(struct device_node *np)
+{
+ void __iomem *base = of_iomap(np, 0);
+ struct clk *clk;
+ u32 freq;
+
+ if (!base) {
+ pr_err("%pOFn: invalid address\n", np);
+ return;
+ }
+
+ freq = __raw_readl(base);
+ iounmap(base);
+ clk = clk_register_fixed_rate(NULL, np->name, NULL, 0, freq);
+
+ if (IS_ERR(clk)) {
+ pr_err("%pOFn: clk registration failed\n", np);
+ return;
+ }
+
+ if (of_clk_add_provider(np, of_clk_src_simple_get, clk)) {
+ pr_err("%pOFn: clk provider registration failed\n", np);
+ return;
+ }
+}
+CLK_OF_DECLARE(xtfpga_clk, "cdns,xtfpga-clock", xtfpga_clk_setup);
+
+#define MAC_LEN 6
+static void __init update_local_mac(struct device_node *node)
+{
+ struct property *newmac;
+ const u8* macaddr;
+ int prop_len;
+
+ macaddr = of_get_property(node, "local-mac-address", &prop_len);
+ if (macaddr == NULL || prop_len != MAC_LEN)
+ return;
+
+ newmac = kzalloc(sizeof(*newmac) + MAC_LEN, GFP_KERNEL);
+ if (newmac == NULL)
+ return;
+
+ newmac->value = newmac + 1;
+ newmac->length = MAC_LEN;
+ newmac->name = kstrdup("local-mac-address", GFP_KERNEL);
+ if (newmac->name == NULL) {
+ kfree(newmac);
+ return;
+ }
+
+ memcpy(newmac->value, macaddr, MAC_LEN);
+ ((u8*)newmac->value)[5] = (*(u32*)DIP_SWITCHES_VADDR) & 0x3f;
+ of_update_property(node, newmac);
+}
+
+static int __init machine_setup(void)
+{
+ struct device_node *eth = NULL;
+
+ if ((eth = of_find_compatible_node(eth, NULL, "opencores,ethoc")))
+ update_local_mac(eth);
+ of_node_put(eth);
+ return 0;
+}
+arch_initcall(machine_setup);
+
+#else
+
+#include <linux/serial_8250.h>
+#include <linux/if.h>
+#include <net/ethoc.h>
+#include <linux/usb/c67x00.h>
+
+/*----------------------------------------------------------------------------
+ * Ethernet -- OpenCores Ethernet MAC (ethoc driver)
+ */
+
+static struct resource ethoc_res[] = {
+ [0] = { /* register space */
+ .start = OETH_REGS_PADDR,
+ .end = OETH_REGS_PADDR + OETH_REGS_SIZE - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ [1] = { /* buffer space */
+ .start = OETH_SRAMBUFF_PADDR,
+ .end = OETH_SRAMBUFF_PADDR + OETH_SRAMBUFF_SIZE - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ [2] = { /* IRQ number */
+ .start = XTENSA_PIC_LINUX_IRQ(OETH_IRQ),
+ .end = XTENSA_PIC_LINUX_IRQ(OETH_IRQ),
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static struct ethoc_platform_data ethoc_pdata = {
+ /*
+ * The MAC address for these boards is 00:50:c2:13:6f:xx.
+ * The last byte (here as zero) is read from the DIP switches on the
+ * board.
+ */
+ .hwaddr = { 0x00, 0x50, 0xc2, 0x13, 0x6f, 0 },
+ .phy_id = -1,
+ .big_endian = XCHAL_HAVE_BE,
+};
+
+static struct platform_device ethoc_device = {
+ .name = "ethoc",
+ .id = -1,
+ .num_resources = ARRAY_SIZE(ethoc_res),
+ .resource = ethoc_res,
+ .dev = {
+ .platform_data = &ethoc_pdata,
+ },
+};
+
+/*----------------------------------------------------------------------------
+ * USB Host/Device -- Cypress CY7C67300
+ */
+
+static struct resource c67x00_res[] = {
+ [0] = { /* register space */
+ .start = C67X00_PADDR,
+ .end = C67X00_PADDR + C67X00_SIZE - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ [1] = { /* IRQ number */
+ .start = XTENSA_PIC_LINUX_IRQ(C67X00_IRQ),
+ .end = XTENSA_PIC_LINUX_IRQ(C67X00_IRQ),
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static struct c67x00_platform_data c67x00_pdata = {
+ .sie_config = C67X00_SIE1_HOST | C67X00_SIE2_UNUSED,
+ .hpi_regstep = 4,
+};
+
+static struct platform_device c67x00_device = {
+ .name = "c67x00",
+ .id = -1,
+ .num_resources = ARRAY_SIZE(c67x00_res),
+ .resource = c67x00_res,
+ .dev = {
+ .platform_data = &c67x00_pdata,
+ },
+};
+
+/*----------------------------------------------------------------------------
+ * UART
+ */
+
+static struct resource serial_resource = {
+ .start = DUART16552_PADDR,
+ .end = DUART16552_PADDR + 0x1f,
+ .flags = IORESOURCE_MEM,
+};
+
+static struct plat_serial8250_port serial_platform_data[] = {
+ [0] = {
+ .mapbase = DUART16552_PADDR,
+ .irq = XTENSA_PIC_LINUX_IRQ(DUART16552_INTNUM),
+ .flags = UPF_BOOT_AUTOCONF | UPF_SKIP_TEST |
+ UPF_IOREMAP,
+ .iotype = XCHAL_HAVE_BE ? UPIO_MEM32BE : UPIO_MEM32,
+ .regshift = 2,
+ .uartclk = 0, /* set in xtavnet_init() */
+ },
+ { },
+};
+
+static struct platform_device xtavnet_uart = {
+ .name = "serial8250",
+ .id = PLAT8250_DEV_PLATFORM,
+ .dev = {
+ .platform_data = serial_platform_data,
+ },
+ .num_resources = 1,
+ .resource = &serial_resource,
+};
+
+/* platform devices */
+static struct platform_device *platform_devices[] __initdata = {
+ &ethoc_device,
+ &c67x00_device,
+ &xtavnet_uart,
+};
+
+
+static int __init xtavnet_init(void)
+{
+ /* Ethernet MAC address. */
+ ethoc_pdata.hwaddr[5] = *(u32 *)DIP_SWITCHES_VADDR;
+
+ /* Clock rate varies among FPGA bitstreams; board specific FPGA register
+ * reports the actual clock rate.
+ */
+ serial_platform_data[0].uartclk = *(long *)XTFPGA_CLKFRQ_VADDR;
+
+
+ /* register platform devices */
+ platform_add_devices(platform_devices, ARRAY_SIZE(platform_devices));
+
+ /* ETHOC driver is a bit quiet; at least display Ethernet MAC, so user
+ * knows whether they set it correctly on the DIP switches.
+ */
+ pr_info("XTFPGA: Ethernet MAC %pM\n", ethoc_pdata.hwaddr);
+ ethoc_pdata.eth_clkfreq = *(long *)XTFPGA_CLKFRQ_VADDR;
+
+ return 0;
+}
+
+/*
+ * Register to be done during do_initcalls().
+ */
+arch_initcall(xtavnet_init);
+
+#endif /* CONFIG_USE_OF */