Skip to content

Commit 1b131e0

Browse files
committed
clk: renesas: rcar-gen4: Add support for fractional multiplication
R-Car Gen4 PLLs support fractional multiplication, which can improve accuracy when configuring a specific frequency. Add support for fractional multiplication to the custom clock driver for PLLs, which is currently used only for PLL2 on R-Car V4H. While at it, add the missing blank line after the function. Note that Fractional Multiplication is not enabled by the driver, but used only if the boot loaded enabled it before. Signed-off-by: Geert Uytterhoeven <[email protected]> Reviewed-by: Yoshihiro Shimoda <[email protected]> Link: https://lore.kernel.org/1a58ebef6f54460f49fb81ba9bbf288164de2646.1721648548.git.geert+renesas@glider.be
1 parent dd82ab4 commit 1b131e0

File tree

1 file changed

+55
-16
lines changed

1 file changed

+55
-16
lines changed

drivers/clk/renesas/rcar-gen4-cpg.c

Lines changed: 55 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ static u32 cpg_mode __initdata;
5454

5555
/* Fractional 8.25 PLL */
5656
#define CPG_PLLxCR0_NI8 GENMASK(27, 20) /* Integer mult. factor */
57+
#define CPG_PLLxCR1_NF25 GENMASK(24, 0) /* Fractional mult. factor */
5758

5859
#define CPG_PLLxCR_STC GENMASK(30, 24) /* R_Car V3U PLLxCR */
5960

@@ -67,6 +68,7 @@ static u32 cpg_mode __initdata;
6768
struct cpg_pll_clk {
6869
struct clk_hw hw;
6970
void __iomem *pllcr0_reg;
71+
void __iomem *pllcr1_reg;
7072
void __iomem *pllecr_reg;
7173
u32 pllecr_pllst_mask;
7274
};
@@ -77,17 +79,26 @@ static unsigned long cpg_pll_8_25_clk_recalc_rate(struct clk_hw *hw,
7779
unsigned long parent_rate)
7880
{
7981
struct cpg_pll_clk *pll_clk = to_pll_clk(hw);
80-
unsigned int mult;
81-
82-
mult = FIELD_GET(CPG_PLLxCR0_NI8, readl(pll_clk->pllcr0_reg)) + 1;
82+
u32 cr0 = readl(pll_clk->pllcr0_reg);
83+
unsigned int ni, nf;
84+
unsigned long rate;
85+
86+
ni = (FIELD_GET(CPG_PLLxCR0_NI8, cr0) + 1) * 2;
87+
rate = parent_rate * ni;
88+
if (cr0 & CPG_PLLxCR0_SSMODE_FM) {
89+
nf = FIELD_GET(CPG_PLLxCR1_NF25, readl(pll_clk->pllcr1_reg));
90+
rate += mul_u64_u32_shr(parent_rate, nf, 24);
91+
}
8392

84-
return parent_rate * mult * 2;
93+
return rate;
8594
}
8695

8796
static int cpg_pll_8_25_clk_determine_rate(struct clk_hw *hw,
8897
struct clk_rate_request *req)
8998
{
90-
unsigned int min_mult, max_mult, mult;
99+
struct cpg_pll_clk *pll_clk = to_pll_clk(hw);
100+
unsigned int min_mult, max_mult, ni, nf;
101+
u32 cr0 = readl(pll_clk->pllcr0_reg);
91102
unsigned long prate;
92103

93104
prate = req->best_parent_rate * 2;
@@ -96,28 +107,58 @@ static int cpg_pll_8_25_clk_determine_rate(struct clk_hw *hw,
96107
if (max_mult < min_mult)
97108
return -EINVAL;
98109

99-
mult = DIV_ROUND_CLOSEST_ULL(req->rate, prate);
100-
mult = clamp(mult, min_mult, max_mult);
110+
if (cr0 & CPG_PLLxCR0_SSMODE_FM) {
111+
ni = div64_ul(req->rate, prate);
112+
if (ni < min_mult) {
113+
ni = min_mult;
114+
nf = 0;
115+
} else {
116+
ni = min(ni, max_mult);
117+
nf = div64_ul((u64)(req->rate - prate * ni) << 24,
118+
req->best_parent_rate);
119+
}
120+
} else {
121+
ni = DIV_ROUND_CLOSEST_ULL(req->rate, prate);
122+
ni = clamp(ni, min_mult, max_mult);
123+
nf = 0;
124+
}
125+
req->rate = prate * ni + mul_u64_u32_shr(req->best_parent_rate, nf, 24);
101126

102-
req->rate = prate * mult;
103127
return 0;
104128
}
105129

106130
static int cpg_pll_8_25_clk_set_rate(struct clk_hw *hw, unsigned long rate,
107131
unsigned long parent_rate)
108132
{
109133
struct cpg_pll_clk *pll_clk = to_pll_clk(hw);
110-
unsigned int mult;
134+
unsigned long prate = parent_rate * 2;
135+
u32 cr0 = readl(pll_clk->pllcr0_reg);
136+
unsigned int ni, nf;
111137
u32 val;
112138

113-
mult = DIV_ROUND_CLOSEST_ULL(rate, parent_rate * 2);
114-
mult = clamp(mult, 1U, 256U);
139+
if (cr0 & CPG_PLLxCR0_SSMODE_FM) {
140+
ni = div64_ul(rate, prate);
141+
if (ni < 1) {
142+
ni = 1;
143+
nf = 0;
144+
} else {
145+
ni = min(ni, 256U);
146+
nf = div64_ul((u64)(rate - prate * ni) << 24,
147+
parent_rate);
148+
}
149+
} else {
150+
ni = DIV_ROUND_CLOSEST_ULL(rate, prate);
151+
ni = clamp(ni, 1U, 256U);
152+
}
115153

116154
if (readl(pll_clk->pllcr0_reg) & CPG_PLLxCR0_KICK)
117155
return -EBUSY;
118156

119157
cpg_reg_modify(pll_clk->pllcr0_reg, CPG_PLLxCR0_NI8,
120-
FIELD_PREP(CPG_PLLxCR0_NI8, mult - 1));
158+
FIELD_PREP(CPG_PLLxCR0_NI8, ni - 1));
159+
if (cr0 & CPG_PLLxCR0_SSMODE_FM)
160+
cpg_reg_modify(pll_clk->pllcr1_reg, CPG_PLLxCR1_NF25,
161+
FIELD_PREP(CPG_PLLxCR1_NF25, nf));
121162

122163
/*
123164
* Set KICK bit in PLLxCR0 to update hardware setting and wait for
@@ -167,19 +208,17 @@ static struct clk * __init cpg_pll_clk_register(const char *name,
167208

168209
pll_clk->hw.init = &init;
169210
pll_clk->pllcr0_reg = base + cr0_offset;
211+
pll_clk->pllcr1_reg = base + cr1_offset;
170212
pll_clk->pllecr_reg = base + CPG_PLLECR;
171213
pll_clk->pllecr_pllst_mask = CPG_PLLECR_PLLST(index);
172214

173-
/* Disable Fractional Multiplication and Frequency Dithering */
174-
writel(0, base + cr1_offset);
175-
cpg_reg_modify(pll_clk->pllcr0_reg, CPG_PLLxCR0_SSMODE, 0);
176-
177215
clk = clk_register(NULL, &pll_clk->hw);
178216
if (IS_ERR(clk))
179217
kfree(pll_clk);
180218

181219
return clk;
182220
}
221+
183222
/*
184223
* Z0 Clock & Z1 Clock
185224
*/

0 commit comments

Comments
 (0)