pci_machdep_ofw.c revision 1.3 1 /* $NetBSD: pci_machdep_ofw.c,v 1.3 2007/10/25 16:55:51 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.3 2007/10/25 16:55:51 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(struct genppc_pci_chipset_businfo *pbi, int pcinode)
178 {
179 int node;
180 u_int32_t map[160];
181 int parent, len;
182 int curdev;
183 int i, reclen, nrofpcidevs=0;
184 u_int32_t acells, icells, pcells;
185 prop_dictionary_t dict;
186 prop_dictionary_t sub=0;
187
188 len = OF_getprop(pcinode, "interrupt-map", map, sizeof(map));
189 if (len == -1)
190 goto nomap;
191
192 if (OF_getprop(pcinode, "#address-cells", &acells,
193 sizeof(acells)) == -1)
194 acells = 1;
195 if (OF_getprop(pcinode, "#interrupt-cells", &icells,
196 sizeof(icells)) == -1)
197 icells = 1;
198
199 parent = map[acells+icells+1];
200 if (OF_getprop(parent, "#interrupt-cells", &pcells,
201 sizeof(pcells)) == -1)
202 pcells = 1;
203
204 reclen = acells+pcells+icells+1;
205 nrofpcidevs = len / (reclen * sizeof(int));
206
207 dict = prop_dictionary_create_with_capacity(nrofpcidevs*2);
208 KASSERT(dict != NULL);
209
210 curdev = -1;
211 prop_dictionary_set(pbi->pbi_properties, "ofw-pci-intrmap", dict);
212 for (i = 0; i < nrofpcidevs; i++) {
213 prop_number_t intr_num;
214 int dev, pin, pic;
215 char key[20];
216
217 pic = genofw_find_picnode(map[i*reclen + acells + icells]);
218 KASSERT(pic != -1);
219 dev = (map[i*reclen] >> 8) / 0x8;
220 if (curdev != dev)
221 sub = prop_dictionary_create_with_capacity(4);
222 pin = map[i*reclen + acells];
223 intr_num = prop_number_create_integer(map[i*reclen + acells + icells + 1] + picnodes[pic].offset);
224 sprintf(key, "pin-%c", 'A' + pin);
225 prop_dictionary_set(sub, key, intr_num);
226 prop_object_release(intr_num);
227 /* should we care about level? */
228
229 sprintf(key, "devfunc-%d", dev);
230 prop_dictionary_set(dict, key, sub);
231 if (curdev != dev) {
232 prop_object_release(sub);
233 curdev = dev;
234 }
235 }
236 /* the mapping is complete */
237 prop_object_release(dict);
238 return;
239
240 nomap:
241 /* so, we have one of those annoying machines that doesn't provide
242 * a nice simple map of interrupts. We get to do this the hard
243 * way instead. Lucky us.
244 */
245 for (node = OF_child(pcinode), nrofpcidevs=0; node;
246 node = OF_peer(node))
247 nrofpcidevs++;
248 dict = prop_dictionary_create_with_capacity(nrofpcidevs*2);
249 KASSERT(dict != NULL);
250 prop_dictionary_set(pbi->pbi_properties, "ofw-pci-intrmap", dict);
251
252 for (node = OF_child(pcinode); node; node = OF_peer(node)) {
253 uint32_t irqs[4], reg[5];
254 prop_number_t intr_num;
255 int dev, pin;
256 char key[20];
257
258 /* walk the bus looking for pci devices and map them */
259 if (OF_getprop(node, "AAPL,interrupts", irqs, 4) > 0) {
260 dev = 0;
261 if (OF_getprop(node, "reg", reg, 5) > 0)
262 dev = ((reg[0] & 0x0000ff00) >> 8) / 0x8;
263 else if (OF_getprop(node, "assigned-addresses",
264 reg, 5) > 0)
265 dev = ((reg[0] & 0x0000ff00) >> 8) / 0x8;
266 if (dev == 0) {
267 aprint_error("cannot figure out device num "
268 "for node 0x%x\n", node);
269 continue;
270 }
271 sub = prop_dictionary_create_with_capacity(4);
272 if (OF_getprop(node, "interrupts", &pin, 4) < 0)
273 pin = 1;
274 intr_num = prop_number_create_integer(irqs[0]);
275 sprintf(key, "pin-%c", 'A' + pin);
276 prop_dictionary_set(sub, key, intr_num);
277 prop_object_release(intr_num);
278 sprintf(key, "devfunc-%d", dev);
279 prop_dictionary_set(dict, key, sub);
280 prop_object_release(sub);
281 }
282 }
283 aprint_normal("%s\n", prop_dictionary_externalize(pbi->pbi_properties));
284 }
285
286 int
287 genofw_find_node_by_devfunc(int startnode, int bus, int dev, int func)
288 {
289 int node, sz, p=0;
290 uint32_t reg;
291
292 for (node = startnode; node; node = p) {
293 sz = OF_getprop(node, "reg", ®, sizeof(reg));
294 if (sz != sizeof(reg))
295 continue;
296 if (OFW_PCI_PHYS_HI_BUS(reg) == bus &&
297 OFW_PCI_PHYS_HI_DEVICE(reg) == dev &&
298 OFW_PCI_PHYS_HI_FUNCTION(reg) == func)
299 return node;
300 if ((p = OF_child(node)))
301 continue;
302 while (node) {
303 if ((p = OF_peer(node)))
304 break;
305 node = OF_parent(node);
306 }
307 }
308 /* couldn't find it */
309 return -1;
310 }
311
312 int
313 genofw_pci_intr_map(struct pci_attach_args *pa, pci_intr_handle_t *ihp)
314 {
315 struct genppc_pci_chipset_businfo *pbi;
316 prop_dictionary_t dict, devsub;
317 prop_object_t pinsub;
318 prop_number_t pbus;
319 int busno, bus, pin, line, swiz, dev, origdev, i;
320 char key[20];
321
322 pin = pa->pa_intrpin;
323 line = pa->pa_intrline;
324 bus = busno = pa->pa_bus;
325 swiz = pa->pa_intrswiz;
326 origdev = dev = pa->pa_device;
327 i = 0;
328
329 pbi = SIMPLEQ_FIRST(&pa->pa_pc->pc_pbi);
330 while (busno--)
331 pbi = SIMPLEQ_NEXT(pbi, next);
332 KASSERT(pbi != NULL);
333
334 dict = prop_dictionary_get(pbi->pbi_properties, "ofw-pci-intrmap");
335
336 if (dict != NULL)
337 i = prop_dictionary_count(dict);
338
339 if (dict == NULL || i == 0) {
340 /* We have an unmapped bus, now it gets hard */
341 pbus = prop_dictionary_get(pbi->pbi_properties,
342 "ofw-pcibus-parent");
343 if (pbus == NULL)
344 goto bad;
345 busno = prop_number_integer_value(pbus);
346 pbus = prop_dictionary_get(pbi->pbi_properties,
347 "ofw-pcibus-rawdevnum");
348 dev = prop_number_integer_value(pbus);
349
350 /* now that we know the parent bus, we need to find it's pbi */
351 pbi = SIMPLEQ_FIRST(&pa->pa_pc->pc_pbi);
352 while (busno--)
353 pbi = SIMPLEQ_NEXT(pbi, next);
354 KASSERT(pbi != NULL);
355
356 /* swizzle the pin */
357 pin = ((pin + origdev - 1) & 3) + 1;
358
359 /* now we have the pbi, ask for dict again */
360 dict = prop_dictionary_get(pbi->pbi_properties,
361 "ofw-pci-intrmap");
362 if (dict == NULL)
363 goto bad;
364 }
365
366 /* No IRQ used. */
367 if (pin == 0)
368 goto bad;
369 if (pin > 4) {
370 aprint_error("pci_intr_map: bad interrupt pin %d\n", pin);
371 goto bad;
372 }
373
374 sprintf(key, "devfunc-%d", dev);
375 devsub = prop_dictionary_get(dict, key);
376 if (devsub == NULL)
377 goto bad;
378 sprintf(key, "pin-%c", 'A' + (pin-1));
379 pinsub = prop_dictionary_get(devsub, key);
380 if (pinsub == NULL)
381 goto bad;
382 line = prop_number_integer_value(pinsub);
383
384 if (line == 0 || line == 255) {
385 aprint_error("pci_intr_map: no mapping for pin %c\n",'@' + pin);
386 goto bad;
387 }
388
389 *ihp = line;
390 return 0;
391
392 bad:
393 *ihp = -1;
394 return 1;
395 }
396
397 int
398 genofw_pci_conf_hook(pci_chipset_tag_t pct, int bus, int dev, int func,
399 pcireg_t id)
400 {
401 struct genppc_pci_chipset_businfo *pbi;
402 prop_number_t pbus;
403 pcitag_t tag;
404 pcireg_t class;
405 int node;
406
407 /* We have already mapped MPIC's if we have them, so leave them alone */
408 if (PCI_VENDOR(id) == PCI_VENDOR_IBM &&
409 PCI_PRODUCT(id) == PCI_PRODUCT_IBM_MPIC2)
410 return 0;
411
412 if (PCI_VENDOR(id) == PCI_VENDOR_IBM &&
413 PCI_PRODUCT(id) == PCI_PRODUCT_IBM_MPIC)
414 return 0;
415
416 /* I highly doubt there are any CHRP ravens, but just in case */
417 if (PCI_VENDOR(id) == PCI_VENDOR_MOT &&
418 PCI_PRODUCT(id) == PCI_PRODUCT_MOT_RAVEN)
419 return (PCI_CONF_ALL & ~PCI_CONF_MAP_MEM);
420
421 /* NOTE, all device specific stuff must be above this line */
422 /* don't do this on the primary host bridge */
423 if (bus == 0 && dev == 0 && func == 0)
424 return PCI_CONF_DEFAULT;
425
426 tag = genppc_pci_indirect_make_tag(pct, bus, dev, func);
427 class = genppc_pci_indirect_conf_read(pct, tag, PCI_CLASS_REG);
428
429 /*
430 * PCI bridges have special needs. We need to discover where they
431 * came from, and wire them appropriately.
432 */
433 if (PCI_CLASS(class) == PCI_CLASS_BRIDGE &&
434 PCI_SUBCLASS(class) == PCI_SUBCLASS_BRIDGE_PCI) {
435 pbi = malloc(sizeof(struct genppc_pci_chipset_businfo),
436 M_DEVBUF, M_NOWAIT);
437 KASSERT(pbi != NULL);
438 pbi->pbi_properties = prop_dictionary_create();
439 KASSERT(pbi->pbi_properties != NULL);
440 node = genofw_find_node_by_devfunc(pct->pc_node, bus, dev,
441 func);
442 if (node == -1) {
443 aprint_error("Cannot find node for device "
444 "bus %d dev %d func %d\n", bus, dev, func);
445 prop_object_release(pbi->pbi_properties);
446 free(pbi, M_DEVBUF);
447 return (PCI_CONF_DEFAULT);
448 }
449 genofw_setup_pciintr_map(pbi, node);
450
451 /* record the parent bus, and the parent device number */
452 pbus = prop_number_create_integer(bus);
453 prop_dictionary_set(pbi->pbi_properties, "ofw-pcibus-parent",
454 pbus);
455 prop_object_release(pbus);
456 pbus = prop_number_create_integer(dev);
457 prop_dictionary_set(pbi->pbi_properties, "ofw-pcibus-rawdevnum",
458 pbus);
459 prop_object_release(pbus);
460
461 SIMPLEQ_INSERT_TAIL(&pct->pc_pbi, pbi, next);
462 }
463
464 return (PCI_CONF_DEFAULT);
465 }
466