xref: /netbsd-src/sys/arch/powerpc/pci/pci_machdep_ofw.c (revision 8b0f9554ff8762542c4defc4f70e1eb76fb508fa)
1 /* $NetBSD: pci_machdep_ofw.c,v 1.6 2007/11/05 15:49:03 garbled Exp $ */
2 
3 /*-
4  * Copyright (c) 2007 The NetBSD Foundation, Inc.
5  * All rights reserved.
6  *
7  * This code is derived from software contributed to The NetBSD Foundation
8  * by Tim Rightnour
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  * 3. All advertising materials mentioning features or use of this software
19  *    must display the following acknowledgement:
20  *        This product includes software developed by the NetBSD
21  *        Foundation, Inc. and its contributors.
22  * 4. Neither the name of The NetBSD Foundation nor the names of its
23  *    contributors may be used to endorse or promote products derived
24  *    from this software without specific prior written permission.
25  *
26  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
27  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
28  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
29  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
30  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
31  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
32  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
33  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
34  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
35  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
36  * POSSIBILITY OF SUCH DAMAGE.
37  */
38 
39 /*
40  * Generic OFW routines for pci_machdep
41  */
42 
43 #include <sys/cdefs.h>
44 __KERNEL_RCSID(0, "$NetBSD: pci_machdep_ofw.c,v 1.6 2007/11/05 15:49:03 garbled Exp $");
45 
46 #include <sys/types.h>
47 #include <sys/param.h>
48 #include <sys/time.h>
49 #include <sys/systm.h>
50 #include <sys/errno.h>
51 #include <sys/device.h>
52 #include <sys/malloc.h>
53 
54 #include <uvm/uvm_extern.h>
55 
56 #include <machine/bus.h>
57 
58 #include <machine/autoconf.h>
59 #include <machine/pio.h>
60 #include <machine/intr.h>
61 
62 #include <dev/pci/pcivar.h>
63 #include <dev/pci/pcireg.h>
64 #include <dev/pci/ppbreg.h>
65 #include <dev/pci/pcidevs.h>
66 #include <dev/pci/pciconf.h>
67 
68 #include <dev/ofw/openfirm.h>
69 #include <dev/ofw/ofw_pci.h>
70 
71 pcitag_t genppc_pci_indirect_make_tag(void *, int, int, int);
72 void genppc_pci_indirect_decompose_tag(void *, pcitag_t, int *, int *, int *);
73 
74 ofw_pic_node_t picnodes[8];
75 int nrofpics = 0;
76 
77 int
78 genofw_find_picnode(int node)
79 {
80 	int i;
81 
82 	for (i = 0; i < 8; i++)
83 		if (node == picnodes[i].node)
84 			return i;
85 	return -1;
86 }
87 
88 void
89 genofw_find_ofpics(int startnode)
90 {
91 	int node, iparent, child, iranges[2];
92 	char name[32];
93 
94 	for (node = startnode; node; node = OF_peer(node)) {
95 		if ((child = OF_child(node)) != 0)
96 			genofw_find_ofpics(child);
97 		memset(name, 0, sizeof(name));
98 		if (OF_getprop(node, "name", name, sizeof(name)) == -1)
99 			continue;
100 		if (strncmp(name, "interrupt-controller", 20) == 0)
101 			goto foundic;
102 
103 		if (OF_getprop(node, "interrupt-controller", name,
104 		    sizeof(name)) > -1)
105 			goto foundic;
106 
107 		if (OF_getprop(node, "device_type", name, sizeof(name)) == -1)
108 			continue;
109 		if (strncmp(name, "interrupt-controller", 20) == 0)
110 			goto foundic;
111 
112 		/* if we didn't find one, skip to the next */
113 		continue;
114 foundic:
115 		picnodes[nrofpics].node = node;
116 		if (OF_getprop(node, "interrupt-parent", &iparent,
117 		    sizeof(iparent)) == sizeof(iparent))
118 			picnodes[nrofpics].parent = iparent;
119 		if (OF_getprop(node, "#interrupt-cells", &iparent,
120 		    sizeof(iparent)) == sizeof(iparent))
121 			picnodes[nrofpics].cells = iparent;
122 		else
123 			picnodes[nrofpics].cells = 1;
124 		if (OF_getprop(node, "interrupt-ranges", iranges,
125 		    sizeof(int)*2) == sizeof(int)*2)
126 			picnodes[nrofpics].intrs = iranges[1];
127 		else
128 			picnodes[nrofpics].intrs = 16;
129 		if (nrofpics > 0)
130 			picnodes[nrofpics].offset = picnodes[nrofpics-1].offset
131 			    + picnodes[nrofpics-1].intrs;
132 		else
133 			picnodes[nrofpics].offset = 0;
134 		OF_getprop(node, "device_type", name, sizeof(name));
135 		if (strcmp(name, "open-pic") == 0)
136 			picnodes[nrofpics].type = PICNODE_TYPE_OPENPIC;
137 		if (strcmp(name, "interrupt-controller") == 0) {
138 			OF_getprop(node, "compatible", name, sizeof(name));
139 			if (strcmp(name, "heathrow") != 0)
140 				picnodes[nrofpics].type = PICNODE_TYPE_HEATHROW;
141 			if (strcmp(name, "chrp,iic") != 0)
142 				picnodes[nrofpics].type = PICNODE_TYPE_8259;
143 		}
144 		if (strlen(name) == 0) {
145 			/* probably a Pegasos, assume 8259 */
146 			picnodes[nrofpics].type = PICNODE_TYPE_8259;
147 		}
148 		nrofpics++;
149 	}
150 }
151 
152 /* Fix up the various picnode offsets */
153 void
154 genofw_fixup_picnode_offsets(void)
155 {
156 	int i, curoff;
157 
158 	curoff=0;
159 
160 	for (i=0; i < nrofpics; i++) {
161 		if (picnodes[i].type == PICNODE_TYPE_8259) {
162 			picnodes[i].offset = 0;
163 			curoff = picnodes[i].intrs;
164 		}
165 	}
166 	for (i=0; i < nrofpics; i++) {
167 		/* now skip the 8259 */
168 		if (picnodes[i].type == PICNODE_TYPE_8259)
169 			continue;
170 		picnodes[i].offset = curoff;
171 		curoff += picnodes[i].intrs;
172 	}
173 }
174 
175 /* we are given a pci devnode, and dig from there */
176 void
177 genofw_setup_pciintr_map(void *v, struct genppc_pci_chipset_businfo *pbi,
178     int pcinode)
179 {
180 	int node;
181 	u_int32_t map[160];
182 	int parent, len;
183 	int curdev, foundirqs=0;
184 	int i, reclen, nrofpcidevs=0;
185 	u_int32_t acells, icells, pcells;
186 	prop_dictionary_t dict;
187 	prop_dictionary_t sub=0;
188 	pci_chipset_tag_t pc = (pci_chipset_tag_t)v;
189 
190 	len = OF_getprop(pcinode, "interrupt-map", map, sizeof(map));
191 	if (len == -1)
192 		goto nomap;
193 
194 	if (OF_getprop(pcinode, "#address-cells", &acells,
195 	    sizeof(acells)) == -1)
196 		acells = 1;
197 	if (OF_getprop(pcinode, "#interrupt-cells", &icells,
198 	    sizeof(icells)) == -1)
199 		icells = 1;
200 
201 	parent = map[acells+icells+1];
202 	if (OF_getprop(parent, "#interrupt-cells", &pcells,
203 	    sizeof(pcells)) == -1)
204 		pcells = 1;
205 
206 	reclen = acells+pcells+icells+1;
207 	nrofpcidevs = len / (reclen * sizeof(int));
208 
209 	dict = prop_dictionary_create_with_capacity(nrofpcidevs*2);
210 	KASSERT(dict != NULL);
211 
212 	curdev = -1;
213 	prop_dictionary_set(pbi->pbi_properties, "ofw-pci-intrmap", dict);
214 	for (i = 0; i < nrofpcidevs; i++) {
215 		prop_number_t intr_num;
216 		int dev, pin, pic;
217 		char key[20];
218 
219 		pic = genofw_find_picnode(map[i*reclen + acells + icells]);
220 		KASSERT(pic != -1);
221 		dev = (map[i*reclen] >> 8) / 0x8;
222 		if (curdev != dev)
223 			sub = prop_dictionary_create_with_capacity(4);
224 		pin = map[i*reclen + acells];
225 		intr_num = prop_number_create_integer(map[i*reclen + acells + icells + 1] + picnodes[pic].offset);
226 		sprintf(key, "pin-%c", 'A' + pin);
227 		prop_dictionary_set(sub, key, intr_num);
228 		prop_object_release(intr_num);
229 		/* should we care about level? */
230 
231 		sprintf(key, "devfunc-%d", dev);
232 		prop_dictionary_set(dict, key, sub);
233 		if (curdev != dev) {
234 			prop_object_release(sub);
235 			curdev = dev;
236 		}
237 	}
238 	/* the mapping is complete */
239 	prop_object_release(dict);
240 	return;
241 
242 nomap:
243 	/* so, we have one of those annoying machines that doesn't provide
244 	 * a nice simple map of interrupts.  We get to do this the hard
245 	 * way instead.  Lucky us.
246 	 */
247 	for (node = OF_child(pcinode), nrofpcidevs=0; node;
248 	    node = OF_peer(node))
249 		nrofpcidevs++;
250 	dict = prop_dictionary_create_with_capacity(nrofpcidevs*2);
251 	KASSERT(dict != NULL);
252 	prop_dictionary_set(pbi->pbi_properties, "ofw-pci-intrmap", dict);
253 
254 	for (node = OF_child(pcinode); node; node = OF_peer(node)) {
255 		uint32_t irqs[4], reg[5];
256 		prop_number_t intr_num;
257 		int dev, pin;
258 		char key[20];
259 
260 		/* walk the bus looking for pci devices and map them */
261 		if (OF_getprop(node, "AAPL,interrupts", irqs, 4) > 0) {
262 			dev = 0;
263 			if (OF_getprop(node, "reg", reg, 5) > 0)
264 				dev = ((reg[0] & 0x0000ff00) >> 8) / 0x8;
265 			else if (OF_getprop(node, "assigned-addresses",
266 				     reg, 5) > 0)
267 				dev = ((reg[0] & 0x0000ff00) >> 8) / 0x8;
268 			if (dev == 0) {
269 				aprint_error("cannot figure out device num "
270 				    "for node 0x%x\n", node);
271 				continue;
272 			}
273 			sub = prop_dictionary_create_with_capacity(4);
274 			if (OF_getprop(node, "interrupts", &pin, 4) < 0)
275 				pin = 1;
276 			intr_num = prop_number_create_integer(irqs[0]);
277 			sprintf(key, "pin-%c", 'A' + pin);
278 			prop_dictionary_set(sub, key, intr_num);
279 			prop_object_release(intr_num);
280 			sprintf(key, "devfunc-%d", dev);
281 			prop_dictionary_set(dict, key, sub);
282 			prop_object_release(sub);
283 			foundirqs++;
284 		}
285 	}
286 	if (foundirqs)
287 		return;
288 
289 	/*
290 	 * If we got this far, we have a super-annoying OFW.
291 	 * They didn't bother to fill in any interrupt properties anywhere,
292 	 * so we pray that they filled in the ones on the pci devices.
293 	 */
294 	for (node = OF_child(pcinode); node; node = OF_peer(node)) {
295 		uint32_t reg[5], irq;
296 		prop_number_t intr_num;
297 		pcitag_t tag;
298 		int dev, pin, func;
299 		char key[20];
300 
301 		if (OF_getprop(node, "reg", reg, 5) > 0) {
302 			dev = ((reg[0] & 0x0000ff00) >> 8) / 0x8;
303 			func = ((reg[0] & 0x0000ff00) >> 8) % 0x8;
304 
305 			tag = pci_make_tag(pc, pc->pc_bus, dev, func);
306 			irq = PCI_INTERRUPT_LINE(pci_conf_read(pc, tag,
307 			    PCI_INTERRUPT_REG));
308 			if (irq == 255)
309 				irq = 0;
310 
311 			sub = prop_dictionary_create_with_capacity(4);
312 			if (OF_getprop(node, "interrupts", &pin, 4) < 0)
313 				pin = 1;
314 			intr_num = prop_number_create_integer(irq);
315 			sprintf(key, "pin-%c", 'A' + (pin-1));
316 			prop_dictionary_set(sub, key, intr_num);
317 			prop_object_release(intr_num);
318 			sprintf(key, "devfunc-%d", dev*0x8 + func);
319 			prop_dictionary_set(dict, key, sub);
320 			prop_object_release(sub);
321 		}
322 	}
323 	aprint_debug("%s\n", prop_dictionary_externalize(pbi->pbi_properties));
324 }
325 
326 int
327 genofw_find_node_by_devfunc(int startnode, int bus, int dev, int func)
328 {
329 	int node, sz, p=0;
330 	uint32_t reg;
331 
332 	for (node = startnode; node; node = p) {
333 		sz = OF_getprop(node, "reg", &reg, sizeof(reg));
334 		if (sz != sizeof(reg))
335 			continue;
336 		if (OFW_PCI_PHYS_HI_BUS(reg) == bus &&
337 		    OFW_PCI_PHYS_HI_DEVICE(reg) == dev &&
338 		    OFW_PCI_PHYS_HI_FUNCTION(reg) == func)
339 			return node;
340 		if ((p = OF_child(node)))
341 			continue;
342 		while (node) {
343 			if ((p = OF_peer(node)))
344 				break;
345 			node = OF_parent(node);
346 		}
347 	}
348 	/* couldn't find it */
349 	return -1;
350 }
351 
352 int
353 genofw_pci_intr_map(struct pci_attach_args *pa, pci_intr_handle_t *ihp)
354 {
355 	struct genppc_pci_chipset_businfo *pbi;
356 	prop_dictionary_t dict, devsub;
357 	prop_object_t pinsub;
358 	prop_number_t pbus;
359 	int busno, bus, pin, line, swiz, dev, origdev, func, i;
360 	char key[20];
361 
362 	pin = pa->pa_intrpin;
363 	line = pa->pa_intrline;
364 	bus = busno = pa->pa_bus;
365 	swiz = pa->pa_intrswiz;
366 	origdev = dev = pa->pa_device;
367 	func = pa->pa_function;
368 	i = 0;
369 
370 	pbi = SIMPLEQ_FIRST(&pa->pa_pc->pc_pbi);
371 	while (busno--)
372 		pbi = SIMPLEQ_NEXT(pbi, next);
373 	KASSERT(pbi != NULL);
374 
375 	dict = prop_dictionary_get(pbi->pbi_properties, "ofw-pci-intrmap");
376 
377 	if (dict != NULL)
378 		i = prop_dictionary_count(dict);
379 
380 	if (dict == NULL || i == 0) {
381 		/* We have an unmapped bus, now it gets hard */
382 		pbus = prop_dictionary_get(pbi->pbi_properties,
383 		    "ofw-pcibus-parent");
384 		if (pbus == NULL)
385 			goto bad;
386 		busno = prop_number_integer_value(pbus);
387 		pbus = prop_dictionary_get(pbi->pbi_properties,
388 		    "ofw-pcibus-rawdevnum");
389 		dev = prop_number_integer_value(pbus);
390 
391 		/* now that we know the parent bus, we need to find it's pbi */
392 		pbi = SIMPLEQ_FIRST(&pa->pa_pc->pc_pbi);
393 		while (busno--)
394 			pbi = SIMPLEQ_NEXT(pbi, next);
395 		KASSERT(pbi != NULL);
396 
397 		/* swizzle the pin */
398 		pin = ((pin + origdev - 1) & 3) + 1;
399 
400 		/* now we have the pbi, ask for dict again */
401 		dict = prop_dictionary_get(pbi->pbi_properties,
402 		    "ofw-pci-intrmap");
403 		if (dict == NULL)
404 			goto bad;
405 	}
406 
407 	/* No IRQ used. */
408 	if (pin == 0)
409 		goto bad;
410 	if (pin > 4) {
411 		aprint_error("pci_intr_map: bad interrupt pin %d\n", pin);
412 		goto bad;
413 	}
414 
415 	sprintf(key, "devfunc-%d", dev*0x8 + func);
416 	devsub = prop_dictionary_get(dict, key);
417 	if (devsub == NULL)
418 		goto bad;
419 	sprintf(key, "pin-%c", 'A' + (pin-1));
420 	pinsub = prop_dictionary_get(devsub, key);
421 	if (pinsub == NULL)
422 		goto bad;
423 	line = prop_number_integer_value(pinsub);
424 
425 	if (line == 0 || line == 255) {
426 		aprint_error("pci_intr_map: no mapping for pin %c\n",'@' + pin);
427 		goto bad;
428 	}
429 
430 	*ihp = line;
431 	return 0;
432 
433 bad:
434 	*ihp = -1;
435 	return 1;
436 }
437 
438 int
439 genofw_pci_conf_hook(pci_chipset_tag_t pct, int bus, int dev, int func,
440 	pcireg_t id)
441 {
442 	struct genppc_pci_chipset_businfo *pbi;
443 	prop_number_t pbus;
444 	pcitag_t tag;
445 	pcireg_t class;
446 	int node;
447 
448 	/* We have already mapped MPIC's if we have them, so leave them alone */
449 	if (PCI_VENDOR(id) == PCI_VENDOR_IBM &&
450 	    PCI_PRODUCT(id) == PCI_PRODUCT_IBM_MPIC2)
451 		return 0;
452 
453 	if (PCI_VENDOR(id) == PCI_VENDOR_IBM &&
454 	    PCI_PRODUCT(id) == PCI_PRODUCT_IBM_MPIC)
455 		return 0;
456 
457 	/* I highly doubt there are any CHRP ravens, but just in case */
458 	if (PCI_VENDOR(id) == PCI_VENDOR_MOT &&
459 	    PCI_PRODUCT(id) == PCI_PRODUCT_MOT_RAVEN)
460 		return (PCI_CONF_ALL & ~PCI_CONF_MAP_MEM);
461 
462 	/* NOTE, all device specific stuff must be above this line */
463 	/* don't do this on the primary host bridge */
464 	if (bus == 0 && dev == 0 && func == 0)
465 		return PCI_CONF_DEFAULT;
466 
467 	tag = genppc_pci_indirect_make_tag(pct, bus, dev, func);
468 	class = genppc_pci_indirect_conf_read(pct, tag, PCI_CLASS_REG);
469 
470 	/*
471 	 * PCI bridges have special needs.  We need to discover where they
472 	 * came from, and wire them appropriately.
473 	 */
474 	if (PCI_CLASS(class) == PCI_CLASS_BRIDGE &&
475 	    PCI_SUBCLASS(class) == PCI_SUBCLASS_BRIDGE_PCI) {
476 		pbi = malloc(sizeof(struct genppc_pci_chipset_businfo),
477 		    M_DEVBUF, M_NOWAIT);
478 		KASSERT(pbi != NULL);
479 		pbi->pbi_properties = prop_dictionary_create();
480 		KASSERT(pbi->pbi_properties != NULL);
481 		node = genofw_find_node_by_devfunc(pct->pc_node, bus, dev,
482 		    func);
483 		if (node == -1) {
484 			aprint_error("Cannot find node for device "
485 			    "bus %d dev %d func %d\n", bus, dev, func);
486 			prop_object_release(pbi->pbi_properties);
487 			free(pbi, M_DEVBUF);
488 			return (PCI_CONF_DEFAULT);
489 		}
490 		genofw_setup_pciintr_map((void *)pct, pbi, node);
491 
492 		/* record the parent bus, and the parent device number */
493 		pbus = prop_number_create_integer(bus);
494 		prop_dictionary_set(pbi->pbi_properties, "ofw-pcibus-parent",
495 		    pbus);
496 		prop_object_release(pbus);
497 		pbus = prop_number_create_integer(dev);
498 		prop_dictionary_set(pbi->pbi_properties, "ofw-pcibus-rawdevnum",
499 		    pbus);
500 		prop_object_release(pbus);
501 
502 		SIMPLEQ_INSERT_TAIL(&pct->pc_pbi, pbi, next);
503 	}
504 
505 	return (PCI_CONF_DEFAULT);
506 }
507