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