[PATCH 4/6] mmc: sdhci_am654: Add support for input tap delay

From: Faiz Abbas
Date: Wed Sep 23 2020 - 06:52:47 EST


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.

With this addition, make sure that DLL is always switched off at the
beginning of the set_clock() call to simplify configuration. This also
removes the need for the dll_on member in struct sdhci_am654_data.

Signed-off-by: Faiz Abbas <faiz_abbas@xxxxxx>
---
drivers/mmc/host/sdhci_am654.c | 114 ++++++++++++++++++++++++---------
1 file changed, 84 insertions(+), 30 deletions(-)

diff --git a/drivers/mmc/host/sdhci_am654.c b/drivers/mmc/host/sdhci_am654.c
index 9f3347bc3757..1213b711e60a 100644
--- a/drivers/mmc/host/sdhci_am654.c
+++ b/drivers/mmc/host/sdhci_am654.c
@@ -66,6 +66,14 @@
#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
@@ -73,7 +81,7 @@
#define DRIVER_STRENGTH_100_OHM 0x3
#define DRIVER_STRENGTH_40_OHM 0x4

-#define CLOCK_TOO_SLOW_HZ 400000
+#define CLOCK_TOO_SLOW_HZ 50000000

/* Command Queue Host Controller Interface Base address */
#define SDHCI_AM654_CQE_BASE_ADDR 0x200
@@ -86,33 +94,55 @@ static struct regmap_config sdhci_am654_regmap_config = {
};

struct timing_data {
- const char *binding;
+ const char *otap_binding;
+ const char *itap_binding;
u32 capability;
};

