spi/pxa2xx: convert to the pump message infrastructure
authorMika Westerberg <mika.westerberg@linux.intel.com>
Tue, 22 Jan 2013 10:26:26 +0000 (12:26 +0200)
committerMark Brown <broonie@opensource.wolfsonmicro.com>
Sat, 26 Jan 2013 07:21:37 +0000 (15:21 +0800)
The SPI core provides infrastructure for standard message queueing so use
that instead of handling everything in the driver. This simplifies the
driver.

Signed-off-by: Mika Westerberg <mika.westerberg@linux.intel.com>
Acked-by: Linus Walleij <linus.walleij@linaro.org>
Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com>
drivers/spi/spi-pxa2xx.c

index ef0a78650e9626ec965ad31871626c48ebb07300..a241891355db52c78bff3c6bb2ca895576a64e4f 100644 (file)
@@ -85,9 +85,6 @@ DEFINE_SSP_REG(SSPSP, 0x2c)
 #define DONE_STATE ((void*)2)
 #define ERROR_STATE ((void*)-1)
 
-#define QUEUE_RUNNING 0
-#define QUEUE_STOPPED 1
-
 struct driver_data {
        /* Driver model hookup */
        struct platform_device *pdev;
@@ -117,14 +114,6 @@ struct driver_data {
        u32 clear_sr;
        u32 mask_sr;
 
-       /* Driver message queue */
-       struct workqueue_struct *workqueue;
-       struct work_struct pump_messages;
-       spinlock_t lock;
-       struct list_head queue;
-       int busy;
-       int run;
-
        /* Message Transfer pump */
        struct tasklet_struct pump_transfers;
 
@@ -173,8 +162,6 @@ struct chip_data {
        void (*cs_control)(u32 command);
 };
 
-static void pump_messages(struct work_struct *work);
-
 static void cs_assert(struct driver_data *drv_data)
 {
        struct chip_data *chip = drv_data->cur_chip;
@@ -444,15 +431,11 @@ static void unmap_dma_buffers(struct driver_data *drv_data)
 static void giveback(struct driver_data *drv_data)
 {
        struct spi_transfer* last_transfer;
-       unsigned long flags;
        struct spi_message *msg;
 
-       spin_lock_irqsave(&drv_data->lock, flags);
        msg = drv_data->cur_msg;
        drv_data->cur_msg = NULL;
        drv_data->cur_transfer = NULL;
-       queue_work(drv_data->workqueue, &drv_data->pump_messages);
-       spin_unlock_irqrestore(&drv_data->lock, flags);
 
        last_transfer = list_entry(msg->transfers.prev,
                                        struct spi_transfer,
@@ -481,13 +464,7 @@ static void giveback(struct driver_data *drv_data)
                 */
 
                /* get a pointer to the next message, if any */
-               spin_lock_irqsave(&drv_data->lock, flags);
-               if (list_empty(&drv_data->queue))
-                       next_msg = NULL;
-               else
-                       next_msg = list_entry(drv_data->queue.next,
-                                       struct spi_message, queue);
-               spin_unlock_irqrestore(&drv_data->lock, flags);
+               next_msg = spi_get_next_queued_message(drv_data->master);
 
                /* see if the next and current messages point
                 * to the same chip
@@ -498,10 +475,7 @@ static void giveback(struct driver_data *drv_data)
                        cs_deassert(drv_data);
        }
 
-       msg->state = NULL;
-       if (msg->complete)
-               msg->complete(msg->context);
-
+       spi_finalize_current_message(drv_data->master);
        drv_data->cur_chip = NULL;
 }
 
@@ -1176,31 +1150,12 @@ static void pump_transfers(unsigned long data)
        write_SSCR1(cr1, reg);
 }
 
-static void pump_messages(struct work_struct *work)
+static int pxa2xx_spi_transfer_one_message(struct spi_master *master,
+                                          struct spi_message *msg)
 {
-       struct driver_data *drv_data =
-               container_of(work, struct driver_data, pump_messages);
-       unsigned long flags;
-
-       /* Lock queue and check for queue work */
-       spin_lock_irqsave(&drv_data->lock, flags);
-       if (list_empty(&drv_data->queue) || drv_data->run == QUEUE_STOPPED) {
-               drv_data->busy = 0;
-               spin_unlock_irqrestore(&drv_data->lock, flags);
-               return;
-       }
-
-       /* Make sure we are not already running a message */
-       if (drv_data->cur_msg) {
-               spin_unlock_irqrestore(&drv_data->lock, flags);
-               return;
-       }
-
-       /* Extract head of queue */
-       drv_data->cur_msg = list_entry(drv_data->queue.next,
-                                       struct spi_message, queue);
-       list_del_init(&drv_data->cur_msg->queue);
+       struct driver_data *drv_data = spi_master_get_devdata(master);
 
+       drv_data->cur_msg = msg;
        /* Initial message state*/
        drv_data->cur_msg->state = START_STATE;
        drv_data->cur_transfer = list_entry(drv_data->cur_msg->transfers.next,
@@ -1213,34 +1168,6 @@ static void pump_messages(struct work_struct *work)
 
        /* Mark as busy and launch transfers */
        tasklet_schedule(&drv_data->pump_transfers);
-
-       drv_data->busy = 1;
-       spin_unlock_irqrestore(&drv_data->lock, flags);
-}
-
-static int transfer(struct spi_device *spi, struct spi_message *msg)
-{
-       struct driver_data *drv_data = spi_master_get_devdata(spi->master);
-       unsigned long flags;
-
-       spin_lock_irqsave(&drv_data->lock, flags);
-
-       if (drv_data->run == QUEUE_STOPPED) {
-               spin_unlock_irqrestore(&drv_data->lock, flags);
-               return -ESHUTDOWN;
-       }
-
-       msg->actual_length = 0;
-       msg->status = -EINPROGRESS;
-       msg->state = START_STATE;
-
-       list_add_tail(&msg->queue, &drv_data->queue);
-
-       if (drv_data->run == QUEUE_RUNNING && !drv_data->busy)
-               queue_work(drv_data->workqueue, &drv_data->pump_messages);
-
-       spin_unlock_irqrestore(&drv_data->lock, flags);
-
        return 0;
 }
 
@@ -1438,94 +1365,6 @@ static void cleanup(struct spi_device *spi)
        kfree(chip);
 }
 
-static int init_queue(struct driver_data *drv_data)
-{
-       INIT_LIST_HEAD(&drv_data->queue);
-       spin_lock_init(&drv_data->lock);
-
-       drv_data->run = QUEUE_STOPPED;
-       drv_data->busy = 0;
-
-       tasklet_init(&drv_data->pump_transfers,
-                       pump_transfers, (unsigned long)drv_data);
-
-       INIT_WORK(&drv_data->pump_messages, pump_messages);
-       drv_data->workqueue = create_singlethread_workqueue(
-                               dev_name(drv_data->master->dev.parent));
-       if (drv_data->workqueue == NULL)
-               return -EBUSY;
-
-       return 0;
-}
-
-static int start_queue(struct driver_data *drv_data)
-{
-       unsigned long flags;
-
-       spin_lock_irqsave(&drv_data->lock, flags);
-
-       if (drv_data->run == QUEUE_RUNNING || drv_data->busy) {
-               spin_unlock_irqrestore(&drv_data->lock, flags);
-               return -EBUSY;
-       }
-
-       drv_data->run = QUEUE_RUNNING;
-       drv_data->cur_msg = NULL;
-       drv_data->cur_transfer = NULL;
-       drv_data->cur_chip = NULL;
-       spin_unlock_irqrestore(&drv_data->lock, flags);
-
-       queue_work(drv_data->workqueue, &drv_data->pump_messages);
-
-       return 0;
-}
-
-static int stop_queue(struct driver_data *drv_data)
-{
-       unsigned long flags;
-       unsigned limit = 500;
-       int status = 0;
-
-       spin_lock_irqsave(&drv_data->lock, flags);
-
-       /* This is a bit lame, but is optimized for the common execution path.
-        * A wait_queue on the drv_data->busy could be used, but then the common
-        * execution path (pump_messages) would be required to call wake_up or
-        * friends on every SPI message. Do this instead */
-       drv_data->run = QUEUE_STOPPED;
-       while ((!list_empty(&drv_data->queue) || drv_data->busy) && limit--) {
-               spin_unlock_irqrestore(&drv_data->lock, flags);
-               msleep(10);
-               spin_lock_irqsave(&drv_data->lock, flags);
-       }
-
-       if (!list_empty(&drv_data->queue) || drv_data->busy)
-               status = -EBUSY;
-
-       spin_unlock_irqrestore(&drv_data->lock, flags);
-
-       return status;
-}
-
-static int destroy_queue(struct driver_data *drv_data)
-{
-       int status;
-
-       status = stop_queue(drv_data);
-       /* we are unloading the module or failing to load (only two calls
-        * to this routine), and neither call can handle a return value.
-        * However, destroy_workqueue calls flush_workqueue, and that will
-        * block until all work is done.  If the reason that stop_queue
-        * timed out is that the work will never finish, then it does no
-        * good to call destroy_workqueue, so return anyway. */
-       if (status != 0)
-               return status;
-
-       destroy_workqueue(drv_data->workqueue);
-
-       return 0;
-}
-
 static int pxa2xx_spi_probe(struct platform_device *pdev)
 {
        struct device *dev = &pdev->dev;
@@ -1573,7 +1412,7 @@ static int pxa2xx_spi_probe(struct platform_device *pdev)
        master->dma_alignment = DMA_ALIGNMENT;
        master->cleanup = cleanup;
        master->setup = setup;
-       master->transfer = transfer;
+       master->transfer_one_message = pxa2xx_spi_transfer_one_message;
 
        drv_data->ssp_type = ssp->type;
        drv_data->null_dma_buf = (u32 *)PTR_ALIGN(&drv_data[1], DMA_ALIGNMENT);
@@ -1646,31 +1485,19 @@ static int pxa2xx_spi_probe(struct platform_device *pdev)
                write_SSTO(0, drv_data->ioaddr);
        write_SSPSP(0, drv_data->ioaddr);
 
-       /* Initial and start queue */
-       status = init_queue(drv_data);
-       if (status != 0) {
-               dev_err(&pdev->dev, "problem initializing queue\n");
-               goto out_error_clock_enabled;
-       }
-       status = start_queue(drv_data);
-       if (status != 0) {
-               dev_err(&pdev->dev, "problem starting queue\n");
-               goto out_error_clock_enabled;
-       }
+       tasklet_init(&drv_data->pump_transfers, pump_transfers,
+                    (unsigned long)drv_data);
 
        /* Register with the SPI framework */
        platform_set_drvdata(pdev, drv_data);
        status = spi_register_master(master);
        if (status != 0) {
                dev_err(&pdev->dev, "problem registering spi master\n");
-               goto out_error_queue_alloc;
+               goto out_error_clock_enabled;
        }
 
        return status;
 
-out_error_queue_alloc:
-       destroy_queue(drv_data);
-
 out_error_clock_enabled:
        clk_disable(ssp->clk);
 
@@ -1693,26 +1520,11 @@ static int pxa2xx_spi_remove(struct platform_device *pdev)
 {
        struct driver_data *drv_data = platform_get_drvdata(pdev);
        struct ssp_device *ssp;
-       int status = 0;
 
        if (!drv_data)
                return 0;
        ssp = drv_data->ssp;
 
-       /* Remove the queue */
-       status = destroy_queue(drv_data);
-       if (status != 0)
-               /* the kernel does not check the return status of this
-                * this routine (mod->exit, within the kernel).  Therefore
-                * nothing is gained by returning from here, the module is
-                * going away regardless, and we should not leave any more
-                * resources allocated than necessary.  We cannot free the
-                * message memory in drv_data->queue, but we can release the
-                * resources below.  I think the kernel should honor -EBUSY
-                * returns but... */
-               dev_err(&pdev->dev, "pxa2xx_spi_remove: workqueue will not "
-                       "complete, message memory not freed\n");
-
        /* Disable the SSP at the peripheral and SOC level */
        write_SSCR0(0, drv_data->ioaddr);
        clk_disable(ssp->clk);
@@ -1755,7 +1567,7 @@ static int pxa2xx_spi_suspend(struct device *dev)
        struct ssp_device *ssp = drv_data->ssp;
        int status = 0;
 
-       status = stop_queue(drv_data);
+       status = spi_master_suspend(drv_data->master);
        if (status != 0)
                return status;
        write_SSCR0(0, drv_data->ioaddr);
@@ -1781,7 +1593,7 @@ static int pxa2xx_spi_resume(struct device *dev)
        clk_enable(ssp->clk);
 
        /* Start the queue running */
-       status = start_queue(drv_data);
+       status = spi_master_resume(drv_data->master);
        if (status != 0) {
                dev_err(dev, "problem starting queue (%d)\n", status);
                return status;