Commit | Line | Data |
---|---|---|
0ad6125b BB |
1 | /* |
2 | * Copyright (C) 2013 Boris BREZILLON <b.brezillon@overkiz.com> | |
3 | * | |
4 | * This program is free software; you can redistribute it and/or modify | |
5 | * it under the terms of the GNU General Public License as published by | |
6 | * the Free Software Foundation; either version 2 of the License, or | |
7 | * (at your option) any later version. | |
8 | * | |
9 | */ | |
10 | ||
11 | #include <linux/clk-provider.h> | |
12 | #include <linux/clkdev.h> | |
13 | #include <linux/clk/at91_pmc.h> | |
14 | #include <linux/of.h> | |
15 | #include <linux/of_address.h> | |
16 | #include <linux/io.h> | |
17 | #include <linux/interrupt.h> | |
18 | #include <linux/irq.h> | |
19 | #include <linux/irqchip/chained_irq.h> | |
20 | #include <linux/irqdomain.h> | |
21 | #include <linux/of_irq.h> | |
22 | ||
23 | #include <asm/proc-fns.h> | |
24 | ||
25 | #include "pmc.h" | |
26 | ||
27 | void __iomem *at91_pmc_base; | |
28 | EXPORT_SYMBOL_GPL(at91_pmc_base); | |
29 | ||
29ee506d AB |
30 | void at91rm9200_idle(void) |
31 | { | |
32 | /* | |
33 | * Disable the processor clock. The processor will be automatically | |
34 | * re-enabled by an interrupt or by a reset. | |
35 | */ | |
36 | at91_pmc_write(AT91_PMC_SCDR, AT91_PMC_PCK); | |
37 | } | |
38 | ||
0ad6125b BB |
39 | void at91sam9_idle(void) |
40 | { | |
41 | at91_pmc_write(AT91_PMC_SCDR, AT91_PMC_PCK); | |
42 | cpu_do_idle(); | |
43 | } | |
44 | ||
45 | int of_at91_get_clk_range(struct device_node *np, const char *propname, | |
46 | struct clk_range *range) | |
47 | { | |
48 | u32 min, max; | |
49 | int ret; | |
50 | ||
51 | ret = of_property_read_u32_index(np, propname, 0, &min); | |
52 | if (ret) | |
53 | return ret; | |
54 | ||
55 | ret = of_property_read_u32_index(np, propname, 1, &max); | |
56 | if (ret) | |
57 | return ret; | |
58 | ||
59 | if (range) { | |
60 | range->min = min; | |
61 | range->max = max; | |
62 | } | |
63 | ||
64 | return 0; | |
65 | } | |
66 | EXPORT_SYMBOL_GPL(of_at91_get_clk_range); | |
67 | ||
68 | static void pmc_irq_mask(struct irq_data *d) | |
69 | { | |
70 | struct at91_pmc *pmc = irq_data_get_irq_chip_data(d); | |
71 | ||
72 | pmc_write(pmc, AT91_PMC_IDR, 1 << d->hwirq); | |
73 | } | |
74 | ||
75 | static void pmc_irq_unmask(struct irq_data *d) | |
76 | { | |
77 | struct at91_pmc *pmc = irq_data_get_irq_chip_data(d); | |
78 | ||
79 | pmc_write(pmc, AT91_PMC_IER, 1 << d->hwirq); | |
80 | } | |
81 | ||
82 | static int pmc_irq_set_type(struct irq_data *d, unsigned type) | |
83 | { | |
84 | if (type != IRQ_TYPE_LEVEL_HIGH) { | |
85 | pr_warn("PMC: type not supported (support only IRQ_TYPE_LEVEL_HIGH type)\n"); | |
86 | return -EINVAL; | |
87 | } | |
88 | ||
89 | return 0; | |
90 | } | |
91 | ||
947f5b10 BB |
92 | static void pmc_irq_suspend(struct irq_data *d) |
93 | { | |
94 | struct at91_pmc *pmc = irq_data_get_irq_chip_data(d); | |
95 | ||
96 | pmc->imr = pmc_read(pmc, AT91_PMC_IMR); | |
97 | pmc_write(pmc, AT91_PMC_IDR, pmc->imr); | |
98 | } | |
99 | ||
100 | static void pmc_irq_resume(struct irq_data *d) | |
101 | { | |
102 | struct at91_pmc *pmc = irq_data_get_irq_chip_data(d); | |
103 | ||
104 | pmc_write(pmc, AT91_PMC_IER, pmc->imr); | |
105 | } | |
106 | ||
0ad6125b BB |
107 | static struct irq_chip pmc_irq = { |
108 | .name = "PMC", | |
109 | .irq_disable = pmc_irq_mask, | |
110 | .irq_mask = pmc_irq_mask, | |
111 | .irq_unmask = pmc_irq_unmask, | |
112 | .irq_set_type = pmc_irq_set_type, | |
947f5b10 BB |
113 | .irq_suspend = pmc_irq_suspend, |
114 | .irq_resume = pmc_irq_resume, | |
0ad6125b BB |
115 | }; |
116 | ||
117 | static struct lock_class_key pmc_lock_class; | |
118 | ||
119 | static int pmc_irq_map(struct irq_domain *h, unsigned int virq, | |
120 | irq_hw_number_t hw) | |
121 | { | |
122 | struct at91_pmc *pmc = h->host_data; | |
123 | ||
124 | irq_set_lockdep_class(virq, &pmc_lock_class); | |
125 | ||
126 | irq_set_chip_and_handler(virq, &pmc_irq, | |
127 | handle_level_irq); | |
128 | set_irq_flags(virq, IRQF_VALID); | |
129 | irq_set_chip_data(virq, pmc); | |
130 | ||
131 | return 0; | |
132 | } | |
133 | ||
134 | static int pmc_irq_domain_xlate(struct irq_domain *d, | |
135 | struct device_node *ctrlr, | |
136 | const u32 *intspec, unsigned int intsize, | |
137 | irq_hw_number_t *out_hwirq, | |
138 | unsigned int *out_type) | |
139 | { | |
140 | struct at91_pmc *pmc = d->host_data; | |
141 | const struct at91_pmc_caps *caps = pmc->caps; | |
142 | ||
143 | if (WARN_ON(intsize < 1)) | |
144 | return -EINVAL; | |
145 | ||
146 | *out_hwirq = intspec[0]; | |
147 | ||
148 | if (!(caps->available_irqs & (1 << *out_hwirq))) | |
149 | return -EINVAL; | |
150 | ||
151 | *out_type = IRQ_TYPE_LEVEL_HIGH; | |
152 | ||
153 | return 0; | |
154 | } | |
155 | ||
d6c27678 | 156 | static const struct irq_domain_ops pmc_irq_ops = { |
0ad6125b BB |
157 | .map = pmc_irq_map, |
158 | .xlate = pmc_irq_domain_xlate, | |
159 | }; | |
160 | ||
161 | static irqreturn_t pmc_irq_handler(int irq, void *data) | |
162 | { | |
163 | struct at91_pmc *pmc = (struct at91_pmc *)data; | |
164 | unsigned long sr; | |
165 | int n; | |
166 | ||
167 | sr = pmc_read(pmc, AT91_PMC_SR) & pmc_read(pmc, AT91_PMC_IMR); | |
168 | if (!sr) | |
169 | return IRQ_NONE; | |
170 | ||
171 | for_each_set_bit(n, &sr, BITS_PER_LONG) | |
172 | generic_handle_irq(irq_find_mapping(pmc->irqdomain, n)); | |
173 | ||
174 | return IRQ_HANDLED; | |
175 | } | |
176 | ||
177 | static const struct at91_pmc_caps at91rm9200_caps = { | |
178 | .available_irqs = AT91_PMC_MOSCS | AT91_PMC_LOCKA | AT91_PMC_LOCKB | | |
179 | AT91_PMC_MCKRDY | AT91_PMC_PCK0RDY | | |
180 | AT91_PMC_PCK1RDY | AT91_PMC_PCK2RDY | | |
181 | AT91_PMC_PCK3RDY, | |
182 | }; | |
183 | ||
184 | static const struct at91_pmc_caps at91sam9260_caps = { | |
185 | .available_irqs = AT91_PMC_MOSCS | AT91_PMC_LOCKA | AT91_PMC_LOCKB | | |
186 | AT91_PMC_MCKRDY | AT91_PMC_PCK0RDY | | |
187 | AT91_PMC_PCK1RDY, | |
188 | }; | |
189 | ||
190 | static const struct at91_pmc_caps at91sam9g45_caps = { | |
191 | .available_irqs = AT91_PMC_MOSCS | AT91_PMC_LOCKA | AT91_PMC_MCKRDY | | |
192 | AT91_PMC_LOCKU | AT91_PMC_PCK0RDY | | |
193 | AT91_PMC_PCK1RDY, | |
194 | }; | |
195 | ||
196 | static const struct at91_pmc_caps at91sam9n12_caps = { | |
197 | .available_irqs = AT91_PMC_MOSCS | AT91_PMC_LOCKA | AT91_PMC_LOCKB | | |
198 | AT91_PMC_MCKRDY | AT91_PMC_PCK0RDY | | |
199 | AT91_PMC_PCK1RDY | AT91_PMC_MOSCSELS | | |
200 | AT91_PMC_MOSCRCS | AT91_PMC_CFDEV, | |
201 | }; | |
202 | ||
203 | static const struct at91_pmc_caps at91sam9x5_caps = { | |
204 | .available_irqs = AT91_PMC_MOSCS | AT91_PMC_LOCKA | AT91_PMC_MCKRDY | | |
205 | AT91_PMC_LOCKU | AT91_PMC_PCK0RDY | | |
206 | AT91_PMC_PCK1RDY | AT91_PMC_MOSCSELS | | |
207 | AT91_PMC_MOSCRCS | AT91_PMC_CFDEV, | |
208 | }; | |
209 | ||
210 | static const struct at91_pmc_caps sama5d3_caps = { | |
211 | .available_irqs = AT91_PMC_MOSCS | AT91_PMC_LOCKA | AT91_PMC_MCKRDY | | |
212 | AT91_PMC_LOCKU | AT91_PMC_PCK0RDY | | |
213 | AT91_PMC_PCK1RDY | AT91_PMC_PCK2RDY | | |
214 | AT91_PMC_MOSCSELS | AT91_PMC_MOSCRCS | | |
215 | AT91_PMC_CFDEV, | |
216 | }; | |
217 | ||
218 | static struct at91_pmc *__init at91_pmc_init(struct device_node *np, | |
219 | void __iomem *regbase, int virq, | |
220 | const struct at91_pmc_caps *caps) | |
221 | { | |
222 | struct at91_pmc *pmc; | |
223 | ||
224 | if (!regbase || !virq || !caps) | |
225 | return NULL; | |
226 | ||
227 | at91_pmc_base = regbase; | |
228 | ||
229 | pmc = kzalloc(sizeof(*pmc), GFP_KERNEL); | |
230 | if (!pmc) | |
231 | return NULL; | |
232 | ||
233 | spin_lock_init(&pmc->lock); | |
234 | pmc->regbase = regbase; | |
235 | pmc->virq = virq; | |
236 | pmc->caps = caps; | |
237 | ||
238 | pmc->irqdomain = irq_domain_add_linear(np, 32, &pmc_irq_ops, pmc); | |
239 | ||
240 | if (!pmc->irqdomain) | |
241 | goto out_free_pmc; | |
242 | ||
243 | pmc_write(pmc, AT91_PMC_IDR, 0xffffffff); | |
947f5b10 BB |
244 | if (request_irq(pmc->virq, pmc_irq_handler, |
245 | IRQF_SHARED | IRQF_COND_SUSPEND, "pmc", pmc)) | |
0ad6125b BB |
246 | goto out_remove_irqdomain; |
247 | ||
248 | return pmc; | |
249 | ||
250 | out_remove_irqdomain: | |
251 | irq_domain_remove(pmc->irqdomain); | |
252 | out_free_pmc: | |
253 | kfree(pmc); | |
254 | ||
255 | return NULL; | |
256 | } | |
257 | ||
7736c715 | 258 | static const struct of_device_id pmc_clk_ids[] __initconst = { |
80eded6c BB |
259 | /* Slow oscillator */ |
260 | { | |
261 | .compatible = "atmel,at91sam9260-clk-slow", | |
262 | .data = of_at91sam9260_clk_slow_setup, | |
263 | }, | |
38d34c31 | 264 | /* Main clock */ |
27cb1c20 BB |
265 | { |
266 | .compatible = "atmel,at91rm9200-clk-main-osc", | |
267 | .data = of_at91rm9200_clk_main_osc_setup, | |
268 | }, | |
269 | { | |
270 | .compatible = "atmel,at91sam9x5-clk-main-rc-osc", | |
271 | .data = of_at91sam9x5_clk_main_rc_osc_setup, | |
272 | }, | |
38d34c31 BB |
273 | { |
274 | .compatible = "atmel,at91rm9200-clk-main", | |
275 | .data = of_at91rm9200_clk_main_setup, | |
276 | }, | |
27cb1c20 BB |
277 | { |
278 | .compatible = "atmel,at91sam9x5-clk-main", | |
279 | .data = of_at91sam9x5_clk_main_setup, | |
280 | }, | |
1a748d2b BB |
281 | /* PLL clocks */ |
282 | { | |
283 | .compatible = "atmel,at91rm9200-clk-pll", | |
284 | .data = of_at91rm9200_clk_pll_setup, | |
285 | }, | |
286 | { | |
287 | .compatible = "atmel,at91sam9g45-clk-pll", | |
288 | .data = of_at91sam9g45_clk_pll_setup, | |
289 | }, | |
290 | { | |
291 | .compatible = "atmel,at91sam9g20-clk-pllb", | |
292 | .data = of_at91sam9g20_clk_pllb_setup, | |
293 | }, | |
294 | { | |
295 | .compatible = "atmel,sama5d3-clk-pll", | |
296 | .data = of_sama5d3_clk_pll_setup, | |
297 | }, | |
298 | { | |
299 | .compatible = "atmel,at91sam9x5-clk-plldiv", | |
300 | .data = of_at91sam9x5_clk_plldiv_setup, | |
301 | }, | |
e442d234 BB |
302 | /* Master clock */ |
303 | { | |
304 | .compatible = "atmel,at91rm9200-clk-master", | |
305 | .data = of_at91rm9200_clk_master_setup, | |
306 | }, | |
307 | { | |
308 | .compatible = "atmel,at91sam9x5-clk-master", | |
309 | .data = of_at91sam9x5_clk_master_setup, | |
310 | }, | |
5fba62ea BB |
311 | /* System clocks */ |
312 | { | |
313 | .compatible = "atmel,at91rm9200-clk-system", | |
314 | .data = of_at91rm9200_clk_sys_setup, | |
315 | }, | |
6114067e BB |
316 | /* Peripheral clocks */ |
317 | { | |
318 | .compatible = "atmel,at91rm9200-clk-peripheral", | |
319 | .data = of_at91rm9200_clk_periph_setup, | |
320 | }, | |
321 | { | |
322 | .compatible = "atmel,at91sam9x5-clk-peripheral", | |
323 | .data = of_at91sam9x5_clk_periph_setup, | |
324 | }, | |
1f22f8bb | 325 | /* Programmable clocks */ |
1f22f8bb BB |
326 | { |
327 | .compatible = "atmel,at91rm9200-clk-programmable", | |
328 | .data = of_at91rm9200_clk_prog_setup, | |
329 | }, | |
330 | { | |
331 | .compatible = "atmel,at91sam9g45-clk-programmable", | |
332 | .data = of_at91sam9g45_clk_prog_setup, | |
333 | }, | |
334 | { | |
335 | .compatible = "atmel,at91sam9x5-clk-programmable", | |
336 | .data = of_at91sam9x5_clk_prog_setup, | |
337 | }, | |
f090fb37 BB |
338 | /* UTMI clock */ |
339 | #if defined(CONFIG_HAVE_AT91_UTMI) | |
340 | { | |
341 | .compatible = "atmel,at91sam9x5-clk-utmi", | |
342 | .data = of_at91sam9x5_clk_utmi_setup, | |
343 | }, | |
c84a61d8 BB |
344 | #endif |
345 | /* USB clock */ | |
346 | #if defined(CONFIG_HAVE_AT91_USB_CLK) | |
347 | { | |
348 | .compatible = "atmel,at91rm9200-clk-usb", | |
349 | .data = of_at91rm9200_clk_usb_setup, | |
350 | }, | |
351 | { | |
352 | .compatible = "atmel,at91sam9x5-clk-usb", | |
353 | .data = of_at91sam9x5_clk_usb_setup, | |
354 | }, | |
355 | { | |
356 | .compatible = "atmel,at91sam9n12-clk-usb", | |
357 | .data = of_at91sam9n12_clk_usb_setup, | |
358 | }, | |
a9c0688f BB |
359 | #endif |
360 | /* SMD clock */ | |
361 | #if defined(CONFIG_HAVE_AT91_SMD) | |
362 | { | |
363 | .compatible = "atmel,at91sam9x5-clk-smd", | |
364 | .data = of_at91sam9x5_clk_smd_setup, | |
365 | }, | |
bcc5fd49 AB |
366 | #endif |
367 | #if defined(CONFIG_HAVE_AT91_H32MX) | |
368 | { | |
369 | .compatible = "atmel,sama5d4-clk-h32mx", | |
370 | .data = of_sama5d4_clk_h32mx_setup, | |
371 | }, | |
1f22f8bb | 372 | #endif |
0ad6125b BB |
373 | { /*sentinel*/ } |
374 | }; | |
375 | ||
376 | static void __init of_at91_pmc_setup(struct device_node *np, | |
377 | const struct at91_pmc_caps *caps) | |
378 | { | |
379 | struct at91_pmc *pmc; | |
380 | struct device_node *childnp; | |
381 | void (*clk_setup)(struct device_node *, struct at91_pmc *); | |
382 | const struct of_device_id *clk_id; | |
383 | void __iomem *regbase = of_iomap(np, 0); | |
384 | int virq; | |
385 | ||
386 | if (!regbase) | |
387 | return; | |
388 | ||
389 | virq = irq_of_parse_and_map(np, 0); | |
390 | if (!virq) | |
391 | return; | |
392 | ||
393 | pmc = at91_pmc_init(np, regbase, virq, caps); | |
394 | if (!pmc) | |
395 | return; | |
396 | for_each_child_of_node(np, childnp) { | |
397 | clk_id = of_match_node(pmc_clk_ids, childnp); | |
398 | if (!clk_id) | |
399 | continue; | |
400 | clk_setup = clk_id->data; | |
401 | clk_setup(childnp, pmc); | |
402 | } | |
403 | } | |
404 | ||
405 | static void __init of_at91rm9200_pmc_setup(struct device_node *np) | |
406 | { | |
407 | of_at91_pmc_setup(np, &at91rm9200_caps); | |
408 | } | |
409 | CLK_OF_DECLARE(at91rm9200_clk_pmc, "atmel,at91rm9200-pmc", | |
410 | of_at91rm9200_pmc_setup); | |
411 | ||
412 | static void __init of_at91sam9260_pmc_setup(struct device_node *np) | |
413 | { | |
414 | of_at91_pmc_setup(np, &at91sam9260_caps); | |
415 | } | |
416 | CLK_OF_DECLARE(at91sam9260_clk_pmc, "atmel,at91sam9260-pmc", | |
417 | of_at91sam9260_pmc_setup); | |
418 | ||
419 | static void __init of_at91sam9g45_pmc_setup(struct device_node *np) | |
420 | { | |
421 | of_at91_pmc_setup(np, &at91sam9g45_caps); | |
422 | } | |
423 | CLK_OF_DECLARE(at91sam9g45_clk_pmc, "atmel,at91sam9g45-pmc", | |
424 | of_at91sam9g45_pmc_setup); | |
425 | ||
426 | static void __init of_at91sam9n12_pmc_setup(struct device_node *np) | |
427 | { | |
428 | of_at91_pmc_setup(np, &at91sam9n12_caps); | |
429 | } | |
430 | CLK_OF_DECLARE(at91sam9n12_clk_pmc, "atmel,at91sam9n12-pmc", | |
431 | of_at91sam9n12_pmc_setup); | |
432 | ||
433 | static void __init of_at91sam9x5_pmc_setup(struct device_node *np) | |
434 | { | |
435 | of_at91_pmc_setup(np, &at91sam9x5_caps); | |
436 | } | |
437 | CLK_OF_DECLARE(at91sam9x5_clk_pmc, "atmel,at91sam9x5-pmc", | |
438 | of_at91sam9x5_pmc_setup); | |
439 | ||
440 | static void __init of_sama5d3_pmc_setup(struct device_node *np) | |
441 | { | |
442 | of_at91_pmc_setup(np, &sama5d3_caps); | |
443 | } | |
444 | CLK_OF_DECLARE(sama5d3_clk_pmc, "atmel,sama5d3-pmc", | |
445 | of_sama5d3_pmc_setup); |