summaryrefslogtreecommitdiffstats
path: root/arch/mips/sibyte
diff options
context:
space:
mode:
Diffstat (limited to 'arch/mips/sibyte')
-rw-r--r--arch/mips/sibyte/Kconfig136
-rw-r--r--arch/mips/sibyte/Makefile23
-rw-r--r--arch/mips/sibyte/Platform32
-rw-r--r--arch/mips/sibyte/bcm1480/Makefile4
-rw-r--r--arch/mips/sibyte/bcm1480/irq.c348
-rw-r--r--arch/mips/sibyte/bcm1480/setup.c126
-rw-r--r--arch/mips/sibyte/bcm1480/smp.c179
-rw-r--r--arch/mips/sibyte/bcm1480/time.c14
-rw-r--r--arch/mips/sibyte/common/Makefile6
-rw-r--r--arch/mips/sibyte/common/bus_watcher.c228
-rw-r--r--arch/mips/sibyte/common/cfe.c309
-rw-r--r--arch/mips/sibyte/common/cfe_console.c81
-rw-r--r--arch/mips/sibyte/common/dma.c14
-rw-r--r--arch/mips/sibyte/common/sb_tbprof.c595
-rw-r--r--arch/mips/sibyte/sb1250/Makefile4
-rw-r--r--arch/mips/sibyte/sb1250/irq.c318
-rw-r--r--arch/mips/sibyte/sb1250/setup.c234
-rw-r--r--arch/mips/sibyte/sb1250/smp.c168
-rw-r--r--arch/mips/sibyte/sb1250/time.c14
-rw-r--r--arch/mips/sibyte/swarm/Makefile5
-rw-r--r--arch/mips/sibyte/swarm/platform.c140
-rw-r--r--arch/mips/sibyte/swarm/rtc_m41t81.c228
-rw-r--r--arch/mips/sibyte/swarm/rtc_xicor1241.c206
-rw-r--r--arch/mips/sibyte/swarm/setup.c164
-rw-r--r--arch/mips/sibyte/swarm/swarm-i2c.c31
25 files changed, 3607 insertions, 0 deletions
diff --git a/arch/mips/sibyte/Kconfig b/arch/mips/sibyte/Kconfig
new file mode 100644
index 0000000000..5fb92fe841
--- /dev/null
+++ b/arch/mips/sibyte/Kconfig
@@ -0,0 +1,136 @@
+# SPDX-License-Identifier: GPL-2.0
+config SIBYTE_SB1250
+ bool
+ select CEVT_SB1250
+ select CSRC_SB1250
+ select HAVE_PCI
+ select IRQ_MIPS_CPU
+ select SIBYTE_ENABLE_LDT_IF_PCI
+ select SIBYTE_HAS_ZBUS_PROFILING
+ select SIBYTE_SB1xxx_SOC
+ select SYS_SUPPORTS_SMP
+
+config SIBYTE_BCM1125
+ bool
+ select CEVT_SB1250
+ select CSRC_SB1250
+ select HAVE_PCI
+ select IRQ_MIPS_CPU
+ select SIBYTE_BCM112X
+ select SIBYTE_HAS_ZBUS_PROFILING
+ select SIBYTE_SB1xxx_SOC
+
+config SIBYTE_BCM112X
+ bool
+ select CEVT_SB1250
+ select CSRC_SB1250
+ select IRQ_MIPS_CPU
+ select SIBYTE_SB1xxx_SOC
+ select SIBYTE_HAS_ZBUS_PROFILING
+
+config SIBYTE_BCM1x80
+ bool
+ select CEVT_BCM1480
+ select CSRC_BCM1480
+ select HAVE_PCI
+ select IRQ_MIPS_CPU
+ select SIBYTE_HAS_ZBUS_PROFILING
+ select SIBYTE_SB1xxx_SOC
+ select SYS_SUPPORTS_SMP
+
+config SIBYTE_SB1xxx_SOC
+ bool
+ select IRQ_MIPS_CPU
+ select SWAP_IO_SPACE
+ select SYS_SUPPORTS_32BIT_KERNEL
+ select SYS_SUPPORTS_64BIT_KERNEL
+ select FW_CFE
+ select SYS_HAS_EARLY_PRINTK
+
+choice
+ prompt "SiByte SOC Stepping"
+ depends on SIBYTE_SB1xxx_SOC
+
+config CPU_SB1_PASS_2_1250
+ bool "1250 An"
+ depends on SIBYTE_SB1250
+ select CPU_SB1_PASS_2
+ help
+ Also called BCM1250 Pass 2
+
+config CPU_SB1_PASS_2_2
+ bool "1250 Bn"
+ depends on SIBYTE_SB1250
+ select CPU_HAS_PREFETCH
+ help
+ Also called BCM1250 Pass 2.2
+
+config CPU_SB1_PASS_4
+ bool "1250 Cn"
+ depends on SIBYTE_SB1250
+ select CPU_HAS_PREFETCH
+ help
+ Also called BCM1250 Pass 3
+
+config CPU_SB1_PASS_2_112x
+ bool "112x Hybrid"
+ depends on SIBYTE_BCM112X
+ select CPU_SB1_PASS_2
+
+config CPU_SB1_PASS_3
+ bool "112x An"
+ depends on SIBYTE_BCM112X
+ select CPU_HAS_PREFETCH
+
+endchoice
+
+config CPU_SB1_PASS_2
+ bool
+
+config SIBYTE_HAS_LDT
+ bool
+
+config SIBYTE_ENABLE_LDT_IF_PCI
+ bool
+ select SIBYTE_HAS_LDT if PCI
+
+config SB1_CEX_ALWAYS_FATAL
+ bool "All cache exceptions considered fatal (no recovery attempted)"
+ depends on SIBYTE_SB1xxx_SOC
+
+config SB1_CERR_STALL
+ bool "Stall (rather than panic) on fatal cache error"
+ depends on SIBYTE_SB1xxx_SOC
+
+config SIBYTE_CFE_CONSOLE
+ bool "Use firmware console"
+ depends on SIBYTE_SB1xxx_SOC
+ help
+ Use the CFE API's console write routines during boot. Other console
+ options (VT console, sb1250 duart console, etc.) should not be
+ configured.
+
+config SIBYTE_BUS_WATCHER
+ bool "Support for Bus Watcher statistics"
+ depends on SIBYTE_SB1xxx_SOC && \
+ (SIBYTE_BCM112X || SIBYTE_SB1250 || SIBYTE_BCM1x80)
+ help
+ Handle and keep statistics on the bus error interrupts (COR_ECC,
+ BAD_ECC, IO_BUS).
+
+config SIBYTE_BW_TRACE
+ bool "Capture bus trace before bus error"
+ depends on SIBYTE_BUS_WATCHER
+ help
+ Run a continuous bus trace, dumping the raw data as soon as
+ a ZBbus error is detected. Cannot work if ZBbus profiling
+ is turned on, and also will interfere with JTAG-based trace
+ buffer activity. Raw buffer data is dumped to console, and
+ must be processed off-line.
+
+config SIBYTE_TBPROF
+ tristate "Support for ZBbus profiling"
+ depends on SIBYTE_HAS_ZBUS_PROFILING
+
+config SIBYTE_HAS_ZBUS_PROFILING
+ bool
diff --git a/arch/mips/sibyte/Makefile b/arch/mips/sibyte/Makefile
new file mode 100644
index 0000000000..47078353fe
--- /dev/null
+++ b/arch/mips/sibyte/Makefile
@@ -0,0 +1,23 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+# Sibyte SB1250 / BCM1480 family of SOCs
+#
+obj-$(CONFIG_SIBYTE_BCM112X) += sb1250/
+obj-$(CONFIG_SIBYTE_BCM112X) += common/
+obj-$(CONFIG_SIBYTE_SB1250) += sb1250/
+obj-$(CONFIG_SIBYTE_SB1250) += common/
+obj-$(CONFIG_SIBYTE_BCM1x80) += bcm1480/
+obj-$(CONFIG_SIBYTE_BCM1x80) += common/
+
+#
+# Sibyte BCM91125C (CRhone) board
+# Sibyte BCM91125E (Rhone) board
+# Sibyte SWARM board
+# Sibyte BCM91x80 (BigSur) board
+#
+obj-$(CONFIG_SIBYTE_CRHONE) += swarm/
+obj-$(CONFIG_SIBYTE_RHONE) += swarm/
+obj-$(CONFIG_SIBYTE_SENTOSA) += swarm/
+obj-$(CONFIG_SIBYTE_SWARM) += swarm/
+obj-$(CONFIG_SIBYTE_BIGSUR) += swarm/
+obj-$(CONFIG_SIBYTE_LITTLESUR) += swarm/
diff --git a/arch/mips/sibyte/Platform b/arch/mips/sibyte/Platform
new file mode 100644
index 0000000000..937108e41f
--- /dev/null
+++ b/arch/mips/sibyte/Platform
@@ -0,0 +1,32 @@
+#
+# These are all rather similar so we consider them a single platform
+#
+
+#
+# Sibyte SB1250 / BCM1480 family of SOCs
+#
+cflags-$(CONFIG_SIBYTE_BCM112X) += \
+ -I$(srctree)/arch/mips/include/asm/mach-sibyte \
+ -DSIBYTE_HDR_FEATURES=SIBYTE_HDR_FMASK_1250_112x_ALL
+
+cflags-$(CONFIG_SIBYTE_SB1250) += \
+ -I$(srctree)/arch/mips/include/asm/mach-sibyte \
+ -DSIBYTE_HDR_FEATURES=SIBYTE_HDR_FMASK_1250_112x_ALL
+
+cflags-$(CONFIG_SIBYTE_BCM1x80) += \
+ -I$(srctree)/arch/mips/include/asm/mach-sibyte \
+ -DSIBYTE_HDR_FEATURES=SIBYTE_HDR_FMASK_1480_ALL
+
+#
+# Sibyte BCM91125C (CRhone) board
+# Sibyte BCM91125E (Rhone) board
+# Sibyte BCM91250A (SWARM) board
+# Sibyte BCM91250C2 (LittleSur) board
+# Sibyte BCM91x80 (BigSur) board
+#
+load-$(CONFIG_SIBYTE_CRHONE) := 0xffffffff80100000
+load-$(CONFIG_SIBYTE_RHONE) := 0xffffffff80100000
+load-$(CONFIG_SIBYTE_SENTOSA) := 0xffffffff80100000
+load-$(CONFIG_SIBYTE_SWARM) := 0xffffffff80100000
+load-$(CONFIG_SIBYTE_BIGSUR) := 0xffffffff80100000
+load-$(CONFIG_SIBYTE_LITTLESUR) := 0xffffffff80100000
diff --git a/arch/mips/sibyte/bcm1480/Makefile b/arch/mips/sibyte/bcm1480/Makefile
new file mode 100644
index 0000000000..cf1327d3fc
--- /dev/null
+++ b/arch/mips/sibyte/bcm1480/Makefile
@@ -0,0 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0-only
+obj-y := setup.o irq.o time.o
+
+obj-$(CONFIG_SMP) += smp.o
diff --git a/arch/mips/sibyte/bcm1480/irq.c b/arch/mips/sibyte/bcm1480/irq.c
new file mode 100644
index 0000000000..7929bee911
--- /dev/null
+++ b/arch/mips/sibyte/bcm1480/irq.c
@@ -0,0 +1,348 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2000,2001,2002,2003,2004 Broadcom Corporation
+ */
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/linkage.h>
+#include <linux/interrupt.h>
+#include <linux/smp.h>
+#include <linux/spinlock.h>
+#include <linux/mm.h>
+#include <linux/kernel_stat.h>
+
+#include <asm/errno.h>
+#include <asm/irq_regs.h>
+#include <asm/signal.h>
+#include <asm/io.h>
+
+#include <asm/sibyte/bcm1480_regs.h>
+#include <asm/sibyte/bcm1480_int.h>
+#include <asm/sibyte/bcm1480_scd.h>
+
+#include <asm/sibyte/sb1250_uart.h>
+#include <asm/sibyte/sb1250.h>
+
+/*
+ * These are the routines that handle all the low level interrupt stuff.
+ * Actions handled here are: initialization of the interrupt map, requesting of
+ * interrupt lines by handlers, dispatching if interrupts to handlers, probing
+ * for interrupt lines
+ */
+
+#ifdef CONFIG_PCI
+extern unsigned long ht_eoi_space;
+#endif
+
+/* Store the CPU id (not the logical number) */
+int bcm1480_irq_owner[BCM1480_NR_IRQS];
+
+static DEFINE_RAW_SPINLOCK(bcm1480_imr_lock);
+
+void bcm1480_mask_irq(int cpu, int irq)
+{
+ unsigned long flags, hl_spacing;
+ u64 cur_ints;
+
+ raw_spin_lock_irqsave(&bcm1480_imr_lock, flags);
+ hl_spacing = 0;
+ if ((irq >= BCM1480_NR_IRQS_HALF) && (irq <= BCM1480_NR_IRQS)) {
+ hl_spacing = BCM1480_IMR_HL_SPACING;
+ irq -= BCM1480_NR_IRQS_HALF;
+ }
+ cur_ints = ____raw_readq(IOADDR(A_BCM1480_IMR_MAPPER(cpu) + R_BCM1480_IMR_INTERRUPT_MASK_H + hl_spacing));
+ cur_ints |= (((u64) 1) << irq);
+ ____raw_writeq(cur_ints, IOADDR(A_BCM1480_IMR_MAPPER(cpu) + R_BCM1480_IMR_INTERRUPT_MASK_H + hl_spacing));
+ raw_spin_unlock_irqrestore(&bcm1480_imr_lock, flags);
+}
+
+void bcm1480_unmask_irq(int cpu, int irq)
+{
+ unsigned long flags, hl_spacing;
+ u64 cur_ints;
+
+ raw_spin_lock_irqsave(&bcm1480_imr_lock, flags);
+ hl_spacing = 0;
+ if ((irq >= BCM1480_NR_IRQS_HALF) && (irq <= BCM1480_NR_IRQS)) {
+ hl_spacing = BCM1480_IMR_HL_SPACING;
+ irq -= BCM1480_NR_IRQS_HALF;
+ }
+ cur_ints = ____raw_readq(IOADDR(A_BCM1480_IMR_MAPPER(cpu) + R_BCM1480_IMR_INTERRUPT_MASK_H + hl_spacing));
+ cur_ints &= ~(((u64) 1) << irq);
+ ____raw_writeq(cur_ints, IOADDR(A_BCM1480_IMR_MAPPER(cpu) + R_BCM1480_IMR_INTERRUPT_MASK_H + hl_spacing));
+ raw_spin_unlock_irqrestore(&bcm1480_imr_lock, flags);
+}
+
+#ifdef CONFIG_SMP
+static int bcm1480_set_affinity(struct irq_data *d, const struct cpumask *mask,
+ bool force)
+{
+ unsigned int irq_dirty, irq = d->irq;
+ int i = 0, old_cpu, cpu, int_on, k;
+ u64 cur_ints;
+ unsigned long flags;
+
+ i = cpumask_first_and(mask, cpu_online_mask);
+
+ /* Convert logical CPU to physical CPU */
+ cpu = cpu_logical_map(i);
+
+ /* Protect against other affinity changers and IMR manipulation */
+ raw_spin_lock_irqsave(&bcm1480_imr_lock, flags);
+
+ /* Swizzle each CPU's IMR (but leave the IP selection alone) */
+ old_cpu = bcm1480_irq_owner[irq];
+ irq_dirty = irq;
+ if ((irq_dirty >= BCM1480_NR_IRQS_HALF) && (irq_dirty <= BCM1480_NR_IRQS)) {
+ irq_dirty -= BCM1480_NR_IRQS_HALF;
+ }
+
+ for (k=0; k<2; k++) { /* Loop through high and low interrupt mask register */
+ cur_ints = ____raw_readq(IOADDR(A_BCM1480_IMR_MAPPER(old_cpu) + R_BCM1480_IMR_INTERRUPT_MASK_H + (k*BCM1480_IMR_HL_SPACING)));
+ int_on = !(cur_ints & (((u64) 1) << irq_dirty));
+ if (int_on) {
+ /* If it was on, mask it */
+ cur_ints |= (((u64) 1) << irq_dirty);
+ ____raw_writeq(cur_ints, IOADDR(A_BCM1480_IMR_MAPPER(old_cpu) + R_BCM1480_IMR_INTERRUPT_MASK_H + (k*BCM1480_IMR_HL_SPACING)));
+ }
+ bcm1480_irq_owner[irq] = cpu;
+ if (int_on) {
+ /* unmask for the new CPU */
+ cur_ints = ____raw_readq(IOADDR(A_BCM1480_IMR_MAPPER(cpu) + R_BCM1480_IMR_INTERRUPT_MASK_H + (k*BCM1480_IMR_HL_SPACING)));
+ cur_ints &= ~(((u64) 1) << irq_dirty);
+ ____raw_writeq(cur_ints, IOADDR(A_BCM1480_IMR_MAPPER(cpu) + R_BCM1480_IMR_INTERRUPT_MASK_H + (k*BCM1480_IMR_HL_SPACING)));
+ }
+ }
+ raw_spin_unlock_irqrestore(&bcm1480_imr_lock, flags);
+
+ return 0;
+}
+#endif
+
+
+/*****************************************************************************/
+
+static void disable_bcm1480_irq(struct irq_data *d)
+{
+ unsigned int irq = d->irq;
+
+ bcm1480_mask_irq(bcm1480_irq_owner[irq], irq);
+}
+
+static void enable_bcm1480_irq(struct irq_data *d)
+{
+ unsigned int irq = d->irq;
+
+ bcm1480_unmask_irq(bcm1480_irq_owner[irq], irq);
+}
+
+
+static void ack_bcm1480_irq(struct irq_data *d)
+{
+ unsigned int irq_dirty, irq = d->irq;
+ u64 pending;
+ int k;
+
+ /*
+ * If the interrupt was an HT interrupt, now is the time to
+ * clear it. NOTE: we assume the HT bridge was set up to
+ * deliver the interrupts to all CPUs (which makes affinity
+ * changing easier for us)
+ */
+ irq_dirty = irq;
+ if ((irq_dirty >= BCM1480_NR_IRQS_HALF) && (irq_dirty <= BCM1480_NR_IRQS)) {
+ irq_dirty -= BCM1480_NR_IRQS_HALF;
+ }
+ for (k=0; k<2; k++) { /* Loop through high and low LDT interrupts */
+ pending = __raw_readq(IOADDR(A_BCM1480_IMR_REGISTER(bcm1480_irq_owner[irq],
+ R_BCM1480_IMR_LDT_INTERRUPT_H + (k*BCM1480_IMR_HL_SPACING))));
+ pending &= ((u64)1 << (irq_dirty));
+ if (pending) {
+#ifdef CONFIG_SMP
+ int i;
+ for (i=0; i<NR_CPUS; i++) {
+ /*
+ * Clear for all CPUs so an affinity switch
+ * doesn't find an old status
+ */
+ __raw_writeq(pending, IOADDR(A_BCM1480_IMR_REGISTER(cpu_logical_map(i),
+ R_BCM1480_IMR_LDT_INTERRUPT_CLR_H + (k*BCM1480_IMR_HL_SPACING))));
+ }
+#else
+ __raw_writeq(pending, IOADDR(A_BCM1480_IMR_REGISTER(0, R_BCM1480_IMR_LDT_INTERRUPT_CLR_H + (k*BCM1480_IMR_HL_SPACING))));
+#endif
+
+ /*
+ * Generate EOI. For Pass 1 parts, EOI is a nop. For
+ * Pass 2, the LDT world may be edge-triggered, but
+ * this EOI shouldn't hurt. If they are
+ * level-sensitive, the EOI is required.
+ */
+#ifdef CONFIG_PCI
+ if (ht_eoi_space)
+ *(uint32_t *)(ht_eoi_space+(irq<<16)+(7<<2)) = 0;
+#endif
+ }
+ }
+ bcm1480_mask_irq(bcm1480_irq_owner[irq], irq);
+}
+
+static struct irq_chip bcm1480_irq_type = {
+ .name = "BCM1480-IMR",
+ .irq_mask_ack = ack_bcm1480_irq,
+ .irq_mask = disable_bcm1480_irq,
+ .irq_unmask = enable_bcm1480_irq,
+#ifdef CONFIG_SMP
+ .irq_set_affinity = bcm1480_set_affinity
+#endif
+};
+
+void __init init_bcm1480_irqs(void)
+{
+ int i;
+
+ for (i = 0; i < BCM1480_NR_IRQS; i++) {
+ irq_set_chip_and_handler(i, &bcm1480_irq_type,
+ handle_level_irq);
+ bcm1480_irq_owner[i] = 0;
+ }
+}
+
+/*
+ * init_IRQ is called early in the boot sequence from init/main.c. It
+ * is responsible for setting up the interrupt mapper and installing the
+ * handler that will be responsible for dispatching interrupts to the
+ * "right" place.
+ */
+/*
+ * For now, map all interrupts to IP[2]. We could save
+ * some cycles by parceling out system interrupts to different
+ * IP lines, but keep it simple for bringup. We'll also direct
+ * all interrupts to a single CPU; we should probably route
+ * PCI and LDT to one cpu and everything else to the other
+ * to balance the load a bit.
+ *
+ * On the second cpu, everything is set to IP5, which is
+ * ignored, EXCEPT the mailbox interrupt. That one is
+ * set to IP[2] so it is handled. This is needed so we
+ * can do cross-cpu function calls, as required by SMP
+ */
+
+#define IMR_IP2_VAL K_BCM1480_INT_MAP_I0
+#define IMR_IP3_VAL K_BCM1480_INT_MAP_I1
+#define IMR_IP4_VAL K_BCM1480_INT_MAP_I2
+#define IMR_IP5_VAL K_BCM1480_INT_MAP_I3
+#define IMR_IP6_VAL K_BCM1480_INT_MAP_I4
+
+void __init arch_init_irq(void)
+{
+ unsigned int i, cpu;
+ u64 tmp;
+ unsigned int imask = STATUSF_IP4 | STATUSF_IP3 | STATUSF_IP2 |
+ STATUSF_IP1 | STATUSF_IP0;
+
+ /* Default everything to IP2 */
+ /* Start with _high registers which has no bit 0 interrupt source */
+ for (i = 1; i < BCM1480_NR_IRQS_HALF; i++) { /* was I0 */
+ for (cpu = 0; cpu < 4; cpu++) {
+ __raw_writeq(IMR_IP2_VAL,
+ IOADDR(A_BCM1480_IMR_REGISTER(cpu,
+ R_BCM1480_IMR_INTERRUPT_MAP_BASE_H) + (i << 3)));
+ }
+ }
+
+ /* Now do _low registers */
+ for (i = 0; i < BCM1480_NR_IRQS_HALF; i++) {
+ for (cpu = 0; cpu < 4; cpu++) {
+ __raw_writeq(IMR_IP2_VAL,
+ IOADDR(A_BCM1480_IMR_REGISTER(cpu,
+ R_BCM1480_IMR_INTERRUPT_MAP_BASE_L) + (i << 3)));
+ }
+ }
+
+ init_bcm1480_irqs();
+
+ /*
+ * Map the high 16 bits of mailbox_0 registers to IP[3], for
+ * inter-cpu messages
+ */
+ /* Was I1 */
+ for (cpu = 0; cpu < 4; cpu++) {
+ __raw_writeq(IMR_IP3_VAL, IOADDR(A_BCM1480_IMR_REGISTER(cpu, R_BCM1480_IMR_INTERRUPT_MAP_BASE_H) +
+ (K_BCM1480_INT_MBOX_0_0 << 3)));
+ }
+
+
+ /* Clear the mailboxes. The firmware may leave them dirty */
+ for (cpu = 0; cpu < 4; cpu++) {
+ __raw_writeq(0xffffffffffffffffULL,
+ IOADDR(A_BCM1480_IMR_REGISTER(cpu, R_BCM1480_IMR_MAILBOX_0_CLR_CPU)));
+ __raw_writeq(0xffffffffffffffffULL,
+ IOADDR(A_BCM1480_IMR_REGISTER(cpu, R_BCM1480_IMR_MAILBOX_1_CLR_CPU)));
+ }
+
+
+ /* Mask everything except the high 16 bit of mailbox_0 registers for all cpus */
+ tmp = ~((u64) 0) ^ ( (((u64) 1) << K_BCM1480_INT_MBOX_0_0));
+ for (cpu = 0; cpu < 4; cpu++) {
+ __raw_writeq(tmp, IOADDR(A_BCM1480_IMR_REGISTER(cpu, R_BCM1480_IMR_INTERRUPT_MASK_H)));
+ }
+ tmp = ~((u64) 0);
+ for (cpu = 0; cpu < 4; cpu++) {
+ __raw_writeq(tmp, IOADDR(A_BCM1480_IMR_REGISTER(cpu, R_BCM1480_IMR_INTERRUPT_MASK_L)));
+ }
+
+ /*
+ * Note that the timer interrupts are also mapped, but this is
+ * done in bcm1480_time_init(). Also, the profiling driver
+ * does its own management of IP7.
+ */
+
+ /* Enable necessary IPs, disable the rest */
+ change_c0_status(ST0_IM, imask);
+}
+
+extern void bcm1480_mailbox_interrupt(void);
+
+static inline void dispatch_ip2(void)
+{
+ unsigned long long mask_h, mask_l;
+ unsigned int cpu = smp_processor_id();
+ unsigned long base;
+
+ /*
+ * Default...we've hit an IP[2] interrupt, which means we've got to
+ * check the 1480 interrupt registers to figure out what to do. Need
+ * to detect which CPU we're on, now that smp_affinity is supported.
+ */
+ base = A_BCM1480_IMR_MAPPER(cpu);
+ mask_h = __raw_readq(
+ IOADDR(base + R_BCM1480_IMR_INTERRUPT_STATUS_BASE_H));
+ mask_l = __raw_readq(
+ IOADDR(base + R_BCM1480_IMR_INTERRUPT_STATUS_BASE_L));
+
+ if (mask_h) {
+ if (mask_h ^ 1)
+ do_IRQ(fls64(mask_h) - 1);
+ else if (mask_l)
+ do_IRQ(63 + fls64(mask_l));
+ }
+}
+
+asmlinkage void plat_irq_dispatch(void)
+{
+ unsigned int cpu = smp_processor_id();
+ unsigned int pending;
+
+ pending = read_c0_cause() & read_c0_status();
+
+ if (pending & CAUSEF_IP4)
+ do_IRQ(K_BCM1480_INT_TIMER_0 + cpu);
+#ifdef CONFIG_SMP
+ else if (pending & CAUSEF_IP3)
+ bcm1480_mailbox_interrupt();
+#endif
+
+ else if (pending & CAUSEF_IP2)
+ dispatch_ip2();
+}
diff --git a/arch/mips/sibyte/bcm1480/setup.c b/arch/mips/sibyte/bcm1480/setup.c
new file mode 100644
index 0000000000..e3e807046a
--- /dev/null
+++ b/arch/mips/sibyte/bcm1480/setup.c
@@ -0,0 +1,126 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2000,2001,2002,2003,2004 Broadcom Corporation
+ */
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/export.h>
+#include <linux/reboot.h>
+#include <linux/string.h>
+
+#include <asm/bootinfo.h>
+#include <asm/cpu.h>
+#include <asm/mipsregs.h>
+#include <asm/io.h>
+#include <asm/sibyte/sb1250.h>
+
+#include <asm/sibyte/bcm1480_regs.h>
+#include <asm/sibyte/bcm1480_scd.h>
+#include <asm/sibyte/sb1250_scd.h>
+
+unsigned int sb1_pass;
+unsigned int soc_pass;
+unsigned int soc_type;
+EXPORT_SYMBOL(soc_type);
+unsigned int periph_rev;
+EXPORT_SYMBOL_GPL(periph_rev);
+unsigned int zbbus_mhz;
+EXPORT_SYMBOL(zbbus_mhz);
+
+static unsigned int part_type;
+
+static char *soc_str;
+static char *pass_str;
+
+static int __init setup_bcm1x80_bcm1x55(void)
+{
+ switch (soc_pass) {
+ case K_SYS_REVISION_BCM1480_S0:
+ periph_rev = 1;
+ pass_str = "S0 (pass1)";
+ break;
+ case K_SYS_REVISION_BCM1480_A1:
+ periph_rev = 1;
+ pass_str = "A1 (pass1)";
+ break;
+ case K_SYS_REVISION_BCM1480_A2:
+ periph_rev = 1;
+ pass_str = "A2 (pass1)";
+ break;
+ case K_SYS_REVISION_BCM1480_A3:
+ periph_rev = 1;
+ pass_str = "A3 (pass1)";
+ break;
+ case K_SYS_REVISION_BCM1480_B0:
+ periph_rev = 1;
+ pass_str = "B0 (pass2)";
+ break;
+ default:
+ printk("Unknown %s rev %x\n", soc_str, soc_pass);
+ periph_rev = 1;
+ pass_str = "Unknown Revision";
+ break;
+ }
+
+ return 0;
+}
+
+/* Setup code likely to be common to all SiByte platforms */
+
+static int __init sys_rev_decode(void)
+{
+ int ret = 0;
+
+ switch (soc_type) {
+ case K_SYS_SOC_TYPE_BCM1x80:
+ if (part_type == K_SYS_PART_BCM1480)
+ soc_str = "BCM1480";
+ else if (part_type == K_SYS_PART_BCM1280)
+ soc_str = "BCM1280";
+ else
+ soc_str = "BCM1x80";
+ ret = setup_bcm1x80_bcm1x55();
+ break;
+
+ case K_SYS_SOC_TYPE_BCM1x55:
+ if (part_type == K_SYS_PART_BCM1455)
+ soc_str = "BCM1455";
+ else if (part_type == K_SYS_PART_BCM1255)
+ soc_str = "BCM1255";
+ else
+ soc_str = "BCM1x55";
+ ret = setup_bcm1x80_bcm1x55();
+ break;
+
+ default:
+ printk("Unknown part type %x\n", part_type);
+ ret = 1;
+ break;
+ }
+
+ return ret;
+}
+
+void __init bcm1480_setup(void)
+{
+ uint64_t sys_rev;
+ int plldiv;
+
+ sb1_pass = read_c0_prid() & PRID_REV_MASK;
+ sys_rev = __raw_readq(IOADDR(A_SCD_SYSTEM_REVISION));
+ soc_type = SYS_SOC_TYPE(sys_rev);
+ part_type = G_SYS_PART(sys_rev);
+ soc_pass = G_SYS_REVISION(sys_rev);
+
+ if (sys_rev_decode()) {
+ printk("Restart after failure to identify SiByte chip\n");
+ machine_restart(NULL);
+ }
+
+ plldiv = G_BCM1480_SYS_PLL_DIV(__raw_readq(IOADDR(A_SCD_SYSTEM_CFG)));
+ zbbus_mhz = ((plldiv >> 1) * 50) + ((plldiv & 1) * 25);
+
+ printk("Broadcom SiByte %s %s @ %d MHz (SB-1A rev %d)\n",
+ soc_str, pass_str, zbbus_mhz * 2, sb1_pass);
+ printk("Board type: %s\n", get_system_type());
+}
diff --git a/arch/mips/sibyte/bcm1480/smp.c b/arch/mips/sibyte/bcm1480/smp.c
new file mode 100644
index 0000000000..5861e50255
--- /dev/null
+++ b/arch/mips/sibyte/bcm1480/smp.c
@@ -0,0 +1,179 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2001,2002,2004 Broadcom Corporation
+ */
+
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/smp.h>
+#include <linux/kernel_stat.h>
+#include <linux/sched.h>
+#include <linux/sched/task_stack.h>
+
+#include <asm/mmu_context.h>
+#include <asm/io.h>
+#include <asm/fw/cfe/cfe_api.h>
+#include <asm/sibyte/sb1250.h>
+#include <asm/sibyte/bcm1480_regs.h>
+#include <asm/sibyte/bcm1480_int.h>
+
+/*
+ * These are routines for dealing with the bcm1480 smp capabilities
+ * independent of board/firmware
+ */
+
+static void *mailbox_0_set_regs[] = {
+ IOADDR(A_BCM1480_IMR_CPU0_BASE + R_BCM1480_IMR_MAILBOX_0_SET_CPU),
+ IOADDR(A_BCM1480_IMR_CPU1_BASE + R_BCM1480_IMR_MAILBOX_0_SET_CPU),
+ IOADDR(A_BCM1480_IMR_CPU2_BASE + R_BCM1480_IMR_MAILBOX_0_SET_CPU),
+ IOADDR(A_BCM1480_IMR_CPU3_BASE + R_BCM1480_IMR_MAILBOX_0_SET_CPU),
+};
+
+static void *mailbox_0_clear_regs[] = {
+ IOADDR(A_BCM1480_IMR_CPU0_BASE + R_BCM1480_IMR_MAILBOX_0_CLR_CPU),
+ IOADDR(A_BCM1480_IMR_CPU1_BASE + R_BCM1480_IMR_MAILBOX_0_CLR_CPU),
+ IOADDR(A_BCM1480_IMR_CPU2_BASE + R_BCM1480_IMR_MAILBOX_0_CLR_CPU),
+ IOADDR(A_BCM1480_IMR_CPU3_BASE + R_BCM1480_IMR_MAILBOX_0_CLR_CPU),
+};
+
+static void *mailbox_0_regs[] = {
+ IOADDR(A_BCM1480_IMR_CPU0_BASE + R_BCM1480_IMR_MAILBOX_0_CPU),
+ IOADDR(A_BCM1480_IMR_CPU1_BASE + R_BCM1480_IMR_MAILBOX_0_CPU),
+ IOADDR(A_BCM1480_IMR_CPU2_BASE + R_BCM1480_IMR_MAILBOX_0_CPU),
+ IOADDR(A_BCM1480_IMR_CPU3_BASE + R_BCM1480_IMR_MAILBOX_0_CPU),
+};
+
+/*
+ * SMP init and finish on secondary CPUs
+ */
+void bcm1480_smp_init(void)
+{
+ unsigned int imask = STATUSF_IP4 | STATUSF_IP3 | STATUSF_IP2 |
+ STATUSF_IP1 | STATUSF_IP0;
+
+ /* Set interrupt mask, but don't enable */
+ change_c0_status(ST0_IM, imask);
+}
+
+/*
+ * These are routines for dealing with the sb1250 smp capabilities
+ * independent of board/firmware
+ */
+
+/*
+ * Simple enough; everything is set up, so just poke the appropriate mailbox
+ * register, and we should be set
+ */
+static void bcm1480_send_ipi_single(int cpu, unsigned int action)
+{
+ __raw_writeq((((u64)action)<< 48), mailbox_0_set_regs[cpu]);
+}
+
+static void bcm1480_send_ipi_mask(const struct cpumask *mask,
+ unsigned int action)
+{
+ unsigned int i;
+
+ for_each_cpu(i, mask)
+ bcm1480_send_ipi_single(i, action);
+}
+
+/*
+ * Code to run on secondary just after probing the CPU
+ */
+static void bcm1480_init_secondary(void)
+{
+ extern void bcm1480_smp_init(void);
+
+ bcm1480_smp_init();
+}
+
+/*
+ * Do any tidying up before marking online and running the idle
+ * loop
+ */
+static void bcm1480_smp_finish(void)
+{
+ extern void sb1480_clockevent_init(void);
+
+ sb1480_clockevent_init();
+ local_irq_enable();
+}
+
+/*
+ * Setup the PC, SP, and GP of a secondary processor and start it
+ * running!
+ */
+static int bcm1480_boot_secondary(int cpu, struct task_struct *idle)
+{
+ int retval;
+
+ retval = cfe_cpu_start(cpu_logical_map(cpu), &smp_bootstrap,
+ __KSTK_TOS(idle),
+ (unsigned long)task_thread_info(idle), 0);
+ if (retval != 0)
+ printk("cfe_start_cpu(%i) returned %i\n" , cpu, retval);
+ return retval;
+}
+
+/*
+ * Use CFE to find out how many CPUs are available, setting up
+ * cpu_possible_mask and the logical/physical mappings.
+ * XXXKW will the boot CPU ever not be physical 0?
+ *
+ * Common setup before any secondaries are started
+ */
+static void __init bcm1480_smp_setup(void)
+{
+ int i, num;
+
+ init_cpu_possible(cpumask_of(0));
+ __cpu_number_map[0] = 0;
+ __cpu_logical_map[0] = 0;
+
+ for (i = 1, num = 0; i < NR_CPUS; i++) {
+ if (cfe_cpu_stop(i) == 0) {
+ set_cpu_possible(i, true);
+ __cpu_number_map[i] = ++num;
+ __cpu_logical_map[num] = i;
+ }
+ }
+ printk(KERN_INFO "Detected %i available secondary CPU(s)\n", num);
+}
+
+static void __init bcm1480_prepare_cpus(unsigned int max_cpus)
+{
+}
+
+const struct plat_smp_ops bcm1480_smp_ops = {
+ .send_ipi_single = bcm1480_send_ipi_single,
+ .send_ipi_mask = bcm1480_send_ipi_mask,
+ .init_secondary = bcm1480_init_secondary,
+ .smp_finish = bcm1480_smp_finish,
+ .boot_secondary = bcm1480_boot_secondary,
+ .smp_setup = bcm1480_smp_setup,
+ .prepare_cpus = bcm1480_prepare_cpus,
+};
+
+void bcm1480_mailbox_interrupt(void)
+{
+ int cpu = smp_processor_id();
+ int irq = K_BCM1480_INT_MBOX_0_0;
+ unsigned int action;
+
+ kstat_incr_irq_this_cpu(irq);
+ /* Load the mailbox register to figure out what we're supposed to do */
+ action = (__raw_readq(mailbox_0_regs[cpu]) >> 48) & 0xffff;
+
+ /* Clear the mailbox to clear the interrupt */
+ __raw_writeq(((u64)action)<<48, mailbox_0_clear_regs[cpu]);
+
+ if (action & SMP_RESCHEDULE_YOURSELF)
+ scheduler_ipi();
+
+ if (action & SMP_CALL_FUNCTION) {
+ irq_enter();
+ generic_smp_call_function_interrupt();
+ irq_exit();
+ }
+}
diff --git a/arch/mips/sibyte/bcm1480/time.c b/arch/mips/sibyte/bcm1480/time.c
new file mode 100644
index 0000000000..e6450d79fb
--- /dev/null
+++ b/arch/mips/sibyte/bcm1480/time.c
@@ -0,0 +1,14 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2000,2001,2004 Broadcom Corporation
+ */
+#include <linux/init.h>
+
+extern void sb1480_clockevent_init(void);
+extern void sb1480_clocksource_init(void);
+
+void __init plat_time_init(void)
+{
+ sb1480_clocksource_init();
+ sb1480_clockevent_init();
+}
diff --git a/arch/mips/sibyte/common/Makefile b/arch/mips/sibyte/common/Makefile
new file mode 100644
index 0000000000..57f670aa16
--- /dev/null
+++ b/arch/mips/sibyte/common/Makefile
@@ -0,0 +1,6 @@
+# SPDX-License-Identifier: GPL-2.0-only
+obj-y := cfe.o
+obj-$(CONFIG_SWIOTLB) += dma.o
+obj-$(CONFIG_SIBYTE_BUS_WATCHER) += bus_watcher.o
+obj-$(CONFIG_SIBYTE_CFE_CONSOLE) += cfe_console.o
+obj-$(CONFIG_SIBYTE_TBPROF) += sb_tbprof.o
diff --git a/arch/mips/sibyte/common/bus_watcher.c b/arch/mips/sibyte/common/bus_watcher.c
new file mode 100644
index 0000000000..a296d2c518
--- /dev/null
+++ b/arch/mips/sibyte/common/bus_watcher.c
@@ -0,0 +1,228 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2002,2003 Broadcom Corporation
+ */
+
+/*
+ * The Bus Watcher monitors internal bus transactions and maintains
+ * counts of transactions with error status, logging details and
+ * causing one of several interrupts. This driver provides a handler
+ * for those interrupts which aggregates the counts (to avoid
+ * saturating the 8-bit counters) and provides a presence in
+ * /proc/bus_watcher if PROC_FS is on.
+ */
+
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/interrupt.h>
+#include <linux/sched.h>
+#include <linux/proc_fs.h>
+#include <linux/seq_file.h>
+#include <asm/io.h>
+
+#include <asm/sibyte/sb1250.h>
+#include <asm/sibyte/sb1250_regs.h>
+#include <asm/sibyte/sb1250_int.h>
+#include <asm/sibyte/sb1250_scd.h>
+#ifdef CONFIG_SIBYTE_BCM1x80
+#include <asm/sibyte/bcm1480_regs.h>
+#endif
+
+
+struct bw_stats_struct {
+ uint64_t status;
+ uint32_t l2_err;
+ uint32_t memio_err;
+ int status_printed;
+ unsigned long l2_cor_d;
+ unsigned long l2_bad_d;
+ unsigned long l2_cor_t;
+ unsigned long l2_bad_t;
+ unsigned long mem_cor_d;
+ unsigned long mem_bad_d;
+ unsigned long bus_error;
+} bw_stats;
+
+
+static void print_summary(uint32_t status, uint32_t l2_err,
+ uint32_t memio_err)
+{
+ printk("Bus watcher error counters: %08x %08x\n", l2_err, memio_err);
+ printk("\nLast recorded signature:\n");
+ printk("Request %02x from %d, answered by %d with Dcode %d\n",
+ (unsigned int)(G_SCD_BERR_TID(status) & 0x3f),
+ (int)(G_SCD_BERR_TID(status) >> 6),
+ (int)G_SCD_BERR_RID(status),
+ (int)G_SCD_BERR_DCODE(status));
+}
+
+/*
+ * check_bus_watcher is exported for use in situations where we want
+ * to see the most recent status of the bus watcher, which might have
+ * already been destructively read out of the registers.
+ *
+ * notes: this is currently used by the cache error handler
+ * should provide locking against the interrupt handler
+ */
+void check_bus_watcher(void)
+{
+ u32 status, l2_err, memio_err;
+
+#if defined(CONFIG_SIBYTE_BCM112X) || defined(CONFIG_SIBYTE_SB1250)
+ /* Use non-destructive register */
+ status = csr_in32(IOADDR(A_SCD_BUS_ERR_STATUS_DEBUG));
+#elif defined(CONFIG_SIBYTE_BCM1x80)
+ /* Use non-destructive register */
+ /* Same as 1250 except BUS_ERR_STATUS_DEBUG is in a different place. */
+ status = csr_in32(IOADDR(A_BCM1480_BUS_ERR_STATUS_DEBUG));
+#else
+#error bus watcher being built for unknown Sibyte SOC!
+#endif
+ if (!(status & 0x7fffffff)) {
+ printk("Using last values reaped by bus watcher driver\n");
+ status = bw_stats.status;
+ l2_err = bw_stats.l2_err;
+ memio_err = bw_stats.memio_err;
+ } else {
+ l2_err = csr_in32(IOADDR(A_BUS_L2_ERRORS));
+ memio_err = csr_in32(IOADDR(A_BUS_MEM_IO_ERRORS));
+ }
+ if (status & ~(1UL << 31))
+ print_summary(status, l2_err, memio_err);
+ else
+ printk("Bus watcher indicates no error\n");
+}
+
+#ifdef CONFIG_PROC_FS
+
+/* For simplicity, I want to assume a single read is required each
+ time */
+static int bw_proc_show(struct seq_file *m, void *v)
+{
+ struct bw_stats_struct *stats = m->private;
+
+ seq_puts(m, "SiByte Bus Watcher statistics\n");
+ seq_puts(m, "-----------------------------\n");
+ seq_printf(m, "L2-d-cor %8ld\nL2-d-bad %8ld\n",
+ stats->l2_cor_d, stats->l2_bad_d);
+ seq_printf(m, "L2-t-cor %8ld\nL2-t-bad %8ld\n",
+ stats->l2_cor_t, stats->l2_bad_t);
+ seq_printf(m, "MC-d-cor %8ld\nMC-d-bad %8ld\n",
+ stats->mem_cor_d, stats->mem_bad_d);
+ seq_printf(m, "IO-err %8ld\n", stats->bus_error);
+ seq_puts(m, "\nLast recorded signature:\n");
+ seq_printf(m, "Request %02x from %d, answered by %d with Dcode %d\n",
+ (unsigned int)(G_SCD_BERR_TID(stats->status) & 0x3f),
+ (int)(G_SCD_BERR_TID(stats->status) >> 6),
+ (int)G_SCD_BERR_RID(stats->status),
+ (int)G_SCD_BERR_DCODE(stats->status));
+ /* XXXKW indicate multiple errors between printings, or stats
+ collection (or both)? */
+ if (stats->status & M_SCD_BERR_MULTERRS)
+ seq_puts(m, "Multiple errors observed since last check.\n");
+ if (stats->status_printed) {
+ seq_puts(m, "(no change since last printing)\n");
+ } else {
+ stats->status_printed = 1;
+ }
+
+ return 0;
+}
+
+static void create_proc_decoder(struct bw_stats_struct *stats)
+{
+ struct proc_dir_entry *ent;
+
+ ent = proc_create_single_data("bus_watcher", S_IWUSR | S_IRUGO, NULL,
+ bw_proc_show, stats);
+ if (!ent) {
+ printk(KERN_INFO "Unable to initialize bus_watcher /proc entry\n");
+ return;
+ }
+}
+
+#endif /* CONFIG_PROC_FS */
+
+/*
+ * sibyte_bw_int - handle bus watcher interrupts and accumulate counts
+ *
+ * notes: possible re-entry due to multiple sources
+ * should check/indicate saturation
+ */
+static irqreturn_t sibyte_bw_int(int irq, void *data)
+{
+ struct bw_stats_struct *stats = data;
+ unsigned long cntr;
+#ifdef CONFIG_SIBYTE_BW_TRACE
+ int i;
+#endif
+
+#ifdef CONFIG_SIBYTE_BW_TRACE
+ csr_out32(M_SCD_TRACE_CFG_FREEZE, IOADDR(A_SCD_TRACE_CFG));
+ csr_out32(M_SCD_TRACE_CFG_START_READ, IOADDR(A_SCD_TRACE_CFG));
+
+ for (i=0; i<256*6; i++)
+ printk("%016llx\n",
+ (long long)__raw_readq(IOADDR(A_SCD_TRACE_READ)));
+
+ csr_out32(M_SCD_TRACE_CFG_RESET, IOADDR(A_SCD_TRACE_CFG));
+ csr_out32(M_SCD_TRACE_CFG_START, IOADDR(A_SCD_TRACE_CFG));
+#endif
+
+ /* Destructive read, clears register and interrupt */
+ stats->status = csr_in32(IOADDR(A_SCD_BUS_ERR_STATUS));
+ stats->status_printed = 0;
+
+ stats->l2_err = cntr = csr_in32(IOADDR(A_BUS_L2_ERRORS));
+ stats->l2_cor_d += G_SCD_L2ECC_CORR_D(cntr);
+ stats->l2_bad_d += G_SCD_L2ECC_BAD_D(cntr);
+ stats->l2_cor_t += G_SCD_L2ECC_CORR_T(cntr);
+ stats->l2_bad_t += G_SCD_L2ECC_BAD_T(cntr);
+ csr_out32(0, IOADDR(A_BUS_L2_ERRORS));
+
+ stats->memio_err = cntr = csr_in32(IOADDR(A_BUS_MEM_IO_ERRORS));
+ stats->mem_cor_d += G_SCD_MEM_ECC_CORR(cntr);
+ stats->mem_bad_d += G_SCD_MEM_ECC_BAD(cntr);
+ stats->bus_error += G_SCD_MEM_BUSERR(cntr);
+ csr_out32(0, IOADDR(A_BUS_MEM_IO_ERRORS));
+
+ return IRQ_HANDLED;
+}
+
+int __init sibyte_bus_watcher(void)
+{
+ memset(&bw_stats, 0, sizeof(struct bw_stats_struct));
+ bw_stats.status_printed = 1;
+
+ if (request_irq(K_INT_BAD_ECC, sibyte_bw_int, 0, "Bus watcher", &bw_stats)) {
+ printk("Failed to register bus watcher BAD_ECC irq\n");
+ return -1;
+ }
+ if (request_irq(K_INT_COR_ECC, sibyte_bw_int, 0, "Bus watcher", &bw_stats)) {
+ free_irq(K_INT_BAD_ECC, &bw_stats);
+ printk("Failed to register bus watcher COR_ECC irq\n");
+ return -1;
+ }
+ if (request_irq(K_INT_IO_BUS, sibyte_bw_int, 0, "Bus watcher", &bw_stats)) {
+ free_irq(K_INT_BAD_ECC, &bw_stats);
+ free_irq(K_INT_COR_ECC, &bw_stats);
+ printk("Failed to register bus watcher IO_BUS irq\n");
+ return -1;
+ }
+
+#ifdef CONFIG_PROC_FS
+ create_proc_decoder(&bw_stats);
+#endif
+
+#ifdef CONFIG_SIBYTE_BW_TRACE
+ csr_out32((M_SCD_TRSEQ_ASAMPLE | M_SCD_TRSEQ_DSAMPLE |
+ K_SCD_TRSEQ_TRIGGER_ALL),
+ IOADDR(A_SCD_TRACE_SEQUENCE_0));
+ csr_out32(M_SCD_TRACE_CFG_RESET, IOADDR(A_SCD_TRACE_CFG));
+ csr_out32(M_SCD_TRACE_CFG_START, IOADDR(A_SCD_TRACE_CFG));
+#endif
+
+ return 0;
+}
+
+device_initcall(sibyte_bus_watcher);
diff --git a/arch/mips/sibyte/common/cfe.c b/arch/mips/sibyte/common/cfe.c
new file mode 100644
index 0000000000..2cb90dbbe8
--- /dev/null
+++ b/arch/mips/sibyte/common/cfe.c
@@ -0,0 +1,309 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2000, 2001, 2002, 2003 Broadcom Corporation
+ */
+
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/linkage.h>
+#include <linux/mm.h>
+#include <linux/memblock.h>
+#include <linux/pm.h>
+#include <linux/smp.h>
+
+#include <asm/bootinfo.h>
+#include <asm/reboot.h>
+#include <asm/setup.h>
+#include <asm/sibyte/board.h>
+#include <asm/smp-ops.h>
+
+#include <asm/fw/cfe/cfe_api.h>
+#include <asm/fw/cfe/cfe_error.h>
+
+/* Max ram addressable in 32-bit segments */
+#ifdef CONFIG_64BIT
+#define MAX_RAM_SIZE (~0ULL)
+#else
+#ifdef CONFIG_HIGHMEM
+#ifdef CONFIG_PHYS_ADDR_T_64BIT
+#define MAX_RAM_SIZE (~0ULL)
+#else
+#define MAX_RAM_SIZE (0xffffffffULL)
+#endif
+#else
+#define MAX_RAM_SIZE (0x1fffffffULL)
+#endif
+#endif
+
+int cfe_cons_handle;
+
+#ifdef CONFIG_BLK_DEV_INITRD
+extern unsigned long initrd_start, initrd_end;
+#endif
+
+static void __noreturn cfe_linux_exit(void *arg)
+{
+ int warm = *(int *)arg;
+
+ if (smp_processor_id()) {
+ static int reboot_smp;
+
+ /* Don't repeat the process from another CPU */
+ if (!reboot_smp) {
+ /* Get CPU 0 to do the cfe_exit */
+ reboot_smp = 1;
+ smp_call_function(cfe_linux_exit, arg, 0);
+ }
+ } else {
+ printk("Passing control back to CFE...\n");
+ cfe_exit(warm, 0);
+ printk("cfe_exit returned??\n");
+ }
+ while (1);
+}
+
+static void __noreturn cfe_linux_restart(char *command)
+{
+ static const int zero;
+
+ cfe_linux_exit((void *)&zero);
+}
+
+static void __noreturn cfe_linux_halt(void)
+{
+ static const int one = 1;
+
+ cfe_linux_exit((void *)&one);
+}
+
+static __init void prom_meminit(void)
+{
+ u64 addr, size, type; /* regardless of PHYS_ADDR_T_64BIT */
+ int mem_flags = 0;
+ unsigned int idx;
+ int rd_flag;
+#ifdef CONFIG_BLK_DEV_INITRD
+ unsigned long initrd_pstart;
+ unsigned long initrd_pend;
+
+ initrd_pstart = CPHYSADDR(initrd_start);
+ initrd_pend = CPHYSADDR(initrd_end);
+ if (initrd_start &&
+ ((initrd_pstart > MAX_RAM_SIZE)
+ || (initrd_pend > MAX_RAM_SIZE))) {
+ panic("initrd out of addressable memory");
+ }
+
+#endif /* INITRD */
+
+ for (idx = 0; cfe_enummem(idx, mem_flags, &addr, &size, &type) != CFE_ERR_NOMORE;
+ idx++) {
+ rd_flag = 0;
+ if (type == CFE_MI_AVAILABLE) {
+ /*
+ * See if this block contains (any portion of) the
+ * ramdisk
+ */
+#ifdef CONFIG_BLK_DEV_INITRD
+ if (initrd_start) {
+ if ((initrd_pstart > addr) &&
+ (initrd_pstart < (addr + size))) {
+ memblock_add(addr,
+ initrd_pstart - addr);
+ rd_flag = 1;
+ }
+ if ((initrd_pend > addr) &&
+ (initrd_pend < (addr + size))) {
+ memblock_add(initrd_pend,
+ (addr + size) - initrd_pend);
+ rd_flag = 1;
+ }
+ }
+#endif
+ if (!rd_flag) {
+ if (addr > MAX_RAM_SIZE)
+ continue;
+ if (addr+size > MAX_RAM_SIZE)
+ size = MAX_RAM_SIZE - (addr+size) + 1;
+ /*
+ * memcpy/__copy_user prefetch, which
+ * will cause a bus error for
+ * KSEG/KUSEG addrs not backed by RAM.
+ * Hence, reserve some padding for the
+ * prefetch distance.
+ */
+ if (size > 512)
+ size -= 512;
+ memblock_add(addr, size);
+ }
+ }
+ }
+#ifdef CONFIG_BLK_DEV_INITRD
+ if (initrd_start) {
+ memblock_add(initrd_pstart, initrd_pend - initrd_pstart);
+ memblock_reserve(initrd_pstart, initrd_pend - initrd_pstart);
+ }
+#endif
+}
+
+#ifdef CONFIG_BLK_DEV_INITRD
+static int __init initrd_setup(char *str)
+{
+ char rdarg[64];
+ int idx;
+ char *tmp, *endptr;
+ unsigned long initrd_size;
+
+ /* Make a copy of the initrd argument so we can smash it up here */
+ for (idx = 0; idx < sizeof(rdarg)-1; idx++) {
+ if (!str[idx] || (str[idx] == ' ')) break;
+ rdarg[idx] = str[idx];
+ }
+
+ rdarg[idx] = 0;
+ str = rdarg;
+
+ /*
+ *Initrd location comes in the form "<hex size of ramdisk in bytes>@<location in memory>"
+ * e.g. initrd=3abfd@80010000. This is set up by the loader.
+ */
+ for (tmp = str; *tmp != '@'; tmp++) {
+ if (!*tmp) {
+ goto fail;
+ }
+ }
+ *tmp = 0;
+ tmp++;
+ if (!*tmp) {
+ goto fail;
+ }
+ initrd_size = simple_strtoul(str, &endptr, 16);
+ if (*endptr) {
+ *(tmp-1) = '@';
+ goto fail;
+ }
+ *(tmp-1) = '@';
+ initrd_start = simple_strtoul(tmp, &endptr, 16);
+ if (*endptr) {
+ goto fail;
+ }
+ initrd_end = initrd_start + initrd_size;
+ printk("Found initrd of %lx@%lx\n", initrd_size, initrd_start);
+ return 1;
+ fail:
+ printk("Bad initrd argument. Disabling initrd\n");
+ initrd_start = 0;
+ initrd_end = 0;
+ return 1;
+}
+
+#endif
+
+extern const struct plat_smp_ops sb_smp_ops;
+extern const struct plat_smp_ops bcm1480_smp_ops;
+
+/*
+ * prom_init is called just after the cpu type is determined, from setup_arch()
+ */
+void __init prom_init(void)
+{
+ uint64_t cfe_ept, cfe_handle;
+ unsigned int cfe_eptseal;
+ int argc = fw_arg0;
+ char **envp = (char **) fw_arg2;
+ int *prom_vec = (int *) fw_arg3;
+
+ _machine_restart = cfe_linux_restart;
+ _machine_halt = cfe_linux_halt;
+ pm_power_off = cfe_linux_halt;
+
+ /*
+ * Check if a loader was used; if NOT, the 4 arguments are
+ * what CFE gives us (handle, 0, EPT and EPTSEAL)
+ */
+ if (argc < 0) {
+ cfe_handle = (uint64_t)(long)argc;
+ cfe_ept = (long)envp;
+ cfe_eptseal = (uint32_t)(unsigned long)prom_vec;
+ } else {
+ if ((int32_t)(long)prom_vec < 0) {
+ /*
+ * Old loader; all it gives us is the handle,
+ * so use the "known" entrypoint and assume
+ * the seal.
+ */
+ cfe_handle = (uint64_t)(long)prom_vec;
+ cfe_ept = (uint64_t)((int32_t)0x9fc00500);
+ cfe_eptseal = CFE_EPTSEAL;
+ } else {
+ /*
+ * Newer loaders bundle the handle/ept/eptseal
+ * Note: prom_vec is in the loader's useg
+ * which is still alive in the TLB.
+ */
+ cfe_handle = (uint64_t)((int32_t *)prom_vec)[0];
+ cfe_ept = (uint64_t)((int32_t *)prom_vec)[2];
+ cfe_eptseal = (unsigned int)((uint32_t *)prom_vec)[3];
+ }
+ }
+ if (cfe_eptseal != CFE_EPTSEAL) {
+ /* too early for panic to do any good */
+ printk("CFE's entrypoint seal doesn't match. Spinning.");
+ while (1) ;
+ }
+ cfe_init(cfe_handle, cfe_ept);
+ /*
+ * Get the handle for (at least) prom_putchar, possibly for
+ * boot console
+ */
+ cfe_cons_handle = cfe_getstdhandle(CFE_STDHANDLE_CONSOLE);
+ if (cfe_getenv("LINUX_CMDLINE", arcs_cmdline, COMMAND_LINE_SIZE) < 0) {
+ if (argc >= 0) {
+ /* The loader should have set the command line */
+ /* too early for panic to do any good */
+ printk("LINUX_CMDLINE not defined in cfe.");
+ while (1) ;
+ }
+ }
+
+#ifdef CONFIG_BLK_DEV_INITRD
+ {
+ char *ptr;
+ /* Need to find out early whether we've got an initrd. So scan
+ the list looking now */
+ for (ptr = arcs_cmdline; *ptr; ptr++) {
+ while (*ptr == ' ') {
+ ptr++;
+ }
+ if (!strncmp(ptr, "initrd=", 7)) {
+ initrd_setup(ptr+7);
+ break;
+ } else {
+ while (*ptr && (*ptr != ' ')) {
+ ptr++;
+ }
+ }
+ }
+ }
+#endif /* CONFIG_BLK_DEV_INITRD */
+
+ /* Not sure this is needed, but it's the safe way. */
+ arcs_cmdline[COMMAND_LINE_SIZE-1] = 0;
+
+ prom_meminit();
+
+#if defined(CONFIG_SIBYTE_BCM112X) || defined(CONFIG_SIBYTE_SB1250)
+ register_smp_ops(&sb_smp_ops);
+#endif
+#ifdef CONFIG_SIBYTE_BCM1x80
+ register_smp_ops(&bcm1480_smp_ops);
+#endif
+}
+
+void prom_putchar(char c)
+{
+ int ret;
+
+ while ((ret = cfe_write(cfe_cons_handle, &c, 1)) == 0)
+ ;
+}
diff --git a/arch/mips/sibyte/common/cfe_console.c b/arch/mips/sibyte/common/cfe_console.c
new file mode 100644
index 0000000000..8af7b41f7c
--- /dev/null
+++ b/arch/mips/sibyte/common/cfe_console.c
@@ -0,0 +1,81 @@
+// SPDX-License-Identifier: GPL-2.0
+#include <linux/init.h>
+#include <linux/errno.h>
+#include <linux/console.h>
+
+#include <asm/sibyte/board.h>
+
+#include <asm/fw/cfe/cfe_api.h>
+#include <asm/fw/cfe/cfe_error.h>
+
+extern int cfe_cons_handle;
+
+static void cfe_console_write(struct console *cons, const char *str,
+ unsigned int count)
+{
+ int i, last, written;
+
+ for (i=0, last=0; i<count; i++) {
+ if (!str[i])
+ /* XXXKW can/should this ever happen? */
+ return;
+ if (str[i] == '\n') {
+ do {
+ written = cfe_write(cfe_cons_handle, &str[last], i-last);
+ if (written < 0)
+ ;
+ last += written;
+ } while (last < i);
+ while (cfe_write(cfe_cons_handle, "\r", 1) <= 0)
+ ;
+ }
+ }
+ if (last != count) {
+ do {
+ written = cfe_write(cfe_cons_handle, &str[last], count-last);
+ if (written < 0)
+ ;
+ last += written;
+ } while (last < count);
+ }
+
+}
+
+static int cfe_console_setup(struct console *cons, char *str)
+{
+ char consdev[32];
+ /* XXXKW think about interaction with 'console=' cmdline arg */
+ /* If none of the console options are configured, the build will break. */
+ if (cfe_getenv("BOOT_CONSOLE", consdev, 32) >= 0) {
+#ifdef CONFIG_SERIAL_SB1250_DUART
+ if (!strcmp(consdev, "uart0")) {
+ setleds("u0cn");
+ } else if (!strcmp(consdev, "uart1")) {
+ setleds("u1cn");
+ } else
+#endif
+#ifdef CONFIG_VGA_CONSOLE
+ if (!strcmp(consdev, "pcconsole0")) {
+ setleds("pccn");
+ } else
+#endif
+ return -ENODEV;
+ }
+ return 0;
+}
+
+static struct console sb1250_cfe_cons = {
+ .name = "cfe",
+ .write = cfe_console_write,
+ .setup = cfe_console_setup,
+ .flags = CON_PRINTBUFFER,
+ .index = -1,
+};
+
+static int __init sb1250_cfe_console_init(void)
+{
+ register_console(&sb1250_cfe_cons);
+ return 0;
+}
+
+console_initcall(sb1250_cfe_console_init);
diff --git a/arch/mips/sibyte/common/dma.c b/arch/mips/sibyte/common/dma.c
new file mode 100644
index 0000000000..c5c2c782af
--- /dev/null
+++ b/arch/mips/sibyte/common/dma.c
@@ -0,0 +1,14 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * DMA support for Broadcom SiByte platforms.
+ *
+ * Copyright (c) 2018 Maciej W. Rozycki
+ */
+
+#include <linux/swiotlb.h>
+#include <asm/bootinfo.h>
+
+void __init plat_swiotlb_setup(void)
+{
+ swiotlb_init(true, SWIOTLB_VERBOSE);
+}
diff --git a/arch/mips/sibyte/common/sb_tbprof.c b/arch/mips/sibyte/common/sb_tbprof.c
new file mode 100644
index 0000000000..408db45efd
--- /dev/null
+++ b/arch/mips/sibyte/common/sb_tbprof.c
@@ -0,0 +1,595 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ *
+ * Copyright (C) 2001, 2002, 2003 Broadcom Corporation
+ * Copyright (C) 2007 Ralf Baechle <ralf@linux-mips.org>
+ * Copyright (C) 2007 MIPS Technologies, Inc.
+ * written by Ralf Baechle <ralf@linux-mips.org>
+ */
+
+#undef DEBUG
+
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/sched.h>
+#include <linux/vmalloc.h>
+#include <linux/fs.h>
+#include <linux/errno.h>
+#include <linux/wait.h>
+#include <asm/io.h>
+#include <asm/sibyte/sb1250.h>
+
+#ifdef CONFIG_SIBYTE_BCM1x80
+#include <asm/sibyte/bcm1480_regs.h>
+#include <asm/sibyte/bcm1480_scd.h>
+#include <asm/sibyte/bcm1480_int.h>
+#elif defined(CONFIG_SIBYTE_SB1250) || defined(CONFIG_SIBYTE_BCM112X)
+#include <asm/sibyte/sb1250_regs.h>
+#include <asm/sibyte/sb1250_scd.h>
+#include <asm/sibyte/sb1250_int.h>
+#else
+#error invalid SiByte UART configuration
+#endif
+
+#ifdef CONFIG_SIBYTE_BCM1x80
+#undef K_INT_TRACE_FREEZE
+#define K_INT_TRACE_FREEZE K_BCM1480_INT_TRACE_FREEZE
+#undef K_INT_PERF_CNT
+#define K_INT_PERF_CNT K_BCM1480_INT_PERF_CNT
+#endif
+
+#include <linux/uaccess.h>
+
+#define SBPROF_TB_MAJOR 240
+
+typedef u64 tb_sample_t[6*256];
+
+enum open_status {
+ SB_CLOSED,
+ SB_OPENING,
+ SB_OPEN
+};
+
+struct sbprof_tb {
+ wait_queue_head_t tb_sync;
+ wait_queue_head_t tb_read;
+ struct mutex lock;
+ enum open_status open;
+ tb_sample_t *sbprof_tbbuf;
+ int next_tb_sample;
+
+ volatile int tb_enable;
+ volatile int tb_armed;
+
+};
+
+static struct sbprof_tb sbp;
+
+#define MAX_SAMPLE_BYTES (24*1024*1024)
+#define MAX_TBSAMPLE_BYTES (12*1024*1024)
+
+#define MAX_SAMPLES (MAX_SAMPLE_BYTES/sizeof(u_int32_t))
+#define TB_SAMPLE_SIZE (sizeof(tb_sample_t))
+#define MAX_TB_SAMPLES (MAX_TBSAMPLE_BYTES/TB_SAMPLE_SIZE)
+
+/* ioctls */
+#define SBPROF_ZBSTART _IOW('s', 0, int)
+#define SBPROF_ZBSTOP _IOW('s', 1, int)
+#define SBPROF_ZBWAITFULL _IOW('s', 2, int)
+
+/*
+ * Routines for using 40-bit SCD cycle counter
+ *
+ * Client responsible for either handling interrupts or making sure
+ * the cycles counter never saturates, e.g., by doing
+ * zclk_timer_init(0) at least every 2^40 - 1 ZCLKs.
+ */
+
+/*
+ * Configures SCD counter 0 to count ZCLKs starting from val;
+ * Configures SCD counters1,2,3 to count nothing.
+ * Must not be called while gathering ZBbus profiles.
+ */
+
+#define zclk_timer_init(val) \
+ __asm__ __volatile__ (".set push;" \
+ ".set mips64;" \
+ "la $8, 0xb00204c0;" /* SCD perf_cnt_cfg */ \
+ "sd %0, 0x10($8);" /* write val to counter0 */ \
+ "sd %1, 0($8);" /* config counter0 for zclks*/ \
+ ".set pop" \
+ : /* no outputs */ \
+ /* enable, counter0 */ \
+ : /* inputs */ "r"(val), "r" ((1ULL << 33) | 1ULL) \
+ : /* modifies */ "$8" )
+
+
+/* Reads SCD counter 0 and puts result in value
+ unsigned long long val; */
+#define zclk_get(val) \
+ __asm__ __volatile__ (".set push;" \
+ ".set mips64;" \
+ "la $8, 0xb00204c0;" /* SCD perf_cnt_cfg */ \
+ "ld %0, 0x10($8);" /* write val to counter0 */ \
+ ".set pop" \
+ : /* outputs */ "=r"(val) \
+ : /* inputs */ \
+ : /* modifies */ "$8" )
+
+#define DEVNAME "sb_tbprof"
+
+#define TB_FULL (sbp.next_tb_sample == MAX_TB_SAMPLES)
+
+/*
+ * Support for ZBbus sampling using the trace buffer
+ *
+ * We use the SCD performance counter interrupt, caused by a Zclk counter
+ * overflow, to trigger the start of tracing.
+ *
+ * We set the trace buffer to sample everything and freeze on
+ * overflow.
+ *
+ * We map the interrupt for trace_buffer_freeze to handle it on CPU 0.
+ *
+ */
+
+static u64 tb_period;
+
+static void arm_tb(void)
+{
+ u64 scdperfcnt;
+ u64 next = (1ULL << 40) - tb_period;
+ u64 tb_options = M_SCD_TRACE_CFG_FREEZE_FULL;
+
+ /*
+ * Generate an SCD_PERFCNT interrupt in TB_PERIOD Zclks to
+ * trigger start of trace. XXX vary sampling period
+ */
+ __raw_writeq(0, IOADDR(A_SCD_PERF_CNT_1));
+ scdperfcnt = __raw_readq(IOADDR(A_SCD_PERF_CNT_CFG));
+
+ /*
+ * Unfortunately, in Pass 2 we must clear all counters to knock down
+ * a previous interrupt request. This means that bus profiling
+ * requires ALL of the SCD perf counters.
+ */
+#ifdef CONFIG_SIBYTE_BCM1x80
+ __raw_writeq((scdperfcnt & ~M_SPC_CFG_SRC1) |
+ /* keep counters 0,2,3,4,5,6,7 as is */
+ V_SPC_CFG_SRC1(1), /* counter 1 counts cycles */
+ IOADDR(A_BCM1480_SCD_PERF_CNT_CFG0));
+ __raw_writeq(
+ M_SPC_CFG_ENABLE | /* enable counting */
+ M_SPC_CFG_CLEAR | /* clear all counters */
+ V_SPC_CFG_SRC1(1), /* counter 1 counts cycles */
+ IOADDR(A_BCM1480_SCD_PERF_CNT_CFG1));
+#else
+ __raw_writeq((scdperfcnt & ~M_SPC_CFG_SRC1) |
+ /* keep counters 0,2,3 as is */
+ M_SPC_CFG_ENABLE | /* enable counting */
+ M_SPC_CFG_CLEAR | /* clear all counters */
+ V_SPC_CFG_SRC1(1), /* counter 1 counts cycles */
+ IOADDR(A_SCD_PERF_CNT_CFG));
+#endif
+ __raw_writeq(next, IOADDR(A_SCD_PERF_CNT_1));
+ /* Reset the trace buffer */
+ __raw_writeq(M_SCD_TRACE_CFG_RESET, IOADDR(A_SCD_TRACE_CFG));
+#if 0 && defined(M_SCD_TRACE_CFG_FORCECNT)
+ /* XXXKW may want to expose control to the data-collector */
+ tb_options |= M_SCD_TRACE_CFG_FORCECNT;
+#endif
+ __raw_writeq(tb_options, IOADDR(A_SCD_TRACE_CFG));
+ sbp.tb_armed = 1;
+}
+
+static irqreturn_t sbprof_tb_intr(int irq, void *dev_id)
+{
+ int i;
+
+ pr_debug(DEVNAME ": tb_intr\n");
+
+ if (sbp.next_tb_sample < MAX_TB_SAMPLES) {
+ /* XXX should use XKPHYS to make writes bypass L2 */
+ u64 *p = sbp.sbprof_tbbuf[sbp.next_tb_sample++];
+ /* Read out trace */
+ __raw_writeq(M_SCD_TRACE_CFG_START_READ,
+ IOADDR(A_SCD_TRACE_CFG));
+ __asm__ __volatile__ ("sync" : : : "memory");
+ /* Loop runs backwards because bundles are read out in reverse order */
+ for (i = 256 * 6; i > 0; i -= 6) {
+ /* Subscripts decrease to put bundle in the order */
+ /* t0 lo, t0 hi, t1 lo, t1 hi, t2 lo, t2 hi */
+ p[i - 1] = __raw_readq(IOADDR(A_SCD_TRACE_READ));
+ /* read t2 hi */
+ p[i - 2] = __raw_readq(IOADDR(A_SCD_TRACE_READ));
+ /* read t2 lo */
+ p[i - 3] = __raw_readq(IOADDR(A_SCD_TRACE_READ));
+ /* read t1 hi */
+ p[i - 4] = __raw_readq(IOADDR(A_SCD_TRACE_READ));
+ /* read t1 lo */
+ p[i - 5] = __raw_readq(IOADDR(A_SCD_TRACE_READ));
+ /* read t0 hi */
+ p[i - 6] = __raw_readq(IOADDR(A_SCD_TRACE_READ));
+ /* read t0 lo */
+ }
+ if (!sbp.tb_enable) {
+ pr_debug(DEVNAME ": tb_intr shutdown\n");
+ __raw_writeq(M_SCD_TRACE_CFG_RESET,
+ IOADDR(A_SCD_TRACE_CFG));
+ sbp.tb_armed = 0;
+ wake_up_interruptible(&sbp.tb_sync);
+ } else {
+ /* knock down current interrupt and get another one later */
+ arm_tb();
+ }
+ } else {
+ /* No more trace buffer samples */
+ pr_debug(DEVNAME ": tb_intr full\n");
+ __raw_writeq(M_SCD_TRACE_CFG_RESET, IOADDR(A_SCD_TRACE_CFG));
+ sbp.tb_armed = 0;
+ if (!sbp.tb_enable)
+ wake_up_interruptible(&sbp.tb_sync);
+ wake_up_interruptible(&sbp.tb_read);
+ }
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t sbprof_pc_intr(int irq, void *dev_id)
+{
+ printk(DEVNAME ": unexpected pc_intr");
+ return IRQ_NONE;
+}
+
+/*
+ * Requires: Already called zclk_timer_init with a value that won't
+ * saturate 40 bits. No subsequent use of SCD performance counters
+ * or trace buffer.
+ */
+
+static int sbprof_zbprof_start(struct file *filp)
+{
+ u64 scdperfcnt;
+ int err;
+
+ if (xchg(&sbp.tb_enable, 1))
+ return -EBUSY;
+
+ pr_debug(DEVNAME ": starting\n");
+
+ sbp.next_tb_sample = 0;
+ filp->f_pos = 0;
+
+ err = request_irq(K_INT_TRACE_FREEZE, sbprof_tb_intr, 0,
+ DEVNAME " trace freeze", &sbp);
+ if (err)
+ return -EBUSY;
+
+ /* Make sure there isn't a perf-cnt interrupt waiting */
+ scdperfcnt = __raw_readq(IOADDR(A_SCD_PERF_CNT_CFG));
+ /* Disable and clear counters, override SRC_1 */
+ __raw_writeq((scdperfcnt & ~(M_SPC_CFG_SRC1 | M_SPC_CFG_ENABLE)) |
+ M_SPC_CFG_ENABLE | M_SPC_CFG_CLEAR | V_SPC_CFG_SRC1(1),
+ IOADDR(A_SCD_PERF_CNT_CFG));
+
+ /*
+ * We grab this interrupt to prevent others from trying to use
+ * it, even though we don't want to service the interrupts
+ * (they only feed into the trace-on-interrupt mechanism)
+ */
+ if (request_irq(K_INT_PERF_CNT, sbprof_pc_intr, 0, DEVNAME " scd perfcnt", &sbp)) {
+ free_irq(K_INT_TRACE_FREEZE, &sbp);
+ return -EBUSY;
+ }
+
+ /*
+ * I need the core to mask these, but the interrupt mapper to
+ * pass them through. I am exploiting my knowledge that
+ * cp0_status masks out IP[5]. krw
+ */
+#ifdef CONFIG_SIBYTE_BCM1x80
+ __raw_writeq(K_BCM1480_INT_MAP_I3,
+ IOADDR(A_BCM1480_IMR_REGISTER(0, R_BCM1480_IMR_INTERRUPT_MAP_BASE_L) +
+ ((K_BCM1480_INT_PERF_CNT & 0x3f) << 3)));
+#else
+ __raw_writeq(K_INT_MAP_I3,
+ IOADDR(A_IMR_REGISTER(0, R_IMR_INTERRUPT_MAP_BASE) +
+ (K_INT_PERF_CNT << 3)));
+#endif
+
+ /* Initialize address traps */
+ __raw_writeq(0, IOADDR(A_ADDR_TRAP_UP_0));
+ __raw_writeq(0, IOADDR(A_ADDR_TRAP_UP_1));
+ __raw_writeq(0, IOADDR(A_ADDR_TRAP_UP_2));
+ __raw_writeq(0, IOADDR(A_ADDR_TRAP_UP_3));
+
+ __raw_writeq(0, IOADDR(A_ADDR_TRAP_DOWN_0));
+ __raw_writeq(0, IOADDR(A_ADDR_TRAP_DOWN_1));
+ __raw_writeq(0, IOADDR(A_ADDR_TRAP_DOWN_2));
+ __raw_writeq(0, IOADDR(A_ADDR_TRAP_DOWN_3));
+
+ __raw_writeq(0, IOADDR(A_ADDR_TRAP_CFG_0));
+ __raw_writeq(0, IOADDR(A_ADDR_TRAP_CFG_1));
+ __raw_writeq(0, IOADDR(A_ADDR_TRAP_CFG_2));
+ __raw_writeq(0, IOADDR(A_ADDR_TRAP_CFG_3));
+
+ /* Initialize Trace Event 0-7 */
+ /* when interrupt */
+ __raw_writeq(M_SCD_TREVT_INTERRUPT, IOADDR(A_SCD_TRACE_EVENT_0));
+ __raw_writeq(0, IOADDR(A_SCD_TRACE_EVENT_1));
+ __raw_writeq(0, IOADDR(A_SCD_TRACE_EVENT_2));
+ __raw_writeq(0, IOADDR(A_SCD_TRACE_EVENT_3));
+ __raw_writeq(0, IOADDR(A_SCD_TRACE_EVENT_4));
+ __raw_writeq(0, IOADDR(A_SCD_TRACE_EVENT_5));
+ __raw_writeq(0, IOADDR(A_SCD_TRACE_EVENT_6));
+ __raw_writeq(0, IOADDR(A_SCD_TRACE_EVENT_7));
+
+ /* Initialize Trace Sequence 0-7 */
+ /* Start on event 0 (interrupt) */
+ __raw_writeq(V_SCD_TRSEQ_FUNC_START | 0x0fff,
+ IOADDR(A_SCD_TRACE_SEQUENCE_0));
+ /* dsamp when d used | asamp when a used */
+ __raw_writeq(M_SCD_TRSEQ_ASAMPLE | M_SCD_TRSEQ_DSAMPLE |
+ K_SCD_TRSEQ_TRIGGER_ALL,
+ IOADDR(A_SCD_TRACE_SEQUENCE_1));
+ __raw_writeq(0, IOADDR(A_SCD_TRACE_SEQUENCE_2));
+ __raw_writeq(0, IOADDR(A_SCD_TRACE_SEQUENCE_3));
+ __raw_writeq(0, IOADDR(A_SCD_TRACE_SEQUENCE_4));
+ __raw_writeq(0, IOADDR(A_SCD_TRACE_SEQUENCE_5));
+ __raw_writeq(0, IOADDR(A_SCD_TRACE_SEQUENCE_6));
+ __raw_writeq(0, IOADDR(A_SCD_TRACE_SEQUENCE_7));
+
+ /* Now indicate the PERF_CNT interrupt as a trace-relevant interrupt */
+#ifdef CONFIG_SIBYTE_BCM1x80
+ __raw_writeq(1ULL << (K_BCM1480_INT_PERF_CNT & 0x3f),
+ IOADDR(A_BCM1480_IMR_REGISTER(0, R_BCM1480_IMR_INTERRUPT_TRACE_L)));
+#else
+ __raw_writeq(1ULL << K_INT_PERF_CNT,
+ IOADDR(A_IMR_REGISTER(0, R_IMR_INTERRUPT_TRACE)));
+#endif
+ arm_tb();
+
+ pr_debug(DEVNAME ": done starting\n");
+
+ return 0;
+}
+
+static int sbprof_zbprof_stop(void)
+{
+ int err = 0;
+
+ pr_debug(DEVNAME ": stopping\n");
+
+ if (sbp.tb_enable) {
+ /*
+ * XXXKW there is a window here where the intr handler may run,
+ * see the disable, and do the wake_up before this sleep
+ * happens.
+ */
+ pr_debug(DEVNAME ": wait for disarm\n");
+ err = wait_event_interruptible(sbp.tb_sync, !sbp.tb_armed);
+ pr_debug(DEVNAME ": disarm complete, stat %d\n", err);
+
+ if (err)
+ return err;
+
+ sbp.tb_enable = 0;
+ free_irq(K_INT_TRACE_FREEZE, &sbp);
+ free_irq(K_INT_PERF_CNT, &sbp);
+ }
+
+ pr_debug(DEVNAME ": done stopping\n");
+
+ return err;
+}
+
+static int sbprof_tb_open(struct inode *inode, struct file *filp)
+{
+ int minor;
+
+ minor = iminor(inode);
+ if (minor != 0)
+ return -ENODEV;
+
+ if (xchg(&sbp.open, SB_OPENING) != SB_CLOSED)
+ return -EBUSY;
+
+ memset(&sbp, 0, sizeof(struct sbprof_tb));
+ sbp.sbprof_tbbuf = vzalloc(MAX_TBSAMPLE_BYTES);
+ if (!sbp.sbprof_tbbuf) {
+ sbp.open = SB_CLOSED;
+ wmb();
+ return -ENOMEM;
+ }
+
+ init_waitqueue_head(&sbp.tb_sync);
+ init_waitqueue_head(&sbp.tb_read);
+ mutex_init(&sbp.lock);
+
+ sbp.open = SB_OPEN;
+ wmb();
+
+ return 0;
+}
+
+static int sbprof_tb_release(struct inode *inode, struct file *filp)
+{
+ int minor;
+
+ minor = iminor(inode);
+ if (minor != 0 || sbp.open != SB_CLOSED)
+ return -ENODEV;
+
+ mutex_lock(&sbp.lock);
+
+ if (sbp.tb_armed || sbp.tb_enable)
+ sbprof_zbprof_stop();
+
+ vfree(sbp.sbprof_tbbuf);
+ sbp.open = SB_CLOSED;
+ wmb();
+
+ mutex_unlock(&sbp.lock);
+
+ return 0;
+}
+
+static ssize_t sbprof_tb_read(struct file *filp, char __user *buf,
+ size_t size, loff_t *offp)
+{
+ int cur_sample, sample_off, cur_count, sample_left;
+ char *src;
+ int count = 0;
+ char __user *dest = buf;
+ long cur_off = *offp;
+
+ if (!access_ok(buf, size))
+ return -EFAULT;
+
+ mutex_lock(&sbp.lock);
+
+ count = 0;
+ cur_sample = cur_off / TB_SAMPLE_SIZE;
+ sample_off = cur_off % TB_SAMPLE_SIZE;
+ sample_left = TB_SAMPLE_SIZE - sample_off;
+
+ while (size && (cur_sample < sbp.next_tb_sample)) {
+ int err;
+
+ cur_count = size < sample_left ? size : sample_left;
+ src = (char *)(((long)sbp.sbprof_tbbuf[cur_sample])+sample_off);
+ err = __copy_to_user(dest, src, cur_count);
+ if (err) {
+ *offp = cur_off + cur_count - err;
+ mutex_unlock(&sbp.lock);
+ return err;
+ }
+ pr_debug(DEVNAME ": read from sample %d, %d bytes\n",
+ cur_sample, cur_count);
+ size -= cur_count;
+ sample_left -= cur_count;
+ if (!sample_left) {
+ cur_sample++;
+ sample_off = 0;
+ sample_left = TB_SAMPLE_SIZE;
+ } else {
+ sample_off += cur_count;
+ }
+ cur_off += cur_count;
+ dest += cur_count;
+ count += cur_count;
+ }
+ *offp = cur_off;
+ mutex_unlock(&sbp.lock);
+
+ return count;
+}
+
+static long sbprof_tb_ioctl(struct file *filp,
+ unsigned int command,
+ unsigned long arg)
+{
+ int err = 0;
+
+ switch (command) {
+ case SBPROF_ZBSTART:
+ mutex_lock(&sbp.lock);
+ err = sbprof_zbprof_start(filp);
+ mutex_unlock(&sbp.lock);
+ break;
+
+ case SBPROF_ZBSTOP:
+ mutex_lock(&sbp.lock);
+ err = sbprof_zbprof_stop();
+ mutex_unlock(&sbp.lock);
+ break;
+
+ case SBPROF_ZBWAITFULL: {
+ err = wait_event_interruptible(sbp.tb_read, TB_FULL);
+ if (err)
+ break;
+
+ err = put_user(TB_FULL, (int __user *) arg);
+ break;
+ }
+
+ default:
+ err = -EINVAL;
+ break;
+ }
+
+ return err;
+}
+
+static const struct file_operations sbprof_tb_fops = {
+ .owner = THIS_MODULE,
+ .open = sbprof_tb_open,
+ .release = sbprof_tb_release,
+ .read = sbprof_tb_read,
+ .unlocked_ioctl = sbprof_tb_ioctl,
+ .compat_ioctl = sbprof_tb_ioctl,
+ .mmap = NULL,
+ .llseek = default_llseek,
+};
+
+static struct class *tb_class;
+static struct device *tb_dev;
+
+static int __init sbprof_tb_init(void)
+{
+ struct device *dev;
+ struct class *tbc;
+ int err;
+
+ if (register_chrdev(SBPROF_TB_MAJOR, DEVNAME, &sbprof_tb_fops)) {
+ printk(KERN_WARNING DEVNAME ": initialization failed (dev %d)\n",
+ SBPROF_TB_MAJOR);
+ return -EIO;
+ }
+
+ tbc = class_create("sb_tracebuffer");
+ if (IS_ERR(tbc)) {
+ err = PTR_ERR(tbc);
+ goto out_chrdev;
+ }
+
+ tb_class = tbc;
+
+ dev = device_create(tbc, NULL, MKDEV(SBPROF_TB_MAJOR, 0), NULL, "tb");
+ if (IS_ERR(dev)) {
+ err = PTR_ERR(dev);
+ goto out_class;
+ }
+ tb_dev = dev;
+
+ sbp.open = SB_CLOSED;
+ wmb();
+ tb_period = zbbus_mhz * 10000LL;
+ pr_info(DEVNAME ": initialized - tb_period = %lld\n",
+ (long long) tb_period);
+ return 0;
+
+out_class:
+ class_destroy(tb_class);
+out_chrdev:
+ unregister_chrdev(SBPROF_TB_MAJOR, DEVNAME);
+
+ return err;
+}
+
+static void __exit sbprof_tb_cleanup(void)
+{
+ device_destroy(tb_class, MKDEV(SBPROF_TB_MAJOR, 0));
+ unregister_chrdev(SBPROF_TB_MAJOR, DEVNAME);
+ class_destroy(tb_class);
+}
+
+module_init(sbprof_tb_init);
+module_exit(sbprof_tb_cleanup);
+
+MODULE_ALIAS_CHARDEV_MAJOR(SBPROF_TB_MAJOR);
+MODULE_AUTHOR("Ralf Baechle <ralf@linux-mips.org>");
+MODULE_LICENSE("GPL");
diff --git a/arch/mips/sibyte/sb1250/Makefile b/arch/mips/sibyte/sb1250/Makefile
new file mode 100644
index 0000000000..cf1327d3fc
--- /dev/null
+++ b/arch/mips/sibyte/sb1250/Makefile
@@ -0,0 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0-only
+obj-y := setup.o irq.o time.o
+
+obj-$(CONFIG_SMP) += smp.o
diff --git a/arch/mips/sibyte/sb1250/irq.c b/arch/mips/sibyte/sb1250/irq.c
new file mode 100644
index 0000000000..2f08ad267a
--- /dev/null
+++ b/arch/mips/sibyte/sb1250/irq.c
@@ -0,0 +1,318 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2000, 2001, 2002, 2003 Broadcom Corporation
+ */
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/linkage.h>
+#include <linux/interrupt.h>
+#include <linux/spinlock.h>
+#include <linux/smp.h>
+#include <linux/mm.h>
+#include <linux/kernel_stat.h>
+
+#include <asm/errno.h>
+#include <asm/signal.h>
+#include <asm/time.h>
+#include <asm/io.h>
+
+#include <asm/sibyte/sb1250_regs.h>
+#include <asm/sibyte/sb1250_int.h>
+#include <asm/sibyte/sb1250_uart.h>
+#include <asm/sibyte/sb1250_scd.h>
+#include <asm/sibyte/sb1250.h>
+
+/*
+ * These are the routines that handle all the low level interrupt stuff.
+ * Actions handled here are: initialization of the interrupt map, requesting of
+ * interrupt lines by handlers, dispatching if interrupts to handlers, probing
+ * for interrupt lines
+ */
+
+#ifdef CONFIG_SIBYTE_HAS_LDT
+extern unsigned long ldt_eoi_space;
+#endif
+
+/* Store the CPU id (not the logical number) */
+int sb1250_irq_owner[SB1250_NR_IRQS];
+
+static DEFINE_RAW_SPINLOCK(sb1250_imr_lock);
+
+void sb1250_mask_irq(int cpu, int irq)
+{
+ unsigned long flags;
+ u64 cur_ints;
+
+ raw_spin_lock_irqsave(&sb1250_imr_lock, flags);
+ cur_ints = ____raw_readq(IOADDR(A_IMR_MAPPER(cpu) +
+ R_IMR_INTERRUPT_MASK));
+ cur_ints |= (((u64) 1) << irq);
+ ____raw_writeq(cur_ints, IOADDR(A_IMR_MAPPER(cpu) +
+ R_IMR_INTERRUPT_MASK));
+ raw_spin_unlock_irqrestore(&sb1250_imr_lock, flags);
+}
+
+void sb1250_unmask_irq(int cpu, int irq)
+{
+ unsigned long flags;
+ u64 cur_ints;
+
+ raw_spin_lock_irqsave(&sb1250_imr_lock, flags);
+ cur_ints = ____raw_readq(IOADDR(A_IMR_MAPPER(cpu) +
+ R_IMR_INTERRUPT_MASK));
+ cur_ints &= ~(((u64) 1) << irq);
+ ____raw_writeq(cur_ints, IOADDR(A_IMR_MAPPER(cpu) +
+ R_IMR_INTERRUPT_MASK));
+ raw_spin_unlock_irqrestore(&sb1250_imr_lock, flags);
+}
+
+#ifdef CONFIG_SMP
+static int sb1250_set_affinity(struct irq_data *d, const struct cpumask *mask,
+ bool force)
+{
+ int i = 0, old_cpu, cpu, int_on;
+ unsigned int irq = d->irq;
+ u64 cur_ints;
+ unsigned long flags;
+
+ i = cpumask_first_and(mask, cpu_online_mask);
+
+ /* Convert logical CPU to physical CPU */
+ cpu = cpu_logical_map(i);
+
+ /* Protect against other affinity changers and IMR manipulation */
+ raw_spin_lock_irqsave(&sb1250_imr_lock, flags);
+
+ /* Swizzle each CPU's IMR (but leave the IP selection alone) */
+ old_cpu = sb1250_irq_owner[irq];
+ cur_ints = ____raw_readq(IOADDR(A_IMR_MAPPER(old_cpu) +
+ R_IMR_INTERRUPT_MASK));
+ int_on = !(cur_ints & (((u64) 1) << irq));
+ if (int_on) {
+ /* If it was on, mask it */
+ cur_ints |= (((u64) 1) << irq);
+ ____raw_writeq(cur_ints, IOADDR(A_IMR_MAPPER(old_cpu) +
+ R_IMR_INTERRUPT_MASK));
+ }
+ sb1250_irq_owner[irq] = cpu;
+ if (int_on) {
+ /* unmask for the new CPU */
+ cur_ints = ____raw_readq(IOADDR(A_IMR_MAPPER(cpu) +
+ R_IMR_INTERRUPT_MASK));
+ cur_ints &= ~(((u64) 1) << irq);
+ ____raw_writeq(cur_ints, IOADDR(A_IMR_MAPPER(cpu) +
+ R_IMR_INTERRUPT_MASK));
+ }
+ raw_spin_unlock_irqrestore(&sb1250_imr_lock, flags);
+
+ return 0;
+}
+#endif
+
+static void disable_sb1250_irq(struct irq_data *d)
+{
+ unsigned int irq = d->irq;
+
+ sb1250_mask_irq(sb1250_irq_owner[irq], irq);
+}
+
+static void enable_sb1250_irq(struct irq_data *d)
+{
+ unsigned int irq = d->irq;
+
+ sb1250_unmask_irq(sb1250_irq_owner[irq], irq);
+}
+
+
+static void ack_sb1250_irq(struct irq_data *d)
+{
+ unsigned int irq = d->irq;
+#ifdef CONFIG_SIBYTE_HAS_LDT
+ u64 pending;
+
+ /*
+ * If the interrupt was an HT interrupt, now is the time to
+ * clear it. NOTE: we assume the HT bridge was set up to
+ * deliver the interrupts to all CPUs (which makes affinity
+ * changing easier for us)
+ */
+ pending = __raw_readq(IOADDR(A_IMR_REGISTER(sb1250_irq_owner[irq],
+ R_IMR_LDT_INTERRUPT)));
+ pending &= ((u64)1 << (irq));
+ if (pending) {
+ int i;
+ for (i=0; i<NR_CPUS; i++) {
+ int cpu;
+#ifdef CONFIG_SMP
+ cpu = cpu_logical_map(i);
+#else
+ cpu = i;
+#endif
+ /*
+ * Clear for all CPUs so an affinity switch
+ * doesn't find an old status
+ */
+ __raw_writeq(pending,
+ IOADDR(A_IMR_REGISTER(cpu,
+ R_IMR_LDT_INTERRUPT_CLR)));
+ }
+
+ /*
+ * Generate EOI. For Pass 1 parts, EOI is a nop. For
+ * Pass 2, the LDT world may be edge-triggered, but
+ * this EOI shouldn't hurt. If they are
+ * level-sensitive, the EOI is required.
+ */
+ *(uint32_t *)(ldt_eoi_space+(irq<<16)+(7<<2)) = 0;
+ }
+#endif
+ sb1250_mask_irq(sb1250_irq_owner[irq], irq);
+}
+
+static struct irq_chip sb1250_irq_type = {
+ .name = "SB1250-IMR",
+ .irq_mask_ack = ack_sb1250_irq,
+ .irq_unmask = enable_sb1250_irq,
+ .irq_mask = disable_sb1250_irq,
+#ifdef CONFIG_SMP
+ .irq_set_affinity = sb1250_set_affinity
+#endif
+};
+
+void __init init_sb1250_irqs(void)
+{
+ int i;
+
+ for (i = 0; i < SB1250_NR_IRQS; i++) {
+ irq_set_chip_and_handler(i, &sb1250_irq_type,
+ handle_level_irq);
+ sb1250_irq_owner[i] = 0;
+ }
+}
+
+
+/*
+ * arch_init_irq is called early in the boot sequence from init/main.c via
+ * init_IRQ. It is responsible for setting up the interrupt mapper and
+ * installing the handler that will be responsible for dispatching interrupts
+ * to the "right" place.
+ */
+/*
+ * For now, map all interrupts to IP[2]. We could save
+ * some cycles by parceling out system interrupts to different
+ * IP lines, but keep it simple for bringup. We'll also direct
+ * all interrupts to a single CPU; we should probably route
+ * PCI and LDT to one cpu and everything else to the other
+ * to balance the load a bit.
+ *
+ * On the second cpu, everything is set to IP5, which is
+ * ignored, EXCEPT the mailbox interrupt. That one is
+ * set to IP[2] so it is handled. This is needed so we
+ * can do cross-cpu function calls, as required by SMP
+ */
+
+#define IMR_IP2_VAL K_INT_MAP_I0
+#define IMR_IP3_VAL K_INT_MAP_I1
+#define IMR_IP4_VAL K_INT_MAP_I2
+#define IMR_IP5_VAL K_INT_MAP_I3
+#define IMR_IP6_VAL K_INT_MAP_I4
+
+void __init arch_init_irq(void)
+{
+
+ unsigned int i;
+ u64 tmp;
+ unsigned int imask = STATUSF_IP4 | STATUSF_IP3 | STATUSF_IP2 |
+ STATUSF_IP1 | STATUSF_IP0;
+
+ /* Default everything to IP2 */
+ for (i = 0; i < SB1250_NR_IRQS; i++) { /* was I0 */
+ __raw_writeq(IMR_IP2_VAL,
+ IOADDR(A_IMR_REGISTER(0,
+ R_IMR_INTERRUPT_MAP_BASE) +
+ (i << 3)));
+ __raw_writeq(IMR_IP2_VAL,
+ IOADDR(A_IMR_REGISTER(1,
+ R_IMR_INTERRUPT_MAP_BASE) +
+ (i << 3)));
+ }
+
+ init_sb1250_irqs();
+
+ /*
+ * Map the high 16 bits of the mailbox registers to IP[3], for
+ * inter-cpu messages
+ */
+ /* Was I1 */
+ __raw_writeq(IMR_IP3_VAL,
+ IOADDR(A_IMR_REGISTER(0, R_IMR_INTERRUPT_MAP_BASE) +
+ (K_INT_MBOX_0 << 3)));
+ __raw_writeq(IMR_IP3_VAL,
+ IOADDR(A_IMR_REGISTER(1, R_IMR_INTERRUPT_MAP_BASE) +
+ (K_INT_MBOX_0 << 3)));
+
+ /* Clear the mailboxes. The firmware may leave them dirty */
+ __raw_writeq(0xffffffffffffffffULL,
+ IOADDR(A_IMR_REGISTER(0, R_IMR_MAILBOX_CLR_CPU)));
+ __raw_writeq(0xffffffffffffffffULL,
+ IOADDR(A_IMR_REGISTER(1, R_IMR_MAILBOX_CLR_CPU)));
+
+ /* Mask everything except the mailbox registers for both cpus */
+ tmp = ~((u64) 0) ^ (((u64) 1) << K_INT_MBOX_0);
+ __raw_writeq(tmp, IOADDR(A_IMR_REGISTER(0, R_IMR_INTERRUPT_MASK)));
+ __raw_writeq(tmp, IOADDR(A_IMR_REGISTER(1, R_IMR_INTERRUPT_MASK)));
+
+ /* Enable necessary IPs, disable the rest */
+ change_c0_status(ST0_IM, imask);
+}
+
+extern void sb1250_mailbox_interrupt(void);
+
+static inline void dispatch_ip2(void)
+{
+ unsigned int cpu = smp_processor_id();
+ unsigned long long mask;
+
+ /*
+ * Default...we've hit an IP[2] interrupt, which means we've got to
+ * check the 1250 interrupt registers to figure out what to do. Need
+ * to detect which CPU we're on, now that smp_affinity is supported.
+ */
+ mask = __raw_readq(IOADDR(A_IMR_REGISTER(cpu,
+ R_IMR_INTERRUPT_STATUS_BASE)));
+ if (mask)
+ do_IRQ(fls64(mask) - 1);
+}
+
+asmlinkage void plat_irq_dispatch(void)
+{
+ unsigned int cpu = smp_processor_id();
+ unsigned int pending;
+
+ /*
+ * What a pain. We have to be really careful saving the upper 32 bits
+ * of any * register across function calls if we don't want them
+ * trashed--since were running in -o32, the calling routing never saves
+ * the full 64 bits of a register across a function call. Being the
+ * interrupt handler, we're guaranteed that interrupts are disabled
+ * during this code so we don't have to worry about random interrupts
+ * blasting the high 32 bits.
+ */
+
+ pending = read_c0_cause() & read_c0_status() & ST0_IM;
+
+ if (pending & CAUSEF_IP7) /* CPU performance counter interrupt */
+ do_IRQ(MIPS_CPU_IRQ_BASE + 7);
+ else if (pending & CAUSEF_IP4)
+ do_IRQ(K_INT_TIMER_0 + cpu); /* sb1250_timer_interrupt() */
+
+#ifdef CONFIG_SMP
+ else if (pending & CAUSEF_IP3)
+ sb1250_mailbox_interrupt();
+#endif
+
+ else if (pending & CAUSEF_IP2)
+ dispatch_ip2();
+ else
+ spurious_interrupt();
+}
diff --git a/arch/mips/sibyte/sb1250/setup.c b/arch/mips/sibyte/sb1250/setup.c
new file mode 100644
index 0000000000..644b190388
--- /dev/null
+++ b/arch/mips/sibyte/sb1250/setup.c
@@ -0,0 +1,234 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2000, 2001, 2002, 2003 Broadcom Corporation
+ */
+#include <linux/export.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/reboot.h>
+#include <linux/string.h>
+
+#include <asm/bootinfo.h>
+#include <asm/cpu.h>
+#include <asm/mipsregs.h>
+#include <asm/io.h>
+#include <asm/sibyte/sb1250.h>
+#include <asm/sibyte/sb1250_regs.h>
+#include <asm/sibyte/sb1250_scd.h>
+
+unsigned int sb1_pass;
+unsigned int soc_pass;
+unsigned int soc_type;
+EXPORT_SYMBOL(soc_type);
+unsigned int periph_rev;
+EXPORT_SYMBOL_GPL(periph_rev);
+unsigned int zbbus_mhz;
+EXPORT_SYMBOL(zbbus_mhz);
+
+static char *soc_str;
+static char *pass_str;
+static unsigned int war_pass; /* XXXKW don't overload PASS defines? */
+
+static int __init setup_bcm1250(void)
+{
+ int ret = 0;
+
+ switch (soc_pass) {
+ case K_SYS_REVISION_BCM1250_PASS1:
+ periph_rev = 1;
+ pass_str = "Pass 1";
+ break;
+ case K_SYS_REVISION_BCM1250_A10:
+ periph_rev = 2;
+ pass_str = "A8/A10";
+ /* XXXKW different war_pass? */
+ war_pass = K_SYS_REVISION_BCM1250_PASS2;
+ break;
+ case K_SYS_REVISION_BCM1250_PASS2_2:
+ periph_rev = 2;
+ pass_str = "B1";
+ break;
+ case K_SYS_REVISION_BCM1250_B2:
+ periph_rev = 2;
+ pass_str = "B2";
+ war_pass = K_SYS_REVISION_BCM1250_PASS2_2;
+ break;
+ case K_SYS_REVISION_BCM1250_PASS3:
+ periph_rev = 3;
+ pass_str = "C0";
+ break;
+ case K_SYS_REVISION_BCM1250_C1:
+ periph_rev = 3;
+ pass_str = "C1";
+ break;
+ default:
+ if (soc_pass < K_SYS_REVISION_BCM1250_PASS2_2) {
+ periph_rev = 2;
+ pass_str = "A0-A6";
+ war_pass = K_SYS_REVISION_BCM1250_PASS2;
+ } else {
+ printk("Unknown BCM1250 rev %x\n", soc_pass);
+ ret = 1;
+ }
+ break;
+ }
+
+ return ret;
+}
+
+int sb1250_m3_workaround_needed(void)
+{
+ switch (soc_type) {
+ case K_SYS_SOC_TYPE_BCM1250:
+ case K_SYS_SOC_TYPE_BCM1250_ALT:
+ case K_SYS_SOC_TYPE_BCM1250_ALT2:
+ case K_SYS_SOC_TYPE_BCM1125:
+ case K_SYS_SOC_TYPE_BCM1125H:
+ return soc_pass < K_SYS_REVISION_BCM1250_C0;
+
+ default:
+ return 0;
+ }
+}
+
+static int __init setup_bcm112x(void)
+{
+ int ret = 0;
+
+ switch (soc_pass) {
+ case 0:
+ /* Early build didn't have revid set */
+ periph_rev = 3;
+ pass_str = "A1";
+ war_pass = K_SYS_REVISION_BCM112x_A1;
+ break;
+ case K_SYS_REVISION_BCM112x_A1:
+ periph_rev = 3;
+ pass_str = "A1";
+ break;
+ case K_SYS_REVISION_BCM112x_A2:
+ periph_rev = 3;
+ pass_str = "A2";
+ break;
+ case K_SYS_REVISION_BCM112x_A3:
+ periph_rev = 3;
+ pass_str = "A3";
+ break;
+ case K_SYS_REVISION_BCM112x_A4:
+ periph_rev = 3;
+ pass_str = "A4";
+ break;
+ case K_SYS_REVISION_BCM112x_B0:
+ periph_rev = 3;
+ pass_str = "B0";
+ break;
+ default:
+ printk("Unknown %s rev %x\n", soc_str, soc_pass);
+ ret = 1;
+ }
+
+ return ret;
+}
+
+/* Setup code likely to be common to all SiByte platforms */
+
+static int __init sys_rev_decode(void)
+{
+ int ret = 0;
+
+ war_pass = soc_pass;
+ switch (soc_type) {
+ case K_SYS_SOC_TYPE_BCM1250:
+ case K_SYS_SOC_TYPE_BCM1250_ALT:
+ case K_SYS_SOC_TYPE_BCM1250_ALT2:
+ soc_str = "BCM1250";
+ ret = setup_bcm1250();
+ break;
+ case K_SYS_SOC_TYPE_BCM1120:
+ soc_str = "BCM1120";
+ ret = setup_bcm112x();
+ break;
+ case K_SYS_SOC_TYPE_BCM1125:
+ soc_str = "BCM1125";
+ ret = setup_bcm112x();
+ break;
+ case K_SYS_SOC_TYPE_BCM1125H:
+ soc_str = "BCM1125H";
+ ret = setup_bcm112x();
+ break;
+ default:
+ printk("Unknown SOC type %x\n", soc_type);
+ ret = 1;
+ break;
+ }
+
+ return ret;
+}
+
+void __init sb1250_setup(void)
+{
+ uint64_t sys_rev;
+ int plldiv;
+ int bad_config = 0;
+
+ sb1_pass = read_c0_prid() & PRID_REV_MASK;
+ sys_rev = __raw_readq(IOADDR(A_SCD_SYSTEM_REVISION));
+ soc_type = SYS_SOC_TYPE(sys_rev);
+ soc_pass = G_SYS_REVISION(sys_rev);
+
+ if (sys_rev_decode()) {
+ printk("Restart after failure to identify SiByte chip\n");
+ machine_restart(NULL);
+ }
+
+ plldiv = G_SYS_PLL_DIV(__raw_readq(IOADDR(A_SCD_SYSTEM_CFG)));
+ zbbus_mhz = ((plldiv >> 1) * 50) + ((plldiv & 1) * 25);
+
+ printk("Broadcom SiByte %s %s @ %d MHz (SB1 rev %d)\n",
+ soc_str, pass_str, zbbus_mhz * 2, sb1_pass);
+ printk("Board type: %s\n", get_system_type());
+
+ switch (war_pass) {
+ case K_SYS_REVISION_BCM1250_PASS1:
+ printk("@@@@ This is a BCM1250 A0-A2 (Pass 1) board, "
+ "and the kernel doesn't have the proper "
+ "workarounds compiled in. @@@@\n");
+ bad_config = 1;
+ break;
+ case K_SYS_REVISION_BCM1250_PASS2:
+ /* Pass 2 - easiest as default for now - so many numbers */
+#if !defined(CONFIG_SB1_PASS_2_WORKAROUNDS) || \
+ !defined(CONFIG_SB1_PASS_2_1_WORKAROUNDS)
+ printk("@@@@ This is a BCM1250 A3-A10 board, and the "
+ "kernel doesn't have the proper workarounds "
+ "compiled in. @@@@\n");
+ bad_config = 1;
+#endif
+#ifdef CONFIG_CPU_HAS_PREFETCH
+ printk("@@@@ Prefetches may be enabled in this kernel, "
+ "but are buggy on this board. @@@@\n");
+ bad_config = 1;
+#endif
+ break;
+ case K_SYS_REVISION_BCM1250_PASS2_2:
+#ifndef CONFIG_SB1_PASS_2_WORKAROUNDS
+ printk("@@@@ This is a BCM1250 B1/B2. board, and the "
+ "kernel doesn't have the proper workarounds "
+ "compiled in. @@@@\n");
+ bad_config = 1;
+#endif
+#if defined(CONFIG_SB1_PASS_2_1_WORKAROUNDS) || \
+ !defined(CONFIG_CPU_HAS_PREFETCH)
+ printk("@@@@ This is a BCM1250 B1/B2, but the kernel is "
+ "conservatively configured for an 'A' stepping. "
+ "@@@@\n");
+#endif
+ break;
+ default:
+ break;
+ }
+ if (bad_config) {
+ printk("Invalid configuration for this chip.\n");
+ machine_restart(NULL);
+ }
+}
diff --git a/arch/mips/sibyte/sb1250/smp.c b/arch/mips/sibyte/sb1250/smp.c
new file mode 100644
index 0000000000..7a794234e3
--- /dev/null
+++ b/arch/mips/sibyte/sb1250/smp.c
@@ -0,0 +1,168 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2001, 2002, 2003 Broadcom Corporation
+ */
+
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/smp.h>
+#include <linux/kernel_stat.h>
+#include <linux/sched/task_stack.h>
+
+#include <asm/mmu_context.h>
+#include <asm/io.h>
+#include <asm/fw/cfe/cfe_api.h>
+#include <asm/sibyte/sb1250.h>
+#include <asm/sibyte/sb1250_regs.h>
+#include <asm/sibyte/sb1250_int.h>
+
+static void *mailbox_set_regs[] = {
+ IOADDR(A_IMR_CPU0_BASE + R_IMR_MAILBOX_SET_CPU),
+ IOADDR(A_IMR_CPU1_BASE + R_IMR_MAILBOX_SET_CPU)
+};
+
+static void *mailbox_clear_regs[] = {
+ IOADDR(A_IMR_CPU0_BASE + R_IMR_MAILBOX_CLR_CPU),
+ IOADDR(A_IMR_CPU1_BASE + R_IMR_MAILBOX_CLR_CPU)
+};
+
+static void *mailbox_regs[] = {
+ IOADDR(A_IMR_CPU0_BASE + R_IMR_MAILBOX_CPU),
+ IOADDR(A_IMR_CPU1_BASE + R_IMR_MAILBOX_CPU)
+};
+
+/*
+ * SMP init and finish on secondary CPUs
+ */
+void sb1250_smp_init(void)
+{
+ unsigned int imask = STATUSF_IP4 | STATUSF_IP3 | STATUSF_IP2 |
+ STATUSF_IP1 | STATUSF_IP0;
+
+ /* Set interrupt mask, but don't enable */
+ change_c0_status(ST0_IM, imask);
+}
+
+/*
+ * These are routines for dealing with the sb1250 smp capabilities
+ * independent of board/firmware
+ */
+
+/*
+ * Simple enough; everything is set up, so just poke the appropriate mailbox
+ * register, and we should be set
+ */
+static void sb1250_send_ipi_single(int cpu, unsigned int action)
+{
+ __raw_writeq((((u64)action) << 48), mailbox_set_regs[cpu]);
+}
+
+static inline void sb1250_send_ipi_mask(const struct cpumask *mask,
+ unsigned int action)
+{
+ unsigned int i;
+
+ for_each_cpu(i, mask)
+ sb1250_send_ipi_single(i, action);
+}
+
+/*
+ * Code to run on secondary just after probing the CPU
+ */
+static void sb1250_init_secondary(void)
+{
+ extern void sb1250_smp_init(void);
+
+ sb1250_smp_init();
+}
+
+/*
+ * Do any tidying up before marking online and running the idle
+ * loop
+ */
+static void sb1250_smp_finish(void)
+{
+ extern void sb1250_clockevent_init(void);
+
+ sb1250_clockevent_init();
+ local_irq_enable();
+}
+
+/*
+ * Setup the PC, SP, and GP of a secondary processor and start it
+ * running!
+ */
+static int sb1250_boot_secondary(int cpu, struct task_struct *idle)
+{
+ int retval;
+
+ retval = cfe_cpu_start(cpu_logical_map(cpu), &smp_bootstrap,
+ __KSTK_TOS(idle),
+ (unsigned long)task_thread_info(idle), 0);
+ if (retval != 0)
+ printk("cfe_start_cpu(%i) returned %i\n" , cpu, retval);
+ return retval;
+}
+
+/*
+ * Use CFE to find out how many CPUs are available, setting up
+ * cpu_possible_mask and the logical/physical mappings.
+ * XXXKW will the boot CPU ever not be physical 0?
+ *
+ * Common setup before any secondaries are started
+ */
+static void __init sb1250_smp_setup(void)
+{
+ int i, num;
+
+ init_cpu_possible(cpumask_of(0));
+ __cpu_number_map[0] = 0;
+ __cpu_logical_map[0] = 0;
+
+ for (i = 1, num = 0; i < NR_CPUS; i++) {
+ if (cfe_cpu_stop(i) == 0) {
+ set_cpu_possible(i, true);
+ __cpu_number_map[i] = ++num;
+ __cpu_logical_map[num] = i;
+ }
+ }
+ printk(KERN_INFO "Detected %i available secondary CPU(s)\n", num);
+}
+
+static void __init sb1250_prepare_cpus(unsigned int max_cpus)
+{
+}
+
+const struct plat_smp_ops sb_smp_ops = {
+ .send_ipi_single = sb1250_send_ipi_single,
+ .send_ipi_mask = sb1250_send_ipi_mask,
+ .init_secondary = sb1250_init_secondary,
+ .smp_finish = sb1250_smp_finish,
+ .boot_secondary = sb1250_boot_secondary,
+ .smp_setup = sb1250_smp_setup,
+ .prepare_cpus = sb1250_prepare_cpus,
+};
+
+void sb1250_mailbox_interrupt(void)
+{
+ int cpu = smp_processor_id();
+ int irq = K_INT_MBOX_0;
+ unsigned int action;
+
+ kstat_incr_irq_this_cpu(irq);
+ /* Load the mailbox register to figure out what we're supposed to do */
+ action = (____raw_readq(mailbox_regs[cpu]) >> 48) & 0xffff;
+
+ /* Clear the mailbox to clear the interrupt */
+ ____raw_writeq(((u64)action) << 48, mailbox_clear_regs[cpu]);
+
+ if (action & SMP_RESCHEDULE_YOURSELF)
+ scheduler_ipi();
+
+ if (action & SMP_CALL_FUNCTION) {
+ irq_enter();
+ generic_smp_call_function_interrupt();
+ irq_exit();
+ }
+}
diff --git a/arch/mips/sibyte/sb1250/time.c b/arch/mips/sibyte/sb1250/time.c
new file mode 100644
index 0000000000..8b63000a4d
--- /dev/null
+++ b/arch/mips/sibyte/sb1250/time.c
@@ -0,0 +1,14 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2000, 2001 Broadcom Corporation
+ */
+#include <linux/init.h>
+
+extern void sb1250_clocksource_init(void);
+extern void sb1250_clockevent_init(void);
+
+void __init plat_time_init(void)
+{
+ sb1250_clocksource_init();
+ sb1250_clockevent_init();
+}
diff --git a/arch/mips/sibyte/swarm/Makefile b/arch/mips/sibyte/swarm/Makefile
new file mode 100644
index 0000000000..96b41a28ff
--- /dev/null
+++ b/arch/mips/sibyte/swarm/Makefile
@@ -0,0 +1,5 @@
+# SPDX-License-Identifier: GPL-2.0-only
+obj-y := platform.o setup.o rtc_xicor1241.o \
+ rtc_m41t81.o
+
+obj-$(CONFIG_I2C_BOARDINFO) += swarm-i2c.o
diff --git a/arch/mips/sibyte/swarm/platform.c b/arch/mips/sibyte/swarm/platform.c
new file mode 100644
index 0000000000..339d77a0a0
--- /dev/null
+++ b/arch/mips/sibyte/swarm/platform.c
@@ -0,0 +1,140 @@
+// SPDX-License-Identifier: GPL-2.0
+#include <linux/err.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/platform_device.h>
+#include <linux/ata_platform.h>
+
+#include <asm/sibyte/board.h>
+#include <asm/sibyte/sb1250_genbus.h>
+#include <asm/sibyte/sb1250_regs.h>
+
+#if defined(CONFIG_SIBYTE_SWARM) || defined(CONFIG_SIBYTE_LITTLESUR)
+
+#define DRV_NAME "pata-swarm"
+
+#define SWARM_IDE_SHIFT 5
+#define SWARM_IDE_BASE 0x1f0
+#define SWARM_IDE_CTRL 0x3f6
+
+static struct resource swarm_pata_resource[] = {
+ {
+ .name = "Swarm GenBus IDE",
+ .flags = IORESOURCE_MEM,
+ }, {
+ .name = "Swarm GenBus IDE",
+ .flags = IORESOURCE_MEM,
+ }, {
+ .name = "Swarm GenBus IDE",
+ .flags = IORESOURCE_IRQ,
+ .start = K_INT_GB_IDE,
+ .end = K_INT_GB_IDE,
+ },
+};
+
+static struct pata_platform_info pata_platform_data = {
+ .ioport_shift = SWARM_IDE_SHIFT,
+};
+
+static struct platform_device swarm_pata_device = {
+ .name = "pata_platform",
+ .id = -1,
+ .resource = swarm_pata_resource,
+ .num_resources = ARRAY_SIZE(swarm_pata_resource),
+ .dev = {
+ .platform_data = &pata_platform_data,
+ .coherent_dma_mask = ~0, /* grumble */
+ },
+};
+
+static int __init swarm_pata_init(void)
+{
+ u8 __iomem *base;
+ phys_addr_t offset, size;
+ struct resource *r;
+
+ if (!SIBYTE_HAVE_IDE)
+ return -ENODEV;
+
+ base = ioremap(A_IO_EXT_BASE, 0x800);
+ offset = __raw_readq(base + R_IO_EXT_REG(R_IO_EXT_START_ADDR, IDE_CS));
+ size = __raw_readq(base + R_IO_EXT_REG(R_IO_EXT_MULT_SIZE, IDE_CS));
+ iounmap(base);
+
+ offset = G_IO_START_ADDR(offset) << S_IO_ADDRBASE;
+ size = (G_IO_MULT_SIZE(size) + 1) << S_IO_REGSIZE;
+ if (offset < A_PHYS_GENBUS || offset >= A_PHYS_GENBUS_END) {
+ pr_info(DRV_NAME ": PATA interface at GenBus disabled\n");
+
+ return -EBUSY;
+ }
+
+ pr_info(DRV_NAME ": PATA interface at GenBus slot %i\n", IDE_CS);
+
+ r = swarm_pata_resource;
+ r[0].start = offset + (SWARM_IDE_BASE << SWARM_IDE_SHIFT);
+ r[0].end = offset + ((SWARM_IDE_BASE + 8) << SWARM_IDE_SHIFT) - 1;
+ r[1].start = offset + (SWARM_IDE_CTRL << SWARM_IDE_SHIFT);
+ r[1].end = offset + ((SWARM_IDE_CTRL + 1) << SWARM_IDE_SHIFT) - 1;
+
+ return platform_device_register(&swarm_pata_device);
+}
+
+device_initcall(swarm_pata_init);
+
+#endif /* defined(CONFIG_SIBYTE_SWARM) || defined(CONFIG_SIBYTE_LITTLESUR) */
+
+#define sb1250_dev_struct(num) \
+ static struct resource sb1250_res##num = { \
+ .name = "SB1250 MAC " __stringify(num), \
+ .flags = IORESOURCE_MEM, \
+ .start = A_MAC_CHANNEL_BASE(num), \
+ .end = A_MAC_CHANNEL_BASE(num + 1) - 1, \
+ };\
+ static struct platform_device sb1250_dev##num = { \
+ .name = "sb1250-mac", \
+ .id = num, \
+ .resource = &sb1250_res##num, \
+ .num_resources = 1, \
+ }
+
+sb1250_dev_struct(0);
+sb1250_dev_struct(1);
+sb1250_dev_struct(2);
+sb1250_dev_struct(3);
+
+static struct platform_device *sb1250_devs[] __initdata = {
+ &sb1250_dev0,
+ &sb1250_dev1,
+ &sb1250_dev2,
+ &sb1250_dev3,
+};
+
+static int __init sb1250_device_init(void)
+{
+ int ret;
+
+ /* Set the number of available units based on the SOC type. */
+ switch (soc_type) {
+ case K_SYS_SOC_TYPE_BCM1250:
+ case K_SYS_SOC_TYPE_BCM1250_ALT:
+ ret = platform_add_devices(sb1250_devs, 3);
+ break;
+ case K_SYS_SOC_TYPE_BCM1120:
+ case K_SYS_SOC_TYPE_BCM1125:
+ case K_SYS_SOC_TYPE_BCM1125H:
+ case K_SYS_SOC_TYPE_BCM1250_ALT2: /* Hybrid */
+ ret = platform_add_devices(sb1250_devs, 2);
+ break;
+ case K_SYS_SOC_TYPE_BCM1x55:
+ case K_SYS_SOC_TYPE_BCM1x80:
+ ret = platform_add_devices(sb1250_devs, 4);
+ break;
+ default:
+ ret = -ENODEV;
+ break;
+ }
+ return ret;
+}
+device_initcall(sb1250_device_init);
diff --git a/arch/mips/sibyte/swarm/rtc_m41t81.c b/arch/mips/sibyte/swarm/rtc_m41t81.c
new file mode 100644
index 0000000000..afe1e3460b
--- /dev/null
+++ b/arch/mips/sibyte/swarm/rtc_m41t81.c
@@ -0,0 +1,228 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2000, 2001 Broadcom Corporation
+ *
+ * Copyright (C) 2002 MontaVista Software Inc.
+ * Author: jsun@mvista.com or jsun@junsun.net
+ */
+#include <linux/bcd.h>
+#include <linux/types.h>
+#include <linux/time.h>
+
+#include <asm/time.h>
+#include <asm/addrspace.h>
+#include <asm/io.h>
+
+#include <asm/sibyte/sb1250.h>
+#include <asm/sibyte/sb1250_regs.h>
+#include <asm/sibyte/sb1250_smbus.h>
+
+
+/* M41T81 definitions */
+
+/*
+ * Register bits
+ */
+
+#define M41T81REG_SC_ST 0x80 /* stop bit */
+#define M41T81REG_HR_CB 0x40 /* century bit */
+#define M41T81REG_HR_CEB 0x80 /* century enable bit */
+#define M41T81REG_CTL_S 0x20 /* sign bit */
+#define M41T81REG_CTL_FT 0x40 /* frequency test bit */
+#define M41T81REG_CTL_OUT 0x80 /* output level */
+#define M41T81REG_WD_RB0 0x01 /* watchdog resolution bit 0 */
+#define M41T81REG_WD_RB1 0x02 /* watchdog resolution bit 1 */
+#define M41T81REG_WD_BMB0 0x04 /* watchdog multiplier bit 0 */
+#define M41T81REG_WD_BMB1 0x08 /* watchdog multiplier bit 1 */
+#define M41T81REG_WD_BMB2 0x10 /* watchdog multiplier bit 2 */
+#define M41T81REG_WD_BMB3 0x20 /* watchdog multiplier bit 3 */
+#define M41T81REG_WD_BMB4 0x40 /* watchdog multiplier bit 4 */
+#define M41T81REG_AMO_ABE 0x20 /* alarm in "battery back-up mode" enable bit */
+#define M41T81REG_AMO_SQWE 0x40 /* square wave enable */
+#define M41T81REG_AMO_AFE 0x80 /* alarm flag enable flag */
+#define M41T81REG_ADT_RPT5 0x40 /* alarm repeat mode bit 5 */
+#define M41T81REG_ADT_RPT4 0x80 /* alarm repeat mode bit 4 */
+#define M41T81REG_AHR_RPT3 0x80 /* alarm repeat mode bit 3 */
+#define M41T81REG_AHR_HT 0x40 /* halt update bit */
+#define M41T81REG_AMN_RPT2 0x80 /* alarm repeat mode bit 2 */
+#define M41T81REG_ASC_RPT1 0x80 /* alarm repeat mode bit 1 */
+#define M41T81REG_FLG_AF 0x40 /* alarm flag (read only) */
+#define M41T81REG_FLG_WDF 0x80 /* watchdog flag (read only) */
+#define M41T81REG_SQW_RS0 0x10 /* sqw frequency bit 0 */
+#define M41T81REG_SQW_RS1 0x20 /* sqw frequency bit 1 */
+#define M41T81REG_SQW_RS2 0x40 /* sqw frequency bit 2 */
+#define M41T81REG_SQW_RS3 0x80 /* sqw frequency bit 3 */
+
+
+/*
+ * Register numbers
+ */
+
+#define M41T81REG_TSC 0x00 /* tenths/hundredths of second */
+#define M41T81REG_SC 0x01 /* seconds */
+#define M41T81REG_MN 0x02 /* minute */
+#define M41T81REG_HR 0x03 /* hour/century */
+#define M41T81REG_DY 0x04 /* day of week */
+#define M41T81REG_DT 0x05 /* date of month */
+#define M41T81REG_MO 0x06 /* month */
+#define M41T81REG_YR 0x07 /* year */
+#define M41T81REG_CTL 0x08 /* control */
+#define M41T81REG_WD 0x09 /* watchdog */
+#define M41T81REG_AMO 0x0A /* alarm: month */
+#define M41T81REG_ADT 0x0B /* alarm: date */
+#define M41T81REG_AHR 0x0C /* alarm: hour */
+#define M41T81REG_AMN 0x0D /* alarm: minute */
+#define M41T81REG_ASC 0x0E /* alarm: second */
+#define M41T81REG_FLG 0x0F /* flags */
+#define M41T81REG_SQW 0x13 /* square wave register */
+
+#define M41T81_CCR_ADDRESS 0x68
+
+#define SMB_CSR(reg) IOADDR(A_SMB_REGISTER(1, reg))
+
+static int m41t81_read(uint8_t addr)
+{
+ while (__raw_readq(SMB_CSR(R_SMB_STATUS)) & M_SMB_BUSY)
+ ;
+
+ __raw_writeq(addr & 0xff, SMB_CSR(R_SMB_CMD));
+ __raw_writeq(V_SMB_ADDR(M41T81_CCR_ADDRESS) | V_SMB_TT_WR1BYTE,
+ SMB_CSR(R_SMB_START));
+
+ while (__raw_readq(SMB_CSR(R_SMB_STATUS)) & M_SMB_BUSY)
+ ;
+
+ __raw_writeq(V_SMB_ADDR(M41T81_CCR_ADDRESS) | V_SMB_TT_RD1BYTE,
+ SMB_CSR(R_SMB_START));
+
+ while (__raw_readq(SMB_CSR(R_SMB_STATUS)) & M_SMB_BUSY)
+ ;
+
+ if (__raw_readq(SMB_CSR(R_SMB_STATUS)) & M_SMB_ERROR) {
+ /* Clear error bit by writing a 1 */
+ __raw_writeq(M_SMB_ERROR, SMB_CSR(R_SMB_STATUS));
+ return -1;
+ }
+
+ return __raw_readq(SMB_CSR(R_SMB_DATA)) & 0xff;
+}
+
+static int m41t81_write(uint8_t addr, int b)
+{
+ while (__raw_readq(SMB_CSR(R_SMB_STATUS)) & M_SMB_BUSY)
+ ;
+
+ __raw_writeq(addr & 0xff, SMB_CSR(R_SMB_CMD));
+ __raw_writeq(b & 0xff, SMB_CSR(R_SMB_DATA));
+ __raw_writeq(V_SMB_ADDR(M41T81_CCR_ADDRESS) | V_SMB_TT_WR2BYTE,
+ SMB_CSR(R_SMB_START));
+
+ while (__raw_readq(SMB_CSR(R_SMB_STATUS)) & M_SMB_BUSY)
+ ;
+
+ if (__raw_readq(SMB_CSR(R_SMB_STATUS)) & M_SMB_ERROR) {
+ /* Clear error bit by writing a 1 */
+ __raw_writeq(M_SMB_ERROR, SMB_CSR(R_SMB_STATUS));
+ return -1;
+ }
+
+ /* read the same byte again to make sure it is written */
+ __raw_writeq(V_SMB_ADDR(M41T81_CCR_ADDRESS) | V_SMB_TT_RD1BYTE,
+ SMB_CSR(R_SMB_START));
+
+ while (__raw_readq(SMB_CSR(R_SMB_STATUS)) & M_SMB_BUSY)
+ ;
+
+ return 0;
+}
+
+int m41t81_set_time(time64_t t)
+{
+ struct rtc_time tm;
+ unsigned long flags;
+
+ /* Note we don't care about the century */
+ rtc_time64_to_tm(t, &tm);
+
+ /*
+ * Note the write order matters as it ensures the correctness.
+ * When we write sec, 10th sec is clear. It is reasonable to
+ * believe we should finish writing min within a second.
+ */
+
+ spin_lock_irqsave(&rtc_lock, flags);
+ tm.tm_sec = bin2bcd(tm.tm_sec);
+ m41t81_write(M41T81REG_SC, tm.tm_sec);
+
+ tm.tm_min = bin2bcd(tm.tm_min);
+ m41t81_write(M41T81REG_MN, tm.tm_min);
+
+ tm.tm_hour = bin2bcd(tm.tm_hour);
+ tm.tm_hour = (tm.tm_hour & 0x3f) | (m41t81_read(M41T81REG_HR) & 0xc0);
+ m41t81_write(M41T81REG_HR, tm.tm_hour);
+
+ /* tm_wday starts from 0 to 6 */
+ if (tm.tm_wday == 0) tm.tm_wday = 7;
+ tm.tm_wday = bin2bcd(tm.tm_wday);
+ m41t81_write(M41T81REG_DY, tm.tm_wday);
+
+ tm.tm_mday = bin2bcd(tm.tm_mday);
+ m41t81_write(M41T81REG_DT, tm.tm_mday);
+
+ /* tm_mon starts from 0, *ick* */
+ tm.tm_mon ++;
+ tm.tm_mon = bin2bcd(tm.tm_mon);
+ m41t81_write(M41T81REG_MO, tm.tm_mon);
+
+ /* we don't do century, everything is beyond 2000 */
+ tm.tm_year %= 100;
+ tm.tm_year = bin2bcd(tm.tm_year);
+ m41t81_write(M41T81REG_YR, tm.tm_year);
+ spin_unlock_irqrestore(&rtc_lock, flags);
+
+ return 0;
+}
+
+time64_t m41t81_get_time(void)
+{
+ unsigned int year, mon, day, hour, min, sec;
+ unsigned long flags;
+
+ /*
+ * min is valid if two reads of sec are the same.
+ */
+ for (;;) {
+ spin_lock_irqsave(&rtc_lock, flags);
+ sec = m41t81_read(M41T81REG_SC);
+ min = m41t81_read(M41T81REG_MN);
+ if (sec == m41t81_read(M41T81REG_SC)) break;
+ spin_unlock_irqrestore(&rtc_lock, flags);
+ }
+ hour = m41t81_read(M41T81REG_HR) & 0x3f;
+ day = m41t81_read(M41T81REG_DT);
+ mon = m41t81_read(M41T81REG_MO);
+ year = m41t81_read(M41T81REG_YR);
+ spin_unlock_irqrestore(&rtc_lock, flags);
+
+ sec = bcd2bin(sec);
+ min = bcd2bin(min);
+ hour = bcd2bin(hour);
+ day = bcd2bin(day);
+ mon = bcd2bin(mon);
+ year = bcd2bin(year);
+
+ year += 2000;
+
+ return mktime64(year, mon, day, hour, min, sec);
+}
+
+int m41t81_probe(void)
+{
+ unsigned int tmp;
+
+ /* enable chip if it is not enabled yet */
+ tmp = m41t81_read(M41T81REG_SC);
+ m41t81_write(M41T81REG_SC, tmp & 0x7f);
+
+ return m41t81_read(M41T81REG_SC) != -1;
+}
diff --git a/arch/mips/sibyte/swarm/rtc_xicor1241.c b/arch/mips/sibyte/swarm/rtc_xicor1241.c
new file mode 100644
index 0000000000..e2164200ce
--- /dev/null
+++ b/arch/mips/sibyte/swarm/rtc_xicor1241.c
@@ -0,0 +1,206 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2000, 2001 Broadcom Corporation
+ *
+ * Copyright (C) 2002 MontaVista Software Inc.
+ * Author: jsun@mvista.com or jsun@junsun.net
+ */
+#include <linux/bcd.h>
+#include <linux/types.h>
+#include <linux/time.h>
+
+#include <asm/time.h>
+#include <asm/addrspace.h>
+#include <asm/io.h>
+
+#include <asm/sibyte/sb1250.h>
+#include <asm/sibyte/sb1250_regs.h>
+#include <asm/sibyte/sb1250_smbus.h>
+
+
+/* Xicor 1241 definitions */
+
+/*
+ * Register bits
+ */
+
+#define X1241REG_SR_BAT 0x80 /* currently on battery power */
+#define X1241REG_SR_RWEL 0x04 /* r/w latch is enabled, can write RTC */
+#define X1241REG_SR_WEL 0x02 /* r/w latch is unlocked, can enable r/w now */
+#define X1241REG_SR_RTCF 0x01 /* clock failed */
+#define X1241REG_BL_BP2 0x80 /* block protect 2 */
+#define X1241REG_BL_BP1 0x40 /* block protect 1 */
+#define X1241REG_BL_BP0 0x20 /* block protect 0 */
+#define X1241REG_BL_WD1 0x10
+#define X1241REG_BL_WD0 0x08
+#define X1241REG_HR_MIL 0x80 /* military time format */
+
+/*
+ * Register numbers
+ */
+
+#define X1241REG_BL 0x10 /* block protect bits */
+#define X1241REG_INT 0x11 /* */
+#define X1241REG_SC 0x30 /* Seconds */
+#define X1241REG_MN 0x31 /* Minutes */
+#define X1241REG_HR 0x32 /* Hours */
+#define X1241REG_DT 0x33 /* Day of month */
+#define X1241REG_MO 0x34 /* Month */
+#define X1241REG_YR 0x35 /* Year */
+#define X1241REG_DW 0x36 /* Day of Week */
+#define X1241REG_Y2K 0x37 /* Year 2K */
+#define X1241REG_SR 0x3F /* Status register */
+
+#define X1241_CCR_ADDRESS 0x6F
+
+#define SMB_CSR(reg) IOADDR(A_SMB_REGISTER(1, reg))
+
+static int xicor_read(uint8_t addr)
+{
+ while (__raw_readq(SMB_CSR(R_SMB_STATUS)) & M_SMB_BUSY)
+ ;
+
+ __raw_writeq((addr >> 8) & 0x7, SMB_CSR(R_SMB_CMD));
+ __raw_writeq(addr & 0xff, SMB_CSR(R_SMB_DATA));
+ __raw_writeq(V_SMB_ADDR(X1241_CCR_ADDRESS) | V_SMB_TT_WR2BYTE,
+ SMB_CSR(R_SMB_START));
+
+ while (__raw_readq(SMB_CSR(R_SMB_STATUS)) & M_SMB_BUSY)
+ ;
+
+ __raw_writeq(V_SMB_ADDR(X1241_CCR_ADDRESS) | V_SMB_TT_RD1BYTE,
+ SMB_CSR(R_SMB_START));
+
+ while (__raw_readq(SMB_CSR(R_SMB_STATUS)) & M_SMB_BUSY)
+ ;
+
+ if (__raw_readq(SMB_CSR(R_SMB_STATUS)) & M_SMB_ERROR) {
+ /* Clear error bit by writing a 1 */
+ __raw_writeq(M_SMB_ERROR, SMB_CSR(R_SMB_STATUS));
+ return -1;
+ }
+
+ return __raw_readq(SMB_CSR(R_SMB_DATA)) & 0xff;
+}
+
+static int xicor_write(uint8_t addr, int b)
+{
+ while (__raw_readq(SMB_CSR(R_SMB_STATUS)) & M_SMB_BUSY)
+ ;
+
+ __raw_writeq(addr, SMB_CSR(R_SMB_CMD));
+ __raw_writeq((addr & 0xff) | ((b & 0xff) << 8), SMB_CSR(R_SMB_DATA));
+ __raw_writeq(V_SMB_ADDR(X1241_CCR_ADDRESS) | V_SMB_TT_WR3BYTE,
+ SMB_CSR(R_SMB_START));
+
+ while (__raw_readq(SMB_CSR(R_SMB_STATUS)) & M_SMB_BUSY)
+ ;
+
+ if (__raw_readq(SMB_CSR(R_SMB_STATUS)) & M_SMB_ERROR) {
+ /* Clear error bit by writing a 1 */
+ __raw_writeq(M_SMB_ERROR, SMB_CSR(R_SMB_STATUS));
+ return -1;
+ } else {
+ return 0;
+ }
+}
+
+int xicor_set_time(time64_t t)
+{
+ struct rtc_time tm;
+ int tmp;
+ unsigned long flags;
+
+ rtc_time64_to_tm(t, &tm);
+ tm.tm_year += 1900;
+
+ spin_lock_irqsave(&rtc_lock, flags);
+ /* unlock writes to the CCR */
+ xicor_write(X1241REG_SR, X1241REG_SR_WEL);
+ xicor_write(X1241REG_SR, X1241REG_SR_WEL | X1241REG_SR_RWEL);
+
+ /* trivial ones */
+ tm.tm_sec = bin2bcd(tm.tm_sec);
+ xicor_write(X1241REG_SC, tm.tm_sec);
+
+ tm.tm_min = bin2bcd(tm.tm_min);
+ xicor_write(X1241REG_MN, tm.tm_min);
+
+ tm.tm_mday = bin2bcd(tm.tm_mday);
+ xicor_write(X1241REG_DT, tm.tm_mday);
+
+ /* tm_mon starts from 0, *ick* */
+ tm.tm_mon ++;
+ tm.tm_mon = bin2bcd(tm.tm_mon);
+ xicor_write(X1241REG_MO, tm.tm_mon);
+
+ /* year is split */
+ tmp = tm.tm_year / 100;
+ tm.tm_year %= 100;
+ xicor_write(X1241REG_YR, tm.tm_year);
+ xicor_write(X1241REG_Y2K, tmp);
+
+ /* hour is the most tricky one */
+ tmp = xicor_read(X1241REG_HR);
+ if (tmp & X1241REG_HR_MIL) {
+ /* 24 hour format */
+ tm.tm_hour = bin2bcd(tm.tm_hour);
+ tmp = (tmp & ~0x3f) | (tm.tm_hour & 0x3f);
+ } else {
+ /* 12 hour format, with 0x2 for pm */
+ tmp = tmp & ~0x3f;
+ if (tm.tm_hour >= 12) {
+ tmp |= 0x20;
+ tm.tm_hour -= 12;
+ }
+ tm.tm_hour = bin2bcd(tm.tm_hour);
+ tmp |= tm.tm_hour;
+ }
+ xicor_write(X1241REG_HR, tmp);
+
+ xicor_write(X1241REG_SR, 0);
+ spin_unlock_irqrestore(&rtc_lock, flags);
+
+ return 0;
+}
+
+time64_t xicor_get_time(void)
+{
+ unsigned int year, mon, day, hour, min, sec, y2k;
+ unsigned long flags;
+
+ spin_lock_irqsave(&rtc_lock, flags);
+ sec = xicor_read(X1241REG_SC);
+ min = xicor_read(X1241REG_MN);
+ hour = xicor_read(X1241REG_HR);
+
+ if (hour & X1241REG_HR_MIL) {
+ hour &= 0x3f;
+ } else {
+ if (hour & 0x20)
+ hour = (hour & 0xf) + 0x12;
+ }
+
+ day = xicor_read(X1241REG_DT);
+ mon = xicor_read(X1241REG_MO);
+ year = xicor_read(X1241REG_YR);
+ y2k = xicor_read(X1241REG_Y2K);
+ spin_unlock_irqrestore(&rtc_lock, flags);
+
+ sec = bcd2bin(sec);
+ min = bcd2bin(min);
+ hour = bcd2bin(hour);
+ day = bcd2bin(day);
+ mon = bcd2bin(mon);
+ year = bcd2bin(year);
+ y2k = bcd2bin(y2k);
+
+ year += (y2k * 100);
+
+ return mktime64(year, mon, day, hour, min, sec);
+}
+
+int xicor_probe(void)
+{
+ return xicor_read(X1241REG_SC) != -1;
+}
diff --git a/arch/mips/sibyte/swarm/setup.c b/arch/mips/sibyte/swarm/setup.c
new file mode 100644
index 0000000000..76683993cd
--- /dev/null
+++ b/arch/mips/sibyte/swarm/setup.c
@@ -0,0 +1,164 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2000, 2001, 2002, 2003, 2004 Broadcom Corporation
+ * Copyright (C) 2004 by Ralf Baechle (ralf@linux-mips.org)
+ */
+
+/*
+ * Setup code for the SWARM board
+ */
+
+#include <linux/spinlock.h>
+#include <linux/mm.h>
+#include <linux/memblock.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/screen_info.h>
+#include <linux/initrd.h>
+
+#include <asm/irq.h>
+#include <asm/io.h>
+#include <asm/bootinfo.h>
+#include <asm/mipsregs.h>
+#include <asm/reboot.h>
+#include <asm/time.h>
+#include <asm/traps.h>
+#include <asm/sibyte/sb1250.h>
+#ifdef CONFIG_SIBYTE_BCM1x80
+#include <asm/sibyte/bcm1480_regs.h>
+#elif defined(CONFIG_SIBYTE_SB1250) || defined(CONFIG_SIBYTE_BCM112X)
+#include <asm/sibyte/sb1250_regs.h>
+#else
+#error invalid SiByte board configuration
+#endif
+#include <asm/sibyte/sb1250_genbus.h>
+#include <asm/sibyte/board.h>
+
+#ifdef CONFIG_SIBYTE_BCM1x80
+extern void bcm1480_setup(void);
+#elif defined(CONFIG_SIBYTE_SB1250) || defined(CONFIG_SIBYTE_BCM112X)
+extern void sb1250_setup(void);
+#else
+#error invalid SiByte board configuration
+#endif
+
+extern int xicor_probe(void);
+extern int xicor_set_time(time64_t);
+extern time64_t xicor_get_time(void);
+
+extern int m41t81_probe(void);
+extern int m41t81_set_time(time64_t);
+extern time64_t m41t81_get_time(void);
+
+const char *get_system_type(void)
+{
+ return "SiByte " SIBYTE_BOARD_NAME;
+}
+
+int swarm_be_handler(struct pt_regs *regs, int is_fixup)
+{
+ if (!is_fixup && (regs->cp0_cause & 4)) {
+ /* Data bus error - print PA */
+ printk("DBE physical address: %010Lx\n",
+ __read_64bit_c0_register($26, 1));
+ }
+ return is_fixup ? MIPS_BE_FIXUP : MIPS_BE_FATAL;
+}
+
+enum swarm_rtc_type {
+ RTC_NONE,
+ RTC_XICOR,
+ RTC_M41T81,
+};
+
+enum swarm_rtc_type swarm_rtc_type;
+
+void read_persistent_clock64(struct timespec64 *ts)
+{
+ time64_t sec;
+
+ switch (swarm_rtc_type) {
+ case RTC_XICOR:
+ sec = xicor_get_time();
+ break;
+
+ case RTC_M41T81:
+ sec = m41t81_get_time();
+ break;
+
+ case RTC_NONE:
+ default:
+ sec = mktime64(2000, 1, 1, 0, 0, 0);
+ break;
+ }
+ ts->tv_sec = sec;
+ ts->tv_nsec = 0;
+}
+
+int update_persistent_clock64(struct timespec64 now)
+{
+ time64_t sec = now.tv_sec;
+
+ switch (swarm_rtc_type) {
+ case RTC_XICOR:
+ return xicor_set_time(sec);
+
+ case RTC_M41T81:
+ return m41t81_set_time(sec);
+
+ case RTC_NONE:
+ default:
+ return -1;
+ }
+}
+
+void __init plat_mem_setup(void)
+{
+#ifdef CONFIG_SIBYTE_BCM1x80
+ bcm1480_setup();
+#elif defined(CONFIG_SIBYTE_SB1250) || defined(CONFIG_SIBYTE_BCM112X)
+ sb1250_setup();
+#else
+#error invalid SiByte board configuration
+#endif
+
+ mips_set_be_handler(swarm_be_handler);
+
+ if (xicor_probe())
+ swarm_rtc_type = RTC_XICOR;
+ if (m41t81_probe())
+ swarm_rtc_type = RTC_M41T81;
+
+#ifdef CONFIG_VT
+ screen_info = (struct screen_info) {
+ .orig_video_page = 52,
+ .orig_video_mode = 3,
+ .orig_video_cols = 80,
+ .flags = 12,
+ .orig_video_ega_bx = 3,
+ .orig_video_lines = 25,
+ .orig_video_isVGA = 0x22,
+ .orig_video_points = 16,
+ };
+ /* XXXKW for CFE, get lines/cols from environment */
+#endif
+}
+
+#ifdef LEDS_PHYS
+
+void setleds(char *str)
+{
+ void *reg;
+ int i;
+
+ for (i = 0; i < 4; i++) {
+ reg = IOADDR(LEDS_PHYS) + 0x20 + ((3 - i) << 3);
+
+ if (!str[i])
+ writeb(' ', reg);
+ else
+ writeb(str[i], reg);
+ }
+}
+
+#endif /* LEDS_PHYS */
diff --git a/arch/mips/sibyte/swarm/swarm-i2c.c b/arch/mips/sibyte/swarm/swarm-i2c.c
new file mode 100644
index 0000000000..1ed2dc96d4
--- /dev/null
+++ b/arch/mips/sibyte/swarm/swarm-i2c.c
@@ -0,0 +1,31 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Broadcom BCM91250A (SWARM), etc. I2C platform setup.
+ *
+ * Copyright (c) 2008 Maciej W. Rozycki
+ */
+
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+
+
+static struct i2c_board_info swarm_i2c_info1[] __initdata = {
+ {
+ I2C_BOARD_INFO("m41t81", 0x68),
+ },
+};
+
+static int __init swarm_i2c_init(void)
+{
+ int err;
+
+ err = i2c_register_board_info(1, swarm_i2c_info1,
+ ARRAY_SIZE(swarm_i2c_info1));
+ if (err < 0)
+ printk(KERN_ERR
+ "swarm-i2c: cannot register board I2C devices\n");
+ return err;
+}
+
+arch_initcall(swarm_i2c_init);