clk-si514.c (9741B)
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 46struct 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 */ 54struct 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 */ 62static 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 68static 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 75static 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 82static 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 */ 96static 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 115static 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 */ 165static 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 */ 206static 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 214static 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 230static 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 */ 251static 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 287static 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 296static 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 307static 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 321static 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 330static int si514_probe(struct i2c_client *client) 331{ 332 struct clk_si514 *data; 333 struct clk_init_data init; 334 int err; 335 336 data = devm_kzalloc(&client->dev, sizeof(*data), GFP_KERNEL); 337 if (!data) 338 return -ENOMEM; 339 340 init.ops = &si514_clk_ops; 341 init.flags = 0; 342 init.num_parents = 0; 343 data->hw.init = &init; 344 data->i2c_client = client; 345 346 if (of_property_read_string(client->dev.of_node, "clock-output-names", 347 &init.name)) 348 init.name = client->dev.of_node->name; 349 350 data->regmap = devm_regmap_init_i2c(client, &si514_regmap_config); 351 if (IS_ERR(data->regmap)) { 352 dev_err(&client->dev, "failed to allocate register map\n"); 353 return PTR_ERR(data->regmap); 354 } 355 356 i2c_set_clientdata(client, data); 357 358 err = devm_clk_hw_register(&client->dev, &data->hw); 359 if (err) { 360 dev_err(&client->dev, "clock registration failed\n"); 361 return err; 362 } 363 err = of_clk_add_hw_provider(client->dev.of_node, of_clk_hw_simple_get, 364 &data->hw); 365 if (err) { 366 dev_err(&client->dev, "unable to add clk provider\n"); 367 return err; 368 } 369 370 return 0; 371} 372 373static int si514_remove(struct i2c_client *client) 374{ 375 of_clk_del_provider(client->dev.of_node); 376 return 0; 377} 378 379static const struct i2c_device_id si514_id[] = { 380 { "si514", 0 }, 381 { } 382}; 383MODULE_DEVICE_TABLE(i2c, si514_id); 384 385static const struct of_device_id clk_si514_of_match[] = { 386 { .compatible = "silabs,si514" }, 387 { }, 388}; 389MODULE_DEVICE_TABLE(of, clk_si514_of_match); 390 391static struct i2c_driver si514_driver = { 392 .driver = { 393 .name = "si514", 394 .of_match_table = clk_si514_of_match, 395 }, 396 .probe_new = si514_probe, 397 .remove = si514_remove, 398 .id_table = si514_id, 399}; 400module_i2c_driver(si514_driver); 401 402MODULE_AUTHOR("Mike Looijmans <mike.looijmans@topic.nl>"); 403MODULE_DESCRIPTION("Si514 driver"); 404MODULE_LICENSE("GPL");