Merge branch 'spi/s3c64xx' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie...
authorGrant Likely <grant.likely@secretlab.ca>
Mon, 30 Jan 2012 15:32:37 +0000 (08:32 -0700)
committerGrant Likely <grant.likely@secretlab.ca>
Mon, 30 Jan 2012 15:32:37 +0000 (08:32 -0700)
Conflicts:
drivers/spi/spi-s3c64xx.c

drivers/spi/spi-s3c64xx.c

index dcf7e1006426d2fef19c8c62a38dac5b5a8b55b2..b899af6640a21fecfe070494e25c515b3bfdbb73 100644 (file)
 #include <linux/init.h>
 #include <linux/module.h>
 #include <linux/workqueue.h>
+#include <linux/interrupt.h>
 #include <linux/delay.h>
 #include <linux/clk.h>
 #include <linux/dma-mapping.h>
 #include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
 #include <linux/spi/spi.h>
 
 #include <mach/dma.h>
@@ -153,6 +155,7 @@ struct s3c64xx_spi_dma_data {
  * @tx_dmach: Controller's DMA channel for Tx.
  * @sfr_start: BUS address of SPI controller regs.
  * @regs: Pointer to ioremap'ed controller registers.
+ * @irq: interrupt
  * @xfer_completion: To indicate completion of xfer task.
  * @cur_mode: Stores the active configuration of the controller.
  * @cur_bpw: Stores the active bits per word settings.
@@ -780,6 +783,8 @@ static void s3c64xx_spi_work(struct work_struct *work)
        while (!acquire_dma(sdd))
                msleep(10);
 
+       pm_runtime_get_sync(&sdd->pdev->dev);
+
        spin_lock_irqsave(&sdd->lock, flags);
 
        while (!list_empty(&sdd->queue)
@@ -808,6 +813,8 @@ static void s3c64xx_spi_work(struct work_struct *work)
        /* Free DMA channels */
        sdd->ops->release(sdd->rx_dma.ch, &s3c64xx_spi_dma_client);
        sdd->ops->release(sdd->tx_dma.ch, &s3c64xx_spi_dma_client);
+
+       pm_runtime_put(&sdd->pdev->dev);
 }
 
 static int s3c64xx_spi_transfer(struct spi_device *spi,
@@ -890,6 +897,8 @@ static int s3c64xx_spi_setup(struct spi_device *spi)
                goto setup_exit;
        }
 
+       pm_runtime_get_sync(&sdd->pdev->dev);
+
        /* Check if we can provide the requested rate */
        if (!sci->clk_from_cmu) {
                u32 psr, speed;
@@ -922,6 +931,8 @@ static int s3c64xx_spi_setup(struct spi_device *spi)
                        err = -EINVAL;
        }
 
+       pm_runtime_put(&sdd->pdev->dev);
+
 setup_exit:
 
        /* setup() returns with device de-selected */
@@ -930,6 +941,33 @@ setup_exit:
        return err;
 }
 
