1208747Sraj/*- 2266128Sian * Copyright (c) 2013 Nathan Whitehorn 3208747Sraj * All rights reserved. 4208747Sraj * 5208747Sraj * Redistribution and use in source and binary forms, with or without 6208747Sraj * modification, are permitted provided that the following conditions 7208747Sraj * are met: 8208747Sraj * 1. Redistributions of source code must retain the above copyright 9208747Sraj * notice, this list of conditions and the following disclaimer. 10208747Sraj * 2. Redistributions in binary form must reproduce the above copyright 11208747Sraj * notice, this list of conditions and the following disclaimer in the 12208747Sraj * documentation and/or other materials provided with the distribution. 13208747Sraj * 14208747Sraj * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15208747Sraj * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16208747Sraj * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17266128Sian * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18208747Sraj * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19208747Sraj * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20208747Sraj * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21208747Sraj * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22208747Sraj * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23208747Sraj * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24208747Sraj * SUCH DAMAGE. 25208747Sraj */ 26208747Sraj 27208747Sraj#include <sys/cdefs.h> 28208747Sraj__FBSDID("$FreeBSD: releng/10.2/sys/dev/fdt/simplebus.c 283477 2015-05-24 17:51:57Z ian $"); 29208747Sraj#include <sys/param.h> 30208747Sraj#include <sys/systm.h> 31208747Sraj#include <sys/module.h> 32208747Sraj#include <sys/bus.h> 33266128Sian#include <sys/conf.h> 34266128Sian#include <sys/kernel.h> 35208747Sraj#include <sys/rman.h> 36208747Sraj 37266128Sian#include <dev/ofw/openfirm.h> 38208747Sraj#include <dev/ofw/ofw_bus.h> 39208747Sraj#include <dev/ofw/ofw_bus_subr.h> 40208747Sraj 41283477Sian#include <dev/fdt/simplebus.h> 42208747Sraj 43208747Sraj/* 44266128Sian * Bus interface. 45208747Sraj */ 46266128Sianstatic int simplebus_probe(device_t dev); 47266128Sianstatic int simplebus_attach(device_t dev); 48208747Srajstatic struct resource *simplebus_alloc_resource(device_t, device_t, int, 49208747Sraj int *, u_long, u_long, u_long, u_int); 50266128Sianstatic void simplebus_probe_nomatch(device_t bus, device_t child); 51266128Sianstatic int simplebus_print_child(device_t bus, device_t child); 52283477Sianstatic device_t simplebus_add_child(device_t dev, u_int order, 53283477Sian const char *name, int unit); 54283477Sianstatic struct resource_list *simplebus_get_resource_list(device_t bus, 55283477Sian device_t child); 56266128Sian/* 57266128Sian * ofw_bus interface 58266128Sian */ 59266128Sianstatic const struct ofw_bus_devinfo *simplebus_get_devinfo(device_t bus, 60266128Sian device_t child); 61208747Sraj 62208747Sraj/* 63266128Sian * local methods 64208747Sraj */ 65266128Sian 66266128Sianstatic int simplebus_fill_ranges(phandle_t node, 67266128Sian struct simplebus_softc *sc); 68266128Sian 69266128Sian/* 70266128Sian * Driver methods. 71266128Sian */ 72266128Sianstatic device_method_t simplebus_methods[] = { 73208747Sraj /* Device interface */ 74208747Sraj DEVMETHOD(device_probe, simplebus_probe), 75208747Sraj DEVMETHOD(device_attach, simplebus_attach), 76283477Sian DEVMETHOD(device_detach, bus_generic_detach), 77283477Sian DEVMETHOD(device_shutdown, bus_generic_shutdown), 78283477Sian DEVMETHOD(device_suspend, bus_generic_suspend), 79283477Sian DEVMETHOD(device_resume, bus_generic_resume), 80208747Sraj 81208747Sraj /* Bus interface */ 82283477Sian DEVMETHOD(bus_add_child, simplebus_add_child), 83208747Sraj DEVMETHOD(bus_print_child, simplebus_print_child), 84266128Sian DEVMETHOD(bus_probe_nomatch, simplebus_probe_nomatch), 85283477Sian DEVMETHOD(bus_read_ivar, bus_generic_read_ivar), 86283477Sian DEVMETHOD(bus_write_ivar, bus_generic_write_ivar), 87266128Sian DEVMETHOD(bus_setup_intr, bus_generic_setup_intr), 88266128Sian DEVMETHOD(bus_teardown_intr, bus_generic_teardown_intr), 89208747Sraj DEVMETHOD(bus_alloc_resource, simplebus_alloc_resource), 90266128Sian DEVMETHOD(bus_release_resource, bus_generic_release_resource), 91266128Sian DEVMETHOD(bus_activate_resource, bus_generic_activate_resource), 92266128Sian DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource), 93266128Sian DEVMETHOD(bus_adjust_resource, bus_generic_adjust_resource), 94283477Sian DEVMETHOD(bus_set_resource, bus_generic_rl_set_resource), 95283477Sian DEVMETHOD(bus_get_resource, bus_generic_rl_get_resource), 96266128Sian DEVMETHOD(bus_child_pnpinfo_str, ofw_bus_gen_child_pnpinfo_str), 97283477Sian DEVMETHOD(bus_get_resource_list, simplebus_get_resource_list), 98208747Sraj 99266128Sian /* ofw_bus interface */ 100208747Sraj DEVMETHOD(ofw_bus_get_devinfo, simplebus_get_devinfo), 101208747Sraj DEVMETHOD(ofw_bus_get_compat, ofw_bus_gen_get_compat), 102208747Sraj DEVMETHOD(ofw_bus_get_model, ofw_bus_gen_get_model), 103208747Sraj DEVMETHOD(ofw_bus_get_name, ofw_bus_gen_get_name), 104208747Sraj DEVMETHOD(ofw_bus_get_node, ofw_bus_gen_get_node), 105208747Sraj DEVMETHOD(ofw_bus_get_type, ofw_bus_gen_get_type), 106208747Sraj 107266128Sian DEVMETHOD_END 108208747Sraj}; 109208747Sraj 110283477SianDEFINE_CLASS_0(simplebus, simplebus_driver, simplebus_methods, 111283477Sian sizeof(struct simplebus_softc)); 112283477Sian 113266128Sianstatic devclass_t simplebus_devclass; 114270075SianEARLY_DRIVER_MODULE(simplebus, ofwbus, simplebus_driver, simplebus_devclass, 115270075Sian 0, 0, BUS_PASS_BUS); 116270075SianEARLY_DRIVER_MODULE(simplebus, simplebus, simplebus_driver, simplebus_devclass, 117270075Sian 0, 0, BUS_PASS_BUS + BUS_PASS_ORDER_MIDDLE); 118208747Sraj 119208747Srajstatic int 120208747Srajsimplebus_probe(device_t dev) 121208747Sraj{ 122266128Sian 123266152Sian if (!ofw_bus_status_okay(dev)) 124266152Sian return (ENXIO); 125266152Sian 126273678Sian /* 127273678Sian * FDT data puts a "simple-bus" compatible string on many things that 128273678Sian * have children but aren't really busses in our world. Without a 129273678Sian * ranges property we will fail to attach, so just fail to probe too. 130273678Sian */ 131273678Sian if (!(ofw_bus_is_compatible(dev, "simple-bus") && 132273678Sian ofw_bus_has_prop(dev, "ranges")) && 133266128Sian (ofw_bus_get_type(dev) == NULL || strcmp(ofw_bus_get_type(dev), 134266128Sian "soc") != 0)) 135208747Sraj return (ENXIO); 136208747Sraj 137208747Sraj device_set_desc(dev, "Flattened device tree simple bus"); 138208747Sraj 139248480Sray return (BUS_PROBE_GENERIC); 140208747Sraj} 141208747Sraj 142208747Srajstatic int 143208747Srajsimplebus_attach(device_t dev) 144208747Sraj{ 145266128Sian struct simplebus_softc *sc; 146266128Sian phandle_t node; 147208747Sraj 148208747Sraj sc = device_get_softc(dev); 149283477Sian simplebus_init(dev, 0); 150283477Sian if (simplebus_fill_ranges(sc->node, sc) < 0) { 151283477Sian device_printf(dev, "could not get ranges\n"); 152283477Sian return (ENXIO); 153283477Sian } 154208747Sraj 155283477Sian /* 156283477Sian * In principle, simplebus could have an interrupt map, but ignore that 157283477Sian * for now 158283477Sian */ 159283477Sian 160283477Sian for (node = OF_child(sc->node); node > 0; node = OF_peer(node)) 161283477Sian simplebus_add_device(dev, node, 0, NULL, -1, NULL); 162283477Sian return (bus_generic_attach(dev)); 163283477Sian} 164283477Sian 165283477Sianvoid 166283477Siansimplebus_init(device_t dev, phandle_t node) 167283477Sian{ 168283477Sian struct simplebus_softc *sc; 169283477Sian 170283477Sian sc = device_get_softc(dev); 171283477Sian if (node == 0) 172283477Sian node = ofw_bus_get_node(dev); 173266128Sian sc->dev = dev; 174266128Sian sc->node = node; 175266128Sian 176208747Sraj /* 177266128Sian * Some important numbers 178208747Sraj */ 179266128Sian sc->acells = 2; 180266128Sian OF_getencprop(node, "#address-cells", &sc->acells, sizeof(sc->acells)); 181266128Sian sc->scells = 1; 182266128Sian OF_getencprop(node, "#size-cells", &sc->scells, sizeof(sc->scells)); 183266128Sian} 184208747Sraj 185266128Sianstatic int 186266128Siansimplebus_fill_ranges(phandle_t node, struct simplebus_softc *sc) 187266128Sian{ 188266128Sian int host_address_cells; 189266128Sian cell_t *base_ranges; 190266128Sian ssize_t nbase_ranges; 191266128Sian int err; 192266128Sian int i, j, k; 193266128Sian 194266128Sian err = OF_searchencprop(OF_parent(node), "#address-cells", 195266128Sian &host_address_cells, sizeof(host_address_cells)); 196266128Sian if (err <= 0) 197266128Sian return (-1); 198266128Sian 199266128Sian nbase_ranges = OF_getproplen(node, "ranges"); 200266128Sian if (nbase_ranges < 0) 201266128Sian return (-1); 202266128Sian sc->nranges = nbase_ranges / sizeof(cell_t) / 203266128Sian (sc->acells + host_address_cells + sc->scells); 204266128Sian if (sc->nranges == 0) 205266128Sian return (0); 206266128Sian 207266128Sian sc->ranges = malloc(sc->nranges * sizeof(sc->ranges[0]), 208266128Sian M_DEVBUF, M_WAITOK); 209266128Sian base_ranges = malloc(nbase_ranges, M_DEVBUF, M_WAITOK); 210266128Sian OF_getencprop(node, "ranges", base_ranges, nbase_ranges); 211266128Sian 212266128Sian for (i = 0, j = 0; i < sc->nranges; i++) { 213266128Sian sc->ranges[i].bus = 0; 214266128Sian for (k = 0; k < sc->acells; k++) { 215266128Sian sc->ranges[i].bus <<= 32; 216266128Sian sc->ranges[i].bus |= base_ranges[j++]; 217208747Sraj } 218266128Sian sc->ranges[i].host = 0; 219266128Sian for (k = 0; k < host_address_cells; k++) { 220266128Sian sc->ranges[i].host <<= 32; 221266128Sian sc->ranges[i].host |= base_ranges[j++]; 222266128Sian } 223266128Sian sc->ranges[i].size = 0; 224266128Sian for (k = 0; k < sc->scells; k++) { 225266128Sian sc->ranges[i].size <<= 32; 226266128Sian sc->ranges[i].size |= base_ranges[j++]; 227266128Sian } 228208747Sraj } 229208747Sraj 230266128Sian free(base_ranges, M_DEVBUF); 231266128Sian return (sc->nranges); 232208747Sraj} 233208747Sraj 234283477Sianstruct simplebus_devinfo * 235283477Siansimplebus_setup_dinfo(device_t dev, phandle_t node, 236283477Sian struct simplebus_devinfo *di) 237208747Sraj{ 238266128Sian struct simplebus_softc *sc; 239266128Sian struct simplebus_devinfo *ndi; 240208747Sraj 241266128Sian sc = device_get_softc(dev); 242283477Sian if (di == NULL) 243283477Sian ndi = malloc(sizeof(*ndi), M_DEVBUF, M_WAITOK | M_ZERO); 244283477Sian else 245283477Sian ndi = di; 246266128Sian if (ofw_bus_gen_setup_devinfo(&ndi->obdinfo, node) != 0) { 247283477Sian if (di == NULL) 248283477Sian free(ndi, M_DEVBUF); 249266128Sian return (NULL); 250266128Sian } 251208747Sraj 252266128Sian resource_list_init(&ndi->rl); 253283334Sian ofw_bus_reg_to_rl(dev, node, sc->acells, sc->scells, &ndi->rl); 254273675Sian ofw_bus_intr_to_rl(dev, node, &ndi->rl); 255266128Sian 256266128Sian return (ndi); 257208747Sraj} 258208747Sraj 259283477Siandevice_t 260283477Siansimplebus_add_device(device_t dev, phandle_t node, u_int order, 261283477Sian const char *name, int unit, struct simplebus_devinfo *di) 262283477Sian{ 263283477Sian struct simplebus_devinfo *ndi; 264283477Sian device_t cdev; 265283477Sian 266283477Sian if ((ndi = simplebus_setup_dinfo(dev, node, di)) == NULL) 267283477Sian return (NULL); 268283477Sian cdev = device_add_child_ordered(dev, order, name, unit); 269283477Sian if (cdev == NULL) { 270283477Sian device_printf(dev, "<%s>: device_add_child failed\n", 271283477Sian ndi->obdinfo.obd_name); 272283477Sian resource_list_free(&ndi->rl); 273283477Sian ofw_bus_gen_destroy_devinfo(&ndi->obdinfo); 274283477Sian if (di == NULL) 275283477Sian free(ndi, M_DEVBUF); 276283477Sian return (NULL); 277283477Sian } 278283477Sian device_set_ivars(cdev, ndi); 279283477Sian 280283477Sian return(cdev); 281283477Sian} 282283477Sian 283283477Sianstatic device_t 284283477Siansimplebus_add_child(device_t dev, u_int order, const char *name, int unit) 285283477Sian{ 286283477Sian device_t cdev; 287283477Sian struct simplebus_devinfo *ndi; 288283477Sian 289283477Sian cdev = device_add_child_ordered(dev, order, name, unit); 290283477Sian if (cdev == NULL) 291283477Sian return (NULL); 292283477Sian 293283477Sian ndi = malloc(sizeof(*ndi), M_DEVBUF, M_WAITOK | M_ZERO); 294283477Sian ndi->obdinfo.obd_node = -1; 295283477Sian resource_list_init(&ndi->rl); 296283477Sian device_set_ivars(cdev, ndi); 297283477Sian 298283477Sian return (cdev); 299283477Sian} 300283477Sian 301266128Sianstatic const struct ofw_bus_devinfo * 302266128Siansimplebus_get_devinfo(device_t bus __unused, device_t child) 303266128Sian{ 304266128Sian struct simplebus_devinfo *ndi; 305266128Sian 306266128Sian ndi = device_get_ivars(child); 307266128Sian return (&ndi->obdinfo); 308266128Sian} 309266128Sian 310283477Sianstatic struct resource_list * 311283477Siansimplebus_get_resource_list(device_t bus __unused, device_t child) 312283477Sian{ 313283477Sian struct simplebus_devinfo *ndi; 314283477Sian 315283477Sian ndi = device_get_ivars(child); 316283477Sian return (&ndi->rl); 317283477Sian} 318283477Sian 319208747Srajstatic struct resource * 320208747Srajsimplebus_alloc_resource(device_t bus, device_t child, int type, int *rid, 321208747Sraj u_long start, u_long end, u_long count, u_int flags) 322208747Sraj{ 323266128Sian struct simplebus_softc *sc; 324208747Sraj struct simplebus_devinfo *di; 325208747Sraj struct resource_list_entry *rle; 326266128Sian int j; 327208747Sraj 328266128Sian sc = device_get_softc(bus); 329266128Sian 330208747Sraj /* 331208747Sraj * Request for the default allocation with a given rid: use resource 332208747Sraj * list stored in the local device info. 333208747Sraj */ 334208747Sraj if ((start == 0UL) && (end == ~0UL)) { 335208747Sraj if ((di = device_get_ivars(child)) == NULL) 336208747Sraj return (NULL); 337208747Sraj 338208747Sraj if (type == SYS_RES_IOPORT) 339208747Sraj type = SYS_RES_MEMORY; 340208747Sraj 341266128Sian rle = resource_list_find(&di->rl, type, *rid); 342208747Sraj if (rle == NULL) { 343248481Sray if (bootverbose) 344248481Sray device_printf(bus, "no default resources for " 345248481Sray "rid = %d, type = %d\n", *rid, type); 346208747Sraj return (NULL); 347208747Sraj } 348208747Sraj start = rle->start; 349208747Sraj end = rle->end; 350208747Sraj count = rle->count; 351266128Sian } 352266128Sian 353266128Sian if (type == SYS_RES_MEMORY) { 354266128Sian /* Remap through ranges property */ 355266128Sian for (j = 0; j < sc->nranges; j++) { 356266128Sian if (start >= sc->ranges[j].bus && end < 357266128Sian sc->ranges[j].bus + sc->ranges[j].size) { 358266128Sian start -= sc->ranges[j].bus; 359266128Sian start += sc->ranges[j].host; 360266128Sian end -= sc->ranges[j].bus; 361266128Sian end += sc->ranges[j].host; 362266128Sian break; 363266128Sian } 364266128Sian } 365266128Sian if (j == sc->nranges && sc->nranges != 0) { 366266128Sian if (bootverbose) 367266128Sian device_printf(bus, "Could not map resource " 368266128Sian "%#lx-%#lx\n", start, end); 369266128Sian 370266128Sian return (NULL); 371266128Sian } 372208747Sraj } 373208747Sraj 374208747Sraj return (bus_generic_alloc_resource(bus, child, type, rid, start, end, 375208747Sraj count, flags)); 376208747Sraj} 377208747Sraj 378257457Sbrooksstatic int 379266128Siansimplebus_print_res(struct simplebus_devinfo *di) 380257457Sbrooks{ 381266128Sian int rv; 382257457Sbrooks 383266128Sian rv = 0; 384266128Sian rv += resource_list_print_type(&di->rl, "mem", SYS_RES_MEMORY, "%#lx"); 385266128Sian rv += resource_list_print_type(&di->rl, "irq", SYS_RES_IRQ, "%ld"); 386266128Sian return (rv); 387257457Sbrooks} 388257457Sbrooks 389266128Sianstatic void 390266128Siansimplebus_probe_nomatch(device_t bus, device_t child) 391257457Sbrooks{ 392266196Sian const char *name, *type, *compat; 393257457Sbrooks 394266128Sian if (!bootverbose) 395266128Sian return; 396257457Sbrooks 397283477Sian compat = ofw_bus_get_compat(child); 398283477Sian if (compat == NULL) 399283477Sian return; 400266128Sian name = ofw_bus_get_name(child); 401266128Sian type = ofw_bus_get_type(child); 402257457Sbrooks 403266128Sian device_printf(bus, "<%s>", name != NULL ? name : "unknown"); 404266128Sian simplebus_print_res(device_get_ivars(child)); 405266196Sian if (!ofw_bus_status_okay(child)) 406266196Sian printf(" disabled"); 407266196Sian if (type) 408266196Sian printf(" type %s", type); 409283477Sian printf(" compat %s (no driver attached)\n", compat); 410257457Sbrooks} 411257457Sbrooks 412208747Srajstatic int 413266128Siansimplebus_print_child(device_t bus, device_t child) 414208747Sraj{ 415266128Sian int rv; 416208747Sraj 417266128Sian rv = bus_print_child_header(bus, child); 418266128Sian rv += simplebus_print_res(device_get_ivars(child)); 419266196Sian if (!ofw_bus_status_okay(child)) 420266196Sian rv += printf(" disabled"); 421266128Sian rv += bus_print_child_footer(bus, child); 422266128Sian return (rv); 423208747Sraj} 424