xref: /dragonfly/sys/bus/gpio/gpio_intel/gpio_intel.c (revision 0d27ae55)
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 *arg,
66 		    driver_intr_t *handler);
67 static int	gpio_intel_free_intr(device_t dev, u_int pin);
68 static int	gpio_intel_read_pin(device_t dev, u_int pin);
69 static void	gpio_intel_write_pin(device_t dev, u_int pin, int value);
70 
71 static void	gpio_intel_intr(void *arg);
72 static int	gpio_intel_pin_exists(struct gpio_intel_softc *sc,
73 		    uint16_t pin);
74 
75 static char *cherryview_ids[] = { "INT33FF", NULL };
76 
77 static int
78 gpio_intel_probe(device_t dev)
79 {
80 
81         if (acpi_disabled("gpio_intel") ||
82             ACPI_ID_PROBE(device_get_parent(dev), dev, cherryview_ids) == NULL)
83                 return (ENXIO);
84 
85 	device_set_desc(dev, "Intel Cherry Trail GPIO Controller");
86 
87 	return (BUS_PROBE_DEFAULT);
88 }
89 
90 static int
91 gpio_intel_attach(device_t dev)
92 {
93 	struct gpio_intel_softc *sc = device_get_softc(dev);
94 	int error, i, rid;
95 
96 	lockinit(&sc->lk, "gpio_intel", 0, LK_CANRECURSE);
97 
98 	sc->dev = dev;
99 
100         if (ACPI_ID_PROBE(device_get_parent(dev), dev, cherryview_ids)
101 	    != NULL) {
102 		error = gpio_cherryview_matchuid(sc);
103 		if (error) {
104 			error = ENXIO;
105 			goto err;
106 		}
107 	} else {
108 		error = ENXIO;
109 		goto err;
110 	}
111 
112 	for (i = 0; i < 16; i++) {
113 		sc->intrmaps[i].pin = -1;
114 	}
115 
116 	rid = 0;
117 	sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY,
118 	    &rid, RF_ACTIVE);
119 	if (sc->mem_res == NULL) {
120 		device_printf(dev, "unable to map registers");
121 		error = ENXIO;
122 		goto err;
123 	}
124 	rid = 0;
125 	sc->irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ,
126 	    &rid, RF_ACTIVE);
127 	if (sc->irq_res == NULL) {
128 		device_printf(dev, "unable to map interrupt");
129 		error = ENXIO;
130 		goto err;
131 	}
132 
133 	lockmgr(&sc->lk, LK_EXCLUSIVE);
134 	/* Activate the interrupt */
135 	error = bus_setup_intr(dev, sc->irq_res, INTR_MPSAFE,
136 	    gpio_intel_intr, sc, &sc->intrhand, NULL);
137 	if (error)
138 		device_printf(dev, "Can't setup IRQ\n");
139 
140 	/* power up the controller */
141 	pci_set_powerstate(dev, PCI_POWERSTATE_D0);
142 
143 	sc->fns->init(sc);
144 	lockmgr(&sc->lk, LK_RELEASE);
145 
146 	sc->acpireg = gpio_acpi_register(dev);
147 
148 	return (0);
149 
150 err:
151 	if (sc->irq_res) {
152 		bus_release_resource(dev, SYS_RES_IRQ,
153 		    rman_get_rid(sc->irq_res), sc->irq_res);
154 		sc->irq_res = NULL;
155 	}
156 	if (sc->mem_res) {
157 		bus_release_resource(dev, SYS_RES_MEMORY,
158 		    rman_get_rid(sc->mem_res), sc->mem_res);
159 		sc->mem_res = NULL;
160 	}
161 	return (error);
162 }
163 
164 static int
165 gpio_intel_detach(device_t dev)
166 {
167 	struct gpio_intel_softc *sc = device_get_softc(dev);
168 
169 	gpio_acpi_unregister(dev, sc->acpireg);
170 
171 	bus_teardown_intr(dev, sc->irq_res, sc->intrhand);
172 	if (sc->irq_res) {
173 		bus_release_resource(dev, SYS_RES_IRQ,
174 		    rman_get_rid(sc->irq_res), sc->irq_res);
175 		sc->irq_res = NULL;
176 	}
177 	if (sc->mem_res) {
178 		bus_release_resource(dev, SYS_RES_MEMORY,
179 		    rman_get_rid(sc->mem_res), sc->mem_res);
180 		sc->mem_res = NULL;
181 	}
182 	lockuninit(&sc->lk);
183 
184 	pci_set_powerstate(dev, PCI_POWERSTATE_D3);
185 
186 	return 0;
187 }
188 
189 /*
190  * The trigger, polarity and termination parameters are only used for
191  * sanity checking. The gpios should already be configured correctly by
192  * the firmware.
193  */
194 static int
195 gpio_intel_alloc_intr(device_t dev, u_int pin, int trigger, int polarity,
196     int termination, void *arg, driver_intr_t *handler)
197 {
198 	struct gpio_intel_softc *sc = device_get_softc(dev);
199 	int i, ret;
200 
201 	lockmgr(&sc->lk, LK_EXCLUSIVE);
202 
203 	if (gpio_intel_pin_exists(sc, pin)) {
204 		/* Make sure this pin isn't mapped yet */
205 		for (i = 0; i < 16; i++) {
206 			if (sc->intrmaps[i].pin == pin)
207 			return (ENOMEM);
208 		}
209 		ret = sc->fns->map_intr(sc, pin, trigger, polarity,
210 		    termination, arg, handler);
211 	} else {
212 		device_printf(sc->dev, "Invalid pin number %d\n", pin);
213 		ret = ENOENT;
214 	}
215 
216 	lockmgr(&sc->lk, LK_RELEASE);
217 
218 	return (ret);
219 }
220 
221 static int
222 gpio_intel_free_intr(device_t dev, u_int pin)
223 {
224 	struct gpio_intel_softc *sc = device_get_softc(dev);
225 	int ret;
226 
227 	lockmgr(&sc->lk, LK_EXCLUSIVE);
228 
229 	if (gpio_intel_pin_exists(sc, pin)) {
230 		sc->fns->unmap_intr(sc, pin);
231 		ret = 0;
232 	} else {
233 		device_printf(sc->dev, "Invalid pin number %d\n", pin);
234 		ret = ENOENT;
235 	}
236 
237 	lockmgr(&sc->lk, LK_RELEASE);
238 
239 	return (ret);
240 }
241 
242 static int
243 gpio_intel_read_pin(device_t dev, u_int pin)
244 {
245 	struct gpio_intel_softc *sc = device_get_softc(dev);
246 	int val;
247 
248         /* This operation mustn't fail, otherwise ACPI would be in trouble */
249         KKASSERT(gpio_intel_pin_exists(sc, pin));
250 
251         lockmgr(&sc->lk, LK_EXCLUSIVE);
252 	val = sc->fns->read_pin(sc, pin);
253         lockmgr(&sc->lk, LK_RELEASE);
254 
255 	return (val);
256 }
257 
258 static void
259 gpio_intel_write_pin(device_t dev, u_int pin, int value)
260 {
261 	struct gpio_intel_softc *sc = device_get_softc(dev);
262 
263         /* This operation mustn't fail, otherwise ACPI would be in trouble */
264         KKASSERT(gpio_intel_pin_exists(sc, pin));
265 
266         lockmgr(&sc->lk, LK_EXCLUSIVE);
267 	sc->fns->write_pin(sc, pin, value);
268         lockmgr(&sc->lk, LK_RELEASE);
269 }
270 
271 static void
272 gpio_intel_intr(void *arg)
273 {
274 	struct gpio_intel_softc *sc = (struct gpio_intel_softc *)arg;
275 
276         lockmgr(&sc->lk, LK_EXCLUSIVE);
277 	sc->fns->intr(arg);
278         lockmgr(&sc->lk, LK_RELEASE);
279 }
280 
281 static int
282 gpio_intel_pin_exists(struct gpio_intel_softc *sc, uint16_t pin)
283 {
284 	struct pinrange *r;
285 
286 	for (r = sc->ranges; r->start != -1 && r->end != -1; r++) {
287 		if (r->start <= (int)pin && r->end >= (int)pin)
288 			return (TRUE);
289 	}
290 
291 	return (FALSE);
292 }
293 
294 static device_method_t gpio_intel_methods[] = {
295 	/* Device interface */
296 	DEVMETHOD(device_probe, gpio_intel_probe),
297 	DEVMETHOD(device_attach, gpio_intel_attach),
298 	DEVMETHOD(device_detach, gpio_intel_detach),
299 
300 	/* GPIO methods */
301 	DEVMETHOD(gpio_alloc_intr, gpio_intel_alloc_intr),
302 	DEVMETHOD(gpio_free_intr, gpio_intel_free_intr),
303 	DEVMETHOD(gpio_read_pin, gpio_intel_read_pin),
304 	DEVMETHOD(gpio_write_pin, gpio_intel_write_pin),
305 
306 	DEVMETHOD_END
307 };
308 
309 static driver_t gpio_intel_driver = {
310         "gpio_intel",
311         gpio_intel_methods,
312         sizeof(struct gpio_intel_softc)
313 };
314 
315 static devclass_t gpio_intel_devclass;
316 
317 DRIVER_MODULE(gpio_intel, acpi, gpio_intel_driver, gpio_intel_devclass,
318     NULL, NULL);
319 MODULE_DEPEND(gpio_intel, acpi, 1, 1, 1);
320 MODULE_DEPEND(gpio_intel, gpio_acpi, 1, 1, 1);
321 MODULE_VERSION(gpio_intel, 1);
322