+static irqreturn_t s3c64xx_spi_irq(int irq, void *data)
+{
+       struct s3c64xx_spi_driver_data *sdd = data;
+       struct spi_master *spi = sdd->master;
+       unsigned int val;
+
+       val = readl(sdd->regs + S3C64XX_SPI_PENDING_CLR);
+
+       val &= S3C64XX_SPI_PND_RX_OVERRUN_CLR |
+               S3C64XX_SPI_PND_RX_UNDERRUN_CLR |
+               S3C64XX_SPI_PND_TX_OVERRUN_CLR |
+               S3C64XX_SPI_PND_TX_UNDERRUN_CLR;
+
+       writel(val, sdd->regs + S3C64XX_SPI_PENDING_CLR);
+
+       if (val & S3C64XX_SPI_PND_RX_OVERRUN_CLR)
+               dev_err(&spi->dev, "RX overrun\n");
+       if (val & S3C64XX_SPI_PND_RX_UNDERRUN_CLR)
+               dev_err(&spi->dev, "RX underrun\n");
+       if (val & S3C64XX_SPI_PND_TX_OVERRUN_CLR)
+               dev_err(&spi->dev, "TX overrun\n");
+       if (val & S3C64XX_SPI_PND_TX_UNDERRUN_CLR)
+               dev_err(&spi->dev, "TX underrun\n");
+
+       return IRQ_HANDLED;
+}
+
 static void s3c64xx_spi_hwinit(struct s3c64xx_spi_driver_data *sdd, int channel)
 {
        struct s3c64xx_spi_info *sci = sdd->cntrlr_info;
@@ -970,7 +1008,7 @@ static int __init s3c64xx_spi_probe(struct platform_device *pdev)
        struct s3c64xx_spi_driver_data *sdd;
        struct s3c64xx_spi_info *sci;
        struct spi_master *master;
-       int ret;
+       int ret, irq;
        char clk_name[16];
 
        if (pdev->id < 0) {
@@ -1006,6 +1044,12 @@ static int __init s3c64xx_spi_probe(struct platform_device *pdev)
                return -ENXIO;
        }
 
+       irq = platform_get_irq(pdev, 0);
+       if (irq < 0) {
+               dev_warn(&pdev->dev, "Failed to get IRQ: %d\n", irq);
+               return irq;
+       }
+
        master = spi_alloc_master(&pdev->dev,
                                sizeof(struct s3c64xx_spi_driver_data));
        if (master == NULL) {
@@ -1100,10 +1144,21 @@ static int __init s3c64xx_spi_probe(struct platform_device *pdev)
        INIT_WORK(&sdd->work, s3c64xx_spi_work);
        INIT_LIST_HEAD(&sdd->queue);
 
+       ret = request_irq(irq, s3c64xx_spi_irq, 0, "spi-s3c64xx", sdd);
+       if (ret != 0) {
+               dev_err(&pdev->dev, "Failed to request IRQ %d: %d\n",
+                       irq, ret);
+               goto err8;
+       }
+
+       writel(S3C64XX_SPI_INT_RX_OVERRUN_EN | S3C64XX_SPI_INT_RX_UNDERRUN_EN |
+              S3C64XX_SPI_INT_TX_OVERRUN_EN | S3C64XX_SPI_INT_TX_UNDERRUN_EN,
+              sdd->regs + S3C64XX_SPI_INT_EN);
+
        if (spi_register_master(master)) {
                dev_err(&pdev->dev, "cannot register SPI master\n");
                ret = -EBUSY;
-               goto err8;
+               goto err9;
        }
 
        dev_dbg(&pdev->dev, "Samsung SoC SPI Driver loaded for Bus SPI-%d "
@@ -1113,8 +1168,12 @@ static int __init s3c64xx_spi_probe(struct platform_device *pdev)
                                        mem_res->end, mem_res->start,
                                        sdd->rx_dma.dmach, sdd->tx_dma.dmach);
 
+       pm_runtime_enable(&pdev->dev);
+
        return 0;
 
+err9:
+       free_irq(irq, sdd);
 err8:
        destroy_workqueue(sdd->workqueue);
 err7:
@@ -1144,6 +1203,8 @@ static int s3c64xx_spi_remove(struct platform_device *pdev)
        struct resource *mem_res;
        unsigned long flags;
 
+       pm_runtime_disable(&pdev->dev);
+
        spin_lock_irqsave(&sdd->lock, flags);
        sdd->state |= SUSPND;
        spin_unlock_irqrestore(&sdd->lock, flags);
@@ -1153,6 +1214,10 @@ static int s3c64xx_spi_remove(struct platform_device *pdev)
 
        spi_unregister_master(master);
 
+       writel(0, sdd->regs + S3C64XX_SPI_INT_EN);
+
+       free_irq(platform_get_irq(pdev, 0), sdd);
+
        destroy_workqueue(sdd->workqueue);
 
        clk_disable(sdd->src_clk);
@@ -1174,9 +1239,9 @@ static int s3c64xx_spi_remove(struct platform_device *pdev)
 }
 
 #ifdef CONFIG_PM
-static int s3c64xx_spi_suspend(struct platform_device *pdev, pm_message_t state)
+static int s3c64xx_spi_suspend(struct device *dev)
 {
-       struct spi_master *master = spi_master_get(platform_get_drvdata(pdev));
+       struct spi_master *master = spi_master_get(dev_get_drvdata(dev));
        struct s3c64xx_spi_driver_data *sdd = spi_master_get_devdata(master);
        unsigned long flags;
 
@@ -1196,9 +1261,10 @@ static int s3c64xx_spi_suspend(struct platform_device *pdev, pm_message_t state)
        return 0;
 }
 
-static int s3c64xx_spi_resume(struct platform_device *pdev)
+static int s3c64xx_spi_resume(struct device *dev)
 {
-       struct spi_master *master = spi_master_get(platform_get_drvdata(pdev));
+       struct platform_device *pdev = to_platform_device(dev);
+       struct spi_master *master = spi_master_get(dev_get_drvdata(dev));
        struct s3c64xx_spi_driver_data *sdd = spi_master_get_devdata(master);
        struct s3c64xx_spi_info *sci = sdd->cntrlr_info;
        unsigned long flags;
@@ -1217,19 +1283,45 @@ static int s3c64xx_spi_resume(struct platform_device *pdev)
 
        return 0;
 }
-#else
-#define s3c64xx_spi_suspend    NULL
-#define s3c64xx_spi_resume     NULL
 #endif /* CONFIG_PM */
 
+#ifdef CONFIG_PM_RUNTIME
+static int s3c64xx_spi_runtime_suspend(struct device *dev)
+{
+       struct spi_master *master = spi_master_get(dev_get_drvdata(dev));
+       struct s3c64xx_spi_driver_data *sdd = spi_master_get_devdata(master);
+
+       clk_disable(sdd->clk);
+       clk_disable(sdd->src_clk);
+
+       return 0;
+}
+
+static int s3c64xx_spi_runtime_resume(struct device *dev)
+{
+       struct spi_master *master = spi_master_get(dev_get_drvdata(dev));
+       struct s3c64xx_spi_driver_data *sdd = spi_master_get_devdata(master);
+
+       clk_enable(sdd->src_clk);
+       clk_enable(sdd->clk);
+
+       return 0;
+}
+#endif /* CONFIG_PM_RUNTIME */
+
+static const struct dev_pm_ops s3c64xx_spi_pm = {
+       SET_SYSTEM_SLEEP_PM_OPS(s3c64xx_spi_suspend, s3c64xx_spi_resume)
+       SET_RUNTIME_PM_OPS(s3c64xx_spi_runtime_suspend,
+                          s3c64xx_spi_runtime_resume, NULL)
+};
+
 static struct platform_driver s3c64xx_spi_driver = {
        .driver = {
                .name   = "s3c64xx-spi",
                .owner = THIS_MODULE,
+               .pm = &s3c64xx_spi_pm,
        },
        .remove = s3c64xx_spi_remove,
-       .suspend = s3c64xx_spi_suspend,
-       .resume = s3c64xx_spi_resume,
 };
 MODULE_ALIAS("platform:s3c64xx-spi");