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$ 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/ofw_bus.h> 48 #include <dev/ofw/openfirm.h> 49 50 #include <machine/vmparam.h> 51 #include <vm/vm.h> 52 #include <vm/pmap.h> 53 #include <machine/pmap.h> 54 55 #include <machine/resource.h> 56 57 #include <powerpc/psim/iobusvar.h> 58 59 struct iobus_softc { 60 phandle_t sc_node; 61 vm_offset_t sc_addr; 62 vm_offset_t sc_size; 63 struct rman sc_mem_rman; 64 }; 65 66 static MALLOC_DEFINE(M_IOBUS, "iobus", "iobus device information"); 67 68 static int iobus_probe(device_t); 69 static int iobus_attach(device_t); 70 static int iobus_print_child(device_t dev, device_t child); 71 static void iobus_probe_nomatch(device_t, device_t); 72 static int iobus_read_ivar(device_t, device_t, int, uintptr_t *); 73 static int iobus_write_ivar(device_t, device_t, int, uintptr_t); 74 static struct resource *iobus_alloc_resource(device_t, device_t, int, int *, 75 rman_res_t, rman_res_t, rman_res_t, 76 u_int); 77 static int iobus_activate_resource(device_t, device_t, int, int, 78 struct resource *); 79 static int iobus_deactivate_resource(device_t, device_t, int, int, 80 struct resource *); 81 static int iobus_release_resource(device_t, device_t, int, int, 82 struct resource *); 83 84 /* 85 * Bus interface definition 86 */ 87 static device_method_t iobus_methods[] = { 88 /* Device interface */ 89 DEVMETHOD(device_probe, iobus_probe), 90 DEVMETHOD(device_attach, iobus_attach), 91 DEVMETHOD(device_detach, bus_generic_detach), 92 DEVMETHOD(device_shutdown, bus_generic_shutdown), 93 DEVMETHOD(device_suspend, bus_generic_suspend), 94 DEVMETHOD(device_resume, bus_generic_resume), 95 96 /* Bus interface */ 97 DEVMETHOD(bus_print_child, iobus_print_child), 98 DEVMETHOD(bus_probe_nomatch, iobus_probe_nomatch), 99 DEVMETHOD(bus_read_ivar, iobus_read_ivar), 100 DEVMETHOD(bus_write_ivar, iobus_write_ivar), 101 DEVMETHOD(bus_setup_intr, bus_generic_setup_intr), 102 DEVMETHOD(bus_teardown_intr, bus_generic_teardown_intr), 103 104 DEVMETHOD(bus_alloc_resource, iobus_alloc_resource), 105 DEVMETHOD(bus_release_resource, iobus_release_resource), 106 DEVMETHOD(bus_activate_resource, iobus_activate_resource), 107 DEVMETHOD(bus_deactivate_resource, iobus_deactivate_resource), 108 109 { 0, 0 } 110 }; 111 112 static driver_t iobus_driver = { 113 "iobus", 114 iobus_methods, 115 sizeof(struct iobus_softc) 116 }; 117 118 devclass_t iobus_devclass; 119 120 DRIVER_MODULE(iobus, ofwbus, iobus_driver, iobus_devclass, 0, 0); 121 122 static int 123 iobus_probe(device_t dev) 124 { 125 const char *type = ofw_bus_get_name(dev); 126 127 if (strcmp(type, "psim-iobus") != 0) 128 return (ENXIO); 129 130 device_set_desc(dev, "PSIM local bus"); 131 return (0); 132 } 133 134 /* 135 * Add interrupt/addr range to the dev's resource list if present 136 */ 137 static void 138 iobus_add_intr(phandle_t devnode, struct iobus_devinfo *dinfo) 139 { 140 u_int intr = -1; 141 142 if (OF_getprop(devnode, "interrupt", &intr, sizeof(intr)) != -1) { 143 resource_list_add(&dinfo->id_resources, 144 SYS_RES_IRQ, 0, intr, intr, 1); 145 } 146 dinfo->id_interrupt = intr; 147 } 148 149 150 static void 151 iobus_add_reg(phandle_t devnode, struct iobus_devinfo *dinfo, 152 vm_offset_t iobus_off) 153 { 154 u_int size; 155 int i; 156 157 size = OF_getprop(devnode, "reg", dinfo->id_reg,sizeof(dinfo->id_reg)); 158 159 if (size != -1) { 160 dinfo->id_nregs = size / (sizeof(dinfo->id_reg[0])); 161 162 for (i = 0; i < dinfo->id_nregs; i+= 3) { 163 /* 164 * Scale the absolute addresses back to iobus 165 * relative offsets. This is to better simulate 166 * macio 167 */ 168 dinfo->id_reg[i+1] -= iobus_off; 169 170 resource_list_add(&dinfo->id_resources, 171 SYS_RES_MEMORY, 0, 172 dinfo->id_reg[i+1], 173 dinfo->id_reg[i+1] + 174 dinfo->id_reg[i+2], 175 dinfo->id_reg[i+2]); 176 } 177 } 178 } 179 180 181 static int 182 iobus_attach(device_t dev) 183 { 184 struct iobus_softc *sc; 185 struct iobus_devinfo *dinfo; 186 phandle_t root; 187 phandle_t child; 188 device_t cdev; 189 char *name; 190 u_int reg[2]; 191 int size; 192 193 sc = device_get_softc(dev); 194 sc->sc_node = ofw_bus_get_node(dev); 195 196 /* 197 * Find the base addr/size of the iobus, and initialize the 198 * resource manager 199 */ 200 size = OF_getprop(sc->sc_node, "reg", reg, sizeof(reg)); 201 if (size == sizeof(reg)) { 202 sc->sc_addr = reg[0]; 203 sc->sc_size = reg[1]; 204 } else { 205 return (ENXIO); 206 } 207 208 sc->sc_mem_rman.rm_type = RMAN_ARRAY; 209 sc->sc_mem_rman.rm_descr = "IOBus Device Memory"; 210 if (rman_init(&sc->sc_mem_rman) != 0) { 211 device_printf(dev, 212 "failed to init mem range resources\n"); 213 return (ENXIO); 214 } 215 rman_manage_region(&sc->sc_mem_rman, 0, sc->sc_size); 216 217 /* 218 * Iterate through the sub-devices 219 */ 220 root = sc->sc_node; 221 222 for (child = OF_child(root); child != 0; child = OF_peer(child)) { 223 OF_getprop_alloc(child, "name", 1, (void **)&name); 224 225 cdev = device_add_child(dev, NULL, -1); 226 if (cdev != NULL) { 227 dinfo = malloc(sizeof(*dinfo), M_IOBUS, M_WAITOK); 228 memset(dinfo, 0, sizeof(*dinfo)); 229 resource_list_init(&dinfo->id_resources); 230 dinfo->id_node = child; 231 dinfo->id_name = name; 232 iobus_add_intr(child, dinfo); 233 iobus_add_reg(child, dinfo, sc->sc_addr); 234 device_set_ivars(cdev, dinfo); 235 } else { 236 free(name, M_OFWPROP); 237 } 238 } 239 240 return (bus_generic_attach(dev)); 241 } 242 243 244 static int 245 iobus_print_child(device_t dev, device_t child) 246 { 247 struct iobus_devinfo *dinfo; 248 struct resource_list *rl; 249 int retval = 0; 250 251 dinfo = device_get_ivars(child); 252 rl = &dinfo->id_resources; 253 254 retval += bus_print_child_header(dev, child); 255 256 retval += printf(" offset 0x%x", dinfo->id_reg[1]); 257 retval += resource_list_print_type(rl, "irq", SYS_RES_IRQ, "%ld"); 258 259 retval += bus_print_child_footer(dev, child); 260 261 return (retval); 262 } 263 264 265 static void 266 iobus_probe_nomatch(device_t dev, device_t child) 267 { 268 } 269 270 271 static int 272 iobus_read_ivar(device_t dev, device_t child, int which, uintptr_t *result) 273 { 274 struct iobus_devinfo *dinfo; 275 276 if ((dinfo = device_get_ivars(child)) == 0) 277 return (ENOENT); 278 279 switch (which) { 280 case IOBUS_IVAR_NODE: 281 *result = dinfo->id_node; 282 break; 283 case IOBUS_IVAR_NAME: 284 *result = (uintptr_t)dinfo->id_name; 285 break; 286 case IOBUS_IVAR_NREGS: 287 *result = dinfo->id_nregs; 288 break; 289 case IOBUS_IVAR_REGS: 290 *result = (uintptr_t)dinfo->id_reg; 291 break; 292 default: 293 return (ENOENT); 294 } 295 296 return (0); 297 } 298 299 300 static int 301 iobus_write_ivar(device_t dev, device_t child, int which, uintptr_t value) 302 { 303 return (EINVAL); 304 } 305 306 307 static struct resource * 308 iobus_alloc_resource(device_t bus, device_t child, int type, int *rid, 309 rman_res_t start, rman_res_t end, rman_res_t count, 310 u_int flags) 311 { 312 struct iobus_softc *sc; 313 int needactivate; 314 struct resource *rv; 315 struct rman *rm; 316 317 sc = device_get_softc(bus); 318 319 needactivate = flags & RF_ACTIVE; 320 flags &= ~RF_ACTIVE; 321 322 switch (type) { 323 case SYS_RES_MEMORY: 324 case SYS_RES_IOPORT: 325 rm = &sc->sc_mem_rman; 326 break; 327 case SYS_RES_IRQ: 328 return (bus_alloc_resource(bus, type, rid, start, end, count, 329 flags)); 330 default: 331 device_printf(bus, "unknown resource request from %s\n", 332 device_get_nameunit(child)); 333 return (NULL); 334 } 335 336 rv = rman_reserve_resource(rm, start, end, count, flags, child); 337 if (rv == NULL) { 338 device_printf(bus, "failed to reserve resource for %s\n", 339 device_get_nameunit(child)); 340 return (NULL); 341 } 342 343 rman_set_rid(rv, *rid); 344 345 if (needactivate) { 346 if (bus_activate_resource(child, type, *rid, rv) != 0) { 347 device_printf(bus, 348 "failed to activate resource for %s\n", 349 device_get_nameunit(child)); 350 rman_release_resource(rv); 351 return (NULL); 352 } 353 } 354 355 return (rv); 356 } 357 358 359 static int 360 iobus_release_resource(device_t bus, device_t child, int type, int rid, 361 struct resource *res) 362 { 363 if (rman_get_flags(res) & RF_ACTIVE) { 364 int error = bus_deactivate_resource(child, type, rid, res); 365 if (error) 366 return error; 367 } 368 369 return (rman_release_resource(res)); 370 } 371 372 373 static int 374 iobus_activate_resource(device_t bus, device_t child, int type, int rid, 375 struct resource *res) 376 { 377 struct iobus_softc *sc; 378 void *p; 379 380 sc = device_get_softc(bus); 381 382 if (type == SYS_RES_IRQ) 383 return (bus_activate_resource(bus, type, rid, res)); 384 385 if ((type == SYS_RES_MEMORY) || (type == SYS_RES_IOPORT)) { 386 p = pmap_mapdev((vm_offset_t)rman_get_start(res) + sc->sc_addr, 387 (vm_size_t)rman_get_size(res)); 388 if (p == NULL) 389 return (ENOMEM); 390 rman_set_virtual(res, p); 391 rman_set_bustag(res, &bs_le_tag); 392 rman_set_bushandle(res, (u_long)p); 393 } 394 395 return (rman_activate_resource(res)); 396 } 397 398 399 static int 400 iobus_deactivate_resource(device_t bus, device_t child, int type, int rid, 401 struct resource *res) 402 { 403 /* 404 * If this is a memory resource, unmap it. 405 */ 406 if ((type == SYS_RES_MEMORY) || (type == SYS_RES_IOPORT)) { 407 u_int32_t psize; 408 409 psize = rman_get_size(res); 410 pmap_unmapdev((vm_offset_t)rman_get_virtual(res), psize); 411 } 412 413 return (rman_deactivate_resource(res)); 414 } 415