mmc: rk_sdmmc: enhance recovery flow
authorShawn Lin <shawn.lin@rock-chips.com>
Fri, 10 Jul 2015 06:03:25 +0000 (14:03 +0800)
committerShawn Lin <shawn.lin@rock-chips.com>
Fri, 10 Jul 2015 06:05:03 +0000 (14:05 +0800)
Signed-off-by: Shawn Lin <shawn.lin@rock-chips.com>
drivers/mmc/core/core.c
drivers/mmc/host/dw_mmc-rockchip.c
drivers/mmc/host/rk_sdmmc.c
include/linux/mmc/rk_mmc.h

index 285949f7d062c28a4b4a6784fa1ed63b310d95d0..b4bd9d33b8889667e2f5f0d759df306ede9a89e1 100755 (executable)
@@ -407,6 +407,8 @@ static void mmc_get_req_timeout(struct mmc_request *mrq, u32 *timeout)
                         ((mrq->cmd->arg == MMC_DISCARD_ARG) ||
                         (mrq->cmd->arg == MMC_TRIM_ARG))) ?
                         (*timeout = 10000) : (*timeout = 25000);
+               else if (mrq->cmd->opcode == MMC_SWITCH)
+                       *timeout = mrq->cmd->cmd_timeout_ms;
                else
                        *timeout = 500;
 
@@ -457,11 +459,11 @@ static int mmc_wait_for_data_req_done(struct mmc_host *host,
                                         msecs_to_jiffies(timeout))) {
                        cmd = mrq->cmd;
                        cmd->error = -ETIMEDOUT;
-                       host->ops->post_tmo(host);
-                       context_info->is_done_rcv = true;
                        dev_err(mmc_dev(host),
                                "req failed (CMD%u): error = %d, timeout = %dms\n",
                                cmd->opcode, cmd->error, timeout);
+                       host->ops->post_tmo(host);
+                       context_info->is_done_rcv = true;
                }
 
                spin_lock_irqsave(&context_info->lock, flags);
@@ -510,13 +512,10 @@ static void mmc_wait_for_req_done(struct mmc_host *host,
                                                 msecs_to_jiffies(timeout))) {
                        cmd = mrq->cmd;
                        cmd->error = -ETIMEDOUT;
-                       host->ops->post_tmo(host);
-
                        dev_err(mmc_dev(host),
                                "req failed (CMD%u): error = %d, timeout = %dms\n",
                                cmd->opcode, cmd->error, timeout);
-                       if (!cmd->data)
-                               break;
+                       host->ops->post_tmo(host);
                }
 
                cmd = mrq->cmd;
index ad998fbab5459ee86620b1028d4e089069dd2850..f0ed5643b2f56d43ecd1dc700f798d155e86c056 100755 (executable)
@@ -83,8 +83,20 @@ static int dw_mci_rockchip_priv_init(struct dw_mci *host)
                                            rockchip_compat[idx].compatible)) {
                        priv->ctrl_type = rockchip_compat[idx].ctrl_type;
                        host->cid = priv->ctrl_type;
-                       host->grf = syscon_find(host->dev->of_node,
-                                               "rockchip,grf");
+                       if (priv->ctrl_type == DW_MCI_TYPE_RK3368) {
+                               host->grf = syscon_regmap_lookup_by_phandle(
+                                               host->dev->of_node, "rockchip,grf");
+                               if (IS_ERR(host->grf)) {
+                                       pr_err("No rockchip,grf phandle specified");
+                                       return PTR_ERR(host->grf);
+                               }
+                               host->cru = syscon_regmap_lookup_by_phandle(
+                                       host->dev->of_node, "rockchip,cru");
+                               if (IS_ERR(host->cru)) {
+                                       pr_err("No rockchip,cru phandle specified");
+                                       return PTR_ERR(host->cru);
+                               }
+                       }
                }
        }
 
index d9138b6a32ebc736123f88691378c3301f1d1589..870c94cc1ec1df4086a4a9f3c8e4a1d7c13e526d 100755 (executable)
@@ -1970,12 +1970,13 @@ static void dw_mci_post_tmo(struct mmc_host *mmc)
        struct dw_mci_slot *slot = mmc_priv(mmc);
        struct dw_mci *host = slot->host;
        struct mmc_data *data;
-       u32 ret, i, regs, cmd_flags;
+       u32 i, regs, cmd_flags;
        u32 sdio_int;
        unsigned long timeout = 0;
-       bool ret_timeout = true;
-       u32 opcode;
+       bool ret_timeout = true, is_retry = false;
+       u32 opcode, offset;
 
+       offset = host->cru_reset_offset;
        opcode = host->mrq->cmd->opcode;
        host->cur_slot->mrq = NULL;
        host->mrq = NULL;
@@ -1985,11 +1986,16 @@ static void dw_mci_post_tmo(struct mmc_host *mmc)
 
        if ((opcode == MMC_SEND_TUNING_BLOCK_HS200) ||
            (opcode == MMC_SEND_TUNING_BLOCK))
-           return;
+               return;
 
        printk("[%s] -- Timeout recovery procedure start --\n",
                mmc_hostname(host->mmc));
 
