]> git.proxmox.com Git - mirror_ubuntu-artful-kernel.git/blobdiff - drivers/spi/spi-atmel.c
Merge remote-tracking branches 'spi/topic/atmel', 'spi/topic/cadence', 'spi/topic...
[mirror_ubuntu-artful-kernel.git] / drivers / spi / spi-atmel.c
index 113c83f44b5cdfd0a5840ff87c74176a3285fde2..e4193ccc4970634f32e4ddec97bef7775c802204 100644 (file)
@@ -26,6 +26,7 @@
 #include <linux/io.h>
 #include <linux/gpio.h>
 #include <linux/pinctrl/consumer.h>
+#include <linux/pm_runtime.h>
 
 /* SPI register offsets */
 #define SPI_CR                                 0x0000
 
 #define SPI_DMA_TIMEOUT                (msecs_to_jiffies(1000))
 
+#define AUTOSUSPEND_TIMEOUT    2000
+
 struct atmel_spi_dma {
        struct dma_chan                 *chan_rx;
        struct dma_chan                 *chan_tx;
@@ -414,23 +417,6 @@ static int atmel_spi_dma_slave_config(struct atmel_spi *as,
        return err;
 }
 
-static bool filter(struct dma_chan *chan, void *pdata)
-{
-       struct atmel_spi_dma *sl_pdata = pdata;
-       struct at_dma_slave *sl;
-
-       if (!sl_pdata)
-               return false;
-
-       sl = &sl_pdata->dma_slave;
-       if (sl->dma_dev == chan->device->dev) {
-               chan->private = sl;
-               return true;
-       } else {
-               return false;
-       }
-}
-
 static int atmel_spi_configure_dma(struct atmel_spi *as)
 {
        struct dma_slave_config slave_config;
@@ -441,19 +427,24 @@ static int atmel_spi_configure_dma(struct atmel_spi *as)
        dma_cap_zero(mask);
        dma_cap_set(DMA_SLAVE, mask);
 
-       as->dma.chan_tx = dma_request_slave_channel_compat(mask, filter,
-                                                          &as->dma,
-                                                          dev, "tx");
-       if (!as->dma.chan_tx) {
+       as->dma.chan_tx = dma_request_slave_channel_reason(dev, "tx");
+       if (IS_ERR(as->dma.chan_tx)) {
+               err = PTR_ERR(as->dma.chan_tx);
+               if (err == -EPROBE_DEFER) {
+                       dev_warn(dev, "no DMA channel available at the moment\n");
+                       return err;
+               }
                dev_err(dev,
                        "DMA TX channel not available, SPI unable to use DMA\n");
                err = -EBUSY;
                goto error;
        }
 
-       as->dma.chan_rx = dma_request_slave_channel_compat(mask, filter,
-                                                          &as->dma,
-                                                          dev, "rx");
+       /*
+        * No reason to check EPROBE_DEFER here since we have already requested
+        * tx channel. If it fails here, it's for another reason.
+        */
+       as->dma.chan_rx = dma_request_slave_channel(dev, "rx");
 
        if (!as->dma.chan_rx) {
                dev_err(dev,
@@ -474,7 +465,7 @@ static int atmel_spi_configure_dma(struct atmel_spi *as)
 error:
        if (as->dma.chan_rx)
                dma_release_channel(as->dma.chan_rx);
-       if (as->dma.chan_tx)
+       if (!IS_ERR(as->dma.chan_tx))
                dma_release_channel(as->dma.chan_tx);
        return err;
 }
@@ -482,11 +473,9 @@ error:
 static void atmel_spi_stop_dma(struct atmel_spi *as)
 {
        if (as->dma.chan_rx)
-               as->dma.chan_rx->device->device_control(as->dma.chan_rx,
-                                                       DMA_TERMINATE_ALL, 0);
+               dmaengine_terminate_all(as->dma.chan_rx);
        if (as->dma.chan_tx)
-               as->dma.chan_tx->device->device_control(as->dma.chan_tx,
-                                                       DMA_TERMINATE_ALL, 0);
+               dmaengine_terminate_all(as->dma.chan_tx);
 }
 
 static void atmel_spi_release_dma(struct atmel_spi *as)
@@ -1315,6 +1304,7 @@ static int atmel_spi_probe(struct platform_device *pdev)
        master->setup = atmel_spi_setup;
        master->transfer_one_message = atmel_spi_transfer_one_message;
        master->cleanup = atmel_spi_cleanup;
+       master->auto_runtime_pm = true;
        platform_set_drvdata(pdev, master);
 
        as = spi_master_get_devdata(master);
@@ -1347,8 +1337,11 @@ static int atmel_spi_probe(struct platform_device *pdev)
        as->use_dma = false;
        as->use_pdc = false;
        if (as->caps.has_dma_support) {
-               if (atmel_spi_configure_dma(as) == 0)
+               ret = atmel_spi_configure_dma(as);
+               if (ret == 0)
                        as->use_dma = true;
+               else if (ret == -EPROBE_DEFER)
+                       return ret;
        } else {
                as->use_pdc = true;
        }
@@ -1387,6 +1380,11 @@ static int atmel_spi_probe(struct platform_device *pdev)
        dev_info(&pdev->dev, "Atmel SPI Controller at 0x%08lx (irq %d)\n",
                        (unsigned long)regs->start, irq);
 
+       pm_runtime_set_autosuspend_delay(&pdev->dev, AUTOSUSPEND_TIMEOUT);
+       pm_runtime_use_autosuspend(&pdev->dev);
+       pm_runtime_set_active(&pdev->dev);
+       pm_runtime_enable(&pdev->dev);
+
        ret = devm_spi_register_master(&pdev->dev, master);
        if (ret)
                goto out_free_dma;
@@ -1394,6 +1392,9 @@ static int atmel_spi_probe(struct platform_device *pdev)
        return 0;
 
 out_free_dma:
+       pm_runtime_disable(&pdev->dev);
+       pm_runtime_set_suspended(&pdev->dev);
+
        if (as->use_dma)
                atmel_spi_release_dma(as);
 
@@ -1415,6 +1416,8 @@ static int atmel_spi_remove(struct platform_device *pdev)
        struct spi_master       *master = platform_get_drvdata(pdev);
        struct atmel_spi        *as = spi_master_get_devdata(master);
 
+       pm_runtime_get_sync(&pdev->dev);
+
        /* reset the hardware and block queue progress */
        spin_lock_irq(&as->lock);
        if (as->use_dma) {
@@ -1432,14 +1435,37 @@ static int atmel_spi_remove(struct platform_device *pdev)
 
        clk_disable_unprepare(as->clk);
 
+       pm_runtime_put_noidle(&pdev->dev);
+       pm_runtime_disable(&pdev->dev);
+
        return 0;
 }
 
-#ifdef CONFIG_PM_SLEEP
+#ifdef CONFIG_PM
+static int atmel_spi_runtime_suspend(struct device *dev)
+{
+       struct spi_master *master = dev_get_drvdata(dev);
+       struct atmel_spi *as = spi_master_get_devdata(master);
+
+       clk_disable_unprepare(as->clk);
+       pinctrl_pm_select_sleep_state(dev);
+
+       return 0;
+}
+
+static int atmel_spi_runtime_resume(struct device *dev)
+{
+       struct spi_master *master = dev_get_drvdata(dev);
+       struct atmel_spi *as = spi_master_get_devdata(master);
+
+       pinctrl_pm_select_default_state(dev);
+
+       return clk_prepare_enable(as->clk);
+}
+
 static int atmel_spi_suspend(struct device *dev)
 {
-       struct spi_master       *master = dev_get_drvdata(dev);
-       struct atmel_spi        *as = spi_master_get_devdata(master);
+       struct spi_master *master = dev_get_drvdata(dev);
        int ret;
 
        /* Stop the queue running */
@@ -1449,22 +1475,22 @@ static int atmel_spi_suspend(struct device *dev)
                return ret;
        }
 
-       clk_disable_unprepare(as->clk);
-
-       pinctrl_pm_select_sleep_state(dev);
+       if (!pm_runtime_suspended(dev))
+               atmel_spi_runtime_suspend(dev);
 
        return 0;
 }
 
 static int atmel_spi_resume(struct device *dev)
 {
-       struct spi_master       *master = dev_get_drvdata(dev);
-       struct atmel_spi        *as = spi_master_get_devdata(master);
+       struct spi_master *master = dev_get_drvdata(dev);
        int ret;
 
-       pinctrl_pm_select_default_state(dev);
-
-       clk_prepare_enable(as->clk);
+       if (!pm_runtime_suspended(dev)) {
+               ret = atmel_spi_runtime_resume(dev);
+               if (ret)
+                       return ret;
+       }
 
        /* Start the queue running */
        ret = spi_master_resume(master);
@@ -1474,8 +1500,11 @@ static int atmel_spi_resume(struct device *dev)
        return ret;
 }
 
-static SIMPLE_DEV_PM_OPS(atmel_spi_pm_ops, atmel_spi_suspend, atmel_spi_resume);
-
+static const struct dev_pm_ops atmel_spi_pm_ops = {
+       SET_SYSTEM_SLEEP_PM_OPS(atmel_spi_suspend, atmel_spi_resume)
+       SET_RUNTIME_PM_OPS(atmel_spi_runtime_suspend,
+                          atmel_spi_runtime_resume, NULL)
+};
 #define ATMEL_SPI_PM_OPS       (&atmel_spi_pm_ops)
 #else
 #define ATMEL_SPI_PM_OPS       NULL