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