Home | History | Annotate | Line # | Download | only in pci
      1 /*	$NetBSD: pci_bus_fixup.c,v 1.3 2019/03/01 09:25:59 msaitoh 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.3 2019/03/01 09:25:59 msaitoh Exp $");
     34 
     35 #include <sys/param.h>
     36 #include <sys/systm.h>
     37 #include <sys/kernel.h>
     38 
     39 #include <sys/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,
    123 				    PCI_BRIDGE_BUS_REG);
    124 				reg &= 0xff000000;
    125 				reg |= bus | (bus_max << 8) | (0xff << 16);
    126 				pci_conf_write(pc, tag, PCI_BRIDGE_BUS_REG,
    127 				    reg);
    128 
    129 				/* Scan subordinate bus. */
    130 				bus_sub = pci_bus_fixup(pc, bus_max);
    131 
    132 				/* Configure the bridge. */
    133 				reg &= 0xff000000;
    134 				reg |= bus | (bus_max << 8) | (bus_sub << 16);
    135 				pci_conf_write(pc, tag, PCI_BRIDGE_BUS_REG,
    136 				    reg);
    137 
    138 				/* record relationship */
    139 				pci_bus_parent[bus_max]=bus;
    140 
    141 				pci_bus_tag[bus_max]=tag;
    142 
    143 				aprint_debug("PCI bridge %d: primary %d, "
    144 				    "secondary %d, subordinate %d\n",
    145 				    bridge, bus, bus_max, bus_sub);
    146 
    147 				/* Next bridge's secondary bus #. */
    148 				bus_max = (bus_sub > bus_max) ?
    149 				    bus_sub : bus_max;
    150 			}
    151 		}
    152 	}
    153 
    154 	return (bus_max);	/* last # of subordinate bus */
    155 }
    156 
    157 /* Reset bus-bridge configuration */
    158 void
    159 pci_bridge_reset(pci_chipset_tag_t pc, pcitag_t tag, void *ctx)
    160 {
    161 	pcireg_t reg;
    162 
    163 	reg = pci_conf_read(pc, tag, PCI_BRIDGE_BUS_REG);
    164 	reg &= 0xff000000;
    165 	reg |= 0x00ffffff;	/* max bus # */
    166 	pci_conf_write(pc, tag, PCI_BRIDGE_BUS_REG, reg);
    167 }
    168