Commit ff39e7f7 authored by Chen-Yu Tsai's avatar Chen-Yu Tsai Committed by Ulf Hansson

mmc: sunxi: Support controllers that can use both old and new timings

On the SoCs that introduced the new timing mode for MMC controllers,
both the old (where the clock delays are set in the CCU) and new
(where the clock delays are set in the MMC controller) timing modes
are available, and we have to support them both. However there are
two bits that control which mode is active. One is in the CCU, the
other is in the MMC controller. The settings on both sides must be
the same, or nothing will work.

The sunxi-ng clock driver provides an API to query and set the
active timing mode. At probe time, we try to set the active mode
to the "new timing mode". If it succeeds, we can then use the MMC
controller in the new mode. If not, we fall back to the old mode.
Signed-off-by: default avatarChen-Yu Tsai <wens@csie.org>
Acked-by: default avatarMaxime Ripard <maxime.ripard@free-electrons.com>
Signed-off-by: default avatarUlf Hansson <ulf.hansson@linaro.org>
parent 81e911d0
...@@ -22,6 +22,7 @@ ...@@ -22,6 +22,7 @@
#include <linux/err.h> #include <linux/err.h>
#include <linux/clk.h> #include <linux/clk.h>
#include <linux/clk/sunxi-ng.h>
#include <linux/gpio.h> #include <linux/gpio.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/spinlock.h> #include <linux/spinlock.h>
...@@ -259,7 +260,11 @@ struct sunxi_mmc_cfg { ...@@ -259,7 +260,11 @@ struct sunxi_mmc_cfg {
/* Does DATA0 needs to be masked while the clock is updated */ /* Does DATA0 needs to be masked while the clock is updated */
bool mask_data0; bool mask_data0;
/* hardware only supports new timing mode */
bool needs_new_timings; bool needs_new_timings;
/* hardware can switch between old and new timing modes */
bool has_timings_switch;
}; };
struct sunxi_mmc_host { struct sunxi_mmc_host {
...@@ -293,6 +298,9 @@ struct sunxi_mmc_host { ...@@ -293,6 +298,9 @@ struct sunxi_mmc_host {
/* vqmmc */ /* vqmmc */
bool vqmmc_enabled; bool vqmmc_enabled;
/* timings */
bool use_new_timings;
}; };
static int sunxi_mmc_reset_host(struct sunxi_mmc_host *host) static int sunxi_mmc_reset_host(struct sunxi_mmc_host *host)
...@@ -714,7 +722,7 @@ static int sunxi_mmc_clk_set_phase(struct sunxi_mmc_host *host, ...@@ -714,7 +722,7 @@ static int sunxi_mmc_clk_set_phase(struct sunxi_mmc_host *host,
{ {
int index; int index;
if (!host->cfg->clk_delays) if (host->use_new_timings)
return 0; return 0;
/* determine delays */ /* determine delays */
...@@ -765,6 +773,15 @@ static int sunxi_mmc_clk_set_rate(struct sunxi_mmc_host *host, ...@@ -765,6 +773,15 @@ static int sunxi_mmc_clk_set_rate(struct sunxi_mmc_host *host,
ios->bus_width == MMC_BUS_WIDTH_8) ios->bus_width == MMC_BUS_WIDTH_8)
clock <<= 1; clock <<= 1;
if (host->use_new_timings) {
ret = sunxi_ccu_set_mmc_timing_mode(host->clk_mmc, true);
if (ret) {
dev_err(mmc_dev(mmc),
"error setting new timing mode\n");
return ret;
}
}
rate = clk_round_rate(host->clk_mmc, clock); rate = clk_round_rate(host->clk_mmc, clock);
if (rate < 0) { if (rate < 0) {
dev_err(mmc_dev(mmc), "error rounding clk to %d: %ld\n", dev_err(mmc_dev(mmc), "error rounding clk to %d: %ld\n",
...@@ -793,7 +810,7 @@ static int sunxi_mmc_clk_set_rate(struct sunxi_mmc_host *host, ...@@ -793,7 +810,7 @@ static int sunxi_mmc_clk_set_rate(struct sunxi_mmc_host *host,
} }
mmc_writel(host, REG_CLKCR, rval); mmc_writel(host, REG_CLKCR, rval);
if (host->cfg->needs_new_timings) { if (host->use_new_timings) {
/* Don't touch the delay bits */ /* Don't touch the delay bits */
rval = mmc_readl(host, REG_SD_NTSR); rval = mmc_readl(host, REG_SD_NTSR);
rval |= SDXC_2X_TIMING_MODE; rval |= SDXC_2X_TIMING_MODE;
...@@ -1263,6 +1280,30 @@ static int sunxi_mmc_probe(struct platform_device *pdev) ...@@ -1263,6 +1280,30 @@ static int sunxi_mmc_probe(struct platform_device *pdev)
goto error_free_host; goto error_free_host;
} }
if (host->cfg->has_timings_switch) {
/*
* Supports both old and new timing modes.
* Try setting the clk to new timing mode.
*/
sunxi_ccu_set_mmc_timing_mode(host->clk_mmc, true);
/* And check the result */
ret = sunxi_ccu_get_mmc_timing_mode(host->clk_mmc);
if (ret < 0) {
/*
* For whatever reason we were not able to get
* the current active mode. Default to old mode.
*/
dev_warn(&pdev->dev, "MMC clk timing mode unknown\n");
host->use_new_timings = false;
} else {
host->use_new_timings = !!ret;
}
} else if (host->cfg->needs_new_timings) {
/* Supports new timing mode only */
host->use_new_timings = true;
}
mmc->ops = &sunxi_mmc_ops; mmc->ops = &sunxi_mmc_ops;
mmc->max_blk_count = 8192; mmc->max_blk_count = 8192;
mmc->max_blk_size = 4096; mmc->max_blk_size = 4096;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment