xref: /linux/drivers/clk/clk-wm831x.c (revision cdd5b5a9761fd66d17586e4f4ba6588c70e640ea)
12874c5fdSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
2f05259a6SMark Brown /*
3f05259a6SMark Brown  * WM831x clock control
4f05259a6SMark Brown  *
5f05259a6SMark Brown  * Copyright 2011-2 Wolfson Microelectronics PLC.
6f05259a6SMark Brown  *
7f05259a6SMark Brown  * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
8f05259a6SMark Brown  */
9f05259a6SMark Brown 
10f05259a6SMark Brown #include <linux/clk-provider.h>
11f05259a6SMark Brown #include <linux/delay.h>
12f05259a6SMark Brown #include <linux/module.h>
13f05259a6SMark Brown #include <linux/slab.h>
14f05259a6SMark Brown #include <linux/platform_device.h>
15f05259a6SMark Brown #include <linux/mfd/wm831x/core.h>
16f05259a6SMark Brown 
17f05259a6SMark Brown struct wm831x_clk {
18f05259a6SMark Brown 	struct wm831x *wm831x;
19f05259a6SMark Brown 	struct clk_hw xtal_hw;
20f05259a6SMark Brown 	struct clk_hw fll_hw;
21f05259a6SMark Brown 	struct clk_hw clkout_hw;
22f05259a6SMark Brown 	bool xtal_ena;
23f05259a6SMark Brown };
24f05259a6SMark Brown 
wm831x_xtal_is_prepared(struct clk_hw * hw)25a5828a6cSMark Brown static int wm831x_xtal_is_prepared(struct clk_hw *hw)
26f05259a6SMark Brown {
27f05259a6SMark Brown 	struct wm831x_clk *clkdata = container_of(hw, struct wm831x_clk,
28f05259a6SMark Brown 						  xtal_hw);
29f05259a6SMark Brown 
30f05259a6SMark Brown 	return clkdata->xtal_ena;
31f05259a6SMark Brown }
32f05259a6SMark Brown 
wm831x_xtal_recalc_rate(struct clk_hw * hw,unsigned long parent_rate)33f05259a6SMark Brown static unsigned long wm831x_xtal_recalc_rate(struct clk_hw *hw,
34f05259a6SMark Brown 					     unsigned long parent_rate)
35f05259a6SMark Brown {
36f05259a6SMark Brown 	struct wm831x_clk *clkdata = container_of(hw, struct wm831x_clk,
37f05259a6SMark Brown 						  xtal_hw);
38f05259a6SMark Brown 
39f05259a6SMark Brown 	if (clkdata->xtal_ena)
40f05259a6SMark Brown 		return 32768;
41f05259a6SMark Brown 	else
42f05259a6SMark Brown 		return 0;
43f05259a6SMark Brown }
44f05259a6SMark Brown 
45f05259a6SMark Brown static const struct clk_ops wm831x_xtal_ops = {
46a5828a6cSMark Brown 	.is_prepared = wm831x_xtal_is_prepared,
47f05259a6SMark Brown 	.recalc_rate = wm831x_xtal_recalc_rate,
48f05259a6SMark Brown };
49f05259a6SMark Brown 
500777066dSBhumika Goyal static const struct clk_init_data wm831x_xtal_init = {
51f05259a6SMark Brown 	.name = "xtal",
52f05259a6SMark Brown 	.ops = &wm831x_xtal_ops,
53f05259a6SMark Brown };
54f05259a6SMark Brown 
55f05259a6SMark Brown static const unsigned long wm831x_fll_auto_rates[] = {
56f05259a6SMark Brown 	 2048000,
57f05259a6SMark Brown 	11289600,
58f05259a6SMark Brown 	12000000,
59f05259a6SMark Brown 	12288000,
60f05259a6SMark Brown 	19200000,
61f05259a6SMark Brown 	22579600,
62f05259a6SMark Brown 	24000000,
63f05259a6SMark Brown 	24576000,
64f05259a6SMark Brown };
65f05259a6SMark Brown 
wm831x_fll_is_prepared(struct clk_hw * hw)66a5828a6cSMark Brown static int wm831x_fll_is_prepared(struct clk_hw *hw)
67f05259a6SMark Brown {
68f05259a6SMark Brown 	struct wm831x_clk *clkdata = container_of(hw, struct wm831x_clk,
69f05259a6SMark Brown 						  fll_hw);
70f05259a6SMark Brown 	struct wm831x *wm831x = clkdata->wm831x;
71f05259a6SMark Brown 	int ret;
72f05259a6SMark Brown 
73f05259a6SMark Brown 	ret = wm831x_reg_read(wm831x, WM831X_FLL_CONTROL_1);
74f05259a6SMark Brown 	if (ret < 0) {
75f05259a6SMark Brown 		dev_err(wm831x->dev, "Unable to read FLL_CONTROL_1: %d\n",
76f05259a6SMark Brown 			ret);
77f05259a6SMark Brown 		return true;
78f05259a6SMark Brown 	}
79f05259a6SMark Brown 
80f05259a6SMark Brown 	return (ret & WM831X_FLL_ENA) != 0;
81f05259a6SMark Brown }
82f05259a6SMark Brown 
wm831x_fll_prepare(struct clk_hw * hw)83f05259a6SMark Brown static int wm831x_fll_prepare(struct clk_hw *hw)
84f05259a6SMark Brown {
85f05259a6SMark Brown 	struct wm831x_clk *clkdata = container_of(hw, struct wm831x_clk,
86f05259a6SMark Brown 						  fll_hw);
87f05259a6SMark Brown 	struct wm831x *wm831x = clkdata->wm831x;
88f05259a6SMark Brown 	int ret;
89f05259a6SMark Brown 
906f8b3145SAxel Lin 	ret = wm831x_set_bits(wm831x, WM831X_FLL_CONTROL_1,
91f05259a6SMark Brown 			      WM831X_FLL_ENA, WM831X_FLL_ENA);
92f05259a6SMark Brown 	if (ret != 0)
93f05259a6SMark Brown 		dev_crit(wm831x->dev, "Failed to enable FLL: %d\n", ret);
94f05259a6SMark Brown 
95ed784c53SNicholas Mc Guire 	/* wait 2-3 ms for new frequency taking effect */
96ed784c53SNicholas Mc Guire 	usleep_range(2000, 3000);
97f05259a6SMark Brown 
98f05259a6SMark Brown 	return ret;
99f05259a6SMark Brown }
100f05259a6SMark Brown 
wm831x_fll_unprepare(struct clk_hw * hw)101f05259a6SMark Brown static void wm831x_fll_unprepare(struct clk_hw *hw)
102f05259a6SMark Brown {
103f05259a6SMark Brown 	struct wm831x_clk *clkdata = container_of(hw, struct wm831x_clk,
104f05259a6SMark Brown 						  fll_hw);
105f05259a6SMark Brown 	struct wm831x *wm831x = clkdata->wm831x;
106f05259a6SMark Brown 	int ret;
107f05259a6SMark Brown 
1086f8b3145SAxel Lin 	ret = wm831x_set_bits(wm831x, WM831X_FLL_CONTROL_1, WM831X_FLL_ENA, 0);
109f05259a6SMark Brown 	if (ret != 0)
1106f8b3145SAxel Lin 		dev_crit(wm831x->dev, "Failed to disable FLL: %d\n", ret);
111f05259a6SMark Brown }
112f05259a6SMark Brown 
wm831x_fll_recalc_rate(struct clk_hw * hw,unsigned long parent_rate)113f05259a6SMark Brown static unsigned long wm831x_fll_recalc_rate(struct clk_hw *hw,
114f05259a6SMark Brown 					    unsigned long parent_rate)
115f05259a6SMark Brown {
116f05259a6SMark Brown 	struct wm831x_clk *clkdata = container_of(hw, struct wm831x_clk,
117f05259a6SMark Brown 						  fll_hw);
118f05259a6SMark Brown 	struct wm831x *wm831x = clkdata->wm831x;
119f05259a6SMark Brown 	int ret;
120f05259a6SMark Brown 
121f05259a6SMark Brown 	ret = wm831x_reg_read(wm831x, WM831X_CLOCK_CONTROL_2);
122f05259a6SMark Brown 	if (ret < 0) {
123f05259a6SMark Brown 		dev_err(wm831x->dev, "Unable to read CLOCK_CONTROL_2: %d\n",
124f05259a6SMark Brown 			ret);
125f05259a6SMark Brown 		return 0;
126f05259a6SMark Brown 	}
127f05259a6SMark Brown 
128f05259a6SMark Brown 	if (ret & WM831X_FLL_AUTO)
129f05259a6SMark Brown 		return wm831x_fll_auto_rates[ret & WM831X_FLL_AUTO_FREQ_MASK];
130f05259a6SMark Brown 
131f05259a6SMark Brown 	dev_err(wm831x->dev, "FLL only supported in AUTO mode\n");
132f05259a6SMark Brown 
133f05259a6SMark Brown 	return 0;
134f05259a6SMark Brown }
135f05259a6SMark Brown 
wm831x_fll_round_rate(struct clk_hw * hw,unsigned long rate,unsigned long * unused)136f05259a6SMark Brown static long wm831x_fll_round_rate(struct clk_hw *hw, unsigned long rate,
137f05259a6SMark Brown 				  unsigned long *unused)
138f05259a6SMark Brown {
139f05259a6SMark Brown 	int best = 0;
140f05259a6SMark Brown 	int i;
141f05259a6SMark Brown 
142f05259a6SMark Brown 	for (i = 0; i < ARRAY_SIZE(wm831x_fll_auto_rates); i++)
143f05259a6SMark Brown 		if (abs(wm831x_fll_auto_rates[i] - rate) <
144f05259a6SMark Brown 		    abs(wm831x_fll_auto_rates[best] - rate))
145f05259a6SMark Brown 			best = i;
146f05259a6SMark Brown 
147f05259a6SMark Brown 	return wm831x_fll_auto_rates[best];
148f05259a6SMark Brown }
149f05259a6SMark Brown 
wm831x_fll_set_rate(struct clk_hw * hw,unsigned long rate,unsigned long parent_rate)150f05259a6SMark Brown static int wm831x_fll_set_rate(struct clk_hw *hw, unsigned long rate,
151f05259a6SMark Brown 			       unsigned long parent_rate)
152f05259a6SMark Brown {
153f05259a6SMark Brown 	struct wm831x_clk *clkdata = container_of(hw, struct wm831x_clk,
154f05259a6SMark Brown 						  fll_hw);
155f05259a6SMark Brown 	struct wm831x *wm831x = clkdata->wm831x;
156f05259a6SMark Brown 	int i;
157f05259a6SMark Brown 
158f05259a6SMark Brown 	for (i = 0; i < ARRAY_SIZE(wm831x_fll_auto_rates); i++)
159f05259a6SMark Brown 		if (wm831x_fll_auto_rates[i] == rate)
160f05259a6SMark Brown 			break;
161f05259a6SMark Brown 	if (i == ARRAY_SIZE(wm831x_fll_auto_rates))
162f05259a6SMark Brown 		return -EINVAL;
163f05259a6SMark Brown 
164a5828a6cSMark Brown 	if (wm831x_fll_is_prepared(hw))
165f05259a6SMark Brown 		return -EPERM;
166f05259a6SMark Brown 
167f05259a6SMark Brown 	return wm831x_set_bits(wm831x, WM831X_CLOCK_CONTROL_2,
168f05259a6SMark Brown 			       WM831X_FLL_AUTO_FREQ_MASK, i);
169f05259a6SMark Brown }
170f05259a6SMark Brown 
171f05259a6SMark Brown static const char *wm831x_fll_parents[] = {
172f05259a6SMark Brown 	"xtal",
173f05259a6SMark Brown 	"clkin",
174f05259a6SMark Brown };
175f05259a6SMark Brown 
wm831x_fll_get_parent(struct clk_hw * hw)176f05259a6SMark Brown static u8 wm831x_fll_get_parent(struct clk_hw *hw)
177f05259a6SMark Brown {
178f05259a6SMark Brown 	struct wm831x_clk *clkdata = container_of(hw, struct wm831x_clk,
179f05259a6SMark Brown 						  fll_hw);
180f05259a6SMark Brown 	struct wm831x *wm831x = clkdata->wm831x;
181f05259a6SMark Brown 	int ret;
182f05259a6SMark Brown 
183f05259a6SMark Brown 	/* AUTO mode is always clocked from the crystal */
184f05259a6SMark Brown 	ret = wm831x_reg_read(wm831x, WM831X_CLOCK_CONTROL_2);
185f05259a6SMark Brown 	if (ret < 0) {
186f05259a6SMark Brown 		dev_err(wm831x->dev, "Unable to read CLOCK_CONTROL_2: %d\n",
187f05259a6SMark Brown 			ret);
188f05259a6SMark Brown 		return 0;
189f05259a6SMark Brown 	}
190f05259a6SMark Brown 
191f05259a6SMark Brown 	if (ret & WM831X_FLL_AUTO)
192f05259a6SMark Brown 		return 0;
193f05259a6SMark Brown 
194f05259a6SMark Brown 	ret = wm831x_reg_read(wm831x, WM831X_FLL_CONTROL_5);
195f05259a6SMark Brown 	if (ret < 0) {
196f05259a6SMark Brown 		dev_err(wm831x->dev, "Unable to read FLL_CONTROL_5: %d\n",
197f05259a6SMark Brown 			ret);
198f05259a6SMark Brown 		return 0;
199f05259a6SMark Brown 	}
200f05259a6SMark Brown 
201f05259a6SMark Brown 	switch (ret & WM831X_FLL_CLK_SRC_MASK) {
202f05259a6SMark Brown 	case 0:
203f05259a6SMark Brown 		return 0;
204f05259a6SMark Brown 	case 1:
205f05259a6SMark Brown 		return 1;
206f05259a6SMark Brown 	default:
207f05259a6SMark Brown 		dev_err(wm831x->dev, "Unsupported FLL clock source %d\n",
208f05259a6SMark Brown 			ret & WM831X_FLL_CLK_SRC_MASK);
209f05259a6SMark Brown 		return 0;
210f05259a6SMark Brown 	}
211f05259a6SMark Brown }
212f05259a6SMark Brown 
213f05259a6SMark Brown static const struct clk_ops wm831x_fll_ops = {
214a5828a6cSMark Brown 	.is_prepared = wm831x_fll_is_prepared,
215f05259a6SMark Brown 	.prepare = wm831x_fll_prepare,
216f05259a6SMark Brown 	.unprepare = wm831x_fll_unprepare,
217f05259a6SMark Brown 	.round_rate = wm831x_fll_round_rate,
218f05259a6SMark Brown 	.recalc_rate = wm831x_fll_recalc_rate,
219f05259a6SMark Brown 	.set_rate = wm831x_fll_set_rate,
220f05259a6SMark Brown 	.get_parent = wm831x_fll_get_parent,
221f05259a6SMark Brown };
222f05259a6SMark Brown 
2230777066dSBhumika Goyal static const struct clk_init_data wm831x_fll_init = {
224f05259a6SMark Brown 	.name = "fll",
225f05259a6SMark Brown 	.ops = &wm831x_fll_ops,
226f05259a6SMark Brown 	.parent_names = wm831x_fll_parents,
227f05259a6SMark Brown 	.num_parents = ARRAY_SIZE(wm831x_fll_parents),
228f05259a6SMark Brown 	.flags = CLK_SET_RATE_GATE,
229f05259a6SMark Brown };
230f05259a6SMark Brown 
wm831x_clkout_is_prepared(struct clk_hw * hw)231a5828a6cSMark Brown static int wm831x_clkout_is_prepared(struct clk_hw *hw)
232f05259a6SMark Brown {
233f05259a6SMark Brown 	struct wm831x_clk *clkdata = container_of(hw, struct wm831x_clk,
234f05259a6SMark Brown 						  clkout_hw);
235f05259a6SMark Brown 	struct wm831x *wm831x = clkdata->wm831x;
236f05259a6SMark Brown 	int ret;
237f05259a6SMark Brown 
238f05259a6SMark Brown 	ret = wm831x_reg_read(wm831x, WM831X_CLOCK_CONTROL_1);
239f05259a6SMark Brown 	if (ret < 0) {
240f05259a6SMark Brown 		dev_err(wm831x->dev, "Unable to read CLOCK_CONTROL_1: %d\n",
241f05259a6SMark Brown 			ret);
24220979202SPan Bian 		return false;
243f05259a6SMark Brown 	}
244f05259a6SMark Brown 
245f05259a6SMark Brown 	return (ret & WM831X_CLKOUT_ENA) != 0;
246f05259a6SMark Brown }
247f05259a6SMark Brown 
wm831x_clkout_prepare(struct clk_hw * hw)248f05259a6SMark Brown static int wm831x_clkout_prepare(struct clk_hw *hw)
249f05259a6SMark Brown {
250f05259a6SMark Brown 	struct wm831x_clk *clkdata = container_of(hw, struct wm831x_clk,
251f05259a6SMark Brown 						  clkout_hw);
252f05259a6SMark Brown 	struct wm831x *wm831x = clkdata->wm831x;
253f05259a6SMark Brown 	int ret;
254f05259a6SMark Brown 
255f05259a6SMark Brown 	ret = wm831x_reg_unlock(wm831x);
256f05259a6SMark Brown 	if (ret != 0) {
257f05259a6SMark Brown 		dev_crit(wm831x->dev, "Failed to lock registers: %d\n", ret);
258f05259a6SMark Brown 		return ret;
259f05259a6SMark Brown 	}
260f05259a6SMark Brown 
261f05259a6SMark Brown 	ret = wm831x_set_bits(wm831x, WM831X_CLOCK_CONTROL_1,
262f05259a6SMark Brown 			      WM831X_CLKOUT_ENA, WM831X_CLKOUT_ENA);
263f05259a6SMark Brown 	if (ret != 0)
264f05259a6SMark Brown 		dev_crit(wm831x->dev, "Failed to enable CLKOUT: %d\n", ret);
265f05259a6SMark Brown 
266f05259a6SMark Brown 	wm831x_reg_lock(wm831x);
267f05259a6SMark Brown 
268f05259a6SMark Brown 	return ret;
269f05259a6SMark Brown }
270f05259a6SMark Brown 
wm831x_clkout_unprepare(struct clk_hw * hw)271f05259a6SMark Brown static void wm831x_clkout_unprepare(struct clk_hw *hw)
272f05259a6SMark Brown {
273f05259a6SMark Brown 	struct wm831x_clk *clkdata = container_of(hw, struct wm831x_clk,
274f05259a6SMark Brown 						  clkout_hw);
275f05259a6SMark Brown 	struct wm831x *wm831x = clkdata->wm831x;
276f05259a6SMark Brown 	int ret;
277f05259a6SMark Brown 
278f05259a6SMark Brown 	ret = wm831x_reg_unlock(wm831x);
279f05259a6SMark Brown 	if (ret != 0) {
280f05259a6SMark Brown 		dev_crit(wm831x->dev, "Failed to lock registers: %d\n", ret);
281f05259a6SMark Brown 		return;
282f05259a6SMark Brown 	}
283f05259a6SMark Brown 
284f05259a6SMark Brown 	ret = wm831x_set_bits(wm831x, WM831X_CLOCK_CONTROL_1,
285f05259a6SMark Brown 			      WM831X_CLKOUT_ENA, 0);
286f05259a6SMark Brown 	if (ret != 0)
287f05259a6SMark Brown 		dev_crit(wm831x->dev, "Failed to disable CLKOUT: %d\n", ret);
288f05259a6SMark Brown 
289f05259a6SMark Brown 	wm831x_reg_lock(wm831x);
290f05259a6SMark Brown }
291f05259a6SMark Brown 
292f05259a6SMark Brown static const char *wm831x_clkout_parents[] = {
293f05259a6SMark Brown 	"fll",
294bcc7fd20SAxel Lin 	"xtal",
295f05259a6SMark Brown };
296f05259a6SMark Brown 
wm831x_clkout_get_parent(struct clk_hw * hw)297f05259a6SMark Brown static u8 wm831x_clkout_get_parent(struct clk_hw *hw)
298f05259a6SMark Brown {
299f05259a6SMark Brown 	struct wm831x_clk *clkdata = container_of(hw, struct wm831x_clk,
300f05259a6SMark Brown 						  clkout_hw);
301f05259a6SMark Brown 	struct wm831x *wm831x = clkdata->wm831x;
302f05259a6SMark Brown 	int ret;
303f05259a6SMark Brown 
304f05259a6SMark Brown 	ret = wm831x_reg_read(wm831x, WM831X_CLOCK_CONTROL_1);
305f05259a6SMark Brown 	if (ret < 0) {
306f05259a6SMark Brown 		dev_err(wm831x->dev, "Unable to read CLOCK_CONTROL_1: %d\n",
307f05259a6SMark Brown 			ret);
308f05259a6SMark Brown 		return 0;
309f05259a6SMark Brown 	}
310f05259a6SMark Brown 
311f05259a6SMark Brown 	if (ret & WM831X_CLKOUT_SRC)
312f05259a6SMark Brown 		return 1;
313bcc7fd20SAxel Lin 	else
314bcc7fd20SAxel Lin 		return 0;
315f05259a6SMark Brown }
316f05259a6SMark Brown 
wm831x_clkout_set_parent(struct clk_hw * hw,u8 parent)317f05259a6SMark Brown static int wm831x_clkout_set_parent(struct clk_hw *hw, u8 parent)
318f05259a6SMark Brown {
319f05259a6SMark Brown 	struct wm831x_clk *clkdata = container_of(hw, struct wm831x_clk,
320f05259a6SMark Brown 						  clkout_hw);
321f05259a6SMark Brown 	struct wm831x *wm831x = clkdata->wm831x;
322f05259a6SMark Brown 
323f05259a6SMark Brown 	return wm831x_set_bits(wm831x, WM831X_CLOCK_CONTROL_1,
324f05259a6SMark Brown 			       WM831X_CLKOUT_SRC,
325f05259a6SMark Brown 			       parent << WM831X_CLKOUT_SRC_SHIFT);
326f05259a6SMark Brown }
327f05259a6SMark Brown 
328f05259a6SMark Brown static const struct clk_ops wm831x_clkout_ops = {
329a5828a6cSMark Brown 	.is_prepared = wm831x_clkout_is_prepared,
330f05259a6SMark Brown 	.prepare = wm831x_clkout_prepare,
331f05259a6SMark Brown 	.unprepare = wm831x_clkout_unprepare,
332*fa2a1931SMaxime Ripard 	.determine_rate = clk_hw_determine_rate_no_reparent,
333f05259a6SMark Brown 	.get_parent = wm831x_clkout_get_parent,
334f05259a6SMark Brown 	.set_parent = wm831x_clkout_set_parent,
335f05259a6SMark Brown };
336f05259a6SMark Brown 
3370777066dSBhumika Goyal static const struct clk_init_data wm831x_clkout_init = {
338f05259a6SMark Brown 	.name = "clkout",
339f05259a6SMark Brown 	.ops = &wm831x_clkout_ops,
340f05259a6SMark Brown 	.parent_names = wm831x_clkout_parents,
341f05259a6SMark Brown 	.num_parents = ARRAY_SIZE(wm831x_clkout_parents),
342f05259a6SMark Brown 	.flags = CLK_SET_RATE_PARENT,
343f05259a6SMark Brown };
344f05259a6SMark Brown 
wm831x_clk_probe(struct platform_device * pdev)345018ae93fSBill Pemberton static int wm831x_clk_probe(struct platform_device *pdev)
346f05259a6SMark Brown {
347f05259a6SMark Brown 	struct wm831x *wm831x = dev_get_drvdata(pdev->dev.parent);
348f05259a6SMark Brown 	struct wm831x_clk *clkdata;
349f05259a6SMark Brown 	int ret;
350f05259a6SMark Brown 
351f05259a6SMark Brown 	clkdata = devm_kzalloc(&pdev->dev, sizeof(*clkdata), GFP_KERNEL);
352f05259a6SMark Brown 	if (!clkdata)
353f05259a6SMark Brown 		return -ENOMEM;
354f05259a6SMark Brown 
35508442ce9SMark Brown 	clkdata->wm831x = wm831x;
35608442ce9SMark Brown 
357f05259a6SMark Brown 	/* XTAL_ENA can only be set via OTP/InstantConfig so just read once */
358f05259a6SMark Brown 	ret = wm831x_reg_read(wm831x, WM831X_CLOCK_CONTROL_2);
359f05259a6SMark Brown 	if (ret < 0) {
360f05259a6SMark Brown 		dev_err(wm831x->dev, "Unable to read CLOCK_CONTROL_2: %d\n",
361f05259a6SMark Brown 			ret);
362f05259a6SMark Brown 		return ret;
363f05259a6SMark Brown 	}
364f05259a6SMark Brown 	clkdata->xtal_ena = ret & WM831X_XTAL_ENA;
365f05259a6SMark Brown 
366f05259a6SMark Brown 	clkdata->xtal_hw.init = &wm831x_xtal_init;
367cc671d13SStephen Boyd 	ret = devm_clk_hw_register(&pdev->dev, &clkdata->xtal_hw);
368cc671d13SStephen Boyd 	if (ret)
369cc671d13SStephen Boyd 		return ret;
370f05259a6SMark Brown 
371f05259a6SMark Brown 	clkdata->fll_hw.init = &wm831x_fll_init;
372cc671d13SStephen Boyd 	ret = devm_clk_hw_register(&pdev->dev, &clkdata->fll_hw);
373cc671d13SStephen Boyd 	if (ret)
374cc671d13SStephen Boyd 		return ret;
375f05259a6SMark Brown 
376f05259a6SMark Brown 	clkdata->clkout_hw.init = &wm831x_clkout_init;
377cc671d13SStephen Boyd 	ret = devm_clk_hw_register(&pdev->dev, &clkdata->clkout_hw);
378cc671d13SStephen Boyd 	if (ret)
379cc671d13SStephen Boyd 		return ret;
380f05259a6SMark Brown 
381c0431037SJingoo Han 	platform_set_drvdata(pdev, clkdata);
382f05259a6SMark Brown 
383f05259a6SMark Brown 	return 0;
384f05259a6SMark Brown }
385f05259a6SMark Brown 
386f05259a6SMark Brown static struct platform_driver wm831x_clk_driver = {
387f05259a6SMark Brown 	.probe = wm831x_clk_probe,
388f05259a6SMark Brown 	.driver		= {
389f05259a6SMark Brown 		.name	= "wm831x-clk",
390f05259a6SMark Brown 	},
391f05259a6SMark Brown };
392f05259a6SMark Brown 
393f05259a6SMark Brown module_platform_driver(wm831x_clk_driver);
394f05259a6SMark Brown 
395f05259a6SMark Brown /* Module information */
396f05259a6SMark Brown MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
397f05259a6SMark Brown MODULE_DESCRIPTION("WM831x clock driver");
398f05259a6SMark Brown MODULE_LICENSE("GPL");
399f05259a6SMark Brown MODULE_ALIAS("platform:wm831x-clk");
400