Blame view
drivers/clocksource/timer-rockchip.c
6.78 KB
d2912cb15 treewide: Replace... |
1 |
// SPDX-License-Identifier: GPL-2.0-only |
468b8c4cf clockevents: rock... |
2 3 4 5 |
/* * Rockchip timer support * * Copyright (C) Daniel Lezcano <daniel.lezcano@linaro.org> |
468b8c4cf clockevents: rock... |
6 7 8 9 10 |
*/ #include <linux/clk.h> #include <linux/clockchips.h> #include <linux/init.h> #include <linux/interrupt.h> |
5e0a39d0f clocksource/drive... |
11 12 |
#include <linux/sched_clock.h> #include <linux/slab.h> |
468b8c4cf 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 clocksource/drive... |
18 19 |
#define TIMER_LOAD_COUNT0 0x00 #define TIMER_LOAD_COUNT1 0x04 |
5e0a39d0f clocksource/drive... |
20 21 |
#define TIMER_CURRENT_VALUE0 0x08 #define TIMER_CURRENT_VALUE1 0x0C |
be6af450b clocksource/drive... |
22 23 |
#define TIMER_CONTROL_REG3288 0x10 #define TIMER_CONTROL_REG3399 0x1c |
a0d2216ec clocksource/drive... |
24 |
#define TIMER_INT_STATUS 0x18 |
468b8c4cf clockevents: rock... |
25 |
|
a0d2216ec 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 clockevents: rock... |
31 |
|
5e0a39d0f clocksource/drive... |
32 |
struct rk_timer { |
468b8c4cf clockevents: rock... |
33 |
void __iomem *base; |
be6af450b clocksource/drive... |
34 |
void __iomem *ctrl; |
5e0a39d0f clocksource/drive... |
35 36 |
struct clk *clk; struct clk *pclk; |
468b8c4cf clockevents: rock... |
37 |
u32 freq; |
5e0a39d0f clocksource/drive... |
38 |
int irq; |
468b8c4cf clockevents: rock... |
39 |
}; |
5e0a39d0f clocksource/drive... |
40 41 42 43 |
struct rk_clkevt { struct clock_event_device ce; struct rk_timer timer; }; |
468b8c4cf clockevents: rock... |
44 |
|
5e0a39d0f clocksource/drive... |
45 46 |
static struct rk_clkevt *rk_clkevt; static struct rk_timer *rk_clksrc; |
468b8c4cf clockevents: rock... |
47 |
|
5e0a39d0f clocksource/drive... |
48 |
static inline struct rk_timer *rk_timer(struct clock_event_device *ce) |
be6af450b clocksource/drive... |
49 |
{ |
5e0a39d0f clocksource/drive... |
50 |
return &container_of(ce, struct rk_clkevt, ce)->timer; |
be6af450b clocksource/drive... |
51 |
} |
5e0a39d0f clocksource/drive... |
52 |
static inline void rk_timer_disable(struct rk_timer *timer) |
468b8c4cf clockevents: rock... |
53 |
{ |
5e0a39d0f clocksource/drive... |
54 |
writel_relaxed(TIMER_DISABLE, timer->ctrl); |
468b8c4cf clockevents: rock... |
55 |
} |
5e0a39d0f clocksource/drive... |
56 |
static inline void rk_timer_enable(struct rk_timer *timer, u32 flags) |
468b8c4cf clockevents: rock... |
57 |
{ |
5e0a39d0f clocksource/drive... |
58 |
writel_relaxed(TIMER_ENABLE | flags, timer->ctrl); |
468b8c4cf clockevents: rock... |
59 60 61 |
} static void rk_timer_update_counter(unsigned long cycles, |
5e0a39d0f clocksource/drive... |
62 |
struct rk_timer *timer) |
468b8c4cf clockevents: rock... |
63 |
{ |
5e0a39d0f clocksource/drive... |
64 65 |
writel_relaxed(cycles, timer->base + TIMER_LOAD_COUNT0); writel_relaxed(0, timer->base + TIMER_LOAD_COUNT1); |
468b8c4cf clockevents: rock... |
66 |
} |
5e0a39d0f clocksource/drive... |
67 |
static void rk_timer_interrupt_clear(struct rk_timer *timer) |
468b8c4cf clockevents: rock... |
68 |
{ |
5e0a39d0f clocksource/drive... |
69 |
writel_relaxed(1, timer->base + TIMER_INT_STATUS); |
468b8c4cf clockevents: rock... |
70 71 72 73 74 |
} static inline int rk_timer_set_next_event(unsigned long cycles, struct clock_event_device *ce) { |
5e0a39d0f 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 clockevents: rock... |
81 82 |
return 0; } |
99b3fa72e clockevents/drive... |
83 |
static int rk_timer_shutdown(struct clock_event_device *ce) |
468b8c4cf clockevents: rock... |
84 |
{ |
5e0a39d0f clocksource/drive... |
85 86 87 |
struct rk_timer *timer = rk_timer(ce); rk_timer_disable(timer); |
99b3fa72e clockevents/drive... |
88 89 90 91 92 |
return 0; } static int rk_timer_set_periodic(struct clock_event_device *ce) { |
5e0a39d0f 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 clockevents/drive... |
98 |
return 0; |
468b8c4cf 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 clocksource/drive... |
104 |
struct rk_timer *timer = rk_timer(ce); |
468b8c4cf clockevents: rock... |
105 |
|
5e0a39d0f clocksource/drive... |
106 |
rk_timer_interrupt_clear(timer); |
468b8c4cf clockevents: rock... |
107 |
|
99b3fa72e clockevents/drive... |
108 |
if (clockevent_state_oneshot(ce)) |
5e0a39d0f clocksource/drive... |
109 |
rk_timer_disable(timer); |
468b8c4cf clockevents: rock... |
110 111 112 113 114 |
ce->event_handler(ce); return IRQ_HANDLED; } |
5e0a39d0f 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 clockevents: rock... |
122 |
{ |
468b8c4cf clockevents: rock... |
123 124 |
struct clk *timer_clk; struct clk *pclk; |
8bdd5a2e7 clocksource/drive... |
125 |
int ret = -EINVAL, irq; |
5e0a39d0f clocksource/drive... |
126 |
u32 ctrl_reg = TIMER_CONTROL_REG3288; |
468b8c4cf clockevents: rock... |
127 |
|
5e0a39d0f clocksource/drive... |
128 129 |
timer->base = of_iomap(np, 0); if (!timer->base) { |
468b8c4cf clockevents: rock... |
130 131 |
pr_err("Failed to get base address for '%s' ", TIMER_NAME); |
8bdd5a2e7 clocksource/drive... |
132 |
return -ENXIO; |
468b8c4cf clockevents: rock... |
133 |
} |
5e0a39d0f 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 clockevents: rock... |
139 140 141 |
pclk = of_clk_get_by_name(np, "pclk"); if (IS_ERR(pclk)) { |
8bdd5a2e7 clocksource/drive... |
142 |
ret = PTR_ERR(pclk); |
468b8c4cf clockevents: rock... |
143 144 |
pr_err("Failed to get pclk for '%s' ", TIMER_NAME); |
522ed95c2 clocksource/drive... |
145 |
goto out_unmap; |
468b8c4cf clockevents: rock... |
146 |
} |
8bdd5a2e7 clocksource/drive... |
147 148 |
ret = clk_prepare_enable(pclk); if (ret) { |
468b8c4cf clockevents: rock... |
149 150 |
pr_err("Failed to enable pclk for '%s' ", TIMER_NAME); |
522ed95c2 clocksource/drive... |
151 |
goto out_unmap; |
468b8c4cf clockevents: rock... |
152 |
} |
5e0a39d0f clocksource/drive... |
153 |
timer->pclk = pclk; |
468b8c4cf clockevents: rock... |
154 155 156 |
timer_clk = of_clk_get_by_name(np, "timer"); if (IS_ERR(timer_clk)) { |
8bdd5a2e7 clocksource/drive... |
157 |
ret = PTR_ERR(timer_clk); |
468b8c4cf clockevents: rock... |
158 159 |
pr_err("Failed to get timer clock for '%s' ", TIMER_NAME); |
522ed95c2 clocksource/drive... |
160 |
goto out_timer_clk; |
468b8c4cf clockevents: rock... |
161 |
} |
8bdd5a2e7 clocksource/drive... |
162 163 |
ret = clk_prepare_enable(timer_clk); if (ret) { |
468b8c4cf clockevents: rock... |
164 165 |
pr_err("Failed to enable timer clock "); |
522ed95c2 clocksource/drive... |
166 |
goto out_timer_clk; |
468b8c4cf clockevents: rock... |
167 |
} |
5e0a39d0f clocksource/drive... |
168 |
timer->clk = timer_clk; |
468b8c4cf clockevents: rock... |
169 |
|
5e0a39d0f clocksource/drive... |
170 |
timer->freq = clk_get_rate(timer_clk); |
468b8c4cf clockevents: rock... |
171 172 |
irq = irq_of_parse_and_map(np, 0); |
ccc42592d clocksource/drive... |
173 |
if (!irq) { |
8bdd5a2e7 clocksource/drive... |
174 |
ret = -EINVAL; |
468b8c4cf clockevents: rock... |
175 176 |
pr_err("Failed to map interrupts for '%s' ", TIMER_NAME); |
522ed95c2 clocksource/drive... |
177 |
goto out_irq; |
468b8c4cf clockevents: rock... |
178 |
} |
5e0a39d0f 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 clockevents: rock... |
212 |
|
5e0a39d0f 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 clockevents: rock... |
218 |
ce->name = TIMER_NAME; |
716897d90 clocksource/drive... |
219 220 |
ce->features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT | CLOCK_EVT_FEAT_DYNIRQ; |
468b8c4cf clockevents: rock... |
221 |
ce->set_next_event = rk_timer_set_next_event; |
99b3fa72e clockevents/drive... |
222 223 |
ce->set_state_shutdown = rk_timer_shutdown; ce->set_state_periodic = rk_timer_set_periodic; |
5e0a39d0f clocksource/drive... |
224 |
ce->irq = rk_clkevt->timer.irq; |
716897d90 clocksource/drive... |
225 |
ce->cpumask = cpu_possible_mask; |
468b8c4cf clockevents: rock... |
226 |
ce->rating = 250; |
5e0a39d0f clocksource/drive... |
227 228 |
ret = request_irq(rk_clkevt->timer.irq, rk_timer_interrupt, IRQF_TIMER, TIMER_NAME, ce); |
468b8c4cf clockevents: rock... |
229 |
if (ret) { |
5e0a39d0f clocksource/drive... |
230 231 232 |
pr_err("Failed to initialize '%s': %d ", TIMER_NAME, ret); |
522ed95c2 clocksource/drive... |
233 |
goto out_irq; |
468b8c4cf clockevents: rock... |
234 |
} |
5e0a39d0f clocksource/drive... |
235 236 |
clockevents_config_and_register(&rk_clkevt->ce, rk_clkevt->timer.freq, 1, UINT_MAX); |
8bdd5a2e7 clocksource/drive... |
237 |
return 0; |
522ed95c2 clocksource/drive... |
238 239 |
out_irq: |
5e0a39d0f 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 clocksource/drive... |
246 |
return ret; |
468b8c4cf clockevents: rock... |
247 |
} |
a0d2216ec clocksource/drive... |
248 |
|
5e0a39d0f clocksource/drive... |
249 |
static int __init rk_clksrc_init(struct device_node *np) |
be6af450b clocksource/drive... |
250 |
{ |
5e0a39d0f 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 clocksource/drive... |
270 271 |
pr_err("Failed to register clocksource "); |
5e0a39d0f 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 clocksource/drive... |
286 |
} |
5e0a39d0f clocksource/drive... |
287 |
static int __init rk_timer_init(struct device_node *np) |
be6af450b clocksource/drive... |
288 |
{ |
5e0a39d0f 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 clocksource/drive... |
298 |
} |
172733959 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); |