From 91e5687805885f9fceb60b95e950a3d3bdcf4764 Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Wed, 31 Oct 2012 22:45:02 +0100 Subject: ACPI: Add support for platform bus type With ACPI 5 it is now possible to enumerate traditional SoC peripherals, like serial bus controllers and slave devices behind them. These devices are typically based on IP-blocks used in many existing SoC platforms and platform drivers for them may already be present in the kernel tree. To make driver "porting" more straightforward, add ACPI support to the platform bus type. Instead of writing ACPI "glue" drivers for the existing platform drivers, register the platform bus type with ACPI to create platform device objects for the drivers and bind the corresponding ACPI handles to those platform devices. This should allow us to reuse the existing platform drivers for the devices in question with the minimum amount of modifications. This changeset is based on Mika Westerberg's and Mathias Nyman's work. Signed-off-by: Mathias Nyman Signed-off-by: Mika Westerberg Acked-by: Greg Kroah-Hartman Acked-by: H. Peter Anvin Acked-by: Tony Luck Signed-off-by: Rafael J. Wysocki --- drivers/acpi/acpi_platform.c | 285 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 285 insertions(+) create mode 100644 drivers/acpi/acpi_platform.c (limited to 'drivers/acpi/acpi_platform.c') diff --git a/drivers/acpi/acpi_platform.c b/drivers/acpi/acpi_platform.c new file mode 100644 index 000000000000..a5a23462287d --- /dev/null +++ b/drivers/acpi/acpi_platform.c @@ -0,0 +1,285 @@ +/* + * ACPI support for platform bus type. + * + * Copyright (C) 2012, Intel Corporation + * Authors: Mika Westerberg + * Mathias Nyman + * Rafael J. Wysocki + * + * 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 +#include + +ACPI_MODULE_NAME("platform"); + +struct resource_info { + struct device *dev; + struct resource *res; + size_t n, cur; +}; + +static acpi_status acpi_platform_count_resources(struct acpi_resource *res, + void *data) +{ + struct acpi_resource_extended_irq *acpi_xirq; + struct resource_info *ri = data; + + switch (res->type) { + case ACPI_RESOURCE_TYPE_FIXED_MEMORY32: + case ACPI_RESOURCE_TYPE_IRQ: + ri->n++; + break; + case ACPI_RESOURCE_TYPE_EXTENDED_IRQ: + acpi_xirq = &res->data.extended_irq; + ri->n += acpi_xirq->interrupt_count; + break; + case ACPI_RESOURCE_TYPE_ADDRESS32: + if (res->data.address32.resource_type == ACPI_IO_RANGE) + ri->n++; + break; + } + + return AE_OK; +} + +static acpi_status acpi_platform_add_resources(struct acpi_resource *res, + void *data) +{ + struct acpi_resource_fixed_memory32 *acpi_mem; + struct acpi_resource_address32 *acpi_add32; + struct acpi_resource_extended_irq *acpi_xirq; + struct acpi_resource_irq *acpi_irq; + struct resource_info *ri = data; + struct resource *r; + int irq, i; + + switch (res->type) { + case ACPI_RESOURCE_TYPE_FIXED_MEMORY32: + acpi_mem = &res->data.fixed_memory32; + r = &ri->res[ri->cur++]; + + r->start = acpi_mem->address; + r->end = r->start + acpi_mem->address_length - 1; + r->flags = IORESOURCE_MEM; + + dev_dbg(ri->dev, "Memory32Fixed %pR\n", r); + break; + + case ACPI_RESOURCE_TYPE_ADDRESS32: + acpi_add32 = &res->data.address32; + + if (acpi_add32->resource_type == ACPI_IO_RANGE) { + r = &ri->res[ri->cur++]; + r->start = acpi_add32->minimum; + r->end = r->start + acpi_add32->address_length - 1; + r->flags = IORESOURCE_IO; + dev_dbg(ri->dev, "Address32 %pR\n", r); + } + break; + + case ACPI_RESOURCE_TYPE_IRQ: + acpi_irq = &res->data.irq; + r = &ri->res[ri->cur++]; + + irq = acpi_register_gsi(ri->dev, + acpi_irq->interrupts[0], + acpi_irq->triggering, + acpi_irq->polarity); + + r->start = r->end = irq; + r->flags = IORESOURCE_IRQ; + + dev_dbg(ri->dev, "IRQ %pR\n", r); + break; + + case ACPI_RESOURCE_TYPE_EXTENDED_IRQ: + acpi_xirq = &res->data.extended_irq; + + for (i = 0; i < acpi_xirq->interrupt_count; i++, ri->cur++) { + r = &ri->res[ri->cur]; + irq = acpi_register_gsi(ri->dev, + acpi_xirq->interrupts[i], + acpi_xirq->triggering, + acpi_xirq->polarity); + + r->start = r->end = irq; + r->flags = IORESOURCE_IRQ; + + dev_dbg(ri->dev, "Interrupt %pR\n", r); + } + break; + } + + return AE_OK; +} + +static acpi_status acpi_platform_get_device_uid(struct acpi_device *adev, + int *uid) +{ + struct acpi_device_info *info; + acpi_status status; + + status = acpi_get_object_info(adev->handle, &info); + if (ACPI_FAILURE(status)) + return status; + + status = AE_NOT_EXIST; + if ((info->valid & ACPI_VALID_UID) && + !kstrtoint(info->unique_id.string, 0, uid)) + status = AE_OK; + + kfree(info); + return status; +} + +/** + * acpi_create_platform_device - Create platform device for ACPI device node + * @adev: ACPI device node to create a platform device for. + * + * Check if the given @adev can be represented as a platform device and, if + * that's the case, create and register a platform device, populate its common + * resources and returns a pointer to it. Otherwise, return %NULL. + * + * The platform device's name will be taken from the @adev's _HID and _UID. + */ +struct platform_device *acpi_create_platform_device(struct acpi_device *adev) +{ + struct platform_device *pdev = NULL; + struct acpi_device *acpi_parent; + struct device *parent = NULL; + struct resource_info ri; + acpi_status status; + int devid; + + /* If the ACPI node already has a physical device attached, skip it. */ + if (adev->physical_node_count) + return NULL; + + /* Use the UID of the device as the new platform device id if found. */ + status = acpi_platform_get_device_uid(adev, &devid); + if (ACPI_FAILURE(status)) + devid = -1; + + memset(&ri, 0, sizeof(ri)); + + /* First, count the resources. */ + status = acpi_walk_resources(adev->handle, METHOD_NAME__CRS, + acpi_platform_count_resources, &ri); + if (ACPI_FAILURE(status) || !ri.n) + return NULL; + + /* Next, allocate memory for all the resources and populate it. */ + ri.dev = &adev->dev; + ri.res = kzalloc(ri.n * sizeof(struct resource), GFP_KERNEL); + if (!ri.res) { + dev_err(&adev->dev, + "failed to allocate memory for resources\n"); + return NULL; + } + + status = acpi_walk_resources(adev->handle, METHOD_NAME__CRS, + acpi_platform_add_resources, &ri); + if (ACPI_FAILURE(status)) { + dev_err(&adev->dev, "failed to walk resources\n"); + goto out; + } + + if (WARN_ON(ri.n != ri.cur)) + goto out; + + /* + * If the ACPI node has a parent and that parent has a physical device + * attached to it, that physical device should be the parent of the + * platform device we are about to create. + */ + acpi_parent = adev->parent; + if (acpi_parent) { + struct acpi_device_physical_node *entry; + struct list_head *list; + + mutex_lock(&acpi_parent->physical_node_lock); + list = &acpi_parent->physical_node_list; + if (!list_empty(list)) { + entry = list_first_entry(list, + struct acpi_device_physical_node, + node); + parent = entry->dev; + } + mutex_unlock(&acpi_parent->physical_node_lock); + } + pdev = platform_device_register_resndata(parent, acpi_device_hid(adev), + devid, ri.res, ri.n, NULL, 0); + if (IS_ERR(pdev)) { + dev_err(&adev->dev, "platform device creation failed: %ld\n", + PTR_ERR(pdev)); + pdev = NULL; + } else { + dev_dbg(&adev->dev, "created platform device %s\n", + dev_name(&pdev->dev)); + } + + out: + kfree(ri.res); + return pdev; +} + +static acpi_status acpi_platform_match(acpi_handle handle, u32 depth, + void *data, void **return_value) +{ + struct platform_device *pdev = data; + struct acpi_device *adev; + acpi_status status; + + status = acpi_bus_get_device(handle, &adev); + if (ACPI_FAILURE(status)) + return status; + + /* Skip ACPI devices that have physical device attached */ + if (adev->physical_node_count) + return AE_OK; + + if (!strcmp(pdev->name, acpi_device_hid(adev))) { + int devid; + + /* Check that both name and UID match if it exists */ + status = acpi_platform_get_device_uid(adev, &devid); + if (ACPI_FAILURE(status)) + devid = -1; + + if (pdev->id != devid) + return AE_OK; + + *(acpi_handle *)return_value = handle; + return AE_CTRL_TERMINATE; + } + + return AE_OK; +} + +static int acpi_platform_find_device(struct device *dev, acpi_handle *handle) +{ + struct platform_device *pdev = to_platform_device(dev); + + *handle = NULL; + acpi_get_devices(pdev->name, acpi_platform_match, pdev, handle); + + return *handle ? 0 : -ENODEV; +} + +static struct acpi_bus_type acpi_platform_bus = { + .bus = &platform_bus_type, + .find_device = acpi_platform_find_device, +}; + +static int __init acpi_platform_init(void) +{ + return register_acpi_bus_type(&acpi_platform_bus); +} +arch_initcall(acpi_platform_init); -- cgit v1.2.3 From b4b6cae2f36d92b31788f10816709d5290a1119a Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Sat, 10 Nov 2012 23:16:02 +0100 Subject: ACPI / platform: use ACPI device name instead of _HID._UID Using _UID makes the ACPI platform bus code depend on BIOS to get it right. If it doesn't we fail to create the platform device as the name should be unique. The ACPI core already makes a unique name when it first creates the ACPI device so we can use that same name as the platform device name instead of trusting that the BIOS sets the _UIDs correctly. Signed-off-by: Mika Westerberg Signed-off-by: Rafael J. Wysocki --- drivers/acpi/acpi_platform.c | 62 ++++++++++++++++---------------------------- 1 file changed, 22 insertions(+), 40 deletions(-) (limited to 'drivers/acpi/acpi_platform.c') diff --git a/drivers/acpi/acpi_platform.c b/drivers/acpi/acpi_platform.c index a5a23462287d..dbb31d61e310 100644 --- a/drivers/acpi/acpi_platform.c +++ b/drivers/acpi/acpi_platform.c @@ -120,25 +120,6 @@ static acpi_status acpi_platform_add_resources(struct acpi_resource *res, return AE_OK; } -static acpi_status acpi_platform_get_device_uid(struct acpi_device *adev, - int *uid) -{ - struct acpi_device_info *info; - acpi_status status; - - status = acpi_get_object_info(adev->handle, &info); - if (ACPI_FAILURE(status)) - return status; - - status = AE_NOT_EXIST; - if ((info->valid & ACPI_VALID_UID) && - !kstrtoint(info->unique_id.string, 0, uid)) - status = AE_OK; - - kfree(info); - return status; -} - /** * acpi_create_platform_device - Create platform device for ACPI device node * @adev: ACPI device node to create a platform device for. @@ -156,19 +137,12 @@ struct platform_device *acpi_create_platform_device(struct acpi_device *adev) struct device *parent = NULL; struct resource_info ri; acpi_status status; - int devid; /* If the ACPI node already has a physical device attached, skip it. */ if (adev->physical_node_count) return NULL; - /* Use the UID of the device as the new platform device id if found. */ - status = acpi_platform_get_device_uid(adev, &devid); - if (ACPI_FAILURE(status)) - devid = -1; - memset(&ri, 0, sizeof(ri)); - /* First, count the resources. */ status = acpi_walk_resources(adev->handle, METHOD_NAME__CRS, acpi_platform_count_resources, &ri); @@ -214,8 +188,8 @@ struct platform_device *acpi_create_platform_device(struct acpi_device *adev) } mutex_unlock(&acpi_parent->physical_node_lock); } - pdev = platform_device_register_resndata(parent, acpi_device_hid(adev), - devid, ri.res, ri.n, NULL, 0); + pdev = platform_device_register_resndata(parent, dev_name(&adev->dev), + -1, ri.res, ri.n, NULL, 0); if (IS_ERR(pdev)) { dev_err(&adev->dev, "platform device creation failed: %ld\n", PTR_ERR(pdev)); @@ -245,17 +219,7 @@ static acpi_status acpi_platform_match(acpi_handle handle, u32 depth, if (adev->physical_node_count) return AE_OK; - if (!strcmp(pdev->name, acpi_device_hid(adev))) { - int devid; - - /* Check that both name and UID match if it exists */ - status = acpi_platform_get_device_uid(adev, &devid); - if (ACPI_FAILURE(status)) - devid = -1; - - if (pdev->id != devid) - return AE_OK; - + if (!strcmp(dev_name(&pdev->dev), dev_name(&adev->dev))) { *(acpi_handle *)return_value = handle; return AE_CTRL_TERMINATE; } @@ -266,10 +230,28 @@ static acpi_status acpi_platform_match(acpi_handle handle, u32 depth, static int acpi_platform_find_device(struct device *dev, acpi_handle *handle) { struct platform_device *pdev = to_platform_device(dev); + char *name, *tmp, *hid; + + /* + * The platform device is named using the ACPI device name + * _HID:INSTANCE so we strip the INSTANCE out in order to find the + * correct device using its _HID. + */ + name = kstrdup(dev_name(dev), GFP_KERNEL); + if (!name) + return -ENOMEM; + + tmp = name; + hid = strsep(&tmp, ":"); + if (!hid) { + kfree(name); + return -ENODEV; + } *handle = NULL; - acpi_get_devices(pdev->name, acpi_platform_match, pdev, handle); + acpi_get_devices(hid, acpi_platform_match, pdev, handle); + kfree(name); return *handle ? 0 : -ENODEV; } -- cgit v1.2.3 From 97d69dc061e968b5e9e56f48bb223b9ab7764b48 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Thu, 15 Nov 2012 00:30:12 +0100 Subject: ACPI / platform: Use common ACPI device resource parsing routines Use common routines in drivers/acpi/resource.c to parse ACPI device resources while creating platform device objects. Signed-off-by: Rafael J. Wysocki Reviewed-by: Mika Westerberg Tested-by: Mika Westerberg --- drivers/acpi/acpi_platform.c | 89 ++++++++++---------------------------------- 1 file changed, 20 insertions(+), 69 deletions(-) (limited to 'drivers/acpi/acpi_platform.c') diff --git a/drivers/acpi/acpi_platform.c b/drivers/acpi/acpi_platform.c index dbb31d61e310..bcbb00ce6d99 100644 --- a/drivers/acpi/acpi_platform.c +++ b/drivers/acpi/acpi_platform.c @@ -29,21 +29,20 @@ static acpi_status acpi_platform_count_resources(struct acpi_resource *res, void *data) { struct acpi_resource_extended_irq *acpi_xirq; + struct acpi_resource_irq *acpi_irq; struct resource_info *ri = data; switch (res->type) { - case ACPI_RESOURCE_TYPE_FIXED_MEMORY32: case ACPI_RESOURCE_TYPE_IRQ: - ri->n++; + acpi_irq = &res->data.irq; + ri->n += acpi_irq->interrupt_count; break; case ACPI_RESOURCE_TYPE_EXTENDED_IRQ: acpi_xirq = &res->data.extended_irq; ri->n += acpi_xirq->interrupt_count; break; - case ACPI_RESOURCE_TYPE_ADDRESS32: - if (res->data.address32.resource_type == ACPI_IO_RANGE) - ri->n++; - break; + default: + ri->n++; } return AE_OK; @@ -52,71 +51,26 @@ static acpi_status acpi_platform_count_resources(struct acpi_resource *res, static acpi_status acpi_platform_add_resources(struct acpi_resource *res, void *data) { - struct acpi_resource_fixed_memory32 *acpi_mem; - struct acpi_resource_address32 *acpi_add32; - struct acpi_resource_extended_irq *acpi_xirq; - struct acpi_resource_irq *acpi_irq; struct resource_info *ri = data; struct resource *r; - int irq, i; - - switch (res->type) { - case ACPI_RESOURCE_TYPE_FIXED_MEMORY32: - acpi_mem = &res->data.fixed_memory32; - r = &ri->res[ri->cur++]; - r->start = acpi_mem->address; - r->end = r->start + acpi_mem->address_length - 1; - r->flags = IORESOURCE_MEM; - - dev_dbg(ri->dev, "Memory32Fixed %pR\n", r); - break; - - case ACPI_RESOURCE_TYPE_ADDRESS32: - acpi_add32 = &res->data.address32; - - if (acpi_add32->resource_type == ACPI_IO_RANGE) { - r = &ri->res[ri->cur++]; - r->start = acpi_add32->minimum; - r->end = r->start + acpi_add32->address_length - 1; - r->flags = IORESOURCE_IO; - dev_dbg(ri->dev, "Address32 %pR\n", r); - } - break; - - case ACPI_RESOURCE_TYPE_IRQ: - acpi_irq = &res->data.irq; - r = &ri->res[ri->cur++]; - - irq = acpi_register_gsi(ri->dev, - acpi_irq->interrupts[0], - acpi_irq->triggering, - acpi_irq->polarity); - - r->start = r->end = irq; - r->flags = IORESOURCE_IRQ; - - dev_dbg(ri->dev, "IRQ %pR\n", r); - break; - - case ACPI_RESOURCE_TYPE_EXTENDED_IRQ: - acpi_xirq = &res->data.extended_irq; - - for (i = 0; i < acpi_xirq->interrupt_count; i++, ri->cur++) { - r = &ri->res[ri->cur]; - irq = acpi_register_gsi(ri->dev, - acpi_xirq->interrupts[i], - acpi_xirq->triggering, - acpi_xirq->polarity); + r = ri->res + ri->cur; + if (acpi_dev_resource_memory(res, r) + || acpi_dev_resource_io(res, r) + || acpi_dev_resource_address_space(res, r) + || acpi_dev_resource_ext_address_space(res, r)) { + ri->cur++; + return AE_OK; + } + if (acpi_dev_resource_interrupt(res, 0, r)) { + int i; - r->start = r->end = irq; - r->flags = IORESOURCE_IRQ; + r++; + for (i = 1; acpi_dev_resource_interrupt(res, i, r); i++) + r++; - dev_dbg(ri->dev, "Interrupt %pR\n", r); - } - break; + ri->cur += i; } - return AE_OK; } @@ -165,9 +119,6 @@ struct platform_device *acpi_create_platform_device(struct acpi_device *adev) goto out; } - if (WARN_ON(ri.n != ri.cur)) - goto out; - /* * If the ACPI node has a parent and that parent has a physical device * attached to it, that physical device should be the parent of the @@ -189,7 +140,7 @@ struct platform_device *acpi_create_platform_device(struct acpi_device *adev) mutex_unlock(&acpi_parent->physical_node_lock); } pdev = platform_device_register_resndata(parent, dev_name(&adev->dev), - -1, ri.res, ri.n, NULL, 0); + -1, ri.res, ri.cur, NULL, 0); if (IS_ERR(pdev)) { dev_err(&adev->dev, "platform device creation failed: %ld\n", PTR_ERR(pdev)); -- cgit v1.2.3 From 8e345c991c8c7a3c081199ef77deada79e37618a Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Thu, 15 Nov 2012 00:30:21 +0100 Subject: ACPI: Centralized processing of ACPI device resources Currently, whoever wants to use ACPI device resources has to call acpi_walk_resources() to browse the buffer returned by the _CRS method for the given device and create filters passed to that routine to apply to the individual resource items. This generally is cumbersome, time-consuming and inefficient. Moreover, it may be problematic if resource conflicts need to be resolved, because the different users of _CRS will need to do that in a consistent way. However, if there are resource conflicts, the ACPI core should be able to resolve them centrally instead of relying on various users of acpi_walk_resources() to handle them correctly together. For this reason, introduce a new function, acpi_dev_get_resources(), that can be used by subsystems to obtain a list of struct resource objects corresponding to the ACPI device resources returned by _CRS and, if necessary, to apply additional preprocessing routine to the ACPI resources before converting them to the struct resource format. Make the ACPI code that creates platform device objects use acpi_dev_get_resources() for resource processing instead of executing acpi_walk_resources() twice by itself, which causes it to be much more straightforward and easier to follow. In the future, acpi_dev_get_resources() can be extended to meet the needs of the ACPI PNP subsystem and other users of _CRS in the kernel. Signed-off-by: Rafael J. Wysocki Reviewed-by: Mika Westerberg Tested-by: Mika Westerberg --- drivers/acpi/acpi_platform.c | 94 ++++++------------------------ drivers/acpi/resource.c | 134 +++++++++++++++++++++++++++++++++++++++++++ include/linux/acpi.h | 10 ++++ 3 files changed, 161 insertions(+), 77 deletions(-) (limited to 'drivers/acpi/acpi_platform.c') diff --git a/drivers/acpi/acpi_platform.c b/drivers/acpi/acpi_platform.c index bcbb00ce6d99..7ac20d8b8f07 100644 --- a/drivers/acpi/acpi_platform.c +++ b/drivers/acpi/acpi_platform.c @@ -19,61 +19,6 @@ ACPI_MODULE_NAME("platform"); -struct resource_info { - struct device *dev; - struct resource *res; - size_t n, cur; -}; - -static acpi_status acpi_platform_count_resources(struct acpi_resource *res, - void *data) -{ - struct acpi_resource_extended_irq *acpi_xirq; - struct acpi_resource_irq *acpi_irq; - struct resource_info *ri = data; - - switch (res->type) { - case ACPI_RESOURCE_TYPE_IRQ: - acpi_irq = &res->data.irq; - ri->n += acpi_irq->interrupt_count; - break; - case ACPI_RESOURCE_TYPE_EXTENDED_IRQ: - acpi_xirq = &res->data.extended_irq; - ri->n += acpi_xirq->interrupt_count; - break; - default: - ri->n++; - } - - return AE_OK; -} - -static acpi_status acpi_platform_add_resources(struct acpi_resource *res, - void *data) -{ - struct resource_info *ri = data; - struct resource *r; - - r = ri->res + ri->cur; - if (acpi_dev_resource_memory(res, r) - || acpi_dev_resource_io(res, r) - || acpi_dev_resource_address_space(res, r) - || acpi_dev_resource_ext_address_space(res, r)) { - ri->cur++; - return AE_OK; - } - if (acpi_dev_resource_interrupt(res, 0, r)) { - int i; - - r++; - for (i = 1; acpi_dev_resource_interrupt(res, i, r); i++) - r++; - - ri->cur += i; - } - return AE_OK; -} - /** * acpi_create_platform_device - Create platform device for ACPI device node * @adev: ACPI device node to create a platform device for. @@ -89,35 +34,31 @@ struct platform_device *acpi_create_platform_device(struct acpi_device *adev) struct platform_device *pdev = NULL; struct acpi_device *acpi_parent; struct device *parent = NULL; - struct resource_info ri; - acpi_status status; + struct resource_list_entry *rentry; + struct list_head resource_list; + struct resource *resources; + int count; /* If the ACPI node already has a physical device attached, skip it. */ if (adev->physical_node_count) return NULL; - memset(&ri, 0, sizeof(ri)); - /* First, count the resources. */ - status = acpi_walk_resources(adev->handle, METHOD_NAME__CRS, - acpi_platform_count_resources, &ri); - if (ACPI_FAILURE(status) || !ri.n) + INIT_LIST_HEAD(&resource_list); + count = acpi_dev_get_resources(adev, &resource_list, NULL, NULL); + if (count <= 0) return NULL; - /* Next, allocate memory for all the resources and populate it. */ - ri.dev = &adev->dev; - ri.res = kzalloc(ri.n * sizeof(struct resource), GFP_KERNEL); - if (!ri.res) { - dev_err(&adev->dev, - "failed to allocate memory for resources\n"); + resources = kmalloc(count * sizeof(struct resource), GFP_KERNEL); + if (!resources) { + dev_err(&adev->dev, "No memory for resources\n"); + acpi_dev_free_resource_list(&resource_list); return NULL; } + count = 0; + list_for_each_entry(rentry, &resource_list, node) + resources[count++] = rentry->res; - status = acpi_walk_resources(adev->handle, METHOD_NAME__CRS, - acpi_platform_add_resources, &ri); - if (ACPI_FAILURE(status)) { - dev_err(&adev->dev, "failed to walk resources\n"); - goto out; - } + acpi_dev_free_resource_list(&resource_list); /* * If the ACPI node has a parent and that parent has a physical device @@ -140,7 +81,7 @@ struct platform_device *acpi_create_platform_device(struct acpi_device *adev) mutex_unlock(&acpi_parent->physical_node_lock); } pdev = platform_device_register_resndata(parent, dev_name(&adev->dev), - -1, ri.res, ri.cur, NULL, 0); + -1, resources, count, NULL, 0); if (IS_ERR(pdev)) { dev_err(&adev->dev, "platform device creation failed: %ld\n", PTR_ERR(pdev)); @@ -150,8 +91,7 @@ struct platform_device *acpi_create_platform_device(struct acpi_device *adev) dev_name(&pdev->dev)); } - out: - kfree(ri.res); + kfree(resources); return pdev; } diff --git a/drivers/acpi/resource.c b/drivers/acpi/resource.c index 3e7fd349e29d..2bafc25482b3 100644 --- a/drivers/acpi/resource.c +++ b/drivers/acpi/resource.c @@ -26,6 +26,7 @@ #include #include #include +#include #ifdef CONFIG_X86 #define valid_IRQ(i) (((i) != 0) && ((i) != 2)) @@ -391,3 +392,136 @@ bool acpi_dev_resource_interrupt(struct acpi_resource *ares, int index, return true; } EXPORT_SYMBOL_GPL(acpi_dev_resource_interrupt); + +/** + * acpi_dev_free_resource_list - Free resource from %acpi_dev_get_resources(). + * @list: The head of the resource list to free. + */ +void acpi_dev_free_resource_list(struct list_head *list) +{ + struct resource_list_entry *rentry, *re; + + list_for_each_entry_safe(rentry, re, list, node) { + list_del(&rentry->node); + kfree(rentry); + } +} +EXPORT_SYMBOL_GPL(acpi_dev_free_resource_list); + +struct res_proc_context { + struct list_head *list; + int (*preproc)(struct acpi_resource *, void *); + void *preproc_data; + int count; + int error; +}; + +static acpi_status acpi_dev_new_resource_entry(struct resource *r, + struct res_proc_context *c) +{ + struct resource_list_entry *rentry; + + rentry = kmalloc(sizeof(*rentry), GFP_KERNEL); + if (!rentry) { + c->error = -ENOMEM; + return AE_NO_MEMORY; + } + INIT_LIST_HEAD(&rentry->node); + rentry->res = *r; + list_add_tail(&rentry->node, c->list); + c->count++; + return AE_OK; +} + +static acpi_status acpi_dev_process_resource(struct acpi_resource *ares, + void *context) +{ + struct res_proc_context *c = context; + struct resource r; + int i; + + if (c->preproc) { + int ret; + + ret = c->preproc(ares, c->preproc_data); + if (ret < 0) { + c->error = ret; + return AE_ABORT_METHOD; + } else if (ret > 0) { + return AE_OK; + } + } + + memset(&r, 0, sizeof(r)); + + if (acpi_dev_resource_memory(ares, &r) + || acpi_dev_resource_io(ares, &r) + || acpi_dev_resource_address_space(ares, &r) + || acpi_dev_resource_ext_address_space(ares, &r)) + return acpi_dev_new_resource_entry(&r, c); + + for (i = 0; acpi_dev_resource_interrupt(ares, i, &r); i++) { + acpi_status status; + + status = acpi_dev_new_resource_entry(&r, c); + if (ACPI_FAILURE(status)) + return status; + } + + return AE_OK; +} + +/** + * acpi_dev_get_resources - Get current resources of a device. + * @adev: ACPI device node to get the resources for. + * @list: Head of the resultant list of resources (must be empty). + * @preproc: The caller's preprocessing routine. + * @preproc_data: Pointer passed to the caller's preprocessing routine. + * + * Evaluate the _CRS method for the given device node and process its output by + * (1) executing the @preproc() rountine provided by the caller, passing the + * resource pointer and @preproc_data to it as arguments, for each ACPI resource + * returned and (2) converting all of the returned ACPI resources into struct + * resource objects if possible. If the return value of @preproc() in step (1) + * is different from 0, step (2) is not applied to the given ACPI resource and + * if that value is negative, the whole processing is aborted and that value is + * returned as the final error code. + * + * The resultant struct resource objects are put on the list pointed to by + * @list, that must be empty initially, as members of struct resource_list_entry + * objects. Callers of this routine should use %acpi_dev_free_resource_list() to + * free that list. + * + * The number of resources in the output list is returned on success, an error + * code reflecting the error condition is returned otherwise. + */ +int acpi_dev_get_resources(struct acpi_device *adev, struct list_head *list, + int (*preproc)(struct acpi_resource *, void *), + void *preproc_data) +{ + struct res_proc_context c; + acpi_handle not_used; + acpi_status status; + + if (!adev || !adev->handle || !list_empty(list)) + return -EINVAL; + + status = acpi_get_handle(adev->handle, METHOD_NAME__CRS, ¬_used); + if (ACPI_FAILURE(status)) + return 0; + + c.list = list; + c.preproc = preproc; + c.preproc_data = preproc_data; + c.count = 0; + c.error = 0; + status = acpi_walk_resources(adev->handle, METHOD_NAME__CRS, + acpi_dev_process_resource, &c); + if (ACPI_FAILURE(status)) { + acpi_dev_free_resource_list(list); + return c.error ? c.error : -EIO; + } + + return c.count; +} +EXPORT_SYMBOL_GPL(acpi_dev_get_resources); diff --git a/include/linux/acpi.h b/include/linux/acpi.h index 16fcaf8dad3d..32fbc4e73a56 100644 --- a/include/linux/acpi.h +++ b/include/linux/acpi.h @@ -261,6 +261,16 @@ unsigned long acpi_dev_irq_flags(u8 triggering, u8 polarity, u8 shareable); bool acpi_dev_resource_interrupt(struct acpi_resource *ares, int index, struct resource *res); +struct resource_list_entry { + struct list_head node; + struct resource res; +}; + +void acpi_dev_free_resource_list(struct list_head *list); +int acpi_dev_get_resources(struct acpi_device *adev, struct list_head *list, + int (*preproc)(struct acpi_resource *, void *), + void *preproc_data); + int acpi_check_resource_conflict(const struct resource *res); int acpi_check_region(resource_size_t start, resource_size_t n, -- cgit v1.2.3 From 863f9f30e6c1e30cb19a0cd17c5cf8879257dfd7 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 21 Nov 2012 00:21:59 +0100 Subject: ACPI / platform: Initialize ACPI handles of platform devices in advance The current platform device creation and registration code in acpi_create_platform_device() is quite convoluted. This function takes an ACPI device node as an argument and eventually calls platform_device_register_resndata() to create and register a platform device object on the basis of the information contained in that code. However, it doesn't associate the new platform device with the ACPI node directly, but instead it relies on acpi_platform_notify(), called from within device_add(), to find that ACPI node again with the help of acpi_platform_find_device() and acpi_platform_match() and then attach the new platform device to it. This causes an additional ACPI namespace walk to happen and is clearly suboptimal. Use the observation that it is now possible to initialize the ACPI handle of a device before calling device_add() for it to make this code more straightforward. Namely, add a new field to struct platform_device_info allowing us to pass the ACPI handle of interest to platform_device_register_full(), which will then use it to initialize the new device's ACPI handle before registering it. This will cause acpi_platform_notify() to use the ACPI handle from the device structure directly instead of using the .find_device() routine provided by the device's bus type. In consequence, acpi_platform_bus, acpi_platform_find_device(), and acpi_platform_match() are not necessary any more, so remove them. Signed-off-by: Rafael J. Wysocki Reviewed-by: Mika Westerberg Acked-by: Greg Kroah-Hartman --- drivers/acpi/acpi_platform.c | 76 ++++++----------------------------------- drivers/base/platform.c | 2 ++ include/linux/platform_device.h | 1 + 3 files changed, 13 insertions(+), 66 deletions(-) (limited to 'drivers/acpi/acpi_platform.c') diff --git a/drivers/acpi/acpi_platform.c b/drivers/acpi/acpi_platform.c index 7ac20d8b8f07..b7df9b197bcf 100644 --- a/drivers/acpi/acpi_platform.c +++ b/drivers/acpi/acpi_platform.c @@ -33,7 +33,7 @@ struct platform_device *acpi_create_platform_device(struct acpi_device *adev) { struct platform_device *pdev = NULL; struct acpi_device *acpi_parent; - struct device *parent = NULL; + struct platform_device_info pdevinfo; struct resource_list_entry *rentry; struct list_head resource_list; struct resource *resources; @@ -60,11 +60,13 @@ struct platform_device *acpi_create_platform_device(struct acpi_device *adev) acpi_dev_free_resource_list(&resource_list); + memset(&pdevinfo, 0, sizeof(pdevinfo)); /* * If the ACPI node has a parent and that parent has a physical device * attached to it, that physical device should be the parent of the * platform device we are about to create. */ + pdevinfo.parent = NULL; acpi_parent = adev->parent; if (acpi_parent) { struct acpi_device_physical_node *entry; @@ -76,12 +78,16 @@ struct platform_device *acpi_create_platform_device(struct acpi_device *adev) entry = list_first_entry(list, struct acpi_device_physical_node, node); - parent = entry->dev; + pdevinfo.parent = entry->dev; } mutex_unlock(&acpi_parent->physical_node_lock); } - pdev = platform_device_register_resndata(parent, dev_name(&adev->dev), - -1, resources, count, NULL, 0); + pdevinfo.name = dev_name(&adev->dev); + pdevinfo.id = -1; + pdevinfo.res = resources; + pdevinfo.num_res = count; + pdevinfo.acpi_node.handle = adev->handle; + pdev = platform_device_register_full(&pdevinfo); if (IS_ERR(pdev)) { dev_err(&adev->dev, "platform device creation failed: %ld\n", PTR_ERR(pdev)); @@ -94,65 +100,3 @@ struct platform_device *acpi_create_platform_device(struct acpi_device *adev) kfree(resources); return pdev; } - -static acpi_status acpi_platform_match(acpi_handle handle, u32 depth, - void *data, void **return_value) -{ - struct platform_device *pdev = data; - struct acpi_device *adev; - acpi_status status; - - status = acpi_bus_get_device(handle, &adev); - if (ACPI_FAILURE(status)) - return status; - - /* Skip ACPI devices that have physical device attached */ - if (adev->physical_node_count) - return AE_OK; - - if (!strcmp(dev_name(&pdev->dev), dev_name(&adev->dev))) { - *(acpi_handle *)return_value = handle; - return AE_CTRL_TERMINATE; - } - - return AE_OK; -} - -static int acpi_platform_find_device(struct device *dev, acpi_handle *handle) -{ - struct platform_device *pdev = to_platform_device(dev); - char *name, *tmp, *hid; - - /* - * The platform device is named using the ACPI device name - * _HID:INSTANCE so we strip the INSTANCE out in order to find the - * correct device using its _HID. - */ - name = kstrdup(dev_name(dev), GFP_KERNEL); - if (!name) - return -ENOMEM; - - tmp = name; - hid = strsep(&tmp, ":"); - if (!hid) { - kfree(name); - return -ENODEV; - } - - *handle = NULL; - acpi_get_devices(hid, acpi_platform_match, pdev, handle); - - kfree(name); - return *handle ? 0 : -ENODEV; -} - -static struct acpi_bus_type acpi_platform_bus = { - .bus = &platform_bus_type, - .find_device = acpi_platform_find_device, -}; - -static int __init acpi_platform_init(void) -{ - return register_acpi_bus_type(&acpi_platform_bus); -} -arch_initcall(acpi_platform_init); diff --git a/drivers/base/platform.c b/drivers/base/platform.c index 7de29ebfce7f..49fd96e23460 100644 --- a/drivers/base/platform.c +++ b/drivers/base/platform.c @@ -437,6 +437,7 @@ struct platform_device *platform_device_register_full( goto err_alloc; pdev->dev.parent = pdevinfo->parent; + ACPI_HANDLE_SET(&pdev->dev, pdevinfo->acpi_node.handle); if (pdevinfo->dma_mask) { /* @@ -467,6 +468,7 @@ struct platform_device *platform_device_register_full( ret = platform_device_add(pdev); if (ret) { err: + ACPI_HANDLE_SET(&pdev->dev, NULL); kfree(pdev->dev.dma_mask); err_alloc: diff --git a/include/linux/platform_device.h b/include/linux/platform_device.h index 5711e9525a2a..a9ded9a3c175 100644 --- a/include/linux/platform_device.h +++ b/include/linux/platform_device.h @@ -55,6 +55,7 @@ extern int platform_add_devices(struct platform_device **, int); struct platform_device_info { struct device *parent; + struct acpi_dev_node acpi_node; const char *name; int id; -- cgit v1.2.3 From 5923f986ac191a32640429d054b94af185ec73a8 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Mon, 26 Nov 2012 10:35:07 +0100 Subject: ACPI / platform: include missed header into acpi_platform.c The internal.h declares the acpi_create_platform_device(). Without that include we get a following warning: drivers/acpi/acpi_platform.c:133:24: warning: symbol 'acpi_create_platform_device' was not declared. Should it be static? Signed-off-by: Andy Shevchenko Acked-by: Mika Westerberg Signed-off-by: Rafael J. Wysocki --- drivers/acpi/acpi_platform.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers/acpi/acpi_platform.c') diff --git a/drivers/acpi/acpi_platform.c b/drivers/acpi/acpi_platform.c index b7df9b197bcf..db129b9f52cb 100644 --- a/drivers/acpi/acpi_platform.c +++ b/drivers/acpi/acpi_platform.c @@ -17,6 +17,8 @@ #include #include +#include "internal.h" + ACPI_MODULE_NAME("platform"); /** -- cgit v1.2.3