| .. | .. |
|---|
| 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); |
|---|