diff mbox

[PATCHv2,1/3] mmc: dw_mmc: Enable the hold reg for certain speed modes

Message ID 1386386424-859-2-git-send-email-dinguyen@altera.com (mailing list archive)
State New, archived
Headers show

Commit Message

Dinh Nguyen Dec. 7, 2013, 3:20 a.m. UTC
From: Dinh Nguyen <dinguyen@altera.com>

This patch will enable the SDMMC_CMD_USE_HOLD_REG bit when the slot is
operating all timing modes, except for SDR50, DDR50, SDR104, and MMC_HS200.

According to the Synopsys databook :"To meet the relatively high Input Hold
Time requirement for SDR12, SDR25, and other MMC speed modes, you should
program bit[29]use_hold_Reg of the CMD register to 1'b1;"..."However, for
the higher speed modes of SDR104, SDR50 and DDR50, you can meet the much
smaller Input Hold Time requirement of 0.8ns by bypassing the Hold Register
(Path A in Figure 10-8, programming CMD.use_hold_reg = 1'b0) and then adding
delay elements on the output path as indicated.

Also, "Never set CMD.use_hold_reg = 1 and cclk_in_drv phase shift to 0 at the
same time. This would add an extra one-cycle delay on the output path, resulting
in incorrect behavior."

This information is taking from the v2.50a of the Synopsys Designware Cores
Mobile Storage Host Databook.

Signed-off-by: Dinh Nguyen <dinguyen@altera.com>
---
v2: Add check for cclk_in_drv phase shift in conjunction with use_hold_reg.
---
 drivers/mmc/host/dw_mmc.c  |   44 ++++++++++++++++++++++++++++++++++++++++++++
 include/linux/mmc/dw_mmc.h |    3 +++
 2 files changed, 47 insertions(+)

Comments

Heiko Stuebner Dec. 7, 2013, 12:50 p.m. UTC | #1
Am Samstag, 7. Dezember 2013, 04:20:22 schrieb dinguyen@altera.com:
> From: Dinh Nguyen <dinguyen@altera.com>
> 
> This patch will enable the SDMMC_CMD_USE_HOLD_REG bit when the slot is
> operating all timing modes, except for SDR50, DDR50, SDR104, and MMC_HS200.
> 
> According to the Synopsys databook :"To meet the relatively high Input Hold
> Time requirement for SDR12, SDR25, and other MMC speed modes, you should
> program bit[29]use_hold_Reg of the CMD register to 1'b1;"..."However, for
> the higher speed modes of SDR104, SDR50 and DDR50, you can meet the much
> smaller Input Hold Time requirement of 0.8ns by bypassing the Hold Register
> (Path A in Figure 10-8, programming CMD.use_hold_reg = 1'b0) and then
> adding delay elements on the output path as indicated.
> 
> Also, "Never set CMD.use_hold_reg = 1 and cclk_in_drv phase shift to 0 at
> the same time. This would add an extra one-cycle delay on the output path,
> resulting in incorrect behavior."
> 
> This information is taking from the v2.50a of the Synopsys Designware Cores
> Mobile Storage Host Databook.
> 
> Signed-off-by: Dinh Nguyen <dinguyen@altera.com>
> ---

Acked-by: Heiko Stuebner <heiko@sntech.de>

on a rockchip,rk3066 (dw_mmc 10214000.dwmmc: Version ID is 240a)
Tested-by: Heiko Stuebner <heiko@sntech.de>
--
To unsubscribe from this list: send the line "unsubscribe linux-mmc" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Jaehoon Chung Dec. 9, 2013, 1:24 a.m. UTC | #2
Hi Dinh,

I known there is IMPLEMENT_HOLD_REG[22] at HCON register.
It didn't affect with this register?

Best Regards,
Jaehoon Chung

On 12/07/2013 12:20 PM, dinguyen@altera.com wrote:
> From: Dinh Nguyen <dinguyen@altera.com>
> 
> This patch will enable the SDMMC_CMD_USE_HOLD_REG bit when the slot is
> operating all timing modes, except for SDR50, DDR50, SDR104, and MMC_HS200.
> 
> According to the Synopsys databook :"To meet the relatively high Input Hold
> Time requirement for SDR12, SDR25, and other MMC speed modes, you should
> program bit[29]use_hold_Reg of the CMD register to 1'b1;"..."However, for
> the higher speed modes of SDR104, SDR50 and DDR50, you can meet the much
> smaller Input Hold Time requirement of 0.8ns by bypassing the Hold Register
> (Path A in Figure 10-8, programming CMD.use_hold_reg = 1'b0) and then adding
> delay elements on the output path as indicated.
> 
> Also, "Never set CMD.use_hold_reg = 1 and cclk_in_drv phase shift to 0 at the
> same time. This would add an extra one-cycle delay on the output path, resulting
> in incorrect behavior."
> 
> This information is taking from the v2.50a of the Synopsys Designware Cores
> Mobile Storage Host Databook.
> 
> Signed-off-by: Dinh Nguyen <dinguyen@altera.com>
> ---
> v2: Add check for cclk_in_drv phase shift in conjunction with use_hold_reg.
> ---
>  drivers/mmc/host/dw_mmc.c  |   44 ++++++++++++++++++++++++++++++++++++++++++++
>  include/linux/mmc/dw_mmc.h |    3 +++
>  2 files changed, 47 insertions(+)
> 
> diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c
> index 4bce0de..9f5492b 100644
> --- a/drivers/mmc/host/dw_mmc.c
> +++ b/drivers/mmc/host/dw_mmc.c
> @@ -279,6 +279,9 @@ static u32 dw_mci_prepare_command(struct mmc_host *mmc, struct mmc_command *cmd)
>  			cmdr |= SDMMC_CMD_DAT_WR;
>  	}
>  
> +	if (slot->host->use_hold_reg)
> +		cmdr |= SDMMC_CMD_USE_HOLD_REG;
> +
>  	if (drv_data && drv_data->prepare_command)
>  		drv_data->prepare_command(slot->host, &cmdr);
>  
> @@ -969,6 +972,24 @@ static void dw_mci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
>  	mci_writel(slot->host, UHS_REG, regs);
>  	slot->host->timing = ios->timing;
>  
> +	/* Per Synopsys spec, use_hold_reg should be set for all modes except for
> +	 * high-speed SDR50, DDR50, SDR104, and MMC_HS200. However, use_hold_reg
> +	 * should be cleared if the cclk_in_drv is 0.
> +	 */
> +	switch (slot->host->timing) {
> +	case MMC_TIMING_UHS_SDR50:
> +	case MMC_TIMING_UHS_SDR104:
> +	case MMC_TIMING_UHS_DDR50:
> +	case MMC_TIMING_MMC_HS200:
> +		slot->host->use_hold_reg = 0;
> +		break;
> +	default:
> +		slot->host->use_hold_reg = 1;
> +	}
> +
> +	if (slot->host->cclk_in_drv == 0)
> +		slot->host->use_hold_reg = 0;
> +
>  	/*
>  	 * Use mirror of ios->clock to prevent race with mmc
>  	 * core ios update when finding the minimum.
> @@ -2339,6 +2360,8 @@ static struct dw_mci_board *dw_mci_parse_dt(struct dw_mci *host)
>  	const struct dw_mci_drv_data *drv_data = host->drv_data;
>  	int idx, ret;
>  	u32 clock_frequency;
> +	int sdr_timing[2];
> +	int ddr_timing[2];
>  
>  	pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
>  	if (!pdata) {
> @@ -2389,6 +2412,25 @@ static struct dw_mci_board *dw_mci_parse_dt(struct dw_mci *host)
>  	if (of_find_property(np, "caps2-mmc-hs200-1_2v", NULL))
>  		pdata->caps2 |= MMC_CAP2_HS200_1_2V_SDR;
>  
> +	/* Check for the "samsung,dw-mshc-sdr-timing" and the
> +	 * "samsung,dw-mshc-ddr-timing" bindings as this will tell us if we
> +	 * can safely set the SDMMC_CMD_USE_HOLD_REG bit. The second paramater
> +	 * in these 2 bindings is the value of the cclk_in_drv. If cclk_in_drv
> +	 * is 0, we cannot set the SDMMC_CMD_USE_HOLD_REG bit. The default
> +	 * behavior will be to set cclk_in_drv, as some platforms do not have
> +	 * to set the sdr or ddr timing parameters.
> +	 */
> +	sdr_timing[1] = ddr_timing[1] = 1;
> +	of_property_read_u32_array(np,
> +			"samsung,dw-mshc-sdr-timing", sdr_timing, 2);
> +
> +	of_property_read_u32_array(np,
> +			"samsung,dw-mshc-ddr-timing", ddr_timing, 2);
> +
> +	pdata->cclk_in_drv = 1;
> +	if ((sdr_timing[1] == 0) || (ddr_timing[1] == 0))
> +		pdata->cclk_in_drv = 0;
> +
>  	return pdata;
>  }
>  
> @@ -2495,6 +2537,8 @@ int dw_mci_probe(struct dw_mci *host)
>  		goto err_regulator;
>  	}
>  
> +	host->cclk_in_drv = host->pdata->cclk_in_drv;
> +
>  	host->quirks = host->pdata->quirks;
>  
>  	spin_lock_init(&host->lock);
> diff --git a/include/linux/mmc/dw_mmc.h b/include/linux/mmc/dw_mmc.h
> index 6ce7d2c..1da20ce 100644
> --- a/include/linux/mmc/dw_mmc.h
> +++ b/include/linux/mmc/dw_mmc.h
> @@ -191,6 +191,8 @@ struct dw_mci {
>  	struct regulator	*vmmc;	/* Power regulator */
>  	unsigned long		irq_flags; /* IRQ flags */
>  	int			irq;
> +	bool			cclk_in_drv;
> +	bool			use_hold_reg;
>  };
>  
>  /* DMA ops for Internal/External DMAC interface */
> @@ -238,6 +240,7 @@ struct dw_mci_board {
>  	u32 caps;	/* Capabilities */
>  	u32 caps2;	/* More capabilities */
>  	u32 pm_caps;	/* PM capabilities */
> +	bool cclk_in_drv;	/*cclk_in_drv phase shift */
>  	/*
>  	 * Override fifo depth. If 0, autodetect it from the FIFOTH register,
>  	 * but note that this may not be reliable after a bootloader has used
> 

--
To unsubscribe from this list: send the line "unsubscribe linux-mmc" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Dinh Nguyen Dec. 9, 2013, 3:26 a.m. UTC | #3
Hi Jaehoon,

On 12/8/13 7:24 PM, Jaehoon Chung wrote:
> Hi Dinh,
>
> I known there is IMPLEMENT_HOLD_REG[22] at HCON register.
> It didn't affect with this register?
The current dw_mmc code does not do any check for this bit in the HCON
register. But I'll add the check in v3:

Dinh
>
> Best Regards,
> Jaehoon Chung
>
> On 12/07/2013 12:20 PM, dinguyen@altera.com wrote:
>> From: Dinh Nguyen <dinguyen@altera.com>
>>
>> This patch will enable the SDMMC_CMD_USE_HOLD_REG bit when the slot is
>> operating all timing modes, except for SDR50, DDR50, SDR104, and MMC_HS200.
>>
>> According to the Synopsys databook :"To meet the relatively high Input Hold
>> Time requirement for SDR12, SDR25, and other MMC speed modes, you should
>> program bit[29]use_hold_Reg of the CMD register to 1'b1;"..."However, for
>> the higher speed modes of SDR104, SDR50 and DDR50, you can meet the much
>> smaller Input Hold Time requirement of 0.8ns by bypassing the Hold Register
>> (Path A in Figure 10-8, programming CMD.use_hold_reg = 1'b0) and then adding
>> delay elements on the output path as indicated.
>>
>> Also, "Never set CMD.use_hold_reg = 1 and cclk_in_drv phase shift to 0 at the
>> same time. This would add an extra one-cycle delay on the output path, resulting
>> in incorrect behavior."
>>
>> This information is taking from the v2.50a of the Synopsys Designware Cores
>> Mobile Storage Host Databook.
>>
>> Signed-off-by: Dinh Nguyen <dinguyen@altera.com>
>> ---
>> v2: Add check for cclk_in_drv phase shift in conjunction with use_hold_reg.
>> ---
>>  drivers/mmc/host/dw_mmc.c  |   44 ++++++++++++++++++++++++++++++++++++++++++++
>>  include/linux/mmc/dw_mmc.h |    3 +++
>>  2 files changed, 47 insertions(+)
>>
>> diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c
>> index 4bce0de..9f5492b 100644
>> --- a/drivers/mmc/host/dw_mmc.c
>> +++ b/drivers/mmc/host/dw_mmc.c
>> @@ -279,6 +279,9 @@ static u32 dw_mci_prepare_command(struct mmc_host *mmc, struct mmc_command *cmd)
>>  			cmdr |= SDMMC_CMD_DAT_WR;
>>  	}
>>  
>> +	if (slot->host->use_hold_reg)
>> +		cmdr |= SDMMC_CMD_USE_HOLD_REG;
>> +
>>  	if (drv_data && drv_data->prepare_command)
>>  		drv_data->prepare_command(slot->host, &cmdr);
>>  
>> @@ -969,6 +972,24 @@ static void dw_mci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
>>  	mci_writel(slot->host, UHS_REG, regs);
>>  	slot->host->timing = ios->timing;
>>  
>> +	/* Per Synopsys spec, use_hold_reg should be set for all modes except for
>> +	 * high-speed SDR50, DDR50, SDR104, and MMC_HS200. However, use_hold_reg
>> +	 * should be cleared if the cclk_in_drv is 0.
>> +	 */
>> +	switch (slot->host->timing) {
>> +	case MMC_TIMING_UHS_SDR50:
>> +	case MMC_TIMING_UHS_SDR104:
>> +	case MMC_TIMING_UHS_DDR50:
>> +	case MMC_TIMING_MMC_HS200:
>> +		slot->host->use_hold_reg = 0;
>> +		break;
>> +	default:
>> +		slot->host->use_hold_reg = 1;
>> +	}
>> +
>> +	if (slot->host->cclk_in_drv == 0)
>> +		slot->host->use_hold_reg = 0;
>> +
>>  	/*
>>  	 * Use mirror of ios->clock to prevent race with mmc
>>  	 * core ios update when finding the minimum.
>> @@ -2339,6 +2360,8 @@ static struct dw_mci_board *dw_mci_parse_dt(struct dw_mci *host)
>>  	const struct dw_mci_drv_data *drv_data = host->drv_data;
>>  	int idx, ret;
>>  	u32 clock_frequency;
>> +	int sdr_timing[2];
>> +	int ddr_timing[2];
>>  
>>  	pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
>>  	if (!pdata) {
>> @@ -2389,6 +2412,25 @@ static struct dw_mci_board *dw_mci_parse_dt(struct dw_mci *host)
>>  	if (of_find_property(np, "caps2-mmc-hs200-1_2v", NULL))
>>  		pdata->caps2 |= MMC_CAP2_HS200_1_2V_SDR;
>>  
>> +	/* Check for the "samsung,dw-mshc-sdr-timing" and the
>> +	 * "samsung,dw-mshc-ddr-timing" bindings as this will tell us if we
>> +	 * can safely set the SDMMC_CMD_USE_HOLD_REG bit. The second paramater
>> +	 * in these 2 bindings is the value of the cclk_in_drv. If cclk_in_drv
>> +	 * is 0, we cannot set the SDMMC_CMD_USE_HOLD_REG bit. The default
>> +	 * behavior will be to set cclk_in_drv, as some platforms do not have
>> +	 * to set the sdr or ddr timing parameters.
>> +	 */
>> +	sdr_timing[1] = ddr_timing[1] = 1;
>> +	of_property_read_u32_array(np,
>> +			"samsung,dw-mshc-sdr-timing", sdr_timing, 2);
>> +
>> +	of_property_read_u32_array(np,
>> +			"samsung,dw-mshc-ddr-timing", ddr_timing, 2);
>> +
>> +	pdata->cclk_in_drv = 1;
>> +	if ((sdr_timing[1] == 0) || (ddr_timing[1] == 0))
>> +		pdata->cclk_in_drv = 0;
>> +
>>  	return pdata;
>>  }
>>  
>> @@ -2495,6 +2537,8 @@ int dw_mci_probe(struct dw_mci *host)
>>  		goto err_regulator;
>>  	}
>>  
>> +	host->cclk_in_drv = host->pdata->cclk_in_drv;
>> +
>>  	host->quirks = host->pdata->quirks;
>>  
>>  	spin_lock_init(&host->lock);
>> diff --git a/include/linux/mmc/dw_mmc.h b/include/linux/mmc/dw_mmc.h
>> index 6ce7d2c..1da20ce 100644
>> --- a/include/linux/mmc/dw_mmc.h
>> +++ b/include/linux/mmc/dw_mmc.h
>> @@ -191,6 +191,8 @@ struct dw_mci {
>>  	struct regulator	*vmmc;	/* Power regulator */
>>  	unsigned long		irq_flags; /* IRQ flags */
>>  	int			irq;
>> +	bool			cclk_in_drv;
>> +	bool			use_hold_reg;
>>  };
>>  
>>  /* DMA ops for Internal/External DMAC interface */
>> @@ -238,6 +240,7 @@ struct dw_mci_board {
>>  	u32 caps;	/* Capabilities */
>>  	u32 caps2;	/* More capabilities */
>>  	u32 pm_caps;	/* PM capabilities */
>> +	bool cclk_in_drv;	/*cclk_in_drv phase shift */
>>  	/*
>>  	 * Override fifo depth. If 0, autodetect it from the FIFOTH register,
>>  	 * but note that this may not be reliable after a bootloader has used
>>

--
To unsubscribe from this list: send the line "unsubscribe linux-mmc" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
diff mbox

Patch

diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c
index 4bce0de..9f5492b 100644
--- a/drivers/mmc/host/dw_mmc.c
+++ b/drivers/mmc/host/dw_mmc.c
@@ -279,6 +279,9 @@  static u32 dw_mci_prepare_command(struct mmc_host *mmc, struct mmc_command *cmd)
 			cmdr |= SDMMC_CMD_DAT_WR;
 	}
 
+	if (slot->host->use_hold_reg)
+		cmdr |= SDMMC_CMD_USE_HOLD_REG;
+
 	if (drv_data && drv_data->prepare_command)
 		drv_data->prepare_command(slot->host, &cmdr);
 
@@ -969,6 +972,24 @@  static void dw_mci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
 	mci_writel(slot->host, UHS_REG, regs);
 	slot->host->timing = ios->timing;
 
+	/* Per Synopsys spec, use_hold_reg should be set for all modes except for
+	 * high-speed SDR50, DDR50, SDR104, and MMC_HS200. However, use_hold_reg
+	 * should be cleared if the cclk_in_drv is 0.
+	 */
+	switch (slot->host->timing) {
+	case MMC_TIMING_UHS_SDR50:
+	case MMC_TIMING_UHS_SDR104:
+	case MMC_TIMING_UHS_DDR50:
+	case MMC_TIMING_MMC_HS200:
+		slot->host->use_hold_reg = 0;
+		break;
+	default:
+		slot->host->use_hold_reg = 1;
+	}
+
+	if (slot->host->cclk_in_drv == 0)
+		slot->host->use_hold_reg = 0;
+
 	/*
 	 * Use mirror of ios->clock to prevent race with mmc
 	 * core ios update when finding the minimum.
@@ -2339,6 +2360,8 @@  static struct dw_mci_board *dw_mci_parse_dt(struct dw_mci *host)
 	const struct dw_mci_drv_data *drv_data = host->drv_data;
 	int idx, ret;
 	u32 clock_frequency;
+	int sdr_timing[2];
+	int ddr_timing[2];
 
 	pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
 	if (!pdata) {
@@ -2389,6 +2412,25 @@  static struct dw_mci_board *dw_mci_parse_dt(struct dw_mci *host)
 	if (of_find_property(np, "caps2-mmc-hs200-1_2v", NULL))
 		pdata->caps2 |= MMC_CAP2_HS200_1_2V_SDR;
 
+	/* Check for the "samsung,dw-mshc-sdr-timing" and the
+	 * "samsung,dw-mshc-ddr-timing" bindings as this will tell us if we
+	 * can safely set the SDMMC_CMD_USE_HOLD_REG bit. The second paramater
+	 * in these 2 bindings is the value of the cclk_in_drv. If cclk_in_drv
+	 * is 0, we cannot set the SDMMC_CMD_USE_HOLD_REG bit. The default
+	 * behavior will be to set cclk_in_drv, as some platforms do not have
+	 * to set the sdr or ddr timing parameters.
+	 */
+	sdr_timing[1] = ddr_timing[1] = 1;
+	of_property_read_u32_array(np,
+			"samsung,dw-mshc-sdr-timing", sdr_timing, 2);
+
+	of_property_read_u32_array(np,
+			"samsung,dw-mshc-ddr-timing", ddr_timing, 2);
+
+	pdata->cclk_in_drv = 1;
+	if ((sdr_timing[1] == 0) || (ddr_timing[1] == 0))
+		pdata->cclk_in_drv = 0;
+
 	return pdata;
 }
 
