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