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