Blame view

drivers/clocksource/timer-rockchip.c 6.78 KB
d2912cb15   Thomas Gleixner   treewide: Replace...
1
  // SPDX-License-Identifier: GPL-2.0-only
468b8c4cf   Daniel Lezcano   clockevents: rock...
2
3
4
5
  /*
   * Rockchip timer support
   *
   * Copyright (C) Daniel Lezcano <daniel.lezcano@linaro.org>
468b8c4cf   Daniel Lezcano   clockevents: rock...
6
7
8
9
10
   */
  #include <linux/clk.h>
  #include <linux/clockchips.h>
  #include <linux/init.h>
  #include <linux/interrupt.h>
5e0a39d0f   Alexander Kochetkov   clocksource/drive...
11
12
  #include <linux/sched_clock.h>
  #include <linux/slab.h>
468b8c4cf   Daniel Lezcano   clockevents: rock...
13
14
15
16
17
  #include <linux/of.h>
  #include <linux/of_address.h>
  #include <linux/of_irq.h>
  
  #define TIMER_NAME "rk_timer"
a0d2216ec   Caesar Wang   clocksource/drive...
18
19
  #define TIMER_LOAD_COUNT0	0x00
  #define TIMER_LOAD_COUNT1	0x04
5e0a39d0f   Alexander Kochetkov   clocksource/drive...
20
21
  #define TIMER_CURRENT_VALUE0	0x08
  #define TIMER_CURRENT_VALUE1	0x0C
be6af450b   Huang, Tao   clocksource/drive...
22
23
  #define TIMER_CONTROL_REG3288	0x10
  #define TIMER_CONTROL_REG3399	0x1c
a0d2216ec   Caesar Wang   clocksource/drive...
24
  #define TIMER_INT_STATUS	0x18
468b8c4cf   Daniel Lezcano   clockevents: rock...
25

a0d2216ec   Caesar Wang   clocksource/drive...
26
27
28
29
30
  #define TIMER_DISABLE		0x0
  #define TIMER_ENABLE		0x1
  #define TIMER_MODE_FREE_RUNNING			(0 << 1)
  #define TIMER_MODE_USER_DEFINED_COUNT		(1 << 1)
  #define TIMER_INT_UNMASK			(1 << 2)
468b8c4cf   Daniel Lezcano   clockevents: rock...
31

5e0a39d0f   Alexander Kochetkov   clocksource/drive...
32
  struct rk_timer {
468b8c4cf   Daniel Lezcano   clockevents: rock...
33
  	void __iomem *base;
be6af450b   Huang, Tao   clocksource/drive...
34
  	void __iomem *ctrl;
5e0a39d0f   Alexander Kochetkov   clocksource/drive...
35
36
  	struct clk *clk;
  	struct clk *pclk;
468b8c4cf   Daniel Lezcano   clockevents: rock...
37
  	u32 freq;
5e0a39d0f   Alexander Kochetkov   clocksource/drive...
38
  	int irq;
468b8c4cf   Daniel Lezcano   clockevents: rock...
39
  };
5e0a39d0f   Alexander Kochetkov   clocksource/drive...
40
41
42
43
  struct rk_clkevt {
  	struct clock_event_device ce;
  	struct rk_timer timer;
  };
468b8c4cf   Daniel Lezcano   clockevents: rock...
44

5e0a39d0f   Alexander Kochetkov   clocksource/drive...
45
46
  static struct rk_clkevt *rk_clkevt;
  static struct rk_timer *rk_clksrc;
468b8c4cf   Daniel Lezcano   clockevents: rock...
47

5e0a39d0f   Alexander Kochetkov   clocksource/drive...
48
  static inline struct rk_timer *rk_timer(struct clock_event_device *ce)
be6af450b   Huang, Tao   clocksource/drive...
49
  {
5e0a39d0f   Alexander Kochetkov   clocksource/drive...
50
  	return &container_of(ce, struct rk_clkevt, ce)->timer;
be6af450b   Huang, Tao   clocksource/drive...
51
  }
5e0a39d0f   Alexander Kochetkov   clocksource/drive...
52
  static inline void rk_timer_disable(struct rk_timer *timer)
