xref: /freebsd-13-stable/sys/dev/ofw/ofw_bus_subr.c (revision 3bc80996974a61a4223eae4c1ccd47b6ee32a48a)
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 **)&reg);
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