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