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