From 91e5687805885f9fceb60b95e950a3d3bdcf4764 Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Wed, 31 Oct 2012 22:45:02 +0100 Subject: ACPI: Add support for platform bus type With ACPI 5 it is now possible to enumerate traditional SoC peripherals, like serial bus controllers and slave devices behind them. These devices are typically based on IP-blocks used in many existing SoC platforms and platform drivers for them may already be present in the kernel tree. To make driver "porting" more straightforward, add ACPI support to the platform bus type. Instead of writing ACPI "glue" drivers for the existing platform drivers, register the platform bus type with ACPI to create platform device objects for the drivers and bind the corresponding ACPI handles to those platform devices. This should allow us to reuse the existing platform drivers for the devices in question with the minimum amount of modifications. This changeset is based on Mika Westerberg's and Mathias Nyman's work. Signed-off-by: Mathias Nyman Signed-off-by: Mika Westerberg Acked-by: Greg Kroah-Hartman Acked-by: H. Peter Anvin Acked-by: Tony Luck Signed-off-by: Rafael J. Wysocki --- drivers/base/platform.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'drivers/base') diff --git a/drivers/base/platform.c b/drivers/base/platform.c index 72c776f2a1f5..7de29ebfce7f 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 v1.2.1 From 863f9f30e6c1e30cb19a0cd17c5cf8879257dfd7 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 21 Nov 2012 00:21:59 +0100 Subject: ACPI / platform: Initialize ACPI handles of platform devices in advance The current platform device creation and registration code in acpi_create_platform_device() is quite convoluted. This function takes an ACPI device node as an argument and eventually calls platform_device_register_resndata() to create and register a platform device object on the basis of the information contained in that code. However, it doesn't associate the new platform device with the ACPI node directly, but instead it relies on acpi_platform_notify(), called from within device_add(), to find that ACPI node again with the help of acpi_platform_find_device() and acpi_platform_match() and then attach the new platform device to it. This causes an additional ACPI namespace walk to happen and is clearly suboptimal. Use the observation that it is now possible to initialize the ACPI handle of a device before calling device_add() for it to make this code more straightforward. Namely, add a new field to struct platform_device_info allowing us to pass the ACPI handle of interest to platform_device_register_full(), which will then use it to initialize the new device's ACPI handle before registering it. This will cause acpi_platform_notify() to use the ACPI handle from the device structure directly instead of using the .find_device() routine provided by the device's bus type. In consequence, acpi_platform_bus, acpi_platform_find_device(), and acpi_platform_match() are not necessary any more, so remove them. Signed-off-by: Rafael J. Wysocki Reviewed-by: Mika Westerberg Acked-by: Greg Kroah-Hartman --- drivers/base/platform.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers/base') diff --git a/drivers/base/platform.c b/drivers/base/platform.c index 7de29ebfce7f..49fd96e23460 100644 --- a/drivers/base/platform.c +++ b/drivers/base/platform.c @@ -437,6 +437,7 @@ struct platform_device *platform_device_register_full( goto err_alloc; pdev->dev.parent = pdevinfo->parent; + ACPI_HANDLE_SET(&pdev->dev, pdevinfo->acpi_node.handle); if (pdevinfo->dma_mask) { /* @@ -467,6 +468,7 @@ struct platform_device *platform_device_register_full( ret = platform_device_add(pdev); if (ret) { err: + ACPI_HANDLE_SET(&pdev->dev, NULL); kfree(pdev->dev.dma_mask); err_alloc: -- cgit v1.2.1 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 --- drivers/base/platform.c | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/platform.c b/drivers/base/platform.c index 49fd96e23460..b2ee3bcd5a41 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 v1.2.1