spi: Provide core support for runtime PM during transfers
authorMark Brown <broonie@linaro.org>
Sun, 28 Jul 2013 13:47:02 +0000 (14:47 +0100)
committerMark Brown <broonie@linaro.org>
Mon, 29 Jul 2013 16:59:20 +0000 (17:59 +0100)
Most SPI drivers that implement runtime PM support use identical code to
do so: they acquire a runtime PM lock in prepare_transfer_hardware() and
then they release it in unprepare_transfer_hardware(). The variations in
this are mostly missing error checking and the choice to use autosuspend.

Since these runtime PM calls are normally the only thing in the prepare
and unprepare callbacks and the autosuspend API transparently does the
right thing on devices with autosuspend disabled factor all of this out
into the core with a flag to enable the behaviour.

Signed-off-by: Mark Brown <broonie@linaro.org>
Reviewed-by: Stephen Warren <swarren@nvidia.com>
Acked-by: Linus Walleij <linus.walleij@linaro.org>
drivers/spi/spi.c
include/linux/spi/spi.h

index 978dda2c523982f62c676573feb1955da0ca82da..361cced68069ef41aefb2b57e4b33dfa340bfcef 100644 (file)
@@ -553,6 +553,10 @@ static void spi_pump_messages(struct kthread_work *work)
                    master->unprepare_transfer_hardware(master))
                        dev_err(&master->dev,
                                "failed to unprepare transfer hardware\n");
+               if (master->auto_runtime_pm) {
+                       pm_runtime_mark_last_busy(master->dev.parent);
+                       pm_runtime_put_autosuspend(master->dev.parent);
+               }
                return;
        }
 
@@ -572,11 +576,23 @@ static void spi_pump_messages(struct kthread_work *work)
                master->busy = true;
        spin_unlock_irqrestore(&master->queue_lock, flags);
 
+       if (!was_busy && master->auto_runtime_pm) {
+               ret = pm_runtime_get_sync(master->dev.parent);
+               if (ret < 0) {
+                       dev_err(&master->dev, "Failed to power device: %d\n",
+                               ret);
+                       return;
+               }
+       }
+
        if (!was_busy && master->prepare_transfer_hardware) {
                ret = master->prepare_transfer_hardware(master);
                if (ret) {
                        dev_err(&master->dev,
                                "failed to prepare transfer hardware\n");
+
+                       if (master->auto_runtime_pm)
+                               pm_runtime_put(master->dev.parent);
                        return;
                }
        }
index 28e440be1c07aafaa4ac62470344b052f9107d8f..bf0204c00053460c133f30c1ef6f2dec97bf707f 100644 (file)
@@ -254,6 +254,9 @@ static inline void spi_unregister_driver(struct spi_driver *sdrv)
  * @busy: message pump is busy
  * @running: message pump is running
  * @rt: whether this queue is set to run as a realtime task
+ * @auto_runtime_pm: the core should ensure a runtime PM reference is held
+ *                   while the hardware is prepared, using the parent
+ *                   device for the spidev
  * @prepare_transfer_hardware: a message will soon arrive from the queue
  *     so the subsystem requests the driver to prepare the transfer hardware
  *     by issuing this call
@@ -374,11 +377,13 @@ struct spi_master {
        bool                            busy;
        bool                            running;
        bool                            rt;
+       bool                            auto_runtime_pm;
 
        int (*prepare_transfer_hardware)(struct spi_master *master);
        int (*transfer_one_message)(struct spi_master *master,
                                    struct spi_message *mesg);
        int (*unprepare_transfer_hardware)(struct spi_master *master);
+
        /* gpio chip select */
        int                     *cs_gpios;
 };
This page took 0.027765 seconds and 5 git commands to generate.