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