summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRafael J. Wysocki <rafael.j.wysocki@intel.com>2014-11-03 23:39:41 +0100
committerRafael J. Wysocki <rafael.j.wysocki@intel.com>2014-11-04 21:58:24 +0100
commitf028d5242d7ecb0a1bfc80e7bd292201c8612641 (patch)
tree269609af6e6448736a8e831eb8740c6d60947f29
parentinput: gpio_keys_polled: Make use of device property API (diff)
downloadlinux-f028d5242d7ecb0a1bfc80e7bd292201c8612641.tar.xz
linux-f028d5242d7ecb0a1bfc80e7bd292201c8612641.zip
ACPI / GPIO: Driver GPIO mappings for ACPI GPIOs
Provide a way for device drivers using GPIOs described by ACPI GpioIo resources in _CRS to tell the GPIO subsystem what names (connection IDs) to associate with specific GPIO pins defined in there. To do that, a driver needs to define a mapping table as a NULL-terminated array of struct acpi_gpio_mapping objects that each contain a name, a pointer to an array of line data (struct acpi_gpio_params) objects and the size of that array. Each struct acpi_gpio_params object consists of three fields, crs_entry_index, line_index, active_low, representing the index of the target GpioIo()/GpioInt() resource in _CRS starting from zero, the index of the target line in that resource starting from zero, and the active-low flag for that line, respectively. Next, the mapping table needs to be passed as the second argument to acpi_dev_add_driver_gpios() that will register it with the ACPI device object pointed to by its first argument. That should be done in the driver's .probe() routine. On removal, the driver should unregister its GPIO mapping table by calling acpi_dev_remove_driver_gpios() on the ACPI device object where that table was previously registered. Included are fixes from Mika Westerberg. Acked-by: Alexandre Courbot <acourbot@nvidia.com> Reviewed-by: Linus Walleij <linus.walleij@linaro.org> Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
-rw-r--r--drivers/gpio/gpiolib-acpi.c43
-rw-r--r--include/acpi/acpi_bus.h3
-rw-r--r--include/linux/acpi.h30
3 files changed, 74 insertions, 2 deletions
diff --git a/drivers/gpio/gpiolib-acpi.c b/drivers/gpio/gpiolib-acpi.c
index 8aa6ca473748..5a4d061e787e 100644
--- a/drivers/gpio/gpiolib-acpi.c
+++ b/drivers/gpio/gpiolib-acpi.c
@@ -287,6 +287,41 @@ void acpi_gpiochip_free_interrupts(struct gpio_chip *chip)
}
}
+int acpi_dev_add_driver_gpios(struct acpi_device *adev,
+ const struct acpi_gpio_mapping *gpios)
+{
+ if (adev && gpios) {
+ adev->driver_gpios = gpios;
+ return 0;
+ }
+ return -EINVAL;
+}
+EXPORT_SYMBOL_GPL(acpi_dev_add_driver_gpios);
+
+static bool acpi_get_driver_gpio_data(struct acpi_device *adev,
+ const char *name, int index,
+ struct acpi_reference_args *args)
+{
+ const struct acpi_gpio_mapping *gm;
+
+ if (!adev->driver_gpios)
+ return false;
+
+ for (gm = adev->driver_gpios; gm->name; gm++)
+ if (!strcmp(name, gm->name) && gm->data && index < gm->size) {
+ const struct acpi_gpio_params *par = gm->data + index;
+
+ args->adev = adev;
+ args->args[0] = par->crs_entry_index;
+ args->args[1] = par->line_index;
+ args->args[2] = par->active_low;
+ args->nargs = 3;
+ return true;
+ }
+
+ return false;
+}
+
struct acpi_gpio_lookup {
struct acpi_gpio_info info;
int index;
@@ -372,8 +407,12 @@ struct gpio_desc *acpi_get_gpiod_by_index(struct acpi_device *adev,
memset(&args, 0, sizeof(args));
ret = acpi_dev_get_property_reference(adev, propname, NULL,
index, &args);
- if (ret)
- return ERR_PTR(ret);
+ if (ret) {
+ bool found = acpi_get_driver_gpio_data(adev, propname,
+ index, &args);
+ if (!found)
+ return ERR_PTR(ret);
+ }
/*
* The property was found and resolved so need to
diff --git a/include/acpi/acpi_bus.h b/include/acpi/acpi_bus.h
index a361f43b1974..7d1ce40e201e 100644
--- a/include/acpi/acpi_bus.h
+++ b/include/acpi/acpi_bus.h
@@ -345,6 +345,8 @@ struct acpi_device_data {
const union acpi_object *of_compatible;
};
+struct acpi_gpio_mapping;
+
/* Device */
struct acpi_device {
int device_type;
@@ -366,6 +368,7 @@ struct acpi_device {
struct acpi_scan_handler *handler;
struct acpi_hotplug_context *hp;
struct acpi_driver *driver;
+ const struct acpi_gpio_mapping *driver_gpios;
void *driver_data;
struct device dev;
unsigned int physical_node_count;
diff --git a/include/linux/acpi.h b/include/linux/acpi.h
index 5b8802216a93..0902426c4521 100644
--- a/include/linux/acpi.h
+++ b/include/linux/acpi.h
@@ -673,6 +673,36 @@ do { \
#endif
#endif
+struct acpi_gpio_params {
+ unsigned int crs_entry_index;
+ unsigned int line_index;
+ bool active_low;
+};
+
+struct acpi_gpio_mapping {
+ const char *name;
+ const struct acpi_gpio_params *data;
+ unsigned int size;
+};
+
+#if defined(CONFIG_ACPI) && defined(CONFIG_GPIOLIB)
+int acpi_dev_add_driver_gpios(struct acpi_device *adev,
+ const struct acpi_gpio_mapping *gpios);
+
+static inline void acpi_dev_remove_driver_gpios(struct acpi_device *adev)
+{
+ if (adev)
+ adev->driver_gpios = NULL;
+}
+#else
+static inline int acpi_dev_add_driver_gpios(struct acpi_device *adev,
+ const struct acpi_gpio_mapping *gpios)
+{
+ return -ENXIO;
+}
+static inline void acpi_dev_remove_driver_gpios(struct acpi_device *adev) {}
+#endif
+
/* Device properties */
#define MAX_ACPI_REFERENCE_ARGS 8