diff options
Diffstat (limited to 'drivers/clk/renesas/rcar-gen4-cpg.c')
-rw-r--r-- | drivers/clk/renesas/rcar-gen4-cpg.c | 210 |
1 files changed, 141 insertions, 69 deletions
diff --git a/drivers/clk/renesas/rcar-gen4-cpg.c b/drivers/clk/renesas/rcar-gen4-cpg.c index 77a4bb3e17f3..31aa790fd003 100644 --- a/drivers/clk/renesas/rcar-gen4-cpg.c +++ b/drivers/clk/renesas/rcar-gen4-cpg.c @@ -45,7 +45,6 @@ static u32 cpg_mode __initdata; #define CPG_PLL6CR1 0x8d8 #define CPG_PLLxCR0_KICK BIT(31) -#define CPG_PLLxCR0_NI GENMASK(27, 20) /* Integer mult. factor */ #define CPG_PLLxCR0_SSMODE GENMASK(18, 16) /* PLL mode */ #define CPG_PLLxCR0_SSMODE_FM BIT(18) /* Fractional Multiplication */ #define CPG_PLLxCR0_SSMODE_DITH BIT(17) /* Frequency Dithering */ @@ -53,35 +52,57 @@ static u32 cpg_mode __initdata; #define CPG_PLLxCR0_SSFREQ GENMASK(14, 8) /* SSCG Modulation Frequency */ #define CPG_PLLxCR0_SSDEPT GENMASK(6, 0) /* SSCG Modulation Depth */ -#define SSMODE_FM BIT(2) /* Fractional Multiplication */ -#define SSMODE_DITHER BIT(1) /* Frequency Dithering */ -#define SSMODE_CENTER BIT(0) /* Center (vs. Down) Spread Dithering */ +/* Fractional 8.25 PLL */ +#define CPG_PLLxCR0_NI8 GENMASK(27, 20) /* Integer mult. factor */ +#define CPG_PLLxCR1_NF25 GENMASK(24, 0) /* Fractional mult. factor */ + +/* Fractional 9.24 PLL */ +#define CPG_PLLxCR0_NI9 GENMASK(28, 20) /* Integer mult. factor */ +#define CPG_PLLxCR1_NF24 GENMASK(23, 0) /* Fractional mult. factor */ + +#define CPG_PLLxCR_STC GENMASK(30, 24) /* R_Car V3U PLLxCR */ + +#define CPG_RPCCKCR 0x874 /* RPC Clock Freq. Control Register */ + +#define CPG_SD0CKCR1 0x8a4 /* SD-IF0 Clock Freq. Control Reg. 1 */ + +#define CPG_SD0CKCR1_SDSRC_SEL GENMASK(30, 29) /* SDSRC clock freq. select */ /* PLL Clocks */ struct cpg_pll_clk { struct clk_hw hw; void __iomem *pllcr0_reg; + void __iomem *pllcr1_reg; void __iomem *pllecr_reg; u32 pllecr_pllst_mask; }; #define to_pll_clk(_hw) container_of(_hw, struct cpg_pll_clk, hw) -static unsigned long cpg_pll_clk_recalc_rate(struct clk_hw *hw, - unsigned long parent_rate) +static unsigned long cpg_pll_8_25_clk_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) { struct cpg_pll_clk *pll_clk = to_pll_clk(hw); - unsigned int mult; - - mult = FIELD_GET(CPG_PLLxCR0_NI, readl(pll_clk->pllcr0_reg)) + 1; + u32 cr0 = readl(pll_clk->pllcr0_reg); + unsigned int ni, nf; + unsigned long rate; + + ni = (FIELD_GET(CPG_PLLxCR0_NI8, cr0) + 1) * 2; + rate = parent_rate * ni; + if (cr0 & CPG_PLLxCR0_SSMODE_FM) { + nf = FIELD_GET(CPG_PLLxCR1_NF25, readl(pll_clk->pllcr1_reg)); + rate += mul_u64_u32_shr(parent_rate, nf, 24); + } - return parent_rate * mult * 2; + return rate; } -static int cpg_pll_clk_determine_rate(struct clk_hw *hw, - struct clk_rate_request *req) +static int cpg_pll_8_25_clk_determine_rate(struct clk_hw *hw, + struct clk_rate_request *req) { - unsigned int min_mult, max_mult, mult; + struct cpg_pll_clk *pll_clk = to_pll_clk(hw); + unsigned int min_mult, max_mult, ni, nf; + u32 cr0 = readl(pll_clk->pllcr0_reg); unsigned long prate; prate = req->best_parent_rate * 2; @@ -90,28 +111,58 @@ static int cpg_pll_clk_determine_rate(struct clk_hw *hw, if (max_mult < min_mult) return -EINVAL; - mult = DIV_ROUND_CLOSEST_ULL(req->rate, prate); - mult = clamp(mult, min_mult, max_mult); + if (cr0 & CPG_PLLxCR0_SSMODE_FM) { + ni = div64_ul(req->rate, prate); + if (ni < min_mult) { + ni = min_mult; + nf = 0; + } else { + ni = min(ni, max_mult); + nf = div64_ul((u64)(req->rate - prate * ni) << 24, + req->best_parent_rate); + } + } else { + ni = DIV_ROUND_CLOSEST_ULL(req->rate, prate); + ni = clamp(ni, min_mult, max_mult); + nf = 0; + } + req->rate = prate * ni + mul_u64_u32_shr(req->best_parent_rate, nf, 24); - req->rate = prate * mult; return 0; } -static int cpg_pll_clk_set_rate(struct clk_hw *hw, unsigned long rate, - unsigned long parent_rate) +static int cpg_pll_8_25_clk_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) { struct cpg_pll_clk *pll_clk = to_pll_clk(hw); - unsigned int mult; + unsigned long prate = parent_rate * 2; + u32 cr0 = readl(pll_clk->pllcr0_reg); + unsigned int ni, nf; u32 val; - mult = DIV_ROUND_CLOSEST_ULL(rate, parent_rate * 2); - mult = clamp(mult, 1U, 256U); + if (cr0 & CPG_PLLxCR0_SSMODE_FM) { + ni = div64_ul(rate, prate); + if (ni < 1) { + ni = 1; + nf = 0; + } else { + ni = min(ni, 256U); + nf = div64_ul((u64)(rate - prate * ni) << 24, + parent_rate); + } + } else { + ni = DIV_ROUND_CLOSEST_ULL(rate, prate); + ni = clamp(ni, 1U, 256U); + } if (readl(pll_clk->pllcr0_reg) & CPG_PLLxCR0_KICK) return -EBUSY; - cpg_reg_modify(pll_clk->pllcr0_reg, CPG_PLLxCR0_NI, - FIELD_PREP(CPG_PLLxCR0_NI, mult - 1)); + cpg_reg_modify(pll_clk->pllcr0_reg, CPG_PLLxCR0_NI8, + FIELD_PREP(CPG_PLLxCR0_NI8, ni - 1)); + if (cr0 & CPG_PLLxCR0_SSMODE_FM) + cpg_reg_modify(pll_clk->pllcr1_reg, CPG_PLLxCR1_NF25, + FIELD_PREP(CPG_PLLxCR1_NF25, nf)); /* * Set KICK bit in PLLxCR0 to update hardware setting and wait for @@ -132,22 +183,55 @@ static int cpg_pll_clk_set_rate(struct clk_hw *hw, unsigned long rate, val & pll_clk->pllecr_pllst_mask, 0, 1000); } -static const struct clk_ops cpg_pll_clk_ops = { - .recalc_rate = cpg_pll_clk_recalc_rate, - .determine_rate = cpg_pll_clk_determine_rate, - .set_rate = cpg_pll_clk_set_rate, +static const struct clk_ops cpg_pll_f8_25_clk_ops = { + .recalc_rate = cpg_pll_8_25_clk_recalc_rate, +}; + +static const struct clk_ops cpg_pll_v8_25_clk_ops = { + .recalc_rate = cpg_pll_8_25_clk_recalc_rate, + .determine_rate = cpg_pll_8_25_clk_determine_rate, + .set_rate = cpg_pll_8_25_clk_set_rate, +}; + +static unsigned long cpg_pll_9_24_clk_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct cpg_pll_clk *pll_clk = to_pll_clk(hw); + u32 cr0 = readl(pll_clk->pllcr0_reg); + unsigned int ni, nf; + unsigned long rate; + + ni = FIELD_GET(CPG_PLLxCR0_NI9, cr0) + 1; + rate = parent_rate * ni; + if (cr0 & CPG_PLLxCR0_SSMODE_FM) { + nf = FIELD_GET(CPG_PLLxCR1_NF24, readl(pll_clk->pllcr1_reg)); + rate += mul_u64_u32_shr(parent_rate, nf, 24); + } else { + rate *= 2; + } + + return rate; +} + +static const struct clk_ops cpg_pll_f9_24_clk_ops = { + .recalc_rate = cpg_pll_9_24_clk_recalc_rate, }; static struct clk * __init cpg_pll_clk_register(const char *name, const char *parent_name, void __iomem *base, - unsigned int cr0_offset, - unsigned int cr1_offset, - unsigned int index) - + unsigned int index, + const struct clk_ops *ops) { - struct cpg_pll_clk *pll_clk; + static const struct { u16 cr0, cr1; } pll_cr_offsets[] __initconst = { + [1 - 1] = { CPG_PLL1CR0, CPG_PLL1CR1 }, + [2 - 1] = { CPG_PLL2CR0, CPG_PLL2CR1 }, + [3 - 1] = { CPG_PLL3CR0, CPG_PLL3CR1 }, + [4 - 1] = { CPG_PLL4CR0, CPG_PLL4CR1 }, + [6 - 1] = { CPG_PLL6CR0, CPG_PLL6CR1 }, + }; struct clk_init_data init = {}; + struct cpg_pll_clk *pll_clk; struct clk *clk; pll_clk = kzalloc(sizeof(*pll_clk), GFP_KERNEL); @@ -155,25 +239,23 @@ static struct clk * __init cpg_pll_clk_register(const char *name, return ERR_PTR(-ENOMEM); init.name = name; - init.ops = &cpg_pll_clk_ops; + init.ops = ops; init.parent_names = &parent_name; init.num_parents = 1; pll_clk->hw.init = &init; - pll_clk->pllcr0_reg = base + cr0_offset; + pll_clk->pllcr0_reg = base + pll_cr_offsets[index - 1].cr0; + pll_clk->pllcr1_reg = base + pll_cr_offsets[index - 1].cr1; pll_clk->pllecr_reg = base + CPG_PLLECR; pll_clk->pllecr_pllst_mask = CPG_PLLECR_PLLST(index); - /* Disable Fractional Multiplication and Frequency Dithering */ - writel(0, base + cr1_offset); - cpg_reg_modify(pll_clk->pllcr0_reg, CPG_PLLxCR0_SSMODE, 0); - clk = clk_register(NULL, &pll_clk->hw); if (IS_ERR(clk)) kfree(pll_clk); return clk; } + /* * Z0 Clock & Z1 Clock */ @@ -358,51 +440,41 @@ struct clk * __init rcar_gen4_cpg_clk_register(struct device *dev, div = cpg_pll_config->pll1_div; break; - case CLK_TYPE_GEN4_PLL2_VAR: - /* - * PLL2 is implemented as a custom clock, to change the - * multiplier when cpufreq changes between normal and boost - * modes. - */ - return cpg_pll_clk_register(core->name, __clk_get_name(parent), - base, CPG_PLL2CR0, CPG_PLL2CR1, 2); - - case CLK_TYPE_GEN4_PLL2: - mult = cpg_pll_config->pll2_mult; - div = cpg_pll_config->pll2_div; - break; - - case CLK_TYPE_GEN4_PLL3: - mult = cpg_pll_config->pll3_mult; - div = cpg_pll_config->pll3_div; - break; - - case CLK_TYPE_GEN4_PLL4: - mult = cpg_pll_config->pll4_mult; - div = cpg_pll_config->pll4_div; - break; - case CLK_TYPE_GEN4_PLL5: mult = cpg_pll_config->pll5_mult; div = cpg_pll_config->pll5_div; break; - case CLK_TYPE_GEN4_PLL6: - mult = cpg_pll_config->pll6_mult; - div = cpg_pll_config->pll6_div; - break; - case CLK_TYPE_GEN4_PLL2X_3X: value = readl(base + core->offset); - mult = (((value >> 24) & 0x7f) + 1) * 2; + mult = (FIELD_GET(CPG_PLLxCR_STC, value) + 1) * 2; break; + case CLK_TYPE_GEN4_PLL_F8_25: + return cpg_pll_clk_register(core->name, __clk_get_name(parent), + base, core->offset, + &cpg_pll_f8_25_clk_ops); + + case CLK_TYPE_GEN4_PLL_V8_25: + return cpg_pll_clk_register(core->name, __clk_get_name(parent), + base, core->offset, + &cpg_pll_v8_25_clk_ops); + + case CLK_TYPE_GEN4_PLL_V9_24: + /* Variable fractional 9.24 is not yet supported, using fixed */ + fallthrough; + case CLK_TYPE_GEN4_PLL_F9_24: + return cpg_pll_clk_register(core->name, __clk_get_name(parent), + base, core->offset, + &cpg_pll_f9_24_clk_ops); + case CLK_TYPE_GEN4_Z: return cpg_z_clk_register(core->name, __clk_get_name(parent), base, core->div, core->offset); case CLK_TYPE_GEN4_SDSRC: - div = ((readl(base + SD0CKCR1) >> 29) & 0x03) + 4; + value = readl(base + CPG_SD0CKCR1); + div = FIELD_GET(CPG_SD0CKCR1_SDSRC_SEL, value) + 4; break; case CLK_TYPE_GEN4_SDH: |