]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/dev/ofw/ofw_bus_subr.c
Update from libxo-0.8.1 to 0.8.4:
[FreeBSD/FreeBSD.git] / sys / dev / ofw / ofw_bus_subr.c
1 /*-
2  * Copyright (c) 2001 - 2003 by Thomas Moestl <tmm@FreeBSD.org>.
3  * Copyright (c) 2005 Marius Strobl <marius@FreeBSD.org>
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions, and the following disclaimer,
11  *    without modification, immediately at the beginning of the file.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in
14  *    the documentation and/or other materials provided with the
15  *    distribution.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
21  * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27  * SUCH DAMAGE.
28  */
29
30 #include <sys/cdefs.h>
31 __FBSDID("$FreeBSD$");
32
33 #include "opt_platform.h"
34 #include <sys/param.h>
35 #include <sys/systm.h>
36 #include <sys/bus.h>
37 #include <sys/errno.h>
38 #include <sys/libkern.h>
39
40 #include <machine/resource.h>
41
42 #include <dev/ofw/ofw_bus.h>
43 #include <dev/ofw/ofw_bus_subr.h>
44 #include <dev/ofw/openfirm.h>
45
46 #include "ofw_bus_if.h"
47
48 #define OFW_COMPAT_LEN  255
49 #define OFW_STATUS_LEN  16
50
51 int
52 ofw_bus_gen_setup_devinfo(struct ofw_bus_devinfo *obd, phandle_t node)
53 {
54
55         if (obd == NULL)
56                 return (ENOMEM);
57         /* The 'name' property is considered mandatory. */
58         if ((OF_getprop_alloc(node, "name", 1, (void **)&obd->obd_name)) == -1)
59                 return (EINVAL);
60         OF_getprop_alloc(node, "compatible", 1, (void **)&obd->obd_compat);
61         OF_getprop_alloc(node, "device_type", 1, (void **)&obd->obd_type);
62         OF_getprop_alloc(node, "model", 1, (void **)&obd->obd_model);
63         OF_getprop_alloc(node, "status", 1, (void **)&obd->obd_status);
64         obd->obd_node = node;
65         return (0);
66 }
67
68 void
69 ofw_bus_gen_destroy_devinfo(struct ofw_bus_devinfo *obd)
70 {
71
72         if (obd == NULL)
73                 return;
74         if (obd->obd_compat != NULL)
75                 free(obd->obd_compat, M_OFWPROP);
76         if (obd->obd_model != NULL)
77                 free(obd->obd_model, M_OFWPROP);
78         if (obd->obd_name != NULL)
79                 free(obd->obd_name, M_OFWPROP);
80         if (obd->obd_type != NULL)
81                 free(obd->obd_type, M_OFWPROP);
82         if (obd->obd_status != NULL)
83                 free(obd->obd_status, M_OFWPROP);
84 }
85
86 int
87 ofw_bus_gen_child_pnpinfo_str(device_t cbdev, device_t child, char *buf,
88     size_t buflen)
89 {
90
91         if (ofw_bus_get_name(child) != NULL) {
92                 strlcat(buf, "name=", buflen);
93                 strlcat(buf, ofw_bus_get_name(child), buflen);
94         }
95
96         if (ofw_bus_get_compat(child) != NULL) {
97                 strlcat(buf, " compat=", buflen);
98                 strlcat(buf, ofw_bus_get_compat(child), buflen);
99         }
100         return (0);
101 };
102
103 const char *
104 ofw_bus_gen_get_compat(device_t bus, device_t dev)
105 {
106         const struct ofw_bus_devinfo *obd;
107
108         obd = OFW_BUS_GET_DEVINFO(bus, dev);
109         if (obd == NULL)
110                 return (NULL);
111         return (obd->obd_compat);
112 }
113
114 const char *
115 ofw_bus_gen_get_model(device_t bus, device_t dev)
116 {
117         const struct ofw_bus_devinfo *obd;
118
119         obd = OFW_BUS_GET_DEVINFO(bus, dev);
120         if (obd == NULL)
121                 return (NULL);
122         return (obd->obd_model);
123 }
124
125 const char *
126 ofw_bus_gen_get_name(device_t bus, device_t dev)
127 {
128         const struct ofw_bus_devinfo *obd;
129
130         obd = OFW_BUS_GET_DEVINFO(bus, dev);
131         if (obd == NULL)
132                 return (NULL);
133         return (obd->obd_name);
134 }
135
136 phandle_t
137 ofw_bus_gen_get_node(device_t bus, device_t dev)
138 {
139         const struct ofw_bus_devinfo *obd;
140
141         obd = OFW_BUS_GET_DEVINFO(bus, dev);
142         if (obd == NULL)
143                 return (0);
144         return (obd->obd_node);
145 }
146
147 const char *
148 ofw_bus_gen_get_type(device_t bus, device_t dev)
149 {
150         const struct ofw_bus_devinfo *obd;
151
152         obd = OFW_BUS_GET_DEVINFO(bus, dev);
153         if (obd == NULL)
154                 return (NULL);
155         return (obd->obd_type);
156 }
157
158 const char *
159 ofw_bus_get_status(device_t dev)
160 {
161         const struct ofw_bus_devinfo *obd;
162
163         obd = OFW_BUS_GET_DEVINFO(device_get_parent(dev), dev);
164         if (obd == NULL)
165                 return (NULL);
166
167         return (obd->obd_status);
168 }
169
170 int
171 ofw_bus_status_okay(device_t dev)
172 {
173         const char *status;
174
175         status = ofw_bus_get_status(dev);
176         if (status == NULL || strcmp(status, "okay") == 0 ||
177             strcmp(status, "ok") == 0)
178                 return (1);
179         
180         return (0);
181 }
182
183 int
184 ofw_bus_node_status_okay(phandle_t node)
185 {
186         char status[OFW_STATUS_LEN];
187         int len;
188
189         len = OF_getproplen(node, "status");
190         if (len <= 0)
191                 return (1);
192
193         OF_getprop(node, "status", status, OFW_STATUS_LEN);
194         if ((len == 5 && (bcmp(status, "okay", len) == 0)) ||
195             (len == 3 && (bcmp(status, "ok", len))))
196                 return (1);
197
198         return (0);
199 }
200
201 static int
202 ofw_bus_node_is_compatible_int(const char *compat, int len,
203     const char *onecompat)
204 {
205         int onelen, l, ret;
206
207         onelen = strlen(onecompat);
208
209         ret = 0;
210         while (len > 0) {
211                 if (strlen(compat) == onelen &&
212                     strncasecmp(compat, onecompat, onelen) == 0) {
213                         /* Found it. */
214                         ret = 1;
215                         break;
216                 }
217
218                 /* Slide to the next sub-string. */
219                 l = strlen(compat) + 1;
220                 compat += l;
221                 len -= l;
222         }
223
224         return (ret);
225 }
226
227 int
228 ofw_bus_node_is_compatible(phandle_t node, const char *compatstr)
229 {
230         char compat[OFW_COMPAT_LEN];
231         int len, rv;
232
233         if ((len = OF_getproplen(node, "compatible")) <= 0)
234                 return (0);
235
236         bzero(compat, OFW_COMPAT_LEN);
237
238         if (OF_getprop(node, "compatible", compat, OFW_COMPAT_LEN) < 0)
239                 return (0);
240
241         rv = ofw_bus_node_is_compatible_int(compat, len, compatstr);
242
243         return (rv);
244 }
245
246 int
247 ofw_bus_is_compatible(device_t dev, const char *onecompat)
248 {
249         phandle_t node;
250         const char *compat;
251         int len;
252
253         if ((compat = ofw_bus_get_compat(dev)) == NULL)
254                 return (0);
255
256         if ((node = ofw_bus_get_node(dev)) == -1)
257                 return (0);
258
259         /* Get total 'compatible' prop len */
260         if ((len = OF_getproplen(node, "compatible")) <= 0)
261                 return (0);
262
263         return (ofw_bus_node_is_compatible_int(compat, len, onecompat));
264 }
265
266 int
267 ofw_bus_is_compatible_strict(device_t dev, const char *compatible)
268 {
269         const char *compat;
270         size_t len;
271
272         if ((compat = ofw_bus_get_compat(dev)) == NULL)
273                 return (0);
274
275         len = strlen(compatible);
276         if (strlen(compat) == len &&
277             strncasecmp(compat, compatible, len) == 0)
278                 return (1);
279
280         return (0);
281 }
282
283 const struct ofw_compat_data *
284 ofw_bus_search_compatible(device_t dev, const struct ofw_compat_data *compat)
285 {
286
287         if (compat == NULL)
288                 return NULL;
289
290         for (; compat->ocd_str != NULL; ++compat) {
291                 if (ofw_bus_is_compatible(dev, compat->ocd_str))
292                         break;
293         }
294
295         return (compat);
296 }
297
298 int
299 ofw_bus_has_prop(device_t dev, const char *propname)
300 {
301         phandle_t node;
302
303         if ((node = ofw_bus_get_node(dev)) == -1)
304                 return (0);
305
306         return (OF_hasprop(node, propname));
307 }
308
309 void
310 ofw_bus_setup_iinfo(phandle_t node, struct ofw_bus_iinfo *ii, int intrsz)
311 {
312         pcell_t addrc;
313         int msksz;
314
315         if (OF_getencprop(node, "#address-cells", &addrc, sizeof(addrc)) == -1)
316                 addrc = 2;
317         ii->opi_addrc = addrc * sizeof(pcell_t);
318
319         ii->opi_imapsz = OF_getencprop_alloc(node, "interrupt-map", 1,
320             (void **)&ii->opi_imap);
321         if (ii->opi_imapsz > 0) {
322                 msksz = OF_getencprop_alloc(node, "interrupt-map-mask", 1,
323                     (void **)&ii->opi_imapmsk);
324                 /*
325                  * Failure to get the mask is ignored; a full mask is used
326                  * then.  We barf on bad mask sizes, however.
327                  */
328                 if (msksz != -1 && msksz != ii->opi_addrc + intrsz)
329                         panic("ofw_bus_setup_iinfo: bad interrupt-map-mask "
330                             "property!");
331         }
332 }
333
334 int
335 ofw_bus_lookup_imap(phandle_t node, struct ofw_bus_iinfo *ii, void *reg,
336     int regsz, void *pintr, int pintrsz, void *mintr, int mintrsz,
337     phandle_t *iparent)
338 {
339         uint8_t maskbuf[regsz + pintrsz];
340         int rv;
341
342         if (ii->opi_imapsz <= 0)
343                 return (0);
344         KASSERT(regsz >= ii->opi_addrc,
345             ("ofw_bus_lookup_imap: register size too small: %d < %d",
346                 regsz, ii->opi_addrc));
347         if (node != -1) {
348                 rv = OF_getencprop(node, "reg", reg, regsz);
349                 if (rv < regsz)
350                         panic("ofw_bus_lookup_imap: cannot get reg property");
351         }
352         return (ofw_bus_search_intrmap(pintr, pintrsz, reg, ii->opi_addrc,
353             ii->opi_imap, ii->opi_imapsz, ii->opi_imapmsk, maskbuf, mintr,
354             mintrsz, iparent));
355 }
356
357 /*
358  * Map an interrupt using the firmware reg, interrupt-map and
359  * interrupt-map-mask properties.
360  * The interrupt property to be mapped must be of size intrsz, and pointed to
361  * by intr.  The regs property of the node for which the mapping is done must
362  * be passed as regs. This property is an array of register specifications;
363  * the size of the address part of such a specification must be passed as
364  * physsz.  Only the first element of the property is used.
365  * imap and imapsz hold the interrupt mask and it's size.
366  * imapmsk is a pointer to the interrupt-map-mask property, which must have
367  * a size of physsz + intrsz; it may be NULL, in which case a full mask is
368  * assumed.
369  * maskbuf must point to a buffer of length physsz + intrsz.
370  * The interrupt is returned in result, which must point to a buffer of length
371  * rintrsz (which gives the expected size of the mapped interrupt).
372  * Returns number of cells in the interrupt if a mapping was found, 0 otherwise.
373  */
374 int
375 ofw_bus_search_intrmap(void *intr, int intrsz, void *regs, int physsz,
376     void *imap, int imapsz, void *imapmsk, void *maskbuf, void *result,
377     int rintrsz, phandle_t *iparent)
378 {
379         phandle_t parent;
380         uint8_t *ref = maskbuf;
381         uint8_t *uiintr = intr;
382         uint8_t *uiregs = regs;
383         uint8_t *uiimapmsk = imapmsk;
384         uint8_t *mptr;
385         pcell_t paddrsz;
386         pcell_t pintrsz;
387         int i, rsz, tsz;
388
389         rsz = -1;
390         if (imapmsk != NULL) {
391                 for (i = 0; i < physsz; i++)
392                         ref[i] = uiregs[i] & uiimapmsk[i];
393                 for (i = 0; i < intrsz; i++)
394                         ref[physsz + i] = uiintr[i] & uiimapmsk[physsz + i];
395         } else {
396                 bcopy(regs, ref, physsz);
397                 bcopy(intr, ref + physsz, intrsz);
398         }
399
400         mptr = imap;
401         i = imapsz;
402         paddrsz = 0;
403         while (i > 0) {
404                 bcopy(mptr + physsz + intrsz, &parent, sizeof(parent));
405 #ifndef OFW_IMAP_NO_IPARENT_ADDR_CELLS
406                 /*
407                  * Find if we need to read the parent address data.
408                  * CHRP-derived OF bindings, including ePAPR-compliant FDTs,
409                  * use this as an optional part of the specifier.
410                  */
411                 if (OF_getencprop(OF_node_from_xref(parent),
412                     "#address-cells", &paddrsz, sizeof(paddrsz)) == -1)
413                         paddrsz = 0;    /* default */
414                 paddrsz *= sizeof(pcell_t);
415 #endif
416
417                 if (OF_searchencprop(OF_node_from_xref(parent),
418                     "#interrupt-cells", &pintrsz, sizeof(pintrsz)) == -1)
419                         pintrsz = 1;    /* default */
420                 pintrsz *= sizeof(pcell_t);
421
422                 /* Compute the map stride size. */
423                 tsz = physsz + intrsz + sizeof(phandle_t) + paddrsz + pintrsz;
424                 KASSERT(i >= tsz, ("ofw_bus_search_intrmap: truncated map"));
425
426                 if (bcmp(ref, mptr, physsz + intrsz) == 0) {
427                         bcopy(mptr + physsz + intrsz + sizeof(parent) + paddrsz,
428                             result, MIN(rintrsz, pintrsz));
429
430                         if (iparent != NULL)
431                                 *iparent = parent;
432                         return (pintrsz/sizeof(pcell_t));
433                 }
434                 mptr += tsz;
435                 i -= tsz;
436         }
437         return (0);
438 }
439
440 int
441 ofw_bus_msimap(phandle_t node, uint16_t pci_rid, phandle_t *msi_parent,
442     uint32_t *msi_rid)
443 {
444         pcell_t *map, mask, msi_base, rid_base, rid_length;
445         ssize_t len;
446         uint32_t masked_rid, rid;
447         int err, i;
448
449         /* TODO: This should be OF_searchprop_alloc if we had it */
450         len = OF_getencprop_alloc(node, "msi-map", sizeof(*map), (void **)&map);
451         if (len < 0) {
452                 if (msi_parent != NULL) {
453                         *msi_parent = 0;
454                         OF_getencprop(node, "msi-parent", msi_parent,
455                             sizeof(*msi_parent));
456                 }
457                 if (msi_rid != NULL)
458                         *msi_rid = pci_rid;
459                 return (0);
460         }
461
462         err = ENOENT;
463         rid = 0;
464         mask = 0xffffffff;
465         OF_getencprop(node, "msi-map-mask", &mask, sizeof(mask));
466
467         masked_rid = pci_rid & mask;
468         for (i = 0; i < len; i += 4) {
469                 rid_base = map[i + 0];
470                 rid_length = map[i + 3];
471
472                 if (masked_rid < rid_base ||
473                     masked_rid >= (rid_base + rid_length))
474                         continue;
475
476                 msi_base = map[i + 2];
477
478                 if (msi_parent != NULL)
479                         *msi_parent = map[i + 1];
480                 if (msi_rid != NULL)
481                         *msi_rid = masked_rid - rid_base + msi_base;
482                 err = 0;
483                 break;
484         }
485
486         free(map, M_OFWPROP);
487
488         return (err);
489 }
490
491 int
492 ofw_bus_reg_to_rl(device_t dev, phandle_t node, pcell_t acells, pcell_t scells,
493     struct resource_list *rl)
494 {
495         uint64_t phys, size;
496         ssize_t i, j, rid, nreg, ret;
497         uint32_t *reg;
498         char *name;
499
500         /*
501          * This may be just redundant when having ofw_bus_devinfo
502          * but makes this routine independent of it.
503          */
504         ret = OF_getprop_alloc(node, "name", sizeof(*name), (void **)&name);
505         if (ret == -1)
506                 name = NULL;
507
508         ret = OF_getencprop_alloc(node, "reg", sizeof(*reg), (void **)&reg);
509         nreg = (ret == -1) ? 0 : ret;
510
511         if (nreg % (acells + scells) != 0) {
512                 if (bootverbose)
513                         device_printf(dev, "Malformed reg property on <%s>\n",
514                             (name == NULL) ? "unknown" : name);
515                 nreg = 0;
516         }
517
518         for (i = 0, rid = 0; i < nreg; i += acells + scells, rid++) {
519                 phys = size = 0;
520                 for (j = 0; j < acells; j++) {
521                         phys <<= 32;
522                         phys |= reg[i + j];
523                 }
524                 for (j = 0; j < scells; j++) {
525                         size <<= 32;
526                         size |= reg[i + acells + j];
527                 }
528                 /* Skip the dummy reg property of glue devices like ssm(4). */
529                 if (size != 0)
530                         resource_list_add(rl, SYS_RES_MEMORY, rid,
531                             phys, phys + size - 1, size);
532         }
533         free(name, M_OFWPROP);
534         free(reg, M_OFWPROP);
535
536         return (0);
537 }
538
539 /*
540  * Get interrupt parent for given node.
541  * Returns 0 if interrupt parent doesn't exist.
542  */
543 phandle_t
544 ofw_bus_find_iparent(phandle_t node)
545 {
546         phandle_t iparent;
547
548         if (OF_searchencprop(node, "interrupt-parent", &iparent,
549                     sizeof(iparent)) == -1) {
550                 for (iparent = node; iparent != 0;
551                     iparent = OF_parent(iparent)) {
552                         if (OF_hasprop(iparent, "interrupt-controller"))
553                                 break;
554                 }
555                 iparent = OF_xref_from_node(iparent);
556         }
557         return (iparent);
558 }
559
560 int
561 ofw_bus_intr_to_rl(device_t dev, phandle_t node,
562     struct resource_list *rl, int *rlen)
563 {
564         phandle_t iparent;
565         uint32_t icells, *intr;
566         int err, i, irqnum, nintr, rid;
567         boolean_t extended;
568
569         nintr = OF_getencprop_alloc(node, "interrupts",  sizeof(*intr),
570             (void **)&intr);
571         if (nintr > 0) {
572                 iparent = ofw_bus_find_iparent(node);
573                 if (iparent == 0) {
574                         device_printf(dev, "No interrupt-parent found, "
575                             "assuming direct parent\n");
576                         iparent = OF_parent(node);
577                         iparent = OF_xref_from_node(iparent);
578                 }
579                 if (OF_searchencprop(OF_node_from_xref(iparent), 
580                     "#interrupt-cells", &icells, sizeof(icells)) == -1) {
581                         device_printf(dev, "Missing #interrupt-cells "
582                             "property, assuming <1>\n");
583                         icells = 1;
584                 }
585                 if (icells < 1 || icells > nintr) {
586                         device_printf(dev, "Invalid #interrupt-cells property "
587                             "value <%d>, assuming <1>\n", icells);
588                         icells = 1;
589                 }
590                 extended = false;
591         } else {
592                 nintr = OF_getencprop_alloc(node, "interrupts-extended",
593                     sizeof(*intr), (void **)&intr);
594                 if (nintr <= 0)
595                         return (0);
596                 extended = true;
597         }
598         err = 0;
599         rid = 0;
600         for (i = 0; i < nintr; i += icells) {
601                 if (extended) {
602                         iparent = intr[i++];
603                         if (OF_searchencprop(OF_node_from_xref(iparent), 
604                             "#interrupt-cells", &icells, sizeof(icells)) == -1) {
605                                 device_printf(dev, "Missing #interrupt-cells "
606                                     "property\n");
607                                 err = ENOENT;
608                                 break;
609                         }
610                         if (icells < 1 || (i + icells) > nintr) {
611                                 device_printf(dev, "Invalid #interrupt-cells "
612                                     "property value <%d>\n", icells);
613                                 err = ERANGE;
614                                 break;
615                         }
616                 }
617                 irqnum = ofw_bus_map_intr(dev, iparent, icells, &intr[i]);
618                 resource_list_add(rl, SYS_RES_IRQ, rid++, irqnum, irqnum, 1);
619         }
620         if (rlen != NULL)
621                 *rlen = rid;
622         free(intr, M_OFWPROP);
623         return (err);
624 }
625
626 int
627 ofw_bus_intr_by_rid(device_t dev, phandle_t node, int wanted_rid,
628     phandle_t *producer, int *ncells, pcell_t **cells)
629 {
630         phandle_t iparent;
631         uint32_t icells, *intr;
632         int err, i, nintr, rid;
633         boolean_t extended;
634
635         nintr = OF_getencprop_alloc(node, "interrupts",  sizeof(*intr),
636             (void **)&intr);
637         if (nintr > 0) {
638                 iparent = ofw_bus_find_iparent(node);
639                 if (iparent == 0) {
640                         device_printf(dev, "No interrupt-parent found, "
641                             "assuming direct parent\n");
642                         iparent = OF_parent(node);
643                         iparent = OF_xref_from_node(iparent);
644                 }
645                 if (OF_searchencprop(OF_node_from_xref(iparent),
646                     "#interrupt-cells", &icells, sizeof(icells)) == -1) {
647                         device_printf(dev, "Missing #interrupt-cells "
648                             "property, assuming <1>\n");
649                         icells = 1;
650                 }
651                 if (icells < 1 || icells > nintr) {
652                         device_printf(dev, "Invalid #interrupt-cells property "
653                             "value <%d>, assuming <1>\n", icells);
654                         icells = 1;
655                 }
656                 extended = false;
657         } else {
658                 nintr = OF_getencprop_alloc(node, "interrupts-extended",
659                     sizeof(*intr), (void **)&intr);
660                 if (nintr <= 0)
661                         return (ESRCH);
662                 extended = true;
663         }
664         err = ESRCH;
665         rid = 0;
666         for (i = 0; i < nintr; i += icells, rid++) {
667                 if (extended) {
668                         iparent = intr[i++];
669                         if (OF_searchencprop(OF_node_from_xref(iparent),
670                             "#interrupt-cells", &icells, sizeof(icells)) == -1) {
671                                 device_printf(dev, "Missing #interrupt-cells "
672                                     "property\n");
673                                 err = ENOENT;
674                                 break;
675                         }
676                         if (icells < 1 || (i + icells) > nintr) {
677                                 device_printf(dev, "Invalid #interrupt-cells "
678                                     "property value <%d>\n", icells);
679                                 err = ERANGE;
680                                 break;
681                         }
682                 }
683                 if (rid == wanted_rid) {
684                         *cells = malloc(icells * sizeof(**cells), M_OFWPROP,
685                             M_WAITOK);
686                         *producer = iparent;
687                         *ncells= icells;
688                         memcpy(*cells, intr + i, icells * sizeof(**cells));
689                         err = 0;
690                         break;
691                 }
692         }
693         free(intr, M_OFWPROP);
694         return (err);
695 }
696
697 phandle_t
698 ofw_bus_find_child(phandle_t start, const char *child_name)
699 {
700         char *name;
701         int ret;
702         phandle_t child;
703
704         for (child = OF_child(start); child != 0; child = OF_peer(child)) {
705                 ret = OF_getprop_alloc(child, "name", sizeof(*name), (void **)&name);
706                 if (ret == -1)
707                         continue;
708                 if (strcmp(name, child_name) == 0) {
709                         free(name, M_OFWPROP);
710                         return (child);
711                 }
712
713                 free(name, M_OFWPROP);
714         }
715
716         return (0);
717 }
718
719 phandle_t
720 ofw_bus_find_compatible(phandle_t node, const char *onecompat)
721 {
722         phandle_t child, ret;
723         void *compat;
724         int len;
725
726         /*
727          * Traverse all children of 'start' node, and find first with
728          * matching 'compatible' property.
729          */
730         for (child = OF_child(node); child != 0; child = OF_peer(child)) {
731                 len = OF_getprop_alloc(child, "compatible", 1, &compat);
732                 if (len >= 0) {
733                         ret = ofw_bus_node_is_compatible_int(compat, len,
734                             onecompat);
735                         free(compat, M_OFWPROP);
736                         if (ret != 0)
737                                 return (child);
738                 }
739
740                 ret = ofw_bus_find_compatible(child, onecompat);
741                 if (ret != 0)
742                         return (ret);
743         }
744         return (0);
745 }
746
747 /**
748  * @brief Return child of bus whose phandle is node
749  *
750  * A direct child of @p will be returned if it its phandle in the
751  * OFW tree is @p node. Otherwise, NULL is returned.
752  *
753  * @param bus           The bus to examine
754  * @param node          The phandle_t to look for.
755  */
756 device_t
757 ofw_bus_find_child_device_by_phandle(device_t bus, phandle_t node)
758 {
759         device_t *children, retval, child;
760         int nkid, i;
761
762         /*
763          * Nothing can match the flag value for no node.
764          */
765         if (node == -1)
766                 return (NULL);
767
768         /*
769          * Search the children for a match. We microoptimize
770          * a bit by not using ofw_bus_get since we already know
771          * the parent. We do not recurse.
772          */
773         if (device_get_children(bus, &children, &nkid) != 0)
774                 return (NULL);
775         retval = NULL;
776         for (i = 0; i < nkid; i++) {
777                 child = children[i];
778                 if (OFW_BUS_GET_NODE(bus, child) == node) {
779                         retval = child;
780                         break;
781                 }
782         }
783         free(children, M_TEMP);
784
785         return (retval);
786 }
787
788 /*
789  * Parse property that contain list of xrefs and values
790  * (like standard "clocks" and "resets" properties)
791  * Input arguments:
792  *  node - consumers device node
793  *  list_name  - name of parsed list - "clocks"
794  *  cells_name - name of size property - "#clock-cells"
795  *  idx - the index of the requested list entry, or, if -1, an indication
796  *        to return the number of entries in the parsed list.
797  * Output arguments:
798  *  producer - handle of producer
799  *  ncells   - number of cells in result or the number of items in the list when
800  *             idx == -1.
801  *  cells    - array of decoded cells
802  */
803 static int
804 ofw_bus_parse_xref_list_internal(phandle_t node, const char *list_name,
805     const char *cells_name, int idx, phandle_t *producer, int *ncells,
806     pcell_t **cells)
807 {
808         phandle_t pnode;
809         phandle_t *elems;
810         uint32_t  pcells;
811         int rv, i, j, nelems, cnt;
812
813         elems = NULL;
814         nelems = OF_getencprop_alloc(node, list_name,  sizeof(*elems),
815             (void **)&elems);
816         if (nelems <= 0)
817                 return (ENOENT);
818         rv = (idx == -1) ? 0 : ENOENT;
819         for (i = 0, cnt = 0; i < nelems; i += pcells, cnt++) {
820                 pnode = elems[i++];
821                 if (OF_getencprop(OF_node_from_xref(pnode),
822                     cells_name, &pcells, sizeof(pcells)) == -1) {
823                         printf("Missing %s property\n", cells_name);
824                         rv = ENOENT;
825                         break;
826                 }
827
828                 if ((i + pcells) > nelems) {
829                         printf("Invalid %s property value <%d>\n", cells_name,
830                             pcells);
831                         rv = ERANGE;
832                         break;
833                 }
834                 if (cnt == idx) {
835                         *cells= malloc(pcells * sizeof(**cells), M_OFWPROP,
836                             M_WAITOK);
837                         *producer = pnode;
838                         *ncells = pcells;
839                         for (j = 0; j < pcells; j++)
840                                 (*cells)[j] = elems[i + j];
841                         rv = 0;
842                         break;
843                 }
844         }
845         if (elems != NULL)
846                 free(elems, M_OFWPROP);
847         if (idx == -1 && rv == 0)
848                 *ncells = cnt;
849         return (rv);
850 }
851
852 /*
853  * Parse property that contain list of xrefs and values
854  * (like standard "clocks" and "resets" properties)
855  * Input arguments:
856  *  node - consumers device node
857  *  list_name  - name of parsed list - "clocks"
858  *  cells_name - name of size property - "#clock-cells"
859  *  idx - the index of the requested list entry (>= 0)
860  * Output arguments:
861  *  producer - handle of producer
862  *  ncells   - number of cells in result
863  *  cells    - array of decoded cells
864  */
865 int
866 ofw_bus_parse_xref_list_alloc(phandle_t node, const char *list_name,
867     const char *cells_name, int idx, phandle_t *producer, int *ncells,
868     pcell_t **cells)
869 {
870
871         KASSERT(idx >= 0,
872             ("ofw_bus_parse_xref_list_alloc: negative index supplied"));
873
874         return (ofw_bus_parse_xref_list_internal(node, list_name, cells_name,
875                     idx, producer, ncells, cells));
876 }
877
878 /*
879  * Parse property that contain list of xrefs and values
880  * (like standard "clocks" and "resets" properties)
881  * and determine the number of items in the list
882  * Input arguments:
883  *  node - consumers device node
884  *  list_name  - name of parsed list - "clocks"
885  *  cells_name - name of size property - "#clock-cells"
886  * Output arguments:
887  *  count - number of items in list
888  */
889 int
890 ofw_bus_parse_xref_list_get_length(phandle_t node, const char *list_name,
891     const char *cells_name, int *count)
892 {
893
894         return (ofw_bus_parse_xref_list_internal(node, list_name, cells_name,
895                     -1, NULL, count, NULL));
896 }
897
898 /*
899  * Find index of string in string list property (case sensitive).
900  */
901 int
902 ofw_bus_find_string_index(phandle_t node, const char *list_name,
903     const char *name, int *idx)
904 {
905         char *elems;
906         int rv, i, cnt, nelems;
907
908         elems = NULL;
909         nelems = OF_getprop_alloc(node, list_name, 1, (void **)&elems);
910         if (nelems <= 0)
911                 return (ENOENT);
912
913         rv = ENOENT;
914         for (i = 0, cnt = 0; i < nelems; cnt++) {
915                 if (strcmp(elems + i, name) == 0) {
916                         *idx = cnt;
917                         rv = 0;
918                         break;
919                 }
920                 i += strlen(elems + i) + 1;
921         }
922
923         if (elems != NULL)
924                 free(elems, M_OFWPROP);
925         return (rv);
926 }
927
928 /*
929  * Create zero terminated array of strings from string list property.
930  */
931 int
932 ofw_bus_string_list_to_array(phandle_t node, const char *list_name,
933    const char ***out_array)
934 {
935         char *elems, *tptr;
936         const char **array;
937         int i, cnt, nelems, len;
938
939         elems = NULL;
940         nelems = OF_getprop_alloc(node, list_name, 1, (void **)&elems);
941         if (nelems <= 0)
942                 return (nelems);
943
944         /* Count number of strings. */
945         for (i = 0, cnt = 0; i < nelems; cnt++)
946                 i += strlen(elems + i) + 1;
947
948         /* Allocate space for arrays and all strings. */
949         array = malloc((cnt + 1) * sizeof(char *) + nelems, M_OFWPROP,
950             M_WAITOK);
951
952         /* Get address of first string. */
953         tptr = (char *)(array + cnt + 1);
954
955         /* Copy strings. */
956         memcpy(tptr, elems, nelems);
957         free(elems, M_OFWPROP);
958
959         /* Fill string pointers. */
960         for (i = 0, cnt = 0; i < nelems; cnt++) {
961                 len = strlen(tptr) + 1;
962                 array[cnt] = tptr;
963                 i += len;
964                 tptr += len;
965         }
966         array[cnt] = NULL;
967         *out_array = array;
968
969         return (cnt);
970 }