mirror of https://gitee.com/openkylin/linux.git
mmc: sdhci_am654: 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. 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@ti.com> Link: https://lore.kernel.org/r/20200923105206.7988-5-faiz_abbas@ti.com Signed-off-by: Ulf Hansson <ulf.hansson@linaro.org>
This commit is contained in:
parent
1e753dbb9c
commit
a0a62497f6
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
sdhci_am654->dll_on = true;
|
||||
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;
|
||||
|
||||
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;
|
||||
|
|
Loading…
Reference in New Issue