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