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
genofw_find_picnode(int node)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
genofw_find_ofpics(int startnode)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
genofw_fixup_picnode_offsets(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
genofw_setup_pciintr_map(void * v,struct genppc_pci_chipset_businfo * pbi,int pcinode)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
genofw_find_node_by_devfunc(int startnode,int bus,int dev,int func)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
genofw_pci_intr_map(const struct pci_attach_args * pa,pci_intr_handle_t * ihp)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
genofw_pci_conf_hook(void * v,int bus,int dev,int func,pcireg_t id)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 }
549