addressed runtime-PM functionality issues that arose from
system integration testing.

changes include the following
1) return a retryable error from transfer() while resuming from runtime-PM
   the driver must autonomously resume from runtime-PM suspend
   but this operation takes time and transfer() may be called
   from a non-sleepable context.
2) fixed runtime-PM initialization for PCI device
   initialized to active/idle
   pm_runtime_allow() required to enable runtime-PM on PCI devices
3) changed runtime idle routine to perform delayed suspend to dampen
   suspend/resume frequency

Signed-off-by: Russ Gorby <[email protected]>
---
 drivers/spi/pw_spi3.c |  130 +++++++++++++++++++++++++++++--------------------
 1 files changed, 77 insertions(+), 53 deletions(-)

diff --git a/drivers/spi/pw_spi3.c b/drivers/spi/pw_spi3.c
index 61f4d27..06a2d69 100644
--- a/drivers/spi/pw_spi3.c
+++ b/drivers/spi/pw_spi3.c
@@ -107,6 +107,16 @@ struct callback_param {
        int *donep;
 };
 
+enum dd_pwrstate {
+       PWRSTATE_ON = 1,
+       PWRSTATE_IDLE,
+       PWRSTATE_OFF,
+};
+
+enum dd_pwrflags {
+       PWRFLAG_RTRESUMING,
+};
+
 struct driver_data {
        /* Driver model hookup */
        struct pci_dev *pdev;
@@ -149,10 +159,8 @@ struct driver_data {
        int dma_inited;
 
        /* pwrstate mgmt */
-       int pwrstate;
-#define PWRSTATE_ON            1
-#define PWRSTATE_IDLE          2
-#define PWRSTATE_OFF           3
+       int pwrstate;           /* enum dd_pwrstate */
+       unsigned long pwrflags; /* enum dd_pwrflags */
 
        /* used by DMA code */
        struct pci_dev *dmac1;
@@ -752,15 +760,19 @@ static int transfer(struct spi_device *spi, struct 
spi_message *msg)
        u32 cr0;
        u32 cr1;
 
-       if (drv_data->pwrstate != PWRSTATE_ON) {
+       if (drv_data->pwrstate == PWRSTATE_OFF) {
                dev_dbg(&drv_data->pdev->dev, "transfer: busy, pwrstate:%d",
                        drv_data->pwrstate);
+               return -EIO;
+       }
+       if (drv_data->pwrstate == PWRSTATE_IDLE) {
+               if (!test_and_set_bit(PWRFLAG_RTRESUMING, &drv_data->pwrflags))
+                       pm_runtime_get(&drv_data->pdev->dev);
                return -EBUSY;
        }
        msg->actual_length = 0;
        msg->status = -EINPROGRESS;
        drv_data->cur_msg = msg;
-       /* Initial message state*/
 
        /* We handle only one transfer message since the protocol module has to
           control the out of band signaling. */
@@ -777,7 +789,8 @@ static int transfer(struct spi_device *spi, struct 
spi_message *msg)
        }
 
        /* Setup the transfer state based on the type of transfer */
-       pm_runtime_get(&drv_data->pdev->dev);
+       if (!test_and_clear_bit(PWRFLAG_RTRESUMING, &drv_data->pwrflags))
+               pm_runtime_get(&drv_data->pdev->dev);
        chip = spi_get_ctldata(msg->spi);
        flush(drv_data);
        drv_data->n_bytes = chip->n_bytes;
@@ -897,10 +910,10 @@ static int setup(struct spi_device *spi)
        u32 clk_div;
        struct driver_data *drv_data = spi_master_get_devdata(spi->master);
 
-       if (drv_data->pwrstate != PWRSTATE_ON) {
+       if (drv_data->pwrstate == PWRSTATE_OFF) {
                dev_dbg(&drv_data->pdev->dev, "setup: busy, pwrstate:%d",
                        drv_data->pwrstate);
-               return -EBUSY;
+               return -EIO;
        }
 
        if (!spi->bits_per_word)
@@ -929,7 +942,6 @@ static int setup(struct spi_device *spi)
        chip_info = spi->controller_data;
 
        /* chip_info isn't always needed */
-
        if (chip_info) {
                if (chip_info->timeout)
                        chip->timeout = chip_info->timeout;
@@ -1114,13 +1126,16 @@ static int pw_spi_probe(struct pci_dev *pdev, const 
struct pci_device_id *ent)
                goto err_free_4;
        }
 
-       /* set pm runtime power state and register with power system */
-       pm_runtime_set_active(dev);
-       pm_runtime_enable(dev);
+
 
        pw_spi_dma_init(drv_data);
        pci_set_drvdata(pdev, drv_data);
 
+       pm_runtime_set_active(&pdev->dev);
+       pm_runtime_enable(&pdev->dev);
+       pm_runtime_idle(&pdev->dev);
+       pm_runtime_allow(dev);
+
        return status;
 
 err_free_4:
@@ -1168,8 +1183,6 @@ static void __devexit pw_spi_remove(struct pci_dev *pdev)
        return;
 }
 
-#ifdef CONFIG_PM
-
 /*
  * for now IDLE and OFF states are treated the same
  */
@@ -1215,29 +1228,6 @@ static int _pm_suspend(struct pci_dev *pdev, int to)
        return 0;
 }
 
-static int pw_spi_suspend(struct pci_dev *pdev, pm_message_t state)
-{
-       int retval;
-       struct driver_data *drv_data = pci_get_drvdata(pdev);
-
-       dev_dbg(&pdev->dev, "suspend");
-
-       if (drv_data->pwrstate != PWRSTATE_ON)
-               dev_warn(&pdev->dev, "suspend: !on, pwrstate:%d",
-                        drv_data->pwrstate);
-       retval = _pm_suspend(pdev, PWRSTATE_OFF);
-       if (retval)
-               return retval;
-       retval = pci_prepare_to_sleep(pdev);
-       if (retval) {
-               dev_err(&pdev->dev, "suspend: prepare to sleep failed");
-               return retval;
-       }
-       pci_disable_device(pdev);
-
-       return 0;
-}
-
 /*
  * for now IDLE and OFF states are treated the same
  */
@@ -1272,13 +1262,36 @@ static void _pm_resume(struct pci_dev *pdev)
        }
 }
 
