1 /* $NetBSD: gpioirq.c,v 1.1 2018/05/19 14:15:39 thorpej Exp $ */ 2 3 /* 4 * Copyright (c) 2016 Brad Spencer <brad@anduin.eldar.org> 5 * 6 * Permission to use, copy, modify, and distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 */ 18 19 #include <sys/cdefs.h> 20 __KERNEL_RCSID(0, "$NetBSD: gpioirq.c,v 1.1 2018/05/19 14:15:39 thorpej Exp $"); 21 22 /* 23 * Example GPIO driver that uses interrupts. 24 */ 25 26 #include <sys/param.h> 27 #include <sys/systm.h> 28 #include <sys/device.h> 29 #include <sys/gpio.h> 30 #include <sys/module.h> 31 32 #include <dev/gpio/gpiovar.h> 33 34 #define GPIOIRQ_NPINS 1 35 36 struct gpioirq_softc { 37 device_t sc_dev; 38 void * sc_gpio; 39 struct gpio_pinmap sc_map; 40 int _map[GPIOIRQ_NPINS]; 41 char sc_intrstr[128]; 42 void * sc_ih; 43 kmutex_t sc_lock; 44 bool sc_verbose; 45 bool sc_functional; 46 }; 47 48 #define GPIOIRQ_FLAGS_IRQMODE GPIO_INTR_MODE_MASK 49 #define GPIOIRQ_FLAGS_VERBOSE 0x1000 50 51 static int gpioirq_match(device_t, cfdata_t, void *); 52 static void gpioirq_attach(device_t, device_t, void *); 53 static int gpioirq_detach(device_t, int); 54 static int gpioirq_activate(device_t, enum devact); 55 56 static int gpioirq_intr(void *); 57 58 CFATTACH_DECL_NEW(gpioirq, sizeof(struct gpioirq_softc), 59 gpioirq_match, gpioirq_attach, 60 gpioirq_detach, gpioirq_activate); 61 62 extern struct cfdriver gpioirq_cd; 63 64 static int 65 gpioirq_match(device_t parent, cfdata_t cf, void *aux) 66 { 67 struct gpio_attach_args *ga = aux; 68 int npins; 69 70 if (strcmp(ga->ga_dvname, cf->cf_name)) 71 return (0); 72 73 if (ga->ga_offset == -1) 74 return (0); 75 76 npins = gpio_npins(ga->ga_mask); 77 if (npins > 1) 78 return (0); 79 80 return (1); 81 } 82 83 static void 84 gpioirq_attach(device_t parent, device_t self, void *aux) 85 { 86 struct gpioirq_softc *sc = device_private(self); 87 struct gpio_attach_args *ga = aux; 88 int npins = gpio_npins(ga->ga_mask); 89 int irqmode, flags; 90 91 sc->sc_dev = self; 92 93 /* Map pins */ 94 sc->sc_gpio = ga->ga_gpio; 95 sc->sc_map.pm_map = sc->_map; 96 97 /* We always map just 1 pin. */ 98 if (gpio_pin_map(sc->sc_gpio, ga->ga_offset, 99 npins ? ga->ga_mask : 0x1, &sc->sc_map)) { 100 aprint_error(": can't map pins\n"); 101 return; 102 } 103 104 aprint_normal("\n"); 105 106 if (ga->ga_flags & GPIOIRQ_FLAGS_VERBOSE) 107 sc->sc_verbose = true; 108 109 irqmode = ga->ga_flags & GPIOIRQ_FLAGS_IRQMODE; 110 111 mutex_init(&sc->sc_lock, MUTEX_DEFAULT, IPL_VM); 112 113 if (!gpio_intr_str(sc->sc_gpio, &sc->sc_map, 0, irqmode, 114 sc->sc_intrstr, sizeof(sc->sc_intrstr))) { 115 aprint_error_dev(self, "failed to decode interrupt\n"); 116 return; 117 } 118 119 if (!gpio_pin_irqmode_issupported(sc->sc_gpio, &sc->sc_map, 0, 120 irqmode)) { 121 aprint_error_dev(self, 122 "irqmode not supported: %s\n", sc->sc_intrstr); 123 gpio_pin_unmap(sc->sc_gpio, &sc->sc_map); 124 return; 125 } 126 127 flags = gpio_pin_get_conf(sc->sc_gpio, &sc->sc_map, 0); 128 flags = (flags & ~(GPIO_PIN_OUTPUT|GPIO_PIN_INOUT)) | 129 GPIO_PIN_INPUT; 130 if (!gpio_pin_set_conf(sc->sc_gpio, &sc->sc_map, 0, flags)) { 131 aprint_error_dev(sc->sc_dev, "pin not capable of input\n"); 132 gpio_pin_unmap(sc->sc_gpio, &sc->sc_map); 133 return; 134 } 135 136 sc->sc_ih = gpio_intr_establish(sc->sc_gpio, &sc->sc_map, 0, IPL_VM, 137 irqmode | GPIO_INTR_MPSAFE, 138 gpioirq_intr, sc); 139 if (sc->sc_ih == NULL) { 140 aprint_error_dev(self, 141 "unable to establish interrupt on %s\n", sc->sc_intrstr); 142 gpio_pin_unmap(sc->sc_gpio, &sc->sc_map); 143 return; 144 } 145 aprint_normal_dev(self, "interrupting on %s\n", sc->sc_intrstr); 146 147 sc->sc_functional = true; 148 } 149 150 int 151 gpioirq_intr(void *arg) 152 { 153 struct gpioirq_softc *sc = arg; 154 int val; 155 156 mutex_enter(&sc->sc_lock); 157 158 val = gpio_pin_read(sc->sc_gpio, &sc->sc_map, 0); 159 160 if (sc->sc_verbose) 161 printf("%s: interrupt on %s --> %d\n", 162 device_xname(sc->sc_dev), sc->sc_intrstr, val); 163 164 mutex_exit(&sc->sc_lock); 165 166 return (1); 167 } 168 169 int 170 gpioirq_detach(device_t self, int flags) 171 { 172 struct gpioirq_softc *sc = device_private(self); 173 174 /* Clear the handler and disable the interrupt. */ 175 gpio_intr_disestablish(sc->sc_gpio, sc->sc_ih); 176 177 /* Release the pin. */ 178 gpio_pin_unmap(sc->sc_gpio, &sc->sc_map); 179 180 return (0); 181 } 182 183 int 184 gpioirq_activate(device_t self, enum devact act) 185 { 186 187 switch (act) { 188 case DVACT_DEACTIVATE: 189 /* We don't really need to do anything. */ 190 return (0); 191 default: 192 return (EOPNOTSUPP); 193 } 194 } 195 196 MODULE(MODULE_CLASS_DRIVER, gpioirq, "gpio"); 197 198 #ifdef _MODULE 199 #include "ioconf.c" 200 #endif 201 202 static int 203 gpioirq_modcmd(modcmd_t cmd, void *opaque) 204 { 205 int error = 0; 206 207 switch (cmd) { 208 case MODULE_CMD_INIT: 209 #ifdef _MODULE 210 error = config_init_component(cfdriver_ioconf_gpioirq, 211 cfattach_ioconf_gpioirq, cfdata_ioconf_gpioirq); 212 if (error) 213 aprint_error("%s: unable to init component\n", 214 gpioirq_cd.cd_name); 215 #endif 216 break; 217 case MODULE_CMD_FINI: 218 #ifdef _MODULE 219 config_fini_component(cfdriver_ioconf_gpioirq, 220 cfattach_ioconf_gpioirq, cfdata_ioconf_gpioirq); 221 #endif 222 break; 223 default: 224 error = ENOTTY; 225 } 226 227 return (error); 228 } 229