@@ -2495,6 +2537,8 @@  int dw_mci_probe(struct dw_mci *host)
 		goto err_regulator;
 	}
 
+	host->cclk_in_drv = host->pdata->cclk_in_drv;
+
 	host->quirks = host->pdata->quirks;
 
 	spin_lock_init(&host->lock);
diff --git a/include/linux/mmc/dw_mmc.h b/include/linux/mmc/dw_mmc.h
index 6ce7d2c..1da20ce 100644
--- a/include/linux/mmc/dw_mmc.h
+++ b/include/linux/mmc/dw_mmc.h
@@ -191,6 +191,8 @@  struct dw_mci {
 	struct regulator	*vmmc;	/* Power regulator */
 	unsigned long		irq_flags; /* IRQ flags */
 	int			irq;
+	bool			cclk_in_drv;
+	bool			use_hold_reg;
 };
 
 /* DMA ops for Internal/External DMAC interface */
@@ -238,6 +240,7 @@  struct dw_mci_board {
 	u32 caps;	/* Capabilities */
 	u32 caps2;	/* More capabilities */
 	u32 pm_caps;	/* PM capabilities */
+	bool cclk_in_drv;	/*cclk_in_drv phase shift */
 	/*
 	 * Override fifo depth. If 0, autodetect it from the FIFOTH register,
 	 * but note that this may not be reliable after a bootloader has used