xref: /dragonfly/sys/bus/gpio/gpio_acpi/gpio_acpi.c (revision 62dc643e)
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 /* Register GPIO device with ACPICA for ACPI-5.0 GPIO functionality */
36 
37 #include <sys/param.h>
38 #include <sys/systm.h>
39 #include <sys/kernel.h>
40 #include <sys/module.h>
41 #include <sys/errno.h>
42 #include <sys/lock.h>
43 #include <sys/bus.h>
44 
45 #include "opt_acpi.h"
46 #include "acpi.h"
47 #include <dev/acpica/acpivar.h>
48 
49 #include "gpio_acpivar.h"
50 
51 #include "gpio_if.h"
52 
53 struct acpi_event_info {
54 	device_t dev;
55 	u_int pin;
56 	void *cookie;
57 	int trigger;
58 };
59 
60 struct acpi_gpio_handler_data {
61 	struct acpi_connection_info info;
62 	device_t dev;
63 };
64 
65 struct gpio_acpi_softc {
66 	device_t dev;
67 	device_t parent;
68 	struct acpi_event_info *infos;
69 	int num_aei;
70 	struct acpi_gpio_handler_data space_handler_data;
71 };
72 
73 static int	gpio_acpi_probe(device_t dev);
74 static int	gpio_acpi_attach(device_t dev);
75 static int	gpio_acpi_detach(device_t dev);
76 
77 static BOOLEAN	gpio_acpi_check_gpioint(device_t dev, ACPI_RESOURCE_GPIO *gpio);
78 static void	**gpioio_alloc_pins(device_t dev, device_t provider,
79 		    ACPI_RESOURCE_GPIO *gpio, uint16_t idx, uint16_t length,
80 		    void **buf);
81 
82 /* GPIO Address Space Handler */
83 static void		gpio_acpi_install_address_space_handler(
84 			    struct gpio_acpi_softc *sc);
85 static void		gpio_acpi_remove_address_space_handler(
86 			    struct gpio_acpi_softc *sc);
87 static ACPI_STATUS	gpio_acpi_space_handler(UINT32 Function,
88 			    ACPI_PHYSICAL_ADDRESS Address, UINT32 BitWidth,
89 			    UINT64 *Value, void *HandlerContext,
90 			    void *RegionContext);
91 
92 /* ACPI Event Interrupts */
93 static void	gpio_acpi_do_map_aei(struct gpio_acpi_softc *sc,
94 		    ACPI_RESOURCE_GPIO *gpio);
95 static void	gpio_acpi_map_aei(struct gpio_acpi_softc *sc);
96 static void	gpio_acpi_unmap_aei(struct gpio_acpi_softc *sc);
97 static void	gpio_acpi_handle_event(void *Context);
98 static void	gpio_acpi_aei_handler(void *arg);
99 
100 /* Sanity-Check for GpioInt resources */
101 static BOOLEAN
102 gpio_acpi_check_gpioint(device_t dev, ACPI_RESOURCE_GPIO *gpio)
103 {
104 	if (gpio->PinTableLength != 1) {
105 		device_printf(dev,
106 		    "Unexepcted GpioInt resource PinTableLength %d\n",
107 		    gpio->PinTableLength);
108 		return (FALSE);
109 	}
110 	switch (gpio->Triggering) {
111 	case ACPI_LEVEL_SENSITIVE:
112 	case ACPI_EDGE_SENSITIVE:
113 		break;
114 	default:
115 		device_printf(dev, "Invalid GpioInt resource Triggering: %d\n",
116 		    gpio->Triggering);
117 		return (FALSE);
118 	}
119 	switch (gpio->Polarity) {
120 	case ACPI_ACTIVE_HIGH:
121 	case ACPI_ACTIVE_LOW:
122 	case ACPI_ACTIVE_BOTH:
123 		break;
124 	default:
125 		device_printf(dev, "Invalid GpioInt resource Polarity: %d\n",
126 		    gpio->Polarity);
127 		return (FALSE);
128 	}
129 
130 	return (TRUE);
131 }
132 
133 /*
134  * GpioIo ACPI resource handling
135  */
136 
137 static void **
138 gpioio_alloc_pins(device_t dev, device_t provider, ACPI_RESOURCE_GPIO *gpio,
139     uint16_t idx, uint16_t length, void **buf)
140 {
141 	void **pins;
142 	int flags, i, j;
143 
144 	if (buf == NULL) {
145 		pins = kmalloc(sizeof(*pins) * length, M_DEVBUF,
146 		    M_WAITOK | M_ZERO);
147 	} else {
148 		pins = buf;
149 	}
150 
151 	if (gpio->IoRestriction == ACPI_IO_RESTRICT_INPUT) {
152 		flags = (1U << 0);
153 	} else if (gpio->IoRestriction ==
154 	    ACPI_IO_RESTRICT_OUTPUT) {
155 		flags = (1U << 1);
156 	} else {
157 		flags = (1U << 0) | (1U << 1);
158 	}
159 	for (i = 0; i < length; i++) {
160 		if (GPIO_ALLOC_IO_PIN(provider, gpio->PinTable[idx + i], flags,
161 		    &pins[i]) != 0) {
162 			device_printf(dev, "Failed to alloc GpioIo pin %u on "
163 			    "ResourceSource \"%s\"\n", gpio->PinTable[idx + i],
164 			    gpio->ResourceSource.StringPtr);
165 			/* Release already alloc-ed pins */
166 			for (j = 0; j < i; j++)
167 				GPIO_RELEASE_IO_PIN(provider, pins[j]);
168 			goto err;
169 		}
170 	}
171 
172 	return (pins);
173 
174 err:
175 	if (buf == NULL)
176 		kfree(pins, M_DEVBUF);
177 	return (NULL);
178 }
179 
180 /*
181  * GPIO Address space handler
182  */
183 
184 static void
185 gpio_acpi_install_address_space_handler(struct gpio_acpi_softc *sc)
186 {
187 	struct acpi_gpio_handler_data *data = &sc->space_handler_data;
188 	ACPI_HANDLE handle;
189 	ACPI_STATUS s;
190 
191 	handle = acpi_get_handle(sc->parent);
192 	data->dev = sc->parent;
193 	s = AcpiInstallAddressSpaceHandler(handle, ACPI_ADR_SPACE_GPIO,
194 	    &gpio_acpi_space_handler, NULL, data);
195 	if (ACPI_FAILURE(s)) {
196 		device_printf(sc->dev,
197 		    "Failed to install GPIO Address Space Handler in ACPI\n");
198 	}
199 }
200 
201 static void
202 gpio_acpi_remove_address_space_handler(struct gpio_acpi_softc *sc)
203 {
204 	ACPI_HANDLE handle;
205 	ACPI_STATUS s;
206 
207 	handle = acpi_get_handle(sc->parent);
208 	s = AcpiRemoveAddressSpaceHandler(handle, ACPI_ADR_SPACE_GPIO,
209 	    &gpio_acpi_space_handler);
210 	if (ACPI_FAILURE(s)) {
211 		device_printf(sc->dev,
212 		    "Failed to remove GPIO Address Space Handler from ACPI\n");
213 	}
214 }
215 
216 static ACPI_STATUS
217 gpio_acpi_space_handler(UINT32 Function, ACPI_PHYSICAL_ADDRESS Address,
218     UINT32 BitWidth, UINT64 *Value, void *HandlerContext, void *RegionContext)
219 {
220 	struct acpi_gpio_handler_data *data = HandlerContext;
221 	device_t dev = data->dev;
222 	struct acpi_connection_info *info = &data->info;
223 	struct acpi_resource_gpio *gpio;
224 	UINT64 val;
225 	UINT8 action = Function & ACPI_IO_MASK;
226 	ACPI_RESOURCE *Resource;
227 	ACPI_STATUS s = AE_OK;
228 	void **pins;
229 	int i;
230 
231 	if (Value == NULL)
232 		return (AE_BAD_PARAMETER);
233 
234 	/* XXX probably unnecessary */
235 	if (BitWidth == 0 || BitWidth > 64)
236 		return (AE_BAD_PARAMETER);
237 
238 	s = AcpiBufferToResource(info->Connection, info->Length, &Resource);
239 	if (ACPI_FAILURE(s)) {
240 		device_printf(dev, "AcpiBufferToResource failed\n");
241 		return (s);
242 	}
243 	if (Resource->Type != ACPI_RESOURCE_TYPE_GPIO) {
244 		device_printf(dev, "Resource->Type is wrong\n");
245 		s = AE_BAD_PARAMETER;
246 		goto err;
247 	}
248 	gpio = &Resource->Data.Gpio;
249 	if (gpio->ConnectionType != ACPI_RESOURCE_GPIO_TYPE_IO) {
250 		device_printf(dev, "gpio->ConnectionType is wrong\n");
251 		s = AE_BAD_PARAMETER;
252 		goto err;
253 	}
254 
255 	if (Address + BitWidth > gpio->PinTableLength) {
256 		device_printf(dev, "Address + BitWidth out of range\n");
257 		s = AE_BAD_ADDRESS;
258 		goto err;
259 	}
260 
261 	if (gpio->IoRestriction == ACPI_IO_RESTRICT_OUTPUT &&
262 	    action == ACPI_READ) {
263 		device_printf(dev,
264 		    "IoRestriction is output only, but action is ACPI_READ\n");
265 		s = AE_BAD_PARAMETER;
266 		goto err;
267 	}
268 	if (gpio->IoRestriction == ACPI_IO_RESTRICT_INPUT &&
269 	    action == ACPI_WRITE) {
270 		device_printf(dev,
271 		    "IoRestriction is input only, but action is ACPI_WRITE\n");
272 		s = AE_BAD_PARAMETER;
273 		goto err;
274 	}
275 
276 	/* Make sure we can access all pins, before trying actual read/write */
277 	pins = gpioio_alloc_pins(dev, dev, gpio, Address, BitWidth, NULL);
278 	if (pins == NULL) {
279 		s = AE_BAD_PARAMETER;
280 		goto err;
281 	}
282 
283 	if (action == ACPI_READ) {
284 		*Value = 0;
285 		for (i = 0; i < BitWidth; i++) {
286 			val = GPIO_READ_PIN(dev, pins[i]);
287 			*Value |= val << i;
288 		}
289 	} else {
290 		for (i = 0; i < BitWidth; i++) {
291 			GPIO_WRITE_PIN(dev, pins[i],
292 			    (*Value & (1ULL << i)) ? 1 : 0);
293 		}
294 	}
295 	for (i = 0; i < BitWidth; i++)
296 		GPIO_RELEASE_IO_PIN(dev, pins[i]);
297 	kfree(pins, M_DEVBUF);
298 
299 err:
300 	ACPI_FREE(Resource);
301 	return (s);
302 }
303 
304 /*
305  * ACPI Event Interrupts
306  */
307 
308 static void
309 gpio_acpi_handle_event(void *Context)
310 {
311 	struct acpi_event_info *info = (struct acpi_event_info *)Context;
312 	ACPI_HANDLE handle, h;
313 	ACPI_STATUS s;
314 	char buf[5];
315 
316 	handle = acpi_get_handle(device_get_parent(info->dev));
317 	if (info->trigger == ACPI_EDGE_SENSITIVE) {
318 		ksnprintf(buf, sizeof(buf), "_E%02X", info->pin);
319 	} else {
320 		ksnprintf(buf, sizeof(buf), "_L%02X", info->pin);
321 	}
322 	if (info->pin <= 255 && ACPI_SUCCESS(AcpiGetHandle(handle, buf, &h))) {
323 		s = AcpiEvaluateObject(handle, buf, NULL, NULL);
324 		if (ACPI_FAILURE(s))
325 			device_printf(info->dev, "evaluating %s failed\n", buf);
326 	} else {
327 		ACPI_OBJECT_LIST arglist;
328 		ACPI_OBJECT arg;
329 
330 		arglist.Pointer = &arg;
331 		arglist.Count = 1;
332 		arg.Type = ACPI_TYPE_INTEGER;
333 		arg.Integer.Value = info->pin;
334 		s = AcpiEvaluateObject(handle, "_EVT", &arglist, NULL);
335 		if (ACPI_FAILURE(s))
336 			device_printf(info->dev, "evaluating _EVT failed\n");
337 	}
338 }
339 
340 static void
341 gpio_acpi_aei_handler(void *arg)
342 {
343 	struct acpi_event_info *info = (struct acpi_event_info *)arg;
344 	ACPI_STATUS s;
345 
346 	s = AcpiOsExecute(OSL_GPE_HANDLER, gpio_acpi_handle_event, arg);
347 	if (ACPI_FAILURE(s)) {
348 		device_printf(info->dev,
349 		    "AcpiOsExecute for Acpi Event handler failed\n");
350 	}
351 }
352 
353 static void
354 gpio_acpi_do_map_aei(struct gpio_acpi_softc *sc, ACPI_RESOURCE_GPIO *gpio)
355 {
356 	struct acpi_event_info *info = &sc->infos[sc->num_aei];
357 	uint16_t pin;
358 	void *cookie;
359 
360 	if (gpio->ConnectionType != ACPI_RESOURCE_GPIO_TYPE_INT) {
361 		device_printf(sc->dev, "Unexpected gpio type %d\n",
362 		    gpio->ConnectionType);
363 		return;
364 	}
365 
366 	/* sc->dev is correct here, since it's only used for device_printf */
367 	if (!gpio_acpi_check_gpioint(sc->dev, gpio))
368 		return;
369 
370 	pin = gpio->PinTable[0];
371 
372 	if (GPIO_ALLOC_INTR(sc->parent, pin, gpio->Triggering, gpio->Polarity,
373 	    gpio->PinConfig, &cookie) != 0) {
374 		device_printf(sc->dev,
375 		    "Failed to allocate AEI interrupt on pin %d\n", pin);
376 		return;
377 	}
378 
379 	info->dev = sc->dev;
380 	info->pin = pin;
381 	info->trigger = gpio->Triggering;
382 	info->cookie = cookie;
383 
384 	GPIO_SETUP_INTR(sc->parent, cookie, info, gpio_acpi_aei_handler);
385 	sc->num_aei++;
386 }
387 
388 /* Map ACPI events */
389 static void
390 gpio_acpi_map_aei(struct gpio_acpi_softc *sc)
391 {
392 	ACPI_HANDLE handle = acpi_get_handle(sc->parent);
393 	ACPI_RESOURCE_GPIO *gpio;
394 	ACPI_RESOURCE *res, *end;
395 	ACPI_BUFFER b;
396 	ACPI_STATUS s;
397 	int n;
398 
399 	sc->infos = NULL;
400 	sc->num_aei = 0;
401 
402 	b.Pointer = NULL;
403 	b.Length = ACPI_ALLOCATE_BUFFER;
404 	s = AcpiGetEventResources(handle, &b);
405 	if (ACPI_FAILURE(s))
406 		return;
407 
408 	end = (ACPI_RESOURCE *)((char *)b.Pointer + b.Length);
409 	/* Count Gpio connections */
410 	n = 0;
411 	for (res = (ACPI_RESOURCE *)b.Pointer; res < end;
412 	    res = ACPI_NEXT_RESOURCE(res)) {
413 		if (res->Type == ACPI_RESOURCE_TYPE_END_TAG) {
414 			break;
415 		} else if (res->Type == ACPI_RESOURCE_TYPE_GPIO) {
416 			n++;
417 		} else {
418 			device_printf(sc->dev, "Unexpected resource type %d\n",
419 			    res->Type);
420 		}
421 	}
422 	if (n <= 0) {
423 		AcpiOsFree(b.Pointer);
424 		return;
425 	}
426 	sc->infos = kmalloc(n*sizeof(*sc->infos), M_DEVBUF, M_WAITOK | M_ZERO);
427 	for (res = (ACPI_RESOURCE *)b.Pointer; res < end && sc->num_aei < n;
428 	    res = ACPI_NEXT_RESOURCE(res)) {
429 		if (res->Type == ACPI_RESOURCE_TYPE_END_TAG)
430 			break;
431 		if (res->Type == ACPI_RESOURCE_TYPE_GPIO) {
432 			gpio = (ACPI_RESOURCE_GPIO *)&res->Data;
433 			gpio_acpi_do_map_aei(sc, gpio);
434 		}
435 	}
436 	AcpiOsFree(b.Pointer);
437 }
438 
439 /*  Unmap ACPI events */
440 static void
441 gpio_acpi_unmap_aei(struct gpio_acpi_softc *sc)
442 {
443 	struct acpi_event_info *info;
444 	int i;
445 
446 	for (i = 0; i < sc->num_aei; i++) {
447 		info = &sc->infos[i];
448 		KKASSERT(info->dev != NULL);
449 		GPIO_TEARDOWN_INTR(sc->parent, info->cookie);
450 		GPIO_FREE_INTR(sc->parent, info->cookie);
451 		/* XXX Wait until ACPI Event handler has finished */
452 		memset(info, 0, sizeof(*info));
453 	}
454 	kfree(sc->infos, M_DEVBUF);
455 	sc->infos = NULL;
456 	sc->num_aei = 0;
457 }
458 
459 static int
460 gpio_acpi_probe(device_t dev)
461 {
462 	if (acpi_get_handle(device_get_parent(dev)) == NULL)
463 		return (ENXIO);
464 
465 	device_set_desc(dev, "ACPI GeneralPurposeIo backend");
466 
467 	return (0);
468 }
469 
470 static int
471 gpio_acpi_attach(device_t dev)
472 {
473 	struct gpio_acpi_softc *sc = device_get_softc(dev);
474 
475 	sc->dev = dev;
476 	sc->parent = device_get_parent(dev);
477 
478 	gpio_acpi_install_address_space_handler(sc);
479 
480 	gpio_acpi_map_aei(sc);
481 
482 	return (0);
483 }
484 
485 static int
486 gpio_acpi_detach(device_t dev)
487 {
488 	struct gpio_acpi_softc *sc = device_get_softc(dev);
489 
490 	if (sc->infos != NULL)
491 		gpio_acpi_unmap_aei(sc);
492 
493 	gpio_acpi_remove_address_space_handler(sc);
494 
495 	return (0);
496 }
497 
498 
499 static device_method_t gpio_acpi_methods[] = {
500 	/* Device interface */
501 	DEVMETHOD(device_probe, gpio_acpi_probe),
502 	DEVMETHOD(device_attach, gpio_acpi_attach),
503 	DEVMETHOD(device_detach, gpio_acpi_detach),
504 
505 	DEVMETHOD_END
506 };
507 
508 static driver_t gpio_acpi_driver = {
509 	"gpio_acpi",
510 	gpio_acpi_methods,
511 	sizeof(struct gpio_acpi_softc)
512 };
513 
514 static devclass_t gpio_acpi_devclass;
515 
516 DRIVER_MODULE(gpio_acpi, gpio_intel, gpio_acpi_driver, gpio_acpi_devclass,
517     NULL, NULL);
518 MODULE_DEPEND(gpio_acpi, acpi, 1, 1, 1);
519 MODULE_VERSION(gpio_acpi, 1);
520