summaryrefslogtreecommitdiffstats
path: root/arch/mips/sni
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2026-08-30 09:22:00 -0700
committerLinus Torvalds <torvalds@linux-foundation.org>2026-08-30 09:22:00 -0700
commit034dd340b08be1f2f0477ad16131d609f9dbd53c (patch)
tree2536f4b2d7893ccd916b5abdf3c454ad6edc03c2 /arch/mips/sni
downloadlinux-stable-034dd340b08be1f2f0477ad16131d609f9dbd53c.tar.gz
linux-stable-034dd340b08be1f2f0477ad16131d609f9dbd53c.zip
Merge tag 'trace-v7.3-2' of git://git.kernel.org/pub/scm/linux/kernel/git/trace/linux-tracegrafted
Pull tracing fixes from Steven Rostedt: - Fix error output of boot instance creation failure Currently if a boot instance creation fails, instead of printing out the name of the instance that failed, it prints "(null)". That is because it prints "cur_str" that had already been processed by strsep(). Print the saved name instead. While at it, print the error code of the failure. - Fix use-after-free for same named historgrams Histograms can be named so that they can be used in multiple events. But if the named histogram has a variable attached, the second event that uses the named histogram which duplicates it and needs to free the original after duplication leaves the old variable in place and still visible. If another histogram uses than variable, it will use the stale one which will try to reference the freed duplicate histogram and crash the kernel. Free the duplicate variables along with the duplicated histogram data. - Check return value of kthread_run() in event self test The events self tests uses a kthread for testing but does not check if it succeeded in creating a kthread. If the kthread creation were to fail, the code will still try to call kthread_stop() on the error returned. - Fix race between reading trace_pipe and updating subbuffer size If a user is reading the trace_pipe file at the same time they update the ring buffer sub-buffer size, can cause the trace_pipe read to read stale data. Add trace_access_lock() around updating the ring buffer sub-buffer size. - Fix eventfs_inode on failure path in creation of the events directory In the creation of the "events" directory, if after allocating the eventfs_inode a failure is detected, it calls cleanup_ei() which calls free_ei(). The free_ei() will test if eventfs_inode being freed has no children. It is a bug if it does. But on the failure case of the creation of the "events" directory, the children lists have not yet been initialized and the free will trigger a warning because list_empty() on an uninitialized list returns false. Move the initialization into init_ei() where it makes more sense and makes sure that a created eventfs_inode has its lists initialized upon creation. - Check return value of kthread_run() in ftrace direct sample code The sample code that shows how to use the ftrace direct calls does not test the return of kthread_run() to see if it succeeds. Return a failure if the kthread_run() doesn't succeed. - Clear user events state on fork in case of alloc failure On fork, the child gets a pointer to the parent's user events state. It makes a copy of it then updates the child's pointer to it. But if the allocation fails, the duplication function leaves the child with a pointer to its parent's descriptor. When the child cleans up its data, it will free the parent's descriptor while the parent is still using it. In the duplication function, set the child's user_event_mm to NULL before testing if the allocation succeeded, and when it exits it will not free the parent's descriptor. - Fix retry exhaustion in simple ring buffer reader swap simple_ring_buffer_swap_reader_page() starts with retry set to 8 and post-decrements it only after a failed link replacement. On the final attempt, a successful replacement leaves retry at zero, while a failed replacement leaves it at -1. But the check for success expects the retry value to be non-zero and exits with an error on zero. This is the opposite result. Fix it. - Fail nicely when the remote swap_reader_page() returns an error Currently, if the swap_reader_page() of a remote buffer fails, it triggers a WARN_ON_ONCE() and continues normally. Instead, have it exit with an error and a pr_warn() print instead of a full WARNING. * tag 'trace-v7.3-2' of git://git.kernel.org/pub/scm/linux/kernel/git/trace/linux-trace: ring-buffer: Stop remote reader update when page swap fails tracing: Fix retry exhaustion in simple ring buffer reader swap tracing/user_events: Clear copied tracing state before fork duplication samples/ftrace: Fix kthread_stop() on ERR_PTR in ftrace-direct-multi-modify samples/ftrace: Fix kthread_stop() on ERR_PTR in ftrace-direct-modify eventfs: Initialize ei->children and ei->list in init_ei() tracing: Fix use-after-free in trace_pipe read on sub-buffer order change tracing: Fix crash passing ERR_PTR to kthread_stop() tracing: Fix use-after-free with same-name named triggers tracing: Fix logged instance name on creation failure
Diffstat (limited to 'arch/mips/sni')
-rw-r--r--arch/mips/sni/Makefile7
-rw-r--r--arch/mips/sni/Platform10
-rw-r--r--arch/mips/sni/a20r.c256
-rw-r--r--arch/mips/sni/eisa.c49
-rw-r--r--arch/mips/sni/irq.c76
-rw-r--r--arch/mips/sni/pcimt.c332
-rw-r--r--arch/mips/sni/pcit.c295
-rw-r--r--arch/mips/sni/reset.c48
-rw-r--r--arch/mips/sni/rm200.c485
-rw-r--r--arch/mips/sni/setup.c265
-rw-r--r--arch/mips/sni/time.c167
11 files changed, 1990 insertions, 0 deletions
diff --git a/arch/mips/sni/Makefile b/arch/mips/sni/Makefile
new file mode 100644
index 000000000..6d97c3e96
--- /dev/null
+++ b/arch/mips/sni/Makefile
@@ -0,0 +1,7 @@
+# SPDX-License-Identifier: GPL-2.0-only
+#
+# Makefile for the SNI specific part of the kernel
+#
+
+obj-y += irq.o reset.o setup.o a20r.o rm200.o pcimt.o pcit.o time.o
+obj-$(CONFIG_EISA) += eisa.o
diff --git a/arch/mips/sni/Platform b/arch/mips/sni/Platform
new file mode 100644
index 000000000..b0b3dde0b
--- /dev/null
+++ b/arch/mips/sni/Platform
@@ -0,0 +1,10 @@
+#
+# SNI RM
+#
+cflags-$(CONFIG_SNI_RM) += -I$(srctree)/arch/mips/include/asm/mach-rm
+ifdef CONFIG_CPU_LITTLE_ENDIAN
+load-$(CONFIG_SNI_RM) += 0xffffffff80600000
+else
+load-$(CONFIG_SNI_RM) += 0xffffffff80030000
+endif
+all-$(CONFIG_SNI_RM) := $(COMPRESSION_FNAME).ecoff
diff --git a/arch/mips/sni/a20r.c b/arch/mips/sni/a20r.c
new file mode 100644
index 000000000..eeeec18c4
--- /dev/null
+++ b/arch/mips/sni/a20r.c
@@ -0,0 +1,256 @@
+/*
+ * A20R specific code
+ *
+ * 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 Thomas Bogendoerfer (tsbogend@alpha.franken.de)
+ */
+
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/platform_device.h>
+#include <linux/serial_8250.h>
+
+#include <asm/sni.h>
+#include <asm/time.h>
+
+#define PORT(_base,_irq) \
+ { \
+ .iobase = _base, \
+ .irq = _irq, \
+ .uartclk = 1843200, \
+ .iotype = UPIO_PORT, \
+ .flags = UPF_BOOT_AUTOCONF, \
+ }
+
+static struct plat_serial8250_port a20r_data[] = {
+ PORT(0x3f8, 4),
+ PORT(0x2f8, 3),
+ { },
+};
+
+static struct platform_device a20r_serial8250_device = {
+ .name = "serial8250",
+ .id = PLAT8250_DEV_PLATFORM,
+ .dev = {
+ .platform_data = a20r_data,
+ },
+};
+
+static struct resource a20r_ds1216_rsrc[] = {
+ {
+ .start = 0x1c081ffc,
+ .end = 0x1c081fff,
+ .flags = IORESOURCE_MEM
+ }
+};
+
+static struct platform_device a20r_ds1216_device = {
+ .name = "rtc-ds1216",
+ .num_resources = ARRAY_SIZE(a20r_ds1216_rsrc),
+ .resource = a20r_ds1216_rsrc
+};
+
+static struct resource snirm_82596_rsrc[] = {
+ {
+ .start = 0x18000000,
+ .end = 0x18000004,
+ .flags = IORESOURCE_MEM
+ },
+ {
+ .start = 0x18010000,
+ .end = 0x18010004,
+ .flags = IORESOURCE_MEM
+ },
+ {
+ .start = 0x1ff00000,
+ .end = 0x1ff00020,
+ .flags = IORESOURCE_MEM
+ },
+ {
+ .start = 22,
+ .end = 22,
+ .flags = IORESOURCE_IRQ
+ },
+ {
+ .flags = 0x01 /* 16bit mpu port access */
+ }
+};
+
+static struct platform_device snirm_82596_pdev = {
+ .name = "snirm_82596",
+ .num_resources = ARRAY_SIZE(snirm_82596_rsrc),
+ .resource = snirm_82596_rsrc
+};
+
+static struct resource snirm_53c710_rsrc[] = {
+ {
+ .start = 0x19000000,
+ .end = 0x190fffff,
+ .flags = IORESOURCE_MEM
+ },
+ {
+ .start = 19,
+ .end = 19,
+ .flags = IORESOURCE_IRQ
+ }
+};
+
+static struct platform_device snirm_53c710_pdev = {
+ .name = "snirm_53c710",
+ .num_resources = ARRAY_SIZE(snirm_53c710_rsrc),
+ .resource = snirm_53c710_rsrc
+};
+
+static struct resource sc26xx_rsrc[] = {
+ {
+ .start = 0x1c070000,
+ .end = 0x1c0700ff,
+ .flags = IORESOURCE_MEM
+ },
+ {
+ .start = 20,
+ .end = 20,
+ .flags = IORESOURCE_IRQ
+ }
+};
+
+#include <linux/platform_data/serial-sccnxp.h>
+
+static struct sccnxp_pdata sccnxp_data = {
+ .reg_shift = 2,
+ .mctrl_cfg[0] = MCTRL_SIG(DTR_OP, LINE_OP7) |
+ MCTRL_SIG(RTS_OP, LINE_OP3) |
+ MCTRL_SIG(DSR_IP, LINE_IP5) |
+ MCTRL_SIG(DCD_IP, LINE_IP6),
+ .mctrl_cfg[1] = MCTRL_SIG(DTR_OP, LINE_OP2) |
+ MCTRL_SIG(RTS_OP, LINE_OP1) |
+ MCTRL_SIG(DSR_IP, LINE_IP0) |
+ MCTRL_SIG(CTS_IP, LINE_IP1) |
+ MCTRL_SIG(DCD_IP, LINE_IP2) |
+ MCTRL_SIG(RNG_IP, LINE_IP3),
+};
+
+static struct platform_device sc26xx_pdev = {
+ .name = "sc2681",
+ .resource = sc26xx_rsrc,
+ .num_resources = ARRAY_SIZE(sc26xx_rsrc),
+ .dev = {
+ .platform_data = &sccnxp_data,
+ },
+};
+
+/*
+ * Trigger chipset to update CPU's CAUSE IP field
+ */
+static u32 a20r_update_cause_ip(void)
+{
+ u32 status = read_c0_status();
+
+ write_c0_status(status | 0x00010000);
+ asm volatile(
+ " .set push \n"
+ " .set noat \n"
+ " .set noreorder \n"
+ " lw $1, 0(%0) \n"
+ " sb $0, 0(%1) \n"
+ " sync \n"
+ " lb %1, 0(%1) \n"
+ " b 1f \n"
+ " ori %1, $1, 2 \n"
+ " .align 8 \n"
+ "1: \n"
+ " nop \n"
+ " sw %1, 0(%0) \n"
+ " sync \n"
+ " li %1, 0x20 \n"
+ "2: \n"
+ " nop \n"
+ " bnez %1,2b \n"
+ " addiu %1, -1 \n"
+ " sw $1, 0(%0) \n"
+ " sync \n"
+ ".set pop \n"
+ :
+ : "Jr" (PCIMT_UCONF), "Jr" (0xbc000000));
+ write_c0_status(status);
+
+ return status;
+}
+
+static inline void unmask_a20r_irq(struct irq_data *d)
+{
+ set_c0_status(0x100 << (d->irq - SNI_A20R_IRQ_BASE));
+ irq_enable_hazard();
+}
+
+static inline void mask_a20r_irq(struct irq_data *d)
+{
+ clear_c0_status(0x100 << (d->irq - SNI_A20R_IRQ_BASE));
+ irq_disable_hazard();
+}
+
+static struct irq_chip a20r_irq_type = {
+ .name = "A20R",
+ .irq_mask = mask_a20r_irq,
+ .irq_unmask = unmask_a20r_irq,
+};
+
+/*
+ * hwint 0 receive all interrupts
+ */
+static void a20r_hwint(void)
+{
+ u32 cause, status;
+ int irq;
+
+ clear_c0_status(IE_IRQ0);
+ status = a20r_update_cause_ip();
+ cause = read_c0_cause();
+
+ irq = ffs(((cause & status) >> 8) & 0xf8);
+ if (likely(irq > 0))
+ do_IRQ(SNI_A20R_IRQ_BASE + irq - 1);
+
+ a20r_update_cause_ip();
+ set_c0_status(IE_IRQ0);
+}
+
+void __init sni_a20r_irq_init(void)
+{
+ int i;
+
+ for (i = SNI_A20R_IRQ_BASE + 2 ; i < SNI_A20R_IRQ_BASE + 8; i++)
+ irq_set_chip_and_handler(i, &a20r_irq_type, handle_level_irq);
+ sni_hwint = a20r_hwint;
+ change_c0_status(ST0_IM, IE_IRQ0);
+ if (request_irq(SNI_A20R_IRQ_BASE + 3, sni_isa_irq_handler,
+ IRQF_SHARED, "ISA", sni_isa_irq_handler))
+ pr_err("Failed to register ISA interrupt\n");
+}
+
+void sni_a20r_init(void)
+{
+ /* FIXME, remove if not needed */
+}
+
+static int __init snirm_a20r_setup_devinit(void)
+{
+ switch (sni_brd_type) {
+ case SNI_BRD_TOWER_OASIC:
+ case SNI_BRD_MINITOWER:
+ platform_device_register(&snirm_82596_pdev);
+ platform_device_register(&snirm_53c710_pdev);
+ platform_device_register(&sc26xx_pdev);
+ platform_device_register(&a20r_serial8250_device);
+ platform_device_register(&a20r_ds1216_device);
+ sni_eisa_root_init();
+ break;
+ }
+ return 0;
+}
+
+device_initcall(snirm_a20r_setup_devinit);
diff --git a/arch/mips/sni/eisa.c b/arch/mips/sni/eisa.c
new file mode 100644
index 000000000..483a43d07
--- /dev/null
+++ b/arch/mips/sni/eisa.c
@@ -0,0 +1,49 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Virtual EISA root driver.
+ * Acts as a placeholder if we don't have a proper EISA bridge.
+ *
+ * (C) 2003 Marc Zyngier <maz@wild-wind.fr.eu.org>
+ * modified for SNI usage by Thomas Bogendoerfer
+ */
+
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <linux/eisa.h>
+#include <linux/init.h>
+
+/* The default EISA device parent (virtual root device).
+ * Now use a platform device, since that's the obvious choice. */
+
+static struct platform_device eisa_root_dev = {
+ .name = "eisa",
+ .id = 0,
+};
+
+static struct eisa_root_device eisa_bus_root = {
+ .dev = &eisa_root_dev.dev,
+ .bus_base_addr = 0,
+ .res = &ioport_resource,
+ .slots = EISA_MAX_SLOTS,
+ .dma_mask = 0xffffffff,
+ .force_probe = 1,
+};
+
+int __init sni_eisa_root_init(void)
+{
+ int r;
+
+ r = platform_device_register(&eisa_root_dev);
+ if (!r)
+ return r;
+
+ dev_set_drvdata(&eisa_root_dev.dev, &eisa_bus_root);
+
+ if (eisa_root_register(&eisa_bus_root)) {
+ /* A real bridge may have been registered before
+ * us. So quietly unregister. */
+ platform_device_unregister(&eisa_root_dev);
+ return -1;
+ }
+ return 0;
+}
diff --git a/arch/mips/sni/irq.c b/arch/mips/sni/irq.c
new file mode 100644
index 000000000..dec89afc9
--- /dev/null
+++ b/arch/mips/sni/irq.c
@@ -0,0 +1,76 @@
+/*
+ * 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) 1992 Linus Torvalds
+ * Copyright (C) 1994 - 2000 Ralf Baechle
+ * Copyright (C) 2006 Thomas Bogendoerfer
+ */
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/kernel.h>
+
+#include <asm/i8259.h>
+#include <asm/io.h>
+#include <asm/sni.h>
+#include <asm/irq.h>
+#include <asm/irq_cpu.h>
+
+void (*sni_hwint)(void);
+
+asmlinkage void plat_irq_dispatch(void)
+{
+ sni_hwint();
+}
+
+/* ISA irq handler */
+irqreturn_t sni_isa_irq_handler(int dummy, void *p)
+{
+ int irq;
+
+ irq = i8259_irq();
+ if (unlikely(irq < 0))
+ return IRQ_NONE;
+
+ generic_handle_irq(irq);
+ return IRQ_HANDLED;
+}
+
+/*
+ * On systems with i8259-style interrupt controllers we assume for
+ * driver compatibility reasons interrupts 0 - 15 to be the i8295
+ * interrupts even if the hardware uses a different interrupt numbering.
+ */
+void __init arch_init_irq(void)
+{
+ init_i8259_irqs(); /* Integrated i8259 */
+ switch (sni_brd_type) {
+ case SNI_BRD_10:
+ case SNI_BRD_10NEW:
+ case SNI_BRD_TOWER_OASIC:
+ case SNI_BRD_MINITOWER:
+ sni_a20r_irq_init();
+ break;
+
+ case SNI_BRD_PCI_TOWER:
+ sni_pcit_irq_init();
+ break;
+
+ case SNI_BRD_PCI_TOWER_CPLUS:
+ sni_pcit_cplus_irq_init();
+ break;
+
+ case SNI_BRD_RM200:
+ sni_rm200_irq_init();
+ break;
+
+ case SNI_BRD_PCI_MTOWER:
+ case SNI_BRD_PCI_DESKTOP:
+ case SNI_BRD_PCI_MTOWER_CPLUS:
+ sni_pcimt_irq_init();
+ break;
+ }
+}
diff --git a/arch/mips/sni/pcimt.c b/arch/mips/sni/pcimt.c
new file mode 100644
index 000000000..12336c2a6
--- /dev/null
+++ b/arch/mips/sni/pcimt.c
@@ -0,0 +1,332 @@
+/*
+ * PCIMT specific code
+ *
+ * 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) 1996, 97, 98, 2000, 03, 04, 06 Ralf Baechle (ralf@linux-mips.org)
+ * Copyright (C) 2006,2007 Thomas Bogendoerfer (tsbogend@alpha.franken.de)
+ */
+
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/pci.h>
+#include <linux/serial_8250.h>
+
+#include <asm/sni.h>
+#include <asm/time.h>
+#include <asm/i8259.h>
+#include <asm/irq_cpu.h>
+
+#define cacheconf (*(volatile unsigned int *)PCIMT_CACHECONF)
+#define invspace (*(volatile unsigned int *)PCIMT_INVSPACE)
+
+static void __init sni_pcimt_sc_init(void)
+{
+ unsigned int scsiz, sc_size;
+
+ scsiz = cacheconf & 7;
+ if (scsiz == 0) {
+ printk("Second level cache is deactivated.\n");
+ return;
+ }
+ if (scsiz >= 6) {
+ printk("Invalid second level cache size configured, "
+ "deactivating second level cache.\n");
+ cacheconf = 0;
+ return;
+ }
+
+ sc_size = 128 << scsiz;
+ printk("%dkb second level cache detected, deactivating.\n", sc_size);
+ cacheconf = 0;
+}
+
+
+/*
+ * A bit more gossip about the iron we're running on ...
+ */
+static inline void sni_pcimt_detect(void)
+{
+ char boardtype[80];
+ unsigned char csmsr;
+ char *p = boardtype;
+ unsigned int asic;
+
+ csmsr = *(volatile unsigned char *)PCIMT_CSMSR;
+
+ p += sprintf(p, "%s PCI", (csmsr & 0x80) ? "RM200" : "RM300");
+ if ((csmsr & 0x80) == 0)
+ p += sprintf(p, ", board revision %s",
+ (csmsr & 0x20) ? "D" : "C");
+ asic = csmsr & 0x80;
+ asic = (csmsr & 0x08) ? asic : !asic;
+ p += sprintf(p, ", ASIC PCI Rev %s", asic ? "1.0" : "1.1");
+ printk("%s.\n", boardtype);
+}
+
+#define PORT(_base,_irq) \
+ { \
+ .iobase = _base, \
+ .irq = _irq, \
+ .uartclk = 1843200, \
+ .iotype = UPIO_PORT, \
+ .flags = UPF_BOOT_AUTOCONF, \
+ }
+
+static struct plat_serial8250_port pcimt_data[] = {
+ PORT(0x3f8, 4),
+ PORT(0x2f8, 3),
+ { },
+};
+
+static struct platform_device pcimt_serial8250_device = {
+ .name = "serial8250",
+ .id = PLAT8250_DEV_PLATFORM,
+ .dev = {
+ .platform_data = pcimt_data,
+ },
+};
+
+static struct resource pcimt_cmos_rsrc[] = {
+ {
+ .start = 0x70,
+ .end = 0x71,
+ .flags = IORESOURCE_IO
+ },
+ {
+ .start = 8,
+ .end = 8,
+ .flags = IORESOURCE_IRQ
+ }
+};
+
+static struct platform_device pcimt_cmos_device = {
+ .name = "rtc_cmos",
+ .num_resources = ARRAY_SIZE(pcimt_cmos_rsrc),
+ .resource = pcimt_cmos_rsrc
+};
+
+
+static struct resource sni_io_resource = {
+ .start = 0x00000000UL,
+ .end = 0x03bfffffUL,
+ .name = "PCIMT IO MEM",
+ .flags = IORESOURCE_IO,
+};
+
+static struct resource pcimt_io_resources[] = {
+ {
+ .start = 0x00,
+ .end = 0x1f,
+ .name = "dma1",
+ .flags = IORESOURCE_BUSY
+ }, {
+ .start = 0x40,
+ .end = 0x5f,
+ .name = "timer",
+ .flags = IORESOURCE_BUSY
+ }, {
+ .start = 0x60,
+ .end = 0x6f,
+ .name = "keyboard",
+ .flags = IORESOURCE_BUSY
+ }, {
+ .start = 0x80,
+ .end = 0x8f,
+ .name = "dma page reg",
+ .flags = IORESOURCE_BUSY
+ }, {
+ .start = 0xc0,
+ .end = 0xdf,
+ .name = "dma2",
+ .flags = IORESOURCE_BUSY
+ }, {
+ .start = 0xcfc,
+ .end = 0xcff,
+ .name = "PCI config data",
+ .flags = IORESOURCE_BUSY
+ }
+};
+
+static struct resource pcimt_mem_resources[] = {
+ {
+ /*
+ * this region should only be 4 bytes long,
+ * but it's 16MB on all RM300C I've checked
+ */
+ .start = 0x1a000000,
+ .end = 0x1affffff,
+ .name = "PCI INT ACK",
+ .flags = IORESOURCE_BUSY
+ }
+};
+
+static struct resource sni_mem_resource = {
+ .start = 0x18000000UL,
+ .end = 0x1fbfffffUL,
+ .name = "PCIMT PCI MEM",
+ .flags = IORESOURCE_MEM
+};
+
+static void __init sni_pcimt_resource_init(void)
+{
+ int i;
+
+ /* request I/O space for devices used on all i[345]86 PCs */
+ for (i = 0; i < ARRAY_SIZE(pcimt_io_resources); i++)
+ request_resource(&sni_io_resource, pcimt_io_resources + i);
+ /* request MEM space for devices used on all i[345]86 PCs */
+ for (i = 0; i < ARRAY_SIZE(pcimt_mem_resources); i++)
+ request_resource(&sni_mem_resource, pcimt_mem_resources + i);
+}
+
+extern struct pci_ops sni_pcimt_ops;
+
+#ifdef CONFIG_PCI
+static struct pci_controller sni_controller = {
+ .pci_ops = &sni_pcimt_ops,
+ .mem_resource = &sni_mem_resource,
+ .mem_offset = 0x00000000UL,
+ .io_resource = &sni_io_resource,
+ .io_offset = 0x00000000UL,
+ .io_map_base = SNI_PORT_BASE
+};
+#endif
+
+static void enable_pcimt_irq(struct irq_data *d)
+{
+ unsigned int mask = 1 << (d->irq - PCIMT_IRQ_INT2);
+
+ *(volatile u8 *) PCIMT_IRQSEL |= mask;
+}
+
+void disable_pcimt_irq(struct irq_data *d)
+{
+ unsigned int mask = ~(1 << (d->irq - PCIMT_IRQ_INT2));
+
+ *(volatile u8 *) PCIMT_IRQSEL &= mask;
+}
+
+static struct irq_chip pcimt_irq_type = {
+ .name = "PCIMT",
+ .irq_mask = disable_pcimt_irq,
+ .irq_unmask = enable_pcimt_irq,
+};
+
+/*
+ * hwint0 should deal with MP agent, ASIC PCI, EISA NMI and debug
+ * button interrupts. Later ...
+ */
+static void pcimt_hwint0(void)
+{
+ panic("Received int0 but no handler yet ...");
+}
+
+/*
+ * hwint 1 deals with EISA and SCSI interrupts,
+ *
+ * The EISA_INT bit in CSITPEND is high active, all others are low active.
+ */
+static void pcimt_hwint1(void)
+{
+ u8 pend = *(volatile char *)PCIMT_CSITPEND;
+ unsigned long flags;
+
+ if (pend & IT_EISA) {
+ int irq;
+ /*
+ * Note: ASIC PCI's builtin interrupt acknowledge feature is
+ * broken. Using it may result in loss of some or all i8259
+ * interrupts, so don't use PCIMT_INT_ACKNOWLEDGE ...
+ */
+ irq = i8259_irq();
+ if (unlikely(irq < 0))
+ return;
+
+ do_IRQ(irq);
+ }
+
+ if (!(pend & IT_SCSI)) {
+ flags = read_c0_status();
+ clear_c0_status(ST0_IM);
+ do_IRQ(PCIMT_IRQ_SCSI);
+ write_c0_status(flags);
+ }
+}
+
+/*
+ * hwint 3 should deal with the PCI A - D interrupts,
+ */
+static void pcimt_hwint3(void)
+{
+ u8 pend = *(volatile char *)PCIMT_CSITPEND;
+ int irq;
+
+ pend &= (IT_INTA | IT_INTB | IT_INTC | IT_INTD);
+ pend ^= (IT_INTA | IT_INTB | IT_INTC | IT_INTD);
+ clear_c0_status(IE_IRQ3);
+ irq = PCIMT_IRQ_INT2 + ffs(pend) - 1;
+ do_IRQ(irq);
+ set_c0_status(IE_IRQ3);
+}
+
+static void sni_pcimt_hwint(void)
+{
+ u32 pending = read_c0_cause() & read_c0_status();
+
+ if (pending & C_IRQ5)
+ do_IRQ(MIPS_CPU_IRQ_BASE + 7);
+ else if (pending & C_IRQ4)
+ do_IRQ(MIPS_CPU_IRQ_BASE + 6);
+ else if (pending & C_IRQ3)
+ pcimt_hwint3();
+ else if (pending & C_IRQ1)
+ pcimt_hwint1();
+ else if (pending & C_IRQ0) {
+ pcimt_hwint0();
+ }
+}
+
+void __init sni_pcimt_irq_init(void)
+{
+ int i;
+
+ *(volatile u8 *) PCIMT_IRQSEL = IT_ETH | IT_EISA;
+ mips_cpu_irq_init();
+ /* Actually we've got more interrupts to handle ... */
+ for (i = PCIMT_IRQ_INT2; i <= PCIMT_IRQ_SCSI; i++)
+ irq_set_chip_and_handler(i, &pcimt_irq_type, handle_level_irq);
+ sni_hwint = sni_pcimt_hwint;
+ change_c0_status(ST0_IM, IE_IRQ1|IE_IRQ3);
+}
+
+void __init sni_pcimt_init(void)
+{
+ sni_pcimt_detect();
+ sni_pcimt_sc_init();
+ ioport_resource.end = sni_io_resource.end;
+#ifdef CONFIG_PCI
+ PCIBIOS_MIN_IO = 0x9000;
+ register_pci_controller(&sni_controller);
+#endif
+ sni_pcimt_resource_init();
+}
+
+static int __init snirm_pcimt_setup_devinit(void)
+{
+ switch (sni_brd_type) {
+ case SNI_BRD_PCI_MTOWER:
+ case SNI_BRD_PCI_DESKTOP:
+ case SNI_BRD_PCI_MTOWER_CPLUS:
+ platform_device_register(&pcimt_serial8250_device);
+ platform_device_register(&pcimt_cmos_device);
+ break;
+ }
+
+ return 0;
+}
+
+device_initcall(snirm_pcimt_setup_devinit);
diff --git a/arch/mips/sni/pcit.c b/arch/mips/sni/pcit.c
new file mode 100644
index 000000000..b331fe22c
--- /dev/null
+++ b/arch/mips/sni/pcit.c
@@ -0,0 +1,295 @@
+/*
+ * PCI Tower specific code
+ *
+ * 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 Thomas Bogendoerfer (tsbogend@alpha.franken.de)
+ */
+
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/pci.h>
+#include <linux/serial_8250.h>
+
+#include <asm/sni.h>
+#include <asm/time.h>
+#include <asm/irq_cpu.h>
+
+
+#define PORT(_base,_irq) \
+ { \
+ .iobase = _base, \
+ .irq = _irq, \
+ .uartclk = 1843200, \
+ .iotype = UPIO_PORT, \
+ .flags = UPF_BOOT_AUTOCONF, \
+ }
+
+static struct plat_serial8250_port pcit_data[] = {
+ PORT(0x3f8, 0),
+ PORT(0x2f8, 3),
+ { },
+};
+
+static struct platform_device pcit_serial8250_device = {
+ .name = "serial8250",
+ .id = PLAT8250_DEV_PLATFORM,
+ .dev = {
+ .platform_data = pcit_data,
+ },
+};
+
+static struct plat_serial8250_port pcit_cplus_data[] = {
+ PORT(0x3f8, 0),
+ PORT(0x2f8, 3),
+ PORT(0x3e8, 4),
+ PORT(0x2e8, 3),
+ { },
+};
+
+static struct platform_device pcit_cplus_serial8250_device = {
+ .name = "serial8250",
+ .id = PLAT8250_DEV_PLATFORM,
+ .dev = {
+ .platform_data = pcit_cplus_data,
+ },
+};
+
+static struct resource pcit_cmos_rsrc[] = {
+ {
+ .start = 0x70,
+ .end = 0x71,
+ .flags = IORESOURCE_IO
+ },
+ {
+ .start = 8,
+ .end = 8,
+ .flags = IORESOURCE_IRQ
+ }
+};
+
+static struct platform_device pcit_cmos_device = {
+ .name = "rtc_cmos",
+ .num_resources = ARRAY_SIZE(pcit_cmos_rsrc),
+ .resource = pcit_cmos_rsrc
+};
+
+static struct platform_device pcit_pcspeaker_pdev = {
+ .name = "pcspkr",
+ .id = -1,
+};
+
+static struct resource sni_io_resource = {
+ .start = 0x00000000UL,
+ .end = 0x03bfffffUL,
+ .name = "PCIT IO",
+ .flags = IORESOURCE_IO,
+};
+
+static struct resource pcit_io_resources[] = {
+ {
+ .start = 0x00,
+ .end = 0x1f,
+ .name = "dma1",
+ .flags = IORESOURCE_BUSY
+ }, {
+ .start = 0x40,
+ .end = 0x5f,
+ .name = "timer",
+ .flags = IORESOURCE_BUSY
+ }, {
+ .start = 0x60,
+ .end = 0x6f,
+ .name = "keyboard",
+ .flags = IORESOURCE_BUSY
+ }, {
+ .start = 0x80,
+ .end = 0x8f,
+ .name = "dma page reg",
+ .flags = IORESOURCE_BUSY
+ }, {
+ .start = 0xc0,
+ .end = 0xdf,
+ .name = "dma2",
+ .flags = IORESOURCE_BUSY
+ }, {
+ .start = 0xcf8,
+ .end = 0xcfb,
+ .name = "PCI config addr",
+ .flags = IORESOURCE_BUSY
+ }, {
+ .start = 0xcfc,
+ .end = 0xcff,
+ .name = "PCI config data",
+ .flags = IORESOURCE_BUSY
+ }
+};
+
+static void __init sni_pcit_resource_init(void)
+{
+ int i;
+
+ /* request I/O space for devices used on all i[345]86 PCs */
+ for (i = 0; i < ARRAY_SIZE(pcit_io_resources); i++)
+ request_resource(&sni_io_resource, pcit_io_resources + i);
+}
+
+
+extern struct pci_ops sni_pcit_ops;
+
+#ifdef CONFIG_PCI
+static struct resource sni_mem_resource = {
+ .start = 0x18000000UL,
+ .end = 0x1fbfffffUL,
+ .name = "PCIT PCI MEM",
+ .flags = IORESOURCE_MEM
+};
+
+static struct pci_controller sni_pcit_controller = {
+ .pci_ops = &sni_pcit_ops,
+ .mem_resource = &sni_mem_resource,
+ .mem_offset = 0x00000000UL,
+ .io_resource = &sni_io_resource,
+ .io_offset = 0x00000000UL,
+ .io_map_base = SNI_PORT_BASE
+};
+#endif /* CONFIG_PCI */
+
+static void enable_pcit_irq(struct irq_data *d)
+{
+ u32 mask = 1 << (d->irq - SNI_PCIT_INT_START + 24);
+
+ *(volatile u32 *)SNI_PCIT_INT_REG |= mask;
+}
+
+void disable_pcit_irq(struct irq_data *d)
+{
+ u32 mask = 1 << (d->irq - SNI_PCIT_INT_START + 24);
+
+ *(volatile u32 *)SNI_PCIT_INT_REG &= ~mask;
+}
+
+static struct irq_chip pcit_irq_type = {
+ .name = "PCIT",
+ .irq_mask = disable_pcit_irq,
+ .irq_unmask = enable_pcit_irq,
+};
+
+static void pcit_hwint1(void)
+{
+ u32 pending = *(volatile u32 *)SNI_PCIT_INT_REG;
+ int irq;
+
+ clear_c0_status(IE_IRQ1);
+ irq = ffs((pending >> 16) & 0x7f);
+
+ if (likely(irq > 0))
+ do_IRQ(irq + SNI_PCIT_INT_START - 1);
+ set_c0_status(IE_IRQ1);
+}
+
+static void pcit_hwint0(void)
+{
+ u32 pending = *(volatile u32 *)SNI_PCIT_INT_REG;
+ int irq;
+
+ clear_c0_status(IE_IRQ0);
+ irq = ffs((pending >> 16) & 0x3f);
+
+ if (likely(irq > 0))
+ do_IRQ(irq + SNI_PCIT_INT_START - 1);
+ set_c0_status(IE_IRQ0);
+}
+
+static void sni_pcit_hwint(void)
+{
+ u32 pending = read_c0_cause() & read_c0_status();
+
+ if (pending & C_IRQ1)
+ pcit_hwint1();
+ else if (pending & C_IRQ2)
+ do_IRQ(MIPS_CPU_IRQ_BASE + 4);
+ else if (pending & C_IRQ3)
+ do_IRQ(MIPS_CPU_IRQ_BASE + 5);
+ else if (pending & C_IRQ5)
+ do_IRQ(MIPS_CPU_IRQ_BASE + 7);
+}
+
+static void sni_pcit_hwint_cplus(void)
+{
+ u32 pending = read_c0_cause() & read_c0_status();
+
+ if (pending & C_IRQ0)
+ pcit_hwint0();
+ else if (pending & C_IRQ1)
+ do_IRQ(MIPS_CPU_IRQ_BASE + 3);
+ else if (pending & C_IRQ2)
+ do_IRQ(MIPS_CPU_IRQ_BASE + 4);
+ else if (pending & C_IRQ3)
+ do_IRQ(MIPS_CPU_IRQ_BASE + 5);
+ else if (pending & C_IRQ5)
+ do_IRQ(MIPS_CPU_IRQ_BASE + 7);
+}
+
+void __init sni_pcit_irq_init(void)
+{
+ int i;
+
+ mips_cpu_irq_init();
+ for (i = SNI_PCIT_INT_START; i <= SNI_PCIT_INT_END; i++)
+ irq_set_chip_and_handler(i, &pcit_irq_type, handle_level_irq);
+ *(volatile u32 *)SNI_PCIT_INT_REG = 0;
+ sni_hwint = sni_pcit_hwint;
+ change_c0_status(ST0_IM, IE_IRQ1);
+ if (request_irq(SNI_PCIT_INT_START + 6, sni_isa_irq_handler, 0, "ISA",
+ NULL))
+ pr_err("Failed to register ISA interrupt\n");
+}
+
+void __init sni_pcit_cplus_irq_init(void)
+{
+ int i;
+
+ mips_cpu_irq_init();
+ for (i = SNI_PCIT_INT_START; i <= SNI_PCIT_INT_END; i++)
+ irq_set_chip_and_handler(i, &pcit_irq_type, handle_level_irq);
+ *(volatile u32 *)SNI_PCIT_INT_REG = 0x40000000;
+ sni_hwint = sni_pcit_hwint_cplus;
+ change_c0_status(ST0_IM, IE_IRQ0);
+ if (request_irq(MIPS_CPU_IRQ_BASE + 3, sni_isa_irq_handler, 0, "ISA",
+ NULL))
+ pr_err("Failed to register ISA interrupt\n");
+}
+
+void __init sni_pcit_init(void)
+{
+ ioport_resource.end = sni_io_resource.end;
+#ifdef CONFIG_PCI
+ PCIBIOS_MIN_IO = 0x9000;
+ register_pci_controller(&sni_pcit_controller);
+#endif
+ sni_pcit_resource_init();
+}
+
+static int __init snirm_pcit_setup_devinit(void)
+{
+ switch (sni_brd_type) {
+ case SNI_BRD_PCI_TOWER:
+ platform_device_register(&pcit_serial8250_device);
+ platform_device_register(&pcit_cmos_device);
+ platform_device_register(&pcit_pcspeaker_pdev);
+ break;
+
+ case SNI_BRD_PCI_TOWER_CPLUS:
+ platform_device_register(&pcit_cplus_serial8250_device);
+ platform_device_register(&pcit_cmos_device);
+ platform_device_register(&pcit_pcspeaker_pdev);
+ break;
+ }
+ return 0;
+}
+
+device_initcall(snirm_pcit_setup_devinit);
diff --git a/arch/mips/sni/reset.c b/arch/mips/sni/reset.c
new file mode 100644
index 000000000..66f963d8d
--- /dev/null
+++ b/arch/mips/sni/reset.c
@@ -0,0 +1,48 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * linux/arch/mips/sni/process.c
+ *
+ * Reset a SNI machine.
+ */
+#include <linux/delay.h>
+
+#include <asm/io.h>
+#include <asm/reboot.h>
+#include <asm/sni.h>
+
+/*
+ * This routine reboots the machine by asking the keyboard
+ * controller to pulse the reset-line low. We try that for a while,
+ * and if it doesn't work, we do some other stupid things.
+ */
+static inline void kb_wait(void)
+{
+ int i;
+
+ for (i = 0; i < 0x10000; i++)
+ if ((inb_p(0x64) & 0x02) == 0)
+ break;
+}
+
+/* XXX This ends up at the ARC firmware prompt ... */
+void sni_machine_restart(char *command)
+{
+ int i;
+
+ /* This does a normal via the keyboard controller like a PC.
+ We can do that easier ... */
+ local_irq_disable();
+ for (;;) {
+ for (i = 0; i < 100; i++) {
+ kb_wait();
+ udelay(50);
+ outb_p(0xfe, 0x64); /* pulse reset low */
+ udelay(50);
+ }
+ }
+}
+
+void sni_machine_power_off(void)
+{
+ *(volatile unsigned char *)PCIMT_CSWCSM = 0xfd;
+}
diff --git a/arch/mips/sni/rm200.c b/arch/mips/sni/rm200.c
new file mode 100644
index 000000000..d84744ca8
--- /dev/null
+++ b/arch/mips/sni/rm200.c
@@ -0,0 +1,485 @@
+/*
+ * RM200 specific code
+ *
+ * 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,2007 Thomas Bogendoerfer (tsbogend@alpha.franken.de)
+ *
+ * i8259 parts ripped out of arch/mips/kernel/i8259.c
+ */
+
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/platform_device.h>
+#include <linux/serial_8250.h>
+#include <linux/io.h>
+
+#include <asm/sni.h>
+#include <asm/time.h>
+#include <asm/irq_cpu.h>
+
+#define RM200_I8259A_IRQ_BASE 32
+
+#define MEMPORT(_base,_irq) \
+ { \
+ .mapbase = _base, \
+ .irq = _irq, \
+ .uartclk = 1843200, \
+ .iotype = UPIO_MEM, \
+ .flags = UPF_BOOT_AUTOCONF|UPF_IOREMAP, \
+ }
+
+static struct plat_serial8250_port rm200_data[] = {
+ MEMPORT(0x160003f8, RM200_I8259A_IRQ_BASE + 4),
+ MEMPORT(0x160002f8, RM200_I8259A_IRQ_BASE + 3),
+ { },
+};
+
+static struct platform_device rm200_serial8250_device = {
+ .name = "serial8250",
+ .id = PLAT8250_DEV_PLATFORM,
+ .dev = {
+ .platform_data = rm200_data,
+ },
+};
+
+static struct resource rm200_ds1216_rsrc[] = {
+ {
+ .start = 0x1cd41ffc,
+ .end = 0x1cd41fff,
+ .flags = IORESOURCE_MEM
+ }
+};
+
+static struct platform_device rm200_ds1216_device = {
+ .name = "rtc-ds1216",
+ .num_resources = ARRAY_SIZE(rm200_ds1216_rsrc),
+ .resource = rm200_ds1216_rsrc
+};
+
+static struct resource snirm_82596_rm200_rsrc[] = {
+ {
+ .start = 0x18000000,
+ .end = 0x180fffff,
+ .flags = IORESOURCE_MEM
+ },
+ {
+ .start = 0x1b000000,
+ .end = 0x1b000004,
+ .flags = IORESOURCE_MEM
+ },
+ {
+ .start = 0x1ff00000,
+ .end = 0x1ff00020,
+ .flags = IORESOURCE_MEM
+ },
+ {
+ .start = 27,
+ .end = 27,
+ .flags = IORESOURCE_IRQ
+ },
+ {
+ .flags = 0x00
+ }
+};
+
+static struct platform_device snirm_82596_rm200_pdev = {
+ .name = "snirm_82596",
+ .num_resources = ARRAY_SIZE(snirm_82596_rm200_rsrc),
+ .resource = snirm_82596_rm200_rsrc
+};
+
+static struct resource snirm_53c710_rm200_rsrc[] = {
+ {
+ .start = 0x19000000,
+ .end = 0x190fffff,
+ .flags = IORESOURCE_MEM
+ },
+ {
+ .start = 26,
+ .end = 26,
+ .flags = IORESOURCE_IRQ
+ }
+};
+
+static struct platform_device snirm_53c710_rm200_pdev = {
+ .name = "snirm_53c710",
+ .num_resources = ARRAY_SIZE(snirm_53c710_rm200_rsrc),
+ .resource = snirm_53c710_rm200_rsrc
+};
+
+static int __init snirm_setup_devinit(void)
+{
+ if (sni_brd_type == SNI_BRD_RM200) {
+ platform_device_register(&rm200_serial8250_device);
+ platform_device_register(&rm200_ds1216_device);
+ platform_device_register(&snirm_82596_rm200_pdev);
+ platform_device_register(&snirm_53c710_rm200_pdev);
+ sni_eisa_root_init();
+ }
+ return 0;
+}
+
+device_initcall(snirm_setup_devinit);
+
+/*
+ * RM200 has an ISA and an EISA bus. The iSA bus is only used
+ * for onboard devices and also has twi i8259 PICs. Since these
+ * PICs are no accessible via inb/outb the following code uses
+ * readb/writeb to access them
+ */
+
+static DEFINE_RAW_SPINLOCK(sni_rm200_i8259A_lock);
+#define PIC_CMD 0x00
+#define PIC_IMR 0x01
+#define PIC_ISR PIC_CMD
+#define PIC_POLL PIC_ISR
+#define PIC_OCW3 PIC_ISR
+
+/* i8259A PIC related value */
+#define PIC_CASCADE_IR 2
+#define MASTER_ICW4_DEFAULT 0x01
+#define SLAVE_ICW4_DEFAULT 0x01
+
+/*
+ * This contains the irq mask for both 8259A irq controllers,
+ */
+static unsigned int rm200_cached_irq_mask = 0xffff;
+static __iomem u8 *rm200_pic_master;
+static __iomem u8 *rm200_pic_slave;
+
+#define cached_master_mask (rm200_cached_irq_mask)
+#define cached_slave_mask (rm200_cached_irq_mask >> 8)
+
+static void sni_rm200_disable_8259A_irq(struct irq_data *d)
+{
+ unsigned int mask, irq = d->irq - RM200_I8259A_IRQ_BASE;
+ unsigned long flags;
+
+ mask = 1 << irq;
+ raw_spin_lock_irqsave(&sni_rm200_i8259A_lock, flags);
+ rm200_cached_irq_mask |= mask;
+ if (irq & 8)
+ writeb(cached_slave_mask, rm200_pic_slave + PIC_IMR);
+ else
+ writeb(cached_master_mask, rm200_pic_master + PIC_IMR);
+ raw_spin_unlock_irqrestore(&sni_rm200_i8259A_lock, flags);
+}
+
+static void sni_rm200_enable_8259A_irq(struct irq_data *d)
+{
+ unsigned int mask, irq = d->irq - RM200_I8259A_IRQ_BASE;
+ unsigned long flags;
+
+ mask = ~(1 << irq);
+ raw_spin_lock_irqsave(&sni_rm200_i8259A_lock, flags);
+ rm200_cached_irq_mask &= mask;
+ if (irq & 8)
+ writeb(cached_slave_mask, rm200_pic_slave + PIC_IMR);
+ else
+ writeb(cached_master_mask, rm200_pic_master + PIC_IMR);
+ raw_spin_unlock_irqrestore(&sni_rm200_i8259A_lock, flags);
+}
+
+static inline int sni_rm200_i8259A_irq_real(unsigned int irq)
+{
+ int value;
+ int irqmask = 1 << irq;
+
+ if (irq < 8) {
+ writeb(0x0B, rm200_pic_master + PIC_CMD);
+ value = readb(rm200_pic_master + PIC_CMD) & irqmask;
+ writeb(0x0A, rm200_pic_master + PIC_CMD);
+ return value;
+ }
+ writeb(0x0B, rm200_pic_slave + PIC_CMD); /* ISR register */
+ value = readb(rm200_pic_slave + PIC_CMD) & (irqmask >> 8);
+ writeb(0x0A, rm200_pic_slave + PIC_CMD);
+ return value;
+}
+
+/*
+ * Careful! The 8259A is a fragile beast, it pretty
+ * much _has_ to be done exactly like this (mask it
+ * first, _then_ send the EOI, and the order of EOI
+ * to the two 8259s is important!
+ */
+void sni_rm200_mask_and_ack_8259A(struct irq_data *d)
+{
+ unsigned int irqmask, irq = d->irq - RM200_I8259A_IRQ_BASE;
+ unsigned long flags;
+
+ irqmask = 1 << irq;
+ raw_spin_lock_irqsave(&sni_rm200_i8259A_lock, flags);
+ /*
+ * Lightweight spurious IRQ detection. We do not want
+ * to overdo spurious IRQ handling - it's usually a sign
+ * of hardware problems, so we only do the checks we can
+ * do without slowing down good hardware unnecessarily.
+ *
+ * Note that IRQ7 and IRQ15 (the two spurious IRQs
+ * usually resulting from the 8259A-1|2 PICs) occur
+ * even if the IRQ is masked in the 8259A. Thus we
+ * can check spurious 8259A IRQs without doing the
+ * quite slow i8259A_irq_real() call for every IRQ.
+ * This does not cover 100% of spurious interrupts,
+ * but should be enough to warn the user that there
+ * is something bad going on ...
+ */
+ if (rm200_cached_irq_mask & irqmask)
+ goto spurious_8259A_irq;
+ rm200_cached_irq_mask |= irqmask;
+
+handle_real_irq:
+ if (irq & 8) {
+ readb(rm200_pic_slave + PIC_IMR);
+ writeb(cached_slave_mask, rm200_pic_slave + PIC_IMR);
+ writeb(0x60+(irq & 7), rm200_pic_slave + PIC_CMD);
+ writeb(0x60+PIC_CASCADE_IR, rm200_pic_master + PIC_CMD);
+ } else {
+ readb(rm200_pic_master + PIC_IMR);
+ writeb(cached_master_mask, rm200_pic_master + PIC_IMR);
+ writeb(0x60+irq, rm200_pic_master + PIC_CMD);
+ }
+ raw_spin_unlock_irqrestore(&sni_rm200_i8259A_lock, flags);
+ return;
+
+spurious_8259A_irq:
+ /*
+ * this is the slow path - should happen rarely.
+ */
+ if (sni_rm200_i8259A_irq_real(irq))
+ /*
+ * oops, the IRQ _is_ in service according to the
+ * 8259A - not spurious, go handle it.
+ */
+ goto handle_real_irq;
+
+ {
+ static int spurious_irq_mask;
+ /*
+ * At this point we can be sure the IRQ is spurious,
+ * let's ACK and report it. [once per IRQ]
+ */
+ if (!(spurious_irq_mask & irqmask)) {
+ printk(KERN_DEBUG
+ "spurious RM200 8259A interrupt: IRQ%d.\n", irq);
+ spurious_irq_mask |= irqmask;
+ }
+ atomic_inc(&irq_err_count);
+ /*
+ * Theoretically we do not have to handle this IRQ,
+ * but in Linux this does not cause problems and is
+ * simpler for us.
+ */
+ goto handle_real_irq;
+ }
+}
+
+static struct irq_chip sni_rm200_i8259A_chip = {
+ .name = "RM200-XT-PIC",
+ .irq_mask = sni_rm200_disable_8259A_irq,
+ .irq_unmask = sni_rm200_enable_8259A_irq,
+ .irq_mask_ack = sni_rm200_mask_and_ack_8259A,
+};
+
+/*
+ * Do the traditional i8259 interrupt polling thing. This is for the few
+ * cases where no better interrupt acknowledge method is available and we
+ * absolutely must touch the i8259.
+ */
+static inline int sni_rm200_i8259_irq(void)
+{
+ int irq;
+
+ raw_spin_lock(&sni_rm200_i8259A_lock);
+
+ /* Perform an interrupt acknowledge cycle on controller 1. */
+ writeb(0x0C, rm200_pic_master + PIC_CMD); /* prepare for poll */
+ irq = readb(rm200_pic_master + PIC_CMD) & 7;
+ if (irq == PIC_CASCADE_IR) {
+ /*
+ * Interrupt is cascaded so perform interrupt
+ * acknowledge on controller 2.
+ */
+ writeb(0x0C, rm200_pic_slave + PIC_CMD); /* prepare for poll */
+ irq = (readb(rm200_pic_slave + PIC_CMD) & 7) + 8;
+ }
+
+ if (unlikely(irq == 7)) {
+ /*
+ * This may be a spurious interrupt.
+ *
+ * Read the interrupt status register (ISR). If the most
+ * significant bit is not set then there is no valid
+ * interrupt.
+ */
+ writeb(0x0B, rm200_pic_master + PIC_ISR); /* ISR register */
+ if (~readb(rm200_pic_master + PIC_ISR) & 0x80)
+ irq = -1;
+ }
+
+ raw_spin_unlock(&sni_rm200_i8259A_lock);
+
+ return likely(irq >= 0) ? irq + RM200_I8259A_IRQ_BASE : irq;
+}
+
+void sni_rm200_init_8259A(void)
+{
+ unsigned long flags;
+
+ raw_spin_lock_irqsave(&sni_rm200_i8259A_lock, flags);
+
+ writeb(0xff, rm200_pic_master + PIC_IMR);
+ writeb(0xff, rm200_pic_slave + PIC_IMR);
+
+ writeb(0x11, rm200_pic_master + PIC_CMD);
+ writeb(0, rm200_pic_master + PIC_IMR);
+ writeb(1U << PIC_CASCADE_IR, rm200_pic_master + PIC_IMR);
+ writeb(MASTER_ICW4_DEFAULT, rm200_pic_master + PIC_IMR);
+ writeb(0x11, rm200_pic_slave + PIC_CMD);
+ writeb(8, rm200_pic_slave + PIC_IMR);
+ writeb(PIC_CASCADE_IR, rm200_pic_slave + PIC_IMR);
+ writeb(SLAVE_ICW4_DEFAULT, rm200_pic_slave + PIC_IMR);
+ udelay(100); /* wait for 8259A to initialize */
+
+ writeb(cached_master_mask, rm200_pic_master + PIC_IMR);
+ writeb(cached_slave_mask, rm200_pic_slave + PIC_IMR);
+
+ raw_spin_unlock_irqrestore(&sni_rm200_i8259A_lock, flags);
+}
+
+/*
+ * IRQ2 is cascade interrupt to second interrupt controller
+ */
+
+static struct resource sni_rm200_pic1_resource = {
+ .name = "onboard ISA pic1",
+ .start = 0x16000020,
+ .end = 0x16000023,
+ .flags = IORESOURCE_BUSY
+};
+
+static struct resource sni_rm200_pic2_resource = {
+ .name = "onboard ISA pic2",
+ .start = 0x160000a0,
+ .end = 0x160000a3,
+ .flags = IORESOURCE_BUSY
+};
+
+/* ISA irq handler */
+static irqreturn_t sni_rm200_i8259A_irq_handler(int dummy, void *p)
+{
+ int irq;
+
+ irq = sni_rm200_i8259_irq();
+ if (unlikely(irq < 0))
+ return IRQ_NONE;
+
+ do_IRQ(irq);
+ return IRQ_HANDLED;
+}
+
+void __init sni_rm200_i8259_irqs(void)
+{
+ int i;
+
+ rm200_pic_master = ioremap(0x16000020, 4);
+ if (!rm200_pic_master)
+ return;
+ rm200_pic_slave = ioremap(0x160000a0, 4);
+ if (!rm200_pic_slave) {
+ iounmap(rm200_pic_master);
+ return;
+ }
+
+ insert_resource(&iomem_resource, &sni_rm200_pic1_resource);
+ insert_resource(&iomem_resource, &sni_rm200_pic2_resource);
+
+ sni_rm200_init_8259A();
+
+ for (i = RM200_I8259A_IRQ_BASE; i < RM200_I8259A_IRQ_BASE + 16; i++)
+ irq_set_chip_and_handler(i, &sni_rm200_i8259A_chip,
+ handle_level_irq);
+
+ if (request_irq(RM200_I8259A_IRQ_BASE + PIC_CASCADE_IR, no_action,
+ IRQF_NO_THREAD, "cascade", NULL))
+ pr_err("Failed to register cascade interrupt\n");
+}
+
+
+#define SNI_RM200_INT_STAT_REG CKSEG1ADDR(0xbc000000)
+#define SNI_RM200_INT_ENA_REG CKSEG1ADDR(0xbc080000)
+
+#define SNI_RM200_INT_START 24
+#define SNI_RM200_INT_END 28
+
+static void enable_rm200_irq(struct irq_data *d)
+{
+ unsigned int mask = 1 << (d->irq - SNI_RM200_INT_START);
+
+ *(volatile u8 *)SNI_RM200_INT_ENA_REG &= ~mask;
+}
+
+void disable_rm200_irq(struct irq_data *d)
+{
+ unsigned int mask = 1 << (d->irq - SNI_RM200_INT_START);
+
+ *(volatile u8 *)SNI_RM200_INT_ENA_REG |= mask;
+}
+
+static struct irq_chip rm200_irq_type = {
+ .name = "RM200",
+ .irq_mask = disable_rm200_irq,
+ .irq_unmask = enable_rm200_irq,
+};
+
+static void sni_rm200_hwint(void)
+{
+ u32 pending = read_c0_cause() & read_c0_status();
+ u8 mask;
+ u8 stat;
+ int irq;
+
+ if (pending & C_IRQ5)
+ do_IRQ(MIPS_CPU_IRQ_BASE + 7);
+ else if (pending & C_IRQ0) {
+ clear_c0_status(IE_IRQ0);
+ mask = *(volatile u8 *)SNI_RM200_INT_ENA_REG ^ 0x1f;
+ stat = *(volatile u8 *)SNI_RM200_INT_STAT_REG ^ 0x14;
+ irq = ffs(stat & mask & 0x1f);
+
+ if (likely(irq > 0))
+ do_IRQ(irq + SNI_RM200_INT_START - 1);
+ set_c0_status(IE_IRQ0);
+ }
+}
+
+void __init sni_rm200_irq_init(void)
+{
+ int i;
+
+ * (volatile u8 *)SNI_RM200_INT_ENA_REG = 0x1f;
+
+ sni_rm200_i8259_irqs();
+ mips_cpu_irq_init();
+ /* Actually we've got more interrupts to handle ... */
+ for (i = SNI_RM200_INT_START; i <= SNI_RM200_INT_END; i++)
+ irq_set_chip_and_handler(i, &rm200_irq_type, handle_level_irq);
+ sni_hwint = sni_rm200_hwint;
+ change_c0_status(ST0_IM, IE_IRQ0);
+ if (request_irq(SNI_RM200_INT_START + 0, sni_rm200_i8259A_irq_handler,
+ 0, "onboard ISA", NULL))
+ pr_err("Failed to register onboard ISA interrupt\n");
+ if (request_irq(SNI_RM200_INT_START + 1, sni_isa_irq_handler, 0, "ISA",
+ NULL))
+ pr_err("Failed to register ISA interrupt\n");
+}
+
+void __init sni_rm200_init(void)
+{
+}
diff --git a/arch/mips/sni/setup.c b/arch/mips/sni/setup.c
new file mode 100644
index 000000000..fc7da1228
--- /dev/null
+++ b/arch/mips/sni/setup.c
@@ -0,0 +1,265 @@
+/*
+ * Setup pointers to hardware-dependent routines.
+ *
+ * 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) 1996, 97, 98, 2000, 03, 04, 06 Ralf Baechle (ralf@linux-mips.org)
+ * Copyright (C) 2006,2007 Thomas Bogendoerfer (tsbogend@alpha.franken.de)
+ */
+#include <linux/eisa.h>
+#include <linux/init.h>
+#include <linux/export.h>
+#include <linux/console.h>
+#include <linux/screen_info.h>
+#include <linux/string.h>
+
+#ifdef CONFIG_FW_ARC
+#include <asm/fw/arc/types.h>
+#include <asm/sgialib.h>
+#endif
+
+#ifdef CONFIG_FW_SNIPROM
+#include <asm/mipsprom.h>
+#endif
+
+#include <asm/bootinfo.h>
+#include <asm/cpu.h>
+#include <asm/io.h>
+#include <asm/reboot.h>
+#include <asm/sni.h>
+
+unsigned int sni_brd_type;
+EXPORT_SYMBOL(sni_brd_type);
+
+extern void sni_machine_restart(char *command);
+extern void sni_machine_power_off(void);
+
+static void __init sni_display_setup(void)
+{
+#if defined(CONFIG_VGA_CONSOLE) && defined(CONFIG_FW_ARC)
+ static struct screen_info si;
+ DISPLAY_STATUS *di;
+
+ di = ArcGetDisplayStatus(1);
+
+ if (di) {
+ si.orig_x = di->CursorXPosition;
+ si.orig_y = di->CursorYPosition;
+ si.orig_video_cols = di->CursorMaxXPosition;
+ si.orig_video_lines = di->CursorMaxYPosition;
+ si.orig_video_isVGA = VIDEO_TYPE_VGAC;
+ si.orig_video_points = 16;
+
+ vgacon_register_screen(&si);
+ }
+#endif
+}
+
+static void __init sni_console_setup(void)
+{
+#ifndef CONFIG_FW_ARC
+ char *ctype;
+ char *cdev;
+ char *baud;
+ int port;
+ static char options[8] __initdata;
+
+ cdev = prom_getenv("console_dev");
+ if (strncmp(cdev, "tty", 3) == 0) {
+ ctype = prom_getenv("console");
+ switch (*ctype) {
+ default:
+ case 'l':
+ port = 0;
+ baud = prom_getenv("lbaud");
+ break;
+ case 'r':
+ port = 1;
+ baud = prom_getenv("rbaud");
+ break;
+ }
+ if (baud)
+ strscpy(options, baud);
+ if (strncmp(cdev, "tty552", 6) == 0)
+ add_preferred_console("ttyS", port,
+ baud ? options : NULL);
+ else
+ add_preferred_console("ttySC", port,
+ baud ? options : NULL);
+ }
+#endif
+}
+
+#ifdef DEBUG
+static void __init sni_idprom_dump(void)
+{
+ int i;
+
+ pr_debug("SNI IDProm dump:\n");
+ for (i = 0; i < 256; i++) {
+ if (i%16 == 0)
+ pr_debug("%04x ", i);
+
+ printk("%02x ", *(unsigned char *) (SNI_IDPROM_BASE + i));
+
+ if (i % 16 == 15)
+ printk("\n");
+ }
+}
+#endif
+
+void __init plat_mem_setup(void)
+{
+ int cputype;
+
+ set_io_port_base(SNI_PORT_BASE);
+// ioport_resource.end = sni_io_resource.end;
+
+ /*
+ * Setup (E)ISA I/O memory access stuff
+ */
+#ifdef CONFIG_EISA
+ EISA_bus = 1;
+#endif
+
+ sni_brd_type = *(unsigned char *)SNI_IDPROM_BRDTYPE;
+ cputype = *(unsigned char *)SNI_IDPROM_CPUTYPE;
+ switch (sni_brd_type) {
+ case SNI_BRD_TOWER_OASIC:
+ switch (cputype) {
+ case SNI_CPU_M8030:
+ system_type = "RM400-330";
+ break;
+ case SNI_CPU_M8031:
+ system_type = "RM400-430";
+ break;
+ case SNI_CPU_M8037:
+ system_type = "RM400-530";
+ break;
+ case SNI_CPU_M8034:
+ system_type = "RM400-730";
+ break;
+ default:
+ system_type = "RM400-xxx";
+ break;
+ }
+ break;
+ case SNI_BRD_MINITOWER:
+ switch (cputype) {
+ case SNI_CPU_M8021:
+ case SNI_CPU_M8043:
+ system_type = "RM400-120";
+ break;
+ case SNI_CPU_M8040:
+ system_type = "RM400-220";
+ break;
+ case SNI_CPU_M8053:
+ system_type = "RM400-225";
+ break;
+ case SNI_CPU_M8050:
+ system_type = "RM400-420";
+ break;
+ default:
+ system_type = "RM400-xxx";
+ break;
+ }
+ break;
+ case SNI_BRD_PCI_TOWER:
+ system_type = "RM400-Cxx";
+ break;
+ case SNI_BRD_RM200:
+ system_type = "RM200-xxx";
+ break;
+ case SNI_BRD_PCI_MTOWER:
+ system_type = "RM300-Cxx";
+ break;
+ case SNI_BRD_PCI_DESKTOP:
+ switch (read_c0_prid() & PRID_IMP_MASK) {
+ case PRID_IMP_R4600:
+ case PRID_IMP_R4700:
+ system_type = "RM200-C20";
+ break;
+ case PRID_IMP_R5000:
+ system_type = "RM200-C40";
+ break;
+ default:
+ system_type = "RM200-Cxx";
+ break;
+ }
+ break;
+ case SNI_BRD_PCI_TOWER_CPLUS:
+ system_type = "RM400-Exx";
+ break;
+ case SNI_BRD_PCI_MTOWER_CPLUS:
+ system_type = "RM300-Exx";
+ break;
+ }
+ pr_debug("Found SNI brdtype %02x name %s\n", sni_brd_type, system_type);
+
+#ifdef DEBUG
+ sni_idprom_dump();
+#endif
+
+ switch (sni_brd_type) {
+ case SNI_BRD_10:
+ case SNI_BRD_10NEW:
+ case SNI_BRD_TOWER_OASIC:
+ case SNI_BRD_MINITOWER:
+ sni_a20r_init();
+ break;
+
+ case SNI_BRD_PCI_TOWER:
+ case SNI_BRD_PCI_TOWER_CPLUS:
+ sni_pcit_init();
+ break;
+
+ case SNI_BRD_RM200:
+ sni_rm200_init();
+ break;
+
+ case SNI_BRD_PCI_MTOWER:
+ case SNI_BRD_PCI_DESKTOP:
+ case SNI_BRD_PCI_MTOWER_CPLUS:
+ sni_pcimt_init();
+ break;
+ }
+
+ _machine_restart = sni_machine_restart;
+ pm_power_off = sni_machine_power_off;
+
+ sni_display_setup();
+ sni_console_setup();
+}
+
+#ifdef CONFIG_PCI
+
+#include <linux/pci.h>
+#include <video/vga.h>
+#include <video/cirrus.h>
+
+static void quirk_cirrus_ram_size(struct pci_dev *dev)
+{
+ u16 cmd;
+
+ /*
+ * firmware doesn't set the ram size correct, so we
+ * need to do it here, otherwise we get screen corruption
+ * on older Cirrus chips
+ */
+ pci_read_config_word(dev, PCI_COMMAND, &cmd);
+ if ((cmd & (PCI_COMMAND_IO|PCI_COMMAND_MEMORY))
+ == (PCI_COMMAND_IO|PCI_COMMAND_MEMORY)) {
+ vga_wseq(NULL, CL_SEQR6, 0x12); /* unlock all extension registers */
+ vga_wseq(NULL, CL_SEQRF, 0x18);
+ }
+}
+
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_CIRRUS, PCI_DEVICE_ID_CIRRUS_5434_8,
+ quirk_cirrus_ram_size);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_CIRRUS, PCI_DEVICE_ID_CIRRUS_5436,
+ quirk_cirrus_ram_size);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_CIRRUS, PCI_DEVICE_ID_CIRRUS_5446,
+ quirk_cirrus_ram_size);
+#endif
diff --git a/arch/mips/sni/time.c b/arch/mips/sni/time.c
new file mode 100644
index 000000000..ff3ba7e77
--- /dev/null
+++ b/arch/mips/sni/time.c
@@ -0,0 +1,167 @@
+// SPDX-License-Identifier: GPL-2.0
+#include <linux/types.h>
+#include <linux/i8253.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/smp.h>
+#include <linux/time.h>
+#include <linux/clockchips.h>
+
+#include <asm/sni.h>
+#include <asm/time.h>
+
+#define SNI_CLOCK_TICK_RATE 3686400
+#define SNI_COUNTER2_DIV 64
+#define SNI_COUNTER0_DIV ((SNI_CLOCK_TICK_RATE / SNI_COUNTER2_DIV) / HZ)
+
+static int a20r_set_periodic(struct clock_event_device *evt)
+{
+ *(volatile u8 *)(A20R_PT_CLOCK_BASE + 12) = 0x34;
+ wmb();
+ *(volatile u8 *)(A20R_PT_CLOCK_BASE + 0) = SNI_COUNTER0_DIV & 0xff;
+ wmb();
+ *(volatile u8 *)(A20R_PT_CLOCK_BASE + 0) = SNI_COUNTER0_DIV >> 8;
+ wmb();
+
+ *(volatile u8 *)(A20R_PT_CLOCK_BASE + 12) = 0xb4;
+ wmb();
+ *(volatile u8 *)(A20R_PT_CLOCK_BASE + 8) = SNI_COUNTER2_DIV & 0xff;
+ wmb();
+ *(volatile u8 *)(A20R_PT_CLOCK_BASE + 8) = SNI_COUNTER2_DIV >> 8;
+ wmb();
+ return 0;
+}
+
+static struct clock_event_device a20r_clockevent_device = {
+ .name = "a20r-timer",
+ .features = CLOCK_EVT_FEAT_PERIODIC,
+
+ /* .mult, .shift, .max_delta_ns and .min_delta_ns left uninitialized */
+
+ .rating = 300,
+ .irq = SNI_A20R_IRQ_TIMER,
+ .set_state_periodic = a20r_set_periodic,
+};
+
+static irqreturn_t a20r_interrupt(int irq, void *dev_id)
+{
+ struct clock_event_device *cd = dev_id;
+
+ *(volatile u8 *)A20R_PT_TIM0_ACK = 0;
+ wmb();
+
+ cd->event_handler(cd);
+
+ return IRQ_HANDLED;
+}
+
+/*
+ * a20r platform uses 2 counters to divide the input frequency.
+ * Counter 2 output is connected to Counter 0 & 1 input.
+ */
+static void __init sni_a20r_timer_setup(void)
+{
+ struct clock_event_device *cd = &a20r_clockevent_device;
+ unsigned int cpu = smp_processor_id();
+
+ cd->cpumask = cpumask_of(cpu);
+ clockevents_register_device(cd);
+ if (request_irq(SNI_A20R_IRQ_TIMER, a20r_interrupt,
+ IRQF_PERCPU | IRQF_TIMER, "a20r-timer", cd))
+ pr_err("Failed to register a20r-timer interrupt\n");
+}
+
+#define SNI_8254_TICK_RATE 1193182UL
+
+#define SNI_8254_TCSAMP_COUNTER ((SNI_8254_TICK_RATE / HZ) + 255)
+
+static __init unsigned long dosample(void)
+{
+ u32 ct0, ct1;
+ volatile u8 msb;
+
+ /* Start the counter. */
+ outb_p(0x34, 0x43);
+ outb_p(SNI_8254_TCSAMP_COUNTER & 0xff, 0x40);
+ outb(SNI_8254_TCSAMP_COUNTER >> 8, 0x40);
+
+ /* Get initial counter invariant */
+ ct0 = read_c0_count();
+
+ /* Latch and spin until top byte of counter0 is zero */
+ do {
+ outb(0x00, 0x43);
+ (void) inb(0x40);
+ msb = inb(0x40);
+ ct1 = read_c0_count();
+ } while (msb);
+
+ /* Stop the counter. */
+ outb(0x38, 0x43);
+ /*
+ * Return the difference, this is how far the r4k counter increments
+ * for every 1/HZ seconds. We round off the nearest 1 MHz of master
+ * clock (= 1000000 / HZ / 2).
+ */
+ /*return (ct1 - ct0 + (500000/HZ/2)) / (500000/HZ) * (500000/HZ);*/
+ return (ct1 - ct0) / (500000/HZ) * (500000/HZ);
+}
+
+/*
+ * Here we need to calibrate the cycle counter to at least be close.
+ */
+void __init plat_time_init(void)
+{
+ unsigned long r4k_ticks[3];
+ unsigned long r4k_tick;
+
+ /*
+ * Figure out the r4k offset, the algorithm is very simple and works in
+ * _all_ cases as long as the 8254 counter register itself works ok (as
+ * an interrupt driving timer it does not because of bug, this is why
+ * we are using the onchip r4k counter/compare register to serve this
+ * purpose, but for r4k_offset calculation it will work ok for us).
+ * There are other very complicated ways of performing this calculation
+ * but this one works just fine so I am not going to futz around. ;-)
+ */
+ printk(KERN_INFO "Calibrating system timer... ");
+ dosample(); /* Prime cache. */
+ dosample(); /* Prime cache. */
+ /* Zero is NOT an option. */
+ do {
+ r4k_ticks[0] = dosample();
+ } while (!r4k_ticks[0]);
+ do {
+ r4k_ticks[1] = dosample();
+ } while (!r4k_ticks[1]);
+
+ if (r4k_ticks[0] != r4k_ticks[1]) {
+ printk("warning: timer counts differ, retrying... ");
+ r4k_ticks[2] = dosample();
+ if (r4k_ticks[2] == r4k_ticks[0]
+ || r4k_ticks[2] == r4k_ticks[1])
+ r4k_tick = r4k_ticks[2];
+ else {
+ printk("disagreement, using average... ");
+ r4k_tick = (r4k_ticks[0] + r4k_ticks[1]
+ + r4k_ticks[2]) / 3;
+ }
+ } else
+ r4k_tick = r4k_ticks[0];
+
+ printk("%d [%d.%04d MHz CPU]\n", (int) r4k_tick,
+ (int) (r4k_tick / (500000 / HZ)),
+ (int) (r4k_tick % (500000 / HZ)));
+
+ mips_hpt_frequency = r4k_tick * HZ;
+
+ switch (sni_brd_type) {
+ case SNI_BRD_10:
+ case SNI_BRD_10NEW:
+ case SNI_BRD_TOWER_OASIC:
+ case SNI_BRD_MINITOWER:
+ sni_a20r_timer_setup();
+ break;
+ }
+ setup_pit_timer();
+}