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