iobus.c revision 131102
1/* 2 * Copyright 2002 by Peter Grehan. All rights reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions 6 * are met: 7 * 1. Redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the following disclaimer. 9 * 2. Redistributions in binary form must reproduce the above copyright 10 * notice, this list of conditions and the following disclaimer in the 11 * documentation and/or other materials provided with the distribution. 12 * 3. The name of the author may not be used to endorse or promote products 13 * derived from this software without specific prior written permission. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 16 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 17 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 19 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 20 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 21 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 22 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 23 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25 * SUCH DAMAGE. 26 * 27 * $FreeBSD: head/sys/powerpc/psim/iobus.c 131102 2004-06-25 13:42:48Z grehan $ 28 */ 29 30/* 31 * PSIM 'iobus' local bus. Should be set up in the device tree like: 32 * 33 * /iobus@0x80000000/name psim-iobus 34 * 35 * Code borrowed from various nexus.c and uninorth.c :-) 36 */ 37 38#include <sys/param.h> 39#include <sys/systm.h> 40#include <sys/kernel.h> 41#include <sys/malloc.h> 42#include <sys/module.h> 43#include <sys/bus.h> 44#include <machine/bus.h> 45#include <sys/rman.h> 46 47#include <dev/ofw/openfirm.h> 48 49#include <machine/vmparam.h> 50#include <vm/vm.h> 51#include <vm/pmap.h> 52#include <machine/pmap.h> 53 54#include <machine/resource.h> 55#include <machine/nexusvar.h> 56 57#include <powerpc/psim/iobusvar.h> 58 59static MALLOC_DEFINE(M_IOBUS, "iobus", "iobus device information"); 60 61static int iobus_probe(device_t); 62static int iobus_attach(device_t); 63static int iobus_print_child(device_t dev, device_t child); 64static void iobus_probe_nomatch(device_t, device_t); 65static int iobus_read_ivar(device_t, device_t, int, uintptr_t *); 66static int iobus_write_ivar(device_t, device_t, int, uintptr_t); 67static struct resource *iobus_alloc_resource(device_t, device_t, int, int *, 68 u_long, u_long, u_long, u_int); 69static int iobus_activate_resource(device_t, device_t, int, int, 70 struct resource *); 71static int iobus_deactivate_resource(device_t, device_t, int, int, 72 struct resource *); 73static int iobus_release_resource(device_t, device_t, int, int, 74 struct resource *); 75 76/* 77 * Bus interface definition 78 */ 79static device_method_t iobus_methods[] = { 80 /* Device interface */ 81 DEVMETHOD(device_probe, iobus_probe), 82 DEVMETHOD(device_attach, iobus_attach), 83 DEVMETHOD(device_detach, bus_generic_detach), 84 DEVMETHOD(device_shutdown, bus_generic_shutdown), 85 DEVMETHOD(device_suspend, bus_generic_suspend), 86 DEVMETHOD(device_resume, bus_generic_resume), 87 88 /* Bus interface */ 89 DEVMETHOD(bus_print_child, iobus_print_child), 90 DEVMETHOD(bus_probe_nomatch, iobus_probe_nomatch), 91 DEVMETHOD(bus_read_ivar, iobus_read_ivar), 92 DEVMETHOD(bus_write_ivar, iobus_write_ivar), 93 DEVMETHOD(bus_setup_intr, bus_generic_setup_intr), 94 DEVMETHOD(bus_teardown_intr, bus_generic_teardown_intr), 95 96 DEVMETHOD(bus_alloc_resource, iobus_alloc_resource), 97 DEVMETHOD(bus_release_resource, iobus_release_resource), 98 DEVMETHOD(bus_activate_resource, iobus_activate_resource), 99 DEVMETHOD(bus_deactivate_resource, iobus_deactivate_resource), 100 101 { 0, 0 } 102}; 103 104static driver_t iobus_driver = { 105 "iobus", 106 iobus_methods, 107 sizeof(struct iobus_softc) 108}; 109 110devclass_t iobus_devclass; 111 112DRIVER_MODULE(iobus, nexus, iobus_driver, iobus_devclass, 0, 0); 113 114static int 115iobus_probe(device_t dev) 116{ 117 char *type = nexus_get_name(dev); 118 119 if (strcmp(type, "psim-iobus") != 0) 120 return (ENXIO); 121 122 device_set_desc(dev, "PSIM local bus"); 123 return (0); 124} 125 126/* 127 * Add interrupt/addr range to the dev's resource list if present 128 */ 129static void 130iobus_add_intr(phandle_t devnode, struct iobus_devinfo *dinfo) 131{ 132 u_int intr = -1; 133 134 if (OF_getprop(devnode, "interrupt", &intr, sizeof(intr)) != -1) { 135 resource_list_add(&dinfo->id_resources, 136 SYS_RES_IRQ, 0, intr, intr, 1); 137 } 138 dinfo->id_interrupt = intr; 139} 140 141 142static void 143iobus_add_reg(phandle_t devnode, struct iobus_devinfo *dinfo, 144 vm_offset_t iobus_off) 145{ 146 u_int size; 147 int i; 148 149 size = OF_getprop(devnode, "reg", dinfo->id_reg,sizeof(dinfo->id_reg)); 150 151 if (size != -1) { 152 dinfo->id_nregs = size / (sizeof(dinfo->id_reg[0])); 153 154 for (i = 0; i < dinfo->id_nregs; i+= 3) { 155 /* 156 * Scale the absolute addresses back to iobus 157 * relative offsets. This is to better simulate 158 * macio 159 */ 160 dinfo->id_reg[i+1] -= iobus_off; 161 162 resource_list_add(&dinfo->id_resources, 163 SYS_RES_MEMORY, 0, 164 dinfo->id_reg[i+1], 165 dinfo->id_reg[i+1] + 166 dinfo->id_reg[i+2], 167 dinfo->id_reg[i+2]); 168 } 169 } 170} 171 172 173static int 174iobus_attach(device_t dev) 175{ 176 struct iobus_softc *sc; 177 struct iobus_devinfo *dinfo; 178 phandle_t root; 179 phandle_t child; 180 device_t cdev; 181 char *name; 182 u_int reg[2]; 183 int size; 184 185 sc = device_get_softc(dev); 186 sc->sc_node = nexus_get_node(dev); 187 188 /* 189 * Find the base addr/size of the iobus, and initialize the 190 * resource manager 191 */ 192 size = OF_getprop(sc->sc_node, "reg", reg, sizeof(reg)); 193 if (size == sizeof(reg)) { 194 sc->sc_addr = reg[0]; 195 sc->sc_size = reg[1]; 196 } else { 197 return (ENXIO); 198 } 199 200 sc->sc_mem_rman.rm_type = RMAN_ARRAY; 201 sc->sc_mem_rman.rm_descr = "IOBus Device Memory"; 202 if (rman_init(&sc->sc_mem_rman) != 0) { 203 device_printf(dev, 204 "failed to init mem range resources\n"); 205 return (ENXIO); 206 } 207 rman_manage_region(&sc->sc_mem_rman, 0, sc->sc_size); 208 209 /* 210 * Iterate through the sub-devices 211 */ 212 root = sc->sc_node; 213 214 for (child = OF_child(root); child != 0; child = OF_peer(child)) { 215 OF_getprop_alloc(child, "name", 1, (void **)&name); 216 217 cdev = device_add_child(dev, NULL, -1); 218 if (cdev != NULL) { 219 dinfo = malloc(sizeof(*dinfo), M_IOBUS, M_WAITOK); 220 memset(dinfo, 0, sizeof(*dinfo)); 221 resource_list_init(&dinfo->id_resources); 222 dinfo->id_node = child; 223 dinfo->id_name = name; 224 iobus_add_intr(child, dinfo); 225 iobus_add_reg(child, dinfo, sc->sc_addr); 226 device_set_ivars(cdev, dinfo); 227 } else { 228 free(name, M_OFWPROP); 229 } 230 } 231 232 return (bus_generic_attach(dev)); 233} 234 235 236static int 237iobus_print_child(device_t dev, device_t child) 238{ 239 struct iobus_devinfo *dinfo; 240 struct resource_list *rl; 241 int retval = 0; 242 243 dinfo = device_get_ivars(child); 244 rl = &dinfo->id_resources; 245 246 retval += bus_print_child_header(dev, child); 247 248 retval += printf(" offset 0x%x", dinfo->id_reg[1]); 249 retval += resource_list_print_type(rl, "irq", SYS_RES_IRQ, "%ld"); 250 251 retval += bus_print_child_footer(dev, child); 252 253 return (retval); 254} 255 256 257static void 258iobus_probe_nomatch(device_t dev, device_t child) 259{ 260} 261 262 263static int 264iobus_read_ivar(device_t dev, device_t child, int which, uintptr_t *result) 265{ 266 struct iobus_devinfo *dinfo; 267 268 if ((dinfo = device_get_ivars(child)) == 0) 269 return (ENOENT); 270 271 switch (which) { 272 case IOBUS_IVAR_NODE: 273 *result = dinfo->id_node; 274 break; 275 case IOBUS_IVAR_NAME: 276 *result = (uintptr_t)dinfo->id_name; 277 break; 278 case IOBUS_IVAR_NREGS: 279 *result = dinfo->id_nregs; 280 break; 281 case IOBUS_IVAR_REGS: 282 *result = (uintptr_t)dinfo->id_reg; 283 break; 284 default: 285 return (ENOENT); 286 } 287 288 return (0); 289} 290 291 292static int 293iobus_write_ivar(device_t dev, device_t child, int which, uintptr_t value) 294{ 295 return (EINVAL); 296} 297 298 299static struct resource * 300iobus_alloc_resource(device_t bus, device_t child, int type, int *rid, 301 u_long start, u_long end, u_long count, u_int flags) 302{ 303 struct iobus_softc *sc; 304 int needactivate; 305 struct resource *rv; 306 struct rman *rm; 307 bus_space_tag_t tagval; 308 309 sc = device_get_softc(bus); 310 311 needactivate = flags & RF_ACTIVE; 312 flags &= ~RF_ACTIVE; 313 314 switch (type) { 315 case SYS_RES_MEMORY: 316 case SYS_RES_IOPORT: 317 rm = &sc->sc_mem_rman; 318 tagval = PPC_BUS_SPACE_MEM; 319 break; 320 case SYS_RES_IRQ: 321 return (bus_alloc_resource(bus, type, rid, start, end, count, 322 flags)); 323 break; 324 default: 325 device_printf(bus, "unknown resource request from %s\n", 326 device_get_nameunit(child)); 327 return (NULL); 328 } 329 330 rv = rman_reserve_resource(rm, start, end, count, flags, child); 331 if (rv == NULL) { 332 device_printf(bus, "failed to reserve resource for %s\n", 333 device_get_nameunit(child)); 334 return (NULL); 335 } 336 337 rman_set_bustag(rv, tagval); 338 rman_set_bushandle(rv, rman_get_start(rv)); 339 340 if (needactivate) { 341 if (bus_activate_resource(child, type, *rid, rv) != 0) { 342 device_printf(bus, 343 "failed to activate resource for %s\n", 344 device_get_nameunit(child)); 345 rman_release_resource(rv); 346 return (NULL); 347 } 348 } 349 350 return (rv); 351} 352 353 354static int 355iobus_release_resource(device_t bus, device_t child, int type, int rid, 356 struct resource *res) 357{ 358 if (rman_get_flags(res) & RF_ACTIVE) { 359 int error = bus_deactivate_resource(child, type, rid, res); 360 if (error) 361 return error; 362 } 363 364 return (rman_release_resource(res)); 365} 366 367 368static int 369iobus_activate_resource(device_t bus, device_t child, int type, int rid, 370 struct resource *res) 371{ 372 struct iobus_softc *sc; 373 void *p; 374 375 sc = device_get_softc(bus); 376 377 if (type == SYS_RES_IRQ) 378 return (bus_activate_resource(bus, type, rid, res)); 379 380 if ((type == SYS_RES_MEMORY) || (type == SYS_RES_IOPORT)) { 381 p = pmap_mapdev((vm_offset_t)rman_get_start(res) + sc->sc_addr, 382 (vm_size_t)rman_get_size(res)); 383 if (p == NULL) 384 return (ENOMEM); 385 rman_set_virtual(res, p); 386 rman_set_bushandle(res, (u_long)p); 387 } 388 389 return (rman_activate_resource(res)); 390} 391 392 393static int 394iobus_deactivate_resource(device_t bus, device_t child, int type, int rid, 395 struct resource *res) 396{ 397 /* 398 * If this is a memory resource, unmap it. 399 */ 400 if ((type == SYS_RES_MEMORY) || (type == SYS_RES_IOPORT)) { 401 u_int32_t psize; 402 403 psize = rman_get_size(res); 404 pmap_unmapdev((vm_offset_t)rman_get_virtual(res), psize); 405 } 406 407 return (rman_deactivate_resource(res)); 408} 409