mmc: sdhci: Add support for dt caps & caps mask
[platform/kernel/u-boot.git] / drivers / mmc / sdhci.c
index e2bb90a..fbc576f 100644 (file)
@@ -8,10 +8,12 @@
  */
 
 #include <common.h>
+#include <dm.h>
 #include <errno.h>
 #include <malloc.h>
 #include <mmc.h>
 #include <sdhci.h>
+#include <dm.h>
 
 #if defined(CONFIG_FIXED_SDHCI_ALIGNED_BUFFER)
 void *aligned_buffer = (void *)CONFIG_FIXED_SDHCI_ALIGNED_BUFFER;
@@ -408,7 +410,7 @@ static int sdhci_execute_tuning(struct udevice *dev, uint opcode)
        return 0;
 }
 #endif
-static int sdhci_set_clock(struct mmc *mmc, unsigned int clock)
+int sdhci_set_clock(struct mmc *mmc, unsigned int clock)
 {
        struct sdhci_host *host = mmc->priv;
        unsigned int div, clk = 0, timeout;
@@ -532,6 +534,34 @@ static void sdhci_set_power(struct sdhci_host *host, unsigned short power)
        sdhci_writeb(host, pwr, SDHCI_POWER_CONTROL);
 }
 
+void sdhci_set_uhs_timing(struct sdhci_host *host)
+{
+       struct mmc *mmc = (struct mmc *)host->mmc;
+       u32 reg;
+
+       reg = sdhci_readw(host, SDHCI_HOST_CONTROL2);
+       reg &= ~SDHCI_CTRL_UHS_MASK;
+
+       switch (mmc->selected_mode) {
+       case UHS_SDR50:
+       case MMC_HS_52:
+               reg |= SDHCI_CTRL_UHS_SDR50;
+               break;
+       case UHS_DDR50:
+       case MMC_DDR_52:
+               reg |= SDHCI_CTRL_UHS_DDR50;
+               break;
+       case UHS_SDR104:
+       case MMC_HS_200:
+               reg |= SDHCI_CTRL_UHS_SDR104;
+               break;
+       default:
+               reg |= SDHCI_CTRL_UHS_SDR12;
+       }
+
+       sdhci_writew(host, reg, SDHCI_HOST_CONTROL2);
+}
+
 #ifdef CONFIG_DM_MMC
 static int sdhci_set_ios(struct udevice *dev)
 {
@@ -582,7 +612,7 @@ static int sdhci_set_ios(struct mmc *mmc)
 
        /* If available, call the driver specific "post" set_ios() function */
        if (host->ops && host->ops->set_ios_post)
-               host->ops->set_ios_post(host);
+               return host->ops->set_ios_post(host);
 
        return 0;
 }
@@ -590,6 +620,12 @@ static int sdhci_set_ios(struct mmc *mmc)
 static int sdhci_init(struct mmc *mmc)
 {
        struct sdhci_host *host = mmc->priv;
+#if CONFIG_IS_ENABLED(DM_MMC) && CONFIG_IS_ENABLED(DM_GPIO)
+       struct udevice *dev = mmc->dev;
+
+       gpio_request_by_name(dev, "cd-gpios", 0,
+                            &host->cd_gpio, GPIOD_IS_IN);
+#endif
 
        sdhci_reset(host, SDHCI_RESET_ALL);
 
@@ -624,9 +660,40 @@ int sdhci_probe(struct udevice *dev)
        return sdhci_init(mmc);
 }
 
+int sdhci_get_cd(struct udevice *dev)
+{
+       struct mmc *mmc = mmc_get_mmc_dev(dev);
+       struct sdhci_host *host = mmc->priv;
+       int value;
+
+       /* If nonremovable, assume that the card is always present. */
+       if (mmc->cfg->host_caps & MMC_CAP_NONREMOVABLE)
+               return 1;
+       /* If polling, assume that the card is always present. */
+       if (mmc->cfg->host_caps & MMC_CAP_NEEDS_POLL)
+               return 1;
+
+#if CONFIG_IS_ENABLED(DM_GPIO)
+       value = dm_gpio_get_value(&host->cd_gpio);
+       if (value >= 0) {
+               if (mmc->cfg->host_caps & MMC_CAP_CD_ACTIVE_HIGH)
+                       return !value;
+               else
+                       return value;
+       }
+#endif
+       value = !!(sdhci_readl(host, SDHCI_PRESENT_STATE) &
+                  SDHCI_CARD_PRESENT);
+       if (mmc->cfg->host_caps & MMC_CAP_CD_ACTIVE_HIGH)
+               return !value;
+       else
+               return value;
+}
+
 const struct dm_mmc_ops sdhci_ops = {
        .send_cmd       = sdhci_send_command,
        .set_ios        = sdhci_set_ios,
+       .get_cd         = sdhci_get_cd,
 #ifdef MMC_SUPPORTS_TUNING
        .execute_tuning = sdhci_execute_tuning,
 #endif
@@ -643,8 +710,20 @@ int sdhci_setup_cfg(struct mmc_config *cfg, struct sdhci_host *host,
                u32 f_max, u32 f_min)
 {
        u32 caps, caps_1 = 0;
-
+#if CONFIG_IS_ENABLED(DM_MMC)
+       u64 dt_caps, dt_caps_mask;
+
+       dt_caps_mask = dev_read_u64_default(host->mmc->dev,
+                                           "sdhci-caps-mask", 0);
+       dt_caps = dev_read_u64_default(host->mmc->dev,
+                                      "sdhci-caps", 0);
+       caps = ~(u32)dt_caps_mask &
+              sdhci_readl(host, SDHCI_CAPABILITIES);
+       caps |= (u32)dt_caps;
+#else
        caps = sdhci_readl(host, SDHCI_CAPABILITIES);
+#endif
+       debug("%s, caps: 0x%x\n", __func__, caps);
 
 #ifdef CONFIG_MMC_SDHCI_SDMA
        if (!(caps & SDHCI_CAN_DO_SDMA)) {
@@ -684,7 +763,14 @@ int sdhci_setup_cfg(struct mmc_config *cfg, struct sdhci_host *host,
 
        /* Check whether the clock multiplier is supported or not */
        if (SDHCI_GET_VERSION(host) >= SDHCI_SPEC_300) {
+#if CONFIG_IS_ENABLED(DM_MMC)
+               caps_1 = ~(u32)(dt_caps_mask >> 32) &
+                        sdhci_readl(host, SDHCI_CAPABILITIES_1);
+               caps_1 |= (u32)(dt_caps >> 32);
+#else
                caps_1 = sdhci_readl(host, SDHCI_CAPABILITIES_1);
+#endif
+               debug("%s, caps_1: 0x%x\n", __func__, caps_1);
                host->clk_mul = (caps_1 & SDHCI_CLOCK_MUL_MASK) >>
                                SDHCI_CLOCK_MUL_SHIFT;
        }
@@ -741,9 +827,6 @@ int sdhci_setup_cfg(struct mmc_config *cfg, struct sdhci_host *host,
                cfg->host_caps &= ~MMC_MODE_HS_52MHz;
        }
 
-       if (SDHCI_GET_VERSION(host) >= SDHCI_SPEC_300)
-               caps_1 = sdhci_readl(host, SDHCI_CAPABILITIES_1);
-
        if (!(cfg->voltages & MMC_VDD_165_195) ||
            (host->quirks & SDHCI_QUIRK_NO_1_8_V))
                caps_1 &= ~(SDHCI_SUPPORT_SDR104 | SDHCI_SUPPORT_SDR50 |