1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * Common clock framework driver for the Versaclock7 family of timing devices. |
4 | * |
5 | * Copyright (c) 2022 Renesas Electronics Corporation |
6 | */ |
7 | |
8 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt |
9 | |
10 | #include <linux/bitfield.h> |
11 | #include <linux/clk.h> |
12 | #include <linux/clk-provider.h> |
13 | #include <linux/i2c.h> |
14 | #include <linux/math64.h> |
15 | #include <linux/module.h> |
16 | #include <linux/of.h> |
17 | #include <linux/property.h> |
18 | #include <linux/regmap.h> |
19 | #include <linux/swab.h> |
20 | |
21 | /* |
22 | * 16-bit register address: the lower 8 bits of the register address come |
23 | * from the offset addr byte and the upper 8 bits come from the page register. |
24 | */ |
25 | #define VC7_PAGE_ADDR 0xFD |
26 | #define VC7_PAGE_WINDOW 256 |
27 | #define VC7_MAX_REG 0x364 |
28 | |
29 | /* Maximum number of banks supported by VC7 */ |
30 | #define VC7_NUM_BANKS 7 |
31 | |
32 | /* Maximum number of FODs supported by VC7 */ |
33 | #define VC7_NUM_FOD 3 |
34 | |
35 | /* Maximum number of IODs supported by VC7 */ |
36 | #define VC7_NUM_IOD 4 |
37 | |
38 | /* Maximum number of outputs supported by VC7 */ |
39 | #define VC7_NUM_OUT 12 |
40 | |
41 | /* VCO valid range is 9.5 GHz to 10.7 GHz */ |
42 | #define VC7_APLL_VCO_MIN 9500000000UL |
43 | #define VC7_APLL_VCO_MAX 10700000000UL |
44 | |
45 | /* APLL denominator is fixed at 2^27 */ |
46 | #define VC7_APLL_DENOMINATOR_BITS 27 |
47 | |
48 | /* FOD 1st stage denominator is fixed 2^34 */ |
49 | #define VC7_FOD_DENOMINATOR_BITS 34 |
50 | |
51 | /* IOD can operate between 1kHz and 650MHz */ |
52 | #define VC7_IOD_RATE_MIN 1000UL |
53 | #define VC7_IOD_RATE_MAX 650000000UL |
54 | #define VC7_IOD_MIN_DIVISOR 14 |
55 | #define VC7_IOD_MAX_DIVISOR 0x1ffffff /* 25-bit */ |
56 | |
57 | #define VC7_FOD_RATE_MIN 1000UL |
58 | #define VC7_FOD_RATE_MAX 650000000UL |
59 | #define VC7_FOD_1ST_STAGE_RATE_MIN 33000000UL /* 33 MHz */ |
60 | #define VC7_FOD_1ST_STAGE_RATE_MAX 650000000UL /* 650 MHz */ |
61 | #define VC7_FOD_1ST_INT_MAX 324 |
62 | #define VC7_FOD_2ND_INT_MIN 2 |
63 | #define VC7_FOD_2ND_INT_MAX 0x1ffff /* 17-bit */ |
64 | |
65 | /* VC7 Registers */ |
66 | |
67 | #define VC7_REG_XO_CNFG 0x2C |
68 | #define VC7_REG_XO_CNFG_COUNT 4 |
69 | #define VC7_REG_XO_IB_H_DIV_SHIFT 24 |
70 | #define VC7_REG_XO_IB_H_DIV_MASK GENMASK(28, VC7_REG_XO_IB_H_DIV_SHIFT) |
71 | |
72 | #define VC7_REG_APLL_FB_DIV_FRAC 0x120 |
73 | #define VC7_REG_APLL_FB_DIV_FRAC_COUNT 4 |
74 | #define VC7_REG_APLL_FB_DIV_FRAC_MASK GENMASK(26, 0) |
75 | |
76 | #define VC7_REG_APLL_FB_DIV_INT 0x124 |
77 | #define VC7_REG_APLL_FB_DIV_INT_COUNT 2 |
78 | #define VC7_REG_APLL_FB_DIV_INT_MASK GENMASK(9, 0) |
79 | |
80 | #define VC7_REG_APLL_CNFG 0x127 |
81 | #define VC7_REG_APLL_EN_DOUBLER BIT(0) |
82 | |
83 | #define VC7_REG_OUT_BANK_CNFG(idx) (0x280 + (0x4 * (idx))) |
84 | #define VC7_REG_OUTPUT_BANK_SRC_MASK GENMASK(2, 0) |
85 | |
86 | #define VC7_REG_FOD_INT_CNFG(idx) (0x1E0 + (0x10 * (idx))) |
87 | #define VC7_REG_FOD_INT_CNFG_COUNT 8 |
88 | #define VC7_REG_FOD_1ST_INT_MASK GENMASK(8, 0) |
89 | #define VC7_REG_FOD_2ND_INT_SHIFT 9 |
90 | #define VC7_REG_FOD_2ND_INT_MASK GENMASK(25, VC7_REG_FOD_2ND_INT_SHIFT) |
91 | #define VC7_REG_FOD_FRAC_SHIFT 26 |
92 | #define VC7_REG_FOD_FRAC_MASK GENMASK_ULL(59, VC7_REG_FOD_FRAC_SHIFT) |
93 | |
94 | #define VC7_REG_IOD_INT_CNFG(idx) (0x1C0 + (0x8 * (idx))) |
95 | #define VC7_REG_IOD_INT_CNFG_COUNT 4 |
96 | #define VC7_REG_IOD_INT_MASK GENMASK(24, 0) |
97 | |
98 | #define VC7_REG_ODRV_EN(idx) (0x240 + (0x4 * (idx))) |
99 | #define VC7_REG_OUT_DIS BIT(0) |
100 | |
101 | struct vc7_driver_data; |
102 | static const struct regmap_config vc7_regmap_config; |
103 | |
104 | /* Supported Renesas VC7 models */ |
105 | enum vc7_model { |
106 | VC7_RC21008A, |
107 | }; |
108 | |
109 | struct vc7_chip_info { |
110 | const enum vc7_model model; |
111 | const unsigned int banks[VC7_NUM_BANKS]; |
112 | const unsigned int num_banks; |
113 | const unsigned int outputs[VC7_NUM_OUT]; |
114 | const unsigned int num_outputs; |
115 | }; |
116 | |
117 | /* |
118 | * Changing the APLL frequency is currently not supported. |
119 | * The APLL will consist of an opaque block between the XO and FOD/IODs and |
120 | * its frequency will be computed based on the current state of the device. |
121 | */ |
122 | struct vc7_apll_data { |
123 | struct clk *clk; |
124 | struct vc7_driver_data *vc7; |
125 | u8 xo_ib_h_div; |
126 | u8 en_doubler; |
127 | u16 apll_fb_div_int; |
128 | u32 apll_fb_div_frac; |
129 | }; |
130 | |
131 | struct vc7_fod_data { |
132 | struct clk_hw hw; |
133 | struct vc7_driver_data *vc7; |
134 | unsigned int num; |
135 | u32 fod_1st_int; |
136 | u32 fod_2nd_int; |
137 | u64 fod_frac; |
138 | }; |
139 | |
140 | struct vc7_iod_data { |
141 | struct clk_hw hw; |
142 | struct vc7_driver_data *vc7; |
143 | unsigned int num; |
144 | u32 iod_int; |
145 | }; |
146 | |
147 | struct vc7_out_data { |
148 | struct clk_hw hw; |
149 | struct vc7_driver_data *vc7; |
150 | unsigned int num; |
151 | unsigned int out_dis; |
152 | }; |
153 | |
154 | struct vc7_driver_data { |
155 | struct i2c_client *client; |
156 | struct regmap *regmap; |
157 | const struct vc7_chip_info *chip_info; |
158 | |
159 | struct clk *pin_xin; |
160 | struct vc7_apll_data clk_apll; |
161 | struct vc7_fod_data clk_fod[VC7_NUM_FOD]; |
162 | struct vc7_iod_data clk_iod[VC7_NUM_IOD]; |
163 | struct vc7_out_data clk_out[VC7_NUM_OUT]; |
164 | }; |
165 | |
166 | struct vc7_bank_src_map { |
167 | enum vc7_bank_src_type { |
168 | VC7_FOD, |
169 | VC7_IOD, |
170 | } type; |
171 | union _divider { |
172 | struct vc7_iod_data *iod; |
173 | struct vc7_fod_data *fod; |
174 | } src; |
175 | }; |
176 | |
177 | static struct clk_hw *vc7_of_clk_get(struct of_phandle_args *clkspec, |
178 | void *data) |
179 | { |
180 | struct vc7_driver_data *vc7 = data; |
181 | unsigned int idx = clkspec->args[0]; |
182 | |
183 | if (idx >= vc7->chip_info->num_outputs) |
184 | return ERR_PTR(error: -EINVAL); |
185 | |
186 | return &vc7->clk_out[idx].hw; |
187 | } |
188 | |
189 | static const unsigned int RC21008A_index_to_output_mapping[] = { |
190 | 1, 2, 3, 6, 7, 8, 10, 11 |
191 | }; |
192 | |
193 | static int vc7_map_index_to_output(const enum vc7_model model, const unsigned int i) |
194 | { |
195 | switch (model) { |
196 | case VC7_RC21008A: |
197 | return RC21008A_index_to_output_mapping[i]; |
198 | default: |
199 | return i; |
200 | } |
201 | } |
202 | |
203 | /* bank to output mapping, same across all variants */ |
204 | static const unsigned int output_bank_mapping[] = { |
205 | 0, /* Output 0 */ |
206 | 1, /* Output 1 */ |
207 | 2, /* Output 2 */ |
208 | 2, /* Output 3 */ |
209 | 3, /* Output 4 */ |
210 | 3, /* Output 5 */ |
211 | 3, /* Output 6 */ |
212 | 3, /* Output 7 */ |
213 | 4, /* Output 8 */ |
214 | 4, /* Output 9 */ |
215 | 5, /* Output 10 */ |
216 | 6 /* Output 11 */ |
217 | }; |
218 | |
219 | /** |
220 | * vc7_64_mul_64_to_128() - Multiply two u64 and return an unsigned 128-bit integer |
221 | * as an upper and lower part. |
222 | * |
223 | * @left: The left argument. |
224 | * @right: The right argument. |
225 | * @hi: The upper 64-bits of the 128-bit product. |
226 | * @lo: The lower 64-bits of the 128-bit product. |
227 | * |
228 | * From mul_64_64 in crypto/ecc.c:350 in the linux kernel, accessed in v5.17.2. |
229 | */ |
230 | static void vc7_64_mul_64_to_128(u64 left, u64 right, u64 *hi, u64 *lo) |
231 | { |
232 | u64 a0 = left & 0xffffffffull; |
233 | u64 a1 = left >> 32; |
234 | u64 b0 = right & 0xffffffffull; |
235 | u64 b1 = right >> 32; |
236 | u64 m0 = a0 * b0; |
237 | u64 m1 = a0 * b1; |
238 | u64 m2 = a1 * b0; |
239 | u64 m3 = a1 * b1; |
240 | |
241 | m2 += (m0 >> 32); |
242 | m2 += m1; |
243 | |
244 | /* Overflow */ |
245 | if (m2 < m1) |
246 | m3 += 0x100000000ull; |
247 | |
248 | *lo = (m0 & 0xffffffffull) | (m2 << 32); |
249 | *hi = m3 + (m2 >> 32); |
250 | } |
251 | |
252 | /** |
253 | * vc7_128_div_64_to_64() - Divides a 128-bit uint by a 64-bit divisor, return a 64-bit quotient. |
254 | * |
255 | * @numhi: The uppper 64-bits of the dividend. |
256 | * @numlo: The lower 64-bits of the dividend. |
257 | * @den: The denominator (divisor). |
258 | * @r: The remainder, pass NULL if the remainder is not needed. |
259 | * |
260 | * Originally from libdivide, modified to use kernel u64/u32 types. |
261 | * |
262 | * See https://github.com/ridiculousfish/libdivide/blob/master/libdivide.h#L471. |
263 | * |
264 | * Return: The 64-bit quotient of the division. |
265 | * |
266 | * In case of overflow of division by zero, max(u64) is returned. |
267 | */ |
268 | static u64 vc7_128_div_64_to_64(u64 numhi, u64 numlo, u64 den, u64 *r) |
269 | { |
270 | /* |
271 | * We work in base 2**32. |
272 | * A uint32 holds a single digit. A uint64 holds two digits. |
273 | * Our numerator is conceptually [num3, num2, num1, num0]. |
274 | * Our denominator is [den1, den0]. |
275 | */ |
276 | const u64 b = ((u64)1 << 32); |
277 | |
278 | /* The high and low digits of our computed quotient. */ |
279 | u32 q1, q0; |
280 | |
281 | /* The normalization shift factor */ |
282 | int shift; |
283 | |
284 | /* |
285 | * The high and low digits of our denominator (after normalizing). |
286 | * Also the low 2 digits of our numerator (after normalizing). |
287 | */ |
288 | u32 den1, den0, num1, num0; |
289 | |
290 | /* A partial remainder; */ |
291 | u64 rem; |
292 | |
293 | /* |
294 | * The estimated quotient, and its corresponding remainder (unrelated |
295 | * to true remainder). |
296 | */ |
297 | u64 qhat, rhat; |
298 | |
299 | /* Variables used to correct the estimated quotient. */ |
300 | u64 c1, c2; |
301 | |
302 | /* Check for overflow and divide by 0. */ |
303 | if (numhi >= den) { |
304 | if (r) |
305 | *r = ~0ull; |
306 | return ~0ull; |
307 | } |
308 | |
309 | /* |
310 | * Determine the normalization factor. We multiply den by this, so that |
311 | * its leading digit is at least half b. In binary this means just |
312 | * shifting left by the number of leading zeros, so that there's a 1 in |
313 | * the MSB. |
314 | * |
315 | * We also shift numer by the same amount. This cannot overflow because |
316 | * numhi < den. The expression (-shift & 63) is the same as (64 - |
317 | * shift), except it avoids the UB of shifting by 64. The funny bitwise |
318 | * 'and' ensures that numlo does not get shifted into numhi if shift is |
319 | * 0. clang 11 has an x86 codegen bug here: see LLVM bug 50118. The |
320 | * sequence below avoids it. |
321 | */ |
322 | shift = __builtin_clzll(den); |
323 | den <<= shift; |
324 | numhi <<= shift; |
325 | numhi |= (numlo >> (-shift & 63)) & (-(s64)shift >> 63); |
326 | numlo <<= shift; |
327 | |
328 | /* |
329 | * Extract the low digits of the numerator and both digits of the |
330 | * denominator. |
331 | */ |
332 | num1 = (u32)(numlo >> 32); |
333 | num0 = (u32)(numlo & 0xFFFFFFFFu); |
334 | den1 = (u32)(den >> 32); |
335 | den0 = (u32)(den & 0xFFFFFFFFu); |
336 | |
337 | /* |
338 | * We wish to compute q1 = [n3 n2 n1] / [d1 d0]. |
339 | * Estimate q1 as [n3 n2] / [d1], and then correct it. |
340 | * Note while qhat may be 2 digits, q1 is always 1 digit. |
341 | */ |
342 | qhat = div64_u64_rem(dividend: numhi, divisor: den1, remainder: &rhat); |
343 | c1 = qhat * den0; |
344 | c2 = rhat * b + num1; |
345 | if (c1 > c2) |
346 | qhat -= (c1 - c2 > den) ? 2 : 1; |
347 | q1 = (u32)qhat; |
348 | |
349 | /* Compute the true (partial) remainder. */ |
350 | rem = numhi * b + num1 - q1 * den; |
351 | |
352 | /* |
353 | * We wish to compute q0 = [rem1 rem0 n0] / [d1 d0]. |
354 | * Estimate q0 as [rem1 rem0] / [d1] and correct it. |
355 | */ |
356 | qhat = div64_u64_rem(dividend: rem, divisor: den1, remainder: &rhat); |
357 | c1 = qhat * den0; |
358 | c2 = rhat * b + num0; |
359 | if (c1 > c2) |
360 | qhat -= (c1 - c2 > den) ? 2 : 1; |
361 | q0 = (u32)qhat; |
362 | |
363 | /* Return remainder if requested. */ |
364 | if (r) |
365 | *r = (rem * b + num0 - q0 * den) >> shift; |
366 | return ((u64)q1 << 32) | q0; |
367 | } |
368 | |
369 | static int vc7_get_bank_clk(struct vc7_driver_data *vc7, |
370 | unsigned int bank_idx, |
371 | unsigned int output_bank_src, |
372 | struct vc7_bank_src_map *map) |
373 | { |
374 | /* Mapping from Table 38 in datasheet */ |
375 | if (bank_idx == 0 || bank_idx == 1) { |
376 | switch (output_bank_src) { |
377 | case 0: |
378 | map->type = VC7_IOD, |
379 | map->src.iod = &vc7->clk_iod[0]; |
380 | return 0; |
381 | case 1: |
382 | map->type = VC7_IOD, |
383 | map->src.iod = &vc7->clk_iod[1]; |
384 | return 0; |
385 | case 4: |
386 | map->type = VC7_FOD, |
387 | map->src.fod = &vc7->clk_fod[0]; |
388 | return 0; |
389 | case 5: |
390 | map->type = VC7_FOD, |
391 | map->src.fod = &vc7->clk_fod[1]; |
392 | return 0; |
393 | default: |
394 | break; |
395 | } |
396 | } else if (bank_idx == 2) { |
397 | switch (output_bank_src) { |
398 | case 1: |
399 | map->type = VC7_IOD, |
400 | map->src.iod = &vc7->clk_iod[1]; |
401 | return 0; |
402 | case 4: |
403 | map->type = VC7_FOD, |
404 | map->src.fod = &vc7->clk_fod[0]; |
405 | return 0; |
406 | case 5: |
407 | map->type = VC7_FOD, |
408 | map->src.fod = &vc7->clk_fod[1]; |
409 | return 0; |
410 | default: |
411 | break; |
412 | } |
413 | } else if (bank_idx == 3) { |
414 | switch (output_bank_src) { |
415 | case 4: |
416 | map->type = VC7_FOD, |
417 | map->src.fod = &vc7->clk_fod[0]; |
418 | return 0; |
419 | case 5: |
420 | map->type = VC7_FOD, |
421 | map->src.fod = &vc7->clk_fod[1]; |
422 | return 0; |
423 | case 6: |
424 | map->type = VC7_FOD, |
425 | map->src.fod = &vc7->clk_fod[2]; |
426 | return 0; |
427 | default: |
428 | break; |
429 | } |
430 | } else if (bank_idx == 4) { |
431 | switch (output_bank_src) { |
432 | case 0: |
433 | /* CLKIN1 not supported in this driver */ |
434 | break; |
435 | case 2: |
436 | map->type = VC7_IOD, |
437 | map->src.iod = &vc7->clk_iod[2]; |
438 | return 0; |
439 | case 5: |
440 | map->type = VC7_FOD, |
441 | map->src.fod = &vc7->clk_fod[1]; |
442 | return 0; |
443 | case 6: |
444 | map->type = VC7_FOD, |
445 | map->src.fod = &vc7->clk_fod[2]; |
446 | return 0; |
447 | case 7: |
448 | /* CLKIN0 not supported in this driver */ |
449 | break; |
450 | default: |
451 | break; |
452 | } |
453 | } else if (bank_idx == 5) { |
454 | switch (output_bank_src) { |
455 | case 0: |
456 | /* CLKIN1 not supported in this driver */ |
457 | break; |
458 | case 1: |
459 | /* XIN_REFIN not supported in this driver */ |
460 | break; |
461 | case 2: |
462 | map->type = VC7_IOD, |
463 | map->src.iod = &vc7->clk_iod[2]; |
464 | return 0; |
465 | case 3: |
466 | map->type = VC7_IOD, |
467 | map->src.iod = &vc7->clk_iod[3]; |
468 | return 0; |
469 | case 5: |
470 | map->type = VC7_FOD, |
471 | map->src.fod = &vc7->clk_fod[1]; |
472 | return 0; |
473 | case 6: |
474 | map->type = VC7_FOD, |
475 | map->src.fod = &vc7->clk_fod[2]; |
476 | return 0; |
477 | case 7: |
478 | /* CLKIN0 not supported in this driver */ |
479 | break; |
480 | default: |
481 | break; |
482 | } |
483 | } else if (bank_idx == 6) { |
484 | switch (output_bank_src) { |
485 | case 0: |
486 | /* CLKIN1 not supported in this driver */ |
487 | break; |
488 | case 2: |
489 | map->type = VC7_IOD, |
490 | map->src.iod = &vc7->clk_iod[2]; |
491 | return 0; |
492 | case 3: |
493 | map->type = VC7_IOD, |
494 | map->src.iod = &vc7->clk_iod[3]; |
495 | return 0; |
496 | case 5: |
497 | map->type = VC7_FOD, |
498 | map->src.fod = &vc7->clk_fod[1]; |
499 | return 0; |
500 | case 6: |
501 | map->type = VC7_FOD, |
502 | map->src.fod = &vc7->clk_fod[2]; |
503 | return 0; |
504 | case 7: |
505 | /* CLKIN0 not supported in this driver */ |
506 | break; |
507 | default: |
508 | break; |
509 | } |
510 | } |
511 | |
512 | pr_warn("bank_src%d = %d is not supported\n" , bank_idx, output_bank_src); |
513 | return -1; |
514 | } |
515 | |
516 | static int vc7_read_apll(struct vc7_driver_data *vc7) |
517 | { |
518 | int err; |
519 | u32 val32; |
520 | u16 val16; |
521 | |
522 | err = regmap_bulk_read(map: vc7->regmap, |
523 | VC7_REG_XO_CNFG, |
524 | val: (u32 *)&val32, |
525 | VC7_REG_XO_CNFG_COUNT); |
526 | if (err) { |
527 | dev_err(&vc7->client->dev, "failed to read XO_CNFG\n" ); |
528 | return err; |
529 | } |
530 | |
531 | vc7->clk_apll.xo_ib_h_div = (val32 & VC7_REG_XO_IB_H_DIV_MASK) |
532 | >> VC7_REG_XO_IB_H_DIV_SHIFT; |
533 | |
534 | err = regmap_read(map: vc7->regmap, |
535 | VC7_REG_APLL_CNFG, |
536 | val: &val32); |
537 | if (err) { |
538 | dev_err(&vc7->client->dev, "failed to read APLL_CNFG\n" ); |
539 | return err; |
540 | } |
541 | |
542 | vc7->clk_apll.en_doubler = val32 & VC7_REG_APLL_EN_DOUBLER; |
543 | |
544 | err = regmap_bulk_read(map: vc7->regmap, |
545 | VC7_REG_APLL_FB_DIV_FRAC, |
546 | val: (u32 *)&val32, |
547 | VC7_REG_APLL_FB_DIV_FRAC_COUNT); |
548 | if (err) { |
549 | dev_err(&vc7->client->dev, "failed to read APLL_FB_DIV_FRAC\n" ); |
550 | return err; |
551 | } |
552 | |
553 | vc7->clk_apll.apll_fb_div_frac = val32 & VC7_REG_APLL_FB_DIV_FRAC_MASK; |
554 | |
555 | err = regmap_bulk_read(map: vc7->regmap, |
556 | VC7_REG_APLL_FB_DIV_INT, |
557 | val: (u16 *)&val16, |
558 | VC7_REG_APLL_FB_DIV_INT_COUNT); |
559 | if (err) { |
560 | dev_err(&vc7->client->dev, "failed to read APLL_FB_DIV_INT\n" ); |
561 | return err; |
562 | } |
563 | |
564 | vc7->clk_apll.apll_fb_div_int = val16 & VC7_REG_APLL_FB_DIV_INT_MASK; |
565 | |
566 | return 0; |
567 | } |
568 | |
569 | static int vc7_read_fod(struct vc7_driver_data *vc7, unsigned int idx) |
570 | { |
571 | int err; |
572 | u64 val; |
573 | |
574 | err = regmap_bulk_read(map: vc7->regmap, |
575 | VC7_REG_FOD_INT_CNFG(idx), |
576 | val: (u64 *)&val, |
577 | VC7_REG_FOD_INT_CNFG_COUNT); |
578 | if (err) { |
579 | dev_err(&vc7->client->dev, "failed to read FOD%d\n" , idx); |
580 | return err; |
581 | } |
582 | |
583 | vc7->clk_fod[idx].fod_1st_int = (val & VC7_REG_FOD_1ST_INT_MASK); |
584 | vc7->clk_fod[idx].fod_2nd_int = |
585 | (val & VC7_REG_FOD_2ND_INT_MASK) >> VC7_REG_FOD_2ND_INT_SHIFT; |
586 | vc7->clk_fod[idx].fod_frac = (val & VC7_REG_FOD_FRAC_MASK) |
587 | >> VC7_REG_FOD_FRAC_SHIFT; |
588 | |
589 | return 0; |
590 | } |
591 | |
592 | static int vc7_write_fod(struct vc7_driver_data *vc7, unsigned int idx) |
593 | { |
594 | int err; |
595 | u64 val; |
596 | |
597 | /* |
598 | * FOD dividers are part of an atomic group where fod_1st_int, |
599 | * fod_2nd_int, and fod_frac must be written together. The new divider |
600 | * is applied when the MSB of fod_frac is written. |
601 | */ |
602 | |
603 | err = regmap_bulk_read(map: vc7->regmap, |
604 | VC7_REG_FOD_INT_CNFG(idx), |
605 | val: (u64 *)&val, |
606 | VC7_REG_FOD_INT_CNFG_COUNT); |
607 | if (err) { |
608 | dev_err(&vc7->client->dev, "failed to read FOD%d\n" , idx); |
609 | return err; |
610 | } |
611 | |
612 | val = u64_replace_bits(old: val, |
613 | val: vc7->clk_fod[idx].fod_1st_int, |
614 | VC7_REG_FOD_1ST_INT_MASK); |
615 | val = u64_replace_bits(old: val, |
616 | val: vc7->clk_fod[idx].fod_2nd_int, |
617 | VC7_REG_FOD_2ND_INT_MASK); |
618 | val = u64_replace_bits(old: val, |
619 | val: vc7->clk_fod[idx].fod_frac, |
620 | VC7_REG_FOD_FRAC_MASK); |
621 | |
622 | err = regmap_bulk_write(map: vc7->regmap, |
623 | VC7_REG_FOD_INT_CNFG(idx), |
624 | val: (u64 *)&val, |
625 | val_count: sizeof(u64)); |
626 | if (err) { |
627 | dev_err(&vc7->client->dev, "failed to write FOD%d\n" , idx); |
628 | return err; |
629 | } |
630 | |
631 | return 0; |
632 | } |
633 | |
634 | static int vc7_read_iod(struct vc7_driver_data *vc7, unsigned int idx) |
635 | { |
636 | int err; |
637 | u32 val; |
638 | |
639 | err = regmap_bulk_read(map: vc7->regmap, |
640 | VC7_REG_IOD_INT_CNFG(idx), |
641 | val: (u32 *)&val, |
642 | VC7_REG_IOD_INT_CNFG_COUNT); |
643 | if (err) { |
644 | dev_err(&vc7->client->dev, "failed to read IOD%d\n" , idx); |
645 | return err; |
646 | } |
647 | |
648 | vc7->clk_iod[idx].iod_int = (val & VC7_REG_IOD_INT_MASK); |
649 | |
650 | return 0; |
651 | } |
652 | |
653 | static int vc7_write_iod(struct vc7_driver_data *vc7, unsigned int idx) |
654 | { |
655 | int err; |
656 | u32 val; |
657 | |
658 | /* |
659 | * IOD divider field is atomic and all bits must be written. |
660 | * The new divider is applied when the MSB of iod_int is written. |
661 | */ |
662 | |
663 | err = regmap_bulk_read(map: vc7->regmap, |
664 | VC7_REG_IOD_INT_CNFG(idx), |
665 | val: (u32 *)&val, |
666 | VC7_REG_IOD_INT_CNFG_COUNT); |
667 | if (err) { |
668 | dev_err(&vc7->client->dev, "failed to read IOD%d\n" , idx); |
669 | return err; |
670 | } |
671 | |
672 | val = u32_replace_bits(old: val, |
673 | val: vc7->clk_iod[idx].iod_int, |
674 | VC7_REG_IOD_INT_MASK); |
675 | |
676 | err = regmap_bulk_write(map: vc7->regmap, |
677 | VC7_REG_IOD_INT_CNFG(idx), |
678 | val: (u32 *)&val, |
679 | val_count: sizeof(u32)); |
680 | if (err) { |
681 | dev_err(&vc7->client->dev, "failed to write IOD%d\n" , idx); |
682 | return err; |
683 | } |
684 | |
685 | return 0; |
686 | } |
687 | |
688 | static int vc7_read_output(struct vc7_driver_data *vc7, unsigned int idx) |
689 | { |
690 | int err; |
691 | unsigned int val, out_num; |
692 | |
693 | out_num = vc7_map_index_to_output(model: vc7->chip_info->model, i: idx); |
694 | err = regmap_read(map: vc7->regmap, |
695 | VC7_REG_ODRV_EN(out_num), |
696 | val: &val); |
697 | if (err) { |
698 | dev_err(&vc7->client->dev, "failed to read ODRV_EN[%d]\n" , idx); |
699 | return err; |
700 | } |
701 | |
702 | vc7->clk_out[idx].out_dis = val & VC7_REG_OUT_DIS; |
703 | |
704 | return 0; |
705 | } |
706 | |
707 | static int vc7_write_output(struct vc7_driver_data *vc7, unsigned int idx) |
708 | { |
709 | int err; |
710 | unsigned int out_num; |
711 | |
712 | out_num = vc7_map_index_to_output(model: vc7->chip_info->model, i: idx); |
713 | err = regmap_write_bits(map: vc7->regmap, |
714 | VC7_REG_ODRV_EN(out_num), |
715 | VC7_REG_OUT_DIS, |
716 | val: vc7->clk_out[idx].out_dis); |
717 | |
718 | if (err) { |
719 | dev_err(&vc7->client->dev, "failed to write ODRV_EN[%d]\n" , idx); |
720 | return err; |
721 | } |
722 | |
723 | return 0; |
724 | } |
725 | |
726 | static unsigned long vc7_get_apll_rate(struct vc7_driver_data *vc7) |
727 | { |
728 | int err; |
729 | unsigned long xtal_rate; |
730 | u64 refin_div, apll_rate; |
731 | |
732 | xtal_rate = clk_get_rate(clk: vc7->pin_xin); |
733 | err = vc7_read_apll(vc7); |
734 | if (err) { |
735 | dev_err(&vc7->client->dev, "unable to read apll\n" ); |
736 | return err; |
737 | } |
738 | |
739 | /* 0 is bypassed, 1 is reserved */ |
740 | if (vc7->clk_apll.xo_ib_h_div < 2) |
741 | refin_div = xtal_rate; |
742 | else |
743 | refin_div = div64_u64(dividend: xtal_rate, divisor: vc7->clk_apll.xo_ib_h_div); |
744 | |
745 | if (vc7->clk_apll.en_doubler) |
746 | refin_div *= 2; |
747 | |
748 | /* divider = int + (frac / 2^27) */ |
749 | apll_rate = (refin_div * vc7->clk_apll.apll_fb_div_int) + |
750 | ((refin_div * vc7->clk_apll.apll_fb_div_frac) >> VC7_APLL_DENOMINATOR_BITS); |
751 | |
752 | pr_debug("%s - xo_ib_h_div: %u, apll_fb_div_int: %u, apll_fb_div_frac: %u\n" , |
753 | __func__, vc7->clk_apll.xo_ib_h_div, vc7->clk_apll.apll_fb_div_int, |
754 | vc7->clk_apll.apll_fb_div_frac); |
755 | pr_debug("%s - refin_div: %llu, apll rate: %llu\n" , |
756 | __func__, refin_div, apll_rate); |
757 | |
758 | return apll_rate; |
759 | } |
760 | |
761 | static void vc7_calc_iod_divider(unsigned long rate, unsigned long parent_rate, |
762 | u32 *divider) |
763 | { |
764 | *divider = DIV_ROUND_UP(parent_rate, rate); |
765 | if (*divider < VC7_IOD_MIN_DIVISOR) |
766 | *divider = VC7_IOD_MIN_DIVISOR; |
767 | if (*divider > VC7_IOD_MAX_DIVISOR) |
768 | *divider = VC7_IOD_MAX_DIVISOR; |
769 | } |
770 | |
771 | static void vc7_calc_fod_1st_stage(unsigned long rate, unsigned long parent_rate, |
772 | u32 *div_int, u64 *div_frac) |
773 | { |
774 | u64 rem; |
775 | |
776 | *div_int = (u32)div64_u64_rem(dividend: parent_rate, divisor: rate, remainder: &rem); |
777 | *div_frac = div64_u64(dividend: rem << VC7_FOD_DENOMINATOR_BITS, divisor: rate); |
778 | } |
779 | |
780 | static unsigned long vc7_calc_fod_1st_stage_rate(unsigned long parent_rate, |
781 | u32 fod_1st_int, u64 fod_frac) |
782 | { |
783 | u64 numer, denom, hi, lo, divisor; |
784 | |
785 | numer = fod_frac; |
786 | denom = BIT_ULL(VC7_FOD_DENOMINATOR_BITS); |
787 | |
788 | if (fod_frac) { |
789 | vc7_64_mul_64_to_128(left: parent_rate, right: denom, hi: &hi, lo: &lo); |
790 | divisor = ((u64)fod_1st_int * denom) + numer; |
791 | return vc7_128_div_64_to_64(numhi: hi, numlo: lo, den: divisor, NULL); |
792 | } |
793 | |
794 | return div64_u64(dividend: parent_rate, divisor: fod_1st_int); |
795 | } |
796 | |
797 | static unsigned long vc7_calc_fod_2nd_stage_rate(unsigned long parent_rate, |
798 | u32 fod_1st_int, u32 fod_2nd_int, u64 fod_frac) |
799 | { |
800 | unsigned long fod_1st_stage_rate; |
801 | |
802 | fod_1st_stage_rate = vc7_calc_fod_1st_stage_rate(parent_rate, fod_1st_int, fod_frac); |
803 | |
804 | if (fod_2nd_int < 2) |
805 | return fod_1st_stage_rate; |
806 | |
807 | /* |
808 | * There is a div-by-2 preceding the 2nd stage integer divider |
809 | * (not shown on block diagram) so the actual 2nd stage integer |
810 | * divisor is 2 * N. |
811 | */ |
812 | return div64_u64(dividend: fod_1st_stage_rate >> 1, divisor: fod_2nd_int); |
813 | } |
814 | |
815 | static void vc7_calc_fod_divider(unsigned long rate, unsigned long parent_rate, |
816 | u32 *fod_1st_int, u32 *fod_2nd_int, u64 *fod_frac) |
817 | { |
818 | unsigned int allow_frac, i, best_frac_i; |
819 | unsigned long first_stage_rate; |
820 | |
821 | vc7_calc_fod_1st_stage(rate, parent_rate, div_int: fod_1st_int, div_frac: fod_frac); |
822 | first_stage_rate = vc7_calc_fod_1st_stage_rate(parent_rate, fod_1st_int: *fod_1st_int, fod_frac: *fod_frac); |
823 | |
824 | *fod_2nd_int = 0; |
825 | |
826 | /* Do we need the second stage integer divider? */ |
827 | if (first_stage_rate < VC7_FOD_1ST_STAGE_RATE_MIN) { |
828 | allow_frac = 0; |
829 | best_frac_i = VC7_FOD_2ND_INT_MIN; |
830 | |
831 | for (i = VC7_FOD_2ND_INT_MIN; i <= VC7_FOD_2ND_INT_MAX; i++) { |
832 | /* |
833 | * 1) There is a div-by-2 preceding the 2nd stage integer divider |
834 | * (not shown on block diagram) so the actual 2nd stage integer |
835 | * divisor is 2 * N. |
836 | * 2) Attempt to find an integer solution first. This means stepping |
837 | * through each 2nd stage integer and recalculating the 1st stage |
838 | * until the 1st stage frequency is out of bounds. If no integer |
839 | * solution is found, use the best fractional solution. |
840 | */ |
841 | vc7_calc_fod_1st_stage(rate: parent_rate, parent_rate: rate * 2 * i, div_int: fod_1st_int, div_frac: fod_frac); |
842 | first_stage_rate = vc7_calc_fod_1st_stage_rate(parent_rate, |
843 | fod_1st_int: *fod_1st_int, |
844 | fod_frac: *fod_frac); |
845 | |
846 | /* Remember the first viable fractional solution */ |
847 | if (best_frac_i == VC7_FOD_2ND_INT_MIN && |
848 | first_stage_rate > VC7_FOD_1ST_STAGE_RATE_MIN) { |
849 | best_frac_i = i; |
850 | } |
851 | |
852 | /* Is the divider viable? Prefer integer solutions over fractional. */ |
853 | if (*fod_1st_int < VC7_FOD_1ST_INT_MAX && |
854 | first_stage_rate >= VC7_FOD_1ST_STAGE_RATE_MIN && |
855 | (allow_frac || *fod_frac == 0)) { |
856 | *fod_2nd_int = i; |
857 | break; |
858 | } |
859 | |
860 | /* Ran out of divisors or the 1st stage frequency is out of range */ |
861 | if (i >= VC7_FOD_2ND_INT_MAX || |
862 | first_stage_rate > VC7_FOD_1ST_STAGE_RATE_MAX) { |
863 | allow_frac = 1; |
864 | i = best_frac_i; |
865 | |
866 | /* Restore the best frac and rerun the loop for the last time */ |
867 | if (best_frac_i != VC7_FOD_2ND_INT_MIN) |
868 | i--; |
869 | |
870 | continue; |
871 | } |
872 | } |
873 | } |
874 | } |
875 | |
876 | static unsigned long vc7_fod_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) |
877 | { |
878 | struct vc7_fod_data *fod = container_of(hw, struct vc7_fod_data, hw); |
879 | struct vc7_driver_data *vc7 = fod->vc7; |
880 | int err; |
881 | unsigned long fod_rate; |
882 | |
883 | err = vc7_read_fod(vc7, idx: fod->num); |
884 | if (err) { |
885 | dev_err(&vc7->client->dev, "error reading registers for %s\n" , |
886 | clk_hw_get_name(hw)); |
887 | return err; |
888 | } |
889 | |
890 | pr_debug("%s - %s: parent_rate: %lu\n" , __func__, clk_hw_get_name(hw), parent_rate); |
891 | |
892 | fod_rate = vc7_calc_fod_2nd_stage_rate(parent_rate, fod_1st_int: fod->fod_1st_int, |
893 | fod_2nd_int: fod->fod_2nd_int, fod_frac: fod->fod_frac); |
894 | |
895 | pr_debug("%s - %s: fod_1st_int: %u, fod_2nd_int: %u, fod_frac: %llu\n" , |
896 | __func__, clk_hw_get_name(hw), |
897 | fod->fod_1st_int, fod->fod_2nd_int, fod->fod_frac); |
898 | pr_debug("%s - %s rate: %lu\n" , __func__, clk_hw_get_name(hw), fod_rate); |
899 | |
900 | return fod_rate; |
901 | } |
902 | |
903 | static long vc7_fod_round_rate(struct clk_hw *hw, unsigned long rate, unsigned long *parent_rate) |
904 | { |
905 | struct vc7_fod_data *fod = container_of(hw, struct vc7_fod_data, hw); |
906 | unsigned long fod_rate; |
907 | |
908 | pr_debug("%s - %s: requested rate: %lu, parent_rate: %lu\n" , |
909 | __func__, clk_hw_get_name(hw), rate, *parent_rate); |
910 | |
911 | vc7_calc_fod_divider(rate, parent_rate: *parent_rate, |
912 | fod_1st_int: &fod->fod_1st_int, fod_2nd_int: &fod->fod_2nd_int, fod_frac: &fod->fod_frac); |
913 | fod_rate = vc7_calc_fod_2nd_stage_rate(parent_rate: *parent_rate, fod_1st_int: fod->fod_1st_int, |
914 | fod_2nd_int: fod->fod_2nd_int, fod_frac: fod->fod_frac); |
915 | |
916 | pr_debug("%s - %s: fod_1st_int: %u, fod_2nd_int: %u, fod_frac: %llu\n" , |
917 | __func__, clk_hw_get_name(hw), |
918 | fod->fod_1st_int, fod->fod_2nd_int, fod->fod_frac); |
919 | pr_debug("%s - %s rate: %lu\n" , __func__, clk_hw_get_name(hw), fod_rate); |
920 | |
921 | return fod_rate; |
922 | } |
923 | |
924 | static int vc7_fod_set_rate(struct clk_hw *hw, unsigned long rate, unsigned long parent_rate) |
925 | { |
926 | struct vc7_fod_data *fod = container_of(hw, struct vc7_fod_data, hw); |
927 | struct vc7_driver_data *vc7 = fod->vc7; |
928 | unsigned long fod_rate; |
929 | |
930 | pr_debug("%s - %s: rate: %lu, parent_rate: %lu\n" , |
931 | __func__, clk_hw_get_name(hw), rate, parent_rate); |
932 | |
933 | if (rate < VC7_FOD_RATE_MIN || rate > VC7_FOD_RATE_MAX) { |
934 | dev_err(&vc7->client->dev, |
935 | "requested frequency %lu Hz for %s is out of range\n" , |
936 | rate, clk_hw_get_name(hw)); |
937 | return -EINVAL; |
938 | } |
939 | |
940 | vc7_write_fod(vc7, idx: fod->num); |
941 | |
942 | fod_rate = vc7_calc_fod_2nd_stage_rate(parent_rate, fod_1st_int: fod->fod_1st_int, |
943 | fod_2nd_int: fod->fod_2nd_int, fod_frac: fod->fod_frac); |
944 | |
945 | pr_debug("%s - %s: fod_1st_int: %u, fod_2nd_int: %u, fod_frac: %llu\n" , |
946 | __func__, clk_hw_get_name(hw), |
947 | fod->fod_1st_int, fod->fod_2nd_int, fod->fod_frac); |
948 | pr_debug("%s - %s rate: %lu\n" , __func__, clk_hw_get_name(hw), fod_rate); |
949 | |
950 | return 0; |
951 | } |
952 | |
953 | static const struct clk_ops vc7_fod_ops = { |
954 | .recalc_rate = vc7_fod_recalc_rate, |
955 | .round_rate = vc7_fod_round_rate, |
956 | .set_rate = vc7_fod_set_rate, |
957 | }; |
958 | |
959 | static unsigned long vc7_iod_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) |
960 | { |
961 | struct vc7_iod_data *iod = container_of(hw, struct vc7_iod_data, hw); |
962 | struct vc7_driver_data *vc7 = iod->vc7; |
963 | int err; |
964 | unsigned long iod_rate; |
965 | |
966 | err = vc7_read_iod(vc7, idx: iod->num); |
967 | if (err) { |
968 | dev_err(&vc7->client->dev, "error reading registers for %s\n" , |
969 | clk_hw_get_name(hw)); |
970 | return err; |
971 | } |
972 | |
973 | iod_rate = div64_u64(dividend: parent_rate, divisor: iod->iod_int); |
974 | |
975 | pr_debug("%s - %s: iod_int: %u\n" , __func__, clk_hw_get_name(hw), iod->iod_int); |
976 | pr_debug("%s - %s rate: %lu\n" , __func__, clk_hw_get_name(hw), iod_rate); |
977 | |
978 | return iod_rate; |
979 | } |
980 | |
981 | static long vc7_iod_round_rate(struct clk_hw *hw, unsigned long rate, unsigned long *parent_rate) |
982 | { |
983 | struct vc7_iod_data *iod = container_of(hw, struct vc7_iod_data, hw); |
984 | unsigned long iod_rate; |
985 | |
986 | pr_debug("%s - %s: requested rate: %lu, parent_rate: %lu\n" , |
987 | __func__, clk_hw_get_name(hw), rate, *parent_rate); |
988 | |
989 | vc7_calc_iod_divider(rate, parent_rate: *parent_rate, divider: &iod->iod_int); |
990 | iod_rate = div64_u64(dividend: *parent_rate, divisor: iod->iod_int); |
991 | |
992 | pr_debug("%s - %s: iod_int: %u\n" , __func__, clk_hw_get_name(hw), iod->iod_int); |
993 | pr_debug("%s - %s rate: %ld\n" , __func__, clk_hw_get_name(hw), iod_rate); |
994 | |
995 | return iod_rate; |
996 | } |
997 | |
998 | static int vc7_iod_set_rate(struct clk_hw *hw, unsigned long rate, unsigned long parent_rate) |
999 | { |
1000 | struct vc7_iod_data *iod = container_of(hw, struct vc7_iod_data, hw); |
1001 | struct vc7_driver_data *vc7 = iod->vc7; |
1002 | unsigned long iod_rate; |
1003 | |
1004 | pr_debug("%s - %s: rate: %lu, parent_rate: %lu\n" , |
1005 | __func__, clk_hw_get_name(hw), rate, parent_rate); |
1006 | |
1007 | if (rate < VC7_IOD_RATE_MIN || rate > VC7_IOD_RATE_MAX) { |
1008 | dev_err(&vc7->client->dev, |
1009 | "requested frequency %lu Hz for %s is out of range\n" , |
1010 | rate, clk_hw_get_name(hw)); |
1011 | return -EINVAL; |
1012 | } |
1013 | |
1014 | vc7_write_iod(vc7, idx: iod->num); |
1015 | |
1016 | iod_rate = div64_u64(dividend: parent_rate, divisor: iod->iod_int); |
1017 | |
1018 | pr_debug("%s - %s: iod_int: %u\n" , __func__, clk_hw_get_name(hw), iod->iod_int); |
1019 | pr_debug("%s - %s rate: %ld\n" , __func__, clk_hw_get_name(hw), iod_rate); |
1020 | |
1021 | return 0; |
1022 | } |
1023 | |
1024 | static const struct clk_ops vc7_iod_ops = { |
1025 | .recalc_rate = vc7_iod_recalc_rate, |
1026 | .round_rate = vc7_iod_round_rate, |
1027 | .set_rate = vc7_iod_set_rate, |
1028 | }; |
1029 | |
1030 | static int vc7_clk_out_prepare(struct clk_hw *hw) |
1031 | { |
1032 | struct vc7_out_data *out = container_of(hw, struct vc7_out_data, hw); |
1033 | struct vc7_driver_data *vc7 = out->vc7; |
1034 | int err; |
1035 | |
1036 | out->out_dis = 0; |
1037 | |
1038 | err = vc7_write_output(vc7, idx: out->num); |
1039 | if (err) { |
1040 | dev_err(&vc7->client->dev, "error writing registers for %s\n" , |
1041 | clk_hw_get_name(hw)); |
1042 | return err; |
1043 | } |
1044 | |
1045 | pr_debug("%s - %s: clk prepared\n" , __func__, clk_hw_get_name(hw)); |
1046 | |
1047 | return 0; |
1048 | } |
1049 | |
1050 | static void vc7_clk_out_unprepare(struct clk_hw *hw) |
1051 | { |
1052 | struct vc7_out_data *out = container_of(hw, struct vc7_out_data, hw); |
1053 | struct vc7_driver_data *vc7 = out->vc7; |
1054 | int err; |
1055 | |
1056 | out->out_dis = 1; |
1057 | |
1058 | err = vc7_write_output(vc7, idx: out->num); |
1059 | if (err) { |
1060 | dev_err(&vc7->client->dev, "error writing registers for %s\n" , |
1061 | clk_hw_get_name(hw)); |
1062 | return; |
1063 | } |
1064 | |
1065 | pr_debug("%s - %s: clk unprepared\n" , __func__, clk_hw_get_name(hw)); |
1066 | } |
1067 | |
1068 | static int vc7_clk_out_is_enabled(struct clk_hw *hw) |
1069 | { |
1070 | struct vc7_out_data *out = container_of(hw, struct vc7_out_data, hw); |
1071 | struct vc7_driver_data *vc7 = out->vc7; |
1072 | int err, is_enabled; |
1073 | |
1074 | err = vc7_read_output(vc7, idx: out->num); |
1075 | if (err) { |
1076 | dev_err(&vc7->client->dev, "error reading registers for %s\n" , |
1077 | clk_hw_get_name(hw)); |
1078 | return err; |
1079 | } |
1080 | |
1081 | is_enabled = !out->out_dis; |
1082 | |
1083 | pr_debug("%s - %s: is_enabled=%d\n" , __func__, clk_hw_get_name(hw), is_enabled); |
1084 | |
1085 | return is_enabled; |
1086 | } |
1087 | |
1088 | static const struct clk_ops vc7_clk_out_ops = { |
1089 | .prepare = vc7_clk_out_prepare, |
1090 | .unprepare = vc7_clk_out_unprepare, |
1091 | .is_enabled = vc7_clk_out_is_enabled, |
1092 | }; |
1093 | |
1094 | static int vc7_probe(struct i2c_client *client) |
1095 | { |
1096 | struct vc7_driver_data *vc7; |
1097 | struct clk_init_data clk_init; |
1098 | struct vc7_bank_src_map bank_src_map; |
1099 | const char *node_name, *apll_name; |
1100 | const char *parent_names[1]; |
1101 | unsigned int i, val, bank_idx, out_num; |
1102 | unsigned long apll_rate; |
1103 | int ret; |
1104 | |
1105 | vc7 = devm_kzalloc(dev: &client->dev, size: sizeof(*vc7), GFP_KERNEL); |
1106 | if (!vc7) |
1107 | return -ENOMEM; |
1108 | |
1109 | i2c_set_clientdata(client, data: vc7); |
1110 | vc7->client = client; |
1111 | vc7->chip_info = i2c_get_match_data(client); |
1112 | |
1113 | vc7->pin_xin = devm_clk_get(dev: &client->dev, id: "xin" ); |
1114 | if (PTR_ERR(ptr: vc7->pin_xin) == -EPROBE_DEFER) { |
1115 | return dev_err_probe(dev: &client->dev, err: -EPROBE_DEFER, |
1116 | fmt: "xin not specified\n" ); |
1117 | } |
1118 | |
1119 | vc7->regmap = devm_regmap_init_i2c(client, &vc7_regmap_config); |
1120 | if (IS_ERR(ptr: vc7->regmap)) { |
1121 | return dev_err_probe(dev: &client->dev, err: PTR_ERR(ptr: vc7->regmap), |
1122 | fmt: "failed to allocate register map\n" ); |
1123 | } |
1124 | |
1125 | if (of_property_read_string(np: client->dev.of_node, propname: "clock-output-names" , |
1126 | out_string: &node_name)) |
1127 | node_name = client->dev.of_node->name; |
1128 | |
1129 | /* Register APLL */ |
1130 | apll_rate = vc7_get_apll_rate(vc7); |
1131 | apll_name = kasprintf(GFP_KERNEL, fmt: "%s_apll" , node_name); |
1132 | vc7->clk_apll.clk = clk_register_fixed_rate(dev: &client->dev, name: apll_name, |
1133 | parent_name: __clk_get_name(clk: vc7->pin_xin), |
1134 | flags: 0, fixed_rate: apll_rate); |
1135 | kfree(objp: apll_name); /* ccf made a copy of the name */ |
1136 | if (IS_ERR(ptr: vc7->clk_apll.clk)) { |
1137 | return dev_err_probe(dev: &client->dev, err: PTR_ERR(ptr: vc7->clk_apll.clk), |
1138 | fmt: "failed to register apll\n" ); |
1139 | } |
1140 | |
1141 | /* Register FODs */ |
1142 | for (i = 0; i < VC7_NUM_FOD; i++) { |
1143 | memset(&clk_init, 0, sizeof(clk_init)); |
1144 | clk_init.name = kasprintf(GFP_KERNEL, fmt: "%s_fod%d" , node_name, i); |
1145 | clk_init.ops = &vc7_fod_ops; |
1146 | clk_init.parent_names = parent_names; |
1147 | parent_names[0] = __clk_get_name(clk: vc7->clk_apll.clk); |
1148 | clk_init.num_parents = 1; |
1149 | vc7->clk_fod[i].num = i; |
1150 | vc7->clk_fod[i].vc7 = vc7; |
1151 | vc7->clk_fod[i].hw.init = &clk_init; |
1152 | ret = devm_clk_hw_register(dev: &client->dev, hw: &vc7->clk_fod[i].hw); |
1153 | if (ret) |
1154 | goto err_clk_register; |
1155 | kfree(objp: clk_init.name); /* ccf made a copy of the name */ |
1156 | } |
1157 | |
1158 | /* Register IODs */ |
1159 | for (i = 0; i < VC7_NUM_IOD; i++) { |
1160 | memset(&clk_init, 0, sizeof(clk_init)); |
1161 | clk_init.name = kasprintf(GFP_KERNEL, fmt: "%s_iod%d" , node_name, i); |
1162 | clk_init.ops = &vc7_iod_ops; |
1163 | clk_init.parent_names = parent_names; |
1164 | parent_names[0] = __clk_get_name(clk: vc7->clk_apll.clk); |
1165 | clk_init.num_parents = 1; |
1166 | vc7->clk_iod[i].num = i; |
1167 | vc7->clk_iod[i].vc7 = vc7; |
1168 | vc7->clk_iod[i].hw.init = &clk_init; |
1169 | ret = devm_clk_hw_register(dev: &client->dev, hw: &vc7->clk_iod[i].hw); |
1170 | if (ret) |
1171 | goto err_clk_register; |
1172 | kfree(objp: clk_init.name); /* ccf made a copy of the name */ |
1173 | } |
1174 | |
1175 | /* Register outputs */ |
1176 | for (i = 0; i < vc7->chip_info->num_outputs; i++) { |
1177 | out_num = vc7_map_index_to_output(model: vc7->chip_info->model, i); |
1178 | |
1179 | /* |
1180 | * This driver does not support remapping FOD/IOD to banks. |
1181 | * The device state is read and the driver is setup to match |
1182 | * the device's existing mapping. |
1183 | */ |
1184 | bank_idx = output_bank_mapping[out_num]; |
1185 | |
1186 | regmap_read(map: vc7->regmap, VC7_REG_OUT_BANK_CNFG(bank_idx), val: &val); |
1187 | val &= VC7_REG_OUTPUT_BANK_SRC_MASK; |
1188 | |
1189 | memset(&bank_src_map, 0, sizeof(bank_src_map)); |
1190 | ret = vc7_get_bank_clk(vc7, bank_idx, output_bank_src: val, map: &bank_src_map); |
1191 | if (ret) { |
1192 | dev_err_probe(dev: &client->dev, err: ret, |
1193 | fmt: "unable to register output %d\n" , i); |
1194 | return ret; |
1195 | } |
1196 | |
1197 | switch (bank_src_map.type) { |
1198 | case VC7_FOD: |
1199 | parent_names[0] = clk_hw_get_name(hw: &bank_src_map.src.fod->hw); |
1200 | break; |
1201 | case VC7_IOD: |
1202 | parent_names[0] = clk_hw_get_name(hw: &bank_src_map.src.iod->hw); |
1203 | break; |
1204 | } |
1205 | |
1206 | memset(&clk_init, 0, sizeof(clk_init)); |
1207 | clk_init.name = kasprintf(GFP_KERNEL, fmt: "%s_out%d" , node_name, i); |
1208 | clk_init.ops = &vc7_clk_out_ops; |
1209 | clk_init.flags = CLK_SET_RATE_PARENT; |
1210 | clk_init.parent_names = parent_names; |
1211 | clk_init.num_parents = 1; |
1212 | vc7->clk_out[i].num = i; |
1213 | vc7->clk_out[i].vc7 = vc7; |
1214 | vc7->clk_out[i].hw.init = &clk_init; |
1215 | ret = devm_clk_hw_register(dev: &client->dev, hw: &vc7->clk_out[i].hw); |
1216 | if (ret) |
1217 | goto err_clk_register; |
1218 | kfree(objp: clk_init.name); /* ccf made a copy of the name */ |
1219 | } |
1220 | |
1221 | ret = of_clk_add_hw_provider(np: client->dev.of_node, get: vc7_of_clk_get, data: vc7); |
1222 | if (ret) { |
1223 | dev_err_probe(dev: &client->dev, err: ret, fmt: "unable to add clk provider\n" ); |
1224 | goto err_clk; |
1225 | } |
1226 | |
1227 | return ret; |
1228 | |
1229 | err_clk_register: |
1230 | dev_err_probe(dev: &client->dev, err: ret, |
1231 | fmt: "unable to register %s\n" , clk_init.name); |
1232 | kfree(objp: clk_init.name); /* ccf made a copy of the name */ |
1233 | err_clk: |
1234 | clk_unregister_fixed_rate(clk: vc7->clk_apll.clk); |
1235 | return ret; |
1236 | } |
1237 | |
1238 | static void vc7_remove(struct i2c_client *client) |
1239 | { |
1240 | struct vc7_driver_data *vc7 = i2c_get_clientdata(client); |
1241 | |
1242 | of_clk_del_provider(np: client->dev.of_node); |
1243 | clk_unregister_fixed_rate(clk: vc7->clk_apll.clk); |
1244 | } |
1245 | |
1246 | static bool vc7_volatile_reg(struct device *dev, unsigned int reg) |
1247 | { |
1248 | if (reg == VC7_PAGE_ADDR) |
1249 | return false; |
1250 | |
1251 | return true; |
1252 | } |
1253 | |
1254 | static const struct vc7_chip_info vc7_rc21008a_info = { |
1255 | .model = VC7_RC21008A, |
1256 | .num_banks = 6, |
1257 | .num_outputs = 8, |
1258 | }; |
1259 | |
1260 | static struct regmap_range_cfg vc7_range_cfg[] = { |
1261 | { |
1262 | .range_min = 0, |
1263 | .range_max = VC7_MAX_REG, |
1264 | .selector_reg = VC7_PAGE_ADDR, |
1265 | .selector_mask = 0xFF, |
1266 | .selector_shift = 0, |
1267 | .window_start = 0, |
1268 | .window_len = VC7_PAGE_WINDOW, |
1269 | }}; |
1270 | |
1271 | static const struct regmap_config vc7_regmap_config = { |
1272 | .reg_bits = 8, |
1273 | .val_bits = 8, |
1274 | .max_register = VC7_MAX_REG, |
1275 | .ranges = vc7_range_cfg, |
1276 | .num_ranges = ARRAY_SIZE(vc7_range_cfg), |
1277 | .volatile_reg = vc7_volatile_reg, |
1278 | .cache_type = REGCACHE_MAPLE, |
1279 | .can_multi_write = true, |
1280 | .reg_format_endian = REGMAP_ENDIAN_LITTLE, |
1281 | .val_format_endian = REGMAP_ENDIAN_LITTLE, |
1282 | }; |
1283 | |
1284 | static const struct i2c_device_id vc7_i2c_id[] = { |
1285 | { "rc21008a" , .driver_data = (kernel_ulong_t)&vc7_rc21008a_info }, |
1286 | {} |
1287 | }; |
1288 | MODULE_DEVICE_TABLE(i2c, vc7_i2c_id); |
1289 | |
1290 | static const struct of_device_id vc7_of_match[] = { |
1291 | { .compatible = "renesas,rc21008a" , .data = &vc7_rc21008a_info }, |
1292 | {} |
1293 | }; |
1294 | MODULE_DEVICE_TABLE(of, vc7_of_match); |
1295 | |
1296 | static struct i2c_driver vc7_i2c_driver = { |
1297 | .driver = { |
1298 | .name = "vc7" , |
1299 | .of_match_table = vc7_of_match, |
1300 | }, |
1301 | .probe = vc7_probe, |
1302 | .remove = vc7_remove, |
1303 | .id_table = vc7_i2c_id, |
1304 | }; |
1305 | module_i2c_driver(vc7_i2c_driver); |
1306 | |
1307 | MODULE_LICENSE("GPL" ); |
1308 | MODULE_AUTHOR("Alex Helms <alexander.helms.jy@renesas.com" ); |
1309 | MODULE_DESCRIPTION("Renesas Versaclock7 common clock framework driver" ); |
1310 | |