summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTomasz Nowicki <tomasz.nowicki@linaro.org>2014-09-03 11:40:59 +0200
committerTomasz Nowicki <tomasz.nowicki@linaro.org>2014-09-03 15:15:01 +0200
commit0267e902b48f7b89edba2206d3f26d05ab813ede (patch)
treeaecf97145859bdc67a6ba53d914e708de3405449
parentaf36e3276b9ef58c4adaaf2b89e76027560eb3c1 (diff)
ACPI: Add GPIO-signaled event emulator.
GPIO-signaled events is quite new thing in Linux kernel. There are not many board which can take advantage of it. However, GPIO events are very useful feature during work on ACPI subsystems. This commit emulates GPIO h/w behaviour and consists on write operation to debugfs file. GPIO device instance is still required in DSDT table along with _AEI resources and event methods. Please, see Kconfig help and driver head section for more details regarding tool usage. Signed-off-by: Tomasz Nowicki <tomasz.nowicki@linaro.org>
-rw-r--r--drivers/acpi/Kconfig10
-rw-r--r--drivers/acpi/Makefile1
-rw-r--r--drivers/acpi/gpio-acpi-evt-emu.c192
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");