diff options
Diffstat (limited to 'drivers/clk/meson/clk-pll.c')
-rw-r--r-- | drivers/clk/meson/clk-pll.c | 306 |
1 files changed, 157 insertions, 149 deletions
diff --git a/drivers/clk/meson/clk-pll.c b/drivers/clk/meson/clk-pll.c index 01341553f50b..65a7bd903551 100644 --- a/drivers/clk/meson/clk-pll.c +++ b/drivers/clk/meson/clk-pll.c | |||
@@ -2,6 +2,9 @@ | |||
2 | * Copyright (c) 2015 Endless Mobile, Inc. | 2 | * Copyright (c) 2015 Endless Mobile, Inc. |
3 | * Author: Carlo Caione <carlo@endlessm.com> | 3 | * Author: Carlo Caione <carlo@endlessm.com> |
4 | * | 4 | * |
5 | * Copyright (c) 2018 Baylibre, SAS. | ||
6 | * Author: Jerome Brunet <jbrunet@baylibre.com> | ||
7 | * | ||
5 | * This program is free software; you can redistribute it and/or modify it | 8 | * This program is free software; you can redistribute it and/or modify it |
6 | * under the terms and conditions of the GNU General Public License, | 9 | * under the terms and conditions of the GNU General Public License, |
7 | * version 2, as published by the Free Software Foundation. | 10 | * version 2, as published by the Free Software Foundation. |
@@ -27,13 +30,14 @@ | |||
27 | * | | | 30 | * | | |
28 | * FREF VCO | 31 | * FREF VCO |
29 | * | 32 | * |
30 | * out = (in * M / N) >> OD | 33 | * out = in * (m + frac / frac_max) / (n << sum(ods)) |
31 | */ | 34 | */ |
32 | 35 | ||
33 | #include <linux/clk-provider.h> | 36 | #include <linux/clk-provider.h> |
34 | #include <linux/delay.h> | 37 | #include <linux/delay.h> |
35 | #include <linux/err.h> | 38 | #include <linux/err.h> |
36 | #include <linux/io.h> | 39 | #include <linux/io.h> |
40 | #include <linux/math64.h> | ||
37 | #include <linux/module.h> | 41 | #include <linux/module.h> |
38 | #include <linux/of_address.h> | 42 | #include <linux/of_address.h> |
39 | #include <linux/slab.h> | 43 | #include <linux/slab.h> |
@@ -41,209 +45,213 @@ | |||
41 | 45 | ||
42 | #include "clkc.h" | 46 | #include "clkc.h" |
43 | 47 | ||
44 | #define MESON_PLL_RESET BIT(29) | 48 | static inline struct meson_clk_pll_data * |
45 | #define MESON_PLL_LOCK BIT(31) | 49 | meson_clk_pll_data(struct clk_regmap *clk) |
46 | |||
47 | #define to_meson_clk_pll(_hw) container_of(_hw, struct meson_clk_pll, hw) | ||
48 | |||
49 | static unsigned long meson_clk_pll_recalc_rate(struct clk_hw *hw, | ||
50 | unsigned long parent_rate) | ||
51 | { | 50 | { |
52 | struct meson_clk_pll *pll = to_meson_clk_pll(hw); | 51 | return (struct meson_clk_pll_data *)clk->data; |
53 | struct parm *p; | ||
54 | unsigned long parent_rate_mhz = parent_rate / 1000000; | ||
55 | unsigned long rate_mhz; | ||
56 | u16 n, m, frac = 0, od, od2 = 0; | ||
57 | u32 reg; | ||
58 | |||
59 | p = &pll->n; | ||
60 | reg = readl(pll->base + p->reg_off); | ||
61 | n = PARM_GET(p->width, p->shift, reg); | ||
62 | |||
63 | p = &pll->m; | ||
64 | reg = readl(pll->base + p->reg_off); | ||
65 | m = PARM_GET(p->width, p->shift, reg); | ||
66 | |||
67 | p = &pll->od; | ||
68 | reg = readl(pll->base + p->reg_off); | ||
69 | od = PARM_GET(p->width, p->shift, reg); | ||
70 | |||
71 | p = &pll->od2; | ||
72 | if (p->width) { | ||
73 | reg = readl(pll->base + p->reg_off); | ||
74 | od2 = PARM_GET(p->width, p->shift, reg); | ||
75 | } | ||
76 | |||
77 | p = &pll->frac; | ||
78 | if (p->width) { | ||
79 | reg = readl(pll->base + p->reg_off); | ||
80 | frac = PARM_GET(p->width, p->shift, reg); | ||
81 | rate_mhz = (parent_rate_mhz * m + \ | ||
82 | (parent_rate_mhz * frac >> 12)) * 2 / n; | ||
83 | rate_mhz = rate_mhz >> od >> od2; | ||
84 | } else | ||
85 | rate_mhz = (parent_rate_mhz * m / n) >> od >> od2; | ||
86 | |||
87 | return rate_mhz * 1000000; | ||
88 | } | 52 | } |
89 | 53 | ||
90 | static long meson_clk_pll_round_rate(struct clk_hw *hw, unsigned long rate, | 54 | static unsigned long __pll_params_to_rate(unsigned long parent_rate, |
91 | unsigned long *parent_rate) | 55 | const struct pll_rate_table *pllt, |
56 | u16 frac, | ||
57 | struct meson_clk_pll_data *pll) | ||
92 | { | 58 | { |
93 | struct meson_clk_pll *pll = to_meson_clk_pll(hw); | 59 | u64 rate = (u64)parent_rate * pllt->m; |
94 | const struct pll_rate_table *rate_table = pll->rate_table; | 60 | unsigned int od = pllt->od + pllt->od2 + pllt->od3; |
95 | int i; | ||
96 | 61 | ||
97 | for (i = 0; i < pll->rate_count; i++) { | 62 | if (frac && MESON_PARM_APPLICABLE(&pll->frac)) { |
98 | if (rate <= rate_table[i].rate) | 63 | u64 frac_rate = (u64)parent_rate * frac; |
99 | return rate_table[i].rate; | 64 | |
65 | rate += DIV_ROUND_UP_ULL(frac_rate, | ||
66 | (1 << pll->frac.width)); | ||
100 | } | 67 | } |
101 | 68 | ||
102 | /* else return the smallest value */ | 69 | return DIV_ROUND_UP_ULL(rate, pllt->n << od); |
103 | return rate_table[0].rate; | ||
104 | } | 70 | } |
105 | 71 | ||
106 | static const struct pll_rate_table *meson_clk_get_pll_settings(struct meson_clk_pll *pll, | 72 | static unsigned long meson_clk_pll_recalc_rate(struct clk_hw *hw, |
107 | unsigned long rate) | 73 | unsigned long parent_rate) |
108 | { | 74 | { |
109 | const struct pll_rate_table *rate_table = pll->rate_table; | 75 | struct clk_regmap *clk = to_clk_regmap(hw); |
110 | int i; | 76 | struct meson_clk_pll_data *pll = meson_clk_pll_data(clk); |
77 | struct pll_rate_table pllt; | ||
78 | u16 frac; | ||
111 | 79 | ||
112 | for (i = 0; i < pll->rate_count; i++) { | 80 | pllt.n = meson_parm_read(clk->map, &pll->n); |
113 | if (rate == rate_table[i].rate) | 81 | pllt.m = meson_parm_read(clk->map, &pll->m); |
114 | return &rate_table[i]; | 82 | pllt.od = meson_parm_read(clk->map, &pll->od); |
115 | } | 83 | |
116 | return NULL; | 84 | pllt.od2 = MESON_PARM_APPLICABLE(&pll->od2) ? |
85 | meson_parm_read(clk->map, &pll->od2) : | ||
86 | 0; | ||
87 | |||
88 | pllt.od3 = MESON_PARM_APPLICABLE(&pll->od3) ? | ||
89 | meson_parm_read(clk->map, &pll->od3) : | ||
90 | 0; | ||
91 | |||
92 | frac = MESON_PARM_APPLICABLE(&pll->frac) ? | ||
93 | meson_parm_read(clk->map, &pll->frac) : | ||
94 | 0; | ||
95 | |||
96 | return __pll_params_to_rate(parent_rate, &pllt, frac, pll); | ||
117 | } | 97 | } |
118 | 98 | ||
119 | /* Specific wait loop for GXL/GXM GP0 PLL */ | 99 | static u16 __pll_params_with_frac(unsigned long rate, |
120 | static int meson_clk_pll_wait_lock_reset(struct meson_clk_pll *pll, | 100 | unsigned long parent_rate, |
121 | struct parm *p_n) | 101 | const struct pll_rate_table *pllt, |
102 | struct meson_clk_pll_data *pll) | ||
122 | { | 103 | { |
123 | int delay = 100; | 104 | u16 frac_max = (1 << pll->frac.width); |
124 | u32 reg; | 105 | u64 val = (u64)rate * pllt->n; |
125 | 106 | ||
126 | while (delay > 0) { | 107 | val <<= pllt->od + pllt->od2 + pllt->od3; |
127 | reg = readl(pll->base + p_n->reg_off); | ||
128 | writel(reg | MESON_PLL_RESET, pll->base + p_n->reg_off); | ||
129 | udelay(10); | ||
130 | writel(reg & ~MESON_PLL_RESET, pll->base + p_n->reg_off); | ||
131 | 108 | ||
132 | /* This delay comes from AMLogic tree clk-gp0-gxl driver */ | 109 | if (pll->flags & CLK_MESON_PLL_ROUND_CLOSEST) |
133 | mdelay(1); | 110 | val = DIV_ROUND_CLOSEST_ULL(val * frac_max, parent_rate); |
111 | else | ||
112 | val = div_u64(val * frac_max, parent_rate); | ||
134 | 113 | ||
135 | reg = readl(pll->base + p_n->reg_off); | 114 | val -= pllt->m * frac_max; |
136 | if (reg & MESON_PLL_LOCK) | 115 | |
137 | return 0; | 116 | return min((u16)val, (u16)(frac_max - 1)); |
138 | delay--; | 117 | } |
118 | |||
119 | static const struct pll_rate_table * | ||
120 | meson_clk_get_pll_settings(unsigned long rate, | ||
121 | struct meson_clk_pll_data *pll) | ||
122 | { | ||
123 | const struct pll_rate_table *table = pll->table; | ||
124 | unsigned int i = 0; | ||
125 | |||
126 | if (!table) | ||
127 | return NULL; | ||
128 | |||
129 | /* Find the first table element exceeding rate */ | ||
130 | while (table[i].rate && table[i].rate <= rate) | ||
131 | i++; | ||
132 | |||
133 | if (i != 0) { | ||
134 | if (MESON_PARM_APPLICABLE(&pll->frac) || | ||
135 | !(pll->flags & CLK_MESON_PLL_ROUND_CLOSEST) || | ||
136 | (abs(rate - table[i - 1].rate) < | ||
137 | abs(rate - table[i].rate))) | ||
138 | i--; | ||
139 | } | 139 | } |
140 | return -ETIMEDOUT; | 140 | |
141 | return (struct pll_rate_table *)&table[i]; | ||
141 | } | 142 | } |
142 | 143 | ||
143 | static int meson_clk_pll_wait_lock(struct meson_clk_pll *pll, | 144 | static long meson_clk_pll_round_rate(struct clk_hw *hw, unsigned long rate, |
144 | struct parm *p_n) | 145 | unsigned long *parent_rate) |
145 | { | 146 | { |
146 | int delay = 24000000; | 147 | struct clk_regmap *clk = to_clk_regmap(hw); |
147 | u32 reg; | 148 | struct meson_clk_pll_data *pll = meson_clk_pll_data(clk); |
149 | const struct pll_rate_table *pllt = | ||
150 | meson_clk_get_pll_settings(rate, pll); | ||
151 | u16 frac; | ||
152 | |||
153 | if (!pllt) | ||
154 | return meson_clk_pll_recalc_rate(hw, *parent_rate); | ||
155 | |||
156 | if (!MESON_PARM_APPLICABLE(&pll->frac) | ||
157 | || rate == pllt->rate) | ||
158 | return pllt->rate; | ||
159 | |||
160 | /* | ||
161 | * The rate provided by the setting is not an exact match, let's | ||
162 | * try to improve the result using the fractional parameter | ||
163 | */ | ||
164 | frac = __pll_params_with_frac(rate, *parent_rate, pllt, pll); | ||
165 | |||
166 | return __pll_params_to_rate(*parent_rate, pllt, frac, pll); | ||
167 | } | ||
148 | 168 | ||
149 | while (delay > 0) { | 169 | static int meson_clk_pll_wait_lock(struct clk_hw *hw) |
150 | reg = readl(pll->base + p_n->reg_off); | 170 | { |
171 | struct clk_regmap *clk = to_clk_regmap(hw); | ||
172 | struct meson_clk_pll_data *pll = meson_clk_pll_data(clk); | ||
173 | int delay = 24000000; | ||
151 | 174 | ||
152 | if (reg & MESON_PLL_LOCK) | 175 | do { |
176 | /* Is the clock locked now ? */ | ||
177 | if (meson_parm_read(clk->map, &pll->l)) | ||
153 | return 0; | 178 | return 0; |
179 | |||
154 | delay--; | 180 | delay--; |
155 | } | 181 | } while (delay > 0); |
182 | |||
156 | return -ETIMEDOUT; | 183 | return -ETIMEDOUT; |
157 | } | 184 | } |
158 | 185 | ||
159 | static void meson_clk_pll_init_params(struct meson_clk_pll *pll) | 186 | static void meson_clk_pll_init(struct clk_hw *hw) |
160 | { | 187 | { |
161 | int i; | 188 | struct clk_regmap *clk = to_clk_regmap(hw); |
162 | 189 | struct meson_clk_pll_data *pll = meson_clk_pll_data(clk); | |
163 | for (i = 0 ; i < pll->params.params_count ; ++i) | 190 | |
164 | writel(pll->params.params_table[i].value, | 191 | if (pll->init_count) { |
165 | pll->base + pll->params.params_table[i].reg_off); | 192 | meson_parm_write(clk->map, &pll->rst, 1); |
193 | regmap_multi_reg_write(clk->map, pll->init_regs, | ||
194 | pll->init_count); | ||
195 | meson_parm_write(clk->map, &pll->rst, 0); | ||
196 | } | ||
166 | } | 197 | } |
167 | 198 | ||
168 | static int meson_clk_pll_set_rate(struct clk_hw *hw, unsigned long rate, | 199 | static int meson_clk_pll_set_rate(struct clk_hw *hw, unsigned long rate, |
169 | unsigned long parent_rate) | 200 | unsigned long parent_rate) |
170 | { | 201 | { |
171 | struct meson_clk_pll *pll = to_meson_clk_pll(hw); | 202 | struct clk_regmap *clk = to_clk_regmap(hw); |
172 | struct parm *p; | 203 | struct meson_clk_pll_data *pll = meson_clk_pll_data(clk); |
173 | const struct pll_rate_table *rate_set; | 204 | const struct pll_rate_table *pllt; |
174 | unsigned long old_rate; | 205 | unsigned long old_rate; |
175 | int ret = 0; | 206 | u16 frac = 0; |
176 | u32 reg; | ||
177 | 207 | ||
178 | if (parent_rate == 0 || rate == 0) | 208 | if (parent_rate == 0 || rate == 0) |
179 | return -EINVAL; | 209 | return -EINVAL; |
180 | 210 | ||
181 | old_rate = rate; | 211 | old_rate = rate; |
182 | 212 | ||
183 | rate_set = meson_clk_get_pll_settings(pll, rate); | 213 | pllt = meson_clk_get_pll_settings(rate, pll); |
184 | if (!rate_set) | 214 | if (!pllt) |
185 | return -EINVAL; | 215 | return -EINVAL; |
186 | 216 | ||
187 | /* Initialize the PLL in a clean state if specified */ | 217 | /* Put the pll in reset to write the params */ |
188 | if (pll->params.params_count) | 218 | meson_parm_write(clk->map, &pll->rst, 1); |
189 | meson_clk_pll_init_params(pll); | ||
190 | |||
191 | /* PLL reset */ | ||
192 | p = &pll->n; | ||
193 | reg = readl(pll->base + p->reg_off); | ||
194 | /* If no_init_reset is provided, avoid resetting at this point */ | ||
195 | if (!pll->params.no_init_reset) | ||
196 | writel(reg | MESON_PLL_RESET, pll->base + p->reg_off); | ||
197 | |||
198 | reg = PARM_SET(p->width, p->shift, reg, rate_set->n); | ||
199 | writel(reg, pll->base + p->reg_off); | ||
200 | |||
201 | p = &pll->m; | ||
202 | reg = readl(pll->base + p->reg_off); | ||
203 | reg = PARM_SET(p->width, p->shift, reg, rate_set->m); | ||
204 | writel(reg, pll->base + p->reg_off); | ||
205 | |||
206 | p = &pll->od; | ||
207 | reg = readl(pll->base + p->reg_off); | ||
208 | reg = PARM_SET(p->width, p->shift, reg, rate_set->od); | ||
209 | writel(reg, pll->base + p->reg_off); | ||
210 | |||
211 | p = &pll->od2; | ||
212 | if (p->width) { | ||
213 | reg = readl(pll->base + p->reg_off); | ||
214 | reg = PARM_SET(p->width, p->shift, reg, rate_set->od2); | ||
215 | writel(reg, pll->base + p->reg_off); | ||
216 | } | ||
217 | 219 | ||
218 | p = &pll->frac; | 220 | meson_parm_write(clk->map, &pll->n, pllt->n); |
219 | if (p->width) { | 221 | meson_parm_write(clk->map, &pll->m, pllt->m); |
220 | reg = readl(pll->base + p->reg_off); | 222 | meson_parm_write(clk->map, &pll->od, pllt->od); |
221 | reg = PARM_SET(p->width, p->shift, reg, rate_set->frac); | 223 | |
222 | writel(reg, pll->base + p->reg_off); | 224 | if (MESON_PARM_APPLICABLE(&pll->od2)) |
223 | } | 225 | meson_parm_write(clk->map, &pll->od2, pllt->od2); |
226 | |||
227 | if (MESON_PARM_APPLICABLE(&pll->od3)) | ||
228 | meson_parm_write(clk->map, &pll->od3, pllt->od3); | ||
224 | 229 | ||
225 | p = &pll->n; | 230 | if (MESON_PARM_APPLICABLE(&pll->frac)) { |
226 | /* If clear_reset_for_lock is provided, remove the reset bit here */ | 231 | frac = __pll_params_with_frac(rate, parent_rate, pllt, pll); |
227 | if (pll->params.clear_reset_for_lock) { | 232 | meson_parm_write(clk->map, &pll->frac, frac); |
228 | reg = readl(pll->base + p->reg_off); | ||
229 | writel(reg & ~MESON_PLL_RESET, pll->base + p->reg_off); | ||
230 | } | 233 | } |
231 | 234 | ||
232 | /* If reset_lock_loop, use a special loop including resetting */ | 235 | /* make sure the reset is cleared at this point */ |
233 | if (pll->params.reset_lock_loop) | 236 | meson_parm_write(clk->map, &pll->rst, 0); |
234 | ret = meson_clk_pll_wait_lock_reset(pll, p); | 237 | |
235 | else | 238 | if (meson_clk_pll_wait_lock(hw)) { |
236 | ret = meson_clk_pll_wait_lock(pll, p); | ||
237 | if (ret) { | ||
238 | pr_warn("%s: pll did not lock, trying to restore old rate %lu\n", | 239 | pr_warn("%s: pll did not lock, trying to restore old rate %lu\n", |
239 | __func__, old_rate); | 240 | __func__, old_rate); |
241 | /* | ||
242 | * FIXME: Do we really need/want this HACK ? | ||
243 | * It looks unsafe. what happens if the clock gets into a | ||
244 | * broken state and we can't lock back on the old_rate ? Looks | ||
245 | * like an infinite recursion is possible | ||
246 | */ | ||
240 | meson_clk_pll_set_rate(hw, old_rate, parent_rate); | 247 | meson_clk_pll_set_rate(hw, old_rate, parent_rate); |
241 | } | 248 | } |
242 | 249 | ||
243 | return ret; | 250 | return 0; |
244 | } | 251 | } |
245 | 252 | ||
246 | const struct clk_ops meson_clk_pll_ops = { | 253 | const struct clk_ops meson_clk_pll_ops = { |
254 | .init = meson_clk_pll_init, | ||
247 | .recalc_rate = meson_clk_pll_recalc_rate, | 255 | .recalc_rate = meson_clk_pll_recalc_rate, |
248 | .round_rate = meson_clk_pll_round_rate, | 256 | .round_rate = meson_clk_pll_round_rate, |
249 | .set_rate = meson_clk_pll_set_rate, | 257 | .set_rate = meson_clk_pll_set_rate, |