simplebus.c revision 266128
1/*- 2 * Copyright (c) 2013 Nathan Whitehorn 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 * SUCH DAMAGE. 25 */ 26 27#include <sys/cdefs.h> 28__FBSDID("$FreeBSD: stable/10/sys/dev/fdt/simplebus.c 266128 2014-05-15 14:26:11Z ian $"); 29#include <sys/param.h> 30#include <sys/systm.h> 31#include <sys/module.h> 32#include <sys/bus.h> 33#include <sys/conf.h> 34#include <sys/kernel.h> 35#include <sys/rman.h> 36 37#include <dev/ofw/openfirm.h> 38#include <dev/ofw/ofw_bus.h> 39#include <dev/ofw/ofw_bus_subr.h> 40 41struct simplebus_range { 42 uint64_t bus; 43 uint64_t host; 44 uint64_t size; 45}; 46 47struct simplebus_softc { 48 device_t dev; 49 phandle_t node; 50 51 struct simplebus_range *ranges; 52 int nranges; 53 54 pcell_t acells, scells; 55}; 56 57struct simplebus_devinfo { 58 struct ofw_bus_devinfo obdinfo; 59 struct resource_list rl; 60}; 61 62/* 63 * Bus interface. 64 */ 65static int simplebus_probe(device_t dev); 66static int simplebus_attach(device_t dev); 67static struct resource *simplebus_alloc_resource(device_t, device_t, int, 68 int *, u_long, u_long, u_long, u_int); 69static void simplebus_probe_nomatch(device_t bus, device_t child); 70static int simplebus_print_child(device_t bus, device_t child); 71 72/* 73 * ofw_bus interface 74 */ 75static const struct ofw_bus_devinfo *simplebus_get_devinfo(device_t bus, 76 device_t child); 77 78/* 79 * local methods 80 */ 81 82static int simplebus_fill_ranges(phandle_t node, 83 struct simplebus_softc *sc); 84static struct simplebus_devinfo *simplebus_setup_dinfo(device_t dev, 85 phandle_t node); 86 87/* 88 * Driver methods. 89 */ 90static device_method_t simplebus_methods[] = { 91 /* Device interface */ 92 DEVMETHOD(device_probe, simplebus_probe), 93 DEVMETHOD(device_attach, simplebus_attach), 94 95 /* Bus interface */ 96 DEVMETHOD(bus_print_child, simplebus_print_child), 97 DEVMETHOD(bus_probe_nomatch, simplebus_probe_nomatch), 98 DEVMETHOD(bus_setup_intr, bus_generic_setup_intr), 99 DEVMETHOD(bus_teardown_intr, bus_generic_teardown_intr), 100 DEVMETHOD(bus_alloc_resource, simplebus_alloc_resource), 101 DEVMETHOD(bus_release_resource, bus_generic_release_resource), 102 DEVMETHOD(bus_activate_resource, bus_generic_activate_resource), 103 DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource), 104 DEVMETHOD(bus_adjust_resource, bus_generic_adjust_resource), 105 DEVMETHOD(bus_child_pnpinfo_str, ofw_bus_gen_child_pnpinfo_str), 106 107 /* ofw_bus interface */ 108 DEVMETHOD(ofw_bus_get_devinfo, simplebus_get_devinfo), 109 DEVMETHOD(ofw_bus_get_compat, ofw_bus_gen_get_compat), 110 DEVMETHOD(ofw_bus_get_model, ofw_bus_gen_get_model), 111 DEVMETHOD(ofw_bus_get_name, ofw_bus_gen_get_name), 112 DEVMETHOD(ofw_bus_get_node, ofw_bus_gen_get_node), 113 DEVMETHOD(ofw_bus_get_type, ofw_bus_gen_get_type), 114 115 DEVMETHOD_END 116}; 117 118static driver_t simplebus_driver = { 119 "simplebus", 120 simplebus_methods, 121 sizeof(struct simplebus_softc) 122}; 123static devclass_t simplebus_devclass; 124DRIVER_MODULE(simplebus, nexus, simplebus_driver, simplebus_devclass, 0, 0); 125DRIVER_MODULE(simplebus, simplebus, simplebus_driver, simplebus_devclass, 0, 0); 126 127static int 128simplebus_probe(device_t dev) 129{ 130 131 if (!ofw_bus_is_compatible(dev, "simple-bus") && 132 (ofw_bus_get_type(dev) == NULL || strcmp(ofw_bus_get_type(dev), 133 "soc") != 0)) 134 return (ENXIO); 135 136 device_set_desc(dev, "Flattened device tree simple bus"); 137 138 return (BUS_PROBE_GENERIC); 139} 140 141static int 142simplebus_attach(device_t dev) 143{ 144 struct simplebus_softc *sc; 145 struct simplebus_devinfo *di; 146 phandle_t node; 147 device_t cdev; 148 149 node = ofw_bus_get_node(dev); 150 sc = device_get_softc(dev); 151 152 sc->dev = dev; 153 sc->node = node; 154 155 /* 156 * Some important numbers 157 */ 158 sc->acells = 2; 159 OF_getencprop(node, "#address-cells", &sc->acells, sizeof(sc->acells)); 160 sc->scells = 1; 161 OF_getencprop(node, "#size-cells", &sc->scells, sizeof(sc->scells)); 162 163 if (simplebus_fill_ranges(node, sc) < 0) { 164 device_printf(dev, "could not get ranges\n"); 165 return (ENXIO); 166 } 167 168 /* 169 * In principle, simplebus could have an interrupt map, but ignore that 170 * for now 171 */ 172 173 for (node = OF_child(node); node > 0; node = OF_peer(node)) { 174 if ((di = simplebus_setup_dinfo(dev, node)) == NULL) 175 continue; 176 cdev = device_add_child(dev, NULL, -1); 177 if (cdev == NULL) { 178 device_printf(dev, "<%s>: device_add_child failed\n", 179 di->obdinfo.obd_name); 180 resource_list_free(&di->rl); 181 ofw_bus_gen_destroy_devinfo(&di->obdinfo); 182 free(di, M_DEVBUF); 183 continue; 184 } 185 device_set_ivars(cdev, di); 186 } 187 188 return (bus_generic_attach(dev)); 189} 190 191static int 192simplebus_fill_ranges(phandle_t node, struct simplebus_softc *sc) 193{ 194 int host_address_cells; 195 cell_t *base_ranges; 196 ssize_t nbase_ranges; 197 int err; 198 int i, j, k; 199 200 err = OF_searchencprop(OF_parent(node), "#address-cells", 201 &host_address_cells, sizeof(host_address_cells)); 202 if (err <= 0) 203 return (-1); 204 205 nbase_ranges = OF_getproplen(node, "ranges"); 206 if (nbase_ranges < 0) 207 return (-1); 208 sc->nranges = nbase_ranges / sizeof(cell_t) / 209 (sc->acells + host_address_cells + sc->scells); 210 if (sc->nranges == 0) 211 return (0); 212 213 sc->ranges = malloc(sc->nranges * sizeof(sc->ranges[0]), 214 M_DEVBUF, M_WAITOK); 215 base_ranges = malloc(nbase_ranges, M_DEVBUF, M_WAITOK); 216 OF_getencprop(node, "ranges", base_ranges, nbase_ranges); 217 218 for (i = 0, j = 0; i < sc->nranges; i++) { 219 sc->ranges[i].bus = 0; 220 for (k = 0; k < sc->acells; k++) { 221 sc->ranges[i].bus <<= 32; 222 sc->ranges[i].bus |= base_ranges[j++]; 223 } 224 sc->ranges[i].host = 0; 225 for (k = 0; k < host_address_cells; k++) { 226 sc->ranges[i].host <<= 32; 227 sc->ranges[i].host |= base_ranges[j++]; 228 } 229 sc->ranges[i].size = 0; 230 for (k = 0; k < sc->scells; k++) { 231 sc->ranges[i].size <<= 32; 232 sc->ranges[i].size |= base_ranges[j++]; 233 } 234 } 235 236 free(base_ranges, M_DEVBUF); 237 return (sc->nranges); 238} 239 240static struct simplebus_devinfo * 241simplebus_setup_dinfo(device_t dev, phandle_t node) 242{ 243 struct simplebus_softc *sc; 244 struct simplebus_devinfo *ndi; 245 uint32_t *reg, *intr, icells; 246 uint64_t phys, size; 247 phandle_t iparent; 248 int i, j, k; 249 int nintr; 250 int nreg; 251 252 sc = device_get_softc(dev); 253 254 ndi = malloc(sizeof(*ndi), M_DEVBUF, M_WAITOK | M_ZERO); 255 if (ofw_bus_gen_setup_devinfo(&ndi->obdinfo, node) != 0) { 256 free(ndi, M_DEVBUF); 257 return (NULL); 258 } 259 260 resource_list_init(&ndi->rl); 261 nreg = OF_getencprop_alloc(node, "reg", sizeof(*reg), (void **)®); 262 if (nreg == -1) 263 nreg = 0; 264 if (nreg % (sc->acells + sc->scells) != 0) { 265 if (bootverbose) 266 device_printf(dev, "Malformed reg property on <%s>\n", 267 ndi->obdinfo.obd_name); 268 nreg = 0; 269 } 270 271 for (i = 0, k = 0; i < nreg; i += sc->acells + sc->scells, k++) { 272 phys = size = 0; 273 for (j = 0; j < sc->acells; j++) { 274 phys <<= 32; 275 phys |= reg[i + j]; 276 } 277 for (j = 0; j < sc->scells; j++) { 278 size <<= 32; 279 size |= reg[i + sc->acells + j]; 280 } 281 282 resource_list_add(&ndi->rl, SYS_RES_MEMORY, k, 283 phys, phys + size - 1, size); 284 } 285 free(reg, M_OFWPROP); 286 287 nintr = OF_getencprop_alloc(node, "interrupts", sizeof(*intr), 288 (void **)&intr); 289 if (nintr > 0) { 290 if (OF_searchencprop(node, "interrupt-parent", &iparent, 291 sizeof(iparent)) == -1) { 292 device_printf(dev, "No interrupt-parent found, " 293 "assuming direct parent\n"); 294 iparent = OF_parent(node); 295 } 296 if (OF_searchencprop(OF_xref_phandle(iparent), 297 "#interrupt-cells", &icells, sizeof(icells)) == -1) { 298 device_printf(dev, "Missing #interrupt-cells property, " 299 "assuming <1>\n"); 300 icells = 1; 301 } 302 if (icells < 1 || icells > nintr) { 303 device_printf(dev, "Invalid #interrupt-cells property " 304 "value <%d>, assuming <1>\n", icells); 305 icells = 1; 306 } 307 for (i = 0, k = 0; i < nintr; i += icells, k++) { 308 intr[i] = ofw_bus_map_intr(dev, iparent, icells, 309 &intr[i]); 310 resource_list_add(&ndi->rl, SYS_RES_IRQ, k, intr[i], 311 intr[i], 1); 312 } 313 free(intr, M_OFWPROP); 314 } 315 316 return (ndi); 317} 318 319static const struct ofw_bus_devinfo * 320simplebus_get_devinfo(device_t bus __unused, device_t child) 321{ 322 struct simplebus_devinfo *ndi; 323 324 ndi = device_get_ivars(child); 325 return (&ndi->obdinfo); 326} 327 328static struct resource * 329simplebus_alloc_resource(device_t bus, device_t child, int type, int *rid, 330 u_long start, u_long end, u_long count, u_int flags) 331{ 332 struct simplebus_softc *sc; 333 struct simplebus_devinfo *di; 334 struct resource_list_entry *rle; 335 int j; 336 337 sc = device_get_softc(bus); 338 339 /* 340 * Request for the default allocation with a given rid: use resource 341 * list stored in the local device info. 342 */ 343 if ((start == 0UL) && (end == ~0UL)) { 344 if ((di = device_get_ivars(child)) == NULL) 345 return (NULL); 346 347 if (type == SYS_RES_IOPORT) 348 type = SYS_RES_MEMORY; 349 350 rle = resource_list_find(&di->rl, type, *rid); 351 if (rle == NULL) { 352 if (bootverbose) 353 device_printf(bus, "no default resources for " 354 "rid = %d, type = %d\n", *rid, type); 355 return (NULL); 356 } 357 start = rle->start; 358 end = rle->end; 359 count = rle->count; 360 } 361 362 if (type == SYS_RES_MEMORY) { 363 /* Remap through ranges property */ 364 for (j = 0; j < sc->nranges; j++) { 365 if (start >= sc->ranges[j].bus && end < 366 sc->ranges[j].bus + sc->ranges[j].size) { 367 start -= sc->ranges[j].bus; 368 start += sc->ranges[j].host; 369 end -= sc->ranges[j].bus; 370 end += sc->ranges[j].host; 371 break; 372 } 373 } 374 if (j == sc->nranges && sc->nranges != 0) { 375 if (bootverbose) 376 device_printf(bus, "Could not map resource " 377 "%#lx-%#lx\n", start, end); 378 379 return (NULL); 380 } 381 } 382 383 return (bus_generic_alloc_resource(bus, child, type, rid, start, end, 384 count, flags)); 385} 386 387static int 388simplebus_print_res(struct simplebus_devinfo *di) 389{ 390 int rv; 391 392 rv = 0; 393 rv += resource_list_print_type(&di->rl, "mem", SYS_RES_MEMORY, "%#lx"); 394 rv += resource_list_print_type(&di->rl, "irq", SYS_RES_IRQ, "%ld"); 395 return (rv); 396} 397 398static void 399simplebus_probe_nomatch(device_t bus, device_t child) 400{ 401 const char *name, *type; 402 403 if (!bootverbose) 404 return; 405 406 name = ofw_bus_get_name(child); 407 type = ofw_bus_get_type(child); 408 409 device_printf(bus, "<%s>", name != NULL ? name : "unknown"); 410 simplebus_print_res(device_get_ivars(child)); 411 printf(" type %s (no driver attached)\n", 412 type != NULL ? type : "unknown"); 413} 414 415static int 416simplebus_print_child(device_t bus, device_t child) 417{ 418 int rv; 419 420 rv = bus_print_child_header(bus, child); 421 rv += simplebus_print_res(device_get_ivars(child)); 422 rv += bus_print_child_footer(bus, child); 423 return (rv); 424} 425 426