]> Pileus Git - ~andy/linux/blobdiff - drivers/spi/spi-coldfire-qspi.c
Merge tag 'scsi-misc' of git://git.kernel.org/pub/scm/linux/kernel/git/jejb/scsi
[~andy/linux] / drivers / spi / spi-coldfire-qspi.c
index 6eee64a5d240d882647d15a2e80f2e547dda91f4..b2d4b9e4e0105d53dff9a3c747bd535b6732a6b6 100644 (file)
 #include <linux/errno.h>
 #include <linux/platform_device.h>
 #include <linux/sched.h>
-#include <linux/workqueue.h>
 #include <linux/delay.h>
 #include <linux/io.h>
 #include <linux/clk.h>
 #include <linux/err.h>
 #include <linux/spi/spi.h>
+#include <linux/pm_runtime.h>
 
 #include <asm/coldfire.h>
 #include <asm/mcfsim.h>
@@ -78,10 +78,7 @@ struct mcfqspi {
 
        wait_queue_head_t waitq;
 
-       struct work_struct work;
-       struct workqueue_struct *workq;
-       spinlock_t lock;
-       struct list_head msgq;
+       struct device *dev;
 };
 
 static void mcfqspi_wr_qmr(struct mcfqspi *mcfqspi, u16 val)
@@ -303,120 +300,80 @@ static void mcfqspi_transfer_msg16(struct mcfqspi *mcfqspi, unsigned count,
        }
 }
 
