fdtbus.c revision 208747
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: head/sys/dev/fdt/fdtbus.c 208747 2010-06-02 17:17:45Z raj $"); 32 33#include <sys/param.h> 34#include <sys/systm.h> 35#include <sys/ktr.h> 36#include <sys/kernel.h> 37#include <sys/module.h> 38#include <sys/bus.h> 39#include <sys/rman.h> 40#include <sys/malloc.h> 41 42#include <dev/ofw/openfirm.h> 43 44#include <machine/fdt.h> 45 46#include "fdt_common.h" 47#include "ofw_bus_if.h" 48 49#define DEBUG 50#undef DEBUG 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 59static MALLOC_DEFINE(M_FDTBUS, "fdtbus", "FDTbus devices information"); 60 61struct fdtbus_devinfo { 62 phandle_t di_node; 63 char *di_name; 64 char *di_type; 65 char *di_compat; 66 struct resource_list di_res; 67 68 /* Interrupts sense-level info for this device */ 69 struct fdt_sense_level di_intr_sl[DI_MAX_INTR_NUM]; 70}; 71 72struct fdtbus_softc { 73 struct rman sc_irq; 74 struct rman sc_mem; 75}; 76 77/* 78 * Prototypes. 79 */ 80static int fdtbus_probe(device_t); 81static int fdtbus_attach(device_t); 82 83static int fdtbus_print_child(device_t, device_t); 84static struct resource *fdtbus_alloc_resource(device_t, device_t, int, 85 int *, u_long, u_long, u_long, u_int); 86static int fdtbus_release_resource(device_t, device_t, int, int, 87 struct resource *); 88static int fdtbus_activate_resource(device_t, device_t, int, int, 89 struct resource *); 90static int fdtbus_deactivate_resource(device_t, device_t, int, int, 91 struct resource *); 92static int fdtbus_setup_intr(device_t, device_t, struct resource *, int, 93 driver_filter_t *, driver_intr_t *, void *, void **); 94static int fdtbus_teardown_intr(device_t, device_t, struct resource *, 95 void *); 96 97static const char *fdtbus_ofw_get_name(device_t, device_t); 98static phandle_t fdtbus_ofw_get_node(device_t, device_t); 99static const char *fdtbus_ofw_get_type(device_t, device_t); 100static const char *fdtbus_ofw_get_compat(device_t, device_t); 101 102/* 103 * Local routines. 104 */ 105static void newbus_device_from_fdt_node(device_t, phandle_t); 106 107/* 108 * Bus interface definition. 109 */ 110static device_method_t fdtbus_methods[] = { 111 /* Device interface */ 112 DEVMETHOD(device_probe, fdtbus_probe), 113 DEVMETHOD(device_attach, fdtbus_attach), 114 DEVMETHOD(device_detach, bus_generic_detach), 115 DEVMETHOD(device_shutdown, bus_generic_shutdown), 116 DEVMETHOD(device_suspend, bus_generic_suspend), 117 DEVMETHOD(device_resume, bus_generic_resume), 118 119 /* Bus interface */ 120 DEVMETHOD(bus_print_child, fdtbus_print_child), 121 DEVMETHOD(bus_alloc_resource, fdtbus_alloc_resource), 122 DEVMETHOD(bus_release_resource, fdtbus_release_resource), 123 DEVMETHOD(bus_activate_resource, fdtbus_activate_resource), 124 DEVMETHOD(bus_deactivate_resource, fdtbus_deactivate_resource), 125 DEVMETHOD(bus_setup_intr, fdtbus_setup_intr), 126 DEVMETHOD(bus_teardown_intr, fdtbus_teardown_intr), 127 128 /* OFW bus interface */ 129 DEVMETHOD(ofw_bus_get_node, fdtbus_ofw_get_node), 130 DEVMETHOD(ofw_bus_get_name, fdtbus_ofw_get_name), 131 DEVMETHOD(ofw_bus_get_type, fdtbus_ofw_get_type), 132 DEVMETHOD(ofw_bus_get_compat, fdtbus_ofw_get_compat), 133 134 { 0, 0 } 135}; 136 137static driver_t fdtbus_driver = { 138 "fdtbus", 139 fdtbus_methods, 140 sizeof(struct fdtbus_softc) 141}; 142 143devclass_t fdtbus_devclass; 144 145DRIVER_MODULE(fdtbus, nexus, fdtbus_driver, fdtbus_devclass, 0, 0); 146 147static int 148fdtbus_probe(device_t dev) 149{ 150 151 device_set_desc(dev, "FDT main bus"); 152 if (!bootverbose) 153 device_quiet(dev); 154 return (BUS_PROBE_DEFAULT); 155} 156 157static int 158fdtbus_attach(device_t dev) 159{ 160 phandle_t root; 161 phandle_t child; 162 struct fdtbus_softc *sc; 163 u_long start, end; 164 int error; 165 166 if ((root = OF_peer(0)) == 0) 167 panic("fdtbus_attach: no root node."); 168 169 sc = device_get_softc(dev); 170 171 /* 172 * IRQ rman. 173 */ 174 start = 0; 175 end = FDT_INTR_MAX - 1; 176 sc->sc_irq.rm_start = start; 177 sc->sc_irq.rm_end = end; 178 sc->sc_irq.rm_type = RMAN_ARRAY; 179 sc->sc_irq.rm_descr = "Interrupt request lines"; 180 if ((error = rman_init(&sc->sc_irq)) != 0) { 181 device_printf(dev, "could not init IRQ rman, error = %d\n", 182 error); 183 return (error); 184 } 185 if ((error = rman_manage_region(&sc->sc_irq, start, end)) != 0) { 186 device_printf(dev, "could not manage IRQ region, error = %d\n", 187 error); 188 return (error); 189 } 190 191 /* 192 * Mem-mapped I/O space rman. 193 */ 194 start = 0; 195 end = ~0u; 196 sc->sc_mem.rm_start = start; 197 sc->sc_mem.rm_end = end; 198 sc->sc_mem.rm_type = RMAN_ARRAY; 199 sc->sc_mem.rm_descr = "I/O memory"; 200 if ((error = rman_init(&sc->sc_mem)) != 0) { 201 device_printf(dev, "could not init I/O mem rman, error = %d\n", 202 error); 203 return (error); 204 } 205 if ((error = rman_manage_region(&sc->sc_mem, start, end)) != 0) { 206 device_printf(dev, "could not manage I/O mem region, " 207 "error = %d\n", error); 208 return (error); 209 } 210 211 /* 212 * Walk the FDT root node and add top-level devices as our children. 213 */ 214 for (child = OF_child(root); child != 0; child = OF_peer(child)) { 215 /* Check and process 'status' property. */ 216 if (!(fdt_is_enabled(child))) 217 continue; 218 219 newbus_device_from_fdt_node(dev, child); 220 } 221 222 return (bus_generic_attach(dev)); 223} 224 225static int 226fdtbus_print_child(device_t dev, device_t child) 227{ 228 struct fdtbus_devinfo *di; 229 struct resource_list *rl; 230 int rv; 231 232 di = device_get_ivars(child); 233 rl = &di->di_res; 234 235 rv = 0; 236 rv += bus_print_child_header(dev, child); 237 rv += resource_list_print_type(rl, "mem", SYS_RES_MEMORY, "%#lx"); 238 rv += resource_list_print_type(rl, "irq", SYS_RES_IRQ, "%ld"); 239 rv += bus_print_child_footer(dev, child); 240 241 return (rv); 242} 243 244static void 245newbus_device_destroy(device_t dev) 246{ 247 struct fdtbus_devinfo *di; 248 249 di = device_get_ivars(dev); 250 251 free(di->di_name, M_OFWPROP); 252 free(di->di_type, M_OFWPROP); 253 free(di->di_compat, M_OFWPROP); 254 255 resource_list_free(&di->di_res); 256 free(di, M_FDTBUS); 257} 258 259static device_t 260newbus_device_create(device_t dev_par, phandle_t node, char *name, char *type, 261 char *compat) 262{ 263 device_t child; 264 struct fdtbus_devinfo *di; 265 266 child = device_add_child(dev_par, NULL, -1); 267 if (child == NULL) { 268 free(name, M_OFWPROP); 269 free(type, M_OFWPROP); 270 free(compat, M_OFWPROP); 271 return (NULL); 272 } 273 274 di = malloc(sizeof(*di), M_FDTBUS, M_WAITOK); 275 di->di_node = node; 276 di->di_name = name; 277 di->di_type = type; 278 di->di_compat = compat; 279 280 resource_list_init(&di->di_res); 281 282 if (fdt_reg_to_rl(node, &di->di_res, fdt_immr_va)) { 283 device_printf(child, "could not process 'reg' property\n"); 284 newbus_device_destroy(child); 285 child = NULL; 286 goto out; 287 } 288 289 if (fdt_intr_to_rl(node, &di->di_res, di->di_intr_sl)) { 290 device_printf(child, "could not process 'interrupts' " 291 "property\n"); 292 newbus_device_destroy(child); 293 child = NULL; 294 goto out; 295 } 296 297 device_set_ivars(child, di); 298 debugf("added child name='%s', node=%p\n", name, (void *)node); 299 300out: 301 return (child); 302} 303 304static device_t 305newbus_pci_create(device_t dev_par, phandle_t dt_node, u_long par_base, 306 u_long par_size) 307{ 308 pcell_t reg[3 + 2]; 309 device_t dev_child; 310 u_long start, end, count; 311 struct fdtbus_devinfo *di; 312 char *name, *type, *compat; 313 int len; 314 315 OF_getprop_alloc(dt_node, "device_type", 1, (void **)&type); 316 if (!(type != NULL && strcmp(type, "pci") == 0)) { 317 /* Only process 'pci' subnodes. */ 318 free(type, M_OFWPROP); 319 return (NULL); 320 } 321 322 OF_getprop_alloc(dt_node, "name", 1, (void **)&name); 323 OF_getprop_alloc(OF_parent(dt_node), "compatible", 1, 324 (void **)&compat); 325 326 dev_child = device_add_child(dev_par, NULL, -1); 327 if (dev_child == NULL) { 328 free(name, M_OFWPROP); 329 free(type, M_OFWPROP); 330 free(compat, M_OFWPROP); 331 return (NULL); 332 } 333 334 di = malloc(sizeof(*di), M_FDTBUS, M_WAITOK); 335 di->di_node = dt_node; 336 di->di_name = name; 337 di->di_type = type; 338 di->di_compat = compat; 339 340 resource_list_init(&di->di_res); 341 342 /* 343 * Produce and set SYS_RES_MEMORY resources. 344 */ 345 start = 0; 346 count = 0; 347 348 len = OF_getprop(dt_node, "reg", ®, sizeof(reg)); 349 if (len > 0) { 350 if (fdt_data_verify((void *)®[1], 2) != 0) { 351 device_printf(dev_child, "'reg' address value out of " 352 "range\n"); 353 newbus_device_destroy(dev_child); 354 dev_child = NULL; 355 goto out; 356 } 357 start = fdt_data_get((void *)®[1], 2); 358 359 if (fdt_data_verify((void *)®[3], 2) != 0) { 360 device_printf(dev_child, "'reg' size value out of " 361 "range\n"); 362 newbus_device_destroy(dev_child); 363 dev_child = NULL; 364 goto out; 365 } 366 count = fdt_data_get((void *)®[3], 2); 367 } 368 369 /* Calculate address range relative to base. */ 370 par_base &= 0x000ffffful; 371 start &= 0x000ffffful; 372 start += par_base + fdt_immr_va; 373 if (count == 0) 374 count = par_size; 375 end = start + count - 1; 376 377 debugf("start = 0x%08lx, end = 0x%08lx, count = 0x%08lx\n", 378 start, end, count); 379 380 if (count > par_size) { 381 device_printf(dev_child, "'reg' size value out of range\n"); 382 newbus_device_destroy(dev_child); 383 dev_child = NULL; 384 goto out; 385 } 386 387 resource_list_add(&di->di_res, SYS_RES_MEMORY, 0, start, end, count); 388 389 /* 390 * Set SYS_RES_IRQ resources. 391 */ 392 if (fdt_intr_to_rl(OF_parent(dt_node), &di->di_res, di->di_intr_sl)) { 393 device_printf(dev_child, "could not process 'interrupts' " 394 "property\n"); 395 newbus_device_destroy(dev_child); 396 dev_child = NULL; 397 goto out; 398 } 399 400 device_set_ivars(dev_child, di); 401 debugf("added child name='%s', node=%p\n", name, 402 (void *)dt_node); 403 404out: 405 return (dev_child); 406} 407 408static void 409pci_from_fdt_node(device_t dev_par, phandle_t dt_node, char *name, 410 char *type, char *compat) 411{ 412 u_long reg_base, reg_size; 413 phandle_t dt_child; 414 415 /* 416 * Retrieve 'reg' property. 417 */ 418 if (fdt_regsize(dt_node, ®_base, ®_size) != 0) { 419 device_printf(dev_par, "could not retrieve 'reg' prop\n"); 420 return; 421 } 422 423 /* 424 * Walk the PCI node and instantiate newbus devices representing 425 * logical resources (bridges / ports). 426 */ 427 for (dt_child = OF_child(dt_node); dt_child != 0; 428 dt_child = OF_peer(dt_child)) { 429 430 if (!(fdt_is_enabled(dt_child))) 431 continue; 432 433 newbus_pci_create(dev_par, dt_child, reg_base, reg_size); 434 } 435} 436 437/* 438 * These FDT nodes do not need a corresponding newbus device object. 439 */ 440static char *fdt_devices_skip[] = { 441 "aliases", 442 "chosen", 443 "memory", 444 NULL 445}; 446 447static void 448newbus_device_from_fdt_node(device_t dev_par, phandle_t node) 449{ 450 char *name, *type, *compat; 451 device_t child; 452 int i; 453 454 OF_getprop_alloc(node, "name", 1, (void **)&name); 455 OF_getprop_alloc(node, "device_type", 1, (void **)&type); 456 OF_getprop_alloc(node, "compatible", 1, (void **)&compat); 457 458 for (i = 0; fdt_devices_skip[i] != NULL; i++) 459 if (name != NULL && strcmp(name, fdt_devices_skip[i]) == 0) { 460 debugf("skipping instantiating FDT device='%s'\n", 461 name); 462 return; 463 } 464 465 if (type != NULL && strcmp(type, "pci") == 0) { 466 pci_from_fdt_node(dev_par, node, name, type, compat); 467 return; 468 } 469 470 child = newbus_device_create(dev_par, node, name, type, compat); 471} 472 473static struct resource * 474fdtbus_alloc_resource(device_t bus, device_t child, int type, int *rid, 475 u_long start, u_long end, u_long count, u_int flags) 476{ 477 struct fdtbus_softc *sc; 478 struct resource *res; 479 struct rman *rm; 480 struct fdtbus_devinfo *di; 481 struct resource_list_entry *rle; 482 int needactivate; 483 484 /* 485 * Request for the default allocation with a given rid: use resource 486 * list stored in the local device info. 487 */ 488 if ((start == 0UL) && (end == ~0UL)) { 489 if ((di = device_get_ivars(child)) == NULL) 490 return (NULL); 491 492 if (type == SYS_RES_IOPORT) 493 type = SYS_RES_MEMORY; 494 495 rle = resource_list_find(&di->di_res, type, *rid); 496 if (rle == NULL) { 497 device_printf(bus, "no default resources for " 498 "rid = %d, type = %d\n", *rid, type); 499 return (NULL); 500 } 501 start = rle->start; 502 end = rle->end; 503 count = rle->count; 504 } 505 506 sc = device_get_softc(bus); 507 508 needactivate = flags & RF_ACTIVE; 509 flags &= ~RF_ACTIVE; 510 511 switch (type) { 512 case SYS_RES_IRQ: 513 rm = &sc->sc_irq; 514 break; 515 516 case SYS_RES_IOPORT: 517 case SYS_RES_MEMORY: 518 rm = &sc->sc_mem; 519 break; 520 521 default: 522 return (NULL); 523 } 524 525 res = rman_reserve_resource(rm, start, end, count, flags, child); 526 if (res == NULL) { 527 device_printf(bus, "failed to reserve resource %#lx - %#lx " 528 "(%#lx)\n", start, end, count); 529 return (NULL); 530 } 531 532 rman_set_rid(res, *rid); 533 534 if (type == SYS_RES_IOPORT || type == SYS_RES_MEMORY) { 535 /* XXX endianess should be set based on SOC node */ 536 rman_set_bustag(res, fdtbus_bs_tag); 537 rman_set_bushandle(res, rman_get_start(res)); 538 } 539 540 if (needactivate) 541 if (bus_activate_resource(child, type, *rid, res)) { 542 device_printf(child, "resource activation failed\n"); 543 rman_release_resource(res); 544 return (NULL); 545 } 546 547 return (res); 548} 549 550static int 551fdtbus_release_resource(device_t bus, device_t child, int type, int rid, 552 struct resource *res) 553{ 554 int err; 555 556 if (rman_get_flags(res) & RF_ACTIVE) { 557 err = bus_deactivate_resource(child, type, rid, res); 558 if (err) 559 return (err); 560 } 561 562 return (rman_release_resource(res)); 563} 564 565static int 566fdtbus_setup_intr(device_t bus, device_t child, struct resource *res, 567 int flags, driver_filter_t *filter, driver_intr_t *ihand, void *arg, 568 void **cookiep) 569{ 570 int err; 571 572 *cookiep = 0; 573 if ((rman_get_flags(res) & RF_SHAREABLE) == 0) 574 flags |= INTR_EXCL; 575 576 err = rman_activate_resource(res); 577 if (err) 578 return (err); 579 580#if defined(__powerpc__) 581 err = powerpc_setup_intr(device_get_nameunit(child), 582 rman_get_start(res), filter, ihand, arg, flags, cookiep); 583#elif defined(__arm__) 584 arm_setup_irqhandler(device_get_nameunit(child), 585 filter, ihand, arg, rman_get_start(res), flags, cookiep); 586 arm_unmask_irq(rman_get_start(res)); 587 err = 0; 588#endif 589 590 return (err); 591} 592 593static int 594fdtbus_activate_resource(device_t bus, device_t child, int type, int rid, 595 struct resource *res) 596{ 597 598 return (rman_activate_resource(res)); 599} 600 601static int 602fdtbus_deactivate_resource(device_t bus, device_t child, int type, int rid, 603 struct resource *res) 604{ 605 606 return (rman_deactivate_resource(res)); 607} 608 609 610static int 611fdtbus_teardown_intr(device_t bus, device_t child, struct resource *res, 612 void *cookie) 613{ 614 615#if defined(__powerpc__) 616 return (powerpc_teardown_intr(cookie)); 617#elif defined(__arm__) 618 return (arm_remove_irqhandler(rman_get_start(res), cookie)); 619#endif 620} 621 622static const char * 623fdtbus_ofw_get_name(device_t bus, device_t dev) 624{ 625 struct fdtbus_devinfo *di; 626 627 return ((di = device_get_ivars(dev)) == NULL ? NULL : di->di_name); 628} 629 630static phandle_t 631fdtbus_ofw_get_node(device_t bus, device_t dev) 632{ 633 struct fdtbus_devinfo *di; 634 635 return ((di = device_get_ivars(dev)) == NULL ? 0 : di->di_node); 636} 637 638static const char * 639fdtbus_ofw_get_type(device_t bus, device_t dev) 640{ 641 struct fdtbus_devinfo *di; 642 643 return ((di = device_get_ivars(dev)) == NULL ? NULL : di->di_type); 644} 645 646static const char * 647fdtbus_ofw_get_compat(device_t bus, device_t dev) 648{ 649 struct fdtbus_devinfo *di; 650 651 return ((di = device_get_ivars(dev)) == NULL ? NULL : di->di_compat); 652} 653 654 655