aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFaiz Abbas <faiz_abbas@ti.com>2021-02-04 15:10:51 +0530
committerLokesh Vutla <lokeshvutla@ti.com>2021-02-04 20:37:57 +0530
commitc964447ea3d6af0f9bfd78ceda8a3cd7c27fd584 (patch)
treeb36e28dce24b2627c7e4e66223e68f4dd155867c
parent8c32b5f3707fedeebe2d34ebd6aa4a77c4bacbd3 (diff)
downloadu-boot-c964447ea3d6af0f9bfd78ceda8a3cd7c27fd584.zip
u-boot-c964447ea3d6af0f9bfd78ceda8a3cd7c27fd584.tar.gz
u-boot-c964447ea3d6af0f9bfd78ceda8a3cd7c27fd584.tar.bz2
mmc: am654_sdhci: Add support for input tap delay
DLL need only be enabled for speed modes and clock frequencies at or above 50 MHz. For speed modes that don't enable the DLL, we need to configure a static input delay value. This involves reading an optional itap-del-sel-* value from the device tree and configuring it for the appropriate speed mode. Therefore, move all dll configurations to their own functions and gate it with 50 MHz speed and a minimum mode. If both these conditions are not satisfied then configure delay chain modes. Signed-off-by: Faiz Abbas <faiz_abbas@ti.com> Signed-off-by: Aswath Govindraju <a-govindraju@ti.com> Reviewed-by: Jaehoon Chung <jh80.chung@samsung.com>
-rw-r--r--drivers/mmc/am654_sdhci.c241
1 files changed, 161 insertions, 80 deletions
diff --git a/drivers/mmc/am654_sdhci.c b/drivers/mmc/am654_sdhci.c
index 5790fa3..b825582 100644
--- a/drivers/mmc/am654_sdhci.c
+++ b/drivers/mmc/am654_sdhci.c
@@ -62,6 +62,16 @@
#define CALDONE_MASK BIT(CALDONE_SHIFT)
#define RETRIM_SHIFT 17
#define RETRIM_MASK BIT(RETRIM_SHIFT)
+#define SELDLYTXCLK_SHIFT 17
+#define SELDLYTXCLK_MASK BIT(SELDLYTXCLK_SHIFT)
+#define SELDLYRXCLK_SHIFT 16
+#define SELDLYRXCLK_MASK BIT(SELDLYRXCLK_SHIFT)
+#define ITAPDLYSEL_SHIFT 0
+#define ITAPDLYSEL_MASK GENMASK(4, 0)
+#define ITAPDLYENA_SHIFT 8
+#define ITAPDLYENA_MASK BIT(ITAPDLYENA_SHIFT)
+#define ITAPCHGWIN_SHIFT 9
+#define ITAPCHGWIN_MASK BIT(ITAPCHGWIN_SHIFT)
#define DRIVER_STRENGTH_50_OHM 0x0
#define DRIVER_STRENGTH_33_OHM 0x1
@@ -70,6 +80,7 @@
#define DRIVER_STRENGTH_40_OHM 0x4
#define AM654_SDHCI_MIN_FREQ 400000
+#define CLOCK_TOO_SLOW_HZ 50000000
struct am654_sdhci_plat {
struct mmc_config cfg;
@@ -77,6 +88,7 @@ struct am654_sdhci_plat {
struct regmap *base;
bool non_removable;
u32 otap_del_sel[MMC_MODES_END];
+ u32 itap_del_sel[MMC_MODES_END];
u32 trm_icp;
u32 drv_strength;
u32 strb_sel;
@@ -89,22 +101,45 @@ struct am654_sdhci_plat {
};
struct timing_data {
- const char *binding;
+ const char *otap_binding;
+ const char *itap_binding;
u32 capability;
};
static const struct timing_data td[] = {
- [MMC_LEGACY] = {"ti,otap-del-sel-legacy", 0},
- [MMC_HS] = {"ti,otap-del-sel-mmc-hs", MMC_CAP(MMC_HS)},
- [SD_HS] = {"ti,otap-del-sel-sd-hs", MMC_CAP(SD_HS)},
- [UHS_SDR12] = {"ti,otap-del-sel-sdr12", MMC_CAP(UHS_SDR12)},
- [UHS_SDR25] = {"ti,otap-del-sel-sdr25", MMC_CAP(UHS_SDR25)},
- [UHS_SDR50] = {"ti,otap-del-sel-sdr50", MMC_CAP(UHS_SDR50)},
- [UHS_SDR104] = {"ti,otap-del-sel-sdr104", MMC_CAP(UHS_SDR104)},
- [UHS_DDR50] = {"ti,otap-del-sel-ddr50", MMC_CAP(UHS_DDR50)},
- [MMC_DDR_52] = {"ti,otap-del-sel-ddr52", MMC_CAP(MMC_DDR_52)},
- [MMC_HS_200] = {"ti,otap-del-sel-hs200", MMC_CAP(MMC_HS_200)},
- [MMC_HS_400] = {"ti,otap-del-sel-hs400", MMC_CAP(MMC_HS_400)},
+ [MMC_LEGACY] = {"ti,otap-del-sel-legacy",
+ "ti,itap-del-sel-legacy",
+ 0},
+ [MMC_HS] = {"ti,otap-del-sel-mmc-hs",
+ "ti,itap-del-sel-mms-hs",
+ MMC_CAP(MMC_HS)},
+ [SD_HS] = {"ti,otap-del-sel-sd-hs",
+ "ti,itap-del-sel-sd-hs",
+ MMC_CAP(SD_HS)},
+ [UHS_SDR12] = {"ti,otap-del-sel-sdr12",
+ "ti,itap-del-sel-sdr12",
+ MMC_CAP(UHS_SDR12)},
+ [UHS_SDR25] = {"ti,otap-del-sel-sdr25",
+ "ti,itap-del-sel-sdr25",
+ MMC_CAP(UHS_SDR25)},
+ [UHS_SDR50] = {"ti,otap-del-sel-sdr50",
+ NULL,
+ MMC_CAP(UHS_SDR50)},
+ [UHS_SDR104] = {"ti,otap-del-sel-sdr104",
+ NULL,
+ MMC_CAP(UHS_SDR104)},
+ [UHS_DDR50] = {"ti,otap-del-sel-ddr50",
+ NULL,
+ MMC_CAP(UHS_DDR50)},
+ [MMC_DDR_52] = {"ti,otap-del-sel-ddr52",
+ "ti,itap-del-sel-ddr52",
+ MMC_CAP(MMC_DDR_52)},
+ [MMC_HS_200] = {"ti,otap-del-sel-hs200",
+ NULL,
+ MMC_CAP(MMC_HS_200)},
+ [MMC_HS_400] = {"ti,otap-del-sel-hs400",
+ NULL,
+ MMC_CAP(MMC_HS_400)},
};
struct am654_driver_data {
@@ -127,12 +162,99 @@ static void am654_sdhci_set_control_reg(struct sdhci_host *host)
sdhci_set_uhs_timing(host);
}
+static int am654_sdhci_setup_dll(struct am654_sdhci_plat *plat,
+ unsigned int speed)
+{
+ int sel50, sel100, freqsel;
+ u32 mask, val;
+ int ret;
+
+ /* Disable delay chain mode */
+ regmap_update_bits(plat->base, PHY_CTRL5,
+ SELDLYTXCLK_MASK | SELDLYRXCLK_MASK, 0);
+
+ if (plat->flags & FREQSEL_2_BIT) {
+ 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);
+ } else {
+ switch (speed) {
+ case 200000000:
+ freqsel = 0x0;
+ break;
+ default:
+ freqsel = 0x4;
+ }
+ regmap_update_bits(plat->base, PHY_CTRL5, FREQSEL_MASK,
+ freqsel << FREQSEL_SHIFT);
+ }
+
+ /* 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);
+
+ return ret;
+}
+
+static void am654_sdhci_write_itapdly(struct am654_sdhci_plat *plat,
+ u32 itapdly)
+{
+ /* Set ITAPCHGWIN before writing to ITAPDLY */
+ regmap_update_bits(plat->base, PHY_CTRL4, ITAPCHGWIN_MASK,
+ 1 << ITAPCHGWIN_SHIFT);
+ regmap_update_bits(plat->base, PHY_CTRL4, ITAPDLYSEL_MASK,
+ itapdly << ITAPDLYSEL_SHIFT);
+ regmap_update_bits(plat->base, PHY_CTRL4, ITAPCHGWIN_MASK, 0);
+}
+
+static void am654_sdhci_setup_delay_chain(struct am654_sdhci_plat *plat,
+ int mode)
+{
+ u32 mask, val;
+
+ val = 1 << SELDLYTXCLK_SHIFT | 1 << SELDLYRXCLK_SHIFT;
+ mask = SELDLYTXCLK_MASK | SELDLYRXCLK_MASK;
+ regmap_update_bits(plat->base, PHY_CTRL5, mask, val);
+
+ am654_sdhci_write_itapdly(plat, plat->itap_del_sel[mode]);
+}
+
static int am654_sdhci_set_ios_post(struct sdhci_host *host)
{
struct udevice *dev = host->mmc->dev;
struct am654_sdhci_plat *plat = dev_get_plat(dev);
unsigned int speed = host->mmc->clock;
- int sel50, sel100, freqsel;
+ int mode = host->mmc->selected_mode;
u32 otap_del_sel;
u32 mask, val;
int ret;
@@ -148,75 +270,29 @@ static int am654_sdhci_set_ios_post(struct sdhci_host *host)
sdhci_set_clock(host->mmc, speed);
/* switch phy back on */
- if (speed > AM654_SDHCI_MIN_FREQ) {
- otap_del_sel = plat->otap_del_sel[host->mmc->selected_mode];
- mask = OTAPDLYENA_MASK | OTAPDLYSEL_MASK;
- val = (1 << OTAPDLYENA_SHIFT) |
- (otap_del_sel << OTAPDLYSEL_SHIFT);
-
- /* Write to STRBSEL for HS400 speed mode */
- if (host->mmc->selected_mode == MMC_HS_400) {
- if (plat->flags & STRBSEL_4_BIT)
- mask |= STRBSEL_4BIT_MASK;
- else
- mask |= STRBSEL_8BIT_MASK;
-
- val |= plat->strb_sel << STRBSEL_SHIFT;
- }
+ otap_del_sel = plat->otap_del_sel[mode];
+ mask = OTAPDLYENA_MASK | OTAPDLYSEL_MASK;
+ val = (1 << OTAPDLYENA_SHIFT) |
+ (otap_del_sel << OTAPDLYSEL_SHIFT);
- regmap_update_bits(plat->base, PHY_CTRL4, mask, val);
-
- if (plat->flags & FREQSEL_2_BIT) {
- 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);
- } else {
- switch (speed) {
- case 200000000:
- freqsel = 0x0;
- break;
- default:
- freqsel = 0x4;
- }
- regmap_update_bits(plat->base, PHY_CTRL5, FREQSEL_MASK,
- freqsel << FREQSEL_SHIFT);
- }
+ /* Write to STRBSEL for HS400 speed mode */
+ if (host->mmc->selected_mode == MMC_HS_400) {
+ if (plat->flags & STRBSEL_4_BIT)
+ mask |= STRBSEL_4BIT_MASK;
+ else
+ mask |= STRBSEL_8BIT_MASK;
- /* 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);
+ val |= plat->strb_sel << STRBSEL_SHIFT;
+ }
+
+ regmap_update_bits(plat->base, PHY_CTRL4, mask, val);
+
+ if (mode > UHS_SDR25 && speed >= CLOCK_TOO_SLOW_HZ) {
+ ret = am654_sdhci_setup_dll(plat, speed);
if (ret)
return ret;
+ } else {
+ am654_sdhci_setup_delay_chain(plat, mode);
}
return 0;
@@ -354,15 +430,20 @@ static int sdhci_am654_get_otap_delay(struct udevice *dev,
* value is not found
*/
for (i = MMC_HS; i <= MMC_HS_400; i++) {
- ret = dev_read_u32(dev, td[i].binding, &plat->otap_del_sel[i]);
+ ret = dev_read_u32(dev, td[i].otap_binding,
+ &plat->otap_del_sel[i]);
if (ret) {
- dev_dbg(dev, "Couldn't find %s\n", td[i].binding);
+ dev_dbg(dev, "Couldn't find %s\n", td[i].otap_binding);
/*
* Remove the corresponding capability
* if an otap-del-sel value is not found
*/
cfg->host_caps &= ~td[i].capability;
}
+
+ if (td[i].itap_binding)
+ dev_read_u32(dev, td[i].itap_binding,
+ &plat->itap_del_sel[i]);
}
return 0;