1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * Driver for Silicon Labs Si570/Si571 Programmable XO/VCXO |
4 | * |
5 | * Copyright (C) 2010, 2011 Ericsson AB. |
6 | * Copyright (C) 2011 Guenter Roeck. |
7 | * Copyright (C) 2011 - 2021 Xilinx Inc. |
8 | * |
9 | * Author: Guenter Roeck <guenter.roeck@ericsson.com> |
10 | * Sören Brinkmann <soren.brinkmann@xilinx.com> |
11 | */ |
12 | |
13 | #include <linux/clk.h> |
14 | #include <linux/clk-provider.h> |
15 | #include <linux/delay.h> |
16 | #include <linux/module.h> |
17 | #include <linux/i2c.h> |
18 | #include <linux/regmap.h> |
19 | #include <linux/slab.h> |
20 | |
21 | /* Si570 registers */ |
22 | #define SI570_REG_HS_N1 7 |
23 | #define SI570_REG_N1_RFREQ0 8 |
24 | #define SI570_REG_RFREQ1 9 |
25 | #define SI570_REG_RFREQ2 10 |
26 | #define SI570_REG_RFREQ3 11 |
27 | #define SI570_REG_RFREQ4 12 |
28 | #define SI570_REG_CONTROL 135 |
29 | #define SI570_REG_FREEZE_DCO 137 |
30 | #define SI570_DIV_OFFSET_7PPM 6 |
31 | |
32 | #define HS_DIV_SHIFT 5 |
33 | #define HS_DIV_MASK 0xe0 |
34 | #define HS_DIV_OFFSET 4 |
35 | #define N1_6_2_MASK 0x1f |
36 | #define N1_1_0_MASK 0xc0 |
37 | #define RFREQ_37_32_MASK 0x3f |
38 | |
39 | #define SI570_MIN_FREQ 10000000L |
40 | #define SI570_MAX_FREQ 1417500000L |
41 | #define SI598_MAX_FREQ 525000000L |
42 | |
43 | #define FDCO_MIN 4850000000LL |
44 | #define FDCO_MAX 5670000000LL |
45 | |
46 | #define SI570_CNTRL_RECALL (1 << 0) |
47 | #define SI570_CNTRL_FREEZE_M (1 << 5) |
48 | #define SI570_CNTRL_NEWFREQ (1 << 6) |
49 | |
50 | #define SI570_FREEZE_DCO (1 << 4) |
51 | |
52 | /** |
53 | * struct clk_si570_info: |
54 | * @max_freq: Maximum frequency for this device |
55 | * @has_temperature_stability: Device support temperature stability |
56 | */ |
57 | struct clk_si570_info { |
58 | u64 max_freq; |
59 | bool has_temperature_stability; |
60 | }; |
61 | |
62 | /** |
63 | * struct clk_si570: |
64 | * @hw: Clock hw struct |
65 | * @regmap: Device's regmap |
66 | * @div_offset: Rgister offset for dividers |
67 | * @info: Device info |
68 | * @fxtal: Factory xtal frequency |
69 | * @n1: Clock divider N1 |
70 | * @hs_div: Clock divider HSDIV |
71 | * @rfreq: Clock multiplier RFREQ |
72 | * @frequency: Current output frequency |
73 | * @i2c_client: I2C client pointer |
74 | */ |
75 | struct clk_si570 { |
76 | struct clk_hw hw; |
77 | struct regmap *regmap; |
78 | unsigned int div_offset; |
79 | const struct clk_si570_info *info; |
80 | u64 fxtal; |
81 | unsigned int n1; |
82 | unsigned int hs_div; |
83 | u64 rfreq; |
84 | u64 frequency; |
85 | struct i2c_client *i2c_client; |
86 | }; |
87 | #define to_clk_si570(_hw) container_of(_hw, struct clk_si570, hw) |
88 | |
89 | /** |
90 | * si570_get_divs() - Read clock dividers from HW |
91 | * @data: Pointer to struct clk_si570 |
92 | * @rfreq: Fractional multiplier (output) |
93 | * @n1: Divider N1 (output) |
94 | * @hs_div: Divider HSDIV (output) |
95 | * Returns 0 on success, negative errno otherwise. |
96 | * |
97 | * Retrieve clock dividers and multipliers from the HW. |
98 | */ |
99 | static int si570_get_divs(struct clk_si570 *data, u64 *rfreq, |
100 | unsigned int *n1, unsigned int *hs_div) |
101 | { |
102 | int err; |
103 | u8 reg[6]; |
104 | u64 tmp; |
105 | |
106 | err = regmap_bulk_read(map: data->regmap, SI570_REG_HS_N1 + data->div_offset, |
107 | val: reg, ARRAY_SIZE(reg)); |
108 | if (err) |
109 | return err; |
110 | |
111 | *hs_div = ((reg[0] & HS_DIV_MASK) >> HS_DIV_SHIFT) + HS_DIV_OFFSET; |
112 | *n1 = ((reg[0] & N1_6_2_MASK) << 2) + ((reg[1] & N1_1_0_MASK) >> 6) + 1; |
113 | /* Handle invalid cases */ |
114 | if (*n1 > 1) |
115 | *n1 &= ~1; |
116 | |
117 | tmp = reg[1] & RFREQ_37_32_MASK; |
118 | tmp = (tmp << 8) + reg[2]; |
119 | tmp = (tmp << 8) + reg[3]; |
120 | tmp = (tmp << 8) + reg[4]; |
121 | tmp = (tmp << 8) + reg[5]; |
122 | *rfreq = tmp; |
123 | |
124 | return 0; |
125 | } |
126 | |
127 | /** |
128 | * si570_get_defaults() - Get default values |
129 | * @data: Driver data structure |
130 | * @fout: Factory frequency output |
131 | * @skip_recall: If true, don't recall NVM into RAM |
132 | * Returns 0 on success, negative errno otherwise. |
133 | */ |
134 | static int si570_get_defaults(struct clk_si570 *data, u64 fout, |
135 | bool skip_recall) |
136 | { |
137 | int err; |
138 | u64 fdco; |
139 | |
140 | if (!skip_recall) |
141 | regmap_write(map: data->regmap, SI570_REG_CONTROL, |
142 | SI570_CNTRL_RECALL); |
143 | |
144 | err = si570_get_divs(data, rfreq: &data->rfreq, n1: &data->n1, hs_div: &data->hs_div); |
145 | if (err) |
146 | return err; |
147 | |
148 | /* |
149 | * Accept optional precision loss to avoid arithmetic overflows. |
150 | * Acceptable per Silicon Labs Application Note AN334. |
151 | */ |
152 | fdco = fout * data->n1 * data->hs_div; |
153 | if (fdco >= (1LL << 36)) |
154 | data->fxtal = div64_u64(dividend: fdco << 24, divisor: data->rfreq >> 4); |
155 | else |
156 | data->fxtal = div64_u64(dividend: fdco << 28, divisor: data->rfreq); |
157 | |
158 | data->frequency = fout; |
159 | |
160 | return 0; |
161 | } |
162 | |
163 | /** |
164 | * si570_update_rfreq() - Update clock multiplier |
165 | * @data: Driver data structure |
166 | * Passes on regmap_bulk_write() return value. |
167 | */ |
168 | static int si570_update_rfreq(struct clk_si570 *data) |
169 | { |
170 | u8 reg[5]; |
171 | |
172 | reg[0] = ((data->n1 - 1) << 6) | |
173 | ((data->rfreq >> 32) & RFREQ_37_32_MASK); |
174 | reg[1] = (data->rfreq >> 24) & 0xff; |
175 | reg[2] = (data->rfreq >> 16) & 0xff; |
176 | reg[3] = (data->rfreq >> 8) & 0xff; |
177 | reg[4] = data->rfreq & 0xff; |
178 | |
179 | return regmap_bulk_write(map: data->regmap, SI570_REG_N1_RFREQ0 + |
180 | data->div_offset, val: reg, ARRAY_SIZE(reg)); |
181 | } |
182 | |
183 | /** |
184 | * si570_calc_divs() - Caluclate clock dividers |
185 | * @frequency: Target frequency |
186 | * @data: Driver data structure |
187 | * @out_rfreq: RFREG fractional multiplier (output) |
188 | * @out_n1: Clock divider N1 (output) |
189 | * @out_hs_div: Clock divider HSDIV (output) |
190 | * Returns 0 on success, negative errno otherwise. |
191 | * |
192 | * Calculate the clock dividers (@out_hs_div, @out_n1) and clock multiplier |
193 | * (@out_rfreq) for a given target @frequency. |
194 | */ |
195 | static int si570_calc_divs(unsigned long frequency, struct clk_si570 *data, |
196 | u64 *out_rfreq, unsigned int *out_n1, unsigned int *out_hs_div) |
197 | { |
198 | int i; |
199 | unsigned int n1, hs_div; |
200 | u64 fdco, best_fdco = ULLONG_MAX; |
201 | static const uint8_t si570_hs_div_values[] = { 11, 9, 7, 6, 5, 4 }; |
202 | |
203 | for (i = 0; i < ARRAY_SIZE(si570_hs_div_values); i++) { |
204 | hs_div = si570_hs_div_values[i]; |
205 | /* Calculate lowest possible value for n1 */ |
206 | n1 = div_u64(dividend: div_u64(FDCO_MIN, divisor: hs_div), divisor: frequency); |
207 | if (!n1 || (n1 & 1)) |
208 | n1++; |
209 | while (n1 <= 128) { |
210 | fdco = (u64)frequency * (u64)hs_div * (u64)n1; |
211 | if (fdco > FDCO_MAX) |
212 | break; |
213 | if (fdco >= FDCO_MIN && fdco < best_fdco) { |
214 | *out_n1 = n1; |
215 | *out_hs_div = hs_div; |
216 | *out_rfreq = div64_u64(dividend: fdco << 28, divisor: data->fxtal); |
217 | best_fdco = fdco; |
218 | } |
219 | n1 += (n1 == 1 ? 1 : 2); |
220 | } |
221 | } |
222 | |
223 | if (best_fdco == ULLONG_MAX) |
224 | return -EINVAL; |
225 | |
226 | return 0; |
227 | } |
228 | |
229 | static unsigned long si570_recalc_rate(struct clk_hw *hw, |
230 | unsigned long parent_rate) |
231 | { |
232 | int err; |
233 | u64 rfreq, rate; |
234 | unsigned int n1, hs_div; |
235 | struct clk_si570 *data = to_clk_si570(hw); |
236 | |
237 | err = si570_get_divs(data, rfreq: &rfreq, n1: &n1, hs_div: &hs_div); |
238 | if (err) { |
239 | dev_err(&data->i2c_client->dev, "unable to recalc rate\n" ); |
240 | return data->frequency; |
241 | } |
242 | |
243 | rfreq = div_u64(dividend: rfreq, divisor: hs_div * n1); |
244 | rate = (data->fxtal * rfreq) >> 28; |
245 | |
246 | return rate; |
247 | } |
248 | |
249 | static long si570_round_rate(struct clk_hw *hw, unsigned long rate, |
250 | unsigned long *parent_rate) |
251 | { |
252 | int err; |
253 | u64 rfreq; |
254 | unsigned int n1, hs_div; |
255 | struct clk_si570 *data = to_clk_si570(hw); |
256 | |
257 | if (!rate) |
258 | return 0; |
259 | |
260 | if (div64_u64(abs(rate - data->frequency) * 10000LL, |
261 | divisor: data->frequency) < 35) { |
262 | rfreq = div64_u64(dividend: (data->rfreq * rate) + |
263 | div64_u64(dividend: data->frequency, divisor: 2), divisor: data->frequency); |
264 | n1 = data->n1; |
265 | hs_div = data->hs_div; |
266 | |
267 | } else { |
268 | err = si570_calc_divs(frequency: rate, data, out_rfreq: &rfreq, out_n1: &n1, out_hs_div: &hs_div); |
269 | if (err) { |
270 | dev_err(&data->i2c_client->dev, |
271 | "unable to round rate\n" ); |
272 | return 0; |
273 | } |
274 | } |
275 | |
276 | return rate; |
277 | } |
278 | |
279 | /** |
280 | * si570_set_frequency() - Adjust output frequency |
281 | * @data: Driver data structure |
282 | * @frequency: Target frequency |
283 | * Returns 0 on success. |
284 | * |
285 | * Update output frequency for big frequency changes (> 3,500 ppm). |
286 | */ |
287 | static int si570_set_frequency(struct clk_si570 *data, unsigned long frequency) |
288 | { |
289 | int err; |
290 | |
291 | err = si570_calc_divs(frequency, data, out_rfreq: &data->rfreq, out_n1: &data->n1, |
292 | out_hs_div: &data->hs_div); |
293 | if (err) |
294 | return err; |
295 | |
296 | /* |
297 | * The DCO reg should be accessed with a read-modify-write operation |
298 | * per AN334 |
299 | */ |
300 | regmap_write(map: data->regmap, SI570_REG_FREEZE_DCO, SI570_FREEZE_DCO); |
301 | regmap_write(map: data->regmap, SI570_REG_HS_N1 + data->div_offset, |
302 | val: ((data->hs_div - HS_DIV_OFFSET) << HS_DIV_SHIFT) | |
303 | (((data->n1 - 1) >> 2) & N1_6_2_MASK)); |
304 | si570_update_rfreq(data); |
305 | regmap_write(map: data->regmap, SI570_REG_FREEZE_DCO, val: 0); |
306 | regmap_write(map: data->regmap, SI570_REG_CONTROL, SI570_CNTRL_NEWFREQ); |
307 | |
308 | /* Applying a new frequency can take up to 10ms */ |
309 | usleep_range(min: 10000, max: 12000); |
310 | |
311 | return 0; |
312 | } |
313 | |
314 | /** |
315 | * si570_set_frequency_small() - Adjust output frequency |
316 | * @data: Driver data structure |
317 | * @frequency: Target frequency |
318 | * Returns 0 on success. |
319 | * |
320 | * Update output frequency for small frequency changes (< 3,500 ppm). |
321 | */ |
322 | static int si570_set_frequency_small(struct clk_si570 *data, |
323 | unsigned long frequency) |
324 | { |
325 | /* |
326 | * This is a re-implementation of DIV_ROUND_CLOSEST |
327 | * using the div64_u64 function lieu of letting the compiler |
328 | * insert EABI calls |
329 | */ |
330 | data->rfreq = div64_u64(dividend: (data->rfreq * frequency) + |
331 | div_u64(dividend: data->frequency, divisor: 2), divisor: data->frequency); |
332 | regmap_write(map: data->regmap, SI570_REG_CONTROL, SI570_CNTRL_FREEZE_M); |
333 | si570_update_rfreq(data); |
334 | regmap_write(map: data->regmap, SI570_REG_CONTROL, val: 0); |
335 | |
336 | /* Applying a new frequency (small change) can take up to 100us */ |
337 | usleep_range(min: 100, max: 200); |
338 | |
339 | return 0; |
340 | } |
341 | |
342 | static int si570_set_rate(struct clk_hw *hw, unsigned long rate, |
343 | unsigned long parent_rate) |
344 | { |
345 | struct clk_si570 *data = to_clk_si570(hw); |
346 | struct i2c_client *client = data->i2c_client; |
347 | int err; |
348 | |
349 | if (rate < SI570_MIN_FREQ || rate > data->info->max_freq) { |
350 | dev_err(&client->dev, |
351 | "requested frequency %lu Hz is out of range\n" , rate); |
352 | return -EINVAL; |
353 | } |
354 | |
355 | if (div64_u64(abs(rate - data->frequency) * 10000LL, |
356 | divisor: data->frequency) < 35) |
357 | err = si570_set_frequency_small(data, frequency: rate); |
358 | else |
359 | err = si570_set_frequency(data, frequency: rate); |
360 | |
361 | if (err) |
362 | return err; |
363 | |
364 | data->frequency = rate; |
365 | |
366 | return 0; |
367 | } |
368 | |
369 | static const struct clk_ops si570_clk_ops = { |
370 | .recalc_rate = si570_recalc_rate, |
371 | .round_rate = si570_round_rate, |
372 | .set_rate = si570_set_rate, |
373 | }; |
374 | |
375 | static bool si570_regmap_is_volatile(struct device *dev, unsigned int reg) |
376 | { |
377 | switch (reg) { |
378 | case SI570_REG_CONTROL: |
379 | return true; |
380 | default: |
381 | return false; |
382 | } |
383 | } |
384 | |
385 | static bool si570_regmap_is_writeable(struct device *dev, unsigned int reg) |
386 | { |
387 | switch (reg) { |
388 | case SI570_REG_HS_N1 ... (SI570_REG_RFREQ4 + SI570_DIV_OFFSET_7PPM): |
389 | case SI570_REG_CONTROL: |
390 | case SI570_REG_FREEZE_DCO: |
391 | return true; |
392 | default: |
393 | return false; |
394 | } |
395 | } |
396 | |
397 | static const struct regmap_config si570_regmap_config = { |
398 | .reg_bits = 8, |
399 | .val_bits = 8, |
400 | .cache_type = REGCACHE_MAPLE, |
401 | .max_register = 137, |
402 | .writeable_reg = si570_regmap_is_writeable, |
403 | .volatile_reg = si570_regmap_is_volatile, |
404 | }; |
405 | |
406 | static int si570_probe(struct i2c_client *client) |
407 | { |
408 | struct clk_si570 *data; |
409 | struct clk_init_data init; |
410 | u32 initial_fout, factory_fout, stability; |
411 | bool skip_recall; |
412 | int err; |
413 | |
414 | data = devm_kzalloc(dev: &client->dev, size: sizeof(*data), GFP_KERNEL); |
415 | if (!data) |
416 | return -ENOMEM; |
417 | |
418 | init.ops = &si570_clk_ops; |
419 | init.flags = 0; |
420 | init.num_parents = 0; |
421 | data->hw.init = &init; |
422 | data->i2c_client = client; |
423 | |
424 | data->info = i2c_get_match_data(client); |
425 | if (data->info->has_temperature_stability) { |
426 | err = of_property_read_u32(np: client->dev.of_node, |
427 | propname: "temperature-stability" , out_value: &stability); |
428 | if (err) { |
429 | dev_err(&client->dev, |
430 | "'temperature-stability' property missing\n" ); |
431 | return err; |
432 | } |
433 | /* adjust register offsets for 7ppm devices */ |
434 | if (stability == 7) |
435 | data->div_offset = SI570_DIV_OFFSET_7PPM; |
436 | } |
437 | |
438 | if (of_property_read_string(np: client->dev.of_node, propname: "clock-output-names" , |
439 | out_string: &init.name)) |
440 | init.name = client->dev.of_node->name; |
441 | |
442 | err = of_property_read_u32(np: client->dev.of_node, propname: "factory-fout" , |
443 | out_value: &factory_fout); |
444 | if (err) { |
445 | dev_err(&client->dev, "'factory-fout' property missing\n" ); |
446 | return err; |
447 | } |
448 | |
449 | skip_recall = of_property_read_bool(np: client->dev.of_node, |
450 | propname: "silabs,skip-recall" ); |
451 | |
452 | data->regmap = devm_regmap_init_i2c(client, &si570_regmap_config); |
453 | if (IS_ERR(ptr: data->regmap)) { |
454 | dev_err(&client->dev, "failed to allocate register map\n" ); |
455 | return PTR_ERR(ptr: data->regmap); |
456 | } |
457 | |
458 | i2c_set_clientdata(client, data); |
459 | err = si570_get_defaults(data, fout: factory_fout, skip_recall); |
460 | if (err) |
461 | return err; |
462 | |
463 | err = devm_clk_hw_register(dev: &client->dev, hw: &data->hw); |
464 | if (err) { |
465 | dev_err(&client->dev, "clock registration failed\n" ); |
466 | return err; |
467 | } |
468 | err = devm_of_clk_add_hw_provider(dev: &client->dev, get: of_clk_hw_simple_get, |
469 | data: &data->hw); |
470 | if (err) { |
471 | dev_err(&client->dev, "unable to add clk provider\n" ); |
472 | return err; |
473 | } |
474 | |
475 | /* Read the requested initial output frequency from device tree */ |
476 | if (!of_property_read_u32(np: client->dev.of_node, propname: "clock-frequency" , |
477 | out_value: &initial_fout)) { |
478 | err = clk_set_rate(clk: data->hw.clk, rate: initial_fout); |
479 | if (err) |
480 | return err; |
481 | } |
482 | |
483 | /* Display a message indicating that we've successfully registered */ |
484 | dev_info(&client->dev, "registered, current frequency %llu Hz\n" , |
485 | data->frequency); |
486 | |
487 | return 0; |
488 | } |
489 | |
490 | static const struct clk_si570_info clk_si570_info = { |
491 | .max_freq = SI570_MAX_FREQ, |
492 | .has_temperature_stability = true, |
493 | }; |
494 | |
495 | static const struct clk_si570_info clk_si590_info = { |
496 | .max_freq = SI598_MAX_FREQ, |
497 | }; |
498 | |
499 | static const struct i2c_device_id si570_id[] = { |
500 | { "si570" , (kernel_ulong_t)&clk_si570_info }, |
501 | { "si571" , (kernel_ulong_t)&clk_si570_info }, |
502 | { "si598" , (kernel_ulong_t)&clk_si590_info }, |
503 | { "si599" , (kernel_ulong_t)&clk_si590_info }, |
504 | { } |
505 | }; |
506 | MODULE_DEVICE_TABLE(i2c, si570_id); |
507 | |
508 | static const struct of_device_id clk_si570_of_match[] = { |
509 | { .compatible = "silabs,si570" , .data = &clk_si570_info }, |
510 | { .compatible = "silabs,si571" , .data = &clk_si570_info }, |
511 | { .compatible = "silabs,si598" , .data = &clk_si590_info }, |
512 | { .compatible = "silabs,si599" , .data = &clk_si590_info }, |
513 | { } |
514 | }; |
515 | MODULE_DEVICE_TABLE(of, clk_si570_of_match); |
516 | |
517 | static struct i2c_driver si570_driver = { |
518 | .driver = { |
519 | .name = "si570" , |
520 | .of_match_table = clk_si570_of_match, |
521 | }, |
522 | .probe = si570_probe, |
523 | .id_table = si570_id, |
524 | }; |
525 | module_i2c_driver(si570_driver); |
526 | |
527 | MODULE_AUTHOR("Guenter Roeck <guenter.roeck@ericsson.com>" ); |
528 | MODULE_AUTHOR("Soeren Brinkmann <soren.brinkmann@xilinx.com>" ); |
529 | MODULE_DESCRIPTION("Si570 driver" ); |
530 | MODULE_LICENSE("GPL" ); |
531 | |