1139825Simp/*- 2103620Sgrehan * Copyright 2002 by Peter Grehan. All rights reserved. 3103620Sgrehan * 4103620Sgrehan * Redistribution and use in source and binary forms, with or without 5103620Sgrehan * modification, are permitted provided that the following conditions 6103620Sgrehan * are met: 7103620Sgrehan * 1. Redistributions of source code must retain the above copyright 8103620Sgrehan * notice, this list of conditions and the following disclaimer. 9103620Sgrehan * 2. Redistributions in binary form must reproduce the above copyright 10103620Sgrehan * notice, this list of conditions and the following disclaimer in the 11103620Sgrehan * documentation and/or other materials provided with the distribution. 12103620Sgrehan * 3. The name of the author may not be used to endorse or promote products 13103620Sgrehan * derived from this software without specific prior written permission. 14103620Sgrehan * 15103620Sgrehan * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 16103620Sgrehan * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 17103620Sgrehan * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18103620Sgrehan * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 19103620Sgrehan * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 20103620Sgrehan * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 21103620Sgrehan * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 22103620Sgrehan * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 23103620Sgrehan * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24103620Sgrehan * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25103620Sgrehan * SUCH DAMAGE. 26103620Sgrehan * 27103620Sgrehan * $FreeBSD$ 28103620Sgrehan */ 29103620Sgrehan 30103620Sgrehan/* 31103620Sgrehan * PSIM 'iobus' local bus. Should be set up in the device tree like: 32103620Sgrehan * 33103620Sgrehan * /iobus@0x80000000/name psim-iobus 34103620Sgrehan * 35103620Sgrehan * Code borrowed from various nexus.c and uninorth.c :-) 36103620Sgrehan */ 37103620Sgrehan 38103620Sgrehan#include <sys/param.h> 39103620Sgrehan#include <sys/systm.h> 40103620Sgrehan#include <sys/kernel.h> 41103620Sgrehan#include <sys/malloc.h> 42131102Sgrehan#include <sys/module.h> 43103620Sgrehan#include <sys/bus.h> 44103620Sgrehan#include <machine/bus.h> 45103620Sgrehan#include <sys/rman.h> 46103620Sgrehan 47183882Snwhitehorn#include <dev/ofw/ofw_bus.h> 48103620Sgrehan#include <dev/ofw/openfirm.h> 49103620Sgrehan 50103620Sgrehan#include <machine/vmparam.h> 51103620Sgrehan#include <vm/vm.h> 52103620Sgrehan#include <vm/pmap.h> 53103620Sgrehan#include <machine/pmap.h> 54103620Sgrehan 55103620Sgrehan#include <machine/resource.h> 56103620Sgrehan 57103620Sgrehan#include <powerpc/psim/iobusvar.h> 58103620Sgrehan 59131400Sgrehanstruct iobus_softc { 60131400Sgrehan phandle_t sc_node; 61131400Sgrehan vm_offset_t sc_addr; 62131400Sgrehan vm_offset_t sc_size; 63131400Sgrehan struct rman sc_mem_rman; 64131400Sgrehan}; 65131400Sgrehan 66103620Sgrehanstatic MALLOC_DEFINE(M_IOBUS, "iobus", "iobus device information"); 67103620Sgrehan 68103620Sgrehanstatic int iobus_probe(device_t); 69103620Sgrehanstatic int iobus_attach(device_t); 70103620Sgrehanstatic int iobus_print_child(device_t dev, device_t child); 71103620Sgrehanstatic void iobus_probe_nomatch(device_t, device_t); 72103620Sgrehanstatic int iobus_read_ivar(device_t, device_t, int, uintptr_t *); 73103620Sgrehanstatic int iobus_write_ivar(device_t, device_t, int, uintptr_t); 74103620Sgrehanstatic struct resource *iobus_alloc_resource(device_t, device_t, int, int *, 75103620Sgrehan u_long, u_long, u_long, u_int); 76103620Sgrehanstatic int iobus_activate_resource(device_t, device_t, int, int, 77103620Sgrehan struct resource *); 78103620Sgrehanstatic int iobus_deactivate_resource(device_t, device_t, int, int, 79103620Sgrehan struct resource *); 80103620Sgrehanstatic int iobus_release_resource(device_t, device_t, int, int, 81103620Sgrehan struct resource *); 82103620Sgrehan 83103620Sgrehan/* 84103620Sgrehan * Bus interface definition 85103620Sgrehan */ 86103620Sgrehanstatic device_method_t iobus_methods[] = { 87103620Sgrehan /* Device interface */ 88103620Sgrehan DEVMETHOD(device_probe, iobus_probe), 89103620Sgrehan DEVMETHOD(device_attach, iobus_attach), 90103620Sgrehan DEVMETHOD(device_detach, bus_generic_detach), 91103620Sgrehan DEVMETHOD(device_shutdown, bus_generic_shutdown), 92103620Sgrehan DEVMETHOD(device_suspend, bus_generic_suspend), 93103620Sgrehan DEVMETHOD(device_resume, bus_generic_resume), 94103620Sgrehan 95103620Sgrehan /* Bus interface */ 96103620Sgrehan DEVMETHOD(bus_print_child, iobus_print_child), 97103620Sgrehan DEVMETHOD(bus_probe_nomatch, iobus_probe_nomatch), 98103620Sgrehan DEVMETHOD(bus_read_ivar, iobus_read_ivar), 99103620Sgrehan DEVMETHOD(bus_write_ivar, iobus_write_ivar), 100103620Sgrehan DEVMETHOD(bus_setup_intr, bus_generic_setup_intr), 101103620Sgrehan DEVMETHOD(bus_teardown_intr, bus_generic_teardown_intr), 102103620Sgrehan 103103620Sgrehan DEVMETHOD(bus_alloc_resource, iobus_alloc_resource), 104103620Sgrehan DEVMETHOD(bus_release_resource, iobus_release_resource), 105103620Sgrehan DEVMETHOD(bus_activate_resource, iobus_activate_resource), 106103620Sgrehan DEVMETHOD(bus_deactivate_resource, iobus_deactivate_resource), 107103620Sgrehan 108103620Sgrehan { 0, 0 } 109103620Sgrehan}; 110103620Sgrehan 111103620Sgrehanstatic driver_t iobus_driver = { 112103620Sgrehan "iobus", 113103620Sgrehan iobus_methods, 114103620Sgrehan sizeof(struct iobus_softc) 115103620Sgrehan}; 116103620Sgrehan 117103620Sgrehandevclass_t iobus_devclass; 118103620Sgrehan 119103620SgrehanDRIVER_MODULE(iobus, nexus, iobus_driver, iobus_devclass, 0, 0); 120103620Sgrehan 121103620Sgrehanstatic int 122103620Sgrehaniobus_probe(device_t dev) 123103620Sgrehan{ 124183882Snwhitehorn const char *type = ofw_bus_get_name(dev); 125103620Sgrehan 126103620Sgrehan if (strcmp(type, "psim-iobus") != 0) 127103620Sgrehan return (ENXIO); 128103620Sgrehan 129103620Sgrehan device_set_desc(dev, "PSIM local bus"); 130103620Sgrehan return (0); 131103620Sgrehan} 132103620Sgrehan 133103620Sgrehan/* 134103620Sgrehan * Add interrupt/addr range to the dev's resource list if present 135103620Sgrehan */ 136103620Sgrehanstatic void 137103620Sgrehaniobus_add_intr(phandle_t devnode, struct iobus_devinfo *dinfo) 138103620Sgrehan{ 139103620Sgrehan u_int intr = -1; 140103620Sgrehan 141103620Sgrehan if (OF_getprop(devnode, "interrupt", &intr, sizeof(intr)) != -1) { 142103620Sgrehan resource_list_add(&dinfo->id_resources, 143103620Sgrehan SYS_RES_IRQ, 0, intr, intr, 1); 144103620Sgrehan } 145103620Sgrehan dinfo->id_interrupt = intr; 146103620Sgrehan} 147103620Sgrehan 148103620Sgrehan 149103620Sgrehanstatic void 150103620Sgrehaniobus_add_reg(phandle_t devnode, struct iobus_devinfo *dinfo, 151103620Sgrehan vm_offset_t iobus_off) 152103620Sgrehan{ 153103620Sgrehan u_int size; 154103620Sgrehan int i; 155103620Sgrehan 156103620Sgrehan size = OF_getprop(devnode, "reg", dinfo->id_reg,sizeof(dinfo->id_reg)); 157103620Sgrehan 158103620Sgrehan if (size != -1) { 159103620Sgrehan dinfo->id_nregs = size / (sizeof(dinfo->id_reg[0])); 160103620Sgrehan 161103620Sgrehan for (i = 0; i < dinfo->id_nregs; i+= 3) { 162103620Sgrehan /* 163103620Sgrehan * Scale the absolute addresses back to iobus 164103620Sgrehan * relative offsets. This is to better simulate 165103620Sgrehan * macio 166103620Sgrehan */ 167103620Sgrehan dinfo->id_reg[i+1] -= iobus_off; 168103620Sgrehan 169103620Sgrehan resource_list_add(&dinfo->id_resources, 170103620Sgrehan SYS_RES_MEMORY, 0, 171103620Sgrehan dinfo->id_reg[i+1], 172103620Sgrehan dinfo->id_reg[i+1] + 173103620Sgrehan dinfo->id_reg[i+2], 174103620Sgrehan dinfo->id_reg[i+2]); 175103620Sgrehan } 176103620Sgrehan } 177103620Sgrehan} 178103620Sgrehan 179103620Sgrehan 180103620Sgrehanstatic int 181103620Sgrehaniobus_attach(device_t dev) 182103620Sgrehan{ 183103620Sgrehan struct iobus_softc *sc; 184103620Sgrehan struct iobus_devinfo *dinfo; 185103620Sgrehan phandle_t root; 186103620Sgrehan phandle_t child; 187103620Sgrehan device_t cdev; 188103620Sgrehan char *name; 189103620Sgrehan u_int reg[2]; 190103620Sgrehan int size; 191103620Sgrehan 192103620Sgrehan sc = device_get_softc(dev); 193183882Snwhitehorn sc->sc_node = ofw_bus_get_node(dev); 194103620Sgrehan 195103620Sgrehan /* 196103620Sgrehan * Find the base addr/size of the iobus, and initialize the 197103620Sgrehan * resource manager 198103620Sgrehan */ 199103620Sgrehan size = OF_getprop(sc->sc_node, "reg", reg, sizeof(reg)); 200103620Sgrehan if (size == sizeof(reg)) { 201103620Sgrehan sc->sc_addr = reg[0]; 202103620Sgrehan sc->sc_size = reg[1]; 203103620Sgrehan } else { 204103620Sgrehan return (ENXIO); 205103620Sgrehan } 206103620Sgrehan 207103620Sgrehan sc->sc_mem_rman.rm_type = RMAN_ARRAY; 208103620Sgrehan sc->sc_mem_rman.rm_descr = "IOBus Device Memory"; 209103620Sgrehan if (rman_init(&sc->sc_mem_rman) != 0) { 210103620Sgrehan device_printf(dev, 211103620Sgrehan "failed to init mem range resources\n"); 212103620Sgrehan return (ENXIO); 213103620Sgrehan } 214103620Sgrehan rman_manage_region(&sc->sc_mem_rman, 0, sc->sc_size); 215103620Sgrehan 216103620Sgrehan /* 217103620Sgrehan * Iterate through the sub-devices 218103620Sgrehan */ 219103620Sgrehan root = sc->sc_node; 220103620Sgrehan 221103620Sgrehan for (child = OF_child(root); child != 0; child = OF_peer(child)) { 222103620Sgrehan OF_getprop_alloc(child, "name", 1, (void **)&name); 223103620Sgrehan 224103620Sgrehan cdev = device_add_child(dev, NULL, -1); 225103620Sgrehan if (cdev != NULL) { 226111119Simp dinfo = malloc(sizeof(*dinfo), M_IOBUS, M_WAITOK); 227103620Sgrehan memset(dinfo, 0, sizeof(*dinfo)); 228103620Sgrehan resource_list_init(&dinfo->id_resources); 229103620Sgrehan dinfo->id_node = child; 230103620Sgrehan dinfo->id_name = name; 231103620Sgrehan iobus_add_intr(child, dinfo); 232103620Sgrehan iobus_add_reg(child, dinfo, sc->sc_addr); 233103620Sgrehan device_set_ivars(cdev, dinfo); 234103620Sgrehan } else { 235103620Sgrehan free(name, M_OFWPROP); 236103620Sgrehan } 237103620Sgrehan } 238103620Sgrehan 239103620Sgrehan return (bus_generic_attach(dev)); 240103620Sgrehan} 241103620Sgrehan 242103620Sgrehan 243103620Sgrehanstatic int 244103620Sgrehaniobus_print_child(device_t dev, device_t child) 245103620Sgrehan{ 246103620Sgrehan struct iobus_devinfo *dinfo; 247103620Sgrehan struct resource_list *rl; 248103620Sgrehan int retval = 0; 249103620Sgrehan 250103620Sgrehan dinfo = device_get_ivars(child); 251103620Sgrehan rl = &dinfo->id_resources; 252103620Sgrehan 253103620Sgrehan retval += bus_print_child_header(dev, child); 254103620Sgrehan 255103620Sgrehan retval += printf(" offset 0x%x", dinfo->id_reg[1]); 256103620Sgrehan retval += resource_list_print_type(rl, "irq", SYS_RES_IRQ, "%ld"); 257103620Sgrehan 258103620Sgrehan retval += bus_print_child_footer(dev, child); 259103620Sgrehan 260103620Sgrehan return (retval); 261103620Sgrehan} 262103620Sgrehan 263103620Sgrehan 264103620Sgrehanstatic void 265103620Sgrehaniobus_probe_nomatch(device_t dev, device_t child) 266103620Sgrehan{ 267103620Sgrehan} 268103620Sgrehan 269103620Sgrehan 270103620Sgrehanstatic int 271103620Sgrehaniobus_read_ivar(device_t dev, device_t child, int which, uintptr_t *result) 272103620Sgrehan{ 273103620Sgrehan struct iobus_devinfo *dinfo; 274103620Sgrehan 275103620Sgrehan if ((dinfo = device_get_ivars(child)) == 0) 276103620Sgrehan return (ENOENT); 277103620Sgrehan 278103620Sgrehan switch (which) { 279103620Sgrehan case IOBUS_IVAR_NODE: 280103620Sgrehan *result = dinfo->id_node; 281103620Sgrehan break; 282103620Sgrehan case IOBUS_IVAR_NAME: 283103620Sgrehan *result = (uintptr_t)dinfo->id_name; 284103620Sgrehan break; 285103620Sgrehan case IOBUS_IVAR_NREGS: 286103620Sgrehan *result = dinfo->id_nregs; 287103620Sgrehan break; 288103620Sgrehan case IOBUS_IVAR_REGS: 289103620Sgrehan *result = (uintptr_t)dinfo->id_reg; 290103620Sgrehan break; 291103620Sgrehan default: 292103620Sgrehan return (ENOENT); 293103620Sgrehan } 294103620Sgrehan 295103620Sgrehan return (0); 296103620Sgrehan} 297103620Sgrehan 298103620Sgrehan 299103620Sgrehanstatic int 300103620Sgrehaniobus_write_ivar(device_t dev, device_t child, int which, uintptr_t value) 301103620Sgrehan{ 302103620Sgrehan return (EINVAL); 303103620Sgrehan} 304103620Sgrehan 305103620Sgrehan 306103620Sgrehanstatic struct resource * 307103620Sgrehaniobus_alloc_resource(device_t bus, device_t child, int type, int *rid, 308103620Sgrehan u_long start, u_long end, u_long count, u_int flags) 309103620Sgrehan{ 310103620Sgrehan struct iobus_softc *sc; 311103620Sgrehan int needactivate; 312103620Sgrehan struct resource *rv; 313103620Sgrehan struct rman *rm; 314103620Sgrehan 315103620Sgrehan sc = device_get_softc(bus); 316103620Sgrehan 317103620Sgrehan needactivate = flags & RF_ACTIVE; 318103620Sgrehan flags &= ~RF_ACTIVE; 319103620Sgrehan 320103620Sgrehan switch (type) { 321103620Sgrehan case SYS_RES_MEMORY: 322103620Sgrehan case SYS_RES_IOPORT: 323103620Sgrehan rm = &sc->sc_mem_rman; 324103620Sgrehan break; 325103620Sgrehan case SYS_RES_IRQ: 326103620Sgrehan return (bus_alloc_resource(bus, type, rid, start, end, count, 327174782Smarcel flags)); 328103620Sgrehan default: 329103620Sgrehan device_printf(bus, "unknown resource request from %s\n", 330174782Smarcel device_get_nameunit(child)); 331103620Sgrehan return (NULL); 332103620Sgrehan } 333103620Sgrehan 334103620Sgrehan rv = rman_reserve_resource(rm, start, end, count, flags, child); 335103620Sgrehan if (rv == NULL) { 336103620Sgrehan device_printf(bus, "failed to reserve resource for %s\n", 337103620Sgrehan device_get_nameunit(child)); 338103620Sgrehan return (NULL); 339103620Sgrehan } 340103620Sgrehan 341157895Simp rman_set_rid(rv, *rid); 342103620Sgrehan 343103620Sgrehan if (needactivate) { 344103620Sgrehan if (bus_activate_resource(child, type, *rid, rv) != 0) { 345103620Sgrehan device_printf(bus, 346103620Sgrehan "failed to activate resource for %s\n", 347103620Sgrehan device_get_nameunit(child)); 348103620Sgrehan rman_release_resource(rv); 349103620Sgrehan return (NULL); 350103620Sgrehan } 351103620Sgrehan } 352103620Sgrehan 353103620Sgrehan return (rv); 354103620Sgrehan} 355103620Sgrehan 356103620Sgrehan 357103620Sgrehanstatic int 358103620Sgrehaniobus_release_resource(device_t bus, device_t child, int type, int rid, 359103620Sgrehan struct resource *res) 360103620Sgrehan{ 361103620Sgrehan if (rman_get_flags(res) & RF_ACTIVE) { 362103620Sgrehan int error = bus_deactivate_resource(child, type, rid, res); 363103620Sgrehan if (error) 364103620Sgrehan return error; 365103620Sgrehan } 366103620Sgrehan 367103620Sgrehan return (rman_release_resource(res)); 368103620Sgrehan} 369103620Sgrehan 370103620Sgrehan 371103620Sgrehanstatic int 372103620Sgrehaniobus_activate_resource(device_t bus, device_t child, int type, int rid, 373103620Sgrehan struct resource *res) 374103620Sgrehan{ 375103620Sgrehan struct iobus_softc *sc; 376103620Sgrehan void *p; 377103620Sgrehan 378103620Sgrehan sc = device_get_softc(bus); 379103620Sgrehan 380103620Sgrehan if (type == SYS_RES_IRQ) 381103620Sgrehan return (bus_activate_resource(bus, type, rid, res)); 382103620Sgrehan 383103620Sgrehan if ((type == SYS_RES_MEMORY) || (type == SYS_RES_IOPORT)) { 384103620Sgrehan p = pmap_mapdev((vm_offset_t)rman_get_start(res) + sc->sc_addr, 385103620Sgrehan (vm_size_t)rman_get_size(res)); 386103620Sgrehan if (p == NULL) 387103620Sgrehan return (ENOMEM); 388103620Sgrehan rman_set_virtual(res, p); 389174822Smarcel rman_set_bustag(res, &bs_le_tag); 390103620Sgrehan rman_set_bushandle(res, (u_long)p); 391103620Sgrehan } 392103620Sgrehan 393103620Sgrehan return (rman_activate_resource(res)); 394103620Sgrehan} 395103620Sgrehan 396103620Sgrehan 397103620Sgrehanstatic int 398103620Sgrehaniobus_deactivate_resource(device_t bus, device_t child, int type, int rid, 399103620Sgrehan struct resource *res) 400103620Sgrehan{ 401103620Sgrehan /* 402103620Sgrehan * If this is a memory resource, unmap it. 403103620Sgrehan */ 404103620Sgrehan if ((type == SYS_RES_MEMORY) || (type == SYS_RES_IOPORT)) { 405103620Sgrehan u_int32_t psize; 406103620Sgrehan 407103620Sgrehan psize = rman_get_size(res); 408103620Sgrehan pmap_unmapdev((vm_offset_t)rman_get_virtual(res), psize); 409103620Sgrehan } 410103620Sgrehan 411103620Sgrehan return (rman_deactivate_resource(res)); 412103620Sgrehan} 413