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