[PATCH 3/3] ACPI: Evaluate _CRS while creating device node objects

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



From: Rafael J. Wysocki <rafael.j.wysocki@xxxxxxxxx>

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.

For this reason, add code to the ACPI core to execute _CRS once,
when the struct acpi_device object is created for a given device
node, and attach a list of ACPI resources returned by _CRS to that
object for future processing.

Convert the ACPI code that creates platform device objects to using
the new resources list instead of executing acpi_walk_resources() by
itself, which makes it much more straightforward and easier to
follow.

Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@xxxxxxxxx>
---
 drivers/acpi/acpi_platform.c |   90 ++++++++++++-------------------------------
 drivers/acpi/resource.c      |   12 +++++
 drivers/acpi/scan.c          |   56 ++++++++++++++++++++++++++
 include/acpi/acpi_bus.h      |    6 ++
 include/linux/acpi.h         |    1 
 5 files changed, 102 insertions(+), 63 deletions(-)

Index: linux/include/acpi/acpi_bus.h
===================================================================
--- linux.orig/include/acpi/acpi_bus.h
+++ linux/include/acpi/acpi_bus.h
@@ -259,6 +259,11 @@ struct acpi_device_physical_node {
 	struct device *dev;
 };
 
+struct acpi_resource_list_entry {
+	struct list_head node;
+	struct acpi_resource resource;
+};
+
 /* set maximum of physical nodes to 32 for expansibility */
 #define ACPI_MAX_PHYSICAL_NODE	32
 