-static void mcfqspi_work(struct work_struct *work)
+static int mcfqspi_transfer_one_message(struct spi_master *master,
+                                        struct spi_message *msg)
 {
-       struct mcfqspi *mcfqspi = container_of(work, struct mcfqspi, work);
-       unsigned long flags;
-
-       spin_lock_irqsave(&mcfqspi->lock, flags);
-       while (!list_empty(&mcfqspi->msgq)) {
-               struct spi_message *msg;
-               struct spi_device *spi;
-               struct spi_transfer *xfer;
-               int status = 0;
-
-               msg = container_of(mcfqspi->msgq.next, struct spi_message,
-                                  queue);
-
-               list_del_init(&msg->queue);
-               spin_unlock_irqrestore(&mcfqspi->lock, flags);
-
-               spi = msg->spi;
-
-               list_for_each_entry(xfer, &msg->transfers, transfer_list) {
-                       bool cs_high = spi->mode & SPI_CS_HIGH;
-                       u16 qmr = MCFQSPI_QMR_MSTR;
-
-                       if (xfer->bits_per_word)
-                               qmr |= xfer->bits_per_word << 10;
-                       else
-                               qmr |= spi->bits_per_word << 10;
-                       if (spi->mode & SPI_CPHA)
-                               qmr |= MCFQSPI_QMR_CPHA;
-                       if (spi->mode & SPI_CPOL)
-                               qmr |= MCFQSPI_QMR_CPOL;
-                       if (xfer->speed_hz)
-                               qmr |= mcfqspi_qmr_baud(xfer->speed_hz);
-                       else
-                               qmr |= mcfqspi_qmr_baud(spi->max_speed_hz);
-                       mcfqspi_wr_qmr(mcfqspi, qmr);
-
-                       mcfqspi_cs_select(mcfqspi, spi->chip_select, cs_high);
-
-                       mcfqspi_wr_qir(mcfqspi, MCFQSPI_QIR_SPIFE);
-                       if ((xfer->bits_per_word ? xfer->bits_per_word :
-                                               spi->bits_per_word) == 8)
-                               mcfqspi_transfer_msg8(mcfqspi, xfer->len,
-                                                     xfer->tx_buf,
-                                                     xfer->rx_buf);
-                       else
-                               mcfqspi_transfer_msg16(mcfqspi, xfer->len / 2,
-                                                      xfer->tx_buf,
-                                                      xfer->rx_buf);
-                       mcfqspi_wr_qir(mcfqspi, 0);
-
-                       if (xfer->delay_usecs)
-                               udelay(xfer->delay_usecs);
-                       if (xfer->cs_change) {
-                               if (!list_is_last(&xfer->transfer_list,
-                                                 &msg->transfers))
-                                       mcfqspi_cs_deselect(mcfqspi,
-                                                           spi->chip_select,
-                                                           cs_high);
-                       } else {
-                               if (list_is_last(&xfer->transfer_list,
-                                                &msg->transfers))
-                                       mcfqspi_cs_deselect(mcfqspi,
-                                                           spi->chip_select,
-                                                           cs_high);
-                       }
-                       msg->actual_length += xfer->len;
+       struct mcfqspi *mcfqspi = spi_master_get_devdata(master);
+       struct spi_device *spi = msg->spi;
+       struct spi_transfer *t;
+       int status = 0;
+
+       list_for_each_entry(t, &msg->transfers, transfer_list) {
+               bool cs_high = spi->mode & SPI_CS_HIGH;
+               u16 qmr = MCFQSPI_QMR_MSTR;
+
+               if (t->bits_per_word)
+                       qmr |= t->bits_per_word << 10;
+               else
+                       qmr |= spi->bits_per_word << 10;
+               if (spi->mode & SPI_CPHA)
+                       qmr |= MCFQSPI_QMR_CPHA;
+               if (spi->mode & SPI_CPOL)
+                       qmr |= MCFQSPI_QMR_CPOL;
+               if (t->speed_hz)
+                       qmr |= mcfqspi_qmr_baud(t->speed_hz);
+               else
+                       qmr |= mcfqspi_qmr_baud(spi->max_speed_hz);
+               mcfqspi_wr_qmr(mcfqspi, qmr);
+
+               mcfqspi_cs_select(mcfqspi, spi->chip_select, cs_high);
+
+               mcfqspi_wr_qir(mcfqspi, MCFQSPI_QIR_SPIFE);
+               if ((t->bits_per_word ? t->bits_per_word :
+                                       spi->bits_per_word) == 8)
+                       mcfqspi_transfer_msg8(mcfqspi, t->len, t->tx_buf,
+                                       t->rx_buf);
+               else
+                       mcfqspi_transfer_msg16(mcfqspi, t->len / 2, t->tx_buf,
+                                       t->rx_buf);
+               mcfqspi_wr_qir(mcfqspi, 0);
+
+               if (t->delay_usecs)
+                       udelay(t->delay_usecs);
+               if (t->cs_change) {
+                       if (!list_is_last(&t->transfer_list, &msg->transfers))
+                               mcfqspi_cs_deselect(mcfqspi, spi->chip_select,
+                                               cs_high);
+               } else {
+                       if (list_is_last(&t->transfer_list, &msg->transfers))
+                               mcfqspi_cs_deselect(mcfqspi, spi->chip_select,
+                                               cs_high);
                }
-               msg->status = status;
-               msg->complete(msg->context);
-
-               spin_lock_irqsave(&mcfqspi->lock, flags);
+               msg->actual_length += t->len;
        }
-       spin_unlock_irqrestore(&mcfqspi->lock, flags);
+       msg->status = status;
+       spi_finalize_current_message(master);
+
+       return status;
+
 }
 
-static int mcfqspi_transfer(struct spi_device *spi, struct spi_message *msg)
+static int mcfqspi_prepare_transfer_hw(struct spi_master *master)
 {
-       struct mcfqspi *mcfqspi;
-       struct spi_transfer *xfer;
-       unsigned long flags;
-
-       mcfqspi = spi_master_get_devdata(spi->master);
-
-       list_for_each_entry(xfer, &msg->transfers, transfer_list) {
-               if (xfer->bits_per_word && ((xfer->bits_per_word < 8)
-                                       || (xfer->bits_per_word > 16))) {
-                       dev_dbg(&spi->dev,
-                               "%d bits per word is not supported\n",
-                               xfer->bits_per_word);
-                       goto fail;
-               }
-               if (xfer->speed_hz) {
-                       u32 real_speed = MCFQSPI_BUSCLK /
-                               mcfqspi_qmr_baud(xfer->speed_hz);
-                       if (real_speed != xfer->speed_hz)
-                               dev_dbg(&spi->dev,
-                                       "using speed %d instead of %d\n",
-                                       real_speed, xfer->speed_hz);
-               }
-       }
-       msg->status = -EINPROGRESS;
-       msg->actual_length = 0;
+       struct mcfqspi *mcfqspi = spi_master_get_devdata(master);
 
-       spin_lock_irqsave(&mcfqspi->lock, flags);
-       list_add_tail(&msg->queue, &mcfqspi->msgq);
-       queue_work(mcfqspi->workq, &mcfqspi->work);
-       spin_unlock_irqrestore(&mcfqspi->lock, flags);
+       pm_runtime_get_sync(mcfqspi->dev);
+
+       return 0;
+}
+
+static int mcfqspi_unprepare_transfer_hw(struct spi_master *master)
+{
+       struct mcfqspi *mcfqspi = spi_master_get_devdata(master);
+
+       pm_runtime_put_sync(mcfqspi->dev);
 
        return 0;
-fail:
-       msg->status = -EINVAL;
-       return -EINVAL;
 }
 
 static int mcfqspi_setup(struct spi_device *spi)
@@ -502,21 +459,10 @@ static int __devinit mcfqspi_probe(struct platform_device *pdev)
        }
        clk_enable(mcfqspi->clk);
 
-       mcfqspi->workq = create_singlethread_workqueue(dev_name(master->dev.parent));
-       if (!mcfqspi->workq) {
-               dev_dbg(&pdev->dev, "create_workqueue failed\n");
-               status = -ENOMEM;
-               goto fail4;
-       }
-       INIT_WORK(&mcfqspi->work, mcfqspi_work);
-       spin_lock_init(&mcfqspi->lock);
-       INIT_LIST_HEAD(&mcfqspi->msgq);
-       init_waitqueue_head(&mcfqspi->waitq);
-
        pdata = pdev->dev.platform_data;
        if (!pdata) {
                dev_dbg(&pdev->dev, "platform data is missing\n");
-               goto fail5;
+               goto fail4;
        }
        master->bus_num = pdata->bus_num;
        master->num_chipselect = pdata->num_chipselect;
@@ -525,28 +471,33 @@ static int __devinit mcfqspi_probe(struct platform_device *pdev)
        status = mcfqspi_cs_setup(mcfqspi);
        if (status) {
                dev_dbg(&pdev->dev, "error initializing cs_control\n");
-               goto fail5;
+               goto fail4;
        }
 
+       init_waitqueue_head(&mcfqspi->waitq);
+       mcfqspi->dev = &pdev->dev;
+
        master->mode_bits = SPI_CS_HIGH | SPI_CPOL | SPI_CPHA;
        master->setup = mcfqspi_setup;
-       master->transfer = mcfqspi_transfer;
+       master->transfer_one_message = mcfqspi_transfer_one_message;
+       master->prepare_transfer_hardware = mcfqspi_prepare_transfer_hw;
+       master->unprepare_transfer_hardware = mcfqspi_unprepare_transfer_hw;
 
        platform_set_drvdata(pdev, master);
 
        status = spi_register_master(master);
        if (status) {
                dev_dbg(&pdev->dev, "spi_register_master failed\n");
-               goto fail6;
+               goto fail5;
        }
+       pm_runtime_enable(mcfqspi->dev);
+
        dev_info(&pdev->dev, "Coldfire QSPI bus driver\n");
 
        return 0;
 
-fail6:
-       mcfqspi_cs_teardown(mcfqspi);
 fail5:
-       destroy_workqueue(mcfqspi->workq);
+       mcfqspi_cs_teardown(mcfqspi);
 fail4:
        clk_disable(mcfqspi->clk);
        clk_put(mcfqspi->clk);
@@ -570,12 +521,12 @@ static int __devexit mcfqspi_remove(struct platform_device *pdev)
        struct mcfqspi *mcfqspi = spi_master_get_devdata(master);
        struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 
+       pm_runtime_disable(mcfqspi->dev);
        /* disable the hardware (set the baud rate to 0) */
        mcfqspi_wr_qmr(mcfqspi, MCFQSPI_QMR_MSTR);
 
        platform_set_drvdata(pdev, NULL);
        mcfqspi_cs_teardown(mcfqspi);
-       destroy_workqueue(mcfqspi->workq);
        clk_disable(mcfqspi->clk);
        clk_put(mcfqspi->clk);
        free_irq(mcfqspi->irq, mcfqspi);
@@ -587,11 +538,13 @@ static int __devexit mcfqspi_remove(struct platform_device *pdev)
        return 0;
 }
 
