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