2 * Copyright (c) 2016 The DragonFly Project. All rights reserved.
4 * This code is derived from software contributed to The DragonFly Project
5 * by Imre Vadász <imre@vdsz.com>
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
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
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.
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
35 #include <sys/param.h>
36 #include <sys/systm.h>
37 #include <sys/kernel.h>
38 #include <sys/module.h>
39 #include <sys/errno.h>
41 #include <sys/mutex.h>
46 #include <dev/acpica/acpivar.h>
48 #include "gpio_acpivar.h"
52 struct acpi_event_info {
58 struct acpi_gpio_handler_data {
59 struct acpi_connection_info info;
63 struct gpio_acpi_data {
64 struct acpi_event_info *infos;
66 struct acpi_gpio_handler_data space_handler_data;
69 /* GPIO Address Space Handler */
70 static void gpio_acpi_install_address_space_handler(device_t dev,
71 struct acpi_gpio_handler_data *data);
72 static void gpio_acpi_remove_address_space_handler(device_t dev,
73 struct acpi_gpio_handler_data *data);
74 static ACPI_STATUS gpio_acpi_space_handler(UINT32 Function,
75 ACPI_PHYSICAL_ADDRESS Address, UINT32 BitWidth,
76 UINT64 *Value, void *HandlerContext,
79 /* ACPI Event Interrupts */
80 static void gpio_acpi_do_map_aei(device_t dev,
81 struct acpi_event_info *info, ACPI_RESOURCE_GPIO *gpio);
82 static void *gpio_acpi_map_aei(device_t dev, int *num_aei);
83 static void gpio_acpi_unmap_aei(device_t dev, struct gpio_acpi_data *data);
84 static void gpio_acpi_handle_event(void *Context);
85 static void gpio_acpi_aei_handler(void *arg);
87 /* Register GPIO device with ACPICA for ACPI-5.0 GPIO functionality */
89 gpio_acpi_register(device_t dev)
91 struct gpio_acpi_data *data;
93 data = kmalloc(sizeof(*data), M_DEVBUF, M_WAITOK | M_ZERO);
95 gpio_acpi_install_address_space_handler(dev,
96 &data->space_handler_data);
98 data->infos = gpio_acpi_map_aei(dev, &data->num_aei);
104 gpio_acpi_unregister(device_t dev, void *arg)
106 struct gpio_acpi_data *data = (struct gpio_acpi_data *)arg;
108 if (data->infos != NULL)
109 gpio_acpi_unmap_aei(dev, data);
111 gpio_acpi_remove_address_space_handler(dev, &data->space_handler_data);
113 kfree(data, M_DEVBUF);
117 * GPIO Address space handler
121 gpio_acpi_install_address_space_handler(device_t dev,
122 struct acpi_gpio_handler_data *data)
127 handle = acpi_get_handle(dev);
130 * XXX Should use the Address Space Setup Handler to check and
131 * reserve the GPIO pins, to avoid checking on each READ/WRITE
132 * call into the Adress Space Handler.
134 s = AcpiInstallAddressSpaceHandler(handle, ACPI_ADR_SPACE_GPIO,
135 &gpio_acpi_space_handler, NULL, data);
136 if (!ACPI_SUCCESS(s)) {
138 "Failed to install GPIO Address Space Handler in ACPI\n");
143 gpio_acpi_remove_address_space_handler(device_t dev,
144 struct acpi_gpio_handler_data *data)
149 handle = acpi_get_handle(dev);
150 s = AcpiRemoveAddressSpaceHandler(handle, ACPI_ADR_SPACE_GPIO,
151 &gpio_acpi_space_handler);
152 if (!ACPI_SUCCESS(s)) {
154 "Failed to remove GPIO Address Space Handler from ACPI\n");
159 gpio_acpi_space_handler(UINT32 Function, ACPI_PHYSICAL_ADDRESS Address,
160 UINT32 BitWidth, UINT64 *Value, void *HandlerContext, void *RegionContext)
162 struct acpi_gpio_handler_data *data = HandlerContext;
163 device_t dev = data->dev;
164 struct acpi_connection_info *info = &data->info;
165 struct acpi_resource_gpio *gpio;
167 UINT8 action = Function & ACPI_IO_MASK;
168 ACPI_RESOURCE *Resource;
169 ACPI_STATUS s = AE_OK;
172 s = AcpiBufferToResource(info->Connection, info->Length, &Resource);
175 if (Value == NULL || Resource->Type != ACPI_RESOURCE_TYPE_GPIO) {
176 s = AE_BAD_PARAMETER;
180 gpio = &Resource->Data.Gpio;
181 if (gpio->ConnectionType != ACPI_RESOURCE_GPIO_TYPE_IO) {
182 s = AE_BAD_PARAMETER;
186 /* XXX probably unnecessary */
187 if (BitWidth == 0 || BitWidth > 64) {
188 s = AE_BAD_PARAMETER;
192 if (Address + BitWidth > gpio->PinTableLength) {
197 if (action == ACPI_READ) {
198 if (gpio->IoRestriction == ACPI_IO_RESTRICT_OUTPUT) {
199 s = AE_BAD_PARAMETER;
203 for (i = 0; i < BitWidth; i++) {
204 val = GPIO_READ_PIN(dev, gpio->PinTable[Address + i]);
208 if (gpio->IoRestriction == ACPI_IO_RESTRICT_INPUT) {
209 s = AE_BAD_PARAMETER;
212 for (i = 0; i < BitWidth; i++) {
213 GPIO_WRITE_PIN(dev, gpio->PinTable[Address + i],
214 *Value & (1ULL << i) ? 1 : 0);
225 * ACPI Event Interrupts
229 gpio_acpi_handle_event(void *Context)
231 struct acpi_event_info *info = (struct acpi_event_info *)Context;
232 ACPI_HANDLE handle, h;
236 handle = acpi_get_handle(info->dev);
237 if (info->trigger == ACPI_EDGE_SENSITIVE) {
238 ksnprintf(buf, sizeof(buf), "_E%02X", info->pin);
240 ksnprintf(buf, sizeof(buf), "_L%02X", info->pin);
242 if (info->pin <= 255 && ACPI_SUCCESS(AcpiGetHandle(handle, buf, &h))) {
243 s = AcpiEvaluateObject(handle, buf, NULL, NULL);
244 if (!ACPI_SUCCESS(s)) {
245 device_printf(info->dev, "evaluating %s failed\n", buf);
248 ACPI_OBJECT_LIST arglist;
251 arglist.Pointer = &arg;
253 arg.Type = ACPI_TYPE_INTEGER;
254 arg.Integer.Value = info->pin;
255 s = AcpiEvaluateObject(handle, "_EVT", &arglist, NULL);
256 if (!ACPI_SUCCESS(s)) {
257 device_printf(info->dev, "evaluating _EVT failed\n");
263 gpio_acpi_aei_handler(void *arg)
265 struct acpi_event_info *info = (struct acpi_event_info *)arg;
268 s = AcpiOsExecute(OSL_GPE_HANDLER, gpio_acpi_handle_event, arg);
269 if (!ACPI_SUCCESS(s)) {
270 device_printf(info->dev,
271 "AcpiOsExecute for Acpi Event handler failed\n");
276 gpio_acpi_do_map_aei(device_t dev, struct acpi_event_info *info,
277 ACPI_RESOURCE_GPIO *gpio)
281 if (gpio->ConnectionType != ACPI_RESOURCE_GPIO_TYPE_INT) {
282 device_printf(dev, "Unexpected gpio type %d\n",
283 gpio->ConnectionType);
286 if (gpio->PinTableLength != 1) {
288 "Unexepcted gpio PinTableLength expected 1 got %d\n",
289 gpio->PinTableLength);
292 pin = gpio->PinTable[0];
295 switch (gpio->Triggering) {
296 case ACPI_LEVEL_SENSITIVE:
297 case ACPI_EDGE_SENSITIVE:
300 device_printf(dev, "Invalid Trigger: %d\n", gpio->Triggering);
303 switch (gpio->Polarity) {
304 case ACPI_ACTIVE_HIGH:
305 case ACPI_ACTIVE_LOW:
306 case ACPI_ACTIVE_BOTH:
309 device_printf(dev, "Invalid Polarity: %d\n", gpio->Polarity);
315 info->trigger = gpio->Triggering;
317 if (GPIO_ALLOC_INTR(dev, pin, gpio->Triggering, gpio->Polarity,
318 gpio->PinConfig, info, gpio_acpi_aei_handler) != 0) {
320 "Failed to map AEI interrupt on pin %d\n", pin);
321 memset(info, 0, sizeof(*info));
325 /* Map ACPI events */
327 gpio_acpi_map_aei(device_t dev, int *num_aei)
329 ACPI_HANDLE handle = acpi_get_handle(dev);
330 ACPI_RESOURCE_GPIO *gpio;
331 ACPI_RESOURCE *res, *end;
334 struct acpi_event_info *infos = NULL;
340 b.Length = ACPI_ALLOCATE_BUFFER;
341 s = AcpiGetEventResources(handle, &b);
342 if (ACPI_SUCCESS(s)) {
343 end = (ACPI_RESOURCE *)((char *)b.Pointer + b.Length);
344 /* Count Gpio connections */
346 for (res = (ACPI_RESOURCE *)b.Pointer; res < end;
347 res = ACPI_NEXT_RESOURCE(res)) {
348 if (res->Type == ACPI_RESOURCE_TYPE_END_TAG)
351 case ACPI_RESOURCE_TYPE_GPIO:
359 AcpiOsFree(b.Pointer);
362 infos = kmalloc(n*sizeof(*infos), M_DEVBUF, M_WAITOK | M_ZERO);
365 for (res = (ACPI_RESOURCE *)b.Pointer; res < end;
366 res = ACPI_NEXT_RESOURCE(res)) {
367 if (res->Type == ACPI_RESOURCE_TYPE_END_TAG)
370 case ACPI_RESOURCE_TYPE_GPIO:
371 gpio = (ACPI_RESOURCE_GPIO *)&res->Data;
372 gpio_acpi_do_map_aei(dev, &infos[n++], gpio);
376 "Unexpected resource type %d\n",
381 AcpiOsFree(b.Pointer);
386 /* Unmap ACPI events */
388 gpio_acpi_unmap_aei(device_t dev, struct gpio_acpi_data *data)
390 struct acpi_event_info *info;
393 for (i = 0; i < data->num_aei; i++) {
394 info = &data->infos[i];
395 if (info->dev != NULL) {
396 GPIO_FREE_INTR(dev, info->pin);
397 /* XXX Wait until ACPI Event handler has finished */
398 memset(info, 0, sizeof(*info));
401 kfree(data->infos, M_DEVBUF);
404 MODULE_DEPEND(gpio_acpi, acpi, 1, 1, 1);
405 MODULE_VERSION(gpio_acpi, 1);