blob: 121522e17608e43f64deaf9b4a2e5b261e3b0e70 [file] [log] [blame]
Channagoud Kadabidd7cb382015-03-23 23:30:25 -07001/*
2 * Copyright (c) 2015, The Linux Foundation. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are met:
6 * * Redistributions of source code must retain the above copyright
7 * notice, this list of conditions and the following disclaimer.
8 * * Redistributions in binary form must reproduce the above copyright
9 * notice, this list of conditions and the following disclaimer in the
10 * documentation and/or other materials provided with the distribution.
11 * * Neither the name of The Linux Foundation nor
12 * the names of its contributors may be used to endorse or promote
13 * products derived from this software without specific prior written
14 * permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19 * NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
20 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
21 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
22 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
23 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
24 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
25 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
26 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29#include <stdint.h>
30#include <string.h>
31#include <debug.h>
32#include <reg.h>
33#include <bits.h>
34#include <limits.h>
35#include <clock.h>
36#include <clock_pll.h>
37#include <clock_alpha_pll.h>
38#include <arch/defines.h>
39#include <platform/timer.h>
40
41#define ALPHA_REG_BITWIDTH 40
42#define ALPHA_BITWIDTH 32
43
44static void set_fsm_mode(uint32_t mode_reg)
45{
46 uint32_t regval = readl_relaxed(mode_reg);
47
48 /* De-assert reset to FSM */
49 regval &= ~PLL_VOTE_FSM_RESET;
50 writel_relaxed(regval, mode_reg);
51
52 /* Program bias count */
53 regval &= ~BM(PLL_BIAS_COUNT_END, PLL_BIAS_COUNT_START);
54 regval |= BVAL(PLL_BIAS_COUNT_END, PLL_BIAS_COUNT_START, PLL_BIAS_COUNT);
55 writel_relaxed(regval, mode_reg);
56
57 /* Program lock count */
58 regval &= ~BM(PLL_LOCK_COUNT_END, PLL_LOCK_COUNT_START);
59 regval |= BVAL(PLL_LOCK_COUNT_END, PLL_LOCK_COUNT_START, 0x0);
60 writel_relaxed(regval, mode_reg);
61
62 /* Enable PLL FSM voting */
63 regval |= PLL_VOTE_FSM_ENA;
64 writel_relaxed(regval, mode_reg);
65}
66
67static inline struct alpha_pll_clk *to_alpha_pll_clk(struct clk *c)
68{
69 return container_of(c, struct alpha_pll_clk, c);
70}
71
72static void init_alpha_pll(struct clk *c)
73{
74 struct alpha_pll_clk *pll = to_alpha_pll_clk(c);
75 struct alpha_pll_masks *masks = pll->masks;
76 uint32_t output_en, userval;
77
78 if (masks->output_mask && pll->enable_config) {
79 output_en = readl_relaxed(OUTPUT_REG(pll));
80 output_en &= ~masks->output_mask;
81 output_en |= pll->enable_config;
82 writel_relaxed(output_en, OUTPUT_REG(pll));
83 }
84
85 if (masks->post_div_mask) {
86 userval = readl_relaxed(USER_CTL_LO_REG(pll));
87 userval &= ~masks->post_div_mask;
88 userval |= pll->post_div_config;
89 writel_relaxed(userval, USER_CTL_LO_REG(pll));
90 }
91
92 if (pll->fsm_en_mask)
93 set_fsm_mode(MODE_REG(pll));
94
95 pll->inited = true;
96}
97
98static bool is_active(struct alpha_pll_clk *pll)
99{
100 uint32_t reg = readl(ACTIVE_REG(pll));
101 uint32_t mask = pll->masks->active_mask;
102 return (reg & mask) == mask;
103}
104
105static bool is_locked(struct alpha_pll_clk *pll)
106{
107 uint32_t reg = readl(LOCK_REG(pll));
108 uint32_t mask = pll->masks->lock_mask;
109 return (reg & mask) == mask;
110}
111
112static bool is_fsm_mode(uint32_t mode_reg)
113{
114 return !!(readl(mode_reg) & PLL_VOTE_FSM_ENA);
115}
116
117static bool update_finish(struct alpha_pll_clk *pll)
118{
119 return is_active(pll);
120}
121
122static int wait_for_update(struct alpha_pll_clk *pll)
123{
124 uint32_t retry = 100;
125
126 while (retry)
127 {
128 if (update_finish(pll))
129 break;
130 udelay(1);
131 retry--;
132 }
133
134 if (!retry)
135 {
136 dprintf(CRITICAL, "%s didn't lock after enabling it!\n", pll->c.dbg_name);
137 return -1;
138 }
139
140 return 0;
141}
142
143static unsigned long compute_rate(struct alpha_pll_clk *pll,
144 uint32_t l_val, uint32_t a_val)
145{
146 uint64_t rate, parent_rate;
147
148 parent_rate = pll->parent->rate;
149 rate = parent_rate * l_val;
150 rate += (parent_rate * a_val) >> ALPHA_BITWIDTH;
151 return rate;
152}
153
154static uint32_t do_div(uint64_t *n, uint32_t divisor)
155{
156 uint32_t remainder = *n % divisor;
157 *n = *n / divisor;
158 return remainder;
159}
160
161static unsigned long calc_values(struct alpha_pll_clk *pll,
162 uint64_t rate, int *l_val, uint64_t *a_val, bool round_up)
163{
164 uint32_t parent_rate;
165 uint64_t remainder;
166 uint64_t quotient;
167 unsigned long freq_hz;
168
169 parent_rate = pll->parent->rate;
170 quotient = rate;
171 remainder = do_div(&quotient, parent_rate);
172
173 *l_val = quotient;
174
175 if (!remainder) {
176 *a_val = 0;
177 return rate;
178 }
179
180 /* Upper ALPHA_BITWIDTH bits of Alpha */
181 quotient = remainder << ALPHA_BITWIDTH;
182 remainder = do_div(&quotient, parent_rate);
183
184 if (remainder && round_up)
185 quotient++;
186
187 *a_val = quotient;
188 freq_hz = compute_rate(pll, *l_val, *a_val);
189 return freq_hz;
190}
191
Channagoud Kadabif016e552015-05-29 16:59:25 -0700192static unsigned long round_rate_down(struct alpha_pll_clk *pll,
193 unsigned long rate, int *l_val, uint64_t *a_val)
194{
195 return calc_values(pll, rate, l_val, a_val, false);
196}
197
Channagoud Kadabidd7cb382015-03-23 23:30:25 -0700198static unsigned long round_rate_up(struct alpha_pll_clk *pll,
199 unsigned long rate, int *l_val, uint64_t *a_val)
200{
201 return calc_values(pll, rate, l_val, a_val, true);
202}
203
204static uint32_t find_vco(struct alpha_pll_clk *pll, unsigned long rate)
205{
206 unsigned long i;
207 struct alpha_pll_vco_tbl *v = pll->vco_tbl;
208
209 for (i = 0; i < pll->vco_num; i++) {
210 if (rate >= v[i].min_freq && rate <= v[i].max_freq)
211 return v[i].vco_val;
212 }
213 return -1;
214}
215
216static int alpha_pll_set_rate(struct clk *c, unsigned long rate)
217{
218 struct alpha_pll_clk *pll = to_alpha_pll_clk(c);
219 struct alpha_pll_masks *masks = pll->masks;
220 unsigned long freq_hz;
221 int regval, l_val;
222 uint64_t a_val;
223 uint32_t vco_val;
224
225 freq_hz = round_rate_up(pll, rate, &l_val, &a_val);
226 if (freq_hz != rate) {
227 dprintf(CRITICAL, "alpha_pll: Call clk_set_rate with rounded rates!\n");
228 return -1;
229 }
230
231 vco_val = find_vco(pll, freq_hz);
232 if (!vco_val)
233 {
234 dprintf(CRITICAL, "alpha pll: VCO not valid\n");
235 return -1;
236 }
237
238 /*
239 * Ensure PLL is off before changing rate. For optimization reasons,
240 * assume no downstream clock is actively using it. No support
241 * for dynamic update at the moment.
242 */
243 if (c->count)
244 alpha_pll_disable(c);
245
246 a_val = a_val << (ALPHA_REG_BITWIDTH - ALPHA_BITWIDTH);
247
248 writel_relaxed(l_val, L_REG(pll));
249 writel_relaxed(a_val, A_REG(pll));
250 a_val = (a_val >> ALPHA_BITWIDTH);
251 writel_relaxed(a_val, A_REG_U(pll));
252
253 if (masks->vco_mask) {
254 regval = readl_relaxed(VCO_REG(pll));
255 regval &= ~(masks->vco_mask << masks->vco_shift);
256 regval |= vco_val << masks->vco_shift;
257 writel_relaxed(regval, VCO_REG(pll));
258 }
259
260 regval = readl_relaxed(ALPHA_EN_REG(pll));
261 regval |= masks->alpha_en_mask;
262 writel_relaxed(regval, ALPHA_EN_REG(pll));
263
264 if (c->count)
265 alpha_pll_enable(c);
266
267 return 0;
268}
269
Channagoud Kadabif016e552015-05-29 16:59:25 -0700270static void update_vco_tbl(struct alpha_pll_clk *pll)
271{
272 uint32_t i;
273 int l_val;
274 uint64_t a_val;
275 uint64_t rate;
276
277 for (i = 0 ; i < pll->vco_num; i++)
278 {
279 rate = round_rate_up(pll, pll->vco_tbl[i].min_freq, &l_val, &a_val);
280 pll->vco_tbl[i].min_freq = rate;
281
282 rate = round_rate_down(pll, pll->vco_tbl[i].max_freq, &l_val, &a_val);
283 pll->vco_tbl[i].max_freq = rate;
284 }
285}
286
Channagoud Kadabidd7cb382015-03-23 23:30:25 -0700287int alpha_pll_enable(struct clk *c)
288{
289 struct alpha_pll_clk *pll = to_alpha_pll_clk(c);
290 uint32_t ena;
291
Channagoud Kadabif016e552015-05-29 16:59:25 -0700292 update_vco_tbl(pll);
293
Channagoud Kadabidd7cb382015-03-23 23:30:25 -0700294 /* if PLL is not initialized already and is not in FSM state */
295 if (!pll->inited && !is_locked(pll))
296 {
297 if (c->rate && alpha_pll_set_rate(c, c->rate))
298 dprintf(INFO, "Warning: Failed to set rate for alpha pll\n");
299 init_alpha_pll(c);
300 }
301 else if (!is_fsm_mode(MODE_REG(pll)))
302 {
303 dprintf(INFO, "FSM mode is not set for: %s\n", c->dbg_name);
304 }
305
306 ena = readl_relaxed(VOTE_REG(pll));
307 ena |= pll->fsm_en_mask;
308 writel_relaxed(ena, VOTE_REG(pll));
309 dmb();
310
311 return wait_for_update(pll);
312}
313
314void alpha_pll_disable(struct clk *c)
315{
316 struct alpha_pll_clk *pll = to_alpha_pll_clk(c);
317 uint32_t ena;
318
319 ena = readl_relaxed(VOTE_REG(pll));
320 ena &= ~pll->fsm_en_mask;
321 writel_relaxed(ena, VOTE_REG(pll));
322 dmb();
323}