.. | .. |
---|
7 | 7 | |
---|
8 | 8 | #include <linux/clk-provider.h> |
---|
9 | 9 | #include <linux/delay.h> |
---|
| 10 | +#include <linux/math64.h> |
---|
10 | 11 | #include <linux/module.h> |
---|
11 | 12 | #include <linux/i2c.h> |
---|
12 | 13 | #include <linux/regmap.h> |
---|
.. | .. |
---|
50 | 51 | /* Lowest frequency synthesizeable using only the HS divider */ |
---|
51 | 52 | #define MIN_HSDIV_FREQ (FVCO_MIN / HS_DIV_MAX) |
---|
52 | 53 | |
---|
| 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 | + |
---|
53 | 59 | enum si544_speed_grade { |
---|
54 | 60 | si544a, |
---|
55 | 61 | si544b, |
---|
.. | .. |
---|
71 | 77 | * @hs_div: 1st divider, 5..2046, must be even when >33 |
---|
72 | 78 | * @ls_div_bits: 2nd divider, as 2^x, range 0..5 |
---|
73 | 79 | * 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 |
---|
74 | 81 | */ |
---|
75 | 82 | struct clk_si544_muldiv { |
---|
76 | 83 | u32 fb_div_frac; |
---|
77 | 84 | u16 fb_div_int; |
---|
78 | 85 | u16 hs_div; |
---|
79 | 86 | u8 ls_div_bits; |
---|
| 87 | + s32 delta_m; |
---|
80 | 88 | }; |
---|
81 | 89 | |
---|
82 | 90 | /* Enables or disables the output driver */ |
---|
.. | .. |
---|
134 | 142 | settings->fb_div_int = reg[4] | (reg[5] & 0x07) << 8; |
---|
135 | 143 | settings->fb_div_frac = reg[0] | reg[1] << 8 | reg[2] << 16 | |
---|
136 | 144 | 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 | + |
---|
137 | 154 | 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); |
---|
138 | 167 | } |
---|
139 | 168 | |
---|
140 | 169 | static int si544_set_muldiv(struct clk_si544 *data, |
---|
.. | .. |
---|
238 | 267 | do_div(vco, FXO); |
---|
239 | 268 | settings->fb_div_frac = vco; |
---|
240 | 269 | |
---|
| 270 | + /* Reset the frequency adjustment */ |
---|
| 271 | + settings->delta_m = 0; |
---|
| 272 | + |
---|
241 | 273 | return 0; |
---|
242 | 274 | } |
---|
243 | 275 | |
---|
244 | 276 | /* 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) |
---|
246 | 279 | { |
---|
247 | 280 | u32 d = settings->hs_div * BIT(settings->ls_div_bits); |
---|
248 | 281 | u64 vco; |
---|
.. | .. |
---|
259 | 292 | do_div(vco, d); |
---|
260 | 293 | |
---|
261 | 294 | 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; |
---|
262 | 314 | } |
---|
263 | 315 | |
---|
264 | 316 | static unsigned long si544_recalc_rate(struct clk_hw *hw, |
---|
.. | .. |
---|
279 | 331 | unsigned long *parent_rate) |
---|
280 | 332 | { |
---|
281 | 333 | struct clk_si544 *data = to_clk_si544(hw); |
---|
282 | | - struct clk_si544_muldiv settings; |
---|
283 | | - int err; |
---|
284 | 334 | |
---|
285 | 335 | if (!is_valid_frequency(data, rate)) |
---|
286 | 336 | return -EINVAL; |
---|
287 | 337 | |
---|
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; |
---|
293 | 340 | } |
---|
294 | 341 | |
---|
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 | + |
---|
298 | 360 | static int si544_set_rate(struct clk_hw *hw, unsigned long rate, |
---|
299 | 361 | unsigned long parent_rate) |
---|
300 | 362 | { |
---|
301 | 363 | struct clk_si544 *data = to_clk_si544(hw); |
---|
302 | 364 | struct clk_si544_muldiv settings; |
---|
| 365 | + unsigned long center; |
---|
| 366 | + long max_delta; |
---|
| 367 | + long delta; |
---|
303 | 368 | unsigned int old_oe_state; |
---|
304 | 369 | int err; |
---|
305 | 370 | |
---|
306 | 371 | if (!is_valid_frequency(data, rate)) |
---|
307 | 372 | return -EINVAL; |
---|
308 | 373 | |
---|
| 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 */ |
---|
309 | 388 | err = si544_calc_muldiv(&settings, rate); |
---|
310 | 389 | if (err) |
---|
311 | 390 | return err; |
---|
.. | .. |
---|
321 | 400 | if (err < 0) |
---|
322 | 401 | return err; |
---|
323 | 402 | |
---|
| 403 | + err = si544_set_delta_m(data, settings.delta_m); |
---|
| 404 | + if (err < 0) |
---|
| 405 | + return err; |
---|
324 | 406 | |
---|
325 | 407 | err = si544_set_muldiv(data, &settings); |
---|
326 | 408 | if (err < 0) |
---|
.. | .. |
---|
373 | 455 | const struct i2c_device_id *id) |
---|
374 | 456 | { |
---|
375 | 457 | struct clk_si544 *data; |
---|
376 | | - struct clk_init_data init = {}; |
---|
| 458 | + struct clk_init_data init; |
---|
377 | 459 | int err; |
---|
378 | 460 | |
---|
379 | 461 | data = devm_kzalloc(&client->dev, sizeof(*data), GFP_KERNEL); |
---|