diff options
author | Laxman Dewangan <ldewangan@nvidia.com> | 2011-09-13 18:27:55 +0530 |
---|---|---|
committer | Dan Willemsen <dwillemsen@nvidia.com> | 2012-03-23 16:10:07 -0700 |
commit | 13da6e9163a8489efcd7b83e767f4bb289a13997 (patch) | |
tree | 4f8e6882afa4e1d22ab52fd857e4bca8db7c6c73 /drivers/spi/spi-tegra.c | |
parent | 8de9bfbe536d931ba94b52c9a06aa9e2acbc8129 (diff) |
spi: tegra: Handles suspend when spi transfer is in progress
Avoiding the suspend of the system if the spi transfer is
in progress for current transfer queue.
bug 864987
Reviewed-on: http://git-master/r/56599
(cherry picked from commit 0ba8ed371f2937a095752a0edbc15ed75664644a)
Change-Id: Ife7ae8a7d66a66d047ee2c8829d16017571b4d58
Reviewed-on: http://git-master/r/57001
Reviewed-by: Laxman Dewangan <ldewangan@nvidia.com>
Tested-by: Laxman Dewangan <ldewangan@nvidia.com>
Reviewed-by: Bitan Biswas <bbiswas@nvidia.com>
Rebase-Id: Rc66d6c7ae51ea6709d5e47331fef30c87029b343
Diffstat (limited to 'drivers/spi/spi-tegra.c')
-rw-r--r-- | drivers/spi/spi-tegra.c | 49 |
1 files changed, 47 insertions, 2 deletions
diff --git a/drivers/spi/spi-tegra.c b/drivers/spi/spi-tegra.c index 223680298d47..28e011d723a4 100644 --- a/drivers/spi/spi-tegra.c +++ b/drivers/spi/spi-tegra.c @@ -220,6 +220,7 @@ struct spi_tegra_data { struct completion rx_dma_complete; struct completion tx_dma_complete; + bool is_transfer_in_progress; u32 rx_complete; u32 tx_complete; @@ -678,6 +679,7 @@ static void spi_tegra_start_transfer(struct spi_device *spi, command2 = tspi->def_command2_reg; if (is_first_of_msg) { + tspi->is_transfer_in_progress = true; if (!tspi->is_clkon_always) { if (!tspi->clk_state) { clk_enable(tspi->clk); @@ -900,6 +902,7 @@ static void spi_tegra_curr_transfer_complete(struct spi_tegra_data *tspi, spi = m->state; m->actual_length += cur_xfer_size; + if (!list_is_last(&tspi->cur->transfer_list, &m->transfers)) { tspi->cur = list_first_entry(&tspi->cur->transfer_list, struct spi_transfer, transfer_list); @@ -908,6 +911,8 @@ static void spi_tegra_curr_transfer_complete(struct spi_tegra_data *tspi, list_del(&m->queue); m->complete(m->context); if (!list_empty(&tspi->queue)) { + if (tspi->is_suspended) + goto stop_transfer; m = list_first_entry(&tspi->queue, struct spi_message, queue); spi = m->state; @@ -926,8 +931,15 @@ static void spi_tegra_curr_transfer_complete(struct spi_tegra_data *tspi, tspi->clk_state = 0; } } + tspi->is_transfer_in_progress = false; } } + return; +stop_transfer: + spi_tegra_writel(tspi, tspi->def_command_reg, SLINK_COMMAND); + spi_tegra_writel(tspi, tspi->def_command2_reg, SLINK_COMMAND2); + tspi->is_transfer_in_progress = false; + return; } static void tegra_spi_tx_dma_complete(struct tegra_dma_req *req) @@ -1126,6 +1138,7 @@ static int __devinit spi_tegra_probe(struct platform_device *pdev) tspi = spi_master_get_devdata(master); tspi->master = master; tspi->pdev = pdev; + tspi->is_transfer_in_progress = false; spin_lock_init(&tspi->lock); r = platform_get_resource(pdev, IORESOURCE_MEM, 0); @@ -1366,9 +1379,12 @@ static int spi_tegra_suspend(struct platform_device *pdev, pm_message_t state) 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)); + /* Wait for all transfer completes */ + if (!list_empty(&tspi->queue)) + dev_warn(&pdev->dev, "The transfer list is not empty " + "Waiting for time %d ms to complete transfer\n", + limit * 20); while (!list_empty(&tspi->queue) && limit--) { spin_unlock_irqrestore(&tspi->lock, flags); @@ -1376,6 +1392,28 @@ static int spi_tegra_suspend(struct platform_device *pdev, pm_message_t state) spin_lock_irqsave(&tspi->lock, flags); } + /* Wait for current transfer completes only */ + tspi->is_suspended = true; + if (!list_empty(&tspi->queue)) { + limit = 50; + dev_err(&pdev->dev, "All transfer has not completed, " + "Waiting for %d ms current transfer to complete\n", + limit * 20); + while (tspi->is_transfer_in_progress && limit--) { + spin_unlock_irqrestore(&tspi->lock, flags); + msleep(20); + spin_lock_irqsave(&tspi->lock, flags); + } + } + + if (tspi->is_transfer_in_progress) { + dev_err(&pdev->dev, "Spi transfer is in progress " + "Avoiding suspend\n"); + tspi->is_suspended = false; + spi_unlock_irqrestore(&tspi->lock, flags); + return -EBUSY; + } + spin_unlock_irqrestore(&tspi->lock, flags); if (tspi->is_clkon_always) { clk_disable(tspi->clk); @@ -1389,6 +1427,8 @@ static int spi_tegra_resume(struct platform_device *pdev) struct spi_master *master; struct spi_tegra_data *tspi; unsigned long flags; + struct spi_message *m; + struct spi_device *spi; master = dev_get_drvdata(&pdev->dev); tspi = spi_master_get_devdata(master); @@ -1404,6 +1444,11 @@ static int spi_tegra_resume(struct platform_device *pdev) tspi->cur_speed = 0; tspi->is_suspended = false; + if (!list_empty(&tspi->queue)) { + m = list_first_entry(&tspi->queue, struct spi_message, queue); + spi = m->state; + spi_tegra_start_message(spi, m); + } spin_unlock_irqrestore(&tspi->lock, flags); return 0; } |