linux-gpio.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH 0/4] msdc: Add tuning support of Mediatek MMC host
@ 2015-08-12  8:24 Chaotian Jing
       [not found] ` <1439367845-5891-1-git-send-email-chaotian.jing-NuS5LvNUpcJWk0Htik3J/w@public.gmane.org>
  0 siblings, 1 reply; 10+ messages in thread
From: Chaotian Jing @ 2015-08-12  8:24 UTC (permalink / raw)
  To: Rob Herring, Matthias Brugger, Chris Ball, Ulf Hansson
  Cc: Mark Rutland, James Liao, Catalin Marinas, Wenbin Mei,
	Will Deacon, Russell King - ARM Linux, Hongzhou Yang, Joe.C,
	devicetree-u79uwXL29TY76Z2rM5mHXA, Arnd Bergmann,
	bin.zhang-NuS5LvNUpcJWk0Htik3J/w,
	linux-gpio-u79uwXL29TY76Z2rM5mHXA,
	linux-mediatek-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, Eddie Huang,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, Liuquan Ji,
	srv_heupstream-NuS5LvNUpcJWk0Htik3J/w,
	linux-mmc-u79uwXL29TY76Z2rM5mHXA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA, Yong Mao, Sascha Hauer

This series add tuning support for EMMC/SD transfer.
The tuning thread was start when CMD/DAT crc error deteted,
for some SD card, tune DAT crc error may occur data timeout,
in this case, try to reset card to recover.
With tuning support, can support SDR104/HS200/HS400 modes

Chaotian Jing (4):
  mmc: mediatek: Add online-tuning support of EMMC/SD
  mmc: mediatek: Add HS400 support
  arm64: dts: mediatek: Support SD/EMMC SDR104/HS200/HS400
  mmc: dt-bindings:  Add 400Mhz clock source

 Documentation/devicetree/bindings/mmc/mtk-sd.txt |  10 +-
 arch/arm64/boot/dts/mediatek/mt8173-evb.dts      |  10 +-
 arch/arm64/boot/dts/mediatek/mt8173.dtsi         |   5 +-
 drivers/mmc/host/mtk-sd.c                        | 256 ++++++++++++++++++++---
 4 files changed, 248 insertions(+), 33 deletions(-)

^ permalink raw reply	[flat|nested] 10+ messages in thread

* [PATCH 1/4] mmc: mediatek: Add online-tuning support of EMMC/SD
       [not found] ` <1439367845-5891-1-git-send-email-chaotian.jing-NuS5LvNUpcJWk0Htik3J/w@public.gmane.org>
@ 2015-08-12  8:24   ` Chaotian Jing
  2015-08-17 11:31     ` Ulf Hansson
  2015-08-12  8:24   ` [PATCH 2/4] mmc: mediatek: Add HS400 support Chaotian Jing
                     ` (2 subsequent siblings)
  3 siblings, 1 reply; 10+ messages in thread
From: Chaotian Jing @ 2015-08-12  8:24 UTC (permalink / raw)
  To: Rob Herring, Matthias Brugger, Chris Ball, Ulf Hansson
  Cc: Mark Rutland, James Liao, Catalin Marinas, Wenbin Mei,
	Will Deacon, Russell King - ARM Linux, Hongzhou Yang,
	Chaotian Jing, Joe.C, devicetree-u79uwXL29TY76Z2rM5mHXA,
	Arnd Bergmann, bin.zhang-NuS5LvNUpcJWk0Htik3J/w,
	linux-gpio-u79uwXL29TY76Z2rM5mHXA,
	linux-mediatek-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, Eddie Huang,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, Liuquan Ji,
	srv_heupstream-NuS5LvNUpcJWk0Htik3J/w,
	linux-mmc-u79uwXL29TY76Z2rM5mHXA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA, Yong Mao, Sascha Hauer

Schedule a workqueue to do tuning when CRC error
Call mmc_hw_reset to re-init card when data timeout

Signed-off-by: Chaotian Jing <chaotian.jing-NuS5LvNUpcJWk0Htik3J/w@public.gmane.org>
---
 drivers/mmc/host/mtk-sd.c | 162 ++++++++++++++++++++++++++++++++++++++++++----
 1 file changed, 150 insertions(+), 12 deletions(-)

diff --git a/drivers/mmc/host/mtk-sd.c b/drivers/mmc/host/mtk-sd.c
index 7153500..eb44fe1 100644
--- a/drivers/mmc/host/mtk-sd.c
+++ b/drivers/mmc/host/mtk-sd.c
@@ -27,6 +27,7 @@
 #include <linux/pm_runtime.h>
 #include <linux/regulator/consumer.h>
 #include <linux/spinlock.h>
+#include <linux/workqueue.h>
 
 #include <linux/mmc/card.h>
 #include <linux/mmc/core.h>
