1 /* $NetBSD: pci_machdep_ofw.c,v 1.22 2017/06/01 02:45:07 chs 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.22 2017/06/01 02:45:07 chs 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/kmem.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 snprintf(key, sizeof(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 snprintf(key, sizeof(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 snprintf(key, sizeof(key), "pin-%c", 'A' + (pin-1));
297 prop_dictionary_set(sub, key, intr_num);
298 prop_object_release(intr_num);
299 snprintf(key, sizeof(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 snprintf(key, sizeof(key), "pin-%c", 'A' + (pin-1));
335 prop_dictionary_set(sub, key, intr_num);
336 prop_object_release(intr_num);
337 snprintf(key, sizeof(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", ®, 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, pin, line, dev, origdev, func, i;
379 char key[20];
380
381 pin = pa->pa_intrpin;
382 line = pa->pa_intrline;
383 busno = pa->pa_bus;
384 origdev = dev = pa->pa_device;
385 func = pa->pa_function;
386 i = 0;
387
388 pbi = SIMPLEQ_FIRST(&pa->pa_pc->pc_pbi);
389 while (busno--)
390 pbi = SIMPLEQ_NEXT(pbi, next);
391 KASSERT(pbi != NULL);
392
393 dict = prop_dictionary_get(pbi->pbi_properties, "ofw-pci-intrmap");
394
395 if (dict != NULL)
396 i = prop_dictionary_count(dict);
397
398 if (dict == NULL || i == 0) {
399 /* We have an unmapped bus, now it gets hard */
400 pbus = prop_dictionary_get(pbi->pbi_properties,
401 "ofw-pcibus-parent");
402 if (pbus == NULL)
403 goto bad;
404 busno = prop_number_integer_value(pbus);
405 pbus = prop_dictionary_get(pbi->pbi_properties,
406 "ofw-pcibus-rawdevnum");
407 dev = prop_number_integer_value(pbus);
408
409 /* now that we know the parent bus, we need to find its pbi */
410 pbi = SIMPLEQ_FIRST(&pa->pa_pc->pc_pbi);
411 while (busno--)
412 pbi = SIMPLEQ_NEXT(pbi, next);
413 KASSERT(pbi != NULL);
414
415 /* swizzle the pin */
416 pin = ((pin + origdev - 1) & 3) + 1;
417
418 /* now we have the pbi, ask for dict again */
419 dict = prop_dictionary_get(pbi->pbi_properties,
420 "ofw-pci-intrmap");
421 if (dict == NULL)
422 goto bad;
423 }
424
425 /* No IRQ used. */
426 if (pin == 0)
427 goto bad;
428 if (pin > 4) {
429 aprint_error("pci_intr_map: bad interrupt pin %d\n", pin);
430 goto bad;
431 }
432
433 snprintf(key, sizeof(key), "devfunc-%d", dev*0x8 + func);
434 devsub = prop_dictionary_get(dict, key);
435 if (devsub == NULL)
436 goto bad;
437 snprintf(key, sizeof(key), "pin-%c", 'A' + (pin-1));
438 pinsub = prop_dictionary_get(devsub, key);
439 if (pinsub == NULL)
440 goto bad;
441 line = prop_number_integer_value(pinsub);
442
443 if (line == 0 || line == 255) {
444 aprint_error("pci_intr_map: no mapping for pin %c\n",'@' + pin);
445 goto bad;
446 }
447
448 *ihp = line;
449 return 0;
450
451 bad:
452 *ihp = -1;
453 return 1;
454 }
455
456 int
457 genofw_pci_conf_hook(void *v, int bus, int dev, int func, pcireg_t id)
458 {
459 pci_chipset_tag_t pct = v;
460 struct genppc_pci_chipset_businfo *pbi;
461 prop_number_t pbus;
462 pcitag_t tag;
463 pcireg_t class;
464 int node;
465
466 /* We have already mapped MPIC's if we have them, so leave them alone */
467 if (PCI_VENDOR(id) == PCI_VENDOR_IBM &&
468 PCI_PRODUCT(id) == PCI_PRODUCT_IBM_MPIC2)
469 return 0;
470
471 if (PCI_VENDOR(id) == PCI_VENDOR_IBM &&
472 PCI_PRODUCT(id) == PCI_PRODUCT_IBM_MPIC)
473 return 0;
474
475 /* I highly doubt there are any CHRP ravens, but just in case */
476 if (PCI_VENDOR(id) == PCI_VENDOR_MOT &&
477 PCI_PRODUCT(id) == PCI_PRODUCT_MOT_RAVEN)
478 return (PCI_CONF_ALL & ~PCI_CONF_MAP_MEM);
479
480 /*
481 * Pegasos2 specific stuff.
482 */
483 if (strncmp(model_name, "Pegasos2", 8) == 0) {
484
485 /* never reconfigure the MV64361 host bridge */
486 if (PCI_VENDOR(id) == PCI_VENDOR_MARVELL &&
487 PCI_PRODUCT(id) == PCI_PRODUCT_MARVELL_MV64360)
488 return 0;
489
490 /* we want to leave viaide(4) alone */
491 if (PCI_VENDOR(id) == PCI_VENDOR_VIATECH &&
492 PCI_PRODUCT(id) == PCI_PRODUCT_VIATECH_VT82C586A_IDE)
493 return 0;
494
495 /* leave the audio IO alone */
496 if (PCI_VENDOR(id) == PCI_VENDOR_VIATECH &&
497 PCI_PRODUCT(id) == PCI_PRODUCT_VIATECH_VT82C686A_AC97)
498 return (PCI_CONF_ALL & ~PCI_CONF_MAP_IO);
499
500 }
501
502 tag = pci_make_tag(pct, bus, dev, func);
503 class = pci_conf_read(pct, tag, PCI_CLASS_REG);
504
505 /* leave video cards alone */
506 if (PCI_CLASS(class) == PCI_CLASS_DISPLAY)
507 return 0;
508
509 /* NOTE, all device specific stuff must be above this line */
510 /* don't do this on the primary host bridge */
511 if (bus == 0 && dev == 0 && func == 0)
512 return PCI_CONF_DEFAULT;
513
514 /*
515 * PCI bridges have special needs. We need to discover where they
516 * came from, and wire them appropriately.
517 */
518 if (PCI_CLASS(class) == PCI_CLASS_BRIDGE &&
519 PCI_SUBCLASS(class) == PCI_SUBCLASS_BRIDGE_PCI) {
520 pbi = kmem_alloc(sizeof(*pbi), KM_SLEEP);
521 pbi->pbi_properties = prop_dictionary_create();
522 KASSERT(pbi->pbi_properties != NULL);
523 node = genofw_find_node_by_devfunc(pct->pc_node, bus, dev,
524 func);
525 if (node == -1) {
526 aprint_error("Cannot find node for device "
527 "bus %d dev %d func %d\n", bus, dev, func);
528 prop_object_release(pbi->pbi_properties);
529 kmem_free(pbi, sizeof(*pbi));
530 return (PCI_CONF_DEFAULT);
531 }
532 genofw_setup_pciintr_map((void *)pct, pbi, node);
533
534 /* record the parent bus, and the parent device number */
535 pbus = prop_number_create_integer(bus);
536 prop_dictionary_set(pbi->pbi_properties, "ofw-pcibus-parent",
537 pbus);
538 prop_object_release(pbus);
539 pbus = prop_number_create_integer(dev);
540 prop_dictionary_set(pbi->pbi_properties, "ofw-pcibus-rawdevnum",
541 pbus);
542 prop_object_release(pbus);
543
544 SIMPLEQ_INSERT_TAIL(&pct->pc_pbi, pbi, next);
545 }
546
547 return (PCI_CONF_DEFAULT);
548 }
Cache object: a1bcb98986c29c4caf0ad6ee872263b1
|