diff options
-rw-r--r-- | drivers/clk/renesas/clk-rcar-gen3.c | 198 | ||||
-rw-r--r-- | drivers/clk/renesas/rcar-gen3-cpg.h | 5 | ||||
-rw-r--r-- | drivers/mmc/renesas-sdhi.c | 21 | ||||
-rw-r--r-- | drivers/mmc/tmio-common.h | 1 |
4 files changed, 130 insertions, 95 deletions
diff --git a/drivers/clk/renesas/clk-rcar-gen3.c b/drivers/clk/renesas/clk-rcar-gen3.c index aea8b1e839..53f16dfb1e 100644 --- a/drivers/clk/renesas/clk-rcar-gen3.c +++ b/drivers/clk/renesas/clk-rcar-gen3.c @@ -34,55 +34,11 @@ #define CPG_PLL2CR 0x002c #define CPG_PLL4CR 0x01f4 -/* Non-constant mask variant of FIELD_GET */ -#define field_get(_mask, _reg) (((_reg) & (_mask)) >> (ffs(_mask) - 1)) - -/* - * SDn Clock - */ -#define CPG_SD_STP_HCK BIT(9) -#define CPG_SD_STP_CK BIT(8) - -#define CPG_SD_STP_MASK (CPG_SD_STP_HCK | CPG_SD_STP_CK) -#define CPG_SD_FC_MASK (0x7 << 2 | 0x3 << 0) - -#define CPG_SD_DIV_TABLE_DATA(stp_hck, stp_ck, sd_srcfc, sd_fc, sd_div) \ -{ \ - .val = ((stp_hck) ? CPG_SD_STP_HCK : 0) | \ - ((stp_ck) ? CPG_SD_STP_CK : 0) | \ - ((sd_srcfc) << 2) | \ - ((sd_fc) << 0), \ - .div = (sd_div), \ -} +#define SDnSRCFC_SHIFT 2 +#define STPnHCK_TABLE (CPG_SDCKCR_STPnHCK >> SDnSRCFC_SHIFT) -/* SDn divider - * sd_srcfc sd_fc div - * stp_hck stp_ck (div) (div) = sd_srcfc x sd_fc - *------------------------------------------------------------------- - * 0 0 0 (1) 1 (4) 4 - * 0 0 1 (2) 1 (4) 8 - * 1 0 2 (4) 1 (4) 16 - * 1 0 3 (8) 1 (4) 32 - * 1 0 4 (16) 1 (4) 64 - * 0 0 0 (1) 0 (2) 2 - * 0 0 1 (2) 0 (2) 4 - * 1 0 2 (4) 0 (2) 8 - * 1 0 3 (8) 0 (2) 16 - * 1 0 4 (16) 0 (2) 32 - */ -static const struct clk_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), - CPG_SD_DIV_TABLE_DATA(1, 0, 2, 1, 16), - CPG_SD_DIV_TABLE_DATA(1, 0, 3, 1, 32), - CPG_SD_DIV_TABLE_DATA(1, 0, 4, 1, 64), - CPG_SD_DIV_TABLE_DATA(0, 0, 0, 0, 2), - CPG_SD_DIV_TABLE_DATA(0, 0, 1, 0, 4), - CPG_SD_DIV_TABLE_DATA(1, 0, 2, 0, 8), - CPG_SD_DIV_TABLE_DATA(1, 0, 3, 0, 16), - CPG_SD_DIV_TABLE_DATA(1, 0, 4, 0, 32), -}; +/* Non-constant mask variant of FIELD_GET/FIELD_PREP */ +#define field_get(_mask, _reg) (((_reg) & (_mask)) >> (ffs(_mask) - 1)) static const struct clk_div_table cpg_rpcsrc_div_table[] = { { 2, 5 }, { 3, 6 }, { 0, 0 }, @@ -92,6 +48,15 @@ static const struct clk_div_table cpg_rpc_div_table[] = { { 1, 2 }, { 3, 4 }, { 5, 6 }, { 7, 8 }, { 0, 0 }, }; +static const struct clk_div_table cpg_sdh_div_table[] = { + { 0, 1 }, { 1, 2 }, { STPnHCK_TABLE | 2, 4 }, { STPnHCK_TABLE | 3, 8 }, + { STPnHCK_TABLE | 4, 16 }, { 0, 0 }, +}; + +static const struct clk_div_table cpg_sd_div_table[] = { + { 0, 2 }, { 1, 4 }, { 0, 0 }, +}; + static unsigned int rcar_clk_get_table_div(const struct clk_div_table *table, const u32 value) { @@ -103,6 +68,17 @@ static unsigned int rcar_clk_get_table_div(const struct clk_div_table *table, return 0; } +static int rcar_clk_get_table_val(const struct clk_div_table *table, + unsigned int div) +{ + const struct clk_div_table *clkt; + + for (clkt = table; clkt->div; clkt++) + if (clkt->div == div) + return clkt->val; + return -EINVAL; +} + static __always_inline s64 rcar_clk_get_rate64_div_table(unsigned int parent, u64 parent_rate, void __iomem *reg, const u32 mask, @@ -145,18 +121,45 @@ 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_enable(struct clk *clk) +{ + struct gen3_clk_priv *priv = dev_get_priv(clk->dev); + + return renesas_clk_endisable(clk, priv->base, priv->info, true); +} + +static int gen3_clk_disable(struct clk *clk) +{ + struct gen3_clk_priv *priv = dev_get_priv(clk->dev); + + return renesas_clk_endisable(clk, priv->base, priv->info, false); +} + +static u64 gen3_clk_get_rate64(struct clk *clk); + 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; + struct clk parent, grandparent; 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; + u32 value = 0, div = 0; + + /* + * The clk may be either CPG_MOD or core clock, in case this is MOD + * clock, use core clock one level up, otherwise use the clock as-is. + * Note that parent clock here always represents core clock. Also note + * that grandparent clock are the parent clock of the core clock here. + */ + if (renesas_clk_is_mod(clk)) { + ret = gen3_clk_get_parent(priv, clk, info, &parent); + if (ret) { + printf("%s[%i] parent fail, ret=%i\n", __func__, __LINE__, ret); + return ret; + } + } else { + parent = *clk; } if (renesas_clk_is_mod(&parent)) @@ -166,32 +169,47 @@ static int gen3_clk_setup_sdif_div(struct clk *clk, ulong rate) if (ret) return ret; - if (core->type != CLK_TYPE_GEN3_SD) - return 0; + ret = renesas_clk_get_parent(&parent, info, &grandparent); + if (ret) { + printf("%s[%i] grandparent fail, ret=%i\n", __func__, __LINE__, ret); + return ret; + } - debug("%s[%i] SDIF offset=%x\n", __func__, __LINE__, core->offset); + switch (core->type) { + case CLK_TYPE_GEN3_SDH: + fallthrough; + case CLK_TYPE_GEN4_SDH: + div = DIV_ROUND_CLOSEST(gen3_clk_get_rate64(&grandparent), rate); + value = rcar_clk_get_table_val(cpg_sdh_div_table, div); + if (value < 0) + return value; - writel((rate == 400000000) ? 0x4 : 0x1, priv->base + core->offset); + clrsetbits_le32(priv->base + core->offset, + GENMASK(9, 2), value << 2); - return 0; -} + debug("%s[%i] SDH clk: parent=%i offset=%x div=%u rate=%lu => val=%u\n", + __func__, __LINE__, core->parent, core->offset, div, rate, value); + break; -static int gen3_clk_enable(struct clk *clk) -{ - struct gen3_clk_priv *priv = dev_get_priv(clk->dev); + case CLK_TYPE_GEN3_SD: + fallthrough; + case CLK_TYPE_GEN4_SD: + div = DIV_ROUND_CLOSEST(gen3_clk_get_rate64(&grandparent), rate); + value = rcar_clk_get_table_val(cpg_sd_div_table, div); + if (value < 0) + return value; - return renesas_clk_endisable(clk, priv->base, priv->info, true); -} + clrsetbits_le32(priv->base + core->offset, + GENMASK(1, 0), value); -static int gen3_clk_disable(struct clk *clk) -{ - struct gen3_clk_priv *priv = dev_get_priv(clk->dev); + debug("%s[%i] SD clk: parent=%i offset=%x div=%u rate=%lu => val=%u\n", + __func__, __LINE__, core->parent, core->offset, div, rate, value); + break; + } - return renesas_clk_endisable(clk, priv->base, priv->info, false); + return 0; } -static u64 gen3_clk_get_rate64(struct clk *clk); - static u64 gen3_clk_get_rate64_pll_mul_reg(struct gen3_clk_priv *priv, struct clk *parent, u32 mul_reg, u32 mult, u32 div, @@ -223,7 +241,7 @@ static u64 gen3_clk_get_rate64(struct clk *clk) priv->cpg_pll_config; u32 value, div; u64 rate = 0; - int i, ret; + int ret; debug("%s[%i] Clock: id=%lu\n", __func__, __LINE__, clk->id); @@ -328,28 +346,26 @@ static u64 gen3_clk_get_rate64(struct clk *clk) case CLK_TYPE_GEN3_SDH: /* Fixed factor 1:1 */ fallthrough; case CLK_TYPE_GEN4_SDH: /* Fixed factor 1:1 */ - return gen3_clk_get_rate64(&parent); + /* + * This takes STPnHCK and STPnCK bits into consideration + * in the table look up too, hence the inobvious GENMASK + * below. Bits [7:5] always read zero, so this is OKish. + */ + return rcar_clk_get_rate64_div_table(core->parent, + gen3_clk_get_rate64(&parent), + priv->base + core->offset, + CPG_SDCKCR_SRCFC_MASK | + GENMASK(9, 5), + cpg_sdh_div_table, "SDH"); - case CLK_TYPE_GEN3_SD: /* FIXME */ + case CLK_TYPE_GEN3_SD: fallthrough; case CLK_TYPE_GEN4_SD: - value = readl(priv->base + core->offset); - value &= CPG_SD_STP_MASK | CPG_SD_FC_MASK; - - for (i = 0; i < ARRAY_SIZE(cpg_sd_div_table); i++) { - if (cpg_sd_div_table[i].val != value) - continue; - - rate = gen3_clk_get_rate64(&parent) / - cpg_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); - - return rate; - } - - return -EINVAL; + return rcar_clk_get_rate64_div_table(core->parent, + gen3_clk_get_rate64(&parent), + priv->base + core->offset, + CPG_SDCKCR_FC_MASK, + cpg_sd_div_table, "SD"); case CLK_TYPE_GEN3_RPCSRC: return rcar_clk_get_rate64_div_table(core->parent, diff --git a/drivers/clk/renesas/rcar-gen3-cpg.h b/drivers/clk/renesas/rcar-gen3-cpg.h index 41a30c569c..008e8928e5 100644 --- a/drivers/clk/renesas/rcar-gen3-cpg.h +++ b/drivers/clk/renesas/rcar-gen3-cpg.h @@ -111,6 +111,11 @@ struct rcar_gen3_cpg_pll_config { #define CPG_RST_MODEMR 0x060 +#define CPG_SDCKCR_STPnHCK BIT(9) +#define CPG_SDCKCR_STPnCK BIT(8) +#define CPG_SDCKCR_SRCFC_MASK GENMASK(4, 2) +#define CPG_SDCKCR_FC_MASK GENMASK(1, 0) + #define CPG_RPCCKCR 0x238 #define CPG_RPCCKCR_DIV_POST_MASK GENMASK(4, 3) #define CPG_RPCCKCR_DIV_PRE_MASK GENMASK(2, 0) diff --git a/drivers/mmc/renesas-sdhi.c b/drivers/mmc/renesas-sdhi.c index f30d7847bf..4a1accebfc 100644 --- a/drivers/mmc/renesas-sdhi.c +++ b/drivers/mmc/renesas-sdhi.c @@ -358,13 +358,21 @@ static int renesas_sdhi_hs400(struct udevice *dev) struct mmc *mmc = mmc_get_mmc_dev(dev); bool hs400 = (mmc->selected_mode == MMC_HS_400); int ret, taps = hs400 ? priv->nrtaps : 8; + const u32 sdn_rate = 200000000; + u32 sdnh_rate = 800000000; unsigned long new_tap; u32 reg; - if (taps == 4) /* HS400 on 4tap SoC needs different clock */ - ret = clk_set_rate(&priv->clk, 400000000); - else - ret = clk_set_rate(&priv->clk, 200000000); + if (clk_valid(&priv->clkh) && !priv->needs_clkh_fallback) { + /* HS400 on 4tap SoC => SDnH=400 MHz, SDn=200 MHz */ + if (taps == 4) + sdnh_rate /= 2; + ret = clk_set_rate(&priv->clkh, sdnh_rate); + if (ret < 0) + return ret; + } + + ret = clk_set_rate(&priv->clk, sdn_rate); if (ret < 0) return ret; @@ -967,6 +975,11 @@ static int renesas_sdhi_probe(struct udevice *dev) return ret; } + /* optional SDnH clock */ + ret = clk_get_by_name(dev, "clkh", &priv->clkh); + if (ret < 0) + dev_dbg(dev, "failed to get clkh\n"); + /* set to max rate */ ret = clk_set_rate(&priv->clk, 200000000); if (ret < 0) { diff --git a/drivers/mmc/tmio-common.h b/drivers/mmc/tmio-common.h index e517ed978b..88244e878b 100644 --- a/drivers/mmc/tmio-common.h +++ b/drivers/mmc/tmio-common.h @@ -138,6 +138,7 @@ struct tmio_sd_priv { #endif #if CONFIG_IS_ENABLED(CLK) struct clk clk; + struct clk clkh; #endif #if CONFIG_IS_ENABLED(RENESAS_SDHI) unsigned int smpcmp; |