The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/dev/ofw/ofw_bus_subr.c

Version: -  FREEBSD  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

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

Cache object: 27e378f74b3dc1b415bf4467c9c5b6b5


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.