Commit | Line | Data |
---|---|---|
d2912cb1 | 1 | // SPDX-License-Identifier: GPL-2.0-only |
0c7665c3 MF |
2 | /* |
3 | * TI CDCE706 programmable 3-PLL clock synthesizer driver | |
4 | * | |
5 | * Copyright (c) 2014 Cadence Design Systems Inc. | |
6 | * | |
5f1d8970 | 7 | * Reference: https://www.ti.com/lit/ds/symlink/cdce706.pdf |
0c7665c3 MF |
8 | */ |
9 | ||
fc1699c8 | 10 | #include <linux/clk.h> |
0c7665c3 MF |
11 | #include <linux/clk-provider.h> |
12 | #include <linux/delay.h> | |
13 | #include <linux/i2c.h> | |
14 | #include <linux/interrupt.h> | |
15 | #include <linux/mod_devicetable.h> | |
16 | #include <linux/module.h> | |
17 | #include <linux/of.h> | |
18 | #include <linux/rational.h> | |
19 | #include <linux/regmap.h> | |
20 | #include <linux/slab.h> | |
21 | ||
22 | #define CDCE706_CLKIN_CLOCK 10 | |
23 | #define CDCE706_CLKIN_SOURCE 11 | |
24 | #define CDCE706_PLL_M_LOW(pll) (1 + 3 * (pll)) | |
25 | #define CDCE706_PLL_N_LOW(pll) (2 + 3 * (pll)) | |
26 | #define CDCE706_PLL_HI(pll) (3 + 3 * (pll)) | |
27 | #define CDCE706_PLL_MUX 3 | |
28 | #define CDCE706_PLL_FVCO 6 | |
29 | #define CDCE706_DIVIDER(div) (13 + (div)) | |
30 | #define CDCE706_CLKOUT(out) (19 + (out)) | |
31 | ||
32 | #define CDCE706_CLKIN_CLOCK_MASK 0x10 | |
33 | #define CDCE706_CLKIN_SOURCE_SHIFT 6 | |
34 | #define CDCE706_CLKIN_SOURCE_MASK 0xc0 | |
35 | #define CDCE706_CLKIN_SOURCE_LVCMOS 0x40 | |
36 | ||
37 | #define CDCE706_PLL_MUX_MASK(pll) (0x80 >> (pll)) | |
38 | #define CDCE706_PLL_LOW_M_MASK 0xff | |
39 | #define CDCE706_PLL_LOW_N_MASK 0xff | |
40 | #define CDCE706_PLL_HI_M_MASK 0x1 | |
41 | #define CDCE706_PLL_HI_N_MASK 0x1e | |
42 | #define CDCE706_PLL_HI_N_SHIFT 1 | |
43 | #define CDCE706_PLL_M_MAX 0x1ff | |
44 | #define CDCE706_PLL_N_MAX 0xfff | |
45 | #define CDCE706_PLL_FVCO_MASK(pll) (0x80 >> (pll)) | |
46 | #define CDCE706_PLL_FREQ_MIN 80000000 | |
47 | #define CDCE706_PLL_FREQ_MAX 300000000 | |
48 | #define CDCE706_PLL_FREQ_HI 180000000 | |
49 | ||
50 | #define CDCE706_DIVIDER_PLL(div) (9 + (div) - ((div) > 2) - ((div) > 4)) | |
51 | #define CDCE706_DIVIDER_PLL_SHIFT(div) ((div) < 2 ? 5 : 3 * ((div) & 1)) | |
52 | #define CDCE706_DIVIDER_PLL_MASK(div) (0x7 << CDCE706_DIVIDER_PLL_SHIFT(div)) | |
53 | #define CDCE706_DIVIDER_DIVIDER_MASK 0x7f | |
54 | #define CDCE706_DIVIDER_DIVIDER_MAX 0x7f | |
55 | ||
56 | #define CDCE706_CLKOUT_DIVIDER_MASK 0x7 | |
57 | #define CDCE706_CLKOUT_ENABLE_MASK 0x8 | |
58 | ||
0d7ef4a6 | 59 | static const struct regmap_config cdce706_regmap_config = { |
0c7665c3 MF |
60 | .reg_bits = 8, |
61 | .val_bits = 8, | |
62 | .val_format_endian = REGMAP_ENDIAN_NATIVE, | |
63 | }; | |
64 | ||
65 | #define to_hw_data(phw) (container_of((phw), struct cdce706_hw_data, hw)) | |
66 | ||
67 | struct cdce706_hw_data { | |
68 | struct cdce706_dev_data *dev_data; | |
69 | unsigned idx; | |
70 | unsigned parent; | |
0c7665c3 MF |
71 | struct clk_hw hw; |
72 | unsigned div; | |
73 | unsigned mul; | |
74 | unsigned mux; | |
75 | }; | |
76 | ||
77 | struct cdce706_dev_data { | |
78 | struct i2c_client *client; | |
79 | struct regmap *regmap; | |
0c7665c3 MF |
80 | struct clk *clkin_clk[2]; |
81 | const char *clkin_name[2]; | |
82 | struct cdce706_hw_data clkin[1]; | |
83 | struct cdce706_hw_data pll[3]; | |
84 | struct cdce706_hw_data divider[6]; | |
85 | struct cdce706_hw_data clkout[6]; | |
86 | }; | |
87 | ||
88 | static const char * const cdce706_source_name[] = { | |
89 | "clk_in0", "clk_in1", | |
90 | }; | |
91 | ||
f3db6f16 | 92 | static const char * const cdce706_clkin_name[] = { |
0c7665c3 MF |
93 | "clk_in", |
94 | }; | |
95 | ||
96 | static const char * const cdce706_pll_name[] = { | |
97 | "pll1", "pll2", "pll3", | |
98 | }; | |
99 | ||
f3db6f16 | 100 | static const char * const cdce706_divider_parent_name[] = { |
0c7665c3 MF |
101 | "clk_in", "pll1", "pll2", "pll2", "pll3", |
102 | }; | |
103 | ||
104 | static const char *cdce706_divider_name[] = { | |
105 | "p0", "p1", "p2", "p3", "p4", "p5", | |
106 | }; | |
107 | ||
108 | static const char * const cdce706_clkout_name[] = { | |
109 | "clk_out0", "clk_out1", "clk_out2", "clk_out3", "clk_out4", "clk_out5", | |
110 | }; | |
111 | ||
112 | static int cdce706_reg_read(struct cdce706_dev_data *dev_data, unsigned reg, | |
113 | unsigned *val) | |
114 | { | |
115 | int rc = regmap_read(dev_data->regmap, reg | 0x80, val); | |
116 | ||
117 | if (rc < 0) | |
118 | dev_err(&dev_data->client->dev, "error reading reg %u", reg); | |
119 | return rc; | |
120 | } | |
121 | ||
122 | static int cdce706_reg_write(struct cdce706_dev_data *dev_data, unsigned reg, | |
123 | unsigned val) | |
124 | { | |
125 | int rc = regmap_write(dev_data->regmap, reg | 0x80, val); | |
126 | ||
127 | if (rc < 0) | |
128 | dev_err(&dev_data->client->dev, "error writing reg %u", reg); | |
129 | return rc; | |
130 | } | |
131 | ||
132 | static int cdce706_reg_update(struct cdce706_dev_data *dev_data, unsigned reg, | |
133 | unsigned mask, unsigned val) | |
134 | { | |
135 | int rc = regmap_update_bits(dev_data->regmap, reg | 0x80, mask, val); | |
136 | ||
137 | if (rc < 0) | |
138 | dev_err(&dev_data->client->dev, "error updating reg %u", reg); | |
139 | return rc; | |
140 | } | |
141 | ||
142 | static int cdce706_clkin_set_parent(struct clk_hw *hw, u8 index) | |
143 | { | |
144 | struct cdce706_hw_data *hwd = to_hw_data(hw); | |
145 | ||
146 | hwd->parent = index; | |
147 | return 0; | |
148 | } | |
149 | ||
150 | static u8 cdce706_clkin_get_parent(struct clk_hw *hw) | |
151 | { | |
152 | struct cdce706_hw_data *hwd = to_hw_data(hw); | |
153 | ||
154 | return hwd->parent; | |
155 | } | |
156 | ||
157 | static const struct clk_ops cdce706_clkin_ops = { | |
43e8f067 | 158 | .determine_rate = clk_hw_determine_rate_no_reparent, |
0c7665c3 MF |
159 | .set_parent = cdce706_clkin_set_parent, |
160 | .get_parent = cdce706_clkin_get_parent, | |
161 | }; | |
162 | ||
163 | static unsigned long cdce706_pll_recalc_rate(struct clk_hw *hw, | |
164 | unsigned long parent_rate) | |
165 | { | |
166 | struct cdce706_hw_data *hwd = to_hw_data(hw); | |
167 | ||
168 | dev_dbg(&hwd->dev_data->client->dev, | |
169 | "%s, pll: %d, mux: %d, mul: %u, div: %u\n", | |
170 | __func__, hwd->idx, hwd->mux, hwd->mul, hwd->div); | |
171 | ||
172 | if (!hwd->mux) { | |
173 | if (hwd->div && hwd->mul) { | |
174 | u64 res = (u64)parent_rate * hwd->mul; | |
175 | ||
176 | do_div(res, hwd->div); | |
177 | return res; | |
178 | } | |
179 | } else { | |
180 | if (hwd->div) | |
181 | return parent_rate / hwd->div; | |
182 | } | |
183 | return 0; | |
184 | } | |
185 | ||
186 | static long cdce706_pll_round_rate(struct clk_hw *hw, unsigned long rate, | |
187 | unsigned long *parent_rate) | |
188 | { | |
189 | struct cdce706_hw_data *hwd = to_hw_data(hw); | |
190 | unsigned long mul, div; | |
191 | u64 res; | |
192 | ||
193 | dev_dbg(&hwd->dev_data->client->dev, | |
194 | "%s, rate: %lu, parent_rate: %lu\n", | |
195 | __func__, rate, *parent_rate); | |
196 | ||
197 | rational_best_approximation(rate, *parent_rate, | |
198 | CDCE706_PLL_N_MAX, CDCE706_PLL_M_MAX, | |
199 | &mul, &div); | |
200 | hwd->mul = mul; | |
201 | hwd->div = div; | |
202 | ||
203 | dev_dbg(&hwd->dev_data->client->dev, | |
204 | "%s, pll: %d, mul: %lu, div: %lu\n", | |
205 | __func__, hwd->idx, mul, div); | |
206 | ||
207 | res = (u64)*parent_rate * hwd->mul; | |
208 | do_div(res, hwd->div); | |
209 | return res; | |
210 | } | |
211 | ||
212 | static int cdce706_pll_set_rate(struct clk_hw *hw, unsigned long rate, | |
213 | unsigned long parent_rate) | |
214 | { | |
215 | struct cdce706_hw_data *hwd = to_hw_data(hw); | |
216 | unsigned long mul = hwd->mul, div = hwd->div; | |
217 | int err; | |
218 | ||
219 | dev_dbg(&hwd->dev_data->client->dev, | |
220 | "%s, pll: %d, mul: %lu, div: %lu\n", | |
221 | __func__, hwd->idx, mul, div); | |
222 | ||
223 | err = cdce706_reg_update(hwd->dev_data, | |
224 | CDCE706_PLL_HI(hwd->idx), | |
225 | CDCE706_PLL_HI_M_MASK | CDCE706_PLL_HI_N_MASK, | |
226 | ((div >> 8) & CDCE706_PLL_HI_M_MASK) | | |
227 | ((mul >> (8 - CDCE706_PLL_HI_N_SHIFT)) & | |
228 | CDCE706_PLL_HI_N_MASK)); | |
229 | if (err < 0) | |
230 | return err; | |
231 | ||
232 | err = cdce706_reg_write(hwd->dev_data, | |
233 | CDCE706_PLL_M_LOW(hwd->idx), | |
234 | div & CDCE706_PLL_LOW_M_MASK); | |
235 | if (err < 0) | |
236 | return err; | |
237 | ||
238 | err = cdce706_reg_write(hwd->dev_data, | |
239 | CDCE706_PLL_N_LOW(hwd->idx), | |
240 | mul & CDCE706_PLL_LOW_N_MASK); | |
241 | if (err < 0) | |
242 | return err; | |
243 | ||
244 | err = cdce706_reg_update(hwd->dev_data, | |
245 | CDCE706_PLL_FVCO, | |
246 | CDCE706_PLL_FVCO_MASK(hwd->idx), | |
247 | rate > CDCE706_PLL_FREQ_HI ? | |
248 | CDCE706_PLL_FVCO_MASK(hwd->idx) : 0); | |
249 | return err; | |
250 | } | |
251 | ||
252 | static const struct clk_ops cdce706_pll_ops = { | |
253 | .recalc_rate = cdce706_pll_recalc_rate, | |
254 | .round_rate = cdce706_pll_round_rate, | |
255 | .set_rate = cdce706_pll_set_rate, | |
256 | }; | |
257 | ||
258 | static int cdce706_divider_set_parent(struct clk_hw *hw, u8 index) | |
259 | { | |
260 | struct cdce706_hw_data *hwd = to_hw_data(hw); | |
261 | ||
262 | if (hwd->parent == index) | |
263 | return 0; | |
264 | hwd->parent = index; | |
265 | return cdce706_reg_update(hwd->dev_data, | |
266 | CDCE706_DIVIDER_PLL(hwd->idx), | |
267 | CDCE706_DIVIDER_PLL_MASK(hwd->idx), | |
268 | index << CDCE706_DIVIDER_PLL_SHIFT(hwd->idx)); | |
269 | } | |
270 | ||
271 | static u8 cdce706_divider_get_parent(struct clk_hw *hw) | |
272 | { | |
273 | struct cdce706_hw_data *hwd = to_hw_data(hw); | |
274 | ||
275 | return hwd->parent; | |
276 | } | |
277 | ||
278 | static unsigned long cdce706_divider_recalc_rate(struct clk_hw *hw, | |
279 | unsigned long parent_rate) | |
280 | { | |
281 | struct cdce706_hw_data *hwd = to_hw_data(hw); | |
282 | ||
283 | dev_dbg(&hwd->dev_data->client->dev, | |
284 | "%s, divider: %d, div: %u\n", | |
285 | __func__, hwd->idx, hwd->div); | |
286 | if (hwd->div) | |
287 | return parent_rate / hwd->div; | |
288 | return 0; | |
289 | } | |
290 | ||
81fdf73a MR |
291 | static int cdce706_divider_determine_rate(struct clk_hw *hw, |
292 | struct clk_rate_request *req) | |
0c7665c3 MF |
293 | { |
294 | struct cdce706_hw_data *hwd = to_hw_data(hw); | |
295 | struct cdce706_dev_data *cdce = hwd->dev_data; | |
81fdf73a | 296 | unsigned long rate = req->rate; |
0c7665c3 MF |
297 | unsigned long mul, div; |
298 | ||
299 | dev_dbg(&hwd->dev_data->client->dev, | |
300 | "%s, rate: %lu, parent_rate: %lu\n", | |
81fdf73a | 301 | __func__, rate, req->best_parent_rate); |
0c7665c3 | 302 | |
81fdf73a | 303 | rational_best_approximation(rate, req->best_parent_rate, |
0c7665c3 MF |
304 | 1, CDCE706_DIVIDER_DIVIDER_MAX, |
305 | &mul, &div); | |
306 | if (!mul) | |
307 | div = CDCE706_DIVIDER_DIVIDER_MAX; | |
308 | ||
98d8a60e | 309 | if (clk_hw_get_flags(hw) & CLK_SET_RATE_PARENT) { |
0c7665c3 MF |
310 | unsigned long best_diff = rate; |
311 | unsigned long best_div = 0; | |
312 | struct clk *gp_clk = cdce->clkin_clk[cdce->clkin[0].parent]; | |
313 | unsigned long gp_rate = gp_clk ? clk_get_rate(gp_clk) : 0; | |
314 | ||
315 | for (div = CDCE706_PLL_FREQ_MIN / rate; best_diff && | |
316 | div <= CDCE706_PLL_FREQ_MAX / rate; ++div) { | |
317 | unsigned long n, m; | |
318 | unsigned long diff; | |
319 | unsigned long div_rate; | |
320 | u64 div_rate64; | |
321 | ||
322 | if (rate * div < CDCE706_PLL_FREQ_MIN) | |
323 | continue; | |
324 | ||
325 | rational_best_approximation(rate * div, gp_rate, | |
326 | CDCE706_PLL_N_MAX, | |
327 | CDCE706_PLL_M_MAX, | |
328 | &n, &m); | |
329 | div_rate64 = (u64)gp_rate * n; | |
330 | do_div(div_rate64, m); | |
331 | do_div(div_rate64, div); | |
332 | div_rate = div_rate64; | |
333 | diff = max(div_rate, rate) - min(div_rate, rate); | |
334 | ||
335 | if (diff < best_diff) { | |
336 | best_diff = diff; | |
337 | best_div = div; | |
338 | dev_dbg(&hwd->dev_data->client->dev, | |
339 | "%s, %lu * %lu / %lu / %lu = %lu\n", | |
340 | __func__, gp_rate, n, m, div, div_rate); | |
341 | } | |
342 | } | |
343 | ||
344 | div = best_div; | |
345 | ||
346 | dev_dbg(&hwd->dev_data->client->dev, | |
347 | "%s, altering parent rate: %lu -> %lu\n", | |
81fdf73a MR |
348 | __func__, req->best_parent_rate, rate * div); |
349 | req->best_parent_rate = rate * div; | |
0c7665c3 MF |
350 | } |
351 | hwd->div = div; | |
352 | ||
353 | dev_dbg(&hwd->dev_data->client->dev, | |
354 | "%s, divider: %d, div: %lu\n", | |
355 | __func__, hwd->idx, div); | |
356 | ||
81fdf73a MR |
357 | req->rate = req->best_parent_rate / div; |
358 | return 0; | |
0c7665c3 MF |
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, | |
81fdf73a | 380 | .determine_rate = cdce706_divider_determine_rate, |
0c7665c3 MF |
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 | ||
924418cb MR |
426 | static int cdce706_clkout_determine_rate(struct clk_hw *hw, |
427 | struct clk_rate_request *req) | |
0c7665c3 | 428 | { |
924418cb MR |
429 | req->best_parent_rate = req->rate; |
430 | ||
431 | return 0; | |
0c7665c3 MF |
432 | } |
433 | ||
434 | static int cdce706_clkout_set_rate(struct clk_hw *hw, unsigned long rate, | |
435 | unsigned long parent_rate) | |
436 | { | |
437 | return 0; | |
438 | } | |
439 | ||
440 | static const struct clk_ops cdce706_clkout_ops = { | |
441 | .prepare = cdce706_clkout_prepare, | |
442 | .unprepare = cdce706_clkout_unprepare, | |
443 | .set_parent = cdce706_clkout_set_parent, | |
444 | .get_parent = cdce706_clkout_get_parent, | |
445 | .recalc_rate = cdce706_clkout_recalc_rate, | |
924418cb | 446 | .determine_rate = cdce706_clkout_determine_rate, |
0c7665c3 MF |
447 | .set_rate = cdce706_clkout_set_rate, |
448 | }; | |
449 | ||
450 | static int cdce706_register_hw(struct cdce706_dev_data *cdce, | |
451 | struct cdce706_hw_data *hw, unsigned num_hw, | |
452 | const char * const *clk_names, | |
453 | struct clk_init_data *init) | |
454 | { | |
455 | unsigned i; | |
01b5200a | 456 | int ret; |
0c7665c3 MF |
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; | |
01b5200a | 463 | ret = devm_clk_hw_register(&cdce->client->dev, |
0c7665c3 | 464 | &hw->hw); |
01b5200a | 465 | if (ret) { |
0c7665c3 MF |
466 | dev_err(&cdce->client->dev, "Failed to register %s\n", |
467 | clk_names[i]); | |
01b5200a | 468 | return ret; |
0c7665c3 MF |
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 | ||
01b5200a SB |
615 | return cdce706_register_hw(cdce, cdce->clkout, |
616 | ARRAY_SIZE(cdce->clkout), | |
617 | cdce706_clkout_name, &init); | |
618 | } | |
0c7665c3 | 619 | |
01b5200a SB |
620 | static struct clk_hw * |
621 | of_clk_cdce_get(struct of_phandle_args *clkspec, void *data) | |
622 | { | |
623 | struct cdce706_dev_data *cdce = data; | |
624 | unsigned int idx = clkspec->args[0]; | |
625 | ||
626 | if (idx >= ARRAY_SIZE(cdce->clkout)) { | |
627 | pr_err("%s: invalid index %u\n", __func__, idx); | |
628 | return ERR_PTR(-EINVAL); | |
629 | } | |
630 | ||
631 | return &cdce->clkout[idx].hw; | |
0c7665c3 MF |
632 | } |
633 | ||
f9edf134 | 634 | static int cdce706_probe(struct i2c_client *client) |
0c7665c3 | 635 | { |
df095f99 | 636 | struct i2c_adapter *adapter = client->adapter; |
0c7665c3 MF |
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; | |
c2e59c7f LPC |
668 | return devm_of_clk_add_hw_provider(&client->dev, of_clk_cdce_get, |
669 | cdce); | |
0c7665c3 MF |
670 | } |
671 | ||
0c7665c3 MF |
672 | #ifdef CONFIG_OF |
673 | static const struct of_device_id cdce706_dt_match[] = { | |
674 | { .compatible = "ti,cdce706" }, | |
675 | { }, | |
676 | }; | |
677 | MODULE_DEVICE_TABLE(of, cdce706_dt_match); | |
678 | #endif | |
679 | ||
680 | static const struct i2c_device_id cdce706_id[] = { | |
681 | { "cdce706", 0 }, | |
682 | { } | |
683 | }; | |
684 | MODULE_DEVICE_TABLE(i2c, cdce706_id); | |
685 | ||
686 | static struct i2c_driver cdce706_i2c_driver = { | |
687 | .driver = { | |
688 | .name = "cdce706", | |
689 | .of_match_table = of_match_ptr(cdce706_dt_match), | |
690 | }, | |
62279db5 | 691 | .probe = cdce706_probe, |
0c7665c3 MF |
692 | .id_table = cdce706_id, |
693 | }; | |
694 | module_i2c_driver(cdce706_i2c_driver); | |
695 | ||
696 | MODULE_AUTHOR("Max Filippov <jcmvbkbc@gmail.com>"); | |
697 | MODULE_DESCRIPTION("TI CDCE 706 clock synthesizer driver"); | |
698 | MODULE_LICENSE("GPL"); |