aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSoren Brinkmann <soren.brinkmann@xilinx.com>2013-09-21 19:40:39 -0400
committerMike Turquette <mturquette@linaro.org>2013-12-15 00:01:35 -0500
commit1459c837036a44e2dcf14a9452ed330201fb23b5 (patch)
treef1e5344cfdaf0f067e4171acd699a05d034ed352
parent3a5aec246f294004564cbe960724fa0ace59a4c5 (diff)
clk: si570: Add a driver for SI570 oscillators
Add a driver for SILabs 570, 571, 598, 599 programmable oscillators. The devices generate low-jitter clock signals and are reprogrammable via an I2C interface. Reviewed-by: Guenter Roeck <linux@roeck-us.net> Signed-off-by: Soren Brinkmann <soren.brinkmann@xilinx.com> Signed-off-by: Mike Turquette <mturquette@linaro.org>
-rw-r--r--Documentation/devicetree/bindings/clock/silabs,si570.txt39
-rw-r--r--drivers/clk/Kconfig10
-rw-r--r--drivers/clk/Makefile1
-rw-r--r--drivers/clk/clk-si570.c531
4 files changed, 581 insertions, 0 deletions
diff --git a/Documentation/devicetree/bindings/clock/silabs,si570.txt b/Documentation/devicetree/bindings/clock/silabs,si570.txt
new file mode 100644
index 000000000000..c09f21e1d98f
--- /dev/null
+++ b/Documentation/devicetree/bindings/clock/silabs,si570.txt
@@ -0,0 +1,39 @@
1Binding for Silicon Labs 570, 571, 598 and 599 programmable
2I2C clock generators.
3
4Reference
5This binding uses the common clock binding[1]. Details about the devices can be
6found in the data sheets[2][3].
7
8[1] Documentation/devicetree/bindings/clock/clock-bindings.txt
9[2] Si570/571 Data Sheet
10 http://www.silabs.com/Support%20Documents/TechnicalDocs/si570.pdf
11[3] Si598/599 Data Sheet
12 http://www.silabs.com/Support%20Documents/TechnicalDocs/si598-99.pdf
13
14Required properties:
15 - compatible: Shall be one of "silabs,si570", "silabs,si571",
16 "silabs,si598", "silabs,si599"
17 - reg: I2C device address.
18 - #clock-cells: From common clock bindings: Shall be 0.
19 - factory-fout: Factory set default frequency. This frequency is part specific.
20 The correct frequency for the part used has to be provided in
21 order to generate the correct output frequencies. For more
22 details, please refer to the data sheet.
23 - temperature-stability: Temperature stability of the device in PPM. Should be
24 one of: 7, 20, 50 or 100.
25
26Optional properties:
27 - clock-output-names: From common clock bindings. Recommended to be "si570".
28 - clock-frequency: Output frequency to generate. This defines the output
29 frequency set during boot. It can be reprogrammed during
30 runtime through the common clock framework.
31
32Example:
33 si570: clock-generator@5d {
34 #clock-cells = <0>;
35 compatible = "silabs,si570";
36 temperature-stability = <50>;
37 reg = <0x5d>;
38 factory-fout = <156250000>;
39 };
diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig
index 5c51115081b3..3089f05ba661 100644
--- a/drivers/clk/Kconfig
+++ b/drivers/clk/Kconfig
@@ -64,6 +64,16 @@ config COMMON_CLK_SI5351
64 This driver supports Silicon Labs 5351A/B/C programmable clock 64 This driver supports Silicon Labs 5351A/B/C programmable clock
65 generators. 65 generators.
66 66
67config COMMON_CLK_SI570
68 tristate "Clock driver for SiLabs 570 and compatible devices"
69 depends on I2C
70 depends on OF
71 select REGMAP_I2C
72 help
73 ---help---
74 This driver supports Silicon Labs 570/571/598/599 programmable
75 clock generators.
76
67config COMMON_CLK_S2MPS11 77config COMMON_CLK_S2MPS11
68 tristate "Clock driver for S2MPS11 MFD" 78 tristate "Clock driver for S2MPS11 MFD"
69 depends on MFD_SEC_CORE 79 depends on MFD_SEC_CORE
diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile
index 2460afd59405..6705d9a82cbc 100644
--- a/drivers/clk/Makefile
+++ b/drivers/clk/Makefile
@@ -45,6 +45,7 @@ obj-$(CONFIG_COMMON_CLK_AXI_CLKGEN) += clk-axi-clkgen.o
45obj-$(CONFIG_COMMON_CLK_WM831X) += clk-wm831x.o 45obj-$(CONFIG_COMMON_CLK_WM831X) += clk-wm831x.o
46obj-$(CONFIG_COMMON_CLK_MAX77686) += clk-max77686.o 46obj-$(CONFIG_COMMON_CLK_MAX77686) += clk-max77686.o
47obj-$(CONFIG_COMMON_CLK_SI5351) += clk-si5351.o 47obj-$(CONFIG_COMMON_CLK_SI5351) += clk-si5351.o
48obj-$(CONFIG_COMMON_CLK_SI570) += clk-si570.o
48obj-$(CONFIG_COMMON_CLK_S2MPS11) += clk-s2mps11.o 49obj-$(CONFIG_COMMON_CLK_S2MPS11) += clk-s2mps11.o
49obj-$(CONFIG_CLK_TWL6040) += clk-twl6040.o 50obj-$(CONFIG_CLK_TWL6040) += clk-twl6040.o
50obj-$(CONFIG_CLK_PPC_CORENET) += clk-ppc-corenet.o 51obj-$(CONFIG_CLK_PPC_CORENET) += clk-ppc-corenet.o
diff --git a/drivers/clk/clk-si570.c b/drivers/clk/clk-si570.c
new file mode 100644
index 000000000000..5d0ac9f8e1ee
--- /dev/null
+++ b/drivers/clk/clk-si570.c
@@ -0,0 +1,531 @@
1/*
2 * Driver for Silicon Labs Si570/Si571 Programmable XO/VCXO
3 *
4 * Copyright (C) 2010, 2011 Ericsson AB.
5 * Copyright (C) 2011 Guenter Roeck.
6 * Copyright (C) 2011 - 2013 Xilinx Inc.
7 *
8 * Author: Guenter Roeck <guenter.roeck@ericsson.com>
9 * Sören Brinkmann <soren.brinkmann@xilinx.com>
10 *
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
15 *
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
20 */
21
22#include <linux/clk-provider.h>
23#include <linux/delay.h>
24#include <linux/module.h>
25#include <linux/i2c.h>
26#include <linux/regmap.h>
27#include <linux/slab.h>
28
29/* Si570 registers */
30#define SI570_REG_HS_N1 7
31#define SI570_REG_N1_RFREQ0 8
32#define SI570_REG_RFREQ1 9
33#define SI570_REG_RFREQ2 10
34#define SI570_REG_RFREQ3 11
35#define SI570_REG_RFREQ4 12
36#define SI570_REG_CONTROL 135
37#define SI570_REG_FREEZE_DCO 137
38#define SI570_DIV_OFFSET_7PPM 6
39
40#define HS_DIV_SHIFT 5
41#define HS_DIV_MASK 0xe0
42#define HS_DIV_OFFSET 4
43#define N1_6_2_MASK 0x1f
44#define N1_1_0_MASK 0xc0
45#define RFREQ_37_32_MASK 0x3f
46
47#define SI570_MIN_FREQ 10000000L
48#define SI570_MAX_FREQ 1417500000L
49#define SI598_MAX_FREQ 525000000L
50
51#define FDCO_MIN 4850000000LL
52#define FDCO_MAX 5670000000LL
53
54#define SI570_CNTRL_RECALL (1 << 0)
55#define SI570_CNTRL_FREEZE_M (1 << 5)
56#define SI570_CNTRL_NEWFREQ (1 << 6)
57
58#define SI570_FREEZE_DCO (1 << 4)
59
60/**
61 * struct clk_si570:
62 * @hw: Clock hw struct
63 * @regmap: Device's regmap
64 * @div_offset: Rgister offset for dividers
65 * @max_freq: Maximum frequency for this device
66 * @fxtal: Factory xtal frequency
67 * @n1: Clock divider N1
68 * @hs_div: Clock divider HSDIV
69 * @rfreq: Clock multiplier RFREQ
70 * @frequency: Current output frequency
71 * @i2c_client: I2C client pointer
72 */
73struct clk_si570 {
74 struct clk_hw hw;
75 struct regmap *regmap;
76 unsigned int div_offset;
77 u64 max_freq;
78 u64 fxtal;
79 unsigned int n1;
80 unsigned int hs_div;
81 u64 rfreq;
82 u64 frequency;
83 struct i2c_client *i2c_client;
84};
85#define to_clk_si570(_hw) container_of(_hw, struct clk_si570, hw)
86
87enum clk_si570_variant {
88 si57x,
89 si59x
90};
91
92/**
93 * si570_get_divs() - Read clock dividers from HW
94 * @data: Pointer to struct clk_si570
95 * @rfreq: Fractional multiplier (output)
96 * @n1: Divider N1 (output)
97 * @hs_div: Divider HSDIV (output)
98 * Returns 0 on success, negative errno otherwise.
99 *
100 * Retrieve clock dividers and multipliers from the HW.
101 */
102static int si570_get_divs(struct clk_si570 *data, u64 *rfreq,
103 unsigned int *n1, unsigned int *hs_div)
104{
105 int err;
106 u8 reg[6];
107 u64 tmp;
108
109 err = regmap_bulk_read(data->regmap, SI570_REG_HS_N1 + data->div_offset,
110 reg, ARRAY_SIZE(reg));
111 if (err)
112 return err;
113
114 *hs_div = ((reg[0] & HS_DIV_MASK) >> HS_DIV_SHIFT) + HS_DIV_OFFSET;
115 *n1 = ((reg[0] & N1_6_2_MASK) << 2) + ((reg[1] & N1_1_0_MASK) >> 6) + 1;
116 /* Handle invalid cases */
117 if (*n1 > 1)
118 *n1 &= ~1;
119
120 tmp = reg[1] & RFREQ_37_32_MASK;
121 tmp = (tmp << 8) + reg[2];
122 tmp = (tmp << 8) + reg[3];
123 tmp = (tmp << 8) + reg[4];
124 tmp = (tmp << 8) + reg[5];
125 *rfreq = tmp;
126
127 return 0;
128}
129
130/**
131 * si570_get_defaults() - Get default values
132 * @data: Driver data structure
133 * @fout: Factory frequency output
134 * Returns 0 on success, negative errno otherwise.
135 */
136static int si570_get_defaults(struct clk_si570 *data, u64 fout)
137{
138 int err;
139 u64 fdco;
140
141 regmap_write(data->regmap, SI570_REG_CONTROL, SI570_CNTRL_RECALL);
142
143 err = si570_get_divs(data, &data->rfreq, &data->n1, &data->hs_div);
144 if (err)
145 return err;
146
147 /*
148 * Accept optional precision loss to avoid arithmetic overflows.
149 * Acceptable per Silicon Labs Application Note AN334.
150 */
151 fdco = fout * data->n1 * data->hs_div;
152 if (fdco >= (1LL << 36))
153 data->fxtal = div64_u64(fdco << 24, data->rfreq >> 4);
154 else
155 data->fxtal = div64_u64(fdco << 28, data->rfreq);
156
157 data->frequency = fout;
158
159 return 0;
160}
161
162/**
163 * si570_update_rfreq() - Update clock multiplier
164 * @data: Driver data structure
165 * Passes on regmap_bulk_write() return value.
166 */
167static int si570_update_rfreq(struct clk_si570 *data)
168{
169 u8 reg[5];
170
171 reg[0] = ((data->n1 - 1) << 6) |
172 ((data->rfreq >> 32) & RFREQ_37_32_MASK);
173 reg[1] = (data->rfreq >> 24) & 0xff;
174 reg[2] = (data->rfreq >> 16) & 0xff;
175 reg[3] = (data->rfreq >> 8) & 0xff;
176 reg[4] = data->rfreq & 0xff;
177
178 return regmap_bulk_write(data->regmap, SI570_REG_N1_RFREQ0 +
179 data->div_offset, reg, ARRAY_SIZE(reg));
180}
181
182/**
183 * si570_calc_divs() - Caluclate clock dividers
184 * @frequency: Target frequency
185 * @data: Driver data structure
186 * @out_rfreq: RFREG fractional multiplier (output)
187 * @out_n1: Clock divider N1 (output)
188 * @out_hs_div: Clock divider HSDIV (output)
189 * Returns 0 on success, negative errno otherwise.
190 *
191 * Calculate the clock dividers (@out_hs_div, @out_n1) and clock multiplier
192 * (@out_rfreq) for a given target @frequency.
193 */
194static int si570_calc_divs(unsigned long frequency, struct clk_si570 *data,
195 u64 *out_rfreq, unsigned int *out_n1, unsigned int *out_hs_div)
196{
197 int i;
198 unsigned int n1, hs_div;
199 u64 fdco, best_fdco = ULLONG_MAX;
200 static const uint8_t si570_hs_div_values[] = { 11, 9, 7, 6, 5, 4 };
201
202 for (i = 0; i < ARRAY_SIZE(si570_hs_div_values); i++) {
203 hs_div = si570_hs_div_values[i];
204 /* Calculate lowest possible value for n1 */
205 n1 = div_u64(div_u64(FDCO_MIN, hs_div), frequency);
206 if (!n1 || (n1 & 1))
207 n1++;
208 while (n1 <= 128) {
209 fdco = (u64)frequency * (u64)hs_div * (u64)n1;
210 if (fdco > FDCO_MAX)
211 break;
212 if (fdco >= FDCO_MIN && fdco < best_fdco) {
213 *out_n1 = n1;
214 *out_hs_div = hs_div;
215 *out_rfreq = div64_u64(fdco << 28, data->fxtal);
216 best_fdco = fdco;
217 }
218 n1 += (n1 == 1 ? 1 : 2);
219 }
220 }
221
222 if (best_fdco == ULLONG_MAX)
223 return -EINVAL;
224
225 return 0;
226}
227
228static unsigned long si570_recalc_rate(struct clk_hw *hw,
229 unsigned long parent_rate)
230{
231 int err;
232 u64 rfreq, rate;
233 unsigned int n1, hs_div;
234 struct clk_si570 *data = to_clk_si570(hw);
235
236 err = si570_get_divs(data, &rfreq, &n1, &hs_div);
237 if (err) {
238 dev_err(&data->i2c_client->dev, "unable to recalc rate\n");
239 return data->frequency;
240 }
241
242 rfreq = div_u64(rfreq, hs_div * n1);
243 rate = (data->fxtal * rfreq) >> 28;
244
245 return rate;
246}
247
248static long si570_round_rate(struct clk_hw *hw, unsigned long rate,
249 unsigned long *parent_rate)
250{
251 int err;
252 u64 rfreq;
253 unsigned int n1, hs_div;
254 struct clk_si570 *data = to_clk_si570(hw);
255
256 if (!rate)
257 return 0;
258
259 if (div64_u64(abs(rate - data->frequency) * 10000LL,
260 data->frequency) < 35) {
261 rfreq = div64_u64((data->rfreq * rate) +
262 div64_u64(data->frequency, 2), data->frequency);
263 n1 = data->n1;
264 hs_div = data->hs_div;
265
266 } else {
267 err = si570_calc_divs(rate, data, &rfreq, &n1, &hs_div);
268 if (err) {
269 dev_err(&data->i2c_client->dev,
270 "unable to round rate\n");
271 return 0;
272 }
273 }
274
275 return rate;
276}
277
278/**
279 * si570_set_frequency() - Adjust output frequency
280 * @data: Driver data structure
281 * @frequency: Target frequency
282 * Returns 0 on success.
283 *
284 * Update output frequency for big frequency changes (> 3,500 ppm).
285 */
286static int si570_set_frequency(struct clk_si570 *data, unsigned long frequency)
287{
288 int err;
289
290 err = si570_calc_divs(frequency, data, &data->rfreq, &data->n1,
291 &data->hs_div);
292 if (err)
293 return err;
294
295 /*
296 * The DCO reg should be accessed with a read-modify-write operation
297 * per AN334
298 */
299 regmap_write(data->regmap, SI570_REG_FREEZE_DCO, SI570_FREEZE_DCO);
300 regmap_write(data->regmap, SI570_REG_HS_N1 + data->div_offset,
301 ((data->hs_div - HS_DIV_OFFSET) << HS_DIV_SHIFT) |
302 (((data->n1 - 1) >> 2) & N1_6_2_MASK));
303 si570_update_rfreq(data);
304 regmap_write(data->regmap, SI570_REG_FREEZE_DCO, 0);
305 regmap_write(data->regmap, SI570_REG_CONTROL, SI570_CNTRL_NEWFREQ);
306
307 /* Applying a new frequency can take up to 10ms */
308 usleep_range(10000, 12000);
309
310 return 0;
311}
312
313/**
314 * si570_set_frequency_small() - Adjust output frequency
315 * @data: Driver data structure
316 * @frequency: Target frequency
317 * Returns 0 on success.
318 *
319 * Update output frequency for small frequency changes (< 3,500 ppm).
320 */
321static int si570_set_frequency_small(struct clk_si570 *data,
322 unsigned long frequency)
323{
324 /*
325 * This is a re-implementation of DIV_ROUND_CLOSEST
326 * using the div64_u64 function lieu of letting the compiler
327 * insert EABI calls
328 */
329 data->rfreq = div64_u64((data->rfreq * frequency) +
330 div_u64(data->frequency, 2), data->frequency);
331 regmap_write(data->regmap, SI570_REG_CONTROL, SI570_CNTRL_FREEZE_M);
332 si570_update_rfreq(data);
333 regmap_write(data->regmap, SI570_REG_CONTROL, 0);
334
335 /* Applying a new frequency (small change) can take up to 100us */
336 usleep_range(100, 200);
337
338 return 0;
339}
340
341static int si570_set_rate(struct clk_hw *hw, unsigned long rate,
342 unsigned long parent_rate)
343{
344 struct clk_si570 *data = to_clk_si570(hw);
345 struct i2c_client *client = data->i2c_client;
346 int err;
347
348 if (rate < SI570_MIN_FREQ || rate > data->max_freq) {
349 dev_err(&client->dev,
350 "requested frequency %lu Hz is out of range\n", rate);
351 return -EINVAL;
352 }
353
354 if (div64_u64(abs(rate - data->frequency) * 10000LL,
355 data->frequency) < 35)
356 err = si570_set_frequency_small(data, rate);
357 else
358 err = si570_set_frequency(data, rate);
359
360 if (err)
361 return err;
362
363 data->frequency = rate;
364
365 return 0;
366}
367
368static const struct clk_ops si570_clk_ops = {
369 .recalc_rate = si570_recalc_rate,
370 .round_rate = si570_round_rate,
371 .set_rate = si570_set_rate,
372};
373
374static bool si570_regmap_is_volatile(struct device *dev, unsigned int reg)
375{
376 switch (reg) {
377 case SI570_REG_CONTROL:
378 return true;
379 default:
380 return false;
381 }
382}
383
384static bool si570_regmap_is_writeable(struct device *dev, unsigned int reg)
385{
386 switch (reg) {
387 case SI570_REG_HS_N1 ... (SI570_REG_RFREQ4 + SI570_DIV_OFFSET_7PPM):
388 case SI570_REG_CONTROL:
389 case SI570_REG_FREEZE_DCO:
390 return true;
391 default:
392 return false;
393 }
394}
395
396static struct regmap_config si570_regmap_config = {
397 .reg_bits = 8,
398 .val_bits = 8,
399 .cache_type = REGCACHE_RBTREE,
400 .max_register = 137,
401 .writeable_reg = si570_regmap_is_writeable,
402 .volatile_reg = si570_regmap_is_volatile,
403};
404
405static int si570_probe(struct i2c_client *client,
406 const struct i2c_device_id *id)
407{
408 struct clk_si570 *data;
409 struct clk_init_data init;
410 struct clk *clk;
411 u32 initial_fout, factory_fout, stability;
412 int err;
413 enum clk_si570_variant variant = id->driver_data;
414
415 data = devm_kzalloc(&client->dev, sizeof(*data), GFP_KERNEL);
416 if (!data)
417 return -ENOMEM;
418
419 init.ops = &si570_clk_ops;
420 init.flags = CLK_IS_ROOT;
421 init.num_parents = 0;
422 data->hw.init = &init;
423 data->i2c_client = client;
424
425 if (variant == si57x) {
426 err = of_property_read_u32(client->dev.of_node,
427 "temperature-stability", &stability);
428 if (err) {
429 dev_err(&client->dev,
430 "'temperature-stability' property missing\n");
431 return err;
432 }
433 /* adjust register offsets for 7ppm devices */
434 if (stability == 7)
435 data->div_offset = SI570_DIV_OFFSET_7PPM;
436
437 data->max_freq = SI570_MAX_FREQ;
438 } else {
439 data->max_freq = SI598_MAX_FREQ;
440 }
441
442 if (of_property_read_string(client->dev.of_node, "clock-output-names",
443 &init.name))
444 init.name = client->dev.of_node->name;
445
446 err = of_property_read_u32(client->dev.of_node, "factory-fout",
447 &factory_fout);
448 if (err) {
449 dev_err(&client->dev, "'factory-fout' property missing\n");
450 return err;
451 }
452
453 data->regmap = devm_regmap_init_i2c(client, &si570_regmap_config);
454 if (IS_ERR(data->regmap)) {
455 dev_err(&client->dev, "failed to allocate register map\n");
456 return PTR_ERR(data->regmap);
457 }
458
459 i2c_set_clientdata(client, data);
460 err = si570_get_defaults(data, factory_fout);
461 if (err)
462 return err;
463
464 clk = devm_clk_register(&client->dev, &data->hw);
465 if (IS_ERR(clk)) {
466 dev_err(&client->dev, "clock registration failed\n");
467 return PTR_ERR(clk);
468 }
469 err = of_clk_add_provider(client->dev.of_node, of_clk_src_simple_get,
470 clk);
471 if (err) {
472 dev_err(&client->dev, "unable to add clk provider\n");
473 return err;
474 }
475
476 /* Read the requested initial output frequency from device tree */
477 if (!of_property_read_u32(client->dev.of_node, "clock-frequency",
478 &initial_fout)) {
479 err = clk_set_rate(clk, initial_fout);
480 if (err) {
481 of_clk_del_provider(client->dev.of_node);
482 return err;
483 }
484 }
485
486 /* Display a message indicating that we've successfully registered */
487 dev_info(&client->dev, "registered, current frequency %llu Hz\n",
488 data->frequency);
489
490 return 0;
491}
492
493static int si570_remove(struct i2c_client *client)
494{
495 of_clk_del_provider(client->dev.of_node);
496 return 0;
497}
498
499static const struct i2c_device_id si570_id[] = {
500 { "si570", si57x },
501 { "si571", si57x },
502 { "si598", si59x },
503 { "si599", si59x },
504 { }
505};
506MODULE_DEVICE_TABLE(i2c, si570_id);
507
508static const struct of_device_id clk_si570_of_match[] = {
509 { .compatible = "silabs,si570" },
510 { .compatible = "silabs,si571" },
511 { .compatible = "silabs,si598" },
512 { .compatible = "silabs,si599" },
513 { },
514};
515MODULE_DEVICE_TABLE(of, clk_si570_of_match);
516
517static struct i2c_driver si570_driver = {
518 .driver = {
519 .name = "si570",
520 .of_match_table = of_match_ptr(clk_si570_of_match),
521 },
522 .probe = si570_probe,
523 .remove = si570_remove,
524 .id_table = si570_id,
525};
526module_i2c_driver(si570_driver);
527
528MODULE_AUTHOR("Guenter Roeck <guenter.roeck@ericsson.com>");
529MODULE_AUTHOR("Soeren Brinkmann <soren.brinkmann@xilinx.com");
530MODULE_DESCRIPTION("Si570 driver");
531MODULE_LICENSE("GPL");