From ebac1dd4613e0d595b1f00327f5a3cc12f7db881 Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Thu, 17 Oct 2013 18:13:59 +0300 Subject: ACPI: Add support for device specific properties Device Tree is used in many embedded systems to describe the system configuration to the OS. It supports attaching properties or name-value pairs to the devices it describe. With these properties one can pass additional information to the drivers that would not be available otherwise. ACPI is another configuration mechanism (among other things) typically seen on but not limited to x86 machines. ACPI allows passing arbitrary data from methods but there is nothing like Device Tree properties in the ACPI specification. In order to facilitate ACPI usage in systems where Device Tree is typically used, it would be beneficial to standardize a way to retrieve Device Tree style properties from ACPI devices, which is what we do in this patch. If a given device described in ACPI namespace wants to export properties it must implement _PRP method that returns the properties in a package of packages. For example: Method (_PRP, 0, NotSerialized) { Return (Package () { Package () { "name1", }, Package () { "name2", }, ... }) } Name must be string but there are no limitations for the value itself. We add several helper functions that can be used to extract these properties and convert them to different Linux data types. The ultimate goal is that we only have one device property API that retrieves the requested properties from Device Tree or from ACPI transparent to the caller. Signed-off-by: Darren Hart Signed-off-by: Mika Westerberg --- drivers/acpi/Makefile | 1 + drivers/acpi/internal.h | 6 + drivers/acpi/property.c | 512 ++++++++++++++++++++++++++++++++++++++++++++++++ drivers/acpi/scan.c | 2 + 4 files changed, 521 insertions(+) create mode 100644 drivers/acpi/property.c (limited to 'drivers') diff --git a/drivers/acpi/Makefile b/drivers/acpi/Makefile index 8e9bbe6b03c0..d030fca22d05 100644 --- a/drivers/acpi/Makefile +++ b/drivers/acpi/Makefile @@ -46,6 +46,7 @@ acpi-y += acpi_pnp.o acpi-y += power.o acpi-y += event.o acpi-y += sysfs.o +acpi-y += property.o acpi-$(CONFIG_X86) += acpi_cmos_rtc.o acpi-$(CONFIG_DEBUG_FS) += debugfs.o acpi-$(CONFIG_ACPI_NUMA) += numa.o diff --git a/drivers/acpi/internal.h b/drivers/acpi/internal.h index 9a1822803479..4b9a3341bd64 100644 --- a/drivers/acpi/internal.h +++ b/drivers/acpi/internal.h @@ -188,4 +188,10 @@ struct platform_device *acpi_create_platform_device(struct acpi_device *adev); bool acpi_osi_is_win8(void); #endif +/*-------------------------------------------------------------------------- + Device properties + -------------------------------------------------------------------------- */ +void acpi_init_properties(struct acpi_device *adev); +void acpi_free_properties(struct acpi_device *adev); + #endif /* _ACPI_INTERNAL_H_ */ diff --git a/drivers/acpi/property.c b/drivers/acpi/property.c new file mode 100644 index 000000000000..489644c6c136 --- /dev/null +++ b/drivers/acpi/property.c @@ -0,0 +1,512 @@ +/* + * Device Tree style properties from ACPI devices. + * + * Copyright (C) 2013, Intel Corporation + * Authors: Mika Westerberg + * Darren Hart + * + * 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 +#include +#include + +#include "internal.h" + +struct acpi_dev_property_lookup { + const char *name; + acpi_object_type type; + const union acpi_object *obj; +}; + +void acpi_init_properties(struct acpi_device *adev) +{ + struct acpi_buffer buf = { ACPI_ALLOCATE_BUFFER }; + + if (ACPI_SUCCESS(acpi_get_properties(adev->handle, &buf))) + adev->properties = buf.pointer; +} + +void acpi_free_properties(struct acpi_device *adev) +{ + ACPI_FREE(adev->properties); + adev->properties = NULL; +} + +/** + * acpi_dev_get_properties - get properties from a device + * @adev: device to get properties from + * @callback: callback that is called for each found property + * @data: data passed to @callback + * + * Function goes over device properties and for each property @callback is + * called. If @callback returns non-zero the iteration is terminated and + * that return value is returned from this function. + */ +int acpi_dev_get_properties(struct acpi_device *adev, + int (*callback)(const union acpi_object *, void *), + void *data) +{ + const union acpi_object *property; + int i, ret; + + if (!adev) + return -EINVAL; + if (!adev->properties) + return -ENODATA; + + for (i = 0; i < adev->properties->package.count; i++) { + property = &adev->properties->package.elements[i]; + ret = callback(property, data); + if (ret) + return ret; + } + + return 0; +} +EXPORT_SYMBOL_GPL(acpi_dev_get_properties); + +/* + * Returns 0 if the property didn̈́'t match, 1 if it did and -EINVAL if the + * value found is not of expected type. + */ +static int acpi_dev_find_property(const union acpi_object *pkg, void *data) +{ + const union acpi_object *obj, *name = &pkg->package.elements[0]; + struct acpi_dev_property_lookup *lookup = data; + + if (strcmp(lookup->name, name->string.pointer)) + return 0; + + obj = pkg->package.count > 1 ? &pkg->package.elements[1] : NULL; + + if (lookup->type == ACPI_TYPE_ANY || + (obj && lookup->type == obj->type)) { + lookup->obj = obj; + return 1; + } + + return -EINVAL; +} + +/** + * acpi_dev_get_property - return an ACPI property with given name + * @adev: ACPI device to get property + * @name: name of the property + * @type: expected type or %ACPI_TYPE_ANY if caller doesn't care + * @obj: property value is placed here if not %NULL + * + * Function looks up a property with @name and returns the resulting ACPI + * object in @obj if found. The returned object should not be released by + * the caller, it is released automatically by the ACPI core when @adev is + * removed. + */ +int acpi_dev_get_property(struct acpi_device *adev, const char *name, + acpi_object_type type, const union acpi_object **obj) +{ + struct acpi_dev_property_lookup lookup = { + .name = name, + .type = type, + }; + int ret; + + ret = acpi_dev_get_properties(adev, acpi_dev_find_property, &lookup); + if (ret == 1) { + if (obj) + *obj = lookup.obj; + return 0; + } + return ret ? ret : -ENODATA; +} +EXPORT_SYMBOL_GPL(acpi_dev_get_property); + +/** + * acpi_dev_get_property_u64 - find and read 64-bit integer property + * @adev: ACPI device to get property + * @name: name of the property + * @value: value of the property is placed here. + * + * Search for a property with the @name and if find, place the value to + * @value. Returns %0 on success, %-ENODATA if the property is not found, + * and %-EINVAL if the property is not in correct format. + * + * A sample ASL might look like this: + * Package () { "property", 0x0000ffffffff0000 } + */ +int acpi_dev_get_property_u64(struct acpi_device *adev, const char *name, + u64 *value) +{ + const union acpi_object *obj; + int ret; + + ret = acpi_dev_get_property(adev, name, ACPI_TYPE_INTEGER, &obj); + if (!ret) + *value = (u64)obj->integer.value; + return ret; +} +EXPORT_SYMBOL_GPL(acpi_dev_get_property_u64); + +/** + * acpi_dev_get_property_u32 - find and read 32-bit integer property + * @adev: ACPI device to get property + * @name: name of the property + * @value: value of the property is placed here. + * + * Search for a property with the @name and if find, place the value to + * @value. Returns %0 on success, %-ENODATA if the property is not found, + * and %-EINVAL if the property is not in correct format. + * + * A sample ASL might look like this: + * Package () { "property", 0x0ffffff0 } + */ +int acpi_dev_get_property_u32(struct acpi_device *adev, const char *name, + u32 *value) +{ + u64 tmp; + int ret; + + ret = acpi_dev_get_property_u64(adev, name, &tmp); + if (!ret) + *value = tmp; + return ret; +} +EXPORT_SYMBOL_GPL(acpi_dev_get_property_u32); + +/** + * acpi_dev_get_property_u16 - find and read 16-bit integer property + * @adev: ACPI device to get property + * @name: name of the property + * @value: value of the property is placed here. + * + * Search for a property with the @name and if find, place the value to + * @value. Returns %0 on success, %-ENODATA if the property is not found, + * and %-EINVAL if the property is not in correct format. + * + * A sample ASL might look like this: + * Package () { "property", 0x0ff0 } + */ +int acpi_dev_get_property_u16(struct acpi_device *adev, const char *name, + u16 *value) +{ + u64 tmp; + int ret; + + ret = acpi_dev_get_property_u64(adev, name, &tmp); + if (!ret) + *value = tmp; + return ret; +} +EXPORT_SYMBOL_GPL(acpi_dev_get_property_u16); + +/** + * acpi_dev_get_property_u8 - find and read 8-bit integer property + * @adev: ACPI device to get property + * @name: name of the property + * @value: value of the property is placed here. + * + * Search for a property with the @name and if find, place the value to + * @value. Returns %0 on success, %-ENODATA if the property is not found, + * and %-EINVAL if the property is not in correct format. + * + * A sample ASL might look like this: + * Package () { "property", 0x3c } + */ +int acpi_dev_get_property_u8(struct acpi_device *adev, const char *name, + u8 *value) +{ + u64 tmp; + int ret; + + ret = acpi_dev_get_property_u64(adev, name, &tmp); + if (!ret) + *value = tmp; + return ret; +} +EXPORT_SYMBOL_GPL(acpi_dev_get_property_u8); + +static int acpi_dev_get_property_array(struct acpi_device *adev, + const char *name, acpi_object_type type, + const union acpi_object **ret_obj) +{ + const union acpi_object *obj; + int ret, i; + + ret = acpi_dev_get_property(adev, name, ACPI_TYPE_PACKAGE, &obj); + if (ret) + return ret; + + /* Check that all elements are of correct type */ + for (i = 0; i < obj->package.count; i++) + if (obj->package.elements[i].type != type) + return -EINVAL; + + *ret_obj = obj; + return 0; +} + +/** + * acpi_dev_get_property_array_u64 - find and read array of u64 from a property + * @adev: ACPI device to get property + * @name: name of the property + * @values: array where the data is placed + * @nvalues: number of elements in @values array + * + * Copies integer properties array with @name into @values and returns + * number of items in the actual array or %-ENODATA if the property doesn't + * exists, %-EINVAL if the array format is invalid. @values and @nvalues + * can be set to %NULL and %0 respectively. In that case the function + * returns number of items in the array but doesn't touch @values. + * + * A sample ASL might look like this: + * Package () { "property", Package () { 1, 2, ... } } + */ +int acpi_dev_get_property_array_u64(struct acpi_device *adev, const char *name, + u64 *values, size_t nvalues) +{ + const union acpi_object *obj; + int ret; + + ret = acpi_dev_get_property_array(adev, name, ACPI_TYPE_INTEGER, &obj); + if (ret) + return ret; + + if (values) { + int i; + + for (i = 0; i < obj->package.count && i < nvalues; i++) + values[i] = obj->package.elements[i].integer.value; + } + + return obj->package.count; +} +EXPORT_SYMBOL_GPL(acpi_dev_get_property_array_u64); + +/** + * acpi_dev_get_property_array_u32 - find and read array of u32 from a property + * @adev: ACPI device to get property + * @name: name of the property + * @values: array where the data is placed + * @nvalues: number of elements in @values array + * + * Copies integer properties array with @name into @values and returns + * number of items in the actual array or %-ENODATA if the property doesn't + * exists, %-EINVAL if the array format is invalid. @values and @nvalues + * can be set to %NULL and %0 respectively. In that case the function + * returns number of items in the array but doesn't touch @values. + * + * A sample ASL might look like this: + * Package () { "property", Package () { 1, 2, ... } } + */ +int acpi_dev_get_property_array_u32(struct acpi_device *adev, const char *name, + u32 *values, size_t nvalues) +{ + const union acpi_object *obj; + int ret; + + ret = acpi_dev_get_property_array(adev, name, ACPI_TYPE_INTEGER, &obj); + if (ret) + return ret; + + if (values) { + int i; + + for (i = 0; i < obj->package.count && i < nvalues; i++) + values[i] = obj->package.elements[i].integer.value; + } + + return obj->package.count; +} +EXPORT_SYMBOL_GPL(acpi_dev_get_property_array_u32); + +/** + * acpi_dev_get_property_array_u16 - find and read array of u16 from a property + * @adev: ACPI device to get property + * @name: name of the property + * @values: array where the data is placed + * @nvalues: number of elements in @values array + * + * Copies integer properties array with @name into @values and returns + * number of items in the actual array or %-ENODATA if the property doesn't + * exists, %-EINVAL if the array format is invalid. @values and @nvalues + * can be set to %NULL and %0 respectively. In that case the function + * returns number of items in the array but doesn't touch @values. + * + * A sample ASL might look like this: + * Package () { "property", Package () { 1, 2, ... } } + */ +int acpi_dev_get_property_array_u16(struct acpi_device *adev, const char *name, + u16 *values, size_t nvalues) +{ + const union acpi_object *obj; + int ret; + + ret = acpi_dev_get_property_array(adev, name, ACPI_TYPE_INTEGER, &obj); + if (ret) + return ret; + + if (values) { + int i; + + for (i = 0; i < obj->package.count && i < nvalues; i++) + values[i] = obj->package.elements[i].integer.value; + } + + return obj->package.count; +} +EXPORT_SYMBOL_GPL(acpi_dev_get_property_array_u16); + +/** + * acpi_dev_get_property_array_u8 - find and read array of u8 from a property + * @adev: ACPI device to get property + * @name: name of the property + * @values: array where the data is placed. Caller allocated can be %NULL. + * @nvalues: number of items in @values array + * + * Copies integer properties array with @name into @values and returns + * number of items in the actual array or %-ENODATA if the property doesn't + * exists, %-EINVAL if the array format is invalid. @values and @nvalues + * can be set to %NULL and %0 respectively. In that case the function + * returns number of items in the array but doesn't touch @values. + * + * Function treats ACPI types package and buffer the same. It first looks + * for a package and then falls back to a buffer. + * + * A sample ASL might look like this if package is used: + * Package () { "property", Package () { 1, 2, ... } } + * + * And like this if buffer is used: + * Package () { "property", Buffer () { 1, 2, ... } } + */ +int acpi_dev_get_property_array_u8(struct acpi_device *adev, const char *name, + u8 *values, size_t nvalues) +{ + const union acpi_object *obj; + int ret, i; + + ret = acpi_dev_get_property_array(adev, name, ACPI_TYPE_INTEGER, &obj); + if (!ret) { + if (values) { + const union acpi_object *elements; + + elements = obj->package.elements; + for (i = 0; i < obj->package.count && i < nvalues; i++) + values[i] = elements[i].integer.value; + } + return obj->package.count; + } + + ret = acpi_dev_get_property(adev, name, ACPI_TYPE_BUFFER, &obj); + if (ret) + return ret; + + if (values) { + for (i = 0; i < obj->buffer.length && i < nvalues; i++) + values[i] = obj->buffer.pointer[i]; + } + + return obj->buffer.length; +} +EXPORT_SYMBOL_GPL(acpi_dev_get_property_array_u8); + +/** + * acpi_dev_get_property_string - returns string property value + * @adev: ACPI device to get property + * @name: name of the property + * @value: pointer to the returned string + * + * Finds property with @name, and places pointer to the string value to + * @value. The memory pointed by @value should not be released by the + * called but it will be released when the corresponding ACPI device object + * is removed. + * + * A sample ASL might look like this: + * Package () { "property", "my string property value" } + */ +int acpi_dev_get_property_string(struct acpi_device *adev, const char *name, + const char **value) +{ + const union acpi_object *obj; + int ret; + + ret = acpi_dev_get_property(adev, name, ACPI_TYPE_STRING, &obj); + if (!ret) + *value = obj->string.pointer; + return ret; +} +EXPORT_SYMBOL_GPL(acpi_dev_get_property_string); + +/** + * acpi_dev_get_property_array_string - find and read an array of strings + * @adev: ACPI device to get property + * @name: name of the property + * @values: array where strings are placed + * @nvalues: number of items in @values array + * + * Finds property with @name, verifies that it contains an array of strings + * and if so, fills in @values with pointers to those strings. Note that + * the caller shouldn't try to release those pointers. They are owned by + * the ACPI device @adev. + * + * String pointers will remain valid as long as the corresponding ACPI + * device object exists. + * + * A sample ASL might look like this: + * Package () { + * "property", + * Package () { "my first string", "my second string" } + * } + */ +int acpi_dev_get_property_array_string(struct acpi_device *adev, + const char *name, const char **values, + size_t nvalues) +{ + const union acpi_object *obj; + int ret; + + ret = acpi_dev_get_property_array(adev, name, ACPI_TYPE_STRING, &obj); + if (ret) + return ret; + + if (values) { + const union acpi_object *elements = obj->package.elements; + int i; + + for (i = 0; i < obj->package.count && i < nvalues; i++) + values[i] = elements[i].string.pointer; + } + + return obj->package.count; +} +EXPORT_SYMBOL_GPL(acpi_dev_get_property_array_string); + +/** + * acpi_dev_get_property_reference - returns handle to the referenced object + * @adev: ACPI device to get property + * @name: name of the property + * @obj_handle: pointer to acpi_handle where the found ACPI handle is placed + * + * Function finds property with @name, verififies that it is an object + * reference and if so, returns the ACPI handle of the referenced object in + * @obj_handle. Returns %0 in case of success, %-ENODATA if the property + * doesn't exists or doesn't have a value, and %-EINVAL if the property + * value is not a reference. + * + * A sample ASL might look like this: + * Package () { "property", \_SB.PCI0.LPC } + */ +int acpi_dev_get_property_reference(struct acpi_device *adev, const char *name, + acpi_handle *obj_handle) +{ + const union acpi_object *obj; + int ret; + + ret = acpi_dev_get_property(adev, name, ACPI_TYPE_LOCAL_REFERENCE, + &obj); + if (!ret) + *obj_handle = obj->reference.handle; + return ret; +} +EXPORT_SYMBOL_GPL(acpi_dev_get_property_reference); diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index f775fa0d850f..fc97e6123864 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -880,6 +880,7 @@ static void acpi_device_release(struct device *dev) { struct acpi_device *acpi_dev = to_acpi_device(dev); + acpi_free_properties(acpi_dev); acpi_free_pnp_ids(&acpi_dev->pnp); acpi_free_power_resources_lists(acpi_dev); kfree(acpi_dev); @@ -1876,6 +1877,7 @@ void acpi_init_device_object(struct acpi_device *device, acpi_handle handle, acpi_set_device_status(device, sta); acpi_device_get_busid(device); acpi_set_pnp_ids(handle, &device->pnp, type); + acpi_init_properties(device); acpi_bus_get_flags(device); device->flags.match_driver = false; device->flags.initialized = true; -- cgit v1.2.3 From 2027b5a324a96e6e5a10c3e591e6e768b533d568 Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Thu, 17 Oct 2013 18:13:58 +0300 Subject: ACPICA: Add acpi_get_properties() public interface In order to support extracting Device Tree style properties from ACPI devices a new method _PRP was introduced. This method, when present, is expected to return a package containing packages with name-value pairs analogous to their Device Tree counterparts. A sample _PRP method migh look like this: Method (_PRP, 0, NotSerialized) { Return (Package () { Package () { "integer-property", 10 }, Package () { "string-property", "mystring" }, }) } Calling acpi_get_properties() will evaluate the _PRP method, validate the result and return it to the caller if everything went fine. Signed-off-by: Mika Westerberg --- drivers/acpi/acpica/Makefile | 2 + drivers/acpi/acpica/prxface.c | 217 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 219 insertions(+) create mode 100644 drivers/acpi/acpica/prxface.c (limited to 'drivers') diff --git a/drivers/acpi/acpica/Makefile b/drivers/acpi/acpica/Makefile index 8bb43f06e11f..4c2e8c0ed9da 100644 --- a/drivers/acpi/acpica/Makefile +++ b/drivers/acpi/acpica/Makefile @@ -177,3 +177,5 @@ acpi-y += \ acpi-$(ACPI_FUTURE_USAGE) += uttrack.o utcache.o +acpi-y += \ + prxface.o diff --git a/drivers/acpi/acpica/prxface.c b/drivers/acpi/acpica/prxface.c new file mode 100644 index 000000000000..4e52b5143374 --- /dev/null +++ b/drivers/acpi/acpica/prxface.c @@ -0,0 +1,217 @@ +/******************************************************************************* + * + * Module Name: prxface - Public interface to ACPI device properties + * + ******************************************************************************/ + +/* + * Copyright (C) 2013, Intel Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + */ + +#include +#include +#include "accommon.h" +#include "acnamesp.h" + +#define _COMPONENT ACPI_RESOURCES +ACPI_MODULE_NAME("prxface") + +/******************************************************************************* + * + * FUNCTION: acpi_pr_validate_parameters + * + * PARAMETERS: device_handle - Handle to a device + * buffer - Pointer to a data buffer + * return_node - Pointer to where the device node is returned + * + * RETURN: Status + * + * DESCRIPTION: Checks validity of the parameters passed to + * acpi_get_device_properties(). + * + ******************************************************************************/ +static acpi_status +acpi_pr_validate_parameters(acpi_handle device_handle, + struct acpi_buffer *buffer, + struct acpi_namespace_node **return_node) +{ + struct acpi_namespace_node *node; + acpi_status status; + + ACPI_FUNCTION_TRACE(acpi_pr_validate_parameters); + + if (!device_handle) { + return_ACPI_STATUS(AE_BAD_PARAMETER); + } + + node = acpi_ns_validate_handle(device_handle); + if (!node) { + return_ACPI_STATUS(AE_BAD_PARAMETER); + } + + if (node->type != ACPI_TYPE_DEVICE) { + return_ACPI_STATUS(AE_TYPE); + } + + /* Make sure the buffer is valid */ + status = acpi_ut_validate_buffer(buffer); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + + *return_node = node; + return_ACPI_STATUS(AE_OK); +} + +/******************************************************************************* + * + * FUNCTION: acpi_get_prp_method_data + * + * PARAMETERS: node - Device node + * ret_buffer - Pointer to a buffer structure where the + * resulting properties are stored + * + * RETURN: Status + * + * DESCRIPTION: This function is called to evaluate _PRP method for a given + * device. In case of success resulting package is stored in + * ret_buffer. + * + * If the function fails an appropriate status will be returned and + * the contents of the callers buffer is undefined. + * + ******************************************************************************/ +static acpi_status +acpi_pr_get_prp_method_data(struct acpi_namespace_node *node, + struct acpi_buffer *ret_buffer) +{ + union acpi_operand_object *obj_desc; + union acpi_operand_object **properties; + acpi_status status; + acpi_size size; + int i; + + ACPI_FUNCTION_TRACE(acpi_pr_get_prp_method_data); + + status = acpi_ut_evaluate_object(node, METHOD_NAME__PRP, + ACPI_BTYPE_PACKAGE, &obj_desc); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + + /* + * Validate that the returned package is in correct format: + * 1) Each property is a package itself holding { key, value } + * 2) Key is mandatory + * 3) Key must be string. + */ + properties = obj_desc->package.elements; + for (i = 0; i < obj_desc->package.count; i++) { + union acpi_operand_object **property; + + if (properties[i]->common.type != ACPI_TYPE_PACKAGE) { + status = AE_BAD_DATA; + goto out; + } + if (properties[i]->package.count < 1) { + status = AE_BAD_DATA; + goto out; + } + + property = properties[i]->package.elements; + if (property[0]->common.type != ACPI_TYPE_STRING) { + status = AE_BAD_DATA; + goto out; + } + } + + /* Copy the internal buffer to ret_buffer */ + status = acpi_ut_get_object_size(obj_desc, &size); + if (ACPI_FAILURE(status)) { + goto out; + } + + status = acpi_ut_initialize_buffer(ret_buffer, size); + if (ACPI_SUCCESS(status)) { + status = acpi_ut_copy_iobject_to_eobject(obj_desc, ret_buffer); + } + + out: + acpi_ut_remove_reference(obj_desc); + return_ACPI_STATUS(status); +} + +/******************************************************************************* + * + * FUNCTION: acpi_get_properties + * + * PARAMETERS: device_handle - Handle to the device object for the + * device we are querying + * ret_buffer - Pointer to a buffer to receive the + * properties for the device + * + * RETURN: Status + * + * DESCRIPTION: This function is called to get properties for + * a specific device The caller must first acquire a handle for the + * desired device The property data is placed in the buffer pointed + * to by the ret_buffer variable parameter. + * + * If the function fails an appropriate status will be returned + * and the value of ret_buffer is undefined. + * + * This function attempts to execute the _PRP method contained in + * the object indicated by the passed device_handle. + * + ******************************************************************************/ +acpi_status +acpi_get_properties(acpi_handle device_handle, struct acpi_buffer *ret_buffer) +{ + struct acpi_namespace_node *node; + acpi_status status; + + ACPI_FUNCTION_TRACE(acpi_get_device_properties); + + status = acpi_pr_validate_parameters(device_handle, ret_buffer, &node); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + + status = acpi_pr_get_prp_method_data(node, ret_buffer); + return_ACPI_STATUS(status); +} + +ACPI_EXPORT_SYMBOL(acpi_get_properties) -- cgit v1.2.3 From 09e5d5c992b75d67b1cc50371edf3c9781363baa Mon Sep 17 00:00:00 2001 From: Graeme Gregory Date: Wed, 24 Jul 2013 11:29:48 +0100 Subject: net: smc91x: add ACPI probing support. Add device ID LINA0003 for this device and add the match table. As its a platform device it needs no other code and will be probed in by acpi_platform once device ID is added. Signed-off-by: Graeme Gregory --- drivers/net/ethernet/smsc/smc91x.c | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'drivers') diff --git a/drivers/net/ethernet/smsc/smc91x.c b/drivers/net/ethernet/smsc/smc91x.c index bcaa41af1e62..ebad8727b327 100644 --- a/drivers/net/ethernet/smsc/smc91x.c +++ b/drivers/net/ethernet/smsc/smc91x.c @@ -81,6 +81,7 @@ static const char version[] = #include #include #include +#include #include #include @@ -2408,6 +2409,14 @@ static struct dev_pm_ops smc_drv_pm_ops = { .resume = smc_drv_resume, }; +#ifdef CONFIG_ACPI +static const struct acpi_device_id smc91x_acpi_match[] = { + { "LNRO0003", }, + { } +}; +MODULE_DEVICE_TABLE(acpi, smc91x_acpi_match); +#endif + static struct platform_driver smc_driver = { .probe = smc_drv_probe, .remove = smc_drv_remove, @@ -2416,6 +2425,7 @@ static struct platform_driver smc_driver = { .owner = THIS_MODULE, .pm = &smc_drv_pm_ops, .of_match_table = of_match_ptr(smc91x_match), + .acpi_match_table = ACPI_PTR(smc91x_acpi_match), }, }; -- cgit v1.2.3 From d9a54ccfa0c28bf25274f9578af9ca79e4123733 Mon Sep 17 00:00:00 2001 From: Graeme Gregory Date: Fri, 26 Jul 2013 17:55:02 +0100 Subject: virtio-mmio: add ACPI probing Added the match table and pointers for ACPI probing to the driver. Signed-off-by: Graeme Gregory --- drivers/virtio/virtio_mmio.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/virtio/virtio_mmio.c b/drivers/virtio/virtio_mmio.c index c600ccfd6922..10c4775368b7 100644 --- a/drivers/virtio/virtio_mmio.c +++ b/drivers/virtio/virtio_mmio.c @@ -100,8 +100,7 @@ #include #include #include - - +#include /* The alignment to use between consumer and producer parts of vring. * Currently hardcoded to the page size. */ @@ -637,6 +636,14 @@ static struct of_device_id virtio_mmio_match[] = { }; MODULE_DEVICE_TABLE(of, virtio_mmio_match); +#ifdef CONFIG_ACPI +static const struct acpi_device_id virtio_mmio_acpi_match[] = { + { "LNRO0005", }, + { } +}; +MODULE_DEVICE_TABLE(acpi, virtio_mmio_acpi_match); +#endif + static struct platform_driver virtio_mmio_driver = { .probe = virtio_mmio_probe, .remove = virtio_mmio_remove, @@ -644,6 +651,7 @@ static struct platform_driver virtio_mmio_driver = { .name = "virtio-mmio", .owner = THIS_MODULE, .of_match_table = virtio_mmio_match, + .acpi_match_table = ACPI_PTR(virtio_mmio_acpi_match), }, }; -- cgit v1.2.3 From 746a4250addae349383e1f7a94643f757db8e9d4 Mon Sep 17 00:00:00 2001 From: Naresh Bhat Date: Wed, 23 Oct 2013 16:53:31 +0530 Subject: v2m_sysreg: Add ACPI probing for SYSREG Add match table and pointers for ACPI probing into vexpress-sysreg driver. Signed-off-by: Naresh Bhat --- drivers/mfd/vexpress-sysreg.c | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'drivers') diff --git a/drivers/mfd/vexpress-sysreg.c b/drivers/mfd/vexpress-sysreg.c index 9e21e4fc9599..c9b69d84ed5f 100644 --- a/drivers/mfd/vexpress-sysreg.c +++ b/drivers/mfd/vexpress-sysreg.c @@ -22,6 +22,7 @@ #include #include #include +#include #define SYS_ID 0x000 #define SYS_SW 0x004 @@ -255,10 +256,18 @@ static const struct of_device_id vexpress_sysreg_match[] = { {}, }; +#ifdef CONFIG_ACPI +static const struct acpi_device_id vexpress_sysreg_acpi_match[] = { + { "LNRO0009", }, + { } +}; +#endif + static struct platform_driver vexpress_sysreg_driver = { .driver = { .name = "vexpress-sysreg", .of_match_table = vexpress_sysreg_match, + .acpi_match_table = ACPI_PTR(vexpress_sysreg_acpi_match), }, .probe = vexpress_sysreg_probe, }; -- cgit v1.2.3 From 78f2a72212e145161b020034f9a4ef9cb1492ccc Mon Sep 17 00:00:00 2001 From: Naresh Bhat Date: Mon, 9 Dec 2013 16:56:47 +0530 Subject: Driver / clk: add platform driver for fixed clock Add platform driver for fixed clock which makes the initialization of fixed clock visible and for easy understand. TODO: maybe introduce a fixed-clock.c and its corresponding Kconfig is better. Signed-off-by: Hanjun Guo Reviewed-by: Naresh Bhat --- drivers/clk/clk-fixed-rate.c | 37 ++++++++++++++++++++++++++++++++++++- 1 file changed, 36 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/clk/clk-fixed-rate.c b/drivers/clk/clk-fixed-rate.c index 0fc56ab6e844..321d3bbebe62 100644 --- a/drivers/clk/clk-fixed-rate.c +++ b/drivers/clk/clk-fixed-rate.c @@ -15,6 +15,7 @@ #include #include #include +#include /* * DOC: basic fixed-rate clock that cannot gate @@ -133,5 +134,39 @@ void of_fixed_clk_setup(struct device_node *node) of_clk_add_provider(node, of_clk_src_simple_get, clk); } EXPORT_SYMBOL_GPL(of_fixed_clk_setup); -CLK_OF_DECLARE(fixed_clk, "fixed-clock", of_fixed_clk_setup); #endif + +static int fixed_clk_probe(struct platform_device *pdev) +{ + if (pdev->dev.of_node) + of_fixed_clk_setup(pdev->dev.of_node); + else + return -ENODEV; + + return 0; +} + +static const struct of_device_id fixed_clk_match[] = { + { .compatible = "fixed-clock" }, + {} +}; + +static struct platform_driver fixed_clk_driver = { + .driver = { + .name = "fixed-clk", + .owner = THIS_MODULE, + .of_match_table = fixed_clk_match, + }, + .probe = fixed_clk_probe, +}; + +static int __init fixed_clk_init(void) +{ + return platform_driver_register(&fixed_clk_driver); +} + +/** + * fixed clock will used for AMBA bus, UART and etc, so it should be + * initialized early enough. + */ +postcore_initcall(fixed_clk_init); -- cgit v1.2.3 From 9cff32099570a841778f2ff21586ec20b750778e Mon Sep 17 00:00:00 2001 From: Naresh Bhat Date: Mon, 9 Dec 2013 17:07:28 +0530 Subject: ACPI / fixed-clock: Add ACPI driver for fixed clk Add the ACPI driver for fixed clock. Signed-off-by: Hanjun Guo Reviewed-by: Naresh Bhat --- drivers/clk/clk-fixed-rate.c | 41 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) (limited to 'drivers') diff --git a/drivers/clk/clk-fixed-rate.c b/drivers/clk/clk-fixed-rate.c index 321d3bbebe62..fa5cd77ece5b 100644 --- a/drivers/clk/clk-fixed-rate.c +++ b/drivers/clk/clk-fixed-rate.c @@ -16,6 +16,8 @@ #include #include #include +#include +#include /* * DOC: basic fixed-rate clock that cannot gate @@ -136,10 +138,43 @@ void of_fixed_clk_setup(struct device_node *node) EXPORT_SYMBOL_GPL(of_fixed_clk_setup); #endif +#ifdef CONFIG_ACPI +static int fixed_clk_probe_acpi(struct platform_device *pdev) +{ + struct clk *clk = ERR_PTR(-ENODEV); + unsigned long long rate = 0; + acpi_status status; + + /* there is a corresponding FREQ method under fixed clock object */ + status = acpi_evaluate_integer(ACPI_HANDLE(&pdev->dev), "FREQ", + NULL, &rate); + if (ACPI_FAILURE(status)) + return -ENODEV; + + clk = clk_register_fixed_rate(NULL, dev_name(&pdev->dev), NULL, + CLK_IS_ROOT, rate); + if (IS_ERR(clk)) + return -ENODEV; + + /* + * if we don't register the clk here, we can't get the clk + * for AMBA bus when CONFIG_OF=n + */ + return clk_register_clkdev(clk, NULL, dev_name(&pdev->dev)); +} +#else +static inline int fixed_clk_probe_acpi(struct platform_device *pdev) +{ + return -ENODEV; +} +#endif /* CONFIG_ACPI */ + static int fixed_clk_probe(struct platform_device *pdev) { if (pdev->dev.of_node) of_fixed_clk_setup(pdev->dev.of_node); + else if (ACPI_HANDLE(&pdev->dev)) + return fixed_clk_probe_acpi(pdev); else return -ENODEV; @@ -151,11 +186,17 @@ static const struct of_device_id fixed_clk_match[] = { {} }; +static const struct acpi_device_id fixed_clk_acpi_match[] = { + { "LNRO0008", 0 }, + { }, +}; + static struct platform_driver fixed_clk_driver = { .driver = { .name = "fixed-clk", .owner = THIS_MODULE, .of_match_table = fixed_clk_match, + .acpi_match_table = ACPI_PTR(fixed_clk_acpi_match), }, .probe = fixed_clk_probe, }; -- cgit v1.2.3 From ab0dc04a86b5db7c76a74ac32a0ce5e3634b6184 Mon Sep 17 00:00:00 2001 From: Naresh Bhat Date: Mon, 9 Dec 2013 17:18:47 +0530 Subject: ACPI/ARM: Add AMBA bus ACPI module This AMBA bus ACPI module provides a generic handler for compatible devices. It uses the same common code as the device tree method to probe for a hardware ID and match up a driver for each device. Signed-off-by: Brandon Anderson Reviewed-by: Naresh Bhat --- drivers/amba/Makefile | 2 +- drivers/amba/acpi.c | 337 ++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 338 insertions(+), 1 deletion(-) create mode 100644 drivers/amba/acpi.c (limited to 'drivers') diff --git a/drivers/amba/Makefile b/drivers/amba/Makefile index 66e81c2f1e3c..6d088e7ed24b 100644 --- a/drivers/amba/Makefile +++ b/drivers/amba/Makefile @@ -1,2 +1,2 @@ -obj-$(CONFIG_ARM_AMBA) += bus.o +obj-$(CONFIG_ARM_AMBA) += bus.o acpi.o obj-$(CONFIG_TEGRA_AHB) += tegra-ahb.o diff --git a/drivers/amba/acpi.c b/drivers/amba/acpi.c new file mode 100644 index 000000000000..9a97cd8ad433 --- /dev/null +++ b/drivers/amba/acpi.c @@ -0,0 +1,337 @@ +/* + * AMBA Connector Resource for ACPI + * + * Copyright (C) 2013 Advanced Micro Devices, Inc. + * + * Author: Brandon Anderson + * + * 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. + */ + +#ifdef CONFIG_ACPI + +#include +#include +#include +#include +#include +#include + +struct acpi_amba_bus_info { + struct platform_device *pdev; + struct clk *clk; + char *clk_name; +}; + +/* 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_amba_dsm_lookup() + * + * Helper to parse through ACPI _DSM object for a device. Each entry + * has three fields. + */ +int acpi_amba_dsm_lookup(acpi_handle handle, + const char *tag, int index, + struct acpi_amba_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_amba_dsm_lookup); + +static int acpi_amba_add_resource(struct acpi_resource *ares, void *data) +{ + struct amba_device *dev = data; + struct resource r; + int irq_idx; + + switch (ares->type) { + case ACPI_RESOURCE_TYPE_FIXED_MEMORY32: + if (!acpi_dev_resource_memory(ares, &dev->res)) + pr_err("failed to map memory resource\n"); + break; + case ACPI_RESOURCE_TYPE_EXTENDED_IRQ: + for (irq_idx = 0; irq_idx < AMBA_NR_IRQS; irq_idx++) { + if (acpi_dev_resource_interrupt(ares, irq_idx, &r)) + dev->irq[irq_idx] = r.start; + else + break; + } + + break; + case ACPI_RESOURCE_TYPE_END_TAG: + /* ignore the end tag */ + break; + default: + /* log an error, but proceed with driver probe */ + pr_err("unhandled acpi resource type= %d\n", + ares->type); + break; + } + + return 1; /* Tell ACPI core that this resource has been handled */ +} + +static struct clk *acpi_amba_get_clk(acpi_handle handle, int index, + char **clk_name) +{ + struct acpi_amba_dsm_entry entry; + acpi_handle clk_handle; + struct acpi_device *adev, *clk_adev; + char *clk_path; + struct clk *clk = NULL; + int len; + + if (acpi_bus_get_device(handle, &adev)) { + pr_err("cannot get device from handle\n"); + return NULL; + } + + /* key=value format for clocks is: + * "clock-name"="apb_pclk \\_SB.CLK0" + */ + if (acpi_amba_dsm_lookup(handle, "clock-name", index, &entry) != 0) + return NULL; + + /* look under the clock device for the clock that matches the entry */ + *clk_name = NULL; + len = strcspn(entry.value, " "); + if (len > 0 && (len + 1) < strlen(entry.value)) { + clk_path = entry.value + len + 1; + *clk_name = kmalloc(len + 1, GFP_KERNEL); + strncpy(*clk_name, entry.value, len); + (*clk_name)[len] = '\0'; + if (ACPI_FAILURE( + acpi_get_handle(NULL, clk_path, &clk_handle)) == 0 && + acpi_bus_get_device(clk_handle, &clk_adev) == 0) + clk = clk_get_sys(dev_name(&clk_adev->dev), *clk_name); + } else + pr_err("Invalid clock-name value format '%s' for %s\n", + entry.value, dev_name(&adev->dev)); + + kfree(entry.key); + kfree(entry.value); + + return clk; +} + +static void acpi_amba_register_clocks(struct acpi_device *adev, + struct clk *default_clk, const char *default_clk_name) +{ + struct clk *clk; + int i; + char *clk_name; + + if (default_clk) { + /* for amba_get_enable_pclk() ... */ + clk_register_clkdev(default_clk, default_clk_name, + dev_name(&adev->dev)); + /* for devm_clk_get() ... */ + clk_register_clkdev(default_clk, NULL, dev_name(&adev->dev)); + } + + for (i = 0;; i++) { + clk_name = NULL; + clk = acpi_amba_get_clk(ACPI_HANDLE(&adev->dev), i, &clk_name); + if (!clk) + break; + + clk_register_clkdev(clk, clk_name, dev_name(&adev->dev)); + + kfree(clk_name); + } +} + +/* acpi_amba_add_device() + * + * ACPI equivalent to of_amba_device_create() + */ +static acpi_status acpi_amba_add_device(acpi_handle handle, + u32 lvl_not_used, void *data, void **not_used) +{ + struct list_head resource_list; + struct acpi_device *adev; + struct amba_device *dev; + int ret; + struct acpi_amba_bus_info *bus_info = data; + struct platform_device *bus_pdev = bus_info->pdev; + + if (acpi_bus_get_device(handle, &adev)) { + pr_err("%s: acpi_bus_get_device failed\n", __func__); + return AE_OK; + } + + pr_debug("Creating amba device %s\n", dev_name(&adev->dev)); + + dev = amba_device_alloc(NULL, 0, 0); + if (!dev) { + pr_err("%s(): amba_device_alloc() failed for %s\n", + __func__, dev_name(&adev->dev)); + return AE_CTRL_TERMINATE; + } + + /* setup generic device info */ + dev->dev.coherent_dma_mask = ~0; + dev->dev.parent = &bus_pdev->dev; + dev_set_name(&dev->dev, "%s", dev_name(&adev->dev)); + + /* setup amba-specific device info */ + ACPI_COMPANION_SET(&dev->dev, adev); + ACPI_COMPANION_SET(&adev->dev, adev); + + INIT_LIST_HEAD(&resource_list); + acpi_dev_get_resources(adev, &resource_list, + acpi_amba_add_resource, dev); + acpi_dev_free_resource_list(&resource_list); + + /* Add clocks */ + acpi_amba_register_clocks(adev, bus_info->clk, bus_info->clk_name); + + /* Read AMBA hardware ID and add device to system. If a driver matching + * hardware ID has already been registered, bind this device to it. + * Otherwise, the platform subsystem will match up the hardware ID + * when the matching driver is registered. + */ + ret = amba_device_add(dev, &iomem_resource); + if (ret) { + pr_err("%s(): amba_device_add() failed (%d) for %s\n", + __func__, ret, dev_name(&adev->dev)); + goto err_free; + } + + return AE_OK; + +err_free: + amba_device_put(dev); + + return AE_OK; /* don't prevent other devices from being probed */ +} + +static int acpi_amba_bus_probe(struct platform_device *pdev) +{ + struct acpi_amba_bus_info bus_info; + bus_info.clk_name = NULL; + + /* see if there's a top-level clock to use as default for sub-devices */ + bus_info.clk = acpi_amba_get_clk(ACPI_HANDLE(&pdev->dev), 0, + &bus_info.clk_name); + + /* probe each device */ + bus_info.pdev = pdev; + acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_HANDLE(&pdev->dev), 1, + acpi_amba_add_device, NULL, &bus_info, NULL); + + kfree(bus_info.clk_name); + + return 0; +} + +static const struct acpi_device_id amba_bus_acpi_match[] = { + { "AMBA0000", 0 }, + { }, +}; + +static struct platform_driver amba_bus_acpi_driver = { + .driver = { + .name = "amba-acpi", + .owner = THIS_MODULE, + .acpi_match_table = ACPI_PTR(amba_bus_acpi_match), + }, + .probe = acpi_amba_bus_probe, +}; + +static int __init acpi_amba_bus_init(void) +{ + return platform_driver_register(&amba_bus_acpi_driver); +} + +postcore_initcall(acpi_amba_bus_init); + +MODULE_AUTHOR("Brandon Anderson "); +MODULE_DESCRIPTION("ACPI Connector Resource for AMBA bus"); +MODULE_LICENSE("GPLv2"); +MODULE_ALIAS("platform:amba-acpi"); + +#endif /* CONFIG_ACPI */ -- cgit v1.2.3 From 178a88b959ad6c42f191d8079f45a57f16460bd4 Mon Sep 17 00:00:00 2001 From: Naresh Bhat Date: Mon, 9 Dec 2013 17:23:41 +0530 Subject: ACPI/ARM: Add ACPI to AMBA SPI driver Neither Foundation nor RTSM have a SPI device, but here are the necessary driver changes as an example of how to use acpi_amba_dsm_lookup() to get non-standard parameters from ACPI. This was tested by wiring up a SPI device into an RTSM Fast Model. Signed-off-by: Brandon Anderson Reviewed-by: Naresh Bhat --- drivers/spi/spi-pl022.c | 49 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) (limited to 'drivers') diff --git a/drivers/spi/spi-pl022.c b/drivers/spi/spi-pl022.c index 66d2ae21e78e..a90f4b1228a4 100644 --- a/drivers/spi/spi-pl022.c +++ b/drivers/spi/spi-pl022.c @@ -43,6 +43,7 @@ #include #include #include +#include /* * This macro is used to define some register default values. @@ -2059,6 +2060,49 @@ pl022_platform_data_dt_get(struct device *dev) return pd; } +#ifdef CONFIG_ACPI +static struct pl022_ssp_controller * +acpi_pl022_get_platform_data(struct device *dev) +{ + struct pl022_ssp_controller *pd, *ret; + struct acpi_amba_dsm_entry entry; + + pd = devm_kzalloc(dev, sizeof(struct pl022_ssp_controller), GFP_KERNEL); + if (!pd) { + dev_err(dev, "cannot allocate platform data memory\n"); + return NULL; + } + ret = pd; + + pd->bus_id = -1; + pd->enable_dma = 1; + if (acpi_amba_dsm_lookup(ACPI_HANDLE(dev), "num-cs", 0, &entry) == 0) { + if (kstrtou8(entry.value, 0, &pd->num_chipselect) != 0) { + dev_err(dev, "invalid 'num-cs' in ACPI definition\n"); + ret = NULL; + } + kfree(entry.key); + kfree(entry.value); + } + if (acpi_amba_dsm_lookup(ACPI_HANDLE(dev), + "autosuspend-delay", 0, &entry) == 0) { + if (kstrtoint(entry.value, 0, &pd->autosuspend_delay) != 0) { + dev_err(dev, "invalid 'autosuspend-delay' in ACPI definition\n"); + ret = NULL; + } + kfree(entry.key); + kfree(entry.value); + } + if (acpi_amba_dsm_lookup(ACPI_HANDLE(dev), "rt", 0, &entry) == 0) { + pd->rt = (entry.value && strcmp(entry.value, "1") == 0); + kfree(entry.key); + kfree(entry.value); + } + + return ret; +} +#endif + static int pl022_probe(struct amba_device *adev, const struct amba_id *id) { struct device *dev = &adev->dev; @@ -2071,6 +2115,11 @@ static int pl022_probe(struct amba_device *adev, const struct amba_id *id) dev_info(&adev->dev, "ARM PL022 driver, device ID: 0x%08x\n", adev->periphid); +#ifdef CONFIG_ACPI + if (!platform_info && ACPI_HANDLE(dev)) + platform_info = acpi_pl022_get_platform_data(dev); + else +#endif if (!platform_info && IS_ENABLED(CONFIG_OF)) platform_info = pl022_platform_data_dt_get(dev); -- cgit v1.2.3 From 4f2ee80f548363819b52df4b16a2a71f058de55f Mon Sep 17 00:00:00 2001 From: Graeme Gregory Date: Thu, 12 Dec 2013 16:14:06 +0000 Subject: 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 Signed-off-by: Graeme Gregory Acked-by: Tomasz Nowicki --- drivers/acpi/Makefile | 1 + drivers/acpi/acpi_keyvalue.c | 115 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 116 insertions(+) create mode 100644 drivers/acpi/acpi_keyvalue.c (limited to 'drivers') diff --git a/drivers/acpi/Makefile b/drivers/acpi/Makefile index d030fca22d05..3808499fba36 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 + * + * Original based on code :- + * + * Copyright (C) 2013 Advanced Micro Devices, Inc. + * + * Author: Brandon Anderson + * + * 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 + +/* 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); -- cgit v1.2.3 From 0959eddd5e16cfa640ceeb0fc57c5caf18c13de0 Mon Sep 17 00:00:00 2001 From: Tomasz Nowicki Date: Fri, 10 Jan 2014 13:24:20 +0100 Subject: acpi, mtd: Add support for flash device in physical memory map based on ACPI description This provides a 'mapping' driver which allows the NOR Flash and ROM driver code to communicate with chips which are mapped physically into the CPU's memory. The mapping description here is taken from DSDT ACPI table. This code was "inspired" by physmap_of.c Signed-off-by: Tomasz Nowicki --- drivers/mtd/maps/Kconfig | 9 + drivers/mtd/maps/Makefile | 1 + drivers/mtd/maps/physmap_acpi.c | 372 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 382 insertions(+) create mode 100644 drivers/mtd/maps/physmap_acpi.c (limited to 'drivers') diff --git a/drivers/mtd/maps/Kconfig b/drivers/mtd/maps/Kconfig index 21b2874a303b..69a44c996f20 100644 --- a/drivers/mtd/maps/Kconfig +++ b/drivers/mtd/maps/Kconfig @@ -74,6 +74,15 @@ config MTD_PHYSMAP_OF physically into the CPU's memory. The mapping description here is taken from OF device tree. +config MTD_PHYSMAP_ACPI + tristate "Flash device in physical memory map based on ACPI description" + depends on ACPI && (MTD_CFI || MTD_JEDECPROBE || MTD_ROM) + help + This provides a 'mapping' driver which allows the NOR Flash and + ROM driver code to communicate with chips which are mapped + physically into the CPU's memory. The mapping description here is + taken from DSDT ACPI table. + config MTD_PMC_MSP_EVM tristate "CFI Flash device mapped on PMC-Sierra MSP" depends on PMC_MSP && MTD_CFI diff --git a/drivers/mtd/maps/Makefile b/drivers/mtd/maps/Makefile index 141c91a5b24c..379cfff569c5 100644 --- a/drivers/mtd/maps/Makefile +++ b/drivers/mtd/maps/Makefile @@ -18,6 +18,7 @@ obj-$(CONFIG_MTD_TSUNAMI) += tsunami_flash.o obj-$(CONFIG_MTD_PXA2XX) += pxa2xx-flash.o obj-$(CONFIG_MTD_PHYSMAP) += physmap.o obj-$(CONFIG_MTD_PHYSMAP_OF) += physmap_of.o +obj-$(CONFIG_MTD_PHYSMAP_ACPI) += physmap_acpi.o obj-$(CONFIG_MTD_PISMO) += pismo.o obj-$(CONFIG_MTD_PMC_MSP_EVM) += pmcmsp-flash.o obj-$(CONFIG_MTD_PCMCIA) += pcmciamtd.o diff --git a/drivers/mtd/maps/physmap_acpi.c b/drivers/mtd/maps/physmap_acpi.c new file mode 100644 index 000000000000..f425dfd27a7a --- /dev/null +++ b/drivers/mtd/maps/physmap_acpi.c @@ -0,0 +1,372 @@ +/* + * Flash mappings described by the ACPI + * + * Copyright (C) 2006 MontaVista Software Inc. + * Author: Vitaly Wool + * Copyright (C) 2007 David Gibson, IBM Corporation. + * + * Revised to handle ACPI style flash binding by: + * Copyright (C) 2013 Tomasz Nowicki + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct acpi_flash_list { + struct mtd_info *mtd; + struct map_info map; + struct resource *res; +}; + +struct acpi_flash { + struct mtd_info *cmtd; + int list_size; /* number of elements in acpi_flash_list */ + struct acpi_flash_list list[0]; +}; + +static const char * const rom_probe_types[] = { + "cfi_probe", "jedec_probe", "map_rom" }; + +/* Helper function to handle probing of the obsolete "direct-mapped" + * compatible binding, which has an extra "probe-type" property + * describing the type of flash probe necessary. */ +static struct mtd_info *obsolete_probe(struct platform_device *dev, + struct map_info *map) +{ + struct acpi_dsm_entry entry; + struct mtd_info *mtd; + acpi_handle handler; + char *acpi_probe; + int i, err, len; + + handler = ACPI_HANDLE(&dev->dev); + dev_warn(&dev->dev, "ACPI uses obsolete \"direct-mapped\" flash " + "binding\n"); + + err = acpi_dsm_lookup_value(handler, "probe-type", 0, &entry); + if (err || entry.value == NULL) { + for (i = 0; i < ARRAY_SIZE(rom_probe_types); i++) { + mtd = do_map_probe(rom_probe_types[i], map); + if (mtd) + return mtd; + } + return NULL; + } + + len = strlen(entry.value) + 1; + acpi_probe = devm_kzalloc(&dev->dev, len, GFP_KERNEL); + strncpy(acpi_probe, entry.value, len); + kfree(entry.key); + kfree(entry.value); + + if (strcmp(acpi_probe, "CFI") == 0) { + return do_map_probe("cfi_probe", map); + } else if (strcmp(acpi_probe, "JEDEC") == 0) { + return do_map_probe("jedec_probe", map); + } else { + if (strcmp(acpi_probe, "ROM") != 0) + dev_warn(&dev->dev, "obsolete_probe: don't know probe " + "type '%s', mapping as rom\n", acpi_probe); + return do_map_probe("mtd_rom", map); + } +} + +/* When partitions are set we look for a linux,part-probe property which + specifies the list of partition probers to use. If none is given then the + default is use. These take precedence over other device tree + information. */ +static const char * const part_probe_types_def[] = { + "cmdlinepart", "RedBoot", NULL }; + +static const char * const *acpi_get_part_probes(struct device *dev) +{ + struct acpi_dsm_entry entry; + const char **res; + int cplen, err, len; + unsigned int count = 0, i; + char *cp = NULL; + + acpi_handle handler = ACPI_HANDLE(dev); + + /* Get space separated strings */ + err = acpi_dsm_lookup_value(handler, "linux,part-probe", 0, &entry); + if (err || entry.value == NULL) + return part_probe_types_def; + + len = strlen(entry.value) + 1; + cp = devm_kzalloc(dev, len, GFP_KERNEL); + strncpy(cp, entry.value, len); + kfree(entry.key); + kfree(entry.value); + + cplen = strlen(cp); + for (i = 0; i != cplen; i++) + if (cp[i] == ' ') + count++; + + /* Create the table with references to strings */ + res = kzalloc((count + 1) * sizeof(char *), GFP_KERNEL); + for (i = 0; i < count + 1; i++) { + res[i] = cp; + cp = strnchr(cp, cplen, ' '); + if (cp == NULL) + break; + + *cp++ = '\0'; + cplen = strlen(cp); + } + return res; +} + +static void acpi_free_probes(const char * const *probes) +{ + if (probes != part_probe_types_def) + kfree(probes); +} + +static const struct acpi_device_id acpi_flash_match[]; +static int acpi_flash_remove(struct platform_device *dev); + +static int acpi_flash_probe(struct platform_device *dev) +{ + const char * const *part_probe_types; + const struct acpi_device_id *id; + resource_size_t res_size; + struct acpi_flash *info; + const char *probe_type; + struct resource *res; + acpi_handle handler; + int err = 0, i; + int bank_width = 0, map_indirect = 0; + struct mtd_info **mtd_list = NULL; + char *mtd_name = NULL; + int len, count = 0; + struct acpi_dsm_entry entry; + + handler = ACPI_HANDLE(&dev->dev); + + id = acpi_match_device(acpi_flash_match, &dev->dev); + if (!id) + return -ENODEV; + + probe_type = (const char *)id->driver_data; + + err = acpi_dsm_lookup_value(handler, "linux,mtd-name", 0, &entry); + if (err == 0 && entry.value) { + len = strlen(entry.value) + 1; + mtd_name = devm_kzalloc(&dev->dev, len, GFP_KERNEL); + strncpy(mtd_name, entry.value, len); + kfree(entry.key); + kfree(entry.value); + } + + err = acpi_dsm_lookup_value(handler, "no-unaligned-direct-access", + 0, &entry); + if (err == 0 && kstrtoint(entry.value, 0, &map_indirect) == 0) { + kfree(entry.key); + kfree(entry.value); + } + + while (platform_get_resource(dev, IORESOURCE_MEM, count)) + count++; + + if (!count) { + dev_err(&dev->dev, "No resources found for %s device\n", + dev_name(&dev->dev)); + err = -ENXIO; + goto err_flash_remove; + } + + info = devm_kzalloc(&dev->dev, + sizeof(struct acpi_flash) + + sizeof(struct acpi_flash_list) * count, GFP_KERNEL); + if (!info) { + err = -ENOMEM; + goto err_flash_remove; + } + + dev_set_drvdata(&dev->dev, info); + + mtd_list = kzalloc(sizeof(*mtd_list) * count, GFP_KERNEL); + if (!mtd_list) + goto err_flash_remove; + + for (i = 0; i < count; i++) { + res = platform_get_resource(dev, IORESOURCE_MEM, i); + dev_dbg(&dev->dev, "resource[%d]: address 0x%lx size 0x%lx\n", + i, (long)res->start, (long)resource_size(res)); + + res_size = resource_size(res); + info->list[i].res = request_mem_region(res->start, res_size, + dev_name(&dev->dev)); + if (!info->list[i].res) { + err = -EBUSY; + goto err_out; + } + + /* Mandatory property */ + err = acpi_dsm_lookup_value(handler, "bank-width", 0, &entry); + if (err || kstrtoint(entry.value, 0, &bank_width) != 0) { + dev_err(&dev->dev, + "Can't get bank width from DSDT\n"); + goto err_out; + } + kfree(entry.key); + kfree(entry.value); + + info->list[i].map.name = mtd_name ?: dev_name(&dev->dev); + info->list[i].map.phys = res->start; + info->list[i].map.size = res_size; + info->list[i].map.bankwidth = bank_width; + info->list[i].map.virt = ioremap(info->list[i].map.phys, + info->list[i].map.size); + if (!info->list[i].map.virt) { + dev_err(&dev->dev, "Failed to ioremap() flash region\n"); + err = -ENOMEM; + goto err_out; + } + + simple_map_init(&info->list[i].map); + + /* + * On some platforms (e.g. MPC5200) a direct 1:1 mapping + * may cause problems with JFFS2 usage, as the local bus (LPB) + * doesn't support unaligned accesses as implemented in the + * JFFS2 code via memcpy(). By setting NO_XIP, the + * flash will not be exposed directly to the MTD users + * (e.g. JFFS2) any more. + */ + if (map_indirect) + info->list[i].map.phys = NO_XIP; + + if (probe_type) { + info->list[i].mtd = do_map_probe(probe_type, + &info->list[i].map); + } else { + info->list[i].mtd = obsolete_probe(dev, + &info->list[i].map); + } + + if (!info->list[i].mtd) { + dev_err(&dev->dev, "do_map_probe() failed\n"); + err = -ENXIO; + goto err_out; + } else + info->list_size++; + + info->list[i].mtd->owner = THIS_MODULE; + info->list[i].mtd->dev.parent = &dev->dev; + mtd_list[i] = info->list[i].mtd; + } + + info->cmtd = NULL; + if (info->list_size == 1) { + info->cmtd = info->list[0].mtd; + } else if (info->list_size > 1) { + /* + * We detected multiple devices. Concatenate them together. + */ + info->cmtd = mtd_concat_create(mtd_list, info->list_size, + dev_name(&dev->dev)); + } + if (info->cmtd == NULL) { + err = -ENXIO; + goto err_out; + } + + part_probe_types = acpi_get_part_probes(&dev->dev); + mtd_device_parse_register(info->cmtd, part_probe_types, NULL, + NULL, 0); + acpi_free_probes(part_probe_types); + + kfree(mtd_list); + + return 0; + +err_out: + kfree(mtd_list); +err_flash_remove: + acpi_flash_remove(dev); + + return err; +} + +static int acpi_flash_remove(struct platform_device *dev) +{ + struct acpi_flash *info; + int i; + + info = dev_get_drvdata(&dev->dev); + if (!info) + return 0; + dev_set_drvdata(&dev->dev, NULL); + + if (info->cmtd != info->list[0].mtd) { + mtd_device_unregister(info->cmtd); + mtd_concat_destroy(info->cmtd); + } + + if (info->cmtd) + mtd_device_unregister(info->cmtd); + + for (i = 0; i < info->list_size; i++) { + if (info->list[i].mtd) + map_destroy(info->list[i].mtd); + + if (info->list[i].map.virt) + iounmap(info->list[i].map.virt); + + if (info->list[i].res) { + release_resource(info->list[i].res); + kfree(info->list[i].res); + } + } + + return 0; +} + +static const struct acpi_device_id acpi_flash_match[] = { + { "LNRO0015", (unsigned long)"cfi_probe"}, + { "LNRO0016", (unsigned long)"jedec_probe"}, + { "LNRO0017", (unsigned long)"map_ram"}, + { "LNRO0018", (unsigned long)"direct-mapped"}, + {}, +}; +MODULE_DEVICE_TABLE(acpi, acpi_flash_match); + +static struct platform_driver acpi_flash_driver = { + .driver = { + .name = "acpi-flash", + .owner = THIS_MODULE, + .acpi_match_table = ACPI_PTR(acpi_flash_match), + }, + .probe = acpi_flash_probe, + .remove = acpi_flash_remove, +}; + +module_platform_driver(acpi_flash_driver); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Tomasz Nowicki "); +MODULE_DESCRIPTION("ACPI based MTD map driver"); -- cgit v1.2.3 From 7c8c13740bf8d06748970f956612829a765f8767 Mon Sep 17 00:00:00 2001 From: Graeme Gregory Date: Fri, 27 Jun 2014 18:08:49 +0100 Subject: acpi: amba: convert to using _PRP and fix _HID Convert from the old _DSM indicated clocks to the newer _PRP method as a staging point for final conversion to _DSD Signed-off-by: Graeme Gregory --- drivers/amba/acpi.c | 160 ++++++++++------------------------------------------ 1 file changed, 31 insertions(+), 129 deletions(-) (limited to 'drivers') diff --git a/drivers/amba/acpi.c b/drivers/amba/acpi.c index 9a97cd8ad433..ae180757f335 100644 --- a/drivers/amba/acpi.c +++ b/drivers/amba/acpi.c @@ -25,102 +25,6 @@ struct acpi_amba_bus_info { char *clk_name; }; -/* 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_amba_dsm_lookup() - * - * Helper to parse through ACPI _DSM object for a device. Each entry - * has three fields. - */ -int acpi_amba_dsm_lookup(acpi_handle handle, - const char *tag, int index, - struct acpi_amba_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_amba_dsm_lookup); - static int acpi_amba_add_resource(struct acpi_resource *ares, void *data) { struct amba_device *dev = data; @@ -157,43 +61,41 @@ static int acpi_amba_add_resource(struct acpi_resource *ares, void *data) static struct clk *acpi_amba_get_clk(acpi_handle handle, int index, char **clk_name) { - struct acpi_amba_dsm_entry entry; acpi_handle clk_handle; struct acpi_device *adev, *clk_adev; - char *clk_path; + const char **clk_paths; struct clk *clk = NULL; - int len; + int ret; if (acpi_bus_get_device(handle, &adev)) { pr_err("cannot get device from handle\n"); return NULL; } - /* key=value format for clocks is: - * "clock-name"="apb_pclk \\_SB.CLK0" - */ - if (acpi_amba_dsm_lookup(handle, "clock-name", index, &entry) != 0) + /* Get number of entries */ + ret = acpi_dev_get_property_array_string(adev, "clocks", NULL, 0); + + if ((ret < 0) || (index >= ret)) return NULL; + clk_paths = kzalloc(sizeof(*clk_paths) * ret, GFP_KERNEL); + /* look under the clock device for the clock that matches the entry */ - *clk_name = NULL; - len = strcspn(entry.value, " "); - if (len > 0 && (len + 1) < strlen(entry.value)) { - clk_path = entry.value + len + 1; - *clk_name = kmalloc(len + 1, GFP_KERNEL); - strncpy(*clk_name, entry.value, len); - (*clk_name)[len] = '\0'; - if (ACPI_FAILURE( - acpi_get_handle(NULL, clk_path, &clk_handle)) == 0 && - acpi_bus_get_device(clk_handle, &clk_adev) == 0) - clk = clk_get_sys(dev_name(&clk_adev->dev), *clk_name); - } else - pr_err("Invalid clock-name value format '%s' for %s\n", - entry.value, dev_name(&adev->dev)); - - kfree(entry.key); - kfree(entry.value); + ret = acpi_dev_get_property_array_string(adev, "clocks", clk_paths, + ret); + + /* Locate the acpi_device from the device name */ + acpi_get_handle(NULL, (acpi_string)clk_paths[index], &clk_handle); + if (!clk_handle) + goto error; + acpi_bus_get_device(clk_handle, &clk_adev); + if (!clk_adev) + goto error; + clk = clk_get_sys(dev_name(&clk_adev->dev), NULL); + +error: + kfree(clk_paths); return clk; } @@ -204,14 +106,6 @@ static void acpi_amba_register_clocks(struct acpi_device *adev, int i; char *clk_name; - if (default_clk) { - /* for amba_get_enable_pclk() ... */ - clk_register_clkdev(default_clk, default_clk_name, - dev_name(&adev->dev)); - /* for devm_clk_get() ... */ - clk_register_clkdev(default_clk, NULL, dev_name(&adev->dev)); - } - for (i = 0;; i++) { clk_name = NULL; clk = acpi_amba_get_clk(ACPI_HANDLE(&adev->dev), i, &clk_name); @@ -222,6 +116,14 @@ static void acpi_amba_register_clocks(struct acpi_device *adev, kfree(clk_name); } + + if (default_clk) { + /* for amba_get_enable_pclk() ... */ + clk_register_clkdev(default_clk, default_clk_name, + dev_name(&adev->dev)); + /* for devm_clk_get() ... */ + clk_register_clkdev(default_clk, NULL, dev_name(&adev->dev)); + } } /* acpi_amba_add_device() @@ -309,7 +211,7 @@ static int acpi_amba_bus_probe(struct platform_device *pdev) } static const struct acpi_device_id amba_bus_acpi_match[] = { - { "AMBA0000", 0 }, + { "LNRO001A", 0 }, { }, }; -- cgit v1.2.3 From 51f474b5f70e0cf5f9123d86b6a6852c073b593d Mon Sep 17 00:00:00 2001 From: Graeme Gregory Date: Fri, 27 Jun 2014 18:10:12 +0100 Subject: clk: fixed-rate update acpi handling to use _PRP Use _PRP instead of _DSM for clock values in preperation for _DSD Signed-off-by: Graeme Gregory --- drivers/clk/clk-fixed-rate.c | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) (limited to 'drivers') diff --git a/drivers/clk/clk-fixed-rate.c b/drivers/clk/clk-fixed-rate.c index fa5cd77ece5b..1b9ad23409d6 100644 --- a/drivers/clk/clk-fixed-rate.c +++ b/drivers/clk/clk-fixed-rate.c @@ -142,13 +142,21 @@ EXPORT_SYMBOL_GPL(of_fixed_clk_setup); static int fixed_clk_probe_acpi(struct platform_device *pdev) { struct clk *clk = ERR_PTR(-ENODEV); - unsigned long long rate = 0; - acpi_status status; + struct acpi_device *adev; + acpi_handle handle; + u64 rate = 0; + int ret; - /* there is a corresponding FREQ method under fixed clock object */ - status = acpi_evaluate_integer(ACPI_HANDLE(&pdev->dev), "FREQ", - NULL, &rate); - if (ACPI_FAILURE(status)) + handle = ACPI_HANDLE(&pdev->dev); + if (!handle) + return -ENODEV; + + ret = acpi_bus_get_device(handle, &adev); + if (ret) + return -ENODEV; + + ret = acpi_dev_get_property_u64(adev, "clock-frequency", &rate); + if (ret) return -ENODEV; clk = clk_register_fixed_rate(NULL, dev_name(&pdev->dev), NULL, @@ -156,10 +164,6 @@ static int fixed_clk_probe_acpi(struct platform_device *pdev) if (IS_ERR(clk)) return -ENODEV; - /* - * if we don't register the clk here, we can't get the clk - * for AMBA bus when CONFIG_OF=n - */ return clk_register_clkdev(clk, NULL, dev_name(&pdev->dev)); } #else -- cgit v1.2.3 From f55eb97f43177405f66dddd6a42df97550f588c5 Mon Sep 17 00:00:00 2001 From: Graeme Gregory Date: Mon, 30 Jun 2014 19:52:02 +0100 Subject: net: smsc911x add support for probing from ACPI This is a standard platform device to resources are converted in the ACPI core in the same fasion as DT resources. For the other DT provided information there is _DSD for ACPI. Signed-off-by: Graeme Gregory --- drivers/net/ethernet/smsc/smsc911x.c | 78 ++++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) (limited to 'drivers') diff --git a/drivers/net/ethernet/smsc/smsc911x.c b/drivers/net/ethernet/smsc/smsc911x.c index 5e13fa5524ae..2b13ad2a827f 100644 --- a/drivers/net/ethernet/smsc/smsc911x.c +++ b/drivers/net/ethernet/smsc/smsc911x.c @@ -59,6 +59,7 @@ #include #include #include +#include #include "smsc911x.h" #define SMSC_CHIPNAME "smsc911x" @@ -2369,9 +2370,77 @@ static inline int smsc911x_probe_config_dt( } #endif /* CONFIG_OF */ +#ifdef CONFIG_ACPI +static int smsc911x_probe_config_acpi(struct smsc911x_platform_config *config, + acpi_handle *ahandle) +{ + struct acpi_device *adev; + u32 width = 0, prop = 0; + const char *phy_mode = NULL; + int ret, i; + + if (!ahandle) + return -ENODEV; + + ret = acpi_bus_get_device(ahandle, &adev); + if (ret) + return -ENODEV; + + acpi_dev_get_property_string(adev, "phy-mode", &phy_mode); + if (phy_mode) { + for (i = 0; i < PHY_INTERFACE_MODE_MAX; i++) + if (!strcasecmp(phy_mode, phy_modes(i))) + break; + + config->phy_interface = i; + } + + acpi_dev_get_property_u32(adev, "reg-shift", &config->shift); + + acpi_dev_get_property_u32(adev, "reg-io-width", &width); + if (width == 4) + config->flags |= SMSC911X_USE_32BIT; + else + config->flags |= SMSC911X_USE_16BIT; + + acpi_dev_get_property_u32(adev, "smsc,irq-active-high", &prop); + if(prop) + config->irq_polarity = SMSC911X_IRQ_POLARITY_ACTIVE_HIGH; + + prop = 0; + acpi_dev_get_property_u32(adev, "smsc,irq-push-pull", &prop); + if (prop) + config->irq_type = SMSC911X_IRQ_TYPE_PUSH_PULL; + + prop = 0; + acpi_dev_get_property_u32(adev, "smsc,force-internal-phy", &prop); + if (prop) + config->flags |= SMSC911X_FORCE_INTERNAL_PHY; + + prop = 0; + acpi_dev_get_property_u32(adev, "smsc,force-external-phy", &prop); + if (prop) + config->flags |= SMSC911X_FORCE_EXTERNAL_PHY; + + prop = 0; + acpi_dev_get_property_u32(adev, "smsc,save-mac-address", &prop); + if (prop) + config->flags |= SMSC911X_SAVE_MAC_ADDRESS; + + return 0; +} +#else +static int smsc911x_probe_config_acpi(struct smsc911x_platform_config *config, + acpi_handle *ahandle) +{ + return -ENODEV; +} +#endif /* CONFIG_ACPI */ + static int smsc911x_drv_probe(struct platform_device *pdev) { struct device_node *np = pdev->dev.of_node; + acpi_handle *ahandle = ACPI_HANDLE(&pdev->dev); struct net_device *dev; struct smsc911x_data *pdata; struct smsc911x_platform_config *config = dev_get_platdata(&pdev->dev); @@ -2436,6 +2505,9 @@ static int smsc911x_drv_probe(struct platform_device *pdev) } retval = smsc911x_probe_config_dt(&pdata->config, np); + if (retval) + retval = smsc911x_probe_config_acpi(&pdata->config, ahandle); + if (retval && config) { /* copy config parameters across to pdata */ memcpy(&pdata->config, config, sizeof(pdata->config)); @@ -2606,6 +2678,11 @@ static const struct of_device_id smsc911x_dt_ids[] = { MODULE_DEVICE_TABLE(of, smsc911x_dt_ids); #endif +static const struct acpi_device_id smsc911x_acpi_ids[] = { + { "LNRO001B", }, + { } +}; + static struct platform_driver smsc911x_driver = { .probe = smsc911x_drv_probe, .remove = smsc911x_drv_remove, @@ -2614,6 +2691,7 @@ static struct platform_driver smsc911x_driver = { .owner = THIS_MODULE, .pm = SMSC911X_PM_OPS, .of_match_table = of_match_ptr(smsc911x_dt_ids), + .acpi_match_table = ACPI_PTR(smsc911x_acpi_ids), }, }; -- cgit v1.2.3 From c8f86f2b1f604dff3e71bb3a4039b7065c35c5f6 Mon Sep 17 00:00:00 2001 From: Tomasz Nowicki Date: Fri, 27 Jun 2014 16:58:56 +0200 Subject: acpi, sysreg: Get vexpress sysreg base address in the ACPI way. vexpress-sysreg is self-contained so it gets resources automatically being platform driver. However, while it is not probed, it still should provides base address so that other related drivers can take advantage of it. Make it possible and find resources based on device HID. Signed-off-by: Tomasz Nowicki --- drivers/mfd/vexpress-sysreg.c | 57 ++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 54 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/mfd/vexpress-sysreg.c b/drivers/mfd/vexpress-sysreg.c index c9b69d84ed5f..e2cd6aa46e5e 100644 --- a/drivers/mfd/vexpress-sysreg.c +++ b/drivers/mfd/vexpress-sysreg.c @@ -56,15 +56,66 @@ static void __iomem *__vexpress_sysreg_base; +#ifdef CONFIG_ACPI +static acpi_status check_vexpress_resource(struct acpi_resource *res, + void *data) +{ + struct resource *vexpress_res = data; + + if (!acpi_dev_resource_memory(res, vexpress_res)) + pr_err("Failed to map vexpress memory resource\n"); + + __vexpress_sysreg_base = ioremap(vexpress_res->start, + resource_size(vexpress_res)); + if (__vexpress_sysreg_base) + return AE_CTRL_TERMINATE; + + return AE_OK; +} + +static acpi_status find_vexpress_resource(acpi_handle handle, u32 lvl, + void *context, void **rv) +{ + struct resource *vexpress_res = context; + + acpi_walk_resources(handle, METHOD_NAME__CRS, + check_vexpress_resource, context); + + if (vexpress_res->flags) + return AE_CTRL_TERMINATE; + + return AE_OK; +} + +static void acpi_vexpress_sysreg_base(void) +{ + struct resource vexpress_res; + + acpi_get_devices("LNRO0009", find_vexpress_resource, &vexpress_res, + NULL); +} +#else +static inline void acpi_vexpress_sysreg_base(void) +{ +} +#endif + static void __iomem *vexpress_sysreg_base(void) { - if (!__vexpress_sysreg_base) { - struct device_node *node = of_find_compatible_node(NULL, NULL, - "arm,vexpress-sysreg"); + struct device_node *node; + if (__vexpress_sysreg_base) + goto ret; + + node = of_find_compatible_node(NULL, NULL, "arm,vexpress-sysreg"); + if (node) { __vexpress_sysreg_base = of_iomap(node, 0); + goto ret; } + acpi_vexpress_sysreg_base(); + +ret: WARN_ON(!__vexpress_sysreg_base); return __vexpress_sysreg_base; -- cgit v1.2.3