xref: /openbmc/linux/drivers/clk/at91/sckc.c (revision 8aa1db9c)
12874c5fdSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
280eded6cSBoris BREZILLON /*
380eded6cSBoris BREZILLON  * drivers/clk/at91/sckc.c
480eded6cSBoris BREZILLON  *
580eded6cSBoris BREZILLON  *  Copyright (C) 2013 Boris BREZILLON <b.brezillon@overkiz.com>
680eded6cSBoris BREZILLON  */
780eded6cSBoris BREZILLON 
880eded6cSBoris BREZILLON #include <linux/clk-provider.h>
980eded6cSBoris BREZILLON #include <linux/clkdev.h>
10ec187ef0SAlexandre Belloni #include <linux/delay.h>
1180eded6cSBoris BREZILLON #include <linux/of.h>
1280eded6cSBoris BREZILLON #include <linux/of_address.h>
1380eded6cSBoris BREZILLON #include <linux/io.h>
1480eded6cSBoris BREZILLON 
15ec187ef0SAlexandre Belloni #define SLOW_CLOCK_FREQ		32768
16ec187ef0SAlexandre Belloni #define SLOWCK_SW_CYCLES	5
17ec187ef0SAlexandre Belloni #define SLOWCK_SW_TIME_USEC	((SLOWCK_SW_CYCLES * USEC_PER_SEC) / \
18ec187ef0SAlexandre Belloni 				 SLOW_CLOCK_FREQ)
19ec187ef0SAlexandre Belloni 
20ec187ef0SAlexandre Belloni #define	AT91_SCKC_CR			0x00
21abaceffcSClaudiu Beznea 
22abaceffcSClaudiu Beznea struct clk_slow_bits {
23abaceffcSClaudiu Beznea 	u32 cr_rcen;
24abaceffcSClaudiu Beznea 	u32 cr_osc32en;
25abaceffcSClaudiu Beznea 	u32 cr_osc32byp;
26abaceffcSClaudiu Beznea 	u32 cr_oscsel;
27abaceffcSClaudiu Beznea };
28ec187ef0SAlexandre Belloni 
29ec187ef0SAlexandre Belloni struct clk_slow_osc {
30ec187ef0SAlexandre Belloni 	struct clk_hw hw;
31ec187ef0SAlexandre Belloni 	void __iomem *sckcr;
32abaceffcSClaudiu Beznea 	const struct clk_slow_bits *bits;
33ec187ef0SAlexandre Belloni 	unsigned long startup_usec;
34ec187ef0SAlexandre Belloni };
35ec187ef0SAlexandre Belloni 
36ec187ef0SAlexandre Belloni #define to_clk_slow_osc(hw) container_of(hw, struct clk_slow_osc, hw)
37ec187ef0SAlexandre Belloni 
384b13b645SAlexandre Belloni struct clk_sama5d4_slow_osc {
394b13b645SAlexandre Belloni 	struct clk_hw hw;
404b13b645SAlexandre Belloni 	void __iomem *sckcr;
41abaceffcSClaudiu Beznea 	const struct clk_slow_bits *bits;
424b13b645SAlexandre Belloni 	unsigned long startup_usec;
434b13b645SAlexandre Belloni 	bool prepared;
444b13b645SAlexandre Belloni };
454b13b645SAlexandre Belloni 
464b13b645SAlexandre Belloni #define to_clk_sama5d4_slow_osc(hw) container_of(hw, struct clk_sama5d4_slow_osc, hw)
474b13b645SAlexandre Belloni 
48ec187ef0SAlexandre Belloni struct clk_slow_rc_osc {
49ec187ef0SAlexandre Belloni 	struct clk_hw hw;
50ec187ef0SAlexandre Belloni 	void __iomem *sckcr;
51abaceffcSClaudiu Beznea 	const struct clk_slow_bits *bits;
52ec187ef0SAlexandre Belloni 	unsigned long frequency;
53ec187ef0SAlexandre Belloni 	unsigned long accuracy;
54ec187ef0SAlexandre Belloni 	unsigned long startup_usec;
55ec187ef0SAlexandre Belloni };
56ec187ef0SAlexandre Belloni 
57ec187ef0SAlexandre Belloni #define to_clk_slow_rc_osc(hw) container_of(hw, struct clk_slow_rc_osc, hw)
58ec187ef0SAlexandre Belloni 
59ec187ef0SAlexandre Belloni struct clk_sam9x5_slow {
60ec187ef0SAlexandre Belloni 	struct clk_hw hw;
61ec187ef0SAlexandre Belloni 	void __iomem *sckcr;
62abaceffcSClaudiu Beznea 	const struct clk_slow_bits *bits;
63ec187ef0SAlexandre Belloni 	u8 parent;
64ec187ef0SAlexandre Belloni };
65ec187ef0SAlexandre Belloni 
66ec187ef0SAlexandre Belloni #define to_clk_sam9x5_slow(hw) container_of(hw, struct clk_sam9x5_slow, hw)
67ec187ef0SAlexandre Belloni 
clk_slow_osc_prepare(struct clk_hw * hw)68ec187ef0SAlexandre Belloni static int clk_slow_osc_prepare(struct clk_hw *hw)
69ec187ef0SAlexandre Belloni {
70ec187ef0SAlexandre Belloni 	struct clk_slow_osc *osc = to_clk_slow_osc(hw);
71ec187ef0SAlexandre Belloni 	void __iomem *sckcr = osc->sckcr;
72ec187ef0SAlexandre Belloni 	u32 tmp = readl(sckcr);
73ec187ef0SAlexandre Belloni 
74abaceffcSClaudiu Beznea 	if (tmp & (osc->bits->cr_osc32byp | osc->bits->cr_osc32en))
75ec187ef0SAlexandre Belloni 		return 0;
76ec187ef0SAlexandre Belloni 
77abaceffcSClaudiu Beznea 	writel(tmp | osc->bits->cr_osc32en, sckcr);
78ec187ef0SAlexandre Belloni 
79658fd65cSAlexandre Belloni 	if (system_state < SYSTEM_RUNNING)
80658fd65cSAlexandre Belloni 		udelay(osc->startup_usec);
81658fd65cSAlexandre Belloni 	else
82ec187ef0SAlexandre Belloni 		usleep_range(osc->startup_usec, osc->startup_usec + 1);
83ec187ef0SAlexandre Belloni 
84ec187ef0SAlexandre Belloni 	return 0;
85ec187ef0SAlexandre Belloni }
86ec187ef0SAlexandre Belloni 
clk_slow_osc_unprepare(struct clk_hw * hw)87ec187ef0SAlexandre Belloni static void clk_slow_osc_unprepare(struct clk_hw *hw)
88ec187ef0SAlexandre Belloni {
89ec187ef0SAlexandre Belloni 	struct clk_slow_osc *osc = to_clk_slow_osc(hw);
90ec187ef0SAlexandre Belloni 	void __iomem *sckcr = osc->sckcr;
91ec187ef0SAlexandre Belloni 	u32 tmp = readl(sckcr);
92ec187ef0SAlexandre Belloni 
93abaceffcSClaudiu Beznea 	if (tmp & osc->bits->cr_osc32byp)
94ec187ef0SAlexandre Belloni 		return;
95ec187ef0SAlexandre Belloni 
96abaceffcSClaudiu Beznea 	writel(tmp & ~osc->bits->cr_osc32en, sckcr);
97ec187ef0SAlexandre Belloni }
98ec187ef0SAlexandre Belloni 
clk_slow_osc_is_prepared(struct clk_hw * hw)99ec187ef0SAlexandre Belloni static int clk_slow_osc_is_prepared(struct clk_hw *hw)
100ec187ef0SAlexandre Belloni {
101ec187ef0SAlexandre Belloni 	struct clk_slow_osc *osc = to_clk_slow_osc(hw);
102ec187ef0SAlexandre Belloni 	void __iomem *sckcr = osc->sckcr;
103ec187ef0SAlexandre Belloni 	u32 tmp = readl(sckcr);
104ec187ef0SAlexandre Belloni 
105abaceffcSClaudiu Beznea 	if (tmp & osc->bits->cr_osc32byp)
106ec187ef0SAlexandre Belloni 		return 1;
107ec187ef0SAlexandre Belloni 
108abaceffcSClaudiu Beznea 	return !!(tmp & osc->bits->cr_osc32en);
109ec187ef0SAlexandre Belloni }
110ec187ef0SAlexandre Belloni 
111ec187ef0SAlexandre Belloni static const struct clk_ops slow_osc_ops = {
112ec187ef0SAlexandre Belloni 	.prepare = clk_slow_osc_prepare,
113ec187ef0SAlexandre Belloni 	.unprepare = clk_slow_osc_unprepare,
114ec187ef0SAlexandre Belloni 	.is_prepared = clk_slow_osc_is_prepared,
115ec187ef0SAlexandre Belloni };
116ec187ef0SAlexandre Belloni 
117ec187ef0SAlexandre Belloni static struct clk_hw * __init
at91_clk_register_slow_osc(void __iomem * sckcr,const char * name,const struct clk_parent_data * parent_data,unsigned long startup,bool bypass,const struct clk_slow_bits * bits)118ec187ef0SAlexandre Belloni at91_clk_register_slow_osc(void __iomem *sckcr,
119ec187ef0SAlexandre Belloni 			   const char *name,
120*8aa1db9cSClaudiu Beznea 			   const struct clk_parent_data *parent_data,
121ec187ef0SAlexandre Belloni 			   unsigned long startup,
122abaceffcSClaudiu Beznea 			   bool bypass,
123abaceffcSClaudiu Beznea 			   const struct clk_slow_bits *bits)
124ec187ef0SAlexandre Belloni {
125ec187ef0SAlexandre Belloni 	struct clk_slow_osc *osc;
126ec187ef0SAlexandre Belloni 	struct clk_hw *hw;
127*8aa1db9cSClaudiu Beznea 	struct clk_init_data init = {};
128ec187ef0SAlexandre Belloni 	int ret;
129ec187ef0SAlexandre Belloni 
130*8aa1db9cSClaudiu Beznea 	if (!sckcr || !name || !parent_data)
131ec187ef0SAlexandre Belloni 		return ERR_PTR(-EINVAL);
132ec187ef0SAlexandre Belloni 
133ec187ef0SAlexandre Belloni 	osc = kzalloc(sizeof(*osc), GFP_KERNEL);
134ec187ef0SAlexandre Belloni 	if (!osc)
135ec187ef0SAlexandre Belloni 		return ERR_PTR(-ENOMEM);
136ec187ef0SAlexandre Belloni 
137ec187ef0SAlexandre Belloni 	init.name = name;
138ec187ef0SAlexandre Belloni 	init.ops = &slow_osc_ops;
139*8aa1db9cSClaudiu Beznea 	init.parent_data = parent_data;
140ec187ef0SAlexandre Belloni 	init.num_parents = 1;
141ec187ef0SAlexandre Belloni 	init.flags = CLK_IGNORE_UNUSED;
142ec187ef0SAlexandre Belloni 
143ec187ef0SAlexandre Belloni 	osc->hw.init = &init;
144ec187ef0SAlexandre Belloni 	osc->sckcr = sckcr;
145ec187ef0SAlexandre Belloni 	osc->startup_usec = startup;
146abaceffcSClaudiu Beznea 	osc->bits = bits;
147ec187ef0SAlexandre Belloni 
148ec187ef0SAlexandre Belloni 	if (bypass)
149abaceffcSClaudiu Beznea 		writel((readl(sckcr) & ~osc->bits->cr_osc32en) |
150abaceffcSClaudiu Beznea 					osc->bits->cr_osc32byp, sckcr);
151ec187ef0SAlexandre Belloni 
152ec187ef0SAlexandre Belloni 	hw = &osc->hw;
153ec187ef0SAlexandre Belloni 	ret = clk_hw_register(NULL, &osc->hw);
154ec187ef0SAlexandre Belloni 	if (ret) {
155ec187ef0SAlexandre Belloni 		kfree(osc);
156ec187ef0SAlexandre Belloni 		hw = ERR_PTR(ret);
157ec187ef0SAlexandre Belloni 	}
158ec187ef0SAlexandre Belloni 
159ec187ef0SAlexandre Belloni 	return hw;
160ec187ef0SAlexandre Belloni }
161ec187ef0SAlexandre Belloni 
at91_clk_unregister_slow_osc(struct clk_hw * hw)1627fb791d0SClaudiu Beznea static void at91_clk_unregister_slow_osc(struct clk_hw *hw)
1637fb791d0SClaudiu Beznea {
1647fb791d0SClaudiu Beznea 	struct clk_slow_osc *osc = to_clk_slow_osc(hw);
1657fb791d0SClaudiu Beznea 
1667fb791d0SClaudiu Beznea 	clk_hw_unregister(hw);
1677fb791d0SClaudiu Beznea 	kfree(osc);
1687fb791d0SClaudiu Beznea }
1697fb791d0SClaudiu Beznea 
clk_slow_rc_osc_recalc_rate(struct clk_hw * hw,unsigned long parent_rate)170ec187ef0SAlexandre Belloni static unsigned long clk_slow_rc_osc_recalc_rate(struct clk_hw *hw,
171ec187ef0SAlexandre Belloni 						 unsigned long parent_rate)
172ec187ef0SAlexandre Belloni {
173ec187ef0SAlexandre Belloni 	struct clk_slow_rc_osc *osc = to_clk_slow_rc_osc(hw);
174ec187ef0SAlexandre Belloni 
175ec187ef0SAlexandre Belloni 	return osc->frequency;
176ec187ef0SAlexandre Belloni }
177ec187ef0SAlexandre Belloni 
clk_slow_rc_osc_recalc_accuracy(struct clk_hw * hw,unsigned long parent_acc)178ec187ef0SAlexandre Belloni static unsigned long clk_slow_rc_osc_recalc_accuracy(struct clk_hw *hw,
179ec187ef0SAlexandre Belloni 						     unsigned long parent_acc)
180ec187ef0SAlexandre Belloni {
181ec187ef0SAlexandre Belloni 	struct clk_slow_rc_osc *osc = to_clk_slow_rc_osc(hw);
182ec187ef0SAlexandre Belloni 
183ec187ef0SAlexandre Belloni 	return osc->accuracy;
184ec187ef0SAlexandre Belloni }
185ec187ef0SAlexandre Belloni 
clk_slow_rc_osc_prepare(struct clk_hw * hw)186ec187ef0SAlexandre Belloni static int clk_slow_rc_osc_prepare(struct clk_hw *hw)
187ec187ef0SAlexandre Belloni {
188ec187ef0SAlexandre Belloni 	struct clk_slow_rc_osc *osc = to_clk_slow_rc_osc(hw);
189ec187ef0SAlexandre Belloni 	void __iomem *sckcr = osc->sckcr;
190ec187ef0SAlexandre Belloni 
191abaceffcSClaudiu Beznea 	writel(readl(sckcr) | osc->bits->cr_rcen, sckcr);
192ec187ef0SAlexandre Belloni 
193658fd65cSAlexandre Belloni 	if (system_state < SYSTEM_RUNNING)
194658fd65cSAlexandre Belloni 		udelay(osc->startup_usec);
195658fd65cSAlexandre Belloni 	else
196ec187ef0SAlexandre Belloni 		usleep_range(osc->startup_usec, osc->startup_usec + 1);
197ec187ef0SAlexandre Belloni 
198ec187ef0SAlexandre Belloni 	return 0;
199ec187ef0SAlexandre Belloni }
200ec187ef0SAlexandre Belloni 
clk_slow_rc_osc_unprepare(struct clk_hw * hw)201ec187ef0SAlexandre Belloni static void clk_slow_rc_osc_unprepare(struct clk_hw *hw)
202ec187ef0SAlexandre Belloni {
203ec187ef0SAlexandre Belloni 	struct clk_slow_rc_osc *osc = to_clk_slow_rc_osc(hw);
204ec187ef0SAlexandre Belloni 	void __iomem *sckcr = osc->sckcr;
205ec187ef0SAlexandre Belloni 
206abaceffcSClaudiu Beznea 	writel(readl(sckcr) & ~osc->bits->cr_rcen, sckcr);
207ec187ef0SAlexandre Belloni }
208ec187ef0SAlexandre Belloni 
clk_slow_rc_osc_is_prepared(struct clk_hw * hw)209ec187ef0SAlexandre Belloni static int clk_slow_rc_osc_is_prepared(struct clk_hw *hw)
210ec187ef0SAlexandre Belloni {
211ec187ef0SAlexandre Belloni 	struct clk_slow_rc_osc *osc = to_clk_slow_rc_osc(hw);
212ec187ef0SAlexandre Belloni 
213abaceffcSClaudiu Beznea 	return !!(readl(osc->sckcr) & osc->bits->cr_rcen);
214ec187ef0SAlexandre Belloni }
215ec187ef0SAlexandre Belloni 
216ec187ef0SAlexandre Belloni static const struct clk_ops slow_rc_osc_ops = {
217ec187ef0SAlexandre Belloni 	.prepare = clk_slow_rc_osc_prepare,
218ec187ef0SAlexandre Belloni 	.unprepare = clk_slow_rc_osc_unprepare,
219ec187ef0SAlexandre Belloni 	.is_prepared = clk_slow_rc_osc_is_prepared,
220ec187ef0SAlexandre Belloni 	.recalc_rate = clk_slow_rc_osc_recalc_rate,
221ec187ef0SAlexandre Belloni 	.recalc_accuracy = clk_slow_rc_osc_recalc_accuracy,
222ec187ef0SAlexandre Belloni };
223ec187ef0SAlexandre Belloni 
224ec187ef0SAlexandre Belloni static struct clk_hw * __init
at91_clk_register_slow_rc_osc(void __iomem * sckcr,const char * name,unsigned long frequency,unsigned long accuracy,unsigned long startup,const struct clk_slow_bits * bits)225ec187ef0SAlexandre Belloni at91_clk_register_slow_rc_osc(void __iomem *sckcr,
226ec187ef0SAlexandre Belloni 			      const char *name,
227ec187ef0SAlexandre Belloni 			      unsigned long frequency,
228ec187ef0SAlexandre Belloni 			      unsigned long accuracy,
229abaceffcSClaudiu Beznea 			      unsigned long startup,
230abaceffcSClaudiu Beznea 			      const struct clk_slow_bits *bits)
231ec187ef0SAlexandre Belloni {
232ec187ef0SAlexandre Belloni 	struct clk_slow_rc_osc *osc;
233ec187ef0SAlexandre Belloni 	struct clk_hw *hw;
234ec187ef0SAlexandre Belloni 	struct clk_init_data init;
235ec187ef0SAlexandre Belloni 	int ret;
236ec187ef0SAlexandre Belloni 
237ec187ef0SAlexandre Belloni 	if (!sckcr || !name)
238ec187ef0SAlexandre Belloni 		return ERR_PTR(-EINVAL);
239ec187ef0SAlexandre Belloni 
240ec187ef0SAlexandre Belloni 	osc = kzalloc(sizeof(*osc), GFP_KERNEL);
241ec187ef0SAlexandre Belloni 	if (!osc)
242ec187ef0SAlexandre Belloni 		return ERR_PTR(-ENOMEM);
243ec187ef0SAlexandre Belloni 
244ec187ef0SAlexandre Belloni 	init.name = name;
245ec187ef0SAlexandre Belloni 	init.ops = &slow_rc_osc_ops;
246ec187ef0SAlexandre Belloni 	init.parent_names = NULL;
247ec187ef0SAlexandre Belloni 	init.num_parents = 0;
248ec187ef0SAlexandre Belloni 	init.flags = CLK_IGNORE_UNUSED;
249ec187ef0SAlexandre Belloni 
250ec187ef0SAlexandre Belloni 	osc->hw.init = &init;
251ec187ef0SAlexandre Belloni 	osc->sckcr = sckcr;
252abaceffcSClaudiu Beznea 	osc->bits = bits;
253ec187ef0SAlexandre Belloni 	osc->frequency = frequency;
254ec187ef0SAlexandre Belloni 	osc->accuracy = accuracy;
255ec187ef0SAlexandre Belloni 	osc->startup_usec = startup;
256ec187ef0SAlexandre Belloni 
257ec187ef0SAlexandre Belloni 	hw = &osc->hw;
258ec187ef0SAlexandre Belloni 	ret = clk_hw_register(NULL, &osc->hw);
259ec187ef0SAlexandre Belloni 	if (ret) {
260ec187ef0SAlexandre Belloni 		kfree(osc);
261ec187ef0SAlexandre Belloni 		hw = ERR_PTR(ret);
262ec187ef0SAlexandre Belloni 	}
263ec187ef0SAlexandre Belloni 
264ec187ef0SAlexandre Belloni 	return hw;
265ec187ef0SAlexandre Belloni }
266ec187ef0SAlexandre Belloni 
at91_clk_unregister_slow_rc_osc(struct clk_hw * hw)26703670246SClaudiu Beznea static void at91_clk_unregister_slow_rc_osc(struct clk_hw *hw)
26803670246SClaudiu Beznea {
26903670246SClaudiu Beznea 	struct clk_slow_rc_osc *osc = to_clk_slow_rc_osc(hw);
27003670246SClaudiu Beznea 
27103670246SClaudiu Beznea 	clk_hw_unregister(hw);
27203670246SClaudiu Beznea 	kfree(osc);
27303670246SClaudiu Beznea }
27403670246SClaudiu Beznea 
clk_sam9x5_slow_set_parent(struct clk_hw * hw,u8 index)275ec187ef0SAlexandre Belloni static int clk_sam9x5_slow_set_parent(struct clk_hw *hw, u8 index)
276ec187ef0SAlexandre Belloni {
277ec187ef0SAlexandre Belloni 	struct clk_sam9x5_slow *slowck = to_clk_sam9x5_slow(hw);
278ec187ef0SAlexandre Belloni 	void __iomem *sckcr = slowck->sckcr;
279ec187ef0SAlexandre Belloni 	u32 tmp;
280ec187ef0SAlexandre Belloni 
281ec187ef0SAlexandre Belloni 	if (index > 1)
282ec187ef0SAlexandre Belloni 		return -EINVAL;
283ec187ef0SAlexandre Belloni 
284ec187ef0SAlexandre Belloni 	tmp = readl(sckcr);
285ec187ef0SAlexandre Belloni 
286abaceffcSClaudiu Beznea 	if ((!index && !(tmp & slowck->bits->cr_oscsel)) ||
287abaceffcSClaudiu Beznea 	    (index && (tmp & slowck->bits->cr_oscsel)))
288ec187ef0SAlexandre Belloni 		return 0;
289ec187ef0SAlexandre Belloni 
290ec187ef0SAlexandre Belloni 	if (index)
291abaceffcSClaudiu Beznea 		tmp |= slowck->bits->cr_oscsel;
292ec187ef0SAlexandre Belloni 	else
293abaceffcSClaudiu Beznea 		tmp &= ~slowck->bits->cr_oscsel;
294ec187ef0SAlexandre Belloni 
295ec187ef0SAlexandre Belloni 	writel(tmp, sckcr);
296ec187ef0SAlexandre Belloni 
297658fd65cSAlexandre Belloni 	if (system_state < SYSTEM_RUNNING)
298658fd65cSAlexandre Belloni 		udelay(SLOWCK_SW_TIME_USEC);
299658fd65cSAlexandre Belloni 	else
300ec187ef0SAlexandre Belloni 		usleep_range(SLOWCK_SW_TIME_USEC, SLOWCK_SW_TIME_USEC + 1);
301ec187ef0SAlexandre Belloni 
302ec187ef0SAlexandre Belloni 	return 0;
303ec187ef0SAlexandre Belloni }
304ec187ef0SAlexandre Belloni 
clk_sam9x5_slow_get_parent(struct clk_hw * hw)305ec187ef0SAlexandre Belloni static u8 clk_sam9x5_slow_get_parent(struct clk_hw *hw)
306ec187ef0SAlexandre Belloni {
307ec187ef0SAlexandre Belloni 	struct clk_sam9x5_slow *slowck = to_clk_sam9x5_slow(hw);
308ec187ef0SAlexandre Belloni 
309abaceffcSClaudiu Beznea 	return !!(readl(slowck->sckcr) & slowck->bits->cr_oscsel);
310ec187ef0SAlexandre Belloni }
311ec187ef0SAlexandre Belloni 
312ec187ef0SAlexandre Belloni static const struct clk_ops sam9x5_slow_ops = {
313ec187ef0SAlexandre Belloni 	.determine_rate = clk_hw_determine_rate_no_reparent,
314ec187ef0SAlexandre Belloni 	.set_parent = clk_sam9x5_slow_set_parent,
315ec187ef0SAlexandre Belloni 	.get_parent = clk_sam9x5_slow_get_parent,
316ec187ef0SAlexandre Belloni };
317ec187ef0SAlexandre Belloni 
318ec187ef0SAlexandre Belloni static struct clk_hw * __init
at91_clk_register_sam9x5_slow(void __iomem * sckcr,const char * name,const struct clk_hw ** parent_hws,int num_parents,const struct clk_slow_bits * bits)319ec187ef0SAlexandre Belloni at91_clk_register_sam9x5_slow(void __iomem *sckcr,
320*8aa1db9cSClaudiu Beznea 			      const char *name,
321abaceffcSClaudiu Beznea 			      const struct clk_hw **parent_hws,
322abaceffcSClaudiu Beznea 			      int num_parents,
323ec187ef0SAlexandre Belloni 			      const struct clk_slow_bits *bits)
324ec187ef0SAlexandre Belloni {
325ec187ef0SAlexandre Belloni 	struct clk_sam9x5_slow *slowck;
326*8aa1db9cSClaudiu Beznea 	struct clk_hw *hw;
327ec187ef0SAlexandre Belloni 	struct clk_init_data init = {};
328ec187ef0SAlexandre Belloni 	int ret;
329*8aa1db9cSClaudiu Beznea 
330ec187ef0SAlexandre Belloni 	if (!sckcr || !name || !parent_hws || !num_parents)
331ec187ef0SAlexandre Belloni 		return ERR_PTR(-EINVAL);
332ec187ef0SAlexandre Belloni 
333ec187ef0SAlexandre Belloni 	slowck = kzalloc(sizeof(*slowck), GFP_KERNEL);
334ec187ef0SAlexandre Belloni 	if (!slowck)
335ec187ef0SAlexandre Belloni 		return ERR_PTR(-ENOMEM);
336ec187ef0SAlexandre Belloni 
337ec187ef0SAlexandre Belloni 	init.name = name;
338*8aa1db9cSClaudiu Beznea 	init.ops = &sam9x5_slow_ops;
339ec187ef0SAlexandre Belloni 	init.parent_hws = parent_hws;
340ec187ef0SAlexandre Belloni 	init.num_parents = num_parents;
341ec187ef0SAlexandre Belloni 	init.flags = 0;
342ec187ef0SAlexandre Belloni 
343ec187ef0SAlexandre Belloni 	slowck->hw.init = &init;
344abaceffcSClaudiu Beznea 	slowck->sckcr = sckcr;
345abaceffcSClaudiu Beznea 	slowck->bits = bits;
346ec187ef0SAlexandre Belloni 	slowck->parent = !!(readl(sckcr) & slowck->bits->cr_oscsel);
347ec187ef0SAlexandre Belloni 
348ec187ef0SAlexandre Belloni 	hw = &slowck->hw;
349ec187ef0SAlexandre Belloni 	ret = clk_hw_register(NULL, &slowck->hw);
350ec187ef0SAlexandre Belloni 	if (ret) {
351ec187ef0SAlexandre Belloni 		kfree(slowck);
352ec187ef0SAlexandre Belloni 		hw = ERR_PTR(ret);
353ec187ef0SAlexandre Belloni 	}
354ec187ef0SAlexandre Belloni 
355ec187ef0SAlexandre Belloni 	return hw;
356ec187ef0SAlexandre Belloni }
357016d22ddSClaudiu Beznea 
at91_clk_unregister_sam9x5_slow(struct clk_hw * hw)358016d22ddSClaudiu Beznea static void at91_clk_unregister_sam9x5_slow(struct clk_hw *hw)
359016d22ddSClaudiu Beznea {
360016d22ddSClaudiu Beznea 	struct clk_sam9x5_slow *slowck = to_clk_sam9x5_slow(hw);
361016d22ddSClaudiu Beznea 
362016d22ddSClaudiu Beznea 	clk_hw_unregister(hw);
363016d22ddSClaudiu Beznea 	kfree(slowck);
364016d22ddSClaudiu Beznea }
3655c16ffa7SAlexandre Belloni 
at91sam9x5_sckc_register(struct device_node * np,unsigned int rc_osc_startup_us,const struct clk_slow_bits * bits)366abaceffcSClaudiu Beznea static void __init at91sam9x5_sckc_register(struct device_node *np,
367abaceffcSClaudiu Beznea 					    unsigned int rc_osc_startup_us,
36880eded6cSBoris BREZILLON 					    const struct clk_slow_bits *bits)
36980eded6cSBoris BREZILLON {
37045b5ec84SAlexandre Belloni 	void __iomem *regbase = of_iomap(np, 0);
37145b5ec84SAlexandre Belloni 	struct device_node *child = NULL;
37282e25dc8SClaudiu Beznea 	const char *xtal_name;
373*8aa1db9cSClaudiu Beznea 	struct clk_hw *slow_rc, *slow_osc, *slowck;
374*8aa1db9cSClaudiu Beznea 	static struct clk_parent_data parent_data = {
375*8aa1db9cSClaudiu Beznea 		.name = "slow_xtal",
376*8aa1db9cSClaudiu Beznea 	};
37745b5ec84SAlexandre Belloni 	const struct clk_hw *parent_hws[2];
37882e25dc8SClaudiu Beznea 	bool bypass;
37980eded6cSBoris BREZILLON 	int ret;
38080eded6cSBoris BREZILLON 
38180eded6cSBoris BREZILLON 	if (!regbase)
38280eded6cSBoris BREZILLON 		return;
383*8aa1db9cSClaudiu Beznea 
38482e25dc8SClaudiu Beznea 	slow_rc = at91_clk_register_slow_rc_osc(regbase, "slow_rc_osc",
38582e25dc8SClaudiu Beznea 						32768, 50000000,
38682e25dc8SClaudiu Beznea 						rc_osc_startup_us, bits);
38745b5ec84SAlexandre Belloni 	if (IS_ERR(slow_rc))
38845b5ec84SAlexandre Belloni 		return;
38945b5ec84SAlexandre Belloni 
39045b5ec84SAlexandre Belloni 	xtal_name = of_clk_get_parent_name(np, 0);
39145b5ec84SAlexandre Belloni 	if (!xtal_name) {
39245b5ec84SAlexandre Belloni 		/* DT backward compatibility */
39345b5ec84SAlexandre Belloni 		child = of_get_compatible_child(np, "atmel,at91sam9x5-clk-slow-osc");
39482e25dc8SClaudiu Beznea 		if (!child)
39545b5ec84SAlexandre Belloni 			goto unregister_slow_rc;
39645b5ec84SAlexandre Belloni 
39745b5ec84SAlexandre Belloni 		xtal_name = of_clk_get_parent_name(child, 0);
39845b5ec84SAlexandre Belloni 		bypass = of_property_read_bool(child, "atmel,osc-bypass");
39945b5ec84SAlexandre Belloni 
40045b5ec84SAlexandre Belloni 		child =  of_get_compatible_child(np, "atmel,at91sam9x5-clk-slow");
40145b5ec84SAlexandre Belloni 	} else {
40280eded6cSBoris BREZILLON 		bypass = of_property_read_bool(np, "atmel,osc-bypass");
40345b5ec84SAlexandre Belloni 	}
40445b5ec84SAlexandre Belloni 
40582e25dc8SClaudiu Beznea 	if (!xtal_name)
40645b5ec84SAlexandre Belloni 		goto unregister_slow_rc;
407*8aa1db9cSClaudiu Beznea 
408*8aa1db9cSClaudiu Beznea 	parent_data.fw_name = xtal_name;
409*8aa1db9cSClaudiu Beznea 
410*8aa1db9cSClaudiu Beznea 	slow_osc = at91_clk_register_slow_osc(regbase, "slow_osc",
41182e25dc8SClaudiu Beznea 					      &parent_data, 1200000, bypass, bits);
41282e25dc8SClaudiu Beznea 	if (IS_ERR(slow_osc))
41345b5ec84SAlexandre Belloni 		goto unregister_slow_rc;
414*8aa1db9cSClaudiu Beznea 
415*8aa1db9cSClaudiu Beznea 	parent_hws[0] = slow_rc;
416*8aa1db9cSClaudiu Beznea 	parent_hws[1] = slow_osc;
41782e25dc8SClaudiu Beznea 	slowck = at91_clk_register_sam9x5_slow(regbase, "slowck", parent_hws,
41882e25dc8SClaudiu Beznea 					       2, bits);
41982e25dc8SClaudiu Beznea 	if (IS_ERR(slowck))
42045b5ec84SAlexandre Belloni 		goto unregister_slow_osc;
42145b5ec84SAlexandre Belloni 
42245b5ec84SAlexandre Belloni 	/* DT backward compatibility */
42382e25dc8SClaudiu Beznea 	if (child)
42482e25dc8SClaudiu Beznea 		ret = of_clk_add_hw_provider(child, of_clk_hw_simple_get,
42582e25dc8SClaudiu Beznea 					     slowck);
42682e25dc8SClaudiu Beznea 	else
42782e25dc8SClaudiu Beznea 		ret = of_clk_add_hw_provider(np, of_clk_hw_simple_get, slowck);
42882e25dc8SClaudiu Beznea 
42982e25dc8SClaudiu Beznea 	if (WARN_ON(ret))
43082e25dc8SClaudiu Beznea 		goto unregister_slowck;
43182e25dc8SClaudiu Beznea 
43282e25dc8SClaudiu Beznea 	return;
43382e25dc8SClaudiu Beznea 
43482e25dc8SClaudiu Beznea unregister_slowck:
43582e25dc8SClaudiu Beznea 	at91_clk_unregister_sam9x5_slow(slowck);
43682e25dc8SClaudiu Beznea unregister_slow_osc:
43782e25dc8SClaudiu Beznea 	at91_clk_unregister_slow_osc(slow_osc);
43882e25dc8SClaudiu Beznea unregister_slow_rc:
43980eded6cSBoris BREZILLON 	at91_clk_unregister_slow_rc_osc(slow_rc);
4405c16ffa7SAlexandre Belloni }
441abaceffcSClaudiu Beznea 
442abaceffcSClaudiu Beznea static const struct clk_slow_bits at91sam9x5_bits = {
443abaceffcSClaudiu Beznea 	.cr_rcen = BIT(0),
444abaceffcSClaudiu Beznea 	.cr_osc32en = BIT(1),
445abaceffcSClaudiu Beznea 	.cr_osc32byp = BIT(2),
446abaceffcSClaudiu Beznea 	.cr_oscsel = BIT(3),
447abaceffcSClaudiu Beznea };
4485c16ffa7SAlexandre Belloni 
of_at91sam9x5_sckc_setup(struct device_node * np)4495c16ffa7SAlexandre Belloni static void __init of_at91sam9x5_sckc_setup(struct device_node *np)
450abaceffcSClaudiu Beznea {
4515c16ffa7SAlexandre Belloni 	at91sam9x5_sckc_register(np, 75, &at91sam9x5_bits);
45280eded6cSBoris BREZILLON }
45380eded6cSBoris BREZILLON CLK_OF_DECLARE(at91sam9x5_clk_sckc, "atmel,at91sam9x5-sckc",
4544b13b645SAlexandre Belloni 	       of_at91sam9x5_sckc_setup);
4555c16ffa7SAlexandre Belloni 
of_sama5d3_sckc_setup(struct device_node * np)4565c16ffa7SAlexandre Belloni static void __init of_sama5d3_sckc_setup(struct device_node *np)
457abaceffcSClaudiu Beznea {
4585c16ffa7SAlexandre Belloni 	at91sam9x5_sckc_register(np, 500, &at91sam9x5_bits);
4595c16ffa7SAlexandre Belloni }
4605c16ffa7SAlexandre Belloni CLK_OF_DECLARE(sama5d3_clk_sckc, "atmel,sama5d3-sckc",
4615c16ffa7SAlexandre Belloni 	       of_sama5d3_sckc_setup);
46204bcc427SClaudiu Beznea 
46304bcc427SClaudiu Beznea static const struct clk_slow_bits at91sam9x60_bits = {
46404bcc427SClaudiu Beznea 	.cr_osc32en = BIT(1),
46504bcc427SClaudiu Beznea 	.cr_osc32byp = BIT(2),
46604bcc427SClaudiu Beznea 	.cr_oscsel = BIT(24),
46704bcc427SClaudiu Beznea };
46804bcc427SClaudiu Beznea 
of_sam9x60_sckc_setup(struct device_node * np)46904bcc427SClaudiu Beznea static void __init of_sam9x60_sckc_setup(struct device_node *np)
47004bcc427SClaudiu Beznea {
47104bcc427SClaudiu Beznea 	void __iomem *regbase = of_iomap(np, 0);
47204bcc427SClaudiu Beznea 	struct clk_hw_onecell_data *clk_data;
47304bcc427SClaudiu Beznea 	struct clk_hw *slow_rc, *slow_osc;
474*8aa1db9cSClaudiu Beznea 	const char *xtal_name;
475*8aa1db9cSClaudiu Beznea 	const struct clk_hw *parent_hws[2];
476*8aa1db9cSClaudiu Beznea 	static struct clk_parent_data parent_data = {
477*8aa1db9cSClaudiu Beznea 		.name = "slow_xtal",
47804bcc427SClaudiu Beznea 	};
47904bcc427SClaudiu Beznea 	bool bypass;
48004bcc427SClaudiu Beznea 	int ret;
48104bcc427SClaudiu Beznea 
48204bcc427SClaudiu Beznea 	if (!regbase)
48304bcc427SClaudiu Beznea 		return;
484*8aa1db9cSClaudiu Beznea 
485e1e3e700SClaudiu Beznea 	slow_rc = clk_hw_register_fixed_rate_with_accuracy(NULL, "slow_rc_osc",
486e1e3e700SClaudiu Beznea 							   NULL, 0, 32768,
48704bcc427SClaudiu Beznea 							   93750000);
48804bcc427SClaudiu Beznea 	if (IS_ERR(slow_rc))
48904bcc427SClaudiu Beznea 		return;
49004bcc427SClaudiu Beznea 
49104bcc427SClaudiu Beznea 	xtal_name = of_clk_get_parent_name(np, 0);
49204bcc427SClaudiu Beznea 	if (!xtal_name)
49304bcc427SClaudiu Beznea 		goto unregister_slow_rc;
494*8aa1db9cSClaudiu Beznea 
49504bcc427SClaudiu Beznea 	parent_data.fw_name = xtal_name;
496*8aa1db9cSClaudiu Beznea 	bypass = of_property_read_bool(np, "atmel,osc-bypass");
497*8aa1db9cSClaudiu Beznea 	slow_osc = at91_clk_register_slow_osc(regbase, "slow_osc",
49804bcc427SClaudiu Beznea 					      &parent_data, 5000000, bypass,
49904bcc427SClaudiu Beznea 					      &at91sam9x60_bits);
50004bcc427SClaudiu Beznea 	if (IS_ERR(slow_osc))
50104bcc427SClaudiu Beznea 		goto unregister_slow_rc;
502e620a1e0SStephen Kitt 
50304bcc427SClaudiu Beznea 	clk_data = kzalloc(struct_size(clk_data, hws, 2), GFP_KERNEL);
50404bcc427SClaudiu Beznea 	if (!clk_data)
50504bcc427SClaudiu Beznea 		goto unregister_slow_osc;
50604bcc427SClaudiu Beznea 
50704bcc427SClaudiu Beznea 	/* MD_SLCK and TD_SLCK. */
508*8aa1db9cSClaudiu Beznea 	clk_data->num = 2;
509*8aa1db9cSClaudiu Beznea 	clk_data->hws[0] = clk_hw_register_fixed_rate_parent_hw(NULL, "md_slck",
51004bcc427SClaudiu Beznea 								slow_rc,
51104bcc427SClaudiu Beznea 								0, 32768);
51204bcc427SClaudiu Beznea 	if (IS_ERR(clk_data->hws[0]))
51304bcc427SClaudiu Beznea 		goto clk_data_free;
514*8aa1db9cSClaudiu Beznea 
515*8aa1db9cSClaudiu Beznea 	parent_hws[0] = slow_rc;
51604bcc427SClaudiu Beznea 	parent_hws[1] = slow_osc;
517*8aa1db9cSClaudiu Beznea 	clk_data->hws[1] = at91_clk_register_sam9x5_slow(regbase, "td_slck",
51804bcc427SClaudiu Beznea 							 parent_hws, 2,
51904bcc427SClaudiu Beznea 							 &at91sam9x60_bits);
52004bcc427SClaudiu Beznea 	if (IS_ERR(clk_data->hws[1]))
52104bcc427SClaudiu Beznea 		goto unregister_md_slck;
52204bcc427SClaudiu Beznea 
52304bcc427SClaudiu Beznea 	ret = of_clk_add_hw_provider(np, of_clk_hw_onecell_get, clk_data);
52404bcc427SClaudiu Beznea 	if (WARN_ON(ret))
52504bcc427SClaudiu Beznea 		goto unregister_td_slck;
52604bcc427SClaudiu Beznea 
52704bcc427SClaudiu Beznea 	return;
52804bcc427SClaudiu Beznea 
529ecbcc2aaSClaudiu Beznea unregister_td_slck:
53004bcc427SClaudiu Beznea 	at91_clk_unregister_sam9x5_slow(clk_data->hws[1]);
53104bcc427SClaudiu Beznea unregister_md_slck:
53204bcc427SClaudiu Beznea 	clk_hw_unregister(clk_data->hws[0]);
53304bcc427SClaudiu Beznea clk_data_free:
53404bcc427SClaudiu Beznea 	kfree(clk_data);
535ecbcc2aaSClaudiu Beznea unregister_slow_osc:
53604bcc427SClaudiu Beznea 	at91_clk_unregister_slow_osc(slow_osc);
53704bcc427SClaudiu Beznea unregister_slow_rc:
53804bcc427SClaudiu Beznea 	clk_hw_unregister(slow_rc);
53904bcc427SClaudiu Beznea }
54004bcc427SClaudiu Beznea CLK_OF_DECLARE(sam9x60_clk_sckc, "microchip,sam9x60-sckc",
54104bcc427SClaudiu Beznea 	       of_sam9x60_sckc_setup);
5424b13b645SAlexandre Belloni 
clk_sama5d4_slow_osc_prepare(struct clk_hw * hw)5434b13b645SAlexandre Belloni static int clk_sama5d4_slow_osc_prepare(struct clk_hw *hw)
5444b13b645SAlexandre Belloni {
5454b13b645SAlexandre Belloni 	struct clk_sama5d4_slow_osc *osc = to_clk_sama5d4_slow_osc(hw);
5464b13b645SAlexandre Belloni 
5474b13b645SAlexandre Belloni 	if (osc->prepared)
5484b13b645SAlexandre Belloni 		return 0;
5494b13b645SAlexandre Belloni 
5504b13b645SAlexandre Belloni 	/*
5517c55e8efSTom Rix 	 * Assume that if it has already been selected (for example by the
5524b13b645SAlexandre Belloni 	 * bootloader), enough time has already passed.
553abaceffcSClaudiu Beznea 	 */
5544b13b645SAlexandre Belloni 	if ((readl(osc->sckcr) & osc->bits->cr_oscsel)) {
5554b13b645SAlexandre Belloni 		osc->prepared = true;
5564b13b645SAlexandre Belloni 		return 0;
5574b13b645SAlexandre Belloni 	}
558658fd65cSAlexandre Belloni 
559658fd65cSAlexandre Belloni 	if (system_state < SYSTEM_RUNNING)
560658fd65cSAlexandre Belloni 		udelay(osc->startup_usec);
5614b13b645SAlexandre Belloni 	else
5624b13b645SAlexandre Belloni 		usleep_range(osc->startup_usec, osc->startup_usec + 1);
5634b13b645SAlexandre Belloni 	osc->prepared = true;
5644b13b645SAlexandre Belloni 
5654b13b645SAlexandre Belloni 	return 0;
5664b13b645SAlexandre Belloni }
5674b13b645SAlexandre Belloni 
clk_sama5d4_slow_osc_is_prepared(struct clk_hw * hw)5684b13b645SAlexandre Belloni static int clk_sama5d4_slow_osc_is_prepared(struct clk_hw *hw)
5694b13b645SAlexandre Belloni {
5704b13b645SAlexandre Belloni 	struct clk_sama5d4_slow_osc *osc = to_clk_sama5d4_slow_osc(hw);
5714b13b645SAlexandre Belloni 
5724b13b645SAlexandre Belloni 	return osc->prepared;
5734b13b645SAlexandre Belloni }
5744b13b645SAlexandre Belloni 
5754b13b645SAlexandre Belloni static const struct clk_ops sama5d4_slow_osc_ops = {
5764b13b645SAlexandre Belloni 	.prepare = clk_sama5d4_slow_osc_prepare,
5774b13b645SAlexandre Belloni 	.is_prepared = clk_sama5d4_slow_osc_is_prepared,
5784b13b645SAlexandre Belloni };
579abaceffcSClaudiu Beznea 
580abaceffcSClaudiu Beznea static const struct clk_slow_bits at91sama5d4_bits = {
581abaceffcSClaudiu Beznea 	.cr_oscsel = BIT(3),
582abaceffcSClaudiu Beznea };
5834b13b645SAlexandre Belloni 
of_sama5d4_sckc_setup(struct device_node * np)5844b13b645SAlexandre Belloni static void __init of_sama5d4_sckc_setup(struct device_node *np)
5854b13b645SAlexandre Belloni {
586d09e6ca1SClaudiu Beznea 	void __iomem *regbase = of_iomap(np, 0);
5874b13b645SAlexandre Belloni 	struct clk_hw *slow_rc, *slowck;
588*8aa1db9cSClaudiu Beznea 	struct clk_sama5d4_slow_osc *osc;
5894b13b645SAlexandre Belloni 	struct clk_init_data init = {};
590*8aa1db9cSClaudiu Beznea 	const char *xtal_name;
591*8aa1db9cSClaudiu Beznea 	const struct clk_hw *parent_hws[2];
592*8aa1db9cSClaudiu Beznea 	static struct clk_parent_data parent_data = {
593*8aa1db9cSClaudiu Beznea 		.name = "slow_xtal",
5944b13b645SAlexandre Belloni 	};
5954b13b645SAlexandre Belloni 	int ret;
5964b13b645SAlexandre Belloni 
5974b13b645SAlexandre Belloni 	if (!regbase)
5984b13b645SAlexandre Belloni 		return;
599d09e6ca1SClaudiu Beznea 
600*8aa1db9cSClaudiu Beznea 	slow_rc = clk_hw_register_fixed_rate_with_accuracy(NULL,
6014b13b645SAlexandre Belloni 							   "slow_rc_osc",
6024b13b645SAlexandre Belloni 							   NULL, 0, 32768,
603d09e6ca1SClaudiu Beznea 							   250000000);
6044b13b645SAlexandre Belloni 	if (IS_ERR(slow_rc))
6054b13b645SAlexandre Belloni 		return;
6064b13b645SAlexandre Belloni 
607*8aa1db9cSClaudiu Beznea 	xtal_name = of_clk_get_parent_name(np, 0);
608*8aa1db9cSClaudiu Beznea 	if (!xtal_name)
609*8aa1db9cSClaudiu Beznea 		goto unregister_slow_rc;
6104b13b645SAlexandre Belloni 	parent_data.fw_name = xtal_name;
6114b13b645SAlexandre Belloni 
6124b13b645SAlexandre Belloni 	osc = kzalloc(sizeof(*osc), GFP_KERNEL);
613d09e6ca1SClaudiu Beznea 	if (!osc)
6144b13b645SAlexandre Belloni 		goto unregister_slow_rc;
615*8aa1db9cSClaudiu Beznea 
6164b13b645SAlexandre Belloni 	init.name = "slow_osc";
617*8aa1db9cSClaudiu Beznea 	init.ops = &sama5d4_slow_osc_ops;
6184b13b645SAlexandre Belloni 	init.parent_data = &parent_data;
6194b13b645SAlexandre Belloni 	init.num_parents = 1;
6204b13b645SAlexandre Belloni 	init.flags = CLK_IGNORE_UNUSED;
6214b13b645SAlexandre Belloni 
6224b13b645SAlexandre Belloni 	osc->hw.init = &init;
6234b13b645SAlexandre Belloni 	osc->sckcr = regbase;
624abaceffcSClaudiu Beznea 	osc->startup_usec = 1200000;
6254b13b645SAlexandre Belloni 	osc->bits = &at91sama5d4_bits;
6264b13b645SAlexandre Belloni 
627d09e6ca1SClaudiu Beznea 	ret = clk_hw_register(NULL, &osc->hw);
628d09e6ca1SClaudiu Beznea 	if (ret)
6294b13b645SAlexandre Belloni 		goto free_slow_osc_data;
630*8aa1db9cSClaudiu Beznea 
631*8aa1db9cSClaudiu Beznea 	parent_hws[0] = slow_rc;
632d09e6ca1SClaudiu Beznea 	parent_hws[1] = &osc->hw;
633*8aa1db9cSClaudiu Beznea 	slowck = at91_clk_register_sam9x5_slow(regbase, "slowck",
634abaceffcSClaudiu Beznea 					       parent_hws, 2,
635d09e6ca1SClaudiu Beznea 					       &at91sama5d4_bits);
636d09e6ca1SClaudiu Beznea 	if (IS_ERR(slowck))
637d09e6ca1SClaudiu Beznea 		goto unregister_slow_osc;
638d09e6ca1SClaudiu Beznea 
639d09e6ca1SClaudiu Beznea 	ret = of_clk_add_hw_provider(np, of_clk_hw_simple_get, slowck);
640d09e6ca1SClaudiu Beznea 	if (WARN_ON(ret))
641d09e6ca1SClaudiu Beznea 		goto unregister_slowck;
6424b13b645SAlexandre Belloni 
6434b13b645SAlexandre Belloni 	return;
644d09e6ca1SClaudiu Beznea 
645d09e6ca1SClaudiu Beznea unregister_slowck:
646d09e6ca1SClaudiu Beznea 	at91_clk_unregister_sam9x5_slow(slowck);
647d09e6ca1SClaudiu Beznea unregister_slow_osc:
648d09e6ca1SClaudiu Beznea 	clk_hw_unregister(&osc->hw);
6494b13b645SAlexandre Belloni free_slow_osc_data:
650d09e6ca1SClaudiu Beznea 	kfree(osc);
651d09e6ca1SClaudiu Beznea unregister_slow_rc:
6524b13b645SAlexandre Belloni 	clk_hw_unregister(slow_rc);
6534b13b645SAlexandre Belloni }
6544b13b645SAlexandre Belloni CLK_OF_DECLARE(sama5d4_clk_sckc, "atmel,sama5d4-sckc",
655 	       of_sama5d4_sckc_setup);
656