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