1 // SPDX-License-Identifier: GPL-2.0
3 * Copyright (c) 2015 Endless Mobile, Inc.
4 * Author: Carlo Caione <carlo@endlessm.com>
6 * Copyright (c) 2018 Baylibre, SAS.
7 * Author: Jerome Brunet <jbrunet@baylibre.com>
11 * In the most basic form, a Meson PLL is composed as follows:
14 * +------------------------------+
16 * in -----[ /N ]---[ *M ]---[ >>OD ]----->> out
18 * +------------------------------+
22 * out = in * (m + frac / frac_max) / (n << sum(ods))
25 #include <linux/clk-provider.h>
26 #include <linux/delay.h>
27 #include <linux/err.h>
29 #include <linux/math64.h>
30 #include <linux/module.h>
31 #include <linux/of_address.h>
32 #include <linux/slab.h>
33 #include <linux/string.h>
37 static inline struct meson_clk_pll_data
*
38 meson_clk_pll_data(struct clk_regmap
*clk
)
40 return (struct meson_clk_pll_data
*)clk
->data
;
43 static unsigned long __pll_params_to_rate(unsigned long parent_rate
,
44 const struct pll_rate_table
*pllt
,
46 struct meson_clk_pll_data
*pll
)
48 u64 rate
= (u64
)parent_rate
* pllt
->m
;
49 unsigned int od
= pllt
->od
+ pllt
->od2
+ pllt
->od3
;
51 if (frac
&& MESON_PARM_APPLICABLE(&pll
->frac
)) {
52 u64 frac_rate
= (u64
)parent_rate
* frac
;
54 rate
+= DIV_ROUND_UP_ULL(frac_rate
,
55 (1 << pll
->frac
.width
));
58 return DIV_ROUND_UP_ULL(rate
, pllt
->n
<< od
);
61 static unsigned long meson_clk_pll_recalc_rate(struct clk_hw
*hw
,
62 unsigned long parent_rate
)
64 struct clk_regmap
*clk
= to_clk_regmap(hw
);
65 struct meson_clk_pll_data
*pll
= meson_clk_pll_data(clk
);
66 struct pll_rate_table pllt
;
69 pllt
.n
= meson_parm_read(clk
->map
, &pll
->n
);
70 pllt
.m
= meson_parm_read(clk
->map
, &pll
->m
);
71 pllt
.od
= meson_parm_read(clk
->map
, &pll
->od
);
73 pllt
.od2
= MESON_PARM_APPLICABLE(&pll
->od2
) ?
74 meson_parm_read(clk
->map
, &pll
->od2
) :
77 pllt
.od3
= MESON_PARM_APPLICABLE(&pll
->od3
) ?
78 meson_parm_read(clk
->map
, &pll
->od3
) :
81 frac
= MESON_PARM_APPLICABLE(&pll
->frac
) ?
82 meson_parm_read(clk
->map
, &pll
->frac
) :
85 return __pll_params_to_rate(parent_rate
, &pllt
, frac
, pll
);
88 static u16
__pll_params_with_frac(unsigned long rate
,
89 unsigned long parent_rate
,
90 const struct pll_rate_table
*pllt
,
91 struct meson_clk_pll_data
*pll
)
93 u16 frac_max
= (1 << pll
->frac
.width
);
94 u64 val
= (u64
)rate
* pllt
->n
;
96 val
<<= pllt
->od
+ pllt
->od2
+ pllt
->od3
;
98 if (pll
->flags
& CLK_MESON_PLL_ROUND_CLOSEST
)
99 val
= DIV_ROUND_CLOSEST_ULL(val
* frac_max
, parent_rate
);
101 val
= div_u64(val
* frac_max
, parent_rate
);
103 val
-= pllt
->m
* frac_max
;
105 return min((u16
)val
, (u16
)(frac_max
- 1));
108 static const struct pll_rate_table
*
109 meson_clk_get_pll_settings(unsigned long rate
,
110 struct meson_clk_pll_data
*pll
)
112 const struct pll_rate_table
*table
= pll
->table
;
118 /* Find the first table element exceeding rate */
119 while (table
[i
].rate
&& table
[i
].rate
<= rate
)
123 if (MESON_PARM_APPLICABLE(&pll
->frac
) ||
124 !(pll
->flags
& CLK_MESON_PLL_ROUND_CLOSEST
) ||
125 (abs(rate
- table
[i
- 1].rate
) <
126 abs(rate
- table
[i
].rate
)))
130 return (struct pll_rate_table
*)&table
[i
];
133 static long meson_clk_pll_round_rate(struct clk_hw
*hw
, unsigned long rate
,
134 unsigned long *parent_rate
)
136 struct clk_regmap
*clk
= to_clk_regmap(hw
);
137 struct meson_clk_pll_data
*pll
= meson_clk_pll_data(clk
);
138 const struct pll_rate_table
*pllt
=
139 meson_clk_get_pll_settings(rate
, pll
);
143 return meson_clk_pll_recalc_rate(hw
, *parent_rate
);
145 if (!MESON_PARM_APPLICABLE(&pll
->frac
)
146 || rate
== pllt
->rate
)
150 * The rate provided by the setting is not an exact match, let's
151 * try to improve the result using the fractional parameter
153 frac
= __pll_params_with_frac(rate
, *parent_rate
, pllt
, pll
);
155 return __pll_params_to_rate(*parent_rate
, pllt
, frac
, pll
);
158 static int meson_clk_pll_wait_lock(struct clk_hw
*hw
)
160 struct clk_regmap
*clk
= to_clk_regmap(hw
);
161 struct meson_clk_pll_data
*pll
= meson_clk_pll_data(clk
);
162 int delay
= 24000000;
165 /* Is the clock locked now ? */
166 if (meson_parm_read(clk
->map
, &pll
->l
))
175 static void meson_clk_pll_init(struct clk_hw
*hw
)
177 struct clk_regmap
*clk
= to_clk_regmap(hw
);
178 struct meson_clk_pll_data
*pll
= meson_clk_pll_data(clk
);
180 if (pll
->init_count
) {
181 meson_parm_write(clk
->map
, &pll
->rst
, 1);
182 regmap_multi_reg_write(clk
->map
, pll
->init_regs
,
184 meson_parm_write(clk
->map
, &pll
->rst
, 0);
188 static int meson_clk_pll_set_rate(struct clk_hw
*hw
, unsigned long rate
,
189 unsigned long parent_rate
)
191 struct clk_regmap
*clk
= to_clk_regmap(hw
);
192 struct meson_clk_pll_data
*pll
= meson_clk_pll_data(clk
);
193 const struct pll_rate_table
*pllt
;
194 unsigned long old_rate
;
197 if (parent_rate
== 0 || rate
== 0)
202 pllt
= meson_clk_get_pll_settings(rate
, pll
);
206 /* Put the pll in reset to write the params */
207 meson_parm_write(clk
->map
, &pll
->rst
, 1);
209 meson_parm_write(clk
->map
, &pll
->n
, pllt
->n
);
210 meson_parm_write(clk
->map
, &pll
->m
, pllt
->m
);
211 meson_parm_write(clk
->map
, &pll
->od
, pllt
->od
);
213 if (MESON_PARM_APPLICABLE(&pll
->od2
))
214 meson_parm_write(clk
->map
, &pll
->od2
, pllt
->od2
);
216 if (MESON_PARM_APPLICABLE(&pll
->od3
))
217 meson_parm_write(clk
->map
, &pll
->od3
, pllt
->od3
);
219 if (MESON_PARM_APPLICABLE(&pll
->frac
)) {
220 frac
= __pll_params_with_frac(rate
, parent_rate
, pllt
, pll
);
221 meson_parm_write(clk
->map
, &pll
->frac
, frac
);
224 /* make sure the reset is cleared at this point */
225 meson_parm_write(clk
->map
, &pll
->rst
, 0);
227 if (meson_clk_pll_wait_lock(hw
)) {
228 pr_warn("%s: pll did not lock, trying to restore old rate %lu\n",
231 * FIXME: Do we really need/want this HACK ?
232 * It looks unsafe. what happens if the clock gets into a
233 * broken state and we can't lock back on the old_rate ? Looks
234 * like an infinite recursion is possible
236 meson_clk_pll_set_rate(hw
, old_rate
, parent_rate
);
242 const struct clk_ops meson_clk_pll_ops
= {
243 .init
= meson_clk_pll_init
,
244 .recalc_rate
= meson_clk_pll_recalc_rate
,
245 .round_rate
= meson_clk_pll_round_rate
,
246 .set_rate
= meson_clk_pll_set_rate
,
249 const struct clk_ops meson_clk_pll_ro_ops
= {
250 .recalc_rate
= meson_clk_pll_recalc_rate
,