]> git.dujemihanovic.xyz Git - u-boot.git/commitdiff
mmc: am654_sdhci: Add Support for PHY
authorFaiz Abbas <faiz_abbas@ti.com>
Mon, 10 Jun 2019 19:13:38 +0000 (00:43 +0530)
committerTom Rini <trini@konsulko.com>
Wed, 17 Jul 2019 15:12:08 +0000 (11:12 -0400)
Add support in the driver for handling phy specific registers.

Signed-off-by: Faiz Abbas <faiz_abbas@ti.com>
Reviewed-by: Tom Rini <trini@konsulko.com>
drivers/mmc/Kconfig
drivers/mmc/am654_sdhci.c

index 98d3f7a1501afe454c04ca8b2b5446dff2d8884c..4cdae41b59a6956e990d8e2259da03a0db6c04c4 100644 (file)
@@ -473,6 +473,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.
index a8c92277f7a92665d4c785bcc7ac45b4dcf1ae37..b9a7924d4e2017bae29612427cf5257c794c0f3d 100644 (file)
 #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);
+
+               /* 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;
+       }
+
+       /* 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 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);
        struct mmc_uclass_priv *upriv = dev_get_uclass_priv(dev);
        struct sdhci_host *host = dev_get_priv(dev);
+       struct mmc_config *cfg = &plat->cfg;
        struct power_domain sdhci_pwrdmn;
        struct clk clk;
        unsigned long clock;
@@ -55,16 +223,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 +244,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;
 }