gpioirq.c revision 1.1 1 /* $NetBSD: gpioirq.c,v 1.1 2018/05/19 14:15:39 thorpej Exp $ */
2
3 /*
4 * Copyright (c) 2016 Brad Spencer <brad (at) 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