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