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