diff options
Diffstat (limited to 'sys/sparc64/pci/ofw_pci.c')
-rw-r--r-- | sys/sparc64/pci/ofw_pci.c | 267 |
1 files changed, 267 insertions, 0 deletions
diff --git a/sys/sparc64/pci/ofw_pci.c b/sys/sparc64/pci/ofw_pci.c new file mode 100644 index 0000000..67ee787 --- /dev/null +++ b/sys/sparc64/pci/ofw_pci.c @@ -0,0 +1,267 @@ +/* + * Copyright (c) 1999, 2000 Matthew R. Green + * All rights reserved. + * Copyright 2001 by Thomas Moestl <tmm@FreeBSD.org>. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 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: NetBSD: psycho.c,v 1.35 2001/09/10 16:17:06 eeh Exp + * + * $FreeBSD$ + */ + +#include "opt_ofw_pci.h" + +#include <sys/param.h> +#include <sys/kernel.h> +#include <sys/systm.h> +#include <sys/bus.h> + +#include <dev/pci/pcivar.h> +#include <dev/pci/pcireg.h> + +#include <dev/ofw/ofw_pci.h> +#include <dev/ofw/openfirm.h> + +#include <sparc64/pci/ofw_pci.h> + +#include <machine/ofw_bus.h> + +#include "pcib_if.h" + +/* + * Find the interrupt-map properties for a node. This might not be a property + * of the parent, because there may be bridges in between, so go up through the + * tree to find it. + * This seems to be only needed for PCI systems, so it has not been moved to + * ofw_bus.c + */ +int +ofw_pci_find_imap(phandle_t node, struct ofw_pci_imap **imap, + struct ofw_pci_imap_msk *imapmsk) +{ + int nimap; + + nimap = -1; + while ((node = OF_parent(node)) != 0) { + if ((nimap = OF_getprop_alloc(node, "interrupt-map", + sizeof(**imap), (void **)imap)) == -1 || + OF_getprop(node, "interrupt-map-mask", + imapmsk, sizeof(*imapmsk)) == -1) { + if (*imap != NULL) { + free(*imap, M_OFWPROP); + *imap = NULL; + } + nimap = -1; + } else + break; + } + return (nimap); +} + +/* + * Route an interrupt using the firmware nodes. Returns 255 for interrupts + * that cannot be routed (suitable for the PCI code). + */ +int +ofw_pci_route_intr2(int intr, struct ofw_pci_register *pcir, + struct ofw_pci_imap *imap, int nimap, struct ofw_pci_imap_msk *imapmsk) +{ + char regm[12]; + int cintr; + + cintr = ofw_bus_route_intr(intr, pcir, sizeof(*pcir), 12, 1, imap, + nimap, imapmsk, regm); + if (cintr == -1) + return (255); + else + return (cintr); +} + +int +ofw_pci_route_intr(phandle_t node, struct ofw_pci_register *pcir, + struct ofw_pci_imap *intrmap, int nintrmap, + struct ofw_pci_imap_msk *intrmapmsk) +{ + int intr; + + if (OF_getprop(node, "interrupts", &intr, sizeof(intr)) == -1) + return (255); + + return (ofw_pci_route_intr2(intr, pcir, intrmap, nintrmap, intrmapmsk)); +} + +#define OFW_PCI_PCIBUS "pci" +/* + * Walk the PCI bus hierarchy, starting with the root PCI bus and descending + * through bridges, and initialize the interrupt line configuration registers + * of attached devices using firmware information. + */ +void +ofw_pci_init_intr(device_t dev, phandle_t bus, struct ofw_pci_imap *intrmap, + int nintrmap, struct ofw_pci_imap_msk *intrmapmsk) +{ + struct ofw_pci_imap_msk lintrmapmsk; + struct ofw_pci_register pcir; + phandle_t node; + char type[32]; + int intr; + int freemap; + + if ((node = OF_child(bus)) == 0) + return; + freemap = 0; + do { + if (node == -1) + panic("ofw_pci_init_intr: OF_child failed"); + if (OF_getprop(node, "device_type", type, sizeof(type)) == -1) + type[0] = '\0'; + else + type[sizeof(type) - 1] = '\0'; + if (strcmp(type, OFW_PCI_PCIBUS) == 0) { + /* + * This is a pci-pci bridge, recurse to initialize the + * child bus. The hierarchy is usually at most 2 levels + * deep, so recursion is feasible. + */ +#ifdef OFW_PCI_DEBUG + device_printf(dev, __func__": descending to " + "subordinate PCI bus\n"); +#endif + ofw_pci_init_intr(dev, node, NULL, 0, NULL); + } else { + if (OF_getprop(node, "reg", &pcir, sizeof(pcir)) == -1) + panic("ofw_pci_route_intr: OF_getprop failed"); + /* + * If we didn't get interrupt map properties passed, + * try to find them now. On some systems, buses that + * have no non-bridge children have no such properties, + * so only try to find them at need. + */ + if (intrmap == NULL) { + nintrmap = OF_getprop_alloc(bus, + "interrupt-map", sizeof(*intrmap), + (void **)&intrmap); + if (nintrmap == -1 || + OF_getprop(bus, "interrupt-map-mask", + &lintrmapmsk, sizeof(lintrmapmsk)) == -1) { + panic("ofw_pci_init_intr: could not get " + "interrupt map properties"); + } + intrmapmsk = &lintrmapmsk; + freemap = 1; + } + if ((intr = ofw_pci_route_intr(node, &pcir, intrmap, + nintrmap, intrmapmsk)) != 255) { +#ifdef OFW_PCI_DEBUG + device_printf(dev, __func__": mapping intr for " + "%d/%d/%d to %d (preset was %d)\n", + OFW_PCI_PHYS_HI_BUS(pcir.phys_hi), + OFW_PCI_PHYS_HI_DEVICE(pcir.phys_hi), + OFW_PCI_PHYS_HI_FUNCTION(pcir.phys_hi), + intr, + (int)PCIB_READ_CONFIG(dev, + OFW_PCI_PHYS_HI_BUS(pcir.phys_hi), + OFW_PCI_PHYS_HI_DEVICE(pcir.phys_hi), + OFW_PCI_PHYS_HI_FUNCTION(pcir.phys_hi), + PCIR_INTLINE, 1)); + +#endif /* OFW_PCI_DEBUG */ + PCIB_WRITE_CONFIG(dev, + OFW_PCI_PHYS_HI_BUS(pcir.phys_hi), + OFW_PCI_PHYS_HI_DEVICE(pcir.phys_hi), + OFW_PCI_PHYS_HI_FUNCTION(pcir.phys_hi), + PCIR_INTLINE, intr, 1); + } else { +#ifdef OFW_PCI_DEBUG + device_printf(dev, __func__": no interrupt " + "mapping found for %d/%d/%d (preset %d)\n", + OFW_PCI_PHYS_HI_BUS(pcir.phys_hi), + OFW_PCI_PHYS_HI_DEVICE(pcir.phys_hi), + OFW_PCI_PHYS_HI_FUNCTION(pcir.phys_hi), + (int)PCIB_READ_CONFIG(dev, + OFW_PCI_PHYS_HI_BUS(pcir.phys_hi), + OFW_PCI_PHYS_HI_DEVICE(pcir.phys_hi), + OFW_PCI_PHYS_HI_FUNCTION(pcir.phys_hi), + PCIR_INTLINE, 1)); +#endif /* OFW_PCI_DEBUG */ + /* The firmware initializes to 0 instead 255 */ + PCIB_WRITE_CONFIG(dev, + OFW_PCI_PHYS_HI_BUS(pcir.phys_hi), + OFW_PCI_PHYS_HI_DEVICE(pcir.phys_hi), + OFW_PCI_PHYS_HI_FUNCTION(pcir.phys_hi), + PCIR_INTLINE, 255, 1); + } + } + } while ((node = OF_peer(node)) != 0); + if (freemap) + free(intrmap, M_OFWPROP); +} + +phandle_t +ofw_pci_find_node(int bus, int slot, int func) +{ + phandle_t node, bnode, parent; + struct ofw_pci_register pcir; + int br[2]; + char name[16]; + + /* 1. Try to find the bus in question. */ + bnode = 0; + name[sizeof(name) - 1] = '\0'; + parent = OF_peer(0); + node = OF_child(parent); + while (node != 0 && node != -1) { + if (OF_getprop(node, "name", name, sizeof(name) - 1) != -1 && + strcmp(name, "pci") == 0 && + OF_getprop(node, "bus-range", br, sizeof(br)) != -1) { + /* Found the bus? */ + if (bus == br[0]) { + bnode = node; + break; + } + /* Need to descend? */ + if (bus > br[0] && bus <= br[1]) { + parent = node; + node = OF_child(node); + continue; + } + } + node = OF_peer(node); + } + if (bnode == 0) + return (0); + for (node = OF_child(bnode); node != 0 && node != -1; + node = OF_peer(node)) { + if (OF_getprop(node, "reg", &pcir, sizeof(pcir)) == -1) + continue; + if (OFW_PCI_PHYS_HI_DEVICE(pcir.phys_hi) == slot && + OFW_PCI_PHYS_HI_FUNCTION(pcir.phys_hi) == func) { + if (OFW_PCI_PHYS_HI_BUS(pcir.phys_hi) != bus) + panic("ofw_pci_find_node: bus number mismatch"); + return (node); + } + } + return (0); +} |