aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGraeme Gregory <graeme.gregory@linaro.org>2013-12-12 16:14:06 +0000
committerGraeme Gregory <graeme.gregory@linaro.org>2014-06-03 09:24:41 +0100
commit9ed4012cc9784bbe27cfa23d445449d1b0fbb550 (patch)
tree4541cc869110de1f6898a6f2ece6b60c5cd42a2c
parent337a30ff28ac7a3e9c9b11785e65479daa3b5d4f (diff)
downloadleg-kernel-9ed4012cc9784bbe27cfa23d445449d1b0fbb550.tar.gz
acpi: add utility acpi_keyvalue to retrieve from _DSM
Adapt code written for AMBA acpi driver to generic case for fetching key/value pairs from _DSM method. Signed-off-by: Brandon Anderson <brandon.anderson@amd.com> Signed-off-by: Graeme Gregory <graeme.gregory@linaro.org> Acked-by: Tomasz Nowicki <tomasz.nowicki@linaro.org>
-rw-r--r--drivers/acpi/Makefile1
-rw-r--r--drivers/acpi/acpi_keyvalue.c115
-rw-r--r--include/linux/acpi.h8
3 files changed, 124 insertions, 0 deletions
diff --git a/drivers/acpi/Makefile b/drivers/acpi/Makefile
index 3433c9e9ca75..8f584067d0ad 100644
--- a/drivers/acpi/Makefile
+++ b/drivers/acpi/Makefile
@@ -15,6 +15,7 @@ obj-$(CONFIG_X86) += blacklist.o
# ACPI Core Subsystem (Interpreter)
#
obj-y += acpi.o \
+ acpi_keyvalue.o \
acpica/
# All the builtin files are in the "acpi." module_param namespace.
diff --git a/drivers/acpi/acpi_keyvalue.c b/drivers/acpi/acpi_keyvalue.c
new file mode 100644
index 000000000000..5c3cde921b22
--- /dev/null
+++ b/drivers/acpi/acpi_keyvalue.c
@@ -0,0 +1,115 @@
+/*
+ * Key/Value handler from _DSM method
+ *
+ * Copyright (C) 2013 Linaro Ltd
+ *
+ * Author: Graeme Gregory <graeme.gregory@linaro.org>
+ *
+ * Original based on code :-
+ *
+ * Copyright (C) 2013 Advanced Micro Devices, Inc.
+ *
+ * Author: Brandon Anderson <brandon.anderson@amd.com>
+ *
+ * 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.
+ */
+
+#include <linux/acpi.h>
+
+/* UUID: a706b112-bf0b-48d2-9fa3-95591a3c4c06 (randomly generated) */
+static const char acpi_amba_dsm_uuid[] = {
+ 0xa7, 0x06, 0xb1, 0x12, 0xbf, 0x0b, 0x48, 0xd2,
+ 0x9f, 0xa3, 0x95, 0x59, 0x1a, 0x3c, 0x4c, 0x06
+};
+
+/* acpi_dsm_lookup_value()
+ *
+ * Helper to parse through ACPI _DSM object for a device. Each entry
+ * has three fields.
+ */
+int acpi_dsm_lookup_value(acpi_handle handle,
+ const char *tag, int index,
+ struct acpi_dsm_entry *entry)
+{
+ acpi_status status;
+ struct acpi_object_list input;
+ struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
+ union acpi_object params[4];
+ union acpi_object *obj;
+ int len, match_count, i;
+
+ /* invalidate output in case there's no entry to supply */
+ entry->key = NULL;
+ entry->value = NULL;
+
+ if (!acpi_has_method(handle, "_DSM"))
+ return -ENOENT;
+
+ input.count = 4;
+ params[0].type = ACPI_TYPE_BUFFER; /* UUID */
+ params[0].buffer.length = sizeof(acpi_amba_dsm_uuid);
+ params[0].buffer.pointer = (char *)acpi_amba_dsm_uuid;
+ params[1].type = ACPI_TYPE_INTEGER; /* Revision */
+ params[1].integer.value = 1;
+ params[2].type = ACPI_TYPE_INTEGER; /* Function # */
+ params[2].integer.value = 1;
+ params[3].type = ACPI_TYPE_PACKAGE; /* Arguments */
+ params[3].package.count = 0;
+ params[3].package.elements = NULL;
+ input.pointer = params;
+
+ status = acpi_evaluate_object_typed(handle, "_DSM",
+ &input, &output, ACPI_TYPE_PACKAGE);
+ if (ACPI_FAILURE(status)) {
+ pr_err("failed to get _DSM package for this device\n");
+ return -ENOENT;
+ }
+
+ obj = (union acpi_object *)output.pointer;
+
+ /* parse 2 objects per entry */
+ match_count = 0;
+ for (i = 0; (i + 2) <= obj->package.count; i += 2) {
+ /* key must be a string */
+ len = obj->package.elements[i].string.length;
+ if (len <= 0)
+ continue;
+
+ /* check to see if this is the entry to return */
+ if (strncmp(tag, obj->package.elements[i].string.pointer,
+ len) != 0 ||
+ match_count < index) {
+ match_count++;
+ continue;
+ }
+
+ /* copy the key */
+ entry->key = kmalloc(len + 1, GFP_KERNEL);
+ strncpy(entry->key,
+ obj->package.elements[i].string.pointer,
+ len);
+ entry->key[len] = '\0';
+
+ /* value is a string with space-delimited fields if necessary */
+ len = obj->package.elements[i + 1].string.length;
+ if (len > 0) {
+ entry->value = kmalloc(len + 1, GFP_KERNEL);
+ strncpy(entry->value,
+ obj->package.elements[i+1].string.pointer,
+ len);
+ entry->value[len] = '\0';
+ }
+
+ break;
+ }
+
+ kfree(output.pointer);
+
+ if (entry->key == NULL)
+ return -ENOENT;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(acpi_dsm_lookup_value);
diff --git a/include/linux/acpi.h b/include/linux/acpi.h
index 651abc4cde29..ca3290d22b78 100644
--- a/include/linux/acpi.h
+++ b/include/linux/acpi.h
@@ -446,6 +446,14 @@ static inline bool acpi_driver_match_device(struct device *dev,
int acpi_device_uevent_modalias(struct device *, struct kobj_uevent_env *);
int acpi_device_modalias(struct device *, char *, int);
+struct acpi_dsm_entry {
+ char *key;
+ char *value;
+};
+
+int acpi_dsm_lookup_value(acpi_handle handle,
+ const char *tag, int index,
+ struct acpi_dsm_entry *entry);
#define ACPI_PTR(_ptr) (_ptr)