Blame view

drivers/clocksource/timer-sun5i.c 9.41 KB
67905540e   Maxime Ripard   clocksource: Add ...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
  /*
   * Allwinner SoCs hstimer driver.
   *
   * Copyright (C) 2013 Maxime Ripard
   *
   * Maxime Ripard <maxime.ripard@free-electrons.com>
   *
   * This file is licensed under the terms of the GNU General Public
   * License version 2.  This program is licensed "as is" without any
   * warranty of any kind, whether express or implied.
   */
  
  #include <linux/clk.h>
  #include <linux/clockchips.h>
459fa246d   Stephen Rothwell   clocksource: Expl...
15
  #include <linux/clocksource.h>
67905540e   Maxime Ripard   clocksource: Add ...
16
17
18
19
  #include <linux/delay.h>
  #include <linux/interrupt.h>
  #include <linux/irq.h>
  #include <linux/irqreturn.h>
e50a00be5   Maxime Ripard   clocksource: sun5...
20
  #include <linux/reset.h>
4a59058f0   Maxime Ripard   clocksource/drive...
21
  #include <linux/slab.h>
67905540e   Maxime Ripard   clocksource: Add ...
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
  #include <linux/of.h>
  #include <linux/of_address.h>
  #include <linux/of_irq.h>
  
  #define TIMER_IRQ_EN_REG		0x00
  #define TIMER_IRQ_EN(val)			BIT(val)
  #define TIMER_IRQ_ST_REG		0x04
  #define TIMER_CTL_REG(val)		(0x20 * (val) + 0x10)
  #define TIMER_CTL_ENABLE			BIT(0)
  #define TIMER_CTL_RELOAD			BIT(1)
  #define TIMER_CTL_CLK_PRES(val)			(((val) & 0x7) << 4)
  #define TIMER_CTL_ONESHOT			BIT(7)
  #define TIMER_INTVAL_LO_REG(val)	(0x20 * (val) + 0x14)
  #define TIMER_INTVAL_HI_REG(val)	(0x20 * (val) + 0x18)
  #define TIMER_CNTVAL_LO_REG(val)	(0x20 * (val) + 0x1c)
  #define TIMER_CNTVAL_HI_REG(val)	(0x20 * (val) + 0x20)
  
  #define TIMER_SYNC_TICKS	3
4a59058f0   Maxime Ripard   clocksource/drive...
40
41
42
  struct sun5i_timer {
  	void __iomem		*base;
  	struct clk		*clk;
3071efa46   Maxime Ripard   clocksource/drive...
43
  	struct notifier_block	clk_rate_cb;
4a59058f0   Maxime Ripard   clocksource/drive...
44
45
  	u32			ticks_per_jiffy;
  };
3071efa46   Maxime Ripard   clocksource/drive...
46
47
  #define to_sun5i_timer(x) \
  	container_of(x, struct sun5i_timer, clk_rate_cb)
4a59058f0   Maxime Ripard   clocksource/drive...
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
  struct sun5i_timer_clksrc {
  	struct sun5i_timer	timer;
  	struct clocksource	clksrc;
  };
  
  #define to_sun5i_timer_clksrc(x) \
  	container_of(x, struct sun5i_timer_clksrc, clksrc)
  
  struct sun5i_timer_clkevt {
  	struct sun5i_timer		timer;
  	struct clock_event_device	clkevt;
  };
  
  #define to_sun5i_timer_clkevt(x) \
  	container_of(x, struct sun5i_timer_clkevt, clkevt)
67905540e   Maxime Ripard   clocksource: Add ...
63
64
65
66
67
68
69
  
  /*
   * When we disable a timer, we need to wait at least for 2 cycles of
   * the timer source clock. We will use for that the clocksource timer
   * that is already setup and runs at the same frequency than the other
   * timers, and we never will be disabled.
   */
4a59058f0   Maxime Ripard   clocksource/drive...
70
  static void sun5i_clkevt_sync(struct sun5i_timer_clkevt *ce)
67905540e   Maxime Ripard   clocksource: Add ...
71
  {
4a59058f0   Maxime Ripard   clocksource/drive...
72
  	u32 old = readl(ce->timer.base + TIMER_CNTVAL_LO_REG(1));
67905540e   Maxime Ripard   clocksource: Add ...
73

4a59058f0   Maxime Ripard   clocksource/drive...
74
  	while ((old - readl(ce->timer.base + TIMER_CNTVAL_LO_REG(1))) < TIMER_SYNC_TICKS)
67905540e   Maxime Ripard   clocksource: Add ...
75
76
  		cpu_relax();
  }