468b8c4cf   Daniel Lezcano   clockevents: rock...
53
  {
5e0a39d0f   Alexander Kochetkov   clocksource/drive...
54
  	writel_relaxed(TIMER_DISABLE, timer->ctrl);
468b8c4cf   Daniel Lezcano   clockevents: rock...
55
  }
5e0a39d0f   Alexander Kochetkov   clocksource/drive...
56
  static inline void rk_timer_enable(struct rk_timer *timer, u32 flags)
468b8c4cf   Daniel Lezcano   clockevents: rock...
57
  {
5e0a39d0f   Alexander Kochetkov   clocksource/drive...
58
  	writel_relaxed(TIMER_ENABLE | flags, timer->ctrl);
468b8c4cf   Daniel Lezcano   clockevents: rock...
59
60
61
  }
  
  static void rk_timer_update_counter(unsigned long cycles,
5e0a39d0f   Alexander Kochetkov   clocksource/drive...
62
  				    struct rk_timer *timer)
468b8c4cf   Daniel Lezcano   clockevents: rock...
63
  {
5e0a39d0f   Alexander Kochetkov   clocksource/drive...
64
65
  	writel_relaxed(cycles, timer->base + TIMER_LOAD_COUNT0);
  	writel_relaxed(0, timer->base + TIMER_LOAD_COUNT1);
468b8c4cf   Daniel Lezcano   clockevents: rock...
66
  }
5e0a39d0f   Alexander Kochetkov   clocksource/drive...
67
  static void rk_timer_interrupt_clear(struct rk_timer *timer)
468b8c4cf   Daniel Lezcano   clockevents: rock...
68
  {
5e0a39d0f   Alexander Kochetkov   clocksource/drive...
69
  	writel_relaxed(1, timer->base + TIMER_INT_STATUS);
468b8c4cf   Daniel Lezcano   clockevents: rock...
70
71
72
73
74
  }
  
  static inline int rk_timer_set_next_event(unsigned long cycles,
  					  struct clock_event_device *ce)
  {
5e0a39d0f   Alexander Kochetkov   clocksource/drive...
75
76
77
78
79
80
  	struct rk_timer *timer = rk_timer(ce);
  
  	rk_timer_disable(timer);
  	rk_timer_update_counter(cycles, timer);
  	rk_timer_enable(timer, TIMER_MODE_USER_DEFINED_COUNT |
  			       TIMER_INT_UNMASK);
468b8c4cf   Daniel Lezcano   clockevents: rock...
81
82
  	return 0;
  }
99b3fa72e   Viresh Kumar   clockevents/drive...
83
  static int rk_timer_shutdown(struct clock_event_device *ce)
468b8c4cf   Daniel Lezcano   clockevents: rock...
84
  {
5e0a39d0f   Alexander Kochetkov   clocksource/drive...
85
86
87
  	struct rk_timer *timer = rk_timer(ce);
  
  	rk_timer_disable(timer);
99b3fa72e   Viresh Kumar   clockevents/drive...
88
89
90
91
92
  	return 0;
  }
  
  static int rk_timer_set_periodic(struct clock_event_device *ce)
  {
5e0a39d0f   Alexander Kochetkov   clocksource/drive...
93
94
95
96
97
  	struct rk_timer *timer = rk_timer(ce);
  
  	rk_timer_disable(timer);
  	rk_timer_update_counter(timer->freq / HZ - 1, timer);
  	rk_timer_enable(timer, TIMER_MODE_FREE_RUNNING | TIMER_INT_UNMASK);
99b3fa72e   Viresh Kumar   clockevents/drive...
98
  	return 0;
468b8c4cf   Daniel Lezcano   clockevents: rock...
99
100
101
102
103
  }
  
  static irqreturn_t rk_timer_interrupt(int irq, void *dev_id)
  {
  	struct clock_event_device *ce = dev_id;
5e0a39d0f   Alexander Kochetkov   clocksource/drive...
104
  	struct rk_timer *timer = rk_timer(ce);
468b8c4cf   Daniel Lezcano   clockevents: rock...
105

5e0a39d0f   Alexander Kochetkov   clocksource/drive...
106
  	rk_timer_interrupt_clear(timer);
468b8c4cf   Daniel Lezcano   clockevents: rock...
107

99b3fa72e   Viresh Kumar   clockevents/drive...
108
  	if (clockevent_state_oneshot(ce))
5e0a39d0f   Alexander Kochetkov   clocksource/drive...
109
  		rk_timer_disable(timer);
468b8c4cf   Daniel Lezcano   clockevents: rock...
110
111
112
113
114
  
  	ce->event_handler(ce);
  
  	return IRQ_HANDLED;
  }
