ofw_subr.c revision 1.42 1 /* $NetBSD: ofw_subr.c,v 1.42 2021/01/18 02:35:49 thorpej Exp $ */
2
3 /*
4 * Copyright 1998
5 * Digital Equipment Corporation. All rights reserved.
6 *
7 * This software is furnished under license and may be used and
8 * copied only in accordance with the following terms and conditions.
9 * Subject to these conditions, you may download, copy, install,
10 * use, modify and distribute this software in source and/or binary
11 * form. No title or ownership is transferred hereby.
12 *
13 * 1) Any source code used, modified or distributed must reproduce
14 * and retain this copyright notice and list of conditions as
15 * they appear in the source file.
16 *
17 * 2) No right is granted to use any trade name, trademark, or logo of
18 * Digital Equipment Corporation. Neither the "Digital Equipment
19 * Corporation" name nor any trademark or logo of Digital Equipment
20 * Corporation may be used to endorse or promote products derived
21 * from this software without the prior written permission of
22 * Digital Equipment Corporation.
23 *
24 * 3) This software is provided "AS-IS" and any express or implied
25 * warranties, including but not limited to, any implied warranties
26 * of merchantability, fitness for a particular purpose, or
27 * non-infringement are disclaimed. In no event shall DIGITAL be
28 * liable for any damages whatsoever, and in particular, DIGITAL
29 * shall not be liable for special, indirect, consequential, or
30 * incidental damages or damages for lost profits, loss of
31 * revenue or loss of use, whether such damages arise in contract,
32 * negligence, tort, under statute, in equity, at law or otherwise,
33 * even if advised of the possibility of such damage.
34 */
35
36 #include <sys/cdefs.h>
37 __KERNEL_RCSID(0, "$NetBSD: ofw_subr.c,v 1.42 2021/01/18 02:35:49 thorpej Exp $");
38
39 #include <sys/param.h>
40 #include <sys/device.h>
41 #include <sys/systm.h>
42 #include <sys/malloc.h>
43 #include <dev/ofw/openfirm.h>
44
45 #define OFW_MAX_STACK_BUF_SIZE 256
46 #define OFW_PATH_BUF_SIZE 512
47
48 /*
49 * int of_decode_int(p)
50 *
51 * This routine converts OFW encoded-int datums
52 * into the integer format of the host machine.
53 *
54 * It is primarily used to convert integer properties
55 * returned by the OF_getprop routine.
56 *
57 * Arguments:
58 * p pointer to unsigned char array which is an
59 * OFW-encoded integer.
60 *
61 * Return Value:
62 * Decoded integer value of argument p.
63 *
64 * Side Effects:
65 * None.
66 */
67 int
68 of_decode_int(const unsigned char *p)
69 {
70 unsigned int i = *p++ << 8;
71 i = (i + *p++) << 8;
72 i = (i + *p++) << 8;
73 return (i + *p);
74 }
75
76 /*
77 * int of_compatible(phandle, strings)
78 *
79 * This routine checks an OFW node's "compatible" entry to see if
80 * it matches any of the provided strings.
81 *
82 * It should be used when determining whether a driver can drive
83 * a particular device.
84 *
85 * Arguments:
86 * phandle OFW phandle of device to be checked for
87 * compatibility.
88 * strings Array of containing expected "compatibility"
89 * property values, presence of any of which
90 * indicates compatibility.
91 *
92 * Return Value:
93 * -1 if none of the strings are found in phandle's "compatibility"
94 * property, or the reverse index of the matching string in the
95 * phandle's "compatibility" property.
96 *
97 * Side Effects:
98 * None.
99 */
100 int
101 of_compatible(int phandle, const char * const *strings)
102 {
103
104 int len, olen, allocated, nstr, cstr, rv;
105 char *buf, sbuf[OFW_MAX_STACK_BUF_SIZE];
106 const char *sp, *nsp;
107
108 len = OF_getproplen(phandle, "compatible");
109 if (len <= 0)
110 return (-1);
111
112 if (len > sizeof(sbuf)) {
113 buf = malloc(len, M_TEMP, M_WAITOK);
114 allocated = 1;
115 } else {
116 buf = sbuf;
117 allocated = 0;
118 }
119
120 /* 'compatible' size should not change. */
121 if (OF_getprop(phandle, "compatible", buf, len) != len) {
122 rv = -1;
123 goto out;
124 }
125
126 /* count 'compatible' strings */
127 sp = buf;
128 nstr = 0;
129 olen = len;
130 while (len && (nsp = memchr(sp, 0, len)) != NULL) {
131 nsp++; /* skip over NUL char */
132 len -= (nsp - sp);
133 sp = nsp;
134 nstr++;
135 }
136 len = olen;
137
138 sp = buf;
139 rv = nstr;
140 while (len && (nsp = memchr(sp, 0, len)) != NULL) {
141 rv--;
142 /* look for a match among the strings provided */
143 for (cstr = 0; strings[cstr] != NULL; cstr++)
144 if (strcmp(sp, strings[cstr]) == 0)
145 goto out;
146
147 nsp++; /* skip over NUL char */
148 len -= (nsp - sp);
149 sp = nsp;
150 }
151 rv = -1;
152
153 out:
154 if (allocated)
155 free(buf, M_TEMP);
156 return (rv);
157 }
158
159 /*
160 * int of_match_compatible(phandle, strings)
161 *
162 * This routine checks an OFW node's "compatible" entry to see if
163 * it matches any of the provided strings.
164 *
165 * It should be used when determining whether a driver can drive
166 * a particular device.
167 *
168 * Arguments:
169 * phandle OFW phandle of device to be checked for
170 * compatibility.
171 * strings Array of containing expected "compatibility"
172 * property values, presence of any of which
173 * indicates compatibility.
174 *
175 * Return Value:
176 * 0 if none of the strings are found in phandle's "compatibility"
177 * property, or a positive number based on the reverse index of the
178 * matching string in the phandle's "compatibility" property, plus 1.
179 *
180 * Side Effects:
181 * None.
182 */
183 int
184 of_match_compatible(int phandle, const char * const *strings)
185 {
186 return of_compatible(phandle, strings) + 1;
187 }
188
189 /*
190 * int of_match_compat_data(phandle, compat_data)
191 *
192 * This routine searches an array of compat_data structures for a
193 * matching "compatible" entry matching the supplied OFW node.
194 *
195 * It should be used when determining whether a driver can drive
196 * a particular device.
197 *
198 * Arguments:
199 * phandle OFW phandle of device to be checked for
200 * compatibility.
201 * compat_data Array of possible compat entry strings and
202 * associated metadata. The last entry in the
203 * list should have a "compat" of NULL to terminate
204 * the list.
205 *
206 * Return Value:
207 * 0 if none of the strings are found in phandle's "compatibility"
208 * property, or a positive number based on the reverse index of the
209 * matching string in the phandle's "compatibility" property, plus 1.
210 *
211 * Side Effects:
212 * None.
213 */
214 int
215 of_match_compat_data(int phandle,
216 const struct device_compatible_entry *compat_data)
217 {
218 for (; compat_data->compat != NULL; compat_data++) {
219 const char *compat[] = { compat_data->compat, NULL };
220 const int match = of_match_compatible(phandle, compat);
221 if (match)
222 return match;
223 }
224 return 0;
225 }
226
227 /*
228 * const struct of_compat_data *of_search_compatible(phandle, compat_data)
229 *
230 * This routine searches an array of compat_data structures for a
231 * matching "compatible" entry matching the supplied OFW node.
232 *
233 * Arguments:
234 * phandle OFW phandle of device to be checked for
235 * compatibility.
236 * compat_data Array of possible compat entry strings and
237 * associated metadata. The last entry in the
238 * list should have a "compat" of NULL to terminate
239 * the list.
240 *
241 * Return Value:
242 * The first matching compat_data entry in the array. If no matches
243 * are found, the terminating ("compat" of NULL) record is returned.
244 *
245 * Side Effects:
246 * None.
247 */
248 const struct device_compatible_entry *
249 of_search_compatible(int phandle,
250 const struct device_compatible_entry *compat_data)
251 {
252 for (; compat_data->compat != NULL; compat_data++) {
253 const char *compat[] = { compat_data->compat, NULL };
254 if (of_match_compatible(phandle, compat))
255 break;
256 }
257 return compat_data;
258 }
259
260 /*
261 * int of_packagename(phandle, buf, bufsize)
262 *
263 * This routine places the last component of an OFW node's name
264 * into a user-provided buffer.
265 *
266 * It can be used during autoconfiguration to make printing of
267 * device names more informative.
268 *
269 * Arguments:
270 * phandle OFW phandle of device whose name name is
271 * desired.
272 * buf Buffer to contain device name, provided by
273 * caller. (For now, must be at least 4
274 * bytes long.)
275 * bufsize Length of buffer referenced by 'buf', in
276 * bytes.
277 *
278 * Return Value:
279 * -1 if the device path name could not be obtained or would
280 * not fit in the allocated temporary buffer, or zero otherwise
281 * (meaning that the leaf node name was successfully extracted).
282 *
283 * Side Effects:
284 * If the leaf node name was successfully extracted, 'buf' is
285 * filled in with at most 'bufsize' bytes of the leaf node
286 * name. If the leaf node was not successfully extracted, a
287 * somewhat meaningful string is placed in the buffer. In
288 * either case, the contents of 'buf' will be NUL-terminated.
289 */
290 int
291 of_packagename(int phandle, char *buf, int bufsize)
292 {
293 char *pbuf;
294 const char *lastslash;
295 int l, rv;
296
297 pbuf = malloc(OFW_PATH_BUF_SIZE, M_TEMP, M_WAITOK);
298 l = OF_package_to_path(phandle, pbuf, OFW_PATH_BUF_SIZE);
299
300 /* check that we could get the name, and that it's not too long. */
301 if (l < 0 ||
302 (l == OFW_PATH_BUF_SIZE && pbuf[OFW_PATH_BUF_SIZE - 1] != '\0')) {
303 if (bufsize >= 25)
304 snprintf(buf, bufsize, "??? (phandle 0x%x)", phandle);
305 else if (bufsize >= 4)
306 strlcpy(buf, "???", bufsize);
307 else
308 panic("of_packagename: bufsize = %d is silly",
309 bufsize);
310 rv = -1;
311 } else {
312 pbuf[l] = '\0';
313 lastslash = strrchr(pbuf, '/');
314 strlcpy(buf, (lastslash == NULL) ? pbuf : (lastslash + 1),
315 bufsize);
316 rv = 0;
317 }
318
319 free(pbuf, M_TEMP);
320 return (rv);
321 }
322
323 /*
324 * Find the first child of a given node that matches name. Does not recurse.
325 */
326 int
327 of_find_firstchild_byname(int node, const char *name)
328 {
329 char namex[32];
330 int nn;
331
332 for (nn = OF_child(node); nn; nn = OF_peer(nn)) {
333 memset(namex, 0, sizeof(namex));
334 if (OF_getprop(nn, "name", namex, sizeof(namex)) == -1)
335 continue;
336 if (strcmp(name, namex) == 0)
337 return nn;
338 }
339 return -1;
340 }
341
342 /*
343 * Find a child node that is compatible with str. Recurses, starting at node.
344 */
345 int
346 of_find_bycompat(int node, const char *str)
347 {
348 const char * compatible[] = { str, NULL };
349 int child, ret;
350
351 for (child = OF_child(node); child; child = OF_peer(child)) {
352 if (of_match_compatible(child, compatible) != 0)
353 return child;
354 ret = of_find_bycompat(child, str);
355 if (ret != -1)
356 return ret;
357 }
358
359 return -1;
360 }
361
362 /*
363 * Find a give node by name. Recurses, and seems to walk upwards too.
364 */
365
366 int
367 of_getnode_byname(int start, const char *target)
368 {
369 int node, next;
370 char name[64];
371
372 if (start == 0)
373 start = OF_peer(0);
374
375 for (node = start; node; node = next) {
376 memset(name, 0, sizeof name);
377 OF_getprop(node, "name", name, sizeof name - 1);
378 if (strcmp(name, target) == 0)
379 break;
380
381 if ((next = OF_child(node)) != 0)
382 continue;
383
384 while (node) {
385 if ((next = OF_peer(node)) != 0)
386 break;
387 node = OF_parent(node);
388 }
389 }
390
391 /* XXX is this correct? */
392 return node;
393 }
394
395 /*
396 * Create a uint32_t integer property from an OFW node property.
397 */
398
399 boolean_t
400 of_to_uint32_prop(prop_dictionary_t dict, int node, const char *ofname,
401 const char *propname)
402 {
403 uint32_t prop;
404
405 if (OF_getprop(node, ofname, &prop, sizeof(prop)) != sizeof(prop))
406 return FALSE;
407
408 return(prop_dictionary_set_uint32(dict, propname, prop));
409 }
410
411 /*
412 * Create a data property from an OFW node property. Max size of 256bytes.
413 */
414
415 boolean_t
416 of_to_dataprop(prop_dictionary_t dict, int node, const char *ofname,
417 const char *propname)
418 {
419 int len;
420 uint8_t prop[256];
421
422 len = OF_getprop(node, ofname, prop, 256);
423 if (len < 1)
424 return FALSE;
425
426 return prop_dictionary_set_data(dict, propname, prop, len);
427 }
428
429 /*
430 * look at output-device, see if there's a Sun-typical video mode specifier as
431 * in screen:r1024x768x60 attached. If found copy it into *buffer, otherwise
432 * return NULL
433 */
434
435 char *
436 of_get_mode_string(char *buffer, int len)
437 {
438 int options;
439 char *pos, output_device[256];
440
441 /*
442 * finally, let's see if there's a video mode specified in
443 * output-device and pass it on so there's at least some way
444 * to program video modes
445 */
446 options = OF_finddevice("/options");
447 if ((options == 0) || (options == -1))
448 return NULL;
449 if (OF_getprop(options, "output-device", output_device, 256) == 0)
450 return NULL;
451
452 /* find the mode string if there is one */
453 pos = strstr(output_device, ":r");
454 if (pos == NULL)
455 return NULL;
456 strncpy(buffer, pos + 2, len);
457 return buffer;
458 }
459
460 /*
461 * Iterate over the subtree of a i2c controller node.
462 * Add all sub-devices into an array as part of the controller's
463 * device properties.
464 * This is used by the i2c bus attach code to do direct configuration.
465 */
466 void
467 of_enter_i2c_devs(prop_dictionary_t props, int ofnode, size_t cell_size,
468 int addr_shift)
469 {
470 int node, len;
471 char name[32];
472 uint64_t reg64;
473 uint32_t reg32;
474 uint64_t addr;
475 prop_array_t array = NULL;
476 prop_dictionary_t dev;
477
478 for (node = OF_child(ofnode); node; node = OF_peer(node)) {
479 if (OF_getprop(node, "name", name, sizeof(name)) <= 0)
480 continue;
481 len = OF_getproplen(node, "reg");
482 addr = 0;
483 if (cell_size == 8 && len >= sizeof(reg64)) {
484 if (OF_getprop(node, "reg", ®64, sizeof(reg64))
485 < sizeof(reg64))
486 continue;
487 addr = be64toh(reg64);
488 /*
489 * The i2c bus number (0 or 1) is encoded in bit 33
490 * of the register, but we encode it in bit 8 of
491 * i2c_addr_t.
492 */
493 if (addr & 0x100000000)
494 addr = (addr & 0xff) | 0x100;
495 } else if (cell_size == 4 && len >= sizeof(reg32)) {
496 if (OF_getprop(node, "reg", ®32, sizeof(reg32))
497 < sizeof(reg32))
498 continue;
499 addr = be32toh(reg32);
500 } else {
501 continue;
502 }
503 addr >>= addr_shift;
504 if (addr == 0) continue;
505
506 if (array == NULL)
507 array = prop_array_create();
508
509 dev = prop_dictionary_create();
510 prop_dictionary_set_string(dev, "name", name);
511 prop_dictionary_set_uint32(dev, "addr", addr);
512 prop_dictionary_set_uint64(dev, "cookie", node);
513 of_to_dataprop(dev, node, "compatible", "compatible");
514 prop_array_add(array, dev);
515 prop_object_release(dev);
516 }
517
518 if (array != NULL) {
519 prop_dictionary_set(props, "i2c-child-devices", array);
520 prop_object_release(array);
521 }
522 }
523
524 void
525 of_enter_spi_devs(prop_dictionary_t props, int ofnode, size_t cell_size)
526 {
527 int node, len;
528 char name[32];
529 uint64_t reg64;
530 uint32_t reg32;
531 uint32_t slave;
532 u_int32_t maxfreq;
533 prop_array_t array = NULL;
534 prop_dictionary_t dev;
535 int mode;
536
537 for (node = OF_child(ofnode); node; node = OF_peer(node)) {
538 if (OF_getprop(node, "name", name, sizeof(name)) <= 0)
539 continue;
540 len = OF_getproplen(node, "reg");
541 slave = 0;
542 if (cell_size == 8 && len >= sizeof(reg64)) {
543 if (OF_getprop(node, "reg", ®64, sizeof(reg64))
544 < sizeof(reg64))
545 continue;
546 slave = be64toh(reg64);
547 } else if (cell_size == 4 && len >= sizeof(reg32)) {
548 if (OF_getprop(node, "reg", ®32, sizeof(reg32))
549 < sizeof(reg32))
550 continue;
551 slave = be32toh(reg32);
552 } else {
553 continue;
554 }
555 if (of_getprop_uint32(node, "spi-max-frequency", &maxfreq)) {
556 maxfreq = 0;
557 }
558 mode = ((int)of_hasprop(node, "cpol") << 1) | (int)of_hasprop(node, "cpha");
559
560 if (array == NULL)
561 array = prop_array_create();
562
563 dev = prop_dictionary_create();
564 prop_dictionary_set_string(dev, "name", name);
565 prop_dictionary_set_uint32(dev, "slave", slave);
566 prop_dictionary_set_uint32(dev, "mode", mode);
567 if (maxfreq > 0)
568 prop_dictionary_set_uint32(dev, "spi-max-frequency", maxfreq);
569 prop_dictionary_set_uint64(dev, "cookie", node);
570 of_to_dataprop(dev, node, "compatible", "compatible");
571 prop_array_add(array, dev);
572 prop_object_release(dev);
573 }
574
575 if (array != NULL) {
576 prop_dictionary_set(props, "spi-child-devices", array);
577 prop_object_release(array);
578 }
579 }
580
581
582 /*
583 * Returns true if the specified property is present.
584 */
585 bool
586 of_hasprop(int node, const char *prop)
587 {
588 return OF_getproplen(node, prop) >= 0;
589 }
590
591 /*
592 * Get the value of a uint32 property, compensating for host byte order.
593 * Returns 0 on success, non-zero on failure.
594 */
595 int
596 of_getprop_uint32(int node, const char *prop, uint32_t *val)
597 {
598 uint32_t v;
599 int len;
600
601 len = OF_getprop(node, prop, &v, sizeof(v));
602 if (len != sizeof(v))
603 return -1;
604
605 *val = be32toh(v);
606 return 0;
607 }
608
609 int
610 of_getprop_uint32_array(int node, const char *prop, uint32_t *array, int n)
611 {
612 uint32_t *v = array;
613 int len;
614
615 len = OF_getprop(node, prop, array, n * sizeof(*v));
616 if (len < (int)(n * sizeof(*v)))
617 return -1;
618
619 for (; n > 0; n--) {
620 BE32TOH(*v);
621 v++;
622 }
623
624 return 0;
625 }
626 /*
627 * Get the value of a uint64 property, compensating for host byte order.
628 * Returns 0 on success, non-zero on failure.
629 */
630 int
631 of_getprop_uint64(int node, const char *prop, uint64_t *val)
632 {
633 uint64_t v;
634 int len;
635
636 len = OF_getprop(node, prop, &v, sizeof(v));
637 if (len != sizeof(v))
638 return -1;
639
640 *val = be64toh(v);
641 return 0;
642 }
643