aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/clk/meson/clk-pll.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/clk/meson/clk-pll.c')
-rw-r--r--drivers/clk/meson/clk-pll.c306
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) 48static inline struct meson_clk_pll_data *
45#define MESON_PLL_LOCK BIT(31) 49meson_clk_pll_data(struct clk_regmap *clk)
46
47#define to_meson_clk_pll(_hw) container_of(_hw, struct meson_clk_pll, hw)
48
49static 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
90static long meson_clk_pll_round_rate(struct clk_hw *hw, unsigned long rate, 54static 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
106static const struct pll_rate_table *meson_clk_get_pll_settings(struct meson_clk_pll *pll, 72static 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 */ 99static u16 __pll_params_with_frac(unsigned long rate,
120static 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
119static const struct pll_rate_table *
120meson_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
143static int meson_clk_pll_wait_lock(struct meson_clk_pll *pll, 144static 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) { 169static 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
159static void meson_clk_pll_init_params(struct meson_clk_pll *pll) 186static 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
168static int meson_clk_pll_set_rate(struct clk_hw *hw, unsigned long rate, 199static 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
246const struct clk_ops meson_clk_pll_ops = { 253const 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,