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 {
59 struct acpi_gpio_handler_data {
60 struct acpi_connection_info info;
64 struct gpio_acpi_data {
65 struct acpi_event_info *infos;
67 struct acpi_gpio_handler_data space_handler_data;
70 /* GPIO Address Space Handler */
71 static void gpio_acpi_install_address_space_handler(device_t dev,
72 struct acpi_gpio_handler_data *data);
73 static void gpio_acpi_remove_address_space_handler(device_t dev,
74 struct acpi_gpio_handler_data *data);
75 static ACPI_STATUS gpio_acpi_space_handler(UINT32 Function,
76 ACPI_PHYSICAL_ADDRESS Address, UINT32 BitWidth,
77 UINT64 *Value, void *HandlerContext,
80 /* ACPI Event Interrupts */
81 static void gpio_acpi_do_map_aei(device_t dev,
82 struct acpi_event_info *info, ACPI_RESOURCE_GPIO *gpio);
83 static void *gpio_acpi_map_aei(device_t dev, int *num_aei);
84 static void gpio_acpi_unmap_aei(device_t dev, struct gpio_acpi_data *data);
85 static void gpio_acpi_handle_event(void *Context);
86 static void gpio_acpi_aei_handler(void *arg);
88 /* Register GPIO device with ACPICA for ACPI-5.0 GPIO functionality */
90 gpio_acpi_register(device_t dev)
92 struct gpio_acpi_data *data;
94 data = kmalloc(sizeof(*data), M_DEVBUF, M_WAITOK | M_ZERO);
96 gpio_acpi_install_address_space_handler(dev,
97 &data->space_handler_data);
99 data->infos = gpio_acpi_map_aei(dev, &data->num_aei);
105 gpio_acpi_unregister(device_t dev, void *arg)
107 struct gpio_acpi_data *data = (struct gpio_acpi_data *)arg;
109 if (data->infos != NULL)
110 gpio_acpi_unmap_aei(dev, data);
112 gpio_acpi_remove_address_space_handler(dev, &data->space_handler_data);
114 kfree(data, M_DEVBUF);
118 * GPIO Address space handler
122 gpio_acpi_install_address_space_handler(device_t dev,
123 struct acpi_gpio_handler_data *data)
128 handle = acpi_get_handle(dev);
131 * XXX Should use the Address Space Setup Handler to check and
132 * reserve the GPIO pins, to avoid checking on each READ/WRITE
133 * call into the Adress Space Handler.
135 s = AcpiInstallAddressSpaceHandler(handle, ACPI_ADR_SPACE_GPIO,
136 &gpio_acpi_space_handler, NULL, data);
137 if (!ACPI_SUCCESS(s)) {
139 "Failed to install GPIO Address Space Handler in ACPI\n");
144 gpio_acpi_remove_address_space_handler(device_t dev,
145 struct acpi_gpio_handler_data *data)
150 handle = acpi_get_handle(dev);
151 s = AcpiRemoveAddressSpaceHandler(handle, ACPI_ADR_SPACE_GPIO,
152 &gpio_acpi_space_handler);
153 if (!ACPI_SUCCESS(s)) {
155 "Failed to remove GPIO Address Space Handler from ACPI\n");
160 gpio_acpi_space_handler(UINT32 Function, ACPI_PHYSICAL_ADDRESS Address,
161 UINT32 BitWidth, UINT64 *Value, void *HandlerContext, void *RegionContext)
163 struct acpi_gpio_handler_data *data = HandlerContext;
164 device_t dev = data->dev;
165 struct acpi_connection_info *info = &data->info;
166 struct acpi_resource_gpio *gpio;
168 UINT8 action = Function & ACPI_IO_MASK;
169 ACPI_RESOURCE *Resource;
170 ACPI_STATUS s = AE_OK;
173 s = AcpiBufferToResource(info->Connection, info->Length, &Resource);
176 if (Value == NULL || Resource->Type != ACPI_RESOURCE_TYPE_GPIO) {
177 s = AE_BAD_PARAMETER;
181 gpio = &Resource->Data.Gpio;
182 if (gpio->ConnectionType != ACPI_RESOURCE_GPIO_TYPE_IO) {
183 s = AE_BAD_PARAMETER;
187 /* XXX probably unnecessary */
188 if (BitWidth == 0 || BitWidth > 64) {
189 s = AE_BAD_PARAMETER;
193 if (Address + BitWidth > gpio->PinTableLength) {
198 if (action == ACPI_READ) {
199 if (gpio->IoRestriction == ACPI_IO_RESTRICT_OUTPUT) {
200 s = AE_BAD_PARAMETER;
204 for (i = 0; i < BitWidth; i++) {
205 val = GPIO_READ_PIN(dev, gpio->PinTable[Address + i]);
209 if (gpio->IoRestriction == ACPI_IO_RESTRICT_INPUT) {
210 s = AE_BAD_PARAMETER;
213 for (i = 0; i < BitWidth; i++) {
214 GPIO_WRITE_PIN(dev, gpio->PinTable[Address + i],
215 *Value & (1ULL << i) ? 1 : 0);
226 * ACPI Event Interrupts
230 gpio_acpi_handle_event(void *Context)
232 struct acpi_event_info *info = (struct acpi_event_info *)Context;
233 ACPI_HANDLE handle, h;
237 handle = acpi_get_handle(info->dev);
238 if (info->trigger == ACPI_EDGE_SENSITIVE) {
239 ksnprintf(buf, sizeof(buf), "_E%02X", info->pin);
241 ksnprintf(buf, sizeof(buf), "_L%02X", info->pin);
243 if (info->pin <= 255 && ACPI_SUCCESS(AcpiGetHandle(handle, buf, &h))) {
244 s = AcpiEvaluateObject(handle, buf, NULL, NULL);
245 if (!ACPI_SUCCESS(s)) {
246 device_printf(info->dev, "evaluating %s failed\n", buf);
249 ACPI_OBJECT_LIST arglist;
252 arglist.Pointer = &arg;
254 arg.Type = ACPI_TYPE_INTEGER;
255 arg.Integer.Value = info->pin;
256 s = AcpiEvaluateObject(handle, "_EVT", &arglist, NULL);
257 if (!ACPI_SUCCESS(s)) {
258 device_printf(info->dev, "evaluating _EVT failed\n");
264 gpio_acpi_aei_handler(void *arg)
266 struct acpi_event_info *info = (struct acpi_event_info *)arg;
269 s = AcpiOsExecute(OSL_GPE_HANDLER, gpio_acpi_handle_event, arg);
270 if (!ACPI_SUCCESS(s)) {
271 device_printf(info->dev,
272 "AcpiOsExecute for Acpi Event handler failed\n");
277 gpio_acpi_do_map_aei(device_t dev, struct acpi_event_info *info,
278 ACPI_RESOURCE_GPIO *gpio)
283 if (gpio->ConnectionType != ACPI_RESOURCE_GPIO_TYPE_INT) {
284 device_printf(dev, "Unexpected gpio type %d\n",
285 gpio->ConnectionType);
288 if (gpio->PinTableLength != 1) {
290 "Unexepcted gpio PinTableLength expected 1 got %d\n",
291 gpio->PinTableLength);
296 switch (gpio->Triggering) {
297 case ACPI_LEVEL_SENSITIVE:
298 case ACPI_EDGE_SENSITIVE:
301 device_printf(dev, "Invalid Trigger: %d\n", gpio->Triggering);
304 switch (gpio->Polarity) {
305 case ACPI_ACTIVE_HIGH:
306 case ACPI_ACTIVE_LOW:
307 case ACPI_ACTIVE_BOTH:
310 device_printf(dev, "Invalid Polarity: %d\n", gpio->Polarity);
314 pin = gpio->PinTable[0];
316 if (GPIO_ALLOC_INTR(dev, pin, gpio->Triggering, gpio->Polarity,
317 gpio->PinConfig, &cookie) != 0) {
319 "Failed to allocate AEI interrupt on pin %d\n", pin);
325 info->trigger = gpio->Triggering;
326 info->cookie = cookie;
328 GPIO_SETUP_INTR(dev, cookie, info, gpio_acpi_aei_handler);
331 /* Map ACPI events */
333 gpio_acpi_map_aei(device_t dev, int *num_aei)
335 ACPI_HANDLE handle = acpi_get_handle(dev);
336 ACPI_RESOURCE_GPIO *gpio;
337 ACPI_RESOURCE *res, *end;
340 struct acpi_event_info *infos = NULL;
346 b.Length = ACPI_ALLOCATE_BUFFER;
347 s = AcpiGetEventResources(handle, &b);
348 if (ACPI_SUCCESS(s)) {
349 end = (ACPI_RESOURCE *)((char *)b.Pointer + b.Length);
350 /* Count Gpio connections */
352 for (res = (ACPI_RESOURCE *)b.Pointer; res < end;
353 res = ACPI_NEXT_RESOURCE(res)) {
354 if (res->Type == ACPI_RESOURCE_TYPE_END_TAG)
357 case ACPI_RESOURCE_TYPE_GPIO:
365 AcpiOsFree(b.Pointer);
368 infos = kmalloc(n*sizeof(*infos), M_DEVBUF, M_WAITOK | M_ZERO);
371 for (res = (ACPI_RESOURCE *)b.Pointer; res < end;
372 res = ACPI_NEXT_RESOURCE(res)) {
373 if (res->Type == ACPI_RESOURCE_TYPE_END_TAG)
376 case ACPI_RESOURCE_TYPE_GPIO:
377 gpio = (ACPI_RESOURCE_GPIO *)&res->Data;
378 gpio_acpi_do_map_aei(dev, &infos[n++], gpio);
382 "Unexpected resource type %d\n",
387 AcpiOsFree(b.Pointer);
392 /* Unmap ACPI events */
394 gpio_acpi_unmap_aei(device_t dev, struct gpio_acpi_data *data)
396 struct acpi_event_info *info;
399 for (i = 0; i < data->num_aei; i++) {
400 info = &data->infos[i];
401 if (info->dev != NULL) {
402 GPIO_TEARDOWN_INTR(dev, info->cookie);
403 GPIO_FREE_INTR(dev, info->cookie);
404 /* XXX Wait until ACPI Event handler has finished */
405 memset(info, 0, sizeof(*info));
408 kfree(data->infos, M_DEVBUF);
411 MODULE_DEPEND(gpio_acpi, acpi, 1, 1, 1);
412 MODULE_VERSION(gpio_acpi, 1);