4a59058f0   Maxime Ripard   clocksource/drive...
77
  static void sun5i_clkevt_time_stop(struct sun5i_timer_clkevt *ce, u8 timer)
67905540e   Maxime Ripard   clocksource: Add ...
78
  {
4a59058f0   Maxime Ripard   clocksource/drive...
79
80
  	u32 val = readl(ce->timer.base + TIMER_CTL_REG(timer));
  	writel(val & ~TIMER_CTL_ENABLE, ce->timer.base + TIMER_CTL_REG(timer));
67905540e   Maxime Ripard   clocksource: Add ...
81

4a59058f0   Maxime Ripard   clocksource/drive...
82
  	sun5i_clkevt_sync(ce);
67905540e   Maxime Ripard   clocksource: Add ...
83
  }
4a59058f0   Maxime Ripard   clocksource/drive...
84
  static void sun5i_clkevt_time_setup(struct sun5i_timer_clkevt *ce, u8 timer, u32 delay)
67905540e   Maxime Ripard   clocksource: Add ...
85
  {
4a59058f0   Maxime Ripard   clocksource/drive...
86
  	writel(delay, ce->timer.base + TIMER_INTVAL_LO_REG(timer));
67905540e   Maxime Ripard   clocksource: Add ...
87
  }
4a59058f0   Maxime Ripard   clocksource/drive...
88
  static void sun5i_clkevt_time_start(struct sun5i_timer_clkevt *ce, u8 timer, bool periodic)
67905540e   Maxime Ripard   clocksource: Add ...
89
  {
4a59058f0   Maxime Ripard   clocksource/drive...
90
  	u32 val = readl(ce->timer.base + TIMER_CTL_REG(timer));
67905540e   Maxime Ripard   clocksource: Add ...
91
92
93
94
95
96
97
  
  	if (periodic)
  		val &= ~TIMER_CTL_ONESHOT;
  	else
  		val |= TIMER_CTL_ONESHOT;
  
  	writel(val | TIMER_CTL_ENABLE | TIMER_CTL_RELOAD,
4a59058f0   Maxime Ripard   clocksource/drive...
98
  	       ce->timer.base + TIMER_CTL_REG(timer));
67905540e   Maxime Ripard   clocksource: Add ...
99
  }
7486f5ad2   Viresh Kumar   clockevents/drive...
100
  static int sun5i_clkevt_shutdown(struct clock_event_device *clkevt)
67905540e   Maxime Ripard   clocksource: Add ...
101
  {
4a59058f0   Maxime Ripard   clocksource/drive...
102
  	struct sun5i_timer_clkevt *ce = to_sun5i_timer_clkevt(clkevt);
7486f5ad2   Viresh Kumar   clockevents/drive...
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
  	sun5i_clkevt_time_stop(ce, 0);
  	return 0;
  }
  
  static int sun5i_clkevt_set_oneshot(struct clock_event_device *clkevt)
  {
  	struct sun5i_timer_clkevt *ce = to_sun5i_timer_clkevt(clkevt);
  
  	sun5i_clkevt_time_stop(ce, 0);
  	sun5i_clkevt_time_start(ce, 0, false);
  	return 0;
  }
  
  static int sun5i_clkevt_set_periodic(struct clock_event_device *clkevt)
  {
  	struct sun5i_timer_clkevt *ce = to_sun5i_timer_clkevt(clkevt);
  
  	sun5i_clkevt_time_stop(ce, 0);
  	sun5i_clkevt_time_setup(ce, 0, ce->timer.ticks_per_jiffy);
  	sun5i_clkevt_time_start(ce, 0, true);
  	return 0;
67905540e   Maxime Ripard   clocksource: Add ...
124
125
126
  }
  
  static int sun5i_clkevt_next_event(unsigned long evt,
4a59058f0   Maxime Ripard   clocksource/drive...
127
  				   struct clock_event_device *clkevt)
67905540e   Maxime Ripard   clocksource: Add ...
128
  {
4a59058f0   Maxime Ripard   clocksource/drive...
129
130
131
132
133
  	struct sun5i_timer_clkevt *ce = to_sun5i_timer_clkevt(clkevt);
  
  	sun5i_clkevt_time_stop(ce, 0);
  	sun5i_clkevt_time_setup(ce, 0, evt - TIMER_SYNC_TICKS);
  	sun5i_clkevt_time_start(ce, 0, false);
67905540e   Maxime Ripard   clocksource: Add ...
134
135
136
  
  	return 0;
  }
