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 start, end, count; 425 pcell_t *reg, *regptr; 426 pcell_t addr_cells, size_cells; 427 int tuple_size, tuples; 428 int i, rv; 429 bus_space_handle_t vaddr; 430 long busaddr, bussize; 431 432 if (fdt_addrsize_cells(OF_parent(node), &addr_cells, &size_cells) != 0) 433 return (ENXIO); 434 if (fdt_get_range(OF_parent(node), 0, &busaddr, &bussize)) { 435 busaddr = 0; 436 bussize = 0; 437 } 438 439 tuple_size = sizeof(pcell_t) * (addr_cells + size_cells); 440 tuples = OF_getprop_alloc(node, "reg", tuple_size, (void **)®); 441 debugf("addr_cells = %d, size_cells = %d\n", addr_cells, size_cells); 442 debugf("tuples = %d, tuple size = %d\n", tuples, tuple_size); 443 if (tuples <= 0) 444 /* No 'reg' property in this node. */ 445 return (0); 446 447 regptr = reg; 448 for (i = 0; i < tuples; i++) { 449 450 rv = fdt_data_to_res(reg, addr_cells, size_cells, &start, 451 &count); 452 if (rv != 0) { 453 resource_list_free(rl); 454 goto out; 455 } 456 reg += addr_cells + size_cells; 457 458 /* Calculate address range relative to base. */ 459 start += busaddr; 460 if (bus_space_map(fdtbus_bs_tag, start, count, 0, &vaddr) != 0) 461 panic("Couldn't map the device memory"); 462 end = vaddr + count - 1; 463 464 debugf("reg addr start = %lx, end = %lx, count = %lx\n", vaddr, 465 end, count); 466 467 resource_list_add(rl, SYS_RES_MEMORY, i, vaddr, end, 468 count); 469 } 470 rv = 0; 471 472 out: 473 free(regptr, M_OFWPROP); 474 return (rv); 475 } 476 477 int 478 fdt_intr_decode(phandle_t intr_parent, pcell_t *intr, int *interrupt, 479 int *trig, int *pol) 480 { 481 fdt_pic_decode_t intr_decode; 482 int i, rv; 483 484 for (i = 0; fdt_pic_table[i] != NULL; i++) { 485 486 /* XXX check if pic_handle has interrupt-controller prop? */ 487 488 intr_decode = fdt_pic_table[i]; 489 rv = intr_decode(intr_parent, intr, interrupt, trig, pol); 490 491 if (rv == 0) 492 /* This was recognized as our PIC and decoded. */ 493 return (0); 494 } 495 496 return (ENXIO); 497 } 498 499 int 500 fdt_intr_to_rl(phandle_t node, struct resource_list *rl, 501 struct fdt_sense_level *intr_sl) 502 { 503 phandle_t intr_par; 504 ihandle_t iph; 505 pcell_t *intr; 506 pcell_t intr_cells; 507 int interrupt, trig, pol; 508 int i, intr_num, irq, rv; 509 510 if (OF_getproplen(node, "interrupts") <= 0) 511 /* Node does not have 'interrupts' property. */ 512 return (0); 513 514 /* 515 * Find #interrupt-cells of the interrupt domain. 516 */ 517 if (OF_getprop(node, "interrupt-parent", &iph, sizeof(iph)) <= 0) { 518 debugf("no intr-parent phandle\n"); 519 intr_par = OF_parent(node); 520 } else { 521 iph = fdt32_to_cpu(iph); 522 intr_par = OF_instance_to_package(iph); 523 } 524 525 if (OF_getprop(intr_par, "#interrupt-cells", &intr_cells, 526 sizeof(intr_cells)) <= 0) { 527 debugf("no intr-cells defined, defaulting to 1\n"); 528 intr_cells = 1; 529 } 530 else 531 intr_cells = fdt32_to_cpu(intr_cells); 532 533 intr_num = OF_getprop_alloc(node, "interrupts", 534 intr_cells * sizeof(pcell_t), (void **)&intr); 535 if (intr_num <= 0 || intr_num > DI_MAX_INTR_NUM) 536 return (ERANGE); 537 538 rv = 0; 539 for (i = 0; i < intr_num; i++) { 540 541 interrupt = -1; 542 trig = pol = 0; 543 544 if (fdt_intr_decode(intr_par, &intr[i * intr_cells], 545 &interrupt, &trig, &pol) != 0) { 546 rv = ENXIO; 547 goto out; 548 } 549 550 if (interrupt < 0) { 551 rv = ERANGE; 552 goto out; 553 } 554 555 debugf("decoded intr = %d, trig = %d, pol = %d\n", interrupt, 556 trig, pol); 557 558 intr_sl[i].trig = trig; 559 intr_sl[i].pol = pol; 560 561 irq = FDT_MAP_IRQ(intr_par, interrupt); 562 resource_list_add(rl, SYS_RES_IRQ, i, irq, irq, 1); 563 } 564 565 out: 566 free(intr, M_OFWPROP); 567 return (rv); 568 } 569 570 int 571 fdt_get_phyaddr(phandle_t node, device_t dev, int *phy_addr, void **phy_sc) 572 { 573 phandle_t phy_node; 574 ihandle_t phy_ihandle; 575 pcell_t phy_handle, phy_reg; 576 uint32_t i; 577 device_t parent, child; 578 579 if (OF_getprop(node, "phy-handle", (void *)&phy_handle, 580 sizeof(phy_handle)) <= 0) 581 return (ENXIO); 582 583 phy_ihandle = (ihandle_t)phy_handle; 584 phy_ihandle = fdt32_to_cpu(phy_ihandle); 585 phy_node = OF_instance_to_package(phy_ihandle); 586 587 if (OF_getprop(phy_node, "reg", (void *)&phy_reg, 588 sizeof(phy_reg)) <= 0) 589 return (ENXIO); 590 591 *phy_addr = fdt32_to_cpu(phy_reg); 592 593 /* 594 * Search for softc used to communicate with phy. 595 */ 596 597 /* 598 * Step 1: Search for ancestor of the phy-node with a "phy-handle" 599 * property set. 600 */ 601 phy_node = OF_parent(phy_node); 602 while (phy_node != 0) { 603 if (OF_getprop(phy_node, "phy-handle", (void *)&phy_handle, 604 sizeof(phy_handle)) > 0) 605 break; 606 phy_node = OF_parent(phy_node); 607 } 608 if (phy_node == 0) 609 return (ENXIO); 610 611 /* 612 * Step 2: For each device with the same parent and name as ours 613 * compare its node with the one found in step 1, ancestor of phy 614 * node (stored in phy_node). 615 */ 616 parent = device_get_parent(dev); 617 i = 0; 618 child = device_find_child(parent, device_get_name(dev), i); 619 while (child != NULL) { 620 if (ofw_bus_get_node(child) == phy_node) 621 break; 622 i++; 623 child = device_find_child(parent, device_get_name(dev), i); 624 } 625 if (child == NULL) 626 return (ENXIO); 627 628 /* 629 * Use softc of the device found. 630 */ 631 *phy_sc = (void *)device_get_softc(child); 632 633 return (0); 634 } 635 636 int 637 fdt_get_reserved_regions(struct mem_region *mr, int *mrcnt) 638 { 639 pcell_t reserve[FDT_REG_CELLS * FDT_MEM_REGIONS]; 640 pcell_t *reservep; 641 phandle_t memory, root; 642 uint32_t memory_size; 643 int addr_cells, size_cells; 644 int i, max_size, res_len, rv, tuple_size, tuples; 645 646 max_size = sizeof(reserve); 647 root = OF_finddevice("/"); 648 memory = OF_finddevice("/memory"); 649 if (memory == -1) { 650 rv = ENXIO; 651 goto out; 652 } 653 654 if ((rv = fdt_addrsize_cells(OF_parent(memory), &addr_cells, 655 &size_cells)) != 0) 656 goto out; 657 658 if (addr_cells > 2) { 659 rv = ERANGE; 660 goto out; 661 } 662 663 tuple_size = sizeof(pcell_t) * (addr_cells + size_cells); 664 665 res_len = OF_getproplen(root, "memreserve"); 666 if (res_len <= 0 || res_len > sizeof(reserve)) { 667 rv = ERANGE; 668 goto out; 669 } 670 671 if (OF_getprop(root, "memreserve", reserve, res_len) <= 0) { 672 rv = ENXIO; 673 goto out; 674 } 675 676 memory_size = 0; 677 tuples = res_len / tuple_size; 678 reservep = (pcell_t *)&reserve; 679 for (i = 0; i < tuples; i++) { 680 681 rv = fdt_data_to_res(reservep, addr_cells, size_cells, 682 (u_long *)&mr[i].mr_start, (u_long *)&mr[i].mr_size); 683 684 if (rv != 0) 685 goto out; 686 687 reservep += addr_cells + size_cells; 688 } 689 690 *mrcnt = i; 691 rv = 0; 692 out: 693 return (rv); 694 } 695 696 int 697 fdt_get_mem_regions(struct mem_region *mr, int *mrcnt, uint32_t *memsize) 698 { 699 pcell_t reg[FDT_REG_CELLS * FDT_MEM_REGIONS]; 700 pcell_t *regp; 701 phandle_t memory; 702 uint32_t memory_size; 703 int addr_cells, size_cells; 704 int i, max_size, reg_len, rv, tuple_size, tuples; 705 706 max_size = sizeof(reg); 707 memory = OF_finddevice("/memory"); 708 if (memory == -1) { 709 rv = ENXIO; 710 goto out; 711 } 712 713 if ((rv = fdt_addrsize_cells(OF_parent(memory), &addr_cells, 714 &size_cells)) != 0) 715 goto out; 716 717 if (addr_cells > 2) { 718 rv = ERANGE; 719 goto out; 720 } 721 722 tuple_size = sizeof(pcell_t) * (addr_cells + size_cells); 723 reg_len = OF_getproplen(memory, "reg"); 724 if (reg_len <= 0 || reg_len > sizeof(reg)) { 725 rv = ERANGE; 726 goto out; 727 } 728 729 if (OF_getprop(memory, "reg", reg, reg_len) <= 0) { 730 rv = ENXIO; 731 goto out; 732 } 733 734 memory_size = 0; 735 tuples = reg_len / tuple_size; 736 regp = (pcell_t *)® 737 for (i = 0; i < tuples; i++) { 738 739 rv = fdt_data_to_res(regp, addr_cells, size_cells, 740 (u_long *)&mr[i].mr_start, (u_long *)&mr[i].mr_size); 741 742 if (rv != 0) 743 goto out; 744 745 regp += addr_cells + size_cells; 746 memory_size += mr[i].mr_size; 747 } 748 749 if (memory_size == 0) { 750 rv = ERANGE; 751 goto out; 752 } 753 754 *mrcnt = i; 755 *memsize = memory_size; 756 rv = 0; 757 out: 758 return (rv); 759 } 760 761 int 762 fdt_get_unit(device_t dev) 763 { 764 const char * name; 765 766 name = ofw_bus_get_name(dev); 767 name = strchr(name, '@') + 1; 768 769 return (strtol(name,NULL,0)); 770 } 771