Add support in the driver for handling phy specific registers.

Signed-off-by: Faiz Abbas <faiz_ab...@ti.com>
Reviewed-by: Tom Rini <tr...@konsulko.com>
---
 drivers/mmc/Kconfig       |   1 +
 drivers/mmc/am654_sdhci.c | 229 +++++++++++++++++++++++++++++++++++++-
 2 files changed, 224 insertions(+), 6 deletions(-)

diff --git a/drivers/mmc/Kconfig b/drivers/mmc/Kconfig
index 0062ad1bb9..41e8a270d8 100644
--- a/drivers/mmc/Kconfig
+++ b/drivers/mmc/Kconfig
@@ -458,6 +458,7 @@ config MMC_SDHCI_AM654
        depends on ARCH_K3
        depends on MMC_SDHCI
        depends on DM_MMC && OF_CONTROL && BLK
+       depends on REGMAP
        help
          Support for Secure Digital Host Controller Interface (SDHCI)
          controllers present on TI's AM654 SOCs.
diff --git a/drivers/mmc/am654_sdhci.c b/drivers/mmc/am654_sdhci.c
index a8c92277f7..5639e1818b 100644
--- a/drivers/mmc/am654_sdhci.c
+++ b/drivers/mmc/am654_sdhci.c
@@ -10,16 +10,190 @@
 #include <dm.h>
 #include <malloc.h>
 #include <power-domain.h>
+#include <regmap.h>
 #include <sdhci.h>
 
+/* CTL_CFG Registers */
+#define CTL_CFG_2              0x14
+
+#define SLOTTYPE_MASK          GENMASK(31, 30)
+#define SLOTTYPE_EMBEDDED      BIT(30)
+
+/* PHY Registers */
+#define PHY_CTRL1      0x100
+#define PHY_CTRL2      0x104
+#define PHY_CTRL3      0x108
+#define PHY_CTRL4      0x10C
+#define PHY_CTRL5      0x110
+#define PHY_CTRL6      0x114
+#define PHY_STAT1      0x130
+#define PHY_STAT2      0x134
+
+#define IOMUX_ENABLE_SHIFT     31
+#define IOMUX_ENABLE_MASK      BIT(IOMUX_ENABLE_SHIFT)
+#define OTAPDLYENA_SHIFT       20
+#define OTAPDLYENA_MASK                BIT(OTAPDLYENA_SHIFT)
+#define OTAPDLYSEL_SHIFT       12
+#define OTAPDLYSEL_MASK                GENMASK(15, 12)
+#define STRBSEL_SHIFT          24
+#define STRBSEL_MASK           GENMASK(27, 24)
+#define SEL50_SHIFT            8
+#define SEL50_MASK             BIT(SEL50_SHIFT)
+#define SEL100_SHIFT           9
+#define SEL100_MASK            BIT(SEL100_SHIFT)
+#define DLL_TRIM_ICP_SHIFT     4
+#define DLL_TRIM_ICP_MASK      GENMASK(7, 4)
+#define DR_TY_SHIFT            20
+#define DR_TY_MASK             GENMASK(22, 20)
+#define ENDLL_SHIFT            1
+#define ENDLL_MASK             BIT(ENDLL_SHIFT)
+#define DLLRDY_SHIFT           0
+#define DLLRDY_MASK            BIT(DLLRDY_SHIFT)
+#define PDB_SHIFT              0
+#define PDB_MASK               BIT(PDB_SHIFT)
+#define CALDONE_SHIFT          1
+#define CALDONE_MASK           BIT(CALDONE_SHIFT)
+#define RETRIM_SHIFT           17
+#define RETRIM_MASK            BIT(RETRIM_SHIFT)
+
+#define DRIVER_STRENGTH_50_OHM 0x0
+#define DRIVER_STRENGTH_33_OHM 0x1
+#define DRIVER_STRENGTH_66_OHM 0x2
+#define DRIVER_STRENGTH_100_OHM        0x3
+#define DRIVER_STRENGTH_40_OHM 0x4
+
 #define AM654_SDHCI_MIN_FREQ   400000
 
 struct am654_sdhci_plat {
        struct mmc_config cfg;
        struct mmc mmc;
        unsigned int f_max;
+       struct regmap *base;
+       bool non_removable;
+       u32 otap_del_sel;
+       u32 trm_icp;
+       u32 drv_strength;
+       bool dll_on;
 };
 