-#ifdef CONFIG_PM
-
+#ifdef CONFIG_PM_SLEEP
 static int mcfqspi_suspend(struct device *dev)
 {
-       struct mcfqspi *mcfqspi = platform_get_drvdata(to_platform_device(dev));
+       struct spi_master *master = spi_master_get(dev_get_drvdata(dev));
+       struct mcfqspi *mcfqspi = spi_master_get_devdata(master);
+
+       spi_master_suspend(master);
 
        clk_disable(mcfqspi->clk);
 
@@ -600,27 +553,47 @@ static int mcfqspi_suspend(struct device *dev)
 
 static int mcfqspi_resume(struct device *dev)
 {
-       struct mcfqspi *mcfqspi = platform_get_drvdata(to_platform_device(dev));
+       struct spi_master *master = spi_master_get(dev_get_drvdata(dev));
+       struct mcfqspi *mcfqspi = spi_master_get_devdata(master);
+
+       spi_master_resume(master);
 
        clk_enable(mcfqspi->clk);
 
        return 0;
 }
+#endif
 
-static struct dev_pm_ops mcfqspi_dev_pm_ops = {
-       .suspend        = mcfqspi_suspend,
-       .resume         = mcfqspi_resume,
-};
+#ifdef CONFIG_PM_RUNTIME
+static int mcfqspi_runtime_suspend(struct device *dev)
+{
+       struct mcfqspi *mcfqspi = platform_get_drvdata(to_platform_device(dev));
 
-#define        MCFQSPI_DEV_PM_OPS      (&mcfqspi_dev_pm_ops)
-#else
-#define        MCFQSPI_DEV_PM_OPS      NULL
+       clk_disable(mcfqspi->clk);
+
+       return 0;
+}
+
+static int mcfqspi_runtime_resume(struct device *dev)
+{
+       struct mcfqspi *mcfqspi = platform_get_drvdata(to_platform_device(dev));
+
+       clk_enable(mcfqspi->clk);
+
+       return 0;
+}
 #endif
 
+static const struct dev_pm_ops mcfqspi_pm = {
+       SET_SYSTEM_SLEEP_PM_OPS(mcfqspi_suspend, mcfqspi_resume)
+       SET_RUNTIME_PM_OPS(mcfqspi_runtime_suspend, mcfqspi_runtime_resume,
+                       NULL)
+};
+
 static struct platform_driver mcfqspi_driver = {
        .driver.name    = DRIVER_NAME,
        .driver.owner   = THIS_MODULE,
-       .driver.pm      = MCFQSPI_DEV_PM_OPS,
+       .driver.pm      = &mcfqspi_pm,
        .probe          = mcfqspi_probe,
        .remove         = __devexit_p(mcfqspi_remove),
 };