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>
diff --git a/drivers/mmc/sdhci.c b/drivers/mmc/sdhci.c
index 504f2d2..b2bf5a0 100644
--- a/drivers/mmc/sdhci.c
+++ b/drivers/mmc/sdhci.c
@@ -294,7 +294,7 @@
 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;
@@ -318,14 +318,36 @@
 		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. */
@@ -333,13 +355,13 @@
 			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;
@@ -513,7 +535,7 @@
 int sdhci_setup_cfg(struct mmc_config *cfg, struct sdhci_host *host,
 		u32 max_clk, u32 min_clk)
 {
-	u32 caps;
+	u32 caps, caps_1;
 
 	caps = sdhci_readl(host, SDHCI_CAPABILITIES);
 
@@ -577,6 +599,14 @@
 
 	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;
 }
 
diff --git a/include/sdhci.h b/include/sdhci.h
index 6844c73..144570f 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
@@ -242,6 +243,7 @@
 	unsigned int quirks;
 	unsigned int host_caps;
 	unsigned int version;
+	unsigned int clk_mul;   /* Clock Multiplier value */
 	unsigned int clock;
 	struct mmc *mmc;
 	const struct sdhci_ops *ops;