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
53183882Snwhitehorn#include "pcib_if.h"
54183882Snwhitehorn#include "pci_if.h"
55183882Snwhitehorn
56186128Snwhitehorntypedef uint32_t ofw_pci_intr_t;
57183882Snwhitehorn
58183882Snwhitehorn/* Methods */
59183882Snwhitehornstatic device_probe_t ofw_pcibus_probe;
60183882Snwhitehornstatic device_attach_t ofw_pcibus_attach;
61183882Snwhitehornstatic pci_assign_interrupt_t ofw_pcibus_assign_interrupt;
62183882Snwhitehornstatic ofw_bus_get_devinfo_t ofw_pcibus_get_devinfo;
63186128Snwhitehornstatic int ofw_pcibus_child_pnpinfo_str_method(device_t cbdev, device_t child,
64186128Snwhitehorn    char *buf, size_t buflen);
65183882Snwhitehorn
66186128Snwhitehornstatic void ofw_pcibus_enum_devtree(device_t dev, u_int domain, u_int busno);
67186128Snwhitehornstatic void ofw_pcibus_enum_bus(device_t dev, u_int domain, u_int busno);
68186128Snwhitehorn
69183882Snwhitehornstatic device_method_t ofw_pcibus_methods[] = {
70183882Snwhitehorn	/* Device interface */
71183882Snwhitehorn	DEVMETHOD(device_probe,		ofw_pcibus_probe),
72183882Snwhitehorn	DEVMETHOD(device_attach,	ofw_pcibus_attach),
73183882Snwhitehorn
74186128Snwhitehorn	/* Bus interface */
75186128Snwhitehorn	DEVMETHOD(bus_child_pnpinfo_str, ofw_pcibus_child_pnpinfo_str_method),
76186128Snwhitehorn
77183882Snwhitehorn	/* PCI interface */
78183882Snwhitehorn	DEVMETHOD(pci_assign_interrupt, ofw_pcibus_assign_interrupt),
79183882Snwhitehorn
80183882Snwhitehorn	/* ofw_bus interface */
81183882Snwhitehorn	DEVMETHOD(ofw_bus_get_devinfo,	ofw_pcibus_get_devinfo),
82183882Snwhitehorn	DEVMETHOD(ofw_bus_get_compat,	ofw_bus_gen_get_compat),
83183882Snwhitehorn	DEVMETHOD(ofw_bus_get_model,	ofw_bus_gen_get_model),
84183882Snwhitehorn	DEVMETHOD(ofw_bus_get_name,	ofw_bus_gen_get_name),
85183882Snwhitehorn	DEVMETHOD(ofw_bus_get_node,	ofw_bus_gen_get_node),
86183882Snwhitehorn	DEVMETHOD(ofw_bus_get_type,	ofw_bus_gen_get_type),
87183882Snwhitehorn
88183882Snwhitehorn	{ 0, 0 }
89183882Snwhitehorn};
90183882Snwhitehorn
91183882Snwhitehornstruct ofw_pcibus_devinfo {
92183882Snwhitehorn	struct pci_devinfo	opd_dinfo;
93183882Snwhitehorn	struct ofw_bus_devinfo	opd_obdinfo;
94183882Snwhitehorn};
95183882Snwhitehorn
96183882Snwhitehornstatic devclass_t pci_devclass;
97183882Snwhitehorn
98232403SjhbDEFINE_CLASS_1(pci, ofw_pcibus_driver, ofw_pcibus_methods,
99232403Sjhb    sizeof(struct pci_softc), pci_driver);
100183882SnwhitehornDRIVER_MODULE(ofw_pcibus, pcib, ofw_pcibus_driver, pci_devclass, 0, 0);
101183882SnwhitehornMODULE_VERSION(ofw_pcibus, 1);
102183882SnwhitehornMODULE_DEPEND(ofw_pcibus, pci, 1, 1, 1);
103183882Snwhitehorn
104255615Snwhitehornstatic int ofw_devices_only = 0;
105255615SnwhitehornTUNABLE_INT("hw.pci.ofw_devices_only", &ofw_devices_only);
106255615Snwhitehorn
107183882Snwhitehornstatic int
108183882Snwhitehornofw_pcibus_probe(device_t dev)
109183882Snwhitehorn{
110186128Snwhitehorn
111233018Snwhitehorn	if (ofw_bus_get_node(dev) == -1)
112183882Snwhitehorn		return (ENXIO);
113183882Snwhitehorn	device_set_desc(dev, "OFW PCI bus");
114183882Snwhitehorn
115255615Snwhitehorn	return (BUS_PROBE_DEFAULT);
116183882Snwhitehorn}
117183882Snwhitehorn
118183882Snwhitehornstatic int
119183882Snwhitehornofw_pcibus_attach(device_t dev)
120183882Snwhitehorn{
121186128Snwhitehorn	u_int busno, domain;
122232403Sjhb	int error;
123186128Snwhitehorn
124232403Sjhb	error = pci_attach_common(dev);
125232403Sjhb	if (error)
126232403Sjhb		return (error);
127186128Snwhitehorn	domain = pcib_get_domain(dev);
128186128Snwhitehorn	busno = pcib_get_bus(dev);
129186128Snwhitehorn
130186128Snwhitehorn	/*
131186128Snwhitehorn	 * Attach those children represented in the device tree.
132186128Snwhitehorn	 */
133186128Snwhitehorn
134186128Snwhitehorn	ofw_pcibus_enum_devtree(dev, domain, busno);
135186128Snwhitehorn
136186128Snwhitehorn	/*
137186128Snwhitehorn	 * We now attach any laggard devices. FDT, for instance, allows
138186128Snwhitehorn	 * the device tree to enumerate only some PCI devices. Apple's
139186128Snwhitehorn	 * OF device tree on some Grackle-based hardware can also miss
140186128Snwhitehorn	 * functions on multi-function cards.
141186128Snwhitehorn	 */
142186128Snwhitehorn
143255615Snwhitehorn	if (!ofw_devices_only)
144255615Snwhitehorn		ofw_pcibus_enum_bus(dev, domain, busno);
145186128Snwhitehorn
146186128Snwhitehorn	return (bus_generic_attach(dev));
147186128Snwhitehorn}
148186128Snwhitehorn
149186128Snwhitehornstatic void
150186128Snwhitehornofw_pcibus_enum_devtree(device_t dev, u_int domain, u_int busno)
151186128Snwhitehorn{
152183882Snwhitehorn	device_t pcib;
153183882Snwhitehorn	struct ofw_pci_register pcir;
154183882Snwhitehorn	struct ofw_pcibus_devinfo *dinfo;
155183882Snwhitehorn	phandle_t node, child;
156186128Snwhitehorn	u_int func, slot;
157186128Snwhitehorn	int intline;
158183882Snwhitehorn
159183882Snwhitehorn	pcib = device_get_parent(dev);
160183882Snwhitehorn	node = ofw_bus_get_node(dev);
161183882Snwhitehorn
162183882Snwhitehorn	for (child = OF_child(node); child != 0; child = OF_peer(child)) {
163183882Snwhitehorn		if (OF_getprop(child, "reg", &pcir, sizeof(pcir)) == -1)
164183882Snwhitehorn			continue;
165183882Snwhitehorn		slot = OFW_PCI_PHYS_HI_DEVICE(pcir.phys_hi);
166183882Snwhitehorn		func = OFW_PCI_PHYS_HI_FUNCTION(pcir.phys_hi);
167183882Snwhitehorn
168183882Snwhitehorn		/* Some OFW device trees contain dupes. */
169183882Snwhitehorn		if (pci_find_dbsf(domain, busno, slot, func) != NULL)
170183882Snwhitehorn			continue;
171183882Snwhitehorn
172186128Snwhitehorn		/*
173186128Snwhitehorn		 * The preset in the intline register is usually bogus.  Reset
174186128Snwhitehorn		 * it such that the PCI code will reroute the interrupt if
175186128Snwhitehorn		 * needed.
176186128Snwhitehorn		 */
177183882Snwhitehorn
178186128Snwhitehorn		intline = PCI_INVALID_IRQ;
179186128Snwhitehorn		if (OF_getproplen(child, "interrupts") > 0)
180186128Snwhitehorn			intline = 0;
181186128Snwhitehorn		PCIB_WRITE_CONFIG(pcib, busno, slot, func, PCIR_INTLINE,
182186128Snwhitehorn		    intline, 1);
183186128Snwhitehorn
184186128Snwhitehorn		/*
185186128Snwhitehorn		 * Now set up the PCI and OFW bus layer devinfo and add it
186186128Snwhitehorn		 * to the PCI bus.
187186128Snwhitehorn		 */
188186128Snwhitehorn
189183882Snwhitehorn		dinfo = (struct ofw_pcibus_devinfo *)pci_read_device(pcib,
190183882Snwhitehorn		    domain, busno, slot, func, sizeof(*dinfo));
191183882Snwhitehorn		if (dinfo == NULL)
192183882Snwhitehorn			continue;
193183882Snwhitehorn		if (ofw_bus_gen_setup_devinfo(&dinfo->opd_obdinfo, child) !=
194183882Snwhitehorn		    0) {
195183882Snwhitehorn			pci_freecfg((struct pci_devinfo *)dinfo);
196183882Snwhitehorn			continue;
197183882Snwhitehorn		}
198183882Snwhitehorn		pci_add_child(dev, (struct pci_devinfo *)dinfo);
199183882Snwhitehorn
200183882Snwhitehorn		/*
201208162Snwhitehorn		 * Some devices don't have an intpin set, but do have
202208162Snwhitehorn		 * interrupts. These are fully specified, and set in the
203186128Snwhitehorn		 * interrupts property, so add that value to the device's
204186128Snwhitehorn		 * resource list.
205208162Snwhitehorn		 */
206208162Snwhitehorn		if (dinfo->opd_dinfo.cfg.intpin == 0) {
207208162Snwhitehorn			ofw_pci_intr_t intr[2];
208208162Snwhitehorn			phandle_t iparent;
209208162Snwhitehorn			int icells;
210183882Snwhitehorn
211186128Snwhitehorn			if (OF_getprop(child, "interrupts", &intr,
212186128Snwhitehorn			    sizeof(intr)) > 0) {
213208162Snwhitehorn				iparent = 0;
214208162Snwhitehorn				icells = 1;
215208162Snwhitehorn				OF_getprop(child, "interrupt-parent", &iparent,
216208162Snwhitehorn				    sizeof(iparent));
217255596Snwhitehorn				if (iparent != 0) {
218255596Snwhitehorn					OF_getprop(OF_xref_phandle(iparent),
219255596Snwhitehorn					    "#interrupt-cells", &icells,
220255596Snwhitehorn					    sizeof(icells));
221218184Smarcel					intr[0] = MAP_IRQ(iparent, intr[0]);
222255596Snwhitehorn				}
223209298Snwhitehorn
224208162Snwhitehorn				if (iparent != 0 && icells > 1) {
225208162Snwhitehorn					powerpc_config_intr(intr[0],
226208162Snwhitehorn					    (intr[1] & 1) ? INTR_TRIGGER_LEVEL :
227208162Snwhitehorn					    INTR_TRIGGER_EDGE,
228209299Snwhitehorn					    INTR_POLARITY_LOW);
229208162Snwhitehorn				}
230209298Snwhitehorn
231208162Snwhitehorn				resource_list_add(&dinfo->opd_dinfo.resources,
232208162Snwhitehorn				    SYS_RES_IRQ, 0, intr[0], intr[0], 1);
233186128Snwhitehorn			}
234208162Snwhitehorn		}
235183882Snwhitehorn	}
236183882Snwhitehorn}
237183882Snwhitehorn
238186128Snwhitehorn/*
239186128Snwhitehorn * The following is an almost exact clone of pci_add_children(), with the
240186128Snwhitehorn * addition that it (a) will not add children that have already been added,
241186128Snwhitehorn * and (b) will set up the OFW devinfo to point to invalid values. This is
242186128Snwhitehorn * to handle non-enumerated PCI children as exist in FDT and on the second
243186128Snwhitehorn * function of the Rage 128 in my Blue & White G3.
244186128Snwhitehorn */
245183882Snwhitehorn
246186128Snwhitehornstatic void
247186128Snwhitehornofw_pcibus_enum_bus(device_t dev, u_int domain, u_int busno)
248183882Snwhitehorn{
249186128Snwhitehorn	device_t pcib;
250183882Snwhitehorn	struct ofw_pcibus_devinfo *dinfo;
251186128Snwhitehorn	int maxslots;
252186128Snwhitehorn	int s, f, pcifunchigh;
253186128Snwhitehorn	uint8_t hdrtype;
254183882Snwhitehorn
255186128Snwhitehorn	pcib = device_get_parent(dev);
256183882Snwhitehorn
257186128Snwhitehorn	maxslots = PCIB_MAXSLOTS(pcib);
258186128Snwhitehorn	for (s = 0; s <= maxslots; s++) {
259186128Snwhitehorn		pcifunchigh = 0;
260186128Snwhitehorn		f = 0;
261186128Snwhitehorn		DELAY(1);
262186128Snwhitehorn		hdrtype = PCIB_READ_CONFIG(pcib, busno, s, f, PCIR_HDRTYPE, 1);
263186128Snwhitehorn		if ((hdrtype & PCIM_HDRTYPE) > PCI_MAXHDRTYPE)
264186128Snwhitehorn			continue;
265186128Snwhitehorn		if (hdrtype & PCIM_MFDEV)
266186128Snwhitehorn			pcifunchigh = PCI_FUNCMAX;
267186128Snwhitehorn		for (f = 0; f <= pcifunchigh; f++) {
268186128Snwhitehorn			/* Filter devices we have already added */
269186128Snwhitehorn			if (pci_find_dbsf(domain, busno, s, f) != NULL)
270186128Snwhitehorn				continue;
271183882Snwhitehorn
272186128Snwhitehorn			dinfo = (struct ofw_pcibus_devinfo *)pci_read_device(
273186128Snwhitehorn			    pcib, domain, busno, s, f, sizeof(*dinfo));
274209299Snwhitehorn			if (dinfo == NULL)
275209299Snwhitehorn				continue;
276183882Snwhitehorn
277209299Snwhitehorn			dinfo->opd_obdinfo.obd_node = -1;
278209299Snwhitehorn
279209299Snwhitehorn			dinfo->opd_obdinfo.obd_name = NULL;
280209299Snwhitehorn			dinfo->opd_obdinfo.obd_compat = NULL;
281209299Snwhitehorn			dinfo->opd_obdinfo.obd_type = NULL;
282209299Snwhitehorn			dinfo->opd_obdinfo.obd_model = NULL;
283209299Snwhitehorn
284209299Snwhitehorn			/*
285209299Snwhitehorn			 * For non OFW-devices, don't believe 0
286209299Snwhitehorn			 * for an interrupt.
287209299Snwhitehorn			 */
288209299Snwhitehorn			if (dinfo->opd_dinfo.cfg.intline == 0) {
289209299Snwhitehorn				dinfo->opd_dinfo.cfg.intline = PCI_INVALID_IRQ;
290209299Snwhitehorn				PCIB_WRITE_CONFIG(pcib, busno, s, f,
291209299Snwhitehorn				    PCIR_INTLINE, PCI_INVALID_IRQ, 1);
292186128Snwhitehorn			}
293209299Snwhitehorn
294209299Snwhitehorn			pci_add_child(dev, (struct pci_devinfo *)dinfo);
295183882Snwhitehorn		}
296183882Snwhitehorn	}
297183882Snwhitehorn}
298183882Snwhitehorn
299183882Snwhitehornstatic int
300186128Snwhitehornofw_pcibus_child_pnpinfo_str_method(device_t cbdev, device_t child, char *buf,
301186128Snwhitehorn    size_t buflen)
302183882Snwhitehorn{
303186128Snwhitehorn	pci_child_pnpinfo_str_method(cbdev, child, buf, buflen);
304183882Snwhitehorn
305186128Snwhitehorn	if (ofw_bus_get_node(child) != -1)  {
306186128Snwhitehorn		strlcat(buf, " ", buflen); /* Separate info */
307186128Snwhitehorn		ofw_bus_gen_child_pnpinfo_str(cbdev, child, buf, buflen);
308186128Snwhitehorn	}
309183882Snwhitehorn
310186128Snwhitehorn	return (0);
311183882Snwhitehorn}
312186128Snwhitehorn
313183882Snwhitehornstatic int
314186128Snwhitehornofw_pcibus_assign_interrupt(device_t dev, device_t child)
315183882Snwhitehorn{
316186128Snwhitehorn	ofw_pci_intr_t intr;
317209298Snwhitehorn	phandle_t node, iparent;
318186128Snwhitehorn	int isz;
319183882Snwhitehorn
320186128Snwhitehorn	node = ofw_bus_get_node(child);
321183882Snwhitehorn
322186128Snwhitehorn	if (node == -1) {
323186128Snwhitehorn		/* Non-firmware enumerated child, use standard routing */
324186128Snwhitehorn
325183882Snwhitehorn		/*
326186128Snwhitehorn		 * XXX: Right now we don't have anything sensible to do here,
327209298Snwhitehorn		 * since the ofw_imap stuff relies on nodes having a reg
328209298Snwhitehorn		 * property. There exist ways around this, so the ePAPR
329186128Snwhitehorn		 * spec will need to be studied.
330183882Snwhitehorn		 */
331183882Snwhitehorn
332209299Snwhitehorn		return (PCI_INVALID_IRQ);
333183882Snwhitehorn
334186128Snwhitehorn#ifdef NOTYET
335186128Snwhitehorn		intr = pci_get_intpin(child);
336186128Snwhitehorn		return (PCIB_ROUTE_INTERRUPT(device_get_parent(dev), child,
337186128Snwhitehorn		    intr));
338186128Snwhitehorn#endif
339183882Snwhitehorn	}
340186128Snwhitehorn
341183882Snwhitehorn	/*
342209298Snwhitehorn	 * Try to determine the node's interrupt parent so we know which
343209298Snwhitehorn	 * PIC to use.
344209298Snwhitehorn	 */
345209298Snwhitehorn
346209298Snwhitehorn	iparent = -1;
347209298Snwhitehorn	if (OF_getprop(node, "interrupt-parent", &iparent, sizeof(iparent)) < 0)
348209298Snwhitehorn		iparent = -1;
349209298Snwhitehorn
350209298Snwhitehorn	/*
351186128Snwhitehorn	 * Any AAPL,interrupts property gets priority and is
352186128Snwhitehorn	 * fully specified (i.e. does not need routing)
353183882Snwhitehorn	 */
354183882Snwhitehorn
355186128Snwhitehorn	isz = OF_getprop(node, "AAPL,interrupts", &intr, sizeof(intr));
356209298Snwhitehorn	if (isz == sizeof(intr))
357218184Smarcel		return ((iparent == -1) ? intr : MAP_IRQ(iparent, intr));
358183882Snwhitehorn
359186128Snwhitehorn	isz = OF_getprop(node, "interrupts", &intr, sizeof(intr));
360209298Snwhitehorn	if (isz == sizeof(intr)) {
361209298Snwhitehorn		if (iparent != -1)
362218184Smarcel			intr = MAP_IRQ(iparent, intr);
363209298Snwhitehorn	} else {
364209298Snwhitehorn		/* No property: our best guess is the intpin. */
365186128Snwhitehorn		intr = pci_get_intpin(child);
366183882Snwhitehorn	}
367183882Snwhitehorn
368183882Snwhitehorn	/*
369186128Snwhitehorn	 * If we got intr from a property, it may or may not be an intpin.
370186128Snwhitehorn	 * For on-board devices, it frequently is not, and is completely out
371186128Snwhitehorn	 * of the valid intpin range.  For PCI slots, it hopefully is,
372186128Snwhitehorn	 * otherwise we will have trouble interfacing with non-OFW buses
373186128Snwhitehorn	 * such as cardbus.
374186128Snwhitehorn	 * Since we cannot tell which it is without violating layering, we
375186128Snwhitehorn	 * will always use the route_interrupt method, and treat exceptions
376186128Snwhitehorn	 * on the level they become apparent.
377183882Snwhitehorn	 */
378186128Snwhitehorn	return (PCIB_ROUTE_INTERRUPT(device_get_parent(dev), child, intr));
379186128Snwhitehorn}
380183882Snwhitehorn
381186128Snwhitehornstatic const struct ofw_bus_devinfo *
382186128Snwhitehornofw_pcibus_get_devinfo(device_t bus, device_t dev)
383186128Snwhitehorn{
384186128Snwhitehorn	struct ofw_pcibus_devinfo *dinfo;
385183882Snwhitehorn
386186128Snwhitehorn	dinfo = device_get_ivars(dev);
387186128Snwhitehorn	return (&dinfo->opd_obdinfo);
388183882Snwhitehorn}
389183882Snwhitehorn
390