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