diff mbox

[U-Boot] mmc: sdhci: Add the programmable clock mode support

Message ID 1466387926-3348-1-git-send-email-wenyou.yang@atmel.com
State Superseded
Delegated to: Jaehoon Chung
Headers show

Commit Message

Wenyou Yang June 20, 2016, 1:58 a.m. UTC
Add the programmable clock mode for the clock generator.

Signed-off-by: Wenyou Yang <wenyou.yang@atmel.com>
---

 drivers/mmc/sdhci.c | 49 +++++++++++++++++++++++++++++++++++++++----------
 include/sdhci.h     | 15 +++++++++++++++
 2 files changed, 54 insertions(+), 10 deletions(-)

Comments

Andreas Bießmann Aug. 14, 2016, 4:17 p.m. UTC | #1
Dear Pantelis,

On 20.06.16 03:58, Wenyou Yang wrote:
> Add the programmable clock mode for the clock generator.
> 

this one is delegated to me, but I think it is out of my scope. Could
you please have a look?

Andreas

> Signed-off-by: Wenyou Yang <wenyou.yang@atmel.com>
> ---
> 
>  drivers/mmc/sdhci.c | 49 +++++++++++++++++++++++++++++++++++++++----------
>  include/sdhci.h     | 15 +++++++++++++++
>  2 files changed, 54 insertions(+), 10 deletions(-)
> 
> diff --git a/drivers/mmc/sdhci.c b/drivers/mmc/sdhci.c
> index 5c71ab8..ee6d4a1 100644
> --- a/drivers/mmc/sdhci.c
> +++ b/drivers/mmc/sdhci.c
> @@ -286,7 +286,7 @@ static int sdhci_send_command(struct mmc *mmc, struct mmc_cmd *cmd,
>  static int sdhci_set_clock(struct mmc *mmc, unsigned int clock)
>  {
>  	struct sdhci_host *host = mmc->priv;
> -	unsigned int div, clk, timeout, reg;
> +	unsigned int div, clk = 0, timeout, reg;
>  
>  	/* Wait max 20 ms */
>  	timeout = 200;
> @@ -310,14 +310,35 @@ static int sdhci_set_clock(struct mmc *mmc, unsigned int clock)
>  		return 0;
>  
>  	if (SDHCI_GET_VERSION(host) >= SDHCI_SPEC_300) {
> -		/* Version 3.00 divisors must be a multiple of 2. */
> -		if (mmc->cfg->f_max <= clock)
> -			div = 1;
> -		else {
> -			for (div = 2; div < SDHCI_MAX_DIV_SPEC_300; div += 2) {
> -				if ((mmc->cfg->f_max / div) <= clock)
> +		/*
> +		 * Check if the Host Controller supports Programmable Clock
> +		 * Mode.
> +		 */
> +		if (host->clk_mul) {
> +			for (div = 1; div <= 1024; div++) {
> +				if ((mmc->cfg->f_max * host->clk_mul / div)
> +					<= clock)
>  					break;
>  			}
> +
> +			/*
> +			 * Set Programmable Clock Mode in the Clock
> +			 * Control register.
> +			 */
> +			clk = SDHCI_PROG_CLOCK_MODE;
> +			div--;
> +		} else {
> +			/* Version 3.00 divisors must be a multiple of 2. */
> +			if (mmc->cfg->f_max <= clock) {
> +				div = 1;
> +			} else {
> +				for (div = 2;
> +				     div < SDHCI_MAX_DIV_SPEC_300; div += 2) {
> +					if ((mmc->cfg->f_max / div) <= clock)
> +						break;
> +				}
> +			}
> +			div >>= 1;
>  		}
>  	} else {
>  		/* Version 2.00 divisors must be a power of 2. */
> @@ -325,13 +346,13 @@ static int sdhci_set_clock(struct mmc *mmc, unsigned int clock)
>  			if ((mmc->cfg->f_max / div) <= clock)
>  				break;
>  		}
> +		div >>= 1;
>  	}
> -	div >>= 1;
>  
>  	if (host->set_clock)
>  		host->set_clock(host->index, div);
>  
> -	clk = (div & SDHCI_DIV_MASK) << SDHCI_DIVIDER_SHIFT;
> +	clk |= (div & SDHCI_DIV_MASK) << SDHCI_DIVIDER_SHIFT;
>  	clk |= ((div & SDHCI_DIV_HI_MASK) >> SDHCI_DIV_MASK_LEN)
>  		<< SDHCI_DIVIDER_HI_SHIFT;
>  	clk |= SDHCI_CLOCK_INT_EN;
> @@ -480,7 +501,7 @@ static const struct mmc_ops sdhci_ops = {
>  
>  int add_sdhci(struct sdhci_host *host, u32 max_clk, u32 min_clk)
>  {
> -	unsigned int caps;
> +	unsigned int caps, caps_1;
>  
>  	host->cfg.name = host->name;
>  	host->cfg.ops = &sdhci_ops;
> @@ -546,6 +567,14 @@ int add_sdhci(struct sdhci_host *host, u32 max_clk, u32 min_clk)
>  
>  	host->cfg.b_max = CONFIG_SYS_MMC_MAX_BLK_COUNT;
>  
> +	/*
> +	 * In case of Host Controller v3.00, find out whether clock
> +	 * multiplier is supported.
> +	 */
> +	caps_1 = sdhci_readl(host, SDHCI_CAPABILITIES_1);
> +	host->clk_mul = (caps_1 & SDHCI_CLOCK_MUL_MASK) >>
> +			SDHCI_CLOCK_MUL_SHIFT;
> +
>  	sdhci_reset(host, SDHCI_RESET_ALL);
>  
>  	host->mmc = mmc_create(&host->cfg, host);
> diff --git a/include/sdhci.h b/include/sdhci.h
> index e0f6667..5abe0a2 100644
> --- a/include/sdhci.h
> +++ b/include/sdhci.h
> @@ -97,6 +97,7 @@
>  #define  SDHCI_DIV_MASK	0xFF
>  #define  SDHCI_DIV_MASK_LEN	8
>  #define  SDHCI_DIV_HI_MASK	0x300
> +#define  SDHCI_PROG_CLOCK_MODE  0x0020
>  #define  SDHCI_CLOCK_CARD_EN	0x0004
>  #define  SDHCI_CLOCK_INT_STABLE	0x0002
>  #define  SDHCI_CLOCK_INT_EN	0x0001
> @@ -166,6 +167,19 @@
>  #define  SDHCI_CAN_64BIT	0x10000000
>  
>  #define SDHCI_CAPABILITIES_1	0x44
> +#define  SDHCI_SUPPORT_SDR50	0x00000001
> +#define  SDHCI_SUPPORT_SDR104	0x00000002
> +#define  SDHCI_SUPPORT_DDR50	0x00000004
> +#define  SDHCI_DRIVER_TYPE_A	0x00000010
> +#define  SDHCI_DRIVER_TYPE_C	0x00000020
> +#define  SDHCI_DRIVER_TYPE_D	0x00000040
> +#define  SDHCI_RETUNING_TIMER_COUNT_MASK	0x00000F00
> +#define  SDHCI_RETUNING_TIMER_COUNT_SHIFT	8
> +#define  SDHCI_USE_SDR50_TUNING			0x00002000
> +#define  SDHCI_RETUNING_MODE_MASK		0x0000C000
> +#define  SDHCI_RETUNING_MODE_SHIFT		14
> +#define  SDHCI_CLOCK_MUL_MASK	0x00FF0000
> +#define  SDHCI_CLOCK_MUL_SHIFT	16
>  
>  #define SDHCI_MAX_CURRENT	0x48
>  
> @@ -240,6 +254,7 @@ struct sdhci_host {
>  	unsigned int quirks;
>  	unsigned int host_caps;
>  	unsigned int version;
> +	unsigned int clk_mul;   /* Clock Muliplier value */
>  	unsigned int clock;
>  	struct mmc *mmc;
>  	const struct sdhci_ops *ops;
>
Jaehoon Chung Aug. 16, 2016, 1:02 a.m. UTC | #2
Hi Andreas,

On 08/15/2016 01:17 AM, Andreas Bießmann wrote:
> Dear Pantelis,
> 
> On 20.06.16 03:58, Wenyou Yang wrote:
>> Add the programmable clock mode for the clock generator.
>>
> 
> this one is delegated to me, but I think it is out of my scope. Could
> you please have a look?

I will be delegated to me. I will check this patch.

Thanks.

Best Regards,
Jaehoon Chung

> 
> Andreas
> 
>> Signed-off-by: Wenyou Yang <wenyou.yang@atmel.com>
>> ---
>>
>>  drivers/mmc/sdhci.c | 49 +++++++++++++++++++++++++++++++++++++++----------
>>  include/sdhci.h     | 15 +++++++++++++++
>>  2 files changed, 54 insertions(+), 10 deletions(-)
>>
>> diff --git a/drivers/mmc/sdhci.c b/drivers/mmc/sdhci.c
>> index 5c71ab8..ee6d4a1 100644
>> --- a/drivers/mmc/sdhci.c
>> +++ b/drivers/mmc/sdhci.c
>> @@ -286,7 +286,7 @@ static int sdhci_send_command(struct mmc *mmc, struct mmc_cmd *cmd,
>>  static int sdhci_set_clock(struct mmc *mmc, unsigned int clock)
>>  {
>>  	struct sdhci_host *host = mmc->priv;
>> -	unsigned int div, clk, timeout, reg;
>> +	unsigned int div, clk = 0, timeout, reg;
>>  
>>  	/* Wait max 20 ms */
>>  	timeout = 200;
>> @@ -310,14 +310,35 @@ static int sdhci_set_clock(struct mmc *mmc, unsigned int clock)
>>  		return 0;
>>  
>>  	if (SDHCI_GET_VERSION(host) >= SDHCI_SPEC_300) {
>> -		/* Version 3.00 divisors must be a multiple of 2. */
>> -		if (mmc->cfg->f_max <= clock)
>> -			div = 1;
>> -		else {
>> -			for (div = 2; div < SDHCI_MAX_DIV_SPEC_300; div += 2) {
>> -				if ((mmc->cfg->f_max / div) <= clock)
>> +		/*
>> +		 * Check if the Host Controller supports Programmable Clock
>> +		 * Mode.
>> +		 */
>> +		if (host->clk_mul) {
>> +			for (div = 1; div <= 1024; div++) {
>> +				if ((mmc->cfg->f_max * host->clk_mul / div)
>> +					<= clock)
>>  					break;
>>  			}
>> +
>> +			/*
>> +			 * Set Programmable Clock Mode in the Clock
>> +			 * Control register.
>> +			 */
>> +			clk = SDHCI_PROG_CLOCK_MODE;
>> +			div--;
>> +		} else {
>> +			/* Version 3.00 divisors must be a multiple of 2. */
>> +			if (mmc->cfg->f_max <= clock) {
>> +				div = 1;
>> +			} else {
>> +				for (div = 2;
>> +				     div < SDHCI_MAX_DIV_SPEC_300; div += 2) {
>> +					if ((mmc->cfg->f_max / div) <= clock)
>> +						break;
>> +				}
>> +			}
>> +			div >>= 1;
>>  		}
>>  	} else {
>>  		/* Version 2.00 divisors must be a power of 2. */
>> @@ -325,13 +346,13 @@ static int sdhci_set_clock(struct mmc *mmc, unsigned int clock)
>>  			if ((mmc->cfg->f_max / div) <= clock)
>>  				break;
>>  		}
>> +		div >>= 1;
>>  	}
>> -	div >>= 1;
>>  
>>  	if (host->set_clock)
>>  		host->set_clock(host->index, div);
>>  
>> -	clk = (div & SDHCI_DIV_MASK) << SDHCI_DIVIDER_SHIFT;
>> +	clk |= (div & SDHCI_DIV_MASK) << SDHCI_DIVIDER_SHIFT;
>>  	clk |= ((div & SDHCI_DIV_HI_MASK) >> SDHCI_DIV_MASK_LEN)
>>  		<< SDHCI_DIVIDER_HI_SHIFT;
>>  	clk |= SDHCI_CLOCK_INT_EN;
>> @@ -480,7 +501,7 @@ static const struct mmc_ops sdhci_ops = {
>>  
>>  int add_sdhci(struct sdhci_host *host, u32 max_clk, u32 min_clk)
>>  {
>> -	unsigned int caps;
>> +	unsigned int caps, caps_1;
>>  
>>  	host->cfg.name = host->name;
>>  	host->cfg.ops = &sdhci_ops;
>> @@ -546,6 +567,14 @@ int add_sdhci(struct sdhci_host *host, u32 max_clk, u32 min_clk)
>>  
>>  	host->cfg.b_max = CONFIG_SYS_MMC_MAX_BLK_COUNT;
>>  
>> +	/*
>> +	 * In case of Host Controller v3.00, find out whether clock
>> +	 * multiplier is supported.
>> +	 */
>> +	caps_1 = sdhci_readl(host, SDHCI_CAPABILITIES_1);
>> +	host->clk_mul = (caps_1 & SDHCI_CLOCK_MUL_MASK) >>
>> +			SDHCI_CLOCK_MUL_SHIFT;
>> +
>>  	sdhci_reset(host, SDHCI_RESET_ALL);
>>  
>>  	host->mmc = mmc_create(&host->cfg, host);
>> diff --git a/include/sdhci.h b/include/sdhci.h
>> index e0f6667..5abe0a2 100644
>> --- a/include/sdhci.h
>> +++ b/include/sdhci.h
>> @@ -97,6 +97,7 @@
>>  #define  SDHCI_DIV_MASK	0xFF
>>  #define  SDHCI_DIV_MASK_LEN	8
>>  #define  SDHCI_DIV_HI_MASK	0x300
>> +#define  SDHCI_PROG_CLOCK_MODE  0x0020
>>  #define  SDHCI_CLOCK_CARD_EN	0x0004
>>  #define  SDHCI_CLOCK_INT_STABLE	0x0002
>>  #define  SDHCI_CLOCK_INT_EN	0x0001
>> @@ -166,6 +167,19 @@
>>  #define  SDHCI_CAN_64BIT	0x10000000
>>  
>>  #define SDHCI_CAPABILITIES_1	0x44
>> +#define  SDHCI_SUPPORT_SDR50	0x00000001
>> +#define  SDHCI_SUPPORT_SDR104	0x00000002
>> +#define  SDHCI_SUPPORT_DDR50	0x00000004
>> +#define  SDHCI_DRIVER_TYPE_A	0x00000010
>> +#define  SDHCI_DRIVER_TYPE_C	0x00000020
>> +#define  SDHCI_DRIVER_TYPE_D	0x00000040
>> +#define  SDHCI_RETUNING_TIMER_COUNT_MASK	0x00000F00
>> +#define  SDHCI_RETUNING_TIMER_COUNT_SHIFT	8
>> +#define  SDHCI_USE_SDR50_TUNING			0x00002000
>> +#define  SDHCI_RETUNING_MODE_MASK		0x0000C000
>> +#define  SDHCI_RETUNING_MODE_SHIFT		14
>> +#define  SDHCI_CLOCK_MUL_MASK	0x00FF0000
>> +#define  SDHCI_CLOCK_MUL_SHIFT	16
>>  
>>  #define SDHCI_MAX_CURRENT	0x48
>>  
>> @@ -240,6 +254,7 @@ struct sdhci_host {
>>  	unsigned int quirks;
>>  	unsigned int host_caps;
>>  	unsigned int version;
>> +	unsigned int clk_mul;   /* Clock Muliplier value */
>>  	unsigned int clock;
>>  	struct mmc *mmc;
>>  	const struct sdhci_ops *ops;
>>
> _______________________________________________
> U-Boot mailing list
> U-Boot@lists.denx.de
> http://lists.denx.de/mailman/listinfo/u-boot
> 
> 
>
Jaehoon Chung Aug. 22, 2016, 2:24 a.m. UTC | #3
Hi Wenyou,

On 08/16/2016 10:02 AM, Jaehoon Chung wrote:
> Hi Andreas,
> 
> On 08/15/2016 01:17 AM, Andreas Bießmann wrote:
>> Dear Pantelis,
>>
>> On 20.06.16 03:58, Wenyou Yang wrote:
>>> Add the programmable clock mode for the clock generator.
>>>

Sorry for reviewing too late. 
First, this patch needs to rebase on latest u-boot-mmc.
Then I will check this patch on my boards..

>>
>> this one is delegated to me, but I think it is out of my scope. Could
>> you please have a look?
> 
> I will be delegated to me. I will check this patch.
> 
> Thanks.
> 
> Best Regards,
> Jaehoon Chung
> 
>>
>> Andreas
>>
>>> Signed-off-by: Wenyou Yang <wenyou.yang@atmel.com>
>>> ---
>>>
>>>  drivers/mmc/sdhci.c | 49 +++++++++++++++++++++++++++++++++++++++----------
>>>  include/sdhci.h     | 15 +++++++++++++++
>>>  2 files changed, 54 insertions(+), 10 deletions(-)
>>>
>>> diff --git a/drivers/mmc/sdhci.c b/drivers/mmc/sdhci.c
>>> index 5c71ab8..ee6d4a1 100644
>>> --- a/drivers/mmc/sdhci.c
>>> +++ b/drivers/mmc/sdhci.c
>>> @@ -286,7 +286,7 @@ static int sdhci_send_command(struct mmc *mmc, struct mmc_cmd *cmd,
>>>  static int sdhci_set_clock(struct mmc *mmc, unsigned int clock)
>>>  {
>>>  	struct sdhci_host *host = mmc->priv;
>>> -	unsigned int div, clk, timeout, reg;
>>> +	unsigned int div, clk = 0, timeout, reg;
>>>  
>>>  	/* Wait max 20 ms */
>>>  	timeout = 200;
>>> @@ -310,14 +310,35 @@ static int sdhci_set_clock(struct mmc *mmc, unsigned int clock)
>>>  		return 0;
>>>  
>>>  	if (SDHCI_GET_VERSION(host) >= SDHCI_SPEC_300) {
>>> -		/* Version 3.00 divisors must be a multiple of 2. */
>>> -		if (mmc->cfg->f_max <= clock)
>>> -			div = 1;
>>> -		else {
>>> -			for (div = 2; div < SDHCI_MAX_DIV_SPEC_300; div += 2) {
>>> -				if ((mmc->cfg->f_max / div) <= clock)
>>> +		/*
>>> +		 * Check if the Host Controller supports Programmable Clock
>>> +		 * Mode.
>>> +		 */
>>> +		if (host->clk_mul) {
>>> +			for (div = 1; div <= 1024; div++) {
>>> +				if ((mmc->cfg->f_max * host->clk_mul / div)
>>> +					<= clock)
>>>  					break;
>>>  			}
>>> +
>>> +			/*
>>> +			 * Set Programmable Clock Mode in the Clock
>>> +			 * Control register.
>>> +			 */
>>> +			clk = SDHCI_PROG_CLOCK_MODE;
>>> +			div--;
>>> +		} else {
>>> +			/* Version 3.00 divisors must be a multiple of 2. */
>>> +			if (mmc->cfg->f_max <= clock) {
>>> +				div = 1;
>>> +			} else {
>>> +				for (div = 2;
>>> +				     div < SDHCI_MAX_DIV_SPEC_300; div += 2) {
>>> +					if ((mmc->cfg->f_max / div) <= clock)
>>> +						break;
>>> +				}
>>> +			}
>>> +			div >>= 1;
>>>  		}
>>>  	} else {
>>>  		/* Version 2.00 divisors must be a power of 2. */
>>> @@ -325,13 +346,13 @@ static int sdhci_set_clock(struct mmc *mmc, unsigned int clock)
>>>  			if ((mmc->cfg->f_max / div) <= clock)
>>>  				break;
>>>  		}
>>> +		div >>= 1;
>>>  	}
>>> -	div >>= 1;
>>>  
>>>  	if (host->set_clock)
>>>  		host->set_clock(host->index, div);
>>>  
>>> -	clk = (div & SDHCI_DIV_MASK) << SDHCI_DIVIDER_SHIFT;
>>> +	clk |= (div & SDHCI_DIV_MASK) << SDHCI_DIVIDER_SHIFT;
>>>  	clk |= ((div & SDHCI_DIV_HI_MASK) >> SDHCI_DIV_MASK_LEN)
>>>  		<< SDHCI_DIVIDER_HI_SHIFT;
>>>  	clk |= SDHCI_CLOCK_INT_EN;
>>> @@ -480,7 +501,7 @@ static const struct mmc_ops sdhci_ops = {
>>>  
>>>  int add_sdhci(struct sdhci_host *host, u32 max_clk, u32 min_clk)
>>>  {
>>> -	unsigned int caps;
>>> +	unsigned int caps, caps_1;
>>>  
>>>  	host->cfg.name = host->name;
>>>  	host->cfg.ops = &sdhci_ops;
>>> @@ -546,6 +567,14 @@ int add_sdhci(struct sdhci_host *host, u32 max_clk, u32 min_clk)
>>>  
>>>  	host->cfg.b_max = CONFIG_SYS_MMC_MAX_BLK_COUNT;
>>>  
>>> +	/*
>>> +	 * In case of Host Controller v3.00, find out whether clock
>>> +	 * multiplier is supported.
>>> +	 */
>>> +	caps_1 = sdhci_readl(host, SDHCI_CAPABILITIES_1);
>>> +	host->clk_mul = (caps_1 & SDHCI_CLOCK_MUL_MASK) >>
>>> +			SDHCI_CLOCK_MUL_SHIFT;
>>> +
>>>  	sdhci_reset(host, SDHCI_RESET_ALL);
>>>  
>>>  	host->mmc = mmc_create(&host->cfg, host);
>>> diff --git a/include/sdhci.h b/include/sdhci.h
>>> index e0f6667..5abe0a2 100644
>>> --- a/include/sdhci.h
>>> +++ b/include/sdhci.h
>>> @@ -97,6 +97,7 @@
>>>  #define  SDHCI_DIV_MASK	0xFF
>>>  #define  SDHCI_DIV_MASK_LEN	8
>>>  #define  SDHCI_DIV_HI_MASK	0x300
>>> +#define  SDHCI_PROG_CLOCK_MODE  0x0020
>>>  #define  SDHCI_CLOCK_CARD_EN	0x0004
>>>  #define  SDHCI_CLOCK_INT_STABLE	0x0002
>>>  #define  SDHCI_CLOCK_INT_EN	0x0001
>>> @@ -166,6 +167,19 @@
>>>  #define  SDHCI_CAN_64BIT	0x10000000
>>>  
>>>  #define SDHCI_CAPABILITIES_1	0x44
>>> +#define  SDHCI_SUPPORT_SDR50	0x00000001
>>> +#define  SDHCI_SUPPORT_SDR104	0x00000002
>>> +#define  SDHCI_SUPPORT_DDR50	0x00000004
>>> +#define  SDHCI_DRIVER_TYPE_A	0x00000010
>>> +#define  SDHCI_DRIVER_TYPE_C	0x00000020
>>> +#define  SDHCI_DRIVER_TYPE_D	0x00000040
>>> +#define  SDHCI_RETUNING_TIMER_COUNT_MASK	0x00000F00
>>> +#define  SDHCI_RETUNING_TIMER_COUNT_SHIFT	8
>>> +#define  SDHCI_USE_SDR50_TUNING			0x00002000
>>> +#define  SDHCI_RETUNING_MODE_MASK		0x0000C000
>>> +#define  SDHCI_RETUNING_MODE_SHIFT		14
>>> +#define  SDHCI_CLOCK_MUL_MASK	0x00FF0000
>>> +#define  SDHCI_CLOCK_MUL_SHIFT	16
>>>  
>>>  #define SDHCI_MAX_CURRENT	0x48
>>>  
>>> @@ -240,6 +254,7 @@ struct sdhci_host {
>>>  	unsigned int quirks;
>>>  	unsigned int host_caps;
>>>  	unsigned int version;
>>> +	unsigned int clk_mul;   /* Clock Muliplier value */

s/Muliplier/Multiplier

Best Regards,
Jaehoon Chung

>>>  	unsigned int clock;
>>>  	struct mmc *mmc;
>>>  	const struct sdhci_ops *ops;
>>>
>> _______________________________________________
>> U-Boot mailing list
>> U-Boot@lists.denx.de
>> http://lists.denx.de/mailman/listinfo/u-boot
>>
>>
>>
> 
> _______________________________________________
> U-Boot mailing list
> U-Boot@lists.denx.de
> http://lists.denx.de/mailman/listinfo/u-boot
> 
> 
>
diff mbox

Patch

diff --git a/drivers/mmc/sdhci.c b/drivers/mmc/sdhci.c
index 5c71ab8..ee6d4a1 100644
--- a/drivers/mmc/sdhci.c
+++ b/drivers/mmc/sdhci.c
@@ -286,7 +286,7 @@  static int sdhci_send_command(struct mmc *mmc, struct mmc_cmd *cmd,
 static int sdhci_set_clock(struct mmc *mmc, unsigned int clock)
 {
 	struct sdhci_host *host = mmc->priv;
-	unsigned int div, clk, timeout, reg;
+	unsigned int div, clk = 0, timeout, reg;
 
 	/* Wait max 20 ms */
 	timeout = 200;
@@ -310,14 +310,35 @@  static int sdhci_set_clock(struct mmc *mmc, unsigned int clock)
 		return 0;
 
 	if (SDHCI_GET_VERSION(host) >= SDHCI_SPEC_300) {
-		/* Version 3.00 divisors must be a multiple of 2. */
-		if (mmc->cfg->f_max <= clock)
-			div = 1;
-		else {
-			for (div = 2; div < SDHCI_MAX_DIV_SPEC_300; div += 2) {
-				if ((mmc->cfg->f_max / div) <= clock)
+		/*
+		 * Check if the Host Controller supports Programmable Clock
+		 * Mode.
+		 */
+		if (host->clk_mul) {
+			for (div = 1; div <= 1024; div++) {
+				if ((mmc->cfg->f_max * host->clk_mul / div)
+					<= clock)
 					break;
 			}
+
+			/*
+			 * Set Programmable Clock Mode in the Clock
+			 * Control register.
+			 */
+			clk = SDHCI_PROG_CLOCK_MODE;
+			div--;
+		} else {
+			/* Version 3.00 divisors must be a multiple of 2. */
+			if (mmc->cfg->f_max <= clock) {
+				div = 1;
+			} else {
+				for (div = 2;
+				     div < SDHCI_MAX_DIV_SPEC_300; div += 2) {
+					if ((mmc->cfg->f_max / div) <= clock)
+						break;
+				}
+			}
+			div >>= 1;
 		}
 	} else {
 		/* Version 2.00 divisors must be a power of 2. */
@@ -325,13 +346,13 @@  static int sdhci_set_clock(struct mmc *mmc, unsigned int clock)
 			if ((mmc->cfg->f_max / div) <= clock)
 				break;
 		}
+		div >>= 1;
 	}
-	div >>= 1;
 
 	if (host->set_clock)
 		host->set_clock(host->index, div);
 
-	clk = (div & SDHCI_DIV_MASK) << SDHCI_DIVIDER_SHIFT;
+	clk |= (div & SDHCI_DIV_MASK) << SDHCI_DIVIDER_SHIFT;
 	clk |= ((div & SDHCI_DIV_HI_MASK) >> SDHCI_DIV_MASK_LEN)
 		<< SDHCI_DIVIDER_HI_SHIFT;
 	clk |= SDHCI_CLOCK_INT_EN;
@@ -480,7 +501,7 @@  static const struct mmc_ops sdhci_ops = {
 
 int add_sdhci(struct sdhci_host *host, u32 max_clk, u32 min_clk)
 {
-	unsigned int caps;
+	unsigned int caps, caps_1;
 
 	host->cfg.name = host->name;
 	host->cfg.ops = &sdhci_ops;
@@ -546,6 +567,14 @@  int add_sdhci(struct sdhci_host *host, u32 max_clk, u32 min_clk)
 
 	host->cfg.b_max = CONFIG_SYS_MMC_MAX_BLK_COUNT;
 
+	/*
+	 * In case of Host Controller v3.00, find out whether clock
+	 * multiplier is supported.
+	 */
+	caps_1 = sdhci_readl(host, SDHCI_CAPABILITIES_1);
+	host->clk_mul = (caps_1 & SDHCI_CLOCK_MUL_MASK) >>
+			SDHCI_CLOCK_MUL_SHIFT;
+
 	sdhci_reset(host, SDHCI_RESET_ALL);
 
 	host->mmc = mmc_create(&host->cfg, host);
diff --git a/include/sdhci.h b/include/sdhci.h
index e0f6667..5abe0a2 100644
--- a/include/sdhci.h
+++ b/include/sdhci.h
@@ -97,6 +97,7 @@ 
 #define  SDHCI_DIV_MASK	0xFF
 #define  SDHCI_DIV_MASK_LEN	8
 #define  SDHCI_DIV_HI_MASK	0x300
+#define  SDHCI_PROG_CLOCK_MODE  0x0020
 #define  SDHCI_CLOCK_CARD_EN	0x0004
 #define  SDHCI_CLOCK_INT_STABLE	0x0002
 #define  SDHCI_CLOCK_INT_EN	0x0001
@@ -166,6 +167,19 @@ 
 #define  SDHCI_CAN_64BIT	0x10000000
 
 #define SDHCI_CAPABILITIES_1	0x44
+#define  SDHCI_SUPPORT_SDR50	0x00000001
+#define  SDHCI_SUPPORT_SDR104	0x00000002
+#define  SDHCI_SUPPORT_DDR50	0x00000004
+#define  SDHCI_DRIVER_TYPE_A	0x00000010
+#define  SDHCI_DRIVER_TYPE_C	0x00000020
+#define  SDHCI_DRIVER_TYPE_D	0x00000040
+#define  SDHCI_RETUNING_TIMER_COUNT_MASK	0x00000F00
+#define  SDHCI_RETUNING_TIMER_COUNT_SHIFT	8
+#define  SDHCI_USE_SDR50_TUNING			0x00002000
+#define  SDHCI_RETUNING_MODE_MASK		0x0000C000
+#define  SDHCI_RETUNING_MODE_SHIFT		14
+#define  SDHCI_CLOCK_MUL_MASK	0x00FF0000
+#define  SDHCI_CLOCK_MUL_SHIFT	16
 
 #define SDHCI_MAX_CURRENT	0x48
 
@@ -240,6 +254,7 @@  struct sdhci_host {
 	unsigned int quirks;
 	unsigned int host_caps;
 	unsigned int version;
+	unsigned int clk_mul;   /* Clock Muliplier value */
 	unsigned int clock;
 	struct mmc *mmc;
 	const struct sdhci_ops *ops;