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 <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 */
49int pci_bus_parent[256];
50
51/* this array lists the pcitag to program each bridge */
52pcitag_t pci_bus_tag[256];
53
54static void pci_bridge_reset(pci_chipset_tag_t, pcitag_t, void *);
55
56int
57pci_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 */
155void
156pci_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