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

1  2 
drivers/spi/spi-s3c64xx.c

index dcf7e1006426d2fef19c8c62a38dac5b5a8b55b2,b0b843b321bb582514522cad7b1847780abd75b3..b899af6640a21fecfe070494e25c515b3bfdbb73
  #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 +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 +783,8 @@@ static void s3c64xx_spi_work(struct wor
        while (!acquire_dma(sdd))
                msleep(10);
  
+       pm_runtime_get_sync(&sdd->pdev->dev);
        spin_lock_irqsave(&sdd->lock, flags);
  
        while (!list_empty(&sdd->queue)
        /* 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 +897,8 @@@ static int s3c64xx_spi_setup(struct 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;
                        err = -EINVAL;
        }
  
+       pm_runtime_put(&sdd->pdev->dev);
  setup_exit:
  
        /* setup() returns with device de-selected */
        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 +1008,7 @@@ static int __init s3c64xx_spi_probe(str
        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) {
        }
  
        sci = pdev->dev.platform_data;
 -      if (!sci->src_clk_name) {
 -              dev_err(&pdev->dev,
 -                      "Board init must call s3c64xx_spi_set_info()\n");
 -              return -EINVAL;
 -      }
  
        /* Check for availability of necessary resource */
  
                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) {
                goto err4;
        }
  
 -      sdd->src_clk = clk_get(&pdev->dev, sci->src_clk_name);
 +      sprintf(clk_name, "spi_busclk%d", sci->src_clk_nr);
 +      sdd->src_clk = clk_get(&pdev->dev, clk_name);
        if (IS_ERR(sdd->src_clk)) {
                dev_err(&pdev->dev,
 -                      "Unable to acquire clock '%s'\n", sci->src_clk_name);
 +                      "Unable to acquire clock '%s'\n", clk_name);
                ret = PTR_ERR(sdd->src_clk);
                goto err5;
        }
  
        if (clk_enable(sdd->src_clk)) {
 -              dev_err(&pdev->dev, "Couldn't enable clock '%s'\n",
 -                                                      sci->src_clk_name);
 +              dev_err(&pdev->dev, "Couldn't enable clock '%s'\n", clk_name);
                ret = -EBUSY;
                goto err6;
        }
        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 "
                                        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 -1208,8 +1203,8 @@@ static int s3c64xx_spi_remove(struct pl
        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);
  
        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);
  }
  
  #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;
  
        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;
  
        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");