fdtbus.c revision 259065
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: releng/10.0/sys/dev/fdt/fdtbus.c 248467 2013-03-18 15:18:55Z ray $"); 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 if (di == NULL) 261 return; 262 263 free(di->di_name, M_OFWPROP); 264 free(di->di_type, M_OFWPROP); 265 free(di->di_compat, M_OFWPROP); 266 267 resource_list_free(&di->di_res); 268 free(di, M_FDTBUS); 269} 270 271static device_t 272newbus_device_create(device_t dev_par, phandle_t node, char *name, char *type, 273 char *compat) 274{ 275 device_t child; 276 struct fdtbus_devinfo *di; 277 278 child = device_add_child(dev_par, NULL, -1); 279 if (child == NULL) { 280 free(name, M_OFWPROP); 281 free(type, M_OFWPROP); 282 free(compat, M_OFWPROP); 283 return (NULL); 284 } 285 286 di = malloc(sizeof(*di), M_FDTBUS, M_WAITOK); 287 di->di_node = node; 288 di->di_name = name; 289 di->di_type = type; 290 di->di_compat = compat; 291 292 resource_list_init(&di->di_res); 293 294 if (fdt_reg_to_rl(node, &di->di_res)) { 295 device_printf(child, "could not process 'reg' property\n"); 296 newbus_device_destroy(child); 297 child = NULL; 298 goto out; 299 } 300 301 if (fdt_intr_to_rl(node, &di->di_res, di->di_intr_sl)) { 302 device_printf(child, "could not process 'interrupts' " 303 "property\n"); 304 newbus_device_destroy(child); 305 child = NULL; 306 goto out; 307 } 308 309 device_set_ivars(child, di); 310 debugf("added child name='%s', node=%p\n", name, (void *)node); 311 312out: 313 return (child); 314} 315 316static device_t 317newbus_pci_create(device_t dev_par, phandle_t dt_node, u_long par_base, 318 u_long par_size) 319{ 320 pcell_t reg[3 + 2]; 321 device_t dev_child; 322 u_long start, end, count; 323 struct fdtbus_devinfo *di; 324 char *name, *type, *compat; 325 int len; 326 327 OF_getprop_alloc(dt_node, "device_type", 1, (void **)&type); 328 if (!(type != NULL && strcmp(type, "pci") == 0)) { 329 /* Only process 'pci' subnodes. */ 330 free(type, M_OFWPROP); 331 return (NULL); 332 } 333 334 OF_getprop_alloc(dt_node, "name", 1, (void **)&name); 335 OF_getprop_alloc(OF_parent(dt_node), "compatible", 1, 336 (void **)&compat); 337 338 dev_child = device_add_child(dev_par, NULL, -1); 339 if (dev_child == NULL) { 340 free(name, M_OFWPROP); 341 free(type, M_OFWPROP); 342 free(compat, M_OFWPROP); 343 return (NULL); 344 } 345 346 di = malloc(sizeof(*di), M_FDTBUS, M_WAITOK); 347 di->di_node = dt_node; 348 di->di_name = name; 349 di->di_type = type; 350 di->di_compat = compat; 351 352 resource_list_init(&di->di_res); 353 354 /* 355 * Produce and set SYS_RES_MEMORY resources. 356 */ 357 start = 0; 358 count = 0; 359 360 len = OF_getprop(dt_node, "reg", ®, sizeof(reg)); 361 if (len > 0) { 362 if (fdt_data_verify((void *)®[1], 2) != 0) { 363 device_printf(dev_child, "'reg' address value out of " 364 "range\n"); 365 newbus_device_destroy(dev_child); 366 dev_child = NULL; 367 goto out; 368 } 369 start = fdt_data_get((void *)®[1], 2); 370 371 if (fdt_data_verify((void *)®[3], 2) != 0) { 372 device_printf(dev_child, "'reg' size value out of " 373 "range\n"); 374 newbus_device_destroy(dev_child); 375 dev_child = NULL; 376 goto out; 377 } 378 count = fdt_data_get((void *)®[3], 2); 379 } 380 381 /* Calculate address range relative to base. */ 382 par_base &= 0x000ffffful; 383 start &= 0x000ffffful; 384 start += par_base + fdt_immr_va; 385 if (count == 0) 386 count = par_size; 387 end = start + count - 1; 388 389 debugf("start = 0x%08lx, end = 0x%08lx, count = 0x%08lx\n", 390 start, end, count); 391 392 if (count > par_size) { 393 device_printf(dev_child, "'reg' size value out of range\n"); 394 newbus_device_destroy(dev_child); 395 dev_child = NULL; 396 goto out; 397 } 398 399 resource_list_add(&di->di_res, SYS_RES_MEMORY, 0, start, end, count); 400 401 /* 402 * Set SYS_RES_IRQ resources. 403 */ 404 if (fdt_intr_to_rl(OF_parent(dt_node), &di->di_res, di->di_intr_sl)) { 405 device_printf(dev_child, "could not process 'interrupts' " 406 "property\n"); 407 newbus_device_destroy(dev_child); 408 dev_child = NULL; 409 goto out; 410 } 411 412 device_set_ivars(dev_child, di); 413 debugf("added child name='%s', node=%p\n", name, 414 (void *)dt_node); 415 416out: 417 return (dev_child); 418} 419 420static void 421pci_from_fdt_node(device_t dev_par, phandle_t dt_node, char *name, 422 char *type, char *compat) 423{ 424 u_long reg_base, reg_size; 425 phandle_t dt_child; 426 427 /* 428 * Retrieve 'reg' property. 429 */ 430 if (fdt_regsize(dt_node, ®_base, ®_size) != 0) { 431 device_printf(dev_par, "could not retrieve 'reg' prop\n"); 432 return; 433 } 434 435 /* 436 * Walk the PCI node and instantiate newbus devices representing 437 * logical resources (bridges / ports). 438 */ 439 for (dt_child = OF_child(dt_node); dt_child != 0; 440 dt_child = OF_peer(dt_child)) { 441 442 if (!(fdt_is_enabled(dt_child))) 443 continue; 444 445 newbus_pci_create(dev_par, dt_child, reg_base, reg_size); 446 } 447} 448 449/* 450 * These FDT nodes do not need a corresponding newbus device object. 451 */ 452static char *fdt_devices_skip[] = { 453 "aliases", 454 "chosen", 455 "memory", 456 NULL 457}; 458 459static void 460newbus_device_from_fdt_node(device_t dev_par, phandle_t node) 461{ 462 char *name, *type, *compat; 463 device_t child; 464 int i; 465 466 OF_getprop_alloc(node, "name", 1, (void **)&name); 467 OF_getprop_alloc(node, "device_type", 1, (void **)&type); 468 OF_getprop_alloc(node, "compatible", 1, (void **)&compat); 469 470 for (i = 0; fdt_devices_skip[i] != NULL; i++) 471 if (name != NULL && strcmp(name, fdt_devices_skip[i]) == 0) { 472 debugf("skipping instantiating FDT device='%s'\n", 473 name); 474 return; 475 } 476 477 child = newbus_device_create(dev_par, node, name, type, compat); 478 if (type != NULL && strcmp(type, "pci") == 0) 479 pci_from_fdt_node(child, node, name, type, compat); 480} 481 482static struct resource * 483fdtbus_alloc_resource(device_t bus, device_t child, int type, int *rid, 484 u_long start, u_long end, u_long count, u_int flags) 485{ 486 struct fdtbus_softc *sc; 487 struct resource *res; 488 struct rman *rm; 489 struct fdtbus_devinfo *di; 490 struct resource_list_entry *rle; 491 int needactivate; 492 493 /* 494 * Request for the default allocation with a given rid: use resource 495 * list stored in the local device info. 496 */ 497 if ((start == 0UL) && (end == ~0UL)) { 498 if ((di = device_get_ivars(child)) == NULL) 499 return (NULL); 500 501 if (type == SYS_RES_IOPORT) 502 type = SYS_RES_MEMORY; 503 504 rle = resource_list_find(&di->di_res, type, *rid); 505 if (rle == NULL) { 506 device_printf(bus, "no default resources for " 507 "rid = %d, type = %d\n", *rid, type); 508 return (NULL); 509 } 510 start = rle->start; 511 end = rle->end; 512 count = rle->count; 513 } 514 515 sc = device_get_softc(bus); 516 517 needactivate = flags & RF_ACTIVE; 518 flags &= ~RF_ACTIVE; 519 520 switch (type) { 521 case SYS_RES_IRQ: 522 rm = &sc->sc_irq; 523 break; 524 525 case SYS_RES_IOPORT: 526 case SYS_RES_MEMORY: 527 rm = &sc->sc_mem; 528 break; 529 530 default: 531 return (NULL); 532 } 533 534 res = rman_reserve_resource(rm, start, end, count, flags, child); 535 if (res == NULL) { 536 device_printf(bus, "failed to reserve resource %#lx - %#lx " 537 "(%#lx)\n", start, end, count); 538 return (NULL); 539 } 540 541 rman_set_rid(res, *rid); 542 543 if (type == SYS_RES_IOPORT || type == SYS_RES_MEMORY) { 544 /* XXX endianess should be set based on SOC node */ 545 rman_set_bustag(res, fdtbus_bs_tag); 546 rman_set_bushandle(res, rman_get_start(res)); 547 } 548 549 if (needactivate) 550 if (bus_activate_resource(child, type, *rid, res)) { 551 device_printf(child, "resource activation failed\n"); 552 rman_release_resource(res); 553 return (NULL); 554 } 555 556 return (res); 557} 558 559static int 560fdtbus_release_resource(device_t bus, device_t child, int type, int rid, 561 struct resource *res) 562{ 563 int err; 564 565 if (rman_get_flags(res) & RF_ACTIVE) { 566 err = bus_deactivate_resource(child, type, rid, res); 567 if (err) 568 return (err); 569 } 570 571 return (rman_release_resource(res)); 572} 573 574static int 575fdtbus_setup_intr(device_t bus, device_t child, struct resource *res, 576 int flags, driver_filter_t *filter, driver_intr_t *ihand, void *arg, 577 void **cookiep) 578{ 579 struct fdtbus_devinfo *di; 580 enum intr_trigger trig; 581 enum intr_polarity pol; 582 int error, rid; 583 584 if (res == NULL) 585 return (EINVAL); 586 587 /* 588 * We are responsible for configuring the interrupts of our direct 589 * children. 590 */ 591 if (device_get_parent(child) == bus) { 592 di = device_get_ivars(child); 593 if (di == NULL) 594 return (ENXIO); 595 596 rid = rman_get_rid(res); 597 if (rid >= DI_MAX_INTR_NUM) 598 return (ENOENT); 599 600 trig = di->di_intr_sl[rid].trig; 601 pol = di->di_intr_sl[rid].pol; 602 if (trig != INTR_TRIGGER_CONFORM || 603 pol != INTR_POLARITY_CONFORM) { 604 error = bus_generic_config_intr(bus, 605 rman_get_start(res), trig, pol); 606 if (error) 607 return (error); 608 } 609 } 610 611 error = bus_generic_setup_intr(bus, child, res, flags, filter, ihand, 612 arg, cookiep); 613 return (error); 614} 615 616static int 617fdtbus_activate_resource(device_t bus, device_t child, int type, int rid, 618 struct resource *res) 619{ 620 bus_space_handle_t p; 621 int error; 622 623 if (type == SYS_RES_MEMORY || type == SYS_RES_IOPORT) { 624 error = bus_space_map(rman_get_bustag(res), 625 rman_get_bushandle(res), rman_get_size(res), 0, &p); 626 if (error) 627 return (error); 628 rman_set_bushandle(res, p); 629 } 630 631 return (rman_activate_resource(res)); 632} 633 634static int 635fdtbus_deactivate_resource(device_t bus, device_t child, int type, int rid, 636 struct resource *res) 637{ 638 639 return (rman_deactivate_resource(res)); 640} 641 642static const char * 643fdtbus_ofw_get_name(device_t bus, device_t dev) 644{ 645 struct fdtbus_devinfo *di; 646 647 return ((di = device_get_ivars(dev)) == NULL ? NULL : di->di_name); 648} 649 650static phandle_t 651fdtbus_ofw_get_node(device_t bus, device_t dev) 652{ 653 struct fdtbus_devinfo *di; 654 655 return ((di = device_get_ivars(dev)) == NULL ? 0 : di->di_node); 656} 657 658static const char * 659fdtbus_ofw_get_type(device_t bus, device_t dev) 660{ 661 struct fdtbus_devinfo *di; 662 663 return ((di = device_get_ivars(dev)) == NULL ? NULL : di->di_type); 664} 665 666static const char * 667fdtbus_ofw_get_compat(device_t bus, device_t dev) 668{ 669 struct fdtbus_devinfo *di; 670 671 return ((di = device_get_ivars(dev)) == NULL ? NULL : di->di_compat); 672} 673 674 675