diff options
author | raj <raj@FreeBSD.org> | 2008-03-03 17:17:00 +0000 |
---|---|---|
committer | raj <raj@FreeBSD.org> | 2008-03-03 17:17:00 +0000 |
commit | 0757a4afb5d18c5b874cc918eb56d7264456bd20 (patch) | |
tree | b0d8321058cccbf59aa2e7cd69b9283a0663316e /sys/powerpc/mpc85xx | |
parent | 05437e53d55e216714c2e1f4a0aa97d4598090b8 (diff) | |
download | FreeBSD-src-0757a4afb5d18c5b874cc918eb56d7264456bd20.zip FreeBSD-src-0757a4afb5d18c5b874cc918eb56d7264456bd20.tar.gz |
Initial support for Freescale PowerQUICC III MPC85xx system-on-chip family.
The PQ3 is a high performance integrated communications processing system
based on the e500 core, which is an embedded RISC processor that implements
the 32-bit Book E definition of the PowerPC architecture. For details refer
to: http://www.freescale.com/webapp/sps/site/prod_summary.jsp?code=MPC8555E
This port was tested and successfully run on the following members of the PQ3
family: MPC8533, MPC8541, MPC8548, MPC8555.
The following major integrated peripherals are supported:
* On-chip peripherals bus
* OpenPIC interrupt controller
* UART
* Ethernet (TSEC)
* Host/PCI bridge
* QUICC engine (SCC functionality)
This commit brings the main functionality and will be followed by individual
drivers that are logically separate from this base.
Approved by: cognet (mentor)
Obtained from: Juniper, Semihalf
MFp4: e500
Diffstat (limited to 'sys/powerpc/mpc85xx')
-rw-r--r-- | sys/powerpc/mpc85xx/nexus.c | 144 | ||||
-rw-r--r-- | sys/powerpc/mpc85xx/ocpbus.c | 585 | ||||
-rw-r--r-- | sys/powerpc/mpc85xx/ocpbus.h | 86 | ||||
-rw-r--r-- | sys/powerpc/mpc85xx/opic.c | 99 | ||||
-rw-r--r-- | sys/powerpc/mpc85xx/pci_ocp.c | 781 |
5 files changed, 1695 insertions, 0 deletions
diff --git a/sys/powerpc/mpc85xx/nexus.c b/sys/powerpc/mpc85xx/nexus.c new file mode 100644 index 0000000..01e0425 --- /dev/null +++ b/sys/powerpc/mpc85xx/nexus.c @@ -0,0 +1,144 @@ +/*- + * Copyright 1998 Massachusetts Institute of Technology + * + * Permission to use, copy, modify, and distribute this software and + * its documentation for any purpose and without fee is hereby + * granted, provided that both the above copyright notice and this + * permission notice appear in all copies, that both the above + * copyright notice and this permission notice appear in all + * supporting documentation, and that the name of M.I.T. not be used + * in advertising or publicity pertaining to distribution of the + * software without specific, written prior permission. M.I.T. makes + * no representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied + * warranty. + * + * THIS SOFTWARE IS PROVIDED BY M.I.T. ``AS IS''. M.I.T. DISCLAIMS + * ALL EXPRESS OR IMPLIED WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT + * SHALL M.I.T. 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. + */ +/*- + * Copyright 2001 by Thomas Moestl <tmm@FreeBSD.org>. 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. + * + * from: FreeBSD: src/sys/i386/i386/nexus.c,v 1.43 2001/02/09 + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/module.h> +#include <sys/bus.h> +#include <sys/cons.h> +#include <sys/kernel.h> +#include <sys/malloc.h> +#include <sys/rman.h> + +/* + * Device interface + */ +static int nexus_probe(device_t); +static int nexus_activate_resource(device_t, device_t, int, int, + struct resource *); +static int nexus_deactivate_resource(device_t, device_t, int, int, + struct resource *); + +/* + * Local routines + */ + +static device_method_t nexus_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, nexus_probe), + DEVMETHOD(device_attach, bus_generic_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. Resource management is business of the children... */ + DEVMETHOD(bus_add_child, NULL), + DEVMETHOD(bus_print_child, bus_generic_print_child), + DEVMETHOD(bus_probe_nomatch, NULL), + DEVMETHOD(bus_read_ivar, NULL), + DEVMETHOD(bus_write_ivar, NULL), + DEVMETHOD(bus_setup_intr, NULL), + DEVMETHOD(bus_teardown_intr, NULL), + DEVMETHOD(bus_alloc_resource, NULL), + DEVMETHOD(bus_activate_resource, nexus_activate_resource), + DEVMETHOD(bus_deactivate_resource, nexus_deactivate_resource), + DEVMETHOD(bus_release_resource, NULL), + + { 0, 0 } +}; + +static driver_t nexus_driver = { + "nexus", + nexus_methods +}; + +static devclass_t nexus_devclass; + +DRIVER_MODULE(nexus, root, nexus_driver, nexus_devclass, 0, 0); + +static int +nexus_probe (device_t dev) +{ + + /* + * Add OCP (on-chip peripheral) bus + */ + device_add_child(dev, "ocpbus", 0); + + device_set_desc(dev, "MPC85xx Nexus device"); + return (0); +} + +static int +nexus_activate_resource (device_t bus, device_t child, int type, int rid, + struct resource *res) +{ + + /* Not much to be done yet... */ + return (rman_activate_resource(res)); +} + +static int +nexus_deactivate_resource (device_t bus, device_t child, int type, int rid, + struct resource *res) +{ + + /* Not much to be done yet... */ + return (rman_deactivate_resource(res)); +} diff --git a/sys/powerpc/mpc85xx/ocpbus.c b/sys/powerpc/mpc85xx/ocpbus.c new file mode 100644 index 0000000..903a8e2 --- /dev/null +++ b/sys/powerpc/mpc85xx/ocpbus.c @@ -0,0 +1,585 @@ +/*- + * Copyright 2006 by Juniper Networks. 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. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 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 <sys/systm.h> +#include <sys/ktr.h> +#include <sys/kernel.h> +#include <sys/module.h> +#include <sys/bus.h> +#include <sys/rman.h> +#include <sys/malloc.h> + +#include <machine/spr.h> +#include <machine/pio.h> +#include <machine/ocpbus.h> +#include <machine/intr_machdep.h> +#include <machine/md_var.h> +#include <machine/vmparam.h> +#include <machine/bootinfo.h> + +#include <powerpc/mpc85xx/ocpbus.h> + +#include "pic_if.h" + +extern struct bus_space bs_be_tag; + +struct ocpbus_softc { + struct rman sc_mem; + struct rman sc_irq; + device_t sc_pic; +}; + +struct ocp_devinfo { + int ocp_devtype; + int ocp_unit; +}; + +static int ocpbus_probe(device_t); +static int ocpbus_attach(device_t); +static int ocpbus_shutdown(device_t); +static int ocpbus_get_resource(device_t, device_t, int, int, u_long *, + u_long *); +static struct resource *ocpbus_alloc_resource(device_t, device_t, int, int *, + u_long, u_long, u_long, u_int); +static int ocpbus_print_child(device_t, device_t); +static int ocpbus_release_resource(device_t, device_t, int, int, + struct resource *); +static int ocpbus_read_ivar(device_t, device_t, int, uintptr_t *); +static int ocpbus_setup_intr(device_t, device_t, struct resource *, int, + driver_filter_t *, driver_intr_t *, void *, void **); +static int ocpbus_teardown_intr(device_t, device_t, struct resource *, void *); + +/* + * Bus interface definition + */ +static device_method_t ocpbus_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, ocpbus_probe), + DEVMETHOD(device_attach, ocpbus_attach), + DEVMETHOD(device_shutdown, ocpbus_shutdown), + + /* Bus interface */ + DEVMETHOD(bus_print_child, ocpbus_print_child), + DEVMETHOD(bus_read_ivar, ocpbus_read_ivar), + DEVMETHOD(bus_setup_intr, ocpbus_setup_intr), + DEVMETHOD(bus_teardown_intr, ocpbus_teardown_intr), + + DEVMETHOD(bus_get_resource, ocpbus_get_resource), + DEVMETHOD(bus_alloc_resource, ocpbus_alloc_resource), + DEVMETHOD(bus_release_resource, ocpbus_release_resource), + DEVMETHOD(bus_activate_resource, bus_generic_activate_resource), + DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource), + + { 0, 0 } +}; + +static driver_t ocpbus_driver = { + "ocpbus", + ocpbus_methods, + sizeof(struct ocpbus_softc) +}; + +devclass_t ocpbus_devclass; + +DRIVER_MODULE(ocpbus, nexus, ocpbus_driver, ocpbus_devclass, 0, 0); + +static device_t +ocpbus_mk_child(device_t dev, int type, int unit) +{ + struct ocp_devinfo *dinfo; + device_t child; + + child = device_add_child(dev, NULL, -1); + if (child == NULL) { + device_printf(dev, "could not add child device\n"); + return (NULL); + } + dinfo = malloc(sizeof(struct ocp_devinfo), M_DEVBUF, M_WAITOK|M_ZERO); + dinfo->ocp_devtype = type; + dinfo->ocp_unit = unit; + device_set_ivars(child, dinfo); + return (child); +} + +static int +ocpbus_write_law(int trgt, int type, u_long *startp, u_long *countp) +{ + u_long addr, size; + int i; + uint32_t bar, sr; + + switch (type) { + case SYS_RES_MEMORY: + switch (trgt) { + case OCP85XX_TGTIF_PCI0: + addr = 0x80000000; + size = 0x10000000; + break; + case OCP85XX_TGTIF_PCI1: + addr = 0x90000000; + size = 0x10000000; + break; + case OCP85XX_TGTIF_PCI2: + addr = 0xA0000000; + size = 0x10000000; + break; + default: + return (EINVAL); + } + break; + case SYS_RES_IOPORT: + switch (trgt) { + case OCP85XX_TGTIF_PCI0: + addr = 0xff000000; + size = 0x00010000; + break; + case OCP85XX_TGTIF_PCI1: + addr = 0xff010000; + size = 0x00010000; + break; + case OCP85XX_TGTIF_PCI2: + addr = 0xff020000; + size = 0x00010000; + break; + default: + return (EINVAL); + } + break; + default: + return (EINVAL); + } + + *startp = addr; + *countp = size; + + /* Program the LAWs for this memory range. */ + bar = addr >> 12; + sr = 0x80000000 | (trgt << 20) | (ffsl(size) - 2); + + /* Check if already programmed. */ + for (i = 0; i < 8; i++) { + if (sr == in32(OCP85XX_LAWSR(i)) && + bar == in32(OCP85XX_LAWBAR(i))) + return (0); + } + + /* Find an unused access window .*/ + for (i = 0; i < 8; i++) { + if ((in32(OCP85XX_LAWSR(i)) & 0x80000000) == 0) + break; + } + if (i == 8) + return (ENOSPC); + + out32(OCP85XX_LAWBAR(i), bar); + out32(OCP85XX_LAWSR(i), sr); + return (0); +} + +static int +ocpbus_probe (device_t dev) +{ + + device_set_desc(dev, "On-Chip Peripherals bus"); + return (BUS_PROBE_DEFAULT); +} + +static int +ocpbus_attach (device_t dev) +{ + struct ocpbus_softc *sc; + int error, i; + uint32_t sr; + u_long start, end; + + sc = device_get_softc(dev); + + /* Add child device nodes. Start with the PIC. */ + sc->sc_pic = ocpbus_mk_child(dev, OCPBUS_DEVTYPE_PIC, 0); + + ocpbus_mk_child(dev, OCPBUS_DEVTYPE_UART, 0); + ocpbus_mk_child(dev, OCPBUS_DEVTYPE_UART, 1); + ocpbus_mk_child(dev, OCPBUS_DEVTYPE_TSEC, 0); + ocpbus_mk_child(dev, OCPBUS_DEVTYPE_TSEC, 1); + ocpbus_mk_child(dev, OCPBUS_DEVTYPE_TSEC, 2); + ocpbus_mk_child(dev, OCPBUS_DEVTYPE_TSEC, 3); + ocpbus_mk_child(dev, OCPBUS_DEVTYPE_PCIB, 0); + ocpbus_mk_child(dev, OCPBUS_DEVTYPE_PCIB, 1); + ocpbus_mk_child(dev, OCPBUS_DEVTYPE_PCIB, 2); + ocpbus_mk_child(dev, OCPBUS_DEVTYPE_LBC, 0); + ocpbus_mk_child(dev, OCPBUS_DEVTYPE_QUICC, 0); + ocpbus_mk_child(dev, OCPBUS_DEVTYPE_I2C, 0); + ocpbus_mk_child(dev, OCPBUS_DEVTYPE_I2C, 1); + + /* Set up IRQ rman */ + start = 0; + end = INTR_VECTORS - 1; + sc->sc_irq.rm_start = start; + sc->sc_irq.rm_end = end; + sc->sc_irq.rm_type = RMAN_ARRAY; + sc->sc_irq.rm_descr = "Interrupt request lines"; + if (rman_init(&sc->sc_irq) || + rman_manage_region(&sc->sc_irq, start, end)) + panic("ocpbus_attach IRQ rman"); + + /* Set up I/O mem rman */ + sc->sc_mem.rm_type = RMAN_ARRAY; + sc->sc_mem.rm_descr = "OCPBus Device Memory"; + error = rman_init(&sc->sc_mem); + if (error) { + device_printf(dev, "rman_init() failed. error = %d\n", error); + return (error); + } + + error = rman_manage_region(&sc->sc_mem, CCSRBAR_VA, + CCSRBAR_VA + CCSRBAR_SIZE - 1); + if (error) { + device_printf(dev, "rman_manage_region() failed. error = %d\n", + error); + return (error); + } + + /* Clear local access windows. */ + for (i = 0; i < 8; i++) { + sr = in32(OCP85XX_LAWSR(i)); + if ((sr & 0x80000000) == 0) + continue; + if ((sr & 0x00f00000) == 0x00f00000) + continue; + out32(OCP85XX_LAWSR(i), sr & 0x7fffffff); + } + + if (bootverbose) + device_printf(dev, "PORDEVSR=%08x, PORDEVSR2=%08x\n", + in32(OCP85XX_PORDEVSR), in32(OCP85XX_PORDEVSR2)); + + return (bus_generic_attach(dev)); +} + +static int +ocpbus_shutdown(device_t dev) +{ + + return(0); +} + +struct ocp_resource { + int sr_devtype; + int sr_unit; + int sr_resource; + int sr_rid; + int sr_offset; + int sr_size; +}; + +const struct ocp_resource mpc8555_resources[] = { + {OCPBUS_DEVTYPE_PIC, 0, SYS_RES_MEMORY, 0, OCP85XX_OPENPIC_OFF, + OCP85XX_OPENPIC_SIZE}, + + {OCPBUS_DEVTYPE_QUICC, 0, SYS_RES_MEMORY, 0, OCP85XX_QUICC_OFF, + OCP85XX_QUICC_SIZE}, + {OCPBUS_DEVTYPE_QUICC, 0, SYS_RES_IRQ, 0, PIC_IRQ_INT(30), 1}, + + {OCPBUS_DEVTYPE_TSEC, 0, SYS_RES_MEMORY, 0, OCP85XX_TSEC0_OFF, + OCP85XX_TSEC_SIZE}, + {OCPBUS_DEVTYPE_TSEC, 0, SYS_RES_IRQ, 0, PIC_IRQ_INT(13), 1}, + {OCPBUS_DEVTYPE_TSEC, 0, SYS_RES_IRQ, 1, PIC_IRQ_INT(14), 1}, + {OCPBUS_DEVTYPE_TSEC, 0, SYS_RES_IRQ, 2, PIC_IRQ_INT(18), 1}, + {OCPBUS_DEVTYPE_TSEC, 1, SYS_RES_MEMORY, 0, OCP85XX_TSEC1_OFF, + OCP85XX_TSEC_SIZE}, + {OCPBUS_DEVTYPE_TSEC, 1, SYS_RES_IRQ, 0, PIC_IRQ_INT(19), 1}, + {OCPBUS_DEVTYPE_TSEC, 1, SYS_RES_IRQ, 1, PIC_IRQ_INT(20), 1}, + {OCPBUS_DEVTYPE_TSEC, 1, SYS_RES_IRQ, 2, PIC_IRQ_INT(24), 1}, + {OCPBUS_DEVTYPE_TSEC, 2, SYS_RES_MEMORY, 0, OCP85XX_TSEC2_OFF, + OCP85XX_TSEC_SIZE}, + {OCPBUS_DEVTYPE_TSEC, 2, SYS_RES_IRQ, 0, PIC_IRQ_INT(15), 1}, + {OCPBUS_DEVTYPE_TSEC, 2, SYS_RES_IRQ, 1, PIC_IRQ_INT(16), 1}, + {OCPBUS_DEVTYPE_TSEC, 2, SYS_RES_IRQ, 2, PIC_IRQ_INT(17), 1}, + {OCPBUS_DEVTYPE_TSEC, 3, SYS_RES_MEMORY, 0, OCP85XX_TSEC3_OFF, + OCP85XX_TSEC_SIZE}, + {OCPBUS_DEVTYPE_TSEC, 3, SYS_RES_IRQ, 0, PIC_IRQ_INT(21), 1}, + {OCPBUS_DEVTYPE_TSEC, 3, SYS_RES_IRQ, 1, PIC_IRQ_INT(22), 1}, + {OCPBUS_DEVTYPE_TSEC, 3, SYS_RES_IRQ, 2, PIC_IRQ_INT(23), 1}, + + {OCPBUS_DEVTYPE_UART, 0, SYS_RES_MEMORY, 0, OCP85XX_UART0_OFF, + OCP85XX_UART_SIZE}, + {OCPBUS_DEVTYPE_UART, 0, SYS_RES_IRQ, 0, PIC_IRQ_INT(26), 1}, + {OCPBUS_DEVTYPE_UART, 1, SYS_RES_MEMORY, 0, OCP85XX_UART1_OFF, + OCP85XX_UART_SIZE}, + {OCPBUS_DEVTYPE_UART, 1, SYS_RES_IRQ, 0, PIC_IRQ_INT(26), 1}, + + {OCPBUS_DEVTYPE_PCIB, 0, SYS_RES_MEMORY, 0, OCP85XX_PCI0_OFF, + OCP85XX_PCI_SIZE}, + {OCPBUS_DEVTYPE_PCIB, 0, SYS_RES_MEMORY, 1, 0, OCP85XX_TGTIF_PCI0}, + {OCPBUS_DEVTYPE_PCIB, 0, SYS_RES_IOPORT, 1, 0, OCP85XX_TGTIF_PCI0}, + {OCPBUS_DEVTYPE_PCIB, 1, SYS_RES_MEMORY, 0, OCP85XX_PCI1_OFF, + OCP85XX_PCI_SIZE}, + {OCPBUS_DEVTYPE_PCIB, 1, SYS_RES_MEMORY, 1, 0, OCP85XX_TGTIF_PCI1}, + {OCPBUS_DEVTYPE_PCIB, 1, SYS_RES_IOPORT, 1, 0, OCP85XX_TGTIF_PCI1}, + {OCPBUS_DEVTYPE_PCIB, 2, SYS_RES_MEMORY, 0, OCP85XX_PCI2_OFF, + OCP85XX_PCI_SIZE}, + {OCPBUS_DEVTYPE_PCIB, 2, SYS_RES_MEMORY, 1, 0, OCP85XX_TGTIF_PCI2}, + {OCPBUS_DEVTYPE_PCIB, 2, SYS_RES_IOPORT, 1, 0, OCP85XX_TGTIF_PCI2}, + + {OCPBUS_DEVTYPE_LBC, 0, SYS_RES_MEMORY, 0, OCP85XX_LBC_OFF, + OCP85XX_LBC_SIZE}, + {OCPBUS_DEVTYPE_LBC, 0, SYS_RES_MEMORY, 1, 0, OCP85XX_TGTIF_LBC}, + + {OCPBUS_DEVTYPE_I2C, 0, SYS_RES_MEMORY, 0, OCP85XX_I2C0_OFF, + OCP85XX_I2C_SIZE}, + {OCPBUS_DEVTYPE_I2C, 0, SYS_RES_IRQ, 0, PIC_IRQ_INT(27), 1}, + {OCPBUS_DEVTYPE_I2C, 1, SYS_RES_MEMORY, 0, OCP85XX_I2C1_OFF, + OCP85XX_I2C_SIZE}, + {OCPBUS_DEVTYPE_I2C, 1, SYS_RES_IRQ, 0, PIC_IRQ_INT(27), 1}, + + {0} +}; + +static int +ocpbus_get_resource(device_t dev, device_t child, int type, int rid, + u_long *startp, u_long *countp) +{ + const struct ocp_resource *res; + struct ocp_devinfo *dinfo; + u_long start = 0, count = 0; + int error; + + dinfo = device_get_ivars(child); + + /* + * Lookup the correct values. + */ + res = mpc8555_resources; + for (; res->sr_devtype; res++) { + if (res->sr_devtype != dinfo->ocp_devtype) + continue; + if (res->sr_unit != dinfo->ocp_unit) + continue; + if (res->sr_rid != rid) + continue; + if (res->sr_resource != type) + continue; + + if (res->sr_offset != 0) { + error = 0; + switch (type) { + case SYS_RES_MEMORY: + start = res->sr_offset + CCSRBAR_VA; + break; + case SYS_RES_IRQ: + start = res->sr_offset; + break; + default: + error = EINVAL; + break; + } + count = res->sr_size; + } else + error = ocpbus_write_law(res->sr_size, type, &start, + &count); + + if (!error) { + if (startp != NULL) + *startp = start; + if (countp != NULL) + *countp = count; + } + return (error); + } + + return (ENOENT); +} + +static struct resource * +ocpbus_alloc_resource(device_t dev, device_t child, int type, int *rid, + u_long start, u_long end, u_long count, u_int flags) +{ + struct ocpbus_softc *sc; + struct resource *rv; + int error; + + sc = device_get_softc(dev); + + if (type == SYS_RES_IRQ) { + if (start == 0ul && end == ~0ul) { + error = ocpbus_get_resource(dev, child, type, *rid, + &start, &count); + if (error) + return (NULL); + } + + rv = rman_reserve_resource(&sc->sc_irq, start - PIC_IRQ_START, + start - PIC_IRQ_START, 1, flags, child); + return (rv); + } + + if (type != SYS_RES_MEMORY) + return (NULL); + + if (start != 0ul || end != ~0ul) + return (NULL); + + error = ocpbus_get_resource(dev, child, type, *rid, &start, &count); + if (error) + return (NULL); + + end = start + count - 1; + rv = rman_reserve_resource(&sc->sc_mem, start, end, count, flags, + child); + if (rv == NULL) + return (NULL); + + rman_set_bustag(rv, &bs_be_tag); + rman_set_bushandle(rv, rman_get_start(rv)); + return (rv); +} + +static int +ocpbus_print_child(device_t dev, device_t child) +{ + u_long size, start; + int error, retval, rid; + + retval = bus_print_child_header(dev, child); + + rid = 0; + while (1) { + error = ocpbus_get_resource(dev, child, SYS_RES_MEMORY, rid, + &start, &size); + if (error) + break; + retval += (rid == 0) ? printf(" iomem ") : printf(","); + retval += printf("%#lx", start); + if (size > 1) + retval += printf("-%#lx", start + size - 1); + rid++; + } + + /* + * The SYS_RES_IOPORT resource of the PCIB has rid 1 because the + * the SYS_RES_MEMORY resource related to the decoding window also + * has rid 1. This is friendlier for the PCIB child... + */ + rid = 1; + while (1) { + error = ocpbus_get_resource(dev, child, SYS_RES_IOPORT, rid, + &start, &size); + if (error) + break; + retval += (rid == 1) ? printf(" ioport ") : printf(","); + retval += printf("%#lx", start); + if (size > 1) + retval += printf("-%#lx", start + size - 1); + rid++; + } + + rid = 0; + while (1) { + error = ocpbus_get_resource(dev, child, SYS_RES_IRQ, rid, + &start, &size); + if (error) + break; + retval += (rid == 0) ? printf(" irq ") : printf(","); + retval += printf("%ld", start); + rid++; + } + + retval += bus_print_child_footer(dev, child); + return (retval); +} + +static int +ocpbus_read_ivar(device_t dev, device_t child, int index, uintptr_t *result) +{ + struct ocp_devinfo *dinfo; + + if (device_get_parent(child) != dev) + return (EINVAL); + + dinfo = device_get_ivars(child); + + switch (index) { + case OCPBUS_IVAR_CLOCK: + *result = bootinfo->bi_bus_clk; + return (0); + case OCPBUS_IVAR_DEVTYPE: + *result = dinfo->ocp_devtype; + return (0); + case OCPBUS_IVAR_HWUNIT: + *result = dinfo->ocp_unit; + return (0); + } + + return (EINVAL); +} + +static int +ocpbus_release_resource(device_t dev, device_t child, int type, int rid, + struct resource *res) +{ + + return (rman_release_resource(res)); +} + +static int +ocpbus_setup_intr(device_t dev, device_t child, struct resource *res, int flags, + driver_filter_t *filter, driver_intr_t *ihand, void *arg, void **cookiep) +{ + int error; + + if (res == NULL) + panic("ocpbus_setup_intr: NULL irq resource!"); + + *cookiep = 0; + if ((rman_get_flags(res) & RF_SHAREABLE) == 0) + flags |= INTR_EXCL; + + /* + * We depend here on rman_activate_resource() being idempotent. + */ + error = rman_activate_resource(res); + if (error) + return (error); + + error = powerpc_setup_intr(device_get_nameunit(child), + rman_get_start(res), filter, ihand, arg, flags, cookiep); + + return (error); +} + +static int +ocpbus_teardown_intr(device_t dev, device_t child, struct resource *res, + void *cookie) +{ + + return (powerpc_teardown_intr(cookie)); +} diff --git a/sys/powerpc/mpc85xx/ocpbus.h b/sys/powerpc/mpc85xx/ocpbus.h new file mode 100644 index 0000000..eb3c5c4 --- /dev/null +++ b/sys/powerpc/mpc85xx/ocpbus.h @@ -0,0 +1,86 @@ +/*- + * Copyright 2006 by Juniper Networks. 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. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 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. + * + * $FreeBSD$ + */ + +#ifndef _MACHINE_OCP85XX_H_ +#define _MACHINE_OCP85XX_H_ + +/* + * Local access registers. + */ +#define OCP85XX_LAWBAR(n) (CCSRBAR_VA + 0xc08 + 0x20 * (n)) +#define OCP85XX_LAWSR(n) (CCSRBAR_VA + 0xc10 + 0x20 * (n)) + +/* + * Power-On Reset configuration. + */ +#define OCP85XX_PORDEVSR (CCSRBAR_VA + 0xe000c) +#define OCP85XX_PORDEVSR2 (CCSRBAR_VA + 0xe0014) + +/* + * OCP Bus Definitions + */ +#define OCP85XX_I2C0_OFF 0x03000 +#define OCP85XX_I2C1_OFF 0x03100 +#define OCP85XX_I2C_SIZE 0x15 +#define OCP85XX_UART0_OFF 0x04500 +#define OCP85XX_UART1_OFF 0x04600 +#define OCP85XX_UART_SIZE 0x10 +#define OCP85XX_LBC_OFF 0x05000 +#define OCP85XX_LBC_SIZE 0x1000 +#define OCP85XX_PCI0_OFF 0x08000 +#define OCP85XX_PCI1_OFF 0x09000 +#define OCP85XX_PCI2_OFF 0x0A000 +#define OCP85XX_PCI_SIZE 0x1000 +#define OCP85XX_TSEC0_OFF 0x24000 +#define OCP85XX_TSEC1_OFF 0x25000 +#define OCP85XX_TSEC2_OFF 0x26000 +#define OCP85XX_TSEC3_OFF 0x27000 +#define OCP85XX_TSEC_SIZE 0x1000 +#define OCP85XX_OPENPIC_OFF 0x40000 +#define OCP85XX_OPENPIC_SIZE 0x200B4 +#define OCP85XX_QUICC_OFF 0x80000 +#define OCP85XX_QUICC_SIZE 0x20000 + +#define OCP85XX_TGTIF_PCI0 0 +#define OCP85XX_TGTIF_PCI1 1 +#define OCP85XX_TGTIF_PCI2 2 +#define OCP85XX_TGTIF_LBC 4 +#define OCP85XX_TGTIF_RAM 15 + +/* + * PIC definitions + */ +#define ISA_IRQ_START 0 +#define PIC_IRQ_START (ISA_IRQ_START + 16) + +#define ISA_IRQ(n) (ISA_IRQ_START + (n)) +#define PIC_IRQ_EXT(n) (PIC_IRQ_START + (n)) +#define PIC_IRQ_INT(n) (PIC_IRQ_START + 16 + (n)) + +#endif /* _MACHINE_OCP85XX_H */ diff --git a/sys/powerpc/mpc85xx/opic.c b/sys/powerpc/mpc85xx/opic.c new file mode 100644 index 0000000..686f539 --- /dev/null +++ b/sys/powerpc/mpc85xx/opic.c @@ -0,0 +1,99 @@ +/*- + * Copyright 2006 by Juniper Networks. 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. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 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 <sys/systm.h> +#include <sys/kernel.h> +#include <sys/module.h> +#include <sys/bus.h> +#include <sys/rman.h> +#include <sys/bus.h> + +#include <machine/bus.h> +#include <machine/intr.h> +#include <machine/intr_machdep.h> +#include <machine/openpicvar.h> +#include <machine/ocpbus.h> + +#include "pic_if.h" + +static int openpic_ocpbus_probe(device_t); + +struct openpic_ocpbus_softc { + struct openpic_softc osc; + int sc_rrid; + struct resource *sc_memr; /* mem resource */ +}; + +/* + * OpenPIC attachment to ocpbus + */ +static device_method_t openpic_ocpbus_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, openpic_ocpbus_probe), + DEVMETHOD(device_attach, openpic_attach), + + /* PIC interface */ + DEVMETHOD(pic_dispatch, openpic_dispatch), + DEVMETHOD(pic_enable, openpic_enable), + DEVMETHOD(pic_eoi, openpic_eoi), + DEVMETHOD(pic_mask, openpic_mask), + DEVMETHOD(pic_unmask, openpic_unmask), + + { 0, 0 }, +}; + +static driver_t openpic_ocpbus_driver = { + "openpic", + openpic_ocpbus_methods, + sizeof(struct openpic_ocpbus_softc) +}; + +DRIVER_MODULE(openpic, ocpbus, openpic_ocpbus_driver, openpic_devclass, 0, 0); + +static int +openpic_ocpbus_probe (device_t dev) +{ + device_t parent; + uintptr_t devtype; + int error; + + parent = device_get_parent(dev); + + error = BUS_READ_IVAR(parent, dev, OCPBUS_IVAR_DEVTYPE, &devtype); + if (error) + return (error); + if (devtype != OCPBUS_DEVTYPE_PIC) + return (ENXIO); + + device_set_desc(dev, OPENPIC_DEVSTR); + return (BUS_PROBE_DEFAULT); +} diff --git a/sys/powerpc/mpc85xx/pci_ocp.c b/sys/powerpc/mpc85xx/pci_ocp.c new file mode 100644 index 0000000..66c2d65 --- /dev/null +++ b/sys/powerpc/mpc85xx/pci_ocp.c @@ -0,0 +1,781 @@ +/*- + * Copyright 2006 by Juniper Networks. 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. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 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 <sys/systm.h> +#include <sys/ktr.h> +#include <sys/sockio.h> +#include <sys/mbuf.h> +#include <sys/malloc.h> +#include <sys/kernel.h> +#include <sys/module.h> +#include <sys/socket.h> +#include <sys/queue.h> +#include <sys/bus.h> +#include <sys/rman.h> +#include <sys/endian.h> + +#include <vm/vm.h> +#include <vm/pmap.h> + +#include <dev/pci/pcivar.h> +#include <dev/pci/pcireg.h> +#include <dev/pci/pcib_private.h> + +#include "pcib_if.h" + +#include <machine/resource.h> +#include <machine/bus.h> +#include <machine/ocpbus.h> +#include <machine/spr.h> + +#include <powerpc/mpc85xx/ocpbus.h> + +#define REG_CFG_ADDR 0x0000 +#define CONFIG_ACCESS_ENABLE 0x80000000 + +#define REG_CFG_DATA 0x0004 +#define REG_INT_ACK 0x0008 + +#define REG_POTAR(n) (0x0c00 + 0x20 * (n)) +#define REG_POTEAR(n) (0x0c04 + 0x20 * (n)) +#define REG_POWBAR(n) (0x0c08 + 0x20 * (n)) +#define REG_POWAR(n) (0x0c10 + 0x20 * (n)) + +#define REG_PITAR(n) (0x0e00 - 0x20 * (n)) +#define REG_PIWBAR(n) (0x0e08 - 0x20 * (n)) +#define REG_PIWBEAR(n) (0x0e0c - 0x20 * (n)) +#define REG_PIWAR(n) (0x0e10 - 0x20 * (n)) + +struct pci_ocp_softc { + device_t sc_dev; + + struct rman sc_iomem; + bus_addr_t sc_iomem_va; /* Virtual mapping. */ + bus_addr_t sc_iomem_alloc; /* Next allocation. */ + struct rman sc_ioport; + bus_addr_t sc_ioport_va; /* Virtual mapping. */ + bus_addr_t sc_ioport_alloc; /* Next allocation. */ + + struct resource *sc_res; + bus_space_handle_t sc_bsh; + bus_space_tag_t sc_bst; + int sc_rid; + + int sc_busnr; + int sc_pcie:1; +}; + +static int pci_ocp_busnr = 0; + +static int pci_ocp_attach(device_t); +static int pci_ocp_probe(device_t); + +static struct resource *pci_ocp_alloc_resource(device_t, device_t, int, int *, + u_long, u_long, u_long, u_int); +static int pci_ocp_read_ivar(device_t, device_t, int, uintptr_t *); +static int pci_ocp_release_resource(device_t, device_t, int, int, + struct resource *); +static int pci_ocp_write_ivar(device_t, device_t, int, uintptr_t); + +static int pci_ocp_maxslots(device_t); +static uint32_t pci_ocp_read_config(device_t, u_int, u_int, u_int, u_int, int); +static void pci_ocp_write_config(device_t, u_int, u_int, u_int, u_int, + uint32_t, int); + +/* + * Bus interface definitions. + */ +static device_method_t pci_ocp_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, pci_ocp_probe), + DEVMETHOD(device_attach, pci_ocp_attach), + + /* Bus interface */ + DEVMETHOD(bus_print_child, bus_generic_print_child), + DEVMETHOD(bus_read_ivar, pci_ocp_read_ivar), + DEVMETHOD(bus_write_ivar, pci_ocp_write_ivar), + DEVMETHOD(bus_alloc_resource, pci_ocp_alloc_resource), + DEVMETHOD(bus_release_resource, pci_ocp_release_resource), + DEVMETHOD(bus_activate_resource, bus_generic_activate_resource), + DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource), + DEVMETHOD(bus_setup_intr, bus_generic_setup_intr), + DEVMETHOD(bus_teardown_intr, bus_generic_teardown_intr), + + /* pcib interface */ + DEVMETHOD(pcib_maxslots, pci_ocp_maxslots), + DEVMETHOD(pcib_read_config, pci_ocp_read_config), + DEVMETHOD(pcib_write_config, pci_ocp_write_config), + DEVMETHOD(pcib_route_interrupt, pcib_route_interrupt), + + { 0, 0 } +}; + +static driver_t pci_ocp_driver = { + "pcib", + pci_ocp_methods, + sizeof(struct pci_ocp_softc), +}; + +devclass_t pcib_devclass; + +DRIVER_MODULE(pcib, ocpbus, pci_ocp_driver, pcib_devclass, 0, 0); + +static uint32_t +pci_ocp_cfgread(struct pci_ocp_softc *sc, u_int bus, u_int slot, u_int func, + u_int reg, int bytes) +{ + uint32_t addr, data; + + if (bus == sc->sc_busnr && !sc->sc_pcie) + bus = 0; + + addr = CONFIG_ACCESS_ENABLE; + addr |= (bus & 0xff) << 16; + addr |= (slot & 0x1f) << 11; + addr |= (func & 0x7) << 8; + addr |= reg & 0xfc; + if (sc->sc_pcie) + addr |= (reg & 0xf00) << 16; + bus_space_write_4(sc->sc_bst, sc->sc_bsh, REG_CFG_ADDR, addr); + + switch (bytes) { + case 1: + data = bus_space_read_1(sc->sc_bst, sc->sc_bsh, + REG_CFG_DATA + (reg & 3)); + break; + case 2: + data = le16toh(bus_space_read_2(sc->sc_bst, sc->sc_bsh, + REG_CFG_DATA + (reg & 2))); + break; + case 4: + data = le32toh(bus_space_read_4(sc->sc_bst, sc->sc_bsh, + REG_CFG_DATA)); + break; + default: + data = ~0; + break; + } + return (data); +} + +static void +pci_ocp_cfgwrite(struct pci_ocp_softc *sc, u_int bus, u_int slot, u_int func, + u_int reg, uint32_t data, int bytes) +{ + uint32_t addr; + + if (bus == sc->sc_busnr && !sc->sc_pcie) + bus = 0; + + addr = CONFIG_ACCESS_ENABLE; + addr |= (bus & 0xff) << 16; + addr |= (slot & 0x1f) << 11; + addr |= (func & 0x7) << 8; + addr |= reg & 0xfc; + if (sc->sc_pcie) + addr |= (reg & 0xf00) << 16; + bus_space_write_4(sc->sc_bst, sc->sc_bsh, REG_CFG_ADDR, addr); + + switch (bytes) { + case 1: + bus_space_write_1(sc->sc_bst, sc->sc_bsh, + REG_CFG_DATA + (reg & 3), data); + break; + case 2: + bus_space_write_2(sc->sc_bst, sc->sc_bsh, + REG_CFG_DATA + (reg & 2), htole16(data)); + break; + case 4: + bus_space_write_4(sc->sc_bst, sc->sc_bsh, + REG_CFG_DATA, htole32(data)); + break; + } +} + +#if 0 +static void +dump(struct pci_ocp_softc *sc) +{ + unsigned int i; + +#define RD(o) bus_space_read_4(sc->sc_bst, sc->sc_bsh, o) + for (i = 0; i < 5; i++) { + printf("POTAR%u =0x%08x\n", i, RD(REG_POTAR(i))); + printf("POTEAR%u =0x%08x\n", i, RD(REG_POTEAR(i))); + printf("POWBAR%u =0x%08x\n", i, RD(REG_POWBAR(i))); + printf("POWAR%u =0x%08x\n", i, RD(REG_POWAR(i))); + } + printf("\n"); + for (i = 1; i < 4; i++) { + printf("PITAR%u =0x%08x\n", i, RD(REG_PITAR(i))); + printf("PIWBAR%u =0x%08x\n", i, RD(REG_PIWBAR(i))); + printf("PIWBEAR%u=0x%08x\n", i, RD(REG_PIWBEAR(i))); + printf("PIWAR%u =0x%08x\n", i, RD(REG_PIWAR(i))); + } + printf("\n"); +#undef RD + + for (i = 0; i < 0x48; i += 4) { + printf("cfg%02x=0x%08x\n", i, pci_ocp_cfgread(sc, 0, 0, 0, + i, 4)); + } +} +#endif + +static int +pci_ocp_maxslots(device_t dev) +{ + struct pci_ocp_softc *sc = device_get_softc(dev); + + return ((sc->sc_pcie) ? 0 : 30); +} + +static uint32_t +pci_ocp_read_config(device_t dev, u_int bus, u_int slot, u_int func, + u_int reg, int bytes) +{ + struct pci_ocp_softc *sc = device_get_softc(dev); + + if (bus == sc->sc_busnr && !sc->sc_pcie && slot < 10) + return (~0); + return (pci_ocp_cfgread(sc, bus, slot, func, reg, bytes)); +} + +static void +pci_ocp_write_config(device_t dev, u_int bus, u_int slot, u_int func, + u_int reg, uint32_t val, int bytes) +{ + struct pci_ocp_softc *sc = device_get_softc(dev); + + if (bus == sc->sc_busnr && !sc->sc_pcie && slot < 10) + return; + pci_ocp_cfgwrite(sc, bus, slot, func, reg, val, bytes); +} + +static int +pci_ocp_probe(device_t dev) +{ + char buf[128]; + struct pci_ocp_softc *sc; + const char *mpcid, *type; + device_t parent; + u_long start, size; + uintptr_t devtype; + uint32_t cfgreg; + int error; + + parent = device_get_parent(dev); + error = BUS_READ_IVAR(parent, dev, OCPBUS_IVAR_DEVTYPE, &devtype); + if (error) + return (error); + if (devtype != OCPBUS_DEVTYPE_PCIB) + return (ENXIO); + + sc = device_get_softc(dev); + + sc->sc_rid = 0; + sc->sc_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &sc->sc_rid, + RF_ACTIVE); + if (sc->sc_res == NULL) + return (ENXIO); + + sc->sc_bst = rman_get_bustag(sc->sc_res); + sc->sc_bsh = rman_get_bushandle(sc->sc_res); + sc->sc_busnr = 0; + + error = ENOENT; + cfgreg = pci_ocp_cfgread(sc, 0, 0, 0, PCIR_VENDOR, 2); + if (cfgreg != 0x1057 && cfgreg != 0x1957) + goto out; + cfgreg = pci_ocp_cfgread(sc, 0, 0, 0, PCIR_DEVICE, 2); + switch (cfgreg) { + case 0x000a: + mpcid = "8555E"; + break; + case 0x0012: + mpcid = "8548E"; + break; + case 0x0013: + mpcid = "8548"; + break; + /* + * Documentation from Freescale is incorrect. + * Use right values after documentation is corrected. + */ + case 0x0030: + mpcid = "8544E"; + break; + case 0x0031: + mpcid = "8544"; + break; + case 0x0032: + mpcid = "8544"; + break; + default: + goto out; + } + + type = "PCI"; + cfgreg = pci_ocp_cfgread(sc, 0, 0, 0, PCIR_CAP_PTR, 1); + while (cfgreg != 0) { + cfgreg = pci_ocp_cfgread(sc, 0, 0, 0, cfgreg, 2); + switch (cfgreg & 0xff) { + case PCIY_PCIX: /* PCI-X */ + type = "PCI-X"; + break; + case PCIY_EXPRESS: /* PCI Express */ + type = "PCI Express"; + sc->sc_pcie = 1; + break; + } + cfgreg = (cfgreg >> 8) & 0xff; + } + + bus_get_resource(dev, SYS_RES_MEMORY, 1, &start, &size); + if (start == 0 || size == 0) + goto out; + + snprintf(buf, sizeof(buf), + "Freescale MPC%s %s host controller", mpcid, type); + device_set_desc_copy(dev, buf); + error = BUS_PROBE_DEFAULT; + +out: + bus_release_resource(dev, SYS_RES_MEMORY, sc->sc_rid, sc->sc_res); + return (error); +} + +static int +pci_ocp_init_bar(struct pci_ocp_softc *sc, int bus, int slot, int func, + int barno) +{ + bus_addr_t *allocp; + uint32_t addr, bar, mask, size; + int reg, width; + + reg = PCIR_BAR(barno); + bar = pci_ocp_read_config(sc->sc_dev, bus, slot, func, reg, 4); + if (bar == 0) + return (1); + width = ((bar & 7) == 4) ? 2 : 1; + + pci_ocp_write_config(sc->sc_dev, bus, slot, func, reg, ~0, 4); + size = pci_ocp_read_config(sc->sc_dev, bus, slot, func, reg, 4); + + if (bar & 1) { /* I/O port */ + allocp = &sc->sc_ioport_alloc; + size &= ~3; + if ((size & 0xffff0000) == 0) + size |= 0xffff0000; + } else { /* memory */ + allocp = &sc->sc_iomem_alloc; + size &= ~15; + } + mask = ~size; + size = mask + 1; + /* Sanity check (must be a power of 2). */ + if (size & mask) + return (width); + + addr = (*allocp + mask) & ~mask; + *allocp = addr + size; + + if (bootverbose) + printf("PCI %u:%u:%u: reg %x: size=%08x: addr=%08x\n", + bus, slot, func, reg, size, addr); + + pci_ocp_write_config(sc->sc_dev, bus, slot, func, reg, addr, 4); + if (width == 2) + pci_ocp_write_config(sc->sc_dev, bus, slot, func, reg + 4, + 0, 4); + return (width); +} + +static void +pci_ocp_init(struct pci_ocp_softc *sc, int bus) +{ + int slot, maxslot; + int func, maxfunc; + int bar, maxbar; + uint16_t vendor, device; + uint8_t cr8, command, hdrtype, class, subclass; + uint8_t intline, intpin; + + maxslot = (sc->sc_pcie) ? 1 : 31; + for (slot = 0; slot < maxslot; slot++) { + maxfunc = 0; + for (func = 0; func <= maxfunc; func++) { + hdrtype = pci_ocp_read_config(sc->sc_dev, bus, slot, + func, PCIR_HDRTYPE, 1); + if ((hdrtype & PCIM_HDRTYPE) > PCI_MAXHDRTYPE) + continue; + + if (func == 0 && (hdrtype & PCIM_MFDEV)) + maxfunc = PCI_FUNCMAX; + + command = pci_ocp_read_config(sc->sc_dev, bus, slot, + func, PCIR_COMMAND, 1); + command &= ~(PCIM_CMD_MEMEN | PCIM_CMD_PORTEN); + pci_ocp_write_config(sc->sc_dev, bus, slot, func, + PCIR_COMMAND, command, 1); + + vendor = pci_ocp_read_config(sc->sc_dev, bus, slot, + func, PCIR_VENDOR, 2); + device = pci_ocp_read_config(sc->sc_dev, bus, slot, + func, PCIR_DEVICE, 2); + + /* + * Make sure the ATA controller on the VIA82C686 + * South bridge is enabled. + */ + if (vendor == 0x1106 && device == 0x0686) { + /* Enable the ATA controller. */ + cr8 = pci_ocp_read_config(sc->sc_dev, bus, + slot, func, 0x48, 1); + if (cr8 & 2) { + device_printf(sc->sc_dev, + "enabling ATA controller\n"); + pci_ocp_write_config(sc->sc_dev, bus, + slot, func, 0x48, cr8 & ~2, 1); + } + } + if (vendor == 0x1106 && device == 0x0571) { + pci_ocp_write_config(sc->sc_dev, bus, slot, + func, 0xc4, 0x00, 1); + /* Set legacy mode. */ + pci_ocp_write_config(sc->sc_dev, bus, slot, + func, 0x40, 0x08, 1); + pci_ocp_write_config(sc->sc_dev, bus, slot, + func, PCIR_PROGIF, 0x00, 1); + pci_ocp_write_config(sc->sc_dev, bus, slot, + func, 0x42, 0x09, 1); + pci_ocp_write_config(sc->sc_dev, bus, slot, + func, 0x40, 0x0b, 1); + } + + /* Program the base address registers. */ + maxbar = (hdrtype & PCIM_HDRTYPE) ? 0 : 6; + bar = 0; + while (bar < maxbar) + bar += pci_ocp_init_bar(sc, bus, slot, func, + bar); + + /* Perform interrupt routing. */ + intpin = pci_ocp_read_config(sc->sc_dev, bus, slot, + func, PCIR_INTPIN, 1); + if (intpin != 0) { + intline = intpin - 1; + intline += (bus != sc->sc_busnr) ? slot : 0; + intline = PIC_IRQ_EXT(intline & 3); + } else + intline = 0xff; + pci_ocp_write_config(sc->sc_dev, bus, slot, func, + PCIR_INTLINE, intline, 1); + + command |= PCIM_CMD_BUSMASTEREN | PCIM_CMD_MEMEN | + PCIM_CMD_PORTEN; + pci_ocp_write_config(sc->sc_dev, bus, slot, func, + PCIR_COMMAND, command, 1); + + /* + * Handle PCI-PCI bridges + */ + class = pci_ocp_read_config(sc->sc_dev, bus, slot, + func, PCIR_CLASS, 1); + if (class != PCIC_BRIDGE) + continue; + subclass = pci_ocp_read_config(sc->sc_dev, bus, slot, + func, PCIR_SUBCLASS, 1); + if (subclass != PCIS_BRIDGE_PCI) + continue; + + /* Program I/O decoder. */ + pci_ocp_write_config(sc->sc_dev, bus, slot, func, + PCIR_IOBASEL_1, sc->sc_ioport.rm_start >> 8, 1); + pci_ocp_write_config(sc->sc_dev, bus, slot, func, + PCIR_IOLIMITL_1, sc->sc_ioport.rm_end >> 8, 1); + pci_ocp_write_config(sc->sc_dev, bus, slot, func, + PCIR_IOBASEH_1, sc->sc_ioport.rm_start >> 16, 2); + pci_ocp_write_config(sc->sc_dev, bus, slot, func, + PCIR_IOLIMITH_1, sc->sc_ioport.rm_end >> 16, 2); + + /* Program (non-prefetchable) memory decoder. */ + pci_ocp_write_config(sc->sc_dev, bus, slot, func, + PCIR_MEMBASE_1, sc->sc_iomem.rm_start >> 16, 2); + pci_ocp_write_config(sc->sc_dev, bus, slot, func, + PCIR_MEMLIMIT_1, sc->sc_iomem.rm_end >> 16, 2); + + /* Program prefetchable memory decoder. */ + pci_ocp_write_config(sc->sc_dev, bus, slot, func, + PCIR_PMBASEL_1, 0x0010, 2); + pci_ocp_write_config(sc->sc_dev, bus, slot, func, + PCIR_PMLIMITL_1, 0x000f, 2); + pci_ocp_write_config(sc->sc_dev, bus, slot, func, + PCIR_PMBASEH_1, 0x00000000, 4); + pci_ocp_write_config(sc->sc_dev, bus, slot, func, + PCIR_PMLIMITH_1, 0x00000000, 4); + + pci_ocp_busnr++; + pci_ocp_write_config(sc->sc_dev, bus, slot, func, + PCIR_PRIBUS_1, bus, 1); + pci_ocp_write_config(sc->sc_dev, bus, slot, func, + PCIR_SECBUS_1, pci_ocp_busnr, 1); + pci_ocp_write_config(sc->sc_dev, bus, slot, func, + PCIR_SUBBUS_1, 0xff, 1); + + pci_ocp_init(sc, pci_ocp_busnr); + + pci_ocp_write_config(sc->sc_dev, bus, slot, func, + PCIR_SUBBUS_1, pci_ocp_busnr, 1); + } + } +} + +static void +pci_ocp_inbound(struct pci_ocp_softc *sc, int wnd, int tgt, u_long start, + u_long size, u_long pci_start) +{ + uint32_t attr, bar, tar; + + KASSERT(wnd > 0, ("%s: inbound window 0 is invalid", __func__)); + + switch (tgt) { + case OCP85XX_TGTIF_RAM: + attr = 0xa0f55000 | (ffsl(size) - 2); + break; + default: + attr = 0; + break; + } + tar = start >> 12; + bar = pci_start >> 12; + + bus_space_write_4(sc->sc_bst, sc->sc_bsh, REG_PITAR(wnd), tar); + bus_space_write_4(sc->sc_bst, sc->sc_bsh, REG_PIWBEAR(wnd), 0); + bus_space_write_4(sc->sc_bst, sc->sc_bsh, REG_PIWBAR(wnd), bar); + bus_space_write_4(sc->sc_bst, sc->sc_bsh, REG_PIWAR(wnd), attr); +} + +static void +pci_ocp_outbound(struct pci_ocp_softc *sc, int wnd, int res, u_long start, + u_long size, u_long pci_start) +{ + uint32_t attr, bar, tar; + + switch (res) { + case SYS_RES_MEMORY: + attr = 0x80044000 | (ffsl(size) - 2); + break; + case SYS_RES_IOPORT: + attr = 0x80088000 | (ffsl(size) - 2); + break; + default: + attr = 0x0004401f; + break; + } + bar = start >> 12; + tar = pci_start >> 12; + + bus_space_write_4(sc->sc_bst, sc->sc_bsh, REG_POTAR(wnd), tar); + bus_space_write_4(sc->sc_bst, sc->sc_bsh, REG_POTEAR(wnd), 0); + bus_space_write_4(sc->sc_bst, sc->sc_bsh, REG_POWBAR(wnd), bar); + bus_space_write_4(sc->sc_bst, sc->sc_bsh, REG_POWAR(wnd), attr); +} + +static int +pci_ocp_iorange(struct pci_ocp_softc *sc, int type, int wnd) +{ + struct rman *rm; + u_long start, end, size, alloc; + bus_addr_t pci_start, pci_end; + bus_addr_t *vap, *allocp; + int error; + + bus_get_resource(sc->sc_dev, type, 1, &start, &size); + end = start + size - 1; + + switch (type) { + case SYS_RES_IOPORT: + rm = &sc->sc_ioport; + pci_start = 0x0000; + pci_end = 0xffff; + alloc = 0x1000; + vap = &sc->sc_ioport_va; + allocp = &sc->sc_ioport_alloc; + break; + case SYS_RES_MEMORY: + rm = &sc->sc_iomem; + pci_start = start; + pci_end = end; + alloc = 0; + vap = &sc->sc_iomem_va; + allocp = &sc->sc_iomem_alloc; + break; + default: + return (EINVAL); + } + + rm->rm_type = RMAN_ARRAY; + rm->rm_start = pci_start; + rm->rm_end = pci_end; + error = rman_init(rm); + if (error) + return (error); + + error = rman_manage_region(rm, pci_start, pci_end); + if (error) { + rman_fini(rm); + return (error); + } + + *allocp = pci_start + alloc; + *vap = (uintptr_t)pmap_mapdev(start, size); + pci_ocp_outbound(sc, wnd, type, start, size, pci_start); + return (0); +} + +static int +pci_ocp_attach(device_t dev) +{ + struct pci_ocp_softc *sc; + uint32_t cfgreg; + int error; + + sc = device_get_softc(dev); + sc->sc_dev = dev; + + sc->sc_rid = 0; + sc->sc_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &sc->sc_rid, + RF_ACTIVE); + if (sc->sc_res == NULL) { + device_printf(dev, "could not map I/O memory\n"); + return (ENXIO); + } + sc->sc_bst = rman_get_bustag(sc->sc_res); + sc->sc_bsh = rman_get_bushandle(sc->sc_res); + + cfgreg = pci_ocp_cfgread(sc, 0, 0, 0, PCIR_COMMAND, 2); + cfgreg |= PCIM_CMD_SERRESPEN | PCIM_CMD_BUSMASTEREN | PCIM_CMD_MEMEN | + PCIM_CMD_PORTEN; + pci_ocp_cfgwrite(sc, 0, 0, 0, PCIR_COMMAND, cfgreg, 2); + + pci_ocp_outbound(sc, 0, -1, 0, 0, 0); + error = pci_ocp_iorange(sc, SYS_RES_MEMORY, 1); + error = pci_ocp_iorange(sc, SYS_RES_IOPORT, 2); + pci_ocp_outbound(sc, 3, -1, 0, 0, 0); + pci_ocp_outbound(sc, 4, -1, 0, 0, 0); + + pci_ocp_inbound(sc, 1, -1, 0, 0, 0); + pci_ocp_inbound(sc, 2, -1, 0, 0, 0); + pci_ocp_inbound(sc, 3, OCP85XX_TGTIF_RAM, 0, 2U*1024U*1024U*1024U, 0); + + sc->sc_busnr = pci_ocp_busnr; + pci_ocp_init(sc, sc->sc_busnr); + pci_ocp_busnr++; + + device_add_child(dev, "pci", -1); + return (bus_generic_attach(dev)); +} + +static struct resource * +pci_ocp_alloc_resource(device_t dev, device_t child, int type, int *rid, + u_long start, u_long end, u_long count, u_int flags) +{ + struct pci_ocp_softc *sc = device_get_softc(dev); + struct rman *rm; + struct resource *res; + bus_addr_t va; + + switch (type) { + case SYS_RES_IOPORT: + rm = &sc->sc_ioport; + va = sc->sc_ioport_va; + break; + case SYS_RES_MEMORY: + rm = &sc->sc_iomem; + va = sc->sc_iomem_va; + break; + case SYS_RES_IRQ: + /* ISA interrupts are routed to IRQ 0 on the PIC. */ + if (start < PIC_IRQ_START) { + device_printf(dev, "%s requested ISA interrupt %lu\n", + device_get_nameunit(child), start); + /* XXX */ + start = PIC_IRQ_EXT(0); + } + return (BUS_ALLOC_RESOURCE(device_get_parent(dev), child, + type, rid, start, end, count, flags)); + default: + return (NULL); + } + + res = rman_reserve_resource(rm, start, end, count, flags, child); + if (res == NULL) + return (NULL); + + rman_set_bustag(res, &bs_le_tag); + rman_set_bushandle(res, va + rman_get_start(res) - rm->rm_start); + return (res); +} + +static int +pci_ocp_release_resource(device_t dev, device_t child, int type, int rid, + struct resource *res) +{ + + return (rman_release_resource(res)); +} + +static int +pci_ocp_read_ivar(device_t dev, device_t child, int which, uintptr_t *result) +{ + struct pci_ocp_softc *sc = device_get_softc(dev); + + switch (which) { + case PCIB_IVAR_BUS: + *result = sc->sc_busnr; + return (0); + } + return (ENOENT); +} + +static int +pci_ocp_write_ivar(device_t dev, device_t child, int which, uintptr_t value) +{ + struct pci_ocp_softc *sc = device_get_softc(dev); + + switch (which) { + case PCIB_IVAR_BUS: + sc->sc_busnr = value; + return (0); + } + return (ENOENT); +} |