xref: /freebsd/sys/dev/ofw/ofw_bus_subr.c (revision b230a7b9a52c0fc948f4f1dcd1225a94674073f6)
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 bool
ofw_bus_is_machine_compatible(const char * compat)302 ofw_bus_is_machine_compatible(const char *compat)
303 {
304 	phandle_t root;
305 
306 	root = OF_finddevice("/");
307 	return (ofw_bus_node_is_compatible(root, compat) != 0);
308 }
309 
310 const struct ofw_compat_data *
ofw_bus_search_compatible(device_t dev,const struct ofw_compat_data * compat)311 ofw_bus_search_compatible(device_t dev, const struct ofw_compat_data *compat)
312 {
313 
314 	if (compat == NULL)
315 		return NULL;
316 
317 	for (; compat->ocd_str != NULL; ++compat) {
318 		if (ofw_bus_is_compatible(dev, compat->ocd_str))
319 			break;
320 	}
321 
322 	return (compat);
323 }
324 
325 int
ofw_bus_has_prop(device_t dev,const char * propname)326 ofw_bus_has_prop(device_t dev, const char *propname)
327 {
328 	phandle_t node;
329 
330 	if ((node = ofw_bus_get_node(dev)) == -1)
331 		return (0);
332 
333 	return (OF_hasprop(node, propname));
334 }
335 
336 void
ofw_bus_setup_iinfo(phandle_t node,struct ofw_bus_iinfo * ii,int intrsz)337 ofw_bus_setup_iinfo(phandle_t node, struct ofw_bus_iinfo *ii, int intrsz)
338 {
339 	pcell_t addrc;
340 	int msksz;
341 
342 	if (OF_getencprop(node, "#address-cells", &addrc, sizeof(addrc)) == -1)
343 		addrc = 2;
344 	ii->opi_addrc = addrc * sizeof(pcell_t);
345 
346 	ii->opi_imapsz = OF_getencprop_alloc(node, "interrupt-map",
347 	    (void **)&ii->opi_imap);
348 	if (ii->opi_imapsz > 0) {
349 		msksz = OF_getencprop_alloc(node, "interrupt-map-mask",
350 		    (void **)&ii->opi_imapmsk);
351 		/*
352 		 * Failure to get the mask is ignored; a full mask is used
353 		 * then.  We barf on bad mask sizes, however.
354 		 */
355 		if (msksz != -1 && msksz != ii->opi_addrc + intrsz)
356 			panic("ofw_bus_setup_iinfo: bad interrupt-map-mask "
357 			    "property!");
358 	}
359 }
360 
361 void
ofw_bus_destroy_iinfo(struct ofw_bus_iinfo * ii)362 ofw_bus_destroy_iinfo(struct ofw_bus_iinfo *ii)
363 {
364 
365 	if (ii->opi_imapsz > 0) {
366 		OF_prop_free(ii->opi_imapmsk);
367 		ii->opi_imapsz = 0;
368 	}
369 
370 	OF_prop_free(ii->opi_imap);
371 }
372 
373 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)374 ofw_bus_lookup_imap(phandle_t node, struct ofw_bus_iinfo *ii, void *reg,
375     int regsz, void *pintr, int pintrsz, void *mintr, int mintrsz,
376     phandle_t *iparent)
377 {
378 	uint8_t maskbuf[regsz + pintrsz];
379 	int rv;
380 
381 	if (ii->opi_imapsz <= 0)
382 		return (0);
383 	KASSERT(regsz >= ii->opi_addrc,
384 	    ("ofw_bus_lookup_imap: register size too small: %d < %d",
385 		regsz, ii->opi_addrc));
386 	if (node != -1) {
387 		rv = OF_getencprop(node, "reg", reg, regsz);
388 		if (rv < regsz)
389 			panic("ofw_bus_lookup_imap: cannot get reg property");
390 	}
391 	return (ofw_bus_search_intrmap(pintr, pintrsz, reg, ii->opi_addrc,
392 	    ii->opi_imap, ii->opi_imapsz, ii->opi_imapmsk, maskbuf, mintr,
393 	    mintrsz, iparent));
394 }
395 
396 /*
397  * Map an interrupt using the firmware reg, interrupt-map and
398  * interrupt-map-mask properties.
399  * The interrupt property to be mapped must be of size intrsz, and pointed to
400  * by intr.  The regs property of the node for which the mapping is done must
401  * be passed as regs. This property is an array of register specifications;
402  * the size of the address part of such a specification must be passed as
403  * physsz.  Only the first element of the property is used.
404  * imap and imapsz hold the interrupt mask and it's size.
405  * imapmsk is a pointer to the interrupt-map-mask property, which must have
406  * a size of physsz + intrsz; it may be NULL, in which case a full mask is
407  * assumed.
408  * maskbuf must point to a buffer of length physsz + intrsz.
409  * The interrupt is returned in result, which must point to a buffer of length
410  * rintrsz (which gives the expected size of the mapped interrupt).
411  * Returns number of cells in the interrupt if a mapping was found, 0 otherwise.
412  */
413 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)414 ofw_bus_search_intrmap(void *intr, int intrsz, void *regs, int physsz,
415     void *imap, int imapsz, void *imapmsk, void *maskbuf, void *result,
416     int rintrsz, phandle_t *iparent)
417 {
418 	phandle_t parent;
419 	uint8_t *ref = maskbuf;
420 	uint8_t *uiintr = intr;
421 	uint8_t *uiregs = regs;
422 	uint8_t *uiimapmsk = imapmsk;
423 	uint8_t *mptr;
424 	pcell_t paddrsz;
425 	pcell_t pintrsz;
426 	int i, tsz;
427 
428 	if (imapmsk != NULL) {
429 		for (i = 0; i < physsz; i++)
430 			ref[i] = uiregs[i] & uiimapmsk[i];
431 		for (i = 0; i < intrsz; i++)
432 			ref[physsz + i] = uiintr[i] & uiimapmsk[physsz + i];
433 	} else {
434 		bcopy(regs, ref, physsz);
435 		bcopy(intr, ref + physsz, intrsz);
436 	}
437 
438 	mptr = imap;
439 	i = imapsz;
440 	paddrsz = 0;
441 	while (i > 0) {
442 		bcopy(mptr + physsz + intrsz, &parent, sizeof(parent));
443 #ifndef OFW_IMAP_NO_IPARENT_ADDR_CELLS
444 		/*
445 		 * Find if we need to read the parent address data.
446 		 * CHRP-derived OF bindings, including ePAPR-compliant FDTs,
447 		 * use this as an optional part of the specifier.
448 		 */
449 		if (OF_getencprop(OF_node_from_xref(parent),
450 		    "#address-cells", &paddrsz, sizeof(paddrsz)) == -1)
451 			paddrsz = 0;	/* default */
452 		paddrsz *= sizeof(pcell_t);
453 #endif
454 
455 		if (OF_searchencprop(OF_node_from_xref(parent),
456 		    "#interrupt-cells", &pintrsz, sizeof(pintrsz)) == -1)
457 			pintrsz = 1;	/* default */
458 		pintrsz *= sizeof(pcell_t);
459 
460 		/* Compute the map stride size. */
461 		tsz = physsz + intrsz + sizeof(phandle_t) + paddrsz + pintrsz;
462 		KASSERT(i >= tsz, ("ofw_bus_search_intrmap: truncated map"));
463 
464 		if (bcmp(ref, mptr, physsz + intrsz) == 0) {
465 			bcopy(mptr + physsz + intrsz + sizeof(parent) + paddrsz,
466 			    result, MIN(rintrsz, pintrsz));
467 
468 			if (iparent != NULL)
469 				*iparent = parent;
470 			return (pintrsz/sizeof(pcell_t));
471 		}
472 		mptr += tsz;
473 		i -= tsz;
474 	}
475 	return (0);
476 }
477 
478 int
ofw_bus_msimap(phandle_t node,uint16_t pci_rid,phandle_t * msi_parent,uint32_t * msi_rid)479 ofw_bus_msimap(phandle_t node, uint16_t pci_rid, phandle_t *msi_parent,
480     uint32_t *msi_rid)
481 {
482 	pcell_t *map, mask, msi_base, rid_base, rid_length;
483 	ssize_t len;
484 	uint32_t masked_rid;
485 	int err, i;
486 
487 	/* TODO: This should be OF_searchprop_alloc if we had it */
488 	len = OF_getencprop_alloc_multi(node, "msi-map", sizeof(*map),
489 	    (void **)&map);
490 	if (len < 0) {
491 		if (msi_parent != NULL) {
492 			*msi_parent = 0;
493 			OF_getencprop(node, "msi-parent", msi_parent,
494 			    sizeof(*msi_parent));
495 		}
496 		if (msi_rid != NULL)
497 			*msi_rid = pci_rid;
498 		return (0);
499 	}
500 
501 	err = ENOENT;
502 	mask = 0xffffffff;
503 	OF_getencprop(node, "msi-map-mask", &mask, sizeof(mask));
504 
505 	masked_rid = pci_rid & mask;
506 	for (i = 0; i < len; i += 4) {
507 		rid_base = map[i + 0];
508 		rid_length = map[i + 3];
509 
510 		if (masked_rid < rid_base ||
511 		    masked_rid >= (rid_base + rid_length))
512 			continue;
513 
514 		msi_base = map[i + 2];
515 
516 		if (msi_parent != NULL)
517 			*msi_parent = map[i + 1];
518 		if (msi_rid != NULL)
519 			*msi_rid = masked_rid - rid_base + msi_base;
520 		err = 0;
521 		break;
522 	}
523 
524 	free(map, M_OFWPROP);
525 
526 	return (err);
527 }
528 
529 int
ofw_bus_iommu_map(phandle_t node,uint16_t pci_rid,phandle_t * iommu_parent,uint32_t * iommu_rid)530 ofw_bus_iommu_map(phandle_t node, uint16_t pci_rid, phandle_t *iommu_parent,
531     uint32_t *iommu_rid)
532 {
533 	pcell_t *map, mask, iommu_base, rid_base, rid_length;
534 	ssize_t len;
535 	uint32_t masked_rid;
536 	int err, i;
537 
538 	len = OF_getencprop_alloc_multi(node, "iommu-map", sizeof(*map),
539 	    (void **)&map);
540 	if (len <= 0)
541 		return (ENOENT);
542 
543 	err = ENOENT;
544 	mask = 0xffffffff;
545 	OF_getencprop(node, "iommu-map-mask", &mask, sizeof(mask));
546 
547 	masked_rid = pci_rid & mask;
548 	for (i = 0; i < len; i += 4) {
549 		rid_base = map[i + 0];
550 		rid_length = map[i + 3];
551 
552 		if (masked_rid < rid_base ||
553 		    masked_rid >= (rid_base + rid_length))
554 			continue;
555 
556 		iommu_base = map[i + 2];
557 
558 		if (iommu_parent != NULL)
559 			*iommu_parent = map[i + 1];
560 		if (iommu_rid != NULL)
561 			*iommu_rid = masked_rid - rid_base + iommu_base;
562 		err = 0;
563 		break;
564 	}
565 
566 	free(map, M_OFWPROP);
567 
568 	return (err);
569 }
570 
571 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)572 ofw_bus_reg_to_rl_helper(device_t dev, phandle_t node, pcell_t acells, pcell_t scells,
573     struct resource_list *rl, const char *reg_source)
574 {
575 	uint64_t phys, size;
576 	ssize_t i, j, rid, nreg, ret;
577 	uint32_t *reg;
578 	char *name;
579 
580 	/*
581 	 * This may be just redundant when having ofw_bus_devinfo
582 	 * but makes this routine independent of it.
583 	 */
584 	ret = OF_getprop_alloc(node, "name", (void **)&name);
585 	if (ret == -1)
586 		name = NULL;
587 
588 	ret = OF_getencprop_alloc_multi(node, reg_source, sizeof(*reg),
589 	    (void **)&reg);
590 	nreg = (ret == -1) ? 0 : ret;
591 
592 	if (nreg % (acells + scells) != 0) {
593 		if (bootverbose)
594 			device_printf(dev, "Malformed reg property on <%s>\n",
595 			    (name == NULL) ? "unknown" : name);
596 		nreg = 0;
597 	}
598 
599 	for (i = 0, rid = 0; i < nreg; i += acells + scells, rid++) {
600 		phys = size = 0;
601 		for (j = 0; j < acells; j++) {
602 			phys <<= 32;
603 			phys |= reg[i + j];
604 		}
605 		for (j = 0; j < scells; j++) {
606 			size <<= 32;
607 			size |= reg[i + acells + j];
608 		}
609 		/* Skip the dummy reg property of glue devices like ssm(4). */
610 		if (size != 0)
611 			resource_list_add(rl, SYS_RES_MEMORY, rid,
612 			    phys, phys + size - 1, size);
613 	}
614 	free(name, M_OFWPROP);
615 	free(reg, M_OFWPROP);
616 
617 	return (0);
618 }
619 
620 int
ofw_bus_reg_to_rl(device_t dev,phandle_t node,pcell_t acells,pcell_t scells,struct resource_list * rl)621 ofw_bus_reg_to_rl(device_t dev, phandle_t node, pcell_t acells, pcell_t scells,
622     struct resource_list *rl)
623 {
624 
625 	return (ofw_bus_reg_to_rl_helper(dev, node, acells, scells, rl, "reg"));
626 }
627 
628 int
ofw_bus_assigned_addresses_to_rl(device_t dev,phandle_t node,pcell_t acells,pcell_t scells,struct resource_list * rl)629 ofw_bus_assigned_addresses_to_rl(device_t dev, phandle_t node, pcell_t acells,
630     pcell_t scells, struct resource_list *rl)
631 {
632 
633 	return (ofw_bus_reg_to_rl_helper(dev, node, acells, scells,
634 	    rl, "assigned-addresses"));
635 }
636 
637 /*
638  * Get interrupt parent for given node.
639  * Returns 0 if interrupt parent doesn't exist.
640  */
641 phandle_t
ofw_bus_find_iparent(phandle_t node)642 ofw_bus_find_iparent(phandle_t node)
643 {
644 	phandle_t iparent;
645 
646 	if (OF_searchencprop(node, "interrupt-parent", &iparent,
647 		    sizeof(iparent)) == -1) {
648 		for (iparent = node; iparent != 0;
649 		    iparent = OF_parent(iparent)) {
650 			if (OF_hasprop(iparent, "interrupt-controller"))
651 				break;
652 		}
653 		iparent = OF_xref_from_node(iparent);
654 	}
655 	return (iparent);
656 }
657 
658 static phandle_t
ofw_bus_search_iparent(phandle_t node)659 ofw_bus_search_iparent(phandle_t node)
660 {
661 	phandle_t iparent;
662 
663 	do {
664 		if (OF_getencprop(node, "interrupt-parent", &iparent,
665 		    sizeof(iparent)) > 0) {
666 			node = OF_node_from_xref(iparent);
667 		} else {
668 			node = OF_parent(node);
669 		}
670 		if (node == 0)
671 			return (0);
672 	} while (!OF_hasprop(node, "#interrupt-cells"));
673 
674 	return (OF_xref_from_node(node));
675 }
676 
677 static int
ofw_bus_traverse_imap(phandle_t inode,phandle_t node,uint32_t * intr,int intrsz,pcell_t * res,int ressz,phandle_t * iparentp)678 ofw_bus_traverse_imap(phandle_t inode, phandle_t node, uint32_t *intr,
679     int intrsz, pcell_t *res, int ressz, phandle_t *iparentp)
680 {
681 	struct ofw_bus_iinfo ii;
682 	void *reg;
683 	uint32_t *intrp;
684 	phandle_t iparent;
685 	int rv = 0;
686 
687 	/* We already have an interrupt controller */
688 	if (OF_hasprop(node, "interrupt-controller"))
689 		return (0);
690 
691 	intrp = malloc(intrsz, M_OFWPROP, M_WAITOK);
692 	memcpy(intrp, intr, intrsz);
693 
694 	while (true) {
695 		/* There is no interrupt-map to follow */
696 		if (!OF_hasprop(inode, "interrupt-map")) {
697 			free(intrp, M_OFWPROP);
698 			return (0);
699 		}
700 
701 		memset(&ii, 0, sizeof(ii));
702 		ofw_bus_setup_iinfo(inode, &ii, sizeof(cell_t));
703 
704 		reg = NULL;
705 		if (ii.opi_addrc > 0)
706 			reg = malloc(ii.opi_addrc, M_OFWPROP, M_WAITOK);
707 
708 		rv = ofw_bus_lookup_imap(node, &ii, reg, ii.opi_addrc, intrp,
709 		    intrsz, res, ressz, &iparent);
710 
711 		free(reg, M_OFWPROP);
712 		free(ii.opi_imap, M_OFWPROP);
713 		free(ii.opi_imapmsk, M_OFWPROP);
714 		free(intrp, M_OFWPROP);
715 
716 		if (rv == 0)
717 			return (0);
718 
719 		node = inode;
720 		inode = OF_node_from_xref(iparent);
721 
722 		/* Stop when we have an interrupt controller */
723 		if (OF_hasprop(inode, "interrupt-controller")) {
724 			*iparentp = iparent;
725 			return (rv);
726 		}
727 
728 		intrsz = rv * sizeof(pcell_t);
729 		intrp = malloc(intrsz, M_OFWPROP, M_WAITOK);
730 		memcpy(intrp, res, intrsz);
731 	}
732 }
733 
734 int
ofw_bus_intr_to_rl(device_t dev,phandle_t node,struct resource_list * rl,int * rlen)735 ofw_bus_intr_to_rl(device_t dev, phandle_t node,
736     struct resource_list *rl, int *rlen)
737 {
738 	phandle_t iparent, iparent_node;
739 	uint32_t result[16];
740 	uint32_t intrpcells, *intrp;
741 	uint32_t icells, *intr;
742 	int err, i, irqnum, nintr, rid;
743 	bool extended;
744 
745 	nintr = OF_getencprop_alloc_multi(node, "interrupts",  sizeof(*intr),
746 	    (void **)&intr);
747 	if (nintr > 0) {
748 		iparent = ofw_bus_search_iparent(node);
749 		if (iparent == 0) {
750 			device_printf(dev, "No interrupt-parent found, "
751 			    "assuming direct parent\n");
752 			iparent = OF_parent(node);
753 			iparent = OF_xref_from_node(iparent);
754 		}
755 		iparent_node = OF_node_from_xref(iparent);
756 		if (OF_searchencprop(iparent_node, "#interrupt-cells", &icells,
757 		    sizeof(icells)) == -1) {
758 			device_printf(dev, "Missing #interrupt-cells "
759 			    "property, assuming <1>\n");
760 			icells = 1;
761 		}
762 		if (icells < 1 || icells > nintr) {
763 			device_printf(dev, "Invalid #interrupt-cells property "
764 			    "value <%d>, assuming <1>\n", icells);
765 			icells = 1;
766 		}
767 		extended = false;
768 	} else {
769 		nintr = OF_getencprop_alloc_multi(node, "interrupts-extended",
770 		    sizeof(*intr), (void **)&intr);
771 		if (nintr <= 0)
772 			return (0);
773 		extended = true;
774 	}
775 	err = 0;
776 	rid = 0;
777 	for (i = 0; i < nintr; i += icells) {
778 		if (extended) {
779 			iparent = intr[i++];
780 			iparent_node = OF_node_from_xref(iparent);
781 			if (OF_searchencprop(iparent_node,
782 			    "#interrupt-cells", &icells, sizeof(icells)) == -1) {
783 				device_printf(dev, "Missing #interrupt-cells "
784 				    "property\n");
785 				err = ENOENT;
786 				break;
787 			}
788 			if (icells < 1 || (i + icells) > nintr) {
789 				device_printf(dev, "Invalid #interrupt-cells "
790 				    "property value <%d>\n", icells);
791 				err = ERANGE;
792 				break;
793 			}
794 		}
795 
796 		intrp = &intr[i];
797 		intrpcells = ofw_bus_traverse_imap(iparent_node, node, intrp,
798 		    icells * sizeof(intr[0]), result, sizeof(result), &iparent);
799 		if (intrpcells > 0)
800 			intrp = result;
801 		else
802 			intrpcells = icells;
803 
804 		irqnum = ofw_bus_map_intr(dev, iparent, intrpcells, intrp);
805 		resource_list_add(rl, SYS_RES_IRQ, rid++, irqnum, irqnum, 1);
806 	}
807 	if (rlen != NULL)
808 		*rlen = rid;
809 	free(intr, M_OFWPROP);
810 	return (err);
811 }
812 
813 int
ofw_bus_intr_by_rid(device_t dev,phandle_t node,int wanted_rid,phandle_t * producer,int * ncells,pcell_t ** cells)814 ofw_bus_intr_by_rid(device_t dev, phandle_t node, int wanted_rid,
815     phandle_t *producer, int *ncells, pcell_t **cells)
816 {
817 	phandle_t iparent;
818 	uint32_t icells, *intr;
819 	int err, i, nintr, rid;
820 	bool extended;
821 
822 	nintr = OF_getencprop_alloc_multi(node, "interrupts",  sizeof(*intr),
823 	    (void **)&intr);
824 	if (nintr > 0) {
825 		iparent = ofw_bus_find_iparent(node);
826 		if (iparent == 0) {
827 			device_printf(dev, "No interrupt-parent found, "
828 			    "assuming direct parent\n");
829 			iparent = OF_parent(node);
830 			iparent = OF_xref_from_node(iparent);
831 		}
832 		if (OF_searchencprop(OF_node_from_xref(iparent),
833 		    "#interrupt-cells", &icells, sizeof(icells)) == -1) {
834 			device_printf(dev, "Missing #interrupt-cells "
835 			    "property, assuming <1>\n");
836 			icells = 1;
837 		}
838 		if (icells < 1 || icells > nintr) {
839 			device_printf(dev, "Invalid #interrupt-cells property "
840 			    "value <%d>, assuming <1>\n", icells);
841 			icells = 1;
842 		}
843 		extended = false;
844 	} else {
845 		nintr = OF_getencprop_alloc_multi(node, "interrupts-extended",
846 		    sizeof(*intr), (void **)&intr);
847 		if (nintr <= 0)
848 			return (ESRCH);
849 		extended = true;
850 	}
851 	err = ESRCH;
852 	rid = 0;
853 	for (i = 0; i < nintr; i += icells, rid++) {
854 		if (extended) {
855 			iparent = intr[i++];
856 			if (OF_searchencprop(OF_node_from_xref(iparent),
857 			    "#interrupt-cells", &icells, sizeof(icells)) == -1) {
858 				device_printf(dev, "Missing #interrupt-cells "
859 				    "property\n");
860 				err = ENOENT;
861 				break;
862 			}
863 			if (icells < 1 || (i + icells) > nintr) {
864 				device_printf(dev, "Invalid #interrupt-cells "
865 				    "property value <%d>\n", icells);
866 				err = ERANGE;
867 				break;
868 			}
869 		}
870 		if (rid == wanted_rid) {
871 			*cells = malloc(icells * sizeof(**cells), M_OFWPROP,
872 			    M_WAITOK);
873 			*producer = iparent;
874 			*ncells= icells;
875 			memcpy(*cells, intr + i, icells * sizeof(**cells));
876 			err = 0;
877 			break;
878 		}
879 	}
880 	free(intr, M_OFWPROP);
881 	return (err);
882 }
883 
884 phandle_t
ofw_bus_find_child(phandle_t start,const char * child_name)885 ofw_bus_find_child(phandle_t start, const char *child_name)
886 {
887 	char *name;
888 	int ret;
889 	phandle_t child;
890 
891 	for (child = OF_child(start); child != 0; child = OF_peer(child)) {
892 		ret = OF_getprop_alloc(child, "name", (void **)&name);
893 		if (ret == -1)
894 			continue;
895 		if (strcmp(name, child_name) == 0) {
896 			free(name, M_OFWPROP);
897 			return (child);
898 		}
899 
900 		free(name, M_OFWPROP);
901 	}
902 
903 	return (0);
904 }
905 
906 phandle_t
ofw_bus_find_compatible(phandle_t node,const char * onecompat)907 ofw_bus_find_compatible(phandle_t node, const char *onecompat)
908 {
909 	phandle_t child, ret;
910 
911 	/*
912 	 * Traverse all children of 'start' node, and find first with
913 	 * matching 'compatible' property.
914 	 */
915 	for (child = OF_child(node); child != 0; child = OF_peer(child)) {
916 		if (ofw_bus_node_is_compatible(child, onecompat) != 0)
917 			return (child);
918 
919 		ret = ofw_bus_find_compatible(child, onecompat);
920 		if (ret != 0)
921 			return (ret);
922 	}
923 	return (0);
924 }
925 
926 /**
927  * @brief Return child of bus whose phandle is node
928  *
929  * A direct child of @p will be returned if it its phandle in the
930  * OFW tree is @p node. Otherwise, NULL is returned.
931  *
932  * @param bus		The bus to examine
933  * @param node		The phandle_t to look for.
934  */
935 device_t
ofw_bus_find_child_device_by_phandle(device_t bus,phandle_t node)936 ofw_bus_find_child_device_by_phandle(device_t bus, phandle_t node)
937 {
938 	device_t *children, retval, child;
939 	int nkid, i;
940 
941 	/*
942 	 * Nothing can match the flag value for no node.
943 	 */
944 	if (node == -1)
945 		return (NULL);
946 
947 	/*
948 	 * Search the children for a match. We microoptimize
949 	 * a bit by not using ofw_bus_get since we already know
950 	 * the parent. We do not recurse.
951 	 */
952 	if (device_get_children(bus, &children, &nkid) != 0)
953 		return (NULL);
954 	retval = NULL;
955 	for (i = 0; i < nkid; i++) {
956 		child = children[i];
957 		if (OFW_BUS_GET_NODE(bus, child) == node) {
958 			retval = child;
959 			break;
960 		}
961 	}
962 	free(children, M_TEMP);
963 
964 	return (retval);
965 }
966 
967 /*
968  * Parse property that contain list of xrefs and values
969  * (like standard "clocks" and "resets" properties)
970  * Input arguments:
971  *  node - consumers device node
972  *  list_name  - name of parsed list - "clocks"
973  *  cells_name - name of size property - "#clock-cells"
974  *  idx - the index of the requested list entry, or, if -1, an indication
975  *        to return the number of entries in the parsed list.
976  * Output arguments:
977  *  producer - handle of producer
978  *  ncells   - number of cells in result or the number of items in the list when
979  *             idx == -1.
980  *  cells    - array of decoded cells
981  */
982 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)983 ofw_bus_parse_xref_list_internal(phandle_t node, const char *list_name,
984     const char *cells_name, int idx, phandle_t *producer, int *ncells,
985     pcell_t **cells)
986 {
987 	phandle_t pnode;
988 	phandle_t *elems;
989 	uint32_t  pcells;
990 	int rv, i, j, nelems, cnt;
991 
992 	elems = NULL;
993 	nelems = OF_getencprop_alloc_multi(node, list_name,  sizeof(*elems),
994 	    (void **)&elems);
995 	if (nelems <= 0)
996 		return (ENOENT);
997 	rv = (idx == -1) ? 0 : ENOENT;
998 	for (i = 0, cnt = 0; i < nelems; i += pcells, cnt++) {
999 		pnode = elems[i++];
1000 		if (OF_getencprop(OF_node_from_xref(pnode),
1001 		    cells_name, &pcells, sizeof(pcells)) == -1) {
1002 			printf("Missing %s property\n", cells_name);
1003 			rv = ENOENT;
1004 			break;
1005 		}
1006 
1007 		if ((i + pcells) > nelems) {
1008 			printf("Invalid %s property value <%d>\n", cells_name,
1009 			    pcells);
1010 			rv = ERANGE;
1011 			break;
1012 		}
1013 		if (cnt == idx) {
1014 			*cells= malloc(pcells * sizeof(**cells), M_OFWPROP,
1015 			    M_WAITOK);
1016 			*producer = pnode;
1017 			*ncells = pcells;
1018 			for (j = 0; j < pcells; j++)
1019 				(*cells)[j] = elems[i + j];
1020 			rv = 0;
1021 			break;
1022 		}
1023 	}
1024 	if (elems != NULL)
1025 		free(elems, M_OFWPROP);
1026 	if (idx == -1 && rv == 0)
1027 		*ncells = cnt;
1028 	return (rv);
1029 }
1030 
1031 /*
1032  * Parse property that contain list of xrefs and values
1033  * (like standard "clocks" and "resets" properties)
1034  * Input arguments:
1035  *  node - consumers device node
1036  *  list_name  - name of parsed list - "clocks"
1037  *  cells_name - name of size property - "#clock-cells"
1038  *  idx - the index of the requested list entry (>= 0)
1039  * Output arguments:
1040  *  producer - handle of producer
1041  *  ncells   - number of cells in result
1042  *  cells    - array of decoded cells
1043  */
1044 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)1045 ofw_bus_parse_xref_list_alloc(phandle_t node, const char *list_name,
1046     const char *cells_name, int idx, phandle_t *producer, int *ncells,
1047     pcell_t **cells)
1048 {
1049 
1050 	KASSERT(idx >= 0,
1051 	    ("ofw_bus_parse_xref_list_alloc: negative index supplied"));
1052 
1053 	return (ofw_bus_parse_xref_list_internal(node, list_name, cells_name,
1054 		    idx, producer, ncells, cells));
1055 }
1056 
1057 /*
1058  * Parse property that contain list of xrefs and values
1059  * (like standard "clocks" and "resets" properties)
1060  * and determine the number of items in the list
1061  * Input arguments:
1062  *  node - consumers device node
1063  *  list_name  - name of parsed list - "clocks"
1064  *  cells_name - name of size property - "#clock-cells"
1065  * Output arguments:
1066  *  count - number of items in list
1067  */
1068 int
ofw_bus_parse_xref_list_get_length(phandle_t node,const char * list_name,const char * cells_name,int * count)1069 ofw_bus_parse_xref_list_get_length(phandle_t node, const char *list_name,
1070     const char *cells_name, int *count)
1071 {
1072 
1073 	return (ofw_bus_parse_xref_list_internal(node, list_name, cells_name,
1074 		    -1, NULL, count, NULL));
1075 }
1076 
1077 /*
1078  * Find index of string in string list property (case sensitive).
1079  */
1080 int
ofw_bus_find_string_index(phandle_t node,const char * list_name,const char * name,int * idx)1081 ofw_bus_find_string_index(phandle_t node, const char *list_name,
1082     const char *name, int *idx)
1083 {
1084 	char *elems;
1085 	int rv, i, cnt, nelems;
1086 
1087 	elems = NULL;
1088 	nelems = OF_getprop_alloc(node, list_name, (void **)&elems);
1089 	if (nelems <= 0)
1090 		return (ENOENT);
1091 
1092 	rv = ENOENT;
1093 	for (i = 0, cnt = 0; i < nelems; cnt++) {
1094 		if (strcmp(elems + i, name) == 0) {
1095 			*idx = cnt;
1096 			rv = 0;
1097 			break;
1098 		}
1099 		i += strlen(elems + i) + 1;
1100 	}
1101 
1102 	if (elems != NULL)
1103 		free(elems, M_OFWPROP);
1104 	return (rv);
1105 }
1106 
1107 /*
1108  * Create zero terminated array of strings from string list property.
1109  */
1110 int
ofw_bus_string_list_to_array(phandle_t node,const char * list_name,const char *** out_array)1111 ofw_bus_string_list_to_array(phandle_t node, const char *list_name,
1112    const char ***out_array)
1113 {
1114 	char *elems, *tptr;
1115 	const char **array;
1116 	int i, cnt, nelems, len;
1117 
1118 	elems = NULL;
1119 	nelems = OF_getprop_alloc(node, list_name, (void **)&elems);
1120 	if (nelems <= 0)
1121 		return (nelems);
1122 
1123 	/* Count number of strings. */
1124 	for (i = 0, cnt = 0; i < nelems; cnt++)
1125 		i += strlen(elems + i) + 1;
1126 
1127 	/* Allocate space for arrays and all strings. */
1128 	array = malloc((cnt + 1) * sizeof(char *) + nelems, M_OFWPROP,
1129 	    M_WAITOK);
1130 
1131 	/* Get address of first string. */
1132 	tptr = (char *)(array + cnt + 1);
1133 
1134 	/* Copy strings. */
1135 	memcpy(tptr, elems, nelems);
1136 	free(elems, M_OFWPROP);
1137 
1138 	/* Fill string pointers. */
1139 	for (i = 0, cnt = 0; i < nelems; cnt++) {
1140 		len = strlen(tptr) + 1;
1141 		array[cnt] = tptr;
1142 		i += len;
1143 		tptr += len;
1144 	}
1145 	array[cnt] = NULL;
1146 	*out_array = array;
1147 
1148 	return (cnt);
1149 }
1150