source: rtems-libbsd/freebsd/sys/dev/ofw/ofw_bus_subr.c @ f0dd0c5

55-freebsd-126-freebsd-12
Last change on this file since f0dd0c5 was f0dd0c5, checked in by Sebastian Huber <sebastian.huber@…>, on 03/02/17 at 15:28:14

FDT(4): Import from FreeBSD

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