mmc: sdhci: Add the programmable clock mode support
authorWenyou Yang <wenyou.yang@atmel.com>
Sun, 18 Sep 2016 01:01:22 +0000 (09:01 +0800)
committerJaehoon Chung <jh80.chung@samsung.com>
Mon, 19 Sep 2016 21:46:01 +0000 (06:46 +0900)
Add the programmable clock mode for the clock generator.

Signed-off-by: Wenyou Yang <wenyou.yang@atmel.com>
drivers/mmc/sdhci.c
include/sdhci.h

index 504f2d29accb55be8001ac61ef486836cb70fc7b..b2bf5a03fa84d9c1be6895bb0ef995cb5c7f6b94 100644 (file)
@@ -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)
 {
        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 @@ 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. */
@@ -333,13 +355,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;
@@ -513,7 +535,7 @@ static const struct mmc_ops sdhci_ops = {
 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 @@ int sdhci_setup_cfg(struct mmc_config *cfg, struct sdhci_host *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;
+
        return 0;
 }
 
index 6844c73bdc58b89af217f9ed80e3e369bee76ab7..144570f2be9662d43015f1a817adaa7ac6b97c79 100644 (file)
@@ -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 @@ struct sdhci_host {
        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;