gpio_acpi: Factor out gpio_acpi_check_gpioint().
[dragonfly.git] / sys / bus / gpio / gpio_intel / gpio_intel.c
1 /*
2  * Copyright (c) 2016 The DragonFly Project.  All rights reserved.
3  *
4  * This code is derived from software contributed to The DragonFly Project
5  * by Imre Vadász <imre@vdsz.com>
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  *
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in
15  *    the documentation and/or other materials provided with the
16  *    distribution.
17  * 3. Neither the name of The DragonFly Project nor the names of its
18  *    contributors may be used to endorse or promote products derived
19  *    from this software without specific, prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
25  * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26  * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
27  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
29  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
30  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
31  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32  * SUCH DAMAGE.
33  */
34 /*
35  * Intel SoC gpio driver.
36  */
37
38 #include <sys/param.h>
39 #include <sys/systm.h>
40 #include <sys/kernel.h>
41 #include <sys/module.h>
42 #include <sys/errno.h>
43 #include <sys/lock.h>
44 #include <sys/mutex.h>
45 #include <sys/bus.h>
46
47 #include <sys/rman.h>
48
49 #include "opt_acpi.h"
50 #include "acpi.h"
51 #include <dev/acpica/acpivar.h>
52
53 #include <bus/pci/pcivar.h>
54
55 #include <bus/gpio/gpio_acpi/gpio_acpivar.h>
56
57 #include "gpio_intel_var.h"
58
59 #include "gpio_if.h"
60
61 static int      gpio_intel_probe(device_t dev);
62 static int      gpio_intel_attach(device_t dev);
63 static int      gpio_intel_detach(device_t dev);
64 static int      gpio_intel_alloc_intr(device_t dev, u_int pin, int trigger,
65                     int polarity, int termination, void **cookiep);
66 static void     gpio_intel_setup_intr(device_t dev, void *cookie, void *arg,
67                     driver_intr_t *handler);
68 static void     gpio_intel_teardown_intr(device_t dev, void *cookie);
69 static void     gpio_intel_free_intr(device_t dev, void *cookie);
70 static int      gpio_intel_read_pin(device_t dev, u_int pin);
71 static void     gpio_intel_write_pin(device_t dev, u_int pin, int value);
72
73 static void     gpio_intel_intr(void *arg);
74 static int      gpio_intel_pin_exists(struct gpio_intel_softc *sc,
75                     uint16_t pin);
76
77 static char *cherryview_ids[] = { "INT33FF", NULL };
78
79 static int
80 gpio_intel_probe(device_t dev)
81 {
82
83         if (acpi_disabled("gpio_intel") ||
84             ACPI_ID_PROBE(device_get_parent(dev), dev, cherryview_ids) == NULL)
85                 return (ENXIO);
86
87         device_set_desc(dev, "Intel Cherry Trail GPIO Controller");
88
89         return (BUS_PROBE_DEFAULT);
90 }
91
92 static int
93 gpio_intel_attach(device_t dev)
94 {
95         struct gpio_intel_softc *sc = device_get_softc(dev);
96         int error, i, rid;
97
98         lockinit(&sc->lk, "gpio_intel", 0, LK_CANRECURSE);
99
100         sc->dev = dev;
101
102         if (ACPI_ID_PROBE(device_get_parent(dev), dev, cherryview_ids)
103             != NULL) {
104                 error = gpio_cherryview_matchuid(sc);
105                 if (error) {
106                         error = ENXIO;
107                         goto err;
108                 }
109         } else {
110                 error = ENXIO;
111                 goto err;
112         }
113
114         for (i = 0; i < 16; i++) {
115                 sc->intrmaps[i].pin = -1;
116         }
117
118         rid = 0;
119         sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY,
120             &rid, RF_ACTIVE);
121         if (sc->mem_res == NULL) {
122                 device_printf(dev, "unable to map registers");
123                 error = ENXIO;
124                 goto err;
125         }
126         rid = 0;
127         sc->irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ,
128             &rid, RF_ACTIVE);
129         if (sc->irq_res == NULL) {
130                 device_printf(dev, "unable to map interrupt");
131                 error = ENXIO;
132                 goto err;
133         }
134
135         lockmgr(&sc->lk, LK_EXCLUSIVE);
136         /* Activate the interrupt */
137         error = bus_setup_intr(dev, sc->irq_res, INTR_MPSAFE,
138             gpio_intel_intr, sc, &sc->intrhand, NULL);
139         if (error)
140                 device_printf(dev, "Can't setup IRQ\n");
141
142         /* power up the controller */
143         pci_set_powerstate(dev, PCI_POWERSTATE_D0);
144
145         sc->fns->init(sc);
146         lockmgr(&sc->lk, LK_RELEASE);
147
148         sc->acpireg = gpio_acpi_register(dev);
149
150         return (0);
151
152 err:
153         if (sc->irq_res) {
154                 bus_release_resource(dev, SYS_RES_IRQ,
155                     rman_get_rid(sc->irq_res), sc->irq_res);
156                 sc->irq_res = NULL;
157         }
158         if (sc->mem_res) {
159                 bus_release_resource(dev, SYS_RES_MEMORY,
160                     rman_get_rid(sc->mem_res), sc->mem_res);
161                 sc->mem_res = NULL;
162         }
163         return (error);
164 }
165
166 static int
167 gpio_intel_detach(device_t dev)
168 {
169         struct gpio_intel_softc *sc = device_get_softc(dev);
170
171         gpio_acpi_unregister(dev, sc->acpireg);
172
173         bus_teardown_intr(dev, sc->irq_res, sc->intrhand);
174         if (sc->irq_res) {
175                 bus_release_resource(dev, SYS_RES_IRQ,
176                     rman_get_rid(sc->irq_res), sc->irq_res);
177                 sc->irq_res = NULL;
178         }
179         if (sc->mem_res) {
180                 bus_release_resource(dev, SYS_RES_MEMORY,
181                     rman_get_rid(sc->mem_res), sc->mem_res);
182                 sc->mem_res = NULL;
183         }
184         lockuninit(&sc->lk);
185
186         pci_set_powerstate(dev, PCI_POWERSTATE_D3);
187
188         return 0;
189 }
190
191 /*
192  * The trigger, polarity and termination parameters are only used for
193  * sanity checking. The gpios should already be configured correctly by
194  * the firmware.
195  */
196 static int
197 gpio_intel_alloc_intr(device_t dev, u_int pin, int trigger, int polarity,
198     int termination, void **cookiep)
199 {
200         struct gpio_intel_softc *sc = device_get_softc(dev);
201         struct pin_intr_map *map = NULL;
202         int i, ret;
203
204         if (cookiep == NULL)
205                 return (EINVAL);
206
207         lockmgr(&sc->lk, LK_EXCLUSIVE);
208
209         if (gpio_intel_pin_exists(sc, pin)) {
210                 /* Make sure this pin isn't mapped yet */
211                 for (i = 0; i < 16; i++) {
212                         if (sc->intrmaps[i].pin == pin)
213                         return (ENOMEM);
214                 }
215                 ret = sc->fns->map_intr(sc, pin, trigger, polarity,
216                     termination);
217                 if (ret == 0) {
218                         /* XXX map_intr should return the pin_intr_map */
219                         for (i = 0; i < NELEM(sc->intrmaps); i++) {
220                                 if (sc->intrmaps[i].pin == pin)
221                                         map = &sc->intrmaps[i];
222                         }
223                         if (map != NULL) {
224                                 *cookiep = map;
225                                 map->arg = NULL;
226                                 map->handler = NULL;
227                         }
228                 }
229         } else {
230                 device_printf(sc->dev, "%s: Invalid pin %d\n", __func__, pin);
231                 ret = ENOENT;
232         }
233
234         lockmgr(&sc->lk, LK_RELEASE);
235
236         return (ret);
237 }
238
239 static void
240 gpio_intel_free_intr(device_t dev, void *cookie)
241 {
242         struct gpio_intel_softc *sc = device_get_softc(dev);
243         struct pin_intr_map *map = (struct pin_intr_map *)cookie;
244
245         KKASSERT(gpio_intel_pin_exists(sc, map->pin));
246
247         lockmgr(&sc->lk, LK_EXCLUSIVE);
248         map->arg = NULL;
249         map->handler = NULL;
250         sc->fns->unmap_intr(sc, map);
251         lockmgr(&sc->lk, LK_RELEASE);
252 }
253
254 static void
255 gpio_intel_setup_intr(device_t dev, void *cookie, void *arg,
256     driver_intr_t *handler)
257 {
258         struct gpio_intel_softc *sc = device_get_softc(dev);
259         struct pin_intr_map *map = (struct pin_intr_map *)cookie;
260
261         KKASSERT(gpio_intel_pin_exists(sc, map->pin));
262
263         lockmgr(&sc->lk, LK_EXCLUSIVE);
264         map->arg = arg;
265         map->handler = handler;
266         sc->fns->enable_intr(sc, map);
267         lockmgr(&sc->lk, LK_RELEASE);
268 }
269
270 static void
271 gpio_intel_teardown_intr(device_t dev, void *cookie)
272 {
273         struct gpio_intel_softc *sc = device_get_softc(dev);
274         struct pin_intr_map *map = (struct pin_intr_map *)cookie;
275
276         KKASSERT(gpio_intel_pin_exists(sc, map->pin));
277
278         lockmgr(&sc->lk, LK_EXCLUSIVE);
279         sc->fns->disable_intr(sc, map);
280         map->arg = NULL;
281         map->handler = NULL;
282         lockmgr(&sc->lk, LK_RELEASE);
283 }
284
285 static int
286 gpio_intel_read_pin(device_t dev, u_int pin)
287 {
288         struct gpio_intel_softc *sc = device_get_softc(dev);
289         int val;
290
291         /* This operation mustn't fail, otherwise ACPI would be in trouble */
292         KKASSERT(gpio_intel_pin_exists(sc, pin));
293
294         lockmgr(&sc->lk, LK_EXCLUSIVE);
295         val = sc->fns->read_pin(sc, pin);
296         lockmgr(&sc->lk, LK_RELEASE);
297
298         return (val);
299 }
300
301 static void
302 gpio_intel_write_pin(device_t dev, u_int pin, int value)
303 {
304         struct gpio_intel_softc *sc = device_get_softc(dev);
305
306         /* This operation mustn't fail, otherwise ACPI would be in trouble */
307         KKASSERT(gpio_intel_pin_exists(sc, pin));
308
309         lockmgr(&sc->lk, LK_EXCLUSIVE);
310         sc->fns->write_pin(sc, pin, value);
311         lockmgr(&sc->lk, LK_RELEASE);
312 }
313
314 static void
315 gpio_intel_intr(void *arg)
316 {
317         struct gpio_intel_softc *sc = (struct gpio_intel_softc *)arg;
318
319         lockmgr(&sc->lk, LK_EXCLUSIVE);
320         sc->fns->intr(arg);
321         lockmgr(&sc->lk, LK_RELEASE);
322 }
323
324 static int
325 gpio_intel_pin_exists(struct gpio_intel_softc *sc, uint16_t pin)
326 {
327         struct pinrange *r;
328
329         for (r = sc->ranges; r->start != -1 && r->end != -1; r++) {
330                 if (r->start <= (int)pin && r->end >= (int)pin)
331                         return (TRUE);
332         }
333
334         return (FALSE);
335 }
336
337 static device_method_t gpio_intel_methods[] = {
338         /* Device interface */
339         DEVMETHOD(device_probe, gpio_intel_probe),
340         DEVMETHOD(device_attach, gpio_intel_attach),
341         DEVMETHOD(device_detach, gpio_intel_detach),
342
343         /* GPIO methods */
344         DEVMETHOD(gpio_alloc_intr, gpio_intel_alloc_intr),
345         DEVMETHOD(gpio_free_intr, gpio_intel_free_intr),
346         DEVMETHOD(gpio_setup_intr, gpio_intel_setup_intr),
347         DEVMETHOD(gpio_teardown_intr, gpio_intel_teardown_intr),
348         DEVMETHOD(gpio_read_pin, gpio_intel_read_pin),
349         DEVMETHOD(gpio_write_pin, gpio_intel_write_pin),
350
351         DEVMETHOD_END
352 };
353
354 static driver_t gpio_intel_driver = {
355         "gpio_intel",
356         gpio_intel_methods,
357         sizeof(struct gpio_intel_softc)
358 };
359
360 static devclass_t gpio_intel_devclass;
361
362 DRIVER_MODULE(gpio_intel, acpi, gpio_intel_driver, gpio_intel_devclass,
363     NULL, NULL);
364 MODULE_DEPEND(gpio_intel, acpi, 1, 1, 1);
365 MODULE_DEPEND(gpio_intel, gpio_acpi, 1, 1, 1);
366 MODULE_VERSION(gpio_intel, 1);