67905540e   Maxime Ripard   clocksource: Add ...
137
138
  static irqreturn_t sun5i_timer_interrupt(int irq, void *dev_id)
  {
4a59058f0   Maxime Ripard   clocksource/drive...
139
  	struct sun5i_timer_clkevt *ce = (struct sun5i_timer_clkevt *)dev_id;
67905540e   Maxime Ripard   clocksource: Add ...
140

4a59058f0   Maxime Ripard   clocksource/drive...
141
142
  	writel(0x1, ce->timer.base + TIMER_IRQ_ST_REG);
  	ce->clkevt.event_handler(&ce->clkevt);
67905540e   Maxime Ripard   clocksource: Add ...
143
144
145
  
  	return IRQ_HANDLED;
  }
a5a1d1c29   Thomas Gleixner   clocksource: Use ...
146
  static u64 sun5i_clksrc_read(struct clocksource *clksrc)
593876838   Chen-Yu Tsai   Revert "clocksour...
147
148
149
150
151
  {
  	struct sun5i_timer_clksrc *cs = to_sun5i_timer_clksrc(clksrc);
  
  	return ~readl(cs->timer.base + TIMER_CNTVAL_LO_REG(1));
  }
3071efa46   Maxime Ripard   clocksource/drive...
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
  static int sun5i_rate_cb_clksrc(struct notifier_block *nb,
  				unsigned long event, void *data)
  {
  	struct clk_notifier_data *ndata = data;
  	struct sun5i_timer *timer = to_sun5i_timer(nb);
  	struct sun5i_timer_clksrc *cs = container_of(timer, struct sun5i_timer_clksrc, timer);
  
  	switch (event) {
  	case PRE_RATE_CHANGE:
  		clocksource_unregister(&cs->clksrc);
  		break;
  
  	case POST_RATE_CHANGE:
  		clocksource_register_hz(&cs->clksrc, ndata->new_rate);
  		break;
  
  	default:
  		break;
  	}
  
  	return NOTIFY_DONE;
  }
