1 // SPDX-License-Identifier: GPL-2.0
3 * Driver for Silicon Labs Si544 Programmable Oscillator
4 * Copyright (C) 2018 Topic Embedded Products
5 * Author: Mike Looijmans <mike.looijmans@topic.nl>
8 #include <linux/clk-provider.h>
9 #include <linux/delay.h>
10 #include <linux/math64.h>
11 #include <linux/module.h>
12 #include <linux/i2c.h>
13 #include <linux/regmap.h>
14 #include <linux/slab.h>
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
34 #define SI544_CONTROL_RESET BIT(7)
35 #define SI544_CONTROL_MS_ICAL2 BIT(3)
37 #define SI544_OE_STATE_ODC_OE BIT(0)
39 /* Max freq depends on speed grade */
40 #define SI544_MIN_FREQ 200000U
42 /* Si544 Internal oscilator runs at 55.05 MHz */
45 /* VCO range is 10.8 .. 12.1 GHz, max depends on speed grade */
46 #define FVCO_MIN 10800000000ULL
48 #define HS_DIV_MAX 2046
49 #define HS_DIV_MAX_ODD 33
51 /* Lowest frequency synthesizeable using only the HS divider */
52 #define MIN_HSDIV_FREQ (FVCO_MIN / HS_DIV_MAX)
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
61 struct regmap
*regmap
;
62 struct i2c_client
*i2c_client
;
63 unsigned long max_freq
;
65 #define to_clk_si544(_hw) container_of(_hw, struct clk_si544, hw)
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
74 * @delta_m: Frequency shift for small -950..+950 ppm changes, 24 bit
76 struct clk_si544_muldiv
{
84 /* Enables or disables the output driver */
85 static int si544_enable_output(struct clk_si544
*data
, bool enable
)
87 return regmap_update_bits(data
->regmap
, SI544_REG_OE_STATE
,
88 SI544_OE_STATE_ODC_OE
, enable
? SI544_OE_STATE_ODC_OE
: 0);
91 static int si544_prepare(struct clk_hw
*hw
)
93 struct clk_si544
*data
= to_clk_si544(hw
);
95 return si544_enable_output(data
, true);
98 static void si544_unprepare(struct clk_hw
*hw
)
100 struct clk_si544
*data
= to_clk_si544(hw
);
102 si544_enable_output(data
, false);
105 static int si544_is_prepared(struct clk_hw
*hw
)
107 struct clk_si544
*data
= to_clk_si544(hw
);
111 err
= regmap_read(data
->regmap
, SI544_REG_OE_STATE
, &val
);
115 return !!(val
& SI544_OE_STATE_ODC_OE
);
118 /* Retrieve clock multiplier and dividers from hardware */
119 static int si544_get_muldiv(struct clk_si544
*data
,
120 struct clk_si544_muldiv
*settings
)
125 err
= regmap_bulk_read(data
->regmap
, SI544_REG_HS_DIV
, reg
, 2);
129 settings
->ls_div_bits
= (reg
[1] >> 4) & 0x07;
130 settings
->hs_div
= (reg
[1] & 0x07) << 8 | reg
[0];
132 err
= regmap_bulk_read(data
->regmap
, SI544_REG_FBDIV0
, reg
, 6);
136 settings
->fb_div_int
= reg
[4] | (reg
[5] & 0x07) << 8;
137 settings
->fb_div_frac
= reg
[0] | reg
[1] << 8 | reg
[2] << 16 |
140 err
= regmap_bulk_read(data
->regmap
, SI544_REG_ADPLL_DELTA_M0
, reg
, 3);
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;
151 static int si544_set_delta_m(struct clk_si544
*data
, s32 delta_m
)
156 reg
[1] = delta_m
>> 8;
157 reg
[2] = delta_m
>> 16;
159 return regmap_bulk_write(data
->regmap
, SI544_REG_ADPLL_DELTA_M0
,
163 static int si544_set_muldiv(struct clk_si544
*data
,
164 struct clk_si544_muldiv
*settings
)
169 reg
[0] = settings
->hs_div
;
170 reg
[1] = settings
->hs_div
>> 8 | settings
->ls_div_bits
<< 4;
172 err
= regmap_bulk_write(data
->regmap
, SI544_REG_HS_DIV
, reg
, 2);
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;
184 * Writing to SI544_REG_FBDIV40 triggers the clock change, so that
185 * must be written last
187 return regmap_bulk_write(data
->regmap
, SI544_REG_FBDIV0
, reg
, 6);
190 static bool is_valid_frequency(const struct clk_si544
*data
,
191 unsigned long frequency
)
193 if (frequency
< SI544_MIN_FREQ
)
196 return frequency
<= data
->max_freq
;
199 /* Calculate divider settings for a given frequency */
200 static int si544_calc_muldiv(struct clk_si544_muldiv
*settings
,
201 unsigned long frequency
)
208 /* Determine the minimum value of LS_DIV and resulting target freq. */
210 settings
->ls_div_bits
= 0;
212 if (frequency
>= MIN_HSDIV_FREQ
) {
213 settings
->ls_div_bits
= 0;
216 tmp
= 2 * HS_DIV_MAX
;
217 while (tmp
<= (HS_DIV_MAX
* 32)) {
218 if (((u64
)frequency
* tmp
) >= FVCO_MIN
)
223 settings
->ls_div_bits
= res
;
224 ls_freq
= frequency
<< res
;
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
;
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
))
237 /* Calculate VCO frequency (in 10..12GHz range) */
238 vco
= (u64
)ls_freq
* settings
->hs_div
;
240 /* Calculate the integer part of the feedback divider */
241 tmp
= do_div(vco
, FXO
);
242 settings
->fb_div_int
= vco
;
244 /* And the fractional bits using the remainder */
245 vco
= (u64
)tmp
<< 32;
246 vco
+= FXO
/ 2; /* Round to nearest multiple */
248 settings
->fb_div_frac
= vco
;
250 /* Reset the frequency adjustment */
251 settings
->delta_m
= 0;
256 /* Calculate resulting frequency given the register settings */
257 static unsigned long si544_calc_center_rate(
258 const struct clk_si544_muldiv
*settings
)
260 u32 d
= settings
->hs_div
* BIT(settings
->ls_div_bits
);
263 /* Calculate VCO from the fractional part */
264 vco
= (u64
)settings
->fb_div_frac
* FXO
;
268 /* Add the integer part of the VCO frequency */
269 vco
+= (u64
)settings
->fb_div_int
* FXO
;
271 /* Apply divider to obtain the generated frequency */
277 static unsigned long si544_calc_rate(const struct clk_si544_muldiv
*settings
)
279 unsigned long rate
= si544_calc_center_rate(settings
);
280 s64 delta
= (s64
)rate
* (DELTA_M_FRAC_NUM
* settings
->delta_m
);
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.
287 if (settings
->delta_m
< 0)
288 delta
-= ((s64
)DELTA_M_MAX
* DELTA_M_FRAC_DEN
) / 2;
290 delta
+= ((s64
)DELTA_M_MAX
* DELTA_M_FRAC_DEN
) / 2;
291 delta
= div64_s64(delta
, ((s64
)DELTA_M_MAX
* DELTA_M_FRAC_DEN
));
296 static unsigned long si544_recalc_rate(struct clk_hw
*hw
,
297 unsigned long parent_rate
)
299 struct clk_si544
*data
= to_clk_si544(hw
);
300 struct clk_si544_muldiv settings
;
303 err
= si544_get_muldiv(data
, &settings
);
307 return si544_calc_rate(&settings
);
310 static long si544_round_rate(struct clk_hw
*hw
, unsigned long rate
,
311 unsigned long *parent_rate
)
313 struct clk_si544
*data
= to_clk_si544(hw
);
315 if (!is_valid_frequency(data
, rate
))
318 /* The accuracy is less than 1 Hz, so any rate is possible */
322 /* Calculates the maximum "small" change, 950 * rate / 1000000 */
323 static unsigned long si544_max_delta(unsigned long rate
)
327 num
*= DELTA_M_FRAC_NUM
;
328 do_div(num
, DELTA_M_FRAC_DEN
);
333 static s32
si544_calc_delta(s32 delta
, s32 max_delta
)
335 s64 n
= (s64
)delta
* DELTA_M_MAX
;
337 return div_s64(n
, max_delta
);
340 static int si544_set_rate(struct clk_hw
*hw
, unsigned long rate
,
341 unsigned long parent_rate
)
343 struct clk_si544
*data
= to_clk_si544(hw
);
344 struct clk_si544_muldiv settings
;
345 unsigned long center
;
348 unsigned int old_oe_state
;
351 if (!is_valid_frequency(data
, rate
))
354 /* Try using the frequency adjustment feature for a <= 950ppm change */
355 err
= si544_get_muldiv(data
, &settings
);
359 center
= si544_calc_center_rate(&settings
);
360 max_delta
= si544_max_delta(center
);
361 delta
= rate
- center
;
363 if (abs(delta
) <= max_delta
)
364 return si544_set_delta_m(data
,
365 si544_calc_delta(delta
, max_delta
));
367 /* Too big for the delta adjustment, need to reprogram */
368 err
= si544_calc_muldiv(&settings
, rate
);
372 err
= regmap_read(data
->regmap
, SI544_REG_OE_STATE
, &old_oe_state
);
376 si544_enable_output(data
, false);
378 /* Allow FCAL for this frequency update */
379 err
= regmap_write(data
->regmap
, SI544_REG_FCAL_OVR
, 0);
383 err
= si544_set_delta_m(data
, settings
.delta_m
);
387 err
= si544_set_muldiv(data
, &settings
);
389 return err
; /* Undefined state now, best to leave disabled */
391 /* Trigger calibration */
392 err
= regmap_write(data
->regmap
, SI544_REG_CONTROL
,
393 SI544_CONTROL_MS_ICAL2
);
397 /* Applying a new frequency can take up to 10ms */
398 usleep_range(10000, 12000);
400 if (old_oe_state
& SI544_OE_STATE_ODC_OE
)
401 si544_enable_output(data
, true);
406 static const struct clk_ops si544_clk_ops
= {
407 .prepare
= si544_prepare
,
408 .unprepare
= si544_unprepare
,
409 .is_prepared
= si544_is_prepared
,
410 .recalc_rate
= si544_recalc_rate
,
411 .round_rate
= si544_round_rate
,
412 .set_rate
= si544_set_rate
,
415 static bool si544_regmap_is_volatile(struct device
*dev
, unsigned int reg
)
418 case SI544_REG_CONTROL
:
419 case SI544_REG_FCAL_OVR
:
426 static const struct regmap_config si544_regmap_config
= {
429 .cache_type
= REGCACHE_MAPLE
,
430 .max_register
= SI544_REG_PAGE_SELECT
,
431 .volatile_reg
= si544_regmap_is_volatile
,
434 static int si544_probe(struct i2c_client
*client
)
436 struct clk_si544
*data
;
437 struct clk_init_data init
;
440 data
= devm_kzalloc(&client
->dev
, sizeof(*data
), GFP_KERNEL
);
444 init
.ops
= &si544_clk_ops
;
446 init
.num_parents
= 0;
447 data
->hw
.init
= &init
;
448 data
->i2c_client
= client
;
449 data
->max_freq
= (uintptr_t)i2c_get_match_data(client
);
451 if (of_property_read_string(client
->dev
.of_node
, "clock-output-names",
453 init
.name
= client
->dev
.of_node
->name
;
455 data
->regmap
= devm_regmap_init_i2c(client
, &si544_regmap_config
);
456 if (IS_ERR(data
->regmap
))
457 return PTR_ERR(data
->regmap
);
459 i2c_set_clientdata(client
, data
);
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);
466 err
= devm_clk_hw_register(&client
->dev
, &data
->hw
);
468 dev_err(&client
->dev
, "clock registration failed\n");
471 err
= devm_of_clk_add_hw_provider(&client
->dev
, of_clk_hw_simple_get
,
474 dev_err(&client
->dev
, "unable to add clk provider\n");
481 static const struct i2c_device_id si544_id
[] = {
482 { "si544a", 1500000000 },
483 { "si544b", 800000000 },
484 { "si544c", 350000000 },
487 MODULE_DEVICE_TABLE(i2c
, si544_id
);
489 static const struct of_device_id clk_si544_of_match
[] = {
490 { .compatible
= "silabs,si544a", .data
= (void *)1500000000 },
491 { .compatible
= "silabs,si544b", .data
= (void *)800000000 },
492 { .compatible
= "silabs,si544c", .data
= (void *)350000000 },
495 MODULE_DEVICE_TABLE(of
, clk_si544_of_match
);
497 static struct i2c_driver si544_driver
= {
500 .of_match_table
= clk_si544_of_match
,
502 .probe
= si544_probe
,
503 .id_table
= si544_id
,
505 module_i2c_driver(si544_driver
);
507 MODULE_AUTHOR("Mike Looijmans <mike.looijmans@topic.nl>");
508 MODULE_DESCRIPTION("Si544 driver");
509 MODULE_LICENSE("GPL");