Commit | Line | Data |
---|---|---|
953cc3e8 ML |
1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* | |
3 | * Driver for Silicon Labs Si544 Programmable Oscillator | |
4 | * Copyright (C) 2018 Topic Embedded Products | |
5 | * Author: Mike Looijmans <mike.looijmans@topic.nl> | |
6 | */ | |
7 | ||
8 | #include <linux/clk-provider.h> | |
9 | #include <linux/delay.h> | |
dc59c133 | 10 | #include <linux/math64.h> |
953cc3e8 ML |
11 | #include <linux/module.h> |
12 | #include <linux/i2c.h> | |
13 | #include <linux/regmap.h> | |
14 | #include <linux/slab.h> | |
15 | ||
16 | /* I2C registers (decimal as in datasheet) */ | |
17 | #define SI544_REG_CONTROL 7 | |
18 | #define SI544_REG_OE_STATE 17 | |
19 | #define SI544_REG_HS_DIV 23 | |
20 | #define SI544_REG_LS_HS_DIV 24 | |
21 | #define SI544_REG_FBDIV0 26 | |
22 | #define SI544_REG_FBDIV8 27 | |
23 | #define SI544_REG_FBDIV16 28 | |
24 | #define SI544_REG_FBDIV24 29 | |
25 | #define SI544_REG_FBDIV32 30 | |
26 | #define SI544_REG_FBDIV40 31 | |
27 | #define SI544_REG_FCAL_OVR 69 | |
28 | #define SI544_REG_ADPLL_DELTA_M0 231 | |
29 | #define SI544_REG_ADPLL_DELTA_M8 232 | |
30 | #define SI544_REG_ADPLL_DELTA_M16 233 | |
31 | #define SI544_REG_PAGE_SELECT 255 | |
32 | ||
33 | /* Register values */ | |
34 | #define SI544_CONTROL_RESET BIT(7) | |
35 | #define SI544_CONTROL_MS_ICAL2 BIT(3) | |
36 | ||
37 | #define SI544_OE_STATE_ODC_OE BIT(0) | |
38 | ||
39 | /* Max freq depends on speed grade */ | |
40 | #define SI544_MIN_FREQ 200000U | |
41 | ||
42 | /* Si544 Internal oscilator runs at 55.05 MHz */ | |
43 | #define FXO 55050000U | |
44 | ||
45 | /* VCO range is 10.8 .. 12.1 GHz, max depends on speed grade */ | |
46 | #define FVCO_MIN 10800000000ULL | |
47 | ||
48 | #define HS_DIV_MAX 2046 | |
49 | #define HS_DIV_MAX_ODD 33 | |
50 | ||
51 | /* Lowest frequency synthesizeable using only the HS divider */ | |
52 | #define MIN_HSDIV_FREQ (FVCO_MIN / HS_DIV_MAX) | |
53 | ||
dc59c133 ML |
54 | /* Range and interpretation of the adjustment value */ |
55 | #define DELTA_M_MAX 8161512 | |
56 | #define DELTA_M_FRAC_NUM 19 | |
57 | #define DELTA_M_FRAC_DEN 20000 | |
58 | ||
953cc3e8 ML |
59 | enum si544_speed_grade { |
60 | si544a, | |
61 | si544b, | |
62 | si544c, | |
63 | }; | |
64 | ||
65 | struct clk_si544 { | |
66 | struct clk_hw hw; | |
67 | struct regmap *regmap; | |
68 | struct i2c_client *i2c_client; | |
69 | enum si544_speed_grade speed_grade; | |
70 | }; | |
71 | #define to_clk_si544(_hw) container_of(_hw, struct clk_si544, hw) | |
72 | ||
73 | /** | |
74 | * struct clk_si544_muldiv - Multiplier/divider settings | |
75 | * @fb_div_frac: integer part of feedback divider (32 bits) | |
76 | * @fb_div_int: fractional part of feedback divider (11 bits) | |
77 | * @hs_div: 1st divider, 5..2046, must be even when >33 | |
78 | * @ls_div_bits: 2nd divider, as 2^x, range 0..5 | |
79 | * If ls_div_bits is non-zero, hs_div must be even | |
dc59c133 | 80 | * @delta_m: Frequency shift for small -950..+950 ppm changes, 24 bit |
953cc3e8 ML |
81 | */ |
82 | struct clk_si544_muldiv { | |
83 | u32 fb_div_frac; | |
84 | u16 fb_div_int; | |
85 | u16 hs_div; | |
86 | u8 ls_div_bits; | |
dc59c133 | 87 | s32 delta_m; |
953cc3e8 ML |
88 | }; |
89 | ||
90 | /* Enables or disables the output driver */ | |
91 | static int si544_enable_output(struct clk_si544 *data, bool enable) | |
92 | { | |
93 | return regmap_update_bits(data->regmap, SI544_REG_OE_STATE, | |
94 | SI544_OE_STATE_ODC_OE, enable ? SI544_OE_STATE_ODC_OE : 0); | |
95 | } | |
96 | ||
e8f127ca ML |
97 | static int si544_prepare(struct clk_hw *hw) |
98 | { | |
99 | struct clk_si544 *data = to_clk_si544(hw); | |
100 | ||
101 | return si544_enable_output(data, true); | |
102 | } | |
103 | ||
104 | static void si544_unprepare(struct clk_hw *hw) | |
105 | { | |
106 | struct clk_si544 *data = to_clk_si544(hw); | |
107 | ||
108 | si544_enable_output(data, false); | |
109 | } | |
110 | ||
111 | static int si544_is_prepared(struct clk_hw *hw) | |
112 | { | |
113 | struct clk_si544 *data = to_clk_si544(hw); | |
114 | unsigned int val; | |
115 | int err; | |
116 | ||
117 | err = regmap_read(data->regmap, SI544_REG_OE_STATE, &val); | |
118 | if (err < 0) | |
119 | return err; | |
120 | ||
121 | return !!(val & SI544_OE_STATE_ODC_OE); | |
122 | } | |
123 | ||
953cc3e8 ML |
124 | /* Retrieve clock multiplier and dividers from hardware */ |
125 | static int si544_get_muldiv(struct clk_si544 *data, | |
126 | struct clk_si544_muldiv *settings) | |
127 | { | |
128 | int err; | |
129 | u8 reg[6]; | |
130 | ||
131 | err = regmap_bulk_read(data->regmap, SI544_REG_HS_DIV, reg, 2); | |
132 | if (err) | |
133 | return err; | |
134 | ||
135 | settings->ls_div_bits = (reg[1] >> 4) & 0x07; | |
136 | settings->hs_div = (reg[1] & 0x07) << 8 | reg[0]; | |
137 | ||
138 | err = regmap_bulk_read(data->regmap, SI544_REG_FBDIV0, reg, 6); | |
139 | if (err) | |
140 | return err; | |
141 | ||
142 | settings->fb_div_int = reg[4] | (reg[5] & 0x07) << 8; | |
143 | settings->fb_div_frac = reg[0] | reg[1] << 8 | reg[2] << 16 | | |
144 | reg[3] << 24; | |
dc59c133 ML |
145 | |
146 | err = regmap_bulk_read(data->regmap, SI544_REG_ADPLL_DELTA_M0, reg, 3); | |
147 | if (err) | |
148 | return err; | |
149 | ||
150 | /* Interpret as 24-bit signed number */ | |
151 | settings->delta_m = reg[0] << 8 | reg[1] << 16 | reg[2] << 24; | |
152 | settings->delta_m >>= 8; | |
153 | ||
953cc3e8 ML |
154 | return 0; |
155 | } | |
156 | ||
dc59c133 ML |
157 | static int si544_set_delta_m(struct clk_si544 *data, s32 delta_m) |
158 | { | |
159 | u8 reg[3]; | |
160 | ||
161 | reg[0] = delta_m; | |
162 | reg[1] = delta_m >> 8; | |
163 | reg[2] = delta_m >> 16; | |
164 | ||
165 | return regmap_bulk_write(data->regmap, SI544_REG_ADPLL_DELTA_M0, | |
166 | reg, 3); | |
167 | } | |
168 | ||
953cc3e8 ML |
169 | static int si544_set_muldiv(struct clk_si544 *data, |
170 | struct clk_si544_muldiv *settings) | |
171 | { | |
172 | int err; | |
173 | u8 reg[6]; | |
174 | ||
175 | reg[0] = settings->hs_div; | |
176 | reg[1] = settings->hs_div >> 8 | settings->ls_div_bits << 4; | |
177 | ||
178 | err = regmap_bulk_write(data->regmap, SI544_REG_HS_DIV, reg, 2); | |
179 | if (err < 0) | |
180 | return err; | |
181 | ||
182 | reg[0] = settings->fb_div_frac; | |
183 | reg[1] = settings->fb_div_frac >> 8; | |
184 | reg[2] = settings->fb_div_frac >> 16; | |
185 | reg[3] = settings->fb_div_frac >> 24; | |
186 | reg[4] = settings->fb_div_int; | |
187 | reg[5] = settings->fb_div_int >> 8; | |
188 | ||
189 | /* | |
190 | * Writing to SI544_REG_FBDIV40 triggers the clock change, so that | |
191 | * must be written last | |
192 | */ | |
193 | return regmap_bulk_write(data->regmap, SI544_REG_FBDIV0, reg, 6); | |
194 | } | |
195 | ||
196 | static bool is_valid_frequency(const struct clk_si544 *data, | |
197 | unsigned long frequency) | |
198 | { | |
199 | unsigned long max_freq = 0; | |
200 | ||
201 | if (frequency < SI544_MIN_FREQ) | |
202 | return false; | |
203 | ||
204 | switch (data->speed_grade) { | |
205 | case si544a: | |
206 | max_freq = 1500000000; | |
207 | break; | |
208 | case si544b: | |
209 | max_freq = 800000000; | |
210 | break; | |
211 | case si544c: | |
212 | max_freq = 350000000; | |
213 | break; | |
214 | } | |
215 | ||
216 | return frequency <= max_freq; | |
217 | } | |
218 | ||
219 | /* Calculate divider settings for a given frequency */ | |
220 | static int si544_calc_muldiv(struct clk_si544_muldiv *settings, | |
221 | unsigned long frequency) | |
222 | { | |
223 | u64 vco; | |
224 | u32 ls_freq; | |
225 | u32 tmp; | |
226 | u8 res; | |
227 | ||
228 | /* Determine the minimum value of LS_DIV and resulting target freq. */ | |
229 | ls_freq = frequency; | |
230 | settings->ls_div_bits = 0; | |
231 | ||
232 | if (frequency >= MIN_HSDIV_FREQ) { | |
233 | settings->ls_div_bits = 0; | |
234 | } else { | |
235 | res = 1; | |
236 | tmp = 2 * HS_DIV_MAX; | |
237 | while (tmp <= (HS_DIV_MAX * 32)) { | |
238 | if (((u64)frequency * tmp) >= FVCO_MIN) | |
239 | break; | |
240 | ++res; | |
241 | tmp <<= 1; | |
242 | } | |
243 | settings->ls_div_bits = res; | |
244 | ls_freq = frequency << res; | |
245 | } | |
246 | ||
247 | /* Determine minimum HS_DIV by rounding up */ | |
248 | vco = FVCO_MIN + ls_freq - 1; | |
249 | do_div(vco, ls_freq); | |
250 | settings->hs_div = vco; | |
251 | ||
252 | /* round up to even number when required */ | |
253 | if ((settings->hs_div & 1) && | |
254 | (settings->hs_div > HS_DIV_MAX_ODD || settings->ls_div_bits)) | |
255 | ++settings->hs_div; | |
256 | ||
257 | /* Calculate VCO frequency (in 10..12GHz range) */ | |
258 | vco = (u64)ls_freq * settings->hs_div; | |
259 | ||
260 | /* Calculate the integer part of the feedback divider */ | |
261 | tmp = do_div(vco, FXO); | |
262 | settings->fb_div_int = vco; | |
263 | ||
264 | /* And the fractional bits using the remainder */ | |
265 | vco = (u64)tmp << 32; | |
4d3f36c5 | 266 | vco += FXO / 2; /* Round to nearest multiple */ |
953cc3e8 ML |
267 | do_div(vco, FXO); |
268 | settings->fb_div_frac = vco; | |
269 | ||
dc59c133 ML |
270 | /* Reset the frequency adjustment */ |
271 | settings->delta_m = 0; | |
272 | ||
953cc3e8 ML |
273 | return 0; |
274 | } | |
275 | ||
276 | /* Calculate resulting frequency given the register settings */ | |
dc59c133 ML |
277 | static unsigned long si544_calc_center_rate( |
278 | const struct clk_si544_muldiv *settings) | |
953cc3e8 ML |
279 | { |
280 | u32 d = settings->hs_div * BIT(settings->ls_div_bits); | |
281 | u64 vco; | |
282 | ||
283 | /* Calculate VCO from the fractional part */ | |
284 | vco = (u64)settings->fb_div_frac * FXO; | |
285 | vco += (FXO / 2); | |
286 | vco >>= 32; | |
287 | ||
288 | /* Add the integer part of the VCO frequency */ | |
289 | vco += (u64)settings->fb_div_int * FXO; | |
290 | ||
291 | /* Apply divider to obtain the generated frequency */ | |
292 | do_div(vco, d); | |
293 | ||
294 | return vco; | |
295 | } | |
296 | ||
dc59c133 ML |
297 | static unsigned long si544_calc_rate(const struct clk_si544_muldiv *settings) |
298 | { | |
299 | unsigned long rate = si544_calc_center_rate(settings); | |
300 | s64 delta = (s64)rate * (DELTA_M_FRAC_NUM * settings->delta_m); | |
301 | ||
302 | /* | |
303 | * The clock adjustment is much smaller than 1 Hz, round to the | |
304 | * nearest multiple. Apparently div64_s64 rounds towards zero, hence | |
305 | * check the sign and adjust into the proper direction. | |
306 | */ | |
307 | if (settings->delta_m < 0) | |
308 | delta -= ((s64)DELTA_M_MAX * DELTA_M_FRAC_DEN) / 2; | |
309 | else | |
310 | delta += ((s64)DELTA_M_MAX * DELTA_M_FRAC_DEN) / 2; | |
311 | delta = div64_s64(delta, ((s64)DELTA_M_MAX * DELTA_M_FRAC_DEN)); | |
312 | ||
313 | return rate + delta; | |
314 | } | |
315 | ||
953cc3e8 ML |
316 | static unsigned long si544_recalc_rate(struct clk_hw *hw, |
317 | unsigned long parent_rate) | |
318 | { | |
319 | struct clk_si544 *data = to_clk_si544(hw); | |
320 | struct clk_si544_muldiv settings; | |
321 | int err; | |
322 | ||
323 | err = si544_get_muldiv(data, &settings); | |
324 | if (err) | |
325 | return 0; | |
326 | ||
327 | return si544_calc_rate(&settings); | |
328 | } | |
329 | ||
330 | static long si544_round_rate(struct clk_hw *hw, unsigned long rate, | |
331 | unsigned long *parent_rate) | |
332 | { | |
333 | struct clk_si544 *data = to_clk_si544(hw); | |
953cc3e8 ML |
334 | |
335 | if (!is_valid_frequency(data, rate)) | |
336 | return -EINVAL; | |
337 | ||
dc59c133 ML |
338 | /* The accuracy is less than 1 Hz, so any rate is possible */ |
339 | return rate; | |
340 | } | |
953cc3e8 | 341 | |
dc59c133 ML |
342 | /* Calculates the maximum "small" change, 950 * rate / 1000000 */ |
343 | static unsigned long si544_max_delta(unsigned long rate) | |
344 | { | |
345 | u64 num = rate; | |
346 | ||
347 | num *= DELTA_M_FRAC_NUM; | |
348 | do_div(num, DELTA_M_FRAC_DEN); | |
349 | ||
350 | return num; | |
351 | } | |
352 | ||
353 | static s32 si544_calc_delta(s32 delta, s32 max_delta) | |
354 | { | |
355 | s64 n = (s64)delta * DELTA_M_MAX; | |
356 | ||
357 | return div_s64(n, max_delta); | |
953cc3e8 ML |
358 | } |
359 | ||
953cc3e8 ML |
360 | static int si544_set_rate(struct clk_hw *hw, unsigned long rate, |
361 | unsigned long parent_rate) | |
362 | { | |
363 | struct clk_si544 *data = to_clk_si544(hw); | |
364 | struct clk_si544_muldiv settings; | |
dc59c133 ML |
365 | unsigned long center; |
366 | long max_delta; | |
367 | long delta; | |
e8f127ca | 368 | unsigned int old_oe_state; |
953cc3e8 ML |
369 | int err; |
370 | ||
371 | if (!is_valid_frequency(data, rate)) | |
372 | return -EINVAL; | |
373 | ||
dc59c133 ML |
374 | /* Try using the frequency adjustment feature for a <= 950ppm change */ |
375 | err = si544_get_muldiv(data, &settings); | |
376 | if (err) | |
377 | return err; | |
378 | ||
379 | center = si544_calc_center_rate(&settings); | |
380 | max_delta = si544_max_delta(center); | |
381 | delta = rate - center; | |
382 | ||
383 | if (abs(delta) <= max_delta) | |
384 | return si544_set_delta_m(data, | |
385 | si544_calc_delta(delta, max_delta)); | |
386 | ||
387 | /* Too big for the delta adjustment, need to reprogram */ | |
953cc3e8 ML |
388 | err = si544_calc_muldiv(&settings, rate); |
389 | if (err) | |
390 | return err; | |
391 | ||
e8f127ca ML |
392 | err = regmap_read(data->regmap, SI544_REG_OE_STATE, &old_oe_state); |
393 | if (err) | |
394 | return err; | |
395 | ||
953cc3e8 ML |
396 | si544_enable_output(data, false); |
397 | ||
398 | /* Allow FCAL for this frequency update */ | |
399 | err = regmap_write(data->regmap, SI544_REG_FCAL_OVR, 0); | |
400 | if (err < 0) | |
401 | return err; | |
402 | ||
dc59c133 ML |
403 | err = si544_set_delta_m(data, settings.delta_m); |
404 | if (err < 0) | |
405 | return err; | |
953cc3e8 ML |
406 | |
407 | err = si544_set_muldiv(data, &settings); | |
408 | if (err < 0) | |
409 | return err; /* Undefined state now, best to leave disabled */ | |
410 | ||
411 | /* Trigger calibration */ | |
412 | err = regmap_write(data->regmap, SI544_REG_CONTROL, | |
413 | SI544_CONTROL_MS_ICAL2); | |
414 | if (err < 0) | |
415 | return err; | |
416 | ||
417 | /* Applying a new frequency can take up to 10ms */ | |
418 | usleep_range(10000, 12000); | |
419 | ||
e8f127ca ML |
420 | if (old_oe_state & SI544_OE_STATE_ODC_OE) |
421 | si544_enable_output(data, true); | |
953cc3e8 ML |
422 | |
423 | return err; | |
424 | } | |
425 | ||
426 | static const struct clk_ops si544_clk_ops = { | |
e8f127ca ML |
427 | .prepare = si544_prepare, |
428 | .unprepare = si544_unprepare, | |
429 | .is_prepared = si544_is_prepared, | |
953cc3e8 ML |
430 | .recalc_rate = si544_recalc_rate, |
431 | .round_rate = si544_round_rate, | |
432 | .set_rate = si544_set_rate, | |
433 | }; | |
434 | ||
435 | static bool si544_regmap_is_volatile(struct device *dev, unsigned int reg) | |
436 | { | |
437 | switch (reg) { | |
438 | case SI544_REG_CONTROL: | |
439 | case SI544_REG_FCAL_OVR: | |
440 | return true; | |
441 | default: | |
442 | return false; | |
443 | } | |
444 | } | |
445 | ||
446 | static const struct regmap_config si544_regmap_config = { | |
447 | .reg_bits = 8, | |
448 | .val_bits = 8, | |
449 | .cache_type = REGCACHE_RBTREE, | |
450 | .max_register = SI544_REG_PAGE_SELECT, | |
451 | .volatile_reg = si544_regmap_is_volatile, | |
452 | }; | |
453 | ||
454 | static int si544_probe(struct i2c_client *client, | |
455 | const struct i2c_device_id *id) | |
456 | { | |
457 | struct clk_si544 *data; | |
458 | struct clk_init_data init; | |
459 | int err; | |
460 | ||
461 | data = devm_kzalloc(&client->dev, sizeof(*data), GFP_KERNEL); | |
462 | if (!data) | |
463 | return -ENOMEM; | |
464 | ||
465 | init.ops = &si544_clk_ops; | |
466 | init.flags = 0; | |
467 | init.num_parents = 0; | |
468 | data->hw.init = &init; | |
469 | data->i2c_client = client; | |
470 | data->speed_grade = id->driver_data; | |
471 | ||
472 | if (of_property_read_string(client->dev.of_node, "clock-output-names", | |
473 | &init.name)) | |
474 | init.name = client->dev.of_node->name; | |
475 | ||
476 | data->regmap = devm_regmap_init_i2c(client, &si544_regmap_config); | |
477 | if (IS_ERR(data->regmap)) | |
478 | return PTR_ERR(data->regmap); | |
479 | ||
480 | i2c_set_clientdata(client, data); | |
481 | ||
482 | /* Select page 0, just to be sure, there appear to be no more */ | |
483 | err = regmap_write(data->regmap, SI544_REG_PAGE_SELECT, 0); | |
484 | if (err < 0) | |
485 | return err; | |
486 | ||
487 | err = devm_clk_hw_register(&client->dev, &data->hw); | |
488 | if (err) { | |
489 | dev_err(&client->dev, "clock registration failed\n"); | |
490 | return err; | |
491 | } | |
492 | err = devm_of_clk_add_hw_provider(&client->dev, of_clk_hw_simple_get, | |
493 | &data->hw); | |
494 | if (err) { | |
495 | dev_err(&client->dev, "unable to add clk provider\n"); | |
496 | return err; | |
497 | } | |
498 | ||
499 | return 0; | |
500 | } | |
501 | ||
502 | static const struct i2c_device_id si544_id[] = { | |
503 | { "si544a", si544a }, | |
504 | { "si544b", si544b }, | |
505 | { "si544c", si544c }, | |
506 | { } | |
507 | }; | |
508 | MODULE_DEVICE_TABLE(i2c, si544_id); | |
509 | ||
510 | static const struct of_device_id clk_si544_of_match[] = { | |
511 | { .compatible = "silabs,si544a" }, | |
512 | { .compatible = "silabs,si544b" }, | |
513 | { .compatible = "silabs,si544c" }, | |
514 | { }, | |
515 | }; | |
516 | MODULE_DEVICE_TABLE(of, clk_si544_of_match); | |
517 | ||
518 | static struct i2c_driver si544_driver = { | |
519 | .driver = { | |
520 | .name = "si544", | |
521 | .of_match_table = clk_si544_of_match, | |
522 | }, | |
523 | .probe = si544_probe, | |
524 | .id_table = si544_id, | |
525 | }; | |
526 | module_i2c_driver(si544_driver); | |
527 | ||
528 | MODULE_AUTHOR("Mike Looijmans <mike.looijmans@topic.nl>"); | |
529 | MODULE_DESCRIPTION("Si544 driver"); | |
530 | MODULE_LICENSE("GPL"); |