mmc: dw_mmc: Add driver callbacks for data read timeout
Current dw_mci driver supports a TMOUT register which consists of a 24 bit field (TMOUT[31:8]) for the DATA_TIMEOUT. The maximum value of this field is 0xFFFFFF, which with a 200MHz clock will give a full DRTO of: 0xFFFFFF / 200000000 => ~84 ms However, the ARTPEC-8 SoC DWMMC IP version has a TMOUT register with an extended DATA_TIMEOUT field, which supports longer timers for the DRTO. In this version the DATA_TIMEOUT field is split into two, which with the same 200MHz clock as above will allow a maximum timeout of: ((TMOUT[10:8] -1) * 0xFFFFFF + TMOUT[31:11] * 8) / 200000000 => ~587 ms Add driver callbacks for implementation specific data timeout, and implement callback functions for the ARTPEC-8 SoC. Signed-off-by: Mårten Lindahl <marten.lindahl@axis.com> Acked-by: Krzysztof Kozlowski <krzysztof.kozlowski@canonical.com> Link: https://lore.kernel.org/r/20211220113026.21129-4-marten.lindahl@axis.com Signed-off-by: Ulf Hansson <ulf.hansson@linaro.org>
This commit is contained in:
parent
91e2ca227b
commit
25d5417a90
@ -539,6 +539,53 @@ static int dw_mci_exynos_prepare_hs400_tuning(struct dw_mci *host,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void dw_mci_exynos_set_data_timeout(struct dw_mci *host,
|
||||||
|
unsigned int timeout_ns)
|
||||||
|
{
|
||||||
|
u32 clk_div, tmout;
|
||||||
|
u64 tmp;
|
||||||
|
unsigned int tmp2;
|
||||||
|
|
||||||
|
clk_div = (mci_readl(host, CLKDIV) & 0xFF) * 2;
|
||||||
|
if (clk_div == 0)
|
||||||
|
clk_div = 1;
|
||||||
|
|
||||||
|
tmp = DIV_ROUND_UP_ULL((u64)timeout_ns * host->bus_hz, NSEC_PER_SEC);
|
||||||
|
tmp = DIV_ROUND_UP_ULL(tmp, clk_div);
|
||||||
|
|
||||||
|
/* TMOUT[7:0] (RESPONSE_TIMEOUT) */
|
||||||
|
tmout = 0xFF; /* Set maximum */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Extended HW timer (max = 0x6FFFFF2):
|
||||||
|
* ((TMOUT[10:8] - 1) * 0xFFFFFF + TMOUT[31:11] * 8)
|
||||||
|
*/
|
||||||
|
if (!tmp || tmp > 0x6FFFFF2)
|
||||||
|
tmout |= (0xFFFFFF << 8);
|
||||||
|
else {
|
||||||
|
/* TMOUT[10:8] */
|
||||||
|
tmp2 = (((unsigned int)tmp / 0xFFFFFF) + 1) & 0x7;
|
||||||
|
tmout |= tmp2 << 8;
|
||||||
|
|
||||||
|
/* TMOUT[31:11] */
|
||||||
|
tmp = tmp - ((tmp2 - 1) * 0xFFFFFF);
|
||||||
|
tmout |= (tmp & 0xFFFFF8) << 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
mci_writel(host, TMOUT, tmout);
|
||||||
|
dev_dbg(host->dev, "timeout_ns: %u => TMOUT[31:8]: %#08x",
|
||||||
|
timeout_ns, tmout >> 8);
|
||||||
|
}
|
||||||
|
|
||||||
|
static u32 dw_mci_exynos_get_drto_clks(struct dw_mci *host)
|
||||||
|
{
|
||||||
|
u32 drto_clks;
|
||||||
|
|
||||||
|
drto_clks = mci_readl(host, TMOUT) >> 8;
|
||||||
|
|
||||||
|
return (((drto_clks & 0x7) - 1) * 0xFFFFFF) + ((drto_clks & 0xFFFFF8));
|
||||||
|
}
|
||||||
|
|
||||||
/* Common capabilities of Exynos4/Exynos5 SoC */
|
/* Common capabilities of Exynos4/Exynos5 SoC */
|
||||||
static unsigned long exynos_dwmmc_caps[4] = {
|
static unsigned long exynos_dwmmc_caps[4] = {
|
||||||
MMC_CAP_1_8V_DDR | MMC_CAP_8_BIT_DATA,
|
MMC_CAP_1_8V_DDR | MMC_CAP_8_BIT_DATA,
|
||||||
@ -564,6 +611,8 @@ static const struct dw_mci_drv_data artpec_drv_data = {
|
|||||||
.set_ios = dw_mci_exynos_set_ios,
|
.set_ios = dw_mci_exynos_set_ios,
|
||||||
.parse_dt = dw_mci_exynos_parse_dt,
|
.parse_dt = dw_mci_exynos_parse_dt,
|
||||||
.execute_tuning = dw_mci_exynos_execute_tuning,
|
.execute_tuning = dw_mci_exynos_execute_tuning,
|
||||||
|
.set_data_timeout = dw_mci_exynos_set_data_timeout,
|
||||||
|
.get_drto_clks = dw_mci_exynos_get_drto_clks,
|
||||||
};
|
};
|
||||||
|
|
||||||
static const struct of_device_id dw_mci_exynos_match[] = {
|
static const struct of_device_id dw_mci_exynos_match[] = {
|
||||||
|
@ -1287,9 +1287,13 @@ static void dw_mci_setup_bus(struct dw_mci_slot *slot, bool force_clkinit)
|
|||||||
static void dw_mci_set_data_timeout(struct dw_mci *host,
|
static void dw_mci_set_data_timeout(struct dw_mci *host,
|
||||||
unsigned int timeout_ns)
|
unsigned int timeout_ns)
|
||||||
{
|
{
|
||||||
|
const struct dw_mci_drv_data *drv_data = host->drv_data;
|
||||||
u32 clk_div, tmout;
|
u32 clk_div, tmout;
|
||||||
u64 tmp;
|
u64 tmp;
|
||||||
|
|
||||||
|
if (drv_data && drv_data->set_data_timeout)
|
||||||
|
return drv_data->set_data_timeout(host, timeout_ns);
|
||||||
|
|
||||||
clk_div = (mci_readl(host, CLKDIV) & 0xFF) * 2;
|
clk_div = (mci_readl(host, CLKDIV) & 0xFF) * 2;
|
||||||
if (clk_div == 0)
|
if (clk_div == 0)
|
||||||
clk_div = 1;
|
clk_div = 1;
|
||||||
@ -1995,12 +1999,16 @@ static int dw_mci_data_complete(struct dw_mci *host, struct mmc_data *data)
|
|||||||
|
|
||||||
static void dw_mci_set_drto(struct dw_mci *host)
|
static void dw_mci_set_drto(struct dw_mci *host)
|
||||||
{
|
{
|
||||||
|
const struct dw_mci_drv_data *drv_data = host->drv_data;
|
||||||
unsigned int drto_clks;
|
unsigned int drto_clks;
|
||||||
unsigned int drto_div;
|
unsigned int drto_div;
|
||||||
unsigned int drto_ms;
|
unsigned int drto_ms;
|
||||||
unsigned long irqflags;
|
unsigned long irqflags;
|
||||||
|
|
||||||
drto_clks = mci_readl(host, TMOUT) >> 8;
|
if (drv_data && drv_data->get_drto_clks)
|
||||||
|
drto_clks = drv_data->get_drto_clks(host);
|
||||||
|
else
|
||||||
|
drto_clks = mci_readl(host, TMOUT) >> 8;
|
||||||
drto_div = (mci_readl(host, CLKDIV) & 0xff) * 2;
|
drto_div = (mci_readl(host, CLKDIV) & 0xff) * 2;
|
||||||
if (drto_div == 0)
|
if (drto_div == 0)
|
||||||
drto_div = 1;
|
drto_div = 1;
|
||||||
@ -2008,6 +2016,8 @@ static void dw_mci_set_drto(struct dw_mci *host)
|
|||||||
drto_ms = DIV_ROUND_UP_ULL((u64)MSEC_PER_SEC * drto_clks * drto_div,
|
drto_ms = DIV_ROUND_UP_ULL((u64)MSEC_PER_SEC * drto_clks * drto_div,
|
||||||
host->bus_hz);
|
host->bus_hz);
|
||||||
|
|
||||||
|
dev_dbg(host->dev, "drto_ms: %u\n", drto_ms);
|
||||||
|
|
||||||
/* add a bit spare time */
|
/* add a bit spare time */
|
||||||
drto_ms += 10;
|
drto_ms += 10;
|
||||||
|
|
||||||
|
@ -556,6 +556,8 @@ struct dw_mci_slot {
|
|||||||
* @set_ios: handle bus specific extensions.
|
* @set_ios: handle bus specific extensions.
|
||||||
* @parse_dt: parse implementation specific device tree properties.
|
* @parse_dt: parse implementation specific device tree properties.
|
||||||
* @execute_tuning: implementation specific tuning procedure.
|
* @execute_tuning: implementation specific tuning procedure.
|
||||||
|
* @set_data_timeout: implementation specific timeout.
|
||||||
|
* @get_drto_clks: implementation specific cycle count for data read timeout.
|
||||||
*
|
*
|
||||||
* Provide controller implementation specific extensions. The usage of this
|
* Provide controller implementation specific extensions. The usage of this
|
||||||
* data structure is fully optional and usage of each member in this structure
|
* data structure is fully optional and usage of each member in this structure
|
||||||
@ -573,5 +575,8 @@ struct dw_mci_drv_data {
|
|||||||
struct mmc_ios *ios);
|
struct mmc_ios *ios);
|
||||||
int (*switch_voltage)(struct mmc_host *mmc,
|
int (*switch_voltage)(struct mmc_host *mmc,
|
||||||
struct mmc_ios *ios);
|
struct mmc_ios *ios);
|
||||||
|
void (*set_data_timeout)(struct dw_mci *host,
|
||||||
|
unsigned int timeout_ns);
|
||||||
|
u32 (*get_drto_clks)(struct dw_mci *host);
|
||||||
};
|
};
|
||||||
#endif /* _DW_MMC_H_ */
|
#endif /* _DW_MMC_H_ */
|
||||||
|
Loading…
x
Reference in New Issue
Block a user