1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * Allwinner SoCs hstimer driver. |
4 | * |
5 | * Copyright (C) 2013 Maxime Ripard |
6 | * |
7 | * Maxime Ripard <maxime.ripard@free-electrons.com> |
8 | */ |
9 | |
10 | #include <linux/clk.h> |
11 | #include <linux/clockchips.h> |
12 | #include <linux/clocksource.h> |
13 | #include <linux/delay.h> |
14 | #include <linux/interrupt.h> |
15 | #include <linux/irq.h> |
16 | #include <linux/irqreturn.h> |
17 | #include <linux/reset.h> |
18 | #include <linux/slab.h> |
19 | #include <linux/platform_device.h> |
20 | |
21 | #define TIMER_IRQ_EN_REG 0x00 |
22 | #define TIMER_IRQ_EN(val) BIT(val) |
23 | #define TIMER_IRQ_ST_REG 0x04 |
24 | #define TIMER_CTL_REG(val) (0x20 * (val) + 0x10) |
25 | #define TIMER_CTL_ENABLE BIT(0) |
26 | #define TIMER_CTL_RELOAD BIT(1) |
27 | #define TIMER_CTL_CLK_PRES(val) (((val) & 0x7) << 4) |
28 | #define TIMER_CTL_ONESHOT BIT(7) |
29 | #define TIMER_INTVAL_LO_REG(val) (0x20 * (val) + 0x14) |
30 | #define TIMER_INTVAL_HI_REG(val) (0x20 * (val) + 0x18) |
31 | #define TIMER_CNTVAL_LO_REG(val) (0x20 * (val) + 0x1c) |
32 | #define TIMER_CNTVAL_HI_REG(val) (0x20 * (val) + 0x20) |
33 | |
34 | #define TIMER_SYNC_TICKS 3 |
35 | |
36 | struct sun5i_timer { |
37 | void __iomem *base; |
38 | struct clk *clk; |
39 | struct notifier_block clk_rate_cb; |
40 | u32 ticks_per_jiffy; |
41 | struct clocksource clksrc; |
42 | struct clock_event_device clkevt; |
43 | }; |
44 | |
45 | #define nb_to_sun5i_timer(x) \ |
46 | container_of(x, struct sun5i_timer, clk_rate_cb) |
47 | #define clksrc_to_sun5i_timer(x) \ |
48 | container_of(x, struct sun5i_timer, clksrc) |
49 | #define clkevt_to_sun5i_timer(x) \ |
50 | container_of(x, struct sun5i_timer, clkevt) |
51 | |
52 | /* |
53 | * When we disable a timer, we need to wait at least for 2 cycles of |
54 | * the timer source clock. We will use for that the clocksource timer |
55 | * that is already setup and runs at the same frequency than the other |
56 | * timers, and we never will be disabled. |
57 | */ |
58 | static void sun5i_clkevt_sync(struct sun5i_timer *ce) |
59 | { |
60 | u32 old = readl(addr: ce->base + TIMER_CNTVAL_LO_REG(1)); |
61 | |
62 | while ((old - readl(addr: ce->base + TIMER_CNTVAL_LO_REG(1))) < TIMER_SYNC_TICKS) |
63 | cpu_relax(); |
64 | } |
65 | |
66 | static void sun5i_clkevt_time_stop(struct sun5i_timer *ce, u8 timer) |
67 | { |
68 | u32 val = readl(addr: ce->base + TIMER_CTL_REG(timer)); |
69 | writel(val: val & ~TIMER_CTL_ENABLE, addr: ce->base + TIMER_CTL_REG(timer)); |
70 | |
71 | sun5i_clkevt_sync(ce); |
72 | } |
73 | |
74 | static void sun5i_clkevt_time_setup(struct sun5i_timer *ce, u8 timer, u32 delay) |
75 | { |
76 | writel(val: delay, addr: ce->base + TIMER_INTVAL_LO_REG(timer)); |
77 | } |
78 | |
79 | static void sun5i_clkevt_time_start(struct sun5i_timer *ce, u8 timer, bool periodic) |
80 | { |
81 | u32 val = readl(addr: ce->base + TIMER_CTL_REG(timer)); |
82 | |
83 | if (periodic) |
84 | val &= ~TIMER_CTL_ONESHOT; |
85 | else |
86 | val |= TIMER_CTL_ONESHOT; |
87 | |
88 | writel(val: val | TIMER_CTL_ENABLE | TIMER_CTL_RELOAD, |
89 | addr: ce->base + TIMER_CTL_REG(timer)); |
90 | } |
91 | |
92 | static int sun5i_clkevt_shutdown(struct clock_event_device *clkevt) |
93 | { |
94 | struct sun5i_timer *ce = clkevt_to_sun5i_timer(clkevt); |
95 | |
96 | sun5i_clkevt_time_stop(ce, timer: 0); |
97 | return 0; |
98 | } |
99 | |
100 | static int sun5i_clkevt_set_oneshot(struct clock_event_device *clkevt) |
101 | { |
102 | struct sun5i_timer *ce = clkevt_to_sun5i_timer(clkevt); |
103 | |
104 | sun5i_clkevt_time_stop(ce, timer: 0); |
105 | sun5i_clkevt_time_start(ce, timer: 0, periodic: false); |
106 | return 0; |
107 | } |
108 | |
109 | static int sun5i_clkevt_set_periodic(struct clock_event_device *clkevt) |
110 | { |
111 | struct sun5i_timer *ce = clkevt_to_sun5i_timer(clkevt); |
112 | |
113 | sun5i_clkevt_time_stop(ce, timer: 0); |
114 | sun5i_clkevt_time_setup(ce, timer: 0, delay: ce->ticks_per_jiffy); |
115 | sun5i_clkevt_time_start(ce, timer: 0, periodic: true); |
116 | return 0; |
117 | } |
118 | |
119 | static int sun5i_clkevt_next_event(unsigned long evt, |
120 | struct clock_event_device *clkevt) |
121 | { |
122 | struct sun5i_timer *ce = clkevt_to_sun5i_timer(clkevt); |
123 | |
124 | sun5i_clkevt_time_stop(ce, timer: 0); |
125 | sun5i_clkevt_time_setup(ce, timer: 0, delay: evt - TIMER_SYNC_TICKS); |
126 | sun5i_clkevt_time_start(ce, timer: 0, periodic: false); |
127 | |
128 | return 0; |
129 | } |
130 | |
131 | static irqreturn_t sun5i_timer_interrupt(int irq, void *dev_id) |
132 | { |
133 | struct sun5i_timer *ce = dev_id; |
134 | |
135 | writel(val: 0x1, addr: ce->base + TIMER_IRQ_ST_REG); |
136 | ce->clkevt.event_handler(&ce->clkevt); |
137 | |
138 | return IRQ_HANDLED; |
139 | } |
140 | |
141 | static u64 sun5i_clksrc_read(struct clocksource *clksrc) |
142 | { |
143 | struct sun5i_timer *cs = clksrc_to_sun5i_timer(clksrc); |
144 | |
145 | return ~readl(addr: cs->base + TIMER_CNTVAL_LO_REG(1)); |
146 | } |
147 | |
148 | static int sun5i_rate_cb(struct notifier_block *nb, |
149 | unsigned long event, void *data) |
150 | { |
151 | struct clk_notifier_data *ndata = data; |
152 | struct sun5i_timer *cs = nb_to_sun5i_timer(nb); |
153 | |
154 | switch (event) { |
155 | case PRE_RATE_CHANGE: |
156 | clocksource_unregister(&cs->clksrc); |
157 | break; |
158 | |
159 | case POST_RATE_CHANGE: |
160 | clocksource_register_hz(cs: &cs->clksrc, hz: ndata->new_rate); |
161 | clockevents_update_freq(ce: &cs->clkevt, freq: ndata->new_rate); |
162 | cs->ticks_per_jiffy = DIV_ROUND_UP(ndata->new_rate, HZ); |
163 | break; |
164 | |
165 | default: |
166 | break; |
167 | } |
168 | |
169 | return NOTIFY_DONE; |
170 | } |
171 | |
172 | static int sun5i_setup_clocksource(struct platform_device *pdev, |
173 | unsigned long rate) |
174 | { |
175 | struct sun5i_timer *cs = platform_get_drvdata(pdev); |
176 | void __iomem *base = cs->base; |
177 | int ret; |
178 | |
179 | writel(val: ~0, addr: base + TIMER_INTVAL_LO_REG(1)); |
180 | writel(TIMER_CTL_ENABLE | TIMER_CTL_RELOAD, |
181 | addr: base + TIMER_CTL_REG(1)); |
182 | |
183 | cs->clksrc.name = pdev->dev.of_node->name; |
184 | cs->clksrc.rating = 340; |
185 | cs->clksrc.read = sun5i_clksrc_read; |
186 | cs->clksrc.mask = CLOCKSOURCE_MASK(32); |
187 | cs->clksrc.flags = CLOCK_SOURCE_IS_CONTINUOUS; |
188 | |
189 | ret = clocksource_register_hz(cs: &cs->clksrc, hz: rate); |
190 | if (ret) { |
191 | dev_err(&pdev->dev, "Couldn't register clock source.\n" ); |
192 | return ret; |
193 | } |
194 | |
195 | return 0; |
196 | } |
197 | |
198 | static int sun5i_setup_clockevent(struct platform_device *pdev, |
199 | unsigned long rate, int irq) |
200 | { |
201 | struct device *dev = &pdev->dev; |
202 | struct sun5i_timer *ce = platform_get_drvdata(pdev); |
203 | void __iomem *base = ce->base; |
204 | int ret; |
205 | u32 val; |
206 | |
207 | ce->clkevt.name = dev->of_node->name; |
208 | ce->clkevt.features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT; |
209 | ce->clkevt.set_next_event = sun5i_clkevt_next_event; |
210 | ce->clkevt.set_state_shutdown = sun5i_clkevt_shutdown; |
211 | ce->clkevt.set_state_periodic = sun5i_clkevt_set_periodic; |
212 | ce->clkevt.set_state_oneshot = sun5i_clkevt_set_oneshot; |
213 | ce->clkevt.tick_resume = sun5i_clkevt_shutdown; |
214 | ce->clkevt.rating = 340; |
215 | ce->clkevt.irq = irq; |
216 | ce->clkevt.cpumask = cpu_possible_mask; |
217 | |
218 | /* Enable timer0 interrupt */ |
219 | val = readl(addr: base + TIMER_IRQ_EN_REG); |
220 | writel(val: val | TIMER_IRQ_EN(0), addr: base + TIMER_IRQ_EN_REG); |
221 | |
222 | clockevents_config_and_register(dev: &ce->clkevt, freq: rate, |
223 | TIMER_SYNC_TICKS, max_delta: 0xffffffff); |
224 | |
225 | ret = devm_request_irq(dev, irq, handler: sun5i_timer_interrupt, |
226 | IRQF_TIMER | IRQF_IRQPOLL, |
227 | devname: "sun5i_timer0" , dev_id: ce); |
228 | if (ret) { |
229 | dev_err(dev, "Unable to register interrupt\n" ); |
230 | return ret; |
231 | } |
232 | |
233 | return 0; |
234 | } |
235 | |
236 | static int sun5i_timer_probe(struct platform_device *pdev) |
237 | { |
238 | struct device *dev = &pdev->dev; |
239 | struct sun5i_timer *st; |
240 | struct reset_control *rstc; |
241 | void __iomem *timer_base; |
242 | struct clk *clk; |
243 | unsigned long rate; |
244 | int irq, ret; |
245 | |
246 | st = devm_kzalloc(dev, size: sizeof(*st), GFP_KERNEL); |
247 | if (!st) |
248 | return -ENOMEM; |
249 | |
250 | platform_set_drvdata(pdev, data: st); |
251 | |
252 | timer_base = devm_platform_ioremap_resource(pdev, index: 0); |
253 | if (IS_ERR(ptr: timer_base)) { |
254 | dev_err(dev, "Can't map registers\n" ); |
255 | return PTR_ERR(ptr: timer_base); |
256 | } |
257 | |
258 | irq = platform_get_irq(pdev, 0); |
259 | if (irq < 0) |
260 | return irq; |
261 | |
262 | clk = devm_clk_get_enabled(dev, NULL); |
263 | if (IS_ERR(ptr: clk)) { |
264 | dev_err(dev, "Can't get timer clock\n" ); |
265 | return PTR_ERR(ptr: clk); |
266 | } |
267 | |
268 | rate = clk_get_rate(clk); |
269 | if (!rate) { |
270 | dev_err(dev, "Couldn't get parent clock rate\n" ); |
271 | return -EINVAL; |
272 | } |
273 | |
274 | st->base = timer_base; |
275 | st->ticks_per_jiffy = DIV_ROUND_UP(rate, HZ); |
276 | st->clk = clk; |
277 | st->clk_rate_cb.notifier_call = sun5i_rate_cb; |
278 | st->clk_rate_cb.next = NULL; |
279 | |
280 | ret = devm_clk_notifier_register(dev, clk, nb: &st->clk_rate_cb); |
281 | if (ret) { |
282 | dev_err(dev, "Unable to register clock notifier.\n" ); |
283 | return ret; |
284 | } |
285 | |
286 | rstc = devm_reset_control_get_optional_exclusive(dev, NULL); |
287 | if (rstc) |
288 | reset_control_deassert(rstc); |
289 | |
290 | ret = sun5i_setup_clocksource(pdev, rate); |
291 | if (ret) |
292 | return ret; |
293 | |
294 | ret = sun5i_setup_clockevent(pdev, rate, irq); |
295 | if (ret) |
296 | goto err_unreg_clocksource; |
297 | |
298 | return 0; |
299 | |
300 | err_unreg_clocksource: |
301 | clocksource_unregister(&st->clksrc); |
302 | return ret; |
303 | } |
304 | |
305 | static void sun5i_timer_remove(struct platform_device *pdev) |
306 | { |
307 | struct sun5i_timer *st = platform_get_drvdata(pdev); |
308 | |
309 | clocksource_unregister(&st->clksrc); |
310 | } |
311 | |
312 | static const struct of_device_id sun5i_timer_of_match[] = { |
313 | { .compatible = "allwinner,sun5i-a13-hstimer" }, |
314 | { .compatible = "allwinner,sun7i-a20-hstimer" }, |
315 | {}, |
316 | }; |
317 | MODULE_DEVICE_TABLE(of, sun5i_timer_of_match); |
318 | |
319 | static struct platform_driver sun5i_timer_driver = { |
320 | .probe = sun5i_timer_probe, |
321 | .remove_new = sun5i_timer_remove, |
322 | .driver = { |
323 | .name = "sun5i-timer" , |
324 | .of_match_table = sun5i_timer_of_match, |
325 | .suppress_bind_attrs = true, |
326 | }, |
327 | }; |
328 | module_platform_driver(sun5i_timer_driver); |
329 | |