]> asedeno.scripts.mit.edu Git - linux.git/blobdiff - drivers/spi/spi-s3c64xx.c
Merge remote-tracking branches 'spi/topic/spidev', 'spi/topic/sunxi', 'spi/topic...
[linux.git] / drivers / spi / spi-s3c64xx.c
index 9f0119f64f93388706b3020dff8608aa6be1bbad..3c09e94cf827f63b20f9e53d4523cbdaeba7718d 100644 (file)
@@ -156,12 +156,14 @@ struct s3c64xx_spi_port_config {
        int     quirks;
        bool    high_speed;
        bool    clk_from_cmu;
+       bool    clk_ioclk;
 };
 
 /**
  * struct s3c64xx_spi_driver_data - Runtime info holder for SPI driver.
  * @clk: Pointer to the spi clock.
  * @src_clk: Pointer to the clock used to generate SPI signals.
+ * @ioclk: Pointer to the i/o clock between master and slave
  * @master: Pointer to the SPI Protocol master.
  * @cntrlr_info: Platform specific data for the controller this driver manages.
  * @tgl_spi: Pointer to the last CS left untoggled by the cs_change hint.
@@ -181,6 +183,7 @@ struct s3c64xx_spi_driver_data {
        void __iomem                    *regs;
        struct clk                      *clk;
        struct clk                      *src_clk;
+       struct clk                      *ioclk;
        struct platform_device          *pdev;
        struct spi_master               *master;
        struct s3c64xx_spi_info  *cntrlr_info;
@@ -596,9 +599,7 @@ static void s3c64xx_spi_config(struct s3c64xx_spi_driver_data *sdd)
        u32 val;
 
        /* Disable Clock */
