From f9a95f0866f6d1d694392c5598330980105e14c9 Mon Sep 17 00:00:00 2001 From: Valentine Barshak Date: Thu, 1 Aug 2019 22:44:03 +0300 Subject: [PATCH 08/23] clk: renesas: cpg: Update SD-IF clock handling Make SD-IF clock division rate table configurable in the cpg_mssr_info structure. This is needed for R-Car V3M, which uses different SD-IF register settings, and thus needs a different division ratio table and a different mask value. While at it, make clk_set_rate callback select the closest ratio value using the SD-IF clock division ratio table instead of hard-coding it. Signed-off-by: Valentine Barshak --- drivers/clk/renesas/clk-rcar-gen3.c | 117 +++++++++++++++++++++------------ drivers/clk/renesas/renesas-cpg-mssr.h | 8 +++ 2 files changed, 82 insertions(+), 43 deletions(-) diff --git a/drivers/clk/renesas/clk-rcar-gen3.c b/drivers/clk/renesas/clk-rcar-gen3.c index 0529fc8..0ddd907 100644 --- a/drivers/clk/renesas/clk-rcar-gen3.c +++ b/drivers/clk/renesas/clk-rcar-gen3.c @@ -51,11 +51,6 @@ .div = (sd_div), \ } -struct sd_div_table { - u32 val; - unsigned int div; -}; - /* SDn divider * sd_srcfc sd_fc div * stp_hck stp_ck (div) (div) = sd_srcfc x sd_fc @@ -71,7 +66,7 @@ struct sd_div_table { * 1 0 3 (8) 0 (2) 16 * 1 0 4 (16) 0 (2) 32 */ -static const struct sd_div_table cpg_sd_div_table[] = { +static const struct cpg_sd_div_table cpg_sd_div_table[] = { /* CPG_SD_DIV_TABLE_DATA(stp_hck, stp_ck, sd_srcfc, sd_fc, sd_div) */ CPG_SD_DIV_TABLE_DATA(0, 0, 0, 1, 4), CPG_SD_DIV_TABLE_DATA(0, 0, 1, 1, 8), @@ -107,37 +102,6 @@ static int gen3_clk_get_parent(struct gen3_clk_priv *priv, struct clk *clk, return renesas_clk_get_parent(clk, info, parent); } -static int gen3_clk_setup_sdif_div(struct clk *clk, ulong rate) -{ - struct gen3_clk_priv *priv = dev_get_priv(clk->dev); - struct cpg_mssr_info *info = priv->info; - const struct cpg_core_clk *core; - struct clk parent; - int ret; - - ret = gen3_clk_get_parent(priv, clk, info, &parent); - if (ret) { - printf("%s[%i] parent fail, ret=%i\n", __func__, __LINE__, ret); - return ret; - } - - if (renesas_clk_is_mod(&parent)) - return 0; - - ret = renesas_clk_get_core(&parent, info, &core); - if (ret) - return ret; - - if (core->type != CLK_TYPE_GEN3_SD) - return 0; - - debug("%s[%i] SDIF offset=%x\n", __func__, __LINE__, core->offset); - - writel((rate == 400000000) ? 0x4 : 0x1, priv->base + core->offset); - - return 0; -} - static int gen3_clk_enable(struct clk *clk) { struct gen3_clk_priv *priv = dev_get_priv(clk->dev); @@ -268,17 +232,17 @@ static u64 gen3_clk_get_rate64(struct clk *clk) case CLK_TYPE_GEN3_SD: /* FIXME */ value = readl(priv->base + core->offset); - value &= CPG_SD_STP_MASK | CPG_SD_FC_MASK; + value &= info->sd_div_mask; - for (i = 0; i < ARRAY_SIZE(cpg_sd_div_table); i++) { - if (cpg_sd_div_table[i].val != value) + for (i = 0; i < info->sd_div_table_size; i++) { + if (info->sd_div_table[i].val != value) continue; rate = gen3_clk_get_rate64(&parent) / - cpg_sd_div_table[i].div; + info->sd_div_table[i].div; debug("%s[%i] SD clk: parent=%i div=%i => rate=%llu\n", __func__, __LINE__, - core->parent, cpg_sd_div_table[i].div, rate); + core->parent, info->sd_div_table[i].div, rate); return rate; } @@ -316,6 +280,59 @@ static u64 gen3_clk_get_rate64(struct clk *clk) return -ENOENT; } +static int gen3_clk_setup_sdif_div(struct clk *clk, ulong rate) +{ + struct gen3_clk_priv *priv = dev_get_priv(clk->dev); + struct cpg_mssr_info *info = priv->info; + int i, ret, up = INT_MAX, div = INT_MAX; + int entry = info->sd_div_table_size; + const struct cpg_core_clk *core; + struct clk parent; + u64 parent_rate; + + if (renesas_clk_is_mod(clk)) + return 0; + + ret = renesas_clk_get_core(clk, info, &core); + if (ret) + return ret; + + if (core->type != CLK_TYPE_GEN3_SD) + return 0; + + ret = gen3_clk_get_parent(priv, clk, info, &parent); + if (ret) { + printf("%s[%i] parent fail, ret=%i\n", __func__, __LINE__, ret); + return ret; + } + + parent_rate = gen3_clk_get_rate64(&parent); + if (rate) + div = DIV_ROUND_CLOSEST(parent_rate, rate); + + for (i = 0; i < info->sd_div_table_size; i++) { + if (info->sd_div_table[i].div < div) + continue; + + if (info->sd_div_table[i].div == div) { + entry = i; + break; + } + + if ((info->sd_div_table[i].div - div) < (up - div)) { + up = info->sd_div_table[i].div; + entry = i; + } + } + + if (entry >= info->sd_div_table_size) + return -EINVAL; + + debug("%s[%i] SDIF offset=%x\n", __func__, __LINE__, core->offset); + writel(info->sd_div_table[entry].val, priv->base + core->offset); + return 0; +} + static ulong gen3_clk_get_rate(struct clk *clk) { return gen3_clk_get_rate64(clk); @@ -323,8 +340,16 @@ static ulong gen3_clk_get_rate(struct clk *clk) static ulong gen3_clk_set_rate(struct clk *clk, ulong rate) { + struct gen3_clk_priv *priv = dev_get_priv(clk->dev); + struct cpg_mssr_info *info = priv->info; + struct clk parent; + int ret; + /* Force correct SD-IF divider configuration if applicable */ - gen3_clk_setup_sdif_div(clk, rate); + ret = gen3_clk_get_parent(priv, clk, info, &parent); + if (!ret) + gen3_clk_setup_sdif_div(&parent, rate); + return gen3_clk_get_rate64(clk); } @@ -366,6 +391,12 @@ int gen3_clk_probe(struct udevice *dev) if (ret < 0) return ret; + if (!info->sd_div_table) { + info->sd_div_table = cpg_sd_div_table; + info->sd_div_table_size = ARRAY_SIZE(cpg_sd_div_table); + info->sd_div_mask = CPG_SD_STP_MASK | CPG_SD_FC_MASK; + } + rst_base = fdtdec_get_addr(gd->fdt_blob, ret, "reg"); if (rst_base == FDT_ADDR_T_NONE) return -EINVAL; diff --git a/drivers/clk/renesas/renesas-cpg-mssr.h b/drivers/clk/renesas/renesas-cpg-mssr.h index bad3938..36072a6 100644 --- a/drivers/clk/renesas/renesas-cpg-mssr.h +++ b/drivers/clk/renesas/renesas-cpg-mssr.h @@ -13,6 +13,11 @@ #ifndef __DRIVERS_CLK_RENESAS_CPG_MSSR__ #define __DRIVERS_CLK_RENESAS_CPG_MSSR__ +struct cpg_sd_div_table { + u32 val; + unsigned int div; +}; + struct cpg_mssr_info { const struct cpg_core_clk *core_clk; unsigned int core_clk_size; @@ -20,6 +25,9 @@ struct cpg_mssr_info { unsigned int mod_clk_size; const struct mstp_stop_table *mstp_table; unsigned int mstp_table_size; + const struct cpg_sd_div_table *sd_div_table; + unsigned int sd_div_table_size; + u32 sd_div_mask; const char *reset_node; const char *extalr_node; const char *extal_usb_node; -- 2.7.4