[PATCH] mmc: dw_mmc: introduce timer for broken command transfer over scheme

Jaehoon Chung jh80.chung at samsung.com
Wed Jul 19 03:30:36 PDT 2017


Hi Shawn,

On 07/11/2017 06:38 PM, Shawn Lin wrote:
> From: Addy Ke <addy.ke at rock-chips.com>
> 
> Per the databook of designware mmc controller 2.70a, table 3-2, cmd
> done interrupt should be fired as soon as the the cmd is sent via
> cmd line. And the response timeout interrupt should be generated
> unconditioinally as well if the controller doesn't receive the resp.
> However that doesn't seem to meet the fact of rockchip specified Soc
> platforms using dwmmc. We have continuously found the the cmd done or
> response timeout interrupt missed somehow which took us a long time to
> understand what was happening. Finally we narrow down the root to
> the reconstruction of sample circuit for dwmmc IP introduced by
> rockchip and the buggy design sweeps over all the existing rockchip
> Socs using dwmmc disastrously.
> 
> It seems no way to work around this bug without the proper break-out
> mechanism so that we seek for a parallel pair the same as the handling
> for missing data response timeout, namely dto timer. Adding this cto
> timer seems easily to handle this bug but it's hard to restrict the code
> under the rockchip specified context. So after merging this patch, it
> sets up the cto timer for all the platforms using dwmmc IP which isn't
> ideal but at least we don't advertise new quirk here. Fortunately, no
> obvious performance regression was found by test and the pre-existing
> similar catch-all timer for sdhci has proved it's an acceptant way to
> make the code as robust as possible.
> 
> Bugzilla: https://bugzilla.kernel.org/show_bug.cgi?id=196321
> Signed-off-by: Addy Ke <addy.ke at rock-chips.com>
> Signed-off-by: Ziyuan Xu <xzy.xu at rock-chips.com>
> [shawn.lin: rewrite the code and the commit msg throughout]
> Signed-off-by: Shawn Lin <shawn.lin at rock-chips.com>

Will pick this after testing with my targets.

Best Regards,
Jaehoon Chung

> 
> ---
> 
>  drivers/mmc/host/dw_mmc.c | 48 +++++++++++++++++++++++++++++++++++++++++++++++
>  drivers/mmc/host/dw_mmc.h |  2 ++
>  2 files changed, 50 insertions(+)
> 
> diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c
> index a9dfb26..2f6121e 100644
> --- a/drivers/mmc/host/dw_mmc.c
> +++ b/drivers/mmc/host/dw_mmc.c
> @@ -398,6 +398,21 @@ static u32 dw_mci_prep_stop_abort(struct dw_mci *host, struct mmc_command *cmd)
>  	return cmdr;
>  }
>  
> +static inline void dw_mci_set_cto(struct dw_mci *host)
> +{
> +	unsigned int cto_clks;
> +	unsigned int cto_ms;
> +
> +	cto_clks = mci_readl(host, TMOUT) & 0xff;
> +	cto_ms = DIV_ROUND_UP(cto_clks, host->bus_hz / 1000);
> +
> +	/* add a bit spare time */
> +	cto_ms += 10;
> +
> +	mod_timer(&host->cto_timer,
> +		  jiffies + msecs_to_jiffies(cto_ms) + 1);
> +}
> +
>  static void dw_mci_start_command(struct dw_mci *host,
>  				 struct mmc_command *cmd, u32 cmd_flags)
>  {
> @@ -410,6 +425,10 @@ static void dw_mci_start_command(struct dw_mci *host,
>  	wmb(); /* drain writebuffer */
>  	dw_mci_wait_while_busy(host, cmd_flags);
>  
> +	/* response expected command only */
> +	if (cmd_flags & SDMMC_CMD_RESP_EXP)
> +		dw_mci_set_cto(host);
> +
>  	mci_writel(host, CMD, cmd_flags | SDMMC_CMD_START);
>  }
>  
> @@ -2599,6 +2618,7 @@ static irqreturn_t dw_mci_interrupt(int irq, void *dev_id)
>  		}
>  
>  		if (pending & DW_MCI_CMD_ERROR_FLAGS) {
> +			del_timer(&host->cto_timer);
>  			mci_writel(host, RINTSTS, DW_MCI_CMD_ERROR_FLAGS);
>  			host->cmd_status = pending;
>  			smp_wmb(); /* drain writebuffer */
> @@ -2642,6 +2662,7 @@ static irqreturn_t dw_mci_interrupt(int irq, void *dev_id)
>  		}
>  
>  		if (pending & SDMMC_INT_CMD_DONE) {
> +			del_timer(&host->cto_timer);
>  			mci_writel(host, RINTSTS, SDMMC_INT_CMD_DONE);
>  			dw_mci_cmd_interrupt(host, pending);
>  		}
> @@ -2914,6 +2935,30 @@ static void dw_mci_cmd11_timer(unsigned long arg)
>  	tasklet_schedule(&host->tasklet);
>  }
>  
> +static void dw_mci_cto_timer(unsigned long arg)
> +{
> +	struct dw_mci *host = (struct dw_mci *)arg;
> +
> +	switch (host->state) {
> +	case STATE_SENDING_CMD11:
> +	case STATE_SENDING_CMD:
> +	case STATE_SENDING_STOP:
> +		/*
> +		 * If CMD_DONE interrupt does NOT come in sending command
> +		 * state, we should notify the driver to terminate current
> +		 * transfer and report a command timeout to the core.
> +		 */
> +		host->cmd_status = SDMMC_INT_RTO;
> +		set_bit(EVENT_CMD_COMPLETE, &host->pending_events);
> +		tasklet_schedule(&host->tasklet);
> +		break;
> +	default:
> +		dev_warn(host->dev, "Unexpected command timeout, state %d\n",
> +			 host->state);
> +		break;
> +	}
> +}
> +
>  static void dw_mci_dto_timer(unsigned long arg)
>  {
>  	struct dw_mci *host = (struct dw_mci *)arg;
> @@ -3085,6 +3130,9 @@ int dw_mci_probe(struct dw_mci *host)
>  	setup_timer(&host->cmd11_timer,
>  		    dw_mci_cmd11_timer, (unsigned long)host);
>  
> +	setup_timer(&host->cto_timer,
> +		    dw_mci_cto_timer, (unsigned long)host);
> +
>  	setup_timer(&host->dto_timer,
>  		    dw_mci_dto_timer, (unsigned long)host);
>  
> diff --git a/drivers/mmc/host/dw_mmc.h b/drivers/mmc/host/dw_mmc.h
> index 75da375..4210927 100644
> --- a/drivers/mmc/host/dw_mmc.h
> +++ b/drivers/mmc/host/dw_mmc.h
> @@ -126,6 +126,7 @@ struct dw_mci_dma_slave {
>   * @irq: The irq value to be passed to request_irq.
>   * @sdio_id0: Number of slot0 in the SDIO interrupt registers.
>   * @cmd11_timer: Timer for SD3.0 voltage switch over scheme.
> + * @cto_timer: Timer for broken command transfer over scheme.
>   * @dto_timer: Timer for broken data transfer over scheme.
>   *
>   * Locking
> @@ -232,6 +233,7 @@ struct dw_mci {
>  	int			sdio_id0;
>  
>  	struct timer_list       cmd11_timer;
> +	struct timer_list       cto_timer;
>  	struct timer_list       dto_timer;
>  };
>  
> 




More information about the Linux-rockchip mailing list