Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-or-later
0002 /*
0003  * Driver for Silicon Labs Si514 Programmable Oscillator
0004  *
0005  * Copyright (C) 2015 Topic Embedded Products
0006  *
0007  * Author: Mike Looijmans <mike.looijmans@topic.nl>
0008  */
0009 
0010 #include <linux/clk-provider.h>
0011 #include <linux/delay.h>
0012 #include <linux/module.h>
0013 #include <linux/i2c.h>
0014 #include <linux/regmap.h>
0015 #include <linux/slab.h>
0016 
0017 /* I2C registers */
0018 #define SI514_REG_LP        0
0019 #define SI514_REG_M_FRAC1   5
0020 #define SI514_REG_M_FRAC2   6
0021 #define SI514_REG_M_FRAC3   7
0022 #define SI514_REG_M_INT_FRAC    8
0023 #define SI514_REG_M_INT     9
0024 #define SI514_REG_HS_DIV    10
0025 #define SI514_REG_LS_HS_DIV 11
0026 #define SI514_REG_OE_STATE  14
0027 #define SI514_REG_RESET     128
0028 #define SI514_REG_CONTROL   132
0029 
0030 /* Register values */
0031 #define SI514_RESET_RST     BIT(7)
0032 
0033 #define SI514_CONTROL_FCAL  BIT(0)
0034 #define SI514_CONTROL_OE    BIT(2)
0035 
0036 #define SI514_MIN_FREQ      100000U
0037 #define SI514_MAX_FREQ   250000000U
0038 
0039 #define FXO       31980000U
0040 
0041 #define FVCO_MIN    2080000000U
0042 #define FVCO_MAX    2500000000U
0043 
0044 #define HS_DIV_MAX  1022
0045 
0046 struct clk_si514 {
0047     struct clk_hw hw;
0048     struct regmap *regmap;
0049     struct i2c_client *i2c_client;
0050 };
0051 #define to_clk_si514(_hw)   container_of(_hw, struct clk_si514, hw)
0052 
0053 /* Multiplier/divider settings */
0054 struct clk_si514_muldiv {
0055     u32 m_frac;  /* 29-bit Fractional part of multiplier M */
0056     u8 m_int; /* Integer part of multiplier M, 65..78 */
0057     u8 ls_div_bits; /* 2nd divider, as 2^x */
0058     u16 hs_div; /* 1st divider, must be even and 10<=x<=1022 */
0059 };
0060 
0061 /* Enables or disables the output driver */
0062 static int si514_enable_output(struct clk_si514 *data, bool enable)
0063 {
0064     return regmap_update_bits(data->regmap, SI514_REG_CONTROL,
0065         SI514_CONTROL_OE, enable ? SI514_CONTROL_OE : 0);
0066 }
0067 
0068 static int si514_prepare(struct clk_hw *hw)
0069 {
0070     struct clk_si514 *data = to_clk_si514(hw);
0071 
0072     return si514_enable_output(data, true);
0073 }
0074 
0075 static void si514_unprepare(struct clk_hw *hw)
0076 {
0077     struct clk_si514 *data = to_clk_si514(hw);
0078 
0079     si514_enable_output(data, false);
0080 }
0081 
0082 static int si514_is_prepared(struct clk_hw *hw)
0083 {
0084     struct clk_si514 *data = to_clk_si514(hw);
0085     unsigned int val;
0086     int err;
0087 
0088     err = regmap_read(data->regmap, SI514_REG_CONTROL, &val);
0089     if (err < 0)
0090         return err;
0091 
0092     return !!(val & SI514_CONTROL_OE);
0093 }
0094 
0095 /* Retrieve clock multiplier and dividers from hardware */
0096 static int si514_get_muldiv(struct clk_si514 *data,
0097     struct clk_si514_muldiv *settings)
0098 {
0099     int err;
0100     u8 reg[7];
0101 
0102     err = regmap_bulk_read(data->regmap, SI514_REG_M_FRAC1,
0103             reg, ARRAY_SIZE(reg));
0104     if (err)
0105         return err;
0106 
0107     settings->m_frac = reg[0] | reg[1] << 8 | reg[2] << 16 |
0108                (reg[3] & 0x1F) << 24;
0109     settings->m_int = (reg[4] & 0x3f) << 3 | reg[3] >> 5;
0110     settings->ls_div_bits = (reg[6] >> 4) & 0x07;
0111     settings->hs_div = (reg[6] & 0x03) << 8 | reg[5];
0112     return 0;
0113 }
0114 
0115 static int si514_set_muldiv(struct clk_si514 *data,
0116     struct clk_si514_muldiv *settings)
0117 {
0118     u8 lp;
0119     u8 reg[7];
0120     int err;
0121 
0122     /* Calculate LP1/LP2 according to table 13 in the datasheet */
0123     /* 65.259980246 */
0124     if (settings->m_int < 65 ||
0125         (settings->m_int == 65 && settings->m_frac <= 139575831))
0126         lp = 0x22;
0127     /* 67.859763463 */
0128     else if (settings->m_int < 67 ||
0129         (settings->m_int == 67 && settings->m_frac <= 461581994))
0130         lp = 0x23;
0131     /* 72.937624981 */
0132     else if (settings->m_int < 72 ||
0133         (settings->m_int == 72 && settings->m_frac <= 503383578))
0134         lp = 0x33;
0135     /* 75.843265046 */
0136     else if (settings->m_int < 75 ||
0137         (settings->m_int == 75 && settings->m_frac <= 452724474))
0138         lp = 0x34;
0139     else
0140         lp = 0x44;
0141 
0142     err = regmap_write(data->regmap, SI514_REG_LP, lp);
0143     if (err < 0)
0144         return err;
0145 
0146     reg[0] = settings->m_frac;
0147     reg[1] = settings->m_frac >> 8;
0148     reg[2] = settings->m_frac >> 16;
0149     reg[3] = settings->m_frac >> 24 | settings->m_int << 5;
0150     reg[4] = settings->m_int >> 3;
0151     reg[5] = settings->hs_div;
0152     reg[6] = (settings->hs_div >> 8) | (settings->ls_div_bits << 4);
0153 
0154     err = regmap_bulk_write(data->regmap, SI514_REG_HS_DIV, reg + 5, 2);
0155     if (err < 0)
0156         return err;
0157     /*
0158      * Writing to SI514_REG_M_INT_FRAC triggers the clock change, so that
0159      * must be written last
0160      */
0161     return regmap_bulk_write(data->regmap, SI514_REG_M_FRAC1, reg, 5);
0162 }
0163 
0164 /* Calculate divider settings for a given frequency */
0165 static int si514_calc_muldiv(struct clk_si514_muldiv *settings,
0166     unsigned long frequency)
0167 {
0168     u64 m;
0169     u32 ls_freq;
0170     u32 tmp;
0171     u8 res;
0172 
0173     if ((frequency < SI514_MIN_FREQ) || (frequency > SI514_MAX_FREQ))
0174         return -EINVAL;
0175 
0176     /* Determine the minimum value of LS_DIV and resulting target freq. */
0177     ls_freq = frequency;
0178     if (frequency >= (FVCO_MIN / HS_DIV_MAX))
0179         settings->ls_div_bits = 0;
0180     else {
0181         res = 1;
0182         tmp = 2 * HS_DIV_MAX;
0183         while (tmp <= (HS_DIV_MAX * 32)) {
0184             if ((frequency * tmp) >= FVCO_MIN)
0185                 break;
0186             ++res;
0187             tmp <<= 1;
0188         }
0189         settings->ls_div_bits = res;
0190         ls_freq = frequency << res;
0191     }
0192 
0193     /* Determine minimum HS_DIV, round up to even number */
0194     settings->hs_div = DIV_ROUND_UP(FVCO_MIN >> 1, ls_freq) << 1;
0195 
0196     /* M = LS_DIV x HS_DIV x frequency / F_XO (in fixed-point) */
0197     m = ((u64)(ls_freq * settings->hs_div) << 29) + (FXO / 2);
0198     do_div(m, FXO);
0199     settings->m_frac = (u32)m & (BIT(29) - 1);
0200     settings->m_int = (u32)(m >> 29);
0201 
0202     return 0;
0203 }
0204 
0205 /* Calculate resulting frequency given the register settings */
0206 static unsigned long si514_calc_rate(struct clk_si514_muldiv *settings)
0207 {
0208     u64 m = settings->m_frac | ((u64)settings->m_int << 29);
0209     u32 d = settings->hs_div * BIT(settings->ls_div_bits);
0210 
0211     return ((u32)(((m * FXO) + (FXO / 2)) >> 29)) / d;
0212 }
0213 
0214 static unsigned long si514_recalc_rate(struct clk_hw *hw,
0215         unsigned long parent_rate)
0216 {
0217     struct clk_si514 *data = to_clk_si514(hw);
0218     struct clk_si514_muldiv settings;
0219     int err;
0220 
0221     err = si514_get_muldiv(data, &settings);
0222     if (err) {
0223         dev_err(&data->i2c_client->dev, "unable to retrieve settings\n");
0224         return 0;
0225     }
0226 
0227     return si514_calc_rate(&settings);
0228 }
0229 
0230 static long si514_round_rate(struct clk_hw *hw, unsigned long rate,
0231         unsigned long *parent_rate)
0232 {
0233     struct clk_si514_muldiv settings;
0234     int err;
0235 
0236     if (!rate)
0237         return 0;
0238 
0239     err = si514_calc_muldiv(&settings, rate);
0240     if (err)
0241         return err;
0242 
0243     return si514_calc_rate(&settings);
0244 }
0245 
0246 /*
0247  * Update output frequency for big frequency changes (> 1000 ppm).
0248  * The chip supports <1000ppm changes "on the fly", we haven't implemented
0249  * that here.
0250  */
0251 static int si514_set_rate(struct clk_hw *hw, unsigned long rate,
0252         unsigned long parent_rate)
0253 {
0254     struct clk_si514 *data = to_clk_si514(hw);
0255     struct clk_si514_muldiv settings;
0256     unsigned int old_oe_state;
0257     int err;
0258 
0259     err = si514_calc_muldiv(&settings, rate);
0260     if (err)
0261         return err;
0262 
0263     err = regmap_read(data->regmap, SI514_REG_CONTROL, &old_oe_state);
0264     if (err)
0265         return err;
0266 
0267     si514_enable_output(data, false);
0268 
0269     err = si514_set_muldiv(data, &settings);
0270     if (err < 0)
0271         return err; /* Undefined state now, best to leave disabled */
0272 
0273     /* Trigger calibration */
0274     err = regmap_write(data->regmap, SI514_REG_CONTROL, SI514_CONTROL_FCAL);
0275     if (err < 0)
0276         return err;
0277 
0278     /* Applying a new frequency can take up to 10ms */
0279     usleep_range(10000, 12000);
0280 
0281     if (old_oe_state & SI514_CONTROL_OE)
0282         si514_enable_output(data, true);
0283 
0284     return err;
0285 }
0286 
0287 static const struct clk_ops si514_clk_ops = {
0288     .prepare = si514_prepare,
0289     .unprepare = si514_unprepare,
0290     .is_prepared = si514_is_prepared,
0291     .recalc_rate = si514_recalc_rate,
0292     .round_rate = si514_round_rate,
0293     .set_rate = si514_set_rate,
0294 };
0295 
0296 static bool si514_regmap_is_volatile(struct device *dev, unsigned int reg)
0297 {
0298     switch (reg) {
0299     case SI514_REG_CONTROL:
0300     case SI514_REG_RESET:
0301         return true;
0302     default:
0303         return false;
0304     }
0305 }
0306 
0307 static bool si514_regmap_is_writeable(struct device *dev, unsigned int reg)
0308 {
0309     switch (reg) {
0310     case SI514_REG_LP:
0311     case SI514_REG_M_FRAC1 ... SI514_REG_LS_HS_DIV:
0312     case SI514_REG_OE_STATE:
0313     case SI514_REG_RESET:
0314     case SI514_REG_CONTROL:
0315         return true;
0316     default:
0317         return false;
0318     }
0319 }
0320 
0321 static const struct regmap_config si514_regmap_config = {
0322     .reg_bits = 8,
0323     .val_bits = 8,
0324     .cache_type = REGCACHE_RBTREE,
0325     .max_register = SI514_REG_CONTROL,
0326     .writeable_reg = si514_regmap_is_writeable,
0327     .volatile_reg = si514_regmap_is_volatile,
0328 };
0329 
0330 static int si514_probe(struct i2c_client *client)
0331 {
0332     struct clk_si514 *data;
0333     struct clk_init_data init;
0334     int err;
0335 
0336     data = devm_kzalloc(&client->dev, sizeof(*data), GFP_KERNEL);
0337     if (!data)
0338         return -ENOMEM;
0339 
0340     init.ops = &si514_clk_ops;
0341     init.flags = 0;
0342     init.num_parents = 0;
0343     data->hw.init = &init;
0344     data->i2c_client = client;
0345 
0346     if (of_property_read_string(client->dev.of_node, "clock-output-names",
0347             &init.name))
0348         init.name = client->dev.of_node->name;
0349 
0350     data->regmap = devm_regmap_init_i2c(client, &si514_regmap_config);
0351     if (IS_ERR(data->regmap)) {
0352         dev_err(&client->dev, "failed to allocate register map\n");
0353         return PTR_ERR(data->regmap);
0354     }
0355 
0356     i2c_set_clientdata(client, data);
0357 
0358     err = devm_clk_hw_register(&client->dev, &data->hw);
0359     if (err) {
0360         dev_err(&client->dev, "clock registration failed\n");
0361         return err;
0362     }
0363     err = of_clk_add_hw_provider(client->dev.of_node, of_clk_hw_simple_get,
0364                      &data->hw);
0365     if (err) {
0366         dev_err(&client->dev, "unable to add clk provider\n");
0367         return err;
0368     }
0369 
0370     return 0;
0371 }
0372 
0373 static int si514_remove(struct i2c_client *client)
0374 {
0375     of_clk_del_provider(client->dev.of_node);
0376     return 0;
0377 }
0378 
0379 static const struct i2c_device_id si514_id[] = {
0380     { "si514", 0 },
0381     { }
0382 };
0383 MODULE_DEVICE_TABLE(i2c, si514_id);
0384 
0385 static const struct of_device_id clk_si514_of_match[] = {
0386     { .compatible = "silabs,si514" },
0387     { },
0388 };
0389 MODULE_DEVICE_TABLE(of, clk_si514_of_match);
0390 
0391 static struct i2c_driver si514_driver = {
0392     .driver = {
0393         .name = "si514",
0394         .of_match_table = clk_si514_of_match,
0395     },
0396     .probe_new  = si514_probe,
0397     .remove     = si514_remove,
0398     .id_table   = si514_id,
0399 };
0400 module_i2c_driver(si514_driver);
0401 
0402 MODULE_AUTHOR("Mike Looijmans <mike.looijmans@topic.nl>");
0403 MODULE_DESCRIPTION("Si514 driver");
0404 MODULE_LICENSE("GPL");