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