aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDong Aisheng <b29396@freescale.com>2013-09-13 07:11:34 -0400
committerChris Ball <cjb@laptop.org>2013-09-26 07:57:23 -0400
commit0322191e62984b94d1b2ae5ff322112e1fa1ef1a (patch)
treee58c406d1dcbf45d77a6cc7fb1acbda8179001a7
parentfed2f6e2d42e4bcdec5ea357e7a9db8602744753 (diff)
mmc: sdhci-esdhc-imx: add sd3.0 SDR clock tuning support
Freescale i.MX6Q/DL uSDHC clock tuning progress is a little different from the standard tuning process defined in host controller spec v3.0. Thus we use platform_execute_tuning instead of standard sdhci tuning. The main difference are: 1) not only generate Buffer Read Ready interrupt when tuning is performing. It generates all other DATA interrupts like the normal data command. 2) SDHCI_CTRL_EXEC_TUNING is not automatically cleared by HW, instead it's controlled by SW. 3) SDHCI_CTRL_TUNED_CLK is not automatically set by HW, it's controlled by SW. 4) the clock delay for every tuning is set by SW. Signed-off-by: Dong Aisheng <b29396@freescale.com> Acked-by: Shawn Guo <shawn.guo@linaro.org> Signed-off-by: Chris Ball <cjb@laptop.org>
-rw-r--r--drivers/mmc/host/sdhci-esdhc-imx.c196
1 files changed, 195 insertions, 1 deletions
diff --git a/drivers/mmc/host/sdhci-esdhc-imx.c b/drivers/mmc/host/sdhci-esdhc-imx.c
index 37fafd7df964..f906c206901d 100644
--- a/drivers/mmc/host/sdhci-esdhc-imx.c
+++ b/drivers/mmc/host/sdhci-esdhc-imx.c
@@ -34,13 +34,25 @@
34/* VENDOR SPEC register */ 34/* VENDOR SPEC register */
35#define ESDHC_VENDOR_SPEC 0xc0 35#define ESDHC_VENDOR_SPEC 0xc0
36#define ESDHC_VENDOR_SPEC_SDIO_QUIRK (1 << 1) 36#define ESDHC_VENDOR_SPEC_SDIO_QUIRK (1 << 1)
37#define ESDHC_VENDOR_SPEC_VSELECT (1 << 1)
37#define ESDHC_VENDOR_SPEC_FRC_SDCLK_ON (1 << 8) 38#define ESDHC_VENDOR_SPEC_FRC_SDCLK_ON (1 << 8)
38#define ESDHC_WTMK_LVL 0x44 39#define ESDHC_WTMK_LVL 0x44
39#define ESDHC_MIX_CTRL 0x48 40#define ESDHC_MIX_CTRL 0x48
40#define ESDHC_MIX_CTRL_AC23EN (1 << 7) 41#define ESDHC_MIX_CTRL_AC23EN (1 << 7)
42#define ESDHC_MIX_CTRL_EXE_TUNE (1 << 22)
43#define ESDHC_MIX_CTRL_SMPCLK_SEL (1 << 23)
44#define ESDHC_MIX_CTRL_FBCLK_SEL (1 << 25)
41/* Bits 3 and 6 are not SDHCI standard definitions */ 45/* Bits 3 and 6 are not SDHCI standard definitions */
42#define ESDHC_MIX_CTRL_SDHCI_MASK 0xb7 46#define ESDHC_MIX_CTRL_SDHCI_MASK 0xb7
43 47
48/* tune control register */
49#define ESDHC_TUNE_CTRL_STATUS 0x68
50#define ESDHC_TUNE_CTRL_STEP 1
51#define ESDHC_TUNE_CTRL_MIN 0
52#define ESDHC_TUNE_CTRL_MAX ((1 << 7) - 1)
53
54#define ESDHC_TUNING_BLOCK_PATTERN_LEN 64
55
44/* 56/*
45 * Our interpretation of the SDHCI_HOST_CONTROL register 57 * Our interpretation of the SDHCI_HOST_CONTROL register
46 */ 58 */
@@ -91,7 +103,7 @@ struct pltfm_imx_data {
91 MULTIBLK_IN_PROCESS, /* exact multiblock cmd in process */ 103 MULTIBLK_IN_PROCESS, /* exact multiblock cmd in process */
92 WAIT_FOR_INT, /* sent CMD12, waiting for response INT */ 104 WAIT_FOR_INT, /* sent CMD12, waiting for response INT */
93 } multiblock_status; 105 } multiblock_status;
94 106 u32 uhs_mode;
95}; 107};
96 108
97static struct platform_device_id imx_esdhc_devtype[] = { 109static struct platform_device_id imx_esdhc_devtype[] = {
@@ -165,6 +177,16 @@ static u32 esdhc_readl_le(struct sdhci_host *host, int reg)
165 struct pltfm_imx_data *imx_data = pltfm_host->priv; 177 struct pltfm_imx_data *imx_data = pltfm_host->priv;
166 u32 val = readl(host->ioaddr + reg); 178 u32 val = readl(host->ioaddr + reg);
167 179
180 if (unlikely(reg == SDHCI_PRESENT_STATE)) {
181 u32 fsl_prss = val;
182 /* save the least 20 bits */
183 val = fsl_prss & 0x000FFFFF;
184 /* move dat[0-3] bits */
185 val |= (fsl_prss & 0x0F000000) >> 4;
186 /* move cmd line bit */
187 val |= (fsl_prss & 0x00800000) << 1;
188 }
189
168 if (unlikely(reg == SDHCI_CAPABILITIES)) { 190 if (unlikely(reg == SDHCI_CAPABILITIES)) {
169 /* In FSL esdhc IC module, only bit20 is used to indicate the 191 /* In FSL esdhc IC module, only bit20 is used to indicate the
170 * ADMA2 capability of esdhc, but this bit is messed up on 192 * ADMA2 capability of esdhc, but this bit is messed up on
@@ -179,6 +201,17 @@ static u32 esdhc_readl_le(struct sdhci_host *host, int reg)
179 } 201 }
180 } 202 }
181 203
204 if (unlikely(reg == SDHCI_CAPABILITIES_1) && is_imx6q_usdhc(imx_data))
205 val = SDHCI_SUPPORT_DDR50 | SDHCI_SUPPORT_SDR104
206 | SDHCI_SUPPORT_SDR50;
207
208 if (unlikely(reg == SDHCI_MAX_CURRENT) && is_imx6q_usdhc(imx_data)) {
209 val = 0;
210 val |= 0xFF << SDHCI_MAX_CURRENT_330_SHIFT;
211 val |= 0xFF << SDHCI_MAX_CURRENT_300_SHIFT;
212 val |= 0xFF << SDHCI_MAX_CURRENT_180_SHIFT;
213 }
214
182 if (unlikely(reg == SDHCI_INT_STATUS)) { 215 if (unlikely(reg == SDHCI_INT_STATUS)) {
183 if (val & ESDHC_INT_VENDOR_SPEC_DMA_ERR) { 216 if (val & ESDHC_INT_VENDOR_SPEC_DMA_ERR) {
184 val &= ~ESDHC_INT_VENDOR_SPEC_DMA_ERR; 217 val &= ~ESDHC_INT_VENDOR_SPEC_DMA_ERR;
@@ -257,6 +290,8 @@ static u16 esdhc_readw_le(struct sdhci_host *host, int reg)
257{ 290{
258 struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 291 struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
259 struct pltfm_imx_data *imx_data = pltfm_host->priv; 292 struct pltfm_imx_data *imx_data = pltfm_host->priv;
293 u16 ret = 0;
294 u32 val;
260 295
261 if (unlikely(reg == SDHCI_HOST_VERSION)) { 296 if (unlikely(reg == SDHCI_HOST_VERSION)) {
262 reg ^= 2; 297 reg ^= 2;
@@ -269,6 +304,25 @@ static u16 esdhc_readw_le(struct sdhci_host *host, int reg)
269 } 304 }
270 } 305 }
271 306
307 if (unlikely(reg == SDHCI_HOST_CONTROL2)) {
308 val = readl(host->ioaddr + ESDHC_VENDOR_SPEC);
309 if (val & ESDHC_VENDOR_SPEC_VSELECT)
310 ret |= SDHCI_CTRL_VDD_180;
311
312 if (is_imx6q_usdhc(imx_data)) {
313 val = readl(host->ioaddr + ESDHC_MIX_CTRL);
314 if (val & ESDHC_MIX_CTRL_EXE_TUNE)
315 ret |= SDHCI_CTRL_EXEC_TUNING;
316 if (val & ESDHC_MIX_CTRL_SMPCLK_SEL)
317 ret |= SDHCI_CTRL_TUNED_CLK;
318 }
319
320 ret |= (imx_data->uhs_mode & SDHCI_CTRL_UHS_MASK);
321 ret &= ~SDHCI_CTRL_PRESET_VAL_ENABLE;
322
323 return ret;
324 }
325
272 return readw(host->ioaddr + reg); 326 return readw(host->ioaddr + reg);
273} 327}
274 328
@@ -276,8 +330,32 @@ static void esdhc_writew_le(struct sdhci_host *host, u16 val, int reg)
276{ 330{
277 struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 331 struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
278 struct pltfm_imx_data *imx_data = pltfm_host->priv; 332 struct pltfm_imx_data *imx_data = pltfm_host->priv;
333 u32 new_val = 0;
279 334
280 switch (reg) { 335 switch (reg) {
336 case SDHCI_CLOCK_CONTROL:
337 new_val = readl(host->ioaddr + ESDHC_VENDOR_SPEC);
338 if (val & SDHCI_CLOCK_CARD_EN)
339 new_val |= ESDHC_VENDOR_SPEC_FRC_SDCLK_ON;
340 else
341 new_val &= ~ESDHC_VENDOR_SPEC_FRC_SDCLK_ON;
342 writel(new_val, host->ioaddr + ESDHC_VENDOR_SPEC);
343 return;
344 case SDHCI_HOST_CONTROL2:
345 new_val = readl(host->ioaddr + ESDHC_VENDOR_SPEC);
346 if (val & SDHCI_CTRL_VDD_180)
347 new_val |= ESDHC_VENDOR_SPEC_VSELECT;
348 else
349 new_val &= ~ESDHC_VENDOR_SPEC_VSELECT;
350 writel(new_val, host->ioaddr + ESDHC_VENDOR_SPEC);
351 imx_data->uhs_mode = val & SDHCI_CTRL_UHS_MASK;
352 new_val = readl(host->ioaddr + ESDHC_MIX_CTRL);
353 if (val & SDHCI_CTRL_TUNED_CLK)
354 new_val |= ESDHC_MIX_CTRL_SMPCLK_SEL;
355 else
356 new_val &= ~ESDHC_MIX_CTRL_SMPCLK_SEL;
357 writel(new_val , host->ioaddr + ESDHC_MIX_CTRL);
358 return;
281 case SDHCI_TRANSFER_MODE: 359 case SDHCI_TRANSFER_MODE:
282 if ((imx_data->flags & ESDHC_FLAG_MULTIBLK_NO_INT) 360 if ((imx_data->flags & ESDHC_FLAG_MULTIBLK_NO_INT)
283 && (host->cmd->opcode == SD_IO_RW_EXTENDED) 361 && (host->cmd->opcode == SD_IO_RW_EXTENDED)
@@ -500,6 +578,121 @@ static int esdhc_pltfm_bus_width(struct sdhci_host *host, int width)
500 return 0; 578 return 0;
501} 579}
502 580
581static void esdhc_prepare_tuning(struct sdhci_host *host, u32 val)
582{
583 u32 reg;
584
585 /* FIXME: delay a bit for card to be ready for next tuning due to errors */
586 mdelay(1);
587
588 reg = readl(host->ioaddr + ESDHC_MIX_CTRL);
589 reg |= ESDHC_MIX_CTRL_EXE_TUNE | ESDHC_MIX_CTRL_SMPCLK_SEL |
590 ESDHC_MIX_CTRL_FBCLK_SEL;
591 writel(reg, host->ioaddr + ESDHC_MIX_CTRL);
592 writel(val << 8, host->ioaddr + ESDHC_TUNE_CTRL_STATUS);
593 dev_dbg(mmc_dev(host->mmc),
594 "tunning with delay 0x%x ESDHC_TUNE_CTRL_STATUS 0x%x\n",
595 val, readl(host->ioaddr + ESDHC_TUNE_CTRL_STATUS));
596}
597
598static void esdhc_request_done(struct mmc_request *mrq)
599{
600 complete(&mrq->completion);
601}
602
603static int esdhc_send_tuning_cmd(struct sdhci_host *host, u32 opcode)
604{
605 struct mmc_command cmd = {0};
606 struct mmc_request mrq = {0};
607 struct mmc_data data = {0};
608 struct scatterlist sg;
609 char tuning_pattern[ESDHC_TUNING_BLOCK_PATTERN_LEN];
610
611 cmd.opcode = opcode;
612 cmd.arg = 0;
613 cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC;
614
615 data.blksz = ESDHC_TUNING_BLOCK_PATTERN_LEN;
616 data.blocks = 1;
617 data.flags = MMC_DATA_READ;
618 data.sg = &sg;
619 data.sg_len = 1;
620
621 sg_init_one(&sg, tuning_pattern, sizeof(tuning_pattern));
622
623 mrq.cmd = &cmd;
624 mrq.cmd->mrq = &mrq;
625 mrq.data = &data;
626 mrq.data->mrq = &mrq;
627 mrq.cmd->data = mrq.data;
628
629 mrq.done = esdhc_request_done;
630 init_completion(&(mrq.completion));
631
632 disable_irq(host->irq);
633 spin_lock(&host->lock);
634 host->mrq = &mrq;
635
636 sdhci_send_command(host, mrq.cmd);
637
638 spin_unlock(&host->lock);
639 enable_irq(host->irq);
640
641 wait_for_completion(&mrq.completion);
642
643 if (cmd.error)
644 return cmd.error;
645 if (data.error)
646 return data.error;
647
648 return 0;
649}
650
651static void esdhc_post_tuning(struct sdhci_host *host)
652{
653 u32 reg;
654
655 reg = readl(host->ioaddr + ESDHC_MIX_CTRL);
656 reg &= ~ESDHC_MIX_CTRL_EXE_TUNE;
657 writel(reg, host->ioaddr + ESDHC_MIX_CTRL);
658}
659
660static int esdhc_executing_tuning(struct sdhci_host *host, u32 opcode)
661{
662 int min, max, avg, ret;
663
664 /* find the mininum delay first which can pass tuning */
665 min = ESDHC_TUNE_CTRL_MIN;
666 while (min < ESDHC_TUNE_CTRL_MAX) {
667 esdhc_prepare_tuning(host, min);
668 if (!esdhc_send_tuning_cmd(host, opcode))
669 break;
670 min += ESDHC_TUNE_CTRL_STEP;
671 }
672
673 /* find the maxinum delay which can not pass tuning */
674 max = min + ESDHC_TUNE_CTRL_STEP;
675 while (max < ESDHC_TUNE_CTRL_MAX) {
676 esdhc_prepare_tuning(host, max);
677 if (esdhc_send_tuning_cmd(host, opcode)) {
678 max -= ESDHC_TUNE_CTRL_STEP;
679 break;
680 }
681 max += ESDHC_TUNE_CTRL_STEP;
682 }
683
684 /* use average delay to get the best timing */
685 avg = (min + max) / 2;
686 esdhc_prepare_tuning(host, avg);
687 ret = esdhc_send_tuning_cmd(host, opcode);
688 esdhc_post_tuning(host);
689
690 dev_dbg(mmc_dev(host->mmc), "tunning %s at 0x%x ret %d\n",
691 ret ? "failed" : "passed", avg, ret);
692
693 return ret;
694}
695
503static const struct sdhci_ops sdhci_esdhc_ops = { 696static const struct sdhci_ops sdhci_esdhc_ops = {
504 .read_l = esdhc_readl_le, 697 .read_l = esdhc_readl_le,
505 .read_w = esdhc_readw_le, 698 .read_w = esdhc_readw_le,
@@ -511,6 +704,7 @@ static const struct sdhci_ops sdhci_esdhc_ops = {
511 .get_min_clock = esdhc_pltfm_get_min_clock, 704 .get_min_clock = esdhc_pltfm_get_min_clock,
512 .get_ro = esdhc_pltfm_get_ro, 705 .get_ro = esdhc_pltfm_get_ro,
513 .platform_bus_width = esdhc_pltfm_bus_width, 706 .platform_bus_width = esdhc_pltfm_bus_width,
707 .platform_execute_tuning = esdhc_executing_tuning,
514}; 708};
515 709
516static const struct sdhci_pltfm_data sdhci_esdhc_imx_pdata = { 710static const struct sdhci_pltfm_data sdhci_esdhc_imx_pdata = {