diff options
-rw-r--r-- | drivers/acpi/Kconfig | 10 | ||||
-rw-r--r-- | drivers/acpi/Makefile | 1 | ||||
-rw-r--r-- | drivers/acpi/gpio-acpi-evt-emu.c | 192 |
3 files changed, 203 insertions, 0 deletions
diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig index dc2c64f4765c..22b093d608bb 100644 --- a/drivers/acpi/Kconfig +++ b/drivers/acpi/Kconfig @@ -126,6 +126,16 @@ config ACPI_BUTTON To compile this driver as a module, choose M here: the module will be called button. +config ACPI_GPIO_EVT_EMULATE + tristate "ACPI GPIO-signaled Events Emulation Support" + depends on DEBUG_FS + help + This will enable your system to emulate GPIO-signaled event through + proc file system. User needs to trigger event method by + echo 1 > /sys/kernel/debug/acpi/events/<GPIO DEVICE>/<PIN> + (where, GPIO DEVICE is a GPIO device name and PIN is a pin number). + This is dangerous and should be used for debugging purposes only. + config ACPI_VIDEO tristate "Video" depends on X86 && BACKLIGHT_CLASS_DEVICE diff --git a/drivers/acpi/Makefile b/drivers/acpi/Makefile index d997506f4c73..ed2df45f0e85 100644 --- a/drivers/acpi/Makefile +++ b/drivers/acpi/Makefile @@ -76,6 +76,7 @@ obj-$(CONFIG_ACPI_HED) += hed.o obj-$(CONFIG_ACPI_EC_DEBUGFS) += ec_sys.o obj-$(CONFIG_ACPI_CUSTOM_METHOD)+= custom_method.o obj-$(CONFIG_ACPI_BGRT) += bgrt.o +obj-$(CONFIG_ACPI_GPIO_EVT_EMULATE) += gpio-acpi-evt-emu.o # processor has its own "processor." module_param namespace processor-y := processor_driver.o processor_throttling.o diff --git a/drivers/acpi/gpio-acpi-evt-emu.c b/drivers/acpi/gpio-acpi-evt-emu.c new file mode 100644 index 000000000000..ca8b12595eff --- /dev/null +++ b/drivers/acpi/gpio-acpi-evt-emu.c @@ -0,0 +1,192 @@ +/* + * Code to emulate GPIO-signaled events. + * + * The sole purpose of this module is to help with GPIO event triggering. + * Usage: + * 1. See the list of available GPIO devices and associated pins under: + * /sys/kernel/debug/acpi/events/<GPIO DEVICE>/<PIN>. Only pins which are to + * be used as GPIO-signaled events will be listed (_AEI resources). + * + * 2. Trigger method corresponding to device pin number: + * $ echo 1 > /sys/kernel/debug/acpi/events/<GPIO DEVICE>/<PIN> + */ + +/* + * Copyright (C) 2014, Linaro Ltd. + * Author: Tomasz Nowicki <tomasz.nowicki@linaro.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/module.h> +#include <linux/uaccess.h> +#include <linux/debugfs.h> +#include <linux/acpi.h> + +#include "acpica/accommon.h" + +#include "internal.h" + +#define _COMPONENT ACPI_SYSTEM_COMPONENT +ACPI_MODULE_NAME("gpio_acpi_evt_emu"); + +struct gpio_pin_parent_data { + acpi_handle handle; + struct dentry *debugfs_dir; + char *name; + unsigned int evt_count; +}; + +struct gpio_pin_data { + struct list_head list; + acpi_handle handle; + unsigned int pin; +}; + +static struct dentry *acpi_evt_debugfs_dir; +static LIST_HEAD(pin_data_list); + +static int gpio_evt_trigger(void *data, u64 val) +{ + struct gpio_pin_data *pin_data = (struct gpio_pin_data *)data; + unsigned int pin = pin_data->pin; + acpi_status status; + + if (pin <= 255) + status = acpi_evaluate_object(pin_data->handle, NULL, NULL, + NULL); + else + status = acpi_execute_simple_method(pin_data->handle, NULL, + pin); + + if (ACPI_FAILURE(status)) + pr_err(PREFIX "evaluating event method failed\n"); + + return 0; +} + +DEFINE_SIMPLE_ATTRIBUTE(gpio_evt_emu_fops, NULL, gpio_evt_trigger, "%llu\n"); + +static acpi_status gpio_list_resource(struct acpi_resource *ares, void *context) +{ + struct acpi_resource_gpio *agpio = &ares->data.gpio; + struct gpio_pin_parent_data *parent_data = context; + struct dentry *pin_debugfs_dir; + struct gpio_pin_data *pin_data; + acpi_handle evt_handle; + acpi_status status; + char str_pin[5]; + char ev_name[5]; + int pin; + + if (ares->type != ACPI_RESOURCE_TYPE_GPIO) + return AE_OK; + + if (agpio->connection_type != ACPI_RESOURCE_GPIO_TYPE_INT) + return AE_OK; + + pin_data = kmalloc(sizeof(*pin_data), GFP_KERNEL); + if (!pin_data) + return AE_NO_MEMORY; + + pin = agpio->pin_table[0]; + snprintf(str_pin, 5, "%d", pin); + pin_debugfs_dir = debugfs_create_file(str_pin, S_IWUSR, + parent_data->debugfs_dir, + pin_data, + &gpio_evt_emu_fops); + if (!pin_debugfs_dir) { + status = AE_NULL_ENTRY; + goto fail; + } + + if (pin <= 255) + sprintf(ev_name, "_%c%02X", + agpio->triggering == ACPI_EDGE_SENSITIVE ? 'E' : 'L', + pin); + else + sprintf(ev_name, "_EVT"); + + status = acpi_get_handle(parent_data->handle, ev_name, &evt_handle); + if (ACPI_FAILURE(status)) { + pr_err(PREFIX "getting handle to <%s> of <%s> failed, there is no method related to 0x%02X pin\n", + ev_name, parent_data->name, pin); + goto fail; + } + + pin_data->handle = evt_handle; + pin_data->pin = pin; + list_add_tail(&pin_data->list, &pin_data_list); + + parent_data->evt_count++; + + return AE_OK; +fail: + kfree(pin_data); + return status; +} + +static acpi_status gpio_find_resource(acpi_handle handle, u32 lvl, + void *context, void **rv) +{ + struct acpi_namespace_node *node; + struct dentry *gpio_debugfs_dir; + struct gpio_pin_parent_data parent_data; + char gpio_name[5]; + + node = ACPI_CAST_PTR(struct acpi_namespace_node, handle); + + snprintf(gpio_name, 5, "%s", node->name.ascii); + gpio_debugfs_dir = debugfs_create_dir(gpio_name, acpi_evt_debugfs_dir); + if (gpio_debugfs_dir == NULL) + return AE_OK; + + parent_data.debugfs_dir = gpio_debugfs_dir; + parent_data.handle = handle; + parent_data.name = gpio_name; + parent_data.evt_count = 0; + + acpi_walk_resources(handle, METHOD_NAME__AEI, gpio_list_resource, + &parent_data); + + if (!parent_data.evt_count) + debugfs_remove(gpio_debugfs_dir); + + return AE_OK; +} + +static int __init gpio_evt_emu_init(void) +{ + if (acpi_debugfs_dir == NULL) + return -ENOENT; + + acpi_evt_debugfs_dir = debugfs_create_dir("events", acpi_debugfs_dir); + if (acpi_evt_debugfs_dir == NULL) + return -ENOENT; + + acpi_get_devices(NULL, gpio_find_resource, NULL, NULL); + + return 0; +} +module_init(gpio_evt_emu_init); + +static void __exit gpio_evt_emu_exit(void) +{ + struct gpio_pin_data *pin_data, *temp; + + debugfs_remove_recursive(acpi_evt_debugfs_dir); + + list_for_each_entry_safe(pin_data, temp, &pin_data_list, list) + kfree(pin_data); +} +module_exit(gpio_evt_emu_exit); + +MODULE_DESCRIPTION("GPIO-signaled events emulator"); +MODULE_LICENSE("GPL v2"); |