From 4ff037c13c1e7ab16362d39a59ebb8fffb929f99 Mon Sep 17 00:00:00 2001 From: Shawn Lin Date: Wed, 15 Apr 2020 09:19:09 +0800 Subject: [PATCH] mmc: dw_mmc-rockchip: add v2 tuning support v2 tuning will inherit pre-stage loader's phase settings for the first time, and do re-tune if necessary. Re-tune will still try the rough degrees, for instance, 90, 180, 270, 360 but continue to do the fine tuning if sample window isn't good enough. Change-Id: I593384ee381d09df5b9adfc29a18eb22517b2764 Signed-off-by: Shawn Lin --- drivers/mmc/host/dw_mmc-rockchip.c | 48 ++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) --- a/drivers/mmc/host/dw_mmc-rockchip.c +++ b/drivers/mmc/host/dw_mmc-rockchip.c @@ -35,6 +35,8 @@ struct dw_mci_rockchip_priv_data { int default_sample_phase; int num_phases; bool internal_phase; + int last_degree; + bool use_v2_tuning; }; /* @@ -279,6 +281,58 @@ static void dw_mci_rk3288_set_ios(struct #define TUNING_ITERATION_TO_PHASE(i, num_phases) \ (DIV_ROUND_UP((i) * 360, num_phases)) +static int dw_mci_v2_execute_tuning(struct dw_mci_slot *slot, u32 opcode) +{ + struct dw_mci *host = slot->host; + struct dw_mci_rockchip_priv_data *priv = host->priv; + struct mmc_host *mmc = slot->mmc; + u32 degrees[4] = {0, 90, 180, 270}, degree; + int i; + static bool inherit = true; + + if (inherit) { + inherit = false; + i = clk_get_phase(priv->sample_clk) / 90; + degree = degrees[i]; + goto done; + } + + /* + * v2 only support 4 degrees in theory. + * First we inherit sample phases from firmware, which should + * be able work fine, at least in the first place. + * If retune is needed, we search forward to pick the last + * one phase from degree list and loop around until we get one. + * It's impossible all 4 fixed phase won't be able to work. + */ + for (i = 0; i < ARRAY_SIZE(degrees); i++) { + degree = degrees[i] + priv->last_degree + 90; + degree = degree % 360; + clk_set_phase(priv->sample_clk, degree); + if (mmc_send_tuning(mmc, opcode, NULL)) { + /* + * Tuning error, the phase is a bad phase, + * then try using the calculated best phase. + */ + dev_info(host->dev, "V2 tuned phase to %d error, try the best phase\n", degree); + degree = (degree + 180) % 360; + clk_set_phase(priv->sample_clk, degree); + if (!mmc_send_tuning(mmc, opcode, NULL)) + break; + } + } + + if (i == ARRAY_SIZE(degrees)) { + dev_warn(host->dev, "V2 All phases bad!"); + return -EIO; + } + +done: + dev_info(host->dev, "V2 Successfully tuned phase to %d\n", degree); + priv->last_degree = degree; + return 0; +} + static int dw_mci_rk3288_execute_tuning(struct dw_mci_slot *slot, u32 opcode) { struct dw_mci *host = slot->host; @@ -303,6 +357,12 @@ static int dw_mci_rk3288_execute_tuning( return -EIO; } + if (priv->use_v2_tuning) { + if (!dw_mci_v2_execute_tuning(slot, opcode)) + return 0; + /* Otherwise we continue using fine tuning */ + } + ranges = kmalloc_array(priv->num_phases / 2 + 1, sizeof(*ranges), GFP_KERNEL); if (!ranges) @@ -431,6 +491,7 @@ static int dw_mci_common_parse_dt(struct static int dw_mci_rk3288_parse_dt(struct dw_mci *host) { + struct device_node *np = host->dev->of_node; struct dw_mci_rockchip_priv_data *priv; int err; @@ -440,6 +501,9 @@ static int dw_mci_rk3288_parse_dt(struct priv = host->priv; + if (of_property_read_bool(np, "rockchip,use-v2-tuning")) + priv->use_v2_tuning = true; + priv->drv_clk = devm_clk_get(host->dev, "ciu-drive"); if (IS_ERR(priv->drv_clk)) dev_dbg(host->dev, "ciu-drive not available\n");