mmc: sdhci: Add the programmable clock mode support

Add the programmable clock mode for the clock generator.

Signed-off-by: Wenyou Yang <wenyou.yang@atmel.com>
master
Wenyou Yang 8 years ago committed by Jaehoon Chung
parent e492dbb41e
commit 6dffdbc3a5
  1. 50
      drivers/mmc/sdhci.c
  2. 2
      include/sdhci.h

@ -294,7 +294,7 @@ static int sdhci_send_command(struct mmc *mmc, struct mmc_cmd *cmd,
static int sdhci_set_clock(struct mmc *mmc, unsigned int clock) static int sdhci_set_clock(struct mmc *mmc, unsigned int clock)
{ {
struct sdhci_host *host = mmc->priv; struct sdhci_host *host = mmc->priv;
unsigned int div, clk, timeout, reg; unsigned int div, clk = 0, timeout, reg;
/* Wait max 20 ms */ /* Wait max 20 ms */
timeout = 200; timeout = 200;
@ -318,14 +318,36 @@ static int sdhci_set_clock(struct mmc *mmc, unsigned int clock)
return 0; return 0;
if (SDHCI_GET_VERSION(host) >= SDHCI_SPEC_300) { if (SDHCI_GET_VERSION(host) >= SDHCI_SPEC_300) {
/* Version 3.00 divisors must be a multiple of 2. */ /*
if (mmc->cfg->f_max <= clock) * Check if the Host Controller supports Programmable Clock
div = 1; * Mode.
else { */
for (div = 2; div < SDHCI_MAX_DIV_SPEC_300; div += 2) { if (host->clk_mul) {
if ((mmc->cfg->f_max / div) <= clock) for (div = 1; div <= 1024; div++) {
if ((mmc->cfg->f_max * host->clk_mul / div)
<= clock)
break; 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 { } else {
/* Version 2.00 divisors must be a power of 2. */ /* Version 2.00 divisors must be a power of 2. */
@ -333,13 +355,13 @@ static int sdhci_set_clock(struct mmc *mmc, unsigned int clock)
if ((mmc->cfg->f_max / div) <= clock) if ((mmc->cfg->f_max / div) <= clock)
break; break;
} }
div >>= 1;
} }
div >>= 1;
if (host->set_clock) if (host->set_clock)
host->set_clock(host->index, div); 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) clk |= ((div & SDHCI_DIV_HI_MASK) >> SDHCI_DIV_MASK_LEN)
<< SDHCI_DIVIDER_HI_SHIFT; << SDHCI_DIVIDER_HI_SHIFT;
clk |= SDHCI_CLOCK_INT_EN; clk |= SDHCI_CLOCK_INT_EN;
@ -513,7 +535,7 @@ static const struct mmc_ops sdhci_ops = {
int sdhci_setup_cfg(struct mmc_config *cfg, struct sdhci_host *host, int sdhci_setup_cfg(struct mmc_config *cfg, struct sdhci_host *host,
u32 max_clk, u32 min_clk) u32 max_clk, u32 min_clk)
{ {
u32 caps; u32 caps, caps_1;
caps = sdhci_readl(host, SDHCI_CAPABILITIES); caps = sdhci_readl(host, SDHCI_CAPABILITIES);
@ -577,6 +599,14 @@ int sdhci_setup_cfg(struct mmc_config *cfg, struct sdhci_host *host,
cfg->b_max = CONFIG_SYS_MMC_MAX_BLK_COUNT; 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;
return 0; return 0;
} }

@ -97,6 +97,7 @@
#define SDHCI_DIV_MASK 0xFF #define SDHCI_DIV_MASK 0xFF
#define SDHCI_DIV_MASK_LEN 8 #define SDHCI_DIV_MASK_LEN 8
#define SDHCI_DIV_HI_MASK 0x300 #define SDHCI_DIV_HI_MASK 0x300
#define SDHCI_PROG_CLOCK_MODE 0x0020
#define SDHCI_CLOCK_CARD_EN 0x0004 #define SDHCI_CLOCK_CARD_EN 0x0004
#define SDHCI_CLOCK_INT_STABLE 0x0002 #define SDHCI_CLOCK_INT_STABLE 0x0002
#define SDHCI_CLOCK_INT_EN 0x0001 #define SDHCI_CLOCK_INT_EN 0x0001
@ -242,6 +243,7 @@ struct sdhci_host {
unsigned int quirks; unsigned int quirks;
unsigned int host_caps; unsigned int host_caps;
unsigned int version; unsigned int version;
unsigned int clk_mul; /* Clock Multiplier value */
unsigned int clock; unsigned int clock;
struct mmc *mmc; struct mmc *mmc;
const struct sdhci_ops *ops; const struct sdhci_ops *ops;

Loading…
Cancel
Save