root/drivers/clk/clk-si514.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. si514_enable_output
  2. si514_prepare
  3. si514_unprepare
  4. si514_is_prepared
  5. si514_get_muldiv
  6. si514_set_muldiv
  7. si514_calc_muldiv
  8. si514_calc_rate
  9. si514_recalc_rate
  10. si514_round_rate
  11. si514_set_rate
  12. si514_regmap_is_volatile
  13. si514_regmap_is_writeable
  14. si514_probe
  15. si514_remove

   1 // SPDX-License-Identifier: GPL-2.0-or-later
   2 /*
   3  * Driver for Silicon Labs Si514 Programmable Oscillator
   4  *
   5  * Copyright (C) 2015 Topic Embedded Products
   6  *
   7  * Author: Mike Looijmans <mike.looijmans@topic.nl>
   8  */
   9 
  10 #include <linux/clk-provider.h>
  11 #include <linux/delay.h>
  12 #include <linux/module.h>
  13 #include <linux/i2c.h>
  14 #include <linux/regmap.h>
  15 #include <linux/slab.h>
  16 
  17 /* I2C registers */
  18 #define SI514_REG_LP            0
  19 #define SI514_REG_M_FRAC1       5
  20 #define SI514_REG_M_FRAC2       6
  21 #define SI514_REG_M_FRAC3       7
  22 #define SI514_REG_M_INT_FRAC    8
  23 #define SI514_REG_M_INT         9
  24 #define SI514_REG_HS_DIV        10
  25 #define SI514_REG_LS_HS_DIV     11
  26 #define SI514_REG_OE_STATE      14
  27 #define SI514_REG_RESET         128
  28 #define SI514_REG_CONTROL       132
  29 
  30 /* Register values */
  31 #define SI514_RESET_RST         BIT(7)
  32 
  33 #define SI514_CONTROL_FCAL      BIT(0)
  34 #define SI514_CONTROL_OE        BIT(2)
  35 
  36 #define SI514_MIN_FREQ      100000U
  37 #define SI514_MAX_FREQ   250000000U
  38 
  39 #define FXO               31980000U
  40 
  41 #define FVCO_MIN        2080000000U
  42 #define FVCO_MAX        2500000000U
  43 
  44 #define HS_DIV_MAX      1022
  45 
  46 struct clk_si514 {
  47         struct clk_hw hw;
  48         struct regmap *regmap;
  49         struct i2c_client *i2c_client;
  50 };
  51 #define to_clk_si514(_hw)       container_of(_hw, struct clk_si514, hw)
  52 
  53 /* Multiplier/divider settings */
  54 struct clk_si514_muldiv {
  55         u32 m_frac;  /* 29-bit Fractional part of multiplier M */
  56         u8 m_int; /* Integer part of multiplier M, 65..78 */
  57         u8 ls_div_bits; /* 2nd divider, as 2^x */
  58         u16 hs_div; /* 1st divider, must be even and 10<=x<=1022 */
  59 };
  60 
  61 /* Enables or disables the output driver */
  62 static int si514_enable_output(struct clk_si514 *data, bool enable)
  63 {
  64         return regmap_update_bits(data->regmap, SI514_REG_CONTROL,
  65                 SI514_CONTROL_OE, enable ? SI514_CONTROL_OE : 0);
  66 }
  67 
  68 static int si514_prepare(struct clk_hw *hw)
  69 {
  70         struct clk_si514 *data = to_clk_si514(hw);
  71 
  72         return si514_enable_output(data, true);
  73 }
  74 
  75 static void si514_unprepare(struct clk_hw *hw)
  76 {
  77         struct clk_si514 *data = to_clk_si514(hw);
  78 
  79         si514_enable_output(data, false);
  80 }
  81 
  82 static int si514_is_prepared(struct clk_hw *hw)
  83 {
  84         struct clk_si514 *data = to_clk_si514(hw);
  85         unsigned int val;
  86         int err;
  87 
  88         err = regmap_read(data->regmap, SI514_REG_CONTROL, &val);
  89         if (err < 0)
  90                 return err;
  91 
  92         return !!(val & SI514_CONTROL_OE);
  93 }
  94 
  95 /* Retrieve clock multiplier and dividers from hardware */
  96 static int si514_get_muldiv(struct clk_si514 *data,
  97         struct clk_si514_muldiv *settings)
  98 {
  99         int err;
 100         u8 reg[7];
 101 
 102         err = regmap_bulk_read(data->regmap, SI514_REG_M_FRAC1,
 103                         reg, ARRAY_SIZE(reg));
 104         if (err)
 105                 return err;
 106 
 107         settings->m_frac = reg[0] | reg[1] << 8 | reg[2] << 16 |
 108                            (reg[3] & 0x1F) << 24;
 109         settings->m_int = (reg[4] & 0x3f) << 3 | reg[3] >> 5;
 110         settings->ls_div_bits = (reg[6] >> 4) & 0x07;
 111         settings->hs_div = (reg[6] & 0x03) << 8 | reg[5];
 112         return 0;
 113 }
 114 
 115 static int si514_set_muldiv(struct clk_si514 *data,
 116         struct clk_si514_muldiv *settings)
 117 {
 118         u8 lp;
 119         u8 reg[7];
 120         int err;
 121 
 122         /* Calculate LP1/LP2 according to table 13 in the datasheet */
 123         /* 65.259980246 */
 124         if (settings->m_int < 65 ||
 125                 (settings->m_int == 65 && settings->m_frac <= 139575831))
 126                 lp = 0x22;
 127         /* 67.859763463 */
 128         else if (settings->m_int < 67 ||
 129                 (settings->m_int == 67 && settings->m_frac <= 461581994))
 130                 lp = 0x23;
 131         /* 72.937624981 */
 132         else if (settings->m_int < 72 ||
 133                 (settings->m_int == 72 && settings->m_frac <= 503383578))
 134                 lp = 0x33;
 135         /* 75.843265046 */
 136         else if (settings->m_int < 75 ||
 137                 (settings->m_int == 75 && settings->m_frac <= 452724474))
 138                 lp = 0x34;
 139         else
 140                 lp = 0x44;
 141 
 142         err = regmap_write(data->regmap, SI514_REG_LP, lp);
 143         if (err < 0)
 144                 return err;
 145 
 146         reg[0] = settings->m_frac;
 147         reg[1] = settings->m_frac >> 8;
 148         reg[2] = settings->m_frac >> 16;
 149         reg[3] = settings->m_frac >> 24 | settings->m_int << 5;
 150         reg[4] = settings->m_int >> 3;
 151         reg[5] = settings->hs_div;
 152         reg[6] = (settings->hs_div >> 8) | (settings->ls_div_bits << 4);
 153 
 154         err = regmap_bulk_write(data->regmap, SI514_REG_HS_DIV, reg + 5, 2);
 155         if (err < 0)
 156                 return err;
 157         /*
 158          * Writing to SI514_REG_M_INT_FRAC triggers the clock change, so that
 159          * must be written last
 160          */
 161         return regmap_bulk_write(data->regmap, SI514_REG_M_FRAC1, reg, 5);
 162 }
 163 
 164 /* Calculate divider settings for a given frequency */
 165 static int si514_calc_muldiv(struct clk_si514_muldiv *settings,
 166         unsigned long frequency)
 167 {
 168         u64 m;
 169         u32 ls_freq;
 170         u32 tmp;
 171         u8 res;
 172 
 173         if ((frequency < SI514_MIN_FREQ) || (frequency > SI514_MAX_FREQ))
 174                 return -EINVAL;
 175 
 176         /* Determine the minimum value of LS_DIV and resulting target freq. */
 177         ls_freq = frequency;
 178         if (frequency >= (FVCO_MIN / HS_DIV_MAX))
 179                 settings->ls_div_bits = 0;
 180         else {
 181                 res = 1;
 182                 tmp = 2 * HS_DIV_MAX;
 183                 while (tmp <= (HS_DIV_MAX * 32)) {
 184                         if ((frequency * tmp) >= FVCO_MIN)
 185                                 break;
 186                         ++res;
 187                         tmp <<= 1;
 188                 }
 189                 settings->ls_div_bits = res;
 190                 ls_freq = frequency << res;
 191         }
 192 
 193         /* Determine minimum HS_DIV, round up to even number */
 194         settings->hs_div = DIV_ROUND_UP(FVCO_MIN >> 1, ls_freq) << 1;
 195 
 196         /* M = LS_DIV x HS_DIV x frequency / F_XO (in fixed-point) */
 197         m = ((u64)(ls_freq * settings->hs_div) << 29) + (FXO / 2);
 198         do_div(m, FXO);
 199         settings->m_frac = (u32)m & (BIT(29) - 1);
 200         settings->m_int = (u32)(m >> 29);
 201 
 202         return 0;
 203 }
 204 
 205 /* Calculate resulting frequency given the register settings */
 206 static unsigned long si514_calc_rate(struct clk_si514_muldiv *settings)
 207 {
 208         u64 m = settings->m_frac | ((u64)settings->m_int << 29);
 209         u32 d = settings->hs_div * BIT(settings->ls_div_bits);
 210 
 211         return ((u32)(((m * FXO) + (FXO / 2)) >> 29)) / d;
 212 }
 213 
 214 static unsigned long si514_recalc_rate(struct clk_hw *hw,
 215                 unsigned long parent_rate)
 216 {
 217         struct clk_si514 *data = to_clk_si514(hw);
 218         struct clk_si514_muldiv settings;
 219         int err;
 220 
 221         err = si514_get_muldiv(data, &settings);
 222         if (err) {
 223                 dev_err(&data->i2c_client->dev, "unable to retrieve settings\n");
 224                 return 0;
 225         }
 226 
 227         return si514_calc_rate(&settings);
 228 }
 229 
 230 static long si514_round_rate(struct clk_hw *hw, unsigned long rate,
 231                 unsigned long *parent_rate)
 232 {
 233         struct clk_si514_muldiv settings;
 234         int err;
 235 
 236         if (!rate)
 237                 return 0;
 238 
 239         err = si514_calc_muldiv(&settings, rate);
 240         if (err)
 241                 return err;
 242 
 243         return si514_calc_rate(&settings);
 244 }
 245 
 246 /*
 247  * Update output frequency for big frequency changes (> 1000 ppm).
 248  * The chip supports <1000ppm changes "on the fly", we haven't implemented
 249  * that here.
 250  */
 251 static int si514_set_rate(struct clk_hw *hw, unsigned long rate,
 252                 unsigned long parent_rate)
 253 {
 254         struct clk_si514 *data = to_clk_si514(hw);
 255         struct clk_si514_muldiv settings;
 256         unsigned int old_oe_state;
 257         int err;
 258 
 259         err = si514_calc_muldiv(&settings, rate);
 260         if (err)
 261                 return err;
 262 
 263         err = regmap_read(data->regmap, SI514_REG_CONTROL, &old_oe_state);
 264         if (err)
 265                 return err;
 266 
 267         si514_enable_output(data, false);
 268 
 269         err = si514_set_muldiv(data, &settings);
 270         if (err < 0)
 271                 return err; /* Undefined state now, best to leave disabled */
 272 
 273         /* Trigger calibration */
 274         err = regmap_write(data->regmap, SI514_REG_CONTROL, SI514_CONTROL_FCAL);
 275         if (err < 0)
 276                 return err;
 277 
 278         /* Applying a new frequency can take up to 10ms */
 279         usleep_range(10000, 12000);
 280 
 281         if (old_oe_state & SI514_CONTROL_OE)
 282                 si514_enable_output(data, true);
 283 
 284         return err;
 285 }
 286 
 287 static const struct clk_ops si514_clk_ops = {
 288         .prepare = si514_prepare,
 289         .unprepare = si514_unprepare,
 290         .is_prepared = si514_is_prepared,
 291         .recalc_rate = si514_recalc_rate,
 292         .round_rate = si514_round_rate,
 293         .set_rate = si514_set_rate,
 294 };
 295 
 296 static bool si514_regmap_is_volatile(struct device *dev, unsigned int reg)
 297 {
 298         switch (reg) {
 299         case SI514_REG_CONTROL:
 300         case SI514_REG_RESET:
 301                 return true;
 302         default:
 303                 return false;
 304         }
 305 }
 306 
 307 static bool si514_regmap_is_writeable(struct device *dev, unsigned int reg)
 308 {
 309         switch (reg) {
 310         case SI514_REG_LP:
 311         case SI514_REG_M_FRAC1 ... SI514_REG_LS_HS_DIV:
 312         case SI514_REG_OE_STATE:
 313         case SI514_REG_RESET:
 314         case SI514_REG_CONTROL:
 315                 return true;
 316         default:
 317                 return false;
 318         }
 319 }
 320 
 321 static const struct regmap_config si514_regmap_config = {
 322         .reg_bits = 8,
 323         .val_bits = 8,
 324         .cache_type = REGCACHE_RBTREE,
 325         .max_register = SI514_REG_CONTROL,
 326         .writeable_reg = si514_regmap_is_writeable,
 327         .volatile_reg = si514_regmap_is_volatile,
 328 };
 329 
 330 static int si514_probe(struct i2c_client *client,
 331                 const struct i2c_device_id *id)
 332 {
 333         struct clk_si514 *data;
 334         struct clk_init_data init;
 335         int err;
 336 
 337         data = devm_kzalloc(&client->dev, sizeof(*data), GFP_KERNEL);
 338         if (!data)
 339                 return -ENOMEM;
 340 
 341         init.ops = &si514_clk_ops;
 342         init.flags = 0;
 343         init.num_parents = 0;
 344         data->hw.init = &init;
 345         data->i2c_client = client;
 346 
 347         if (of_property_read_string(client->dev.of_node, "clock-output-names",
 348                         &init.name))
 349                 init.name = client->dev.of_node->name;
 350 
 351         data->regmap = devm_regmap_init_i2c(client, &si514_regmap_config);
 352         if (IS_ERR(data->regmap)) {
 353                 dev_err(&client->dev, "failed to allocate register map\n");
 354                 return PTR_ERR(data->regmap);
 355         }
 356 
 357         i2c_set_clientdata(client, data);
 358 
 359         err = devm_clk_hw_register(&client->dev, &data->hw);
 360         if (err) {
 361                 dev_err(&client->dev, "clock registration failed\n");
 362                 return err;
 363         }
 364         err = of_clk_add_hw_provider(client->dev.of_node, of_clk_hw_simple_get,
 365                                      &data->hw);
 366         if (err) {
 367                 dev_err(&client->dev, "unable to add clk provider\n");
 368                 return err;
 369         }
 370 
 371         return 0;
 372 }
 373 
 374 static int si514_remove(struct i2c_client *client)
 375 {
 376         of_clk_del_provider(client->dev.of_node);
 377         return 0;
 378 }
 379 
 380 static const struct i2c_device_id si514_id[] = {
 381         { "si514", 0 },
 382         { }
 383 };
 384 MODULE_DEVICE_TABLE(i2c, si514_id);
 385 
 386 static const struct of_device_id clk_si514_of_match[] = {
 387         { .compatible = "silabs,si514" },
 388         { },
 389 };
 390 MODULE_DEVICE_TABLE(of, clk_si514_of_match);
 391 
 392 static struct i2c_driver si514_driver = {
 393         .driver = {
 394                 .name = "si514",
 395                 .of_match_table = clk_si514_of_match,
 396         },
 397         .probe          = si514_probe,
 398         .remove         = si514_remove,
 399         .id_table       = si514_id,
 400 };
 401 module_i2c_driver(si514_driver);
 402 
 403 MODULE_AUTHOR("Mike Looijmans <mike.looijmans@topic.nl>");
 404 MODULE_DESCRIPTION("Si514 driver");
 405 MODULE_LICENSE("GPL");

/* [<][>][^][v][top][bottom][index][help] */