@@ -290,6 +291,10 @@ struct msdc_host {
 	struct pinctrl_state *pins_default;
 	struct pinctrl_state *pins_uhs;
 	struct delayed_work req_timeout;
+	struct workqueue_struct *repeat_workqueue;
+	struct work_struct repeat_req;
+	struct mmc_request *repeat_mrq;
+	u32 tune_cmd_counter;
 	int irq;		/* host interrupt */
 
 	struct clk *src_clk;	/* msdc source clock */
@@ -353,7 +358,10 @@ static void msdc_reset_hw(struct msdc_host *host)
 static void msdc_cmd_next(struct msdc_host *host,
 		struct mmc_request *mrq, struct mmc_command *cmd);
 
-static u32 data_ints_mask = MSDC_INTEN_XFER_COMPL | MSDC_INTEN_DATTMO |
+static const u32 cmd_ints_mask = MSDC_INTEN_CMDRDY | MSDC_INTEN_RSPCRCERR |
+			MSDC_INTEN_CMDTMO | MSDC_INTEN_ACMDRDY |
+			MSDC_INTEN_ACMDCRCERR | MSDC_INTEN_ACMDTMO;
+static const u32 data_ints_mask = MSDC_INTEN_XFER_COMPL | MSDC_INTEN_DATTMO |
 			MSDC_INTEN_DATCRCERR | MSDC_INTEN_DMA_BDCSERR |
 			MSDC_INTEN_DMA_GPDCSERR | MSDC_INTEN_DMA_PROTECT;
 
@@ -690,6 +698,18 @@ static void msdc_request_done(struct msdc_host *host, struct mmc_request *mrq)
 	msdc_track_cmd_data(host, mrq->cmd, mrq->data);
 	if (mrq->data)
 		msdc_unprepare_data(host, mrq);
+	if (host->error && host->mmc->card &&
+	    !mmc_card_sdio(host->mmc->card)) {
+		if (mrq->cmd->error == (unsigned int)-EILSEQ ||
+		    (mrq->stop && mrq->stop->error == (unsigned int)-EILSEQ) ||
+		    (mrq->sbc && mrq->sbc->error == (unsigned int)-EILSEQ) ||
+		    (mrq->data && mrq->data->error))  {
+			host->repeat_mrq = mrq;
+			queue_work(host->repeat_workqueue, &host->repeat_req);
+			return;
+		}
+	}
+	host->tune_cmd_counter = 0;
 	mmc_request_done(host->mmc, mrq);
 
 	pm_runtime_mark_last_busy(host->dev);
@@ -725,11 +745,7 @@ static bool msdc_cmd_done(struct msdc_host *host, int events,
 	if (done)
 		return true;
 
-	sdr_clr_bits(host->base + MSDC_INTEN, MSDC_INTEN_CMDRDY |
-			MSDC_INTEN_RSPCRCERR | MSDC_INTEN_CMDTMO |
-			MSDC_INTEN_ACMDRDY | MSDC_INTEN_ACMDCRCERR |
-			MSDC_INTEN_ACMDTMO);
-	writel(cmd->arg, host->base + SDC_ARG);
+	sdr_clr_bits(host->base + MSDC_INTEN, cmd_ints_mask);
 
 	if (cmd->flags & MMC_RSP_PRESENT) {
 		if (cmd->flags & MMC_RSP_136) {
@@ -819,10 +835,7 @@ static void msdc_start_command(struct msdc_host *host,
 	rawcmd = msdc_cmd_prepare_raw_cmd(host, mrq, cmd);
 	mod_delayed_work(system_wq, &host->req_timeout, DAT_TIMEOUT);
 
-	sdr_set_bits(host->base + MSDC_INTEN, MSDC_INTEN_CMDRDY |
-			MSDC_INTEN_RSPCRCERR | MSDC_INTEN_CMDTMO |
-			MSDC_INTEN_ACMDRDY | MSDC_INTEN_ACMDCRCERR |
-			MSDC_INTEN_ACMDTMO);
+	sdr_set_bits(host->base + MSDC_INTEN, cmd_ints_mask);
 	writel(cmd->arg, host->base + SDC_ARG);
 	writel(rawcmd, host->base + SDC_CMD);
 }
@@ -942,6 +955,8 @@ static bool msdc_data_xfer_done(struct msdc_host *host, u32 events,
 
 			if (events & MSDC_INT_DATTMO)
 				data->error = -ETIMEDOUT;
+			else if (events & MSDC_INT_DATCRCERR)
+				data->error = -EILSEQ;
 
 			dev_err(host->dev, "%s: cmd=%d; blocks=%d",
 				__func__, mrq->cmd->opcode, data->blocks);
@@ -1113,8 +1128,8 @@ static void msdc_init_hw(struct msdc_host *host)
 
 	writel(0, host->base + MSDC_PAD_TUNE);
 	writel(0, host->base + MSDC_IOCON);
-	sdr_set_field(host->base + MSDC_IOCON, MSDC_IOCON_DDLSEL, 1);
-	writel(0x403c004f, host->base + MSDC_PATCH_BIT);
+	sdr_set_field(host->base + MSDC_IOCON, MSDC_IOCON_DDLSEL, 0);
+	writel(0x403c0046, host->base + MSDC_PATCH_BIT);
 	sdr_set_field(host->base + MSDC_PATCH_BIT, MSDC_CKGEN_MSDC_DLY_SEL, 1);
 	writel(0xffff0089, host->base + MSDC_PATCH_BIT1);
 	/* Configure to enable SDIO mode.
@@ -1176,6 +1191,7 @@ static void msdc_ops_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
 	switch (ios->power_mode) {
 	case MMC_POWER_UP:
 		if (!IS_ERR(mmc->supply.vmmc)) {
+			msdc_init_hw(host);
 			ret = mmc_regulator_set_ocr(mmc, mmc->supply.vmmc,
 					ios->vdd);
 			if (ret) {
@@ -1214,6 +1230,120 @@ end:
 	pm_runtime_put_autosuspend(host->dev);
 }
 
+static void msdc_reset_mrq(struct mmc_request *mrq)
+{
+	mrq->cmd->error = 0;
+	if (mrq->sbc)
+		mrq->sbc->error = 0;
+	if (mrq->data)
+		mrq->data->error = 0;
+	if (mrq->stop)
+		mrq->stop->error = 0;
+}
+
+/* Send CMD12 when tuning, do not check CRC error and timeout */
+static void msdc_send_stop(struct msdc_host *host)
+{
+	u32 opcode = MMC_STOP_TRANSMISSION;
+	u32 arg = 0;
+	u32 rawcmd = 0;
+	u32 intsts = 0;
+
+	/* Reset host first */
+	msdc_reset_hw(host);
+
+	rawcmd = (opcode & 0x3f) | (7 << 7);
+	rawcmd |= (1 << 14); /* stop cmd */
+
+	sdr_clr_bits(host->base + MSDC_INTEN, cmd_ints_mask);
+	writel(arg, host->base + SDC_ARG);
+	writel(rawcmd, host->base + SDC_CMD);
+
+	while (1) {
+		intsts = readl(host->base + MSDC_INT);
+		if (intsts) {
+			writel(intsts, host->base + MSDC_INT);
+			if (intsts & cmd_ints_mask) {
+				dev_dbg(host->dev, "result of cmd12: %x\n",
+						intsts);
+				break;
+			}
+		}
+		udelay(1);
+	}
+}
+
+/* When tuning, CMD13 may also get crc error, so use MSDC_PS to get card status */
+static void msdc_wait_card_not_busy(struct msdc_host *host)
+{
+	while (1) {
+		if ((readl(host->base + MSDC_PS) & BIT(16)) == 0) { /* check dat0 status */
+			msleep_interruptible(10);
+			dev_dbg(host->dev, "MSDC_PS: %08x, SDC_STS: %08x\n",
+				readl(host->base + MSDC_PS), readl(host->base + SDC_STS));
+		} else
+			break;
+	}
+}
+
+static void msdc_tune_request(struct msdc_host *host)
+{
+	u32 orig_rsmpl, orig_cksel;
+	u32 cur_rsmpl, cur_cksel = 0;
+	u32 ddr, clkmode;
+	struct mmc_ios ios = host->mmc->ios;
+
+	sdr_get_field(host->base + MSDC_IOCON, MSDC_IOCON_RSPL, &orig_rsmpl);
+	sdr_get_field(host->base + MSDC_PATCH_BIT, MSDC_CKGEN_MSDC_DLY_SEL,
+		      &orig_cksel);
+	cur_rsmpl = (orig_rsmpl + 1);
+	sdr_set_field(host->base + MSDC_IOCON, MSDC_IOCON_RSPL, cur_rsmpl % 2);
+
+	if (ios.timing != MMC_TIMING_MMC_HS400) {
+		sdr_set_field(host->base + MSDC_IOCON, MSDC_IOCON_DSPL,
+			      cur_rsmpl % 2);
+		sdr_set_field(host->base + MSDC_IOCON, MSDC_IOCON_W_DSPL,
+			      cur_rsmpl % 2);
+	}
+
+	if (cur_rsmpl >= 2) {
+		cur_cksel = orig_cksel + 1;
+		sdr_set_field(host->base + MSDC_PATCH_BIT,
+			      MSDC_CKGEN_MSDC_DLY_SEL, cur_cksel % 16);
+	}
+
+	if (host->tune_cmd_counter++ >= 2 * 16) {
+		dev_warn(host->dev, "Tune fail at %dhz\n", host->mclk);
+		host->tune_cmd_counter = 0;
+		sdr_set_field(host->base + MSDC_IOCON, MSDC_IOCON_RSPL, 0);
+		sdr_set_field(host->base + MSDC_PATCH_BIT,
+			      MSDC_CKGEN_MSDC_DLY_SEL, 0);
+		sdr_get_field(host->base + MSDC_CFG, MSDC_CFG_CKMOD, &clkmode);
+		ddr = (clkmode == 2) ? 1 : 0;
+		msdc_set_mclk(host, ddr, host->mclk / 2);
+	}
+}
+
+static void msdc_repeat_request(struct work_struct *work)
+{
+	struct msdc_host *host = container_of(work, struct msdc_host, repeat_req);
+	struct mmc_request *mrq;
+	unsigned long flags;
+
+	spin_lock_irqsave(&host->lock, flags);
+	mrq = host->repeat_mrq;
+	host->repeat_mrq = NULL;
+	spin_unlock_irqrestore(&host->lock, flags);
+	msdc_send_stop(host);
+	msdc_wait_card_not_busy(host);
+	if (mrq->data && mrq->data->error == -ETIMEDOUT)
+		mmc_hw_reset(host->mmc);
+	else
+		msdc_tune_request(host);
+	msdc_reset_mrq(mrq);
+	msdc_ops_request(host->mmc, mrq);
+}
+
 static struct mmc_host_ops mt_msdc_ops = {
 	.post_req = msdc_post_req,
 	.pre_req = msdc_pre_req,
@@ -1324,6 +1454,12 @@ static int msdc_drv_probe(struct platform_device *pdev)
 	}
 	msdc_init_gpd_bd(host, &host->dma);
 	INIT_DELAYED_WORK(&host->req_timeout, msdc_request_timeout);
+	host->repeat_workqueue = create_singlethread_workqueue("repeat_workqueue");
+	if (!host->repeat_workqueue) {
+		ret = -ENOMEM;
+		goto release_mem;
+	}
+	INIT_WORK(&host->repeat_req, msdc_repeat_request);
 	spin_lock_init(&host->lock);
 
 	platform_set_drvdata(pdev, mmc);
@@ -1348,6 +1484,7 @@ static int msdc_drv_probe(struct platform_device *pdev)
 end:
 	pm_runtime_disable(host->dev);
 release:
+	destroy_workqueue(host->repeat_workqueue);
 	platform_set_drvdata(pdev, NULL);
 	msdc_deinit_hw(host);
 	msdc_gate_clock(host);
@@ -1383,6 +1520,7 @@ static int msdc_drv_remove(struct platform_device *pdev)
 
 	pm_runtime_disable(host->dev);
 	pm_runtime_put_noidle(host->dev);
+	destroy_workqueue(host->repeat_workqueue);
 	dma_free_coherent(&pdev->dev,
 			sizeof(struct mt_gpdma_desc),
 			host->dma.gpd, host->dma.gpd_addr);
-- 
1.8.1.1.dirty

^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [PATCH 2/4] mmc: mediatek: Add HS400 support
       [not found] ` <1439367845-5891-1-git-send-email-chaotian.jing-NuS5LvNUpcJWk0Htik3J/w@public.gmane.org>
  2015-08-12  8:24   ` [PATCH 1/4] mmc: mediatek: Add online-tuning support of EMMC/SD Chaotian Jing
@ 2015-08-12  8:24   ` Chaotian Jing
  2015-08-12  8:24   ` [PATCH 3/4] arm64: dts: mediatek: Support SD/EMMC SDR104/HS200/HS400 Chaotian Jing
  2015-08-12  8:24   ` [PATCH 4/4] mmc: dt-bindings: Add 400Mhz clock source Chaotian Jing
  3 siblings, 0 replies; 10+ messages in thread
From: Chaotian Jing @ 2015-08-12  8:24 UTC (permalink / raw)
  To: Rob Herring, Matthias Brugger, Chris Ball, Ulf Hansson
  Cc: Mark Rutland, James Liao, Catalin Marinas, Wenbin Mei,
	Will Deacon, Russell King - ARM Linux, Hongzhou Yang,
	Chaotian Jing, Joe.C, devicetree-u79uwXL29TY76Z2rM5mHXA,
	Arnd Bergmann, bin.zhang-NuS5LvNUpcJWk0Htik3J/w,
	linux-gpio-u79uwXL29TY76Z2rM5mHXA,
	linux-mediatek-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, Eddie Huang,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, Liuquan Ji,
	srv_heupstream-NuS5LvNUpcJWk0Htik3J/w,
	linux-mmc-u79uwXL29TY76Z2rM5mHXA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA, Yong Mao, Sascha Hauer

Support HS400 mode in driver

Signed-off-by: Chaotian Jing <chaotian.jing-NuS5LvNUpcJWk0Htik3J/w@public.gmane.org>
---
 drivers/mmc/host/mtk-sd.c | 108 ++++++++++++++++++++++++++++++++++++----------
 1 file changed, 86 insertions(+), 22 deletions(-)

diff --git a/drivers/mmc/host/mtk-sd.c b/drivers/mmc/host/mtk-sd.c
index eb44fe1..457d919 100644
--- a/drivers/mmc/host/mtk-sd.c
+++ b/drivers/mmc/host/mtk-sd.c
@@ -72,6 +72,8 @@
 #define MSDC_PATCH_BIT   0xb0
 #define MSDC_PATCH_BIT1  0xb4
 #define MSDC_PAD_TUNE    0xec
+#define PAD_DS_TUNE      0x188
+#define EMMC50_CFG0      0x208
 
 /*--------------------------------------------------------------------------*/
 /* Register Mask                                                            */
@@ -205,6 +207,14 @@
 #define MSDC_PATCH_BIT_SPCPUSH    (0x1 << 29)	/* RW */
 #define MSDC_PATCH_BIT_DECRCTMO   (0x1 << 30)	/* RW */
 
+#define PAD_DS_TUNE_DLY1	  (0x1f << 2)
+#define PAD_DS_TUNE_DLY2	  (0x1f << 7)
+#define PAD_DS_TUNE_DLY3	  (0x1f << 12)
+
+#define EMMC50_CFG_PADCMD_LATCHCK (0x1 << 0)
+#define EMMC50_CFG_CRCSTS_EDGE    (0x1 << 3)
+#define EMMC50_CFG_CFCSTS_SEL     (0x1 << 4)
+
 #define REQ_CMD_EIO  (0x1 << 0)
 #define REQ_CMD_TMO  (0x1 << 1)
 #define REQ_DAT_ERR  (0x1 << 2)
@@ -266,6 +276,8 @@ struct msdc_save_para {
 	u32 pad_tune;
 	u32 patch_bit0;
 	u32 patch_bit1;
+	u32 pad_ds_tune;
+	u32 emmc50_cfg0;
 };
 
 struct msdc_host {
@@ -295,14 +307,17 @@ struct msdc_host {
 	struct work_struct repeat_req;
 	struct mmc_request *repeat_mrq;
 	u32 tune_cmd_counter;
+	u32 tune_hs400_rw_counter;
 	int irq;		/* host interrupt */
 
 	struct clk *src_clk;	/* msdc source clock */
+	struct clk *src_clk_parent; /* src_clk's parent */
+	struct clk *hs400_src;	/* 400Mhz source clock */
 	struct clk *h_clk;      /* msdc h_clk */
 	u32 mclk;		/* mmc subsystem clock frequency */
 	u32 src_clk_freq;	/* source clock frequency */
 	u32 sclk;		/* SD/MS bus clock frequency */
-	bool ddr;
+	unsigned char timing;
 	bool vqmmc_enabled;
 	struct msdc_save_para save_para; /* used when gate HCLK */
 };
@@ -493,7 +508,7 @@ static void msdc_ungate_clock(struct msdc_host *host)
 		cpu_relax();
 }
 
-static void msdc_set_mclk(struct msdc_host *host, int ddr, u32 hz)
+static void msdc_set_mclk(struct msdc_host *host, unsigned char timing, u32 hz)
 {
 	u32 mode;
 	u32 flags;
@@ -509,8 +524,13 @@ static void msdc_set_mclk(struct msdc_host *host, int ddr, u32 hz)
 
 	flags = readl(host->base + MSDC_INTEN);
 	sdr_clr_bits(host->base + MSDC_INTEN, flags);
-	if (ddr) { /* may need to modify later */
-		mode = 0x2; /* ddr mode and use divisor */
+	if (timing == MMC_TIMING_UHS_DDR50 ||
+	    timing == MMC_TIMING_MMC_DDR52 ||
+	    timing == MMC_TIMING_MMC_HS400) { /* may need to modify later */
+		if (timing == MMC_TIMING_MMC_HS400)
+			mode = 0x3;
+		else
+			mode = 0x2; /* ddr mode and use divisor */
 		if (hz >= (host->src_clk_freq >> 2)) {
 			div = 0; /* mean div = 1/4 */
 			sclk = host->src_clk_freq >> 2; /* sclk = clk / 4 */
@@ -540,12 +560,12 @@ static void msdc_set_mclk(struct msdc_host *host, int ddr, u32 hz)
 		cpu_relax();
 	host->sclk = sclk;
 	host->mclk = hz;
-	host->ddr = ddr;
+	host->timing = timing;
 	/* need because clk changed. */
 	msdc_set_timeout(host, host->timeout_ns, host->timeout_clks);
 	sdr_set_bits(host->base + MSDC_INTEN, flags);
 
-	dev_dbg(host->dev, "sclk: %d, ddr: %d\n", host->sclk, ddr);
+	dev_dbg(host->dev, "sclk: %d, timing: %d\n", host->sclk, timing);
 }
 
 static inline u32 msdc_cmd_find_resp(struct msdc_host *host,
@@ -710,6 +730,7 @@ static void msdc_request_done(struct msdc_host *host, struct mmc_request *mrq)
 		}
 	}
 	host->tune_cmd_counter = 0;
+	host->tune_hs400_rw_counter = 0;
 	mmc_request_done(host->mmc, mrq);
 
 	pm_runtime_mark_last_busy(host->dev);
@@ -1132,6 +1153,7 @@ static void msdc_init_hw(struct msdc_host *host)
 	writel(0x403c0046, host->base + MSDC_PATCH_BIT);
 	sdr_set_field(host->base + MSDC_PATCH_BIT, MSDC_CKGEN_MSDC_DLY_SEL, 1);
 	writel(0xffff0089, host->base + MSDC_PATCH_BIT1);
+	sdr_set_bits(host->base + EMMC50_CFG0, EMMC50_CFG_CFCSTS_SEL);
 	/* Configure to enable SDIO mode.
 	 * it's must otherwise sdio cmd5 failed
 	 */
@@ -1163,11 +1185,14 @@ static void msdc_init_gpd_bd(struct msdc_host *host, struct msdc_dma *dma)
 	struct mt_bdma_desc *bd = dma->bd;
 	int i;
 
-	memset(gpd, 0, sizeof(struct mt_gpdma_desc));
+	memset(gpd, 0, sizeof(struct mt_gpdma_desc) * 2);
 
 	gpd->gpd_info = GPDMA_DESC_BDP; /* hwo, cs, bd pointer */
 	gpd->ptr = (u32)dma->bd_addr; /* physical address */
-
+	/* gpd->next is must set for desc DMA
+	 * That's why must alloc 2 gpd structure.
+	 */
+	gpd->next = (u32)dma->gpd_addr + sizeof(struct mt_gpdma_desc);
 	memset(bd, 0, sizeof(struct mt_bdma_desc) * MAX_BD_NUM);
 	for (i = 0; i < (MAX_BD_NUM - 1); i++)
 		bd[i].next = (u32)dma->bd_addr + sizeof(*bd) * (i + 1);
@@ -1177,14 +1202,9 @@ static void msdc_ops_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
 {
 	struct msdc_host *host = mmc_priv(mmc);
 	int ret;
-	u32 ddr = 0;
 
 	pm_runtime_get_sync(host->dev);
 
-	if (ios->timing == MMC_TIMING_UHS_DDR50 ||
-	    ios->timing == MMC_TIMING_MMC_DDR52)
-		ddr = 1;
-
 	msdc_set_buswidth(host, ios->bus_width);
 
 	/* Suspend/Resume will do power off/on */
@@ -1222,8 +1242,8 @@ static void msdc_ops_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
 		break;
 	}
 
-	if (host->mclk != ios->clock || host->ddr != ddr)
-		msdc_set_mclk(host, ddr, ios->clock);
+	if (host->mclk != ios->clock || host->timing != ios->timing)
+		msdc_set_mclk(host, ios->timing, ios->clock);
 
 end:
 	pm_runtime_mark_last_busy(host->dev);
@@ -1290,7 +1310,6 @@ static void msdc_tune_request(struct msdc_host *host)
 {
 	u32 orig_rsmpl, orig_cksel;
 	u32 cur_rsmpl, cur_cksel = 0;
-	u32 ddr, clkmode;
 	struct mmc_ios ios = host->mmc->ios;
 
 	sdr_get_field(host->base + MSDC_IOCON, MSDC_IOCON_RSPL, &orig_rsmpl);
@@ -1318,9 +1337,38 @@ static void msdc_tune_request(struct msdc_host *host)
 		sdr_set_field(host->base + MSDC_IOCON, MSDC_IOCON_RSPL, 0);
 		sdr_set_field(host->base + MSDC_PATCH_BIT,
 			      MSDC_CKGEN_MSDC_DLY_SEL, 0);
-		sdr_get_field(host->base + MSDC_CFG, MSDC_CFG_CKMOD, &clkmode);
-		ddr = (clkmode == 2) ? 1 : 0;
-		msdc_set_mclk(host, ddr, host->mclk / 2);
+		msdc_set_mclk(host, host->timing, host->mclk / 2);
+	}
+}
+
+static void emmc_hs400_tune_rw(struct msdc_host *host)
+{
+	int cur_ds_dly1, cur_ds_dly3, orig_ds_dly1, orig_ds_dly3;
+
+	sdr_get_field(host->base + PAD_DS_TUNE, PAD_DS_TUNE_DLY1, &orig_ds_dly1);
+	sdr_get_field(host->base + PAD_DS_TUNE, PAD_DS_TUNE_DLY3, &orig_ds_dly3);
+
+	cur_ds_dly1 = orig_ds_dly1 - 1;
+	cur_ds_dly3 = orig_ds_dly3;
+	if (cur_ds_dly1 < 0) {
+		cur_ds_dly1 = 31;
+		cur_ds_dly3 = orig_ds_dly3 + 1;
+		if (cur_ds_dly3 >= 32)
+			cur_ds_dly3 = 0;
+	}
+
+	if (++host->tune_hs400_rw_counter >= 32 * 32) {
+		dev_warn(host->dev, "HS400 tune fail at %dhz\n", host->mclk);
+		host->tune_hs400_rw_counter = 0;
+		msdc_set_mclk(host, host->timing, host->mclk / 2);
+	} else {
+		sdr_set_field(host->base + PAD_DS_TUNE, PAD_DS_TUNE_DLY1, cur_ds_dly1);
+		if (cur_ds_dly3 != orig_ds_dly3) {
+			sdr_set_field(host->base + PAD_DS_TUNE, PAD_DS_TUNE_DLY3,
+				      cur_ds_dly3);
+		}
+		dev_dbg(host->dev, "cur_ds_dly1<0x%x>, cur_ds_dly3<0x%x>\n",
+				cur_ds_dly1, cur_ds_dly3);
 	}
 }
 
@@ -1336,10 +1384,15 @@ static void msdc_repeat_request(struct work_struct *work)
 	spin_unlock_irqrestore(&host->lock, flags);
 	msdc_send_stop(host);
 	msdc_wait_card_not_busy(host);
-	if (mrq->data && mrq->data->error == -ETIMEDOUT)
+	if (mrq->data && mrq->data->error == -ETIMEDOUT) {
 		mmc_hw_reset(host->mmc);
-	else
-		msdc_tune_request(host);
+	} else {
+		if (mrq->data && mrq->data->error == -EILSEQ &&
+		    host->timing == MMC_TIMING_MMC_HS400)
+			emmc_hs400_tune_rw(host);
+		else
+			msdc_tune_request(host);
+	}
 	msdc_reset_mrq(mrq);
 	msdc_ops_request(host->mmc, mrq);
 }
@@ -1389,6 +1442,13 @@ static int msdc_drv_probe(struct platform_device *pdev)
 	if (IS_ERR(host->src_clk)) {
 		ret = PTR_ERR(host->src_clk);
 		goto host_free;
+	} else {
+		host->src_clk_parent = clk_get_parent(host->src_clk);
+		host->hs400_src = devm_clk_get(&pdev->dev, "400Mhz_clk");
+		if (IS_ERR(host->hs400_src))
+			dev_dbg(&pdev->dev, "Cannot find 400Mhz_clk at dts!\n");
+		else if (clk_set_parent(host->src_clk_parent, host->hs400_src) < 0)
+				dev_err(host->dev, "Failed to set 400Mhz source clock!\n");
 	}
 
 	host->h_clk = devm_clk_get(&pdev->dev, "hclk");
@@ -1541,6 +1601,8 @@ static void msdc_save_reg(struct msdc_host *host)
 	host->save_para.pad_tune = readl(host->base + MSDC_PAD_TUNE);
 	host->save_para.patch_bit0 = readl(host->base + MSDC_PATCH_BIT);
 	host->save_para.patch_bit1 = readl(host->base + MSDC_PATCH_BIT1);
+	host->save_para.pad_ds_tune = readl(host->base + PAD_DS_TUNE);
+	host->save_para.emmc50_cfg0 = readl(host->base + EMMC50_CFG0);
 }
 
 static void msdc_restore_reg(struct msdc_host *host)
@@ -1551,6 +1613,8 @@ static void msdc_restore_reg(struct msdc_host *host)
 	writel(host->save_para.pad_tune, host->base + MSDC_PAD_TUNE);
 	writel(host->save_para.patch_bit0, host->base + MSDC_PATCH_BIT);
 	writel(host->save_para.patch_bit1, host->base + MSDC_PATCH_BIT1);
+	writel(host->save_para.pad_ds_tune, host->base + PAD_DS_TUNE);
+	writel(host->save_para.emmc50_cfg0, host->base + EMMC50_CFG0);
 }
 
 static int msdc_runtime_suspend(struct device *dev)
-- 
1.8.1.1.dirty

^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [PATCH 3/4] arm64: dts: mediatek: Support SD/EMMC SDR104/HS200/HS400
       [not found] ` <1439367845-5891-1-git-send-email-chaotian.jing-NuS5LvNUpcJWk0Htik3J/w@public.gmane.org>
  2015-08-12  8:24   ` [PATCH 1/4] mmc: mediatek: Add online-tuning support of EMMC/SD Chaotian Jing
  2015-08-12  8:24   ` [PATCH 2/4] mmc: mediatek: Add HS400 support Chaotian Jing
@ 2015-08-12  8:24   ` Chaotian Jing
  2015-08-12  8:24   ` [PATCH 4/4] mmc: dt-bindings: Add 400Mhz clock source Chaotian Jing
  3 siblings, 0 replies; 10+ messages in thread
From: Chaotian Jing @ 2015-08-12  8:24 UTC (permalink / raw)
  To: Rob Herring, Matthias Brugger, Chris Ball, Ulf Hansson
  Cc: Mark Rutland, James Liao, Catalin Marinas, Wenbin Mei,
	Will Deacon, Russell King - ARM Linux, Hongzhou Yang,
	Chaotian Jing, Joe.C, devicetree-u79uwXL29TY76Z2rM5mHXA,
	Arnd Bergmann, bin.zhang-NuS5LvNUpcJWk0Htik3J/w,
	linux-gpio-u79uwXL29TY76Z2rM5mHXA,
	linux-mediatek-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, Eddie Huang,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, Liuquan Ji,
	srv_heupstream-NuS5LvNUpcJWk0Htik3J/w,
	linux-mmc-u79uwXL29TY76Z2rM5mHXA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA, Yong Mao, Sascha Hauer

Add 400Mhz source clock for EMMC HS400 mode
Support EMMC DDR50/HS200/HS400 of mt8173-evb
Support SD SDR25/SDR50/DDR50/SDR104 of mt8173-evb

Signed-off-by: Chaotian Jing <chaotian.jing-NuS5LvNUpcJWk0Htik3J/w@public.gmane.org>
---
 arch/arm64/boot/dts/mediatek/mt8173-evb.dts | 10 ++++++++--
 arch/arm64/boot/dts/mediatek/mt8173.dtsi    |  5 +++--
 2 files changed, 11 insertions(+), 4 deletions(-)

diff --git a/arch/arm64/boot/dts/mediatek/mt8173-evb.dts b/arch/arm64/boot/dts/mediatek/mt8173-evb.dts
index 4be66ca..cd5317f 100644
--- a/arch/arm64/boot/dts/mediatek/mt8173-evb.dts
+++ b/arch/arm64/boot/dts/mediatek/mt8173-evb.dts
@@ -70,8 +70,11 @@
 	pinctrl-0 = <&mmc0_pins_default>;
 	pinctrl-1 = <&mmc0_pins_uhs>;
 	bus-width = <8>;
-	max-frequency = <50000000>;
+	max-frequency = <200000000>;
 	cap-mmc-highspeed;
+	mmc-ddr-1_8v;
+	mmc-hs200-1_8v;
+	mmc-hs400-1_8v;
 	vmmc-supply = <&mt6397_vemc_3v3_reg>;
 	vqmmc-supply = <&mt6397_vio18_reg>;
 	non-removable;
@@ -83,9 +86,12 @@
 	pinctrl-0 = <&mmc1_pins_default>;
 	pinctrl-1 = <&mmc1_pins_uhs>;
 	bus-width = <4>;
-	max-frequency = <50000000>;
+	max-frequency = <200000000>;
 	cap-sd-highspeed;
 	sd-uhs-sdr25;
+	sd-uhs-sdr50;
+	sd-uhs-ddr50;
+	sd-uhs-sdr104;
 	cd-gpios = <&pio 132 0>;
 	vmmc-supply = <&mt6397_vmch_reg>;
 	vqmmc-supply = <&mt6397_vmc_reg>;
diff --git a/arch/arm64/boot/dts/mediatek/mt8173.dtsi b/arch/arm64/boot/dts/mediatek/mt8173.dtsi
index d18ee42..495ed94 100644
--- a/arch/arm64/boot/dts/mediatek/mt8173.dtsi
+++ b/arch/arm64/boot/dts/mediatek/mt8173.dtsi
@@ -450,8 +450,9 @@
 			reg = <0 0x11230000 0 0x1000>;
 			interrupts = <GIC_SPI 71 IRQ_TYPE_LEVEL_LOW>;
 			clocks = <&pericfg CLK_PERI_MSDC30_0>,
-				 <&topckgen CLK_TOP_MSDC50_0_H_SEL>;
-			clock-names = "source", "hclk";
+				 <&topckgen CLK_TOP_MSDC50_0_H_SEL>,
+				 <&topckgen CLK_TOP_MSDCPLL_D2>;
+			clock-names = "source", "hclk", "400Mhz_clk";
 			status = "disabled";
 		};
 
-- 
1.8.1.1.dirty

^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [PATCH 4/4] mmc: dt-bindings:  Add 400Mhz clock source
       [not found] ` <1439367845-5891-1-git-send-email-chaotian.jing-NuS5LvNUpcJWk0Htik3J/w@public.gmane.org>
                     ` (2 preceding siblings ...)
  2015-08-12  8:24   ` [PATCH 3/4] arm64: dts: mediatek: Support SD/EMMC SDR104/HS200/HS400 Chaotian Jing
@ 2015-08-12  8:24   ` Chaotian Jing
  3 siblings, 0 replies; 10+ messages in thread
From: Chaotian Jing @ 2015-08-12  8:24 UTC (permalink / raw)
  To: Rob Herring, Matthias Brugger, Chris Ball, Ulf Hansson
  Cc: Mark Rutland, James Liao, Catalin Marinas, Wenbin Mei,
	Will Deacon, Russell King - ARM Linux, Hongzhou Yang,
	Chaotian Jing, Joe.C, devicetree-u79uwXL29TY76Z2rM5mHXA,
	Arnd Bergmann, bin.zhang-NuS5LvNUpcJWk0Htik3J/w,
	linux-gpio-u79uwXL29TY76Z2rM5mHXA,
	linux-mediatek-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, Eddie Huang,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, Liuquan Ji,
	srv_heupstream-NuS5LvNUpcJWk0Htik3J/w,
	linux-mmc-u79uwXL29TY76Z2rM5mHXA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA, Yong Mao, Sascha Hauer

Add 400Mhz clock source for HS400 mode

Signed-off-by: Chaotian Jing <chaotian.jing-NuS5LvNUpcJWk0Htik3J/w@public.gmane.org>
---
 Documentation/devicetree/bindings/mmc/mtk-sd.txt | 10 ++++++++--
 1 file changed, 8 insertions(+), 2 deletions(-)

diff --git a/Documentation/devicetree/bindings/mmc/mtk-sd.txt b/Documentation/devicetree/bindings/mmc/mtk-sd.txt
index a1adfa4..2c28305 100644
--- a/Documentation/devicetree/bindings/mmc/mtk-sd.txt
+++ b/Documentation/devicetree/bindings/mmc/mtk-sd.txt
@@ -17,6 +17,10 @@ Required properties:
 - vmmc-supply: power to the Core
 - vqmmc-supply: power to the IO
 
+Optional properties:
+- clocks: 400Mhz clk, used for HS400 mode, 400Mhz source clock
+- clock-name: "400Mhz_clk"
+
 Examples:
 mmc0: mmc@11230000 {
 	compatible = "mediatek,mt8173-mmc", "mediatek,mt8135-mmc";
@@ -24,8 +28,10 @@ mmc0: mmc@11230000 {
 	interrupts = <GIC_SPI 39 IRQ_TYPE_LEVEL_LOW>;
 	vmmc-supply = <&mt6397_vemc_3v3_reg>;
 	vqmmc-supply = <&mt6397_vio18_reg>;
-	clocks = <&pericfg CLK_PERI_MSDC30_0>, <&topckgen CLK_TOP_MSDC50_0_H_SEL>;
-	clock-names = "source", "hclk";
+	clocks = <&pericfg CLK_PERI_MSDC30_0>,
+	         <&topckgen CLK_TOP_MSDC50_0_H_SEL>,
+	         <&topckgen CLK_TOP_MSDCPLL_D2>;
+	clock-names = "source", "hclk", "400Mhz_clk";
 	pinctrl-names = "default", "state_uhs";
 	pinctrl-0 = <&mmc0_pins_default>;
 	pinctrl-1 = <&mmc0_pins_uhs>;
-- 
1.8.1.1.dirty

^ permalink raw reply related	[flat|nested] 10+ messages in thread

* Re: [PATCH 1/4] mmc: mediatek: Add online-tuning support of EMMC/SD
  2015-08-12  8:24   ` [PATCH 1/4] mmc: mediatek: Add online-tuning support of EMMC/SD Chaotian Jing
@ 2015-08-17 11:31     ` Ulf Hansson
       [not found]       ` <CAPDyKFpX4Pp1+U9eNBuGd83sBeBgLU17qysAJ-2V-ABZZHYAGQ-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
  0 siblings, 1 reply; 10+ messages in thread
From: Ulf Hansson @ 2015-08-17 11:31 UTC (permalink / raw)
  To: Chaotian Jing
  Cc: Rob Herring, Matthias Brugger, Chris Ball, Mark Rutland,
	James Liao, srv_heupstream, Arnd Bergmann,
	devicetree@vger.kernel.org, Hongzhou Yang, Catalin Marinas,
	linux-mmc, Will Deacon, linux-kernel@vger.kernel.org,
	linux-gpio@vger.kernel.org, Sascha Hauer, Joe.C, Eddie Huang,
	Bin Zhang (章斌),
	linux-arm-kernel@lists.infradead.org

On 12 August 2015 at 10:24, Chaotian Jing <chaotian.jing@mediatek.com> wrote:
> Schedule a workqueue to do tuning when CRC error
> Call mmc_hw_reset to re-init card when data timeout

Thanks to Adrian Hunter, the mmc core already supports re-tuning for
the above scenarios through the mmc_retune_*() APIs.

SDHCI driver has already adopted to use that feature, you should do
that for the mtk-sd driver as well.

Kind regards
Uffe

>
> Signed-off-by: Chaotian Jing <chaotian.jing@mediatek.com>
> ---
>  drivers/mmc/host/mtk-sd.c | 162 ++++++++++++++++++++++++++++++++++++++++++----
>  1 file changed, 150 insertions(+), 12 deletions(-)
>
> diff --git a/drivers/mmc/host/mtk-sd.c b/drivers/mmc/host/mtk-sd.c
> index 7153500..eb44fe1 100644
> --- a/drivers/mmc/host/mtk-sd.c
> +++ b/drivers/mmc/host/mtk-sd.c
> @@ -27,6 +27,7 @@
>  #include <linux/pm_runtime.h>
>  #include <linux/regulator/consumer.h>
>  #include <linux/spinlock.h>
> +#include <linux/workqueue.h>
>
>  #include <linux/mmc/card.h>
>  #include <linux/mmc/core.h>
> @@ -290,6 +291,10 @@ struct msdc_host {
>         struct pinctrl_state *pins_default;
>         struct pinctrl_state *pins_uhs;
>         struct delayed_work req_timeout;
> +       struct workqueue_struct *repeat_workqueue;
> +       struct work_struct repeat_req;
> +       struct mmc_request *repeat_mrq;
> +       u32 tune_cmd_counter;
>         int irq;                /* host interrupt */
>
>         struct clk *src_clk;    /* msdc source clock */
> @@ -353,7 +358,10 @@ static void msdc_reset_hw(struct msdc_host *host)
>  static void msdc_cmd_next(struct msdc_host *host,
>                 struct mmc_request *mrq, struct mmc_command *cmd);
>
> -static u32 data_ints_mask = MSDC_INTEN_XFER_COMPL | MSDC_INTEN_DATTMO |
> +static const u32 cmd_ints_mask = MSDC_INTEN_CMDRDY | MSDC_INTEN_RSPCRCERR |
> +                       MSDC_INTEN_CMDTMO | MSDC_INTEN_ACMDRDY |
> +                       MSDC_INTEN_ACMDCRCERR | MSDC_INTEN_ACMDTMO;
> +static const u32 data_ints_mask = MSDC_INTEN_XFER_COMPL | MSDC_INTEN_DATTMO |
>                         MSDC_INTEN_DATCRCERR | MSDC_INTEN_DMA_BDCSERR |
>                         MSDC_INTEN_DMA_GPDCSERR | MSDC_INTEN_DMA_PROTECT;
>
> @@ -690,6 +698,18 @@ static void msdc_request_done(struct msdc_host *host, struct mmc_request *mrq)
>         msdc_track_cmd_data(host, mrq->cmd, mrq->data);
>         if (mrq->data)
>                 msdc_unprepare_data(host, mrq);
> +       if (host->error && host->mmc->card &&
> +           !mmc_card_sdio(host->mmc->card)) {
> +               if (mrq->cmd->error == (unsigned int)-EILSEQ ||
> +                   (mrq->stop && mrq->stop->error == (unsigned int)-EILSEQ) ||
> +                   (mrq->sbc && mrq->sbc->error == (unsigned int)-EILSEQ) ||
> +                   (mrq->data && mrq->data->error))  {
> +                       host->repeat_mrq = mrq;
> +                       queue_work(host->repeat_workqueue, &host->repeat_req);
> +                       return;
> +               }
> +       }
> +       host->tune_cmd_counter = 0;
>         mmc_request_done(host->mmc, mrq);
>
>         pm_runtime_mark_last_busy(host->dev);
> @@ -725,11 +745,7 @@ static bool msdc_cmd_done(struct msdc_host *host, int events,
>         if (done)
>                 return true;
>
> -       sdr_clr_bits(host->base + MSDC_INTEN, MSDC_INTEN_CMDRDY |
> -                       MSDC_INTEN_RSPCRCERR | MSDC_INTEN_CMDTMO |
> -                       MSDC_INTEN_ACMDRDY | MSDC_INTEN_ACMDCRCERR |
> -                       MSDC_INTEN_ACMDTMO);
> -       writel(cmd->arg, host->base + SDC_ARG);
> +       sdr_clr_bits(host->base + MSDC_INTEN, cmd_ints_mask);
>
>         if (cmd->flags & MMC_RSP_PRESENT) {
>                 if (cmd->flags & MMC_RSP_136) {
> @@ -819,10 +835,7 @@ static void msdc_start_command(struct msdc_host *host,
>         rawcmd = msdc_cmd_prepare_raw_cmd(host, mrq, cmd);
>         mod_delayed_work(system_wq, &host->req_timeout, DAT_TIMEOUT);
>
> -       sdr_set_bits(host->base + MSDC_INTEN, MSDC_INTEN_CMDRDY |
> -                       MSDC_INTEN_RSPCRCERR | MSDC_INTEN_CMDTMO |
> -                       MSDC_INTEN_ACMDRDY | MSDC_INTEN_ACMDCRCERR |
> -                       MSDC_INTEN_ACMDTMO);
> +       sdr_set_bits(host->base + MSDC_INTEN, cmd_ints_mask);
>         writel(cmd->arg, host->base + SDC_ARG);
>         writel(rawcmd, host->base + SDC_CMD);
>  }
> @@ -942,6 +955,8 @@ static bool msdc_data_xfer_done(struct msdc_host *host, u32 events,
>
>                         if (events & MSDC_INT_DATTMO)
>                                 data->error = -ETIMEDOUT;
> +                       else if (events & MSDC_INT_DATCRCERR)
> +                               data->error = -EILSEQ;
>
>                         dev_err(host->dev, "%s: cmd=%d; blocks=%d",
>                                 __func__, mrq->cmd->opcode, data->blocks);
> @@ -1113,8 +1128,8 @@ static void msdc_init_hw(struct msdc_host *host)
>
>         writel(0, host->base + MSDC_PAD_TUNE);
>         writel(0, host->base + MSDC_IOCON);
> -       sdr_set_field(host->base + MSDC_IOCON, MSDC_IOCON_DDLSEL, 1);
> -       writel(0x403c004f, host->base + MSDC_PATCH_BIT);
> +       sdr_set_field(host->base + MSDC_IOCON, MSDC_IOCON_DDLSEL, 0);
> +       writel(0x403c0046, host->base + MSDC_PATCH_BIT);
>         sdr_set_field(host->base + MSDC_PATCH_BIT, MSDC_CKGEN_MSDC_DLY_SEL, 1);
>         writel(0xffff0089, host->base + MSDC_PATCH_BIT1);
>         /* Configure to enable SDIO mode.
> @@ -1176,6 +1191,7 @@ static void msdc_ops_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
>         switch (ios->power_mode) {
>         case MMC_POWER_UP:
>                 if (!IS_ERR(mmc->supply.vmmc)) {
> +                       msdc_init_hw(host);
>                         ret = mmc_regulator_set_ocr(mmc, mmc->supply.vmmc,
>                                         ios->vdd);
>                         if (ret) {
> @@ -1214,6 +1230,120 @@ end:
>         pm_runtime_put_autosuspend(host->dev);
>  }
>
> +static void msdc_reset_mrq(struct mmc_request *mrq)
> +{
> +       mrq->cmd->error = 0;
> +       if (mrq->sbc)
> +               mrq->sbc->error = 0;
> +       if (mrq->data)
> +               mrq->data->error = 0;
> +       if (mrq->stop)
> +               mrq->stop->error = 0;
> +}
> +
> +/* Send CMD12 when tuning, do not check CRC error and timeout */
> +static void msdc_send_stop(struct msdc_host *host)
> +{
> +       u32 opcode = MMC_STOP_TRANSMISSION;
> +       u32 arg = 0;
> +       u32 rawcmd = 0;
> +       u32 intsts = 0;
> +
> +       /* Reset host first */
> +       msdc_reset_hw(host);
> +
> +       rawcmd = (opcode & 0x3f) | (7 << 7);
> +       rawcmd |= (1 << 14); /* stop cmd */
> +
> +       sdr_clr_bits(host->base + MSDC_INTEN, cmd_ints_mask);
> +       writel(arg, host->base + SDC_ARG);
> +       writel(rawcmd, host->base + SDC_CMD);
> +
> +       while (1) {
> +               intsts = readl(host->base + MSDC_INT);
> +               if (intsts) {
> +                       writel(intsts, host->base + MSDC_INT);
> +                       if (intsts & cmd_ints_mask) {
> +                               dev_dbg(host->dev, "result of cmd12: %x\n",
> +                                               intsts);
> +                               break;
> +                       }
> +               }
> +               udelay(1);
> +       }
> +}
> +
> +/* When tuning, CMD13 may also get crc error, so use MSDC_PS to get card status */
> +static void msdc_wait_card_not_busy(struct msdc_host *host)
> +{
> +       while (1) {
> +               if ((readl(host->base + MSDC_PS) & BIT(16)) == 0) { /* check dat0 status */
> +                       msleep_interruptible(10);
> +                       dev_dbg(host->dev, "MSDC_PS: %08x, SDC_STS: %08x\n",
> +                               readl(host->base + MSDC_PS), readl(host->base + SDC_STS));
> +               } else
> +                       break;
> +       }
> +}
> +
> +static void msdc_tune_request(struct msdc_host *host)
> +{
> +       u32 orig_rsmpl, orig_cksel;
> +       u32 cur_rsmpl, cur_cksel = 0;
> +       u32 ddr, clkmode;
> +       struct mmc_ios ios = host->mmc->ios;
> +
> +       sdr_get_field(host->base + MSDC_IOCON, MSDC_IOCON_RSPL, &orig_rsmpl);
> +       sdr_get_field(host->base + MSDC_PATCH_BIT, MSDC_CKGEN_MSDC_DLY_SEL,
> +                     &orig_cksel);
> +       cur_rsmpl = (orig_rsmpl + 1);
> +       sdr_set_field(host->base + MSDC_IOCON, MSDC_IOCON_RSPL, cur_rsmpl % 2);
> +
> +       if (ios.timing != MMC_TIMING_MMC_HS400) {
> +               sdr_set_field(host->base + MSDC_IOCON, MSDC_IOCON_DSPL,
> +                             cur_rsmpl % 2);
> +               sdr_set_field(host->base + MSDC_IOCON, MSDC_IOCON_W_DSPL,
> +                             cur_rsmpl % 2);
> +       }
> +
> +       if (cur_rsmpl >= 2) {
> +               cur_cksel = orig_cksel + 1;
> +               sdr_set_field(host->base + MSDC_PATCH_BIT,
> +                             MSDC_CKGEN_MSDC_DLY_SEL, cur_cksel % 16);
> +       }
> +
> +       if (host->tune_cmd_counter++ >= 2 * 16) {
> +               dev_warn(host->dev, "Tune fail at %dhz\n", host->mclk);
> +               host->tune_cmd_counter = 0;
> +               sdr_set_field(host->base + MSDC_IOCON, MSDC_IOCON_RSPL, 0);
> +               sdr_set_field(host->base + MSDC_PATCH_BIT,
> +                             MSDC_CKGEN_MSDC_DLY_SEL, 0);
> +               sdr_get_field(host->base + MSDC_CFG, MSDC_CFG_CKMOD, &clkmode);
> +               ddr = (clkmode == 2) ? 1 : 0;
> +               msdc_set_mclk(host, ddr, host->mclk / 2);
> +       }
> +}
> +
> +static void msdc_repeat_request(struct work_struct *work)
> +{
> +       struct msdc_host *host = container_of(work, struct msdc_host, repeat_req);
> +       struct mmc_request *mrq;
> +       unsigned long flags;
> +
> +       spin_lock_irqsave(&host->lock, flags);
> +       mrq = host->repeat_mrq;
> +       host->repeat_mrq = NULL;
> +       spin_unlock_irqrestore(&host->lock, flags);
> +       msdc_send_stop(host);
> +       msdc_wait_card_not_busy(host);
> +       if (mrq->data && mrq->data->error == -ETIMEDOUT)
> +               mmc_hw_reset(host->mmc);
> +       else
> +               msdc_tune_request(host);
> +       msdc_reset_mrq(mrq);
> +       msdc_ops_request(host->mmc, mrq);
> +}
> +
>  static struct mmc_host_ops mt_msdc_ops = {
>         .post_req = msdc_post_req,
>         .pre_req = msdc_pre_req,
> @@ -1324,6 +1454,12 @@ static int msdc_drv_probe(struct platform_device *pdev)
>         }
>         msdc_init_gpd_bd(host, &host->dma);
>         INIT_DELAYED_WORK(&host->req_timeout, msdc_request_timeout);
> +       host->repeat_workqueue = create_singlethread_workqueue("repeat_workqueue");
> +       if (!host->repeat_workqueue) {
> +               ret = -ENOMEM;
> +               goto release_mem;
> +       }
> +       INIT_WORK(&host->repeat_req, msdc_repeat_request);
>         spin_lock_init(&host->lock);
>
>         platform_set_drvdata(pdev, mmc);
> @@ -1348,6 +1484,7 @@ static int msdc_drv_probe(struct platform_device *pdev)
>  end:
>         pm_runtime_disable(host->dev);
>  release:
> +       destroy_workqueue(host->repeat_workqueue);
>         platform_set_drvdata(pdev, NULL);
>         msdc_deinit_hw(host);
>         msdc_gate_clock(host);
> @@ -1383,6 +1520,7 @@ static int msdc_drv_remove(struct platform_device *pdev)
>
>         pm_runtime_disable(host->dev);
>         pm_runtime_put_noidle(host->dev);
> +       destroy_workqueue(host->repeat_workqueue);
>         dma_free_coherent(&pdev->dev,
>                         sizeof(struct mt_gpdma_desc),
>                         host->dma.gpd, host->dma.gpd_addr);
> --
> 1.8.1.1.dirty
>

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH 1/4] mmc: mediatek: Add online-tuning support of EMMC/SD
       [not found]       ` <CAPDyKFpX4Pp1+U9eNBuGd83sBeBgLU17qysAJ-2V-ABZZHYAGQ-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
@ 2015-08-17 12:01         ` Chaotian Jing
  2015-08-25 12:07           ` Ulf Hansson
  0 siblings, 1 reply; 10+ messages in thread
From: Chaotian Jing @ 2015-08-17 12:01 UTC (permalink / raw)
  To: Ulf Hansson
  Cc: Mark Rutland, James Liao, Catalin Marinas, Wenbin Mei,
	Will Deacon, Chris Ball, Russell King - ARM Linux, Hongzhou Yang,
	Joe.C, devicetree-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
	Arnd Bergmann, Bin Zhang (章斌),
	linux-gpio-u79uwXL29TY76Z2rM5mHXA@public.gmane.org, Rob Herring,
	linux-mediatek-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, Matthias Brugger,
	Eddie Huang,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r@public.gmane.org,
	Liuquan Ji

Hi Ulf,
Thanks, please see my comment:
On Mon, 2015-08-17 at 13:31 +0200, Ulf Hansson wrote:
> On 12 August 2015 at 10:24, Chaotian Jing <chaotian.jing-NuS5LvNUpcJWk0Htik3J/w@public.gmane.org> wrote:
> > Schedule a workqueue to do tuning when CRC error
> > Call mmc_hw_reset to re-init card when data timeout
> 
> Thanks to Adrian Hunter, the mmc core already supports re-tuning for
> the above scenarios through the mmc_retune_*() APIs.
> 
> SDHCI driver has already adopted to use that feature, you should do
> that for the mtk-sd driver as well.
> 
> Kind regards
> Uffe
> 
I also noticed that the mmc core already supports re-tuning, but it is
not suitable for our host.
For EMMC, the CMD21 only support HS200/HS400 mode, for SD card, CMD19
only support SDR50/SDR104 mode, but in our host, even 50Mhz clock
frequency may also occur CRC error, Cannot find a parameter that can
cover all SD cards which running at 50Mhz, even 25Mhz, will occur CRC
error for stress test, DDR50 mode is worse.
By the way,there are too many tune parameters need try for response,
read data, write crc status CRC error, these parameters are
multidimensional, it is hard to find a best parameter, and, try
thousands of parameters will take long time.
> >
> > Signed-off-by: Chaotian Jing <chaotian.jing-NuS5LvNUpcJWk0Htik3J/w@public.gmane.org>
> > ---
> >  drivers/mmc/host/mtk-sd.c | 162 ++++++++++++++++++++++++++++++++++++++++++----
> >  1 file changed, 150 insertions(+), 12 deletions(-)
> >
> > diff --git a/drivers/mmc/host/mtk-sd.c b/drivers/mmc/host/mtk-sd.c
> > index 7153500..eb44fe1 100644
> > --- a/drivers/mmc/host/mtk-sd.c
> > +++ b/drivers/mmc/host/mtk-sd.c
> > @@ -27,6 +27,7 @@
> >  #include <linux/pm_runtime.h>
> >  #include <linux/regulator/consumer.h>
> >  #include <linux/spinlock.h>
> > +#include <linux/workqueue.h>
> >
> >  #include <linux/mmc/card.h>
> >  #include <linux/mmc/core.h>
> > @@ -290,6 +291,10 @@ struct msdc_host {
> >         struct pinctrl_state *pins_default;
> >         struct pinctrl_state *pins_uhs;
> >         struct delayed_work req_timeout;
> > +       struct workqueue_struct *repeat_workqueue;
> > +       struct work_struct repeat_req;
> > +       struct mmc_request *repeat_mrq;
> > +       u32 tune_cmd_counter;
> >         int irq;                /* host interrupt */
> >
> >         struct clk *src_clk;    /* msdc source clock */
> > @@ -353,7 +358,10 @@ static void msdc_reset_hw(struct msdc_host *host)
> >  static void msdc_cmd_next(struct msdc_host *host,
> >                 struct mmc_request *mrq, struct mmc_command *cmd);
> >
> > -static u32 data_ints_mask = MSDC_INTEN_XFER_COMPL | MSDC_INTEN_DATTMO |
> > +static const u32 cmd_ints_mask = MSDC_INTEN_CMDRDY | MSDC_INTEN_RSPCRCERR |
> > +                       MSDC_INTEN_CMDTMO | MSDC_INTEN_ACMDRDY |
> > +                       MSDC_INTEN_ACMDCRCERR | MSDC_INTEN_ACMDTMO;
> > +static const u32 data_ints_mask = MSDC_INTEN_XFER_COMPL | MSDC_INTEN_DATTMO |
> >                         MSDC_INTEN_DATCRCERR | MSDC_INTEN_DMA_BDCSERR |
> >                         MSDC_INTEN_DMA_GPDCSERR | MSDC_INTEN_DMA_PROTECT;
> >
> > @@ -690,6 +698,18 @@ static void msdc_request_done(struct msdc_host *host, struct mmc_request *mrq)
> >         msdc_track_cmd_data(host, mrq->cmd, mrq->data);
> >         if (mrq->data)
> >                 msdc_unprepare_data(host, mrq);
> > +       if (host->error && host->mmc->card &&
> > +           !mmc_card_sdio(host->mmc->card)) {
> > +               if (mrq->cmd->error == (unsigned int)-EILSEQ ||
> > +                   (mrq->stop && mrq->stop->error == (unsigned int)-EILSEQ) ||
> > +                   (mrq->sbc && mrq->sbc->error == (unsigned int)-EILSEQ) ||
> > +                   (mrq->data && mrq->data->error))  {
> > +                       host->repeat_mrq = mrq;
> > +                       queue_work(host->repeat_workqueue, &host->repeat_req);
> > +                       return;
> > +               }
> > +       }
> > +       host->tune_cmd_counter = 0;
> >         mmc_request_done(host->mmc, mrq);
> >
> >         pm_runtime_mark_last_busy(host->dev);
> > @@ -725,11 +745,7 @@ static bool msdc_cmd_done(struct msdc_host *host, int events,
> >         if (done)
> >                 return true;
> >
> > -       sdr_clr_bits(host->base + MSDC_INTEN, MSDC_INTEN_CMDRDY |
> > -                       MSDC_INTEN_RSPCRCERR | MSDC_INTEN_CMDTMO |
> > -                       MSDC_INTEN_ACMDRDY | MSDC_INTEN_ACMDCRCERR |
> > -                       MSDC_INTEN_ACMDTMO);
> > -       writel(cmd->arg, host->base + SDC_ARG);
> > +       sdr_clr_bits(host->base + MSDC_INTEN, cmd_ints_mask);
> >
> >         if (cmd->flags & MMC_RSP_PRESENT) {
> >                 if (cmd->flags & MMC_RSP_136) {
> > @@ -819,10 +835,7 @@ static void msdc_start_command(struct msdc_host *host,
> >         rawcmd = msdc_cmd_prepare_raw_cmd(host, mrq, cmd);
> >         mod_delayed_work(system_wq, &host->req_timeout, DAT_TIMEOUT);
> >
> > -       sdr_set_bits(host->base + MSDC_INTEN, MSDC_INTEN_CMDRDY |
> > -                       MSDC_INTEN_RSPCRCERR | MSDC_INTEN_CMDTMO |
> > -                       MSDC_INTEN_ACMDRDY | MSDC_INTEN_ACMDCRCERR |
> > -                       MSDC_INTEN_ACMDTMO);
> > +       sdr_set_bits(host->base + MSDC_INTEN, cmd_ints_mask);
> >         writel(cmd->arg, host->base + SDC_ARG);
> >         writel(rawcmd, host->base + SDC_CMD);
> >  }
> > @@ -942,6 +955,8 @@ static bool msdc_data_xfer_done(struct msdc_host *host, u32 events,
> >
> >                         if (events & MSDC_INT_DATTMO)
> >                                 data->error = -ETIMEDOUT;
> > +                       else if (events & MSDC_INT_DATCRCERR)
> > +                               data->error = -EILSEQ;
> >
> >                         dev_err(host->dev, "%s: cmd=%d; blocks=%d",
> >                                 __func__, mrq->cmd->opcode, data->blocks);
> > @@ -1113,8 +1128,8 @@ static void msdc_init_hw(struct msdc_host *host)
> >
> >         writel(0, host->base + MSDC_PAD_TUNE);
> >         writel(0, host->base + MSDC_IOCON);
> > -       sdr_set_field(host->base + MSDC_IOCON, MSDC_IOCON_DDLSEL, 1);
> > -       writel(0x403c004f, host->base + MSDC_PATCH_BIT);
> > +       sdr_set_field(host->base + MSDC_IOCON, MSDC_IOCON_DDLSEL, 0);
> > +       writel(0x403c0046, host->base + MSDC_PATCH_BIT);
> >         sdr_set_field(host->base + MSDC_PATCH_BIT, MSDC_CKGEN_MSDC_DLY_SEL, 1);
> >         writel(0xffff0089, host->base + MSDC_PATCH_BIT1);
> >         /* Configure to enable SDIO mode.
> > @@ -1176,6 +1191,7 @@ static void msdc_ops_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
> >         switch (ios->power_mode) {
> >         case MMC_POWER_UP:
> >                 if (!IS_ERR(mmc->supply.vmmc)) {
> > +                       msdc_init_hw(host);
> >                         ret = mmc_regulator_set_ocr(mmc, mmc->supply.vmmc,
> >                                         ios->vdd);
> >                         if (ret) {
> > @@ -1214,6 +1230,120 @@ end:
> >         pm_runtime_put_autosuspend(host->dev);
> >  }
> >
> > +static void msdc_reset_mrq(struct mmc_request *mrq)
> > +{
> > +       mrq->cmd->error = 0;
> > +       if (mrq->sbc)
> > +               mrq->sbc->error = 0;
> > +       if (mrq->data)
> > +               mrq->data->error = 0;
> > +       if (mrq->stop)
> > +               mrq->stop->error = 0;
> > +}
> > +
> > +/* Send CMD12 when tuning, do not check CRC error and timeout */
> > +static void msdc_send_stop(struct msdc_host *host)
> > +{
> > +       u32 opcode = MMC_STOP_TRANSMISSION;
> > +       u32 arg = 0;
> > +       u32 rawcmd = 0;
> > +       u32 intsts = 0;
> > +
> > +       /* Reset host first */
> > +       msdc_reset_hw(host);
> > +
> > +       rawcmd = (opcode & 0x3f) | (7 << 7);
> > +       rawcmd |= (1 << 14); /* stop cmd */
> > +
> > +       sdr_clr_bits(host->base + MSDC_INTEN, cmd_ints_mask);
> > +       writel(arg, host->base + SDC_ARG);
> > +       writel(rawcmd, host->base + SDC_CMD);
> > +
> > +       while (1) {
> > +               intsts = readl(host->base + MSDC_INT);
> > +               if (intsts) {
> > +                       writel(intsts, host->base + MSDC_INT);
> > +                       if (intsts & cmd_ints_mask) {
> > +                               dev_dbg(host->dev, "result of cmd12: %x\n",
> > +                                               intsts);
> > +                               break;
> > +                       }
> > +               }
> > +               udelay(1);
> > +       }
> > +}
> > +
> > +/* When tuning, CMD13 may also get crc error, so use MSDC_PS to get card status */
> > +static void msdc_wait_card_not_busy(struct msdc_host *host)
> > +{
> > +       while (1) {
> > +               if ((readl(host->base + MSDC_PS) & BIT(16)) == 0) { /* check dat0 status */
> > +                       msleep_interruptible(10);
> > +                       dev_dbg(host->dev, "MSDC_PS: %08x, SDC_STS: %08x\n",
> > +                               readl(host->base + MSDC_PS), readl(host->base + SDC_STS));
> > +               } else
> > +                       break;
> > +       }
> > +}
> > +
> > +static void msdc_tune_request(struct msdc_host *host)
> > +{
> > +       u32 orig_rsmpl, orig_cksel;
> > +       u32 cur_rsmpl, cur_cksel = 0;
> > +       u32 ddr, clkmode;
> > +       struct mmc_ios ios = host->mmc->ios;
> > +
> > +       sdr_get_field(host->base + MSDC_IOCON, MSDC_IOCON_RSPL, &orig_rsmpl);
> > +       sdr_get_field(host->base + MSDC_PATCH_BIT, MSDC_CKGEN_MSDC_DLY_SEL,
> > +                     &orig_cksel);
> > +       cur_rsmpl = (orig_rsmpl + 1);
> > +       sdr_set_field(host->base + MSDC_IOCON, MSDC_IOCON_RSPL, cur_rsmpl % 2);
> > +
> > +       if (ios.timing != MMC_TIMING_MMC_HS400) {
> > +               sdr_set_field(host->base + MSDC_IOCON, MSDC_IOCON_DSPL,
> > +                             cur_rsmpl % 2);
> > +               sdr_set_field(host->base + MSDC_IOCON, MSDC_IOCON_W_DSPL,
> > +                             cur_rsmpl % 2);
> > +       }
> > +
> > +       if (cur_rsmpl >= 2) {
> > +               cur_cksel = orig_cksel + 1;
> > +               sdr_set_field(host->base + MSDC_PATCH_BIT,
> > +                             MSDC_CKGEN_MSDC_DLY_SEL, cur_cksel % 16);
> > +       }
> > +
> > +       if (host->tune_cmd_counter++ >= 2 * 16) {
> > +               dev_warn(host->dev, "Tune fail at %dhz\n", host->mclk);
> > +               host->tune_cmd_counter = 0;
> > +               sdr_set_field(host->base + MSDC_IOCON, MSDC_IOCON_RSPL, 0);
> > +               sdr_set_field(host->base + MSDC_PATCH_BIT,
> > +                             MSDC_CKGEN_MSDC_DLY_SEL, 0);
> > +               sdr_get_field(host->base + MSDC_CFG, MSDC_CFG_CKMOD, &clkmode);
> > +               ddr = (clkmode == 2) ? 1 : 0;
> > +               msdc_set_mclk(host, ddr, host->mclk / 2);
> > +       }
> > +}
> > +
> > +static void msdc_repeat_request(struct work_struct *work)
> > +{
> > +       struct msdc_host *host = container_of(work, struct msdc_host, repeat_req);
> > +       struct mmc_request *mrq;
> > +       unsigned long flags;
> > +
> > +       spin_lock_irqsave(&host->lock, flags);
> > +       mrq = host->repeat_mrq;
> > +       host->repeat_mrq = NULL;
> > +       spin_unlock_irqrestore(&host->lock, flags);
> > +       msdc_send_stop(host);
> > +       msdc_wait_card_not_busy(host);
> > +       if (mrq->data && mrq->data->error == -ETIMEDOUT)
> > +               mmc_hw_reset(host->mmc);
> > +       else
> > +               msdc_tune_request(host);
> > +       msdc_reset_mrq(mrq);
> > +       msdc_ops_request(host->mmc, mrq);
> > +}
> > +
> >  static struct mmc_host_ops mt_msdc_ops = {
> >         .post_req = msdc_post_req,
> >         .pre_req = msdc_pre_req,
> > @@ -1324,6 +1454,12 @@ static int msdc_drv_probe(struct platform_device *pdev)
> >         }
> >         msdc_init_gpd_bd(host, &host->dma);
> >         INIT_DELAYED_WORK(&host->req_timeout, msdc_request_timeout);
> > +       host->repeat_workqueue = create_singlethread_workqueue("repeat_workqueue");
> > +       if (!host->repeat_workqueue) {
> > +               ret = -ENOMEM;
> > +               goto release_mem;
> > +       }
> > +       INIT_WORK(&host->repeat_req, msdc_repeat_request);
> >         spin_lock_init(&host->lock);
> >
> >         platform_set_drvdata(pdev, mmc);
> > @@ -1348,6 +1484,7 @@ static int msdc_drv_probe(struct platform_device *pdev)
> >  end:
> >         pm_runtime_disable(host->dev);
> >  release:
> > +       destroy_workqueue(host->repeat_workqueue);
> >         platform_set_drvdata(pdev, NULL);
> >         msdc_deinit_hw(host);
> >         msdc_gate_clock(host);
> > @@ -1383,6 +1520,7 @@ static int msdc_drv_remove(struct platform_device *pdev)
> >
> >         pm_runtime_disable(host->dev);
> >         pm_runtime_put_noidle(host->dev);
> > +       destroy_workqueue(host->repeat_workqueue);
> >         dma_free_coherent(&pdev->dev,
> >                         sizeof(struct mt_gpdma_desc),
> >                         host->dma.gpd, host->dma.gpd_addr);
> > --
> > 1.8.1.1.dirty
> >

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH 1/4] mmc: mediatek: Add online-tuning support of EMMC/SD
  2015-08-17 12:01         ` Chaotian Jing
@ 2015-08-25 12:07           ` Ulf Hansson
       [not found]             ` <CAPDyKFpGk9gcLLjBnN=+BD5WP-7dZ0+XHC1bcEEGGLvLbTyKHw-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
  0 siblings, 1 reply; 10+ messages in thread
From: Ulf Hansson @ 2015-08-25 12:07 UTC (permalink / raw)
  To: Chaotian Jing
  Cc: Rob Herring, Matthias Brugger, Chris Ball, Mark Rutland,
	James Liao, srv_heupstream, Arnd Bergmann,
	devicetree@vger.kernel.org, Hongzhou Yang, Catalin Marinas,
	linux-mmc, Will Deacon, linux-kernel@vger.kernel.org,
	linux-gpio@vger.kernel.org, Sascha Hauer, Joe.C, Eddie Huang,
	Bin Zhang (章斌),
	linux-arm-kernel@lists.infradead.org

On 17 August 2015 at 14:01, Chaotian Jing <chaotian.jing@mediatek.com> wrote:
> Hi Ulf,
> Thanks, please see my comment:
> On Mon, 2015-08-17 at 13:31 +0200, Ulf Hansson wrote:
>> On 12 August 2015 at 10:24, Chaotian Jing <chaotian.jing@mediatek.com> wrote:
>> > Schedule a workqueue to do tuning when CRC error
>> > Call mmc_hw_reset to re-init card when data timeout
>>
>> Thanks to Adrian Hunter, the mmc core already supports re-tuning for
>> the above scenarios through the mmc_retune_*() APIs.
>>
>> SDHCI driver has already adopted to use that feature, you should do
>> that for the mtk-sd driver as well.
>>
>> Kind regards
>> Uffe
>>
> I also noticed that the mmc core already supports re-tuning, but it is
> not suitable for our host.
> For EMMC, the CMD21 only support HS200/HS400 mode, for SD card, CMD19
> only support SDR50/SDR104 mode, but in our host, even 50Mhz clock
> frequency may also occur CRC error, Cannot find a parameter that can
> cover all SD cards which running at 50Mhz, even 25Mhz, will occur CRC
> error for stress test, DDR50 mode is worse.

I don't follow. You may run for example HS200 in lower speed, nothing
will prevent tuning and re-tuning from happen for these scenarios.

Or you are talking about other speed modes than HS200/400 and
SDR50/104? If so, which speed modes are these?

BTW, there are currently a patch being discussed which is about adding
tuning for DDR mode. Please have look.
http://www.spinics.net/lists/arm-kernel/msg438434.html

Regarding re-tuning on CRC errors, that's already supported by the mmc
core. More precisely when a host driver returns -EILSEQ for a request.

> By the way,there are too many tune parameters need try for response,
> read data, write crc status CRC error, these parameters are
> multidimensional, it is hard to find a best parameter, and, try
> thousands of parameters will take long time.

As I see, it's your responsibility from the host driver to propagate
the proper error code towards the mmc core. If you encounter an error
that you want to trigger a retune, just return -EILSEQ.

Moreover, if you see a need to extend the tuning/re-tuning support in
the mmc core to suit your need - I am definitely open to look into
that. More importantly, I don't want to see host specific hacks trying
to deal with this.

Kind regards
Uffe

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH 1/4] mmc: mediatek: Add online-tuning support of EMMC/SD
       [not found]             ` <CAPDyKFpGk9gcLLjBnN=+BD5WP-7dZ0+XHC1bcEEGGLvLbTyKHw-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
@ 2015-08-26  8:40               ` Chaotian Jing
  2015-08-26 15:06                 ` Ulf Hansson
  0 siblings, 1 reply; 10+ messages in thread
From: Chaotian Jing @ 2015-08-26  8:40 UTC (permalink / raw)
  To: Ulf Hansson
  Cc: Mark Rutland, James Liao, Catalin Marinas, Wenbin Mei,
	Will Deacon, Chris Ball, Russell King - ARM Linux, Hongzhou Yang,
	Joe.C, devicetree-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
	Arnd Bergmann, Bin Zhang (章斌),
	linux-gpio-u79uwXL29TY76Z2rM5mHXA@public.gmane.org, Rob Herring,
	linux-mediatek-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, Matthias Brugger,
	Eddie Huang,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r@public.gmane.org,
	Liuquan Ji

On Tue, 2015-08-25 at 14:07 +0200, Ulf Hansson wrote:
> On 17 August 2015 at 14:01, Chaotian Jing <chaotian.jing-NuS5LvNUpcJWk0Htik3J/w@public.gmane.org> wrote:
> > Hi Ulf,
> > Thanks, please see my comment:
> > On Mon, 2015-08-17 at 13:31 +0200, Ulf Hansson wrote:
> >> On 12 August 2015 at 10:24, Chaotian Jing <chaotian.jing-NuS5LvNUpcJWk0Htik3J/w@public.gmane.org> wrote:
> >> > Schedule a workqueue to do tuning when CRC error
> >> > Call mmc_hw_reset to re-init card when data timeout
> >>
> >> Thanks to Adrian Hunter, the mmc core already supports re-tuning for
> >> the above scenarios through the mmc_retune_*() APIs.
> >>
> >> SDHCI driver has already adopted to use that feature, you should do
> >> that for the mtk-sd driver as well.
> >>
> >> Kind regards
> >> Uffe
> >>
> > I also noticed that the mmc core already supports re-tuning, but it is
> > not suitable for our host.
> > For EMMC, the CMD21 only support HS200/HS400 mode, for SD card, CMD19
> > only support SDR50/SDR104 mode, but in our host, even 50Mhz clock
> > frequency may also occur CRC error, Cannot find a parameter that can
> > cover all SD cards which running at 50Mhz, even 25Mhz, will occur CRC
> > error for stress test, DDR50 mode is worse.
> 
> I don't follow. You may run for example HS200 in lower speed, nothing
> will prevent tuning and re-tuning from happen for these scenarios.
> 
> Or you are talking about other speed modes than HS200/400 and
> SDR50/104? If so, which speed modes are these?
> 
Yes, I am talking about other speed modes.
For SD card, the speed mode is High Speed, for EMMC, the speed may be
High Speed or DDR mode.
If CRC error occurs at these speed modes, the MMC core layer will never
handler it. even propagate the -EILSEQ towards to the mmc core, it will
not start mmc_retune.
> BTW, there are currently a patch being discussed which is about adding
> tuning for DDR mode. Please have look.
> http://www.spinics.net/lists/arm-kernel/msg438434.html
> 
> Regarding re-tuning on CRC errors, that's already supported by the mmc
> core. More precisely when a host driver returns -EILSEQ for a request.
> 
> > By the way,there are too many tune parameters need try for response,
> > read data, write crc status CRC error, these parameters are
> > multidimensional, it is hard to find a best parameter, and, try
> > thousands of parameters will take long time.
> 
> As I see, it's your responsibility from the host driver to propagate
> the proper error code towards the mmc core. If you encounter an error
> that you want to trigger a retune, just return -EILSEQ.
> 
> Moreover, if you see a need to extend the tuning/re-tuning support in
> the mmc core to suit your need - I am definitely open to look into
> that. More importantly, I don't want to see host specific hacks trying
> to deal with this.
> 
In addition, the CMD21 is only valid for HS200 mode, do not support
HS400, So that there is no chance to tune the HS400 read/write data with
current mmc core layer codes.
As you see, in our platform, tune of HS200 and HS400 is different, but
if use the CMD21, it will never work at HS400 mode, even with
"prepare_hs400_tuning", but it prepare what ? what we need is that
running at HS400 mode and tune the read/write parameters, but not HS200,
because for data Rx path, the tune result of HS200 is meaningless for
HS400.
> Kind regards
> Uffe

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH 1/4] mmc: mediatek: Add online-tuning support of EMMC/SD
  2015-08-26  8:40               ` Chaotian Jing
@ 2015-08-26 15:06                 ` Ulf Hansson
  0 siblings, 0 replies; 10+ messages in thread
From: Ulf Hansson @ 2015-08-26 15:06 UTC (permalink / raw)
  To: Chaotian Jing
  Cc: Rob Herring, Matthias Brugger, Chris Ball, Mark Rutland,
	James Liao, srv_heupstream, Arnd Bergmann,
	devicetree@vger.kernel.org, Hongzhou Yang, Catalin Marinas,
	linux-mmc, Will Deacon, linux-kernel@vger.kernel.org,
	linux-gpio@vger.kernel.org, Sascha Hauer, Joe.C, Eddie Huang,
	Bin Zhang (章斌),
	linux-arm-kernel@lists.infradead.org

On 26 August 2015 at 10:40, Chaotian Jing <chaotian.jing@mediatek.com> wrote:
> On Tue, 2015-08-25 at 14:07 +0200, Ulf Hansson wrote:
>> On 17 August 2015 at 14:01, Chaotian Jing <chaotian.jing@mediatek.com> wrote:
>> > Hi Ulf,
>> > Thanks, please see my comment:
>> > On Mon, 2015-08-17 at 13:31 +0200, Ulf Hansson wrote:
>> >> On 12 August 2015 at 10:24, Chaotian Jing <chaotian.jing@mediatek.com> wrote:
>> >> > Schedule a workqueue to do tuning when CRC error
>> >> > Call mmc_hw_reset to re-init card when data timeout
>> >>
>> >> Thanks to Adrian Hunter, the mmc core already supports re-tuning for
>> >> the above scenarios through the mmc_retune_*() APIs.
>> >>
>> >> SDHCI driver has already adopted to use that feature, you should do
>> >> that for the mtk-sd driver as well.
>> >>
>> >> Kind regards
>> >> Uffe
>> >>
>> > I also noticed that the mmc core already supports re-tuning, but it is
>> > not suitable for our host.
>> > For EMMC, the CMD21 only support HS200/HS400 mode, for SD card, CMD19
>> > only support SDR50/SDR104 mode, but in our host, even 50Mhz clock
>> > frequency may also occur CRC error, Cannot find a parameter that can
>> > cover all SD cards which running at 50Mhz, even 25Mhz, will occur CRC
>> > error for stress test, DDR50 mode is worse.
>>
>> I don't follow. You may run for example HS200 in lower speed, nothing
>> will prevent tuning and re-tuning from happen for these scenarios.
>>
>> Or you are talking about other speed modes than HS200/400 and
>> SDR50/104? If so, which speed modes are these?
>>
> Yes, I am talking about other speed modes.
> For SD card, the speed mode is High Speed, for EMMC, the speed may be
> High Speed or DDR mode.
> If CRC error occurs at these speed modes, the MMC core layer will never
> handler it. even propagate the -EILSEQ towards to the mmc core, it will
> not start mmc_retune.

That's true and that's because the MMC/SD/SDIO specs states it.
Moreover, there a no tuning ever executed even while initializing such
cards. I think you need to elaborate more on what kind of "tuning"
your controller needs for these legacy speed modes, can you please do
that?

Currently the mmc block layer has request retry mechanism when
encountering IO errors. That may even reset or power cycle the card,
via mmc_hw_reset(). Isn't that sufficient?

>> BTW, there are currently a patch being discussed which is about adding
>> tuning for DDR mode. Please have look.
>> http://www.spinics.net/lists/arm-kernel/msg438434.html
>>
>> Regarding re-tuning on CRC errors, that's already supported by the mmc
>> core. More precisely when a host driver returns -EILSEQ for a request.
>>
>> > By the way,there are too many tune parameters need try for response,
>> > read data, write crc status CRC error, these parameters are
>> > multidimensional, it is hard to find a best parameter, and, try
>> > thousands of parameters will take long time.
>>
>> As I see, it's your responsibility from the host driver to propagate
>> the proper error code towards the mmc core. If you encounter an error
>> that you want to trigger a retune, just return -EILSEQ.
>>
>> Moreover, if you see a need to extend the tuning/re-tuning support in
>> the mmc core to suit your need - I am definitely open to look into
>> that. More importantly, I don't want to see host specific hacks trying
>> to deal with this.
>>
> In addition, the CMD21 is only valid for HS200 mode, do not support
> HS400, So that there is no chance to tune the HS400 read/write data with
> current mmc core layer codes.
> As you see, in our platform, tune of HS200 and HS400 is different, but
> if use the CMD21, it will never work at HS400 mode, even with
> "prepare_hs400_tuning", but it prepare what ? what we need is that
> running at HS400 mode and tune the read/write parameters, but not HS200,
> because for data Rx path, the tune result of HS200 is meaningless for
> HS400.

So, should we perhaps add another host_ops callback to satisfy your
need for HS400?

Kind regards
Uffe

^ permalink raw reply	[flat|nested] 10+ messages in thread

end of thread, other threads:[~2015-08-26 15:06 UTC | newest]

Thread overview: 10+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2015-08-12  8:24 [PATCH 0/4] msdc: Add tuning support of Mediatek MMC host Chaotian Jing
     [not found] ` <1439367845-5891-1-git-send-email-chaotian.jing-NuS5LvNUpcJWk0Htik3J/w@public.gmane.org>
2015-08-12  8:24   ` [PATCH 1/4] mmc: mediatek: Add online-tuning support of EMMC/SD Chaotian Jing
2015-08-17 11:31     ` Ulf Hansson
     [not found]       ` <CAPDyKFpX4Pp1+U9eNBuGd83sBeBgLU17qysAJ-2V-ABZZHYAGQ-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
2015-08-17 12:01         ` Chaotian Jing
2015-08-25 12:07           ` Ulf Hansson
     [not found]             ` <CAPDyKFpGk9gcLLjBnN=+BD5WP-7dZ0+XHC1bcEEGGLvLbTyKHw-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
2015-08-26  8:40               ` Chaotian Jing
2015-08-26 15:06                 ` Ulf Hansson
2015-08-12  8:24   ` [PATCH 2/4] mmc: mediatek: Add HS400 support Chaotian Jing
2015-08-12  8:24   ` [PATCH 3/4] arm64: dts: mediatek: Support SD/EMMC SDR104/HS200/HS400 Chaotian Jing
2015-08-12  8:24   ` [PATCH 4/4] mmc: dt-bindings: Add 400Mhz clock source Chaotian Jing

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).