From f94784e8182eea2a175f8a183eb69578e8a44392 Mon Sep 17 00:00:00 2001 From: Warner Losh Date: Sat, 4 Jul 2009 03:05:48 +0000 Subject: [PATCH] Add sibyte device support. Submitted by: Neelkanth Natu --- sys/mips/sibyte/ata_zbbus.c | 170 ++++++++++++ sys/mips/sibyte/files.sibyte | 9 + sys/mips/sibyte/sb_asm.S | 155 +++++++++++ sys/mips/sibyte/sb_machdep.c | 259 ++++++++++++++++++ sys/mips/sibyte/sb_scd.c | 152 +++++++++++ sys/mips/sibyte/sb_scd.h | 46 ++++ sys/mips/sibyte/sb_zbbus.c | 501 +++++++++++++++++++++++++++++++++++ sys/mips/sibyte/sb_zbpci.c | 283 ++++++++++++++++++++ 8 files changed, 1575 insertions(+) create mode 100644 sys/mips/sibyte/ata_zbbus.c create mode 100644 sys/mips/sibyte/files.sibyte create mode 100644 sys/mips/sibyte/sb_asm.S create mode 100644 sys/mips/sibyte/sb_machdep.c create mode 100644 sys/mips/sibyte/sb_scd.c create mode 100644 sys/mips/sibyte/sb_scd.h create mode 100644 sys/mips/sibyte/sb_zbbus.c create mode 100644 sys/mips/sibyte/sb_zbpci.c diff --git a/sys/mips/sibyte/ata_zbbus.c b/sys/mips/sibyte/ata_zbbus.c new file mode 100644 index 000000000000..87a2a42943e9 --- /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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +#include +#include + +#include + +__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", ®shift); + 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", ®offset); + 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 000000000000..56f317d31e3a --- /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 000000000000..d878113c0227 --- /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 + +/* + * 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 000000000000..79a1b8975cc8 --- /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 +__FBSDID("$FreeBSD$"); + +#include +#include + +#include "opt_ddb.h" +#include "opt_kdb.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef CFE +#include +#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 000000000000..d6a80e1be1b1 --- /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 +#include +#include +#include +#include + +#include + +#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 000000000000..8de3aae7dd74 --- /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 000000000000..e67ae3592978 --- /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 +#include +#include +#include +#include +#include +#include + +#include +#include + +#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 000000000000..6017aa5ff8c4 --- /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 +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +#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);