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