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