diff options
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/acpi/Makefile | 2 | ||||
-rw-r--r-- | drivers/acpi/acpi_keyvalue.c | 115 | ||||
-rw-r--r-- | drivers/acpi/acpica/Makefile | 2 | ||||
-rw-r--r-- | drivers/acpi/acpica/prxface.c | 217 | ||||
-rw-r--r-- | drivers/acpi/internal.h | 6 | ||||
-rw-r--r-- | drivers/acpi/property.c | 512 | ||||
-rw-r--r-- | drivers/acpi/scan.c | 2 | ||||
-rw-r--r-- | drivers/amba/Makefile | 2 | ||||
-rw-r--r-- | drivers/amba/acpi.c | 239 | ||||
-rw-r--r-- | drivers/clk/clk-fixed-rate.c | 82 | ||||
-rw-r--r-- | drivers/mfd/vexpress-sysreg.c | 66 | ||||
-rw-r--r-- | drivers/mtd/maps/Kconfig | 9 | ||||
-rw-r--r-- | drivers/mtd/maps/Makefile | 1 | ||||
-rw-r--r-- | drivers/mtd/maps/physmap_acpi.c | 372 | ||||
-rw-r--r-- | drivers/net/ethernet/smsc/smc91x.c | 10 | ||||
-rw-r--r-- | drivers/net/ethernet/smsc/smsc911x.c | 78 | ||||
-rw-r--r-- | drivers/spi/spi-pl022.c | 49 | ||||
-rw-r--r-- | drivers/virtio/virtio_mmio.c | 12 |
18 files changed, 1769 insertions, 7 deletions
diff --git a/drivers/acpi/Makefile b/drivers/acpi/Makefile index 8e9bbe6b03c0..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. @@ -46,6 +47,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/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/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 <linux/export.h> +#include <acpi/acpi.h> +#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) 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 <mika.westerberg@linux.intel.com> + * Darren Hart <dvhart@linux.intel.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> +#include <linux/device.h> +#include <linux/export.h> + +#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; 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..ae180757f335 --- /dev/null +++ b/drivers/amba/acpi.c @@ -0,0 +1,239 @@ +/* + * AMBA Connector Resource for ACPI + * + * 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. + */ + +#ifdef CONFIG_ACPI + +#include <linux/module.h> +#include <linux/amba/bus.h> +#include <linux/platform_device.h> +#include <linux/acpi.h> +#include <linux/clkdev.h> +#include <linux/amba/acpi.h> + +struct acpi_amba_bus_info { + struct platform_device *pdev; + struct clk *clk; + char *clk_name; +}; + +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) +{ + acpi_handle clk_handle; + struct acpi_device *adev, *clk_adev; + const char **clk_paths; + struct clk *clk = NULL; + int ret; + + if (acpi_bus_get_device(handle, &adev)) { + pr_err("cannot get device from handle\n"); + return NULL; + } + + /* 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 */ + 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; +} + +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; + + 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); + } + + 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() + * + * 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[] = { + { "LNRO001A", 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 <brandon.anderson@amd.com>"); +MODULE_DESCRIPTION("ACPI Connector Resource for AMBA bus"); +MODULE_LICENSE("GPLv2"); +MODULE_ALIAS("platform:amba-acpi"); + +#endif /* CONFIG_ACPI */ diff --git a/drivers/clk/clk-fixed-rate.c b/drivers/clk/clk-fixed-rate.c index 0fc56ab6e844..1b9ad23409d6 100644 --- a/drivers/clk/clk-fixed-rate.c +++ b/drivers/clk/clk-fixed-rate.c @@ -15,6 +15,9 @@ #include <linux/io.h> #include <linux/err.h> #include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/acpi.h> +#include <linux/clkdev.h> /* * DOC: basic fixed-rate clock that cannot gate @@ -133,5 +136,82 @@ 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 + +#ifdef CONFIG_ACPI +static int fixed_clk_probe_acpi(struct platform_device *pdev) +{ + struct clk *clk = ERR_PTR(-ENODEV); + struct acpi_device *adev; + acpi_handle handle; + u64 rate = 0; + int ret; + + 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, + CLK_IS_ROOT, rate); + if (IS_ERR(clk)) + return -ENODEV; + + 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; + + return 0; +} + +static const struct of_device_id fixed_clk_match[] = { + { .compatible = "fixed-clock" }, + {} +}; + +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, +}; + +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); diff --git a/drivers/mfd/vexpress-sysreg.c b/drivers/mfd/vexpress-sysreg.c index 9e21e4fc9599..e2cd6aa46e5e 100644 --- a/drivers/mfd/vexpress-sysreg.c +++ b/drivers/mfd/vexpress-sysreg.c @@ -22,6 +22,7 @@ #include <linux/slab.h> #include <linux/stat.h> #include <linux/vexpress.h> +#include <linux/acpi.h> #define SYS_ID 0x000 #define SYS_SW 0x004 @@ -55,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; @@ -255,10 +307,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, }; 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 <vwool@ru.mvista.com> + * Copyright (C) 2007 David Gibson, IBM Corporation. + * + * Revised to handle ACPI style flash binding by: + * Copyright (C) 2013 Tomasz Nowicki <tomasz.nowicki@linaro.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <linux/module.h> +#include <linux/types.h> +#include <linux/init.h> +#include <linux/device.h> +#include <linux/mtd/mtd.h> +#include <linux/mtd/map.h> +#include <linux/mtd/partitions.h> +#include <linux/mtd/concat.h> +#include <linux/acpi.h> +#include <linux/slab.h> +#include <linux/platform_device.h> + +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 <tomasz.nowicki@linaro.org>"); +MODULE_DESCRIPTION("ACPI based MTD map driver"); 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 <linux/workqueue.h> #include <linux/of.h> #include <linux/of_device.h> +#include <linux/acpi.h> #include <linux/netdevice.h> #include <linux/etherdevice.h> @@ -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), }, }; 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 <linux/of_device.h> #include <linux/of_gpio.h> #include <linux/of_net.h> +#include <linux/acpi.h> #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), }, }; 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 <linux/gpio.h> #include <linux/of_gpio.h> #include <linux/pinctrl/consumer.h> +#include <linux/amba/acpi.h> /* * 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); 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 <linux/virtio_config.h> #include <linux/virtio_mmio.h> #include <linux/virtio_ring.h> - - +#include <linux/acpi.h> /* 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), }, }; |