summaryrefslogtreecommitdiffstats
path: root/sys/mips/sibyte
diff options
context:
space:
mode:
authorimp <imp@FreeBSD.org>2009-07-04 03:05:48 +0000
committerimp <imp@FreeBSD.org>2009-07-04 03:05:48 +0000
commit47d1110ae29b91d9ae9501c1b3322a47e70b6437 (patch)
tree7050090cd7a7f5aa8b93f03560c464a758093178 /sys/mips/sibyte
parenteef46435c0a0407ec0d46b03bce68680c398662e (diff)
downloadFreeBSD-src-47d1110ae29b91d9ae9501c1b3322a47e70b6437.zip
FreeBSD-src-47d1110ae29b91d9ae9501c1b3322a47e70b6437.tar.gz
Add sibyte device support.
Submitted by: Neelkanth Natu
Diffstat (limited to 'sys/mips/sibyte')
-rw-r--r--sys/mips/sibyte/ata_zbbus.c170
-rw-r--r--sys/mips/sibyte/files.sibyte9
-rw-r--r--sys/mips/sibyte/sb_asm.S155
-rw-r--r--sys/mips/sibyte/sb_machdep.c259
-rw-r--r--sys/mips/sibyte/sb_scd.c152
-rw-r--r--sys/mips/sibyte/sb_scd.h46
-rw-r--r--sys/mips/sibyte/sb_zbbus.c501
-rw-r--r--sys/mips/sibyte/sb_zbpci.c283
8 files changed, 1575 insertions, 0 deletions
diff --git a/sys/mips/sibyte/ata_zbbus.c b/sys/mips/sibyte/ata_zbbus.c
new file mode 100644
index 0000000..87a2a42
--- /dev/null
+++ b/sys/mips/sibyte/ata_zbbus.c
@@ -0,0 +1,170 @@
+/*-
+ * Copyright (c) 2009 Neelkanth Natu
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/param.h>
+#include <sys/kernel.h>
+#include <sys/systm.h>
+#include <sys/module.h>
+#include <sys/bus.h>
+#include <sys/rman.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/sema.h>
+#include <sys/taskqueue.h>
+
+#include <machine/bus.h>
+
+#include <vm/uma.h>
+
+#include <sys/ata.h>
+#include <dev/ata/ata-all.h>
+
+#include <machine/resource.h>
+
+__FBSDID("$FreeBSD$");
+
+static int
+ata_zbbus_probe(device_t dev)
+{
+
+ return (ata_probe(dev));
+}
+
+static int
+ata_zbbus_attach(device_t dev)
+{
+ int i, rid, regshift, regoffset;
+ struct ata_channel *ch;
+ struct resource *io;
+
+ ch = device_get_softc(dev);
+
+ if (ch->attached)
+ return (0);
+ ch->attached = 1;
+
+ rid = 0;
+ io = bus_alloc_resource(dev, SYS_RES_MEMORY, &rid, 0, ~0, 1, RF_ACTIVE);
+ if (io == NULL)
+ return (ENXIO);
+
+ /*
+ * SWARM needs an address shift of 5 when accessing ATA registers.
+ *
+ * For e.g. an access to register 4 actually needs an address
+ * of (4 << 5) to be output on the generic bus.
+ */
+ regshift = 5;
+ resource_int_value(device_get_name(dev), device_get_unit(dev),
+ "regshift", &regshift);
+ if (regshift && bootverbose)
+ device_printf(dev, "using a register shift of %d\n", regshift);
+
+ regoffset = 0x1F0;
+ resource_int_value(device_get_name(dev), device_get_unit(dev),
+ "regoffset", &regoffset);
+ if (regoffset && bootverbose) {
+ device_printf(dev, "using a register offset of 0x%0x\n",
+ regoffset);
+ }
+
+ /* setup the ata register addresses */
+ for (i = ATA_DATA; i <= ATA_COMMAND; ++i) {
+ ch->r_io[i].res = io;
+ ch->r_io[i].offset = (regoffset + i) << regshift;
+ }
+
+ ch->r_io[ATA_CONTROL].res = io;
+ ch->r_io[ATA_CONTROL].offset = (regoffset + ATA_CTLOFFSET) << regshift;
+ ch->r_io[ATA_IDX_ADDR].res = io; /* XXX what is this used for */
+ ata_default_registers(dev);
+
+ /* initialize softc for this channel */
+ ch->unit = 0;
+ ch->flags |= ATA_USE_16BIT;
+ ata_generic_hw(dev);
+
+ return (ata_attach(dev));
+}
+
+static int
+ata_zbbus_detach(device_t dev)
+{
+ int error;
+ struct ata_channel *ch = device_get_softc(dev);
+
+ if (!ch->attached)
+ return (0);
+ ch->attached = 0;
+
+ error = ata_detach(dev);
+
+ bus_release_resource(dev, SYS_RES_MEMORY, 0,
+ ch->r_io[ATA_IDX_ADDR].res);
+
+ return (error);
+}
+
+static int
+ata_zbbus_suspend(device_t dev)
+{
+ struct ata_channel *ch = device_get_softc(dev);
+
+ if (!ch->attached)
+ return (0);
+
+ return (ata_suspend(dev));
+}
+
+static int
+ata_zbbus_resume(device_t dev)
+{
+ struct ata_channel *ch = device_get_softc(dev);
+
+ if (!ch->attached)
+ return (0);
+
+ return (ata_resume(dev));
+}
+
+static device_method_t ata_zbbus_methods[] = {
+ /* device interface */
+ DEVMETHOD(device_probe, ata_zbbus_probe),
+ DEVMETHOD(device_attach, ata_zbbus_attach),
+ DEVMETHOD(device_detach, ata_zbbus_detach),
+ DEVMETHOD(device_suspend, ata_zbbus_suspend),
+ DEVMETHOD(device_resume, ata_zbbus_resume),
+
+ { 0, 0 }
+};
+
+static driver_t ata_zbbus_driver = {
+ "ata",
+ ata_zbbus_methods,
+ sizeof(struct ata_channel)
+};
+
+DRIVER_MODULE(ata, zbbus, ata_zbbus_driver, ata_devclass, 0, 0);
diff --git a/sys/mips/sibyte/files.sibyte b/sys/mips/sibyte/files.sibyte
new file mode 100644
index 0000000..56f317d
--- /dev/null
+++ b/sys/mips/sibyte/files.sibyte
@@ -0,0 +1,9 @@
+# $FreeBSD$
+
+mips/sibyte/sb_machdep.c standard
+mips/sibyte/sb_zbbus.c standard
+mips/sibyte/sb_zbpci.c standard
+mips/sibyte/sb_scd.c standard
+mips/sibyte/ata_zbbus.c standard
+
+mips/sibyte/sb_asm.S standard
diff --git a/sys/mips/sibyte/sb_asm.S b/sys/mips/sibyte/sb_asm.S
new file mode 100644
index 0000000..d878113
--- /dev/null
+++ b/sys/mips/sibyte/sb_asm.S
@@ -0,0 +1,155 @@
+/*-
+ * Copyright (c) 2009 Neelkanth Natu
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <machine/asm.h>
+
+/*
+ * We compile a 32-bit kernel to run on the SB-1 processor which is a 64-bit
+ * processor. It has some registers that must be accessed using 64-bit load
+ * and store instructions.
+ *
+ * So we have to resort to assembly because the compiler does not emit the
+ * 'ld' and 'sd' instructions since it thinks that it is compiling for a
+ * 32-bit mips processor.
+ */
+
+.set mips64
+.set noat
+.set noreorder
+
+/*
+ * return (MIPS_PHYS_TO_KSEG1(0x10020008))
+ * Parameters: none
+ */
+LEAF(sb_read_syscfg)
+ lui v0, 0xb002
+ ori v0, v0, 0x8
+ ld v1, 0(v0) /* syscfg = MIPS_PHYS_TO_KSEG1(0x10020008) */
+ move v0, v1
+ dsll32 v0, v0, 0
+ dsrl32 v0, v0, 0 /* v0 = lower_uint32(mask) */
+ jr ra
+ dsrl32 v1, v1, 0 /* v1 = upper_uint32(mask) */
+END(sb_read_syscfg)
+
+/*
+ * MIPS_PHYS_TO_KSEG1(0x10020008) = (uint64_t)val
+ * Parameters:
+ * - lower_uint32(val): a0
+ * - upper_uint32(val): a1
+ */
+LEAF(sb_write_syscfg)
+ lui v0, 0xb002
+ ori v0, v0, 0x8
+ dsll32 a1, a1, 0 /* clear lower 32 bits of a1 */
+ dsll32 a0, a0, 0
+ dsrl32 a0, a0, 0 /* clear upper 32 bits of a0 */
+ or a1, a1, a0
+ sd a1, 0(v0) /* MIPS_PHYS_TO_KSEG1(0x10020008) = val */
+ jr ra
+ nop
+ nop
+END(sb_write_syscfg)
+
+/*
+ * MIPS_PHYS_TO_KSEG1(0x10020028) |= (1 << intsrc)
+ *
+ * Parameters:
+ * - intsrc (a0)
+ */
+LEAF(sb_disable_intsrc)
+ lui v0, 0xb002
+ ori v0, v0, 0x28
+ ld v1, 0(v0) /* mask = MIPS_PHYS_TO_KSEG1(0x10020028) */
+ li a1, 1
+ dsllv a1, a1, a0
+ or a1, a1, v1 /* mask |= (1 << intsrc) */
+ jr ra
+ sd a1, 0(v0) /* MIPS_PHYS_TO_KSEG1(0x10020028) = mask */
+END(sb_disable_intsrc)
+
+/*
+ * MIPS_PHYS_TO_KSEG1(0x10020028) &= ~(1 << intsrc)
+ *
+ * Parameters:
+ * - intsrc (a0)
+ */
+LEAF(sb_enable_intsrc)
+ lui v0, 0xb002
+ ori v0, v0, 0x28
+ ld v1, 0(v0) /* mask = MIPS_PHYS_TO_KSEG1(0x10020028) */
+ li a2, 1
+ dsllv a2, a2, a0
+ nor a2, zero, a2
+ and a2, a2, v1 /* mask &= ~(1 << intsrc) */
+ sd a2, 0(v0) /* MIPS_PHYS_TO_KSEG1(0x10020028) = mask */
+ jr ra
+ nop
+END(sb_enable_intsrc)
+
+/*
+ * return ((uint64_t)MIPS_PHYS_TO_KSEG1(0x10020028))
+ * Parameters: none
+ */
+LEAF(sb_read_intsrc_mask)
+ lui v0, 0xb002
+ ori v0, v0, 0x28
+ ld v1, 0(v0) /* mask = MIPS_PHYS_TO_KSEG1(0x10020028) */
+ move v0, v1
+ dsll32 v0, v0, 0
+ dsrl32 v0, v0, 0 /* v0 = lower_uint32(mask) */
+ jr ra
+ dsrl32 v1, v1, 0 /* v1 = upper_uint32(mask) */
+END(sb_read_intsrc_mask)
+
+/*
+ * return ((uint64_t *)MIPS_PHYS_TO_KSEG1(0x10020200) + intsrc)
+ * Parameters:
+ * - intsrc (a0)
+ */
+LEAF(sb_read_intmap)
+ sll a0, a0, 3 /* compute the offset of the intmap register */
+ lui v0, 0xb002
+ addu a0, a0, v0
+ ld v0, 512(a0) /* v0 = MIPS_PHYS_TO_KSEG1(0x10020200) + off */
+ jr ra
+ nop
+END(sb_read_intmap)
+
+/*
+ * (uint64_t *)MIPS_PHYS_TO_KSEG1(0x10020200) + intsrc = irq
+ * Parameters:
+ * - intsrc (a0)
+ * - irq (a1)
+ */
+LEAF(sb_write_intmap)
+ sll a0, a0, 0x3 /* compute the offset of the intmap register */
+ lui v0, 0xb002
+ addu a0, a0, v0
+ sd a1, 512(a0) /* MIPS_PHYS_TO_KSEG1(0x10020200) + off = irq */
+ jr ra
+ nop
+END(sb_write_intmap)
diff --git a/sys/mips/sibyte/sb_machdep.c b/sys/mips/sibyte/sb_machdep.c
new file mode 100644
index 0000000..79a1b89
--- /dev/null
+++ b/sys/mips/sibyte/sb_machdep.c
@@ -0,0 +1,259 @@
+/*-
+ * Copyright (c) 2007 Bruce M. Simpson.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <machine/cpuregs.h>
+
+#include "opt_ddb.h"
+#include "opt_kdb.h"
+
+#include <sys/param.h>
+#include <sys/conf.h>
+#include <sys/kernel.h>
+#include <sys/systm.h>
+#include <sys/imgact.h>
+#include <sys/bio.h>
+#include <sys/buf.h>
+#include <sys/bus.h>
+#include <sys/cpu.h>
+#include <sys/cons.h>
+#include <sys/exec.h>
+#include <sys/ucontext.h>
+#include <sys/proc.h>
+#include <sys/kdb.h>
+#include <sys/ptrace.h>
+#include <sys/reboot.h>
+#include <sys/signalvar.h>
+#include <sys/sysent.h>
+#include <sys/sysproto.h>
+#include <sys/user.h>
+
+#include <vm/vm.h>
+#include <vm/vm_object.h>
+#include <vm/vm_page.h>
+#include <vm/vm_pager.h>
+
+#include <machine/cache.h>
+#include <machine/clock.h>
+#include <machine/cpu.h>
+#include <machine/cpuinfo.h>
+#include <machine/cpufunc.h>
+#include <machine/cpuregs.h>
+#include <machine/hwfunc.h>
+#include <machine/intr_machdep.h>
+#include <machine/locore.h>
+#include <machine/md_var.h>
+#include <machine/pte.h>
+#include <machine/sigframe.h>
+#include <machine/trap.h>
+#include <machine/vmparam.h>
+
+#ifdef CFE
+#include <dev/cfe/cfe_api.h>
+#endif
+
+#include "sb_scd.h"
+
+#ifdef DDB
+#ifndef KDB
+#error KDB must be enabled in order for DDB to work!
+#endif
+#endif
+
+#ifdef CFE
+extern uint32_t cfe_handle;
+extern uint32_t cfe_vector;
+#endif
+
+#ifdef CFE_ENV
+extern void cfe_env_init(void);
+#endif
+
+extern int *edata;
+extern int *end;
+
+static void
+mips_init(void)
+{
+ int i, cfe_mem_idx, tmp;
+ uint64_t maxmem;
+
+#ifdef CFE_ENV
+ cfe_env_init();
+#endif
+
+ TUNABLE_INT_FETCH("boothowto", &boothowto);
+
+ if (boothowto & RB_VERBOSE)
+ bootverbose++;
+
+#ifdef MAXMEM
+ tmp = MAXMEM;
+#else
+ tmp = 0;
+#endif
+ TUNABLE_INT_FETCH("hw.physmem", &tmp);
+ maxmem = (uint64_t)tmp * 1024;
+
+#ifdef CFE
+ /*
+ * Query DRAM memory map from CFE.
+ */
+ physmem = 0;
+ cfe_mem_idx = 0;
+ for (i = 0; i < 10; i += 2) {
+ int result;
+ uint64_t addr, len, type;
+
+ result = cfe_enummem(cfe_mem_idx++, 0, &addr, &len, &type);
+ if (result < 0) {
+ phys_avail[i] = phys_avail[i + 1] = 0;
+ break;
+ }
+
+ KASSERT(type == CFE_MI_AVAILABLE,
+ ("CFE DRAM region is not available?"));
+
+ if (bootverbose)
+ printf("cfe_enummem: 0x%016jx/%llu.\n", addr, len);
+
+ if (maxmem != 0) {
+ if (addr >= maxmem) {
+ printf("Ignoring %llu bytes of memory at 0x%jx "
+ "that is above maxmem %dMB\n",
+ len, addr,
+ (int)(maxmem / (1024 * 1024)));
+ continue;
+ }
+
+ if (addr + len > maxmem) {
+ printf("Ignoring %llu bytes of memory "
+ "that is above maxmem %dMB\n",
+ (addr + len) - maxmem,
+ (int)(maxmem / (1024 * 1024)));
+ len = maxmem - addr;
+ }
+ }
+
+ phys_avail[i] = addr;
+ if (i == 0 && addr == 0) {
+ /*
+ * If this is the first physical memory segment probed
+ * from CFE, omit the region at the start of physical
+ * memory where the kernel has been loaded.
+ */
+ phys_avail[i] += MIPS_KSEG0_TO_PHYS((vm_offset_t)&end);
+ }
+ phys_avail[i + 1] = addr + len;
+ physmem += len;
+ }
+
+ realmem = btoc(physmem);
+#endif
+
+ physmem = realmem;
+
+ init_param1();
+ init_param2(physmem);
+ mips_cpu_init();
+ pmap_bootstrap();
+ mips_proc0_init();
+ mutex_init();
+
+ kdb_init();
+#ifdef KDB
+ if (boothowto & RB_KDB)
+ kdb_enter(KDB_WHY_BOOTFLAGS, "Boot flags requested debugger");
+#endif
+}
+
+void
+platform_halt(void)
+{
+
+}
+
+
+void
+platform_identify(void)
+{
+
+}
+
+void
+platform_reset(void)
+{
+
+ /*
+ * XXX SMP
+ * XXX flush data caches
+ */
+ sb_system_reset();
+}
+
+void
+platform_trap_enter(void)
+{
+
+}
+
+void
+platform_trap_exit(void)
+{
+
+}
+
+void
+platform_start(__register_t a0 __unused, __register_t a1 __unused,
+ __register_t a2 __unused, __register_t a3 __unused)
+{
+ vm_offset_t kernend;
+
+ /* clear the BSS and SBSS segments */
+ memset(&edata, 0, (vm_offset_t)&end - (vm_offset_t)&edata);
+ kernend = round_page((vm_offset_t)&end);
+
+#ifdef CFE
+ /*
+ * Initialize CFE firmware trampolines before
+ * we initialize the low-level console.
+ */
+ if (cfe_handle != 0)
+ cfe_init(cfe_handle, cfe_vector);
+#endif
+ cninit();
+
+#ifdef CFE
+ if (cfe_handle == 0)
+ panic("CFE was not detected by locore.\n");
+#endif
+ mips_init();
+
+ mips_timer_init_params(sb_cpu_speed(), 0);
+}
diff --git a/sys/mips/sibyte/sb_scd.c b/sys/mips/sibyte/sb_scd.c
new file mode 100644
index 0000000..d6a80e1
--- /dev/null
+++ b/sys/mips/sibyte/sb_scd.c
@@ -0,0 +1,152 @@
+/*-
+ * Copyright (c) 2009 Neelkanth Natu
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+#include <sys/param.h>
+#include <sys/kernel.h>
+#include <sys/systm.h>
+#include <sys/module.h>
+#include <sys/bus.h>
+
+#include <machine/resource.h>
+
+#include "sb_scd.h"
+
+__FBSDID("$FreeBSD$");
+
+/*
+ * System Control and Debug (SCD) unit on the Sibyte ZBbus.
+ */
+
+/*
+ * Extract the value starting at bit position 'b' for 'n' bits from 'x'.
+ */
+#define GET_VAL_64(x, b, n) (((x) >> (b)) & ((1ULL << (n)) - 1))
+
+#define SYSCFG_PLLDIV(x) GET_VAL_64((x), 7, 5)
+
+uint64_t
+sb_cpu_speed(void)
+{
+ int plldiv;
+ const uint64_t MHZ = 1000000;
+
+ plldiv = SYSCFG_PLLDIV(sb_read_syscfg());
+ if (plldiv == 0) {
+ printf("PLL_DIV is 0 - assuming 6 (300MHz).\n");
+ plldiv = 6;
+ }
+
+ return (plldiv * 50 * MHZ);
+}
+
+void
+sb_system_reset(void)
+{
+ uint64_t syscfg;
+
+ const uint64_t SYSTEM_RESET = 1ULL << 60;
+ const uint64_t EXT_RESET = 1ULL << 59;
+ const uint64_t SOFT_RESET = 1ULL << 58;
+
+ syscfg = sb_read_syscfg();
+ syscfg &= ~SOFT_RESET;
+ syscfg |= SYSTEM_RESET | EXT_RESET;
+ sb_write_syscfg(syscfg);
+}
+
+int
+sb_route_intsrc(int intsrc)
+{
+ int intrnum;
+
+ KASSERT(intsrc >= 0 && intsrc < NUM_INTSRC,
+ ("Invalid interrupt source number (%d)", intsrc));
+
+ /*
+ * Interrupt 5 is used by sources internal to the CPU (e.g. timer).
+ * Use a deterministic mapping for the remaining sources to map to
+ * interrupt numbers 0 through 4.
+ */
+ intrnum = intsrc % 5;
+
+ /*
+ * Program the interrupt mapper while we are here.
+ */
+ sb_write_intmap(intsrc, intrnum);
+
+ return (intrnum);
+}
+
+#define SCD_PHYSADDR 0x10000000
+#define SCD_SIZE 0x00060000
+
+static int
+scd_probe(device_t dev)
+{
+
+ device_set_desc(dev, "Broadcom/Sibyte System Control and Debug");
+ return (0);
+}
+
+static int
+scd_attach(device_t dev)
+{
+ int rid;
+ struct resource *res;
+
+ if (bootverbose) {
+ device_printf(dev, "attached.\n");
+ }
+
+ rid = 0;
+ res = bus_alloc_resource(dev, SYS_RES_MEMORY, &rid, SCD_PHYSADDR,
+ SCD_PHYSADDR + SCD_SIZE - 1, SCD_SIZE, 0);
+ if (res == NULL) {
+ panic("Cannot allocate resource for system control and debug.");
+ }
+
+ return (0);
+}
+
+static device_method_t scd_methods[] ={
+ /* Device interface */
+ DEVMETHOD(device_probe, scd_probe),
+ DEVMETHOD(device_attach, scd_attach),
+ DEVMETHOD(device_detach, bus_generic_detach),
+ DEVMETHOD(device_shutdown, bus_generic_shutdown),
+ DEVMETHOD(device_suspend, bus_generic_suspend),
+ DEVMETHOD(device_resume, bus_generic_resume),
+
+ { 0, 0 }
+};
+
+static driver_t scd_driver = {
+ "scd",
+ scd_methods
+};
+
+static devclass_t scd_devclass;
+
+DRIVER_MODULE(scd, zbbus, scd_driver, scd_devclass, 0, 0);
diff --git a/sys/mips/sibyte/sb_scd.h b/sys/mips/sibyte/sb_scd.h
new file mode 100644
index 0000000..8de3aae
--- /dev/null
+++ b/sys/mips/sibyte/sb_scd.h
@@ -0,0 +1,46 @@
+/*-
+ * Copyright (c) 2009 Neelkanth Natu
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef _SB_SCD_H_
+#define _SB_SCD_H_
+
+#define NUM_INTSRC 64 /* total number of interrupt sources */
+
+uint64_t sb_cpu_speed(void);
+void sb_system_reset(void);
+
+int sb_route_intsrc(int src);
+void sb_enable_intsrc(int src);
+void sb_disable_intsrc(int src);
+uint64_t sb_read_intsrc_mask(void);
+
+int sb_read_intmap(int intsrc);
+void sb_write_intmap(int intsrc, int intrnum);
+
+uint64_t sb_read_syscfg(void);
+void sb_write_syscfg(uint64_t val);
+
+#endif /* _SB_SCD_H_ */
diff --git a/sys/mips/sibyte/sb_zbbus.c b/sys/mips/sibyte/sb_zbbus.c
new file mode 100644
index 0000000..e67ae359
--- /dev/null
+++ b/sys/mips/sibyte/sb_zbbus.c
@@ -0,0 +1,501 @@
+/*-
+ * Copyright (c) 2009 Neelkanth Natu
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/param.h>
+#include <sys/kernel.h>
+#include <sys/systm.h>
+#include <sys/module.h>
+#include <sys/bus.h>
+#include <sys/malloc.h>
+#include <sys/rman.h>
+
+#include <machine/resource.h>
+#include <machine/intr_machdep.h>
+
+#include "sb_scd.h"
+
+__FBSDID("$FreeBSD$");
+
+static MALLOC_DEFINE(M_INTMAP, "sb1250 intmap", "Sibyte 1250 Interrupt Mapper");
+
+#define NUM_HARD_IRQS 6
+
+struct sb_intmap {
+ int intsrc; /* interrupt mapper register number (0 - 63) */
+ int active; /* Does this source generate interrupts? */
+
+ /*
+ * The device that the interrupt belongs to. Note that multiple
+ * devices may share an interrupt. For e.g. PCI_INT_x lines.
+ *
+ * The device 'dev' in combination with the 'rid' uniquely
+ * identify this interrupt source.
+ */
+ device_t dev;
+ int rid;
+
+ SLIST_ENTRY(sb_intmap) next;
+};
+
+/*
+ * We register 'sb_intsrc.isrc' using cpu_register_hard_intsrc() for each
+ * hard interrupt source [0-5].
+ *
+ * The mask/unmask callbacks use the information in 'sb_intmap' to figure
+ * out the corresponding interrupt sources to mask/unmask.
+ */
+struct sb_intsrc {
+ struct intsrc isrc;
+ SLIST_HEAD(, sb_intmap) sb_intmap_head;
+};
+
+static struct sb_intsrc sb_intsrc[NUM_HARD_IRQS];
+
+static struct sb_intmap *
+sb_intmap_lookup(int intrnum, device_t dev, int rid)
+{
+ struct sb_intsrc *isrc;
+ struct sb_intmap *map;
+
+ isrc = &sb_intsrc[intrnum];
+ SLIST_FOREACH(map, &isrc->sb_intmap_head, next) {
+ if (dev == map->dev && rid == map->rid)
+ break;
+ }
+ return (map);
+}
+
+/*
+ * Keep track of which (dev,rid) tuple is using the interrupt source.
+ *
+ * We don't actually unmask the interrupt source until the device calls
+ * a bus_setup_intr() on the resource.
+ */
+static void
+sb_intmap_add(int intrnum, device_t dev, int rid, int intsrc)
+{
+ struct sb_intsrc *isrc;
+ struct sb_intmap *map;
+ register_t sr;
+
+ KASSERT(intrnum >= 0 && intrnum < NUM_HARD_IRQS,
+ ("intrnum is out of range: %d", intrnum));
+
+ isrc = &sb_intsrc[intrnum];
+ map = sb_intmap_lookup(intrnum, dev, rid);
+ if (map) {
+ KASSERT(intsrc == map->intsrc,
+ ("%s%d allocating SYS_RES_IRQ resource with rid %d "
+ "with a different intsrc (%d versus %d)",
+ device_get_name(dev), device_get_unit(dev), rid,
+ intsrc, map->intsrc));
+ return;
+ }
+
+ map = malloc(sizeof(*map), M_INTMAP, M_WAITOK | M_ZERO);
+ map->intsrc = intsrc;
+ map->dev = dev;
+ map->rid = rid;
+
+ sr = intr_disable();
+ SLIST_INSERT_HEAD(&isrc->sb_intmap_head, map, next);
+ intr_restore(sr);
+}
+
+static void
+sb_intmap_activate(int intrnum, device_t dev, int rid)
+{
+ struct sb_intmap *map;
+ register_t sr;
+
+ KASSERT(intrnum >= 0 && intrnum < NUM_HARD_IRQS,
+ ("intrnum is out of range: %d", intrnum));
+
+ map = sb_intmap_lookup(intrnum, dev, rid);
+ if (map) {
+ /*
+ * See comments in sb_unmask_func() about disabling cpu intr
+ */
+ sr = intr_disable();
+ map->active = 1;
+ sb_enable_intsrc(map->intsrc);
+ intr_restore(sr);
+ } else {
+ /*
+ * In zbbus_setup_intr() we blindly call sb_intmap_activate()
+ * for every interrupt activation that comes our way.
+ *
+ * We might end up here if we did not "hijack" the SYS_RES_IRQ
+ * resource in zbbus_alloc_resource().
+ */
+ printf("sb_intmap_activate: unable to activate interrupt %d "
+ "for device %s%d rid %d.\n", intrnum,
+ device_get_name(dev), device_get_unit(dev), rid);
+ }
+}
+
+static void
+sb_mask_func(struct intsrc *arg)
+{
+ struct sb_intmap *map;
+ struct sb_intsrc *isrc;
+ uint64_t isrc_bitmap;
+
+ isrc_bitmap = 0;
+ isrc = (struct sb_intsrc *)arg;
+ SLIST_FOREACH(map, &isrc->sb_intmap_head, next) {
+ if (map->active == 0)
+ continue;
+ /*
+ * If we have already disabled this interrupt source then don't
+ * do it again. This can happen when multiple devices share
+ * an interrupt source (e.g. PCI_INT_x).
+ */
+ if (isrc_bitmap & (1ULL << map->intsrc))
+ continue;
+ sb_disable_intsrc(map->intsrc);
+ isrc_bitmap |= 1ULL << map->intsrc;
+ }
+}
+
+static void
+sb_unmask_func(struct intsrc *arg)
+{
+ struct sb_intmap *map;
+ struct sb_intsrc *sb_isrc;
+ uint64_t isrc_bitmap;
+ register_t sr;
+
+ isrc_bitmap = 0;
+ sb_isrc = (struct sb_intsrc *)arg;
+
+ /*
+ * Make sure we disable the cpu interrupts when enabling the
+ * interrupt sources.
+ *
+ * This is to prevent a condition where some interrupt sources have
+ * been enabled (but not all) and one of those interrupt sources
+ * triggers an interrupt.
+ *
+ * If any of the interrupt handlers executes in an ithread then
+ * cpu_intr() will return with all interrupt sources feeding into
+ * that cpu irq masked. But when the loop below picks up where it
+ * left off it will enable the remaining interrupt sources!!!
+ *
+ * If the disable the cpu interrupts then this race does not happen.
+ */
+ sr = intr_disable();
+
+ SLIST_FOREACH(map, &sb_isrc->sb_intmap_head, next) {
+ if (map->active == 0)
+ continue;
+ /*
+ * If we have already enabled this interrupt source then don't
+ * do it again. This can happen when multiple devices share
+ * an interrupt source (e.g. PCI_INT_x).
+ */
+ if (isrc_bitmap & (1ULL << map->intsrc))
+ continue;
+ sb_enable_intsrc(map->intsrc);
+ isrc_bitmap |= 1ULL << map->intsrc;
+ }
+
+ intr_restore(sr);
+}
+
+struct zbbus_devinfo {
+ struct resource_list resources;
+};
+
+static MALLOC_DEFINE(M_ZBBUSDEV, "zbbusdev", "zbbusdev");
+
+static int
+zbbus_probe(device_t dev)
+{
+
+ device_set_desc(dev, "Broadcom/Sibyte ZBbus");
+ return (0);
+}
+
+static int
+zbbus_attach(device_t dev)
+{
+ int i, error;
+ struct intsrc *isrc;
+
+ if (bootverbose) {
+ device_printf(dev, "attached.\n");
+ }
+
+ for (i = 0; i < NUM_HARD_IRQS; ++i) {
+ isrc = &sb_intsrc[i].isrc;
+ isrc->intrnum = i;
+ isrc->mask_func = sb_mask_func;
+ isrc->unmask_func = sb_unmask_func;
+ error = cpu_register_hard_intsrc(isrc);
+ if (error)
+ panic("Error %d registering intsrc %d", error, i);
+ }
+
+ bus_generic_probe(dev);
+ bus_enumerate_hinted_children(dev);
+ bus_generic_attach(dev);
+
+ return (0);
+}
+
+static void
+zbbus_hinted_child(device_t bus, const char *dname, int dunit)
+{
+ device_t child;
+ long maddr, msize;
+ int err, irq;
+
+ if (resource_disabled(dname, dunit))
+ return;
+
+ child = BUS_ADD_CHILD(bus, 0, dname, dunit);
+ if (child == NULL) {
+ panic("zbbus: could not add child %s unit %d\n", dname, dunit);
+ }
+
+ if (bootverbose)
+ device_printf(bus, "Adding hinted child %s%d\n", dname, dunit);
+
+ /*
+ * Assign any pre-defined resources to the child.
+ */
+ if (resource_long_value(dname, dunit, "msize", &msize) == 0 &&
+ resource_long_value(dname, dunit, "maddr", &maddr) == 0) {
+ if (bootverbose) {
+ device_printf(bus, "Assigning memory resource "
+ "0x%0lx/%ld to child %s%d\n",
+ maddr, msize, dname, dunit);
+ }
+ err = bus_set_resource(child, SYS_RES_MEMORY, 0, maddr, msize);
+ if (err) {
+ device_printf(bus, "Unable to set memory resource "
+ "0x%0lx/%ld for child %s%d: %d\n",
+ maddr, msize, dname, dunit, err);
+ }
+ }
+
+ if (resource_int_value(dname, dunit, "irq", &irq) == 0) {
+ if (bootverbose) {
+ device_printf(bus, "Assigning irq resource %d to "
+ "child %s%d\n", irq, dname, dunit);
+ }
+ err = bus_set_resource(child, SYS_RES_IRQ, 0, irq, 1);
+ if (err) {
+ device_printf(bus, "Unable to set irq resource %d"
+ "for child %s%d: %d\n",
+ irq, dname, dunit, err);
+ }
+ }
+}
+
+static struct resource *
+zbbus_alloc_resource(device_t bus, device_t child, int type, int *rid,
+ u_long start, u_long end, u_long count, u_int flags)
+{
+ struct resource *res;
+ int intrnum, intsrc, isdefault;
+ struct resource_list *rl;
+ struct resource_list_entry *rle;
+ struct zbbus_devinfo *dinfo;
+
+ isdefault = (start == 0UL && end == ~0UL && count == 1);
+
+ /*
+ * Our direct child is asking for a default resource allocation.
+ */
+ if (device_get_parent(child) == bus) {
+ dinfo = device_get_ivars(child);
+ rl = &dinfo->resources;
+ rle = resource_list_find(rl, type, *rid);
+ if (rle) {
+ if (rle->res)
+ panic("zbbus_alloc_resource: resource is busy");
+ if (isdefault) {
+ start = rle->start;
+ count = ulmax(count, rle->count);
+ end = ulmax(rle->end, start + count - 1);
+ }
+ } else {
+ if (isdefault) {
+ /*
+ * Our child is requesting a default
+ * resource allocation but we don't have the
+ * 'type/rid' tuple in the resource list.
+ *
+ * We have to fail the resource allocation.
+ */
+ return (NULL);
+ } else {
+ /*
+ * The child is requesting a non-default
+ * resource. We just pass the request up
+ * to our parent. If the resource allocation
+ * succeeds we will create a resource list
+ * entry corresponding to that resource.
+ */
+ }
+ }
+ } else {
+ rl = NULL;
+ rle = NULL;
+ }
+
+ /*
+ * nexus doesn't know about the interrupt mapper and only wants to
+ * see the hard irq numbers [0-6]. We translate from the interrupt
+ * source presented to the mapper to the interrupt number presented
+ * to the cpu.
+ */
+ if ((count == 1) && (type == SYS_RES_IRQ)) {
+ intsrc = start;
+ intrnum = sb_route_intsrc(intsrc);
+ start = end = intrnum;
+ } else {
+ intsrc = -1; /* satisfy gcc */
+ intrnum = -1;
+ }
+
+ res = bus_generic_alloc_resource(bus, child, type, rid,
+ start, end, count, flags);
+
+ /*
+ * Keep track of the input into the interrupt mapper that maps
+ * to the resource allocated by 'child' with resource id 'rid'.
+ *
+ * If we don't record the mapping here then we won't be able to
+ * locate the interrupt source when bus_setup_intr(child,rid) is
+ * called.
+ */
+ if (res != NULL && intrnum != -1)
+ sb_intmap_add(intrnum, child, rman_get_rid(res), intsrc);
+
+ /*
+ * If a non-default resource allocation by our child was successful
+ * then keep track of the resource in the resource list associated
+ * with the child.
+ */
+ if (res != NULL && rle == NULL && device_get_parent(child) == bus) {
+ resource_list_add(rl, type, *rid, start, end, count);
+ rle = resource_list_find(rl, type, *rid);
+ if (rle == NULL)
+ panic("zbbus_alloc_resource: cannot find resource");
+ }
+
+ if (rle != NULL) {
+ KASSERT(device_get_parent(child) == bus,
+ ("rle should be NULL for passthru device"));
+ rle->res = res;
+ if (rle->res) {
+ rle->start = rman_get_start(rle->res);
+ rle->end = rman_get_end(rle->res);
+ rle->count = count;
+ }
+ }
+
+ return (res);
+}
+
+static int
+zbbus_setup_intr(device_t dev, device_t child, struct resource *irq, int flags,
+ driver_filter_t *filter, driver_intr_t *intr, void *arg,
+ void **cookiep)
+{
+ int error;
+
+ error = bus_generic_setup_intr(dev, child, irq, flags,
+ filter, intr, arg, cookiep);
+ if (error == 0)
+ sb_intmap_activate(rman_get_start(irq), child,
+ rman_get_rid(irq));
+
+ return (error);
+}
+
+static device_t
+zbbus_add_child(device_t bus, int order, const char *name, int unit)
+{
+ device_t child;
+ struct zbbus_devinfo *dinfo;
+
+ child = device_add_child_ordered(bus, order, name, unit);
+ if (child != NULL) {
+ dinfo = malloc(sizeof(struct zbbus_devinfo), M_ZBBUSDEV,
+ M_WAITOK | M_ZERO);
+ resource_list_init(&dinfo->resources);
+ device_set_ivars(child, dinfo);
+ }
+
+ return (child);
+}
+
+static struct resource_list *
+zbbus_get_resource_list(device_t dev, device_t child)
+{
+ struct zbbus_devinfo *dinfo = device_get_ivars(child);
+
+ return (&dinfo->resources);
+}
+
+static device_method_t zbbus_methods[] ={
+ /* Device interface */
+ DEVMETHOD(device_probe, zbbus_probe),
+ DEVMETHOD(device_attach, zbbus_attach),
+ DEVMETHOD(device_detach, bus_generic_detach),
+ DEVMETHOD(device_shutdown, bus_generic_shutdown),
+ DEVMETHOD(device_suspend, bus_generic_suspend),
+ DEVMETHOD(device_resume, bus_generic_resume),
+
+ /* Bus interface */
+ DEVMETHOD(bus_alloc_resource, zbbus_alloc_resource),
+ DEVMETHOD(bus_activate_resource, bus_generic_activate_resource),
+ DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource),
+ DEVMETHOD(bus_release_resource, bus_generic_release_resource),
+ DEVMETHOD(bus_get_resource_list,zbbus_get_resource_list),
+ DEVMETHOD(bus_set_resource, bus_generic_rl_set_resource),
+ DEVMETHOD(bus_get_resource, bus_generic_rl_get_resource),
+ DEVMETHOD(bus_delete_resource, bus_generic_rl_delete_resource),
+ DEVMETHOD(bus_setup_intr, zbbus_setup_intr),
+ DEVMETHOD(bus_teardown_intr, bus_generic_teardown_intr),
+ DEVMETHOD(bus_add_child, zbbus_add_child),
+ DEVMETHOD(bus_hinted_child, zbbus_hinted_child),
+
+ { 0, 0 }
+};
+
+static driver_t zbbus_driver = {
+ "zbbus",
+ zbbus_methods
+};
+
+static devclass_t zbbus_devclass;
+
+DRIVER_MODULE(zbbus, nexus, zbbus_driver, zbbus_devclass, 0, 0);
diff --git a/sys/mips/sibyte/sb_zbpci.c b/sys/mips/sibyte/sb_zbpci.c
new file mode 100644
index 0000000..6017aa5
--- /dev/null
+++ b/sys/mips/sibyte/sb_zbpci.c
@@ -0,0 +1,283 @@
+/*-
+ * Copyright (c) 2009 Neelkanth Natu
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/kernel.h>
+#include <sys/systm.h>
+#include <sys/module.h>
+#include <sys/bus.h>
+#include <sys/pcpu.h>
+#include <sys/smp.h>
+
+#include <vm/vm.h>
+#include <vm/vm_param.h>
+#include <vm/vm_kern.h>
+#include <vm/vm_extern.h>
+#include <vm/pmap.h>
+
+#include <dev/pci/pcivar.h>
+#include <dev/pci/pcib_private.h>
+
+#include <machine/pmap.h>
+#include <machine/resource.h>
+
+#include "pcib_if.h"
+
+#include "sb_scd.h"
+
+__FBSDID("$FreeBSD$");
+
+static struct {
+ vm_offset_t vaddr;
+ vm_paddr_t paddr;
+} zbpci_config_space[MAXCPU];
+
+static const vm_paddr_t CFG_PADDR_BASE = 0xFE000000;
+
+static int
+zbpci_probe(device_t dev)
+{
+
+ device_set_desc(dev, "Broadcom/Sibyte PCI I/O Bridge");
+ return (0);
+}
+
+static int
+zbpci_attach(device_t dev)
+{
+ int n, rid, size;
+ vm_offset_t va;
+ struct resource *res;
+
+ /*
+ * Reserve the the physical memory that is used to read/write to the
+ * pci config space but don't activate it. We are using a page worth
+ * of KVA as a window over this region.
+ */
+ rid = 0;
+ size = (PCI_BUSMAX + 1) * (PCI_SLOTMAX + 1) * (PCI_FUNCMAX + 1) * 256;
+ res = bus_alloc_resource(dev, SYS_RES_MEMORY, &rid, CFG_PADDR_BASE,
+ CFG_PADDR_BASE + size - 1, size, 0);
+ if (res == NULL) {
+ panic("Cannot allocate resource for config space accesses.");
+ }
+
+ /*
+ * Allocate KVA for accessing PCI config space.
+ */
+ va = kmem_alloc_nofault(kernel_map, PAGE_SIZE * mp_ncpus);
+ if (va == 0) {
+ device_printf(dev, "Cannot allocate virtual addresses for "
+ "config space access.\n");
+ return (ENOMEM);
+ }
+
+ for (n = 0; n < mp_ncpus; ++n) {
+ zbpci_config_space[n].vaddr = va + n * PAGE_SIZE;
+ }
+
+ /*
+ * Sibyte has the PCI bus hierarchy rooted at bus 0 and HT-PCI
+ * hierarchy rooted at bus 1.
+ */
+ if (device_add_child(dev, "pci", 0) == NULL) {
+ panic("zbpci_attach: could not add pci bus 0.\n");
+ }
+
+ if (device_add_child(dev, "pci", 1) == NULL) {
+ panic("zbpci_attach: could not add pci bus 1.\n");
+ }
+
+ if (bootverbose) {
+ device_printf(dev, "attached.\n");
+ }
+
+ return (bus_generic_attach(dev));
+}
+
+static int
+zbpci_read_ivar(device_t dev, device_t child, int which, uintptr_t *result)
+{
+
+ switch (which) {
+ case PCIB_IVAR_DOMAIN:
+ *result = 0; /* single PCI domain */
+ return (0);
+ case PCIB_IVAR_BUS:
+ *result = device_get_unit(child); /* PCI bus 0 or 1 */
+ return (0);
+ default:
+ return (ENOENT);
+ }
+}
+
+/*
+ * We rely on the CFE to have configured the intline correctly to point to
+ * one of PCI-A/PCI-B/PCI-C/PCI-D in the interupt mapper.
+ */
+static int
+zbpci_route_interrupt(device_t pcib, device_t dev, int pin)
+{
+
+ return (PCI_INVALID_IRQ);
+}
+
+/*
+ * This function is expected to be called in a critical section since it
+ * changes the per-cpu pci config space va-to-pa mappings.
+ */
+static vm_offset_t
+zbpci_config_space_va(int bus, int slot, int func, int reg, int bytes)
+{
+ int cpu;
+ vm_offset_t va_page;
+ vm_paddr_t pa, pa_page;
+
+ if (bus <= PCI_BUSMAX && slot <= PCI_SLOTMAX && func <= PCI_FUNCMAX &&
+ reg <= PCI_REGMAX && (bytes == 1 || bytes == 2 || bytes == 4) &&
+ ((reg & (bytes - 1)) == 0)) {
+ cpu = PCPU_GET(cpuid);
+ va_page = zbpci_config_space[cpu].vaddr;
+ pa = CFG_PADDR_BASE |
+ (bus << 16) | (slot << 11) | (func << 8) | reg;
+ pa_page = pa & ~(PAGE_SIZE - 1);
+ if (zbpci_config_space[cpu].paddr != pa_page) {
+ pmap_kremove(va_page);
+ pmap_kenter(va_page, pa_page);
+ zbpci_config_space[cpu].paddr = pa_page;
+ }
+ return (va_page + (pa - pa_page));
+ } else {
+ return (0);
+ }
+}
+
+static uint32_t
+zbpci_read_config(device_t dev, u_int b, u_int s, u_int f, u_int r, int w)
+{
+ uint32_t data;
+ vm_offset_t va;
+
+ critical_enter();
+
+ va = zbpci_config_space_va(b, s, f, r, w);
+ if (va == 0) {
+ panic("zbpci_read_config: invalid %d/%d/%d[%d] %d\n",
+ b, s, f, r, w);
+ }
+
+ switch (w) {
+ case 4:
+ data = *(uint32_t *)va;
+ break;
+ case 2:
+ data = *(uint16_t *)va;
+ break;
+ case 1:
+ data = *(uint8_t *)va;
+ break;
+ default:
+ panic("zbpci_read_config: invalid width %d\n", w);
+ }
+
+ critical_exit();
+
+ return (data);
+}
+
+static void
+zbpci_write_config(device_t d, u_int b, u_int s, u_int f, u_int r,
+ uint32_t data, int w)
+{
+ vm_offset_t va;
+
+ critical_enter();
+
+ va = zbpci_config_space_va(b, s, f, r, w);
+ if (va == 0) {
+ panic("zbpci_write_config: invalid %d/%d/%d[%d] %d/%d\n",
+ b, s, f, r, data, w);
+ }
+
+ switch (w) {
+ case 4:
+ *(uint32_t *)va = data;
+ break;
+ case 2:
+ *(uint16_t *)va = data;
+ break;
+ case 1:
+ *(uint8_t *)va = data;
+ break;
+ default:
+ panic("zbpci_write_config: invalid width %d\n", w);
+ }
+
+ critical_exit();
+}
+
+static device_method_t zbpci_methods[] ={
+ /* Device interface */
+ DEVMETHOD(device_probe, zbpci_probe),
+ DEVMETHOD(device_attach, zbpci_attach),
+ DEVMETHOD(device_detach, bus_generic_detach),
+ DEVMETHOD(device_shutdown, bus_generic_shutdown),
+ DEVMETHOD(device_suspend, bus_generic_suspend),
+ DEVMETHOD(device_resume, bus_generic_resume),
+
+ /* Bus interface */
+ DEVMETHOD(bus_read_ivar, zbpci_read_ivar),
+ DEVMETHOD(bus_write_ivar, bus_generic_write_ivar),
+ DEVMETHOD(bus_alloc_resource, bus_generic_alloc_resource),
+ DEVMETHOD(bus_activate_resource, bus_generic_activate_resource),
+ DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource),
+ DEVMETHOD(bus_release_resource, bus_generic_release_resource),
+ DEVMETHOD(bus_setup_intr, bus_generic_setup_intr),
+ DEVMETHOD(bus_teardown_intr, bus_generic_teardown_intr),
+ DEVMETHOD(bus_add_child, bus_generic_add_child),
+
+ /* pcib interface */
+ DEVMETHOD(pcib_maxslots, pcib_maxslots),
+ DEVMETHOD(pcib_read_config, zbpci_read_config),
+ DEVMETHOD(pcib_write_config, zbpci_write_config),
+ DEVMETHOD(pcib_route_interrupt, zbpci_route_interrupt),
+
+ { 0, 0 }
+};
+
+/*
+ * The "zbpci" class inherits from the "pcib" base class. Therefore in
+ * addition to drivers that belong to the "zbpci" class we will also
+ * consider drivers belonging to the "pcib" when probing children of
+ * "zbpci".
+ */
+DECLARE_CLASS(pcib_driver);
+DEFINE_CLASS_1(zbpci, zbpci_driver, zbpci_methods, 0, pcib_driver);
+
+static devclass_t zbpci_devclass;
+
+DRIVER_MODULE(zbpci, zbbus, zbpci_driver, zbpci_devclass, 0, 0);
OpenPOWER on IntegriCloud