]>
Commit | Line | Data |
---|---|---|
0c7665c3 MF |
1 | /* |
2 | * TI CDCE706 programmable 3-PLL clock synthesizer driver | |
3 | * | |
4 | * Copyright (c) 2014 Cadence Design Systems Inc. | |
5 | * | |
6 | * Reference: http://www.ti.com/lit/ds/symlink/cdce706.pdf | |
7 | * | |
8 | * This program is free software; you can redistribute it and/or modify | |
9 | * it under the terms of the GNU General Public License version 2 as | |
10 | * published by the Free Software Foundation. | |
11 | */ | |
12 | ||
fc1699c8 | 13 | #include <linux/clk.h> |
0c7665c3 MF |
14 | #include <linux/clk-provider.h> |
15 | #include <linux/delay.h> | |
16 | #include <linux/i2c.h> | |
17 | #include <linux/interrupt.h> | |
18 | #include <linux/mod_devicetable.h> | |
19 | #include <linux/module.h> | |
20 | #include <linux/of.h> | |
21 | #include <linux/rational.h> | |
22 | #include <linux/regmap.h> | |
23 | #include <linux/slab.h> | |
24 | ||
25 | #define CDCE706_CLKIN_CLOCK 10 | |
26 | #define CDCE706_CLKIN_SOURCE 11 | |
27 | #define CDCE706_PLL_M_LOW(pll) (1 + 3 * (pll)) | |
28 | #define CDCE706_PLL_N_LOW(pll) (2 + 3 * (pll)) | |
29 | #define CDCE706_PLL_HI(pll) (3 + 3 * (pll)) | |
30 | #define CDCE706_PLL_MUX 3 | |
31 | #define CDCE706_PLL_FVCO 6 | |
32 | #define CDCE706_DIVIDER(div) (13 + (div)) | |
33 | #define CDCE706_CLKOUT(out) (19 + (out)) | |
34 | ||
35 | #define CDCE706_CLKIN_CLOCK_MASK 0x10 | |
36 | #define CDCE706_CLKIN_SOURCE_SHIFT 6 | |
37 | #define CDCE706_CLKIN_SOURCE_MASK 0xc0 | |
38 | #define CDCE706_CLKIN_SOURCE_LVCMOS 0x40 | |
39 | ||
40 | #define CDCE706_PLL_MUX_MASK(pll) (0x80 >> (pll)) | |
41 | #define CDCE706_PLL_LOW_M_MASK 0xff | |
42 | #define CDCE706_PLL_LOW_N_MASK 0xff | |
43 | #define CDCE706_PLL_HI_M_MASK 0x1 | |
44 | #define CDCE706_PLL_HI_N_MASK 0x1e | |
45 | #define CDCE706_PLL_HI_N_SHIFT 1 | |
46 | #define CDCE706_PLL_M_MAX 0x1ff | |
47 | #define CDCE706_PLL_N_MAX 0xfff | |
48 | #define CDCE706_PLL_FVCO_MASK(pll) (0x80 >> (pll)) | |
49 | #define CDCE706_PLL_FREQ_MIN 80000000 | |
50 | #define CDCE706_PLL_FREQ_MAX 300000000 | |
51 | #define CDCE706_PLL_FREQ_HI 180000000 | |
52 | ||
53 | #define CDCE706_DIVIDER_PLL(div) (9 + (div) - ((div) > 2) - ((div) > 4)) | |
54 | #define CDCE706_DIVIDER_PLL_SHIFT(div) ((div) < 2 ? 5 : 3 * ((div) & 1)) | |
55 | #define CDCE706_DIVIDER_PLL_MASK(div) (0x7 << CDCE706_DIVIDER_PLL_SHIFT(div)) | |
56 | #define CDCE706_DIVIDER_DIVIDER_MASK 0x7f | |
57 | #define CDCE706_DIVIDER_DIVIDER_MAX 0x7f | |
58 | ||
59 | #define CDCE706_CLKOUT_DIVIDER_MASK 0x7 | |
60 | #define CDCE706_CLKOUT_ENABLE_MASK 0x8 | |
61 | ||
0d7ef4a6 | 62 | static const struct regmap_config cdce706_regmap_config = { |
0c7665c3 MF |
63 | .reg_bits = 8, |
64 | .val_bits = 8, | |
65 | .val_format_endian = REGMAP_ENDIAN_NATIVE, | |
66 | }; | |
67 | ||
68 | #define to_hw_data(phw) (container_of((phw), struct cdce706_hw_data, hw)) | |
69 | ||
70 | struct cdce706_hw_data { | |
71 | struct cdce706_dev_data *dev_data; | |
72 | unsigned idx; | |
73 | unsigned parent; | |
0c7665c3 MF |
74 | struct clk_hw hw; |
75 | unsigned div; | |
76 | unsigned mul; | |
77 | unsigned mux; | |
78 | }; | |
79 | ||
80 | struct cdce706_dev_data { | |
81 | struct i2c_client *client; | |
82 | struct regmap *regmap; | |
0c7665c3 MF |
83 | struct clk *clkin_clk[2]; |
84 | const char *clkin_name[2]; | |
85 | struct cdce706_hw_data clkin[1]; | |
86 | struct cdce706_hw_data pll[3]; | |
87 | struct cdce706_hw_data divider[6]; | |
88 | struct cdce706_hw_data clkout[6]; | |
89 | }; | |
90 | ||
91 | static const char * const cdce706_source_name[] = { | |
92 | "clk_in0", "clk_in1", | |
93 | }; | |
94 | ||
f3db6f16 | 95 | static const char * const cdce706_clkin_name[] = { |
0c7665c3 MF |
96 | "clk_in", |
97 | }; | |
98 | ||
99 | static const char * const cdce706_pll_name[] = { | |
100 | "pll1", "pll2", "pll3", | |
101 | }; | |
102 | ||
f3db6f16 | 103 | static const char * const cdce706_divider_parent_name[] = { |
0c7665c3 MF |
104 | "clk_in", "pll1", "pll2", "pll2", "pll3", |
105 | }; | |
106 | ||
107 | static const char *cdce706_divider_name[] = { | |
108 | "p0", "p1", "p2", "p3", "p4", "p5", | |
109 | }; | |
110 | ||
111 | static const char * const cdce706_clkout_name[] = { | |
112 | "clk_out0", "clk_out1", "clk_out2", "clk_out3", "clk_out4", "clk_out5", | |
113 | }; | |
114 | ||
115 | static int cdce706_reg_read(struct cdce706_dev_data *dev_data, unsigned reg, | |
116 | unsigned *val) | |
117 | { | |
118 | int rc = regmap_read(dev_data->regmap, reg | 0x80, val); | |
119 | ||
120 | if (rc < 0) | |
121 | dev_err(&dev_data->client->dev, "error reading reg %u", reg); | |
122 | return rc; | |
123 | } | |
124 | ||
125 | static int cdce706_reg_write(struct cdce706_dev_data *dev_data, unsigned reg, | |
126 | unsigned val) | |
127 | { | |
128 | int rc = regmap_write(dev_data->regmap, reg | 0x80, val); | |
129 | ||
130 | if (rc < 0) | |
131 | dev_err(&dev_data->client->dev, "error writing reg %u", reg); | |
132 | return rc; | |
133 | } | |
134 | ||
135 | static int cdce706_reg_update(struct cdce706_dev_data *dev_data, unsigned reg, | |
136 | unsigned mask, unsigned val) | |
137 | { | |
138 | int rc = regmap_update_bits(dev_data->regmap, reg | 0x80, mask, val); | |
139 | ||
140 | if (rc < 0) | |
141 | dev_err(&dev_data->client->dev, "error updating reg %u", reg); | |
142 | return rc; | |
143 | } | |
144 | ||
145 | static int cdce706_clkin_set_parent(struct clk_hw *hw, u8 index) | |
146 | { | |
147 | struct cdce706_hw_data *hwd = to_hw_data(hw); | |
148 | ||
149 | hwd->parent = index; | |
150 | return 0; | |
151 | } | |
152 | ||
153 | static u8 cdce706_clkin_get_parent(struct clk_hw *hw) | |
154 | { | |
155 | struct cdce706_hw_data *hwd = to_hw_data(hw); | |
156 | ||
157 | return hwd->parent; | |
158 | } | |
159 | ||
160 | static const struct clk_ops cdce706_clkin_ops = { | |
161 | .set_parent = cdce706_clkin_set_parent, | |
162 | .get_parent = cdce706_clkin_get_parent, | |
163 | }; | |
164 | ||
165 | static unsigned long cdce706_pll_recalc_rate(struct clk_hw *hw, | |
166 | unsigned long parent_rate) | |
167 | { | |
168 | struct cdce706_hw_data *hwd = to_hw_data(hw); | |
169 | ||
170 | dev_dbg(&hwd->dev_data->client->dev, | |
171 | "%s, pll: %d, mux: %d, mul: %u, div: %u\n", | |
172 | __func__, hwd->idx, hwd->mux, hwd->mul, hwd->div); | |
173 | ||
174 | if (!hwd->mux) { | |
175 | if (hwd->div && hwd->mul) { | |
176 | u64 res = (u64)parent_rate * hwd->mul; | |
177 | ||
178 | do_div(res, hwd->div); | |
179 | return res; | |
180 | } | |
181 | } else { | |
182 | if (hwd->div) | |
183 | return parent_rate / hwd->div; | |
184 | } | |
185 | return 0; | |
186 | } | |
187 | ||
188 | static long cdce706_pll_round_rate(struct clk_hw *hw, unsigned long rate, | |
189 | unsigned long *parent_rate) | |
190 | { | |
191 | struct cdce706_hw_data *hwd = to_hw_data(hw); | |
192 | unsigned long mul, div; | |
193 | u64 res; | |
194 | ||
195 | dev_dbg(&hwd->dev_data->client->dev, | |
196 | "%s, rate: %lu, parent_rate: %lu\n", | |
197 | __func__, rate, *parent_rate); | |
198 | ||
199 | rational_best_approximation(rate, *parent_rate, | |
200 | CDCE706_PLL_N_MAX, CDCE706_PLL_M_MAX, | |
201 | &mul, &div); | |
202 | hwd->mul = mul; | |
203 | hwd->div = div; | |
204 | ||
205 | dev_dbg(&hwd->dev_data->client->dev, | |
206 | "%s, pll: %d, mul: %lu, div: %lu\n", | |
207 | __func__, hwd->idx, mul, div); | |
208 | ||
209 | res = (u64)*parent_rate * hwd->mul; | |
210 | do_div(res, hwd->div); | |
211 | return res; | |
212 | } | |
213 | ||
214 | static int cdce706_pll_set_rate(struct clk_hw *hw, unsigned long rate, | |
215 | unsigned long parent_rate) | |
216 | { | |
217 | struct cdce706_hw_data *hwd = to_hw_data(hw); | |
218 | unsigned long mul = hwd->mul, div = hwd->div; | |
219 | int err; | |
220 | ||
221 | dev_dbg(&hwd->dev_data->client->dev, | |
222 | "%s, pll: %d, mul: %lu, div: %lu\n", | |
223 | __func__, hwd->idx, mul, div); | |
224 | ||
225 | err = cdce706_reg_update(hwd->dev_data, | |
226 | CDCE706_PLL_HI(hwd->idx), | |
227 | CDCE706_PLL_HI_M_MASK | CDCE706_PLL_HI_N_MASK, | |
228 | ((div >> 8) & CDCE706_PLL_HI_M_MASK) | | |
229 | ((mul >> (8 - CDCE706_PLL_HI_N_SHIFT)) & | |
230 | CDCE706_PLL_HI_N_MASK)); | |
231 | if (err < 0) | |
232 | return err; | |
233 | ||
234 | err = cdce706_reg_write(hwd->dev_data, | |
235 | CDCE706_PLL_M_LOW(hwd->idx), | |
236 | div & CDCE706_PLL_LOW_M_MASK); | |
237 | if (err < 0) | |
238 | return err; | |
239 | ||
240 | err = cdce706_reg_write(hwd->dev_data, | |
241 | CDCE706_PLL_N_LOW(hwd->idx), | |
242 | mul & CDCE706_PLL_LOW_N_MASK); | |
243 | if (err < 0) | |
244 | return err; | |
245 | ||
246 | err = cdce706_reg_update(hwd->dev_data, | |
247 | CDCE706_PLL_FVCO, | |
248 | CDCE706_PLL_FVCO_MASK(hwd->idx), | |
249 | rate > CDCE706_PLL_FREQ_HI ? | |
250 | CDCE706_PLL_FVCO_MASK(hwd->idx) : 0); | |
251 | return err; | |
252 | } | |
253 | ||
254 | static const struct clk_ops cdce706_pll_ops = { | |
255 | .recalc_rate = cdce706_pll_recalc_rate, | |
256 | .round_rate = cdce706_pll_round_rate, | |
257 | .set_rate = cdce706_pll_set_rate, | |
258 | }; | |
259 | ||
260 | static int cdce706_divider_set_parent(struct clk_hw *hw, u8 index) | |
261 | { | |
262 | struct cdce706_hw_data *hwd = to_hw_data(hw); | |
263 | ||
264 | if (hwd->parent == index) | |
265 | return 0; | |
266 | hwd->parent = index; | |
267 | return cdce706_reg_update(hwd->dev_data, | |
268 | CDCE706_DIVIDER_PLL(hwd->idx), | |
269 | CDCE706_DIVIDER_PLL_MASK(hwd->idx), | |
270 | index << CDCE706_DIVIDER_PLL_SHIFT(hwd->idx)); | |
271 | } | |
272 | ||
273 | static u8 cdce706_divider_get_parent(struct clk_hw *hw) | |
274 | { | |
275 | struct cdce706_hw_data *hwd = to_hw_data(hw); | |
276 | ||
277 | return hwd->parent; | |
278 | } | |
279 | ||
280 | static unsigned long cdce706_divider_recalc_rate(struct clk_hw *hw, | |
281 | unsigned long parent_rate) | |
282 | { | |
283 | struct cdce706_hw_data *hwd = to_hw_data(hw); | |
284 | ||
285 | dev_dbg(&hwd->dev_data->client->dev, | |
286 | "%s, divider: %d, div: %u\n", | |
287 | __func__, hwd->idx, hwd->div); | |
288 | if (hwd->div) | |
289 | return parent_rate / hwd->div; | |
290 | return 0; | |
291 | } | |
292 | ||
293 | static long cdce706_divider_round_rate(struct clk_hw *hw, unsigned long rate, | |
294 | unsigned long *parent_rate) | |
295 | { | |
296 | struct cdce706_hw_data *hwd = to_hw_data(hw); | |
297 | struct cdce706_dev_data *cdce = hwd->dev_data; | |
298 | unsigned long mul, div; | |
299 | ||
300 | dev_dbg(&hwd->dev_data->client->dev, | |
301 | "%s, rate: %lu, parent_rate: %lu\n", | |
302 | __func__, rate, *parent_rate); | |
303 | ||
304 | rational_best_approximation(rate, *parent_rate, | |
305 | 1, CDCE706_DIVIDER_DIVIDER_MAX, | |
306 | &mul, &div); | |
307 | if (!mul) | |
308 | div = CDCE706_DIVIDER_DIVIDER_MAX; | |
309 | ||
98d8a60e | 310 | if (clk_hw_get_flags(hw) & CLK_SET_RATE_PARENT) { |
0c7665c3 MF |
311 | unsigned long best_diff = rate; |
312 | unsigned long best_div = 0; | |
313 | struct clk *gp_clk = cdce->clkin_clk[cdce->clkin[0].parent]; | |
314 | unsigned long gp_rate = gp_clk ? clk_get_rate(gp_clk) : 0; | |
315 | ||
316 | for (div = CDCE706_PLL_FREQ_MIN / rate; best_diff && | |
317 | div <= CDCE706_PLL_FREQ_MAX / rate; ++div) { | |
318 | unsigned long n, m; | |
319 | unsigned long diff; | |
320 | unsigned long div_rate; | |
321 | u64 div_rate64; | |
322 | ||
323 | if (rate * div < CDCE706_PLL_FREQ_MIN) | |
324 | continue; | |
325 | ||
326 | rational_best_approximation(rate * div, gp_rate, | |
327 | CDCE706_PLL_N_MAX, | |
328 | CDCE706_PLL_M_MAX, | |
329 | &n, &m); | |
330 | div_rate64 = (u64)gp_rate * n; | |
331 | do_div(div_rate64, m); | |
332 | do_div(div_rate64, div); | |
333 | div_rate = div_rate64; | |
334 | diff = max(div_rate, rate) - min(div_rate, rate); | |
335 | ||
336 | if (diff < best_diff) { | |
337 | best_diff = diff; | |
338 | best_div = div; | |
339 | dev_dbg(&hwd->dev_data->client->dev, | |
340 | "%s, %lu * %lu / %lu / %lu = %lu\n", | |
341 | __func__, gp_rate, n, m, div, div_rate); | |
342 | } | |
343 | } | |
344 | ||
345 | div = best_div; | |
346 | ||
347 | dev_dbg(&hwd->dev_data->client->dev, | |
348 | "%s, altering parent rate: %lu -> %lu\n", | |
349 | __func__, *parent_rate, rate * div); | |
350 | *parent_rate = rate * div; | |
351 | } | |
352 | hwd->div = div; | |
353 | ||
354 | dev_dbg(&hwd->dev_data->client->dev, | |
355 | "%s, divider: %d, div: %lu\n", | |
356 | __func__, hwd->idx, div); | |
357 | ||
358 | return *parent_rate / div; | |
359 | } | |
360 | ||
361 | static int cdce706_divider_set_rate(struct clk_hw *hw, unsigned long rate, | |
362 | unsigned long parent_rate) | |
363 | { | |
364 | struct cdce706_hw_data *hwd = to_hw_data(hw); | |
365 | ||
366 | dev_dbg(&hwd->dev_data->client->dev, | |
367 | "%s, divider: %d, div: %u\n", | |
368 | __func__, hwd->idx, hwd->div); | |
369 | ||
370 | return cdce706_reg_update(hwd->dev_data, | |
371 | CDCE706_DIVIDER(hwd->idx), | |
372 | CDCE706_DIVIDER_DIVIDER_MASK, | |
373 | hwd->div); | |
374 | } | |
375 | ||
376 | static const struct clk_ops cdce706_divider_ops = { | |
377 | .set_parent = cdce706_divider_set_parent, | |
378 | .get_parent = cdce706_divider_get_parent, | |
379 | .recalc_rate = cdce706_divider_recalc_rate, | |
380 | .round_rate = cdce706_divider_round_rate, | |
381 | .set_rate = cdce706_divider_set_rate, | |
382 | }; | |
383 | ||
384 | static int cdce706_clkout_prepare(struct clk_hw *hw) | |
385 | { | |
386 | struct cdce706_hw_data *hwd = to_hw_data(hw); | |
387 | ||
388 | return cdce706_reg_update(hwd->dev_data, CDCE706_CLKOUT(hwd->idx), | |
389 | CDCE706_CLKOUT_ENABLE_MASK, | |
390 | CDCE706_CLKOUT_ENABLE_MASK); | |
391 | } | |
392 | ||
393 | static void cdce706_clkout_unprepare(struct clk_hw *hw) | |
394 | { | |
395 | struct cdce706_hw_data *hwd = to_hw_data(hw); | |
396 | ||
397 | cdce706_reg_update(hwd->dev_data, CDCE706_CLKOUT(hwd->idx), | |
398 | CDCE706_CLKOUT_ENABLE_MASK, 0); | |
399 | } | |
400 | ||
401 | static int cdce706_clkout_set_parent(struct clk_hw *hw, u8 index) | |
402 | { | |
403 | struct cdce706_hw_data *hwd = to_hw_data(hw); | |
404 | ||
405 | if (hwd->parent == index) | |
406 | return 0; | |
407 | hwd->parent = index; | |
408 | return cdce706_reg_update(hwd->dev_data, | |
409 | CDCE706_CLKOUT(hwd->idx), | |
410 | CDCE706_CLKOUT_ENABLE_MASK, index); | |
411 | } | |
412 | ||
413 | static u8 cdce706_clkout_get_parent(struct clk_hw *hw) | |
414 | { | |
415 | struct cdce706_hw_data *hwd = to_hw_data(hw); | |
416 | ||
417 | return hwd->parent; | |
418 | } | |
419 | ||
420 | static unsigned long cdce706_clkout_recalc_rate(struct clk_hw *hw, | |
421 | unsigned long parent_rate) | |
422 | { | |
423 | return parent_rate; | |
424 | } | |
425 | ||
426 | static long cdce706_clkout_round_rate(struct clk_hw *hw, unsigned long rate, | |
427 | unsigned long *parent_rate) | |
428 | { | |
429 | *parent_rate = rate; | |
430 | return rate; | |
431 | } | |
432 | ||
433 | static int cdce706_clkout_set_rate(struct clk_hw *hw, unsigned long rate, | |
434 | unsigned long parent_rate) | |
435 | { | |
436 | return 0; | |
437 | } | |
438 | ||
439 | static const struct clk_ops cdce706_clkout_ops = { | |
440 | .prepare = cdce706_clkout_prepare, | |
441 | .unprepare = cdce706_clkout_unprepare, | |
442 | .set_parent = cdce706_clkout_set_parent, | |
443 | .get_parent = cdce706_clkout_get_parent, | |
444 | .recalc_rate = cdce706_clkout_recalc_rate, | |
445 | .round_rate = cdce706_clkout_round_rate, | |
446 | .set_rate = cdce706_clkout_set_rate, | |
447 | }; | |
448 | ||
449 | static int cdce706_register_hw(struct cdce706_dev_data *cdce, | |
450 | struct cdce706_hw_data *hw, unsigned num_hw, | |
451 | const char * const *clk_names, | |
452 | struct clk_init_data *init) | |
453 | { | |
454 | unsigned i; | |
01b5200a | 455 | int ret; |
0c7665c3 MF |
456 | |
457 | for (i = 0; i < num_hw; ++i, ++hw) { | |
458 | init->name = clk_names[i]; | |
459 | hw->dev_data = cdce; | |
460 | hw->idx = i; | |
461 | hw->hw.init = init; | |
01b5200a | 462 | ret = devm_clk_hw_register(&cdce->client->dev, |
0c7665c3 | 463 | &hw->hw); |
01b5200a | 464 | if (ret) { |
0c7665c3 MF |
465 | dev_err(&cdce->client->dev, "Failed to register %s\n", |
466 | clk_names[i]); | |
01b5200a | 467 | return ret; |
0c7665c3 MF |
468 | } |
469 | } | |
470 | return 0; | |
471 | } | |
472 | ||
473 | static int cdce706_register_clkin(struct cdce706_dev_data *cdce) | |
474 | { | |
475 | struct clk_init_data init = { | |
476 | .ops = &cdce706_clkin_ops, | |
477 | .parent_names = cdce->clkin_name, | |
478 | .num_parents = ARRAY_SIZE(cdce->clkin_name), | |
479 | }; | |
480 | unsigned i; | |
481 | int ret; | |
482 | unsigned clock, source; | |
483 | ||
484 | for (i = 0; i < ARRAY_SIZE(cdce->clkin_name); ++i) { | |
485 | struct clk *parent = devm_clk_get(&cdce->client->dev, | |
486 | cdce706_source_name[i]); | |
487 | ||
488 | if (IS_ERR(parent)) { | |
489 | cdce->clkin_name[i] = cdce706_source_name[i]; | |
490 | } else { | |
491 | cdce->clkin_name[i] = __clk_get_name(parent); | |
492 | cdce->clkin_clk[i] = parent; | |
493 | } | |
494 | } | |
495 | ||
496 | ret = cdce706_reg_read(cdce, CDCE706_CLKIN_SOURCE, &source); | |
497 | if (ret < 0) | |
498 | return ret; | |
499 | if ((source & CDCE706_CLKIN_SOURCE_MASK) == | |
500 | CDCE706_CLKIN_SOURCE_LVCMOS) { | |
501 | ret = cdce706_reg_read(cdce, CDCE706_CLKIN_CLOCK, &clock); | |
502 | if (ret < 0) | |
503 | return ret; | |
504 | cdce->clkin[0].parent = !!(clock & CDCE706_CLKIN_CLOCK_MASK); | |
505 | } | |
506 | ||
507 | ret = cdce706_register_hw(cdce, cdce->clkin, | |
508 | ARRAY_SIZE(cdce->clkin), | |
509 | cdce706_clkin_name, &init); | |
510 | return ret; | |
511 | } | |
512 | ||
513 | static int cdce706_register_plls(struct cdce706_dev_data *cdce) | |
514 | { | |
515 | struct clk_init_data init = { | |
516 | .ops = &cdce706_pll_ops, | |
517 | .parent_names = cdce706_clkin_name, | |
518 | .num_parents = ARRAY_SIZE(cdce706_clkin_name), | |
519 | }; | |
520 | unsigned i; | |
521 | int ret; | |
522 | unsigned mux; | |
523 | ||
524 | ret = cdce706_reg_read(cdce, CDCE706_PLL_MUX, &mux); | |
525 | if (ret < 0) | |
526 | return ret; | |
527 | ||
528 | for (i = 0; i < ARRAY_SIZE(cdce->pll); ++i) { | |
529 | unsigned m, n, v; | |
530 | ||
531 | ret = cdce706_reg_read(cdce, CDCE706_PLL_M_LOW(i), &m); | |
532 | if (ret < 0) | |
533 | return ret; | |
534 | ret = cdce706_reg_read(cdce, CDCE706_PLL_N_LOW(i), &n); | |
535 | if (ret < 0) | |
536 | return ret; | |
537 | ret = cdce706_reg_read(cdce, CDCE706_PLL_HI(i), &v); | |
538 | if (ret < 0) | |
539 | return ret; | |
540 | cdce->pll[i].div = m | ((v & CDCE706_PLL_HI_M_MASK) << 8); | |
541 | cdce->pll[i].mul = n | ((v & CDCE706_PLL_HI_N_MASK) << | |
542 | (8 - CDCE706_PLL_HI_N_SHIFT)); | |
543 | cdce->pll[i].mux = mux & CDCE706_PLL_MUX_MASK(i); | |
544 | dev_dbg(&cdce->client->dev, | |
545 | "%s: i: %u, div: %u, mul: %u, mux: %d\n", __func__, i, | |
546 | cdce->pll[i].div, cdce->pll[i].mul, cdce->pll[i].mux); | |
547 | } | |
548 | ||
549 | ret = cdce706_register_hw(cdce, cdce->pll, | |
550 | ARRAY_SIZE(cdce->pll), | |
551 | cdce706_pll_name, &init); | |
552 | return ret; | |
553 | } | |
554 | ||
555 | static int cdce706_register_dividers(struct cdce706_dev_data *cdce) | |
556 | { | |
557 | struct clk_init_data init = { | |
558 | .ops = &cdce706_divider_ops, | |
559 | .parent_names = cdce706_divider_parent_name, | |
560 | .num_parents = ARRAY_SIZE(cdce706_divider_parent_name), | |
561 | .flags = CLK_SET_RATE_PARENT, | |
562 | }; | |
563 | unsigned i; | |
564 | int ret; | |
565 | ||
566 | for (i = 0; i < ARRAY_SIZE(cdce->divider); ++i) { | |
567 | unsigned val; | |
568 | ||
569 | ret = cdce706_reg_read(cdce, CDCE706_DIVIDER_PLL(i), &val); | |
570 | if (ret < 0) | |
571 | return ret; | |
572 | cdce->divider[i].parent = | |
573 | (val & CDCE706_DIVIDER_PLL_MASK(i)) >> | |
574 | CDCE706_DIVIDER_PLL_SHIFT(i); | |
575 | ||
576 | ret = cdce706_reg_read(cdce, CDCE706_DIVIDER(i), &val); | |
577 | if (ret < 0) | |
578 | return ret; | |
579 | cdce->divider[i].div = val & CDCE706_DIVIDER_DIVIDER_MASK; | |
580 | dev_dbg(&cdce->client->dev, | |
581 | "%s: i: %u, parent: %u, div: %u\n", __func__, i, | |
582 | cdce->divider[i].parent, cdce->divider[i].div); | |
583 | } | |
584 | ||
585 | ret = cdce706_register_hw(cdce, cdce->divider, | |
586 | ARRAY_SIZE(cdce->divider), | |
587 | cdce706_divider_name, &init); | |
588 | return ret; | |
589 | } | |
590 | ||
591 | static int cdce706_register_clkouts(struct cdce706_dev_data *cdce) | |
592 | { | |
593 | struct clk_init_data init = { | |
594 | .ops = &cdce706_clkout_ops, | |
595 | .parent_names = cdce706_divider_name, | |
596 | .num_parents = ARRAY_SIZE(cdce706_divider_name), | |
597 | .flags = CLK_SET_RATE_PARENT, | |
598 | }; | |
599 | unsigned i; | |
600 | int ret; | |
601 | ||
602 | for (i = 0; i < ARRAY_SIZE(cdce->clkout); ++i) { | |
603 | unsigned val; | |
604 | ||
605 | ret = cdce706_reg_read(cdce, CDCE706_CLKOUT(i), &val); | |
606 | if (ret < 0) | |
607 | return ret; | |
608 | cdce->clkout[i].parent = val & CDCE706_CLKOUT_DIVIDER_MASK; | |
609 | dev_dbg(&cdce->client->dev, | |
610 | "%s: i: %u, parent: %u\n", __func__, i, | |
611 | cdce->clkout[i].parent); | |
612 | } | |
613 | ||
01b5200a SB |
614 | return cdce706_register_hw(cdce, cdce->clkout, |
615 | ARRAY_SIZE(cdce->clkout), | |
616 | cdce706_clkout_name, &init); | |
617 | } | |
0c7665c3 | 618 | |
01b5200a SB |
619 | static struct clk_hw * |
620 | of_clk_cdce_get(struct of_phandle_args *clkspec, void *data) | |
621 | { | |
622 | struct cdce706_dev_data *cdce = data; | |
623 | unsigned int idx = clkspec->args[0]; | |
624 | ||
625 | if (idx >= ARRAY_SIZE(cdce->clkout)) { | |
626 | pr_err("%s: invalid index %u\n", __func__, idx); | |
627 | return ERR_PTR(-EINVAL); | |
628 | } | |
629 | ||
630 | return &cdce->clkout[idx].hw; | |
0c7665c3 MF |
631 | } |
632 | ||
633 | static int cdce706_probe(struct i2c_client *client, | |
634 | const struct i2c_device_id *id) | |
635 | { | |
636 | struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent); | |
637 | struct cdce706_dev_data *cdce; | |
638 | int ret; | |
639 | ||
640 | if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) | |
641 | return -EIO; | |
642 | ||
643 | cdce = devm_kzalloc(&client->dev, sizeof(*cdce), GFP_KERNEL); | |
644 | if (!cdce) | |
645 | return -ENOMEM; | |
646 | ||
647 | cdce->client = client; | |
648 | cdce->regmap = devm_regmap_init_i2c(client, &cdce706_regmap_config); | |
649 | if (IS_ERR(cdce->regmap)) { | |
650 | dev_err(&client->dev, "Failed to initialize regmap\n"); | |
651 | return -EINVAL; | |
652 | } | |
653 | ||
654 | i2c_set_clientdata(client, cdce); | |
655 | ||
656 | ret = cdce706_register_clkin(cdce); | |
657 | if (ret < 0) | |
658 | return ret; | |
659 | ret = cdce706_register_plls(cdce); | |
660 | if (ret < 0) | |
661 | return ret; | |
662 | ret = cdce706_register_dividers(cdce); | |
663 | if (ret < 0) | |
664 | return ret; | |
665 | ret = cdce706_register_clkouts(cdce); | |
666 | if (ret < 0) | |
667 | return ret; | |
01b5200a SB |
668 | return of_clk_add_hw_provider(client->dev.of_node, of_clk_cdce_get, |
669 | cdce); | |
0c7665c3 MF |
670 | } |
671 | ||
672 | static int cdce706_remove(struct i2c_client *client) | |
673 | { | |
8cdea502 | 674 | of_clk_del_provider(client->dev.of_node); |
0c7665c3 MF |
675 | return 0; |
676 | } | |
677 | ||
678 | ||
679 | #ifdef CONFIG_OF | |
680 | static const struct of_device_id cdce706_dt_match[] = { | |
681 | { .compatible = "ti,cdce706" }, | |
682 | { }, | |
683 | }; | |
684 | MODULE_DEVICE_TABLE(of, cdce706_dt_match); | |
685 | #endif | |
686 | ||
687 | static const struct i2c_device_id cdce706_id[] = { | |
688 | { "cdce706", 0 }, | |
689 | { } | |
690 | }; | |
691 | MODULE_DEVICE_TABLE(i2c, cdce706_id); | |
692 | ||
693 | static struct i2c_driver cdce706_i2c_driver = { | |
694 | .driver = { | |
695 | .name = "cdce706", | |
696 | .of_match_table = of_match_ptr(cdce706_dt_match), | |
697 | }, | |
698 | .probe = cdce706_probe, | |
699 | .remove = cdce706_remove, | |
700 | .id_table = cdce706_id, | |
701 | }; | |
702 | module_i2c_driver(cdce706_i2c_driver); | |
703 | ||
704 | MODULE_AUTHOR("Max Filippov <[email protected]>"); | |
705 | MODULE_DESCRIPTION("TI CDCE 706 clock synthesizer driver"); | |
706 | MODULE_LICENSE("GPL"); |