+static int am654_sdhci_set_ios_post(struct sdhci_host *host)
+{
+       struct udevice *dev = host->mmc->dev;
+       struct am654_sdhci_plat *plat = dev_get_platdata(dev);
+       unsigned int speed = host->mmc->clock;
+       int sel50, sel100;
+       u32 mask, val;
+       int ret;
+
+       /* Reset SD Clock Enable */
+       val = sdhci_readw(host, SDHCI_CLOCK_CONTROL);
+       val &= ~SDHCI_CLOCK_CARD_EN;
+       sdhci_writew(host, val, SDHCI_CLOCK_CONTROL);
+
+       /* power off phy */
+       if (plat->dll_on) {
+               regmap_update_bits(plat->base, PHY_CTRL1, ENDLL_MASK, 0);
+
+               plat->dll_on = false;
+       }
+
+       /* restart clock */
+       sdhci_set_clock(host->mmc, speed);
+
+       /* switch phy back on */
+       if (speed > AM654_SDHCI_MIN_FREQ) {
+               mask = OTAPDLYENA_MASK | OTAPDLYSEL_MASK;
+               val = (1 << OTAPDLYENA_SHIFT) |
+                     (plat->otap_del_sel << OTAPDLYSEL_SHIFT);
+               regmap_update_bits(plat->base, PHY_CTRL4,
+                                  mask, val);
+               switch (speed) {
+               case 200000000:
+                       sel50 = 0;
+                       sel100 = 0;
+                       break;
+               case 100000000:
+                       sel50 = 0;
+                       sel100 = 1;
+                       break;
+               default:
+                       sel50 = 1;
+                       sel100 = 0;
+               }
+
+               /* Configure PHY DLL frequency */
+               mask = SEL50_MASK | SEL100_MASK;
+               val = (sel50 << SEL50_SHIFT) | (sel100 << SEL100_SHIFT);
+               regmap_update_bits(plat->base, PHY_CTRL5,
+                                  mask, val);
+               /* Configure DLL TRIM */
+               mask = DLL_TRIM_ICP_MASK;
+               val = plat->trm_icp << DLL_TRIM_ICP_SHIFT;
+
+               /* Configure DLL driver strength */
+               mask |= DR_TY_MASK;
+               val |= plat->drv_strength << DR_TY_SHIFT;
+               regmap_update_bits(plat->base, PHY_CTRL1,
+                                  mask, val);
+               /* Enable DLL */
+               regmap_update_bits(plat->base, PHY_CTRL1,
+                                  ENDLL_MASK, 0x1 << ENDLL_SHIFT);
+               /*
+                * Poll for DLL ready. Use a one second timeout.
+                * Works in all experiments done so far
+                */
+               ret = regmap_read_poll_timeout(plat->base,
+                                        PHY_STAT1, val,
+                                        val & DLLRDY_MASK,
+                                        1000, 1000000);
+               if (ret)
+                       return ret;
+
+               plat->dll_on = true;
+       }
+
+       return 0;
+}
+
+const struct sdhci_ops am654_sdhci_ops = {
+       .set_ios_post = &am654_sdhci_set_ios_post,
+};
+
+int am654_sdhci_init(struct am654_sdhci_plat *plat)
+{
+       u32 ctl_cfg_2 = 0;
+       u32 mask, val;
+       int ret;
+
+       /* Reset OTAP to default value */
+       mask = OTAPDLYENA_MASK | OTAPDLYSEL_MASK;
+       regmap_update_bits(plat->base, PHY_CTRL4, mask, 0x0);
+
+       regmap_read(plat->base, PHY_STAT1, &val);
+       if (~val & CALDONE_MASK) {
+               /* Calibrate IO lines */
+               regmap_update_bits(plat->base, PHY_CTRL1,
+                                  PDB_MASK, PDB_MASK);
+               ret = regmap_read_poll_timeout(plat->base, PHY_STAT1,
+                                              val, val & CALDONE_MASK,
+                                              1, 20);
+               if (ret)
+                       return ret;
+       }
+
+       /* Enable pins by setting IO mux to 0 */
+       regmap_update_bits(plat->base, PHY_CTRL1, IOMUX_ENABLE_MASK, 0);
+
+       /* Set slot type based on SD or eMMC */
+       if (plat->non_removable)
+               ctl_cfg_2 = SLOTTYPE_EMBEDDED;
+
+       regmap_update_bits(plat->base, CTL_CFG_2, SLOTTYPE_MASK,
+                          ctl_cfg_2);
+
+       return 0;
+}
+
 static int am654_sdhci_probe(struct udevice *dev)
 {
        struct am654_sdhci_plat *plat = dev_get_platdata(dev);
@@ -55,16 +229,20 @@ static int am654_sdhci_probe(struct udevice *dev)
        }
 
        host->max_clk = clock;