-static int pw_spi_resume(struct pci_dev *pdev)
+
+#ifdef CONFIG_PM
+
+static int pw_spi_suspend(struct pci_dev *pdev, pm_message_t state)
 {
        int retval;
        struct driver_data *drv_data = pci_get_drvdata(pdev);
 
-       dev_dbg(&pdev->dev, "resume");
+       dev_dbg(&pdev->dev, "%s called", __func__);
+       if (drv_data->pwrstate != PWRSTATE_ON)
+               dev_warn(&pdev->dev, "suspend: !on, pwrstate:%d",
+                        drv_data->pwrstate);
+       retval = _pm_suspend(pdev, PWRSTATE_OFF);
+       if (retval)
+               return retval;
+       retval = pci_prepare_to_sleep(pdev);
+       if (retval) {
+               dev_err(&pdev->dev, "suspend: prepare to sleep failed");
+               return retval;
+       }
+       pci_disable_device(pdev);
+       return 0;
+}
+
+static int pw_spi_resume(struct pci_dev *pdev)
+{
+       int retval;
+       struct driver_data *drv_data = pci_get_drvdata(pdev);
 
+       dev_dbg(&pdev->dev, "%s called", __func__);
        if (drv_data->pwrstate != PWRSTATE_OFF)
                dev_warn(&pdev->dev, "resume: !off, pwrstate:%d",
                         drv_data->pwrstate);
@@ -1293,9 +1306,9 @@ static int pw_spi_resume(struct pci_dev *pdev)
                return retval;
        }
        _pm_resume(pdev);
-
        return 0;
 }
+
 #else
 #define pw_spi_suspend NULL
 #define pw_spi_resume NULL
@@ -1308,17 +1321,20 @@ static int pw_spi_pm_runtime_resume(struct device *dev)
        struct pci_dev *pdev = to_pci_dev(dev);
        struct driver_data *drv_data = pci_get_drvdata(pdev);
 
-       dev_dbg(dev, "pm runtime resume");
-
+       dev_dbg(dev, "%s called", __func__);
+       if (drv_data->pwrstate == PWRSTATE_ON)
+               return 0;
        if (drv_data->pwrstate != PWRSTATE_IDLE)
-               dev_warn(&pdev->dev, "rt suspend: !idle, pwrstate:%d",
+               dev_warn(&pdev->dev, "rt resume: !idle, pwrstate:%d",
                         drv_data->pwrstate);
-       retval = pci_back_from_sleep(pdev);
+       pci_set_power_state(pdev, PCI_D0);
+       pci_restore_state(pdev);
+       retval = pci_enable_device(pdev);
        if (retval)
-               dev_err(&pdev->dev, "rt resume: back from sleep failed");
+               return retval;
        _pm_resume(pdev);
 
-       return 0;
+       return retval;
 }
 
 static int pw_spi_pm_runtime_suspend(struct device *dev)
@@ -1327,24 +1343,32 @@ static int pw_spi_pm_runtime_suspend(struct device *dev)
        struct pci_dev *pdev = to_pci_dev(dev);
        struct driver_data *drv_data = pci_get_drvdata(pdev);
 
-       dev_dbg(dev, "pm runtime suspend");
-
+       dev_dbg(dev, "%s called", __func__);
        if (drv_data->pwrstate != PWRSTATE_ON)
                dev_warn(&pdev->dev, "rt suspend: !on, pwrstate:%d",
                         drv_data->pwrstate);
        retval = _pm_suspend(pdev, PWRSTATE_IDLE);
        if (retval)
                return retval;
-       retval = pci_prepare_to_sleep(pdev);
-       if (retval)
-               dev_err(&pdev->dev, "rt suspend: prepare to sleep failed");
+       pci_save_state(pdev);
+       pci_enable_wake(pdev, PCI_D3hot, 0);
+       pci_disable_device(pdev);
+       pci_set_power_state(pdev, PCI_D3hot);
 
-       return 0;
+       return retval;
+}
+
+static int pw_spi_pm_runtime_idle(struct device *dev)
+{
+       dev_dbg(dev, "%s called", __func__);
+       pm_schedule_suspend(dev, 100);
+       return -EBUSY;
 }
 
 static const struct dev_pm_ops pw_spi_pm = {
        .runtime_resume = pw_spi_pm_runtime_resume,
        .runtime_suspend = pw_spi_pm_runtime_suspend,
+       .runtime_idle =  pw_spi_pm_runtime_idle,
 };
 
 static const struct pci_device_id pci_ids[] __devinitdata = {
-- 
1.6.0.6

_______________________________________________
MeeGo-kernel mailing list
[email protected]
http://lists.meego.com/listinfo/meego-kernel

Reply via email to