+       /* unmask irq */
+       mci_writel(host, INTMASK, 0x0);
+
+retry_stop:
+       /* send stop cmd */
        mci_writel(host, CMDARG, 0);
        wmb();
        cmd_flags = SDMMC_CMD_STOP | SDMMC_CMD_RESP_CRC |
@@ -2000,7 +2006,7 @@ static void dw_mci_post_tmo(struct mmc_host *mmc)
 
        mci_writel(host, CMD, cmd_flags | SDMMC_CMD_START);
        wmb();
-       timeout = jiffies + msecs_to_jiffies(500);
+       timeout = jiffies + msecs_to_jiffies(10);
 
        while(ret_timeout) {
                ret_timeout = time_before(jiffies, timeout);
@@ -2008,13 +2014,29 @@ static void dw_mci_post_tmo(struct mmc_host *mmc)
                        break;
        }
 
-       if (false == ret_timeout)
+       if (false == ret_timeout) {
                MMC_DBG_ERR_FUNC(host->mmc, "stop recovery failed![%s]",
                                 mmc_hostname(host->mmc));
-
-       if (!dw_mci_ctrl_all_reset(host)) {
-               ret = -ENODEV;
-               return ;
+               if (host->cid == DW_MCI_TYPE_RK3368) {
+                       /* pd_peri mmc AHB bus software reset request */
+                       regmap_write(host->cru, host->cru_regsbase,
+                                       (0x1<<offset)<<16 | (0x1 << offset));
+                       mdelay(1);
+                       regmap_write(host->cru, host->cru_regsbase,
+                                       (0x1<<offset)<<16 | (0x0 << offset));
+               } else {
+                       /* pd_peri mmc AHB bus software reset request */
+                       cru_writel(((0x1<<offset)<<16) | (0x1 << offset),
+                                       host->cru_regsbase);
+                       mdelay(1);
+                       cru_writel(((0x1<<offset)<<16) | (0x0 << offset),
+                                       host->cru_regsbase);
+               }
+       } else {
+               if (!dw_mci_ctrl_all_reset(host))
+                       return;
+               if (is_retry == true)
+                       goto recovery_end;
        }
 
 #ifdef CONFIG_MMC_DW_IDMAC
@@ -2024,15 +2046,16 @@ static void dw_mci_post_tmo(struct mmc_host *mmc)
 #endif
 
        /*
-       * Restore the initial value at FIFOTH register
-       * And Invalidate the prev_blksz with zero
-       */
+        * Restore the initial value at FIFOTH register
+        * And Invalidate the prev_blksz with zero
+        */
        mci_writel(host, FIFOTH, host->fifoth_val);
        host->prev_blksz = 0;
        mci_writel(host, TMOUT, 0xFFFFFFFF);
        mci_writel(host, RINTSTS, 0xFFFFFFFF);
-       regs = SDMMC_INT_CMD_DONE | SDMMC_INT_DATA_OVER | SDMMC_INT_TXDR
-                       | SDMMC_INT_RXDR | SDMMC_INT_VSI | DW_MCI_ERROR_FLAGS;
+       regs = SDMMC_INT_CMD_DONE | SDMMC_INT_DATA_OVER |
+               SDMMC_INT_TXDR | SDMMC_INT_RXDR | SDMMC_INT_VSI |
+               DW_MCI_ERROR_FLAGS;
        if (!(host->mmc->restrict_caps & RESTRICT_CARD_TYPE_SDIO))
                regs |= SDMMC_INT_CD;
 
@@ -2058,7 +2081,13 @@ static void dw_mci_post_tmo(struct mmc_host *mmc)
                }
        }
        mci_writel(host, RINTSTS, 0xFFFFFFFF);
+       if (ret_timeout == false) {
+               ret_timeout = true;
+               is_retry = true;
+               goto retry_stop;
+       }
 
+recovery_end:
        printk("[%s] -- Timeout recovery procedure finished --\n",
                mmc_hostname(host->mmc));
 }
@@ -3949,6 +3978,20 @@ static struct dw_mci_board *dw_mci_parse_dt(struct dw_mci *host)
        if (of_get_property(np, "assume_removable", NULL))
                mmc_assume_removable = 0;
 
+       if (!of_property_read_u32(np, "cru_regsbase", &host->cru_regsbase)) {
+               printk("dw cru_regsbase addr 0x%03x.\n", host->cru_regsbase);
+       } else {
+               pr_err("dw cru_regsbase addr is missing!\n");
+               return ERR_PTR(-1);
+       }
+
+       if (!of_property_read_u32(np, "cru_reset_offset", &host->cru_reset_offset)) {
+               printk("dw cru_reset_offset val %d.\n", host->cru_reset_offset);
+       } else {
+               pr_err("dw cru_reset_offset val is missing!\n");
+               return ERR_PTR(-1);
+       }
+
        return pdata;
 }
 
index a736e4afb859bfc85a259e0deac734618ed3790b..25cb1473d9316d1045523c95a676344af29fd606 100755 (executable)
@@ -221,6 +221,9 @@ struct dw_mci {
        u32 *regs_buffer;
        const struct dw_mci_rst_ops *rst_ops;
        u32     tune_regsbase;
+       u32     cru_regsbase;
+       u32     cru_reset_offset;
+       struct regmap *cru;
 };
 
 /* DMA ops for Internal/External DMAC interface */