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