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