mmc: tmio: Add tuning support
authorAi Kyuse <ai.kyuse.uw@renesas.com>
Thu, 3 Nov 2016 14:16:03 +0000 (15:16 +0100)
committerUlf Hansson <ulf.hansson@linaro.org>
Tue, 29 Nov 2016 08:00:57 +0000 (09:00 +0100)
Add tuning support for use with SDR104 mode

Signed-off-by: Ai Kyuse <ai.kyuse.uw@renesas.com>
Signed-off-by: Simon Horman <horms+renesas@verge.net.au>
Acked-by: Wolfram Sang <wsa+renesas@sang-engineering.com>
Tested-by: Wolfram Sang <wsa+renesas@sang-engineering.com>
Signed-off-by: Ulf Hansson <ulf.hansson@linaro.org>
drivers/mmc/host/tmio_mmc.h
drivers/mmc/host/tmio_mmc_pio.c

index b8469f2de42b48541c4244996611827dcbc4233c..9e20bcf3aa8d2da5fcb263e81a15845326592b36 100644 (file)
@@ -153,6 +153,7 @@ struct tmio_mmc_host {
        struct mutex            ios_lock;       /* protect set_ios() context */
        bool                    native_hotplug;
        bool                    sdio_irq_enabled;
+       u32                     scc_tappos;
 
        /* Mandatory callback */
        int (*clk_enable)(struct tmio_mmc_host *host);
@@ -168,6 +169,19 @@ struct tmio_mmc_host {
                                           struct mmc_ios *ios);
        int (*write16_hook)(struct tmio_mmc_host *host, int addr);
        void (*hw_reset)(struct tmio_mmc_host *host);
+       void (*prepare_tuning)(struct tmio_mmc_host *host, unsigned long tap);
+       bool (*check_scc_error)(struct tmio_mmc_host *host);
+
+       /*
+        * Mandatory callback for tuning to occur which is optional for SDR50
+        * and mandatory for SDR104.
+        */
+       unsigned int (*init_tuning)(struct tmio_mmc_host *host);
+       int (*select_tuning)(struct tmio_mmc_host *host);
+
+       /* Tuning values: 1 for success, 0 for failure */
+       DECLARE_BITMAP(taps, BITS_PER_BYTE * sizeof(long));
+       unsigned int tap_num;
 };
 
 struct tmio_mmc_host *tmio_mmc_host_alloc(struct platform_device *pdev);
index 011d04e102310e62625c8708bdb9b6312652febc..1b84c635841eb2bbfccabbd7994be8712b3dd286 100644 (file)
@@ -36,6 +36,7 @@
 #include <linux/io.h>
 #include <linux/irq.h>
 #include <linux/mfd/tmio.h>
+#include <linux/mmc/card.h>
 #include <linux/mmc/host.h>
 #include <linux/mmc/mmc.h>
 #include <linux/mmc/slot-gpio.h>
@@ -298,6 +299,9 @@ static void tmio_mmc_finish_request(struct tmio_mmc_host *host)
        if (mrq->cmd->error || (mrq->data && mrq->data->error))
                tmio_mmc_abort_dma(host);
 
+       if (host->check_scc_error)
+               host->check_scc_error(host);
+
        mmc_request_done(host->mmc, mrq);
 }
 
@@ -797,6 +801,55 @@ static void tmio_mmc_hw_reset(struct mmc_host *mmc)
                host->hw_reset(host);
 }
 
+static int tmio_mmc_execute_tuning(struct mmc_host *mmc, u32 opcode)
+{
+       struct tmio_mmc_host *host = mmc_priv(mmc);
+       int i, ret = 0;
+
+       if (!host->tap_num) {
+               if (!host->init_tuning || !host->select_tuning)
+                       /* Tuning is not supported */
+                       goto out;
+
+               host->tap_num = host->init_tuning(host);
+               if (!host->tap_num)
+                       /* Tuning is not supported */
+                       goto out;
+       }
+
+       if (host->tap_num * 2 >= sizeof(host->taps) * BITS_PER_BYTE) {
+               dev_warn_once(&host->pdev->dev,
+                     "Too many taps, skipping tuning. Please consider updating size of taps field of tmio_mmc_host\n");
+               goto out;
+       }
+
+       bitmap_zero(host->taps, host->tap_num * 2);
+
+       /* Issue CMD19 twice for each tap */
+       for (i = 0; i < 2 * host->tap_num; i++) {
+               if (host->prepare_tuning)
+                       host->prepare_tuning(host, i % host->tap_num);
+
+               ret = mmc_send_tuning(mmc, opcode, NULL);
+               if (ret && ret != -EILSEQ)
+                       goto out;
+               if (ret == 0)
+                       set_bit(i, host->taps);
+
+               mdelay(1);
+       }
+
+       ret = host->select_tuning(host);
+
+out:
+       if (ret < 0) {
+               dev_warn(&host->pdev->dev, "Tuning procedure failed\n");
+               tmio_mmc_hw_reset(mmc);
+       }
+
+       return ret;
+}
+
 /* Process requests from the MMC layer */
 static void tmio_mmc_request(struct mmc_host *mmc, struct mmc_request *mrq)
 {
@@ -1014,6 +1067,7 @@ static struct mmc_host_ops tmio_mmc_ops = {
        .enable_sdio_irq = tmio_mmc_enable_sdio_irq,
        .multi_io_quirk = tmio_multi_io_quirk,
        .hw_reset       = tmio_mmc_hw_reset,
+       .execute_tuning = tmio_mmc_execute_tuning,
 };
 
 static int tmio_mmc_init_ocr(struct tmio_mmc_host *host)
@@ -1260,6 +1314,11 @@ int tmio_mmc_host_runtime_suspend(struct device *dev)
 }
 EXPORT_SYMBOL(tmio_mmc_host_runtime_suspend);
 
+static bool tmio_mmc_can_retune(struct tmio_mmc_host *host)
+{
+       return host->tap_num && mmc_can_retune(host->mmc);
+}
+
 int tmio_mmc_host_runtime_resume(struct device *dev)
 {
        struct mmc_host *mmc = dev_get_drvdata(dev);
@@ -1273,6 +1332,9 @@ int tmio_mmc_host_runtime_resume(struct device *dev)
 
        tmio_mmc_enable_dma(host, true);
 
+       if (tmio_mmc_can_retune(host) && host->select_tuning(host))
+               dev_warn(&host->pdev->dev, "Tuning selection failed\n");
+
        return 0;
 }
 EXPORT_SYMBOL(tmio_mmc_host_runtime_resume);