-
-       ret = sdhci_setup_cfg(&plat->cfg, host, plat->f_max,
-                             AM654_SDHCI_MIN_FREQ);
        host->mmc = &plat->mmc;
+       host->mmc->dev = dev;
+       ret = sdhci_setup_cfg(cfg, host, cfg->f_max,
+                             AM654_SDHCI_MIN_FREQ);
        if (ret)
                return ret;
+       host->ops = &am654_sdhci_ops;
        host->mmc->priv = host;
-       host->mmc->dev = dev;
        upriv->mmc = host->mmc;
 
+       regmap_init_mem_index(dev_ofnode(dev), &plat->base, 1);
+
+       am654_sdhci_init(plat);
+
        return sdhci_probe(dev);
 }
 
@@ -72,11 +250,50 @@ static int am654_sdhci_ofdata_to_platdata(struct udevice 
*dev)
 {
        struct am654_sdhci_plat *plat = dev_get_platdata(dev);
        struct sdhci_host *host = dev_get_priv(dev);
+       struct mmc_config *cfg = &plat->cfg;
+       u32 drv_strength;
+       int ret;
 
        host->name = dev->name;
        host->ioaddr = (void *)dev_read_addr(dev);
-       host->bus_width = dev_read_u32_default(dev, "bus-width", 4);
-       plat->f_max = dev_read_u32_default(dev, "max-frequency", 0);
+       plat->non_removable = dev_read_bool(dev, "non-removable");
+
+       ret = dev_read_u32(dev, "ti,trm-icp", &plat->trm_icp);
+       if (ret)
+               return ret;
+
+       ret = dev_read_u32(dev, "ti,otap-del-sel", &plat->otap_del_sel);
+       if (ret)
+               return ret;
+
+       ret = dev_read_u32(dev, "ti,driver-strength-ohm", &drv_strength);
+       if (ret)
+               return ret;
+
+       switch (drv_strength) {
+       case 50:
+               plat->drv_strength = DRIVER_STRENGTH_50_OHM;
+               break;
+       case 33:
+               plat->drv_strength = DRIVER_STRENGTH_33_OHM;
+               break;
+       case 66:
+               plat->drv_strength = DRIVER_STRENGTH_66_OHM;
+               break;
+       case 100:
+               plat->drv_strength = DRIVER_STRENGTH_100_OHM;
+               break;
+       case 40:
+               plat->drv_strength = DRIVER_STRENGTH_40_OHM;
+               break;
+       default:
+               dev_err(dev, "Invalid driver strength\n");
+               return -EINVAL;
+       }
+
+       ret = mmc_of_parse(dev, cfg);
+       if (ret)
+               return ret;
 
        return 0;
 }
-- 
2.19.2

_______________________________________________
U-Boot mailing list
U-Boot@lists.denx.de
https://lists.denx.de/listinfo/u-boot

Reply via email to