fdtbus.c revision 238043
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 238043 2012-07-02 23:49:29Z 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#ifdef DEBUG 50#define debugf(fmt, args...) do { printf("%s(): ", __func__); \ 51 printf(fmt,##args); } while (0) 52#else 53#define debugf(fmt, args...) 54#endif 55 56static MALLOC_DEFINE(M_FDTBUS, "fdtbus", "FDTbus devices information"); 57 58struct fdtbus_devinfo { 59 phandle_t di_node; 60 char *di_name; 61 char *di_type; 62 char *di_compat; 63 struct resource_list di_res; 64 65 /* Interrupts sense-level info for this device */ 66 struct fdt_sense_level di_intr_sl[DI_MAX_INTR_NUM]; 67}; 68 69struct fdtbus_softc { 70 struct rman sc_irq; 71 struct rman sc_mem; 72}; 73 74/* 75 * Prototypes. 76 */ 77static void fdtbus_identify(driver_t *, device_t); 78static int fdtbus_probe(device_t); 79static int fdtbus_attach(device_t); 80 81static int fdtbus_print_child(device_t, device_t); 82static struct resource *fdtbus_alloc_resource(device_t, device_t, int, 83 int *, u_long, u_long, u_long, u_int); 84static int fdtbus_release_resource(device_t, device_t, int, int, 85 struct resource *); 86static int fdtbus_activate_resource(device_t, device_t, int, int, 87 struct resource *); 88static int fdtbus_deactivate_resource(device_t, device_t, int, int, 89 struct resource *); 90static int fdtbus_setup_intr(device_t, device_t, struct resource *, int, 91 driver_filter_t *, driver_intr_t *, void *, void **); 92 93static const char *fdtbus_ofw_get_name(device_t, device_t); 94static phandle_t fdtbus_ofw_get_node(device_t, device_t); 95static const char *fdtbus_ofw_get_type(device_t, device_t); 96static const char *fdtbus_ofw_get_compat(device_t, device_t); 97 98/* 99 * Local routines. 100 */ 101static void newbus_device_from_fdt_node(device_t, phandle_t); 102 103/* 104 * Bus interface definition. 105 */ 106static device_method_t fdtbus_methods[] = { 107 /* Device interface */ 108 DEVMETHOD(device_identify, fdtbus_identify), 109 DEVMETHOD(device_probe, fdtbus_probe), 110 DEVMETHOD(device_attach, fdtbus_attach), 111 DEVMETHOD(device_detach, bus_generic_detach), 112 DEVMETHOD(device_shutdown, bus_generic_shutdown), 113 DEVMETHOD(device_suspend, bus_generic_suspend), 114 DEVMETHOD(device_resume, bus_generic_resume), 115 116 /* Bus interface */ 117 DEVMETHOD(bus_print_child, fdtbus_print_child), 118 DEVMETHOD(bus_alloc_resource, fdtbus_alloc_resource), 119 DEVMETHOD(bus_release_resource, fdtbus_release_resource), 120 DEVMETHOD(bus_activate_resource, fdtbus_activate_resource), 121 DEVMETHOD(bus_deactivate_resource, fdtbus_deactivate_resource), 122 DEVMETHOD(bus_config_intr, bus_generic_config_intr), 123 DEVMETHOD(bus_setup_intr, fdtbus_setup_intr), 124 DEVMETHOD(bus_teardown_intr, bus_generic_teardown_intr), 125 126 /* OFW bus interface */ 127 DEVMETHOD(ofw_bus_get_node, fdtbus_ofw_get_node), 128 DEVMETHOD(ofw_bus_get_name, fdtbus_ofw_get_name), 129 DEVMETHOD(ofw_bus_get_type, fdtbus_ofw_get_type), 130 DEVMETHOD(ofw_bus_get_compat, fdtbus_ofw_get_compat), 131 132 { 0, 0 } 133}; 134 135static driver_t fdtbus_driver = { 136 "fdtbus", 137 fdtbus_methods, 138 sizeof(struct fdtbus_softc) 139}; 140 141devclass_t fdtbus_devclass; 142 143DRIVER_MODULE(fdtbus, nexus, fdtbus_driver, fdtbus_devclass, 0, 0); 144 145static void 146fdtbus_identify(driver_t *driver, device_t parent) 147{ 148 149 debugf("%s(driver=%p, parent=%p)\n", __func__, driver, parent); 150 151 if (device_find_child(parent, "fdtbus", -1) == NULL) 152 BUS_ADD_CHILD(parent, 0, "fdtbus", -1); 153} 154 155static int 156fdtbus_probe(device_t dev) 157{ 158 159 debugf("%s(dev=%p); pass=%u\n", __func__, dev, bus_current_pass); 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_finddevice("/")) == -1) 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 = ~0ul; 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 child = newbus_device_create(dev_par, node, name, type, compat); 476 if (type != NULL && strcmp(type, "pci") == 0) 477 pci_from_fdt_node(child, node, name, type, compat); 478} 479 480static struct resource * 481fdtbus_alloc_resource(device_t bus, device_t child, int type, int *rid, 482 u_long start, u_long end, u_long count, u_int flags) 483{ 484 struct fdtbus_softc *sc; 485 struct resource *res; 486 struct rman *rm; 487 struct fdtbus_devinfo *di; 488 struct resource_list_entry *rle; 489 int needactivate; 490 491 /* 492 * Request for the default allocation with a given rid: use resource 493 * list stored in the local device info. 494 */ 495 if ((start == 0UL) && (end == ~0UL)) { 496 if ((di = device_get_ivars(child)) == NULL) 497 return (NULL); 498 499 if (type == SYS_RES_IOPORT) 500 type = SYS_RES_MEMORY; 501 502 rle = resource_list_find(&di->di_res, type, *rid); 503 if (rle == NULL) { 504 device_printf(bus, "no default resources for " 505 "rid = %d, type = %d\n", *rid, type); 506 return (NULL); 507 } 508 start = rle->start; 509 end = rle->end; 510 count = rle->count; 511 } 512 513 sc = device_get_softc(bus); 514 515 needactivate = flags & RF_ACTIVE; 516 flags &= ~RF_ACTIVE; 517 518 switch (type) { 519 case SYS_RES_IRQ: 520 rm = &sc->sc_irq; 521 break; 522 523 case SYS_RES_IOPORT: 524 case SYS_RES_MEMORY: 525 rm = &sc->sc_mem; 526 break; 527 528 default: 529 return (NULL); 530 } 531 532 res = rman_reserve_resource(rm, start, end, count, flags, child); 533 if (res == NULL) { 534 device_printf(bus, "failed to reserve resource %#lx - %#lx " 535 "(%#lx)\n", start, end, count); 536 return (NULL); 537 } 538 539 rman_set_rid(res, *rid); 540 541 if (type == SYS_RES_IOPORT || type == SYS_RES_MEMORY) { 542 /* XXX endianess should be set based on SOC node */ 543 rman_set_bustag(res, fdtbus_bs_tag); 544 rman_set_bushandle(res, rman_get_start(res)); 545 } 546 547 if (needactivate) 548 if (bus_activate_resource(child, type, *rid, res)) { 549 device_printf(child, "resource activation failed\n"); 550 rman_release_resource(res); 551 return (NULL); 552 } 553 554 return (res); 555} 556 557static int 558fdtbus_release_resource(device_t bus, device_t child, int type, int rid, 559 struct resource *res) 560{ 561 int err; 562 563 if (rman_get_flags(res) & RF_ACTIVE) { 564 err = bus_deactivate_resource(child, type, rid, res); 565 if (err) 566 return (err); 567 } 568 569 return (rman_release_resource(res)); 570} 571 572static int 573fdtbus_setup_intr(device_t bus, device_t child, struct resource *res, 574 int flags, driver_filter_t *filter, driver_intr_t *ihand, void *arg, 575 void **cookiep) 576{ 577 struct fdtbus_devinfo *di; 578 enum intr_trigger trig; 579 enum intr_polarity pol; 580 int error, rid; 581 582 if (res == NULL) 583 return (EINVAL); 584 585 /* 586 * We are responsible for configuring the interrupts of our direct 587 * children. 588 */ 589 if (device_get_parent(child) == bus) { 590 di = device_get_ivars(child); 591 if (di == NULL) 592 return (ENXIO); 593 594 rid = rman_get_rid(res); 595 if (rid >= DI_MAX_INTR_NUM) 596 return (ENOENT); 597 598 trig = di->di_intr_sl[rid].trig; 599 pol = di->di_intr_sl[rid].pol; 600 if (trig != INTR_TRIGGER_CONFORM || 601 pol != INTR_POLARITY_CONFORM) { 602 error = bus_generic_config_intr(bus, 603 rman_get_start(res), trig, pol); 604 if (error) 605 return (error); 606 } 607 } 608 609 error = bus_generic_setup_intr(bus, child, res, flags, filter, ihand, 610 arg, cookiep); 611 return (error); 612} 613 614static int 615fdtbus_activate_resource(device_t bus, device_t child, int type, int rid, 616 struct resource *res) 617{ 618 619 return (rman_activate_resource(res)); 620} 621 622static int 623fdtbus_deactivate_resource(device_t bus, device_t child, int type, int rid, 624 struct resource *res) 625{ 626 627 return (rman_deactivate_resource(res)); 628} 629 630static const char * 631fdtbus_ofw_get_name(device_t bus, device_t dev) 632{ 633 struct fdtbus_devinfo *di; 634 635 return ((di = device_get_ivars(dev)) == NULL ? NULL : di->di_name); 636} 637 638static phandle_t 639fdtbus_ofw_get_node(device_t bus, device_t dev) 640{ 641 struct fdtbus_devinfo *di; 642 643 return ((di = device_get_ivars(dev)) == NULL ? 0 : di->di_node); 644} 645 646static const char * 647fdtbus_ofw_get_type(device_t bus, device_t dev) 648{ 649 struct fdtbus_devinfo *di; 650 651 return ((di = device_get_ivars(dev)) == NULL ? NULL : di->di_type); 652} 653 654static const char * 655fdtbus_ofw_get_compat(device_t bus, device_t dev) 656{ 657 struct fdtbus_devinfo *di; 658 659 return ((di = device_get_ivars(dev)) == NULL ? NULL : di->di_compat); 660} 661 662 663