Home | History | Annotate | Line # | Download | only in pci
pci_bus_fixup.c revision 1.1
      1 /*	$NetBSD: pci_bus_fixup.c,v 1.1 2008/05/18 02:06:14 jmcneill Exp $	*/
      2 
      3 /*
      4  * Copyright (c) 1999, by UCHIYAMA Yasushi
      5  * All rights reserved.
      6  *
      7  * Redistribution and use in source and binary forms, with or without
      8  * modification, are permitted provided that the following conditions
      9  * are met:
     10  * 1. Redistributions of source code must retain the above copyright
     11  *    notice, this list of conditions and the following disclaimer.
     12  * 2. The name of the developer may NOT be used to endorse or promote products
     13  *    derived from this software without specific prior written permission.
     14  *
     15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
     16  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     18  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
     19  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     20  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
     21  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     22  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     23  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     24  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     25  * SUCH DAMAGE.
     26  */
     27 
     28 /*
     29  * PCI bus renumbering support.
     30  */
     31 
     32 #include <sys/cdefs.h>
     33 __KERNEL_RCSID(0, "$NetBSD: pci_bus_fixup.c,v 1.1 2008/05/18 02:06:14 jmcneill Exp $");
     34 
     35 #include <sys/param.h>
     36 #include <sys/systm.h>
     37 #include <sys/kernel.h>
     38 
     39 #include <machine/bus.h>
     40 
     41 #include <dev/pci/pcireg.h>
     42 #include <dev/pci/pcivar.h>
     43 #include <dev/pci/pcidevs.h>
     44 #include <dev/pci/ppbreg.h>
     45 
     46 #include <x86/pci/pci_bus_fixup.h>
     47 
     48 /* this array lists the parent for each bus number */
     49 int pci_bus_parent[256];
     50 
     51 /* this array lists the pcitag to program each bridge */
     52 pcitag_t pci_bus_tag[256];
     53 
     54 static void pci_bridge_reset(pci_chipset_tag_t, pcitag_t, void *);
     55 
     56 int
     57 pci_bus_fixup(pci_chipset_tag_t pc, int bus)
     58 {
     59 	static int bus_total;
     60 	static int bridge_cnt;
     61 	int device, maxdevs, function, nfuncs, bridge, bus_max, bus_sub;
     62 	const struct pci_quirkdata *qd;
     63 	pcireg_t reg;
     64 	pcitag_t tag;
     65 
     66 	bus_max = bus;
     67 	bus_sub = 0;
     68 
     69 	if (++bus_total > 256)
     70 		panic("pci_bus_fixup: more than 256 PCI busses?");
     71 
     72 	/* Reset bridge configuration on this bus */
     73 	pci_bridge_foreach(pc, bus, bus, pci_bridge_reset, 0);
     74 
     75 	maxdevs = pci_bus_maxdevs(pc, bus);
     76 
     77 	for (device = 0; device < maxdevs; device++) {
     78 		tag = pci_make_tag(pc, bus, device, 0);
     79 		reg = pci_conf_read(pc, tag, PCI_ID_REG);
     80 
     81 		/* Invalid vendor ID value? */
     82 		if (PCI_VENDOR(reg) == PCI_VENDOR_INVALID)
     83 			continue;
     84 		/* XXX Not invalid, but we've done this ~forever. */
     85 		if (PCI_VENDOR(reg) == 0)
     86 			continue;
     87 
     88 		qd = pci_lookup_quirkdata(PCI_VENDOR(reg), PCI_PRODUCT(reg));
     89 
     90 		reg = pci_conf_read(pc, tag, PCI_BHLC_REG);
     91 		if (PCI_HDRTYPE_MULTIFN(reg) ||
     92 		    (qd != NULL &&
     93 		     (qd->quirks & PCI_QUIRK_MULTIFUNCTION) != 0))
     94 			nfuncs = 8;
     95 		else
     96 			nfuncs = 1;
     97 
     98 		for (function = 0; function < nfuncs; function++) {
     99 			tag = pci_make_tag(pc, bus, device, function);
    100 			reg = pci_conf_read(pc, tag, PCI_ID_REG);
    101 
    102 			/* Invalid vendor ID value? */
    103 			if (PCI_VENDOR(reg) == PCI_VENDOR_INVALID)
    104 				continue;
    105 			/* XXX Not invalid, but we've done this ~forever. */
    106 			if (PCI_VENDOR(reg) == 0)
    107 				continue;
    108 
    109 			aprint_debug("PCI fixup examining %02x:%02x\n",
    110 			       PCI_VENDOR(reg), PCI_PRODUCT(reg));
    111 
    112 			reg = pci_conf_read(pc, tag, PCI_CLASS_REG);
    113 			if (PCI_CLASS(reg) == PCI_CLASS_BRIDGE &&
    114 			    (PCI_SUBCLASS(reg) == PCI_SUBCLASS_BRIDGE_PCI ||
    115 			     PCI_SUBCLASS(reg) == PCI_SUBCLASS_BRIDGE_CARDBUS)) {
    116 				/* Assign the bridge #. */
    117 				bridge = bridge_cnt++;
    118 
    119 				/* Assign the bridge's secondary bus #. */
    120 				bus_max++;
    121 
    122 				reg = pci_conf_read(pc, tag, PPB_REG_BUSINFO);
    123 				reg &= 0xff000000;
    124 				reg |= bus | (bus_max << 8) | (0xff << 16);
    125 				pci_conf_write(pc, tag, PPB_REG_BUSINFO, reg);
    126 
    127 				/* Scan subordinate bus. */
    128 				bus_sub = pci_bus_fixup(pc, bus_max);
    129 
    130 				/* Configure the bridge. */
    131 				reg &= 0xff000000;
    132 				reg |= bus | (bus_max << 8) | (bus_sub << 16);
    133 				pci_conf_write(pc, tag, PPB_REG_BUSINFO, reg);
    134 
    135 				/* record relationship */
    136 				pci_bus_parent[bus_max]=bus;
    137 
    138 				pci_bus_tag[bus_max]=tag;
    139 
    140 				aprint_debug("PCI bridge %d: primary %d, "
    141 				    "secondary %d, subordinate %d\n",
    142 				    bridge, bus, bus_max, bus_sub);
    143 
    144 				/* Next bridge's secondary bus #. */
    145 				bus_max = (bus_sub > bus_max) ?
    146 				    bus_sub : bus_max;
    147 			}
    148 		}
    149 	}
    150 
    151 	return (bus_max);	/* last # of subordinate bus */
    152 }
    153 
    154 /* Reset bus-bridge configuration */
    155 void
    156 pci_bridge_reset(pci_chipset_tag_t pc, pcitag_t tag, void *ctx)
    157 {
    158 	pcireg_t reg;
    159 
    160 	reg = pci_conf_read(pc, tag, PPB_REG_BUSINFO);
    161 	reg &= 0xff000000;
    162 	reg |= 0x00ffffff;	/* max bus # */
    163 	pci_conf_write(pc, tag, PPB_REG_BUSINFO, reg);
    164 }
    165