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
