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