5e0a39d0f   Alexander Kochetkov   clocksource/drive...
115
116
117
118
119
120
121
  static u64 notrace rk_timer_sched_read(void)
  {
  	return ~readl_relaxed(rk_clksrc->base + TIMER_CURRENT_VALUE0);
  }
  
  static int __init
  rk_timer_probe(struct rk_timer *timer, struct device_node *np)
468b8c4cf   Daniel Lezcano   clockevents: rock...
122
  {
468b8c4cf   Daniel Lezcano   clockevents: rock...
123
124
  	struct clk *timer_clk;
  	struct clk *pclk;
8bdd5a2e7   Daniel Lezcano   clocksource/drive...
125
  	int ret = -EINVAL, irq;
5e0a39d0f   Alexander Kochetkov   clocksource/drive...
126
  	u32 ctrl_reg = TIMER_CONTROL_REG3288;
468b8c4cf   Daniel Lezcano   clockevents: rock...
127

5e0a39d0f   Alexander Kochetkov   clocksource/drive...
128
129
  	timer->base = of_iomap(np, 0);
  	if (!timer->base) {
468b8c4cf   Daniel Lezcano   clockevents: rock...
130
131
  		pr_err("Failed to get base address for '%s'
  ", TIMER_NAME);
8bdd5a2e7   Daniel Lezcano   clocksource/drive...
132
  		return -ENXIO;
468b8c4cf   Daniel Lezcano   clockevents: rock...
133
  	}
5e0a39d0f   Alexander Kochetkov   clocksource/drive...
134
135
136
137
138
  
  	if (of_device_is_compatible(np, "rockchip,rk3399-timer"))
  		ctrl_reg = TIMER_CONTROL_REG3399;
  
  	timer->ctrl = timer->base + ctrl_reg;
468b8c4cf   Daniel Lezcano   clockevents: rock...
139
140
141
  
  	pclk = of_clk_get_by_name(np, "pclk");
  	if (IS_ERR(pclk)) {
8bdd5a2e7   Daniel Lezcano   clocksource/drive...
142
  		ret = PTR_ERR(pclk);
468b8c4cf   Daniel Lezcano   clockevents: rock...
143
144
  		pr_err("Failed to get pclk for '%s'
  ", TIMER_NAME);
522ed95c2   Shawn Lin   clocksource/drive...
145
  		goto out_unmap;
468b8c4cf   Daniel Lezcano   clockevents: rock...
146
  	}
8bdd5a2e7   Daniel Lezcano   clocksource/drive...
147
148
  	ret = clk_prepare_enable(pclk);
  	if (ret) {
468b8c4cf   Daniel Lezcano   clockevents: rock...
149
150
  		pr_err("Failed to enable pclk for '%s'
  ", TIMER_NAME);
522ed95c2   Shawn Lin   clocksource/drive...
151
  		goto out_unmap;
468b8c4cf   Daniel Lezcano   clockevents: rock...
152
  	}
5e0a39d0f   Alexander Kochetkov   clocksource/drive...
153
  	timer->pclk = pclk;
468b8c4cf   Daniel Lezcano   clockevents: rock...
154
155
156
  
  	timer_clk = of_clk_get_by_name(np, "timer");
  	if (IS_ERR(timer_clk)) {
8bdd5a2e7   Daniel Lezcano   clocksource/drive...
157
  		ret = PTR_ERR(timer_clk);
468b8c4cf   Daniel Lezcano   clockevents: rock...
158
159
  		pr_err("Failed to get timer clock for '%s'
  ", TIMER_NAME);
522ed95c2   Shawn Lin   clocksource/drive...
160
  		goto out_timer_clk;
468b8c4cf   Daniel Lezcano   clockevents: rock...
161
  	}
8bdd5a2e7   Daniel Lezcano   clocksource/drive...
162
163
  	ret = clk_prepare_enable(timer_clk);
  	if (ret) {
468b8c4cf   Daniel Lezcano   clockevents: rock...
164
165
  		pr_err("Failed to enable timer clock
  ");
522ed95c2   Shawn Lin   clocksource/drive...
166
  		goto out_timer_clk;
468b8c4cf   Daniel Lezcano   clockevents: rock...
167
  	}
5e0a39d0f   Alexander Kochetkov   clocksource/drive...
168
  	timer->clk = timer_clk;
468b8c4cf   Daniel Lezcano   clockevents: rock...
169

5e0a39d0f   Alexander Kochetkov   clocksource/drive...
170
  	timer->freq = clk_get_rate(timer_clk);
468b8c4cf   Daniel Lezcano   clockevents: rock...
171
172
  
  	irq = irq_of_parse_and_map(np, 0);
ccc42592d   Daniel Lezcano   clocksource/drive...
173
  	if (!irq) {
8bdd5a2e7   Daniel Lezcano   clocksource/drive...
174
  		ret = -EINVAL;
468b8c4cf   Daniel Lezcano   clockevents: rock...
175
176
  		pr_err("Failed to map interrupts for '%s'
  ", TIMER_NAME);
522ed95c2   Shawn Lin   clocksource/drive...
177
  		goto out_irq;
468b8c4cf   Daniel Lezcano   clockevents: rock...
178
  	}
5e0a39d0f   Alexander Kochetkov   clocksource/drive...
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
  	timer->irq = irq;
  
  	rk_timer_interrupt_clear(timer);
  	rk_timer_disable(timer);
  	return 0;
  
  out_irq:
  	clk_disable_unprepare(timer_clk);
  out_timer_clk:
  	clk_disable_unprepare(pclk);
  out_unmap:
  	iounmap(timer->base);
  
  	return ret;
  }
  
  static void __init rk_timer_cleanup(struct rk_timer *timer)
  {
  	clk_disable_unprepare(timer->clk);
  	clk_disable_unprepare(timer->pclk);
  	iounmap(timer->base);
  }
  
  static int __init rk_clkevt_init(struct device_node *np)
  {
  	struct clock_event_device *ce;
  	int ret = -EINVAL;
  
  	rk_clkevt = kzalloc(sizeof(struct rk_clkevt), GFP_KERNEL);
  	if (!rk_clkevt) {
  		ret = -ENOMEM;
  		goto out;
  	}
468b8c4cf   Daniel Lezcano   clockevents: rock...
212

5e0a39d0f   Alexander Kochetkov   clocksource/drive...
213
214
215
216
217
  	ret = rk_timer_probe(&rk_clkevt->timer, np);
  	if (ret)
  		goto out_probe;
  
  	ce = &rk_clkevt->ce;
468b8c4cf   Daniel Lezcano   clockevents: rock...
218
  	ce->name = TIMER_NAME;
716897d90   Huang, Tao   clocksource/drive...
219
220
  	ce->features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT |
  		       CLOCK_EVT_FEAT_DYNIRQ;
468b8c4cf   Daniel Lezcano   clockevents: rock...
221
  	ce->set_next_event = rk_timer_set_next_event;
99b3fa72e   Viresh Kumar   clockevents/drive...
222
223
  	ce->set_state_shutdown = rk_timer_shutdown;
  	ce->set_state_periodic = rk_timer_set_periodic;
5e0a39d0f   Alexander Kochetkov   clocksource/drive...
224
  	ce->irq = rk_clkevt->timer.irq;
716897d90   Huang, Tao   clocksource/drive...
225
  	ce->cpumask = cpu_possible_mask;
468b8c4cf   Daniel Lezcano   clockevents: rock...
226
  	ce->rating = 250;
5e0a39d0f   Alexander Kochetkov   clocksource/drive...
227
228
  	ret = request_irq(rk_clkevt->timer.irq, rk_timer_interrupt, IRQF_TIMER,
  			  TIMER_NAME, ce);
468b8c4cf   Daniel Lezcano   clockevents: rock...
229
  	if (ret) {
5e0a39d0f   Alexander Kochetkov   clocksource/drive...
230
231
232
  		pr_err("Failed to initialize '%s': %d
  ",
  			TIMER_NAME, ret);
522ed95c2   Shawn Lin   clocksource/drive...
233
  		goto out_irq;
468b8c4cf   Daniel Lezcano   clockevents: rock...
234
  	}
5e0a39d0f   Alexander Kochetkov   clocksource/drive...
235
236
  	clockevents_config_and_register(&rk_clkevt->ce,
  					rk_clkevt->timer.freq, 1, UINT_MAX);
8bdd5a2e7   Daniel Lezcano   clocksource/drive...
237
  	return 0;
522ed95c2   Shawn Lin   clocksource/drive...
238
239
  
  out_irq:
5e0a39d0f   Alexander Kochetkov   clocksource/drive...
240
241
242
243
244
245
  	rk_timer_cleanup(&rk_clkevt->timer);
  out_probe:
  	kfree(rk_clkevt);
  out:
  	/* Leave rk_clkevt not NULL to prevent future init */
  	rk_clkevt = ERR_PTR(ret);
8bdd5a2e7   Daniel Lezcano   clocksource/drive...
246
  	return ret;
468b8c4cf   Daniel Lezcano   clockevents: rock...
247
  }
a0d2216ec   Caesar Wang   clocksource/drive...
248

5e0a39d0f   Alexander Kochetkov   clocksource/drive...
249
  static int __init rk_clksrc_init(struct device_node *np)
be6af450b   Huang, Tao   clocksource/drive...
250
  {
5e0a39d0f   Alexander Kochetkov   clocksource/drive...
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
  	int ret = -EINVAL;
  
  	rk_clksrc = kzalloc(sizeof(struct rk_timer), GFP_KERNEL);
  	if (!rk_clksrc) {
  		ret = -ENOMEM;
  		goto out;
  	}
  
  	ret = rk_timer_probe(rk_clksrc, np);
  	if (ret)
  		goto out_probe;
  
  	rk_timer_update_counter(UINT_MAX, rk_clksrc);
  	rk_timer_enable(rk_clksrc, 0);
  
  	ret = clocksource_mmio_init(rk_clksrc->base + TIMER_CURRENT_VALUE0,
  		TIMER_NAME, rk_clksrc->freq, 250, 32,
  		clocksource_mmio_readl_down);
  	if (ret) {
2554828b1   Arvind Yadav   clocksource/drive...
270
271
  		pr_err("Failed to register clocksource
  ");
5e0a39d0f   Alexander Kochetkov   clocksource/drive...
272
273
274
275
276
277
278
279
280
281
282
283
284
285
  		goto out_clocksource;
  	}
  
  	sched_clock_register(rk_timer_sched_read, 32, rk_clksrc->freq);
  	return 0;
  
  out_clocksource:
  	rk_timer_cleanup(rk_clksrc);
  out_probe:
  	kfree(rk_clksrc);
  out:
  	/* Leave rk_clksrc not NULL to prevent future init */
  	rk_clksrc = ERR_PTR(ret);
  	return ret;
be6af450b   Huang, Tao   clocksource/drive...
286
  }
5e0a39d0f   Alexander Kochetkov   clocksource/drive...
287
  static int __init rk_timer_init(struct device_node *np)
be6af450b   Huang, Tao   clocksource/drive...
288
  {
5e0a39d0f   Alexander Kochetkov   clocksource/drive...
289
290
291
292
293
294
295
296
297
  	if (!rk_clkevt)
  		return rk_clkevt_init(np);
  
  	if (!rk_clksrc)
  		return rk_clksrc_init(np);
  
  	pr_err("Too many timer definitions for '%s'
  ", TIMER_NAME);
  	return -EINVAL;
be6af450b   Huang, Tao   clocksource/drive...
298
  }
172733959   Daniel Lezcano   clocksource/drive...
299
300
  TIMER_OF_DECLARE(rk3288_timer, "rockchip,rk3288-timer", rk_timer_init);
  TIMER_OF_DECLARE(rk3399_timer, "rockchip,rk3399-timer", rk_timer_init);