1183882Snwhitehorn/*-
2183882Snwhitehorn * Copyright (c) 1997, Stefan Esser <se@freebsd.org>
3183882Snwhitehorn * Copyright (c) 2000, Michael Smith <msmith@freebsd.org>
4183882Snwhitehorn * Copyright (c) 2000, BSDi
5183882Snwhitehorn * Copyright (c) 2003, Thomas Moestl <tmm@FreeBSD.org>
6183882Snwhitehorn * All rights reserved.
7183882Snwhitehorn *
8183882Snwhitehorn * Redistribution and use in source and binary forms, with or without
9183882Snwhitehorn * modification, are permitted provided that the following conditions
10183882Snwhitehorn * are met:
11183882Snwhitehorn * 1. Redistributions of source code must retain the above copyright
12183882Snwhitehorn *    notice unmodified, this list of conditions, and the following
13183882Snwhitehorn *    disclaimer.
14183882Snwhitehorn * 2. Redistributions in binary form must reproduce the above copyright
15183882Snwhitehorn *    notice, this list of conditions and the following disclaimer in the
16183882Snwhitehorn *    documentation and/or other materials provided with the distribution.
17183882Snwhitehorn *
18183882Snwhitehorn * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
19183882Snwhitehorn * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
20183882Snwhitehorn * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
21183882Snwhitehorn * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
22183882Snwhitehorn * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
23183882Snwhitehorn * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24183882Snwhitehorn * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25183882Snwhitehorn * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26183882Snwhitehorn * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27183882Snwhitehorn * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28183882Snwhitehorn */
29183882Snwhitehorn
30183882Snwhitehorn#include <sys/cdefs.h>
31183882Snwhitehorn__FBSDID("$FreeBSD$");
32183882Snwhitehorn
33183882Snwhitehorn#include <sys/param.h>
34183882Snwhitehorn#include <sys/bus.h>
35183882Snwhitehorn#include <sys/kernel.h>
36183882Snwhitehorn#include <sys/libkern.h>
37183882Snwhitehorn#include <sys/module.h>
38183882Snwhitehorn#include <sys/pciio.h>
39183882Snwhitehorn
40183882Snwhitehorn#include <dev/ofw/ofw_bus.h>
41183882Snwhitehorn#include <dev/ofw/ofw_bus_subr.h>
42183882Snwhitehorn#include <dev/ofw/ofw_pci.h>
43183882Snwhitehorn#include <dev/ofw/openfirm.h>
44183882Snwhitehorn
45183882Snwhitehorn#include <machine/bus.h>
46208162Snwhitehorn#include <machine/intr_machdep.h>
47183882Snwhitehorn#include <machine/resource.h>
48183882Snwhitehorn
49183882Snwhitehorn#include <dev/pci/pcireg.h>
50183882Snwhitehorn#include <dev/pci/pcivar.h>
51183882Snwhitehorn#include <dev/pci/pci_private.h>
52183882Snwhitehorn
53259257Sandreast#include "ofw_pcibus.h"
54183882Snwhitehorn#include "pcib_if.h"
55183882Snwhitehorn#include "pci_if.h"
56183882Snwhitehorn
57186128Snwhitehorntypedef uint32_t ofw_pci_intr_t;
58183882Snwhitehorn
59183882Snwhitehorn/* Methods */
60183882Snwhitehornstatic device_probe_t ofw_pcibus_probe;
61183882Snwhitehornstatic device_attach_t ofw_pcibus_attach;
62183882Snwhitehornstatic pci_assign_interrupt_t ofw_pcibus_assign_interrupt;
63183882Snwhitehornstatic ofw_bus_get_devinfo_t ofw_pcibus_get_devinfo;
64186128Snwhitehornstatic int ofw_pcibus_child_pnpinfo_str_method(device_t cbdev, device_t child,
65186128Snwhitehorn    char *buf, size_t buflen);
66183882Snwhitehorn
67186128Snwhitehornstatic void ofw_pcibus_enum_devtree(device_t dev, u_int domain, u_int busno);
68186128Snwhitehornstatic void ofw_pcibus_enum_bus(device_t dev, u_int domain, u_int busno);
69186128Snwhitehorn
70183882Snwhitehornstatic device_method_t ofw_pcibus_methods[] = {
71183882Snwhitehorn	/* Device interface */
72183882Snwhitehorn	DEVMETHOD(device_probe,		ofw_pcibus_probe),
73183882Snwhitehorn	DEVMETHOD(device_attach,	ofw_pcibus_attach),
74183882Snwhitehorn
75186128Snwhitehorn	/* Bus interface */
76186128Snwhitehorn	DEVMETHOD(bus_child_pnpinfo_str, ofw_pcibus_child_pnpinfo_str_method),
77186128Snwhitehorn
78183882Snwhitehorn	/* PCI interface */
79183882Snwhitehorn	DEVMETHOD(pci_assign_interrupt, ofw_pcibus_assign_interrupt),
80183882Snwhitehorn
81183882Snwhitehorn	/* ofw_bus interface */
82183882Snwhitehorn	DEVMETHOD(ofw_bus_get_devinfo,	ofw_pcibus_get_devinfo),
83183882Snwhitehorn	DEVMETHOD(ofw_bus_get_compat,	ofw_bus_gen_get_compat),
84183882Snwhitehorn	DEVMETHOD(ofw_bus_get_model,	ofw_bus_gen_get_model),
85183882Snwhitehorn	DEVMETHOD(ofw_bus_get_name,	ofw_bus_gen_get_name),
86183882Snwhitehorn	DEVMETHOD(ofw_bus_get_node,	ofw_bus_gen_get_node),
87183882Snwhitehorn	DEVMETHOD(ofw_bus_get_type,	ofw_bus_gen_get_type),
88183882Snwhitehorn
89259257Sandreast	DEVMETHOD_END
90183882Snwhitehorn};
91183882Snwhitehorn
92183882Snwhitehornstatic devclass_t pci_devclass;
93183882Snwhitehorn
94232403SjhbDEFINE_CLASS_1(pci, ofw_pcibus_driver, ofw_pcibus_methods,
95232403Sjhb    sizeof(struct pci_softc), pci_driver);
96183882SnwhitehornDRIVER_MODULE(ofw_pcibus, pcib, ofw_pcibus_driver, pci_devclass, 0, 0);
97183882SnwhitehornMODULE_VERSION(ofw_pcibus, 1);
98183882SnwhitehornMODULE_DEPEND(ofw_pcibus, pci, 1, 1, 1);
99183882Snwhitehorn
100255615Snwhitehornstatic int ofw_devices_only = 0;
101255615SnwhitehornTUNABLE_INT("hw.pci.ofw_devices_only", &ofw_devices_only);
102255615Snwhitehorn
103183882Snwhitehornstatic int
104183882Snwhitehornofw_pcibus_probe(device_t dev)
105183882Snwhitehorn{
106186128Snwhitehorn
107233018Snwhitehorn	if (ofw_bus_get_node(dev) == -1)
108183882Snwhitehorn		return (ENXIO);
109183882Snwhitehorn	device_set_desc(dev, "OFW PCI bus");
110183882Snwhitehorn
111255615Snwhitehorn	return (BUS_PROBE_DEFAULT);
112183882Snwhitehorn}
113183882Snwhitehorn
114183882Snwhitehornstatic int
115183882Snwhitehornofw_pcibus_attach(device_t dev)
116183882Snwhitehorn{
117186128Snwhitehorn	u_int busno, domain;
118232403Sjhb	int error;
119186128Snwhitehorn
120232403Sjhb	error = pci_attach_common(dev);
121232403Sjhb	if (error)
122232403Sjhb		return (error);
123186128Snwhitehorn	domain = pcib_get_domain(dev);
124186128Snwhitehorn	busno = pcib_get_bus(dev);
125186128Snwhitehorn
126186128Snwhitehorn	/*
127186128Snwhitehorn	 * Attach those children represented in the device tree.
128186128Snwhitehorn	 */
129186128Snwhitehorn
130186128Snwhitehorn	ofw_pcibus_enum_devtree(dev, domain, busno);
131186128Snwhitehorn
132186128Snwhitehorn	/*
133186128Snwhitehorn	 * We now attach any laggard devices. FDT, for instance, allows
134186128Snwhitehorn	 * the device tree to enumerate only some PCI devices. Apple's
135186128Snwhitehorn	 * OF device tree on some Grackle-based hardware can also miss
136186128Snwhitehorn	 * functions on multi-function cards.
137186128Snwhitehorn	 */
138186128Snwhitehorn
139255615Snwhitehorn	if (!ofw_devices_only)
140255615Snwhitehorn		ofw_pcibus_enum_bus(dev, domain, busno);
141186128Snwhitehorn
142186128Snwhitehorn	return (bus_generic_attach(dev));
143186128Snwhitehorn}
144186128Snwhitehorn
145186128Snwhitehornstatic void
146186128Snwhitehornofw_pcibus_enum_devtree(device_t dev, u_int domain, u_int busno)
147186128Snwhitehorn{
148183882Snwhitehorn	device_t pcib;
149183882Snwhitehorn	struct ofw_pci_register pcir;
150183882Snwhitehorn	struct ofw_pcibus_devinfo *dinfo;
151183882Snwhitehorn	phandle_t node, child;
152186128Snwhitehorn	u_int func, slot;
153186128Snwhitehorn	int intline;
154183882Snwhitehorn
155183882Snwhitehorn	pcib = device_get_parent(dev);
156183882Snwhitehorn	node = ofw_bus_get_node(dev);
157183882Snwhitehorn
158183882Snwhitehorn	for (child = OF_child(node); child != 0; child = OF_peer(child)) {
159183882Snwhitehorn		if (OF_getprop(child, "reg", &pcir, sizeof(pcir)) == -1)
160183882Snwhitehorn			continue;
161183882Snwhitehorn		slot = OFW_PCI_PHYS_HI_DEVICE(pcir.phys_hi);
162183882Snwhitehorn		func = OFW_PCI_PHYS_HI_FUNCTION(pcir.phys_hi);
163183882Snwhitehorn
164183882Snwhitehorn		/* Some OFW device trees contain dupes. */
165183882Snwhitehorn		if (pci_find_dbsf(domain, busno, slot, func) != NULL)
166183882Snwhitehorn			continue;
167183882Snwhitehorn
168186128Snwhitehorn		/*
169186128Snwhitehorn		 * The preset in the intline register is usually bogus.  Reset
170186128Snwhitehorn		 * it such that the PCI code will reroute the interrupt if
171186128Snwhitehorn		 * needed.
172186128Snwhitehorn		 */
173183882Snwhitehorn
174186128Snwhitehorn		intline = PCI_INVALID_IRQ;
175186128Snwhitehorn		if (OF_getproplen(child, "interrupts") > 0)
176186128Snwhitehorn			intline = 0;
177186128Snwhitehorn		PCIB_WRITE_CONFIG(pcib, busno, slot, func, PCIR_INTLINE,
178186128Snwhitehorn		    intline, 1);
179186128Snwhitehorn
180186128Snwhitehorn		/*
181186128Snwhitehorn		 * Now set up the PCI and OFW bus layer devinfo and add it
182186128Snwhitehorn		 * to the PCI bus.
183186128Snwhitehorn		 */
184186128Snwhitehorn
185183882Snwhitehorn		dinfo = (struct ofw_pcibus_devinfo *)pci_read_device(pcib,
186183882Snwhitehorn		    domain, busno, slot, func, sizeof(*dinfo));
187183882Snwhitehorn		if (dinfo == NULL)
188183882Snwhitehorn			continue;
189183882Snwhitehorn		if (ofw_bus_gen_setup_devinfo(&dinfo->opd_obdinfo, child) !=
190183882Snwhitehorn		    0) {
191183882Snwhitehorn			pci_freecfg((struct pci_devinfo *)dinfo);
192183882Snwhitehorn			continue;
193183882Snwhitehorn		}
194259257Sandreast		dinfo->opd_dma_tag = NULL;
195183882Snwhitehorn		pci_add_child(dev, (struct pci_devinfo *)dinfo);
196183882Snwhitehorn
197183882Snwhitehorn		/*
198208162Snwhitehorn		 * Some devices don't have an intpin set, but do have
199208162Snwhitehorn		 * interrupts. These are fully specified, and set in the
200186128Snwhitehorn		 * interrupts property, so add that value to the device's
201186128Snwhitehorn		 * resource list.
202208162Snwhitehorn		 */
203208162Snwhitehorn		if (dinfo->opd_dinfo.cfg.intpin == 0) {
204208162Snwhitehorn			ofw_pci_intr_t intr[2];
205208162Snwhitehorn			phandle_t iparent;
206208162Snwhitehorn			int icells;
207183882Snwhitehorn
208186128Snwhitehorn			if (OF_getprop(child, "interrupts", &intr,
209186128Snwhitehorn			    sizeof(intr)) > 0) {
210208162Snwhitehorn				iparent = 0;
211208162Snwhitehorn				icells = 1;
212208162Snwhitehorn				OF_getprop(child, "interrupt-parent", &iparent,
213208162Snwhitehorn				    sizeof(iparent));
214255596Snwhitehorn				if (iparent != 0) {
215255596Snwhitehorn					OF_getprop(OF_xref_phandle(iparent),
216255596Snwhitehorn					    "#interrupt-cells", &icells,
217255596Snwhitehorn					    sizeof(icells));
218265969Sian					intr[0] = ofw_bus_map_intr(dev, iparent,
219266128Sian					    icells, intr);
220255596Snwhitehorn				}
221209298Snwhitehorn
222208162Snwhitehorn				resource_list_add(&dinfo->opd_dinfo.resources,
223208162Snwhitehorn				    SYS_RES_IRQ, 0, intr[0], intr[0], 1);
224186128Snwhitehorn			}
225208162Snwhitehorn		}
226183882Snwhitehorn	}
227183882Snwhitehorn}
228183882Snwhitehorn
229186128Snwhitehorn/*
230186128Snwhitehorn * The following is an almost exact clone of pci_add_children(), with the
231186128Snwhitehorn * addition that it (a) will not add children that have already been added,
232186128Snwhitehorn * and (b) will set up the OFW devinfo to point to invalid values. This is
233186128Snwhitehorn * to handle non-enumerated PCI children as exist in FDT and on the second
234186128Snwhitehorn * function of the Rage 128 in my Blue & White G3.
235186128Snwhitehorn */
236183882Snwhitehorn
237186128Snwhitehornstatic void
238186128Snwhitehornofw_pcibus_enum_bus(device_t dev, u_int domain, u_int busno)
239183882Snwhitehorn{
240186128Snwhitehorn	device_t pcib;
241183882Snwhitehorn	struct ofw_pcibus_devinfo *dinfo;
242186128Snwhitehorn	int maxslots;
243186128Snwhitehorn	int s, f, pcifunchigh;
244186128Snwhitehorn	uint8_t hdrtype;
245183882Snwhitehorn
246186128Snwhitehorn	pcib = device_get_parent(dev);
247183882Snwhitehorn
248186128Snwhitehorn	maxslots = PCIB_MAXSLOTS(pcib);
249186128Snwhitehorn	for (s = 0; s <= maxslots; s++) {
250186128Snwhitehorn		pcifunchigh = 0;
251186128Snwhitehorn		f = 0;
252186128Snwhitehorn		DELAY(1);
253186128Snwhitehorn		hdrtype = PCIB_READ_CONFIG(pcib, busno, s, f, PCIR_HDRTYPE, 1);
254186128Snwhitehorn		if ((hdrtype & PCIM_HDRTYPE) > PCI_MAXHDRTYPE)
255186128Snwhitehorn			continue;
256186128Snwhitehorn		if (hdrtype & PCIM_MFDEV)
257186128Snwhitehorn			pcifunchigh = PCI_FUNCMAX;
258186128Snwhitehorn		for (f = 0; f <= pcifunchigh; f++) {
259186128Snwhitehorn			/* Filter devices we have already added */
260186128Snwhitehorn			if (pci_find_dbsf(domain, busno, s, f) != NULL)
261186128Snwhitehorn				continue;
262183882Snwhitehorn
263186128Snwhitehorn			dinfo = (struct ofw_pcibus_devinfo *)pci_read_device(
264186128Snwhitehorn			    pcib, domain, busno, s, f, sizeof(*dinfo));
265209299Snwhitehorn			if (dinfo == NULL)
266209299Snwhitehorn				continue;
267183882Snwhitehorn
268259257Sandreast			dinfo->opd_dma_tag = NULL;
269209299Snwhitehorn			dinfo->opd_obdinfo.obd_node = -1;
270209299Snwhitehorn
271209299Snwhitehorn			dinfo->opd_obdinfo.obd_name = NULL;
272209299Snwhitehorn			dinfo->opd_obdinfo.obd_compat = NULL;
273209299Snwhitehorn			dinfo->opd_obdinfo.obd_type = NULL;
274209299Snwhitehorn			dinfo->opd_obdinfo.obd_model = NULL;
275209299Snwhitehorn
276209299Snwhitehorn			/*
277209299Snwhitehorn			 * For non OFW-devices, don't believe 0
278209299Snwhitehorn			 * for an interrupt.
279209299Snwhitehorn			 */
280209299Snwhitehorn			if (dinfo->opd_dinfo.cfg.intline == 0) {
281209299Snwhitehorn				dinfo->opd_dinfo.cfg.intline = PCI_INVALID_IRQ;
282209299Snwhitehorn				PCIB_WRITE_CONFIG(pcib, busno, s, f,
283209299Snwhitehorn				    PCIR_INTLINE, PCI_INVALID_IRQ, 1);
284186128Snwhitehorn			}
285209299Snwhitehorn
286209299Snwhitehorn			pci_add_child(dev, (struct pci_devinfo *)dinfo);
287183882Snwhitehorn		}
288183882Snwhitehorn	}
289183882Snwhitehorn}
290183882Snwhitehorn
291183882Snwhitehornstatic int
292186128Snwhitehornofw_pcibus_child_pnpinfo_str_method(device_t cbdev, device_t child, char *buf,
293186128Snwhitehorn    size_t buflen)
294183882Snwhitehorn{
295186128Snwhitehorn	pci_child_pnpinfo_str_method(cbdev, child, buf, buflen);
296183882Snwhitehorn
297186128Snwhitehorn	if (ofw_bus_get_node(child) != -1)  {
298186128Snwhitehorn		strlcat(buf, " ", buflen); /* Separate info */
299186128Snwhitehorn		ofw_bus_gen_child_pnpinfo_str(cbdev, child, buf, buflen);
300186128Snwhitehorn	}
301183882Snwhitehorn
302186128Snwhitehorn	return (0);
303183882Snwhitehorn}
304186128Snwhitehorn
305183882Snwhitehornstatic int
306186128Snwhitehornofw_pcibus_assign_interrupt(device_t dev, device_t child)
307183882Snwhitehorn{
308266128Sian	ofw_pci_intr_t intr[2];
309209298Snwhitehorn	phandle_t node, iparent;
310266128Sian	int isz, icells;
311183882Snwhitehorn
312186128Snwhitehorn	node = ofw_bus_get_node(child);
313183882Snwhitehorn
314186128Snwhitehorn	if (node == -1) {
315186128Snwhitehorn		/* Non-firmware enumerated child, use standard routing */
316186128Snwhitehorn
317266128Sian		intr[0] = pci_get_intpin(child);
318186128Snwhitehorn		return (PCIB_ROUTE_INTERRUPT(device_get_parent(dev), child,
319266128Sian		    intr[0]));
320183882Snwhitehorn	}
321186128Snwhitehorn
322183882Snwhitehorn	/*
323209298Snwhitehorn	 * Try to determine the node's interrupt parent so we know which
324209298Snwhitehorn	 * PIC to use.
325209298Snwhitehorn	 */
326209298Snwhitehorn
327209298Snwhitehorn	iparent = -1;
328209298Snwhitehorn	if (OF_getprop(node, "interrupt-parent", &iparent, sizeof(iparent)) < 0)
329209298Snwhitehorn		iparent = -1;
330266128Sian	icells = 1;
331266128Sian	if (iparent != -1)
332266128Sian		OF_getprop(OF_xref_phandle(iparent), "#interrupt-cells",
333266128Sian		    &icells, sizeof(icells));
334209298Snwhitehorn
335209298Snwhitehorn	/*
336186128Snwhitehorn	 * Any AAPL,interrupts property gets priority and is
337186128Snwhitehorn	 * fully specified (i.e. does not need routing)
338183882Snwhitehorn	 */
339183882Snwhitehorn
340266128Sian	isz = OF_getprop(node, "AAPL,interrupts", intr, sizeof(intr));
341266128Sian	if (isz == sizeof(intr[0])*icells)
342266128Sian		return ((iparent == -1) ? intr[0] : ofw_bus_map_intr(dev,
343266128Sian		    iparent, icells, intr));
344183882Snwhitehorn
345266128Sian	isz = OF_getprop(node, "interrupts", intr, sizeof(intr));
346266128Sian	if (isz == sizeof(intr[0])*icells) {
347209298Snwhitehorn		if (iparent != -1)
348266128Sian			intr[0] = ofw_bus_map_intr(dev, iparent, icells, intr);
349209298Snwhitehorn	} else {
350209298Snwhitehorn		/* No property: our best guess is the intpin. */
351266128Sian		intr[0] = pci_get_intpin(child);
352183882Snwhitehorn	}
353183882Snwhitehorn
354183882Snwhitehorn	/*
355186128Snwhitehorn	 * If we got intr from a property, it may or may not be an intpin.
356186128Snwhitehorn	 * For on-board devices, it frequently is not, and is completely out
357186128Snwhitehorn	 * of the valid intpin range.  For PCI slots, it hopefully is,
358186128Snwhitehorn	 * otherwise we will have trouble interfacing with non-OFW buses
359186128Snwhitehorn	 * such as cardbus.
360186128Snwhitehorn	 * Since we cannot tell which it is without violating layering, we
361186128Snwhitehorn	 * will always use the route_interrupt method, and treat exceptions
362186128Snwhitehorn	 * on the level they become apparent.
363183882Snwhitehorn	 */
364266128Sian	return (PCIB_ROUTE_INTERRUPT(device_get_parent(dev), child, intr[0]));
365186128Snwhitehorn}
366183882Snwhitehorn
367186128Snwhitehornstatic const struct ofw_bus_devinfo *
368186128Snwhitehornofw_pcibus_get_devinfo(device_t bus, device_t dev)
369186128Snwhitehorn{
370186128Snwhitehorn	struct ofw_pcibus_devinfo *dinfo;
371183882Snwhitehorn
372186128Snwhitehorn	dinfo = device_get_ivars(dev);
373186128Snwhitehorn	return (&dinfo->opd_obdinfo);
374183882Snwhitehorn}
375183882Snwhitehorn
376