Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /*
0003  * Driver for Silicon Labs Si544 Programmable Oscillator
0004  * Copyright (C) 2018 Topic Embedded Products
0005  * Author: Mike Looijmans <mike.looijmans@topic.nl>
0006  */
0007 
0008 #include <linux/clk-provider.h>
0009 #include <linux/delay.h>
0010 #include <linux/math64.h>
0011 #include <linux/module.h>
0012 #include <linux/i2c.h>
0013 #include <linux/regmap.h>
0014 #include <linux/slab.h>
0015 
0016 /* I2C registers (decimal as in datasheet) */
0017 #define SI544_REG_CONTROL   7
0018 #define SI544_REG_OE_STATE  17
0019 #define SI544_REG_HS_DIV    23
0020 #define SI544_REG_LS_HS_DIV 24
0021 #define SI544_REG_FBDIV0    26
0022 #define SI544_REG_FBDIV8    27
0023 #define SI544_REG_FBDIV16   28
0024 #define SI544_REG_FBDIV24   29
0025 #define SI544_REG_FBDIV32   30
0026 #define SI544_REG_FBDIV40   31
0027 #define SI544_REG_FCAL_OVR  69
0028 #define SI544_REG_ADPLL_DELTA_M0    231
0029 #define SI544_REG_ADPLL_DELTA_M8    232
0030 #define SI544_REG_ADPLL_DELTA_M16   233
0031 #define SI544_REG_PAGE_SELECT   255
0032 
0033 /* Register values */
0034 #define SI544_CONTROL_RESET BIT(7)
0035 #define SI544_CONTROL_MS_ICAL2  BIT(3)
0036 
0037 #define SI544_OE_STATE_ODC_OE   BIT(0)
0038 
0039 /* Max freq depends on speed grade */
0040 #define SI544_MIN_FREQ      200000U
0041 
0042 /* Si544 Internal oscilator runs at 55.05 MHz */
0043 #define FXO       55050000U
0044 
0045 /* VCO range is 10.8 .. 12.1 GHz, max depends on speed grade */
0046 #define FVCO_MIN       10800000000ULL
0047 
0048 #define HS_DIV_MAX  2046
0049 #define HS_DIV_MAX_ODD  33
0050 
0051 /* Lowest frequency synthesizeable using only the HS divider */
0052 #define MIN_HSDIV_FREQ  (FVCO_MIN / HS_DIV_MAX)
0053 
0054 /* Range and interpretation of the adjustment value */
0055 #define DELTA_M_MAX 8161512
0056 #define DELTA_M_FRAC_NUM    19
0057 #define DELTA_M_FRAC_DEN    20000
0058 
0059 enum si544_speed_grade {
0060     si544a,
0061     si544b,
0062     si544c,
0063 };
0064 
0065 struct clk_si544 {
0066     struct clk_hw hw;
0067     struct regmap *regmap;
0068     struct i2c_client *i2c_client;
0069     enum si544_speed_grade speed_grade;
0070 };
0071 #define to_clk_si544(_hw)   container_of(_hw, struct clk_si544, hw)
0072 
0073 /**
0074  * struct clk_si544_muldiv - Multiplier/divider settings
0075  * @fb_div_frac:    integer part of feedback divider (32 bits)
0076  * @fb_div_int:     fractional part of feedback divider (11 bits)
0077  * @hs_div:     1st divider, 5..2046, must be even when >33
0078  * @ls_div_bits:    2nd divider, as 2^x, range 0..5
0079  *                      If ls_div_bits is non-zero, hs_div must be even
0080  * @delta_m:        Frequency shift for small -950..+950 ppm changes, 24 bit
0081  */
0082 struct clk_si544_muldiv {
0083     u32 fb_div_frac;
0084     u16 fb_div_int;
0085     u16 hs_div;
0086     u8 ls_div_bits;
0087     s32 delta_m;
0088 };
0089 
0090 /* Enables or disables the output driver */
0091 static int si544_enable_output(struct clk_si544 *data, bool enable)
0092 {
0093     return regmap_update_bits(data->regmap, SI544_REG_OE_STATE,
0094         SI544_OE_STATE_ODC_OE, enable ? SI544_OE_STATE_ODC_OE : 0);
0095 }
0096 
0097 static int si544_prepare(struct clk_hw *hw)
0098 {
0099     struct clk_si544 *data = to_clk_si544(hw);
0100 
0101     return si544_enable_output(data, true);
0102 }
0103 
0104 static void si544_unprepare(struct clk_hw *hw)
0105 {
0106     struct clk_si544 *data = to_clk_si544(hw);
0107 
0108     si544_enable_output(data, false);
0109 }
0110 
0111 static int si544_is_prepared(struct clk_hw *hw)
0112 {
0113     struct clk_si544 *data = to_clk_si544(hw);
0114     unsigned int val;
0115     int err;
0116 
0117     err = regmap_read(data->regmap, SI544_REG_OE_STATE, &val);
0118     if (err < 0)
0119         return err;
0120 
0121     return !!(val & SI544_OE_STATE_ODC_OE);
0122 }
0123 
0124 /* Retrieve clock multiplier and dividers from hardware */
0125 static int si544_get_muldiv(struct clk_si544 *data,
0126     struct clk_si544_muldiv *settings)
0127 {
0128     int err;
0129     u8 reg[6];
0130 
0131     err = regmap_bulk_read(data->regmap, SI544_REG_HS_DIV, reg, 2);
0132     if (err)
0133         return err;
0134 
0135     settings->ls_div_bits = (reg[1] >> 4) & 0x07;
0136     settings->hs_div = (reg[1] & 0x07) << 8 | reg[0];
0137 
0138     err = regmap_bulk_read(data->regmap, SI544_REG_FBDIV0, reg, 6);
0139     if (err)
0140         return err;
0141 
0142     settings->fb_div_int = reg[4] | (reg[5] & 0x07) << 8;
0143     settings->fb_div_frac = reg[0] | reg[1] << 8 | reg[2] << 16 |
0144                 reg[3] << 24;
0145 
0146     err = regmap_bulk_read(data->regmap, SI544_REG_ADPLL_DELTA_M0, reg, 3);
0147     if (err)
0148         return err;
0149 
0150     /* Interpret as 24-bit signed number */
0151     settings->delta_m = reg[0] << 8 | reg[1] << 16 | reg[2] << 24;
0152     settings->delta_m >>= 8;
0153 
0154     return 0;
0155 }
0156 
0157 static int si544_set_delta_m(struct clk_si544 *data, s32 delta_m)
0158 {
0159     u8 reg[3];
0160 
0161     reg[0] = delta_m;
0162     reg[1] = delta_m >> 8;
0163     reg[2] = delta_m >> 16;
0164 
0165     return regmap_bulk_write(data->regmap, SI544_REG_ADPLL_DELTA_M0,
0166                  reg, 3);
0167 }
0168 
0169 static int si544_set_muldiv(struct clk_si544 *data,
0170     struct clk_si544_muldiv *settings)
0171 {
0172     int err;
0173     u8 reg[6];
0174 
0175     reg[0] = settings->hs_div;
0176     reg[1] = settings->hs_div >> 8 | settings->ls_div_bits << 4;
0177 
0178     err = regmap_bulk_write(data->regmap, SI544_REG_HS_DIV, reg, 2);
0179     if (err < 0)
0180         return err;
0181 
0182     reg[0] = settings->fb_div_frac;
0183     reg[1] = settings->fb_div_frac >> 8;
0184     reg[2] = settings->fb_div_frac >> 16;
0185     reg[3] = settings->fb_div_frac >> 24;
0186     reg[4] = settings->fb_div_int;
0187     reg[5] = settings->fb_div_int >> 8;
0188 
0189     /*
0190      * Writing to SI544_REG_FBDIV40 triggers the clock change, so that
0191      * must be written last
0192      */
0193     return regmap_bulk_write(data->regmap, SI544_REG_FBDIV0, reg, 6);
0194 }
0195 
0196 static bool is_valid_frequency(const struct clk_si544 *data,
0197     unsigned long frequency)
0198 {
0199     unsigned long max_freq = 0;
0200 
0201     if (frequency < SI544_MIN_FREQ)
0202         return false;
0203 
0204     switch (data->speed_grade) {
0205     case si544a:
0206         max_freq = 1500000000;
0207         break;
0208     case si544b:
0209         max_freq = 800000000;
0210         break;
0211     case si544c:
0212         max_freq = 350000000;
0213         break;
0214     }
0215 
0216     return frequency <= max_freq;
0217 }
0218 
0219 /* Calculate divider settings for a given frequency */
0220 static int si544_calc_muldiv(struct clk_si544_muldiv *settings,
0221     unsigned long frequency)
0222 {
0223     u64 vco;
0224     u32 ls_freq;
0225     u32 tmp;
0226     u8 res;
0227 
0228     /* Determine the minimum value of LS_DIV and resulting target freq. */
0229     ls_freq = frequency;
0230     settings->ls_div_bits = 0;
0231 
0232     if (frequency >= MIN_HSDIV_FREQ) {
0233         settings->ls_div_bits = 0;
0234     } else {
0235         res = 1;
0236         tmp = 2 * HS_DIV_MAX;
0237         while (tmp <= (HS_DIV_MAX * 32)) {
0238             if (((u64)frequency * tmp) >= FVCO_MIN)
0239                 break;
0240             ++res;
0241             tmp <<= 1;
0242         }
0243         settings->ls_div_bits = res;
0244         ls_freq = frequency << res;
0245     }
0246 
0247     /* Determine minimum HS_DIV by rounding up */
0248     vco = FVCO_MIN + ls_freq - 1;
0249     do_div(vco, ls_freq);
0250     settings->hs_div = vco;
0251 
0252     /* round up to even number when required */
0253     if ((settings->hs_div & 1) &&
0254         (settings->hs_div > HS_DIV_MAX_ODD || settings->ls_div_bits))
0255         ++settings->hs_div;
0256 
0257     /* Calculate VCO frequency (in 10..12GHz range) */
0258     vco = (u64)ls_freq * settings->hs_div;
0259 
0260     /* Calculate the integer part of the feedback divider */
0261     tmp = do_div(vco, FXO);
0262     settings->fb_div_int = vco;
0263 
0264     /* And the fractional bits using the remainder */
0265     vco = (u64)tmp << 32;
0266     vco += FXO / 2; /* Round to nearest multiple */
0267     do_div(vco, FXO);
0268     settings->fb_div_frac = vco;
0269 
0270     /* Reset the frequency adjustment */
0271     settings->delta_m = 0;
0272 
0273     return 0;
0274 }
0275 
0276 /* Calculate resulting frequency given the register settings */
0277 static unsigned long si544_calc_center_rate(
0278         const struct clk_si544_muldiv *settings)
0279 {
0280     u32 d = settings->hs_div * BIT(settings->ls_div_bits);
0281     u64 vco;
0282 
0283     /* Calculate VCO from the fractional part */
0284     vco = (u64)settings->fb_div_frac * FXO;
0285     vco += (FXO / 2);
0286     vco >>= 32;
0287 
0288     /* Add the integer part of the VCO frequency */
0289     vco += (u64)settings->fb_div_int * FXO;
0290 
0291     /* Apply divider to obtain the generated frequency */
0292     do_div(vco, d);
0293 
0294     return vco;
0295 }
0296 
0297 static unsigned long si544_calc_rate(const struct clk_si544_muldiv *settings)
0298 {
0299     unsigned long rate = si544_calc_center_rate(settings);
0300     s64 delta = (s64)rate * (DELTA_M_FRAC_NUM * settings->delta_m);
0301 
0302     /*
0303      * The clock adjustment is much smaller than 1 Hz, round to the
0304      * nearest multiple. Apparently div64_s64 rounds towards zero, hence
0305      * check the sign and adjust into the proper direction.
0306      */
0307     if (settings->delta_m < 0)
0308         delta -= ((s64)DELTA_M_MAX * DELTA_M_FRAC_DEN) / 2;
0309     else
0310         delta += ((s64)DELTA_M_MAX * DELTA_M_FRAC_DEN) / 2;
0311     delta = div64_s64(delta, ((s64)DELTA_M_MAX * DELTA_M_FRAC_DEN));
0312 
0313     return rate + delta;
0314 }
0315 
0316 static unsigned long si544_recalc_rate(struct clk_hw *hw,
0317         unsigned long parent_rate)
0318 {
0319     struct clk_si544 *data = to_clk_si544(hw);
0320     struct clk_si544_muldiv settings;
0321     int err;
0322 
0323     err = si544_get_muldiv(data, &settings);
0324     if (err)
0325         return 0;
0326 
0327     return si544_calc_rate(&settings);
0328 }
0329 
0330 static long si544_round_rate(struct clk_hw *hw, unsigned long rate,
0331         unsigned long *parent_rate)
0332 {
0333     struct clk_si544 *data = to_clk_si544(hw);
0334 
0335     if (!is_valid_frequency(data, rate))
0336         return -EINVAL;
0337 
0338     /* The accuracy is less than 1 Hz, so any rate is possible */
0339     return rate;
0340 }
0341 
0342 /* Calculates the maximum "small" change, 950 * rate / 1000000 */
0343 static unsigned long si544_max_delta(unsigned long rate)
0344 {
0345     u64 num = rate;
0346 
0347     num *= DELTA_M_FRAC_NUM;
0348     do_div(num, DELTA_M_FRAC_DEN);
0349 
0350     return num;
0351 }
0352 
0353 static s32 si544_calc_delta(s32 delta, s32 max_delta)
0354 {
0355     s64 n = (s64)delta * DELTA_M_MAX;
0356 
0357     return div_s64(n, max_delta);
0358 }
0359 
0360 static int si544_set_rate(struct clk_hw *hw, unsigned long rate,
0361         unsigned long parent_rate)
0362 {
0363     struct clk_si544 *data = to_clk_si544(hw);
0364     struct clk_si544_muldiv settings;
0365     unsigned long center;
0366     long max_delta;
0367     long delta;
0368     unsigned int old_oe_state;
0369     int err;
0370 
0371     if (!is_valid_frequency(data, rate))
0372         return -EINVAL;
0373 
0374     /* Try using the frequency adjustment feature for a <= 950ppm change */
0375     err = si544_get_muldiv(data, &settings);
0376     if (err)
0377         return err;
0378 
0379     center = si544_calc_center_rate(&settings);
0380     max_delta = si544_max_delta(center);
0381     delta = rate - center;
0382 
0383     if (abs(delta) <= max_delta)
0384         return si544_set_delta_m(data,
0385                      si544_calc_delta(delta, max_delta));
0386 
0387     /* Too big for the delta adjustment, need to reprogram */
0388     err = si544_calc_muldiv(&settings, rate);
0389     if (err)
0390         return err;
0391 
0392     err = regmap_read(data->regmap, SI544_REG_OE_STATE, &old_oe_state);
0393     if (err)
0394         return err;
0395 
0396     si544_enable_output(data, false);
0397 
0398     /* Allow FCAL for this frequency update */
0399     err = regmap_write(data->regmap, SI544_REG_FCAL_OVR, 0);
0400     if (err < 0)
0401         return err;
0402 
0403     err = si544_set_delta_m(data, settings.delta_m);
0404     if (err < 0)
0405         return err;
0406 
0407     err = si544_set_muldiv(data, &settings);
0408     if (err < 0)
0409         return err; /* Undefined state now, best to leave disabled */
0410 
0411     /* Trigger calibration */
0412     err = regmap_write(data->regmap, SI544_REG_CONTROL,
0413                SI544_CONTROL_MS_ICAL2);
0414     if (err < 0)
0415         return err;
0416 
0417     /* Applying a new frequency can take up to 10ms */
0418     usleep_range(10000, 12000);
0419 
0420     if (old_oe_state & SI544_OE_STATE_ODC_OE)
0421         si544_enable_output(data, true);
0422 
0423     return err;
0424 }
0425 
0426 static const struct clk_ops si544_clk_ops = {
0427     .prepare = si544_prepare,
0428     .unprepare = si544_unprepare,
0429     .is_prepared = si544_is_prepared,
0430     .recalc_rate = si544_recalc_rate,
0431     .round_rate = si544_round_rate,
0432     .set_rate = si544_set_rate,
0433 };
0434 
0435 static bool si544_regmap_is_volatile(struct device *dev, unsigned int reg)
0436 {
0437     switch (reg) {
0438     case SI544_REG_CONTROL:
0439     case SI544_REG_FCAL_OVR:
0440         return true;
0441     default:
0442         return false;
0443     }
0444 }
0445 
0446 static const struct regmap_config si544_regmap_config = {
0447     .reg_bits = 8,
0448     .val_bits = 8,
0449     .cache_type = REGCACHE_RBTREE,
0450     .max_register = SI544_REG_PAGE_SELECT,
0451     .volatile_reg = si544_regmap_is_volatile,
0452 };
0453 
0454 static const struct i2c_device_id si544_id[] = {
0455     { "si544a", si544a },
0456     { "si544b", si544b },
0457     { "si544c", si544c },
0458     { }
0459 };
0460 MODULE_DEVICE_TABLE(i2c, si544_id);
0461 
0462 static int si544_probe(struct i2c_client *client)
0463 {
0464     struct clk_si544 *data;
0465     struct clk_init_data init;
0466     const struct i2c_device_id *id = i2c_match_id(si544_id, client);
0467     int err;
0468 
0469     data = devm_kzalloc(&client->dev, sizeof(*data), GFP_KERNEL);
0470     if (!data)
0471         return -ENOMEM;
0472 
0473     init.ops = &si544_clk_ops;
0474     init.flags = 0;
0475     init.num_parents = 0;
0476     data->hw.init = &init;
0477     data->i2c_client = client;
0478     data->speed_grade = id->driver_data;
0479 
0480     if (of_property_read_string(client->dev.of_node, "clock-output-names",
0481             &init.name))
0482         init.name = client->dev.of_node->name;
0483 
0484     data->regmap = devm_regmap_init_i2c(client, &si544_regmap_config);
0485     if (IS_ERR(data->regmap))
0486         return PTR_ERR(data->regmap);
0487 
0488     i2c_set_clientdata(client, data);
0489 
0490     /* Select page 0, just to be sure, there appear to be no more */
0491     err = regmap_write(data->regmap, SI544_REG_PAGE_SELECT, 0);
0492     if (err < 0)
0493         return err;
0494 
0495     err = devm_clk_hw_register(&client->dev, &data->hw);
0496     if (err) {
0497         dev_err(&client->dev, "clock registration failed\n");
0498         return err;
0499     }
0500     err = devm_of_clk_add_hw_provider(&client->dev, of_clk_hw_simple_get,
0501                       &data->hw);
0502     if (err) {
0503         dev_err(&client->dev, "unable to add clk provider\n");
0504         return err;
0505     }
0506 
0507     return 0;
0508 }
0509 
0510 static const struct of_device_id clk_si544_of_match[] = {
0511     { .compatible = "silabs,si544a" },
0512     { .compatible = "silabs,si544b" },
0513     { .compatible = "silabs,si544c" },
0514     { },
0515 };
0516 MODULE_DEVICE_TABLE(of, clk_si544_of_match);
0517 
0518 static struct i2c_driver si544_driver = {
0519     .driver = {
0520         .name = "si544",
0521         .of_match_table = clk_si544_of_match,
0522     },
0523     .probe_new  = si544_probe,
0524     .id_table   = si544_id,
0525 };
0526 module_i2c_driver(si544_driver);
0527 
0528 MODULE_AUTHOR("Mike Looijmans <mike.looijmans@topic.nl>");
0529 MODULE_DESCRIPTION("Si544 driver");
0530 MODULE_LICENSE("GPL");