diff options
author | Martin Pieuchot <mpi@cvs.openbsd.org> | 2013-08-07 07:29:21 +0000 |
---|---|---|
committer | Martin Pieuchot <mpi@cvs.openbsd.org> | 2013-08-07 07:29:21 +0000 |
commit | 90995fd1f69222c92bffafb661665b88a5668b65 (patch) | |
tree | 30806dbdd563ebc9c8dd8c9eb7c1a0b24f41d9c3 /sys/arch/macppc/pci/pci_machdep.c | |
parent | c894b994ad2bbf72ae4a92f82b403a148212f1ed (diff) |
Mimic what's done on sparc64 and parse the OpenFirmware device tree to
enumerate the pci devices. This allow us to not store the interrupt
vector in the interrupt line register and to not try to attach pci
devices disabled by firmware.
With inputs from and ok kettenis@
Diffstat (limited to 'sys/arch/macppc/pci/pci_machdep.c')
-rw-r--r-- | sys/arch/macppc/pci/pci_machdep.c | 340 |
1 files changed, 340 insertions, 0 deletions
diff --git a/sys/arch/macppc/pci/pci_machdep.c b/sys/arch/macppc/pci/pci_machdep.c new file mode 100644 index 00000000000..fc32e87f12e --- /dev/null +++ b/sys/arch/macppc/pci/pci_machdep.c @@ -0,0 +1,340 @@ +/* $OpenBSD: pci_machdep.c,v 1.1 2013/08/07 07:29:19 mpi Exp $ */ + +/* + * Copyright (c) 2013 Martin Pieuchot + * Copyright (c) 1997 Per Fogelstrom + * + * 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 <sys/param.h> +#include <sys/systm.h> + +#include <machine/bus.h> +#include <machine/autoconf.h> + +#include <dev/pci/pcireg.h> +#include <dev/pci/pcivar.h> + +#include <dev/ofw/openfirm.h> +#include <dev/ofw/ofw_pci.h> + + +struct powerpc_bus_dma_tag pci_bus_dma_tag = { + NULL, + _dmamap_create, + _dmamap_destroy, + _dmamap_load, + _dmamap_load_mbuf, + _dmamap_load_uio, + _dmamap_load_raw, + _dmamap_unload, + _dmamap_sync, + _dmamem_alloc, + _dmamem_free, + _dmamem_map, + _dmamem_unmap, + _dmamem_mmap +}; + +void +pci_attach_hook(struct device *parent, struct device *self, + struct pcibus_attach_args *pba) +{ +} + +int +pci_bus_maxdevs(pci_chipset_tag_t pc, int busno) +{ + return (32); +} + +pcitag_t +pci_make_tag(pci_chipset_tag_t pc, int b, int d, int f) +{ + struct ofw_pci_register reg; + pcitag_t tag; + int node, busrange[2]; + + if (pc->busnode[b]) + return PCITAG_CREATE(0, b, d, f); + + for (node = pc->pc_node; node; node = OF_peer(node)) { + /* + * Check for PCI-PCI bridges. If the device we want is + * in the bus-range for that bridge, work our way down. + */ + while ((OF_getprop(node, "bus-range", &busrange, + sizeof(busrange)) == sizeof(busrange)) && + (b >= busrange[0] && b <= busrange[1])) { + node = OF_child(node); + } + + if (OF_getprop(node, "reg", ®, sizeof(reg)) < sizeof(reg)) + continue; + + if (b != OFW_PCI_PHYS_HI_BUS(reg.phys_hi)) + continue; + if (d != OFW_PCI_PHYS_HI_DEVICE(reg.phys_hi)) + continue; + if (f != OFW_PCI_PHYS_HI_FUNCTION(reg.phys_hi)) + continue; + + tag = PCITAG_CREATE(node, b, d, f); + + return (tag); + } + + return (PCITAG_CREATE(-1, b, d, f)); +} + +void +pci_decompose_tag(pci_chipset_tag_t pc, pcitag_t tag, int *b, int *d, int *f) +{ + if (b != NULL) + *b = PCITAG_BUS(tag); + if (d != NULL) + *d = PCITAG_DEV(tag); + if (f != NULL) + *f = PCITAG_FUN(tag); +} + +int +pci_conf_size(pci_chipset_tag_t pc, pcitag_t tag) +{ + return (PCI_CONFIG_SPACE_SIZE); +} + +pcireg_t +pci_conf_read(pci_chipset_tag_t pc, pcitag_t tag, int reg) +{ + if (PCITAG_NODE(tag) != -1) + return (*(pc)->pc_conf_read)(pc->pc_conf_v, tag, reg); + + return ((pcireg_t)~0); +} + +void +pci_conf_write(pci_chipset_tag_t pc, pcitag_t tag, int reg, pcireg_t data) +{ + if (PCITAG_NODE(tag) != -1) + (*(pc)->pc_conf_write)(pc->pc_conf_v, tag, reg, data); +} + +int +pci_intr_map(struct pci_attach_args *pa, pci_intr_handle_t *ihp) +{ + struct ofw_pci_register reg; + int node = PCITAG_NODE(pa->pa_tag); + int intr[4], nintr, len; + + if (OF_getprop(node, "reg", ®, sizeof(reg)) < sizeof(reg)) + return (ENODEV); + + /* Try to get the old Apple OFW interrupt property first. */ + len = OF_getprop(node, "AAPL,interrupts", &intr, sizeof(intr)); + if (len == sizeof(intr[0])) + goto found; + + len = OF_getprop(node, "interrupts", intr, sizeof(intr)); + if (len < sizeof(intr[0])) + return (ENODEV); + + /* + * If we have multiple interrupts for a device, choose the one + * that corresponds to the PCI function. + */ + nintr = len / sizeof(intr[0]); + if (PCITAG_FUN(pa->pa_tag) < nintr) + intr[0] = intr[PCITAG_FUN(pa->pa_tag)]; + + reg.size_hi = intr[0]; + if (ofw_intr_map(OF_parent(node), (uint32_t *)®, intr)) { + /* + * This can fail on some machines where the parent's + * node doesn't have any "interrupt-map" and friends. + * + * In this case just trust what we got in "interrupts". + */ + } + +found: + *ihp = intr[0]; + + return (0); +} + +int +pci_intr_map_msi(struct pci_attach_args *pa, pci_intr_handle_t *ihp) +{ + return (-1); +} + +int +pci_intr_line(pci_chipset_tag_t pc, pci_intr_handle_t ih) +{ + return (ih); +} + +const char * +pci_intr_string(pci_chipset_tag_t pc, pci_intr_handle_t ih) +{ + static char str[16]; + + snprintf(str, sizeof(str), "irq %ld", ih); + + return (str); +} + +void * +pci_intr_establish(pci_chipset_tag_t pc, pci_intr_handle_t ih, int lvl, + int (*func)(void *), void *arg, const char *what) +{ + return (*intr_establish_func)(pc, ih, IST_LEVEL, lvl, func, arg, what); +} + +void +pci_intr_disestablish(pci_chipset_tag_t pc, void *cookie) +{ + (*intr_disestablish_func)(pc, cookie); +} + +int +pci_ether_hw_addr(pci_chipset_tag_t pc, uint8_t *oaddr) +{ + uint8_t laddr[6]; + int node, len; + + node = OF_finddevice("enet"); + len = OF_getprop(node, "local-mac-address", laddr, sizeof(laddr)); + if (sizeof(laddr) == len) { + memcpy(oaddr, laddr, sizeof(laddr)); + return (1); + } + + oaddr[0] = oaddr[1] = oaddr[2] = 0xff; + oaddr[3] = oaddr[4] = oaddr[5] = 0xff; + + return (0); +} + +int +ofw_enumerate_pcibus(struct pci_softc *sc, + int (*match)(struct pci_attach_args *), struct pci_attach_args *pap) +{ + pci_chipset_tag_t pc = sc->sc_pc; + struct ofw_pci_register reg; + int node, b, d, f, ret; + pcireg_t bhlcr; + pcitag_t tag; + + if (sc->sc_bridgetag) + node = PCITAG_NODE(*sc->sc_bridgetag); + else + node = pc->pc_node; + + for (node = OF_child(node); node; node = OF_peer(node)) { + if (OF_getprop(node, "reg", ®, sizeof(reg)) < sizeof(reg)) + continue; + + b = OFW_PCI_PHYS_HI_BUS(reg.phys_hi); + d = OFW_PCI_PHYS_HI_DEVICE(reg.phys_hi); + f = OFW_PCI_PHYS_HI_FUNCTION(reg.phys_hi); + + tag = PCITAG_CREATE(node, b, d, f); + + bhlcr = pci_conf_read(pc, tag, PCI_BHLC_REG); + if (PCI_HDRTYPE_TYPE(bhlcr) > 2) + continue; + + ret = pci_probe_device(sc, tag, match, pap); + if (match != NULL && ret != 0) + return (ret); + } + + return (0); +} + +int +ofw_intr_map(int node, uint32_t *addr, uint32_t *intr) +{ + uint32_t imap[144], mmask[8], *mp, *mp1; + uint32_t acells, icells, mcells; + int ilen, mlen, i, step = 0; + int parent; + + ilen = OF_getprop(node, "interrupt-map", imap, sizeof(imap)); + mlen = OF_getprop(node, "interrupt-map-mask", mmask, sizeof(mmask)); + if (ilen < 0 || mlen < 0) + return (-1); + + if ((OF_getprop(node, "#address-cells", &acells, 4) < 0) || + (OF_getprop(node, "#interrupt-cells", &icells, 4) < 0)) + return (-1); + + mcells = acells + icells; + if (mcells != (mlen / sizeof(mmask[0]))) + return (-1); + + for (i = 0; i < mcells; i++) + addr[i] &= mmask[i]; + + /* interrupt-map is formatted as follows + * int * #address-cells, int * #interrupt-cells, int, int, int + * eg + * address-cells = 3 + * interrupt-cells = 1 + * 00001000 00000000 00000000 00000000 ff911258 00000034 00000001 + * 00001800 00000000 00000000 00000000 ff911258 00000035 00000001 + * 00002000 00000000 00000000 00000000 ff911258 00000036 00000001 + * | address cells | | intr | |node| | irq | |edge/level| + * | cells| | interrupt cells | + * | of node | + * or at least something close to that. + */ + for (mp = imap; ilen > mlen; mp += step) { + mp1 = mp + mcells; + parent = *mp1; + + if (bcmp(mp, addr, mlen) == 0) { + /* + * If we have a match and the parent is not an + * interrupt controller continue recursively. + */ + if (OF_getprop(parent, "interrupt-controller", NULL, 0)) + return ofw_intr_map(parent, &mp1[1], intr); + + *intr = mp1[1]; + return (0); + } + + if (OF_getprop(parent, "#address-cells", &acells, 4) < 0) + acells = 0; + if (OF_getprop(parent, "#interrupt-cells", &icells, 4) < 0) + break; + + step = mcells + 1 + acells + icells; + ilen -= step * sizeof(imap[0]); + } + + return (-1); +} |