xref: /dragonfly/sys/bus/gpio/gpio_intel/gpio_intel.c (revision 6700dd34)
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/bus.h>
45 
46 #include <sys/rman.h>
47 
48 #include "opt_acpi.h"
49 #include "acpi.h"
50 #include <dev/acpica/acpivar.h>
51 
52 #include <bus/pci/pcivar.h>
53 
54 #include "gpio_intel_var.h"
55 
56 #include "gpio_if.h"
57 
58 static int	gpio_intel_probe(device_t dev);
59 static int	gpio_intel_attach(device_t dev);
60 static int	gpio_intel_detach(device_t dev);
61 static int	gpio_intel_alloc_intr(device_t dev, u_int pin, int trigger,
62 		    int polarity, int termination, void **cookiep);
63 static void	gpio_intel_setup_intr(device_t dev, void *cookie, void *arg,
64 		    driver_intr_t *handler);
65 static void	gpio_intel_teardown_intr(device_t dev, void *cookie);
66 static void	gpio_intel_free_intr(device_t dev, void *cookie);
67 static int	gpio_intel_alloc_io_pin(device_t dev, u_int pin, int flags,
68 		    void **cookiep);
69 static void	gpio_intel_release_io_pin(device_t dev, void *cookie);
70 static int	gpio_intel_read_pin(device_t dev, void *cookie);
71 static void	gpio_intel_write_pin(device_t dev, void *cookie, 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 < NELEM(sc->intrmaps); i++) {
115 		sc->intrmaps[i].pin = -1;
116 	}
117 	for (i = 0; i < NELEM(sc->iomaps); i++) {
118 		sc->iomaps[i].pin = -1;
119 	}
120 
121 	rid = 0;
122 	sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY,
123 	    &rid, RF_ACTIVE);
124 	if (sc->mem_res == NULL) {
125 		device_printf(dev, "unable to map registers");
126 		error = ENXIO;
127 		goto err;
128 	}
129 	rid = 0;
130 	sc->irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ,
131 	    &rid, RF_ACTIVE);
132 	if (sc->irq_res == NULL) {
133 		device_printf(dev, "unable to map interrupt");
134 		error = ENXIO;
135 		goto err;
136 	}
137 
138 	lockmgr(&sc->lk, LK_EXCLUSIVE);
139 	/* Activate the interrupt */
140 	error = bus_setup_intr(dev, sc->irq_res, INTR_MPSAFE,
141 	    gpio_intel_intr, sc, &sc->intrhand, NULL);
142 	if (error)
143 		device_printf(dev, "Can't setup IRQ\n");
144 
145 	/* power up the controller */
146 	pci_set_powerstate(dev, PCI_POWERSTATE_D0);
147 
148 	sc->fns->init(sc);
149 	lockmgr(&sc->lk, LK_RELEASE);
150 
151 	device_add_child(dev, "gpio_acpi", -1);
152 	bus_generic_attach(dev);
153 
154 	return (0);
155 
156 err:
157 	if (sc->irq_res) {
158 		bus_release_resource(dev, SYS_RES_IRQ,
159 		    rman_get_rid(sc->irq_res), sc->irq_res);
160 		sc->irq_res = NULL;
161 	}
162 	if (sc->mem_res) {
163 		bus_release_resource(dev, SYS_RES_MEMORY,
164 		    rman_get_rid(sc->mem_res), sc->mem_res);
165 		sc->mem_res = NULL;
166 	}
167 	return (error);
168 }
169 
170 static int
171 gpio_intel_detach(device_t dev)
172 {
173 	struct gpio_intel_softc *sc = device_get_softc(dev);
174 	int error;
175 
176 	error = bus_generic_detach(dev);
177 	if (error)
178 		return (error);
179 
180 	device_delete_children(dev);
181 
182 	bus_teardown_intr(dev, sc->irq_res, sc->intrhand);
183 	if (sc->irq_res) {
184 		bus_release_resource(dev, SYS_RES_IRQ,
185 		    rman_get_rid(sc->irq_res), sc->irq_res);
186 		sc->irq_res = NULL;
187 	}
188 	if (sc->mem_res) {
189 		bus_release_resource(dev, SYS_RES_MEMORY,
190 		    rman_get_rid(sc->mem_res), sc->mem_res);
191 		sc->mem_res = NULL;
192 	}
193 	lockuninit(&sc->lk);
194 
195 	pci_set_powerstate(dev, PCI_POWERSTATE_D3);
196 
197 	return 0;
198 }
199 
200 /*
201  * The trigger, polarity and termination parameters are only used for
202  * sanity checking. The gpios should already be configured correctly by
203  * the firmware.
204  */
205 static int
206 gpio_intel_alloc_intr(device_t dev, u_int pin, int trigger, int polarity,
207     int termination, void **cookiep)
208 {
209 	struct gpio_intel_softc *sc = device_get_softc(dev);
210 	struct pin_intr_map *map = NULL;
211 	int i, ret;
212 
213 	if (cookiep == NULL)
214 		return (EINVAL);
215 
216 	lockmgr(&sc->lk, LK_EXCLUSIVE);
217 
218 	if (gpio_intel_pin_exists(sc, pin)) {
219 		/* Make sure this pin isn't mapped yet */
220 		for (i = 0; i < NELEM(sc->intrmaps); i++) {
221 			if (sc->intrmaps[i].pin == pin) {
222 				lockmgr(&sc->lk, LK_RELEASE);
223 				return (EBUSY);
224 			}
225 		}
226 		ret = sc->fns->map_intr(sc, pin, trigger, polarity,
227 		    termination);
228 		if (ret == 0) {
229 			/* XXX map_intr should return the pin_intr_map */
230 			for (i = 0; i < NELEM(sc->intrmaps); i++) {
231 				if (sc->intrmaps[i].pin == pin)
232 					map = &sc->intrmaps[i];
233 			}
234 			if (map != NULL) {
235 				*cookiep = map;
236 				map->arg = NULL;
237 				map->handler = NULL;
238 			}
239 		}
240 	} else {
241 		device_printf(sc->dev, "%s: Invalid pin %d\n", __func__, pin);
242 		ret = ENOENT;
243 	}
244 
245 	lockmgr(&sc->lk, LK_RELEASE);
246 
247 	return (ret);
248 }
249 
250 static void
251 gpio_intel_free_intr(device_t dev, void *cookie)
252 {
253 	struct gpio_intel_softc *sc = device_get_softc(dev);
254 	struct pin_intr_map *map = (struct pin_intr_map *)cookie;
255 
256 	KKASSERT(gpio_intel_pin_exists(sc, map->pin));
257 
258 	lockmgr(&sc->lk, LK_EXCLUSIVE);
259 	map->arg = NULL;
260 	map->handler = NULL;
261 	sc->fns->unmap_intr(sc, map);
262 	lockmgr(&sc->lk, LK_RELEASE);
263 }
264 
265 static void
266 gpio_intel_setup_intr(device_t dev, void *cookie, void *arg,
267     driver_intr_t *handler)
268 {
269 	struct gpio_intel_softc *sc = device_get_softc(dev);
270 	struct pin_intr_map *map = (struct pin_intr_map *)cookie;
271 
272 	KKASSERT(gpio_intel_pin_exists(sc, map->pin));
273 
274 	lockmgr(&sc->lk, LK_EXCLUSIVE);
275 	map->arg = arg;
276 	map->handler = handler;
277 	sc->fns->enable_intr(sc, map);
278 	lockmgr(&sc->lk, LK_RELEASE);
279 }
280 
281 static void
282 gpio_intel_teardown_intr(device_t dev, void *cookie)
283 {
284 	struct gpio_intel_softc *sc = device_get_softc(dev);
285 	struct pin_intr_map *map = (struct pin_intr_map *)cookie;
286 
287 	KKASSERT(gpio_intel_pin_exists(sc, map->pin));
288 
289 	lockmgr(&sc->lk, LK_EXCLUSIVE);
290 	sc->fns->disable_intr(sc, map);
291 	map->arg = NULL;
292 	map->handler = NULL;
293 	lockmgr(&sc->lk, LK_RELEASE);
294 }
295 
296 static int
297 gpio_intel_alloc_io_pin(device_t dev, u_int pin, int flags, void **cookiep)
298 {
299 	struct gpio_intel_softc *sc = device_get_softc(dev);
300 	struct pin_io_map *map = NULL;
301 	int i, ret;
302 
303 	if (cookiep == NULL)
304 		return (EINVAL);
305 
306 	if (!gpio_intel_pin_exists(sc, pin))
307 		return (ENOENT);
308 
309 	lockmgr(&sc->lk, LK_EXCLUSIVE);
310 
311 	for (i = 0; i < NELEM(sc->iomaps); i++) {
312 		if (sc->iomaps[i].pin == pin) {
313 			lockmgr(&sc->lk, LK_RELEASE);
314 			return (EBUSY);
315 		}
316 	}
317 	for (i = 0; i < NELEM(sc->iomaps); i++) {
318 		if (sc->iomaps[i].pin == -1) {
319 			map = &sc->iomaps[i];
320 			break;
321 		}
322 	}
323 	if (map == NULL) {
324 		ret = EBUSY;
325 	} else if (sc->fns->check_io_pin(sc, pin, flags)) {
326 		/*
327 		 * XXX It's possible that RX gets disabled when the interrupt
328 		 *     on this pin gets released.
329 		 */
330 		map->pin = pin;
331 		map->flags = flags;
332 		*cookiep = map;
333 		ret = 0;
334 	} else {
335 		ret = EBUSY;
336 	}
337 
338 	lockmgr(&sc->lk, LK_RELEASE);
339 
340 	return (ret);
341 }
342 
343 static void
344 gpio_intel_release_io_pin(device_t dev, void *cookie)
345 {
346 	struct gpio_intel_softc *sc = device_get_softc(dev);
347 	struct pin_io_map *map = (struct pin_io_map *)cookie;
348 
349 	lockmgr(&sc->lk, LK_EXCLUSIVE);
350 	map->pin = -1;
351 	map->flags = 0;
352 	lockmgr(&sc->lk, LK_RELEASE);
353 }
354 
355 static int
356 gpio_intel_read_pin(device_t dev, void *cookie)
357 {
358 	struct gpio_intel_softc *sc = device_get_softc(dev);
359 	struct pin_io_map *map = (struct pin_io_map *)cookie;
360 	int val;
361 
362 	KKASSERT(map->pin >= 0);
363 	KKASSERT(gpio_intel_pin_exists(sc, map->pin));
364 
365 	lockmgr(&sc->lk, LK_EXCLUSIVE);
366 	val = sc->fns->read_pin(sc, map->pin);
367 	lockmgr(&sc->lk, LK_RELEASE);
368 
369 	return (val);
370 }
371 
372 static void
373 gpio_intel_write_pin(device_t dev, void *cookie, int value)
374 {
375 	struct gpio_intel_softc *sc = device_get_softc(dev);
376 	struct pin_io_map *map = (struct pin_io_map *)cookie;
377 
378 	KKASSERT(map->pin >= 0);
379 	KKASSERT(gpio_intel_pin_exists(sc, map->pin));
380 
381 	lockmgr(&sc->lk, LK_EXCLUSIVE);
382 	sc->fns->write_pin(sc, map->pin, value);
383 	lockmgr(&sc->lk, LK_RELEASE);
384 }
385 
386 static void
387 gpio_intel_intr(void *arg)
388 {
389 	struct gpio_intel_softc *sc = (struct gpio_intel_softc *)arg;
390 
391 	lockmgr(&sc->lk, LK_EXCLUSIVE);
392 	sc->fns->intr(arg);
393 	lockmgr(&sc->lk, LK_RELEASE);
394 }
395 
396 static int
397 gpio_intel_pin_exists(struct gpio_intel_softc *sc, uint16_t pin)
398 {
399 	struct pinrange *r;
400 
401 	for (r = sc->ranges; r->start != -1 && r->end != -1; r++) {
402 		if (r->start <= (int)pin && r->end >= (int)pin)
403 			return (TRUE);
404 	}
405 
406 	return (FALSE);
407 }
408 
409 static device_method_t gpio_intel_methods[] = {
410 	/* Device interface */
411 	DEVMETHOD(device_probe, gpio_intel_probe),
412 	DEVMETHOD(device_attach, gpio_intel_attach),
413 	DEVMETHOD(device_detach, gpio_intel_detach),
414 
415 	/* GPIO methods */
416 	DEVMETHOD(gpio_alloc_intr, gpio_intel_alloc_intr),
417 	DEVMETHOD(gpio_free_intr, gpio_intel_free_intr),
418 	DEVMETHOD(gpio_setup_intr, gpio_intel_setup_intr),
419 	DEVMETHOD(gpio_teardown_intr, gpio_intel_teardown_intr),
420 	DEVMETHOD(gpio_alloc_io_pin, gpio_intel_alloc_io_pin),
421 	DEVMETHOD(gpio_release_io_pin, gpio_intel_release_io_pin),
422 	DEVMETHOD(gpio_read_pin, gpio_intel_read_pin),
423 	DEVMETHOD(gpio_write_pin, gpio_intel_write_pin),
424 
425 	DEVMETHOD_END
426 };
427 
428 static driver_t gpio_intel_driver = {
429         "gpio_intel",
430         gpio_intel_methods,
431         sizeof(struct gpio_intel_softc)
432 };
433 
434 static devclass_t gpio_intel_devclass;
435 
436 DRIVER_MODULE(gpio_intel, acpi, gpio_intel_driver, gpio_intel_devclass,
437     NULL, NULL);
438 MODULE_DEPEND(gpio_intel, acpi, 1, 1, 1);
439 MODULE_DEPEND(gpio_intel, gpio_acpi, 1, 1, 1);
440 MODULE_VERSION(gpio_intel, 1);
441