static const struct timing_data td[] = {
- [MMC_TIMING_LEGACY] = {"ti,otap-del-sel-legacy", 0},
- [MMC_TIMING_MMC_HS] = {"ti,otap-del-sel-mmc-hs", MMC_CAP_MMC_HIGHSPEED},
- [MMC_TIMING_SD_HS] = {"ti,otap-del-sel-sd-hs", MMC_CAP_SD_HIGHSPEED},
- [MMC_TIMING_UHS_SDR12] = {"ti,otap-del-sel-sdr12", MMC_CAP_UHS_SDR12},
- [MMC_TIMING_UHS_SDR25] = {"ti,otap-del-sel-sdr25", MMC_CAP_UHS_SDR25},
- [MMC_TIMING_UHS_SDR50] = {"ti,otap-del-sel-sdr50", MMC_CAP_UHS_SDR50},
- [MMC_TIMING_UHS_SDR104] = {"ti,otap-del-sel-sdr104",
+ [MMC_TIMING_LEGACY] = {"ti,otap-del-sel-legacy",
+ "ti,itap-del-sel-legacy",
+ 0},
+ [MMC_TIMING_MMC_HS] = {"ti,otap-del-sel-mmc-hs",
+ "ti,itap-del-sel-mmc-hs",
+ MMC_CAP_MMC_HIGHSPEED},
+ [MMC_TIMING_SD_HS] = {"ti,otap-del-sel-sd-hs",
+ "ti,itap-del-sel-sd-hs",
+ MMC_CAP_SD_HIGHSPEED},
+ [MMC_TIMING_UHS_SDR12] = {"ti,otap-del-sel-sdr12",
+ "ti,itap-del-sel-sdr12",
+ MMC_CAP_UHS_SDR12},
+ [MMC_TIMING_UHS_SDR25] = {"ti,otap-del-sel-sdr25",
+ "ti,itap-del-sel-sdr25",
+ MMC_CAP_UHS_SDR25},
+ [MMC_TIMING_UHS_SDR50] = {"ti,otap-del-sel-sdr50",
+ NULL,
+ MMC_CAP_UHS_SDR50},
+ [MMC_TIMING_UHS_SDR104] = {"ti,otap-del-sel-sdr104",
+ NULL,
MMC_CAP_UHS_SDR104},
- [MMC_TIMING_UHS_DDR50] = {"ti,otap-del-sel-ddr50", MMC_CAP_UHS_DDR50},
- [MMC_TIMING_MMC_DDR52] = {"ti,otap-del-sel-ddr52", MMC_CAP_DDR},
- [MMC_TIMING_MMC_HS200] = {"ti,otap-del-sel-hs200", MMC_CAP2_HS200},
- [MMC_TIMING_MMC_HS400] = {"ti,otap-del-sel-hs400", MMC_CAP2_HS400},
+ [MMC_TIMING_UHS_DDR50] = {"ti,otap-del-sel-ddr50",
+ NULL,
+ MMC_CAP_UHS_DDR50},
+ [MMC_TIMING_MMC_DDR52] = {"ti,otap-del-sel-ddr52",
+ "ti,itap-del-sel-ddr52",
+ MMC_CAP_DDR},
+ [MMC_TIMING_MMC_HS200] = {"ti,otap-del-sel-hs200",
+ NULL,
+ MMC_CAP2_HS200},
+ [MMC_TIMING_MMC_HS400] = {"ti,otap-del-sel-hs400",
+ NULL,
+ MMC_CAP2_HS400},
};

struct sdhci_am654_data {
struct regmap *base;
bool legacy_otapdly;
int otap_del_sel[ARRAY_SIZE(td)];
+ int itap_del_sel[ARRAY_SIZE(td)];
int clkbuf_sel;
int trm_icp;
int drv_strength;
- bool dll_on;
int strb_sel;
u32 flags;
};
@@ -135,6 +165,10 @@ static void sdhci_am654_setup_dll(struct sdhci_host *host, unsigned int clock)
u32 mask, val;
int ret;

+ /* Disable delay chain mode */
+ regmap_update_bits(sdhci_am654->base, PHY_CTRL5,
+ SELDLYTXCLK_MASK | SELDLYRXCLK_MASK, 0);
+
if (sdhci_am654->flags & FREQSEL_2_BIT) {
switch (clock) {
case 200000000:
@@ -189,8 +223,32 @@ static void sdhci_am654_setup_dll(struct sdhci_host *host, unsigned int clock)
dev_err(mmc_dev(host->mmc), "DLL failed to relock\n");
return;
}
+}
+
+static void sdhci_am654_write_itapdly(struct sdhci_am654_data *sdhci_am654,
+ u32 itapdly)
+{
+ /* Set ITAPCHGWIN before writing to ITAPDLY */
+ regmap_update_bits(sdhci_am654->base, PHY_CTRL4, ITAPCHGWIN_MASK,
+ 1 << ITAPCHGWIN_SHIFT);
+ regmap_update_bits(sdhci_am654->base, PHY_CTRL4, ITAPDLYSEL_MASK,
+ itapdly << ITAPDLYSEL_SHIFT);
+ regmap_update_bits(sdhci_am654->base, PHY_CTRL4, ITAPCHGWIN_MASK, 0);
+}
+
+static void sdhci_am654_setup_delay_chain(struct sdhci_am654_data *sdhci_am654,
+ unsigned char timing)
+{
+ u32 mask, val;

- sdhci_am654->dll_on = true;
+ regmap_update_bits(sdhci_am654->base, PHY_CTRL1, ENDLL_MASK, 0);
+
+ val = 1 << SELDLYTXCLK_SHIFT | 1 << SELDLYRXCLK_SHIFT;
+ mask = SELDLYTXCLK_MASK | SELDLYRXCLK_MASK;
+ regmap_update_bits(sdhci_am654->base, PHY_CTRL5, mask, val);
+
+ sdhci_am654_write_itapdly(sdhci_am654,
+ sdhci_am654->itap_del_sel[timing]);
}

static void sdhci_am654_set_clock(struct sdhci_host *host, unsigned int clock)
@@ -202,11 +260,7 @@ static void sdhci_am654_set_clock(struct sdhci_host *host, unsigned int clock)
u32 otap_del_ena;
u32 mask, val;

- if (sdhci_am654->dll_on) {
- regmap_update_bits(sdhci_am654->base, PHY_CTRL1, ENDLL_MASK, 0);
-
- sdhci_am654->dll_on = false;
- }
+ regmap_update_bits(sdhci_am654->base, PHY_CTRL1, ENDLL_MASK, 0);

sdhci_set_clock(host, clock);

@@ -234,14 +288,10 @@ static void sdhci_am654_set_clock(struct sdhci_host *host, unsigned int clock)

regmap_update_bits(sdhci_am654->base, PHY_CTRL4, mask, val);

- if (timing > MMC_TIMING_UHS_SDR25 && clock > CLOCK_TOO_SLOW_HZ) {
- regmap_update_bits(sdhci_am654->base, PHY_CTRL5,
- SELDLYTXCLK_MASK, 0);
+ if (timing > MMC_TIMING_UHS_SDR25 && clock >= CLOCK_TOO_SLOW_HZ)
sdhci_am654_setup_dll(host, clock);
- } else {
- regmap_update_bits(sdhci_am654->base, PHY_CTRL5,
- SELDLYTXCLK_MASK, 1 << SELDLYTXCLK_SHIFT);
- }
+ else
+ sdhci_am654_setup_delay_chain(sdhci_am654, timing);

regmap_update_bits(sdhci_am654->base, PHY_CTRL5, CLKBUFSEL_MASK,
sdhci_am654->clkbuf_sel);
@@ -469,7 +519,7 @@ static int sdhci_am654_get_otap_delay(struct sdhci_host *host,
int i;
int ret;

- ret = device_property_read_u32(dev, td[MMC_TIMING_LEGACY].binding,
+ ret = device_property_read_u32(dev, td[MMC_TIMING_LEGACY].otap_binding,
&sdhci_am654->otap_del_sel[MMC_TIMING_LEGACY]);
if (ret) {
/*
@@ -492,11 +542,11 @@ static int sdhci_am654_get_otap_delay(struct sdhci_host *host,

for (i = MMC_TIMING_MMC_HS; i <= MMC_TIMING_MMC_HS400; i++) {

- ret = device_property_read_u32(dev, td[i].binding,
+ ret = device_property_read_u32(dev, td[i].otap_binding,
&sdhci_am654->otap_del_sel[i]);
if (ret) {
dev_dbg(dev, "Couldn't find %s\n",
- td[i].binding);
+ td[i].otap_binding);
/*
* Remove the corresponding capability
* if an otap-del-sel value is not found
@@ -506,6 +556,10 @@ static int sdhci_am654_get_otap_delay(struct sdhci_host *host,
else
host->mmc->caps2 &= ~td[i].capability;
}
+
+ if (td[i].itap_binding)
+ device_property_read_u32(dev, td[i].itap_binding,
+ &sdhci_am654->itap_del_sel[i]);
}

return 0;
--
2.17.1