1 /*- 2 * Copyright 2008 by Nathan Whitehorn. 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 * 13 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 14 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 15 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 16 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 17 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 18 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 19 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 20 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 21 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 22 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 23 * SUCH DAMAGE. 24 * 25 * $FreeBSD$ 26 */ 27 28 /* 29 * Driver for MacIO GPIO controller 30 */ 31 32 #include <sys/param.h> 33 #include <sys/systm.h> 34 #include <sys/kernel.h> 35 #include <sys/malloc.h> 36 #include <sys/module.h> 37 #include <sys/bus.h> 38 #include <machine/bus.h> 39 #include <sys/rman.h> 40 41 #include <machine/vmparam.h> 42 #include <vm/vm.h> 43 #include <vm/pmap.h> 44 #include <machine/pmap.h> 45 46 #include <machine/resource.h> 47 48 #include <dev/ofw/ofw_bus.h> 49 #include <dev/ofw/ofw_bus_subr.h> 50 #include <dev/ofw/openfirm.h> 51 52 #include <powerpc/powermac/macgpiovar.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 /* 78 * Bus interface definition 79 */ 80 static device_method_t macgpio_methods[] = { 81 /* Device interface */ 82 DEVMETHOD(device_probe, macgpio_probe), 83 DEVMETHOD(device_attach, macgpio_attach), 84 DEVMETHOD(device_detach, bus_generic_detach), 85 DEVMETHOD(device_shutdown, bus_generic_shutdown), 86 DEVMETHOD(device_suspend, bus_generic_suspend), 87 DEVMETHOD(device_resume, bus_generic_resume), 88 89 /* Bus interface */ 90 DEVMETHOD(bus_print_child, macgpio_print_child), 91 DEVMETHOD(bus_probe_nomatch, macgpio_probe_nomatch), 92 DEVMETHOD(bus_setup_intr, bus_generic_setup_intr), 93 DEVMETHOD(bus_teardown_intr, bus_generic_teardown_intr), 94 95 DEVMETHOD(bus_alloc_resource, macgpio_alloc_resource), 96 DEVMETHOD(bus_activate_resource, macgpio_activate_resource), 97 DEVMETHOD(bus_deactivate_resource, macgpio_deactivate_resource), 98 DEVMETHOD(bus_release_resource, bus_generic_release_resource), 99 100 /* ofw_bus interface */ 101 DEVMETHOD(ofw_bus_get_devinfo, macgpio_get_devinfo), 102 DEVMETHOD(ofw_bus_get_compat, ofw_bus_gen_get_compat), 103 DEVMETHOD(ofw_bus_get_model, ofw_bus_gen_get_model), 104 DEVMETHOD(ofw_bus_get_name, ofw_bus_gen_get_name), 105 DEVMETHOD(ofw_bus_get_node, ofw_bus_gen_get_node), 106 DEVMETHOD(ofw_bus_get_type, ofw_bus_gen_get_type), 107 108 { 0, 0 } 109 }; 110 111 static driver_t macgpio_pci_driver = { 112 "macgpio", 113 macgpio_methods, 114 sizeof(struct macgpio_softc) 115 }; 116 117 devclass_t macgpio_devclass; 118 119 DRIVER_MODULE(macgpio, macio, macgpio_pci_driver, macgpio_devclass, 0, 0); 120 121 struct macgpio_devinfo { 122 struct ofw_bus_devinfo mdi_obdinfo; 123 struct resource_list mdi_resources; 124 125 int gpio_num; 126 }; 127 128 static int 129 macgpio_probe(device_t dev) 130 { 131 const char *name; 132 133 name = ofw_bus_get_name(dev); 134 if (name && strcmp(name, "gpio") == 0) { 135 device_set_desc(dev, "MacIO GPIO Controller"); 136 return (0); 137 } 138 139 return (ENXIO); 140 } 141 142 /* 143 * Scan Open Firmware child nodes, and attach these as children 144 * of the macgpio bus 145 */ 146 static int 147 macgpio_attach(device_t dev) 148 { 149 struct macgpio_softc *sc; 150 struct macgpio_devinfo *dinfo; 151 phandle_t root; 152 phandle_t child; 153 device_t cdev; 154 uint32_t irq; 155 156 sc = device_get_softc(dev); 157 root = sc->sc_node = ofw_bus_get_node(dev); 158 159 sc->sc_gpios = bus_alloc_resource_any(dev, SYS_RES_MEMORY, 160 &sc->sc_gpios_rid, RF_ACTIVE); 161 162 /* 163 * Iterate through the sub-devices 164 */ 165 for (child = OF_child(root); child != 0; child = OF_peer(child)) { 166 dinfo = malloc(sizeof(*dinfo), M_MACGPIO, M_WAITOK | M_ZERO); 167 if (ofw_bus_gen_setup_devinfo(&dinfo->mdi_obdinfo, child) != 168 0) { 169 free(dinfo, M_MACGPIO); 170 continue; 171 } 172 173 if (OF_getprop(child,"reg",&dinfo->gpio_num, 174 sizeof(dinfo->gpio_num)) != sizeof(dinfo->gpio_num)) { 175 /* 176 * Some early GPIO controllers don't provide GPIO 177 * numbers for GPIOs designed only to provide 178 * interrupt resources. We should still allow these 179 * to attach, but with caution. 180 */ 181 182 dinfo->gpio_num = -1; 183 } 184 185 resource_list_init(&dinfo->mdi_resources); 186 187 if (OF_getprop(child,"interrupts",&irq, sizeof(irq)) == 188 sizeof(irq)) { 189 resource_list_add(&dinfo->mdi_resources, SYS_RES_IRQ, 190 0, irq, irq, 1); 191 } 192 193 /* Fix messed-up offsets */ 194 if (dinfo->gpio_num > 0x50) 195 dinfo->gpio_num -= 0x50; 196 197 cdev = device_add_child(dev, NULL, -1); 198 if (cdev == NULL) { 199 device_printf(dev, "<%s>: device_add_child failed\n", 200 dinfo->mdi_obdinfo.obd_name); 201 ofw_bus_gen_destroy_devinfo(&dinfo->mdi_obdinfo); 202 free(dinfo, M_MACGPIO); 203 continue; 204 } 205 device_set_ivars(cdev, dinfo); 206 } 207 208 return (bus_generic_attach(dev)); 209 } 210 211 212 static int 213 macgpio_print_child(device_t dev, device_t child) 214 { 215 struct macgpio_devinfo *dinfo; 216 int retval = 0; 217 218 dinfo = device_get_ivars(child); 219 220 retval += bus_print_child_header(dev, child); 221 222 if (dinfo->gpio_num >= GPIO_BASE) 223 printf(" gpio %d", dinfo->gpio_num - GPIO_BASE); 224 else if (dinfo->gpio_num >= GPIO_EXTINT_BASE) 225 printf(" extint-gpio %d", dinfo->gpio_num - GPIO_EXTINT_BASE); 226 else if (dinfo->gpio_num >= 0) 227 printf(" addr 0x%02x", dinfo->gpio_num); /* should not happen */ 228 229 resource_list_print_type(&dinfo->mdi_resources, "irq", SYS_RES_IRQ, 230 "%ld"); 231 retval += bus_print_child_footer(dev, child); 232 233 return (retval); 234 } 235 236 237 static void 238 macgpio_probe_nomatch(device_t dev, device_t child) 239 { 240 struct macgpio_devinfo *dinfo; 241 const char *type; 242 243 if (bootverbose) { 244 dinfo = device_get_ivars(child); 245 246 if ((type = ofw_bus_get_type(child)) == NULL) 247 type = "(unknown)"; 248 device_printf(dev, "<%s, %s>", type, ofw_bus_get_name(child)); 249 if (dinfo->gpio_num >= 0) 250 printf(" gpio %d",dinfo->gpio_num); 251 resource_list_print_type(&dinfo->mdi_resources, "irq", 252 SYS_RES_IRQ, "%ld"); 253 printf(" (no driver attached)\n"); 254 } 255 } 256 257 258 static struct resource * 259 macgpio_alloc_resource(device_t bus, device_t child, int type, int *rid, 260 u_long start, u_long end, u_long count, u_int flags) 261 { 262 struct macgpio_softc *sc; 263 struct macgpio_devinfo *dinfo; 264 265 sc = device_get_softc(bus); 266 dinfo = device_get_ivars(child); 267 268 if (type != SYS_RES_IRQ) 269 return (NULL); 270 271 return (resource_list_alloc(&dinfo->mdi_resources, bus, child, type, 272 rid, start, end, count, flags)); 273 } 274 275 static int 276 macgpio_activate_resource(device_t bus, device_t child, int type, int rid, 277 struct resource *res) 278 { 279 struct macgpio_softc *sc; 280 struct macgpio_devinfo *dinfo; 281 u_char val; 282 283 sc = device_get_softc(bus); 284 dinfo = device_get_ivars(child); 285 286 if (type != SYS_RES_IRQ) 287 return ENXIO; 288 289 if (dinfo->gpio_num >= 0) { 290 val = bus_read_1(sc->sc_gpios,dinfo->gpio_num); 291 val |= 0x80; 292 bus_write_1(sc->sc_gpios,dinfo->gpio_num,val); 293 } 294 295 return (bus_activate_resource(bus, type, rid, res)); 296 } 297 298 299 static int 300 macgpio_deactivate_resource(device_t bus, device_t child, int type, int rid, 301 struct resource *res) 302 { 303 struct macgpio_softc *sc; 304 struct macgpio_devinfo *dinfo; 305 u_char val; 306 307 sc = device_get_softc(bus); 308 dinfo = device_get_ivars(child); 309 310 if (type != SYS_RES_IRQ) 311 return ENXIO; 312 313 if (dinfo->gpio_num >= 0) { 314 val = bus_read_1(sc->sc_gpios,dinfo->gpio_num); 315 val &= ~0x80; 316 bus_write_1(sc->sc_gpios,dinfo->gpio_num,val); 317 } 318 319 return (bus_deactivate_resource(bus, type, rid, res)); 320 } 321 322 uint8_t 323 macgpio_read(device_t dev) 324 { 325 struct macgpio_softc *sc; 326 struct macgpio_devinfo *dinfo; 327 328 sc = device_get_softc(device_get_parent(dev)); 329 dinfo = device_get_ivars(dev); 330 331 if (dinfo->gpio_num < 0) 332 return (0); 333 334 return (bus_read_1(sc->sc_gpios,dinfo->gpio_num)); 335 } 336 337 void 338 macgpio_write(device_t dev, uint8_t val) 339 { 340 struct macgpio_softc *sc; 341 struct macgpio_devinfo *dinfo; 342 343 sc = device_get_softc(device_get_parent(dev)); 344 dinfo = device_get_ivars(dev); 345 346 if (dinfo->gpio_num < 0) 347 return; 348 349 bus_write_1(sc->sc_gpios,dinfo->gpio_num,val); 350 } 351 352 static const struct ofw_bus_devinfo * 353 macgpio_get_devinfo(device_t dev, device_t child) 354 { 355 struct macgpio_devinfo *dinfo; 356 357 dinfo = device_get_ivars(child); 358 return (&dinfo->mdi_obdinfo); 359 } 360 361