hc
2024-05-10 37f49e37ab4cb5d0bc4c60eb5c6d4dd57db767bb
kernel/drivers/clk/clk-si544.c
....@@ -7,6 +7,7 @@
77
88 #include <linux/clk-provider.h>
99 #include <linux/delay.h>
10
+#include <linux/math64.h>
1011 #include <linux/module.h>
1112 #include <linux/i2c.h>
1213 #include <linux/regmap.h>
....@@ -50,6 +51,11 @@
5051 /* Lowest frequency synthesizeable using only the HS divider */
5152 #define MIN_HSDIV_FREQ (FVCO_MIN / HS_DIV_MAX)
5253
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
+
5359 enum si544_speed_grade {
5460 si544a,
5561 si544b,
....@@ -71,12 +77,14 @@
7177 * @hs_div: 1st divider, 5..2046, must be even when >33
7278 * @ls_div_bits: 2nd divider, as 2^x, range 0..5
7379 * If ls_div_bits is non-zero, hs_div must be even
80
+ * @delta_m: Frequency shift for small -950..+950 ppm changes, 24 bit
7481 */
7582 struct clk_si544_muldiv {
7683 u32 fb_div_frac;
7784 u16 fb_div_int;
7885 u16 hs_div;
7986 u8 ls_div_bits;
87
+ s32 delta_m;
8088 };
8189
8290 /* Enables or disables the output driver */
....@@ -134,7 +142,28 @@
134142 settings->fb_div_int = reg[4] | (reg[5] & 0x07) << 8;
135143 settings->fb_div_frac = reg[0] | reg[1] << 8 | reg[2] << 16 |
136144 reg[3] << 24;
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
+
137154 return 0;
155
+}
156
+
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);
138167 }
139168
140169 static int si544_set_muldiv(struct clk_si544 *data,
....@@ -238,11 +267,15 @@
238267 do_div(vco, FXO);
239268 settings->fb_div_frac = vco;
240269
270
+ /* Reset the frequency adjustment */
271
+ settings->delta_m = 0;
272
+
241273 return 0;
242274 }
243275
244276 /* Calculate resulting frequency given the register settings */
245
-static unsigned long si544_calc_rate(struct clk_si544_muldiv *settings)
277
+static unsigned long si544_calc_center_rate(
278
+ const struct clk_si544_muldiv *settings)
246279 {
247280 u32 d = settings->hs_div * BIT(settings->ls_div_bits);
248281 u64 vco;
....@@ -259,6 +292,25 @@
259292 do_div(vco, d);
260293
261294 return vco;
295
+}
296
+
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;
262314 }
263315
264316 static unsigned long si544_recalc_rate(struct clk_hw *hw,
....@@ -279,33 +331,60 @@
279331 unsigned long *parent_rate)
280332 {
281333 struct clk_si544 *data = to_clk_si544(hw);
282
- struct clk_si544_muldiv settings;
283
- int err;
284334
285335 if (!is_valid_frequency(data, rate))
286336 return -EINVAL;
287337
288
- err = si544_calc_muldiv(&settings, rate);
289
- if (err)
290
- return err;
291
-
292
- return si544_calc_rate(&settings);
338
+ /* The accuracy is less than 1 Hz, so any rate is possible */
339
+ return rate;
293340 }
294341
295
-/*
296
- * Update output frequency for "big" frequency changes
297
- */
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);
358
+}
359
+
298360 static int si544_set_rate(struct clk_hw *hw, unsigned long rate,
299361 unsigned long parent_rate)
300362 {
301363 struct clk_si544 *data = to_clk_si544(hw);
302364 struct clk_si544_muldiv settings;
365
+ unsigned long center;
366
+ long max_delta;
367
+ long delta;
303368 unsigned int old_oe_state;
304369 int err;
305370
306371 if (!is_valid_frequency(data, rate))
307372 return -EINVAL;
308373
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 */
309388 err = si544_calc_muldiv(&settings, rate);
310389 if (err)
311390 return err;
....@@ -321,6 +400,9 @@
321400 if (err < 0)
322401 return err;
323402
403
+ err = si544_set_delta_m(data, settings.delta_m);
404
+ if (err < 0)
405
+ return err;
324406
325407 err = si544_set_muldiv(data, &settings);
326408 if (err < 0)
....@@ -373,7 +455,7 @@
373455 const struct i2c_device_id *id)
374456 {
375457 struct clk_si544 *data;
376
- struct clk_init_data init = {};
458
+ struct clk_init_data init;
377459 int err;
378460
379461 data = devm_kzalloc(&client->dev, sizeof(*data), GFP_KERNEL);