/* $OpenBSD: pci_machdep.c,v 1.7 2016/07/04 09:30:18 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 #include #include #include #include #include #include #include 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_alloc_range, _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); node = pc->pc_node; /* * Because ht(4) controller nodes do not have a "bus-range" * property, we need to start iterating from one of their * PCI bridge nodes to be able to find our devices. */ if (OF_getprop(node, "bus-range", &busrange, sizeof(busrange)) < 0) node = OF_child(pc->pc_node); for (; 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], 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); 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 len, node, b, d, f, ret; uint32_t val = 0; char compat[32]; pcireg_t bhlcr; pcitag_t tag; if (sc->sc_bridgetag) node = PCITAG_NODE(*sc->sc_bridgetag); else node = pc->pc_node; /* The AGP bridge is not in the device-tree. */ len = OF_getprop(node, "compatible", compat, sizeof(compat)); if (len > 0 && strcmp(compat, "u3-agp") == 0) { tag = PCITAG_CREATE(0, sc->sc_bus, 11, 0); ret = pci_probe_device(sc, tag, match, pap); if (match != NULL && ret != 0) return (ret); } /* * An HT-PCI bridge is needed for interrupt mapping, attach it first */ if (len > 0 && strcmp(compat, "u3-ht") == 0) { int snode; for (snode = OF_child(node); snode; snode = OF_peer(snode)) { val = 0; if ((OF_getprop(snode, "shasta-interrupt-sequencer", &val, sizeof(val)) < sizeof(val)) || val != 1) continue; if (OF_getprop(snode, "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(snode, 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); } } for (node = OF_child(node); node; node = OF_peer(node)) { if (OF_getprop(node, "reg", ®, sizeof(reg)) < sizeof(reg)) continue; /* * Skip HT-PCI bridge, it has been attached before. */ val = 0; if ((OF_getprop(node, "shasta-interrupt-sequencer", &val, sizeof(val)) >= sizeof(val)) && val == 1) 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) { char ic[20]; /* * If we have a match and the parent is not an * interrupt controller continue recursively. */ if (OF_getprop(parent, "interrupt-controller", ic, 20)) 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); }