xref: /dflybsd-src/sys/bus/gpio/gpio_acpi/gpio_acpi.c (revision d56572d4c91ad8e80f65eb6d78dee9a963fd0ad9)
1bca7db71SImre Vadász /*
2bca7db71SImre Vadász  * Copyright (c) 2016 The DragonFly Project.  All rights reserved.
3bca7db71SImre Vadász  *
4bca7db71SImre Vadász  * This code is derived from software contributed to The DragonFly Project
5bca7db71SImre Vadász  * by Imre Vadász <imre@vdsz.com>
6bca7db71SImre Vadász  *
7bca7db71SImre Vadász  * Redistribution and use in source and binary forms, with or without
8bca7db71SImre Vadász  * modification, are permitted provided that the following conditions
9bca7db71SImre Vadász  * are met:
10bca7db71SImre Vadász  *
11bca7db71SImre Vadász  * 1. Redistributions of source code must retain the above copyright
12bca7db71SImre Vadász  *    notice, this list of conditions and the following disclaimer.
13bca7db71SImre Vadász  * 2. Redistributions in binary form must reproduce the above copyright
14bca7db71SImre Vadász  *    notice, this list of conditions and the following disclaimer in
15bca7db71SImre Vadász  *    the documentation and/or other materials provided with the
16bca7db71SImre Vadász  *    distribution.
17bca7db71SImre Vadász  * 3. Neither the name of The DragonFly Project nor the names of its
18bca7db71SImre Vadász  *    contributors may be used to endorse or promote products derived
19bca7db71SImre Vadász  *    from this software without specific, prior written permission.
20bca7db71SImre Vadász  *
21bca7db71SImre Vadász  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22bca7db71SImre Vadász  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23bca7db71SImre Vadász  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24bca7db71SImre Vadász  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
25bca7db71SImre Vadász  * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26bca7db71SImre Vadász  * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
27bca7db71SImre Vadász  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28bca7db71SImre Vadász  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
29bca7db71SImre Vadász  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
30bca7db71SImre Vadász  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
31bca7db71SImre Vadász  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32bca7db71SImre Vadász  * SUCH DAMAGE.
33bca7db71SImre Vadász  */
34bca7db71SImre Vadász 
35bca7db71SImre Vadász #include <sys/param.h>
36bca7db71SImre Vadász #include <sys/systm.h>
37bca7db71SImre Vadász #include <sys/kernel.h>
38bca7db71SImre Vadász #include <sys/module.h>
39bca7db71SImre Vadász #include <sys/errno.h>
40bca7db71SImre Vadász #include <sys/lock.h>
41bca7db71SImre Vadász #include <sys/mutex.h>
42bca7db71SImre Vadász #include <sys/bus.h>
43bca7db71SImre Vadász 
44bca7db71SImre Vadász #include "opt_acpi.h"
4585e653c7SImre Vadász #include "acpi.h"
46bca7db71SImre Vadász #include <dev/acpica/acpivar.h>
47bca7db71SImre Vadász 
48bca7db71SImre Vadász #include "gpio_acpivar.h"
49bca7db71SImre Vadász 
50bca7db71SImre Vadász #include "gpio_if.h"
51bca7db71SImre Vadász 
52bca7db71SImre Vadász struct acpi_event_info {
53bca7db71SImre Vadász 	device_t dev;
54ad9cd008SImre Vadász 	u_int pin;
55ad9cd008SImre Vadász 	void *cookie;
56bca7db71SImre Vadász 	int trigger;
57bca7db71SImre Vadász };
58bca7db71SImre Vadász 
59bca7db71SImre Vadász struct acpi_gpio_handler_data {
60bca7db71SImre Vadász 	struct acpi_connection_info info;
61bca7db71SImre Vadász 	device_t dev;
62bca7db71SImre Vadász };
63bca7db71SImre Vadász 
64bca7db71SImre Vadász struct gpio_acpi_data {
65bca7db71SImre Vadász 	struct acpi_event_info *infos;
66bca7db71SImre Vadász 	int num_aei;
67bca7db71SImre Vadász 	struct acpi_gpio_handler_data space_handler_data;
68bca7db71SImre Vadász };
69bca7db71SImre Vadász 
701ad3434bSImre Vadász static BOOLEAN	gpio_acpi_check_gpioint(device_t dev, ACPI_RESOURCE_GPIO *gpio);
71*d56572d4SImre Vadász static void	**gpioio_alloc_pins(device_t dev, device_t provider,
72*d56572d4SImre Vadász 		    ACPI_RESOURCE_GPIO *gpio, uint16_t idx, uint16_t length,
73*d56572d4SImre Vadász 		    void **buf);
741ad3434bSImre Vadász 
75bca7db71SImre Vadász /* GPIO Address Space Handler */
76bca7db71SImre Vadász static void		gpio_acpi_install_address_space_handler(device_t dev,
77bca7db71SImre Vadász 			    struct acpi_gpio_handler_data *data);
78bca7db71SImre Vadász static void		gpio_acpi_remove_address_space_handler(device_t dev,
79bca7db71SImre Vadász 			    struct acpi_gpio_handler_data *data);
80bca7db71SImre Vadász static ACPI_STATUS	gpio_acpi_space_handler(UINT32 Function,
81bca7db71SImre Vadász 			    ACPI_PHYSICAL_ADDRESS Address, UINT32 BitWidth,
82bca7db71SImre Vadász 			    UINT64 *Value, void *HandlerContext,
83bca7db71SImre Vadász 			    void *RegionContext);
84bca7db71SImre Vadász 
85bca7db71SImre Vadász /* ACPI Event Interrupts */
86bca7db71SImre Vadász static void	gpio_acpi_do_map_aei(device_t dev,
87bca7db71SImre Vadász 		    struct acpi_event_info *info, ACPI_RESOURCE_GPIO *gpio);
88bca7db71SImre Vadász static void	*gpio_acpi_map_aei(device_t dev, int *num_aei);
89bca7db71SImre Vadász static void	gpio_acpi_unmap_aei(device_t dev, struct gpio_acpi_data *data);
90bca7db71SImre Vadász static void	gpio_acpi_handle_event(void *Context);
91bca7db71SImre Vadász static void	gpio_acpi_aei_handler(void *arg);
92bca7db71SImre Vadász 
93bca7db71SImre Vadász /* Register GPIO device with ACPICA for ACPI-5.0 GPIO functionality */
94bca7db71SImre Vadász void *
95bca7db71SImre Vadász gpio_acpi_register(device_t dev)
96bca7db71SImre Vadász {
97bca7db71SImre Vadász 	struct gpio_acpi_data *data;
98bca7db71SImre Vadász 
99bca7db71SImre Vadász 	data = kmalloc(sizeof(*data), M_DEVBUF, M_WAITOK | M_ZERO);
100bca7db71SImre Vadász 
101bca7db71SImre Vadász 	gpio_acpi_install_address_space_handler(dev,
102bca7db71SImre Vadász 	    &data->space_handler_data);
103bca7db71SImre Vadász 
104bca7db71SImre Vadász 	data->infos = gpio_acpi_map_aei(dev, &data->num_aei);
105bca7db71SImre Vadász 
106bca7db71SImre Vadász 	return data;
107bca7db71SImre Vadász }
108bca7db71SImre Vadász 
109bca7db71SImre Vadász void
110bca7db71SImre Vadász gpio_acpi_unregister(device_t dev, void *arg)
111bca7db71SImre Vadász {
112bca7db71SImre Vadász 	struct gpio_acpi_data *data = (struct gpio_acpi_data *)arg;
113bca7db71SImre Vadász 
114bca7db71SImre Vadász 	if (data->infos != NULL)
115bca7db71SImre Vadász 		gpio_acpi_unmap_aei(dev, data);
116bca7db71SImre Vadász 
117bca7db71SImre Vadász 	gpio_acpi_remove_address_space_handler(dev, &data->space_handler_data);
118bca7db71SImre Vadász 
119bca7db71SImre Vadász 	kfree(data, M_DEVBUF);
120bca7db71SImre Vadász }
121bca7db71SImre Vadász 
1221ad3434bSImre Vadász /* Sanity-Check for GpioInt resources */
1231ad3434bSImre Vadász static BOOLEAN
1241ad3434bSImre Vadász gpio_acpi_check_gpioint(device_t dev, ACPI_RESOURCE_GPIO *gpio)
1251ad3434bSImre Vadász {
1261ad3434bSImre Vadász 
1271ad3434bSImre Vadász 	if (gpio->PinTableLength != 1) {
1281ad3434bSImre Vadász 		device_printf(dev,
1291ad3434bSImre Vadász 		    "Unexepcted GpioInt resource PinTableLength %d\n",
1301ad3434bSImre Vadász 		    gpio->PinTableLength);
1311ad3434bSImre Vadász 		return (FALSE);
1321ad3434bSImre Vadász 	}
1331ad3434bSImre Vadász 	switch (gpio->Triggering) {
1341ad3434bSImre Vadász 	case ACPI_LEVEL_SENSITIVE:
1351ad3434bSImre Vadász 	case ACPI_EDGE_SENSITIVE:
1361ad3434bSImre Vadász 		break;
1371ad3434bSImre Vadász 	default:
1381ad3434bSImre Vadász 		device_printf(dev, "Invalid GpioInt resource Triggering: %d\n",
1391ad3434bSImre Vadász 		    gpio->Triggering);
1401ad3434bSImre Vadász 		return (FALSE);
1411ad3434bSImre Vadász 	}
1421ad3434bSImre Vadász 	switch (gpio->Polarity) {
1431ad3434bSImre Vadász 	case ACPI_ACTIVE_HIGH:
1441ad3434bSImre Vadász 	case ACPI_ACTIVE_LOW:
1451ad3434bSImre Vadász 	case ACPI_ACTIVE_BOTH:
1461ad3434bSImre Vadász 		break;
1471ad3434bSImre Vadász 	default:
1481ad3434bSImre Vadász 		device_printf(dev, "Invalid GpioInt resource Polarity: %d\n",
1491ad3434bSImre Vadász 		    gpio->Polarity);
1501ad3434bSImre Vadász 		return (FALSE);
1511ad3434bSImre Vadász 	}
1521ad3434bSImre Vadász 
1531ad3434bSImre Vadász 	return (TRUE);
1541ad3434bSImre Vadász }
1551ad3434bSImre Vadász 
156bca7db71SImre Vadász /*
157*d56572d4SImre Vadász  * GpioIo ACPI resource handling
158*d56572d4SImre Vadász  */
159*d56572d4SImre Vadász 
160*d56572d4SImre Vadász static void **
161*d56572d4SImre Vadász gpioio_alloc_pins(device_t dev, device_t provider, ACPI_RESOURCE_GPIO *gpio,
162*d56572d4SImre Vadász     uint16_t idx, uint16_t length, void **buf)
163*d56572d4SImre Vadász {
164*d56572d4SImre Vadász 	void **pins;
165*d56572d4SImre Vadász 	int flags, i, j;
166*d56572d4SImre Vadász 
167*d56572d4SImre Vadász 	if (buf == NULL) {
168*d56572d4SImre Vadász 		pins = kmalloc(sizeof(*pins) * length, M_DEVBUF,
169*d56572d4SImre Vadász 		    M_WAITOK | M_ZERO);
170*d56572d4SImre Vadász 	} else {
171*d56572d4SImre Vadász 		pins = buf;
172*d56572d4SImre Vadász 	}
173*d56572d4SImre Vadász 
174*d56572d4SImre Vadász 	if (gpio->IoRestriction == ACPI_IO_RESTRICT_INPUT) {
175*d56572d4SImre Vadász 		flags = (1U << 0);
176*d56572d4SImre Vadász 	} else if (gpio->IoRestriction ==
177*d56572d4SImre Vadász 	    ACPI_IO_RESTRICT_OUTPUT) {
178*d56572d4SImre Vadász 		flags = (1U << 1);
179*d56572d4SImre Vadász 	} else {
180*d56572d4SImre Vadász 		flags = (1U << 0) | (1U << 1);
181*d56572d4SImre Vadász 	}
182*d56572d4SImre Vadász 	for (i = 0; i < length; i++) {
183*d56572d4SImre Vadász 		if (GPIO_ALLOC_IO_PIN(provider, gpio->PinTable[idx + i], flags,
184*d56572d4SImre Vadász 		    &pins[i]) != 0) {
185*d56572d4SImre Vadász 			device_printf(dev, "Failed to alloc GpioIo pin %u on "
186*d56572d4SImre Vadász 			    "ResourceSource \"%s\"\n", gpio->PinTable[idx + i],
187*d56572d4SImre Vadász 			    gpio->ResourceSource.StringPtr);
188*d56572d4SImre Vadász 			/* Release already alloc-ed pins */
189*d56572d4SImre Vadász 			for (j = 0; j < i; j++)
190*d56572d4SImre Vadász 				GPIO_RELEASE_IO_PIN(provider, pins[j]);
191*d56572d4SImre Vadász 			goto err;
192*d56572d4SImre Vadász 		}
193*d56572d4SImre Vadász 	}
194*d56572d4SImre Vadász 
195*d56572d4SImre Vadász 	return (pins);
196*d56572d4SImre Vadász 
197*d56572d4SImre Vadász err:
198*d56572d4SImre Vadász 	if (buf == NULL)
199*d56572d4SImre Vadász 		kfree(pins, M_DEVBUF);
200*d56572d4SImre Vadász 	return (NULL);
201*d56572d4SImre Vadász }
202*d56572d4SImre Vadász 
203*d56572d4SImre Vadász /*
204bca7db71SImre Vadász  * GPIO Address space handler
205bca7db71SImre Vadász  */
206bca7db71SImre Vadász 
207bca7db71SImre Vadász static void
208bca7db71SImre Vadász gpio_acpi_install_address_space_handler(device_t dev,
209bca7db71SImre Vadász     struct acpi_gpio_handler_data *data)
210bca7db71SImre Vadász {
211bca7db71SImre Vadász 	ACPI_HANDLE handle;
212bca7db71SImre Vadász 	ACPI_STATUS s;
213bca7db71SImre Vadász 
214bca7db71SImre Vadász 	handle = acpi_get_handle(dev);
215bca7db71SImre Vadász 	data->dev = dev;
216bca7db71SImre Vadász 	s = AcpiInstallAddressSpaceHandler(handle, ACPI_ADR_SPACE_GPIO,
217bca7db71SImre Vadász 	    &gpio_acpi_space_handler, NULL, data);
2181ad3434bSImre Vadász 	if (ACPI_FAILURE(s)) {
219bca7db71SImre Vadász 		device_printf(dev,
220bca7db71SImre Vadász 		    "Failed to install GPIO Address Space Handler in ACPI\n");
221bca7db71SImre Vadász 	}
222bca7db71SImre Vadász }
223bca7db71SImre Vadász 
224bca7db71SImre Vadász static void
225bca7db71SImre Vadász gpio_acpi_remove_address_space_handler(device_t dev,
226bca7db71SImre Vadász     struct acpi_gpio_handler_data *data)
227bca7db71SImre Vadász {
228bca7db71SImre Vadász 	ACPI_HANDLE handle;
229bca7db71SImre Vadász 	ACPI_STATUS s;
230bca7db71SImre Vadász 
231bca7db71SImre Vadász 	handle = acpi_get_handle(dev);
232bca7db71SImre Vadász 	s = AcpiRemoveAddressSpaceHandler(handle, ACPI_ADR_SPACE_GPIO,
233bca7db71SImre Vadász 	    &gpio_acpi_space_handler);
2341ad3434bSImre Vadász 	if (ACPI_FAILURE(s)) {
235bca7db71SImre Vadász 		device_printf(dev,
236bca7db71SImre Vadász 		    "Failed to remove GPIO Address Space Handler from ACPI\n");
237bca7db71SImre Vadász 	}
238bca7db71SImre Vadász }
239bca7db71SImre Vadász 
240bca7db71SImre Vadász static ACPI_STATUS
241bca7db71SImre Vadász gpio_acpi_space_handler(UINT32 Function, ACPI_PHYSICAL_ADDRESS Address,
242bca7db71SImre Vadász     UINT32 BitWidth, UINT64 *Value, void *HandlerContext, void *RegionContext)
243bca7db71SImre Vadász {
244bca7db71SImre Vadász 	struct acpi_gpio_handler_data *data = HandlerContext;
245bca7db71SImre Vadász 	device_t dev = data->dev;
246bca7db71SImre Vadász 	struct acpi_connection_info *info = &data->info;
247bca7db71SImre Vadász 	struct acpi_resource_gpio *gpio;
248bca7db71SImre Vadász 	UINT64 val;
249bca7db71SImre Vadász 	UINT8 action = Function & ACPI_IO_MASK;
250bca7db71SImre Vadász 	ACPI_RESOURCE *Resource;
251bca7db71SImre Vadász 	ACPI_STATUS s = AE_OK;
252*d56572d4SImre Vadász 	void **pins;
253bca7db71SImre Vadász 	int i;
254bca7db71SImre Vadász 
255*d56572d4SImre Vadász 	if (Value == NULL)
256*d56572d4SImre Vadász 		return (AE_BAD_PARAMETER);
257bca7db71SImre Vadász 
258bca7db71SImre Vadász 	/* XXX probably unnecessary */
259*d56572d4SImre Vadász 	if (BitWidth == 0 || BitWidth > 64)
260*d56572d4SImre Vadász 		return (AE_BAD_PARAMETER);
261*d56572d4SImre Vadász 
262*d56572d4SImre Vadász 	s = AcpiBufferToResource(info->Connection, info->Length, &Resource);
263*d56572d4SImre Vadász 	if (ACPI_FAILURE(s)) {
264*d56572d4SImre Vadász 		device_printf(dev, "AcpiBufferToResource failed\n");
265*d56572d4SImre Vadász 		return (s);
266*d56572d4SImre Vadász 	}
267*d56572d4SImre Vadász 	if (Resource->Type != ACPI_RESOURCE_TYPE_GPIO) {
268*d56572d4SImre Vadász 		device_printf(dev, "Resource->Type is wrong\n");
269*d56572d4SImre Vadász 		s = AE_BAD_PARAMETER;
270*d56572d4SImre Vadász 		goto err;
271*d56572d4SImre Vadász 	}
272*d56572d4SImre Vadász 	gpio = &Resource->Data.Gpio;
273*d56572d4SImre Vadász 	if (gpio->ConnectionType != ACPI_RESOURCE_GPIO_TYPE_IO) {
274*d56572d4SImre Vadász 		device_printf(dev, "gpio->ConnectionType is wrong\n");
275bca7db71SImre Vadász 		s = AE_BAD_PARAMETER;
276bca7db71SImre Vadász 		goto err;
277bca7db71SImre Vadász 	}
278bca7db71SImre Vadász 
279bca7db71SImre Vadász 	if (Address + BitWidth > gpio->PinTableLength) {
280*d56572d4SImre Vadász 		device_printf(dev, "Address + BitWidth out of range\n");
281bca7db71SImre Vadász 		s = AE_BAD_ADDRESS;
282bca7db71SImre Vadász 		goto err;
283bca7db71SImre Vadász 	}
284bca7db71SImre Vadász 
285*d56572d4SImre Vadász 	if (gpio->IoRestriction == ACPI_IO_RESTRICT_OUTPUT &&
286*d56572d4SImre Vadász 	    action == ACPI_READ) {
287*d56572d4SImre Vadász 		device_printf(dev,
288*d56572d4SImre Vadász 		    "IoRestriction is output only, but action is ACPI_READ\n");
289bca7db71SImre Vadász 		s = AE_BAD_PARAMETER;
290bca7db71SImre Vadász 		goto err;
291bca7db71SImre Vadász 	}
292*d56572d4SImre Vadász 	if (gpio->IoRestriction == ACPI_IO_RESTRICT_INPUT &&
293*d56572d4SImre Vadász 	    action == ACPI_WRITE) {
294*d56572d4SImre Vadász 		device_printf(dev,
295*d56572d4SImre Vadász 		    "IoRestriction is input only, but action is ACPI_WRITE\n");
296*d56572d4SImre Vadász 		s = AE_BAD_PARAMETER;
297*d56572d4SImre Vadász 		goto err;
298*d56572d4SImre Vadász 	}
299*d56572d4SImre Vadász 
300*d56572d4SImre Vadász 	/* Make sure we can access all pins, before trying actual read/write */
301*d56572d4SImre Vadász 	pins = gpioio_alloc_pins(dev, dev, gpio, Address, BitWidth, NULL);
302*d56572d4SImre Vadász 	if (pins == NULL) {
303*d56572d4SImre Vadász 		s = AE_BAD_PARAMETER;
304*d56572d4SImre Vadász 		goto err;
305*d56572d4SImre Vadász 	}
306*d56572d4SImre Vadász 
307*d56572d4SImre Vadász 	if (action == ACPI_READ) {
308bca7db71SImre Vadász 		*Value = 0;
309bca7db71SImre Vadász 		for (i = 0; i < BitWidth; i++) {
310*d56572d4SImre Vadász 			val = GPIO_READ_PIN(dev, pins[i]);
311bca7db71SImre Vadász 			*Value |= val << i;
312bca7db71SImre Vadász 		}
313bca7db71SImre Vadász 	} else {
314bca7db71SImre Vadász 		for (i = 0; i < BitWidth; i++) {
315*d56572d4SImre Vadász 			GPIO_WRITE_PIN(dev, pins[i],
316*d56572d4SImre Vadász 			    (*Value & (1ULL << i)) ? 1 : 0);
317bca7db71SImre Vadász 		}
318bca7db71SImre Vadász 	}
319*d56572d4SImre Vadász 	for (i = 0; i < BitWidth; i++)
320*d56572d4SImre Vadász 		GPIO_RELEASE_IO_PIN(dev, pins[i]);
321*d56572d4SImre Vadász 	kfree(pins, M_DEVBUF);
322bca7db71SImre Vadász 
323bca7db71SImre Vadász err:
324bca7db71SImre Vadász 	ACPI_FREE(Resource);
325bca7db71SImre Vadász 	return (s);
326bca7db71SImre Vadász }
327bca7db71SImre Vadász 
328bca7db71SImre Vadász /*
329bca7db71SImre Vadász  * ACPI Event Interrupts
330bca7db71SImre Vadász  */
331bca7db71SImre Vadász 
332bca7db71SImre Vadász static void
333bca7db71SImre Vadász gpio_acpi_handle_event(void *Context)
334bca7db71SImre Vadász {
335bca7db71SImre Vadász 	struct acpi_event_info *info = (struct acpi_event_info *)Context;
336bca7db71SImre Vadász 	ACPI_HANDLE handle, h;
337bca7db71SImre Vadász 	ACPI_STATUS s;
338bca7db71SImre Vadász 	char buf[5];
339bca7db71SImre Vadász 
340bca7db71SImre Vadász 	handle = acpi_get_handle(info->dev);
341bca7db71SImre Vadász 	if (info->trigger == ACPI_EDGE_SENSITIVE) {
342bca7db71SImre Vadász 		ksnprintf(buf, sizeof(buf), "_E%02X", info->pin);
343bca7db71SImre Vadász 	} else {
344bca7db71SImre Vadász 		ksnprintf(buf, sizeof(buf), "_L%02X", info->pin);
345bca7db71SImre Vadász 	}
346bca7db71SImre Vadász 	if (info->pin <= 255 && ACPI_SUCCESS(AcpiGetHandle(handle, buf, &h))) {
347bca7db71SImre Vadász 		s = AcpiEvaluateObject(handle, buf, NULL, NULL);
3481ad3434bSImre Vadász 		if (ACPI_FAILURE(s))
349bca7db71SImre Vadász 			device_printf(info->dev, "evaluating %s failed\n", buf);
350bca7db71SImre Vadász 	} else {
351bca7db71SImre Vadász 		ACPI_OBJECT_LIST arglist;
352bca7db71SImre Vadász 		ACPI_OBJECT arg;
353bca7db71SImre Vadász 
354bca7db71SImre Vadász 		arglist.Pointer = &arg;
355bca7db71SImre Vadász 		arglist.Count = 1;
356bca7db71SImre Vadász 		arg.Type = ACPI_TYPE_INTEGER;
357bca7db71SImre Vadász 		arg.Integer.Value = info->pin;
358bca7db71SImre Vadász 		s = AcpiEvaluateObject(handle, "_EVT", &arglist, NULL);
3591ad3434bSImre Vadász 		if (ACPI_FAILURE(s))
360bca7db71SImre Vadász 			device_printf(info->dev, "evaluating _EVT failed\n");
361bca7db71SImre Vadász 	}
362bca7db71SImre Vadász }
363bca7db71SImre Vadász 
364bca7db71SImre Vadász static void
365bca7db71SImre Vadász gpio_acpi_aei_handler(void *arg)
366bca7db71SImre Vadász {
367bca7db71SImre Vadász 	struct acpi_event_info *info = (struct acpi_event_info *)arg;
368bca7db71SImre Vadász 	ACPI_STATUS s;
369bca7db71SImre Vadász 
370bca7db71SImre Vadász 	s = AcpiOsExecute(OSL_GPE_HANDLER, gpio_acpi_handle_event, arg);
3711ad3434bSImre Vadász 	if (ACPI_FAILURE(s)) {
372bca7db71SImre Vadász 		device_printf(info->dev,
373bca7db71SImre Vadász 		    "AcpiOsExecute for Acpi Event handler failed\n");
374bca7db71SImre Vadász 	}
375bca7db71SImre Vadász }
376bca7db71SImre Vadász 
377bca7db71SImre Vadász static void
378bca7db71SImre Vadász gpio_acpi_do_map_aei(device_t dev, struct acpi_event_info *info,
379bca7db71SImre Vadász     ACPI_RESOURCE_GPIO *gpio)
380bca7db71SImre Vadász {
381bca7db71SImre Vadász 	uint16_t pin;
382ad9cd008SImre Vadász 	void *cookie;
383bca7db71SImre Vadász 
384bca7db71SImre Vadász 	if (gpio->ConnectionType != ACPI_RESOURCE_GPIO_TYPE_INT) {
385bca7db71SImre Vadász 		device_printf(dev, "Unexpected gpio type %d\n",
386bca7db71SImre Vadász 		    gpio->ConnectionType);
387bca7db71SImre Vadász 		return;
388bca7db71SImre Vadász 	}
389bca7db71SImre Vadász 
3901ad3434bSImre Vadász 	if (!gpio_acpi_check_gpioint(dev, gpio))
391bca7db71SImre Vadász 		return;
392bca7db71SImre Vadász 
3930b0166b0SImre Vadász 	pin = gpio->PinTable[0];
3940b0166b0SImre Vadász 
3950b0166b0SImre Vadász 	if (GPIO_ALLOC_INTR(dev, pin, gpio->Triggering, gpio->Polarity,
396ad9cd008SImre Vadász 	    gpio->PinConfig, &cookie) != 0) {
3970b0166b0SImre Vadász 		device_printf(dev,
3980b0166b0SImre Vadász 		    "Failed to allocate AEI interrupt on pin %d\n", pin);
3990b0166b0SImre Vadász 		return;
4000b0166b0SImre Vadász 	}
4010b0166b0SImre Vadász 
402bca7db71SImre Vadász 	info->dev = dev;
403bca7db71SImre Vadász 	info->pin = pin;
404bca7db71SImre Vadász 	info->trigger = gpio->Triggering;
405ad9cd008SImre Vadász 	info->cookie = cookie;
406bca7db71SImre Vadász 
407ad9cd008SImre Vadász 	GPIO_SETUP_INTR(dev, cookie, info, gpio_acpi_aei_handler);
408bca7db71SImre Vadász }
409bca7db71SImre Vadász 
410bca7db71SImre Vadász /* Map ACPI events */
411bca7db71SImre Vadász static void *
412bca7db71SImre Vadász gpio_acpi_map_aei(device_t dev, int *num_aei)
413bca7db71SImre Vadász {
414bca7db71SImre Vadász 	ACPI_HANDLE handle = acpi_get_handle(dev);
415bca7db71SImre Vadász 	ACPI_RESOURCE_GPIO *gpio;
416bca7db71SImre Vadász 	ACPI_RESOURCE *res, *end;
417bca7db71SImre Vadász 	ACPI_BUFFER b;
418bca7db71SImre Vadász 	ACPI_STATUS s;
419bca7db71SImre Vadász 	struct acpi_event_info *infos = NULL;
420bca7db71SImre Vadász 	int n;
421bca7db71SImre Vadász 
422bca7db71SImre Vadász 	*num_aei = 0;
423bca7db71SImre Vadász 
424bca7db71SImre Vadász 	b.Pointer = NULL;
425bca7db71SImre Vadász 	b.Length = ACPI_ALLOCATE_BUFFER;
426bca7db71SImre Vadász 	s = AcpiGetEventResources(handle, &b);
427bca7db71SImre Vadász 	if (ACPI_SUCCESS(s)) {
428bca7db71SImre Vadász 		end = (ACPI_RESOURCE *)((char *)b.Pointer + b.Length);
429bca7db71SImre Vadász 		/* Count Gpio connections */
430bca7db71SImre Vadász 		n = 0;
431bca7db71SImre Vadász 		for (res = (ACPI_RESOURCE *)b.Pointer; res < end;
432bca7db71SImre Vadász 		    res = ACPI_NEXT_RESOURCE(res)) {
433*d56572d4SImre Vadász 			if (res->Type == ACPI_RESOURCE_TYPE_END_TAG) {
434bca7db71SImre Vadász 				break;
435*d56572d4SImre Vadász 			} else if (res->Type == ACPI_RESOURCE_TYPE_GPIO) {
436bca7db71SImre Vadász 				n++;
437*d56572d4SImre Vadász 			} else {
438*d56572d4SImre Vadász 				device_printf(dev,
439*d56572d4SImre Vadász 				    "Unexpected resource type %d\n",
440*d56572d4SImre Vadász 				    res->Type);
441bca7db71SImre Vadász 			}
442bca7db71SImre Vadász 		}
443bca7db71SImre Vadász 		if (n <= 0) {
444bca7db71SImre Vadász 			AcpiOsFree(b.Pointer);
445bca7db71SImre Vadász 			return (infos);
446bca7db71SImre Vadász 		}
447bca7db71SImre Vadász 		infos = kmalloc(n*sizeof(*infos), M_DEVBUF, M_WAITOK | M_ZERO);
448bca7db71SImre Vadász 		*num_aei = n;
449bca7db71SImre Vadász 		n = 0;
450bca7db71SImre Vadász 		for (res = (ACPI_RESOURCE *)b.Pointer; res < end;
451bca7db71SImre Vadász 		    res = ACPI_NEXT_RESOURCE(res)) {
452bca7db71SImre Vadász 			if (res->Type == ACPI_RESOURCE_TYPE_END_TAG)
453bca7db71SImre Vadász 				break;
454*d56572d4SImre Vadász 			if (res->Type == ACPI_RESOURCE_TYPE_GPIO) {
455bca7db71SImre Vadász 				gpio = (ACPI_RESOURCE_GPIO *)&res->Data;
456bca7db71SImre Vadász 				gpio_acpi_do_map_aei(dev, &infos[n++], gpio);
457*d56572d4SImre Vadász 			}
458bca7db71SImre Vadász 		}
459bca7db71SImre Vadász 		AcpiOsFree(b.Pointer);
460bca7db71SImre Vadász 	}
461bca7db71SImre Vadász 	return (infos);
462bca7db71SImre Vadász }
463bca7db71SImre Vadász 
464bca7db71SImre Vadász /*  Unmap ACPI events */
465bca7db71SImre Vadász static void
466bca7db71SImre Vadász gpio_acpi_unmap_aei(device_t dev, struct gpio_acpi_data *data)
467bca7db71SImre Vadász {
468bca7db71SImre Vadász 	struct acpi_event_info *info;
469bca7db71SImre Vadász 	int i;
470bca7db71SImre Vadász 
471bca7db71SImre Vadász 	for (i = 0; i < data->num_aei; i++) {
472bca7db71SImre Vadász 		info = &data->infos[i];
473bca7db71SImre Vadász 		if (info->dev != NULL) {
474ad9cd008SImre Vadász 			GPIO_TEARDOWN_INTR(dev, info->cookie);
475ad9cd008SImre Vadász 			GPIO_FREE_INTR(dev, info->cookie);
476bca7db71SImre Vadász 			/* XXX Wait until ACPI Event handler has finished */
477bca7db71SImre Vadász 			memset(info, 0, sizeof(*info));
478bca7db71SImre Vadász 		}
479bca7db71SImre Vadász 	}
480bca7db71SImre Vadász 	kfree(data->infos, M_DEVBUF);
481bca7db71SImre Vadász }
482bca7db71SImre Vadász 
483bca7db71SImre Vadász MODULE_DEPEND(gpio_acpi, acpi, 1, 1, 1);
484bca7db71SImre Vadász MODULE_VERSION(gpio_acpi, 1);
485