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 = {
313d2e88be3SMaxime Ripard .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,
320ec187ef0SAlexandre Belloni const char *name,
321*8aa1db9cSClaudiu Beznea const struct clk_hw **parent_hws,
322abaceffcSClaudiu Beznea int num_parents,
323abaceffcSClaudiu Beznea const struct clk_slow_bits *bits)
324ec187ef0SAlexandre Belloni {
325ec187ef0SAlexandre Belloni struct clk_sam9x5_slow *slowck;
326ec187ef0SAlexandre Belloni struct clk_hw *hw;
327*8aa1db9cSClaudiu Beznea struct clk_init_data init = {};
328ec187ef0SAlexandre Belloni int ret;
329ec187ef0SAlexandre Belloni
330*8aa1db9cSClaudiu Beznea 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;
338ec187ef0SAlexandre Belloni init.ops = &sam9x5_slow_ops;
339*8aa1db9cSClaudiu Beznea 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;
344ec187ef0SAlexandre Belloni slowck->sckcr = sckcr;
345abaceffcSClaudiu Beznea slowck->bits = bits;
346abaceffcSClaudiu Beznea 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 }
357ec187ef0SAlexandre Belloni
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 }
365016d22ddSClaudiu Beznea
at91sam9x5_sckc_register(struct device_node * np,unsigned int rc_osc_startup_us,const struct clk_slow_bits * bits)3665c16ffa7SAlexandre Belloni static void __init at91sam9x5_sckc_register(struct device_node *np,
367abaceffcSClaudiu Beznea unsigned int rc_osc_startup_us,
368abaceffcSClaudiu Beznea const struct clk_slow_bits *bits)
36980eded6cSBoris BREZILLON {
37080eded6cSBoris BREZILLON void __iomem *regbase = of_iomap(np, 0);
37145b5ec84SAlexandre Belloni struct device_node *child = NULL;
37245b5ec84SAlexandre Belloni const char *xtal_name;
37382e25dc8SClaudiu 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 };
377*8aa1db9cSClaudiu Beznea const struct clk_hw *parent_hws[2];
37845b5ec84SAlexandre Belloni bool bypass;
37982e25dc8SClaudiu Beznea int ret;
38080eded6cSBoris BREZILLON
38180eded6cSBoris BREZILLON if (!regbase)
38280eded6cSBoris BREZILLON return;
38380eded6cSBoris BREZILLON
384*8aa1db9cSClaudiu Beznea slow_rc = at91_clk_register_slow_rc_osc(regbase, "slow_rc_osc",
38582e25dc8SClaudiu Beznea 32768, 50000000,
38682e25dc8SClaudiu Beznea rc_osc_startup_us, bits);
38782e25dc8SClaudiu Beznea 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");
39445b5ec84SAlexandre Belloni if (!child)
39582e25dc8SClaudiu Beznea 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 {
40245b5ec84SAlexandre Belloni bypass = of_property_read_bool(np, "atmel,osc-bypass");
40380eded6cSBoris BREZILLON }
40445b5ec84SAlexandre Belloni
40545b5ec84SAlexandre Belloni if (!xtal_name)
40682e25dc8SClaudiu Beznea goto unregister_slow_rc;
40745b5ec84SAlexandre Belloni
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",
411*8aa1db9cSClaudiu Beznea &parent_data, 1200000, bypass, bits);
41282e25dc8SClaudiu Beznea if (IS_ERR(slow_osc))
41382e25dc8SClaudiu Beznea goto unregister_slow_rc;
41445b5ec84SAlexandre Belloni
415*8aa1db9cSClaudiu Beznea parent_hws[0] = slow_rc;
416*8aa1db9cSClaudiu Beznea parent_hws[1] = slow_osc;
417*8aa1db9cSClaudiu Beznea slowck = at91_clk_register_sam9x5_slow(regbase, "slowck", parent_hws,
41882e25dc8SClaudiu Beznea 2, bits);
41982e25dc8SClaudiu Beznea if (IS_ERR(slowck))
42082e25dc8SClaudiu Beznea goto unregister_slow_osc;
42145b5ec84SAlexandre Belloni
42245b5ec84SAlexandre Belloni /* DT backward compatibility */
42345b5ec84SAlexandre Belloni 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:
43982e25dc8SClaudiu Beznea at91_clk_unregister_slow_rc_osc(slow_rc);
44080eded6cSBoris BREZILLON }
4415c16ffa7SAlexandre Belloni
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 };
448abaceffcSClaudiu Beznea
of_at91sam9x5_sckc_setup(struct device_node * np)4495c16ffa7SAlexandre Belloni static void __init of_at91sam9x5_sckc_setup(struct device_node *np)
4505c16ffa7SAlexandre Belloni {
451abaceffcSClaudiu Beznea at91sam9x5_sckc_register(np, 75, &at91sam9x5_bits);
4525c16ffa7SAlexandre Belloni }
45380eded6cSBoris BREZILLON CLK_OF_DECLARE(at91sam9x5_clk_sckc, "atmel,at91sam9x5-sckc",
45480eded6cSBoris BREZILLON of_at91sam9x5_sckc_setup);
4554b13b645SAlexandre Belloni
of_sama5d3_sckc_setup(struct device_node * np)4565c16ffa7SAlexandre Belloni static void __init of_sama5d3_sckc_setup(struct device_node *np)
4575c16ffa7SAlexandre Belloni {
458abaceffcSClaudiu Beznea 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);
4625c16ffa7SAlexandre Belloni
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;
47404bcc427SClaudiu 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",
478*8aa1db9cSClaudiu Beznea };
47904bcc427SClaudiu Beznea bool bypass;
48004bcc427SClaudiu Beznea int ret;
48104bcc427SClaudiu Beznea
48204bcc427SClaudiu Beznea if (!regbase)
48304bcc427SClaudiu Beznea return;
48404bcc427SClaudiu Beznea
485*8aa1db9cSClaudiu Beznea slow_rc = clk_hw_register_fixed_rate_with_accuracy(NULL, "slow_rc_osc",
486e1e3e700SClaudiu Beznea NULL, 0, 32768,
487e1e3e700SClaudiu 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;
49404bcc427SClaudiu Beznea
495*8aa1db9cSClaudiu Beznea parent_data.fw_name = xtal_name;
49604bcc427SClaudiu Beznea bypass = of_property_read_bool(np, "atmel,osc-bypass");
497*8aa1db9cSClaudiu Beznea slow_osc = at91_clk_register_slow_osc(regbase, "slow_osc",
498*8aa1db9cSClaudiu Beznea &parent_data, 5000000, bypass,
49904bcc427SClaudiu Beznea &at91sam9x60_bits);
50004bcc427SClaudiu Beznea if (IS_ERR(slow_osc))
50104bcc427SClaudiu Beznea goto unregister_slow_rc;
50204bcc427SClaudiu Beznea
503e620a1e0SStephen Kitt 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. */
50804bcc427SClaudiu Beznea clk_data->num = 2;
509*8aa1db9cSClaudiu Beznea clk_data->hws[0] = clk_hw_register_fixed_rate_parent_hw(NULL, "md_slck",
510*8aa1db9cSClaudiu Beznea slow_rc,
51104bcc427SClaudiu Beznea 0, 32768);
51204bcc427SClaudiu Beznea if (IS_ERR(clk_data->hws[0]))
51304bcc427SClaudiu Beznea goto clk_data_free;
51404bcc427SClaudiu Beznea
515*8aa1db9cSClaudiu Beznea parent_hws[0] = slow_rc;
516*8aa1db9cSClaudiu Beznea parent_hws[1] = slow_osc;
51704bcc427SClaudiu Beznea clk_data->hws[1] = at91_clk_register_sam9x5_slow(regbase, "td_slck",
518*8aa1db9cSClaudiu 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
52904bcc427SClaudiu Beznea unregister_td_slck:
530ecbcc2aaSClaudiu 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);
53504bcc427SClaudiu Beznea unregister_slow_osc:
536ecbcc2aaSClaudiu 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);
54204bcc427SClaudiu Beznea
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 /*
5514b13b645SAlexandre Belloni * Assume that if it has already been selected (for example by the
5527c55e8efSTom Rix * bootloader), enough time has already passed.
5534b13b645SAlexandre Belloni */
554abaceffcSClaudiu Beznea if ((readl(osc->sckcr) & osc->bits->cr_oscsel)) {
5554b13b645SAlexandre Belloni osc->prepared = true;
5564b13b645SAlexandre Belloni return 0;
5574b13b645SAlexandre Belloni }
5584b13b645SAlexandre Belloni
559658fd65cSAlexandre Belloni if (system_state < SYSTEM_RUNNING)
560658fd65cSAlexandre Belloni udelay(osc->startup_usec);
561658fd65cSAlexandre 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 };
5794b13b645SAlexandre Belloni
580abaceffcSClaudiu Beznea static const struct clk_slow_bits at91sama5d4_bits = {
581abaceffcSClaudiu Beznea .cr_oscsel = BIT(3),
582abaceffcSClaudiu Beznea };
583abaceffcSClaudiu Beznea
of_sama5d4_sckc_setup(struct device_node * np)5844b13b645SAlexandre Belloni static void __init of_sama5d4_sckc_setup(struct device_node *np)
5854b13b645SAlexandre Belloni {
5864b13b645SAlexandre Belloni void __iomem *regbase = of_iomap(np, 0);
587d09e6ca1SClaudiu Beznea struct clk_hw *slow_rc, *slowck;
5884b13b645SAlexandre Belloni struct clk_sama5d4_slow_osc *osc;
589*8aa1db9cSClaudiu Beznea struct clk_init_data init = {};
5904b13b645SAlexandre Belloni 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",
594*8aa1db9cSClaudiu Beznea };
5954b13b645SAlexandre Belloni int ret;
5964b13b645SAlexandre Belloni
5974b13b645SAlexandre Belloni if (!regbase)
5984b13b645SAlexandre Belloni return;
5994b13b645SAlexandre Belloni
600d09e6ca1SClaudiu Beznea slow_rc = clk_hw_register_fixed_rate_with_accuracy(NULL,
601*8aa1db9cSClaudiu Beznea "slow_rc_osc",
6024b13b645SAlexandre Belloni NULL, 0, 32768,
6034b13b645SAlexandre Belloni 250000000);
604d09e6ca1SClaudiu Beznea if (IS_ERR(slow_rc))
6054b13b645SAlexandre Belloni return;
6064b13b645SAlexandre Belloni
6074b13b645SAlexandre Belloni xtal_name = of_clk_get_parent_name(np, 0);
608*8aa1db9cSClaudiu Beznea if (!xtal_name)
609*8aa1db9cSClaudiu Beznea goto unregister_slow_rc;
610*8aa1db9cSClaudiu Beznea parent_data.fw_name = xtal_name;
6114b13b645SAlexandre Belloni
6124b13b645SAlexandre Belloni osc = kzalloc(sizeof(*osc), GFP_KERNEL);
6134b13b645SAlexandre Belloni if (!osc)
614d09e6ca1SClaudiu Beznea goto unregister_slow_rc;
6154b13b645SAlexandre Belloni
616*8aa1db9cSClaudiu Beznea init.name = "slow_osc";
6174b13b645SAlexandre Belloni init.ops = &sama5d4_slow_osc_ops;
618*8aa1db9cSClaudiu Beznea 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;
6244b13b645SAlexandre Belloni osc->startup_usec = 1200000;
625abaceffcSClaudiu Beznea osc->bits = &at91sama5d4_bits;
6264b13b645SAlexandre Belloni
6274b13b645SAlexandre Belloni ret = clk_hw_register(NULL, &osc->hw);
628d09e6ca1SClaudiu Beznea if (ret)
629d09e6ca1SClaudiu Beznea goto free_slow_osc_data;
6304b13b645SAlexandre Belloni
631*8aa1db9cSClaudiu Beznea parent_hws[0] = slow_rc;
632*8aa1db9cSClaudiu Beznea parent_hws[1] = &osc->hw;
633d09e6ca1SClaudiu Beznea slowck = at91_clk_register_sam9x5_slow(regbase, "slowck",
634*8aa1db9cSClaudiu Beznea parent_hws, 2,
635abaceffcSClaudiu 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;
642d09e6ca1SClaudiu Beznea
6434b13b645SAlexandre Belloni return;
6444b13b645SAlexandre Belloni
645d09e6ca1SClaudiu Beznea unregister_slowck:
646d09e6ca1SClaudiu Beznea at91_clk_unregister_sam9x5_slow(slowck);
647d09e6ca1SClaudiu Beznea unregister_slow_osc:
648d09e6ca1SClaudiu Beznea clk_hw_unregister(&osc->hw);
649d09e6ca1SClaudiu Beznea free_slow_osc_data:
6504b13b645SAlexandre Belloni kfree(osc);
651d09e6ca1SClaudiu Beznea unregister_slow_rc:
652d09e6ca1SClaudiu Beznea clk_hw_unregister(slow_rc);
6534b13b645SAlexandre Belloni }
6544b13b645SAlexandre Belloni CLK_OF_DECLARE(sama5d4_clk_sckc, "atmel,sama5d4-sckc",
6554b13b645SAlexandre Belloni of_sama5d4_sckc_setup);
656