@@ -268,6 +273,7 @@ struct acpi_device {
 	acpi_handle handle;		/* no handle for fixed hardware */
 	struct acpi_device *parent;
 	struct list_head children;
+	struct list_head resources;	/* Device resources. */
 	struct list_head node;
 	struct list_head wakeup_list;
 	struct acpi_device_status status;
Index: linux/drivers/acpi/scan.c
===================================================================
--- linux.orig/drivers/acpi/scan.c
+++ linux/drivers/acpi/scan.c
@@ -382,6 +382,52 @@ static void acpi_device_remove_files(str
 			ACPI Bus operations
    -------------------------------------------------------------------------- */
 
+static void acpi_bus_drop_resources(struct acpi_device *adev)
+{
+	struct acpi_resource_list_entry *entry, *s;
+
+	list_for_each_entry_safe(entry, s, &adev->resources, node) {
+		list_del(&entry->node);
+		kfree(entry);
+	}
+}
+
+static acpi_status acpi_bus_add_resource(struct acpi_resource *res,
+					 void *context)
+{
+	struct list_head *list = context;
+	struct acpi_resource_list_entry *entry;
+
+	entry = kzalloc(sizeof(*entry), GFP_KERNEL);
+	if (!entry)
+		return AE_NO_MEMORY;
+
+	entry->resource = *res;
+	INIT_LIST_HEAD(&entry->node);
+	list_add_tail(&entry->node, list);
+	return AE_OK;
+}
+
+static int acpi_bus_get_resources(struct acpi_device *adev)
+{
+	acpi_status status;
+	acpi_handle not_used;
+	int ret = 0;
+
+	status = acpi_get_handle(adev->handle, METHOD_NAME__CRS, &not_used);
+	if (ACPI_FAILURE(status))
+		return 0;
+
+	status = acpi_walk_resources(adev->handle, METHOD_NAME__CRS,
+				     acpi_bus_add_resource, &adev->resources);
+	if (ACPI_FAILURE(status)) {
+		acpi_bus_drop_resources(adev);
+		ret = status == AE_NO_MEMORY ? -ENOMEM : -EIO;
+	}
+
+	return ret;
+}
+
 static const struct acpi_device_id *__acpi_match_device(
 	struct acpi_device *device, const struct acpi_device_id *ids)
 {
@@ -681,6 +727,7 @@ static void acpi_device_unregister(struc
 
 	acpi_device_remove_files(device);
 	device_unregister(&device->dev);
+	acpi_bus_drop_resources(device);
 }
 
 /* --------------------------------------------------------------------------
@@ -1412,6 +1459,15 @@ static int acpi_add_single_object(struct
 	acpi_device_set_id(device);
 
 	/*
+	 * Device Resources
+	 * ----------------
+	 */
+	INIT_LIST_HEAD(&device->resources);
+	result = acpi_bus_get_resources(device);
+	if (result)
+		goto end;
+
+	/*
 	 * Power Management
 	 * ----------------
 	 */
Index: linux/drivers/acpi/resource.c
===================================================================
--- linux.orig/drivers/acpi/resource.c
+++ linux/drivers/acpi/resource.c
@@ -391,3 +391,15 @@ bool acpi_dev_resource_interrupt(struct
 	return true;
 }
 EXPORT_SYMBOL_GPL(acpi_dev_resource_interrupt);
+
+unsigned int acpi_dev_resource_count(struct acpi_resource *ares)
+{
+	switch (ares->type) {
+	case ACPI_RESOURCE_TYPE_IRQ:
+		return ares->data.irq.interrupt_count;
+	case ACPI_RESOURCE_TYPE_EXTENDED_IRQ:
+		return ares->data.extended_irq.interrupt_count;
+	}
+	return 1;
+}
+EXPORT_SYMBOL_GPL(acpi_dev_resource_count);
Index: linux/drivers/acpi/acpi_platform.c
===================================================================
--- linux.orig/drivers/acpi/acpi_platform.c
+++ linux/drivers/acpi/acpi_platform.c
@@ -19,59 +19,26 @@
 
 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)
+static unsigned int acpi_platform_add_resource(struct acpi_resource *res,
+					       struct resource *r)
 {
-	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;
-	}
+	    || acpi_dev_resource_ext_address_space(res, r))
+		return 1;
+
 	if (acpi_dev_resource_interrupt(res, 0, r)) {
-		int i;
+		unsigned int i;
 
 		r++;
 		for (i = 1; acpi_dev_resource_interrupt(res, i, r); i++)
 			r++;
 
-		ri->cur += i;
+		return i;
 	}
-	return AE_OK;
+
+	return 0;
 }
 
 /**
@@ -89,35 +56,32 @@ struct platform_device *acpi_create_plat
 	struct platform_device *pdev = NULL;
 	struct acpi_device *acpi_parent;
 	struct device *parent = NULL;
-	struct resource_info ri;
-	acpi_status status;
+	struct acpi_resource_list_entry *entry;
+	struct resource *resources;
+	unsigned 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)
+	count = 0;
+	list_for_each_entry(entry, &adev->resources, node)
+		count += acpi_dev_resource_count(&entry->resource);
+
+	if (!count)
 		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 = kzalloc(count * sizeof(struct resource), GFP_KERNEL);
+	if (!resources) {
+		dev_err(&adev->dev, "No 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;
-	}
+	count = 0;
+	list_for_each_entry(entry, &adev->resources, node)
+		count += acpi_platform_add_resource(&entry->resource,
+						    resources + count);
 
 	/*
 	 * If the ACPI node has a parent and that parent has a physical device
@@ -139,8 +103,9 @@ struct platform_device *acpi_create_plat
 		}
 		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 +115,7 @@ struct platform_device *acpi_create_plat
 			dev_name(&pdev->dev));
 	}
 
- out:
-	kfree(ri.res);
+	kfree(resources);
 	return pdev;
 }
 
Index: linux/include/linux/acpi.h
===================================================================
--- linux.orig/include/linux/acpi.h
+++ linux/include/linux/acpi.h
@@ -260,6 +260,7 @@ bool acpi_dev_resource_ext_address_space
 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);
+unsigned int acpi_dev_resource_count(struct acpi_resource *ares);
 
 int acpi_check_resource_conflict(const struct resource *res);
 

--
To unsubscribe from this list: send the line "unsubscribe linux-acpi" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[Index of Archives]     [Linux IBM ACPI]     [Linux Power Management]     [Linux Kernel]     [Linux Laptop]     [Kernel Newbies]     [Share Photos]     [Security]     [Netfilter]     [Bugtraq]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Samba]     [Video 4 Linux]     [Device Mapper]     [Linux Resources]

  Powered by Linux