-       if (sdd->port_conf->clk_from_cmu) {
-               clk_disable_unprepare(sdd->src_clk);
-       } else {
+       if (!sdd->port_conf->clk_from_cmu) {
                val = readl(regs + S3C64XX_SPI_CLK_CFG);
                val &= ~S3C64XX_SPI_ENCLK_ENABLE;
                writel(val, regs + S3C64XX_SPI_CLK_CFG);
@@ -641,11 +642,8 @@ static void s3c64xx_spi_config(struct s3c64xx_spi_driver_data *sdd)
        writel(val, regs + S3C64XX_SPI_MODE_CFG);
 
        if (sdd->port_conf->clk_from_cmu) {
-               /* Configure Clock */
-               /* There is half-multiplier before the SPI */
+               /* The src_clk clock is divided internally by 2 */
                clk_set_rate(sdd->src_clk, sdd->cur_speed * 2);
-               /* Enable Clock */
-               clk_prepare_enable(sdd->src_clk);
        } else {
                /* Configure Clock */
                val = readl(regs + S3C64XX_SPI_CLK_CFG);
@@ -1084,7 +1082,7 @@ static int s3c64xx_spi_probe(struct platform_device *pdev)
                if (ret < 0) {
                        dev_err(&pdev->dev, "failed to get alias id, errno %d\n",
                                ret);
-                       goto err0;
+                       goto err_deref_master;
                }
                sdd->port_id = ret;
        } else {
@@ -1122,13 +1120,13 @@ static int s3c64xx_spi_probe(struct platform_device *pdev)
        sdd->regs = devm_ioremap_resource(&pdev->dev, mem_res);
        if (IS_ERR(sdd->regs)) {
                ret = PTR_ERR(sdd->regs);
-               goto err0;
+               goto err_deref_master;
        }
 
        if (sci->cfg_gpio && sci->cfg_gpio()) {
                dev_err(&pdev->dev, "Unable to config gpio\n");
                ret = -EBUSY;
-               goto err0;
+               goto err_deref_master;
        }
 
        /* Setup clocks */
@@ -1136,13 +1134,13 @@ static int s3c64xx_spi_probe(struct platform_device *pdev)
        if (IS_ERR(sdd->clk)) {
                dev_err(&pdev->dev, "Unable to acquire clock 'spi'\n");
                ret = PTR_ERR(sdd->clk);
-               goto err0;
+               goto err_deref_master;
        }
 
-       if (clk_prepare_enable(sdd->clk)) {
+       ret = clk_prepare_enable(sdd->clk);
+       if (ret) {
                dev_err(&pdev->dev, "Couldn't enable clock 'spi'\n");
-               ret = -EBUSY;
-               goto err0;
+               goto err_deref_master;
        }
 
        sprintf(clk_name, "spi_busclk%d", sci->src_clk_nr);
@@ -1151,13 +1149,28 @@ static int s3c64xx_spi_probe(struct platform_device *pdev)
                dev_err(&pdev->dev,
                        "Unable to acquire clock '%s'\n", clk_name);
                ret = PTR_ERR(sdd->src_clk);
-               goto err2;
+               goto err_disable_clk;
        }
 
-       if (clk_prepare_enable(sdd->src_clk)) {
+       ret = clk_prepare_enable(sdd->src_clk);
+       if (ret) {
                dev_err(&pdev->dev, "Couldn't enable clock '%s'\n", clk_name);
-               ret = -EBUSY;
-               goto err2;
+               goto err_disable_clk;
+       }
+
+       if (sdd->port_conf->clk_ioclk) {
+               sdd->ioclk = devm_clk_get(&pdev->dev, "spi_ioclk");
+               if (IS_ERR(sdd->ioclk)) {
+                       dev_err(&pdev->dev, "Unable to acquire 'ioclk'\n");
+                       ret = PTR_ERR(sdd->ioclk);
+                       goto err_disable_src_clk;
+               }
+
+               ret = clk_prepare_enable(sdd->ioclk);
+               if (ret) {
+                       dev_err(&pdev->dev, "Couldn't enable clock 'ioclk'\n");
+                       goto err_disable_src_clk;
+               }
        }
 
        pm_runtime_set_autosuspend_delay(&pdev->dev, AUTOSUSPEND_TIMEOUT);
@@ -1177,7 +1190,7 @@ static int s3c64xx_spi_probe(struct platform_device *pdev)
        if (ret != 0) {
                dev_err(&pdev->dev, "Failed to request IRQ %d: %d\n",
                        irq, ret);
-               goto err3;
+               goto err_pm_put;
        }
 
        writel(S3C64XX_SPI_INT_RX_OVERRUN_EN | S3C64XX_SPI_INT_RX_UNDERRUN_EN |
@@ -1187,7 +1200,7 @@ static int s3c64xx_spi_probe(struct platform_device *pdev)
        ret = devm_spi_register_master(&pdev->dev, master);
        if (ret != 0) {
                dev_err(&pdev->dev, "cannot register SPI master: %d\n", ret);
-               goto err3;
+               goto err_pm_put;
        }
 
        dev_dbg(&pdev->dev, "Samsung SoC SPI Driver loaded for Bus SPI-%d with %d Slaves attached\n",
@@ -1201,15 +1214,17 @@ static int s3c64xx_spi_probe(struct platform_device *pdev)
 
        return 0;
 
-err3:
+err_pm_put:
        pm_runtime_put_noidle(&pdev->dev);
        pm_runtime_disable(&pdev->dev);
        pm_runtime_set_suspended(&pdev->dev);
 
+       clk_disable_unprepare(sdd->ioclk);
+err_disable_src_clk:
        clk_disable_unprepare(sdd->src_clk);
-err2:
+err_disable_clk:
        clk_disable_unprepare(sdd->clk);
-err0:
+err_deref_master:
        spi_master_put(master);
 
        return ret;
@@ -1217,13 +1232,15 @@ static int s3c64xx_spi_probe(struct platform_device *pdev)
 
 static int s3c64xx_spi_remove(struct platform_device *pdev)
 {
-       struct spi_master *master = spi_master_get(platform_get_drvdata(pdev));
+       struct spi_master *master = platform_get_drvdata(pdev);
        struct s3c64xx_spi_driver_data *sdd = spi_master_get_devdata(master);
 
        pm_runtime_get_sync(&pdev->dev);
 
        writel(0, sdd->regs + S3C64XX_SPI_INT_EN);
 
+       clk_disable_unprepare(sdd->ioclk);
+
        clk_disable_unprepare(sdd->src_clk);
 
        clk_disable_unprepare(sdd->clk);
@@ -1282,6 +1299,7 @@ static int s3c64xx_spi_runtime_suspend(struct device *dev)
 
        clk_disable_unprepare(sdd->clk);
        clk_disable_unprepare(sdd->src_clk);
+       clk_disable_unprepare(sdd->ioclk);
 
        return 0;
 }
@@ -1292,17 +1310,28 @@ static int s3c64xx_spi_runtime_resume(struct device *dev)
        struct s3c64xx_spi_driver_data *sdd = spi_master_get_devdata(master);
        int ret;
 
+       if (sdd->port_conf->clk_ioclk) {
+               ret = clk_prepare_enable(sdd->ioclk);
+               if (ret != 0)
+                       return ret;
+       }
+
        ret = clk_prepare_enable(sdd->src_clk);
        if (ret != 0)
-               return ret;
+               goto err_disable_ioclk;
 
        ret = clk_prepare_enable(sdd->clk);
-       if (ret != 0) {
-               clk_disable_unprepare(sdd->src_clk);
-               return ret;
-       }
+       if (ret != 0)
+               goto err_disable_src_clk;
 
        return 0;
+
+err_disable_src_clk:
+       clk_disable_unprepare(sdd->src_clk);
+err_disable_ioclk:
+       clk_disable_unprepare(sdd->ioclk);
+
+       return ret;
 }
 #endif /* CONFIG_PM */
 
@@ -1358,6 +1387,16 @@ static struct s3c64xx_spi_port_config exynos7_spi_port_config = {
        .quirks         = S3C64XX_SPI_QUIRK_CS_AUTO,
 };
 
+static struct s3c64xx_spi_port_config exynos5433_spi_port_config = {
+       .fifo_lvl_mask  = { 0x1ff, 0x7f, 0x7f, 0x7f, 0x7f, 0x1ff},
+       .rx_lvl_offset  = 15,
+       .tx_st_done     = 25,
+       .high_speed     = true,
+       .clk_from_cmu   = true,
+       .clk_ioclk      = true,
+       .quirks         = S3C64XX_SPI_QUIRK_CS_AUTO,
+};
+
 static const struct platform_device_id s3c64xx_spi_driver_ids[] = {
        {
                .name           = "s3c2443-spi",
@@ -1388,6 +1427,9 @@ static const struct of_device_id s3c64xx_spi_dt_match[] = {
        { .compatible = "samsung,exynos7-spi",
                        .data = (void *)&exynos7_spi_port_config,
        },
+       { .compatible = "samsung,exynos5433-spi",
+                       .data = (void *)&exynos5433_spi_port_config,
+       },
        { },
 };
 MODULE_DEVICE_TABLE(of, s3c64xx_spi_dt_match);