1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 3 * 4 * Copyright (c) 2009-2014 The FreeBSD Foundation 5 * All rights reserved. 6 * 7 * This software was developed by Andrew Turner under sponsorship from 8 * the FreeBSD Foundation. 9 * This software was developed by Semihalf under sponsorship from 10 * the FreeBSD Foundation. 11 * 12 * Redistribution and use in source and binary forms, with or without 13 * modification, are permitted provided that the following conditions 14 * are met: 15 * 1. Redistributions of source code must retain the above copyright 16 * notice, this list of conditions and the following disclaimer. 17 * 2. Redistributions in binary form must reproduce the above copyright 18 * notice, this list of conditions and the following disclaimer in the 19 * documentation and/or other materials provided with the distribution. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31 * SUCH DAMAGE. 32 */ 33 34 #include <sys/cdefs.h> 35 __FBSDID("$FreeBSD$"); 36 37 #include <sys/param.h> 38 #include <sys/systm.h> 39 #include <sys/kernel.h> 40 #include <sys/module.h> 41 #include <sys/bus.h> 42 #include <sys/limits.h> 43 #include <sys/sysctl.h> 44 45 #include <machine/resource.h> 46 47 #include <dev/fdt/fdt_common.h> 48 #include <dev/ofw/ofw_bus.h> 49 #include <dev/ofw/ofw_bus_subr.h> 50 #include <dev/ofw/openfirm.h> 51 52 #include "ofw_bus_if.h" 53 54 #ifdef DEBUG 55 #define debugf(fmt, args...) do { printf("%s(): ", __func__); \ 56 printf(fmt,##args); } while (0) 57 #else 58 #define debugf(fmt, args...) 59 #endif 60 61 #define FDT_COMPAT_LEN 255 62 #define FDT_TYPE_LEN 64 63 64 #define FDT_REG_CELLS 4 65 #define FDT_RANGES_SIZE 48 66 67 SYSCTL_NODE(_hw, OID_AUTO, fdt, CTLFLAG_RD, 0, "Flattened Device Tree"); 68 69 vm_paddr_t fdt_immr_pa; 70 vm_offset_t fdt_immr_va; 71 vm_offset_t fdt_immr_size; 72 73 struct fdt_ic_list fdt_ic_list_head = SLIST_HEAD_INITIALIZER(fdt_ic_list_head); 74 75 static int fdt_is_compatible(phandle_t, const char *); 76 77 static int 78 fdt_get_range_by_busaddr(phandle_t node, u_long addr, u_long *base, 79 u_long *size) 80 { 81 pcell_t ranges[32], *rangesptr; 82 pcell_t addr_cells, size_cells, par_addr_cells; 83 u_long bus_addr, par_bus_addr, pbase, psize; 84 int err, i, len, tuple_size, tuples; 85 86 if (node == 0) { 87 *base = 0; 88 *size = ULONG_MAX; 89 return (0); 90 } 91 92 if ((fdt_addrsize_cells(node, &addr_cells, &size_cells)) != 0) 93 return (ENXIO); 94 /* 95 * Process 'ranges' property. 96 */ 97 par_addr_cells = fdt_parent_addr_cells(node); 98 if (par_addr_cells > 2) { 99 return (ERANGE); 100 } 101 102 len = OF_getproplen(node, "ranges"); 103 if (len < 0) 104 return (-1); 105 if (len > sizeof(ranges)) 106 return (ENOMEM); 107 if (len == 0) { 108 return (fdt_get_range_by_busaddr(OF_parent(node), addr, 109 base, size)); 110 } 111 112 if (OF_getprop(node, "ranges", ranges, sizeof(ranges)) <= 0) 113 return (EINVAL); 114 115 tuple_size = addr_cells + par_addr_cells + size_cells; 116 tuples = len / (tuple_size * sizeof(cell_t)); 117 118 if (par_addr_cells > 2 || addr_cells > 2 || size_cells > 2) 119 return (ERANGE); 120 121 *base = 0; 122 *size = 0; 123 124 for (i = 0; i < tuples; i++) { 125 rangesptr = &ranges[i * tuple_size]; 126 127 bus_addr = fdt_data_get((void *)rangesptr, addr_cells); 128 if (bus_addr != addr) 129 continue; 130 rangesptr += addr_cells; 131 132 par_bus_addr = fdt_data_get((void *)rangesptr, par_addr_cells); 133 rangesptr += par_addr_cells; 134 135 err = fdt_get_range_by_busaddr(OF_parent(node), par_bus_addr, 136 &pbase, &psize); 137 if (err > 0) 138 return (err); 139 if (err == 0) 140 *base = pbase; 141 else 142 *base = par_bus_addr; 143 144 *size = fdt_data_get((void *)rangesptr, size_cells); 145 146 return (0); 147 } 148 149 return (EINVAL); 150 } 151 152 int 153 fdt_get_range(phandle_t node, int range_id, u_long *base, u_long *size) 154 { 155 pcell_t ranges[FDT_RANGES_SIZE], *rangesptr; 156 pcell_t addr_cells, size_cells, par_addr_cells; 157 u_long par_bus_addr, pbase, psize; 158 int err, len, tuple_size, tuples; 159 160 if ((fdt_addrsize_cells(node, &addr_cells, &size_cells)) != 0) 161 return (ENXIO); 162 /* 163 * Process 'ranges' property. 164 */ 165 par_addr_cells = fdt_parent_addr_cells(node); 166 if (par_addr_cells > 2) 167 return (ERANGE); 168 169 len = OF_getproplen(node, "ranges"); 170 if (len > sizeof(ranges)) 171 return (ENOMEM); 172 if (len == 0) { 173 *base = 0; 174 *size = ULONG_MAX; 175 return (0); 176 } 177 178 if (!(range_id < len)) 179 return (ERANGE); 180 181 if (OF_getprop(node, "ranges", ranges, sizeof(ranges)) <= 0) 182 return (EINVAL); 183 184 tuple_size = sizeof(pcell_t) * (addr_cells + par_addr_cells + 185 size_cells); 186 tuples = len / tuple_size; 187 188 if (par_addr_cells > 2 || addr_cells > 2 || size_cells > 2) 189 return (ERANGE); 190 191 *base = 0; 192 *size = 0; 193 rangesptr = &ranges[range_id]; 194 195 *base = fdt_data_get((void *)rangesptr, addr_cells); 196 rangesptr += addr_cells; 197 198 par_bus_addr = fdt_data_get((void *)rangesptr, par_addr_cells); 199 rangesptr += par_addr_cells; 200 201 err = fdt_get_range_by_busaddr(OF_parent(node), par_bus_addr, 202 &pbase, &psize); 203 if (err == 0) 204 *base += pbase; 205 else 206 *base += par_bus_addr; 207 208 *size = fdt_data_get((void *)rangesptr, size_cells); 209 return (0); 210 } 211 212 int 213 fdt_immr_addr(vm_offset_t immr_va) 214 { 215 phandle_t node; 216 u_long base, size; 217 int r; 218 219 /* 220 * Try to access the SOC node directly i.e. through /aliases/. 221 */ 222 if ((node = OF_finddevice("soc")) != 0) 223 if (fdt_is_compatible(node, "simple-bus")) 224 goto moveon; 225 /* 226 * Find the node the long way. 227 */ 228 if ((node = OF_finddevice("/")) == 0) 229 return (ENXIO); 230 231 if ((node = fdt_find_compatible(node, "simple-bus", 0)) == 0) 232 return (ENXIO); 233 234 moveon: 235 if ((r = fdt_get_range(node, 0, &base, &size)) == 0) { 236 fdt_immr_pa = base; 237 fdt_immr_va = immr_va; 238 fdt_immr_size = size; 239 } 240 241 return (r); 242 } 243 244 /* 245 * This routine is an early-usage version of the ofw_bus_is_compatible() when 246 * the ofw_bus I/F is not available (like early console routines and similar). 247 * Note the buffer has to be on the stack since malloc() is usually not 248 * available in such cases either. 249 */ 250 static int 251 fdt_is_compatible(phandle_t node, const char *compatstr) 252 { 253 char buf[FDT_COMPAT_LEN]; 254 char *compat; 255 int len, onelen, l, rv; 256 257 if ((len = OF_getproplen(node, "compatible")) <= 0) 258 return (0); 259 260 compat = (char *)&buf; 261 bzero(compat, FDT_COMPAT_LEN); 262 263 if (OF_getprop(node, "compatible", compat, FDT_COMPAT_LEN) < 0) 264 return (0); 265 266 onelen = strlen(compatstr); 267 rv = 0; 268 while (len > 0) { 269 if (strncasecmp(compat, compatstr, onelen) == 0) { 270 /* Found it. */ 271 rv = 1; 272 break; 273 } 274 /* Slide to the next sub-string. */ 275 l = strlen(compat) + 1; 276 compat += l; 277 len -= l; 278 } 279 280 return (rv); 281 } 282 283 int 284 fdt_is_compatible_strict(phandle_t node, const char *compatible) 285 { 286 char compat[FDT_COMPAT_LEN]; 287 288 if (OF_getproplen(node, "compatible") <= 0) 289 return (0); 290 291 if (OF_getprop(node, "compatible", compat, FDT_COMPAT_LEN) < 0) 292 return (0); 293 294 if (strncasecmp(compat, compatible, FDT_COMPAT_LEN) == 0) 295 /* This fits. */ 296 return (1); 297 298 return (0); 299 } 300 301 phandle_t 302 fdt_find_compatible(phandle_t start, const char *compat, int strict) 303 { 304 phandle_t child; 305 306 /* 307 * Traverse all children of 'start' node, and find first with 308 * matching 'compatible' property. 309 */ 310 for (child = OF_child(start); child != 0; child = OF_peer(child)) 311 if (fdt_is_compatible(child, compat)) { 312 if (strict) 313 if (!fdt_is_compatible_strict(child, compat)) 314 continue; 315 return (child); 316 } 317 return (0); 318 } 319 320 phandle_t 321 fdt_depth_search_compatible(phandle_t start, const char *compat, int strict) 322 { 323 phandle_t child, node; 324 325 /* 326 * Depth-search all descendants of 'start' node, and find first with 327 * matching 'compatible' property. 328 */ 329 for (node = OF_child(start); node != 0; node = OF_peer(node)) { 330 if (fdt_is_compatible(node, compat) && 331 (strict == 0 || fdt_is_compatible_strict(node, compat))) { 332 return (node); 333 } 334 child = fdt_depth_search_compatible(node, compat, strict); 335 if (child != 0) 336 return (child); 337 } 338 return (0); 339 } 340 341 int 342 fdt_is_enabled(phandle_t node) 343 { 344 char *stat; 345 int ena, len; 346 347 len = OF_getprop_alloc(node, "status", sizeof(char), 348 (void **)&stat); 349 350 if (len <= 0) 351 /* It is OK if no 'status' property. */ 352 return (1); 353 354 /* Anything other than 'okay' means disabled. */ 355 ena = 0; 356 if (strncmp((char *)stat, "okay", len) == 0) 357 ena = 1; 358 359 OF_prop_free(stat); 360 return (ena); 361 } 362 363 int 364 fdt_is_type(phandle_t node, const char *typestr) 365 { 366 char type[FDT_TYPE_LEN]; 367 368 if (OF_getproplen(node, "device_type") <= 0) 369 return (0); 370 371 if (OF_getprop(node, "device_type", type, FDT_TYPE_LEN) < 0) 372 return (0); 373 374 if (strncasecmp(type, typestr, FDT_TYPE_LEN) == 0) 375 /* This fits. */ 376 return (1); 377 378 return (0); 379 } 380 381 int 382 fdt_parent_addr_cells(phandle_t node) 383 { 384 pcell_t addr_cells; 385 386 /* Find out #address-cells of the superior bus. */ 387 if (OF_searchprop(OF_parent(node), "#address-cells", &addr_cells, 388 sizeof(addr_cells)) <= 0) 389 return (2); 390 391 return ((int)fdt32_to_cpu(addr_cells)); 392 } 393 394 int 395 fdt_pm_is_enabled(phandle_t node) 396 { 397 int ret; 398 399 ret = 1; 400 401 #if defined(SOC_MV_KIRKWOOD) || defined(SOC_MV_DISCOVERY) 402 ret = fdt_pm(node); 403 #endif 404 return (ret); 405 } 406 407 u_long 408 fdt_data_get(void *data, int cells) 409 { 410 411 if (cells == 1) 412 return (fdt32_to_cpu(*((uint32_t *)data))); 413 414 return (fdt64_to_cpu(*((uint64_t *)data))); 415 } 416 417 int 418 fdt_addrsize_cells(phandle_t node, int *addr_cells, int *size_cells) 419 { 420 pcell_t cell; 421 int cell_size; 422 423 /* 424 * Retrieve #{address,size}-cells. 425 */ 426 cell_size = sizeof(cell); 427 if (OF_getencprop(node, "#address-cells", &cell, cell_size) < cell_size) 428 cell = 2; 429 *addr_cells = (int)cell; 430 431 if (OF_getencprop(node, "#size-cells", &cell, cell_size) < cell_size) 432 cell = 1; 433 *size_cells = (int)cell; 434 435 if (*addr_cells > 3 || *size_cells > 2) 436 return (ERANGE); 437 return (0); 438 } 439 440 int 441 fdt_data_to_res(pcell_t *data, int addr_cells, int size_cells, u_long *start, 442 u_long *count) 443 { 444 445 /* Address portion. */ 446 if (addr_cells > 2) 447 return (ERANGE); 448 449 *start = fdt_data_get((void *)data, addr_cells); 450 data += addr_cells; 451 452 /* Size portion. */ 453 if (size_cells > 2) 454 return (ERANGE); 455 456 *count = fdt_data_get((void *)data, size_cells); 457 return (0); 458 } 459 460 int 461 fdt_regsize(phandle_t node, u_long *base, u_long *size) 462 { 463 pcell_t reg[4]; 464 int addr_cells, len, size_cells; 465 466 if (fdt_addrsize_cells(OF_parent(node), &addr_cells, &size_cells)) 467 return (ENXIO); 468 469 if ((sizeof(pcell_t) * (addr_cells + size_cells)) > sizeof(reg)) 470 return (ENOMEM); 471 472 len = OF_getprop(node, "reg", ®, sizeof(reg)); 473 if (len <= 0) 474 return (EINVAL); 475 476 *base = fdt_data_get(®[0], addr_cells); 477 *size = fdt_data_get(®[addr_cells], size_cells); 478 return (0); 479 } 480 481 int 482 fdt_reg_to_rl(phandle_t node, struct resource_list *rl) 483 { 484 u_long end, count, start; 485 pcell_t *reg, *regptr; 486 pcell_t addr_cells, size_cells; 487 int tuple_size, tuples; 488 int i, rv; 489 long busaddr, bussize; 490 491 if (fdt_addrsize_cells(OF_parent(node), &addr_cells, &size_cells) != 0) 492 return (ENXIO); 493 if (fdt_get_range(OF_parent(node), 0, &busaddr, &bussize)) { 494 busaddr = 0; 495 bussize = 0; 496 } 497 498 tuple_size = sizeof(pcell_t) * (addr_cells + size_cells); 499 tuples = OF_getprop_alloc(node, "reg", tuple_size, (void **)®); 500 debugf("addr_cells = %d, size_cells = %d\n", addr_cells, size_cells); 501 debugf("tuples = %d, tuple size = %d\n", tuples, tuple_size); 502 if (tuples <= 0) 503 /* No 'reg' property in this node. */ 504 return (0); 505 506 regptr = reg; 507 for (i = 0; i < tuples; i++) { 508 509 rv = fdt_data_to_res(reg, addr_cells, size_cells, &start, 510 &count); 511 if (rv != 0) { 512 resource_list_free(rl); 513 goto out; 514 } 515 reg += addr_cells + size_cells; 516 517 /* Calculate address range relative to base. */ 518 start += busaddr; 519 end = start + count - 1; 520 521 debugf("reg addr start = %lx, end = %lx, count = %lx\n", start, 522 end, count); 523 524 resource_list_add(rl, SYS_RES_MEMORY, i, start, end, 525 count); 526 } 527 rv = 0; 528 529 out: 530 OF_prop_free(regptr); 531 return (rv); 532 } 533 534 int 535 fdt_get_phyaddr(phandle_t node, device_t dev, int *phy_addr, void **phy_sc) 536 { 537 phandle_t phy_node; 538 pcell_t phy_handle, phy_reg; 539 uint32_t i; 540 device_t parent, child; 541 542 if (OF_getencprop(node, "phy-handle", (void *)&phy_handle, 543 sizeof(phy_handle)) <= 0) 544 return (ENXIO); 545 546 phy_node = OF_node_from_xref(phy_handle); 547 548 if (OF_getencprop(phy_node, "reg", (void *)&phy_reg, 549 sizeof(phy_reg)) <= 0) 550 return (ENXIO); 551 552 *phy_addr = phy_reg; 553 554 /* 555 * Search for softc used to communicate with phy. 556 */ 557 558 /* 559 * Step 1: Search for ancestor of the phy-node with a "phy-handle" 560 * property set. 561 */ 562 phy_node = OF_parent(phy_node); 563 while (phy_node != 0) { 564 if (OF_getprop(phy_node, "phy-handle", (void *)&phy_handle, 565 sizeof(phy_handle)) > 0) 566 break; 567 phy_node = OF_parent(phy_node); 568 } 569 if (phy_node == 0) 570 return (ENXIO); 571 572 /* 573 * Step 2: For each device with the same parent and name as ours 574 * compare its node with the one found in step 1, ancestor of phy 575 * node (stored in phy_node). 576 */ 577 parent = device_get_parent(dev); 578 i = 0; 579 child = device_find_child(parent, device_get_name(dev), i); 580 while (child != NULL) { 581 if (ofw_bus_get_node(child) == phy_node) 582 break; 583 i++; 584 child = device_find_child(parent, device_get_name(dev), i); 585 } 586 if (child == NULL) 587 return (ENXIO); 588 589 /* 590 * Use softc of the device found. 591 */ 592 *phy_sc = (void *)device_get_softc(child); 593 594 return (0); 595 } 596 597 int 598 fdt_get_reserved_regions(struct mem_region *mr, int *mrcnt) 599 { 600 pcell_t reserve[FDT_REG_CELLS * FDT_MEM_REGIONS]; 601 pcell_t *reservep; 602 phandle_t memory, root; 603 uint32_t memory_size; 604 int addr_cells, size_cells; 605 int i, max_size, res_len, rv, tuple_size, tuples; 606 607 max_size = sizeof(reserve); 608 root = OF_finddevice("/"); 609 memory = OF_finddevice("/memory"); 610 if (memory == -1) { 611 rv = ENXIO; 612 goto out; 613 } 614 615 if ((rv = fdt_addrsize_cells(OF_parent(memory), &addr_cells, 616 &size_cells)) != 0) 617 goto out; 618 619 if (addr_cells > 2) { 620 rv = ERANGE; 621 goto out; 622 } 623 624 tuple_size = sizeof(pcell_t) * (addr_cells + size_cells); 625 626 res_len = OF_getproplen(root, "memreserve"); 627 if (res_len <= 0 || res_len > sizeof(reserve)) { 628 rv = ERANGE; 629 goto out; 630 } 631 632 if (OF_getprop(root, "memreserve", reserve, res_len) <= 0) { 633 rv = ENXIO; 634 goto out; 635 } 636 637 memory_size = 0; 638 tuples = res_len / tuple_size; 639 reservep = (pcell_t *)&reserve; 640 for (i = 0; i < tuples; i++) { 641 642 rv = fdt_data_to_res(reservep, addr_cells, size_cells, 643 (u_long *)&mr[i].mr_start, (u_long *)&mr[i].mr_size); 644 645 if (rv != 0) 646 goto out; 647 648 reservep += addr_cells + size_cells; 649 } 650 651 *mrcnt = i; 652 rv = 0; 653 out: 654 return (rv); 655 } 656 657 int 658 fdt_get_mem_regions(struct mem_region *mr, int *mrcnt, uint64_t *memsize) 659 { 660 pcell_t reg[FDT_REG_CELLS * FDT_MEM_REGIONS]; 661 pcell_t *regp; 662 phandle_t memory; 663 uint64_t memory_size; 664 int addr_cells, size_cells; 665 int i, max_size, reg_len, rv, tuple_size, tuples; 666 667 max_size = sizeof(reg); 668 memory = OF_finddevice("/memory"); 669 if (memory == -1) { 670 rv = ENXIO; 671 goto out; 672 } 673 674 if ((rv = fdt_addrsize_cells(OF_parent(memory), &addr_cells, 675 &size_cells)) != 0) 676 goto out; 677 678 if (addr_cells > 2) { 679 rv = ERANGE; 680 goto out; 681 } 682 683 tuple_size = sizeof(pcell_t) * (addr_cells + size_cells); 684 reg_len = OF_getproplen(memory, "reg"); 685 if (reg_len <= 0 || reg_len > sizeof(reg)) { 686 rv = ERANGE; 687 goto out; 688 } 689 690 if (OF_getprop(memory, "reg", reg, reg_len) <= 0) { 691 rv = ENXIO; 692 goto out; 693 } 694 695 memory_size = 0; 696 tuples = reg_len / tuple_size; 697 regp = (pcell_t *)® 698 for (i = 0; i < tuples; i++) { 699 700 rv = fdt_data_to_res(regp, addr_cells, size_cells, 701 (u_long *)&mr[i].mr_start, (u_long *)&mr[i].mr_size); 702 703 if (rv != 0) 704 goto out; 705 706 regp += addr_cells + size_cells; 707 memory_size += mr[i].mr_size; 708 } 709 710 if (memory_size == 0) { 711 rv = ERANGE; 712 goto out; 713 } 714 715 *mrcnt = i; 716 if (memsize != NULL) 717 *memsize = memory_size; 718 rv = 0; 719 out: 720 return (rv); 721 } 722 723 int 724 fdt_get_unit(device_t dev) 725 { 726 const char * name; 727 728 name = ofw_bus_get_name(dev); 729 name = strchr(name, '@') + 1; 730 731 return (strtol(name,NULL,0)); 732 } 733 734 int 735 fdt_get_chosen_bootargs(char *bootargs, size_t max_size) 736 { 737 phandle_t chosen; 738 739 chosen = OF_finddevice("/chosen"); 740 if (chosen == -1) 741 return (ENXIO); 742 if (OF_getprop(chosen, "bootargs", bootargs, max_size) == -1) 743 return (ENXIO); 744 return (0); 745 } 746