ofw_pci.c revision 90613
1/*
2 * Copyright (c) 1999, 2000 Matthew R. Green
3 * All rights reserved.
4 * Copyright 2001 by Thomas Moestl <tmm@FreeBSD.org>.  All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 *    notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 *    notice, this list of conditions and the following disclaimer in the
13 *    documentation and/or other materials provided with the distribution.
14 * 3. The name of the author may not be used to endorse or promote products
15 *    derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
22 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
23 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
24 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
25 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 * SUCH DAMAGE.
28 *
29 *	from: NetBSD: psycho.c,v 1.35 2001/09/10 16:17:06 eeh Exp
30 *
31 * $FreeBSD: head/sys/sparc64/pci/ofw_pci.c 90613 2002-02-13 15:44:58Z tmm $
32 */
33
34#include "opt_ofw_pci.h"
35
36#include <sys/param.h>
37#include <sys/kernel.h>
38#include <sys/systm.h>
39#include <sys/bus.h>
40
41#include <dev/pci/pcivar.h>
42#include <dev/pci/pcireg.h>
43
44#include <dev/ofw/ofw_pci.h>
45#include <dev/ofw/openfirm.h>
46
47#include <sparc64/pci/ofw_pci.h>
48
49#include <machine/ofw_bus.h>
50
51#include "pcib_if.h"
52
53/*
54 * Find the interrupt-map properties for a node. This might not be a property
55 * of the parent, because there may be bridges in between, so go up through the
56 * tree to find it.
57 * This seems to be only needed for PCI systems, so it has not been moved to
58 * ofw_bus.c
59 */
60int
61ofw_pci_find_imap(phandle_t node, struct ofw_pci_imap **imap,
62    struct ofw_pci_imap_msk *imapmsk)
63{
64	int nimap;
65
66	nimap = -1;
67	while ((node = OF_parent(node)) != 0) {
68		if ((nimap = OF_getprop_alloc(node, "interrupt-map",
69		    sizeof(**imap), (void **)imap)) == -1 ||
70		    OF_getprop(node, "interrupt-map-mask",
71		    imapmsk, sizeof(*imapmsk)) == -1) {
72			if (*imap != NULL) {
73				free(*imap, M_OFWPROP);
74				*imap = NULL;
75			}
76			nimap = -1;
77		} else
78			break;
79	}
80	return (nimap);
81}
82
83/*
84 * Route an interrupt using the firmware nodes. Returns 255 for interrupts
85 * that cannot be routed (suitable for the PCI code).
86 */
87int
88ofw_pci_route_intr2(int intr, struct ofw_pci_register *pcir,
89    struct ofw_pci_imap *imap, int nimap, struct ofw_pci_imap_msk *imapmsk)
90{
91	char regm[12];
92	int cintr;
93
94	cintr = ofw_bus_route_intr(intr, pcir, sizeof(*pcir), 12, 1, imap,
95	    nimap, imapmsk, regm);
96	if (cintr == -1)
97		return (255);
98	else
99		return (cintr);
100}
101
102int
103ofw_pci_route_intr(phandle_t node, struct ofw_pci_register *pcir,
104    struct ofw_pci_imap *intrmap, int nintrmap,
105    struct ofw_pci_imap_msk *intrmapmsk)
106{
107	int intr;
108
109	if (OF_getprop(node, "interrupts", &intr, sizeof(intr)) == -1)
110		return (255);
111
112	return (ofw_pci_route_intr2(intr, pcir, intrmap, nintrmap, intrmapmsk));
113}
114
115#define	OFW_PCI_PCIBUS	"pci"
116/*
117 * Walk the PCI bus hierarchy, starting with the root PCI bus and descending
118 * through bridges, and initialize the interrupt line configuration registers
119 * of attached devices using firmware information.
120 */
121void
122ofw_pci_init_intr(device_t dev, phandle_t bus, struct ofw_pci_imap *intrmap,
123    int nintrmap, struct ofw_pci_imap_msk *intrmapmsk)
124{
125	struct ofw_pci_imap_msk lintrmapmsk;
126	struct ofw_pci_register pcir;
127	phandle_t node;
128	char type[32];
129	int intr;
130	int freemap;
131
132	if ((node = OF_child(bus)) == 0)
133		return;
134	freemap = 0;
135	do {
136		if (node == -1)
137			panic("ofw_pci_init_intr: OF_child failed");
138		if (OF_getprop(node, "device_type", type, sizeof(type)) == -1)
139			type[0] = '\0';
140		else
141			type[sizeof(type) - 1] = '\0';
142		if (strcmp(type, OFW_PCI_PCIBUS) == 0) {
143			/*
144			 * This is a pci-pci bridge, recurse to initialize the
145			 * child bus. The hierarchy is usually at most 2 levels
146			 * deep, so recursion is feasible.
147			 */
148#ifdef OFW_PCI_DEBUG
149			device_printf(dev, __func__": descending to "
150			    "subordinate PCI bus\n");
151#endif
152			ofw_pci_init_intr(dev, node, NULL, 0, NULL);
153		} else {
154			if (OF_getprop(node, "reg", &pcir, sizeof(pcir)) == -1)
155				panic("ofw_pci_route_intr: OF_getprop failed");
156			/*
157			 * If we didn't get interrupt map properties passed,
158			 * try to find them now. On some systems, buses that
159			 * have no non-bridge children have no such properties,
160			 * so only try to find them at need.
161			 */
162			if (intrmap == NULL) {
163				nintrmap = OF_getprop_alloc(bus,
164				    "interrupt-map", sizeof(*intrmap),
165				    (void **)&intrmap);
166				if (nintrmap == -1 ||
167				    OF_getprop(bus, "interrupt-map-mask",
168				    &lintrmapmsk, sizeof(lintrmapmsk)) == -1) {
169					printf("ofw_pci_init_intr: could not get "
170					    "interrupt map properties\n");
171					if (nintrmap != -1)
172						free(intrmap, M_OFWPROP);
173					return;
174				}
175				intrmapmsk = &lintrmapmsk;
176				freemap = 1;
177			}
178			if ((intr = ofw_pci_route_intr(node, &pcir, intrmap,
179			    nintrmap, intrmapmsk)) != 255) {
180#ifdef OFW_PCI_DEBUG
181				device_printf(dev, __func__": mapping intr for "
182				    "%d/%d/%d to %d (preset was %d)\n",
183				    OFW_PCI_PHYS_HI_BUS(pcir.phys_hi),
184				    OFW_PCI_PHYS_HI_DEVICE(pcir.phys_hi),
185				    OFW_PCI_PHYS_HI_FUNCTION(pcir.phys_hi),
186				    intr,
187				    (int)PCIB_READ_CONFIG(dev,
188					OFW_PCI_PHYS_HI_BUS(pcir.phys_hi),
189					OFW_PCI_PHYS_HI_DEVICE(pcir.phys_hi),
190					OFW_PCI_PHYS_HI_FUNCTION(pcir.phys_hi),
191					PCIR_INTLINE, 1));
192
193#endif /* OFW_PCI_DEBUG */
194				PCIB_WRITE_CONFIG(dev,
195				    OFW_PCI_PHYS_HI_BUS(pcir.phys_hi),
196				    OFW_PCI_PHYS_HI_DEVICE(pcir.phys_hi),
197				    OFW_PCI_PHYS_HI_FUNCTION(pcir.phys_hi),
198				    PCIR_INTLINE, intr, 1);
199			} else {
200#ifdef OFW_PCI_DEBUG
201				device_printf(dev, __func__": no interrupt "
202				    "mapping found for %d/%d/%d (preset %d)\n",
203				    OFW_PCI_PHYS_HI_BUS(pcir.phys_hi),
204				    OFW_PCI_PHYS_HI_DEVICE(pcir.phys_hi),
205				    OFW_PCI_PHYS_HI_FUNCTION(pcir.phys_hi),
206				    (int)PCIB_READ_CONFIG(dev,
207					OFW_PCI_PHYS_HI_BUS(pcir.phys_hi),
208					OFW_PCI_PHYS_HI_DEVICE(pcir.phys_hi),
209					OFW_PCI_PHYS_HI_FUNCTION(pcir.phys_hi),
210					PCIR_INTLINE, 1));
211#endif /* OFW_PCI_DEBUG */
212				/* The firmware initializes to 0 instead 255 */
213				PCIB_WRITE_CONFIG(dev,
214				    OFW_PCI_PHYS_HI_BUS(pcir.phys_hi),
215				    OFW_PCI_PHYS_HI_DEVICE(pcir.phys_hi),
216				    OFW_PCI_PHYS_HI_FUNCTION(pcir.phys_hi),
217				    PCIR_INTLINE, 255, 1);
218			}
219		}
220	} while ((node = OF_peer(node)) != 0);
221	if (freemap)
222		free(intrmap, M_OFWPROP);
223}
224
225phandle_t
226ofw_pci_find_node(int bus, int slot, int func)
227{
228	phandle_t node, bnode, parent;
229	struct ofw_pci_register pcir;
230	int br[2];
231	char name[16];
232
233	/* 1. Try to find the bus in question. */
234	bnode = 0;
235	name[sizeof(name) - 1] = '\0';
236	parent = OF_peer(0);
237	node = OF_child(parent);
238	while (node != 0 && node != -1) {
239		if (OF_getprop(node, "name", name, sizeof(name) - 1) != -1 &&
240		    strcmp(name, "pci") == 0 &&
241		    OF_getprop(node, "bus-range", br, sizeof(br)) != -1) {
242			/* Found the bus? */
243			if (bus == br[0]) {
244				bnode = node;
245				break;
246			}
247			/* Need to descend? */
248			if (bus > br[0] && bus <= br[1]) {
249				parent = node;
250				node = OF_child(node);
251				continue;
252			}
253		}
254		node = OF_peer(node);
255	}
256	if (bnode == 0)
257		return (0);
258	for (node = OF_child(bnode); node != 0 && node != -1;
259	     node = OF_peer(node)) {
260		if (OF_getprop(node, "reg", &pcir, sizeof(pcir)) == -1)
261			continue;
262		if (OFW_PCI_PHYS_HI_DEVICE(pcir.phys_hi) == slot &&
263		    OFW_PCI_PHYS_HI_FUNCTION(pcir.phys_hi) == func) {
264			if (OFW_PCI_PHYS_HI_BUS(pcir.phys_hi) != bus)
265				panic("ofw_pci_find_node: bus number mismatch");
266			return (node);
267		}
268	}
269	return (0);
270}
271