xref: /openbmc/linux/drivers/clk/at91/sckc.c (revision e620a1e0)
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 
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 
79ec187ef0SAlexandre Belloni 	usleep_range(osc->startup_usec, osc->startup_usec + 1);
80ec187ef0SAlexandre Belloni 
81ec187ef0SAlexandre Belloni 	return 0;
82ec187ef0SAlexandre Belloni }
83ec187ef0SAlexandre Belloni 
84ec187ef0SAlexandre Belloni static void clk_slow_osc_unprepare(struct clk_hw *hw)
85ec187ef0SAlexandre Belloni {
86ec187ef0SAlexandre Belloni 	struct clk_slow_osc *osc = to_clk_slow_osc(hw);
87ec187ef0SAlexandre Belloni 	void __iomem *sckcr = osc->sckcr;
88ec187ef0SAlexandre Belloni 	u32 tmp = readl(sckcr);
89ec187ef0SAlexandre Belloni 
90abaceffcSClaudiu Beznea 	if (tmp & osc->bits->cr_osc32byp)
91ec187ef0SAlexandre Belloni 		return;
92ec187ef0SAlexandre Belloni 
93abaceffcSClaudiu Beznea 	writel(tmp & ~osc->bits->cr_osc32en, sckcr);
94ec187ef0SAlexandre Belloni }
95ec187ef0SAlexandre Belloni 
96ec187ef0SAlexandre Belloni static int clk_slow_osc_is_prepared(struct clk_hw *hw)
97ec187ef0SAlexandre Belloni {
98ec187ef0SAlexandre Belloni 	struct clk_slow_osc *osc = to_clk_slow_osc(hw);
99ec187ef0SAlexandre Belloni 	void __iomem *sckcr = osc->sckcr;
100ec187ef0SAlexandre Belloni 	u32 tmp = readl(sckcr);
101ec187ef0SAlexandre Belloni 
102abaceffcSClaudiu Beznea 	if (tmp & osc->bits->cr_osc32byp)
103ec187ef0SAlexandre Belloni 		return 1;
104ec187ef0SAlexandre Belloni 
105abaceffcSClaudiu Beznea 	return !!(tmp & osc->bits->cr_osc32en);
106ec187ef0SAlexandre Belloni }
107ec187ef0SAlexandre Belloni 
108ec187ef0SAlexandre Belloni static const struct clk_ops slow_osc_ops = {
109ec187ef0SAlexandre Belloni 	.prepare = clk_slow_osc_prepare,
110ec187ef0SAlexandre Belloni 	.unprepare = clk_slow_osc_unprepare,
111ec187ef0SAlexandre Belloni 	.is_prepared = clk_slow_osc_is_prepared,
112ec187ef0SAlexandre Belloni };
113ec187ef0SAlexandre Belloni 
114ec187ef0SAlexandre Belloni static struct clk_hw * __init
115ec187ef0SAlexandre Belloni at91_clk_register_slow_osc(void __iomem *sckcr,
116ec187ef0SAlexandre Belloni 			   const char *name,
117ec187ef0SAlexandre Belloni 			   const char *parent_name,
118ec187ef0SAlexandre Belloni 			   unsigned long startup,
119abaceffcSClaudiu Beznea 			   bool bypass,
120abaceffcSClaudiu Beznea 			   const struct clk_slow_bits *bits)
121ec187ef0SAlexandre Belloni {
122ec187ef0SAlexandre Belloni 	struct clk_slow_osc *osc;
123ec187ef0SAlexandre Belloni 	struct clk_hw *hw;
124ec187ef0SAlexandre Belloni 	struct clk_init_data init;
125ec187ef0SAlexandre Belloni 	int ret;
126ec187ef0SAlexandre Belloni 
127ec187ef0SAlexandre Belloni 	if (!sckcr || !name || !parent_name)
128ec187ef0SAlexandre Belloni 		return ERR_PTR(-EINVAL);
129ec187ef0SAlexandre Belloni 
130ec187ef0SAlexandre Belloni 	osc = kzalloc(sizeof(*osc), GFP_KERNEL);
131ec187ef0SAlexandre Belloni 	if (!osc)
132ec187ef0SAlexandre Belloni 		return ERR_PTR(-ENOMEM);
133ec187ef0SAlexandre Belloni 
134ec187ef0SAlexandre Belloni 	init.name = name;
135ec187ef0SAlexandre Belloni 	init.ops = &slow_osc_ops;
136ec187ef0SAlexandre Belloni 	init.parent_names = &parent_name;
137ec187ef0SAlexandre Belloni 	init.num_parents = 1;
138ec187ef0SAlexandre Belloni 	init.flags = CLK_IGNORE_UNUSED;
139ec187ef0SAlexandre Belloni 
140ec187ef0SAlexandre Belloni 	osc->hw.init = &init;
141ec187ef0SAlexandre Belloni 	osc->sckcr = sckcr;
142ec187ef0SAlexandre Belloni 	osc->startup_usec = startup;
143abaceffcSClaudiu Beznea 	osc->bits = bits;
144ec187ef0SAlexandre Belloni 
145ec187ef0SAlexandre Belloni 	if (bypass)
146abaceffcSClaudiu Beznea 		writel((readl(sckcr) & ~osc->bits->cr_osc32en) |
147abaceffcSClaudiu Beznea 					osc->bits->cr_osc32byp, sckcr);
148ec187ef0SAlexandre Belloni 
149ec187ef0SAlexandre Belloni 	hw = &osc->hw;
150ec187ef0SAlexandre Belloni 	ret = clk_hw_register(NULL, &osc->hw);
151ec187ef0SAlexandre Belloni 	if (ret) {
152ec187ef0SAlexandre Belloni 		kfree(osc);
153ec187ef0SAlexandre Belloni 		hw = ERR_PTR(ret);
154ec187ef0SAlexandre Belloni 	}
155ec187ef0SAlexandre Belloni 
156ec187ef0SAlexandre Belloni 	return hw;
157ec187ef0SAlexandre Belloni }
158ec187ef0SAlexandre Belloni 
1597fb791d0SClaudiu Beznea static void at91_clk_unregister_slow_osc(struct clk_hw *hw)
1607fb791d0SClaudiu Beznea {
1617fb791d0SClaudiu Beznea 	struct clk_slow_osc *osc = to_clk_slow_osc(hw);
1627fb791d0SClaudiu Beznea 
1637fb791d0SClaudiu Beznea 	clk_hw_unregister(hw);
1647fb791d0SClaudiu Beznea 	kfree(osc);
1657fb791d0SClaudiu Beznea }
1667fb791d0SClaudiu Beznea 
167ec187ef0SAlexandre Belloni static unsigned long clk_slow_rc_osc_recalc_rate(struct clk_hw *hw,
168ec187ef0SAlexandre Belloni 						 unsigned long parent_rate)
169ec187ef0SAlexandre Belloni {
170ec187ef0SAlexandre Belloni 	struct clk_slow_rc_osc *osc = to_clk_slow_rc_osc(hw);
171ec187ef0SAlexandre Belloni 
172ec187ef0SAlexandre Belloni 	return osc->frequency;
173ec187ef0SAlexandre Belloni }
174ec187ef0SAlexandre Belloni 
175ec187ef0SAlexandre Belloni static unsigned long clk_slow_rc_osc_recalc_accuracy(struct clk_hw *hw,
176ec187ef0SAlexandre Belloni 						     unsigned long parent_acc)
177ec187ef0SAlexandre Belloni {
178ec187ef0SAlexandre Belloni 	struct clk_slow_rc_osc *osc = to_clk_slow_rc_osc(hw);
179ec187ef0SAlexandre Belloni 
180ec187ef0SAlexandre Belloni 	return osc->accuracy;
181ec187ef0SAlexandre Belloni }
182ec187ef0SAlexandre Belloni 
183ec187ef0SAlexandre Belloni static int clk_slow_rc_osc_prepare(struct clk_hw *hw)
184ec187ef0SAlexandre Belloni {
185ec187ef0SAlexandre Belloni 	struct clk_slow_rc_osc *osc = to_clk_slow_rc_osc(hw);
186ec187ef0SAlexandre Belloni 	void __iomem *sckcr = osc->sckcr;
187ec187ef0SAlexandre Belloni 
188abaceffcSClaudiu Beznea 	writel(readl(sckcr) | osc->bits->cr_rcen, sckcr);
189ec187ef0SAlexandre Belloni 
190ec187ef0SAlexandre Belloni 	usleep_range(osc->startup_usec, osc->startup_usec + 1);
191ec187ef0SAlexandre Belloni 
192ec187ef0SAlexandre Belloni 	return 0;
193ec187ef0SAlexandre Belloni }
194ec187ef0SAlexandre Belloni 
195ec187ef0SAlexandre Belloni static void clk_slow_rc_osc_unprepare(struct clk_hw *hw)
196ec187ef0SAlexandre Belloni {
197ec187ef0SAlexandre Belloni 	struct clk_slow_rc_osc *osc = to_clk_slow_rc_osc(hw);
198ec187ef0SAlexandre Belloni 	void __iomem *sckcr = osc->sckcr;
199ec187ef0SAlexandre Belloni 
200abaceffcSClaudiu Beznea 	writel(readl(sckcr) & ~osc->bits->cr_rcen, sckcr);
201ec187ef0SAlexandre Belloni }
202ec187ef0SAlexandre Belloni 
203ec187ef0SAlexandre Belloni static int clk_slow_rc_osc_is_prepared(struct clk_hw *hw)
204ec187ef0SAlexandre Belloni {
205ec187ef0SAlexandre Belloni 	struct clk_slow_rc_osc *osc = to_clk_slow_rc_osc(hw);
206ec187ef0SAlexandre Belloni 
207abaceffcSClaudiu Beznea 	return !!(readl(osc->sckcr) & osc->bits->cr_rcen);
208ec187ef0SAlexandre Belloni }
209ec187ef0SAlexandre Belloni 
210ec187ef0SAlexandre Belloni static const struct clk_ops slow_rc_osc_ops = {
211ec187ef0SAlexandre Belloni 	.prepare = clk_slow_rc_osc_prepare,
212ec187ef0SAlexandre Belloni 	.unprepare = clk_slow_rc_osc_unprepare,
213ec187ef0SAlexandre Belloni 	.is_prepared = clk_slow_rc_osc_is_prepared,
214ec187ef0SAlexandre Belloni 	.recalc_rate = clk_slow_rc_osc_recalc_rate,
215ec187ef0SAlexandre Belloni 	.recalc_accuracy = clk_slow_rc_osc_recalc_accuracy,
216ec187ef0SAlexandre Belloni };
217ec187ef0SAlexandre Belloni 
218ec187ef0SAlexandre Belloni static struct clk_hw * __init
219ec187ef0SAlexandre Belloni at91_clk_register_slow_rc_osc(void __iomem *sckcr,
220ec187ef0SAlexandre Belloni 			      const char *name,
221ec187ef0SAlexandre Belloni 			      unsigned long frequency,
222ec187ef0SAlexandre Belloni 			      unsigned long accuracy,
223abaceffcSClaudiu Beznea 			      unsigned long startup,
224abaceffcSClaudiu Beznea 			      const struct clk_slow_bits *bits)
225ec187ef0SAlexandre Belloni {
226ec187ef0SAlexandre Belloni 	struct clk_slow_rc_osc *osc;
227ec187ef0SAlexandre Belloni 	struct clk_hw *hw;
228ec187ef0SAlexandre Belloni 	struct clk_init_data init;
229ec187ef0SAlexandre Belloni 	int ret;
230ec187ef0SAlexandre Belloni 
231ec187ef0SAlexandre Belloni 	if (!sckcr || !name)
232ec187ef0SAlexandre Belloni 		return ERR_PTR(-EINVAL);
233ec187ef0SAlexandre Belloni 
234ec187ef0SAlexandre Belloni 	osc = kzalloc(sizeof(*osc), GFP_KERNEL);
235ec187ef0SAlexandre Belloni 	if (!osc)
236ec187ef0SAlexandre Belloni 		return ERR_PTR(-ENOMEM);
237ec187ef0SAlexandre Belloni 
238ec187ef0SAlexandre Belloni 	init.name = name;
239ec187ef0SAlexandre Belloni 	init.ops = &slow_rc_osc_ops;
240ec187ef0SAlexandre Belloni 	init.parent_names = NULL;
241ec187ef0SAlexandre Belloni 	init.num_parents = 0;
242ec187ef0SAlexandre Belloni 	init.flags = CLK_IGNORE_UNUSED;
243ec187ef0SAlexandre Belloni 
244ec187ef0SAlexandre Belloni 	osc->hw.init = &init;
245ec187ef0SAlexandre Belloni 	osc->sckcr = sckcr;
246abaceffcSClaudiu Beznea 	osc->bits = bits;
247ec187ef0SAlexandre Belloni 	osc->frequency = frequency;
248ec187ef0SAlexandre Belloni 	osc->accuracy = accuracy;
249ec187ef0SAlexandre Belloni 	osc->startup_usec = startup;
250ec187ef0SAlexandre Belloni 
251ec187ef0SAlexandre Belloni 	hw = &osc->hw;
252ec187ef0SAlexandre Belloni 	ret = clk_hw_register(NULL, &osc->hw);
253ec187ef0SAlexandre Belloni 	if (ret) {
254ec187ef0SAlexandre Belloni 		kfree(osc);
255ec187ef0SAlexandre Belloni 		hw = ERR_PTR(ret);
256ec187ef0SAlexandre Belloni 	}
257ec187ef0SAlexandre Belloni 
258ec187ef0SAlexandre Belloni 	return hw;
259ec187ef0SAlexandre Belloni }
260ec187ef0SAlexandre Belloni 
26103670246SClaudiu Beznea static void at91_clk_unregister_slow_rc_osc(struct clk_hw *hw)
26203670246SClaudiu Beznea {
26303670246SClaudiu Beznea 	struct clk_slow_rc_osc *osc = to_clk_slow_rc_osc(hw);
26403670246SClaudiu Beznea 
26503670246SClaudiu Beznea 	clk_hw_unregister(hw);
26603670246SClaudiu Beznea 	kfree(osc);
26703670246SClaudiu Beznea }
26803670246SClaudiu Beznea 
269ec187ef0SAlexandre Belloni static int clk_sam9x5_slow_set_parent(struct clk_hw *hw, u8 index)
270ec187ef0SAlexandre Belloni {
271ec187ef0SAlexandre Belloni 	struct clk_sam9x5_slow *slowck = to_clk_sam9x5_slow(hw);
272ec187ef0SAlexandre Belloni 	void __iomem *sckcr = slowck->sckcr;
273ec187ef0SAlexandre Belloni 	u32 tmp;
274ec187ef0SAlexandre Belloni 
275ec187ef0SAlexandre Belloni 	if (index > 1)
276ec187ef0SAlexandre Belloni 		return -EINVAL;
277ec187ef0SAlexandre Belloni 
278ec187ef0SAlexandre Belloni 	tmp = readl(sckcr);
279ec187ef0SAlexandre Belloni 
280abaceffcSClaudiu Beznea 	if ((!index && !(tmp & slowck->bits->cr_oscsel)) ||
281abaceffcSClaudiu Beznea 	    (index && (tmp & slowck->bits->cr_oscsel)))
282ec187ef0SAlexandre Belloni 		return 0;
283ec187ef0SAlexandre Belloni 
284ec187ef0SAlexandre Belloni 	if (index)
285abaceffcSClaudiu Beznea 		tmp |= slowck->bits->cr_oscsel;
286ec187ef0SAlexandre Belloni 	else
287abaceffcSClaudiu Beznea 		tmp &= ~slowck->bits->cr_oscsel;
288ec187ef0SAlexandre Belloni 
289ec187ef0SAlexandre Belloni 	writel(tmp, sckcr);
290ec187ef0SAlexandre Belloni 
291ec187ef0SAlexandre Belloni 	usleep_range(SLOWCK_SW_TIME_USEC, SLOWCK_SW_TIME_USEC + 1);
292ec187ef0SAlexandre Belloni 
293ec187ef0SAlexandre Belloni 	return 0;
294ec187ef0SAlexandre Belloni }
295ec187ef0SAlexandre Belloni 
296ec187ef0SAlexandre Belloni static u8 clk_sam9x5_slow_get_parent(struct clk_hw *hw)
297ec187ef0SAlexandre Belloni {
298ec187ef0SAlexandre Belloni 	struct clk_sam9x5_slow *slowck = to_clk_sam9x5_slow(hw);
299ec187ef0SAlexandre Belloni 
300abaceffcSClaudiu Beznea 	return !!(readl(slowck->sckcr) & slowck->bits->cr_oscsel);
301ec187ef0SAlexandre Belloni }
302ec187ef0SAlexandre Belloni 
303ec187ef0SAlexandre Belloni static const struct clk_ops sam9x5_slow_ops = {
304ec187ef0SAlexandre Belloni 	.set_parent = clk_sam9x5_slow_set_parent,
305ec187ef0SAlexandre Belloni 	.get_parent = clk_sam9x5_slow_get_parent,
306ec187ef0SAlexandre Belloni };
307ec187ef0SAlexandre Belloni 
308ec187ef0SAlexandre Belloni static struct clk_hw * __init
309ec187ef0SAlexandre Belloni at91_clk_register_sam9x5_slow(void __iomem *sckcr,
310ec187ef0SAlexandre Belloni 			      const char *name,
311ec187ef0SAlexandre Belloni 			      const char **parent_names,
312abaceffcSClaudiu Beznea 			      int num_parents,
313abaceffcSClaudiu Beznea 			      const struct clk_slow_bits *bits)
314ec187ef0SAlexandre Belloni {
315ec187ef0SAlexandre Belloni 	struct clk_sam9x5_slow *slowck;
316ec187ef0SAlexandre Belloni 	struct clk_hw *hw;
317ec187ef0SAlexandre Belloni 	struct clk_init_data init;
318ec187ef0SAlexandre Belloni 	int ret;
319ec187ef0SAlexandre Belloni 
320ec187ef0SAlexandre Belloni 	if (!sckcr || !name || !parent_names || !num_parents)
321ec187ef0SAlexandre Belloni 		return ERR_PTR(-EINVAL);
322ec187ef0SAlexandre Belloni 
323ec187ef0SAlexandre Belloni 	slowck = kzalloc(sizeof(*slowck), GFP_KERNEL);
324ec187ef0SAlexandre Belloni 	if (!slowck)
325ec187ef0SAlexandre Belloni 		return ERR_PTR(-ENOMEM);
326ec187ef0SAlexandre Belloni 
327ec187ef0SAlexandre Belloni 	init.name = name;
328ec187ef0SAlexandre Belloni 	init.ops = &sam9x5_slow_ops;
329ec187ef0SAlexandre Belloni 	init.parent_names = parent_names;
330ec187ef0SAlexandre Belloni 	init.num_parents = num_parents;
331ec187ef0SAlexandre Belloni 	init.flags = 0;
332ec187ef0SAlexandre Belloni 
333ec187ef0SAlexandre Belloni 	slowck->hw.init = &init;
334ec187ef0SAlexandre Belloni 	slowck->sckcr = sckcr;
335abaceffcSClaudiu Beznea 	slowck->bits = bits;
336abaceffcSClaudiu Beznea 	slowck->parent = !!(readl(sckcr) & slowck->bits->cr_oscsel);
337ec187ef0SAlexandre Belloni 
338ec187ef0SAlexandre Belloni 	hw = &slowck->hw;
339ec187ef0SAlexandre Belloni 	ret = clk_hw_register(NULL, &slowck->hw);
340ec187ef0SAlexandre Belloni 	if (ret) {
341ec187ef0SAlexandre Belloni 		kfree(slowck);
342ec187ef0SAlexandre Belloni 		hw = ERR_PTR(ret);
343ec187ef0SAlexandre Belloni 	}
344ec187ef0SAlexandre Belloni 
345ec187ef0SAlexandre Belloni 	return hw;
346ec187ef0SAlexandre Belloni }
347ec187ef0SAlexandre Belloni 
348016d22ddSClaudiu Beznea static void at91_clk_unregister_sam9x5_slow(struct clk_hw *hw)
349016d22ddSClaudiu Beznea {
350016d22ddSClaudiu Beznea 	struct clk_sam9x5_slow *slowck = to_clk_sam9x5_slow(hw);
351016d22ddSClaudiu Beznea 
352016d22ddSClaudiu Beznea 	clk_hw_unregister(hw);
353016d22ddSClaudiu Beznea 	kfree(slowck);
354016d22ddSClaudiu Beznea }
355016d22ddSClaudiu Beznea 
3565c16ffa7SAlexandre Belloni static void __init at91sam9x5_sckc_register(struct device_node *np,
357abaceffcSClaudiu Beznea 					    unsigned int rc_osc_startup_us,
358abaceffcSClaudiu Beznea 					    const struct clk_slow_bits *bits)
35980eded6cSBoris BREZILLON {
36045b5ec84SAlexandre Belloni 	const char *parent_names[2] = { "slow_rc_osc", "slow_osc" };
36180eded6cSBoris BREZILLON 	void __iomem *regbase = of_iomap(np, 0);
36245b5ec84SAlexandre Belloni 	struct device_node *child = NULL;
36345b5ec84SAlexandre Belloni 	const char *xtal_name;
36482e25dc8SClaudiu Beznea 	struct clk_hw *slow_rc, *slow_osc, *slowck;
36545b5ec84SAlexandre Belloni 	bool bypass;
36682e25dc8SClaudiu Beznea 	int ret;
36780eded6cSBoris BREZILLON 
36880eded6cSBoris BREZILLON 	if (!regbase)
36980eded6cSBoris BREZILLON 		return;
37080eded6cSBoris BREZILLON 
37182e25dc8SClaudiu Beznea 	slow_rc = at91_clk_register_slow_rc_osc(regbase, parent_names[0],
37282e25dc8SClaudiu Beznea 						32768, 50000000,
37382e25dc8SClaudiu Beznea 						rc_osc_startup_us, bits);
37482e25dc8SClaudiu Beznea 	if (IS_ERR(slow_rc))
37545b5ec84SAlexandre Belloni 		return;
37645b5ec84SAlexandre Belloni 
37745b5ec84SAlexandre Belloni 	xtal_name = of_clk_get_parent_name(np, 0);
37845b5ec84SAlexandre Belloni 	if (!xtal_name) {
37945b5ec84SAlexandre Belloni 		/* DT backward compatibility */
38045b5ec84SAlexandre Belloni 		child = of_get_compatible_child(np, "atmel,at91sam9x5-clk-slow-osc");
38145b5ec84SAlexandre Belloni 		if (!child)
38282e25dc8SClaudiu Beznea 			goto unregister_slow_rc;
38345b5ec84SAlexandre Belloni 
38445b5ec84SAlexandre Belloni 		xtal_name = of_clk_get_parent_name(child, 0);
38545b5ec84SAlexandre Belloni 		bypass = of_property_read_bool(child, "atmel,osc-bypass");
38645b5ec84SAlexandre Belloni 
38745b5ec84SAlexandre Belloni 		child =  of_get_compatible_child(np, "atmel,at91sam9x5-clk-slow");
38845b5ec84SAlexandre Belloni 	} else {
38945b5ec84SAlexandre Belloni 		bypass = of_property_read_bool(np, "atmel,osc-bypass");
39080eded6cSBoris BREZILLON 	}
39145b5ec84SAlexandre Belloni 
39245b5ec84SAlexandre Belloni 	if (!xtal_name)
39382e25dc8SClaudiu Beznea 		goto unregister_slow_rc;
39445b5ec84SAlexandre Belloni 
39582e25dc8SClaudiu Beznea 	slow_osc = at91_clk_register_slow_osc(regbase, parent_names[1],
39682e25dc8SClaudiu Beznea 					      xtal_name, 1200000, bypass, bits);
39782e25dc8SClaudiu Beznea 	if (IS_ERR(slow_osc))
39882e25dc8SClaudiu Beznea 		goto unregister_slow_rc;
39945b5ec84SAlexandre Belloni 
40082e25dc8SClaudiu Beznea 	slowck = at91_clk_register_sam9x5_slow(regbase, "slowck", parent_names,
40182e25dc8SClaudiu Beznea 					       2, bits);
40282e25dc8SClaudiu Beznea 	if (IS_ERR(slowck))
40382e25dc8SClaudiu Beznea 		goto unregister_slow_osc;
40445b5ec84SAlexandre Belloni 
40545b5ec84SAlexandre Belloni 	/* DT backward compatibility */
40645b5ec84SAlexandre Belloni 	if (child)
40782e25dc8SClaudiu Beznea 		ret = of_clk_add_hw_provider(child, of_clk_hw_simple_get,
40882e25dc8SClaudiu Beznea 					     slowck);
40982e25dc8SClaudiu Beznea 	else
41082e25dc8SClaudiu Beznea 		ret = of_clk_add_hw_provider(np, of_clk_hw_simple_get, slowck);
41182e25dc8SClaudiu Beznea 
41282e25dc8SClaudiu Beznea 	if (WARN_ON(ret))
41382e25dc8SClaudiu Beznea 		goto unregister_slowck;
41482e25dc8SClaudiu Beznea 
41582e25dc8SClaudiu Beznea 	return;
41682e25dc8SClaudiu Beznea 
41782e25dc8SClaudiu Beznea unregister_slowck:
41882e25dc8SClaudiu Beznea 	at91_clk_unregister_sam9x5_slow(slowck);
41982e25dc8SClaudiu Beznea unregister_slow_osc:
42082e25dc8SClaudiu Beznea 	at91_clk_unregister_slow_osc(slow_osc);
42182e25dc8SClaudiu Beznea unregister_slow_rc:
42282e25dc8SClaudiu Beznea 	at91_clk_unregister_slow_rc_osc(slow_rc);
42380eded6cSBoris BREZILLON }
4245c16ffa7SAlexandre Belloni 
425abaceffcSClaudiu Beznea static const struct clk_slow_bits at91sam9x5_bits = {
426abaceffcSClaudiu Beznea 	.cr_rcen = BIT(0),
427abaceffcSClaudiu Beznea 	.cr_osc32en = BIT(1),
428abaceffcSClaudiu Beznea 	.cr_osc32byp = BIT(2),
429abaceffcSClaudiu Beznea 	.cr_oscsel = BIT(3),
430abaceffcSClaudiu Beznea };
431abaceffcSClaudiu Beznea 
4325c16ffa7SAlexandre Belloni static void __init of_at91sam9x5_sckc_setup(struct device_node *np)
4335c16ffa7SAlexandre Belloni {
434abaceffcSClaudiu Beznea 	at91sam9x5_sckc_register(np, 75, &at91sam9x5_bits);
4355c16ffa7SAlexandre Belloni }
43680eded6cSBoris BREZILLON CLK_OF_DECLARE(at91sam9x5_clk_sckc, "atmel,at91sam9x5-sckc",
43780eded6cSBoris BREZILLON 	       of_at91sam9x5_sckc_setup);
4384b13b645SAlexandre Belloni 
4395c16ffa7SAlexandre Belloni static void __init of_sama5d3_sckc_setup(struct device_node *np)
4405c16ffa7SAlexandre Belloni {
441abaceffcSClaudiu Beznea 	at91sam9x5_sckc_register(np, 500, &at91sam9x5_bits);
4425c16ffa7SAlexandre Belloni }
4435c16ffa7SAlexandre Belloni CLK_OF_DECLARE(sama5d3_clk_sckc, "atmel,sama5d3-sckc",
4445c16ffa7SAlexandre Belloni 	       of_sama5d3_sckc_setup);
4455c16ffa7SAlexandre Belloni 
44604bcc427SClaudiu Beznea static const struct clk_slow_bits at91sam9x60_bits = {
44704bcc427SClaudiu Beznea 	.cr_osc32en = BIT(1),
44804bcc427SClaudiu Beznea 	.cr_osc32byp = BIT(2),
44904bcc427SClaudiu Beznea 	.cr_oscsel = BIT(24),
45004bcc427SClaudiu Beznea };
45104bcc427SClaudiu Beznea 
45204bcc427SClaudiu Beznea static void __init of_sam9x60_sckc_setup(struct device_node *np)
45304bcc427SClaudiu Beznea {
45404bcc427SClaudiu Beznea 	void __iomem *regbase = of_iomap(np, 0);
45504bcc427SClaudiu Beznea 	struct clk_hw_onecell_data *clk_data;
45604bcc427SClaudiu Beznea 	struct clk_hw *slow_rc, *slow_osc;
45704bcc427SClaudiu Beznea 	const char *xtal_name;
45804bcc427SClaudiu Beznea 	const char *parent_names[2] = { "slow_rc_osc", "slow_osc" };
45904bcc427SClaudiu Beznea 	bool bypass;
46004bcc427SClaudiu Beznea 	int ret;
46104bcc427SClaudiu Beznea 
46204bcc427SClaudiu Beznea 	if (!regbase)
46304bcc427SClaudiu Beznea 		return;
46404bcc427SClaudiu Beznea 
46504bcc427SClaudiu Beznea 	slow_rc = clk_hw_register_fixed_rate(NULL, parent_names[0], NULL, 0,
46604bcc427SClaudiu Beznea 					     32768);
46704bcc427SClaudiu Beznea 	if (IS_ERR(slow_rc))
46804bcc427SClaudiu Beznea 		return;
46904bcc427SClaudiu Beznea 
47004bcc427SClaudiu Beznea 	xtal_name = of_clk_get_parent_name(np, 0);
47104bcc427SClaudiu Beznea 	if (!xtal_name)
47204bcc427SClaudiu Beznea 		goto unregister_slow_rc;
47304bcc427SClaudiu Beznea 
47404bcc427SClaudiu Beznea 	bypass = of_property_read_bool(np, "atmel,osc-bypass");
47504bcc427SClaudiu Beznea 	slow_osc = at91_clk_register_slow_osc(regbase, parent_names[1],
47604bcc427SClaudiu Beznea 					      xtal_name, 5000000, bypass,
47704bcc427SClaudiu Beznea 					      &at91sam9x60_bits);
47804bcc427SClaudiu Beznea 	if (IS_ERR(slow_osc))
47904bcc427SClaudiu Beznea 		goto unregister_slow_rc;
48004bcc427SClaudiu Beznea 
481e620a1e0SStephen Kitt 	clk_data = kzalloc(struct_size(clk_data, hws, 2), GFP_KERNEL);
48204bcc427SClaudiu Beznea 	if (!clk_data)
48304bcc427SClaudiu Beznea 		goto unregister_slow_osc;
48404bcc427SClaudiu Beznea 
48504bcc427SClaudiu Beznea 	/* MD_SLCK and TD_SLCK. */
48604bcc427SClaudiu Beznea 	clk_data->num = 2;
48704bcc427SClaudiu Beznea 	clk_data->hws[0] = clk_hw_register_fixed_rate(NULL, "md_slck",
48804bcc427SClaudiu Beznea 						      parent_names[0],
48904bcc427SClaudiu Beznea 						      0, 32768);
49004bcc427SClaudiu Beznea 	if (IS_ERR(clk_data->hws[0]))
49104bcc427SClaudiu Beznea 		goto clk_data_free;
49204bcc427SClaudiu Beznea 
49304bcc427SClaudiu Beznea 	clk_data->hws[1] = at91_clk_register_sam9x5_slow(regbase, "td_slck",
49404bcc427SClaudiu Beznea 							 parent_names, 2,
49504bcc427SClaudiu Beznea 							 &at91sam9x60_bits);
49604bcc427SClaudiu Beznea 	if (IS_ERR(clk_data->hws[1]))
49704bcc427SClaudiu Beznea 		goto unregister_md_slck;
49804bcc427SClaudiu Beznea 
49904bcc427SClaudiu Beznea 	ret = of_clk_add_hw_provider(np, of_clk_hw_onecell_get, clk_data);
50004bcc427SClaudiu Beznea 	if (WARN_ON(ret))
50104bcc427SClaudiu Beznea 		goto unregister_td_slck;
50204bcc427SClaudiu Beznea 
50304bcc427SClaudiu Beznea 	return;
50404bcc427SClaudiu Beznea 
50504bcc427SClaudiu Beznea unregister_td_slck:
506ecbcc2aaSClaudiu Beznea 	at91_clk_unregister_sam9x5_slow(clk_data->hws[1]);
50704bcc427SClaudiu Beznea unregister_md_slck:
50804bcc427SClaudiu Beznea 	clk_hw_unregister(clk_data->hws[0]);
50904bcc427SClaudiu Beznea clk_data_free:
51004bcc427SClaudiu Beznea 	kfree(clk_data);
51104bcc427SClaudiu Beznea unregister_slow_osc:
512ecbcc2aaSClaudiu Beznea 	at91_clk_unregister_slow_osc(slow_osc);
51304bcc427SClaudiu Beznea unregister_slow_rc:
51404bcc427SClaudiu Beznea 	clk_hw_unregister(slow_rc);
51504bcc427SClaudiu Beznea }
51604bcc427SClaudiu Beznea CLK_OF_DECLARE(sam9x60_clk_sckc, "microchip,sam9x60-sckc",
51704bcc427SClaudiu Beznea 	       of_sam9x60_sckc_setup);
51804bcc427SClaudiu Beznea 
5194b13b645SAlexandre Belloni static int clk_sama5d4_slow_osc_prepare(struct clk_hw *hw)
5204b13b645SAlexandre Belloni {
5214b13b645SAlexandre Belloni 	struct clk_sama5d4_slow_osc *osc = to_clk_sama5d4_slow_osc(hw);
5224b13b645SAlexandre Belloni 
5234b13b645SAlexandre Belloni 	if (osc->prepared)
5244b13b645SAlexandre Belloni 		return 0;
5254b13b645SAlexandre Belloni 
5264b13b645SAlexandre Belloni 	/*
5274b13b645SAlexandre Belloni 	 * Assume that if it has already been selected (for example by the
5284b13b645SAlexandre Belloni 	 * bootloader), enough time has aready passed.
5294b13b645SAlexandre Belloni 	 */
530abaceffcSClaudiu Beznea 	if ((readl(osc->sckcr) & osc->bits->cr_oscsel)) {
5314b13b645SAlexandre Belloni 		osc->prepared = true;
5324b13b645SAlexandre Belloni 		return 0;
5334b13b645SAlexandre Belloni 	}
5344b13b645SAlexandre Belloni 
5354b13b645SAlexandre Belloni 	usleep_range(osc->startup_usec, osc->startup_usec + 1);
5364b13b645SAlexandre Belloni 	osc->prepared = true;
5374b13b645SAlexandre Belloni 
5384b13b645SAlexandre Belloni 	return 0;
5394b13b645SAlexandre Belloni }
5404b13b645SAlexandre Belloni 
5414b13b645SAlexandre Belloni static int clk_sama5d4_slow_osc_is_prepared(struct clk_hw *hw)
5424b13b645SAlexandre Belloni {
5434b13b645SAlexandre Belloni 	struct clk_sama5d4_slow_osc *osc = to_clk_sama5d4_slow_osc(hw);
5444b13b645SAlexandre Belloni 
5454b13b645SAlexandre Belloni 	return osc->prepared;
5464b13b645SAlexandre Belloni }
5474b13b645SAlexandre Belloni 
5484b13b645SAlexandre Belloni static const struct clk_ops sama5d4_slow_osc_ops = {
5494b13b645SAlexandre Belloni 	.prepare = clk_sama5d4_slow_osc_prepare,
5504b13b645SAlexandre Belloni 	.is_prepared = clk_sama5d4_slow_osc_is_prepared,
5514b13b645SAlexandre Belloni };
5524b13b645SAlexandre Belloni 
553abaceffcSClaudiu Beznea static const struct clk_slow_bits at91sama5d4_bits = {
554abaceffcSClaudiu Beznea 	.cr_oscsel = BIT(3),
555abaceffcSClaudiu Beznea };
556abaceffcSClaudiu Beznea 
5574b13b645SAlexandre Belloni static void __init of_sama5d4_sckc_setup(struct device_node *np)
5584b13b645SAlexandre Belloni {
5594b13b645SAlexandre Belloni 	void __iomem *regbase = of_iomap(np, 0);
560d09e6ca1SClaudiu Beznea 	struct clk_hw *slow_rc, *slowck;
5614b13b645SAlexandre Belloni 	struct clk_sama5d4_slow_osc *osc;
5624b13b645SAlexandre Belloni 	struct clk_init_data init;
5634b13b645SAlexandre Belloni 	const char *xtal_name;
5644b13b645SAlexandre Belloni 	const char *parent_names[2] = { "slow_rc_osc", "slow_osc" };
5654b13b645SAlexandre Belloni 	int ret;
5664b13b645SAlexandre Belloni 
5674b13b645SAlexandre Belloni 	if (!regbase)
5684b13b645SAlexandre Belloni 		return;
5694b13b645SAlexandre Belloni 
570d09e6ca1SClaudiu Beznea 	slow_rc = clk_hw_register_fixed_rate_with_accuracy(NULL,
571d09e6ca1SClaudiu Beznea 							   parent_names[0],
5724b13b645SAlexandre Belloni 							   NULL, 0, 32768,
5734b13b645SAlexandre Belloni 							   250000000);
574d09e6ca1SClaudiu Beznea 	if (IS_ERR(slow_rc))
5754b13b645SAlexandre Belloni 		return;
5764b13b645SAlexandre Belloni 
5774b13b645SAlexandre Belloni 	xtal_name = of_clk_get_parent_name(np, 0);
5784b13b645SAlexandre Belloni 
5794b13b645SAlexandre Belloni 	osc = kzalloc(sizeof(*osc), GFP_KERNEL);
5804b13b645SAlexandre Belloni 	if (!osc)
581d09e6ca1SClaudiu Beznea 		goto unregister_slow_rc;
5824b13b645SAlexandre Belloni 
5834b13b645SAlexandre Belloni 	init.name = parent_names[1];
5844b13b645SAlexandre Belloni 	init.ops = &sama5d4_slow_osc_ops;
5854b13b645SAlexandre Belloni 	init.parent_names = &xtal_name;
5864b13b645SAlexandre Belloni 	init.num_parents = 1;
5874b13b645SAlexandre Belloni 	init.flags = CLK_IGNORE_UNUSED;
5884b13b645SAlexandre Belloni 
5894b13b645SAlexandre Belloni 	osc->hw.init = &init;
5904b13b645SAlexandre Belloni 	osc->sckcr = regbase;
5914b13b645SAlexandre Belloni 	osc->startup_usec = 1200000;
592abaceffcSClaudiu Beznea 	osc->bits = &at91sama5d4_bits;
5934b13b645SAlexandre Belloni 
5944b13b645SAlexandre Belloni 	ret = clk_hw_register(NULL, &osc->hw);
595d09e6ca1SClaudiu Beznea 	if (ret)
596d09e6ca1SClaudiu Beznea 		goto free_slow_osc_data;
5974b13b645SAlexandre Belloni 
598d09e6ca1SClaudiu Beznea 	slowck = at91_clk_register_sam9x5_slow(regbase, "slowck",
599d09e6ca1SClaudiu Beznea 					       parent_names, 2,
600abaceffcSClaudiu Beznea 					       &at91sama5d4_bits);
601d09e6ca1SClaudiu Beznea 	if (IS_ERR(slowck))
602d09e6ca1SClaudiu Beznea 		goto unregister_slow_osc;
603d09e6ca1SClaudiu Beznea 
604d09e6ca1SClaudiu Beznea 	ret = of_clk_add_hw_provider(np, of_clk_hw_simple_get, slowck);
605d09e6ca1SClaudiu Beznea 	if (WARN_ON(ret))
606d09e6ca1SClaudiu Beznea 		goto unregister_slowck;
607d09e6ca1SClaudiu Beznea 
6084b13b645SAlexandre Belloni 	return;
6094b13b645SAlexandre Belloni 
610d09e6ca1SClaudiu Beznea unregister_slowck:
611d09e6ca1SClaudiu Beznea 	at91_clk_unregister_sam9x5_slow(slowck);
612d09e6ca1SClaudiu Beznea unregister_slow_osc:
613d09e6ca1SClaudiu Beznea 	clk_hw_unregister(&osc->hw);
614d09e6ca1SClaudiu Beznea free_slow_osc_data:
6154b13b645SAlexandre Belloni 	kfree(osc);
616d09e6ca1SClaudiu Beznea unregister_slow_rc:
617d09e6ca1SClaudiu Beznea 	clk_hw_unregister(slow_rc);
6184b13b645SAlexandre Belloni }
6194b13b645SAlexandre Belloni CLK_OF_DECLARE(sama5d4_clk_sckc, "atmel,sama5d4-sckc",
6204b13b645SAlexandre Belloni 	       of_sama5d4_sckc_setup);
621