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