4a59058f0   Maxime Ripard   clocksource/drive...
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
  static int __init sun5i_setup_clocksource(struct device_node *node,
  					  void __iomem *base,
  					  struct clk *clk, int irq)
  {
  	struct sun5i_timer_clksrc *cs;
  	unsigned long rate;
  	int ret;
  
  	cs = kzalloc(sizeof(*cs), GFP_KERNEL);
  	if (!cs)
  		return -ENOMEM;
  
  	ret = clk_prepare_enable(clk);
  	if (ret) {
  		pr_err("Couldn't enable parent clock
  ");
  		goto err_free;
  	}
  
  	rate = clk_get_rate(clk);
e7e7e0d7b   Chen-Yu Tsai   clocksource/drive...
194
195
196
197
198
199
  	if (!rate) {
  		pr_err("Couldn't get parent clock rate
  ");
  		ret = -EINVAL;
  		goto err_disable_clk;
  	}
4a59058f0   Maxime Ripard   clocksource/drive...
200
201
202
  
  	cs->timer.base = base;
  	cs->timer.clk = clk;
3071efa46   Maxime Ripard   clocksource/drive...
203
204
205
206
207
208
209
210
211
  	cs->timer.clk_rate_cb.notifier_call = sun5i_rate_cb_clksrc;
  	cs->timer.clk_rate_cb.next = NULL;
  
  	ret = clk_notifier_register(clk, &cs->timer.clk_rate_cb);
  	if (ret) {
  		pr_err("Unable to register clock notifier.
  ");
  		goto err_disable_clk;
  	}
4a59058f0   Maxime Ripard   clocksource/drive...
212
213
214
215
  
  	writel(~0, base + TIMER_INTVAL_LO_REG(1));
  	writel(TIMER_CTL_ENABLE | TIMER_CTL_RELOAD,
  	       base + TIMER_CTL_REG(1));
593876838   Chen-Yu Tsai   Revert "clocksour...
216
217
218
219
220
221
222
  	cs->clksrc.name = node->name;
  	cs->clksrc.rating = 340;
  	cs->clksrc.read = sun5i_clksrc_read;
  	cs->clksrc.mask = CLOCKSOURCE_MASK(32);
  	cs->clksrc.flags = CLOCK_SOURCE_IS_CONTINUOUS;
  
  	ret = clocksource_register_hz(&cs->clksrc, rate);
4a59058f0   Maxime Ripard   clocksource/drive...
223
224
225
  	if (ret) {
  		pr_err("Couldn't register clock source.
  ");
3071efa46   Maxime Ripard   clocksource/drive...
226
  		goto err_remove_notifier;
4a59058f0   Maxime Ripard   clocksource/drive...
227
228
229
  	}
  
  	return 0;
3071efa46   Maxime Ripard   clocksource/drive...
230
231
  err_remove_notifier:
  	clk_notifier_unregister(clk, &cs->timer.clk_rate_cb);
4a59058f0   Maxime Ripard   clocksource/drive...
232
233
234
235
236
237
  err_disable_clk:
  	clk_disable_unprepare(clk);
  err_free:
  	kfree(cs);
  	return ret;
  }
3071efa46   Maxime Ripard   clocksource/drive...
238
239
240
241
242
243
244
245
246
247
248
249
250
251
  static int sun5i_rate_cb_clkevt(struct notifier_block *nb,
  				unsigned long event, void *data)
  {
  	struct clk_notifier_data *ndata = data;
  	struct sun5i_timer *timer = to_sun5i_timer(nb);
  	struct sun5i_timer_clkevt *ce = container_of(timer, struct sun5i_timer_clkevt, timer);
  
  	if (event == POST_RATE_CHANGE) {
  		clockevents_update_freq(&ce->clkevt, ndata->new_rate);
  		ce->timer.ticks_per_jiffy = DIV_ROUND_UP(ndata->new_rate, HZ);
  	}
  
  	return NOTIFY_DONE;
  }
4a59058f0   Maxime Ripard   clocksource/drive...
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
  static int __init sun5i_setup_clockevent(struct device_node *node, void __iomem *base,
  					 struct clk *clk, int irq)
  {
  	struct sun5i_timer_clkevt *ce;
  	unsigned long rate;
  	int ret;
  	u32 val;
  
  	ce = kzalloc(sizeof(*ce), GFP_KERNEL);
  	if (!ce)
  		return -ENOMEM;
  
  	ret = clk_prepare_enable(clk);
  	if (ret) {
  		pr_err("Couldn't enable parent clock
  ");
  		goto err_free;
  	}
  
  	rate = clk_get_rate(clk);
e7e7e0d7b   Chen-Yu Tsai   clocksource/drive...
272
273
274
275
276
277
  	if (!rate) {
  		pr_err("Couldn't get parent clock rate
  ");
  		ret = -EINVAL;
  		goto err_disable_clk;
  	}
4a59058f0   Maxime Ripard   clocksource/drive...
278
279
280
281
  
  	ce->timer.base = base;
  	ce->timer.ticks_per_jiffy = DIV_ROUND_UP(rate, HZ);
  	ce->timer.clk = clk;
3071efa46   Maxime Ripard   clocksource/drive...
282
283
284
285
286
287
288
289
290
  	ce->timer.clk_rate_cb.notifier_call = sun5i_rate_cb_clkevt;
  	ce->timer.clk_rate_cb.next = NULL;
  
  	ret = clk_notifier_register(clk, &ce->timer.clk_rate_cb);
  	if (ret) {
  		pr_err("Unable to register clock notifier.
  ");
  		goto err_disable_clk;
  	}
4a59058f0   Maxime Ripard   clocksource/drive...
291
292
293
294
  
  	ce->clkevt.name = node->name;
  	ce->clkevt.features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT;
  	ce->clkevt.set_next_event = sun5i_clkevt_next_event;
7486f5ad2   Viresh Kumar   clockevents/drive...
295
296
297
298
  	ce->clkevt.set_state_shutdown = sun5i_clkevt_shutdown;
  	ce->clkevt.set_state_periodic = sun5i_clkevt_set_periodic;
  	ce->clkevt.set_state_oneshot = sun5i_clkevt_set_oneshot;
  	ce->clkevt.tick_resume = sun5i_clkevt_shutdown;
4a59058f0   Maxime Ripard   clocksource/drive...
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
  	ce->clkevt.rating = 340;
  	ce->clkevt.irq = irq;
  	ce->clkevt.cpumask = cpu_possible_mask;
  
  	/* Enable timer0 interrupt */
  	val = readl(base + TIMER_IRQ_EN_REG);
  	writel(val | TIMER_IRQ_EN(0), base + TIMER_IRQ_EN_REG);
  
  	clockevents_config_and_register(&ce->clkevt, rate,
  					TIMER_SYNC_TICKS, 0xffffffff);
  
  	ret = request_irq(irq, sun5i_timer_interrupt, IRQF_TIMER | IRQF_IRQPOLL,
  			  "sun5i_timer0", ce);
  	if (ret) {
  		pr_err("Unable to register interrupt
  ");
3071efa46   Maxime Ripard   clocksource/drive...
315
  		goto err_remove_notifier;
4a59058f0   Maxime Ripard   clocksource/drive...
316
317
318
  	}
  
  	return 0;
3071efa46   Maxime Ripard   clocksource/drive...
319
320
  err_remove_notifier:
  	clk_notifier_unregister(clk, &ce->timer.clk_rate_cb);
4a59058f0   Maxime Ripard   clocksource/drive...
321
322
323
324
325
326
  err_disable_clk:
  	clk_disable_unprepare(clk);
  err_free:
  	kfree(ce);
  	return ret;
  }
e4d9f2ee4   Daniel Lezcano   clocksource/drive...
327
  static int __init sun5i_timer_init(struct device_node *node)
67905540e   Maxime Ripard   clocksource: Add ...
328
  {
e50a00be5   Maxime Ripard   clocksource: sun5...
329
  	struct reset_control *rstc;
4a59058f0   Maxime Ripard   clocksource/drive...
330
  	void __iomem *timer_base;
67905540e   Maxime Ripard   clocksource: Add ...
331
  	struct clk *clk;
e4d9f2ee4   Daniel Lezcano   clocksource/drive...
332
  	int irq, ret;
67905540e   Maxime Ripard   clocksource: Add ...
333

a45860d0b   Maxime Ripard   clocksource/drive...
334
  	timer_base = of_io_request_and_map(node, 0, of_node_full_name(node));
e4d9f2ee4   Daniel Lezcano   clocksource/drive...
335
  	if (IS_ERR(timer_base)) {
ac9ce6d1a   Rafał Miłecki   clocksource: Add ...
336
337
  		pr_err("Can't map registers
  ");
ed7158bae   Ingo Molnar   treewide/trivial:...
338
  		return PTR_ERR(timer_base);
e4d9f2ee4   Daniel Lezcano   clocksource/drive...
339
  	}
67905540e   Maxime Ripard   clocksource: Add ...
340
341
  
  	irq = irq_of_parse_and_map(node, 0);
e4d9f2ee4   Daniel Lezcano   clocksource/drive...
342
  	if (irq <= 0) {
ac9ce6d1a   Rafał Miłecki   clocksource: Add ...
343
344
  		pr_err("Can't parse IRQ
  ");
e4d9f2ee4   Daniel Lezcano   clocksource/drive...
345
346
  		return -EINVAL;
  	}
67905540e   Maxime Ripard   clocksource: Add ...
347
348
  
  	clk = of_clk_get(node, 0);
e4d9f2ee4   Daniel Lezcano   clocksource/drive...
349
  	if (IS_ERR(clk)) {
ac9ce6d1a   Rafał Miłecki   clocksource: Add ...
350
351
  		pr_err("Can't get timer clock
  ");
e4d9f2ee4   Daniel Lezcano   clocksource/drive...
352
353
  		return PTR_ERR(clk);
  	}
67905540e   Maxime Ripard   clocksource: Add ...
354

e50a00be5   Maxime Ripard   clocksource: sun5...
355
356
357
  	rstc = of_reset_control_get(node, NULL);
  	if (!IS_ERR(rstc))
  		reset_control_deassert(rstc);
e4d9f2ee4   Daniel Lezcano   clocksource/drive...
358
359
360
361
362
  	ret = sun5i_setup_clocksource(node, timer_base, clk, irq);
  	if (ret)
  		return ret;
  
  	return sun5i_setup_clockevent(node, timer_base, clk, irq);
67905540e   Maxime Ripard   clocksource: Add ...
363
  }
172733959   Daniel Lezcano   clocksource/drive...
364
  TIMER_OF_DECLARE(sun5i_a13, "allwinner,sun5i-a13-hstimer",
e4d9f2ee4   Daniel Lezcano   clocksource/drive...
365
  			   sun5i_timer_init);
172733959   Daniel Lezcano   clocksource/drive...
366
  TIMER_OF_DECLARE(sun7i_a20, "allwinner,sun7i-a20-hstimer",
e4d9f2ee4   Daniel Lezcano   clocksource/drive...
367
  			   sun5i_timer_init);