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