Linux-2.6.12-rc2

Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.

Let it rip!
diff --git a/arch/mips/sibyte/sb1250/Makefile b/arch/mips/sibyte/sb1250/Makefile
new file mode 100644
index 0000000..a8af846
--- /dev/null
+++ b/arch/mips/sibyte/sb1250/Makefile
@@ -0,0 +1,8 @@
+obj-y := setup.o irq.o irq_handler.o time.o
+
+obj-$(CONFIG_SMP)			+= smp.o
+obj-$(CONFIG_SIBYTE_TBPROF)		+= bcm1250_tbprof.o
+obj-$(CONFIG_SIBYTE_STANDALONE)		+= prom.o
+obj-$(CONFIG_SIBYTE_BUS_WATCHER)	+= bus_watcher.o
+
+EXTRA_AFLAGS := $(CFLAGS)
diff --git a/arch/mips/sibyte/sb1250/bcm1250_tbprof.c b/arch/mips/sibyte/sb1250/bcm1250_tbprof.c
new file mode 100644
index 0000000..7f813ae
--- /dev/null
+++ b/arch/mips/sibyte/sb1250/bcm1250_tbprof.c
@@ -0,0 +1,390 @@
+/*
+ * Copyright (C) 2001, 2002, 2003 Broadcom Corporation
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ */
+
+#define SBPROF_TB_DEBUG 0
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/slab.h>
+#include <linux/vmalloc.h>
+#include <linux/fs.h>
+#include <linux/errno.h>
+#include <linux/reboot.h>
+#include <asm/uaccess.h>
+#include <asm/io.h>
+#include <asm/sibyte/sb1250.h>
+#include <asm/sibyte/sb1250_regs.h>
+#include <asm/sibyte/sb1250_scd.h>
+#include <asm/sibyte/sb1250_int.h>
+#include <asm/sibyte/trace_prof.h>
+
+#define DEVNAME "bcm1250_tbprof"
+
+static struct sbprof_tb sbp;
+
+#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 u_int64_t tb_period;
+
+static void arm_tb(void)
+{
+        u_int64_t scdperfcnt;
+	u_int64_t next = (1ULL << 40) - tb_period;
+	u_int64_t 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 */
+	bus_writeq(0, IOADDR(A_SCD_PERF_CNT_1));
+	scdperfcnt = bus_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. */
+	bus_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));
+	bus_writeq(next, IOADDR(A_SCD_PERF_CNT_1));
+	/* Reset the trace buffer */
+	bus_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
+	bus_writeq(tb_options, IOADDR(A_SCD_TRACE_CFG));
+	sbp.tb_armed = 1;
+}
+
+static irqreturn_t sbprof_tb_intr(int irq, void *dev_id, struct pt_regs *regs)
+{
+	int i;
+	DBG(printk(DEVNAME ": tb_intr\n"));
+	if (sbp.next_tb_sample < MAX_TB_SAMPLES) {
+		/* XXX should use XKPHYS to make writes bypass L2 */
+		u_int64_t *p = sbp.sbprof_tbbuf[sbp.next_tb_sample++];
+		/* Read out trace */
+		bus_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] = bus_readq(IOADDR(A_SCD_TRACE_READ)); // read t2 hi
+			p[i-2] = bus_readq(IOADDR(A_SCD_TRACE_READ)); // read t2 lo
+			p[i-3] = bus_readq(IOADDR(A_SCD_TRACE_READ)); // read t1 hi
+			p[i-4] = bus_readq(IOADDR(A_SCD_TRACE_READ)); // read t1 lo
+			p[i-5] = bus_readq(IOADDR(A_SCD_TRACE_READ)); // read t0 hi
+			p[i-6] = bus_readq(IOADDR(A_SCD_TRACE_READ)); // read t0 lo
+		}
+		if (!sbp.tb_enable) {
+			DBG(printk(DEVNAME ": tb_intr shutdown\n"));
+			bus_writeq(M_SCD_TRACE_CFG_RESET,
+				   IOADDR(A_SCD_TRACE_CFG));
+			sbp.tb_armed = 0;
+			wake_up(&sbp.tb_sync);
+		} else {
+			arm_tb();	// knock down current interrupt and get another one later
+		}
+	} else {
+		/* No more trace buffer samples */
+		DBG(printk(DEVNAME ": tb_intr full\n"));
+		bus_writeq(M_SCD_TRACE_CFG_RESET, IOADDR(A_SCD_TRACE_CFG));
+		sbp.tb_armed = 0;
+		if (!sbp.tb_enable) {
+			wake_up(&sbp.tb_sync);
+		}
+		wake_up(&sbp.tb_read);
+	}
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t sbprof_pc_intr(int irq, void *dev_id, struct pt_regs *regs)
+{
+	printk(DEVNAME ": unexpected pc_intr");
+	return IRQ_NONE;
+}
+
+int sbprof_zbprof_start(struct file *filp)
+{
+	u_int64_t scdperfcnt;
+
+	if (sbp.tb_enable)
+		return -EBUSY;
+
+	DBG(printk(DEVNAME ": starting\n"));
+
+	sbp.tb_enable = 1;
+	sbp.next_tb_sample = 0;
+	filp->f_pos = 0;
+
+	if (request_irq
+	    (K_INT_TRACE_FREEZE, sbprof_tb_intr, 0, DEVNAME " trace freeze", &sbp)) {
+		return -EBUSY;
+	}
+	/* Make sure there isn't a perf-cnt interrupt waiting */
+	scdperfcnt = bus_readq(IOADDR(A_SCD_PERF_CNT_CFG));
+	/* Disable and clear counters, override SRC_1 */
+	bus_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 */
+	bus_writeq(K_INT_MAP_I3,
+		   IOADDR(A_IMR_REGISTER(0, R_IMR_INTERRUPT_MAP_BASE) +
+			  (K_INT_PERF_CNT << 3)));
+
+	/* Initialize address traps */
+	bus_writeq(0, IOADDR(A_ADDR_TRAP_UP_0));
+	bus_writeq(0, IOADDR(A_ADDR_TRAP_UP_1));
+	bus_writeq(0, IOADDR(A_ADDR_TRAP_UP_2));
+	bus_writeq(0, IOADDR(A_ADDR_TRAP_UP_3));
+
+	bus_writeq(0, IOADDR(A_ADDR_TRAP_DOWN_0));
+	bus_writeq(0, IOADDR(A_ADDR_TRAP_DOWN_1));
+	bus_writeq(0, IOADDR(A_ADDR_TRAP_DOWN_2));
+	bus_writeq(0, IOADDR(A_ADDR_TRAP_DOWN_3));
+
+	bus_writeq(0, IOADDR(A_ADDR_TRAP_CFG_0));
+	bus_writeq(0, IOADDR(A_ADDR_TRAP_CFG_1));
+	bus_writeq(0, IOADDR(A_ADDR_TRAP_CFG_2));
+	bus_writeq(0, IOADDR(A_ADDR_TRAP_CFG_3));
+
+	/* Initialize Trace Event 0-7 */
+	//				when interrupt
+	bus_writeq(M_SCD_TREVT_INTERRUPT, IOADDR(A_SCD_TRACE_EVENT_0));
+	bus_writeq(0, IOADDR(A_SCD_TRACE_EVENT_1));
+	bus_writeq(0, IOADDR(A_SCD_TRACE_EVENT_2));
+	bus_writeq(0, IOADDR(A_SCD_TRACE_EVENT_3));
+	bus_writeq(0, IOADDR(A_SCD_TRACE_EVENT_4));
+	bus_writeq(0, IOADDR(A_SCD_TRACE_EVENT_5));
+	bus_writeq(0, IOADDR(A_SCD_TRACE_EVENT_6));
+	bus_writeq(0, IOADDR(A_SCD_TRACE_EVENT_7));
+
+	/* Initialize Trace Sequence 0-7 */
+	//				     Start on event 0 (interrupt)
+	bus_writeq(V_SCD_TRSEQ_FUNC_START | 0x0fff,
+		   IOADDR(A_SCD_TRACE_SEQUENCE_0));
+	//			  dsamp when d used | asamp when a used
+	bus_writeq(M_SCD_TRSEQ_ASAMPLE | M_SCD_TRSEQ_DSAMPLE |
+		   K_SCD_TRSEQ_TRIGGER_ALL,
+		   IOADDR(A_SCD_TRACE_SEQUENCE_1));
+	bus_writeq(0, IOADDR(A_SCD_TRACE_SEQUENCE_2));
+	bus_writeq(0, IOADDR(A_SCD_TRACE_SEQUENCE_3));
+	bus_writeq(0, IOADDR(A_SCD_TRACE_SEQUENCE_4));
+	bus_writeq(0, IOADDR(A_SCD_TRACE_SEQUENCE_5));
+	bus_writeq(0, IOADDR(A_SCD_TRACE_SEQUENCE_6));
+	bus_writeq(0, IOADDR(A_SCD_TRACE_SEQUENCE_7));
+
+	/* Now indicate the PERF_CNT interrupt as a trace-relevant interrupt */
+	bus_writeq((1ULL << K_INT_PERF_CNT),
+		   IOADDR(A_IMR_REGISTER(0, R_IMR_INTERRUPT_TRACE)));
+
+	arm_tb();
+
+	DBG(printk(DEVNAME ": done starting\n"));
+
+	return 0;
+}
+
+int sbprof_zbprof_stop(void)
+{
+	DBG(printk(DEVNAME ": stopping\n"));
+
+	if (sbp.tb_enable) {
+		sbp.tb_enable = 0;
+		/* XXXKW there is a window here where the intr handler
+		   may run, see the disable, and do the wake_up before
+		   this sleep happens. */
+		if (sbp.tb_armed) {
+			DBG(printk(DEVNAME ": wait for disarm\n"));
+			interruptible_sleep_on(&sbp.tb_sync);
+			DBG(printk(DEVNAME ": disarm complete\n"));
+		}
+		free_irq(K_INT_TRACE_FREEZE, &sbp);
+		free_irq(K_INT_PERF_CNT, &sbp);
+	}
+
+	DBG(printk(DEVNAME ": done stopping\n"));
+
+	return 0;
+}
+
+static int sbprof_tb_open(struct inode *inode, struct file *filp)
+{
+	int minor;
+
+	minor = iminor(inode);
+	if (minor != 0) {
+		return -ENODEV;
+	}
+	if (sbp.open) {
+		return -EBUSY;
+	}
+
+	memset(&sbp, 0, sizeof(struct sbprof_tb));
+	sbp.sbprof_tbbuf = vmalloc(MAX_TBSAMPLE_BYTES);
+	if (!sbp.sbprof_tbbuf) {
+		return -ENOMEM;
+	}
+	memset(sbp.sbprof_tbbuf, 0, MAX_TBSAMPLE_BYTES);
+	init_waitqueue_head(&sbp.tb_sync);
+	init_waitqueue_head(&sbp.tb_read);
+	sbp.open = 1;
+
+	return 0;
+}
+
+static int sbprof_tb_release(struct inode *inode, struct file *filp)
+{
+	int minor;
+
+	minor = iminor(inode);
+	if (minor != 0 || !sbp.open) {
+		return -ENODEV;
+	}
+
+	if (sbp.tb_armed || sbp.tb_enable) {
+		sbprof_zbprof_stop();
+	}
+
+	vfree(sbp.sbprof_tbbuf);
+	sbp.open = 0;
+
+	return 0;
+}
+
+static ssize_t sbprof_tb_read(struct file *filp, char *buf,
+			      size_t size, loff_t *offp)
+{
+	int cur_sample, sample_off, cur_count, sample_left;
+	char *src;
+	int   count   =	 0;
+	char *dest    =	 buf;
+	long  cur_off = *offp;
+
+	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)) {
+		cur_count = size < sample_left ? size : sample_left;
+		src = (char *)(((long)sbp.sbprof_tbbuf[cur_sample])+sample_off);
+		copy_to_user(dest, src, cur_count);
+		DBG(printk(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;
+
+	return count;
+}
+
+static int sbprof_tb_ioctl(struct inode *inode,
+			   struct file *filp,
+			   unsigned int command,
+			   unsigned long arg)
+{
+	int error = 0;
+
+	switch (command) {
+	case SBPROF_ZBSTART:
+		error = sbprof_zbprof_start(filp);
+		break;
+	case SBPROF_ZBSTOP:
+		error = sbprof_zbprof_stop();
+		break;
+	case SBPROF_ZBWAITFULL:
+		interruptible_sleep_on(&sbp.tb_read);
+		/* XXXKW check if interrupted? */
+		return put_user(TB_FULL, (int *) arg);
+	default:
+		error = -EINVAL;
+		break;
+	}
+
+	return error;
+}
+
+static struct file_operations sbprof_tb_fops = {
+	.owner		= THIS_MODULE,
+	.open		= sbprof_tb_open,
+	.release	= sbprof_tb_release,
+	.read		= sbprof_tb_read,
+	.ioctl		= sbprof_tb_ioctl,
+	.mmap		= NULL,
+};
+
+static int __init sbprof_tb_init(void)
+{
+	if (register_chrdev(SBPROF_TB_MAJOR, DEVNAME, &sbprof_tb_fops)) {
+		printk(KERN_WARNING DEVNAME ": initialization failed (dev %d)\n",
+		       SBPROF_TB_MAJOR);
+		return -EIO;
+	}
+	sbp.open = 0;
+	tb_period = zbbus_mhz * 10000LL;
+	printk(KERN_INFO DEVNAME ": initialized - tb_period = %lld\n", tb_period);
+	return 0;
+}
+
+static void __exit sbprof_tb_cleanup(void)
+{
+	unregister_chrdev(SBPROF_TB_MAJOR, DEVNAME);
+}
+
+module_init(sbprof_tb_init);
+module_exit(sbprof_tb_cleanup);
diff --git a/arch/mips/sibyte/sb1250/bus_watcher.c b/arch/mips/sibyte/sb1250/bus_watcher.c
new file mode 100644
index 0000000..182a16f
--- /dev/null
+++ b/arch/mips/sibyte/sb1250/bus_watcher.c
@@ -0,0 +1,259 @@
+/*
+ * Copyright (C) 2002,2003 Broadcom Corporation
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ */
+
+/* 
+ * 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/config.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/interrupt.h>
+#include <linux/sched.h>
+#include <linux/proc_fs.h>
+#include <asm/system.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>
+
+
+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;
+
+#ifdef CONFIG_SB1_PASS_1_WORKAROUNDS
+	/* Destructive read, clears register and interrupt */
+	status = csr_in32(IOADDR(A_SCD_BUS_ERR_STATUS));
+#else
+	/* Use non-destructive register */
+	status = csr_in32(IOADDR(A_SCD_BUS_ERR_STATUS_DEBUG));
+#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");
+}
+
+static int bw_print_buffer(char *page, struct bw_stats_struct *stats)
+{
+	int len;
+
+	len = sprintf(page, "SiByte Bus Watcher statistics\n");
+	len += sprintf(page+len, "-----------------------------\n");
+	len += sprintf(page+len, "L2-d-cor %8ld\nL2-d-bad %8ld\n",
+		       stats->l2_cor_d, stats->l2_bad_d);
+	len += sprintf(page+len, "L2-t-cor %8ld\nL2-t-bad %8ld\n",
+		       stats->l2_cor_t, stats->l2_bad_t);
+	len += sprintf(page+len, "MC-d-cor %8ld\nMC-d-bad %8ld\n",
+		       stats->mem_cor_d, stats->mem_bad_d);
+	len += sprintf(page+len, "IO-err   %8ld\n", stats->bus_error);
+	len += sprintf(page+len, "\nLast recorded signature:\n");
+	len += sprintf(page+len, "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)
+		len += sprintf(page+len, "Multiple errors observed since last check.\n");
+	if (stats->status_printed) {
+		len += sprintf(page+len, "(no change since last printing)\n");
+	} else {
+		stats->status_printed = 1;
+	}
+
+	return len;
+}
+
+#ifdef CONFIG_PROC_FS
+
+/* For simplicity, I want to assume a single read is required each
+   time */
+static int bw_read_proc(char *page, char **start, off_t off,
+			int count, int *eof, void *data)
+{
+	int len;
+
+	if (off == 0) {
+		len = bw_print_buffer(page, data);
+		*start = page;
+	} else {
+		len = 0;
+		*eof = 1;
+	}
+	return len;
+}
+
+static void create_proc_decoder(struct bw_stats_struct *stats)
+{
+	struct proc_dir_entry *ent;
+	
+	ent = create_proc_read_entry("bus_watcher", S_IWUSR | S_IRUGO, NULL,
+				     bw_read_proc, 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 pt_regs *regs)
+{
+	struct bw_stats_struct *stats = data;
+	unsigned long cntr;
+#ifdef CONFIG_SIBYTE_BW_TRACE
+	int i;
+#endif
+#ifndef CONFIG_PROC_FS
+	char bw_buf[1024];
+#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",
+		       (unsigned long long)bus_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));
+
+#ifndef CONFIG_PROC_FS
+	bw_print_buffer(bw_buf, stats);
+	printk(bw_buf);
+#endif
+
+	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;
+}
+
+__initcall(sibyte_bus_watcher);
diff --git a/arch/mips/sibyte/sb1250/irq.c b/arch/mips/sibyte/sb1250/irq.c
new file mode 100644
index 0000000..2728abb
--- /dev/null
+++ b/arch/mips/sibyte/sb1250/irq.c
@@ -0,0 +1,431 @@
+/*
+ * Copyright (C) 2000, 2001, 2002, 2003 Broadcom Corporation
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ */
+#include <linux/config.h>
+#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/slab.h>
+#include <linux/kernel_stat.h>
+
+#include <asm/errno.h>
+#include <asm/signal.h>
+#include <asm/system.h>
+#include <asm/ptrace.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
+ */
+
+
+#define shutdown_sb1250_irq	disable_sb1250_irq
+static void end_sb1250_irq(unsigned int irq);
+static void enable_sb1250_irq(unsigned int irq);
+static void disable_sb1250_irq(unsigned int irq);
+static unsigned int startup_sb1250_irq(unsigned int irq);
+static void ack_sb1250_irq(unsigned int irq);
+#ifdef CONFIG_SMP
+static void sb1250_set_affinity(unsigned int irq, unsigned long mask);
+#endif
+
+#ifdef CONFIG_SIBYTE_HAS_LDT
+extern unsigned long ldt_eoi_space;
+#endif
+
+#ifdef CONFIG_KGDB
+static int kgdb_irq;
+
+/* Default to UART1 */
+int kgdb_port = 1;
+#ifdef CONFIG_SIBYTE_SB1250_DUART
+extern char sb1250_duart_present[];
+#endif
+#endif
+
+static struct hw_interrupt_type sb1250_irq_type = {
+	"SB1250-IMR",
+	startup_sb1250_irq,
+	shutdown_sb1250_irq,
+	enable_sb1250_irq,
+	disable_sb1250_irq,
+	ack_sb1250_irq,
+	end_sb1250_irq,
+#ifdef CONFIG_SMP
+	sb1250_set_affinity
+#else
+	NULL
+#endif
+};
+
+/* Store the CPU id (not the logical number) */
+int sb1250_irq_owner[SB1250_NR_IRQS];
+
+DEFINE_SPINLOCK(sb1250_imr_lock);
+
+void sb1250_mask_irq(int cpu, int irq)
+{
+	unsigned long flags;
+	u64 cur_ints;
+
+	spin_lock_irqsave(&sb1250_imr_lock, flags);
+	cur_ints = __bus_readq(IOADDR(A_IMR_MAPPER(cpu) +
+				      R_IMR_INTERRUPT_MASK));
+	cur_ints |= (((u64) 1) << irq);
+	__bus_writeq(cur_ints, IOADDR(A_IMR_MAPPER(cpu) +
+				      R_IMR_INTERRUPT_MASK));
+	spin_unlock_irqrestore(&sb1250_imr_lock, flags);
+}
+
+void sb1250_unmask_irq(int cpu, int irq)
+{
+	unsigned long flags;
+	u64 cur_ints;
+
+	spin_lock_irqsave(&sb1250_imr_lock, flags);
+	cur_ints = __bus_readq(IOADDR(A_IMR_MAPPER(cpu) +
+				      R_IMR_INTERRUPT_MASK));
+	cur_ints &= ~(((u64) 1) << irq);
+	__bus_writeq(cur_ints, IOADDR(A_IMR_MAPPER(cpu) +
+				      R_IMR_INTERRUPT_MASK));
+	spin_unlock_irqrestore(&sb1250_imr_lock, flags);
+}
+
+#ifdef CONFIG_SMP
+static void sb1250_set_affinity(unsigned int irq, unsigned long mask)
+{
+	int i = 0, old_cpu, cpu, int_on;
+	u64 cur_ints;
+	irq_desc_t *desc = irq_desc + irq;
+	unsigned long flags;
+
+	while (mask) {
+		if (mask & 1) {
+			mask >>= 1;
+			break;
+		}
+		mask >>= 1;
+		i++;
+	}
+
+	if (mask) {
+		printk("attempted to set irq affinity for irq %d to multiple CPUs\n", irq);
+		return;
+	}
+
+	/* Convert logical CPU to physical CPU */
+	cpu = cpu_logical_map(i);
+
+	/* Protect against other affinity changers and IMR manipulation */
+	spin_lock_irqsave(&desc->lock, flags);
+	spin_lock(&sb1250_imr_lock);
+
+	/* Swizzle each CPU's IMR (but leave the IP selection alone) */
+	old_cpu = sb1250_irq_owner[irq];
+	cur_ints = __bus_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);
+		__bus_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 = __bus_readq(IOADDR(A_IMR_MAPPER(cpu) +
+				       R_IMR_INTERRUPT_MASK));
+		cur_ints &= ~(((u64) 1) << irq);
+		__bus_writeq(cur_ints, IOADDR(A_IMR_MAPPER(cpu) +
+					      R_IMR_INTERRUPT_MASK));
+	}
+	spin_unlock(&sb1250_imr_lock);
+	spin_unlock_irqrestore(&desc->lock, flags);
+}
+#endif
+
+
+/* Defined in arch/mips/sibyte/sb1250/irq_handler.S */
+extern void sb1250_irq_handler(void);
+
+/*****************************************************************************/
+
+static unsigned int startup_sb1250_irq(unsigned int irq)
+{
+	sb1250_unmask_irq(sb1250_irq_owner[irq], irq);
+
+	return 0;		/* never anything pending */
+}
+
+
+static void disable_sb1250_irq(unsigned int irq)
+{
+	sb1250_mask_irq(sb1250_irq_owner[irq], irq);
+}
+
+static void enable_sb1250_irq(unsigned int irq)
+{
+	sb1250_unmask_irq(sb1250_irq_owner[irq], irq);
+}
+
+
+static void ack_sb1250_irq(unsigned int 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 = bus_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
+			 */
+			bus_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 void end_sb1250_irq(unsigned int irq)
+{
+	if (!(irq_desc[irq].status & (IRQ_DISABLED | IRQ_INPROGRESS))) {
+		sb1250_unmask_irq(sb1250_irq_owner[irq], irq);
+	}
+}
+
+
+void __init init_sb1250_irqs(void)
+{
+	int i;
+
+	for (i = 0; i < NR_IRQS; i++) {
+		irq_desc[i].status = IRQ_DISABLED;
+		irq_desc[i].action = 0;
+		irq_desc[i].depth = 1;
+		if (i < SB1250_NR_IRQS) {
+			irq_desc[i].handler = &sb1250_irq_type;
+			sb1250_irq_owner[i] = 0;
+		} else {
+			irq_desc[i].handler = &no_irq_type;
+		}
+	}
+}
+
+
+static irqreturn_t  sb1250_dummy_handler(int irq, void *dev_id,
+	struct pt_regs *regs)
+{
+	return IRQ_NONE;
+}
+
+static struct irqaction sb1250_dummy_action = {
+	.handler = sb1250_dummy_handler,
+	.flags   = 0,
+	.mask    = CPU_MASK_NONE,
+	.name    = "sb1250-private",
+	.next    = NULL,
+	.dev_id  = 0
+};
+
+int sb1250_steal_irq(int irq)
+{
+	irq_desc_t *desc = irq_desc + irq;
+	unsigned long flags;
+	int retval = 0;
+
+	if (irq >= SB1250_NR_IRQS)
+		return -EINVAL;
+
+	spin_lock_irqsave(&desc->lock,flags);
+	/* Don't allow sharing at all for these */
+	if (desc->action != NULL)
+		retval = -EBUSY;
+	else {
+		desc->action = &sb1250_dummy_action;
+		desc->depth = 0;
+	}
+	spin_unlock_irqrestore(&desc->lock,flags);
+	return 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 requred 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 */
+		bus_writeq(IMR_IP2_VAL,
+			   IOADDR(A_IMR_REGISTER(0, R_IMR_INTERRUPT_MAP_BASE) +
+				  (i << 3)));
+		bus_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 */
+	bus_writeq(IMR_IP3_VAL,
+		   IOADDR(A_IMR_REGISTER(0, R_IMR_INTERRUPT_MAP_BASE) +
+			  (K_INT_MBOX_0 << 3)));
+	bus_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 */
+	bus_writeq(0xffffffffffffffffULL,
+		   IOADDR(A_IMR_REGISTER(0, R_IMR_MAILBOX_CLR_CPU)));
+	bus_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);
+	bus_writeq(tmp, IOADDR(A_IMR_REGISTER(0, R_IMR_INTERRUPT_MASK)));
+	bus_writeq(tmp, IOADDR(A_IMR_REGISTER(1, R_IMR_INTERRUPT_MASK)));
+
+	sb1250_steal_irq(K_INT_MBOX_0);
+
+	/*
+	 * Note that the timer interrupts are also mapped, but this is
+	 * done in sb1250_time_init().  Also, the profiling driver 
+	 * does its own management of IP7.
+	 */
+
+#ifdef CONFIG_KGDB
+	imask |= STATUSF_IP6;
+#endif
+	/* Enable necessary IPs, disable the rest */
+	change_c0_status(ST0_IM, imask);
+	set_except_vector(0, sb1250_irq_handler);
+
+#ifdef CONFIG_KGDB
+	if (kgdb_flag) {
+		kgdb_irq = K_INT_UART_0 + kgdb_port;
+
+#ifdef CONFIG_SIBYTE_SB1250_DUART	
+		sb1250_duart_present[kgdb_port] = 0;
+#endif
+		/* Setup uart 1 settings, mapper */
+		bus_writeq(M_DUART_IMR_BRK, IOADDR(A_DUART_IMRREG(kgdb_port)));
+
+		sb1250_steal_irq(kgdb_irq);
+		bus_writeq(IMR_IP6_VAL,
+			   IOADDR(A_IMR_REGISTER(0, R_IMR_INTERRUPT_MAP_BASE) +
+				  (kgdb_irq<<3)));
+		sb1250_unmask_irq(0, kgdb_irq);
+	}
+#endif
+}
+
+#ifdef CONFIG_KGDB
+
+#include <linux/delay.h>
+
+#define duart_out(reg, val)     csr_out32(val, IOADDR(A_DUART_CHANREG(kgdb_port,reg)))
+#define duart_in(reg)           csr_in32(IOADDR(A_DUART_CHANREG(kgdb_port,reg)))
+
+void sb1250_kgdb_interrupt(struct pt_regs *regs)
+{
+	/*
+	 * Clear break-change status (allow some time for the remote
+	 * host to stop the break, since we would see another
+	 * interrupt on the end-of-break too)
+	 */
+	kstat_this_cpu.irqs[kgdb_irq]++;
+	mdelay(500);
+	duart_out(R_DUART_CMD, V_DUART_MISC_CMD_RESET_BREAK_INT |
+				M_DUART_RX_EN | M_DUART_TX_EN);
+	set_async_breakpoint(&regs->cp0_epc);
+}
+
+#endif 	/* CONFIG_KGDB */
diff --git a/arch/mips/sibyte/sb1250/irq_handler.S b/arch/mips/sibyte/sb1250/irq_handler.S
new file mode 100644
index 0000000..60edc8f
--- /dev/null
+++ b/arch/mips/sibyte/sb1250/irq_handler.S
@@ -0,0 +1,147 @@
+/*
+ * Copyright (C) 2000, 2001, 2002, 2003 Broadcom Corporation
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ */
+
+/*
+ * sb1250_handle_int() is the routine that is actually called when an interrupt
+ * occurs.  It is installed as the exception vector handler in arch_init_irq()
+ * in arch/mips/sibyte/sb1250/irq.c
+ *
+ * In the handle we figure out which interrupts need handling, and use that to
+ * call the dispatcher, which will take care of actually calling registered
+ * handlers
+ *
+ * Note that we take care of all raised interrupts in one go at the handler.
+ * This is more BSDish than the Indy code, and also, IMHO, more sane.
+ */
+#include <linux/config.h>
+
+#include <asm/addrspace.h>
+#include <asm/asm.h>
+#include <asm/mipsregs.h>
+#include <asm/regdef.h>
+#include <asm/stackframe.h>
+#include <asm/sibyte/sb1250_defs.h>
+#include <asm/sibyte/sb1250_regs.h>
+#include <asm/sibyte/sb1250_int.h>
+
+/*
+ * 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.
+ */
+
+	.text
+	.set	push
+	.set	noreorder
+	.set	noat
+	.set	mips64
+	.align	5
+	NESTED(sb1250_irq_handler, PT_SIZE, sp)
+	SAVE_ALL
+	CLI
+
+#ifdef CONFIG_SIBYTE_SB1250_PROF
+	/* Set compare to count to silence count/compare timer interrupts */
+	mfc0	t1, CP0_COUNT
+	mtc0	t1, CP0_COMPARE /* pause to clear IP[7] bit of cause ? */
+#endif
+	/* Read cause */
+	mfc0	s0, CP0_CAUSE
+
+#ifdef CONFIG_SIBYTE_SB1250_PROF
+	/* Cpu performance counter interrupt is routed to IP[7] */
+	andi	t1, s0, CAUSEF_IP7
+	beqz	t1, 0f
+	 srl	t1, s0, (CAUSEB_BD-2)  /* Shift BD bit to bit 2 */
+	and	t1, t1, 0x4		/* mask to get just BD bit */
+	mfc0	a0, CP0_EPC
+	jal	sbprof_cpu_intr
+	 addu	a0, a0, t1		/* a0 = EPC + (BD ? 4 :	0) */
+	j	ret_from_irq
+	 nop
+0:
+#endif
+
+	/* Timer interrupt is routed to IP[4] */
+	andi	t1, s0, CAUSEF_IP4
+	beqz	t1, 1f
+	 nop
+	jal	sb1250_timer_interrupt
+	 move	a0, sp			/* Pass the registers along */
+	j	ret_from_irq
+	 nop				# delay slot
+1:
+
+#ifdef CONFIG_SMP
+	/* Mailbox interrupt is routed to IP[3] */
+	andi	 t1, s0, CAUSEF_IP3
+	beqz	 t1, 2f
+	 nop
+	jal	 sb1250_mailbox_interrupt
+	 move    a0, sp
+	j	ret_from_irq
+	 nop				# delay slot
+2:
+#endif
+
+#ifdef CONFIG_KGDB
+	/* KGDB (uart 1) interrupt is routed to IP[6] */
+	andi	t1, s0, CAUSEF_IP6
+	beqz	t1, 1f
+	nop                            # delay slot
+	jal	sb1250_kgdb_interrupt
+         move	a0, sp
+	j	ret_from_irq
+	nop                            # delay slot
+1:
+#endif
+
+	and      t1, s0, CAUSEF_IP2
+	beqz     t1, 4f
+	 nop
+
+	/*
+	 * 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.
+	 */
+	PTR_LA	v0, CKSEG1 + A_IMR_CPU0_BASE
+#ifdef CONFIG_SMP
+	lw	t1, TI_CPU($28)
+	sll	t1, IMR_REGISTER_SPACING_SHIFT
+	addu	v0, t1
+#endif
+	ld	s0, R_IMR_INTERRUPT_STATUS_BASE(v0)	/* read IP[2] status */
+
+	beqz	s0, 4f		/* No interrupts.  Return */
+	 move	a1, sp
+
+3:	dclz	s1, s0		/* Find the next interrupt */
+	dsubu	a0, zero, s1
+	daddiu	a0, a0, 63
+	jal	 do_IRQ
+	 nop
+
+4:	j        ret_from_irq
+	 nop
+
+	.set pop
+	END(sb1250_irq_handler)
diff --git a/arch/mips/sibyte/sb1250/prom.c b/arch/mips/sibyte/sb1250/prom.c
new file mode 100644
index 0000000..de62ab0
--- /dev/null
+++ b/arch/mips/sibyte/sb1250/prom.c
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2000, 2001 Broadcom Corporation
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ */
+
+#include <linux/config.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/blkdev.h>
+#include <linux/bootmem.h>
+#include <linux/smp.h>
+#include <linux/initrd.h>
+
+#include <asm/bootinfo.h>
+#include <asm/reboot.h>
+
+#define MAX_RAM_SIZE ((CONFIG_SIBYTE_STANDALONE_RAM_SIZE * 1024 * 1024) - 1)
+
+static __init void prom_meminit(void)
+{
+#ifdef CONFIG_BLK_DEV_INITRD
+	unsigned long initrd_pstart;
+	unsigned long initrd_pend;
+
+	initrd_pstart = __pa(initrd_start);
+	initrd_pend = __pa(initrd_end);
+	if (initrd_start &&
+	    ((initrd_pstart > MAX_RAM_SIZE)
+	     || (initrd_pend > MAX_RAM_SIZE))) {
+		panic("initrd out of addressable memory");
+	}
+
+	add_memory_region(0, initrd_pstart,
+			  BOOT_MEM_RAM);
+	add_memory_region(initrd_pstart, initrd_pend - initrd_pstart,
+			  BOOT_MEM_RESERVED);
+	add_memory_region(initrd_pend,
+			  (CONFIG_SIBYTE_STANDALONE_RAM_SIZE * 1024 * 1024) - initrd_pend,
+			  BOOT_MEM_RAM);
+#else
+	add_memory_region(0, CONFIG_SIBYTE_STANDALONE_RAM_SIZE * 1024 * 1024,
+			  BOOT_MEM_RAM);
+#endif
+}
+
+void prom_cpu0_exit(void *unused)
+{
+        while (1) ;
+}
+
+static void prom_linux_exit(void)
+{
+#ifdef CONFIG_SMP
+	if (smp_processor_id()) {
+		smp_call_function(prom_cpu0_exit,NULL,1,1);
+	}
+#endif
+	while(1);
+}
+
+/*
+ * prom_init is called just after the cpu type is determined, from setup_arch()
+ */
+void __init prom_init(void)
+{
+	_machine_restart   = (void (*)(char *))prom_linux_exit;
+	_machine_halt      = prom_linux_exit;
+	_machine_power_off = prom_linux_exit;
+
+	strcpy(arcs_cmdline, "root=/dev/ram0 ");
+
+	mips_machgroup = MACH_GROUP_SIBYTE;
+	prom_meminit();
+}
+
+unsigned long __init prom_free_prom_memory(void)
+{
+	/* Not sure what I'm supposed to do here.  Nothing, I think */
+	return 0;
+}
+
+void prom_putchar(char c)
+{
+}
diff --git a/arch/mips/sibyte/sb1250/setup.c b/arch/mips/sibyte/sb1250/setup.c
new file mode 100644
index 0000000..f8c605b
--- /dev/null
+++ b/arch/mips/sibyte/sb1250/setup.c
@@ -0,0 +1,206 @@
+/*
+ * Copyright (C) 2000, 2001, 2002, 2003 Broadcom Corporation
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ */
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/reboot.h>
+#include <linux/string.h>
+
+#include <asm/bootinfo.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;
+unsigned int periph_rev;
+unsigned int zbbus_mhz;
+
+static char *soc_str;
+static char *pass_str;
+static unsigned int war_pass;	/* XXXKW don't overload PASS defines? */
+
+static inline int setup_bcm1250(void);
+static inline int setup_bcm112x(void);
+
+/* Setup code likely to be common to all SiByte platforms */
+
+static inline int 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:
+		prom_printf("Unknown SOC type %x\n", soc_type);
+		ret = 1;
+		break;
+	}
+	return ret;
+}
+
+static inline int 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 {
+			prom_printf("Unknown BCM1250 rev %x\n", soc_pass);
+			ret = 1;
+		}
+		break;
+	}
+	return ret;
+}
+
+static inline int 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;
+	default:
+		prom_printf("Unknown %s rev %x\n", soc_str, soc_pass);
+		ret = 1;
+	}
+	return ret;
+}
+
+void sb1250_setup(void)
+{
+	uint64_t sys_rev;
+	int plldiv;
+	int bad_config = 0;
+
+	sb1_pass = read_c0_prid() & 0xff;
+	sys_rev = bus_readq(IOADDR(A_SCD_SYSTEM_REVISION));
+	soc_type = SYS_SOC_TYPE(sys_rev);
+	soc_pass = G_SYS_REVISION(sys_rev);
+
+	if (sys_rev_decode()) {
+		prom_printf("Restart after failure to identify SiByte chip\n");
+		machine_restart(NULL);
+	}
+
+	plldiv = G_SYS_PLL_DIV(bus_readq(IOADDR(A_SCD_SYSTEM_CFG)));
+	zbbus_mhz = ((plldiv >> 1) * 50) + ((plldiv & 1) * 25);
+
+	prom_printf("Broadcom SiByte %s %s @ %d MHz (SB1 rev %d)\n",
+		    soc_str, pass_str, zbbus_mhz * 2, sb1_pass);
+	prom_printf("Board type: %s\n", get_system_type());
+
+	switch(war_pass) {
+	case K_SYS_REVISION_BCM1250_PASS1:
+#ifndef CONFIG_SB1_PASS_1_WORKAROUNDS
+		prom_printf("@@@@ This is a BCM1250 A0-A2 (Pass 1) board, and the kernel doesn't have the proper workarounds compiled in. @@@@\n");
+		bad_config = 1;
+#endif
+		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)
+		prom_printf("@@@@ 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
+		prom_printf("@@@@ 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
+		prom_printf("@@@@ 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)
+		prom_printf("@@@@ This is a BCM1250 B1/B2, but the kernel is conservatively configured for an 'A' stepping. @@@@\n");
+#endif
+		break;
+	default:
+		break;
+	}
+	if (bad_config) {
+		prom_printf("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 0000000..be91b39
--- /dev/null
+++ b/arch/mips/sibyte/sb1250/smp.c
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2001, 2002, 2003 Broadcom Corporation
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ */
+
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/smp.h>
+#include <linux/kernel_stat.h>
+
+#include <asm/mmu_context.h>
+#include <asm/io.h>
+#include <asm/sibyte/sb1250.h>
+#include <asm/sibyte/sb1250_regs.h>
+#include <asm/sibyte/sb1250_int.h>
+
+static void *mailbox_set_regs[] = {
+	(void *)IOADDR(A_IMR_CPU0_BASE + R_IMR_MAILBOX_SET_CPU),
+	(void *)IOADDR(A_IMR_CPU1_BASE + R_IMR_MAILBOX_SET_CPU)
+};
+
+static void *mailbox_clear_regs[] = {
+	(void *)IOADDR(A_IMR_CPU0_BASE + R_IMR_MAILBOX_CLR_CPU),
+	(void *)IOADDR(A_IMR_CPU1_BASE + R_IMR_MAILBOX_CLR_CPU)
+};
+
+static void *mailbox_regs[] = {
+	(void *)IOADDR(A_IMR_CPU0_BASE + R_IMR_MAILBOX_CPU),
+	(void *)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);
+}
+
+void sb1250_smp_finish(void)
+{
+	extern void sb1250_time_init(void);
+	sb1250_time_init();
+	local_irq_enable();
+}
+
+/*
+ * 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
+ */
+void core_send_ipi(int cpu, unsigned int action)
+{
+	bus_writeq((((u64)action) << 48), mailbox_set_regs[cpu]);
+}
+
+void sb1250_mailbox_interrupt(struct pt_regs *regs)
+{
+	int cpu = smp_processor_id();
+	unsigned int action;
+
+	kstat_this_cpu.irqs[K_INT_MBOX_0]++;
+	/* Load the mailbox register to figure out what we're supposed to do */
+	action = (__bus_readq(mailbox_regs[cpu]) >> 48) & 0xffff;
+
+	/* Clear the mailbox to clear the interrupt */
+	__bus_writeq(((u64)action) << 48, mailbox_clear_regs[cpu]);
+
+	/*
+	 * Nothing to do for SMP_RESCHEDULE_YOURSELF; returning from the
+	 * interrupt will do the reschedule for us
+	 */
+
+	if (action & SMP_CALL_FUNCTION)
+		smp_call_function_interrupt();
+}
diff --git a/arch/mips/sibyte/sb1250/time.c b/arch/mips/sibyte/sb1250/time.c
new file mode 100644
index 0000000..8b4c848
--- /dev/null
+++ b/arch/mips/sibyte/sb1250/time.c
@@ -0,0 +1,136 @@
+/*
+ * Copyright (C) 2000, 2001 Broadcom Corporation
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ */
+
+/*
+ * These are routines to set up and handle interrupts from the
+ * sb1250 general purpose timer 0.  We're using the timer as a
+ * system clock, so we set it up to run at 100 Hz.  On every
+ * interrupt, we update our idea of what the time of day is,
+ * then call do_timer() in the architecture-independent kernel
+ * code to do general bookkeeping (e.g. update jiffies, run
+ * bottom halves, etc.)
+ */
+#include <linux/config.h>
+#include <linux/interrupt.h>
+#include <linux/sched.h>
+#include <linux/spinlock.h>
+#include <linux/kernel_stat.h>
+
+#include <asm/irq.h>
+#include <asm/ptrace.h>
+#include <asm/addrspace.h>
+#include <asm/time.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>
+
+
+#define IMR_IP2_VAL	K_INT_MAP_I0
+#define IMR_IP3_VAL	K_INT_MAP_I1
+#define IMR_IP4_VAL	K_INT_MAP_I2
+
+extern int sb1250_steal_irq(int irq);
+
+void sb1250_time_init(void)
+{
+	int cpu = smp_processor_id();
+	int irq = K_INT_TIMER_0+cpu;
+
+	/* Only have 4 general purpose timers */
+	if (cpu > 3) {
+		BUG();
+	}
+
+	if (!cpu) {
+		/* Use our own gettimeoffset() routine */
+		do_gettimeoffset = sb1250_gettimeoffset;
+	}
+
+	sb1250_mask_irq(cpu, irq);
+
+	/* Map the timer interrupt to ip[4] of this cpu */
+	bus_writeq(IMR_IP4_VAL,
+		   IOADDR(A_IMR_REGISTER(cpu, R_IMR_INTERRUPT_MAP_BASE) +
+			  (irq << 3)));
+
+	/* the general purpose timer ticks at 1 Mhz independent if the rest of the system */
+	/* Disable the timer and set up the count */
+	bus_writeq(0, IOADDR(A_SCD_TIMER_REGISTER(cpu, R_SCD_TIMER_CFG)));
+#ifdef CONFIG_SIMULATION
+	bus_writeq(50000 / HZ,
+		   IOADDR(A_SCD_TIMER_REGISTER(cpu, R_SCD_TIMER_INIT)));
+#else
+	bus_writeq(1000000/HZ,
+		   IOADDR(A_SCD_TIMER_REGISTER(cpu, R_SCD_TIMER_INIT)));
+#endif
+
+	/* Set the timer running */
+	bus_writeq(M_SCD_TIMER_ENABLE | M_SCD_TIMER_MODE_CONTINUOUS,
+		   IOADDR(A_SCD_TIMER_REGISTER(cpu, R_SCD_TIMER_CFG)));
+
+	sb1250_unmask_irq(cpu, irq);
+	sb1250_steal_irq(irq);
+	/*
+	 * This interrupt is "special" in that it doesn't use the request_irq
+	 * way to hook the irq line.  The timer interrupt is initialized early
+	 * enough to make this a major pain, and it's also firing enough to
+	 * warrant a bit of special case code.  sb1250_timer_interrupt is
+	 * called directly from irq_handler.S when IP[4] is set during an
+	 * interrupt
+	 */
+}
+
+void sb1250_timer_interrupt(struct pt_regs *regs)
+{
+	extern asmlinkage void ll_local_timer_interrupt(int irq, struct pt_regs *regs);
+	int cpu = smp_processor_id();
+	int irq = K_INT_TIMER_0 + cpu;
+
+	/* Reset the timer */
+	__bus_writeq(M_SCD_TIMER_ENABLE | M_SCD_TIMER_MODE_CONTINUOUS,
+		     IOADDR(A_SCD_TIMER_REGISTER(cpu, R_SCD_TIMER_CFG)));
+
+	/*
+	 * CPU 0 handles the global timer interrupt job
+	 */
+	if (cpu == 0) {
+		ll_timer_interrupt(irq, regs);
+	}
+
+	/*
+	 * every CPU should do profiling and process accouting
+	 */
+	ll_local_timer_interrupt(irq, regs);
+}
+
+/*
+ * We use our own do_gettimeoffset() instead of the generic one,
+ * because the generic one does not work for SMP case.
+ * In addition, since we use general timer 0 for system time,
+ * we can get accurate intra-jiffy offset without calibration.
+ */
+unsigned long sb1250_gettimeoffset(void)
+{
+	unsigned long count =
+		bus_readq(IOADDR(A_SCD_TIMER_REGISTER(0, R_SCD_TIMER_CNT)));
+
+	return 1000000/HZ - count;
+ }