spi: Provide per-message prepare and unprepare operations
authorMark Brown <broonie@linaro.org>
Fri, 4 Oct 2013 23:23:12 +0000 (00:23 +0100)
committerMark Brown <broonie@linaro.org>
Fri, 11 Oct 2013 19:09:13 +0000 (20:09 +0100)
Many SPI drivers perform setup and tear down on every message, usually
doing things like DMA mapping the message. Provide hooks for them to use
to provide such operations.

This is of limited value for drivers that implement transfer_one_message()
but will be of much greater utility with future factoring out of standard
implementations of that function.

Signed-off-by: Mark Brown <broonie@linaro.org>
drivers/spi/spi.c
include/linux/spi/spi.h

index 8bef0c9..8a30c6b 100644 (file)
@@ -606,6 +606,18 @@ static void spi_pump_messages(struct kthread_work *work)
 
        trace_spi_message_start(master->cur_msg);
 
+       if (master->prepare_message) {
+               ret = master->prepare_message(master, master->cur_msg);
+               if (ret) {
+                       dev_err(&master->dev,
+                               "failed to prepare message: %d\n", ret);
+                       master->cur_msg->status = ret;
+                       spi_finalize_current_message(master);
+                       return;
+               }
+               master->cur_msg_prepared = true;
+       }
+
        ret = master->transfer_one_message(master, master->cur_msg);
        if (ret) {
                dev_err(&master->dev,
@@ -687,6 +699,7 @@ void spi_finalize_current_message(struct spi_master *master)
 {
        struct spi_message *mesg;
        unsigned long flags;
+       int ret;
 
        spin_lock_irqsave(&master->queue_lock, flags);
        mesg = master->cur_msg;
@@ -695,6 +708,15 @@ void spi_finalize_current_message(struct spi_master *master)
        queue_kthread_work(&master->kworker, &master->pump_messages);
        spin_unlock_irqrestore(&master->queue_lock, flags);
 
+       if (master->cur_msg_prepared && master->unprepare_message) {
+               ret = master->unprepare_message(master, mesg);
+               if (ret) {
+                       dev_err(&master->dev,
+                               "failed to unprepare message: %d\n", ret);
+               }
+       }
+       master->cur_msg_prepared = false;
+
        mesg->state = NULL;
        if (mesg->complete)
                mesg->complete(mesg->context);
index 887116d..000b50b 100644 (file)
@@ -257,6 +257,8 @@ static inline void spi_unregister_driver(struct spi_driver *sdrv)
  * @queue_lock: spinlock to syncronise access to message queue
  * @queue: message queue
  * @cur_msg: the currently in-flight message
+ * @cur_msg_prepared: spi_prepare_message was called for the currently
+ *                    in-flight message
  * @busy: message pump is busy
  * @running: message pump is running
  * @rt: whether this queue is set to run as a realtime task
@@ -274,6 +276,10 @@ static inline void spi_unregister_driver(struct spi_driver *sdrv)
  * @unprepare_transfer_hardware: there are currently no more messages on the
  *     queue so the subsystem notifies the driver that it may relax the
  *     hardware by issuing this call
+ * @prepare_message: set up the controller to transfer a single message,
+ *                   for example doing DMA mapping.  Called from threaded
+ *                   context.
+ * @unprepare_message: undo any work done by prepare_message().
  * @cs_gpios: Array of GPIOs to use as chip select lines; one per CS
  *     number. Any individual value may be -ENOENT for CS lines that
  *     are not GPIOs (driven by the SPI controller itself).
@@ -388,11 +394,16 @@ struct spi_master {
        bool                            running;
        bool                            rt;
        bool                            auto_runtime_pm;
+       bool                            cur_msg_prepared;
 
        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);
+       int (*prepare_message)(struct spi_master *master,
+                              struct spi_message *message);
+       int (*unprepare_message)(struct spi_master *master,
+                                struct spi_message *message);
 
        /* gpio chip select */
        int                     *cs_gpios;