Commit | Line | Data |
---|---|---|
9952f691 | 1 | // SPDX-License-Identifier: GPL-2.0-only |
dc11bae7 DL |
2 | /* |
3 | * Copyright (c) 2017, Linaro Ltd. All rights reserved. | |
4 | * | |
5 | * Author: Daniel Lezcano <daniel.lezcano@linaro.org> | |
dc11bae7 DL |
6 | */ |
7 | #include <linux/clk.h> | |
8 | #include <linux/interrupt.h> | |
9 | #include <linux/of.h> | |
10 | #include <linux/of_address.h> | |
11 | #include <linux/of_irq.h> | |
12 | #include <linux/slab.h> | |
13 | ||
14 | #include "timer-of.h" | |
15 | ||
cf7f46b9 DL |
16 | /** |
17 | * timer_of_irq_exit - Release the interrupt | |
18 | * @of_irq: an of_timer_irq structure pointer | |
19 | * | |
20 | * Free the irq resource | |
21 | */ | |
5bbf4ad9 | 22 | static __init void timer_of_irq_exit(struct of_timer_irq *of_irq) |
dc11bae7 DL |
23 | { |
24 | struct timer_of *to = container_of(of_irq, struct timer_of, of_irq); | |
25 | ||
26 | struct clock_event_device *clkevt = &to->clkevt; | |
27 | ||
28 | of_irq->percpu ? free_percpu_irq(of_irq->irq, clkevt) : | |
29 | free_irq(of_irq->irq, clkevt); | |
30 | } | |
31 | ||
cf7f46b9 DL |
32 | /** |
33 | * timer_of_irq_init - Request the interrupt | |
34 | * @np: a device tree node pointer | |
35 | * @of_irq: an of_timer_irq structure pointer | |
36 | * | |
37 | * Get the interrupt number from the DT from its definition and | |
38 | * request it. The interrupt is gotten by falling back the following way: | |
39 | * | |
40 | * - Get interrupt number by name | |
41 | * - Get interrupt number by index | |
42 | * | |
43 | * When the interrupt is per CPU, 'request_percpu_irq()' is called, | |
44 | * otherwise 'request_irq()' is used. | |
45 | * | |
46 | * Returns 0 on success, < 0 otherwise | |
47 | */ | |
5bbf4ad9 DL |
48 | static __init int timer_of_irq_init(struct device_node *np, |
49 | struct of_timer_irq *of_irq) | |
dc11bae7 DL |
50 | { |
51 | int ret; | |
52 | struct timer_of *to = container_of(of_irq, struct timer_of, of_irq); | |
53 | struct clock_event_device *clkevt = &to->clkevt; | |
54 | ||
32f2fea6 SS |
55 | if (of_irq->name) { |
56 | of_irq->irq = ret = of_irq_get_byname(np, of_irq->name); | |
57 | if (ret < 0) { | |
58 | pr_err("Failed to get interrupt %s for %s\n", | |
59 | of_irq->name, np->full_name); | |
60 | return ret; | |
61 | } | |
62 | } else { | |
63 | of_irq->irq = irq_of_parse_and_map(np, of_irq->index); | |
64 | } | |
dc11bae7 | 65 | if (!of_irq->irq) { |
469869d1 | 66 | pr_err("Failed to map interrupt for %pOF\n", np); |
dc11bae7 DL |
67 | return -EINVAL; |
68 | } | |
69 | ||
70 | ret = of_irq->percpu ? | |
71 | request_percpu_irq(of_irq->irq, of_irq->handler, | |
72 | np->full_name, clkevt) : | |
73 | request_irq(of_irq->irq, of_irq->handler, | |
74 | of_irq->flags ? of_irq->flags : IRQF_TIMER, | |
75 | np->full_name, clkevt); | |
76 | if (ret) { | |
469869d1 | 77 | pr_err("Failed to request irq %d for %pOF\n", of_irq->irq, np); |
dc11bae7 DL |
78 | return ret; |
79 | } | |
80 | ||
81 | clkevt->irq = of_irq->irq; | |
82 | ||
83 | return 0; | |
84 | } | |
85 | ||
cf7f46b9 DL |
86 | /** |
87 | * timer_of_clk_exit - Release the clock resources | |
88 | * @of_clk: a of_timer_clk structure pointer | |
89 | * | |
90 | * Disables and releases the refcount on the clk | |
91 | */ | |
5bbf4ad9 | 92 | static __init void timer_of_clk_exit(struct of_timer_clk *of_clk) |
dc11bae7 DL |
93 | { |
94 | of_clk->rate = 0; | |
95 | clk_disable_unprepare(of_clk->clk); | |
96 | clk_put(of_clk->clk); | |
97 | } | |
98 | ||
cf7f46b9 DL |
99 | /** |
100 | * timer_of_clk_init - Initialize the clock resources | |
101 | * @np: a device tree node pointer | |
102 | * @of_clk: a of_timer_clk structure pointer | |
103 | * | |
104 | * Get the clock by name or by index, enable it and get the rate | |
105 | * | |
106 | * Returns 0 on success, < 0 otherwise | |
107 | */ | |
5bbf4ad9 DL |
108 | static __init int timer_of_clk_init(struct device_node *np, |
109 | struct of_timer_clk *of_clk) | |
dc11bae7 DL |
110 | { |
111 | int ret; | |
112 | ||
113 | of_clk->clk = of_clk->name ? of_clk_get_by_name(np, of_clk->name) : | |
114 | of_clk_get(np, of_clk->index); | |
115 | if (IS_ERR(of_clk->clk)) { | |
76371977 JH |
116 | ret = PTR_ERR(of_clk->clk); |
117 | if (ret != -EPROBE_DEFER) | |
118 | pr_err("Failed to get clock for %pOF\n", np); | |
119 | goto out; | |
dc11bae7 DL |
120 | } |
121 | ||
122 | ret = clk_prepare_enable(of_clk->clk); | |
123 | if (ret) { | |
469869d1 | 124 | pr_err("Failed for enable clock for %pOF\n", np); |
dc11bae7 DL |
125 | goto out_clk_put; |
126 | } | |
127 | ||
128 | of_clk->rate = clk_get_rate(of_clk->clk); | |
129 | if (!of_clk->rate) { | |
130 | ret = -EINVAL; | |
469869d1 | 131 | pr_err("Failed to get clock rate for %pOF\n", np); |
dc11bae7 DL |
132 | goto out_clk_disable; |
133 | } | |
134 | ||
135 | of_clk->period = DIV_ROUND_UP(of_clk->rate, HZ); | |
136 | out: | |
137 | return ret; | |
138 | ||
139 | out_clk_disable: | |
140 | clk_disable_unprepare(of_clk->clk); | |
141 | out_clk_put: | |
142 | clk_put(of_clk->clk); | |
143 | ||
144 | goto out; | |
145 | } | |
146 | ||
5bbf4ad9 | 147 | static __init void timer_of_base_exit(struct of_timer_base *of_base) |
dc11bae7 DL |
148 | { |
149 | iounmap(of_base->base); | |
150 | } | |
151 | ||
5bbf4ad9 DL |
152 | static __init int timer_of_base_init(struct device_node *np, |
153 | struct of_timer_base *of_base) | |
dc11bae7 | 154 | { |
9aea417a DL |
155 | of_base->base = of_base->name ? |
156 | of_io_request_and_map(np, of_base->index, of_base->name) : | |
157 | of_iomap(np, of_base->index); | |
9e80dbd8 | 158 | if (IS_ERR(of_base->base)) { |
9aea417a | 159 | pr_err("Failed to iomap (%s)\n", of_base->name); |
9e80dbd8 | 160 | return PTR_ERR(of_base->base); |
dc11bae7 DL |
161 | } |
162 | ||
163 | return 0; | |
164 | } | |
165 | ||
166 | int __init timer_of_init(struct device_node *np, struct timer_of *to) | |
167 | { | |
b7dcc4ea | 168 | int ret = -EINVAL; |
dc11bae7 DL |
169 | int flags = 0; |
170 | ||
171 | if (to->flags & TIMER_OF_BASE) { | |
5bbf4ad9 | 172 | ret = timer_of_base_init(np, &to->of_base); |
dc11bae7 DL |
173 | if (ret) |
174 | goto out_fail; | |
175 | flags |= TIMER_OF_BASE; | |
176 | } | |
177 | ||
178 | if (to->flags & TIMER_OF_CLOCK) { | |
5bbf4ad9 | 179 | ret = timer_of_clk_init(np, &to->of_clk); |
dc11bae7 DL |
180 | if (ret) |
181 | goto out_fail; | |
182 | flags |= TIMER_OF_CLOCK; | |
183 | } | |
184 | ||
185 | if (to->flags & TIMER_OF_IRQ) { | |
5bbf4ad9 | 186 | ret = timer_of_irq_init(np, &to->of_irq); |
dc11bae7 DL |
187 | if (ret) |
188 | goto out_fail; | |
189 | flags |= TIMER_OF_IRQ; | |
190 | } | |
191 | ||
192 | if (!to->clkevt.name) | |
193 | to->clkevt.name = np->name; | |
1c63c1c0 DL |
194 | |
195 | to->np = np; | |
196 | ||
dc11bae7 DL |
197 | return ret; |
198 | ||
199 | out_fail: | |
200 | if (flags & TIMER_OF_IRQ) | |
5bbf4ad9 | 201 | timer_of_irq_exit(&to->of_irq); |
dc11bae7 DL |
202 | |
203 | if (flags & TIMER_OF_CLOCK) | |
5bbf4ad9 | 204 | timer_of_clk_exit(&to->of_clk); |
dc11bae7 DL |
205 | |
206 | if (flags & TIMER_OF_BASE) | |
5bbf4ad9 | 207 | timer_of_base_exit(&to->of_base); |
b7dcc4ea | 208 | return ret; |
dc11bae7 | 209 | } |
f48729a9 | 210 | |
558de282 BG |
211 | /** |
212 | * timer_of_cleanup - release timer_of ressources | |
213 | * @to: timer_of structure | |
214 | * | |
215 | * Release the ressources that has been used in timer_of_init(). | |
216 | * This function should be called in init error cases | |
217 | */ | |
218 | void __init timer_of_cleanup(struct timer_of *to) | |
f48729a9 BG |
219 | { |
220 | if (to->flags & TIMER_OF_IRQ) | |
5bbf4ad9 | 221 | timer_of_irq_exit(&to->of_irq); |
f48729a9 BG |
222 | |
223 | if (to->flags & TIMER_OF_CLOCK) | |
5bbf4ad9 | 224 | timer_of_clk_exit(&to->of_clk); |
f48729a9 BG |
225 | |
226 | if (to->flags & TIMER_OF_BASE) | |
5bbf4ad9 | 227 | timer_of_base_exit(&to->of_base); |
f48729a9 | 228 | } |