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 * Driver for MacIO GPIO controller 32 */ 33 34 #include <sys/param.h> 35 #include <sys/systm.h> 36 #include <sys/kernel.h> 37 #include <sys/malloc.h> 38 #include <sys/module.h> 39 #include <sys/bus.h> 40 #include <machine/bus.h> 41 #include <sys/rman.h> 42 43 #include <machine/vmparam.h> 44 #include <vm/vm.h> 45 #include <vm/pmap.h> 46 #include <machine/pmap.h> 47 48 #include <machine/resource.h> 49 50 #include <dev/ofw/ofw_bus.h> 51 #include <dev/ofw/ofw_bus_subr.h> 52 #include <dev/ofw/openfirm.h> 53 54 /* 55 * Macgpio softc 56 */ 57 struct macgpio_softc { 58 phandle_t sc_node; 59 struct resource *sc_gpios; 60 int sc_gpios_rid; 61 }; 62 63 static MALLOC_DEFINE(M_MACGPIO, "macgpio", "macgpio device information"); 64 65 static int macgpio_probe(device_t); 66 static int macgpio_attach(device_t); 67 static int macgpio_print_child(device_t dev, device_t child); 68 static void macgpio_probe_nomatch(device_t, device_t); 69 static struct resource *macgpio_alloc_resource(device_t, device_t, int, int *, 70 u_long, u_long, u_long, u_int); 71 static int macgpio_activate_resource(device_t, device_t, int, int, 72 struct resource *); 73 static int macgpio_deactivate_resource(device_t, device_t, int, int, 74 struct resource *); 75 static ofw_bus_get_devinfo_t macgpio_get_devinfo; 76 77 uint8_t macgpio_read(device_t dev); 78 void macgpio_write(device_t dev,uint8_t); 79 80 /* 81 * Bus interface definition 82 */ 83 static device_method_t macgpio_methods[] = { 84 /* Device interface */ 85 DEVMETHOD(device_probe, macgpio_probe), 86 DEVMETHOD(device_attach, macgpio_attach), 87 DEVMETHOD(device_detach, bus_generic_detach), 88 DEVMETHOD(device_shutdown, bus_generic_shutdown), 89 DEVMETHOD(device_suspend, bus_generic_suspend), 90 DEVMETHOD(device_resume, bus_generic_resume), 91 92 /* Bus interface */ 93 DEVMETHOD(bus_print_child, macgpio_print_child), 94 DEVMETHOD(bus_probe_nomatch, macgpio_probe_nomatch), 95 DEVMETHOD(bus_setup_intr, bus_generic_setup_intr), 96 DEVMETHOD(bus_teardown_intr, bus_generic_teardown_intr), 97 98 DEVMETHOD(bus_alloc_resource, macgpio_alloc_resource), 99 DEVMETHOD(bus_activate_resource, macgpio_activate_resource), 100 DEVMETHOD(bus_deactivate_resource, macgpio_deactivate_resource), 101 DEVMETHOD(bus_release_resource, bus_generic_release_resource), 102 103 /* ofw_bus interface */ 104 DEVMETHOD(ofw_bus_get_devinfo, macgpio_get_devinfo), 105 DEVMETHOD(ofw_bus_get_compat, ofw_bus_gen_get_compat), 106 DEVMETHOD(ofw_bus_get_model, ofw_bus_gen_get_model), 107 DEVMETHOD(ofw_bus_get_name, ofw_bus_gen_get_name), 108 DEVMETHOD(ofw_bus_get_node, ofw_bus_gen_get_node), 109 DEVMETHOD(ofw_bus_get_type, ofw_bus_gen_get_type), 110 111 { 0, 0 } 112 }; 113 114 static driver_t macgpio_pci_driver = { 115 "macgpio", 116 macgpio_methods, 117 sizeof(struct macgpio_softc) 118 }; 119 120 devclass_t macgpio_devclass; 121 122 DRIVER_MODULE(macgpio, macio, macgpio_pci_driver, macgpio_devclass, 0, 0); 123 124 struct macgpio_devinfo { 125 struct ofw_bus_devinfo mdi_obdinfo; 126 struct resource_list mdi_resources; 127 128 int gpio_num; 129 }; 130 131 static int 132 macgpio_probe(device_t dev) 133 { 134 const char *name; 135 136 name = ofw_bus_get_name(dev); 137 if (name && strcmp(name, "gpio") == 0) { 138 device_set_desc(dev, "MacIO GPIO Controller"); 139 return (0); 140 } 141 142 return (ENXIO); 143 } 144 145 /* 146 * Scan Open Firmware child nodes, and attach these as children 147 * of the macgpio bus 148 */ 149 static int 150 macgpio_attach(device_t dev) 151 { 152 struct macgpio_softc *sc; 153 struct macgpio_devinfo *dinfo; 154 phandle_t root; 155 phandle_t child; 156 device_t cdev; 157 uint32_t irq; 158 159 sc = device_get_softc(dev); 160 root = sc->sc_node = ofw_bus_get_node(dev); 161 162 sc->sc_gpios = bus_alloc_resource_any(dev, SYS_RES_MEMORY, 163 &sc->sc_gpios_rid, RF_ACTIVE); 164 165 /* 166 * Iterate through the sub-devices 167 */ 168 for (child = OF_child(root); child != 0; child = OF_peer(child)) { 169 dinfo = malloc(sizeof(*dinfo), M_MACGPIO, M_WAITOK | M_ZERO); 170 if (ofw_bus_gen_setup_devinfo(&dinfo->mdi_obdinfo, child) != 171 0) { 172 free(dinfo, M_MACGPIO); 173 continue; 174 } 175 176 if (OF_getprop(child,"reg",&dinfo->gpio_num, 177 sizeof(dinfo->gpio_num)) != sizeof(dinfo->gpio_num)) { 178 free(dinfo, M_MACGPIO); 179 continue; 180 } 181 182 resource_list_init(&dinfo->mdi_resources); 183 184 if (OF_getprop(child,"interrupts",&irq, sizeof(irq)) == 185 sizeof(irq)) { 186 resource_list_add(&dinfo->mdi_resources, SYS_RES_IRQ, 187 0, irq, irq, 1); 188 } 189 190 /* Fix messed-up offsets */ 191 if (dinfo->gpio_num > 0x50) 192 dinfo->gpio_num -= 0x50; 193 194 cdev = device_add_child(dev, NULL, -1); 195 if (cdev == NULL) { 196 device_printf(dev, "<%s>: device_add_child failed\n", 197 dinfo->mdi_obdinfo.obd_name); 198 ofw_bus_gen_destroy_devinfo(&dinfo->mdi_obdinfo); 199 free(dinfo, M_MACGPIO); 200 continue; 201 } 202 device_set_ivars(cdev, dinfo); 203 } 204 205 return (bus_generic_attach(dev)); 206 } 207 208 209 static int 210 macgpio_print_child(device_t dev, device_t child) 211 { 212 struct macgpio_devinfo *dinfo; 213 int retval = 0; 214 215 dinfo = device_get_ivars(child); 216 217 retval += bus_print_child_header(dev, child); 218 219 printf(" gpio %d",dinfo->gpio_num); 220 resource_list_print_type(&dinfo->mdi_resources, "irq", SYS_RES_IRQ, 221 "%ld"); 222 retval += bus_print_child_footer(dev, child); 223 224 return (retval); 225 } 226 227 228 static void 229 macgpio_probe_nomatch(device_t dev, device_t child) 230 { 231 struct macgpio_devinfo *dinfo; 232 const char *type; 233 234 if (bootverbose) { 235 dinfo = device_get_ivars(child); 236 237 if ((type = ofw_bus_get_type(child)) == NULL) 238 type = "(unknown)"; 239 device_printf(dev, "<%s, %s>", type, ofw_bus_get_name(child)); 240 printf(" gpio %d",dinfo->gpio_num); 241 resource_list_print_type(&dinfo->mdi_resources, "irq", 242 SYS_RES_IRQ, "%ld"); 243 printf(" (no driver attached)\n"); 244 } 245 } 246 247 248 static struct resource * 249 macgpio_alloc_resource(device_t bus, device_t child, int type, int *rid, 250 u_long start, u_long end, u_long count, u_int flags) 251 { 252 struct macgpio_softc *sc; 253 struct macgpio_devinfo *dinfo; 254 255 sc = device_get_softc(bus); 256 dinfo = device_get_ivars(child); 257 258 if (type != SYS_RES_IRQ) 259 return (NULL); 260 261 return (resource_list_alloc(&dinfo->mdi_resources, bus, child, type, 262 rid, start, end, count, flags)); 263 } 264 265 static int 266 macgpio_activate_resource(device_t bus, device_t child, int type, int rid, 267 struct resource *res) 268 { 269 struct macgpio_softc *sc; 270 struct macgpio_devinfo *dinfo; 271 u_char val; 272 273 sc = device_get_softc(bus); 274 dinfo = device_get_ivars(child); 275 276 if (type != SYS_RES_IRQ) 277 return ENXIO; 278 279 val = bus_read_1(sc->sc_gpios,dinfo->gpio_num); 280 val |= 0x80; 281 bus_write_1(sc->sc_gpios,dinfo->gpio_num,val); 282 283 return (bus_activate_resource(bus, type, rid, res)); 284 } 285 286 287 static int 288 macgpio_deactivate_resource(device_t bus, device_t child, int type, int rid, 289 struct resource *res) 290 { 291 struct macgpio_softc *sc; 292 struct macgpio_devinfo *dinfo; 293 u_char val; 294 295 sc = device_get_softc(bus); 296 dinfo = device_get_ivars(child); 297 298 if (type != SYS_RES_IRQ) 299 return ENXIO; 300 301 val = bus_read_1(sc->sc_gpios,dinfo->gpio_num); 302 val &= ~0x80; 303 bus_write_1(sc->sc_gpios,dinfo->gpio_num,val); 304 305 return (bus_deactivate_resource(bus, type, rid, res)); 306 } 307 308 uint8_t 309 macgpio_read(device_t dev) 310 { 311 struct macgpio_softc *sc; 312 struct macgpio_devinfo *dinfo; 313 314 sc = device_get_softc(device_get_parent(dev)); 315 dinfo = device_get_ivars(dev); 316 317 return (bus_read_1(sc->sc_gpios,dinfo->gpio_num)); 318 } 319 320 void 321 macgpio_write(device_t dev, uint8_t val) 322 { 323 struct macgpio_softc *sc; 324 struct macgpio_devinfo *dinfo; 325 326 sc = device_get_softc(device_get_parent(dev)); 327 dinfo = device_get_ivars(dev); 328 329 bus_write_1(sc->sc_gpios,dinfo->gpio_num,val); 330 } 331 332 static const struct ofw_bus_devinfo * 333 macgpio_get_devinfo(device_t dev, device_t child) 334 { 335 struct macgpio_devinfo *dinfo; 336 337 dinfo = device_get_ivars(child); 338 return (&dinfo->mdi_obdinfo); 339 } 340 341