ACPI / LPSS: introduce a 'proxy' device to power on LPSS for DMA
authorAndy Shevchenko <andriy.shevchenko@linux.intel.com>
Wed, 5 Nov 2014 16:34:47 +0000 (18:34 +0200)
committerRafael J. Wysocki <rafael.j.wysocki@intel.com>
Tue, 11 Nov 2014 22:44:19 +0000 (23:44 +0100)
The LPSS DMA controller does not have _PS0 and _PS3 methods. Moreover it can be
powered off automatically whenever the last LPSS device goes down. In case of
no power any access to the DMA controller will hang the system. The behaviour
is reproduced on some HP laptops based on Intel Bay Trail [1] as well as on
Asus T100 transformer.

This patch introduces a so called 'proxy' device that has the knobs to handle a
power of the LPSS island. When the system needs to program the DMA controller
it calls to the ACPI LPSS power domain callbacks that wake or suspend the
'proxy' device.

[1] http://www.spinics.net/lists/dmaengine/msg01514.html

Suggested-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
Signed-off-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
Tested-by: Scott Ashcroft <scott.ashcroft@talk21.com>
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
drivers/acpi/acpi_lpss.c

index 4804ae31b05708649901839511a2be3c85cd9a92..d1dd0ada14b735cad4e14f5f00b92df90aa1f56d 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * ACPI support for Intel Lynxpoint LPSS.
  *
- * Copyright (C) 2013, Intel Corporation
+ * Copyright (C) 2013, 2014, Intel Corporation
  * Authors: Mika Westerberg <mika.westerberg@linux.intel.com>
  *          Rafael J. Wysocki <rafael.j.wysocki@intel.com>
  *
@@ -60,6 +60,8 @@ ACPI_MODULE_NAME("acpi_lpss");
 #define LPSS_CLK_DIVIDER               BIT(2)
 #define LPSS_LTR                       BIT(3)
 #define LPSS_SAVE_CTX                  BIT(4)
+#define LPSS_DEV_PROXY                 BIT(5)
+#define LPSS_PROXY_REQ                 BIT(6)
 
 struct lpss_private_data;
 
@@ -70,8 +72,10 @@ struct lpss_device_desc {
        void (*setup)(struct lpss_private_data *pdata);
 };
 
+static struct device *proxy_device;
+
 static struct lpss_device_desc lpss_dma_desc = {
-       .flags = LPSS_CLK,
+       .flags = LPSS_CLK | LPSS_PROXY_REQ,
 };
 
 struct lpss_private_data {
@@ -146,22 +150,24 @@ static struct lpss_device_desc byt_pwm_dev_desc = {
 };
 
 static struct lpss_device_desc byt_uart_dev_desc = {
-       .flags = LPSS_CLK | LPSS_CLK_GATE | LPSS_CLK_DIVIDER | LPSS_SAVE_CTX,
+       .flags = LPSS_CLK | LPSS_CLK_GATE | LPSS_CLK_DIVIDER | LPSS_SAVE_CTX |
+                LPSS_DEV_PROXY,
        .prv_offset = 0x800,
        .setup = lpss_uart_setup,
 };
 
 static struct lpss_device_desc byt_spi_dev_desc = {
-       .flags = LPSS_CLK | LPSS_CLK_GATE | LPSS_CLK_DIVIDER | LPSS_SAVE_CTX,
+       .flags = LPSS_CLK | LPSS_CLK_GATE | LPSS_CLK_DIVIDER | LPSS_SAVE_CTX |
+                LPSS_DEV_PROXY,
        .prv_offset = 0x400,
 };
 
 static struct lpss_device_desc byt_sdio_dev_desc = {
-       .flags = LPSS_CLK,
+       .flags = LPSS_CLK | LPSS_DEV_PROXY,
 };
 
 static struct lpss_device_desc byt_i2c_dev_desc = {
-       .flags = LPSS_CLK | LPSS_SAVE_CTX,
+       .flags = LPSS_CLK | LPSS_SAVE_CTX | LPSS_DEV_PROXY,
        .prv_offset = 0x800,
        .setup = byt_i2c_setup,
 };
@@ -368,6 +374,8 @@ static int acpi_lpss_create_device(struct acpi_device *adev,
        adev->driver_data = pdata;
        pdev = acpi_create_platform_device(adev);
        if (!IS_ERR_OR_NULL(pdev)) {
+               if (!proxy_device && dev_desc->flags & LPSS_DEV_PROXY)
+                       proxy_device = &pdev->dev;
                return 1;
        }
 
@@ -593,7 +601,14 @@ static int acpi_lpss_runtime_suspend(struct device *dev)
        if (pdata->dev_desc->flags & LPSS_SAVE_CTX)
                acpi_lpss_save_ctx(dev, pdata);
 
-       return acpi_dev_runtime_suspend(dev);
+       ret = acpi_dev_runtime_suspend(dev);
+       if (ret)
+               return ret;
+
+       if (pdata->dev_desc->flags & LPSS_PROXY_REQ && proxy_device)
+               return pm_runtime_put_sync_suspend(proxy_device);
+
+       return 0;
 }
 
 static int acpi_lpss_runtime_resume(struct device *dev)
@@ -601,6 +616,12 @@ static int acpi_lpss_runtime_resume(struct device *dev)
        struct lpss_private_data *pdata = acpi_driver_data(ACPI_COMPANION(dev));
        int ret;
 
+       if (pdata->dev_desc->flags & LPSS_PROXY_REQ && proxy_device) {
+               ret = pm_runtime_get_sync(proxy_device);
+               if (ret)
+                       return ret;
+       }
+
        ret = acpi_dev_runtime_resume(dev);
        if (ret)
                return ret;