aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTony Lindgren <tony@atomide.com>2015-03-22 18:35:26 -0400
committerTero Kristo <t-kristo@ti.com>2015-03-24 14:26:14 -0400
commit9089848d9afa34a796988b5b666c2c4e611ccb61 (patch)
tree74be3f0142c1b8a1f7109747ee2519f4eed7a2a6
parentcafeb002cf2cd8b0f8796b59130f9c1b91da4fcf (diff)
clk: ti: Implement FAPLL set_rate for the PLL
Since we have a fractional divider for the synthesizer, just implement a simple multiply logic for the PLL. It seems the PLL divider needs to have also the multiplier set for the PLL to lock. At least I have not yet figured out if divided rates are doable. So let's just ignore the PLL divider for now as the synthesizer has both integer and fractional dividers so we don't even need to use the PLL divider for the rates we know work with PLL locking. Cc: Brian Hutchinson <b.hutchman@gmail.com> Cc: Matthijs van Duin <matthijsvanduin@gmail.com> Cc: Tero Kristo <t-kristo@ti.com> Signed-off-by: Tony Lindgren <tony@atomide.com> Signed-off-by: Tero Kristo <t-kristo@ti.com>
-rw-r--r--drivers/clk/ti/fapll.c125
1 files changed, 125 insertions, 0 deletions
diff --git a/drivers/clk/ti/fapll.c b/drivers/clk/ti/fapll.c
index fc06abe5eaaf..e8291c3a0e76 100644
--- a/drivers/clk/ti/fapll.c
+++ b/drivers/clk/ti/fapll.c
@@ -18,11 +18,20 @@
18#include <linux/clk/ti.h> 18#include <linux/clk/ti.h>
19 19
20/* FAPLL Control Register PLL_CTRL */ 20/* FAPLL Control Register PLL_CTRL */
21#define FAPLL_MAIN_MULT_N_SHIFT 16
22#define FAPLL_MAIN_DIV_P_SHIFT 8
21#define FAPLL_MAIN_LOCK BIT(7) 23#define FAPLL_MAIN_LOCK BIT(7)
22#define FAPLL_MAIN_PLLEN BIT(3) 24#define FAPLL_MAIN_PLLEN BIT(3)
23#define FAPLL_MAIN_BP BIT(2) 25#define FAPLL_MAIN_BP BIT(2)
24#define FAPLL_MAIN_LOC_CTL BIT(0) 26#define FAPLL_MAIN_LOC_CTL BIT(0)
25 27
28#define FAPLL_MAIN_MAX_MULT_N 0xffff
29#define FAPLL_MAIN_MAX_DIV_P 0xff
30#define FAPLL_MAIN_CLEAR_MASK \
31 ((FAPLL_MAIN_MAX_MULT_N << FAPLL_MAIN_MULT_N_SHIFT) | \
32 (FAPLL_MAIN_DIV_P_SHIFT << FAPLL_MAIN_DIV_P_SHIFT) | \
33 FAPLL_MAIN_LOC_CTL)
34
26/* FAPLL powerdown register PWD */ 35/* FAPLL powerdown register PWD */
27#define FAPLL_PWD_OFFSET 4 36#define FAPLL_PWD_OFFSET 4
28 37
@@ -82,6 +91,48 @@ static bool ti_fapll_clock_is_bypass(struct fapll_data *fd)
82 return !!(v & FAPLL_MAIN_BP); 91 return !!(v & FAPLL_MAIN_BP);
83} 92}
84 93
94static void ti_fapll_set_bypass(struct fapll_data *fd)
95{
96 u32 v = readl_relaxed(fd->base);
97
98 if (fd->bypass_bit_inverted)
99 v &= ~FAPLL_MAIN_BP;
100 else
101 v |= FAPLL_MAIN_BP;
102 writel_relaxed(v, fd->base);
103}
104
105static void ti_fapll_clear_bypass(struct fapll_data *fd)
106{
107 u32 v = readl_relaxed(fd->base);
108
109 if (fd->bypass_bit_inverted)
110 v |= FAPLL_MAIN_BP;
111 else
112 v &= ~FAPLL_MAIN_BP;
113 writel_relaxed(v, fd->base);
114}
115
116static int ti_fapll_wait_lock(struct fapll_data *fd)
117{
118 int retries = FAPLL_MAX_RETRIES;
119 u32 v;
120
121 while ((v = readl_relaxed(fd->base))) {
122 if (v & FAPLL_MAIN_LOCK)
123 return 0;
124
125 if (retries-- <= 0)
126 break;
127
128 udelay(1);
129 }
130
131 pr_err("%s failed to lock\n", fd->name);
132
133 return -ETIMEDOUT;
134}
135
85static int ti_fapll_enable(struct clk_hw *hw) 136static int ti_fapll_enable(struct clk_hw *hw)
86{ 137{
87 struct fapll_data *fd = to_fapll(hw); 138 struct fapll_data *fd = to_fapll(hw);
@@ -89,6 +140,7 @@ static int ti_fapll_enable(struct clk_hw *hw)
89 140
90 v |= (1 << FAPLL_MAIN_PLLEN); 141 v |= (1 << FAPLL_MAIN_PLLEN);
91 writel_relaxed(v, fd->base); 142 writel_relaxed(v, fd->base);
143 ti_fapll_wait_lock(fd);
92 144
93 return 0; 145 return 0;
94} 146}
@@ -144,12 +196,85 @@ static u8 ti_fapll_get_parent(struct clk_hw *hw)
144 return 0; 196 return 0;
145} 197}
146 198
199static int ti_fapll_set_div_mult(unsigned long rate,
200 unsigned long parent_rate,
201 u32 *pre_div_p, u32 *mult_n)
202{
203 /*
204 * So far no luck getting decent clock with PLL divider,
205 * PLL does not seem to lock and the signal does not look
206 * right. It seems the divider can only be used together
207 * with the multiplier?
208 */
209 if (rate < parent_rate) {
210 pr_warn("FAPLL main divider rates unsupported\n");
211 return -EINVAL;
212 }
213
214 *mult_n = rate / parent_rate;
215 if (*mult_n > FAPLL_MAIN_MAX_MULT_N)
216 return -EINVAL;
217 *pre_div_p = 1;
218
219 return 0;
220}
221
222static long ti_fapll_round_rate(struct clk_hw *hw, unsigned long rate,
223 unsigned long *parent_rate)
224{
225 u32 pre_div_p, mult_n;
226 int error;
227
228 if (!rate)
229 return -EINVAL;
230
231 error = ti_fapll_set_div_mult(rate, *parent_rate,
232 &pre_div_p, &mult_n);
233 if (error)
234 return error;
235
236 rate = *parent_rate / pre_div_p;
237 rate *= mult_n;
238
239 return rate;
240}
241
242static int ti_fapll_set_rate(struct clk_hw *hw, unsigned long rate,
243 unsigned long parent_rate)
244{
245 struct fapll_data *fd = to_fapll(hw);
246 u32 pre_div_p, mult_n, v;
247 int error;
248
249 if (!rate)
250 return -EINVAL;
251
252 error = ti_fapll_set_div_mult(rate, parent_rate,
253 &pre_div_p, &mult_n);
254 if (error)
255 return error;
256
257 ti_fapll_set_bypass(fd);
258 v = readl_relaxed(fd->base);
259 v &= ~FAPLL_MAIN_CLEAR_MASK;
260 v |= pre_div_p << FAPLL_MAIN_DIV_P_SHIFT;
261 v |= mult_n << FAPLL_MAIN_MULT_N_SHIFT;
262 writel_relaxed(v, fd->base);
263 if (ti_fapll_is_enabled(hw))
264 ti_fapll_wait_lock(fd);
265 ti_fapll_clear_bypass(fd);
266
267 return 0;
268}
269
147static struct clk_ops ti_fapll_ops = { 270static struct clk_ops ti_fapll_ops = {
148 .enable = ti_fapll_enable, 271 .enable = ti_fapll_enable,
149 .disable = ti_fapll_disable, 272 .disable = ti_fapll_disable,
150 .is_enabled = ti_fapll_is_enabled, 273 .is_enabled = ti_fapll_is_enabled,
151 .recalc_rate = ti_fapll_recalc_rate, 274 .recalc_rate = ti_fapll_recalc_rate,
152 .get_parent = ti_fapll_get_parent, 275 .get_parent = ti_fapll_get_parent,
276 .round_rate = ti_fapll_round_rate,
277 .set_rate = ti_fapll_set_rate,
153}; 278};
154 279
155static int ti_fapll_synth_enable(struct clk_hw *hw) 280static int ti_fapll_synth_enable(struct clk_hw *hw)