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