I took the _DSM lookup code from Brandon and moved it to generic acpi drivers directory so it can be accessed from any driver.
I then implemented a fixed-regulator in ACPI using the _DSM lookups for the regulator parameters.
This is obviously all prototype stuff until upstream/UEFI guys make a decision on _DSM vs _PRP and also unified key/value fetching.
ASL patch will follow.
Graeme
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 --- drivers/acpi/Makefile | 1 + drivers/acpi/acpi_keyvalue.c | 115 +++++++++++++++++++++++++++++++++++++++++++ include/linux/acpi.h | 9 ++++ 3 files changed, 125 insertions(+) create mode 100644 drivers/acpi/acpi_keyvalue.c
diff --git a/drivers/acpi/Makefile b/drivers/acpi/Makefile index 262f0be..171c15f 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 0000000..5c3cde9 --- /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 6324f8b..dad1530 100644 --- a/include/linux/acpi.h +++ b/include/linux/acpi.h @@ -402,6 +402,15 @@ static inline bool acpi_driver_match_device(struct device *dev, return !!acpi_match_device(drv->acpi_match_table, dev); }
+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)
#else /* !CONFIG_ACPI */
Signed-off-by: Graeme Gregory graeme.gregory@linaro.org --- drivers/regulator/fixed.c | 104 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 104 insertions(+)
diff --git a/drivers/regulator/fixed.c b/drivers/regulator/fixed.c index 7610920..8ae94b0 100644 --- a/drivers/regulator/fixed.c +++ b/drivers/regulator/fixed.c @@ -28,6 +28,7 @@ #include <linux/slab.h> #include <linux/of.h> #include <linux/of_gpio.h> +#include <linux/acpi.h> #include <linux/regulator/of_regulator.h> #include <linux/regulator/machine.h>
@@ -108,6 +109,97 @@ of_get_fixed_voltage_config(struct device *dev) return config; }
+#if defined(CONFIG_ACPI) +static struct fixed_voltage_config * +acpi_get_fixed_voltage_config(struct device *dev) +{ + struct fixed_voltage_config *config; + struct regulator_init_data *init_data; + struct acpi_dsm_entry entry; + int len, always_on; + + config = devm_kzalloc(dev, sizeof(struct fixed_voltage_config), + GFP_KERNEL); + if (!config) + return ERR_PTR(-ENOMEM); + + config->init_data = devm_kzalloc(dev, + sizeof(struct regulator_init_data), + GFP_KERNEL); + if (!config->init_data) + return ERR_PTR(-ENOMEM); + + init_data = config->init_data; + init_data->constraints.apply_uV = 0; + + if (acpi_dsm_lookup_value(ACPI_HANDLE(dev), + "regulator-name", 0, &entry) == 0) { + if (!entry.value) { + dev_err(dev, "invalid 'name' in ACPI definition\n"); + return ERR_PTR(-EINVAL); + } + + len = strlen(entry.value) + 1; + init_data->constraints.name = devm_kzalloc(dev, len, + GFP_KERNEL); + strncpy((char *)init_data->constraints.name, entry.value, len); + kfree(entry.key); + kfree(entry.value); + } + + if (acpi_dsm_lookup_value(ACPI_HANDLE(dev), "regulator-min-microvolts", + 0, &entry) == 0) { + if (kstrtoint(entry.value, 0, &init_data->constraints.min_uV) + != 0) { + dev_err(dev, "invalid 'min_uV' in ACPI definition\n"); + return ERR_PTR(-EINVAL); + } + kfree(entry.key); + kfree(entry.value); + } + + if (acpi_dsm_lookup_value(ACPI_HANDLE(dev), "regulator-max-microvolts", + 0, &entry) == 0) { + if (kstrtoint(entry.value, 0, &init_data->constraints.max_uV) + != 0) { + dev_err(dev, "invalid 'max_uV' in ACPI definition\n"); + return ERR_PTR(-EINVAL); + } + kfree(entry.key); + kfree(entry.value); + } + + if (acpi_dsm_lookup_value(ACPI_HANDLE(dev), "regulator-always-on", + 0, &entry) == 0) { + if (kstrtoint(entry.value, 0, &always_on) != 0) { + dev_err(dev, "invalid 'max_uV' in ACPI definition\n"); + return ERR_PTR(-EINVAL); + } else { + init_data->constraints.always_on = always_on; + } + kfree(entry.key); + kfree(entry.value); + } + + config->supply_name = init_data->constraints.name; + if (init_data->constraints.min_uV == init_data->constraints.max_uV) { + config->microvolts = init_data->constraints.min_uV; + } else { + dev_err(dev, + "Fixed regulator specified with variable voltages\n"); + return ERR_PTR(-EINVAL); + } + + return config; +} +#else +static struct fixed_voltage_config * +acpi_get_fixed_voltage_config(struct device *dev) +{ + return ERR_PTR(-EINVAL); +} +#endif + static int fixed_voltage_get_voltage(struct regulator_dev *dev) { struct fixed_voltage_data *data = rdev_get_drvdata(dev); @@ -145,6 +237,10 @@ static int reg_fixed_voltage_probe(struct platform_device *pdev) config = of_get_fixed_voltage_config(&pdev->dev); if (IS_ERR(config)) return PTR_ERR(config); + } else if (ACPI_HANDLE(&pdev->dev)) { + config = acpi_get_fixed_voltage_config(&pdev->dev); + if (IS_ERR(config)) + return PTR_ERR(config); } else { config = dev_get_platdata(&pdev->dev); } @@ -253,6 +349,13 @@ static const struct of_device_id fixed_of_match[] = { MODULE_DEVICE_TABLE(of, fixed_of_match); #endif
+#if defined(CONFIG_ACPI) +static const struct acpi_device_id fixed_acpi_match[] = { + { .id = "LNRO0019", }, + {}, +}; +#endif + static struct platform_driver regulator_fixed_voltage_driver = { .probe = reg_fixed_voltage_probe, .remove = reg_fixed_voltage_remove, @@ -260,6 +363,7 @@ static struct platform_driver regulator_fixed_voltage_driver = { .name = "reg-fixed-voltage", .owner = THIS_MODULE, .of_match_table = of_match_ptr(fixed_of_match), + .acpi_match_table = ACPI_PTR(fixed_acpi_match), }, };
Signed-off-by: Graeme Gregory graeme.gregory@linaro.org --- drivers/acpi/acpi_platform.c | 1 + 1 file changed, 1 insertion(+)
diff --git a/drivers/acpi/acpi_platform.c b/drivers/acpi/acpi_platform.c index 094d7e7..c574895 100644 --- a/drivers/acpi/acpi_platform.c +++ b/drivers/acpi/acpi_platform.c @@ -37,6 +37,7 @@ static const struct acpi_device_id acpi_platform_device_ids[] = { { "LNRO0008" }, /* Fixed clock */ { "LNRO0009" }, /* vexpress-sysreg */ { "LNRO000A" }, /* uart-pl011 */ + { "LNRO0019" }, /* fixed regulator */
{ "AMBA0000" },
Acked-by: Tomasz Nowicki tomasz.nowicki@linaro.org
On 12/12/13 17:14, Graeme Gregory wrote:
I took the _DSM lookup code from Brandon and moved it to generic acpi drivers directory so it can be accessed from any driver.
I then implemented a fixed-regulator in ACPI using the _DSM lookups for the regulator parameters.
This is obviously all prototype stuff until upstream/UEFI guys make a decision on _DSM vs _PRP and also unified key/value fetching.
ASL patch will follow.
Graeme
Linaro-acpi mailing list Linaro-acpi@lists.linaro.org http://lists.linaro.org/mailman/listinfo/linaro-acpi