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