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