From 06f64c8f239a47b359c60301914c783b56b32c13 Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Wed, 31 Oct 2012 22:44:33 +0100 Subject: driver core / ACPI: Move ACPI support to core device and driver types With ACPI 5 we are starting to see devices that don't natively support discovery but can be enumerated with the help of the ACPI namespace. Typically, these devices can be represented in the Linux device driver model as platform devices or some serial bus devices, like SPI or I2C devices. Since we want to re-use existing drivers for those devices, we need a way for drivers to specify the ACPI IDs of supported devices, so that they can be matched against device nodes in the ACPI namespace. To this end, it is sufficient to add a pointer to an array of supported ACPI device IDs, that can be provided by the driver, to struct device. Moreover, things like ACPI power management need to have access to the ACPI handle of each supported device, because that handle is used to invoke AML methods associated with the corresponding ACPI device node. The ACPI handles of devices are now stored in the archdata member structure of struct device whose definition depends on the architecture and includes the ACPI handle only on x86 and ia64. Since the pointer to an array of supported ACPI IDs is added to struct device_driver in an architecture-independent way, it is logical to move the ACPI handle from archdata to struct device itself at the same time. This also makes code more straightforward in some places and follows the example of Device Trees that have a poiter to struct device_node in there too. This changeset is based on Mika Westerberg's work. 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 diff --git a/arch/ia64/include/asm/device.h b/arch/ia64/include/asm/device.h index d05e78f..f69c32f 100644 --- a/arch/ia64/include/asm/device.h +++ b/arch/ia64/include/asm/device.h @@ -7,9 +7,6 @@ #define _ASM_IA64_DEVICE_H struct dev_archdata { -#ifdef CONFIG_ACPI - void *acpi_handle; -#endif #ifdef CONFIG_INTEL_IOMMU void *iommu; /* hook for IOMMU specific extension */ #endif diff --git a/arch/x86/include/asm/device.h b/arch/x86/include/asm/device.h index 93e1c55..03dd729 100644 --- a/arch/x86/include/asm/device.h +++ b/arch/x86/include/asm/device.h @@ -2,9 +2,6 @@ #define _ASM_X86_DEVICE_H struct dev_archdata { -#ifdef CONFIG_ACPI - void *acpi_handle; -#endif #ifdef CONFIG_X86_DEV_DMA_OPS struct dma_map_ops *dma_ops; #endif diff --git a/drivers/acpi/glue.c b/drivers/acpi/glue.c index 0837308..2f3849a 100644 --- a/drivers/acpi/glue.c +++ b/drivers/acpi/glue.c @@ -134,7 +134,7 @@ static int acpi_bind_one(struct device *dev, acpi_handle handle) char physical_node_name[sizeof(PHYSICAL_NODE_STRING) + 2]; int retval = -EINVAL; - if (dev->archdata.acpi_handle) { + if (dev->acpi_handle) { dev_warn(dev, "Drivers changed 'acpi_handle'\n"); return -EINVAL; } @@ -169,7 +169,7 @@ static int acpi_bind_one(struct device *dev, acpi_handle handle) acpi_dev->physical_node_count++; mutex_unlock(&acpi_dev->physical_node_lock); - dev->archdata.acpi_handle = handle; + dev->acpi_handle = handle; if (!physical_node->node_id) strcpy(physical_node_name, PHYSICAL_NODE_STRING); @@ -198,11 +198,10 @@ static int acpi_unbind_one(struct device *dev) acpi_status status; struct list_head *node, *next; - if (!dev->archdata.acpi_handle) + if (!dev->acpi_handle) return 0; - status = acpi_bus_get_device(dev->archdata.acpi_handle, - &acpi_dev); + status = acpi_bus_get_device(dev->acpi_handle, &acpi_dev); if (ACPI_FAILURE(status)) goto err; @@ -228,7 +227,7 @@ static int acpi_unbind_one(struct device *dev) sysfs_remove_link(&acpi_dev->dev.kobj, physical_node_name); sysfs_remove_link(&dev->kobj, "firmware_node"); - dev->archdata.acpi_handle = NULL; + dev->acpi_handle = NULL; /* acpi_bind_one increase refcnt by one */ put_device(dev); kfree(entry); @@ -269,8 +268,7 @@ static int acpi_platform_notify(struct device *dev) if (!ret) { struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; - acpi_get_name(dev->archdata.acpi_handle, - ACPI_FULL_PATHNAME, &buffer); + acpi_get_name(dev->acpi_handle, ACPI_FULL_PATHNAME, &buffer); DBG("Device %s -> %s\n", dev_name(dev), (char *)buffer.pointer); kfree(buffer.pointer); } else diff --git a/include/acpi/acpi_bus.h b/include/acpi/acpi_bus.h index 0daa0fb..bb1537c 100644 --- a/include/acpi/acpi_bus.h +++ b/include/acpi/acpi_bus.h @@ -410,7 +410,7 @@ acpi_handle acpi_get_child(acpi_handle, u64); int acpi_is_root_bridge(acpi_handle); acpi_handle acpi_get_pci_rootbridge_handle(unsigned int, unsigned int); struct acpi_pci_root *acpi_pci_find_root(acpi_handle handle); -#define DEVICE_ACPI_HANDLE(dev) ((acpi_handle)((dev)->archdata.acpi_handle)) +#define DEVICE_ACPI_HANDLE(dev) ((acpi_handle)((dev)->acpi_handle)) int acpi_enable_wakeup_device_power(struct acpi_device *dev, int state); int acpi_disable_wakeup_device_power(struct acpi_device *dev); diff --git a/include/linux/device.h b/include/linux/device.h index 86ef6ab..cc3aee5 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -190,6 +190,7 @@ extern struct klist *bus_get_device_klist(struct bus_type *bus); * @mod_name: Used for built-in modules. * @suppress_bind_attrs: Disables bind/unbind via sysfs. * @of_match_table: The open firmware table. + * @acpi_match_table: The ACPI match table. * @probe: Called to query the existence of a specific device, * whether this driver can work with it, and bind the driver * to a specific device. @@ -223,6 +224,7 @@ struct device_driver { bool suppress_bind_attrs; /* disables bind/unbind via sysfs */ const struct of_device_id *of_match_table; + const struct acpi_device_id *acpi_match_table; int (*probe) (struct device *dev); int (*remove) (struct device *dev); @@ -616,6 +618,7 @@ struct device_dma_parameters { * @dma_mem: Internal for coherent mem override. * @archdata: For arch-specific additions. * @of_node: Associated device tree node. + * @acpi_handle: Associated ACPI device node's namespace handle. * @devt: For creating the sysfs "dev". * @id: device instance * @devres_lock: Spinlock to protect the resource of the device. @@ -680,6 +683,7 @@ struct device { struct dev_archdata archdata; struct device_node *of_node; /* associated device tree node */ + void *acpi_handle; /* associated ACPI device node */ dev_t devt; /* dev_t, creates the sysfs "dev" */ u32 id; /* device instance */ -- cgit v0.10.2 From cf761af9ee0f2c172710ad2b7ca029016b5d4c45 Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Wed, 31 Oct 2012 22:44:41 +0100 Subject: ACPI: Provide generic functions for matching ACPI device nodes Introduce function acpi_match_device() allowing callers to match struct device objects with populated acpi_handle fields against arrays of ACPI device IDs. Also introduce function acpi_driver_match_device() using acpi_match_device() internally and allowing callers to match a struct device object against an array of ACPI device IDs provided by a device driver. Additionally, introduce macro ACPI_PTR() that may be used by device drivers to escape pointers to data structures whose definitions depend on CONFIG_ACPI. 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 diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index 1fcb867..a0dfdff 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -340,8 +340,8 @@ static void acpi_device_remove_files(struct acpi_device *dev) ACPI Bus operations -------------------------------------------------------------------------- */ -int acpi_match_device_ids(struct acpi_device *device, - const struct acpi_device_id *ids) +static const struct acpi_device_id *__acpi_match_device( + struct acpi_device *device, const struct acpi_device_id *ids) { const struct acpi_device_id *id; struct acpi_hardware_id *hwid; @@ -351,14 +351,44 @@ int acpi_match_device_ids(struct acpi_device *device, * driver for it. */ if (!device->status.present) - return -ENODEV; + return NULL; for (id = ids; id->id[0]; id++) list_for_each_entry(hwid, &device->pnp.ids, list) if (!strcmp((char *) id->id, hwid->id)) - return 0; + return id; + + return NULL; +} - return -ENOENT; +/** + * acpi_match_device - Match a struct device against a given list of ACPI IDs + * @ids: Array of struct acpi_device_id object to match against. + * @dev: The device structure to match. + * + * Check if @dev has a valid ACPI handle and if there is a struct acpi_device + * object for that handle and use that object to match against a given list of + * device IDs. + * + * Return a pointer to the first matching ID on success or %NULL on failure. + */ +const struct acpi_device_id *acpi_match_device(const struct acpi_device_id *ids, + const struct device *dev) +{ + struct acpi_device *adev; + + if (!ids || !dev->acpi_handle + || ACPI_FAILURE(acpi_bus_get_device(dev->acpi_handle, &adev))) + return NULL; + + return __acpi_match_device(adev, ids); +} +EXPORT_SYMBOL_GPL(acpi_match_device); + +int acpi_match_device_ids(struct acpi_device *device, + const struct acpi_device_id *ids) +{ + return __acpi_match_device(device, ids) ? 0 : -ENOENT; } EXPORT_SYMBOL(acpi_match_device_ids); diff --git a/include/linux/acpi.h b/include/linux/acpi.h index 90be989..48761cb 100644 --- a/include/linux/acpi.h +++ b/include/linux/acpi.h @@ -26,6 +26,7 @@ #define _LINUX_ACPI_H #include /* for struct resource */ +#include #ifdef CONFIG_ACPI @@ -364,6 +365,17 @@ extern int acpi_nvs_register(__u64 start, __u64 size); extern int acpi_nvs_for_each_region(int (*func)(__u64, __u64, void *), void *data); +const struct acpi_device_id *acpi_match_device(const struct acpi_device_id *ids, + const struct device *dev); + +static inline bool acpi_driver_match_device(struct device *dev, + const struct device_driver *drv) +{ + return !!acpi_match_device(drv->acpi_match_table, dev); +} + +#define ACPI_PTR(_ptr) (_ptr) + #else /* !CONFIG_ACPI */ #define acpi_disabled 1 @@ -418,6 +430,22 @@ static inline int acpi_nvs_for_each_region(int (*func)(__u64, __u64, void *), return 0; } +struct acpi_device_id; + +static inline const struct acpi_device_id *acpi_match_device( + const struct acpi_device_id *ids, const struct device *dev) +{ + return NULL; +} + +static inline bool acpi_driver_match_device(struct device *dev, + const struct device_driver *drv) +{ + return false; +} + +#define ACPI_PTR(_ptr) (NULL) + #endif /* !CONFIG_ACPI */ #ifdef CONFIG_ACPI -- cgit v0.10.2 From 35e92b78c1d327b1624e94d1c9c65ea7065d6b95 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Wed, 31 Oct 2012 22:44:48 +0100 Subject: ACPI / x86: Export acpi_[un]register_gsi() These functions might be called from modules as well so make sure they are exported. In addition, implement empty version of acpi_unregister_gsi() and remove the one from pci_irq.c. Signed-off-by: Andy Shevchenko 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 diff --git a/arch/x86/kernel/acpi/boot.c b/arch/x86/kernel/acpi/boot.c index e651f7a..e48cafc 100644 --- a/arch/x86/kernel/acpi/boot.c +++ b/arch/x86/kernel/acpi/boot.c @@ -574,6 +574,12 @@ int acpi_register_gsi(struct device *dev, u32 gsi, int trigger, int polarity) return irq; } +EXPORT_SYMBOL_GPL(acpi_register_gsi); + +void acpi_unregister_gsi(u32 gsi) +{ +} +EXPORT_SYMBOL_GPL(acpi_unregister_gsi); void __init acpi_set_irq_model_pic(void) { diff --git a/drivers/acpi/pci_irq.c b/drivers/acpi/pci_irq.c index 0eefa12..1be25a5 100644 --- a/drivers/acpi/pci_irq.c +++ b/drivers/acpi/pci_irq.c @@ -495,11 +495,6 @@ int acpi_pci_irq_enable(struct pci_dev *dev) return 0; } -/* FIXME: implement x86/x86_64 version */ -void __attribute__ ((weak)) acpi_unregister_gsi(u32 i) -{ -} - void acpi_pci_irq_disable(struct pci_dev *dev) { struct acpi_prt_entry *entry; -- cgit v0.10.2 From a42b9bfe959519772fd8d97557c760f7cda4d325 Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Wed, 31 Oct 2012 22:44:55 +0100 Subject: ACPI / ia64: Export acpi_[un]register_gsi() These functions might be called from modules as well so make sure they are exported. 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 diff --git a/arch/ia64/kernel/acpi.c b/arch/ia64/kernel/acpi.c index 44057885..e9682f5 100644 --- a/arch/ia64/kernel/acpi.c +++ b/arch/ia64/kernel/acpi.c @@ -633,6 +633,7 @@ int acpi_register_gsi(struct device *dev, u32 gsi, int triggering, int polarity) ACPI_EDGE_SENSITIVE) ? IOSAPIC_EDGE : IOSAPIC_LEVEL); } +EXPORT_SYMBOL_GPL(acpi_register_gsi); void acpi_unregister_gsi(u32 gsi) { @@ -644,6 +645,7 @@ void acpi_unregister_gsi(u32 gsi) iosapic_unregister_intr(gsi); } +EXPORT_SYMBOL_GPL(acpi_unregister_gsi); static int __init acpi_parse_fadt(struct acpi_table_header *table) { -- cgit v0.10.2 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 diff --git a/drivers/acpi/Makefile b/drivers/acpi/Makefile index 82422fe..227c82b 100644 --- a/drivers/acpi/Makefile +++ b/drivers/acpi/Makefile @@ -36,6 +36,7 @@ acpi-y += processor_core.o acpi-y += ec.o acpi-$(CONFIG_ACPI_DOCK) += dock.o acpi-y += pci_root.o pci_link.o pci_irq.o pci_bind.o +acpi-y += acpi_platform.o acpi-y += power.o acpi-y += event.o acpi-y += sysfs.o diff --git a/drivers/acpi/acpi_platform.c b/drivers/acpi/acpi_platform.c new file mode 100644 index 0000000..a5a2346 --- /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); diff --git a/drivers/acpi/internal.h b/drivers/acpi/internal.h index ca75b9c..57d41f6 100644 --- a/drivers/acpi/internal.h +++ b/drivers/acpi/internal.h @@ -93,4 +93,11 @@ static inline int suspend_nvs_save(void) { return 0; } static inline void suspend_nvs_restore(void) {} #endif +/*-------------------------------------------------------------------------- + Platform bus support + -------------------------------------------------------------------------- */ +struct platform_device; + +struct platform_device *acpi_create_platform_device(struct acpi_device *adev); + #endif /* _ACPI_INTERNAL_H_ */ diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index a0dfdff..d842569 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -29,6 +29,15 @@ extern struct acpi_device *acpi_root; static const char *dummy_hid = "device"; +/* + * The following ACPI IDs are known to be suitable for representing as + * platform devices. + */ +static const struct acpi_device_id acpi_platform_device_ids[] = { + + { } +}; + static LIST_HEAD(acpi_device_list); static LIST_HEAD(acpi_bus_id_list); DEFINE_MUTEX(acpi_device_lock); @@ -1513,8 +1522,13 @@ static acpi_status acpi_bus_check_add(acpi_handle handle, u32 lvl, */ device = NULL; acpi_bus_get_device(handle, &device); - if (ops->acpi_op_add && !device) + if (ops->acpi_op_add && !device) { acpi_add_single_object(&device, handle, type, sta, ops); + /* Is the device a known good platform device? */ + if (device + && !acpi_match_device_ids(device, acpi_platform_device_ids)) + acpi_create_platform_device(device); + } if (!device) return AE_CTRL_DEPTH; diff --git a/drivers/base/platform.c b/drivers/base/platform.c index 72c776f..7de29eb 100644 --- a/drivers/base/platform.c +++ b/drivers/base/platform.c @@ -21,6 +21,7 @@ #include #include #include +#include #include "base.h" #include "power/power.h" @@ -709,6 +710,10 @@ static int platform_match(struct device *dev, struct device_driver *drv) if (of_driver_match_device(dev, drv)) return 1; + /* Then try ACPI style match */ + if (acpi_driver_match_device(dev, drv)) + return 1; + /* Then try to match against the id table */ if (pdrv->id_table) return platform_match_id(pdrv->id_table, pdev) != NULL; -- cgit v0.10.2 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 diff --git a/drivers/acpi/acpi_platform.c b/drivers/acpi/acpi_platform.c index a5a2346..dbb31d6 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 v0.10.2 From 046d9ce6820e99087e81511284045eada94950e8 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Thu, 15 Nov 2012 00:30:01 +0100 Subject: ACPI: Move device resources interpretation code from PNP to ACPI core Move some code used for parsing ACPI device resources from the PNP subsystem to the ACPI core, so that other bus types (platform, SPI, I2C) can use the same routines for parsing resources in a consistent way, without duplicating code. Signed-off-by: Rafael J. Wysocki Reviewed-by: Mika Westerberg Tested-by: Mika Westerberg diff --git a/drivers/acpi/Makefile b/drivers/acpi/Makefile index 227c82b..3223edf 100644 --- a/drivers/acpi/Makefile +++ b/drivers/acpi/Makefile @@ -32,6 +32,7 @@ acpi-$(CONFIG_ACPI_SLEEP) += proc.o # acpi-y += bus.o glue.o acpi-y += scan.o +acpi-y += resource.o acpi-y += processor_core.o acpi-y += ec.o acpi-$(CONFIG_ACPI_DOCK) += dock.o diff --git a/drivers/acpi/resource.c b/drivers/acpi/resource.c new file mode 100644 index 0000000..3e7fd34 --- /dev/null +++ b/drivers/acpi/resource.c @@ -0,0 +1,393 @@ +/* + * drivers/acpi/resource.c - ACPI device resources interpretation. + * + * Copyright (C) 2012, Intel Corp. + * Author: 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. + * + * 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, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ + +#include +#include +#include +#include + +#ifdef CONFIG_X86 +#define valid_IRQ(i) (((i) != 0) && ((i) != 2)) +#else +#define valid_IRQ(i) (true) +#endif + +static unsigned long acpi_dev_memresource_flags(u64 len, u8 write_protect, + bool window) +{ + unsigned long flags = IORESOURCE_MEM; + + if (len == 0) + flags |= IORESOURCE_DISABLED; + + if (write_protect == ACPI_READ_WRITE_MEMORY) + flags |= IORESOURCE_MEM_WRITEABLE; + + if (window) + flags |= IORESOURCE_WINDOW; + + return flags; +} + +static void acpi_dev_get_memresource(struct resource *res, u64 start, u64 len, + u8 write_protect) +{ + res->start = start; + res->end = start + len - 1; + res->flags = acpi_dev_memresource_flags(len, write_protect, false); +} + +/** + * acpi_dev_resource_memory - Extract ACPI memory resource information. + * @ares: Input ACPI resource object. + * @res: Output generic resource object. + * + * Check if the given ACPI resource object represents a memory resource and + * if that's the case, use the information in it to populate the generic + * resource object pointed to by @res. + */ +bool acpi_dev_resource_memory(struct acpi_resource *ares, struct resource *res) +{ + struct acpi_resource_memory24 *memory24; + struct acpi_resource_memory32 *memory32; + struct acpi_resource_fixed_memory32 *fixed_memory32; + + switch (ares->type) { + case ACPI_RESOURCE_TYPE_MEMORY24: + memory24 = &ares->data.memory24; + acpi_dev_get_memresource(res, memory24->minimum, + memory24->address_length, + memory24->write_protect); + break; + case ACPI_RESOURCE_TYPE_MEMORY32: + memory32 = &ares->data.memory32; + acpi_dev_get_memresource(res, memory32->minimum, + memory32->address_length, + memory32->write_protect); + break; + case ACPI_RESOURCE_TYPE_FIXED_MEMORY32: + fixed_memory32 = &ares->data.fixed_memory32; + acpi_dev_get_memresource(res, fixed_memory32->address, + fixed_memory32->address_length, + fixed_memory32->write_protect); + break; + default: + return false; + } + return true; +} +EXPORT_SYMBOL_GPL(acpi_dev_resource_memory); + +static unsigned int acpi_dev_ioresource_flags(u64 start, u64 end, u8 io_decode, + bool window) +{ + int flags = IORESOURCE_IO; + + if (io_decode == ACPI_DECODE_16) + flags |= IORESOURCE_IO_16BIT_ADDR; + + if (start > end || end >= 0x10003) + flags |= IORESOURCE_DISABLED; + + if (window) + flags |= IORESOURCE_WINDOW; + + return flags; +} + +static void acpi_dev_get_ioresource(struct resource *res, u64 start, u64 len, + u8 io_decode) +{ + u64 end = start + len - 1; + + res->start = start; + res->end = end; + res->flags = acpi_dev_ioresource_flags(start, end, io_decode, false); +} + +/** + * acpi_dev_resource_io - Extract ACPI I/O resource information. + * @ares: Input ACPI resource object. + * @res: Output generic resource object. + * + * Check if the given ACPI resource object represents an I/O resource and + * if that's the case, use the information in it to populate the generic + * resource object pointed to by @res. + */ +bool acpi_dev_resource_io(struct acpi_resource *ares, struct resource *res) +{ + struct acpi_resource_io *io; + struct acpi_resource_fixed_io *fixed_io; + + switch (ares->type) { + case ACPI_RESOURCE_TYPE_IO: + io = &ares->data.io; + acpi_dev_get_ioresource(res, io->minimum, + io->address_length, + io->io_decode); + break; + case ACPI_RESOURCE_TYPE_FIXED_IO: + fixed_io = &ares->data.fixed_io; + acpi_dev_get_ioresource(res, fixed_io->address, + fixed_io->address_length, + ACPI_DECODE_10); + break; + default: + return false; + } + return true; +} +EXPORT_SYMBOL_GPL(acpi_dev_resource_io); + +/** + * acpi_dev_resource_address_space - Extract ACPI address space information. + * @ares: Input ACPI resource object. + * @res: Output generic resource object. + * + * Check if the given ACPI resource object represents an address space resource + * and if that's the case, use the information in it to populate the generic + * resource object pointed to by @res. + */ +bool acpi_dev_resource_address_space(struct acpi_resource *ares, + struct resource *res) +{ + acpi_status status; + struct acpi_resource_address64 addr; + bool window; + u64 len; + u8 io_decode; + + switch (ares->type) { + case ACPI_RESOURCE_TYPE_ADDRESS16: + case ACPI_RESOURCE_TYPE_ADDRESS32: + case ACPI_RESOURCE_TYPE_ADDRESS64: + break; + default: + return false; + } + + status = acpi_resource_to_address64(ares, &addr); + if (ACPI_FAILURE(status)) + return true; + + res->start = addr.minimum; + res->end = addr.maximum; + window = addr.producer_consumer == ACPI_PRODUCER; + + switch(addr.resource_type) { + case ACPI_MEMORY_RANGE: + len = addr.maximum - addr.minimum + 1; + res->flags = acpi_dev_memresource_flags(len, + addr.info.mem.write_protect, + window); + break; + case ACPI_IO_RANGE: + io_decode = addr.granularity == 0xfff ? + ACPI_DECODE_10 : ACPI_DECODE_16; + res->flags = acpi_dev_ioresource_flags(addr.minimum, + addr.maximum, + io_decode, window); + break; + case ACPI_BUS_NUMBER_RANGE: + res->flags = IORESOURCE_BUS; + break; + default: + res->flags = 0; + } + + return true; +} +EXPORT_SYMBOL_GPL(acpi_dev_resource_address_space); + +/** + * acpi_dev_resource_ext_address_space - Extract ACPI address space information. + * @ares: Input ACPI resource object. + * @res: Output generic resource object. + * + * Check if the given ACPI resource object represents an extended address space + * resource and if that's the case, use the information in it to populate the + * generic resource object pointed to by @res. + */ +bool acpi_dev_resource_ext_address_space(struct acpi_resource *ares, + struct resource *res) +{ + struct acpi_resource_extended_address64 *ext_addr; + bool window; + u64 len; + u8 io_decode; + + if (ares->type != ACPI_RESOURCE_TYPE_EXTENDED_ADDRESS64) + return false; + + ext_addr = &ares->data.ext_address64; + + res->start = ext_addr->minimum; + res->end = ext_addr->maximum; + window = ext_addr->producer_consumer == ACPI_PRODUCER; + + switch(ext_addr->resource_type) { + case ACPI_MEMORY_RANGE: + len = ext_addr->maximum - ext_addr->minimum + 1; + res->flags = acpi_dev_memresource_flags(len, + ext_addr->info.mem.write_protect, + window); + break; + case ACPI_IO_RANGE: + io_decode = ext_addr->granularity == 0xfff ? + ACPI_DECODE_10 : ACPI_DECODE_16; + res->flags = acpi_dev_ioresource_flags(ext_addr->minimum, + ext_addr->maximum, + io_decode, window); + break; + case ACPI_BUS_NUMBER_RANGE: + res->flags = IORESOURCE_BUS; + break; + default: + res->flags = 0; + } + + return true; +} +EXPORT_SYMBOL_GPL(acpi_dev_resource_ext_address_space); + +/** + * acpi_dev_irq_flags - Determine IRQ resource flags. + * @triggering: Triggering type as provided by ACPI. + * @polarity: Interrupt polarity as provided by ACPI. + * @shareable: Whether or not the interrupt is shareable. + */ +unsigned long acpi_dev_irq_flags(u8 triggering, u8 polarity, u8 shareable) +{ + unsigned long flags; + + if (triggering == ACPI_LEVEL_SENSITIVE) + flags = polarity == ACPI_ACTIVE_LOW ? + IORESOURCE_IRQ_LOWLEVEL : IORESOURCE_IRQ_HIGHLEVEL; + else + flags = polarity == ACPI_ACTIVE_LOW ? + IORESOURCE_IRQ_LOWEDGE : IORESOURCE_IRQ_HIGHEDGE; + + if (shareable == ACPI_SHARED) + flags |= IORESOURCE_IRQ_SHAREABLE; + + return flags | IORESOURCE_IRQ; +} +EXPORT_SYMBOL_GPL(acpi_dev_irq_flags); + +static void acpi_dev_irqresource_disabled(struct resource *res, u32 gsi) +{ + res->start = gsi; + res->end = gsi; + res->flags = IORESOURCE_IRQ | IORESOURCE_DISABLED; +} + +static void acpi_dev_get_irqresource(struct resource *res, u32 gsi, + u8 triggering, u8 polarity, u8 shareable) +{ + int irq, p, t; + + if (!valid_IRQ(gsi)) { + acpi_dev_irqresource_disabled(res, gsi); + return; + } + + /* + * In IO-APIC mode, use overrided attribute. Two reasons: + * 1. BIOS bug in DSDT + * 2. BIOS uses IO-APIC mode Interrupt Source Override + */ + if (!acpi_get_override_irq(gsi, &t, &p)) { + u8 trig = t ? ACPI_LEVEL_SENSITIVE : ACPI_EDGE_SENSITIVE; + u8 pol = p ? ACPI_ACTIVE_LOW : ACPI_ACTIVE_HIGH; + + if (triggering != trig || polarity != pol) { + pr_warning("ACPI: IRQ %d override to %s, %s\n", gsi, + t ? "edge" : "level", p ? "low" : "high"); + triggering = trig; + polarity = pol; + } + } + + res->flags = acpi_dev_irq_flags(triggering, polarity, shareable); + irq = acpi_register_gsi(NULL, gsi, triggering, polarity); + if (irq >= 0) { + res->start = irq; + res->end = irq; + } else { + acpi_dev_irqresource_disabled(res, gsi); + } +} + +/** + * acpi_dev_resource_interrupt - Extract ACPI interrupt resource information. + * @ares: Input ACPI resource object. + * @index: Index into the array of GSIs represented by the resource. + * @res: Output generic resource object. + * + * Check if the given ACPI resource object represents an interrupt resource + * and @index does not exceed the resource's interrupt count (true is returned + * in that case regardless of the results of the other checks)). If that's the + * case, register the GSI corresponding to @index from the array of interrupts + * represented by the resource and populate the generic resource object pointed + * to by @res accordingly. If the registration of the GSI is not successful, + * IORESOURCE_DISABLED will be set it that object's flags. + */ +bool acpi_dev_resource_interrupt(struct acpi_resource *ares, int index, + struct resource *res) +{ + struct acpi_resource_irq *irq; + struct acpi_resource_extended_irq *ext_irq; + + switch (ares->type) { + case ACPI_RESOURCE_TYPE_IRQ: + /* + * Per spec, only one interrupt per descriptor is allowed in + * _CRS, but some firmware violates this, so parse them all. + */ + irq = &ares->data.irq; + if (index >= irq->interrupt_count) { + acpi_dev_irqresource_disabled(res, 0); + return false; + } + acpi_dev_get_irqresource(res, irq->interrupts[index], + irq->triggering, irq->polarity, + irq->sharable); + break; + case ACPI_RESOURCE_TYPE_EXTENDED_IRQ: + ext_irq = &ares->data.extended_irq; + if (index >= ext_irq->interrupt_count) { + acpi_dev_irqresource_disabled(res, 0); + return false; + } + acpi_dev_get_irqresource(res, ext_irq->interrupts[index], + ext_irq->triggering, ext_irq->polarity, + ext_irq->sharable); + break; + default: + return false; + } + + return true; +} +EXPORT_SYMBOL_GPL(acpi_dev_resource_interrupt); diff --git a/drivers/pnp/base.h b/drivers/pnp/base.h index fa4e0a5..ffd53e3 100644 --- a/drivers/pnp/base.h +++ b/drivers/pnp/base.h @@ -159,6 +159,8 @@ struct pnp_resource { void pnp_free_resource(struct pnp_resource *pnp_res); +struct pnp_resource *pnp_add_resource(struct pnp_dev *dev, + struct resource *res); struct pnp_resource *pnp_add_irq_resource(struct pnp_dev *dev, int irq, int flags); struct pnp_resource *pnp_add_dma_resource(struct pnp_dev *dev, int dma, diff --git a/drivers/pnp/pnpacpi/rsparser.c b/drivers/pnp/pnpacpi/rsparser.c index 5be4a39..b8f4ea7 100644 --- a/drivers/pnp/pnpacpi/rsparser.c +++ b/drivers/pnp/pnpacpi/rsparser.c @@ -28,37 +28,6 @@ #include "../base.h" #include "pnpacpi.h" -#ifdef CONFIG_IA64 -#define valid_IRQ(i) (1) -#else -#define valid_IRQ(i) (((i) != 0) && ((i) != 2)) -#endif - -/* - * Allocated Resources - */ -static int irq_flags(int triggering, int polarity, int shareable) -{ - int flags; - - if (triggering == ACPI_LEVEL_SENSITIVE) { - if (polarity == ACPI_ACTIVE_LOW) - flags = IORESOURCE_IRQ_LOWLEVEL; - else - flags = IORESOURCE_IRQ_HIGHLEVEL; - } else { - if (polarity == ACPI_ACTIVE_LOW) - flags = IORESOURCE_IRQ_LOWEDGE; - else - flags = IORESOURCE_IRQ_HIGHEDGE; - } - - if (shareable == ACPI_SHARED) - flags |= IORESOURCE_IRQ_SHAREABLE; - - return flags; -} - static void decode_irq_flags(struct pnp_dev *dev, int flags, int *triggering, int *polarity, int *shareable) { @@ -94,45 +63,6 @@ static void decode_irq_flags(struct pnp_dev *dev, int flags, int *triggering, *shareable = ACPI_EXCLUSIVE; } -static void pnpacpi_parse_allocated_irqresource(struct pnp_dev *dev, - u32 gsi, int triggering, - int polarity, int shareable) -{ - int irq, flags; - int p, t; - - if (!valid_IRQ(gsi)) { - pnp_add_irq_resource(dev, gsi, IORESOURCE_DISABLED); - return; - } - - /* - * in IO-APIC mode, use overrided attribute. Two reasons: - * 1. BIOS bug in DSDT - * 2. BIOS uses IO-APIC mode Interrupt Source Override - */ - if (!acpi_get_override_irq(gsi, &t, &p)) { - t = t ? ACPI_LEVEL_SENSITIVE : ACPI_EDGE_SENSITIVE; - p = p ? ACPI_ACTIVE_LOW : ACPI_ACTIVE_HIGH; - - if (triggering != t || polarity != p) { - dev_warn(&dev->dev, "IRQ %d override to %s, %s\n", - gsi, t ? "edge":"level", p ? "low":"high"); - triggering = t; - polarity = p; - } - } - - flags = irq_flags(triggering, polarity, shareable); - irq = acpi_register_gsi(&dev->dev, gsi, triggering, polarity); - if (irq >= 0) - pcibios_penalize_isa_irq(irq, 1); - else - flags |= IORESOURCE_DISABLED; - - pnp_add_irq_resource(dev, irq, flags); -} - static int dma_flags(struct pnp_dev *dev, int type, int bus_master, int transfer) { @@ -177,21 +107,16 @@ static int dma_flags(struct pnp_dev *dev, int type, int bus_master, return flags; } -static void pnpacpi_parse_allocated_ioresource(struct pnp_dev *dev, u64 start, - u64 len, int io_decode, - int window) -{ - int flags = 0; - u64 end = start + len - 1; +/* + * Allocated Resources + */ - if (io_decode == ACPI_DECODE_16) - flags |= IORESOURCE_IO_16BIT_ADDR; - if (len == 0 || end >= 0x10003) - flags |= IORESOURCE_DISABLED; - if (window) - flags |= IORESOURCE_WINDOW; +static void pnpacpi_add_irqresource(struct pnp_dev *dev, struct resource *r) +{ + if (!(r->flags & IORESOURCE_DISABLED)) + pcibios_penalize_isa_irq(r->start, 1); - pnp_add_io_resource(dev, start, end, flags); + pnp_add_resource(dev, r); } /* @@ -249,130 +174,49 @@ static void pnpacpi_parse_allocated_vendor(struct pnp_dev *dev, } } -static void pnpacpi_parse_allocated_memresource(struct pnp_dev *dev, - u64 start, u64 len, - int write_protect, int window) -{ - int flags = 0; - u64 end = start + len - 1; - - if (len == 0) - flags |= IORESOURCE_DISABLED; - if (write_protect == ACPI_READ_WRITE_MEMORY) - flags |= IORESOURCE_MEM_WRITEABLE; - if (window) - flags |= IORESOURCE_WINDOW; - - pnp_add_mem_resource(dev, start, end, flags); -} - -static void pnpacpi_parse_allocated_busresource(struct pnp_dev *dev, - u64 start, u64 len) -{ - u64 end = start + len - 1; - - pnp_add_bus_resource(dev, start, end); -} - -static void pnpacpi_parse_allocated_address_space(struct pnp_dev *dev, - struct acpi_resource *res) -{ - struct acpi_resource_address64 addr, *p = &addr; - acpi_status status; - int window; - u64 len; - - status = acpi_resource_to_address64(res, p); - if (!ACPI_SUCCESS(status)) { - dev_warn(&dev->dev, "failed to convert resource type %d\n", - res->type); - return; - } - - /* Windows apparently computes length rather than using _LEN */ - len = p->maximum - p->minimum + 1; - window = (p->producer_consumer == ACPI_PRODUCER) ? 1 : 0; - - if (p->resource_type == ACPI_MEMORY_RANGE) - pnpacpi_parse_allocated_memresource(dev, p->minimum, len, - p->info.mem.write_protect, window); - else if (p->resource_type == ACPI_IO_RANGE) - pnpacpi_parse_allocated_ioresource(dev, p->minimum, len, - p->granularity == 0xfff ? ACPI_DECODE_10 : - ACPI_DECODE_16, window); - else if (p->resource_type == ACPI_BUS_NUMBER_RANGE) - pnpacpi_parse_allocated_busresource(dev, p->minimum, len); -} - -static void pnpacpi_parse_allocated_ext_address_space(struct pnp_dev *dev, - struct acpi_resource *res) -{ - struct acpi_resource_extended_address64 *p = &res->data.ext_address64; - int window; - u64 len; - - /* Windows apparently computes length rather than using _LEN */ - len = p->maximum - p->minimum + 1; - window = (p->producer_consumer == ACPI_PRODUCER) ? 1 : 0; - - if (p->resource_type == ACPI_MEMORY_RANGE) - pnpacpi_parse_allocated_memresource(dev, p->minimum, len, - p->info.mem.write_protect, window); - else if (p->resource_type == ACPI_IO_RANGE) - pnpacpi_parse_allocated_ioresource(dev, p->minimum, len, - p->granularity == 0xfff ? ACPI_DECODE_10 : - ACPI_DECODE_16, window); - else if (p->resource_type == ACPI_BUS_NUMBER_RANGE) - pnpacpi_parse_allocated_busresource(dev, p->minimum, len); -} - static acpi_status pnpacpi_allocated_resource(struct acpi_resource *res, void *data) { struct pnp_dev *dev = data; - struct acpi_resource_irq *irq; struct acpi_resource_dma *dma; - struct acpi_resource_io *io; - struct acpi_resource_fixed_io *fixed_io; struct acpi_resource_vendor_typed *vendor_typed; - struct acpi_resource_memory24 *memory24; - struct acpi_resource_memory32 *memory32; - struct acpi_resource_fixed_memory32 *fixed_memory32; - struct acpi_resource_extended_irq *extended_irq; + struct resource r; int i, flags; - switch (res->type) { - case ACPI_RESOURCE_TYPE_IRQ: - /* - * Per spec, only one interrupt per descriptor is allowed in - * _CRS, but some firmware violates this, so parse them all. - */ - irq = &res->data.irq; - if (irq->interrupt_count == 0) - pnp_add_irq_resource(dev, 0, IORESOURCE_DISABLED); - else { - for (i = 0; i < irq->interrupt_count; i++) { - pnpacpi_parse_allocated_irqresource(dev, - irq->interrupts[i], - irq->triggering, - irq->polarity, - irq->sharable); - } + 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)) { + pnp_add_resource(dev, &r); + return AE_OK; + } + r.flags = 0; + if (acpi_dev_resource_interrupt(res, 0, &r)) { + pnpacpi_add_irqresource(dev, &r); + for (i = 1; acpi_dev_resource_interrupt(res, i, &r); i++) + pnpacpi_add_irqresource(dev, &r); + + if (i > 1) { /* * The IRQ encoder puts a single interrupt in each * descriptor, so if a _CRS descriptor has more than * one interrupt, we won't be able to re-encode it. */ - if (pnp_can_write(dev) && irq->interrupt_count > 1) { + if (pnp_can_write(dev)) { dev_warn(&dev->dev, "multiple interrupts in " "_CRS descriptor; configuration can't " "be changed\n"); dev->capabilities &= ~PNP_WRITE; } } - break; + return AE_OK; + } else if (r.flags & IORESOURCE_DISABLED) { + pnp_add_irq_resource(dev, 0, IORESOURCE_DISABLED); + return AE_OK; + } + switch (res->type) { case ACPI_RESOURCE_TYPE_DMA: dma = &res->data.dma; if (dma->channel_count > 0 && dma->channels[0] != (u8) -1) @@ -383,26 +227,10 @@ static acpi_status pnpacpi_allocated_resource(struct acpi_resource *res, pnp_add_dma_resource(dev, dma->channels[0], flags); break; - case ACPI_RESOURCE_TYPE_IO: - io = &res->data.io; - pnpacpi_parse_allocated_ioresource(dev, - io->minimum, - io->address_length, - io->io_decode, 0); - break; - case ACPI_RESOURCE_TYPE_START_DEPENDENT: case ACPI_RESOURCE_TYPE_END_DEPENDENT: break; - case ACPI_RESOURCE_TYPE_FIXED_IO: - fixed_io = &res->data.fixed_io; - pnpacpi_parse_allocated_ioresource(dev, - fixed_io->address, - fixed_io->address_length, - ACPI_DECODE_10, 0); - break; - case ACPI_RESOURCE_TYPE_VENDOR: vendor_typed = &res->data.vendor_typed; pnpacpi_parse_allocated_vendor(dev, vendor_typed); @@ -411,66 +239,6 @@ static acpi_status pnpacpi_allocated_resource(struct acpi_resource *res, case ACPI_RESOURCE_TYPE_END_TAG: break; - case ACPI_RESOURCE_TYPE_MEMORY24: - memory24 = &res->data.memory24; - pnpacpi_parse_allocated_memresource(dev, - memory24->minimum, - memory24->address_length, - memory24->write_protect, 0); - break; - case ACPI_RESOURCE_TYPE_MEMORY32: - memory32 = &res->data.memory32; - pnpacpi_parse_allocated_memresource(dev, - memory32->minimum, - memory32->address_length, - memory32->write_protect, 0); - break; - case ACPI_RESOURCE_TYPE_FIXED_MEMORY32: - fixed_memory32 = &res->data.fixed_memory32; - pnpacpi_parse_allocated_memresource(dev, - fixed_memory32->address, - fixed_memory32->address_length, - fixed_memory32->write_protect, 0); - break; - case ACPI_RESOURCE_TYPE_ADDRESS16: - case ACPI_RESOURCE_TYPE_ADDRESS32: - case ACPI_RESOURCE_TYPE_ADDRESS64: - pnpacpi_parse_allocated_address_space(dev, res); - break; - - case ACPI_RESOURCE_TYPE_EXTENDED_ADDRESS64: - pnpacpi_parse_allocated_ext_address_space(dev, res); - break; - - case ACPI_RESOURCE_TYPE_EXTENDED_IRQ: - extended_irq = &res->data.extended_irq; - - if (extended_irq->interrupt_count == 0) - pnp_add_irq_resource(dev, 0, IORESOURCE_DISABLED); - else { - for (i = 0; i < extended_irq->interrupt_count; i++) { - pnpacpi_parse_allocated_irqresource(dev, - extended_irq->interrupts[i], - extended_irq->triggering, - extended_irq->polarity, - extended_irq->sharable); - } - - /* - * The IRQ encoder puts a single interrupt in each - * descriptor, so if a _CRS descriptor has more than - * one interrupt, we won't be able to re-encode it. - */ - if (pnp_can_write(dev) && - extended_irq->interrupt_count > 1) { - dev_warn(&dev->dev, "multiple interrupts in " - "_CRS descriptor; configuration can't " - "be changed\n"); - dev->capabilities &= ~PNP_WRITE; - } - } - break; - case ACPI_RESOURCE_TYPE_GENERIC_REGISTER: break; @@ -531,7 +299,7 @@ static __init void pnpacpi_parse_irq_option(struct pnp_dev *dev, if (p->interrupts[i]) __set_bit(p->interrupts[i], map.bits); - flags = irq_flags(p->triggering, p->polarity, p->sharable); + flags = acpi_dev_irq_flags(p->triggering, p->polarity, p->sharable); pnp_register_irq_resource(dev, option_flags, &map, flags); } @@ -555,7 +323,7 @@ static __init void pnpacpi_parse_ext_irq_option(struct pnp_dev *dev, } } - flags = irq_flags(p->triggering, p->polarity, p->sharable); + flags = acpi_dev_irq_flags(p->triggering, p->polarity, p->sharable); pnp_register_irq_resource(dev, option_flags, &map, flags); } diff --git a/drivers/pnp/resource.c b/drivers/pnp/resource.c index b0ecacb..3e6db1c 100644 --- a/drivers/pnp/resource.c +++ b/drivers/pnp/resource.c @@ -503,6 +503,22 @@ static struct pnp_resource *pnp_new_resource(struct pnp_dev *dev) return pnp_res; } +struct pnp_resource *pnp_add_resource(struct pnp_dev *dev, + struct resource *res) +{ + struct pnp_resource *pnp_res; + + pnp_res = pnp_new_resource(dev); + if (!pnp_res) { + dev_err(&dev->dev, "can't add resource %pR\n", res); + return NULL; + } + + pnp_res->res = *res; + dev_dbg(&dev->dev, "%pR\n", res); + return pnp_res; +} + struct pnp_resource *pnp_add_irq_resource(struct pnp_dev *dev, int irq, int flags) { diff --git a/include/linux/acpi.h b/include/linux/acpi.h index 48761cb..16fcaf8 100644 --- a/include/linux/acpi.h +++ b/include/linux/acpi.h @@ -251,6 +251,16 @@ extern int pnpacpi_disabled; #define PXM_INVAL (-1) +bool acpi_dev_resource_memory(struct acpi_resource *ares, struct resource *res); +bool acpi_dev_resource_io(struct acpi_resource *ares, struct resource *res); +bool acpi_dev_resource_address_space(struct acpi_resource *ares, + struct resource *res); +bool acpi_dev_resource_ext_address_space(struct acpi_resource *ares, + struct resource *res); +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); + int acpi_check_resource_conflict(const struct resource *res); int acpi_check_region(resource_size_t start, resource_size_t n, -- cgit v0.10.2 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 diff --git a/drivers/acpi/acpi_platform.c b/drivers/acpi/acpi_platform.c index dbb31d6..bcbb00c 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 v0.10.2 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 diff --git a/drivers/acpi/acpi_platform.c b/drivers/acpi/acpi_platform.c index bcbb00c..7ac20d8 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 3e7fd34..2bafc25 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 16fcaf8..32fbc4e 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 v0.10.2 From 8a66790b7850a6669129af078768a1d42076a0ef Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Fri, 16 Nov 2012 21:55:48 +0100 Subject: ACPI / resources: Use AE_CTRL_TERMINATE to terminate resources walks Currently acpi_dev_process_resource() returns AE_ABORT_METHOD to terminate the acpi_walk_resources() it is called from if the .preproc() routine provided by the caller of acpi_dev_get_resources() initiating the resources walk returns an error code. It is better to use AE_CTRL_TERMINATE for this purpose, however, so do that. Signed-off-by: Rafael J. Wysocki Reviewed-by: Mika Westerberg diff --git a/drivers/acpi/resource.c b/drivers/acpi/resource.c index 2bafc25..4107c00 100644 --- a/drivers/acpi/resource.c +++ b/drivers/acpi/resource.c @@ -446,7 +446,7 @@ static acpi_status acpi_dev_process_resource(struct acpi_resource *ares, ret = c->preproc(ares, c->preproc_data); if (ret < 0) { c->error = ret; - return AE_ABORT_METHOD; + return AE_CTRL_TERMINATE; } else if (ret > 0) { return AE_OK; } -- cgit v0.10.2 From f3fd0c8a7fc1e4f3107a09a75e622781d3007b56 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 21 Nov 2012 00:21:39 +0100 Subject: ACPI: Allow ACPI handles of devices to be initialized in advance Currently, the ACPI handles of devices are initialized from within device_add(), by acpi_bind_one() called from acpi_platform_notify() which first uses the .find_device() routine provided by the device's bus type to find the matching device node in the ACPI namespace. This is a source of some computational overhead and, moreover, the correctness of the result depends on the implementation of .find_device() which is known to fail occasionally for some bus types (e.g. PCI). In some cases, however, the corresponding ACPI device node is known already before calling device_add() for the given struct device object and the whole .find_device() dance in acpi_platform_notify() is then simply unnecessary. For this reason, make it possible to initialize the ACPI handles of devices before calling device_add() for them. Modify acpi_platform_notify() to call acpi_bind_one() in advance to check the device's existing ACPI handle and skip the .find_device() search if that is successful. Change acpi_bind_one() accordingly. Signed-off-by: Rafael J. Wysocki Reviewed-by: Mika Westerberg diff --git a/drivers/acpi/glue.c b/drivers/acpi/glue.c index 2f3849a..3e75d6e 100644 --- a/drivers/acpi/glue.c +++ b/drivers/acpi/glue.c @@ -130,46 +130,59 @@ static int acpi_bind_one(struct device *dev, acpi_handle handle) { struct acpi_device *acpi_dev; acpi_status status; - struct acpi_device_physical_node *physical_node; + struct acpi_device_physical_node *physical_node, *pn; char physical_node_name[sizeof(PHYSICAL_NODE_STRING) + 2]; int retval = -EINVAL; if (dev->acpi_handle) { - dev_warn(dev, "Drivers changed 'acpi_handle'\n"); - return -EINVAL; + if (handle) { + dev_warn(dev, "ACPI handle is already set\n"); + return -EINVAL; + } else { + handle = dev->acpi_handle; + } } + if (!handle) + return -EINVAL; get_device(dev); status = acpi_bus_get_device(handle, &acpi_dev); if (ACPI_FAILURE(status)) goto err; - physical_node = kzalloc(sizeof(struct acpi_device_physical_node), - GFP_KERNEL); + physical_node = kzalloc(sizeof(*physical_node), GFP_KERNEL); if (!physical_node) { retval = -ENOMEM; goto err; } mutex_lock(&acpi_dev->physical_node_lock); + + /* Sanity check. */ + list_for_each_entry(pn, &acpi_dev->physical_node_list, node) + if (pn->dev == dev) { + dev_warn(dev, "Already associated with ACPI node\n"); + goto err_free; + } + /* allocate physical node id according to physical_node_id_bitmap */ physical_node->node_id = find_first_zero_bit(acpi_dev->physical_node_id_bitmap, ACPI_MAX_PHYSICAL_NODE); if (physical_node->node_id >= ACPI_MAX_PHYSICAL_NODE) { retval = -ENOSPC; - mutex_unlock(&acpi_dev->physical_node_lock); - kfree(physical_node); - goto err; + goto err_free; } set_bit(physical_node->node_id, acpi_dev->physical_node_id_bitmap); physical_node->dev = dev; list_add_tail(&physical_node->node, &acpi_dev->physical_node_list); acpi_dev->physical_node_count++; + mutex_unlock(&acpi_dev->physical_node_lock); - dev->acpi_handle = handle; + if (!dev->acpi_handle) + dev->acpi_handle = handle; if (!physical_node->node_id) strcpy(physical_node_name, PHYSICAL_NODE_STRING); @@ -187,8 +200,14 @@ static int acpi_bind_one(struct device *dev, acpi_handle handle) return 0; err: + dev->acpi_handle = NULL; put_device(dev); return retval; + + err_free: + mutex_unlock(&acpi_dev->physical_node_lock); + kfree(physical_node); + goto err; } static int acpi_unbind_one(struct device *dev) @@ -247,6 +266,10 @@ static int acpi_platform_notify(struct device *dev) acpi_handle handle; int ret = -EINVAL; + ret = acpi_bind_one(dev, NULL); + if (!ret) + goto out; + if (!dev->bus || !dev->parent) { /* bridge devices genernally haven't bus or parent */ ret = acpi_find_bridge_device(dev, &handle); @@ -260,10 +283,11 @@ static int acpi_platform_notify(struct device *dev) } if ((ret = type->find_device(dev, &handle)) != 0) DBG("Can't get handler for %s\n", dev_name(dev)); - end: + end: if (!ret) acpi_bind_one(dev, handle); + out: #if ACPI_GLUE_DEBUG if (!ret) { struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; -- cgit v0.10.2 From 95f8a082b9b1ead0c2859f2a7b1ac91ff63d8765 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 21 Nov 2012 00:21:50 +0100 Subject: ACPI / driver core: Introduce struct acpi_dev_node and related macros To avoid adding an ACPI handle pointer to struct device on architectures that don't use ACPI, or generally when CONFIG_ACPI is not set, in which cases that pointer is useless, define struct acpi_dev_node that will contain the handle pointer if CONFIG_ACPI is set and will be empty otherwise and use it to represent the ACPI device node field in struct device. In addition to that define macros for reading and setting the ACPI handle of a device that don't generate code when CONFIG_ACPI is unset. Modify the ACPI subsystem to use those macros instead of referring to the given device's ACPI handle directly. Signed-off-by: Rafael J. Wysocki Reviewed-by: Mika Westerberg Acked-by: Greg Kroah-Hartman diff --git a/drivers/acpi/glue.c b/drivers/acpi/glue.c index 3e75d6e..0155184 100644 --- a/drivers/acpi/glue.c +++ b/drivers/acpi/glue.c @@ -134,12 +134,12 @@ static int acpi_bind_one(struct device *dev, acpi_handle handle) char physical_node_name[sizeof(PHYSICAL_NODE_STRING) + 2]; int retval = -EINVAL; - if (dev->acpi_handle) { + if (ACPI_HANDLE(dev)) { if (handle) { dev_warn(dev, "ACPI handle is already set\n"); return -EINVAL; } else { - handle = dev->acpi_handle; + handle = ACPI_HANDLE(dev); } } if (!handle) @@ -181,8 +181,8 @@ static int acpi_bind_one(struct device *dev, acpi_handle handle) mutex_unlock(&acpi_dev->physical_node_lock); - if (!dev->acpi_handle) - dev->acpi_handle = handle; + if (!ACPI_HANDLE(dev)) + ACPI_HANDLE_SET(dev, acpi_dev->handle); if (!physical_node->node_id) strcpy(physical_node_name, PHYSICAL_NODE_STRING); @@ -200,7 +200,7 @@ static int acpi_bind_one(struct device *dev, acpi_handle handle) return 0; err: - dev->acpi_handle = NULL; + ACPI_HANDLE_SET(dev, NULL); put_device(dev); return retval; @@ -217,10 +217,10 @@ static int acpi_unbind_one(struct device *dev) acpi_status status; struct list_head *node, *next; - if (!dev->acpi_handle) + if (!ACPI_HANDLE(dev)) return 0; - status = acpi_bus_get_device(dev->acpi_handle, &acpi_dev); + status = acpi_bus_get_device(ACPI_HANDLE(dev), &acpi_dev); if (ACPI_FAILURE(status)) goto err; @@ -246,7 +246,7 @@ static int acpi_unbind_one(struct device *dev) sysfs_remove_link(&acpi_dev->dev.kobj, physical_node_name); sysfs_remove_link(&dev->kobj, "firmware_node"); - dev->acpi_handle = NULL; + ACPI_HANDLE_SET(dev, NULL); /* acpi_bind_one increase refcnt by one */ put_device(dev); kfree(entry); diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index d842569..e92ca67 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -386,8 +386,8 @@ const struct acpi_device_id *acpi_match_device(const struct acpi_device_id *ids, { struct acpi_device *adev; - if (!ids || !dev->acpi_handle - || ACPI_FAILURE(acpi_bus_get_device(dev->acpi_handle, &adev))) + if (!ids || !ACPI_HANDLE(dev) + || ACPI_FAILURE(acpi_bus_get_device(ACPI_HANDLE(dev), &adev))) return NULL; return __acpi_match_device(adev, ids); diff --git a/include/acpi/acpi_bus.h b/include/acpi/acpi_bus.h index bb1537c..d165990 100644 --- a/include/acpi/acpi_bus.h +++ b/include/acpi/acpi_bus.h @@ -410,7 +410,7 @@ acpi_handle acpi_get_child(acpi_handle, u64); int acpi_is_root_bridge(acpi_handle); acpi_handle acpi_get_pci_rootbridge_handle(unsigned int, unsigned int); struct acpi_pci_root *acpi_pci_find_root(acpi_handle handle); -#define DEVICE_ACPI_HANDLE(dev) ((acpi_handle)((dev)->acpi_handle)) +#define DEVICE_ACPI_HANDLE(dev) ((acpi_handle)ACPI_HANDLE(dev)) int acpi_enable_wakeup_device_power(struct acpi_device *dev, int state); int acpi_disable_wakeup_device_power(struct acpi_device *dev); diff --git a/include/linux/device.h b/include/linux/device.h index cc3aee5..05292e4 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -578,6 +578,12 @@ struct device_dma_parameters { unsigned long segment_boundary_mask; }; +struct acpi_dev_node { +#ifdef CONFIG_ACPI + void *handle; +#endif +}; + /** * struct device - The basic device structure * @parent: The device's "parent" device, the device to which it is attached. @@ -618,7 +624,7 @@ struct device_dma_parameters { * @dma_mem: Internal for coherent mem override. * @archdata: For arch-specific additions. * @of_node: Associated device tree node. - * @acpi_handle: Associated ACPI device node's namespace handle. + * @acpi_node: Associated ACPI device node. * @devt: For creating the sysfs "dev". * @id: device instance * @devres_lock: Spinlock to protect the resource of the device. @@ -683,7 +689,7 @@ struct device { struct dev_archdata archdata; struct device_node *of_node; /* associated device tree node */ - void *acpi_handle; /* associated ACPI device node */ + struct acpi_dev_node acpi_node; /* associated ACPI device node */ dev_t devt; /* dev_t, creates the sysfs "dev" */ u32 id; /* device instance */ @@ -704,6 +710,14 @@ static inline struct device *kobj_to_dev(struct kobject *kobj) return container_of(kobj, struct device, kobj); } +#ifdef CONFIG_ACPI +#define ACPI_HANDLE(dev) ((dev)->acpi_node.handle) +#define ACPI_HANDLE_SET(dev, _handle_) (dev)->acpi_node.handle = (_handle_) +#else +#define ACPI_HANDLE(dev) (NULL) +#define ACPI_HANDLE_SET(dev, _handle_) do { } while (0) +#endif + /* Get the wakeup routines, which depend on struct device */ #include -- cgit v0.10.2 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 diff --git a/drivers/acpi/acpi_platform.c b/drivers/acpi/acpi_platform.c index 7ac20d8..b7df9b1 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 7de29eb..49fd96e 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 5711e95..a9ded9a 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 v0.10.2 From 907ddf89d0bb7f57e1e21485900e6564a1ab512a Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Fri, 23 Nov 2012 12:23:40 +0100 Subject: i2c / ACPI: add ACPI enumeration support ACPI 5 introduced I2cSerialBus resource that makes it possible to enumerate and configure the I2C slave devices behind the I2C controller. This patch adds helper functions to support I2C slave enumeration. An ACPI enabled I2C controller driver only needs to call acpi_i2c_register_devices() in order to get its slave devices enumerated, created and bound to the corresponding ACPI handle. Signed-off-by: Mika Westerberg Signed-off-by: Rafael J. Wysocki diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig index 119d58d..0300bf6 100644 --- a/drivers/acpi/Kconfig +++ b/drivers/acpi/Kconfig @@ -181,6 +181,12 @@ config ACPI_DOCK This driver supports ACPI-controlled docking stations and removable drive bays such as the IBM Ultrabay and the Dell Module Bay. +config ACPI_I2C + def_tristate I2C + depends on I2C + help + ACPI I2C enumeration support. + config ACPI_PROCESSOR tristate "Processor" select THERMAL diff --git a/drivers/acpi/Makefile b/drivers/acpi/Makefile index 3223edf..7198b6d 100644 --- a/drivers/acpi/Makefile +++ b/drivers/acpi/Makefile @@ -69,6 +69,7 @@ obj-$(CONFIG_ACPI_HED) += hed.o obj-$(CONFIG_ACPI_EC_DEBUGFS) += ec_sys.o obj-$(CONFIG_ACPI_CUSTOM_METHOD)+= custom_method.o obj-$(CONFIG_ACPI_BGRT) += bgrt.o +obj-$(CONFIG_ACPI_I2C) += acpi_i2c.o # processor has its own "processor." module_param namespace processor-y := processor_driver.o processor_throttling.o diff --git a/drivers/acpi/acpi_i2c.c b/drivers/acpi/acpi_i2c.c new file mode 100644 index 0000000..82045e3 --- /dev/null +++ b/drivers/acpi/acpi_i2c.c @@ -0,0 +1,103 @@ +/* + * ACPI I2C enumeration support + * + * Copyright (C) 2012, Intel Corporation + * Author: Mika Westerberg + * + * 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("i2c"); + +static int acpi_i2c_add_resource(struct acpi_resource *ares, void *data) +{ + struct i2c_board_info *info = data; + + if (ares->type == ACPI_RESOURCE_TYPE_SERIAL_BUS) { + struct acpi_resource_i2c_serialbus *sb; + + sb = &ares->data.i2c_serial_bus; + if (sb->type == ACPI_RESOURCE_SERIAL_TYPE_I2C) { + info->addr = sb->slave_address; + if (sb->access_mode == ACPI_I2C_10BIT_MODE) + info->flags |= I2C_CLIENT_TEN; + } + } else if (info->irq < 0) { + struct resource r; + + if (acpi_dev_resource_interrupt(ares, 0, &r)) + info->irq = r.start; + } + + /* Tell the ACPI core to skip this resource */ + return 1; +} + +static acpi_status acpi_i2c_add_device(acpi_handle handle, u32 level, + void *data, void **return_value) +{ + struct i2c_adapter *adapter = data; + struct list_head resource_list; + struct i2c_board_info info; + struct acpi_device *adev; + int ret; + + if (acpi_bus_get_device(handle, &adev)) + return AE_OK; + if (acpi_bus_get_status(adev) || !adev->status.present) + return AE_OK; + + memset(&info, 0, sizeof(info)); + info.acpi_node.handle = handle; + info.irq = -1; + + INIT_LIST_HEAD(&resource_list); + ret = acpi_dev_get_resources(adev, &resource_list, + acpi_i2c_add_resource, &info); + acpi_dev_free_resource_list(&resource_list); + + if (ret < 0 || !info.addr) + return AE_OK; + + strlcpy(info.type, dev_name(&adev->dev), sizeof(info.type)); + if (!i2c_new_device(adapter, &info)) { + dev_err(&adapter->dev, + "failed to add I2C device %s from ACPI\n", + dev_name(&adev->dev)); + } + + return AE_OK; +} + +/** + * acpi_i2c_register_devices - enumerate I2C slave devices behind adapter + * @adapter: pointer to adapter + * + * Enumerate all I2C slave devices behind this adapter by walking the ACPI + * namespace. When a device is found it will be added to the Linux device + * model and bound to the corresponding ACPI handle. + */ +void acpi_i2c_register_devices(struct i2c_adapter *adapter) +{ + acpi_handle handle; + acpi_status status; + + handle = ACPI_HANDLE(&adapter->dev); + if (!handle) + return; + + status = acpi_walk_namespace(ACPI_TYPE_DEVICE, handle, 1, + acpi_i2c_add_device, NULL, + adapter, NULL); + if (ACPI_FAILURE(status)) + dev_warn(&adapter->dev, "failed to enumerate I2C slaves\n"); +} +EXPORT_SYMBOL_GPL(acpi_i2c_register_devices); diff --git a/drivers/i2c/i2c-core.c b/drivers/i2c/i2c-core.c index a7edf98..e388590 100644 --- a/drivers/i2c/i2c-core.c +++ b/drivers/i2c/i2c-core.c @@ -39,6 +39,7 @@ #include #include #include +#include #include #include "i2c-core.h" @@ -78,6 +79,10 @@ static int i2c_device_match(struct device *dev, struct device_driver *drv) if (of_driver_match_device(dev, drv)) return 1; + /* Then ACPI style match */ + if (acpi_driver_match_device(dev, drv)) + return 1; + driver = to_i2c_driver(drv); /* match on an id table if there is one */ if (driver->id_table) @@ -539,6 +544,7 @@ i2c_new_device(struct i2c_adapter *adap, struct i2c_board_info const *info) client->dev.bus = &i2c_bus_type; client->dev.type = &i2c_client_type; client->dev.of_node = info->of_node; + ACPI_HANDLE_SET(&client->dev, info->acpi_node.handle); /* For 10-bit clients, add an arbitrary offset to avoid collisions */ dev_set_name(&client->dev, "%d-%04x", i2c_adapter_id(adap), diff --git a/include/linux/i2c.h b/include/linux/i2c.h index 800de22..d0c4db7 100644 --- a/include/linux/i2c.h +++ b/include/linux/i2c.h @@ -259,6 +259,7 @@ static inline void i2c_set_clientdata(struct i2c_client *dev, void *data) * @platform_data: stored in i2c_client.dev.platform_data * @archdata: copied into i2c_client.dev.archdata * @of_node: pointer to OpenFirmware device node + * @acpi_node: ACPI device node * @irq: stored in i2c_client.irq * * I2C doesn't actually support hardware probing, although controllers and @@ -279,6 +280,7 @@ struct i2c_board_info { void *platform_data; struct dev_archdata *archdata; struct device_node *of_node; + struct acpi_dev_node acpi_node; int irq; }; @@ -501,4 +503,11 @@ static inline int i2c_adapter_id(struct i2c_adapter *adap) i2c_del_driver) #endif /* I2C */ + +#if IS_ENABLED(CONFIG_ACPI_I2C) +extern void acpi_i2c_register_devices(struct i2c_adapter *adap); +#else +static inline void acpi_i2c_register_devices(struct i2c_adapter *adap) {} +#endif + #endif /* _LINUX_I2C_H */ -- cgit v0.10.2 From 2905875344f977acd188a2b0f1d163491e91459b Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Fri, 23 Nov 2012 21:07:12 +0100 Subject: ACPI / PNP: skip ACPI device nodes associated with physical nodes already Make pnpacpi_add_device() ignore ACPI device nodes already associated with struct device objects representing physical devices. In particular, this will prevent PNP device objects from being created for ACPI device nodes already associated with platform devices. This change was originally proposed by Mika Westerberg. [rjw: Modified the subject and changelog.] Signed-off-by: Adrian Hunter Signed-off-by: Rafael J. Wysocki diff --git a/drivers/pnp/pnpacpi/core.c b/drivers/pnp/pnpacpi/core.c index 26b5d4b..653d563 100644 --- a/drivers/pnp/pnpacpi/core.c +++ b/drivers/pnp/pnpacpi/core.c @@ -242,6 +242,10 @@ static int __init pnpacpi_add_device(struct acpi_device *device) char *pnpid; struct acpi_hardware_id *id; + /* Skip devices that are already bound */ + if (device->physical_node_count) + return 0; + /* * If a PnPacpi device is not present , the device * driver should not be loaded. -- cgit v0.10.2 From 142b007b65aa763957627ea5c343a1001d5ed449 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Fri, 23 Nov 2012 21:11:47 +0100 Subject: ACPI: add SDHCI to ACPI platform devices Add the generic ACPI SDHCI device ID to acpi_platform_device_ids[] to make the ACPI core create a platform device object for the ACPI device node of that ID. [rjw: Added the changelog.] Signed-off-by: Adrian Hunter Reviewed-by: Mika Westerberg Signed-off-by: Rafael J. Wysocki diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index e92ca67..671bbe6 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -35,6 +35,8 @@ static const char *dummy_hid = "device"; */ static const struct acpi_device_id acpi_platform_device_ids[] = { + { "PNP0D40" }, + { } }; -- cgit v0.10.2 From c4e050376c69bb9d67895842665264df2a2004d9 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Fri, 23 Nov 2012 21:17:34 +0100 Subject: mmc: sdhci-acpi: add SDHCI ACPI driver Add a driver for SDHCI controllers enumerated via ACPI and identified by the ACPI Compatibility ID PNP0D40 (or other SDHCI-specific ACPI hardware IDs in the future). [rjw: Added the changelog.] Signed-off-by: Adrian Hunter Acked-by: Chris Ball Reviewed-by: Mika Westerberg Signed-off-by: Rafael J. Wysocki diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig index 9bf10e7..56eac10 100644 --- a/drivers/mmc/host/Kconfig +++ b/drivers/mmc/host/Kconfig @@ -81,6 +81,18 @@ config MMC_RICOH_MMC If unsure, say Y. +config MMC_SDHCI_ACPI + tristate "SDHCI support for ACPI enumerated SDHCI controllers" + depends on MMC_SDHCI && ACPI + help + This selects support for ACPI enumerated SDHCI controllers, + identified by ACPI Compatibility ID PNP0D40 or specific + ACPI Hardware IDs. + + If you have a controller with this interface, say Y or M here. + + If unsure, say N. + config MMC_SDHCI_PLTFM tristate "SDHCI platform and OF driver helper" depends on MMC_SDHCI diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile index 17ad0a7..0e4960a 100644 --- a/drivers/mmc/host/Makefile +++ b/drivers/mmc/host/Makefile @@ -9,6 +9,7 @@ obj-$(CONFIG_MMC_MXS) += mxs-mmc.o obj-$(CONFIG_MMC_SDHCI) += sdhci.o obj-$(CONFIG_MMC_SDHCI_PCI) += sdhci-pci.o obj-$(subst m,y,$(CONFIG_MMC_SDHCI_PCI)) += sdhci-pci-data.o +obj-$(CONFIG_MMC_SDHCI_ACPI) += sdhci-acpi.o obj-$(CONFIG_MMC_SDHCI_PXAV3) += sdhci-pxav3.o obj-$(CONFIG_MMC_SDHCI_PXAV2) += sdhci-pxav2.o obj-$(CONFIG_MMC_SDHCI_S3C) += sdhci-s3c.o diff --git a/drivers/mmc/host/sdhci-acpi.c b/drivers/mmc/host/sdhci-acpi.c new file mode 100644 index 0000000..6ac3617 --- /dev/null +++ b/drivers/mmc/host/sdhci-acpi.c @@ -0,0 +1,304 @@ +/* + * Secure Digital Host Controller Interface ACPI driver. + * + * Copyright (c) 2012, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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, write to the Free Software Foundation, Inc., + * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "sdhci.h" + +enum { + SDHCI_ACPI_SD_CD = BIT(0), + SDHCI_ACPI_RUNTIME_PM = BIT(1), +}; + +struct sdhci_acpi_chip { + const struct sdhci_ops *ops; + unsigned int quirks; + unsigned int quirks2; + unsigned long caps; + unsigned int caps2; + mmc_pm_flag_t pm_caps; +}; + +struct sdhci_acpi_slot { + const struct sdhci_acpi_chip *chip; + unsigned int quirks; + unsigned int quirks2; + unsigned long caps; + unsigned int caps2; + mmc_pm_flag_t pm_caps; + unsigned int flags; +}; + +struct sdhci_acpi_host { + struct sdhci_host *host; + const struct sdhci_acpi_slot *slot; + struct platform_device *pdev; + bool use_runtime_pm; +}; + +static inline bool sdhci_acpi_flag(struct sdhci_acpi_host *c, unsigned int flag) +{ + return c->slot && (c->slot->flags & flag); +} + +static int sdhci_acpi_enable_dma(struct sdhci_host *host) +{ + return 0; +} + +static const struct sdhci_ops sdhci_acpi_ops_dflt = { + .enable_dma = sdhci_acpi_enable_dma, +}; + +static const struct acpi_device_id sdhci_acpi_ids[] = { + { "PNP0D40" }, + { }, +}; +MODULE_DEVICE_TABLE(acpi, sdhci_acpi_ids); + +static const struct sdhci_acpi_slot *sdhci_acpi_get_slot(const char *hid) +{ + const struct acpi_device_id *id; + + for (id = sdhci_acpi_ids; id->id[0]; id++) + if (!strcmp(id->id, hid)) + return (const struct sdhci_acpi_slot *)id->driver_data; + return NULL; +} + +static int __devinit sdhci_acpi_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + acpi_handle handle = ACPI_HANDLE(dev); + struct acpi_device *device; + struct sdhci_acpi_host *c; + struct sdhci_host *host; + struct resource *iomem; + resource_size_t len; + const char *hid; + int err; + + if (acpi_bus_get_device(handle, &device)) + return -ENODEV; + + if (acpi_bus_get_status(device) || !device->status.present) + return -ENODEV; + + hid = acpi_device_hid(device); + + iomem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!iomem) + return -ENOMEM; + + len = resource_size(iomem); + if (len < 0x100) + dev_err(dev, "Invalid iomem size!\n"); + + if (!devm_request_mem_region(dev, iomem->start, len, dev_name(dev))) + return -ENOMEM; + + host = sdhci_alloc_host(dev, sizeof(struct sdhci_acpi_host)); + if (IS_ERR(host)) + return PTR_ERR(host); + + c = sdhci_priv(host); + c->host = host; + c->slot = sdhci_acpi_get_slot(hid); + c->pdev = pdev; + c->use_runtime_pm = sdhci_acpi_flag(c, SDHCI_ACPI_RUNTIME_PM); + + platform_set_drvdata(pdev, c); + + host->hw_name = "ACPI"; + host->ops = &sdhci_acpi_ops_dflt; + host->irq = platform_get_irq(pdev, 0); + + host->ioaddr = devm_ioremap_nocache(dev, iomem->start, + resource_size(iomem)); + if (host->ioaddr == NULL) { + err = -ENOMEM; + goto err_free; + } + + if (!dev->dma_mask) { + u64 dma_mask; + + if (sdhci_readl(host, SDHCI_CAPABILITIES) & SDHCI_CAN_64BIT) { + /* 64-bit DMA is not supported at present */ + dma_mask = DMA_BIT_MASK(32); + } else { + dma_mask = DMA_BIT_MASK(32); + } + + dev->dma_mask = &dev->coherent_dma_mask; + dev->coherent_dma_mask = dma_mask; + } + + if (c->slot) { + if (c->slot->chip) { + host->ops = c->slot->chip->ops; + host->quirks |= c->slot->chip->quirks; + host->quirks2 |= c->slot->chip->quirks2; + host->mmc->caps |= c->slot->chip->caps; + host->mmc->caps2 |= c->slot->chip->caps2; + host->mmc->pm_caps |= c->slot->chip->pm_caps; + } + host->quirks |= c->slot->quirks; + host->quirks2 |= c->slot->quirks2; + host->mmc->caps |= c->slot->caps; + host->mmc->caps2 |= c->slot->caps2; + host->mmc->pm_caps |= c->slot->pm_caps; + } + + err = sdhci_add_host(host); + if (err) + goto err_free; + + if (c->use_runtime_pm) { + pm_suspend_ignore_children(dev, 1); + pm_runtime_set_autosuspend_delay(dev, 50); + pm_runtime_use_autosuspend(dev); + pm_runtime_enable(dev); + } + + return 0; + +err_free: + platform_set_drvdata(pdev, NULL); + sdhci_free_host(c->host); + return err; +} + +static int __devexit sdhci_acpi_remove(struct platform_device *pdev) +{ + struct sdhci_acpi_host *c = platform_get_drvdata(pdev); + struct device *dev = &pdev->dev; + int dead; + + if (c->use_runtime_pm) { + pm_runtime_get_sync(dev); + pm_runtime_disable(dev); + pm_runtime_put_noidle(dev); + } + + dead = (sdhci_readl(c->host, SDHCI_INT_STATUS) == ~0); + sdhci_remove_host(c->host, dead); + platform_set_drvdata(pdev, NULL); + sdhci_free_host(c->host); + + return 0; +} + +#ifdef CONFIG_PM_SLEEP + +static int sdhci_acpi_suspend(struct device *dev) +{ + struct sdhci_acpi_host *c = dev_get_drvdata(dev); + + return sdhci_suspend_host(c->host); +} + +static int sdhci_acpi_resume(struct device *dev) +{ + struct sdhci_acpi_host *c = dev_get_drvdata(dev); + + return sdhci_resume_host(c->host); +} + +#else + +#define sdhci_acpi_suspend NULL +#define sdhci_acpi_resume NULL + +#endif + +#ifdef CONFIG_PM_RUNTIME + +static int sdhci_acpi_runtime_suspend(struct device *dev) +{ + struct sdhci_acpi_host *c = dev_get_drvdata(dev); + + return sdhci_runtime_suspend_host(c->host); +} + +static int sdhci_acpi_runtime_resume(struct device *dev) +{ + struct sdhci_acpi_host *c = dev_get_drvdata(dev); + + return sdhci_runtime_resume_host(c->host); +} + +static int sdhci_acpi_runtime_idle(struct device *dev) +{ + return 0; +} + +#else + +#define sdhci_acpi_runtime_suspend NULL +#define sdhci_acpi_runtime_resume NULL +#define sdhci_acpi_runtime_idle NULL + +#endif + +static const struct dev_pm_ops sdhci_acpi_pm_ops = { + .suspend = sdhci_acpi_suspend, + .resume = sdhci_acpi_resume, + .runtime_suspend = sdhci_acpi_runtime_suspend, + .runtime_resume = sdhci_acpi_runtime_resume, + .runtime_idle = sdhci_acpi_runtime_idle, +}; + +static struct platform_driver sdhci_acpi_driver = { + .driver = { + .name = "sdhci-acpi", + .owner = THIS_MODULE, + .acpi_match_table = sdhci_acpi_ids, + .pm = &sdhci_acpi_pm_ops, + }, + .probe = sdhci_acpi_probe, + .remove = __devexit_p(sdhci_acpi_remove), +}; + +module_platform_driver(sdhci_acpi_driver); + +MODULE_DESCRIPTION("Secure Digital Host Controller Interface ACPI driver"); +MODULE_AUTHOR("Adrian Hunter"); +MODULE_LICENSE("GPL v2"); -- cgit v0.10.2 From 94d76d5de38d7502c3e78fcd6bf50da95e3e0361 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Mon, 26 Nov 2012 10:04:53 +0100 Subject: platform / ACPI: Attach/detach ACPI PM during probe/remove/shutdown Drivers usually expect that the devices they are supposed to handle will be operational when their .probe() routines are called, but that need not be the case on some ACPI-based systems with ACPI-based device enumeration where the BIOSes don't put devices into D0 by default. To work around this problem it is sufficient to change bus type .probe() routines to ensure that devices will be powered on before the drivers' .probe() routines run (and their .remove() and .shutdown() routines accordingly). Modify platform_drv_probe() to run acpi_dev_pm_attach() for devices whose ACPI handles are present, so that ACPI power management is used to change their power states. Analogously, modify platform_drv_remove() and platform_drv_shutdown() to call acpi_dev_pm_detach() for those devices, so that they are not subject to ACPI PM any more. Signed-off-by: Rafael J. Wysocki Acked-by: Greg Kroah-Hartman Reviewed-by: Mika Westerberg Tested-by: Mika Westerberg diff --git a/drivers/base/platform.c b/drivers/base/platform.c index 49fd96e..b2ee3bc 100644 --- a/drivers/base/platform.c +++ b/drivers/base/platform.c @@ -484,8 +484,16 @@ static int platform_drv_probe(struct device *_dev) { struct platform_driver *drv = to_platform_driver(_dev->driver); struct platform_device *dev = to_platform_device(_dev); + int ret; - return drv->probe(dev); + if (ACPI_HANDLE(_dev)) + acpi_dev_pm_attach(_dev, true); + + ret = drv->probe(dev); + if (ret && ACPI_HANDLE(_dev)) + acpi_dev_pm_detach(_dev, true); + + return ret; } static int platform_drv_probe_fail(struct device *_dev) @@ -497,8 +505,13 @@ static int platform_drv_remove(struct device *_dev) { struct platform_driver *drv = to_platform_driver(_dev->driver); struct platform_device *dev = to_platform_device(_dev); + int ret; - return drv->remove(dev); + ret = drv->remove(dev); + if (ACPI_HANDLE(_dev)) + acpi_dev_pm_detach(_dev, true); + + return ret; } static void platform_drv_shutdown(struct device *_dev) @@ -507,6 +520,8 @@ static void platform_drv_shutdown(struct device *_dev) struct platform_device *dev = to_platform_device(_dev); drv->shutdown(dev); + if (ACPI_HANDLE(_dev)) + acpi_dev_pm_detach(_dev, true); } /** -- cgit v0.10.2 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 diff --git a/drivers/acpi/acpi_platform.c b/drivers/acpi/acpi_platform.c index b7df9b1..db129b9 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 v0.10.2 From 752cad760b19e85926341880dc317a99f400eacc Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Tue, 27 Nov 2012 13:49:36 +0100 Subject: ACPI: remove unnecessary INIT_LIST_HEAD There is no need to initialize the node before appending it to the list. Signed-off-by: Andy Shevchenko Reviewed-by: Mika Westerberg Signed-off-by: Rafael J. Wysocki diff --git a/drivers/acpi/resource.c b/drivers/acpi/resource.c index 4107c00..a3868f6 100644 --- a/drivers/acpi/resource.c +++ b/drivers/acpi/resource.c @@ -426,7 +426,6 @@ static acpi_status acpi_dev_new_resource_entry(struct resource *r, c->error = -ENOMEM; return AE_NO_MEMORY; } - INIT_LIST_HEAD(&rentry->node); rentry->res = *r; list_add_tail(&rentry->node, c->list); c->count++; -- cgit v0.10.2