From: Sebastian Huber <sebastian.hu...@embedded-brains.de> --- freebsd/sys/dev/ofw/ofwpci.c | 677 +++++++++++++ freebsd/sys/dev/ofw/ofwpci.h | 87 ++ freebsd/sys/dev/pci/pci_subr.c | 388 +++++++ freebsd/sys/powerpc/include/machine/hid.h | 224 ++++ freebsd/sys/powerpc/include/machine/pio.h | 306 ++++++ .../sys/powerpc/include/machine/platformvar.h | 91 ++ freebsd/sys/powerpc/mpc85xx/mpc85xx.c | 361 +++++++ freebsd/sys/powerpc/mpc85xx/mpc85xx.h | 177 ++++ freebsd/sys/powerpc/mpc85xx/pci_mpc85xx.c | 959 ++++++++++++++++++ .../sys/powerpc/mpc85xx/pci_mpc85xx_pcib.c | 111 ++ .../sys/powerpc/mpc85xx/platform_mpc85xx.c | 710 +++++++++++++ freebsd/sys/powerpc/ofw/ofw_pcib_pci.c | 178 ++++ 12 files changed, 4269 insertions(+) create mode 100644 freebsd/sys/dev/ofw/ofwpci.c create mode 100644 freebsd/sys/dev/ofw/ofwpci.h create mode 100644 freebsd/sys/dev/pci/pci_subr.c create mode 100644 freebsd/sys/powerpc/include/machine/hid.h create mode 100644 freebsd/sys/powerpc/include/machine/pio.h create mode 100644 freebsd/sys/powerpc/include/machine/platformvar.h create mode 100644 freebsd/sys/powerpc/mpc85xx/mpc85xx.c create mode 100644 freebsd/sys/powerpc/mpc85xx/mpc85xx.h create mode 100644 freebsd/sys/powerpc/mpc85xx/pci_mpc85xx.c create mode 100644 freebsd/sys/powerpc/mpc85xx/pci_mpc85xx_pcib.c create mode 100644 freebsd/sys/powerpc/mpc85xx/platform_mpc85xx.c create mode 100644 freebsd/sys/powerpc/ofw/ofw_pcib_pci.c
diff --git a/freebsd/sys/dev/ofw/ofwpci.c b/freebsd/sys/dev/ofw/ofwpci.c new file mode 100644 index 00000000..d576cb67 --- /dev/null +++ b/freebsd/sys/dev/ofw/ofwpci.c @@ -0,0 +1,677 @@ +#include <machine/rtems-bsd-kernel-space.h> + +/*- + * Copyright (c) 2011 Nathan Whitehorn + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/module.h> +#include <sys/bus.h> +#include <sys/conf.h> +#include <sys/kernel.h> +#include <sys/rman.h> + +#include <dev/ofw/openfirm.h> +#include <dev/ofw/ofw_pci.h> +#include <dev/ofw/ofw_bus.h> +#include <dev/ofw/ofw_bus_subr.h> +#include <dev/ofw/ofwpci.h> + +#include <dev/pci/pcivar.h> +#include <dev/pci/pcireg.h> +#include <dev/pci/pcib_private.h> + +#include <machine/bus.h> +#include <machine/md_var.h> +#include <machine/resource.h> + +#include <vm/vm.h> +#include <vm/pmap.h> + +#include <rtems/bsd/local/pcib_if.h> + +/* + * If it is necessary to set another value of this for + * some platforms it should be set at fdt.h file + */ +#ifndef PCI_MAP_INTR +#define PCI_MAP_INTR 4 +#endif + +#define PCI_INTR_PINS 4 + +/* + * bus interface. + */ +static struct resource * ofw_pci_alloc_resource(device_t, device_t, + int, int *, rman_res_t, rman_res_t, rman_res_t, u_int); +static int ofw_pci_release_resource(device_t, device_t, int, int, + struct resource *); +static int ofw_pci_activate_resource(device_t, device_t, int, int, + struct resource *); +static int ofw_pci_deactivate_resource(device_t, device_t, int, int, + struct resource *); +static int ofw_pci_adjust_resource(device_t, device_t, int, + struct resource *, rman_res_t, rman_res_t); + +#ifdef __powerpc__ +static bus_space_tag_t ofw_pci_bus_get_bus_tag(device_t, device_t); +#endif + +/* + * pcib interface + */ +static int ofw_pci_maxslots(device_t); + +/* + * ofw_bus interface + */ +static phandle_t ofw_pci_get_node(device_t, device_t); + +/* + * local methods + */ +static int ofw_pci_fill_ranges(phandle_t, struct ofw_pci_range *); +static struct rman *ofw_pci_get_rman(struct ofw_pci_softc *, int, u_int); + +/* + * Driver methods. + */ +static device_method_t ofw_pci_methods[] = { + + /* Device interface */ + DEVMETHOD(device_attach, ofw_pci_attach), + + /* Bus interface */ + DEVMETHOD(bus_print_child, bus_generic_print_child), + DEVMETHOD(bus_read_ivar, ofw_pci_read_ivar), + DEVMETHOD(bus_write_ivar, ofw_pci_write_ivar), + DEVMETHOD(bus_setup_intr, bus_generic_setup_intr), + DEVMETHOD(bus_teardown_intr, bus_generic_teardown_intr), + DEVMETHOD(bus_alloc_resource, ofw_pci_alloc_resource), + DEVMETHOD(bus_release_resource, ofw_pci_release_resource), + DEVMETHOD(bus_activate_resource, ofw_pci_activate_resource), + DEVMETHOD(bus_deactivate_resource, ofw_pci_deactivate_resource), + DEVMETHOD(bus_adjust_resource, ofw_pci_adjust_resource), +#ifdef __powerpc__ + DEVMETHOD(bus_get_bus_tag, ofw_pci_bus_get_bus_tag), +#endif + + /* pcib interface */ + DEVMETHOD(pcib_maxslots, ofw_pci_maxslots), + DEVMETHOD(pcib_route_interrupt, ofw_pci_route_interrupt), + DEVMETHOD(pcib_request_feature, pcib_request_feature_allow), + + /* ofw_bus interface */ + DEVMETHOD(ofw_bus_get_node, ofw_pci_get_node), + + DEVMETHOD_END +}; + +DEFINE_CLASS_0(ofw_pci, ofw_pci_driver, ofw_pci_methods, 0); + +int +ofw_pci_init(device_t dev) +{ + struct ofw_pci_softc *sc; + phandle_t node; + u_int32_t busrange[2]; + struct ofw_pci_range *rp; + int i, error; + struct ofw_pci_cell_info *cell_info; + + node = ofw_bus_get_node(dev); + sc = device_get_softc(dev); + sc->sc_initialized = 1; + sc->sc_range = NULL; + sc->sc_pci_domain = device_get_unit(dev); + + cell_info = (struct ofw_pci_cell_info *)malloc(sizeof(*cell_info), + M_DEVBUF, M_WAITOK | M_ZERO); + + sc->sc_cell_info = cell_info; + + if (OF_getencprop(node, "bus-range", busrange, sizeof(busrange)) != 8) + busrange[0] = 0; + + sc->sc_dev = dev; + sc->sc_node = node; + sc->sc_bus = busrange[0]; + + if (sc->sc_quirks & OFW_PCI_QUIRK_RANGES_ON_CHILDREN) { + phandle_t c; + int n, i; + + sc->sc_nrange = 0; + for (c = OF_child(node); c != 0; c = OF_peer(c)) { + n = ofw_pci_nranges(c, cell_info); + if (n > 0) + sc->sc_nrange += n; + } + if (sc->sc_nrange == 0) { + error = ENXIO; + goto out; + } + sc->sc_range = malloc(sc->sc_nrange * sizeof(sc->sc_range[0]), + M_DEVBUF, M_WAITOK); + i = 0; + for (c = OF_child(node); c != 0; c = OF_peer(c)) { + n = ofw_pci_fill_ranges(c, &sc->sc_range[i]); + if (n > 0) + i += n; + } + KASSERT(i == sc->sc_nrange, ("range count mismatch")); + } else { + sc->sc_nrange = ofw_pci_nranges(node, cell_info); + if (sc->sc_nrange <= 0) { + device_printf(dev, "could not getranges\n"); + error = ENXIO; + goto out; + } + sc->sc_range = malloc(sc->sc_nrange * sizeof(sc->sc_range[0]), + M_DEVBUF, M_WAITOK); + ofw_pci_fill_ranges(node, sc->sc_range); + } + + sc->sc_io_rman.rm_type = RMAN_ARRAY; + sc->sc_io_rman.rm_descr = "PCI I/O Ports"; + error = rman_init(&sc->sc_io_rman); + if (error != 0) { + device_printf(dev, "rman_init() failed. error = %d\n", error); + goto out; + } + + sc->sc_mem_rman.rm_type = RMAN_ARRAY; + sc->sc_mem_rman.rm_descr = "PCI Non Prefetchable Memory"; + error = rman_init(&sc->sc_mem_rman); + if (error != 0) { + device_printf(dev, "rman_init() failed. error = %d\n", error); + goto out; + } + + sc->sc_pmem_rman.rm_type = RMAN_ARRAY; + sc->sc_pmem_rman.rm_descr = "PCI Prefetchable Memory"; + error = rman_init(&sc->sc_pmem_rman); + if (error != 0) { + device_printf(dev, "rman_init() failed. error = %d\n", error); + goto out; + } + + for (i = 0; i < sc->sc_nrange; i++) { + error = 0; + rp = sc->sc_range + i; + + if (sc->sc_range_mask & ((uint64_t)1 << i)) + continue; + switch (rp->pci_hi & OFW_PCI_PHYS_HI_SPACEMASK) { + case OFW_PCI_PHYS_HI_SPACE_CONFIG: + break; + case OFW_PCI_PHYS_HI_SPACE_IO: + error = rman_manage_region(&sc->sc_io_rman, rp->pci, + rp->pci + rp->size - 1); + break; + case OFW_PCI_PHYS_HI_SPACE_MEM32: + case OFW_PCI_PHYS_HI_SPACE_MEM64: + if (rp->pci_hi & OFW_PCI_PHYS_HI_PREFETCHABLE) { + sc->sc_have_pmem = 1; + error = rman_manage_region(&sc->sc_pmem_rman, + rp->pci, rp->pci + rp->size - 1); + } else { + error = rman_manage_region(&sc->sc_mem_rman, + rp->pci, rp->pci + rp->size - 1); + } + break; + } + + if (error != 0) { + device_printf(dev, + "rman_manage_region(%x, %#jx, %#jx) failed. " + "error = %d\n", rp->pci_hi & + OFW_PCI_PHYS_HI_SPACEMASK, rp->pci, + rp->pci + rp->size - 1, error); + goto out; + } + } + + ofw_bus_setup_iinfo(node, &sc->sc_pci_iinfo, sizeof(cell_t)); + return (0); + +out: + free(cell_info, M_DEVBUF); + free(sc->sc_range, M_DEVBUF); + rman_fini(&sc->sc_io_rman); + rman_fini(&sc->sc_mem_rman); + rman_fini(&sc->sc_pmem_rman); + + return (error); +} + +int +ofw_pci_attach(device_t dev) +{ + struct ofw_pci_softc *sc; + int error; + + sc = device_get_softc(dev); + if (!sc->sc_initialized) { + error = ofw_pci_init(dev); + if (error != 0) + return (error); + } + + device_add_child(dev, "pci", -1); + return (bus_generic_attach(dev)); +} + +static int +ofw_pci_maxslots(device_t dev) +{ + + return (PCI_SLOTMAX); +} + +int +ofw_pci_route_interrupt(device_t bus, device_t dev, int pin) +{ + struct ofw_pci_softc *sc; + struct ofw_pci_register reg; + uint32_t pintr, mintr[PCI_MAP_INTR]; + int intrcells; + phandle_t iparent; + + sc = device_get_softc(bus); + pintr = pin; + + /* Fabricate imap information in case this isn't an OFW device */ + bzero(®, sizeof(reg)); + reg.phys_hi = (pci_get_bus(dev) << OFW_PCI_PHYS_HI_BUSSHIFT) | + (pci_get_slot(dev) << OFW_PCI_PHYS_HI_DEVICESHIFT) | + (pci_get_function(dev) << OFW_PCI_PHYS_HI_FUNCTIONSHIFT); + + intrcells = ofw_bus_lookup_imap(ofw_bus_get_node(dev), + &sc->sc_pci_iinfo, ®, sizeof(reg), &pintr, sizeof(pintr), + mintr, sizeof(mintr), &iparent); + if (intrcells != 0) { + pintr = ofw_bus_map_intr(dev, iparent, intrcells, mintr); + return (pintr); + } + + /* + * Maybe it's a real interrupt, not an intpin + */ + if (pin > PCI_INTR_PINS) + return (pin); + + device_printf(bus, "could not route pin %d for device %d.%d\n", + pin, pci_get_slot(dev), pci_get_function(dev)); + return (PCI_INVALID_IRQ); +} + +int +ofw_pci_read_ivar(device_t dev, device_t child, int which, uintptr_t *result) +{ + struct ofw_pci_softc *sc; + + sc = device_get_softc(dev); + + switch (which) { + case PCIB_IVAR_DOMAIN: + *result = sc->sc_pci_domain; + return (0); + case PCIB_IVAR_BUS: + *result = sc->sc_bus; + return (0); + default: + break; + } + + return (ENOENT); +} + +int +ofw_pci_write_ivar(device_t dev, device_t child, int which, uintptr_t value) +{ + struct ofw_pci_softc *sc; + + sc = device_get_softc(dev); + + switch (which) { + case PCIB_IVAR_BUS: + sc->sc_bus = value; + return (0); + default: + break; + } + + return (ENOENT); +} + +int +ofw_pci_nranges(phandle_t node, struct ofw_pci_cell_info *info) +{ + ssize_t nbase_ranges; + + if (info == NULL) + return (-1); + + info->host_address_cells = 1; + info->size_cells = 2; + info->pci_address_cell = 3; + + OF_getencprop(OF_parent(node), "#address-cells", + &(info->host_address_cells), sizeof(info->host_address_cells)); + OF_getencprop(node, "#address-cells", + &(info->pci_address_cell), sizeof(info->pci_address_cell)); + OF_getencprop(node, "#size-cells", &(info->size_cells), + sizeof(info->size_cells)); + + nbase_ranges = OF_getproplen(node, "ranges"); + if (nbase_ranges <= 0) + return (-1); + + return (nbase_ranges / sizeof(cell_t) / + (info->pci_address_cell + info->host_address_cells + + info->size_cells)); +} + +static struct resource * +ofw_pci_alloc_resource(device_t bus, device_t child, int type, int *rid, + rman_res_t start, rman_res_t end, rman_res_t count, u_int flags) +{ + struct ofw_pci_softc *sc; + struct resource *rv; + struct rman *rm; + int needactivate; + + + needactivate = flags & RF_ACTIVE; + flags &= ~RF_ACTIVE; + + sc = device_get_softc(bus); + +#if defined(NEW_PCIB) && defined(PCI_RES_BUS) + if (type == PCI_RES_BUS) { + return (pci_domain_alloc_bus(sc->sc_pci_domain, child, rid, + start, end, count, flags | needactivate)); + } +#endif + + rm = ofw_pci_get_rman(sc, type, flags); + if (rm == NULL) { + return (bus_generic_alloc_resource(bus, child, type, rid, + start, end, count, flags | needactivate)); + } + + rv = rman_reserve_resource(rm, start, end, count, flags, child); + if (rv == NULL) { + device_printf(bus, "failed to reserve resource for %s\n", + device_get_nameunit(child)); + return (NULL); + } + + rman_set_rid(rv, *rid); + + if (needactivate) { + if (bus_activate_resource(child, type, *rid, rv) != 0) { + device_printf(bus, + "failed to activate resource for %s\n", + device_get_nameunit(child)); + rman_release_resource(rv); + return (NULL); + } + } + + return (rv); +} + +static int +ofw_pci_release_resource(device_t bus, device_t child, int type, int rid, + struct resource *res) +{ + struct ofw_pci_softc *sc; + struct rman *rm; + int error; + + sc = device_get_softc(bus); + +#if defined(NEW_PCIB) && defined(PCI_RES_BUS) + if (type == PCI_RES_BUS) + return (pci_domain_release_bus(sc->sc_pci_domain, child, rid, + res)); +#endif + + rm = ofw_pci_get_rman(sc, type, rman_get_flags(res)); + if (rm == NULL) { + return (bus_generic_release_resource(bus, child, type, rid, + res)); + } + KASSERT(rman_is_region_manager(res, rm), ("rman mismatch")); + + if (rman_get_flags(res) & RF_ACTIVE) { + error = bus_deactivate_resource(child, type, rid, res); + if (error != 0) + return (error); + } + return (rman_release_resource(res)); +} + +static int +ofw_pci_activate_resource(device_t bus, device_t child, int type, int rid, + struct resource *res) +{ + struct ofw_pci_softc *sc; + bus_space_handle_t handle; + bus_space_tag_t tag; + struct ofw_pci_range *rp; + vm_paddr_t start; + int space; + int rv; + + sc = device_get_softc(bus); + + if (type != SYS_RES_IOPORT && type != SYS_RES_MEMORY) { + return (bus_generic_activate_resource(bus, child, type, rid, + res)); + } + + start = (vm_paddr_t)rman_get_start(res); + + /* + * Map this through the ranges list + */ + for (rp = sc->sc_range; rp < sc->sc_range + sc->sc_nrange && + rp->pci_hi != 0; rp++) { + if (start < rp->pci || start >= rp->pci + rp->size) + continue; + + switch (rp->pci_hi & OFW_PCI_PHYS_HI_SPACEMASK) { + case OFW_PCI_PHYS_HI_SPACE_IO: + space = SYS_RES_IOPORT; + break; + case OFW_PCI_PHYS_HI_SPACE_MEM32: + case OFW_PCI_PHYS_HI_SPACE_MEM64: + space = SYS_RES_MEMORY; + break; + default: + space = -1; + } + + if (type == space) { + start += (rp->host - rp->pci); + break; + } + } + + if (bootverbose) + printf("ofw_pci mapdev: start %jx, len %jd\n", + (rman_res_t)start, rman_get_size(res)); + + tag = BUS_GET_BUS_TAG(child, child); + if (tag == NULL) + return (ENOMEM); + + rman_set_bustag(res, tag); + rv = bus_space_map(tag, start, + rman_get_size(res), 0, &handle); + if (rv != 0) + return (ENOMEM); + + rman_set_bushandle(res, handle); + rman_set_virtual(res, (void *)handle); /* XXX for powerpc only ? */ + + return (rman_activate_resource(res)); +} + +#ifdef __powerpc__ +static bus_space_tag_t +ofw_pci_bus_get_bus_tag(device_t bus, device_t child) +{ + + return (&bs_le_tag); +} +#endif + +static int +ofw_pci_deactivate_resource(device_t bus, device_t child, int type, int rid, + struct resource *res) +{ + vm_size_t psize; + + if (type != SYS_RES_IOPORT && type != SYS_RES_MEMORY) { + return (bus_generic_deactivate_resource(bus, child, type, rid, + res)); + } + + psize = rman_get_size(res); + pmap_unmapdev((vm_offset_t)rman_get_virtual(res), psize); + + return (rman_deactivate_resource(res)); +} + +static int +ofw_pci_adjust_resource(device_t bus, device_t child, int type, + struct resource *res, rman_res_t start, rman_res_t end) +{ + struct rman *rm; + struct ofw_pci_softc *sc; + + sc = device_get_softc(bus); +#if defined(NEW_PCIB) && defined(PCI_RES_BUS) + if (type == PCI_RES_BUS) + return (pci_domain_adjust_bus(sc->sc_pci_domain, child, res, + start, end)); +#endif + + rm = ofw_pci_get_rman(sc, type, rman_get_flags(res)); + if (rm == NULL) { + return (bus_generic_adjust_resource(bus, child, type, res, + start, end)); + } + KASSERT(rman_is_region_manager(res, rm), ("rman mismatch")); + KASSERT(!(rman_get_flags(res) & RF_ACTIVE), + ("active resources cannot be adjusted")); + + return (rman_adjust_resource(res, start, end)); +} + +static phandle_t +ofw_pci_get_node(device_t bus, device_t dev) +{ + struct ofw_pci_softc *sc; + + sc = device_get_softc(bus); + /* We only have one child, the PCI bus, which needs our own node. */ + + return (sc->sc_node); +} + +static int +ofw_pci_fill_ranges(phandle_t node, struct ofw_pci_range *ranges) +{ + int host_address_cells = 1, pci_address_cells = 3, size_cells = 2; + cell_t *base_ranges; + ssize_t nbase_ranges; + int nranges; + int i, j, k; + + OF_getencprop(OF_parent(node), "#address-cells", &host_address_cells, + sizeof(host_address_cells)); + OF_getencprop(node, "#address-cells", &pci_address_cells, + sizeof(pci_address_cells)); + OF_getencprop(node, "#size-cells", &size_cells, sizeof(size_cells)); + + nbase_ranges = OF_getproplen(node, "ranges"); + if (nbase_ranges <= 0) + return (-1); + nranges = nbase_ranges / sizeof(cell_t) / + (pci_address_cells + host_address_cells + size_cells); + + base_ranges = malloc(nbase_ranges, M_DEVBUF, M_WAITOK); + OF_getencprop(node, "ranges", base_ranges, nbase_ranges); + + for (i = 0, j = 0; i < nranges; i++) { + ranges[i].pci_hi = base_ranges[j++]; + ranges[i].pci = 0; + for (k = 0; k < pci_address_cells - 1; k++) { + ranges[i].pci <<= 32; + ranges[i].pci |= base_ranges[j++]; + } + ranges[i].host = 0; + for (k = 0; k < host_address_cells; k++) { + ranges[i].host <<= 32; + ranges[i].host |= base_ranges[j++]; + } + ranges[i].size = 0; + for (k = 0; k < size_cells; k++) { + ranges[i].size <<= 32; + ranges[i].size |= base_ranges[j++]; + } + } + + free(base_ranges, M_DEVBUF); + return (nranges); +} + +static struct rman * +ofw_pci_get_rman(struct ofw_pci_softc *sc, int type, u_int flags) +{ + + switch (type) { + case SYS_RES_IOPORT: + return (&sc->sc_io_rman); + case SYS_RES_MEMORY: + if (sc->sc_have_pmem && (flags & RF_PREFETCHABLE)) + return (&sc->sc_pmem_rman); + else + return (&sc->sc_mem_rman); + default: + break; + } + + return (NULL); +} diff --git a/freebsd/sys/dev/ofw/ofwpci.h b/freebsd/sys/dev/ofw/ofwpci.h new file mode 100644 index 00000000..3257fe68 --- /dev/null +++ b/freebsd/sys/dev/ofw/ofwpci.h @@ -0,0 +1,87 @@ +/*- + * Copyright (c) 2011 Nathan Whitehorn + * 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. + * + * $FreeBSD$ + */ + +#ifndef _DEV_OFW_OFWPCI_H_ +#define _DEV_OFW_OFWPCI_H_ + +/* + * Export class definition for inheritance purposes + */ +DECLARE_CLASS(ofw_pci_driver); + +struct ofw_pci_cell_info { + pcell_t host_address_cells; + pcell_t pci_address_cell; + pcell_t size_cells; + }; + +struct ofw_pci_range { + uint32_t pci_hi; + uint64_t pci; + uint64_t host; + uint64_t size; +}; + +/* + * Quirks for some adapters + */ +enum { + OFW_PCI_QUIRK_RANGES_ON_CHILDREN = 1, +}; + +struct ofw_pci_softc { + device_t sc_dev; + phandle_t sc_node; + int sc_bus; + int sc_initialized; + int sc_quirks; + int sc_have_pmem; + + struct ofw_pci_range *sc_range; + int sc_nrange; + uint64_t sc_range_mask; + struct ofw_pci_cell_info *sc_cell_info; + + struct rman sc_io_rman; + struct rman sc_mem_rman; + struct rman sc_pmem_rman; + bus_space_tag_t sc_memt; + bus_dma_tag_t sc_dmat; + int sc_pci_domain; + + struct ofw_bus_iinfo sc_pci_iinfo; +}; + +int ofw_pci_init(device_t); +int ofw_pci_attach(device_t); +int ofw_pci_read_ivar(device_t, device_t, int, uintptr_t *); +int ofw_pci_write_ivar(device_t, device_t, int, uintptr_t); +int ofw_pci_route_interrupt(device_t, device_t, int); +int ofw_pci_nranges(phandle_t, struct ofw_pci_cell_info *); + +#endif /* _DEV_OFW_OFWPCI_H_ */ diff --git a/freebsd/sys/dev/pci/pci_subr.c b/freebsd/sys/dev/pci/pci_subr.c new file mode 100644 index 00000000..1b0fac29 --- /dev/null +++ b/freebsd/sys/dev/pci/pci_subr.c @@ -0,0 +1,388 @@ +#include <machine/rtems-bsd-kernel-space.h> + +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Copyright (c) 2011 Hudson River Trading LLC + * Written by: John H. Baldwin <j...@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. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +/* + * Support APIs for Host to PCI bridge drivers and drivers that + * provide PCI domains. + */ + +#include <sys/param.h> +#include <sys/bus.h> +#include <sys/malloc.h> +#include <sys/rman.h> +#include <sys/systm.h> + +#include <dev/pci/pcireg.h> +#include <dev/pci/pcivar.h> +#include <dev/pci/pcib_private.h> + +/* + * Try to read the bus number of a host-PCI bridge using appropriate config + * registers. + */ +int +host_pcib_get_busno(pci_read_config_fn read_config, int bus, int slot, int func, + uint8_t *busnum) +{ + uint32_t id; + + id = read_config(bus, slot, func, PCIR_DEVVENDOR, 4); + if (id == 0xffffffff) + return (0); + + switch (id) { + case 0x12258086: + /* Intel 824?? */ + /* XXX This is a guess */ + /* *busnum = read_config(bus, slot, func, 0x41, 1); */ + *busnum = bus; + break; + case 0x84c48086: + /* Intel 82454KX/GX (Orion) */ + *busnum = read_config(bus, slot, func, 0x4a, 1); + break; + case 0x84ca8086: + /* + * For the 450nx chipset, there is a whole bundle of + * things pretending to be host bridges. The MIOC will + * be seen first and isn't really a pci bridge (the + * actual buses are attached to the PXB's). We need to + * read the registers of the MIOC to figure out the + * bus numbers for the PXB channels. + * + * Since the MIOC doesn't have a pci bus attached, we + * pretend it wasn't there. + */ + return (0); + case 0x84cb8086: + switch (slot) { + case 0x12: + /* Intel 82454NX PXB#0, Bus#A */ + *busnum = read_config(bus, 0x10, func, 0xd0, 1); + break; + case 0x13: + /* Intel 82454NX PXB#0, Bus#B */ + *busnum = read_config(bus, 0x10, func, 0xd1, 1) + 1; + break; + case 0x14: + /* Intel 82454NX PXB#1, Bus#A */ + *busnum = read_config(bus, 0x10, func, 0xd3, 1); + break; + case 0x15: + /* Intel 82454NX PXB#1, Bus#B */ + *busnum = read_config(bus, 0x10, func, 0xd4, 1) + 1; + break; + } + break; + + /* ServerWorks -- vendor 0x1166 */ + case 0x00051166: + case 0x00061166: + case 0x00081166: + case 0x00091166: + case 0x00101166: + case 0x00111166: + case 0x00171166: + case 0x01011166: + case 0x010f1014: + case 0x01101166: + case 0x02011166: + case 0x02251166: + case 0x03021014: + *busnum = read_config(bus, slot, func, 0x44, 1); + break; + + /* Compaq/HP -- vendor 0x0e11 */ + case 0x60100e11: + *busnum = read_config(bus, slot, func, 0xc8, 1); + break; + default: + /* Don't know how to read bus number. */ + return 0; + } + + return 1; +} + +#ifdef NEW_PCIB +/* + * Return a pointer to a pretty name for a PCI device. If the device + * has a driver attached, the device's name is used, otherwise a name + * is generated from the device's PCI address. + */ +const char * +pcib_child_name(device_t child) +{ + static char buf[64]; + + if (device_get_nameunit(child) != NULL) + return (device_get_nameunit(child)); + snprintf(buf, sizeof(buf), "pci%d:%d:%d:%d", pci_get_domain(child), + pci_get_bus(child), pci_get_slot(child), pci_get_function(child)); + return (buf); +} + +/* + * Some Host-PCI bridge drivers know which resource ranges they can + * decode and should only allocate subranges to child PCI devices. + * This API provides a way to manage this. The bridge driver should + * initialize this structure during attach and call + * pcib_host_res_decodes() on each resource range it decodes. It can + * then use pcib_host_res_alloc() and pcib_host_res_adjust() as helper + * routines for BUS_ALLOC_RESOURCE() and BUS_ADJUST_RESOURCE(). This + * API assumes that resources for any decoded ranges can be safely + * allocated from the parent via bus_generic_alloc_resource(). + */ +int +pcib_host_res_init(device_t pcib, struct pcib_host_resources *hr) +{ + + hr->hr_pcib = pcib; + resource_list_init(&hr->hr_rl); + return (0); +} + +int +pcib_host_res_free(device_t pcib, struct pcib_host_resources *hr) +{ + + resource_list_free(&hr->hr_rl); + return (0); +} + +int +pcib_host_res_decodes(struct pcib_host_resources *hr, int type, rman_res_t start, + rman_res_t end, u_int flags) +{ + struct resource_list_entry *rle; + int rid; + + if (bootverbose) + device_printf(hr->hr_pcib, "decoding %d %srange %#jx-%#jx\n", + type, flags & RF_PREFETCHABLE ? "prefetchable ": "", start, + end); + rid = resource_list_add_next(&hr->hr_rl, type, start, end, + end - start + 1); + if (flags & RF_PREFETCHABLE) { + KASSERT(type == SYS_RES_MEMORY, + ("only memory is prefetchable")); + rle = resource_list_find(&hr->hr_rl, type, rid); + rle->flags = RLE_PREFETCH; + } + return (0); +} + +struct resource * +pcib_host_res_alloc(struct pcib_host_resources *hr, device_t dev, int type, + int *rid, rman_res_t start, rman_res_t end, rman_res_t count, u_int flags) +{ + struct resource_list_entry *rle; + struct resource *r; + rman_res_t new_start, new_end; + + if (flags & RF_PREFETCHABLE) + KASSERT(type == SYS_RES_MEMORY, + ("only memory is prefetchable")); + + rle = resource_list_find(&hr->hr_rl, type, 0); + if (rle == NULL) { + /* + * No decoding ranges for this resource type, just pass + * the request up to the parent. + */ + return (bus_generic_alloc_resource(hr->hr_pcib, dev, type, rid, + start, end, count, flags)); + } + +restart: + /* Try to allocate from each decoded range. */ + for (; rle != NULL; rle = STAILQ_NEXT(rle, link)) { + if (rle->type != type) + continue; + if (((flags & RF_PREFETCHABLE) != 0) != + ((rle->flags & RLE_PREFETCH) != 0)) + continue; + new_start = ummax(start, rle->start); + new_end = ummin(end, rle->end); + if (new_start > new_end || + new_start + count - 1 > new_end || + new_start + count < new_start) + continue; + r = bus_generic_alloc_resource(hr->hr_pcib, dev, type, rid, + new_start, new_end, count, flags); + if (r != NULL) { + if (bootverbose) + device_printf(hr->hr_pcib, + "allocated type %d (%#jx-%#jx) for rid %x of %s\n", + type, rman_get_start(r), rman_get_end(r), + *rid, pcib_child_name(dev)); + return (r); + } + } + + /* + * If we failed to find a prefetch range for a memory + * resource, try again without prefetch. + */ + if (flags & RF_PREFETCHABLE) { + flags &= ~RF_PREFETCHABLE; + rle = resource_list_find(&hr->hr_rl, type, 0); + goto restart; + } + return (NULL); +} + +int +pcib_host_res_adjust(struct pcib_host_resources *hr, device_t dev, int type, + struct resource *r, rman_res_t start, rman_res_t end) +{ + struct resource_list_entry *rle; + + rle = resource_list_find(&hr->hr_rl, type, 0); + if (rle == NULL) { + /* + * No decoding ranges for this resource type, just pass + * the request up to the parent. + */ + return (bus_generic_adjust_resource(hr->hr_pcib, dev, type, r, + start, end)); + } + + /* Only allow adjustments that stay within a decoded range. */ + for (; rle != NULL; rle = STAILQ_NEXT(rle, link)) { + if (rle->start <= start && rle->end >= end) + return (bus_generic_adjust_resource(hr->hr_pcib, dev, + type, r, start, end)); + } + return (ERANGE); +} + +#ifdef PCI_RES_BUS +struct pci_domain { + int pd_domain; + struct rman pd_bus_rman; + TAILQ_ENTRY(pci_domain) pd_link; +}; + +static TAILQ_HEAD(, pci_domain) domains = TAILQ_HEAD_INITIALIZER(domains); + +/* + * Each PCI domain maintains its own resource manager for PCI bus + * numbers in that domain. Domain objects are created on first use. + * Host to PCI bridge drivers and PCI-PCI bridge drivers should + * allocate their bus ranges from their domain. + */ +static struct pci_domain * +pci_find_domain(int domain) +{ + struct pci_domain *d; + char buf[64]; + int error; + + TAILQ_FOREACH(d, &domains, pd_link) { + if (d->pd_domain == domain) + return (d); + } + + snprintf(buf, sizeof(buf), "PCI domain %d bus numbers", domain); + d = malloc(sizeof(*d) + strlen(buf) + 1, M_DEVBUF, M_WAITOK | M_ZERO); + d->pd_domain = domain; + d->pd_bus_rman.rm_start = 0; + d->pd_bus_rman.rm_end = PCI_BUSMAX; + d->pd_bus_rman.rm_type = RMAN_ARRAY; + strcpy((char *)(d + 1), buf); + d->pd_bus_rman.rm_descr = (char *)(d + 1); + error = rman_init(&d->pd_bus_rman); + if (error == 0) + error = rman_manage_region(&d->pd_bus_rman, 0, PCI_BUSMAX); + if (error) + panic("Failed to initialize PCI domain %d rman", domain); + TAILQ_INSERT_TAIL(&domains, d, pd_link); + return (d); +} + +struct resource * +pci_domain_alloc_bus(int domain, device_t dev, int *rid, rman_res_t start, + rman_res_t end, rman_res_t count, u_int flags) +{ + struct pci_domain *d; + struct resource *res; + + if (domain < 0 || domain > PCI_DOMAINMAX) + return (NULL); + d = pci_find_domain(domain); + res = rman_reserve_resource(&d->pd_bus_rman, start, end, count, flags, + dev); + if (res == NULL) + return (NULL); + + rman_set_rid(res, *rid); + return (res); +} + +int +pci_domain_adjust_bus(int domain, device_t dev, struct resource *r, + rman_res_t start, rman_res_t end) +{ +#ifdef INVARIANTS + struct pci_domain *d; +#endif + + if (domain < 0 || domain > PCI_DOMAINMAX) + return (EINVAL); +#ifdef INVARIANTS + d = pci_find_domain(domain); + KASSERT(rman_is_region_manager(r, &d->pd_bus_rman), ("bad resource")); +#endif + return (rman_adjust_resource(r, start, end)); +} + +int +pci_domain_release_bus(int domain, device_t dev, int rid, struct resource *r) +{ +#ifdef INVARIANTS + struct pci_domain *d; +#endif + + if (domain < 0 || domain > PCI_DOMAINMAX) + return (EINVAL); +#ifdef INVARIANTS + d = pci_find_domain(domain); + KASSERT(rman_is_region_manager(r, &d->pd_bus_rman), ("bad resource")); +#endif + return (rman_release_resource(r)); +} +#endif /* PCI_RES_BUS */ + +#endif /* NEW_PCIB */ diff --git a/freebsd/sys/powerpc/include/machine/hid.h b/freebsd/sys/powerpc/include/machine/hid.h new file mode 100644 index 00000000..1b038111 --- /dev/null +++ b/freebsd/sys/powerpc/include/machine/hid.h @@ -0,0 +1,224 @@ +/*- + * SPDX-License-Identifier: BSD-3-Clause + * + * Copyright (c) 2000 Tsubai Masanari. 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. + * + * $NetBSD: hid.h,v 1.2 2001/08/22 21:05:25 matt Exp $ + * $FreeBSD$ + */ + +#ifndef _POWERPC_HID_H_ +#define _POWERPC_HID_H_ + +/* Hardware Implementation Dependent registers for the PowerPC */ +#define HID0_RADIX 0x0080000000000000 /* Enable Radix page tables (POWER9) */ + +#define HID0_EMCP 0x80000000 /* Enable machine check pin */ +#define HID0_DBP 0x40000000 /* Disable 60x bus parity generation */ +#define HID0_EBA 0x20000000 /* Enable 60x bus address parity checking */ +#define HID0_EBD 0x10000000 /* Enable 60x bus data parity checking */ +#define HID0_BCLK 0x08000000 /* CLK_OUT clock type selection */ +#define HID0_EICE 0x04000000 /* Enable ICE output */ +#define HID0_ECLK 0x02000000 /* CLK_OUT clock type selection */ +#define HID0_PAR 0x01000000 /* Disable precharge of ARTRY */ +#define HID0_STEN 0x01000000 /* Software table search enable (7450) */ +#define HID0_DEEPNAP 0x01000000 /* Enable deep nap mode (970) */ +#define HID0_HBATEN 0x00800000 /* High BAT enable (74[45][578]) */ +#define HID0_DOZE 0x00800000 /* Enable doze mode */ +#define HID0_NAP 0x00400000 /* Enable nap mode */ +#define HID0_SLEEP 0x00200000 /* Enable sleep mode */ +#define HID0_DPM 0x00100000 /* Enable Dynamic power management */ +#define HID0_RISEG 0x00080000 /* Read I-SEG */ +#define HID0_TG 0x00040000 /* Timebase Granularity (OEA64) */ +#define HID0_BHTCLR 0x00040000 /* Clear branch history table (7450) */ +#define HID0_EIEC 0x00040000 /* Enable internal error checking */ +#define HID0_XAEN 0x00020000 /* Enable eXtended Addressing (7450) */ +#define HID0_NHR 0x00010000 /* Not hard reset */ +#define HID0_ICE 0x00008000 /* Enable i-cache */ +#define HID0_DCE 0x00004000 /* Enable d-cache */ +#define HID0_ILOCK 0x00002000 /* i-cache lock */ +#define HID0_DLOCK 0x00001000 /* d-cache lock */ +#define HID0_ICFI 0x00000800 /* i-cache flush invalidate */ +#define HID0_DCFI 0x00000400 /* d-cache flush invalidate */ +#define HID0_SPD 0x00000200 /* Disable speculative cache access */ +#define HID0_XBSEN 0x00000100 /* Extended BAT block-size enable (7457) */ +#define HID0_IFEM 0x00000100 /* Enable M-bit for I-fetch */ +#define HID0_XBSEN 0x00000100 /* Extended BAT block size enable (7455+)*/ +#define HID0_SGE 0x00000080 /* Enable store gathering */ +#define HID0_DCFA 0x00000040 /* Data cache flush assist */ +#define HID0_BTIC 0x00000020 /* Enable BTIC */ +#define HID0_LRSTK 0x00000010 /* Link register stack enable (7450) */ +#define HID0_ABE 0x00000008 /* Enable address broadcast */ +#define HID0_FOLD 0x00000008 /* Branch folding enable (7450) */ +#define HID0_BHT 0x00000004 /* Enable branch history table */ +#define HID0_NOPTI 0x00000001 /* No-op the dcbt(st) */ + +#define HID0_AIM_TBEN 0x04000000 /* Time base enable (7450) */ + +#define HID0_E500_TBEN 0x00004000 /* Time Base and decr. enable */ +#define HID0_E500_SEL_TBCLK 0x00002000 /* Select Time Base clock */ +#define HID0_E500_MAS7UPDEN 0x00000080 /* Enable MAS7 update (e500v2) */ + +#define HID0_E500MC_L2MMU_MHD 0x40000000 /* L2MMU Multiple Hit Detection */ + +#define HID0_BITMASK \ + "\20" \ + "\040EMCP\037DBP\036EBA\035EBD\034BCLK\033EICE\032ECLK\031PAR" \ + "\030DOZE\027NAP\026SLEEP\025DPM\024RISEG\023EIEC\022res\021NHR" \ + "\020ICE\017DCE\016ILOCK\015DLOCK\014ICFI\013DCFI\012SPD\011IFEM" \ + "\010SGE\007DCFA\006BTIC\005FBIOB\004ABE\003BHT\002NOPDST\001NOPTI" + +#define HID0_7450_BITMASK \ + "\20" \ + "\040EMCP\037b1\036b2\035b3\034b4\033TBEN\032b6\031STEN" \ + "\030HBATEN\027NAP\026SLEEP\025DPM\024b12\023BHTCLR\022XAEN\021NHR" \ + "\020ICE\017DCE\016ILOCK\015DLOCK\014ICFI\013DCFI\012SPD\011XBSEN" \ + "\010SGE\007b25\006BTIC\005LRSTK\004FOLD\003BHT\002NOPDST\001NOPTI" + +#define HID0_E500_BITMASK \ + "\20" \ + "\040EMCP\037b1\036b2\035b3\034b4\033b5\032b6\031b7" \ + "\030DOZE\027NAP\026SLEEP\025b11\024b12\023b13\022b14\021b15" \ + "\020b16\017TBEN\016SEL_TBCLK\015b19\014b20\013b21\012b22\011b23" \ + "\010EN_MAS7_UPDATE\007DCFA\006b26\005b27\004b28\003b29\002b30\001NOPTI" + +#define HID0_970_BITMASK \ + "\20" \ + "\040ONEPPC\037SINGLE\036ISYNCSC\035SERGP\031DEEPNAP\030DOZE" \ + "\027NAP\025DPM\023TG\022HANGDETECT\021NHR\020INORDER" \ + "\016TBCTRL\015TBEN\012CIABREN\011HDICEEN\001ENATTN" + +#define HID0_E500MC_BITMASK \ + "\20" \ + "\040EMCP\037EN_L2MMU_MHD\036b2\035b3\034b4\033b5\032b6\031b7" \ + "\030b8\027b9\026b10\025b11\024b12\023b13\022b14\021b15" \ + "\020b16\017b17\016b18\015b19\014b20\013b21\012b22\011b23" \ + "\010EN_MAS7_UPDATE\007DCFA\006b26\005CIGLSO\004b28\003b29\002b30\001NOPTI" + +#define HID0_E5500_BITMASK \ + "\20" \ + "\040EMCP\037EN_L2MMU_MHD\036b2\035b3\034b4\033b5\032b6\031b7" \ + "\030b8\027b9\026b10\025b11\024b12\023b13\022b14\021b15" \ + "\020b16\017b17\016b18\015b19\014b20\013b21\012b22\011b23" \ + "\010b24\007DCFA\006b26\005CIGLSO\004b28\003b29\002b30\001NOPTI" + +/* + * HID0 bit definitions per cpu model + * + * bit 603 604 750 7400 7410 7450 7457 e500 + * 0 EMCP EMCP EMCP EMCP EMCP - - EMCP + * 1 - ECP DBP - - - - - + * 2 EBA EBA EBA EBA EDA - - - + * 3 EBD EBD EBD EBD EBD - - - + * 4 SBCLK - BCLK BCKL BCLK - - - + * 5 EICE - - - - TBEN TBEN - + * 6 ECLK - ECLK ECLK ECLK - - - + * 7 PAR PAR PAR PAR PAR STEN STEN - + * 8 DOZE - DOZE DOZE DOZE - HBATEN DOZE + * 9 NAP - NAP NAP NAP NAP NAP NAP + * 10 SLEEP - SLEEP SLEEP SLEEP SLEEP SLEEP SLEEP + * 11 DPM - DPM DPM DPM DPM DPM - + * 12 RISEG - - RISEG - - - - + * 13 - - - EIEC EIEC BHTCLR BHTCLR - + * 14 - - - - - XAEN XAEN - + * 15 - NHR NHR NHR NHR NHR NHR - + * 16 ICE ICE ICE ICE ICE ICE ICE - + * 17 DCE DCE DCE DCE DCE DCE DCE TBEN + * 18 ILOCK ILOCK ILOCK ILOCK ILOCK ILOCK ILOCK SEL_TBCLK + * 19 DLOCK DLOCK DLOCK DLOCK DLOCK DLOCK DLOCK - + * 20 ICFI ICFI ICFI ICFI ICFI ICFI ICFI - + * 21 DCFI DCFI DCFI DCFI DCFI DCFI DCFI - + * 22 - - SPD SPD SPG SPD SPD - + * 23 - - IFEM IFTT IFTT - XBSEN - + * 24 - SIE SGE SGE SGE SGE SGE EN_MAS7_UPDATE + * 25 - - DCFA DCFA DCFA - - DCFA + * 26 - - BTIC BTIC BTIC BTIC BTIC - + * 27 FBIOB - - - - LRSTK LRSTK - + * 28 - - ABE - - FOLD FOLD - + * 29 - BHT BHT BHT BHT BHT BHT - + * 30 - - - NOPDST NOPDST NOPDST NOPDST - + * 31 NOOPTI - NOOPTI NOPTI NOPTI NOPTI NOPTI NOPTI + * + * bit e500mc e5500 + * 0 EMCP EMCP + * 1 EN_L2MMU_MHD EN_L2MMU_MHD + * 2 - - + * 3 - - + * 4 - - + * 5 - - + * 6 - - + * 7 - - + * 8 - - + * 9 - - + * 10 - - + * 11 - - + * 12 - - + * 13 - - + * 14 - - + * 15 - - + * 16 - - + * 17 - - + * 18 - - + * 19 - - + * 20 - - + * 21 - - + * 22 - - + * 23 - - + * 24 EN_MAS7_UPDATE - + * 25 DCFA DCFA + * 26 - - + * 27 CIGLSO CIGLSO + * 28 - - + * 29 - - + * 30 - - + * 31 NOPTI NOPTI + * + * 604: ECP = Enable cache parity checking + * 604: SIE = Serial instruction execution disable + * 7450: TBEN = Time Base Enable + * 7450: STEN = Software table lookup enable + * 7450: BHTCLR = Branch history clear + * 7450: XAEN = Extended Addressing Enabled + * 7450: LRSTK = Link Register Stack Enable + * 7450: FOLD = Branch folding enable + * 7457: HBATEN = High BAT Enable + * 7457: XBSEN = Extended BAT Block Size Enable + */ + +#define HID1_E500_ABE 0x00001000 /* Address broadcast enable */ +#define HID1_E500_ASTME 0x00002000 /* Address bus streaming mode enable */ +#define HID1_E500_RFXE 0x00020000 /* Read fault exception enable */ + +#define HID0_E500_DEFAULT_SET (HID0_EMCP | HID0_E500_TBEN | \ + HID0_E500_MAS7UPDEN) +#define HID1_E500_DEFAULT_SET (HID1_E500_ABE | HID1_E500_ASTME) +#define HID0_E500MC_DEFAULT_SET (HID0_EMCP | HID0_E500MC_L2MMU_MHD | \ + HID0_E500_MAS7UPDEN) +#define HID0_E5500_DEFAULT_SET (HID0_EMCP | HID0_E500MC_L2MMU_MHD) + +#define HID5_970_DCBZ_SIZE_HI 0x00000080UL /* dcbz does a 32-byte store */ +#define HID4_970_DISABLE_LG_PG 0x00000004ULL /* disables large pages */ + +#endif /* _POWERPC_HID_H_ */ diff --git a/freebsd/sys/powerpc/include/machine/pio.h b/freebsd/sys/powerpc/include/machine/pio.h new file mode 100644 index 00000000..a4d9b327 --- /dev/null +++ b/freebsd/sys/powerpc/include/machine/pio.h @@ -0,0 +1,306 @@ +/*- + * SPDX-License-Identifier: BSD-4-Clause + * + * Copyright (c) 1997 Per Fogelstrom, Opsycon AB and RTMX Inc, USA. + * + * 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. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed under OpenBSD by + * Per Fogelstrom Opsycon AB for RTMX Inc, North Carolina, USA. + * 4. 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. + * + * $NetBSD: pio.h,v 1.1 1998/05/15 10:15:54 tsubai Exp $ + * $OpenBSD: pio.h,v 1.1 1997/10/13 10:53:47 pefo Exp $ + * $FreeBSD$ + */ + +#ifndef _MACHINE_PIO_H_ +#define _MACHINE_PIO_H_ +/* + * I/O macros. + */ + +/* + * Use sync so that bus space operations cannot sneak out the bottom of + * mutex-protected sections (mutex release does not guarantee completion of + * accesses to caching-inhibited memory on some systems) + */ +#define powerpc_iomb() __asm __volatile("sync" : : : "memory") + +static __inline void +__outb(volatile u_int8_t *a, u_int8_t v) +{ + *a = v; + powerpc_iomb(); +} + +static __inline void +__outw(volatile u_int16_t *a, u_int16_t v) +{ + *a = v; + powerpc_iomb(); +} + +static __inline void +__outl(volatile u_int32_t *a, u_int32_t v) +{ + *a = v; + powerpc_iomb(); +} + +static __inline void +__outll(volatile u_int64_t *a, u_int64_t v) +{ + *a = v; + powerpc_iomb(); +} + +static __inline void +__outwrb(volatile u_int16_t *a, u_int16_t v) +{ + __asm__ volatile("sthbrx %0, 0, %1" :: "r"(v), "r"(a)); + powerpc_iomb(); +} + +static __inline void +__outlrb(volatile u_int32_t *a, u_int32_t v) +{ + __asm__ volatile("stwbrx %0, 0, %1" :: "r"(v), "r"(a)); + powerpc_iomb(); +} + +static __inline u_int8_t +__inb(volatile u_int8_t *a) +{ + u_int8_t _v_; + + _v_ = *a; + powerpc_iomb(); + return _v_; +} + +static __inline u_int16_t +__inw(volatile u_int16_t *a) +{ + u_int16_t _v_; + + _v_ = *a; + powerpc_iomb(); + return _v_; +} + +static __inline u_int32_t +__inl(volatile u_int32_t *a) +{ + u_int32_t _v_; + + _v_ = *a; + powerpc_iomb(); + return _v_; +} + +static __inline u_int64_t +__inll(volatile u_int64_t *a) +{ + u_int64_t _v_; + + _v_ = *a; + powerpc_iomb(); + return _v_; +} + +static __inline u_int16_t +__inwrb(volatile u_int16_t *a) +{ + u_int16_t _v_; + + __asm__ volatile("lhbrx %0, 0, %1" : "=r"(_v_) : "r"(a)); + powerpc_iomb(); + return _v_; +} + +static __inline u_int32_t +__inlrb(volatile u_int32_t *a) +{ + u_int32_t _v_; + + __asm__ volatile("lwbrx %0, 0, %1" : "=r"(_v_) : "r"(a)); + powerpc_iomb(); + return _v_; +} + +#define outb(a,v) (__outb((volatile u_int8_t *)(a), v)) +#define out8(a,v) outb(a,v) +#define outw(a,v) (__outw((volatile u_int16_t *)(a), v)) +#define out16(a,v) outw(a,v) +#define outl(a,v) (__outl((volatile u_int32_t *)(a), v)) +#define out32(a,v) outl(a,v) +#define outll(a,v) (__outll((volatile u_int64_t *)(a), v)) +#define out64(a,v) outll(a,v) +#define inb(a) (__inb((volatile u_int8_t *)(a))) +#define in8(a) inb(a) +#define inw(a) (__inw((volatile u_int16_t *)(a))) +#define in16(a) inw(a) +#define inl(a) (__inl((volatile u_int32_t *)(a))) +#define in32(a) inl(a) +#define inll(a) (__inll((volatile u_int64_t *)(a))) +#define in64(a) inll(a) + +#define out8rb(a,v) outb(a,v) +#define outwrb(a,v) (__outwrb((volatile u_int16_t *)(a), v)) +#define out16rb(a,v) outwrb(a,v) +#define outlrb(a,v) (__outlrb((volatile u_int32_t *)(a), v)) +#define out32rb(a,v) outlrb(a,v) +#define in8rb(a) inb(a) +#define inwrb(a) (__inwrb((volatile u_int16_t *)(a))) +#define in16rb(a) inwrb(a) +#define inlrb(a) (__inlrb((volatile u_int32_t *)(a))) +#define in32rb(a) inlrb(a) + + +static __inline void +__outsb(volatile u_int8_t *a, const u_int8_t *s, size_t c) +{ + while (c--) + *a = *s++; + powerpc_iomb(); +} + +static __inline void +__outsw(volatile u_int16_t *a, const u_int16_t *s, size_t c) +{ + while (c--) + *a = *s++; + powerpc_iomb(); +} + +static __inline void +__outsl(volatile u_int32_t *a, const u_int32_t *s, size_t c) +{ + while (c--) + *a = *s++; + powerpc_iomb(); +} + +static __inline void +__outsll(volatile u_int64_t *a, const u_int64_t *s, size_t c) +{ + while (c--) + *a = *s++; + powerpc_iomb(); +} + +static __inline void +__outswrb(volatile u_int16_t *a, const u_int16_t *s, size_t c) +{ + while (c--) + __asm__ volatile("sthbrx %0, 0, %1" :: "r"(*s++), "r"(a)); + powerpc_iomb(); +} + +static __inline void +__outslrb(volatile u_int32_t *a, const u_int32_t *s, size_t c) +{ + while (c--) + __asm__ volatile("stwbrx %0, 0, %1" :: "r"(*s++), "r"(a)); + powerpc_iomb(); +} + +static __inline void +__insb(volatile u_int8_t *a, u_int8_t *d, size_t c) +{ + while (c--) + *d++ = *a; + powerpc_iomb(); +} + +static __inline void +__insw(volatile u_int16_t *a, u_int16_t *d, size_t c) +{ + while (c--) + *d++ = *a; + powerpc_iomb(); +} + +static __inline void +__insl(volatile u_int32_t *a, u_int32_t *d, size_t c) +{ + while (c--) + *d++ = *a; + powerpc_iomb(); +} + +static __inline void +__insll(volatile u_int64_t *a, u_int64_t *d, size_t c) +{ + while (c--) + *d++ = *a; + powerpc_iomb(); +} + +static __inline void +__inswrb(volatile u_int16_t *a, u_int16_t *d, size_t c) +{ + while (c--) + __asm__ volatile("lhbrx %0, 0, %1" : "=r"(*d++) : "r"(a)); + powerpc_iomb(); +} + +static __inline void +__inslrb(volatile u_int32_t *a, u_int32_t *d, size_t c) +{ + while (c--) + __asm__ volatile("lwbrx %0, 0, %1" : "=r"(*d++) : "r"(a)); + powerpc_iomb(); +} + +#define outsb(a,s,c) (__outsb((volatile u_int8_t *)(a), s, c)) +#define outs8(a,s,c) outsb(a,s,c) +#define outsw(a,s,c) (__outsw((volatile u_int16_t *)(a), s, c)) +#define outs16(a,s,c) outsw(a,s,c) +#define outsl(a,s,c) (__outsl((volatile u_int32_t *)(a), s, c)) +#define outs32(a,s,c) outsl(a,s,c) +#define outsll(a,s,c) (__outsll((volatile u_int64_t *)(a), s, c)) +#define outs64(a,s,c) outsll(a,s,c) +#define insb(a,d,c) (__insb((volatile u_int8_t *)(a), d, c)) +#define ins8(a,d,c) insb(a,d,c) +#define insw(a,d,c) (__insw((volatile u_int16_t *)(a), d, c)) +#define ins16(a,d,c) insw(a,d,c) +#define insl(a,d,c) (__insl((volatile u_int32_t *)(a), d, c)) +#define ins32(a,d,c) insl(a,d,c) +#define insll(a,d,c) (__insll((volatile u_int64_t *)(a), d, c)) +#define ins64(a,d,c) insll(a,d,c) + +#define outs8rb(a,s,c) outsb(a,s,c) +#define outswrb(a,s,c) (__outswrb((volatile u_int16_t *)(a), s, c)) +#define outs16rb(a,s,c) outswrb(a,s,c) +#define outslrb(a,s,c) (__outslrb((volatile u_int32_t *)(a), s, c)) +#define outs32rb(a,s,c) outslrb(a,s,c) +#define ins8rb(a,d,c) insb(a,d,c) +#define inswrb(a,d,c) (__inswrb((volatile u_int16_t *)(a), d, c)) +#define ins16rb(a,d,c) inswrb(a,d,c) +#define inslrb(a,d,c) (__inslrb((volatile u_int32_t *)(a), d, c)) +#define ins32rb(a,d,c) inslrb(a,d,c) + +#endif /*_MACHINE_PIO_H_*/ diff --git a/freebsd/sys/powerpc/include/machine/platformvar.h b/freebsd/sys/powerpc/include/machine/platformvar.h new file mode 100644 index 00000000..d5928e72 --- /dev/null +++ b/freebsd/sys/powerpc/include/machine/platformvar.h @@ -0,0 +1,91 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Copyright (c) 2005 Peter Grehan + * 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. + * + * $FreeBSD$ + */ + +#ifndef _MACHINE_PLATFORMVAR_H_ +#define _MACHINE_PLATFORMVAR_H_ + +/* + * A PowerPC platform implementation is declared with a kernel object and + * an associated method table, similar to a device driver. + * + * e.g. + * + * static platform_method_t chrp_methods[] = { + * PLATFORMMETHOD(platform_probe, chrp_probe), + * PLATFORMMETHOD(platform_mem_regions, ofw_mem_regions), + * ... + * PLATFORMMETHOD(platform_smp_first_cpu, chrp_smp_first_cpu), + * { 0, 0 } + * }; + * + * static platform_def_t chrp_platform = { + * "chrp", + * chrp_methods, + * sizeof(chrp_platform_softc), // or 0 if no softc + * }; + * + * PLATFORM_DEF(chrp_platform); + */ + +#include <sys/kobj.h> + +struct platform_kobj { + /* + * A platform instance is a kernel object + */ + KOBJ_FIELDS; + + /* + * Utility elements that an instance may use + */ + struct mtx platform_mtx; /* available for instance use */ + void *platform_iptr; /* instance data pointer */ + + /* + * Opaque data that can be overlaid with an instance-private + * structure. Platform code can test that this is large enough at + * compile time with a sizeof() test againt it's softc. There + * is also a run-time test when the platform kernel object is + * registered. + */ +#define PLATFORM_OPAQUESZ 64 + u_int platform_opaque[PLATFORM_OPAQUESZ]; +}; + +typedef struct platform_kobj *platform_t; +typedef struct kobj_class platform_def_t; +#define platform_method_t kobj_method_t + +#define PLATFORMMETHOD KOBJMETHOD +#define PLATFORMMETHOD_END KOBJMETHOD_END + +#define PLATFORM_DEF(name) DATA_SET(platform_set, name) + +#endif /* _MACHINE_PLATFORMVAR_H_ */ diff --git a/freebsd/sys/powerpc/mpc85xx/mpc85xx.c b/freebsd/sys/powerpc/mpc85xx/mpc85xx.c new file mode 100644 index 00000000..4c4b4c2f --- /dev/null +++ b/freebsd/sys/powerpc/mpc85xx/mpc85xx.c @@ -0,0 +1,361 @@ +#include <machine/rtems-bsd-kernel-space.h> + +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Copyright (C) 2008 Semihalf, Rafal Jaworowski + * 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 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 AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <rtems/bsd/local/opt_platform.h> +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/lock.h> +#include <sys/mutex.h> +#include <sys/reboot.h> +#include <sys/rman.h> + +#include <vm/vm.h> +#include <vm/vm_param.h> +#include <vm/pmap.h> + +#include <machine/cpu.h> +#include <machine/cpufunc.h> +#ifndef __rtems__ +#include <machine/machdep.h> +#endif /* __rtems__ */ +#include <machine/pio.h> +#include <machine/spr.h> + +#include <dev/fdt/fdt_common.h> + +#include <dev/fdt/fdt_common.h> +#include <dev/ofw/ofw_bus.h> +#include <dev/ofw/ofw_bus_subr.h> +#include <dev/ofw/openfirm.h> + +#include <powerpc/mpc85xx/mpc85xx.h> + + +/* + * MPC85xx system specific routines + */ + +uint32_t +ccsr_read4(uintptr_t addr) +{ + volatile uint32_t *ptr = (void *)addr; + + return (*ptr); +} + +void +ccsr_write4(uintptr_t addr, uint32_t val) +{ + volatile uint32_t *ptr = (void *)addr; + + *ptr = val; + powerpc_iomb(); +} + +int +law_getmax(void) +{ + uint32_t ver; + int law_max; + + ver = SVR_VER(mfspr(SPR_SVR)); + switch (ver) { + case SVR_MPC8555: + case SVR_MPC8555E: + law_max = 8; + break; + case SVR_MPC8533: + case SVR_MPC8533E: + case SVR_MPC8548: + case SVR_MPC8548E: + law_max = 10; + break; + case SVR_P5020: + case SVR_P5020E: + case SVR_P5021: + case SVR_P5021E: + case SVR_P5040: + case SVR_P5040E: + law_max = 32; + break; + default: + law_max = 8; + } + + return (law_max); +} + +static inline void +law_write(uint32_t n, uint64_t bar, uint32_t sr) +{ + + if (mpc85xx_is_qoriq()) { + ccsr_write4(OCP85XX_LAWBARH(n), bar >> 32); + ccsr_write4(OCP85XX_LAWBARL(n), bar); + ccsr_write4(OCP85XX_LAWSR_QORIQ(n), sr); + ccsr_read4(OCP85XX_LAWSR_QORIQ(n)); + } else { + ccsr_write4(OCP85XX_LAWBAR(n), bar >> 12); + ccsr_write4(OCP85XX_LAWSR_85XX(n), sr); + ccsr_read4(OCP85XX_LAWSR_85XX(n)); + } + + /* + * The last write to LAWAR should be followed by a read + * of LAWAR before any device try to use any of windows. + * What more the read of LAWAR should be followed by isync + * instruction. + */ + + isync(); +} + +static inline void +law_read(uint32_t n, uint64_t *bar, uint32_t *sr) +{ + + if (mpc85xx_is_qoriq()) { + *bar = (uint64_t)ccsr_read4(OCP85XX_LAWBARH(n)) << 32 | + ccsr_read4(OCP85XX_LAWBARL(n)); + *sr = ccsr_read4(OCP85XX_LAWSR_QORIQ(n)); + } else { + *bar = (uint64_t)ccsr_read4(OCP85XX_LAWBAR(n)) << 12; + *sr = ccsr_read4(OCP85XX_LAWSR_85XX(n)); + } +} + +static int +law_find_free(void) +{ + uint32_t i,sr; + uint64_t bar; + int law_max; + + law_max = law_getmax(); + /* Find free LAW */ + for (i = 0; i < law_max; i++) { + law_read(i, &bar, &sr); + if ((sr & 0x80000000) == 0) + break; + } + + return (i); +} + +#define _LAW_SR(trgt,size) (0x80000000 | (trgt << 20) | \ + (flsl(size + (size - 1)) - 2)) + +int +law_enable(int trgt, uint64_t bar, uint32_t size) +{ + uint64_t bar_tmp; + uint32_t sr, sr_tmp; + int i, law_max; + + if (size == 0) + return (0); + + law_max = law_getmax(); + sr = _LAW_SR(trgt, size); + + /* Bail if already programmed. */ + for (i = 0; i < law_max; i++) { + law_read(i, &bar_tmp, &sr_tmp); + if (sr == sr_tmp && bar == bar_tmp) + return (0); + } + + /* Find an unused access window. */ + i = law_find_free(); + + if (i == law_max) + return (ENOSPC); + + law_write(i, bar, sr); + return (0); +} + +int +law_disable(int trgt, uint64_t bar, uint32_t size) +{ + uint64_t bar_tmp; + uint32_t sr, sr_tmp; + int i, law_max; + + law_max = law_getmax(); + sr = _LAW_SR(trgt, size); + + /* Find and disable requested LAW. */ + for (i = 0; i < law_max; i++) { + law_read(i, &bar_tmp, &sr_tmp); + if (sr == sr_tmp && bar == bar_tmp) { + law_write(i, 0, 0); + return (0); + } + } + + return (ENOENT); +} + +int +law_pci_target(struct resource *res, int *trgt_mem, int *trgt_io) +{ + u_long start; + uint32_t ver; + int trgt, rv; + + ver = SVR_VER(mfspr(SPR_SVR)); + + start = rman_get_start(res) & 0xf000; + + rv = 0; + trgt = -1; + switch (start) { + case 0x0000: + case 0x8000: + trgt = 0; + break; + case 0x1000: + case 0x9000: + trgt = 1; + break; + case 0x2000: + case 0xa000: + if (ver == SVR_MPC8548E || ver == SVR_MPC8548) + trgt = 3; + else + trgt = 2; + break; + case 0x3000: + case 0xb000: + if (ver == SVR_MPC8548E || ver == SVR_MPC8548) + rv = EINVAL; + else + trgt = 3; + break; + default: + rv = ENXIO; + } + if (rv == 0) { + *trgt_mem = trgt; + *trgt_io = trgt; + } + return (rv); +} + +static void +l3cache_inval(void) +{ + + /* Flash invalidate the CPC and clear all the locks */ + ccsr_write4(OCP85XX_CPC_CSR0, OCP85XX_CPC_CSR0_FI | + OCP85XX_CPC_CSR0_LFC); + while (ccsr_read4(OCP85XX_CPC_CSR0) & (OCP85XX_CPC_CSR0_FI | + OCP85XX_CPC_CSR0_LFC)) + ; +} + +static void +l3cache_enable(void) +{ + + ccsr_write4(OCP85XX_CPC_CSR0, OCP85XX_CPC_CSR0_CE | + OCP85XX_CPC_CSR0_PE); + /* Read back to sync write */ + ccsr_read4(OCP85XX_CPC_CSR0); +} + +void +mpc85xx_enable_l3_cache(void) +{ + uint32_t csr, size, ver; + + /* Enable L3 CoreNet Platform Cache (CPC) */ + ver = SVR_VER(mfspr(SPR_SVR)); + if (ver == SVR_P2041 || ver == SVR_P2041E || ver == SVR_P3041 || + ver == SVR_P3041E || ver == SVR_P5020 || ver == SVR_P5020E) { + csr = ccsr_read4(OCP85XX_CPC_CSR0); + if ((csr & OCP85XX_CPC_CSR0_CE) == 0) { + l3cache_inval(); + l3cache_enable(); + } + + csr = ccsr_read4(OCP85XX_CPC_CSR0); + if ((boothowto & RB_VERBOSE) != 0 || + (csr & OCP85XX_CPC_CSR0_CE) == 0) { + size = OCP85XX_CPC_CFG0_SZ_K(ccsr_read4(OCP85XX_CPC_CFG0)); + printf("L3 Corenet Platform Cache: %d KB %sabled\n", + size, (csr & OCP85XX_CPC_CSR0_CE) == 0 ? + "dis" : "en"); + } + } +} + +int +mpc85xx_is_qoriq(void) +{ + uint16_t pvr = mfpvr() >> 16; + + /* QorIQ register set is only in e500mc and derivative core based SoCs. */ + if (pvr == FSL_E500mc || pvr == FSL_E5500 || pvr == FSL_E6500) + return (1); + + return (0); +} + +uint32_t +mpc85xx_get_platform_clock(void) +{ + phandle_t soc; + static uint32_t freq; + + if (freq != 0) + return (freq); + + soc = OF_finddevice("/soc"); + + /* freq isn't modified on error. */ + OF_getencprop(soc, "bus-frequency", (void *)&freq, sizeof(freq)); + + return (freq); +} + +uint32_t +mpc85xx_get_system_clock(void) +{ + uint32_t freq; + + freq = mpc85xx_get_platform_clock(); + + return (freq / 2); +} diff --git a/freebsd/sys/powerpc/mpc85xx/mpc85xx.h b/freebsd/sys/powerpc/mpc85xx/mpc85xx.h new file mode 100644 index 00000000..1a0be6cb --- /dev/null +++ b/freebsd/sys/powerpc/mpc85xx/mpc85xx.h @@ -0,0 +1,177 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Copyright (C) 2008 Semihalf, Rafal Jaworowski + * 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. + * + * 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 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. + * + * $FreeBSD$ + */ + +#ifndef _MPC85XX_H_ +#define _MPC85XX_H_ + +#include <machine/platformvar.h> + +/* + * Configuration control and status registers + */ +extern vm_offset_t ccsrbar_va; +extern vm_paddr_t ccsrbar_pa; +extern vm_size_t ccsrbar_size; +#define CCSRBAR_VA ccsrbar_va +#define OCP85XX_CCSRBAR (CCSRBAR_VA + 0x0) +#define OCP85XX_BPTR (CCSRBAR_VA + 0x20) + +#define OCP85XX_BSTRH (CCSRBAR_VA + 0x20) +#define OCP85XX_BSTRL (CCSRBAR_VA + 0x24) +#define OCP85XX_BSTAR (CCSRBAR_VA + 0x28) + +#define OCP85XX_COREDISR (CCSRBAR_VA + 0xE0094) +#define OCP85XX_BRR (CCSRBAR_VA + 0xE00E4) + +/* + * Run Control and Power Management registers + */ +#define CCSR_CTBENR (CCSRBAR_VA + 0xE2084) +#define CCSR_CTBCKSELR (CCSRBAR_VA + 0xE208C) +#define CCSR_CTBCHLTCR (CCSRBAR_VA + 0xE2094) + +/* + * DDR Memory controller. + */ +#define OCP85XX_DDR1_CS0_CONFIG (CCSRBAR_VA + 0x8080) + +/* + * E500 Coherency Module registers + */ +#define OCP85XX_EEBPCR (CCSRBAR_VA + 0x1010) + +/* + * Local access registers + */ +/* Write order: OCP_LAWBARH -> OCP_LAWBARL -> OCP_LAWSR */ +#define OCP85XX_LAWBARH(n) (CCSRBAR_VA + 0xc00 + 0x10 * (n)) +#define OCP85XX_LAWBARL(n) (CCSRBAR_VA + 0xc04 + 0x10 * (n)) +#define OCP85XX_LAWSR_QORIQ(n) (CCSRBAR_VA + 0xc08 + 0x10 * (n)) +#define OCP85XX_LAWBAR(n) (CCSRBAR_VA + 0xc08 + 0x10 * (n)) +#define OCP85XX_LAWSR_85XX(n) (CCSRBAR_VA + 0xc10 + 0x10 * (n)) +#define OCP85XX_LAWSR(n) (mpc85xx_is_qoriq() ? OCP85XX_LAWSR_QORIQ(n) : \ + OCP85XX_LAWSR_85XX(n)) + +/* Attribute register */ +#define OCP85XX_ENA_MASK 0x80000000 +#define OCP85XX_DIS_MASK 0x7fffffff + +#define OCP85XX_TGTIF_LBC_QORIQ 0x1f +#define OCP85XX_TGTIF_RAM_INTL_QORIQ 0x14 +#define OCP85XX_TGTIF_RAM1_QORIQ 0x10 +#define OCP85XX_TGTIF_RAM2_QORIQ 0x11 +#define OCP85XX_TGTIF_BMAN 0x18 +#define OCP85XX_TGTIF_DCSR 0x1D +#define OCP85XX_TGTIF_QMAN 0x3C +#define OCP85XX_TRGT_SHIFT_QORIQ 20 + +#define OCP85XX_TGTIF_LBC_85XX 0x04 +#define OCP85XX_TGTIF_RAM_INTL_85XX 0x0b +#define OCP85XX_TGTIF_RIO_85XX 0x0c +#define OCP85XX_TGTIF_RAM1_85XX 0x0f +#define OCP85XX_TGTIF_RAM2_85XX 0x16 + +#define OCP85XX_TGTIF_LBC \ + (mpc85xx_is_qoriq() ? OCP85XX_TGTIF_LBC_QORIQ : OCP85XX_TGTIF_LBC_85XX) +#define OCP85XX_TGTIF_RAM_INTL \ + (mpc85xx_is_qoriq() ? OCP85XX_TGTIF_RAM_INTL_QORIQ : OCP85XX_TGTIF_RAM_INTL_85XX) +#define OCP85XX_TGTIF_RIO \ + (mpc85xx_is_qoriq() ? OCP85XX_TGTIF_RIO_QORIQ : OCP85XX_TGTIF_RIO_85XX) +#define OCP85XX_TGTIF_RAM1 \ + (mpc85xx_is_qoriq() ? OCP85XX_TGTIF_RAM1_QORIQ : OCP85XX_TGTIF_RAM1_85XX) +#define OCP85XX_TGTIF_RAM2 \ + (mpc85xx_is_qoriq() ? OCP85XX_TGTIF_RAM2_QORIQ : OCP85XX_TGTIF_RAM2_85XX) + +/* + * L2 cache registers + */ +#define OCP85XX_L2CTL (CCSRBAR_VA + 0x20000) + +/* + * L3 CoreNet platform cache (CPC) registers + */ +#define OCP85XX_CPC_CSR0 (CCSRBAR_VA + 0x10000) +#define OCP85XX_CPC_CSR0_CE 0x80000000 +#define OCP85XX_CPC_CSR0_PE 0x40000000 +#define OCP85XX_CPC_CSR0_FI 0x00200000 +#define OCP85XX_CPC_CSR0_WT 0x00080000 +#define OCP85XX_CPC_CSR0_FL 0x00000800 +#define OCP85XX_CPC_CSR0_LFC 0x00000400 +#define OCP85XX_CPC_CFG0 (CCSRBAR_VA + 0x10008) +#define OCP85XX_CPC_CFG_SZ_MASK 0x00003fff +#define OCP85XX_CPC_CFG0_SZ_K(x) (((x) & OCP85XX_CPC_CFG_SZ_MASK) << 6) + +/* + * Power-On Reset configuration + */ +#define OCP85XX_PORDEVSR (CCSRBAR_VA + 0xe000c) +#define OCP85XX_PORDEVSR_IO_SEL 0x00780000 +#define OCP85XX_PORDEVSR_IO_SEL_SHIFT 19 + +#define OCP85XX_PORDEVSR2 (CCSRBAR_VA + 0xe0014) + +/* + * Status Registers. + */ +#define OCP85XX_RSTCR (CCSRBAR_VA + 0xe00b0) + +#define OCP85XX_CLKDVDR (CCSRBAR_VA + 0xe0800) +#define OCP85XX_CLKDVDR_PXCKEN 0x80000000 +#define OCP85XX_CLKDVDR_SSICKEN 0x20000000 +#define OCP85XX_CLKDVDR_PXCKINV 0x10000000 +#define OCP85XX_CLKDVDR_PXCLK_MASK 0x00FF0000 +#define OCP85XX_CLKDVDR_SSICLK_MASK 0x000000FF + +/* + * Run Control/Power Management Registers. + */ +#define OCP85XX_RCPM_CDOZSR (CCSRBAR_VA + 0xe2004) +#define OCP85XX_RCPM_CDOZCR (CCSRBAR_VA + 0xe200c) + +/* + * Prototypes. + */ +uint32_t ccsr_read4(uintptr_t addr); +void ccsr_write4(uintptr_t addr, uint32_t val); +int law_enable(int trgt, uint64_t bar, uint32_t size); +int law_disable(int trgt, uint64_t bar, uint32_t size); +int law_getmax(void); +int law_pci_target(struct resource *, int *, int *); + +DECLARE_CLASS(mpc85xx_platform); +int mpc85xx_attach(platform_t); + +void mpc85xx_enable_l3_cache(void); +int mpc85xx_is_qoriq(void); +uint32_t mpc85xx_get_platform_clock(void); +uint32_t mpc85xx_get_system_clock(void); + +#endif /* _MPC85XX_H_ */ diff --git a/freebsd/sys/powerpc/mpc85xx/pci_mpc85xx.c b/freebsd/sys/powerpc/mpc85xx/pci_mpc85xx.c new file mode 100644 index 00000000..f615fafa --- /dev/null +++ b/freebsd/sys/powerpc/mpc85xx/pci_mpc85xx.c @@ -0,0 +1,959 @@ +#include <machine/rtems-bsd-kernel-space.h> + +/*- + * SPDX-License-Identifier: BSD-3-Clause + * + * Copyright 2006-2007 by Juniper Networks. + * Copyright 2008 Semihalf. + * Copyright 2010 The FreeBSD Foundation + * All rights reserved. + * + * Portions of this software were developed by Semihalf + * under sponsorship from the FreeBSD Foundation. + * + * 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. + * + * From: FreeBSD: src/sys/powerpc/mpc85xx/pci_ocp.c,v 1.9 2010/03/23 23:46:28 marcel + */ + +#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/lock.h> +#include <sys/mutex.h> +#include <sys/queue.h> +#include <sys/rman.h> +#include <sys/endian.h> +#include <sys/vmem.h> + +#include <vm/vm.h> +#include <vm/pmap.h> + +#include <dev/ofw/ofw_pci.h> +#include <dev/ofw/ofw_bus.h> +#include <dev/ofw/ofw_bus_subr.h> +#include <dev/ofw/ofwpci.h> +#include <dev/pci/pcivar.h> +#include <dev/pci/pcireg.h> +#include <dev/pci/pcib_private.h> + +#include <rtems/bsd/local/ofw_bus_if.h> +#include <rtems/bsd/local/pcib_if.h> +#include <rtems/bsd/local/pic_if.h> + +#include <machine/resource.h> +#include <machine/bus.h> +#include <machine/intr_machdep.h> + +#include <powerpc/mpc85xx/mpc85xx.h> + +#define REG_CFG_ADDR 0x0000 +#define CONFIG_ACCESS_ENABLE 0x80000000 + +#define REG_CFG_DATA 0x0004 +#define REG_INT_ACK 0x0008 + +#define REG_PEX_IP_BLK_REV1 0x0bf8 +#define IP_MJ_M 0x0000ff00 +#define IP_MJ_S 8 +#define IP_MN_M 0x000000ff +#define IP_MN_S 0 + +#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)) +#define PIWAR_EN 0x80000000 +#define PIWAR_PF 0x40000000 +#define PIWAR_TRGT_M 0x00f00000 +#define PIWAR_TRGT_S 20 +#define PIWAR_TRGT_CCSR 0xe +#define PIWAR_TRGT_LOCAL 0xf + +#define REG_PEX_MES_DR 0x0020 +#define REG_PEX_MES_IER 0x0028 +#define REG_PEX_ERR_DR 0x0e00 +#define REG_PEX_ERR_EN 0x0e08 + +#define REG_PEX_ERR_DR 0x0e00 +#define REG_PEX_ERR_DR_ME 0x80000000 +#define REG_PEX_ERR_DR_PCT 0x800000 +#define REG_PEX_ERR_DR_PAT 0x400000 +#define REG_PEX_ERR_DR_PCAC 0x200000 +#define REG_PEX_ERR_DR_PNM 0x100000 +#define REG_PEX_ERR_DR_CDNSC 0x80000 +#define REG_PEX_ERR_DR_CRSNC 0x40000 +#define REG_PEX_ERR_DR_ICCA 0x20000 +#define REG_PEX_ERR_DR_IACA 0x10000 +#define REG_PEX_ERR_DR_CRST 0x8000 +#define REG_PEX_ERR_DR_MIS 0x4000 +#define REG_PEX_ERR_DR_IOIS 0x2000 +#define REG_PEX_ERR_DR_CIS 0x1000 +#define REG_PEX_ERR_DR_CIEP 0x800 +#define REG_PEX_ERR_DR_IOIEP 0x400 +#define REG_PEX_ERR_DR_OAC 0x200 +#define REG_PEX_ERR_DR_IOIA 0x100 +#define REG_PEX_ERR_DR_IMBA 0x80 +#define REG_PEX_ERR_DR_IIOBA 0x40 +#define REG_PEX_ERR_DR_LDDE 0x20 +#define REG_PEX_ERR_EN 0x0e08 + +#define PCIR_LTSSM 0x404 +#define LTSSM_STAT_L0 0x16 + +#define DEVFN(b, s, f) ((b << 16) | (s << 8) | f) + +#define FSL_NUM_MSIS 256 /* 8 registers of 32 bits (8 hardware IRQs) */ + +struct fsl_pcib_softc { + struct ofw_pci_softc pci_sc; + device_t sc_dev; + struct mtx sc_cfg_mtx; + int sc_ip_maj; + int sc_ip_min; + + int sc_iomem_target; + bus_addr_t sc_iomem_start, sc_iomem_end; + int sc_ioport_target; + bus_addr_t sc_ioport_start, sc_ioport_end; + + struct resource *sc_res; + bus_space_handle_t sc_bsh; + bus_space_tag_t sc_bst; + int sc_rid; + + struct resource *sc_irq_res; + void *sc_ih; + + int sc_busnr; + int sc_pcie; + uint8_t sc_pcie_capreg; /* PCI-E Capability Reg Set */ +}; + +struct fsl_pcib_err_dr { + const char *msg; + uint32_t err_dr_mask; +}; + +struct fsl_msi_map { + SLIST_ENTRY(fsl_msi_map) slist; + uint32_t irq_base; + bus_addr_t target; +}; + +SLIST_HEAD(msi_head, fsl_msi_map) fsl_msis = SLIST_HEAD_INITIALIZER(msi_head); + +static const struct fsl_pcib_err_dr pci_err[] = { + {"ME", REG_PEX_ERR_DR_ME}, + {"PCT", REG_PEX_ERR_DR_PCT}, + {"PAT", REG_PEX_ERR_DR_PAT}, + {"PCAC", REG_PEX_ERR_DR_PCAC}, + {"PNM", REG_PEX_ERR_DR_PNM}, + {"CDNSC", REG_PEX_ERR_DR_CDNSC}, + {"CRSNC", REG_PEX_ERR_DR_CRSNC}, + {"ICCA", REG_PEX_ERR_DR_ICCA}, + {"IACA", REG_PEX_ERR_DR_IACA}, + {"CRST", REG_PEX_ERR_DR_CRST}, + {"MIS", REG_PEX_ERR_DR_MIS}, + {"IOIS", REG_PEX_ERR_DR_IOIS}, + {"CIS", REG_PEX_ERR_DR_CIS}, + {"CIEP", REG_PEX_ERR_DR_CIEP}, + {"IOIEP", REG_PEX_ERR_DR_IOIEP}, + {"OAC", REG_PEX_ERR_DR_OAC}, + {"IOIA", REG_PEX_ERR_DR_IOIA}, + {"IMBA", REG_PEX_ERR_DR_IMBA}, + {"IIOBA", REG_PEX_ERR_DR_IIOBA}, + {"LDDE", REG_PEX_ERR_DR_LDDE} +}; + +/* Local forward declerations. */ +static uint32_t fsl_pcib_cfgread(struct fsl_pcib_softc *, u_int, u_int, u_int, + u_int, int); +static void fsl_pcib_cfgwrite(struct fsl_pcib_softc *, u_int, u_int, u_int, + u_int, uint32_t, int); +static int fsl_pcib_decode_win(phandle_t, struct fsl_pcib_softc *); +static void fsl_pcib_err_init(device_t); +static void fsl_pcib_inbound(struct fsl_pcib_softc *, int, int, uint64_t, + uint64_t, uint64_t); +static void fsl_pcib_outbound(struct fsl_pcib_softc *, int, int, uint64_t, + uint64_t, uint64_t); + +/* Forward declerations. */ +static int fsl_pcib_attach(device_t); +static int fsl_pcib_detach(device_t); +static int fsl_pcib_probe(device_t); + +static int fsl_pcib_maxslots(device_t); +static uint32_t fsl_pcib_read_config(device_t, u_int, u_int, u_int, u_int, int); +static void fsl_pcib_write_config(device_t, u_int, u_int, u_int, u_int, + uint32_t, int); +static int fsl_pcib_alloc_msi(device_t dev, device_t child, + int count, int maxcount, int *irqs); +static int fsl_pcib_release_msi(device_t dev, device_t child, + int count, int *irqs); +static int fsl_pcib_alloc_msix(device_t dev, device_t child, int *irq); +static int fsl_pcib_release_msix(device_t dev, device_t child, int irq); +static int fsl_pcib_map_msi(device_t dev, device_t child, + int irq, uint64_t *addr, uint32_t *data); + +static vmem_t *msi_vmem; /* Global MSI vmem, holds all MSI ranges. */ + +/* + * Bus interface definitions. + */ +static device_method_t fsl_pcib_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, fsl_pcib_probe), + DEVMETHOD(device_attach, fsl_pcib_attach), + DEVMETHOD(device_detach, fsl_pcib_detach), + + /* pcib interface */ + DEVMETHOD(pcib_maxslots, fsl_pcib_maxslots), + DEVMETHOD(pcib_read_config, fsl_pcib_read_config), + DEVMETHOD(pcib_write_config, fsl_pcib_write_config), + DEVMETHOD(pcib_alloc_msi, fsl_pcib_alloc_msi), + DEVMETHOD(pcib_release_msi, fsl_pcib_release_msi), + DEVMETHOD(pcib_alloc_msix, fsl_pcib_alloc_msix), + DEVMETHOD(pcib_release_msix, fsl_pcib_release_msix), + DEVMETHOD(pcib_map_msi, fsl_pcib_map_msi), + + DEVMETHOD_END +}; + +static devclass_t fsl_pcib_devclass; + +DEFINE_CLASS_1(pcib, fsl_pcib_driver, fsl_pcib_methods, + sizeof(struct fsl_pcib_softc), ofw_pci_driver); +EARLY_DRIVER_MODULE(pcib, ofwbus, fsl_pcib_driver, fsl_pcib_devclass, 0, 0, + BUS_PASS_BUS); + +static void +fsl_pcib_err_intr(void *v) +{ + struct fsl_pcib_softc *sc; + device_t dev; + uint32_t err_reg, clear_reg; + uint8_t i; + + dev = (device_t)v; + sc = device_get_softc(dev); + + clear_reg = 0; + err_reg = bus_space_read_4(sc->sc_bst, sc->sc_bsh, REG_PEX_ERR_DR); + + /* Check which one error occurred */ + for (i = 0; i < sizeof(pci_err)/sizeof(struct fsl_pcib_err_dr); i++) { + if (err_reg & pci_err[i].err_dr_mask) { + device_printf(dev, "PCI %d: report %s error\n", + device_get_unit(dev), pci_err[i].msg); + clear_reg |= pci_err[i].err_dr_mask; + } + } + + /* Clear pending errors */ + bus_space_write_4(sc->sc_bst, sc->sc_bsh, REG_PEX_ERR_DR, clear_reg); +} + +static int +fsl_pcib_probe(device_t dev) +{ + + if (ofw_bus_get_type(dev) == NULL || + strcmp(ofw_bus_get_type(dev), "pci") != 0) + return (ENXIO); + + if (!(ofw_bus_is_compatible(dev, "fsl,mpc8540-pci") || + ofw_bus_is_compatible(dev, "fsl,mpc8540-pcie") || + ofw_bus_is_compatible(dev, "fsl,mpc8548-pcie") || + ofw_bus_is_compatible(dev, "fsl,p5020-pcie") || + ofw_bus_is_compatible(dev, "fsl,qoriq-pcie-v2.2") || + ofw_bus_is_compatible(dev, "fsl,qoriq-pcie"))) + return (ENXIO); + + device_set_desc(dev, "Freescale Integrated PCI/PCI-E Controller"); + return (BUS_PROBE_DEFAULT); +} + +static int +fsl_pcib_attach(device_t dev) +{ + struct fsl_pcib_softc *sc; + phandle_t node; + uint32_t cfgreg, brctl, ipreg; + int error, rid; + uint8_t ltssm, capptr; + + 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); + sc->sc_busnr = 0; + + ipreg = bus_read_4(sc->sc_res, REG_PEX_IP_BLK_REV1); + sc->sc_ip_min = (ipreg & IP_MN_M) >> IP_MN_S; + sc->sc_ip_maj = (ipreg & IP_MJ_M) >> IP_MJ_S; + mtx_init(&sc->sc_cfg_mtx, "pcicfg", NULL, MTX_SPIN); + + cfgreg = fsl_pcib_cfgread(sc, 0, 0, 0, PCIR_VENDOR, 2); + if (cfgreg != 0x1057 && cfgreg != 0x1957) + goto err; + + capptr = fsl_pcib_cfgread(sc, 0, 0, 0, PCIR_CAP_PTR, 1); + while (capptr != 0) { + cfgreg = fsl_pcib_cfgread(sc, 0, 0, 0, capptr, 2); + switch (cfgreg & 0xff) { + case PCIY_PCIX: + break; + case PCIY_EXPRESS: + sc->sc_pcie = 1; + sc->sc_pcie_capreg = capptr; + break; + } + capptr = (cfgreg >> 8) & 0xff; + } + + node = ofw_bus_get_node(dev); + + /* + * Initialize generic OF PCI interface (ranges, etc.) + */ + + error = ofw_pci_init(dev); + if (error) + return (error); + + /* + * Configure decode windows for PCI(E) access. + */ + if (fsl_pcib_decode_win(node, sc) != 0) + goto err; + + cfgreg = fsl_pcib_cfgread(sc, 0, 0, 0, PCIR_COMMAND, 2); + cfgreg |= PCIM_CMD_SERRESPEN | PCIM_CMD_BUSMASTEREN | PCIM_CMD_MEMEN | + PCIM_CMD_PORTEN; + fsl_pcib_cfgwrite(sc, 0, 0, 0, PCIR_COMMAND, cfgreg, 2); + + /* Reset the bus. Needed for Radeon video cards. */ + brctl = fsl_pcib_read_config(sc->sc_dev, 0, 0, 0, + PCIR_BRIDGECTL_1, 1); + brctl |= PCIB_BCR_SECBUS_RESET; + fsl_pcib_write_config(sc->sc_dev, 0, 0, 0, + PCIR_BRIDGECTL_1, brctl, 1); + DELAY(100000); + brctl &= ~PCIB_BCR_SECBUS_RESET; + fsl_pcib_write_config(sc->sc_dev, 0, 0, 0, + PCIR_BRIDGECTL_1, brctl, 1); + DELAY(100000); + + if (sc->sc_pcie) { + ltssm = fsl_pcib_cfgread(sc, 0, 0, 0, PCIR_LTSSM, 1); + if (ltssm < LTSSM_STAT_L0) { + if (bootverbose) + printf("PCI %d: no PCIE link, skipping\n", + device_get_unit(dev)); + return (0); + } + } + + /* Allocate irq */ + rid = 0; + sc->sc_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, + RF_ACTIVE | RF_SHAREABLE); + if (sc->sc_irq_res == NULL) { + error = fsl_pcib_detach(dev); + if (error != 0) { + device_printf(dev, + "Detach of the driver failed with error %d\n", + error); + } + return (ENXIO); + } + + /* Setup interrupt handler */ + error = bus_setup_intr(dev, sc->sc_irq_res, INTR_TYPE_MISC | INTR_MPSAFE, + NULL, fsl_pcib_err_intr, dev, &sc->sc_ih); + if (error != 0) { + device_printf(dev, "Could not setup irq, %d\n", error); + sc->sc_ih = NULL; + error = fsl_pcib_detach(dev); + if (error != 0) { + device_printf(dev, + "Detach of the driver failed with error %d\n", + error); + } + return (ENXIO); + } + + fsl_pcib_err_init(dev); + + return (ofw_pci_attach(dev)); + +err: + return (ENXIO); +} + +static uint32_t +fsl_pcib_cfgread(struct fsl_pcib_softc *sc, u_int bus, u_int slot, u_int func, + u_int reg, int bytes) +{ + uint32_t addr, data; + + 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; + + mtx_lock_spin(&sc->sc_cfg_mtx); + 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; + } + mtx_unlock_spin(&sc->sc_cfg_mtx); + return (data); +} + +static void +fsl_pcib_cfgwrite(struct fsl_pcib_softc *sc, u_int bus, u_int slot, u_int func, + u_int reg, uint32_t data, int bytes) +{ + uint32_t addr; + + 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; + + mtx_lock_spin(&sc->sc_cfg_mtx); + 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; + } + mtx_unlock_spin(&sc->sc_cfg_mtx); +} + +#if 0 +static void +dump(struct fsl_pcib_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, fsl_pcib_cfgread(sc, 0, 0, 0, + i, 4)); + } +} +#endif + +static int +fsl_pcib_maxslots(device_t dev) +{ + struct fsl_pcib_softc *sc = device_get_softc(dev); + + return ((sc->sc_pcie) ? 0 : PCI_SLOTMAX); +} + +static uint32_t +fsl_pcib_read_config(device_t dev, u_int bus, u_int slot, u_int func, + u_int reg, int bytes) +{ + struct fsl_pcib_softc *sc = device_get_softc(dev); + u_int devfn; + + if (bus == sc->sc_busnr && !sc->sc_pcie && slot < 10) + return (~0); + devfn = DEVFN(bus, slot, func); + + return (fsl_pcib_cfgread(sc, bus, slot, func, reg, bytes)); +} + +static void +fsl_pcib_write_config(device_t dev, u_int bus, u_int slot, u_int func, + u_int reg, uint32_t val, int bytes) +{ + struct fsl_pcib_softc *sc = device_get_softc(dev); + + if (bus == sc->sc_busnr && !sc->sc_pcie && slot < 10) + return; + fsl_pcib_cfgwrite(sc, bus, slot, func, reg, val, bytes); +} + +static void +fsl_pcib_inbound(struct fsl_pcib_softc *sc, int wnd, int tgt, uint64_t start, + uint64_t size, uint64_t pci_start) +{ + uint32_t attr, bar, tar; + + KASSERT(wnd > 0, ("%s: inbound window 0 is invalid", __func__)); + + attr = PIWAR_EN; + + switch (tgt) { + case -1: + attr &= ~PIWAR_EN; + break; + case PIWAR_TRGT_LOCAL: + attr |= (ffsl(size) - 2); + default: + attr |= (tgt << PIWAR_TRGT_S); + 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 +fsl_pcib_outbound(struct fsl_pcib_softc *sc, int wnd, int res, uint64_t start, + uint64_t size, uint64_t pci_start) +{ + uint32_t attr, bar, tar; + + switch (res) { + case SYS_RES_MEMORY: + attr = 0x80044000 | (ffsll(size) - 2); + break; + case SYS_RES_IOPORT: + attr = 0x80088000 | (ffsll(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 void +fsl_pcib_err_init(device_t dev) +{ + struct fsl_pcib_softc *sc; + uint16_t sec_stat, dsr; + uint32_t dcr, err_en; + + sc = device_get_softc(dev); + + sec_stat = fsl_pcib_cfgread(sc, 0, 0, 0, PCIR_SECSTAT_1, 2); + if (sec_stat) + fsl_pcib_cfgwrite(sc, 0, 0, 0, PCIR_SECSTAT_1, 0xffff, 2); + if (sc->sc_pcie) { + /* Clear error bits */ + bus_space_write_4(sc->sc_bst, sc->sc_bsh, REG_PEX_MES_IER, + 0xffffffff); + bus_space_write_4(sc->sc_bst, sc->sc_bsh, REG_PEX_MES_DR, + 0xffffffff); + bus_space_write_4(sc->sc_bst, sc->sc_bsh, REG_PEX_ERR_DR, + 0xffffffff); + + dsr = fsl_pcib_cfgread(sc, 0, 0, 0, + sc->sc_pcie_capreg + PCIER_DEVICE_STA, 2); + if (dsr) + fsl_pcib_cfgwrite(sc, 0, 0, 0, + sc->sc_pcie_capreg + PCIER_DEVICE_STA, + 0xffff, 2); + + /* Enable all errors reporting */ + err_en = 0x00bfff00; + bus_space_write_4(sc->sc_bst, sc->sc_bsh, REG_PEX_ERR_EN, + err_en); + + /* Enable error reporting: URR, FER, NFER */ + dcr = fsl_pcib_cfgread(sc, 0, 0, 0, + sc->sc_pcie_capreg + PCIER_DEVICE_CTL, 4); + dcr |= PCIEM_CTL_URR_ENABLE | PCIEM_CTL_FER_ENABLE | + PCIEM_CTL_NFER_ENABLE; + fsl_pcib_cfgwrite(sc, 0, 0, 0, + sc->sc_pcie_capreg + PCIER_DEVICE_CTL, dcr, 4); + } +} + +static int +fsl_pcib_detach(device_t dev) +{ + struct fsl_pcib_softc *sc; + + sc = device_get_softc(dev); + + mtx_destroy(&sc->sc_cfg_mtx); + + return (bus_generic_detach(dev)); +} + +static int +fsl_pcib_decode_win(phandle_t node, struct fsl_pcib_softc *sc) +{ + device_t dev; + int error, i, trgt; + + dev = sc->sc_dev; + + fsl_pcib_outbound(sc, 0, -1, 0, 0, 0); + + /* + * Configure LAW decode windows. + */ + error = law_pci_target(sc->sc_res, &sc->sc_iomem_target, + &sc->sc_ioport_target); + if (error != 0) { + device_printf(dev, "could not retrieve PCI LAW target info\n"); + return (error); + } + + for (i = 0; i < sc->pci_sc.sc_nrange; i++) { + switch (sc->pci_sc.sc_range[i].pci_hi & + OFW_PCI_PHYS_HI_SPACEMASK) { + case OFW_PCI_PHYS_HI_SPACE_CONFIG: + continue; + case OFW_PCI_PHYS_HI_SPACE_IO: + trgt = sc->sc_ioport_target; + fsl_pcib_outbound(sc, 2, SYS_RES_IOPORT, + sc->pci_sc.sc_range[i].host, + sc->pci_sc.sc_range[i].size, + sc->pci_sc.sc_range[i].pci); + sc->sc_ioport_start = sc->pci_sc.sc_range[i].pci; + sc->sc_ioport_end = sc->pci_sc.sc_range[i].pci + + sc->pci_sc.sc_range[i].size - 1; + break; + case OFW_PCI_PHYS_HI_SPACE_MEM32: + case OFW_PCI_PHYS_HI_SPACE_MEM64: + trgt = sc->sc_iomem_target; + fsl_pcib_outbound(sc, 1, SYS_RES_MEMORY, + sc->pci_sc.sc_range[i].host, + sc->pci_sc.sc_range[i].size, + sc->pci_sc.sc_range[i].pci); + sc->sc_iomem_start = sc->pci_sc.sc_range[i].pci; + sc->sc_iomem_end = sc->pci_sc.sc_range[i].pci + + sc->pci_sc.sc_range[i].size - 1; + break; + default: + panic("Unknown range type %#x\n", + sc->pci_sc.sc_range[i].pci_hi & + OFW_PCI_PHYS_HI_SPACEMASK); + } + error = law_enable(trgt, sc->pci_sc.sc_range[i].host, + sc->pci_sc.sc_range[i].size); + if (error != 0) { + device_printf(dev, "could not program LAW for range " + "%d\n", i); + return (error); + } + } + + /* + * Set outbout and inbound windows. + */ + fsl_pcib_outbound(sc, 3, -1, 0, 0, 0); + fsl_pcib_outbound(sc, 4, -1, 0, 0, 0); + + fsl_pcib_inbound(sc, 1, -1, 0, 0, 0); + fsl_pcib_inbound(sc, 2, -1, 0, 0, 0); + fsl_pcib_inbound(sc, 3, PIWAR_TRGT_LOCAL, 0, + ptoa(Maxmem), 0); + + /* Direct-map the CCSR for MSIs. */ + /* Freescale PCIe 2.x has a dedicated MSI window. */ + /* inbound window 8 makes it hit 0xD00 offset, the MSI window. */ + if (sc->sc_ip_maj >= 2) + fsl_pcib_inbound(sc, 8, PIWAR_TRGT_CCSR, ccsrbar_pa, + ccsrbar_size, ccsrbar_pa); + else + fsl_pcib_inbound(sc, 1, PIWAR_TRGT_CCSR, ccsrbar_pa, + ccsrbar_size, ccsrbar_pa); + + return (0); +} + +static int fsl_pcib_alloc_msi(device_t dev, device_t child, + int count, int maxcount, int *irqs) +{ + struct fsl_pcib_softc *sc; + vmem_addr_t start; + int err, i; + + sc = device_get_softc(dev); + if (msi_vmem == NULL) + return (ENODEV); + + err = vmem_xalloc(msi_vmem, count, powerof2(count), 0, 0, + VMEM_ADDR_MIN, VMEM_ADDR_MAX, M_BESTFIT | M_WAITOK, &start); + + if (err) + return (err); + + for (i = 0; i < count; i++) + irqs[i] = start + i; + + return (0); +} + +static int fsl_pcib_release_msi(device_t dev, device_t child, + int count, int *irqs) +{ + if (msi_vmem == NULL) + return (ENODEV); + + vmem_xfree(msi_vmem, irqs[0], count); + return (0); +} + +static int fsl_pcib_alloc_msix(device_t dev, device_t child, int *irq) +{ + return (fsl_pcib_alloc_msi(dev, child, 1, 1, irq)); +} + +static int fsl_pcib_release_msix(device_t dev, device_t child, int irq) +{ + return (fsl_pcib_release_msi(dev, child, 1, &irq)); +} + +static int fsl_pcib_map_msi(device_t dev, device_t child, + int irq, uint64_t *addr, uint32_t *data) +{ + struct fsl_msi_map *mp; + + SLIST_FOREACH(mp, &fsl_msis, slist) { + if (irq >= mp->irq_base && irq < mp->irq_base + FSL_NUM_MSIS) + break; + } + + if (mp == NULL) + return (ENODEV); + + *data = (irq & 255); + *addr = ccsrbar_pa + mp->target; + + return (0); +} + + +/* + * Linux device trees put the msi@<x> as children of the SoC, with ranges based + * on the CCSR. Since rman doesn't permit overlapping or sub-ranges between + * devices (bus_space_subregion(9) could do it, but let's not touch the PIC + * driver just to allocate a subregion for a sibling driver). This driver will + * use ccsr_write() and ccsr_read() instead. + */ + +#define FSL_NUM_IRQS 8 +#define FSL_NUM_MSI_PER_IRQ 32 +#define FSL_MSI_TARGET 0x140 + +struct fsl_msi_softc { + vm_offset_t sc_base; + vm_offset_t sc_target; + int sc_msi_base_irq; + struct fsl_msi_map sc_map; + struct fsl_msi_irq { + /* This struct gets passed as the filter private data. */ + struct fsl_msi_softc *sc_ptr; /* Pointer back to softc. */ + struct resource *res; + int irq; + void *cookie; + int vectors[FSL_NUM_MSI_PER_IRQ]; + vm_offset_t reg; + } sc_msi_irq[FSL_NUM_IRQS]; +}; + +static int +fsl_msi_intr_filter(void *priv) +{ + struct fsl_msi_irq *data = priv; + uint32_t reg; + int i; + + reg = ccsr_read4(ccsrbar_va + data->reg); + i = 0; + while (reg != 0) { + if (reg & 1) + powerpc_dispatch_intr(data->vectors[i], NULL); + reg >>= 1; + i++; + } + + return (FILTER_HANDLED); +} + +static int +fsl_msi_probe(device_t dev) +{ + if (!ofw_bus_is_compatible(dev, "fsl,mpic-msi")) + return (ENXIO); + + device_set_desc(dev, "Freescale MSI"); + + return (BUS_PROBE_DEFAULT); +} + +static int +fsl_msi_attach(device_t dev) +{ + struct fsl_msi_softc *sc; + struct fsl_msi_irq *irq; + int i; + + sc = device_get_softc(dev); + + if (msi_vmem == NULL) + msi_vmem = vmem_create("MPIC MSI", 0, 0, 1, 0, M_BESTFIT | M_WAITOK); + + /* Manually play with resource entries. */ + sc->sc_base = bus_get_resource_start(dev, SYS_RES_MEMORY, 0); + sc->sc_map.target = bus_get_resource_start(dev, SYS_RES_MEMORY, 1); + + if (sc->sc_map.target == 0) + sc->sc_map.target = sc->sc_base + FSL_MSI_TARGET; + + for (i = 0; i < FSL_NUM_IRQS; i++) { + irq = &sc->sc_msi_irq[i]; + irq->irq = i; + irq->reg = sc->sc_base + 16 * i; + irq->res = bus_alloc_resource_any(dev, SYS_RES_IRQ, + &irq->irq, RF_ACTIVE); + bus_setup_intr(dev, irq->res, INTR_TYPE_MISC | INTR_MPSAFE, + fsl_msi_intr_filter, NULL, irq, &irq->cookie); + } + sc->sc_map.irq_base = powerpc_register_pic(dev, ofw_bus_get_node(dev), + FSL_NUM_MSIS, 0, 0); + + /* Let vmem and the IRQ subsystem work their magic for allocations. */ + vmem_add(msi_vmem, sc->sc_map.irq_base, FSL_NUM_MSIS, M_WAITOK); + + SLIST_INSERT_HEAD(&fsl_msis, &sc->sc_map, slist); + + return (0); +} + +static void +fsl_msi_enable(device_t dev, u_int irq, u_int vector, void **priv) +{ + struct fsl_msi_softc *sc; + struct fsl_msi_irq *irqd; + + sc = device_get_softc(dev); + + irqd = &sc->sc_msi_irq[irq / FSL_NUM_MSI_PER_IRQ]; + irqd->vectors[irq % FSL_NUM_MSI_PER_IRQ] = vector; +} + +static device_method_t fsl_msi_methods[] = { + DEVMETHOD(device_probe, fsl_msi_probe), + DEVMETHOD(device_attach, fsl_msi_attach), + + DEVMETHOD(pic_enable, fsl_msi_enable), + DEVMETHOD_END +}; + +static devclass_t fsl_msi_devclass; + +static driver_t fsl_msi_driver = { + "fsl_msi", + fsl_msi_methods, + sizeof(struct fsl_msi_softc) +}; + +EARLY_DRIVER_MODULE(fsl_msi, simplebus, fsl_msi_driver, fsl_msi_devclass, 0, 0, + BUS_PASS_INTERRUPT + 1); diff --git a/freebsd/sys/powerpc/mpc85xx/pci_mpc85xx_pcib.c b/freebsd/sys/powerpc/mpc85xx/pci_mpc85xx_pcib.c new file mode 100644 index 00000000..74a580f5 --- /dev/null +++ b/freebsd/sys/powerpc/mpc85xx/pci_mpc85xx_pcib.c @@ -0,0 +1,111 @@ +#include <machine/rtems-bsd-kernel-space.h> + +/*- + * Copyright 2015 Justin Hibbits + * 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. + * + * From: FreeBSD: src/sys/powerpc/mpc85xx/pci_ocp.c,v 1.9 2010/03/23 23:46:28 marcel + */ + +#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/lock.h> +#include <sys/mutex.h> +#include <sys/rman.h> +#include <sys/endian.h> + +#include <vm/vm.h> +#include <vm/pmap.h> + +#include <dev/ofw/openfirm.h> +#include <dev/ofw/ofw_pci.h> +#include <dev/ofw/ofw_bus.h> +#include <dev/ofw/ofw_bus_subr.h> + +#include <dev/pci/pcivar.h> +#include <dev/pci/pcireg.h> +#include <dev/pci/pcib_private.h> + +#include <machine/intr_machdep.h> + +#include <rtems/bsd/local/pcib_if.h> + +DECLARE_CLASS(ofw_pcib_pci_driver); + +struct fsl_pcib_softc { + /* + * This is here so that we can use pci bridge methods, too - the + * generic routines only need the dev, secbus and subbus members + * filled. + * + * XXX: This should be extracted from ofw_pcib_pci.c, and shared in a + * header. + */ + struct pcib_softc ops_pcib_sc; + phandle_t ops_node; + struct ofw_bus_iinfo ops_iinfo; +}; + +static int +fsl_pcib_rc_probe(device_t dev) +{ + + if (pci_get_vendor(dev) != 0x1957) + return (ENXIO); + if (pci_get_progif(dev) != 0) + return (ENXIO); + if (pci_get_class(dev) != PCIC_PROCESSOR) + return (ENXIO); + if (pci_get_subclass(dev) != PCIS_PROCESSOR_POWERPC) + return (ENXIO); + + device_set_desc(dev, "MPC85xx Root Complex bridge"); + + return (BUS_PROBE_DEFAULT); +} + +static device_method_t fsl_pcib_rc_methods[] = { + DEVMETHOD(device_probe, fsl_pcib_rc_probe), + DEVMETHOD_END +}; + +static devclass_t fsl_pcib_rc_devclass; +DEFINE_CLASS_1(pcib, fsl_pcib_rc_driver, fsl_pcib_rc_methods, + sizeof(struct fsl_pcib_softc), ofw_pcib_pci_driver); +EARLY_DRIVER_MODULE(rcpcib, pci, fsl_pcib_rc_driver, fsl_pcib_rc_devclass, 0, 0, + BUS_PASS_BUS); diff --git a/freebsd/sys/powerpc/mpc85xx/platform_mpc85xx.c b/freebsd/sys/powerpc/mpc85xx/platform_mpc85xx.c new file mode 100644 index 00000000..1d806970 --- /dev/null +++ b/freebsd/sys/powerpc/mpc85xx/platform_mpc85xx.c @@ -0,0 +1,710 @@ +#include <machine/rtems-bsd-kernel-space.h> + +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Copyright (c) 2008-2012 Semihalf. + * 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 ``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 <rtems/bsd/local/opt_platform.h> +#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/pcpu.h> +#include <sys/proc.h> +#include <sys/smp.h> + +#include <machine/bus.h> +#include <machine/cpu.h> +#include <machine/hid.h> +#include <machine/_inttypes.h> +#include <machine/machdep.h> +#include <machine/md_var.h> +#include <machine/platform.h> +#include <machine/platformvar.h> +#include <machine/smp.h> +#include <machine/spr.h> +#include <machine/vmparam.h> + +#include <dev/fdt/fdt_common.h> +#include <dev/ofw/ofw_bus.h> +#include <dev/ofw/ofw_bus_subr.h> +#include <dev/ofw/openfirm.h> + +#include <vm/vm.h> +#include <vm/pmap.h> +#include <vm/vm_extern.h> + +#include <powerpc/mpc85xx/mpc85xx.h> + +#include <rtems/bsd/local/platform_if.h> + +#ifdef SMP +extern void *ap_pcpu; +extern vm_paddr_t kernload; /* Kernel physical load address */ +extern uint8_t __boot_page[]; /* Boot page body */ +extern uint32_t bp_kernload; +extern vm_offset_t __startkernel; + +struct cpu_release { + uint32_t entry_h; + uint32_t entry_l; + uint32_t r3_h; + uint32_t r3_l; + uint32_t reserved; + uint32_t pir; +}; +#endif + +extern uint32_t *bootinfo; +vm_paddr_t ccsrbar_pa; +vm_offset_t ccsrbar_va; +vm_size_t ccsrbar_size; + +static int cpu, maxcpu; + +static device_t rcpm_dev; +static void dummy_freeze(device_t, bool); + +static void (*freeze_timebase)(device_t, bool) = dummy_freeze; + +static int mpc85xx_probe(platform_t); +static void mpc85xx_mem_regions(platform_t, struct mem_region *phys, + int *physsz, struct mem_region *avail, int *availsz); +static u_long mpc85xx_timebase_freq(platform_t, struct cpuref *cpuref); +static int mpc85xx_smp_first_cpu(platform_t, struct cpuref *cpuref); +static int mpc85xx_smp_next_cpu(platform_t, struct cpuref *cpuref); +static int mpc85xx_smp_get_bsp(platform_t, struct cpuref *cpuref); +static int mpc85xx_smp_start_cpu(platform_t, struct pcpu *cpu); +static void mpc85xx_smp_timebase_sync(platform_t, u_long tb, int ap); + +static void mpc85xx_reset(platform_t); + +static platform_method_t mpc85xx_methods[] = { + PLATFORMMETHOD(platform_probe, mpc85xx_probe), + PLATFORMMETHOD(platform_attach, mpc85xx_attach), + PLATFORMMETHOD(platform_mem_regions, mpc85xx_mem_regions), + PLATFORMMETHOD(platform_timebase_freq, mpc85xx_timebase_freq), + + PLATFORMMETHOD(platform_smp_first_cpu, mpc85xx_smp_first_cpu), + PLATFORMMETHOD(platform_smp_next_cpu, mpc85xx_smp_next_cpu), + PLATFORMMETHOD(platform_smp_get_bsp, mpc85xx_smp_get_bsp), + PLATFORMMETHOD(platform_smp_start_cpu, mpc85xx_smp_start_cpu), + PLATFORMMETHOD(platform_smp_timebase_sync, mpc85xx_smp_timebase_sync), + + PLATFORMMETHOD(platform_reset, mpc85xx_reset), + + PLATFORMMETHOD_END +}; + +DEFINE_CLASS_0(mpc85xx, mpc85xx_platform, mpc85xx_methods, 0); + +PLATFORM_DEF(mpc85xx_platform); + +static int +mpc85xx_probe(platform_t plat) +{ + u_int pvr = (mfpvr() >> 16) & 0xFFFF; + + switch (pvr) { + case FSL_E500v1: + case FSL_E500v2: + case FSL_E500mc: + case FSL_E5500: + case FSL_E6500: + return (BUS_PROBE_DEFAULT); + } + return (ENXIO); +} + +int +mpc85xx_attach(platform_t plat) +{ + phandle_t cpus, child, ccsr; + const char *soc_name_guesses[] = {"/soc", "soc", NULL}; + const char **name; + pcell_t ranges[6], acells, pacells, scells; + uint64_t ccsrbar, ccsrsize; + int i; + + if ((cpus = OF_finddevice("/cpus")) != -1) { + for (maxcpu = 0, child = OF_child(cpus); child != 0; + child = OF_peer(child), maxcpu++) + ; + } else + maxcpu = 1; + + /* + * Locate CCSR region. Irritatingly, there is no way to find it + * unless you already know where it is. Try to infer its location + * from the device tree. + */ + + ccsr = -1; + for (name = soc_name_guesses; *name != NULL && ccsr == -1; name++) + ccsr = OF_finddevice(*name); + if (ccsr == -1) { + char type[64]; + + /* That didn't work. Search for devices of type "soc" */ + child = OF_child(OF_peer(0)); + for (OF_child(child); child != 0; child = OF_peer(child)) { + if (OF_getprop(child, "device_type", type, sizeof(type)) + <= 0) + continue; + + if (strcmp(type, "soc") == 0) { + ccsr = child; + break; + } + } + } + + if (ccsr == -1) + panic("Could not locate CCSR window!"); + + OF_getprop(ccsr, "#size-cells", &scells, sizeof(scells)); + OF_getprop(ccsr, "#address-cells", &acells, sizeof(acells)); + OF_searchprop(OF_parent(ccsr), "#address-cells", &pacells, + sizeof(pacells)); + OF_getprop(ccsr, "ranges", ranges, sizeof(ranges)); + ccsrbar = ccsrsize = 0; + for (i = acells; i < acells + pacells; i++) { + ccsrbar <<= 32; + ccsrbar |= ranges[i]; + } + for (i = acells + pacells; i < acells + pacells + scells; i++) { + ccsrsize <<= 32; + ccsrsize |= ranges[i]; + } + ccsrbar_va = pmap_early_io_map(ccsrbar, ccsrsize); + ccsrbar_pa = ccsrbar; + ccsrbar_size = ccsrsize; + + mpc85xx_enable_l3_cache(); + + return (0); +} + +void +mpc85xx_mem_regions(platform_t plat, struct mem_region *phys, int *physsz, + struct mem_region *avail, int *availsz) +{ + + ofw_mem_regions(phys, physsz, avail, availsz); +} + +static u_long +mpc85xx_timebase_freq(platform_t plat, struct cpuref *cpuref) +{ + u_long ticks; + phandle_t cpus, child; + pcell_t freq; + + if (bootinfo != NULL) { + if (bootinfo[0] == 1) { + /* Backward compatibility. See 8-STABLE. */ + ticks = bootinfo[3] >> 3; + } else { + /* Compatibility with Juniper's loader. */ + ticks = bootinfo[5] >> 3; + } + } else + ticks = 0; + + if ((cpus = OF_finddevice("/cpus")) == -1) + goto out; + + if ((child = OF_child(cpus)) == 0) + goto out; + + switch (OF_getproplen(child, "timebase-frequency")) { + case 4: + { + uint32_t tbase; + OF_getprop(child, "timebase-frequency", &tbase, sizeof(tbase)); + ticks = tbase; + return (ticks); + } + case 8: + { + uint64_t tbase; + OF_getprop(child, "timebase-frequency", &tbase, sizeof(tbase)); + ticks = tbase; + return (ticks); + } + default: + break; + } + + freq = 0; + if (OF_getprop(child, "bus-frequency", (void *)&freq, + sizeof(freq)) <= 0) + goto out; + + if (freq == 0) + goto out; + + /* + * Time Base and Decrementer are updated every 8 CCB bus clocks. + * HID0[SEL_TBCLK] = 0 + */ + if (mpc85xx_is_qoriq()) + ticks = freq / 32; + else + ticks = freq / 8; + +out: + if (ticks <= 0) + panic("Unable to determine timebase frequency!"); + + return (ticks); +} + +static int +mpc85xx_smp_first_cpu(platform_t plat, struct cpuref *cpuref) +{ + + cpu = 0; + cpuref->cr_cpuid = cpu; + cpuref->cr_hwref = cpuref->cr_cpuid; + if (bootverbose) + printf("powerpc_smp_first_cpu: cpuid %d\n", cpuref->cr_cpuid); + cpu++; + + return (0); +} + +static int +mpc85xx_smp_next_cpu(platform_t plat, struct cpuref *cpuref) +{ + + if (cpu >= maxcpu) + return (ENOENT); + + cpuref->cr_cpuid = cpu++; + cpuref->cr_hwref = cpuref->cr_cpuid; + if (bootverbose) + printf("powerpc_smp_next_cpu: cpuid %d\n", cpuref->cr_cpuid); + + return (0); +} + +static int +mpc85xx_smp_get_bsp(platform_t plat, struct cpuref *cpuref) +{ + + cpuref->cr_cpuid = mfspr(SPR_PIR); + cpuref->cr_hwref = cpuref->cr_cpuid; + + return (0); +} + +#ifdef SMP +static int +mpc85xx_smp_start_cpu_epapr(platform_t plat, struct pcpu *pc) +{ + vm_paddr_t rel_pa, bptr; + volatile struct cpu_release *rel; + vm_offset_t rel_va, rel_page; + phandle_t node; + int i; + + /* If we're calling this, the node already exists. */ + node = OF_finddevice("/cpus"); + for (i = 0, node = OF_child(node); i < pc->pc_cpuid; + i++, node = OF_peer(node)) + ; + if (OF_getencprop(node, "cpu-release-addr", (pcell_t *)&rel_pa, + sizeof(rel_pa)) == -1) { + return (ENOENT); + } + + rel_page = kva_alloc(PAGE_SIZE); + if (rel_page == 0) + return (ENOMEM); + + critical_enter(); + rel_va = rel_page + (rel_pa & PAGE_MASK); + pmap_kenter(rel_page, rel_pa & ~PAGE_MASK); + rel = (struct cpu_release *)rel_va; + bptr = pmap_kextract((uintptr_t)__boot_page); + cpu_flush_dcache(__DEVOLATILE(struct cpu_release *,rel), sizeof(*rel)); + rel->pir = pc->pc_cpuid; __asm __volatile("sync"); + rel->entry_h = (bptr >> 32); + rel->entry_l = bptr; __asm __volatile("sync"); + cpu_flush_dcache(__DEVOLATILE(struct cpu_release *,rel), sizeof(*rel)); + if (bootverbose) + printf("Waking up CPU %d via CPU release page %p\n", + pc->pc_cpuid, rel); + critical_exit(); + pmap_kremove(rel_page); + kva_free(rel_page, PAGE_SIZE); + + return (0); +} +#endif + +static int +mpc85xx_smp_start_cpu(platform_t plat, struct pcpu *pc) +{ +#ifdef SMP + vm_paddr_t bptr; + uint32_t reg; + int timeout; + uintptr_t brr; + int cpuid; + int epapr_boot = 0; + uint32_t tgt; + + if (mpc85xx_is_qoriq()) { + reg = ccsr_read4(OCP85XX_COREDISR); + cpuid = pc->pc_cpuid; + + if ((reg & (1 << cpuid)) != 0) { + printf("%s: CPU %d is disabled!\n", __func__, pc->pc_cpuid); + return (-1); + } + + brr = OCP85XX_BRR; + } else { + brr = OCP85XX_EEBPCR; + cpuid = pc->pc_cpuid + 24; + } + bp_kernload = kernload; + /* + * bp_kernload is in the boot page. Sync the cache because ePAPR + * booting has the other core(s) already running. + */ + cpu_flush_dcache(&bp_kernload, sizeof(bp_kernload)); + + ap_pcpu = pc; + __asm __volatile("msync; isync"); + + /* First try the ePAPR way. */ + if (mpc85xx_smp_start_cpu_epapr(plat, pc) == 0) { + epapr_boot = 1; + goto spin_wait; + } + + reg = ccsr_read4(brr); + if ((reg & (1 << cpuid)) != 0) { + printf("SMP: CPU %d already out of hold-off state!\n", + pc->pc_cpuid); + return (ENXIO); + } + + /* Flush caches to have our changes hit DRAM. */ + cpu_flush_dcache(__boot_page, 4096); + + bptr = pmap_kextract((uintptr_t)__boot_page); + KASSERT((bptr & 0xfff) == 0, + ("%s: boot page is not aligned (%#jx)", __func__, (uintmax_t)bptr)); + if (mpc85xx_is_qoriq()) { + /* + * Read DDR controller configuration to select proper BPTR target ID. + * + * On P5020 bit 29 of DDR1_CS0_CONFIG enables DDR controllers + * interleaving. If this bit is set, we have to use + * OCP85XX_TGTIF_RAM_INTL as BPTR target ID. On other QorIQ DPAA SoCs, + * this bit is reserved and always 0. + */ + + reg = ccsr_read4(OCP85XX_DDR1_CS0_CONFIG); + if (reg & (1 << 29)) + tgt = OCP85XX_TGTIF_RAM_INTL; + else + tgt = OCP85XX_TGTIF_RAM1; + + /* + * Set BSTR to the physical address of the boot page + */ + ccsr_write4(OCP85XX_BSTRH, bptr >> 32); + ccsr_write4(OCP85XX_BSTRL, bptr); + ccsr_write4(OCP85XX_BSTAR, OCP85XX_ENA_MASK | + (tgt << OCP85XX_TRGT_SHIFT_QORIQ) | (ffsl(PAGE_SIZE) - 2)); + + /* Read back OCP85XX_BSTAR to synchronize write */ + ccsr_read4(OCP85XX_BSTAR); + + /* + * Enable and configure time base on new CPU. + */ + + /* Set TB clock source to platform clock / 32 */ + reg = ccsr_read4(CCSR_CTBCKSELR); + ccsr_write4(CCSR_CTBCKSELR, reg & ~(1 << pc->pc_cpuid)); + + /* Enable TB */ + reg = ccsr_read4(CCSR_CTBENR); + ccsr_write4(CCSR_CTBENR, reg | (1 << pc->pc_cpuid)); + } else { + /* + * Set BPTR to the physical address of the boot page + */ + bptr = (bptr >> 12) | 0x80000000u; + ccsr_write4(OCP85XX_BPTR, bptr); + __asm __volatile("isync; msync"); + } + + /* + * Release AP from hold-off state + */ + reg = ccsr_read4(brr); + ccsr_write4(brr, reg | (1 << cpuid)); + __asm __volatile("isync; msync"); + +spin_wait: + timeout = 500; + while (!pc->pc_awake && timeout--) + DELAY(1000); /* wait 1ms */ + + /* + * Disable boot page translation so that the 4K page at the default + * address (= 0xfffff000) isn't permanently remapped and thus not + * usable otherwise. + */ + if (!epapr_boot) { + if (mpc85xx_is_qoriq()) + ccsr_write4(OCP85XX_BSTAR, 0); + else + ccsr_write4(OCP85XX_BPTR, 0); + __asm __volatile("isync; msync"); + } + + if (!pc->pc_awake) + panic("SMP: CPU %d didn't wake up.\n", pc->pc_cpuid); + return ((pc->pc_awake) ? 0 : EBUSY); +#else + /* No SMP support */ + return (ENXIO); +#endif +} + +static void +mpc85xx_reset(platform_t plat) +{ + + /* + * Try the dedicated reset register first. + * If the SoC doesn't have one, we'll fall + * back to using the debug control register. + */ + ccsr_write4(OCP85XX_RSTCR, 2); + + /* Clear DBCR0, disables debug interrupts and events. */ + mtspr(SPR_DBCR0, 0); + __asm __volatile("isync"); + + /* Enable Debug Interrupts in MSR. */ + mtmsr(mfmsr() | PSL_DE); + + /* Enable debug interrupts and issue reset. */ + mtspr(SPR_DBCR0, mfspr(SPR_DBCR0) | DBCR0_IDM | DBCR0_RST_SYSTEM); + + printf("Reset failed...\n"); + while (1) + ; +} + +static void +mpc85xx_smp_timebase_sync(platform_t plat, u_long tb, int ap) +{ + static volatile bool tb_ready; + static volatile int cpu_done; + + if (ap) { + /* APs. Hold off until we get a stable timebase. */ + while (!tb_ready) + atomic_thread_fence_seq_cst(); + mttb(tb); + atomic_add_int(&cpu_done, 1); + while (cpu_done < mp_ncpus) + atomic_thread_fence_seq_cst(); + } else { + /* BSP */ + freeze_timebase(rcpm_dev, true); + tb_ready = true; + mttb(tb); + atomic_add_int(&cpu_done, 1); + while (cpu_done < mp_ncpus) + atomic_thread_fence_seq_cst(); + freeze_timebase(rcpm_dev, false); + } +} + +/* Fallback freeze. In case no real handler is found in the device tree. */ +static void +dummy_freeze(device_t dev, bool freeze) +{ + /* Nothing to do here, move along. */ +} + + +/* QorIQ Run control/power management timebase management. */ + +#define RCPM_CTBENR 0x00000084 +struct mpc85xx_rcpm_softc { + struct resource *sc_mem; +}; + +static void +mpc85xx_rcpm_freeze_timebase(device_t dev, bool freeze) +{ + struct mpc85xx_rcpm_softc *sc; + + sc = device_get_softc(dev); + + if (freeze) + bus_write_4(sc->sc_mem, RCPM_CTBENR, 0); + else + bus_write_4(sc->sc_mem, RCPM_CTBENR, (1 << maxcpu) - 1); +} + +static int +mpc85xx_rcpm_probe(device_t dev) +{ + if (!ofw_bus_is_compatible(dev, "fsl,qoriq-rcpm-1.0")) + return (ENXIO); + + device_set_desc(dev, "QorIQ Run control and power management"); + return (BUS_PROBE_GENERIC); +} + +static int +mpc85xx_rcpm_attach(device_t dev) +{ + struct mpc85xx_rcpm_softc *sc; + int rid; + + sc = device_get_softc(dev); + freeze_timebase = mpc85xx_rcpm_freeze_timebase; + rcpm_dev = dev; + + rid = 0; + sc->sc_mem = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, + RF_ACTIVE | RF_SHAREABLE); + + return (0); +} + +static device_method_t mpc85xx_rcpm_methods[] = { + DEVMETHOD(device_probe, mpc85xx_rcpm_probe), + DEVMETHOD(device_attach, mpc85xx_rcpm_attach), + DEVMETHOD_END +}; + +static devclass_t mpc85xx_rcpm_devclass; + +static driver_t mpc85xx_rcpm_driver = { + "rcpm", + mpc85xx_rcpm_methods, + sizeof(struct mpc85xx_rcpm_softc) +}; + +EARLY_DRIVER_MODULE(mpc85xx_rcpm, simplebus, mpc85xx_rcpm_driver, + mpc85xx_rcpm_devclass, 0, 0, BUS_PASS_BUS); + + +/* "Global utilities" power management/Timebase management. */ + +#define GUTS_DEVDISR 0x00000070 +#define DEVDISR_TB0 0x00004000 +#define DEVDISR_TB1 0x00001000 + +struct mpc85xx_guts_softc { + struct resource *sc_mem; +}; + +static void +mpc85xx_guts_freeze_timebase(device_t dev, bool freeze) +{ + struct mpc85xx_guts_softc *sc; + uint32_t devdisr; + + sc = device_get_softc(dev); + + devdisr = bus_read_4(sc->sc_mem, GUTS_DEVDISR); + if (freeze) + bus_write_4(sc->sc_mem, GUTS_DEVDISR, + devdisr | (DEVDISR_TB0 | DEVDISR_TB1)); + else + bus_write_4(sc->sc_mem, GUTS_DEVDISR, + devdisr & ~(DEVDISR_TB0 | DEVDISR_TB1)); +} + +static int +mpc85xx_guts_probe(device_t dev) +{ + if (!ofw_bus_is_compatible(dev, "fsl,mpc8572-guts") && + !ofw_bus_is_compatible(dev, "fsl,p1020-guts") && + !ofw_bus_is_compatible(dev, "fsl,p1021-guts") && + !ofw_bus_is_compatible(dev, "fsl,p1022-guts") && + !ofw_bus_is_compatible(dev, "fsl,p1023-guts") && + !ofw_bus_is_compatible(dev, "fsl,p2020-guts")) + return (ENXIO); + + device_set_desc(dev, "MPC85xx Global Utilities"); + return (BUS_PROBE_GENERIC); +} + +static int +mpc85xx_guts_attach(device_t dev) +{ + struct mpc85xx_rcpm_softc *sc; + int rid; + + sc = device_get_softc(dev); + freeze_timebase = mpc85xx_guts_freeze_timebase; + rcpm_dev = dev; + + rid = 0; + sc->sc_mem = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, + RF_ACTIVE | RF_SHAREABLE); + + return (0); +} + +static device_method_t mpc85xx_guts_methods[] = { + DEVMETHOD(device_probe, mpc85xx_guts_probe), + DEVMETHOD(device_attach, mpc85xx_guts_attach), + DEVMETHOD_END +}; + +static driver_t mpc85xx_guts_driver = { + "guts", + mpc85xx_guts_methods, + sizeof(struct mpc85xx_guts_softc) +}; + +static devclass_t mpc85xx_guts_devclass; + +EARLY_DRIVER_MODULE(mpc85xx_guts, simplebus, mpc85xx_guts_driver, + mpc85xx_guts_devclass, 0, 0, BUS_PASS_BUS); diff --git a/freebsd/sys/powerpc/ofw/ofw_pcib_pci.c b/freebsd/sys/powerpc/ofw/ofw_pcib_pci.c new file mode 100644 index 00000000..26dd52ba --- /dev/null +++ b/freebsd/sys/powerpc/ofw/ofw_pcib_pci.c @@ -0,0 +1,178 @@ +#include <machine/rtems-bsd-kernel-space.h> + +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Copyright (c) 2000 Michael Smith + * Copyright (c) 2000 BSDi + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/module.h> +#include <sys/bus.h> +#include <sys/malloc.h> +#include <sys/kernel.h> + +#include <dev/ofw/openfirm.h> +#include <dev/ofw/ofw_pci.h> +#include <dev/ofw/ofw_bus.h> +#include <dev/ofw/ofw_bus_subr.h> + +#include <dev/pci/pcivar.h> +#include <dev/pci/pcireg.h> +#include <dev/pci/pcib_private.h> + +#include <machine/intr_machdep.h> + +#include <rtems/bsd/local/pcib_if.h> + +static int ofw_pcib_pci_probe(device_t bus); +static int ofw_pcib_pci_attach(device_t bus); +static phandle_t ofw_pcib_pci_get_node(device_t bus, device_t dev); +static int ofw_pcib_pci_route_interrupt(device_t bridge, device_t dev, + int intpin); + +static device_method_t ofw_pcib_pci_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, ofw_pcib_pci_probe), + DEVMETHOD(device_attach, ofw_pcib_pci_attach), + + /* pcib interface */ + DEVMETHOD(pcib_route_interrupt, ofw_pcib_pci_route_interrupt), + DEVMETHOD(pcib_request_feature, pcib_request_feature_allow), + + /* ofw_bus interface */ + DEVMETHOD(ofw_bus_get_node, ofw_pcib_pci_get_node), + + DEVMETHOD_END +}; + +static devclass_t pcib_devclass; + +struct ofw_pcib_softc { + /* + * This is here so that we can use pci bridge methods, too - the + * generic routines only need the dev, secbus and subbus members + * filled. + */ + struct pcib_softc ops_pcib_sc; + phandle_t ops_node; + struct ofw_bus_iinfo ops_iinfo; +}; + +DEFINE_CLASS_1(pcib, ofw_pcib_pci_driver, ofw_pcib_pci_methods, + sizeof(struct ofw_pcib_softc), pcib_driver); +EARLY_DRIVER_MODULE(ofw_pcib, pci, ofw_pcib_pci_driver, pcib_devclass, 0, 0, + BUS_PASS_BUS); + +static int +ofw_pcib_pci_probe(device_t dev) +{ + + if ((pci_get_class(dev) != PCIC_BRIDGE) || + (pci_get_subclass(dev) != PCIS_BRIDGE_PCI)) { + return (ENXIO); + } + + if (ofw_bus_get_node(dev) == -1) + return (ENXIO); + + device_set_desc(dev, "OFW PCI-PCI bridge"); + return (0); +} + +static int +ofw_pcib_pci_attach(device_t dev) +{ + struct ofw_pcib_softc *sc; + + sc = device_get_softc(dev); + sc->ops_pcib_sc.dev = dev; + sc->ops_node = ofw_bus_get_node(dev); + + ofw_bus_setup_iinfo(sc->ops_node, &sc->ops_iinfo, + sizeof(cell_t)); + + pcib_attach_common(dev); + return (pcib_attach_child(dev)); +} + +static phandle_t +ofw_pcib_pci_get_node(device_t bridge, device_t dev) +{ + /* We have only one child, the PCI bus, so pass it our node */ + + return (ofw_bus_get_node(bridge)); +} + +static int +ofw_pcib_pci_route_interrupt(device_t bridge, device_t dev, int intpin) +{ + struct ofw_pcib_softc *sc; + struct ofw_bus_iinfo *ii; + struct ofw_pci_register reg; + cell_t pintr, mintr[2]; + int intrcells; + phandle_t iparent; + + sc = device_get_softc(bridge); + ii = &sc->ops_iinfo; + if (ii->opi_imapsz > 0) { + pintr = intpin; + + /* Fabricate imap information if this isn't an OFW device */ + bzero(®, sizeof(reg)); + reg.phys_hi = (pci_get_bus(dev) << OFW_PCI_PHYS_HI_BUSSHIFT) | + (pci_get_slot(dev) << OFW_PCI_PHYS_HI_DEVICESHIFT) | + (pci_get_function(dev) << OFW_PCI_PHYS_HI_FUNCTIONSHIFT); + + intrcells = ofw_bus_lookup_imap(ofw_bus_get_node(dev), ii, ®, + sizeof(reg), &pintr, sizeof(pintr), mintr, sizeof(mintr), + &iparent); + if (intrcells) { + /* + * If we've found a mapping, return it and don't map + * it again on higher levels - that causes problems + * in some cases, and never seems to be required. + */ + mintr[0] = ofw_bus_map_intr(dev, iparent, intrcells, + mintr); + return (mintr[0]); + } + } else if (intpin >= 1 && intpin <= 4) { + /* + * When an interrupt map is missing, we need to do the + * standard PCI swizzle and continue mapping at the parent. + */ + return (pcib_route_interrupt(bridge, dev, intpin)); + } + return (PCIB_ROUTE_INTERRUPT(device_get_parent(device_get_parent( + bridge)), bridge, intpin)); +} + -- 2.35.3 _______________________________________________ devel mailing list devel@rtems.org http://lists.rtems.org/mailman/listinfo/devel