summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-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");