diff options
author | Todd Poynor <toddpoynor@google.com> | 2010-07-30 12:09:22 -0700 |
---|---|---|
committer | Dan Willemsen <dwillemsen@nvidia.com> | 2011-11-30 21:35:41 -0800 |
commit | 193fa20152f593c8a16c656edba874191e8a5c99 (patch) | |
tree | f40e3e9429cb5fd073f11a04eedddd742ad870b4 /drivers/spi | |
parent | 8f15e1e10ef3ba8eeebd97a6a81c6b63666c9034 (diff) |
[ARM] Tegra: SPI: Suspend/resume.
Save/restore SLINK_COMMAND_0 register.
Wait for in-progress transactions to complete before suspend.
Reject and WARN_ON transactions when suspended.
Change-Id: I0527781f0bf95781afa3a35a68282cde2f0189ae
Signed-off-by: Todd Poynor <toddpoynor@google.com>
Diffstat (limited to 'drivers/spi')
-rw-r--r-- | drivers/spi/spi-tegra.c | 58 |
1 files changed, 57 insertions, 1 deletions
diff --git a/drivers/spi/spi-tegra.c b/drivers/spi/spi-tegra.c index a5a6302dc8e0..aa25ed77f0b5 100644 --- a/drivers/spi/spi-tegra.c +++ b/drivers/spi/spi-tegra.c @@ -165,6 +165,8 @@ struct spi_tegra_data { struct tegra_dma_channel *rx_dma; u32 *rx_bb; dma_addr_t rx_bb_phys; + bool is_suspended; + unsigned long save_slink_cmd; }; @@ -450,9 +452,15 @@ static int spi_tegra_transfer(struct spi_device *spi, struct spi_message *m) return -EINVAL; } + spin_lock_irqsave(&tspi->lock, flags); + + if (WARN_ON(tspi->is_suspended)) { + spin_unlock_irqrestore(&tspi->lock, flags); + return -EBUSY; + } + m->state = spi; - spin_lock_irqsave(&tspi->lock, flags); was_empty = list_empty(&tspi->queue); list_add_tail(&m->queue, &tspi->queue); @@ -594,6 +602,50 @@ static int __devexit spi_tegra_remove(struct platform_device *pdev) return 0; } +#ifdef CONFIG_PM +static int spi_tegra_suspend(struct platform_device *pdev, pm_message_t state) +{ + struct spi_master *master; + struct spi_tegra_data *tspi; + unsigned long flags; + unsigned limit = 500; + + master = dev_get_drvdata(&pdev->dev); + tspi = spi_master_get_devdata(master); + spin_lock_irqsave(&tspi->lock, flags); + tspi->is_suspended = true; + WARN_ON(!list_empty(&tspi->queue)); + + while (!list_empty(&tspi->queue) && limit--) { + spin_unlock_irqrestore(&tspi->lock, flags); + msleep(10); + spin_lock_irqsave(&tspi->lock, flags); + } + + tspi->save_slink_cmd = spi_tegra_readl(tspi, SLINK_COMMAND); + spin_unlock_irqrestore(&tspi->lock, flags); + return 0; +} + +static int spi_tegra_resume(struct platform_device *pdev) +{ + struct spi_master *master; + struct spi_tegra_data *tspi; + unsigned long flags; + + master = dev_get_drvdata(&pdev->dev); + tspi = spi_master_get_devdata(master); + spin_lock_irqsave(&tspi->lock, flags); + clk_enable(tspi->clk); + spi_tegra_writel(tspi, tspi->save_slink_cmd, SLINK_COMMAND); + clk_disable(tspi->clk); + tspi->cur_speed = 0; + tspi->is_suspended = false; + spin_unlock_irqrestore(&tspi->lock, flags); + return 0; +} +#endif + MODULE_ALIAS("platform:spi_tegra"); #ifdef CONFIG_OF @@ -613,6 +665,10 @@ static struct platform_driver spi_tegra_driver = { .of_match_table = spi_tegra_of_match_table, }, .remove = __devexit_p(spi_tegra_remove), +#ifdef CONFIG_PM + .suspend = spi_tegra_suspend, + .resume = spi_tegra_resume, +#endif }; static int __init spi_tegra_init(void) |