aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMark Brown <broonie@opensource.wolfsonmicro.com>2012-05-17 05:04:57 -0400
committerMike Turquette <mturquette@linaro.org>2012-07-11 18:36:44 -0400
commitf05259a6ffa40097b5565f25c3fcf833a9d3ecf5 (patch)
tree7e690ed1971a4db7191f807ef8adde5ef324e7c7
parentdc4cd941c900fda27f0146ab615122426229de73 (diff)
clk: wm831x: Add initial WM831x clock driver
The WM831x and WM832x series of PMICs contain a flexible clocking subsystem intended to provide always on and system core clocks. It features: - A 32.768kHz crystal oscillator which can optionally be used to pass through an externally generated clock. - A FLL which can be clocked from either the 32.768kHz oscillator or the CLKIN pin. - A CLKOUT pin which can bring out either the oscillator or the FLL output. - The 32.768kHz clock can also optionally be brought out on the GPIO pins of the device. This driver fully supports the 32.768kHz oscillator and CLKOUT. The FLL is supported only in AUTO mode, the full flexibility of the FLL cannot currently be used. Due to a lack of access to systems where the core SoC has been converted to use the generic clock API this driver has been compile tested only. Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com> Signed-off-by: Mike Turquette <mturquette@linaro.org>
-rw-r--r--MAINTAINERS1
-rw-r--r--drivers/clk/Kconfig7
-rw-r--r--drivers/clk/Makefile3
-rw-r--r--drivers/clk/clk-wm831x.c428
4 files changed, 439 insertions, 0 deletions
diff --git a/MAINTAINERS b/MAINTAINERS
index 03df1d15ebf3..51b2e1c1585b 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -7577,6 +7577,7 @@ W: http://opensource.wolfsonmicro.com/content/linux-drivers-wolfson-devices
7577S: Supported 7577S: Supported
7578F: Documentation/hwmon/wm83?? 7578F: Documentation/hwmon/wm83??
7579F: arch/arm/mach-s3c64xx/mach-crag6410* 7579F: arch/arm/mach-s3c64xx/mach-crag6410*
7580F: drivers/clk/clk-wm83*.c
7580F: drivers/leds/leds-wm83*.c 7581F: drivers/leds/leds-wm83*.c
7581F: drivers/hwmon/wm83??-hwmon.c 7582F: drivers/hwmon/wm83??-hwmon.c
7582F: drivers/input/misc/wm831x-on.c 7583F: drivers/input/misc/wm831x-on.c
diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig
index 4864407e3fc4..3f99b9099658 100644
--- a/drivers/clk/Kconfig
+++ b/drivers/clk/Kconfig
@@ -34,4 +34,11 @@ config COMMON_CLK_DEBUG
34 clk_flags, clk_prepare_count, clk_enable_count & 34 clk_flags, clk_prepare_count, clk_enable_count &
35 clk_notifier_count. 35 clk_notifier_count.
36 36
37config COMMON_CLK_WM831X
38 tristate "Clock driver for WM831x/2x PMICs"
39 depends on MFD_WM831X
40 ---help---
41 Supports the clocking subsystem of the WM831x/2x series of
42 PMICs from Wolfson Microlectronics.
43
37endmenu 44endmenu
diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile
index b9a5158a30b1..b679f117f3cc 100644
--- a/drivers/clk/Makefile
+++ b/drivers/clk/Makefile
@@ -5,3 +5,6 @@ obj-$(CONFIG_COMMON_CLK) += clk.o clk-fixed-rate.o clk-gate.o \
5# SoCs specific 5# SoCs specific
6obj-$(CONFIG_ARCH_MXS) += mxs/ 6obj-$(CONFIG_ARCH_MXS) += mxs/
7obj-$(CONFIG_PLAT_SPEAR) += spear/ 7obj-$(CONFIG_PLAT_SPEAR) += spear/
8
9# Chip specific
10obj-$(CONFIG_COMMON_CLK_WM831X) += clk-wm831x.o
diff --git a/drivers/clk/clk-wm831x.c b/drivers/clk/clk-wm831x.c
new file mode 100644
index 000000000000..e7b7765e85f3
--- /dev/null
+++ b/drivers/clk/clk-wm831x.c
@@ -0,0 +1,428 @@
1/*
2 * WM831x clock control
3 *
4 * Copyright 2011-2 Wolfson Microelectronics PLC.
5 *
6 * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
7 *
8 * This program is free software; you can redistribute it and/or modify it
9 * under the terms of the GNU General Public License as published by the
10 * Free Software Foundation; either version 2 of the License, or (at your
11 * option) any later version.
12 *
13 */
14
15#include <linux/clk.h>
16#include <linux/clk-provider.h>
17#include <linux/delay.h>
18#include <linux/module.h>
19#include <linux/slab.h>
20#include <linux/platform_device.h>
21#include <linux/mfd/wm831x/core.h>
22
23struct wm831x_clk {
24 struct wm831x *wm831x;
25 struct clk_hw xtal_hw;
26 struct clk_hw fll_hw;
27 struct clk_hw clkout_hw;
28 struct clk *xtal;
29 struct clk *fll;
30 struct clk *clkout;
31 bool xtal_ena;
32};
33
34static int wm831x_xtal_is_enabled(struct clk_hw *hw)
35{
36 struct wm831x_clk *clkdata = container_of(hw, struct wm831x_clk,
37 xtal_hw);
38
39 return clkdata->xtal_ena;
40}
41
42static unsigned long wm831x_xtal_recalc_rate(struct clk_hw *hw,
43 unsigned long parent_rate)
44{
45 struct wm831x_clk *clkdata = container_of(hw, struct wm831x_clk,
46 xtal_hw);
47
48 if (clkdata->xtal_ena)
49 return 32768;
50 else
51 return 0;
52}
53
54static const struct clk_ops wm831x_xtal_ops = {
55 .is_enabled = wm831x_xtal_is_enabled,
56 .recalc_rate = wm831x_xtal_recalc_rate,
57};
58
59static struct clk_init_data wm831x_xtal_init = {
60 .name = "xtal",
61 .ops = &wm831x_xtal_ops,
62 .flags = CLK_IS_ROOT,
63};
64
65static const unsigned long wm831x_fll_auto_rates[] = {
66 2048000,
67 11289600,
68 12000000,
69 12288000,
70 19200000,
71 22579600,
72 24000000,
73 24576000,
74};
75
76static int wm831x_fll_is_enabled(struct clk_hw *hw)
77{
78 struct wm831x_clk *clkdata = container_of(hw, struct wm831x_clk,
79 fll_hw);
80 struct wm831x *wm831x = clkdata->wm831x;
81 int ret;
82
83 ret = wm831x_reg_read(wm831x, WM831X_FLL_CONTROL_1);
84 if (ret < 0) {
85 dev_err(wm831x->dev, "Unable to read FLL_CONTROL_1: %d\n",
86 ret);
87 return true;
88 }
89
90 return (ret & WM831X_FLL_ENA) != 0;
91}
92
93static int wm831x_fll_prepare(struct clk_hw *hw)
94{
95 struct wm831x_clk *clkdata = container_of(hw, struct wm831x_clk,
96 fll_hw);
97 struct wm831x *wm831x = clkdata->wm831x;
98 int ret;
99
100 ret = wm831x_set_bits(wm831x, WM831X_FLL_CONTROL_2,
101 WM831X_FLL_ENA, WM831X_FLL_ENA);
102 if (ret != 0)
103 dev_crit(wm831x->dev, "Failed to enable FLL: %d\n", ret);
104
105 usleep_range(2000, 2000);
106
107 return ret;
108}
109
110static void wm831x_fll_unprepare(struct clk_hw *hw)
111{
112 struct wm831x_clk *clkdata = container_of(hw, struct wm831x_clk,
113 fll_hw);
114 struct wm831x *wm831x = clkdata->wm831x;
115 int ret;
116
117 ret = wm831x_set_bits(wm831x, WM831X_FLL_CONTROL_2, WM831X_FLL_ENA, 0);
118 if (ret != 0)
119 dev_crit(wm831x->dev, "Failed to disaable FLL: %d\n", ret);
120}
121
122static unsigned long wm831x_fll_recalc_rate(struct clk_hw *hw,
123 unsigned long parent_rate)
124{
125 struct wm831x_clk *clkdata = container_of(hw, struct wm831x_clk,
126 fll_hw);
127 struct wm831x *wm831x = clkdata->wm831x;
128 int ret;
129
130 ret = wm831x_reg_read(wm831x, WM831X_CLOCK_CONTROL_2);
131 if (ret < 0) {
132 dev_err(wm831x->dev, "Unable to read CLOCK_CONTROL_2: %d\n",
133 ret);
134 return 0;
135 }
136
137 if (ret & WM831X_FLL_AUTO)
138 return wm831x_fll_auto_rates[ret & WM831X_FLL_AUTO_FREQ_MASK];
139
140 dev_err(wm831x->dev, "FLL only supported in AUTO mode\n");
141
142 return 0;
143}
144
145static long wm831x_fll_round_rate(struct clk_hw *hw, unsigned long rate,
146 unsigned long *unused)
147{
148 int best = 0;
149 int i;
150
151 for (i = 0; i < ARRAY_SIZE(wm831x_fll_auto_rates); i++)
152 if (abs(wm831x_fll_auto_rates[i] - rate) <
153 abs(wm831x_fll_auto_rates[best] - rate))
154 best = i;
155
156 return wm831x_fll_auto_rates[best];
157}
158
159static int wm831x_fll_set_rate(struct clk_hw *hw, unsigned long rate,
160 unsigned long parent_rate)
161{
162 struct wm831x_clk *clkdata = container_of(hw, struct wm831x_clk,
163 fll_hw);
164 struct wm831x *wm831x = clkdata->wm831x;
165 int i;
166
167 for (i = 0; i < ARRAY_SIZE(wm831x_fll_auto_rates); i++)
168 if (wm831x_fll_auto_rates[i] == rate)
169 break;
170 if (i == ARRAY_SIZE(wm831x_fll_auto_rates))
171 return -EINVAL;
172
173 if (wm831x_fll_is_enabled(hw))
174 return -EPERM;
175
176 return wm831x_set_bits(wm831x, WM831X_CLOCK_CONTROL_2,
177 WM831X_FLL_AUTO_FREQ_MASK, i);
178}
179
180static const char *wm831x_fll_parents[] = {
181 "xtal",
182 "clkin",
183};
184
185static u8 wm831x_fll_get_parent(struct clk_hw *hw)
186{
187 struct wm831x_clk *clkdata = container_of(hw, struct wm831x_clk,
188 fll_hw);
189 struct wm831x *wm831x = clkdata->wm831x;
190 int ret;
191
192 /* AUTO mode is always clocked from the crystal */
193 ret = wm831x_reg_read(wm831x, WM831X_CLOCK_CONTROL_2);
194 if (ret < 0) {
195 dev_err(wm831x->dev, "Unable to read CLOCK_CONTROL_2: %d\n",
196 ret);
197 return 0;
198 }
199
200 if (ret & WM831X_FLL_AUTO)
201 return 0;
202
203 ret = wm831x_reg_read(wm831x, WM831X_FLL_CONTROL_5);
204 if (ret < 0) {
205 dev_err(wm831x->dev, "Unable to read FLL_CONTROL_5: %d\n",
206 ret);
207 return 0;
208 }
209
210 switch (ret & WM831X_FLL_CLK_SRC_MASK) {
211 case 0:
212 return 0;
213 case 1:
214 return 1;
215 default:
216 dev_err(wm831x->dev, "Unsupported FLL clock source %d\n",
217 ret & WM831X_FLL_CLK_SRC_MASK);
218 return 0;
219 }
220}
221
222static const struct clk_ops wm831x_fll_ops = {
223 .is_enabled = wm831x_fll_is_enabled,
224 .prepare = wm831x_fll_prepare,
225 .unprepare = wm831x_fll_unprepare,
226 .round_rate = wm831x_fll_round_rate,
227 .recalc_rate = wm831x_fll_recalc_rate,
228 .set_rate = wm831x_fll_set_rate,
229 .get_parent = wm831x_fll_get_parent,
230};
231
232static struct clk_init_data wm831x_fll_init = {
233 .name = "fll",
234 .ops = &wm831x_fll_ops,
235 .parent_names = wm831x_fll_parents,
236 .num_parents = ARRAY_SIZE(wm831x_fll_parents),
237 .flags = CLK_SET_RATE_GATE,
238};
239
240static int wm831x_clkout_is_enabled(struct clk_hw *hw)
241{
242 struct wm831x_clk *clkdata = container_of(hw, struct wm831x_clk,
243 clkout_hw);
244 struct wm831x *wm831x = clkdata->wm831x;
245 int ret;
246
247 ret = wm831x_reg_read(wm831x, WM831X_CLOCK_CONTROL_1);
248 if (ret < 0) {
249 dev_err(wm831x->dev, "Unable to read CLOCK_CONTROL_1: %d\n",
250 ret);
251 return true;
252 }
253
254 return (ret & WM831X_CLKOUT_ENA) != 0;
255}
256
257static int wm831x_clkout_prepare(struct clk_hw *hw)
258{
259 struct wm831x_clk *clkdata = container_of(hw, struct wm831x_clk,
260 clkout_hw);
261 struct wm831x *wm831x = clkdata->wm831x;
262 int ret;
263
264 ret = wm831x_reg_unlock(wm831x);
265 if (ret != 0) {
266 dev_crit(wm831x->dev, "Failed to lock registers: %d\n", ret);
267 return ret;
268 }
269
270 ret = wm831x_set_bits(wm831x, WM831X_CLOCK_CONTROL_1,
271 WM831X_CLKOUT_ENA, WM831X_CLKOUT_ENA);
272 if (ret != 0)
273 dev_crit(wm831x->dev, "Failed to enable CLKOUT: %d\n", ret);
274
275 wm831x_reg_lock(wm831x);
276
277 return ret;
278}
279
280static void wm831x_clkout_unprepare(struct clk_hw *hw)
281{
282 struct wm831x_clk *clkdata = container_of(hw, struct wm831x_clk,
283 clkout_hw);
284 struct wm831x *wm831x = clkdata->wm831x;
285 int ret;
286
287 ret = wm831x_reg_unlock(wm831x);
288 if (ret != 0) {
289 dev_crit(wm831x->dev, "Failed to lock registers: %d\n", ret);
290 return;
291 }
292
293 ret = wm831x_set_bits(wm831x, WM831X_CLOCK_CONTROL_1,
294 WM831X_CLKOUT_ENA, 0);
295 if (ret != 0)
296 dev_crit(wm831x->dev, "Failed to disable CLKOUT: %d\n", ret);
297
298 wm831x_reg_lock(wm831x);
299}
300
301static const char *wm831x_clkout_parents[] = {
302 "xtal",
303 "fll",
304};
305
306static u8 wm831x_clkout_get_parent(struct clk_hw *hw)
307{
308 struct wm831x_clk *clkdata = container_of(hw, struct wm831x_clk,
309 clkout_hw);
310 struct wm831x *wm831x = clkdata->wm831x;
311 int ret;
312
313 ret = wm831x_reg_read(wm831x, WM831X_CLOCK_CONTROL_1);
314 if (ret < 0) {
315 dev_err(wm831x->dev, "Unable to read CLOCK_CONTROL_1: %d\n",
316 ret);
317 return 0;
318 }
319
320 if (ret & WM831X_CLKOUT_SRC)
321 return 0;
322 else
323 return 1;
324}
325
326static int wm831x_clkout_set_parent(struct clk_hw *hw, u8 parent)
327{
328 struct wm831x_clk *clkdata = container_of(hw, struct wm831x_clk,
329 clkout_hw);
330 struct wm831x *wm831x = clkdata->wm831x;
331
332 return wm831x_set_bits(wm831x, WM831X_CLOCK_CONTROL_1,
333 WM831X_CLKOUT_SRC,
334 parent << WM831X_CLKOUT_SRC_SHIFT);
335}
336
337static const struct clk_ops wm831x_clkout_ops = {
338 .is_enabled = wm831x_clkout_is_enabled,
339 .prepare = wm831x_clkout_prepare,
340 .unprepare = wm831x_clkout_unprepare,
341 .get_parent = wm831x_clkout_get_parent,
342 .set_parent = wm831x_clkout_set_parent,
343};
344
345static struct clk_init_data wm831x_clkout_init = {
346 .name = "clkout",
347 .ops = &wm831x_clkout_ops,
348 .parent_names = wm831x_clkout_parents,
349 .num_parents = ARRAY_SIZE(wm831x_clkout_parents),
350 .flags = CLK_SET_RATE_PARENT,
351};
352
353static __devinit int wm831x_clk_probe(struct platform_device *pdev)
354{
355 struct wm831x *wm831x = dev_get_drvdata(pdev->dev.parent);
356 struct wm831x_clk *clkdata;
357 int ret;
358
359 clkdata = devm_kzalloc(&pdev->dev, sizeof(*clkdata), GFP_KERNEL);
360 if (!clkdata)
361 return -ENOMEM;
362
363 /* XTAL_ENA can only be set via OTP/InstantConfig so just read once */
364 ret = wm831x_reg_read(wm831x, WM831X_CLOCK_CONTROL_2);
365 if (ret < 0) {
366 dev_err(wm831x->dev, "Unable to read CLOCK_CONTROL_2: %d\n",
367 ret);
368 return ret;
369 }
370 clkdata->xtal_ena = ret & WM831X_XTAL_ENA;
371
372 clkdata->xtal_hw.init = &wm831x_xtal_init;
373 clkdata->xtal = clk_register(&pdev->dev, &clkdata->xtal_hw);
374 if (!clkdata->xtal)
375 return -EINVAL;
376
377 clkdata->fll_hw.init = &wm831x_fll_init;
378 clkdata->fll = clk_register(&pdev->dev, &clkdata->fll_hw);
379 if (!clkdata->fll) {
380 ret = -EINVAL;
381 goto err_xtal;
382 }
383
384 clkdata->clkout_hw.init = &wm831x_clkout_init;
385 clkdata->clkout = clk_register(&pdev->dev, &clkdata->clkout_hw);
386 if (!clkdata->clkout) {
387 ret = -EINVAL;
388 goto err_fll;
389 }
390
391 dev_set_drvdata(&pdev->dev, clkdata);
392
393 return 0;
394
395err_fll:
396 clk_unregister(clkdata->fll);
397err_xtal:
398 clk_unregister(clkdata->xtal);
399 return ret;
400}
401
402static int __devexit wm831x_clk_remove(struct platform_device *pdev)
403{
404 struct wm831x_clk *clkdata = dev_get_drvdata(&pdev->dev);
405
406 clk_unregister(clkdata->clkout);
407 clk_unregister(clkdata->fll);
408 clk_unregister(clkdata->xtal);
409
410 return 0;
411}
412
413static struct platform_driver wm831x_clk_driver = {
414 .probe = wm831x_clk_probe,
415 .remove = __devexit_p(wm831x_clk_remove),
416 .driver = {
417 .name = "wm831x-clk",
418 .owner = THIS_MODULE,
419 },
420};
421
422module_platform_driver(wm831x_clk_driver);
423
424/* Module information */
425MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
426MODULE_DESCRIPTION("WM831x clock driver");
427MODULE_LICENSE("GPL");
428MODULE_ALIAS("platform:wm831x-clk");