1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * Copyright (C) 2015 ARM Limited |
4 | * |
5 | * Author: Vladimir Murzin <vladimir.murzin@arm.com> |
6 | */ |
7 | |
8 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt |
9 | |
10 | #include <linux/clk.h> |
11 | #include <linux/clockchips.h> |
12 | #include <linux/clocksource.h> |
13 | #include <linux/err.h> |
14 | #include <linux/interrupt.h> |
15 | #include <linux/io.h> |
16 | #include <linux/irq.h> |
17 | #include <linux/of_address.h> |
18 | #include <linux/of.h> |
19 | #include <linux/of_irq.h> |
20 | #include <linux/sched_clock.h> |
21 | #include <linux/slab.h> |
22 | |
23 | #define TIMER_CTRL 0x0 |
24 | #define TIMER_CTRL_ENABLE BIT(0) |
25 | #define TIMER_CTRL_IE BIT(3) |
26 | |
27 | #define TIMER_VALUE 0x4 |
28 | #define TIMER_RELOAD 0x8 |
29 | #define TIMER_INT 0xc |
30 | |
31 | struct clockevent_mps2 { |
32 | void __iomem *reg; |
33 | u32 clock_count_per_tick; |
34 | struct clock_event_device clkevt; |
35 | }; |
36 | |
37 | static void __iomem *sched_clock_base; |
38 | |
39 | static u64 notrace mps2_sched_read(void) |
40 | { |
41 | return ~readl_relaxed(sched_clock_base + TIMER_VALUE); |
42 | } |
43 | |
44 | static inline struct clockevent_mps2 *to_mps2_clkevt(struct clock_event_device *c) |
45 | { |
46 | return container_of(c, struct clockevent_mps2, clkevt); |
47 | } |
48 | |
49 | static void clockevent_mps2_writel(u32 val, struct clock_event_device *c, u32 offset) |
50 | { |
51 | writel_relaxed(val, to_mps2_clkevt(c)->reg + offset); |
52 | } |
53 | |
54 | static int mps2_timer_shutdown(struct clock_event_device *ce) |
55 | { |
56 | clockevent_mps2_writel(val: 0, c: ce, TIMER_RELOAD); |
57 | clockevent_mps2_writel(val: 0, c: ce, TIMER_CTRL); |
58 | |
59 | return 0; |
60 | } |
61 | |
62 | static int mps2_timer_set_next_event(unsigned long next, struct clock_event_device *ce) |
63 | { |
64 | clockevent_mps2_writel(val: next, c: ce, TIMER_VALUE); |
65 | clockevent_mps2_writel(TIMER_CTRL_IE | TIMER_CTRL_ENABLE, c: ce, TIMER_CTRL); |
66 | |
67 | return 0; |
68 | } |
69 | |
70 | static int mps2_timer_set_periodic(struct clock_event_device *ce) |
71 | { |
72 | u32 clock_count_per_tick = to_mps2_clkevt(c: ce)->clock_count_per_tick; |
73 | |
74 | clockevent_mps2_writel(val: clock_count_per_tick, c: ce, TIMER_RELOAD); |
75 | clockevent_mps2_writel(val: clock_count_per_tick, c: ce, TIMER_VALUE); |
76 | clockevent_mps2_writel(TIMER_CTRL_IE | TIMER_CTRL_ENABLE, c: ce, TIMER_CTRL); |
77 | |
78 | return 0; |
79 | } |
80 | |
81 | static irqreturn_t mps2_timer_interrupt(int irq, void *dev_id) |
82 | { |
83 | struct clockevent_mps2 *ce = dev_id; |
84 | u32 status = readl_relaxed(ce->reg + TIMER_INT); |
85 | |
86 | if (!status) { |
87 | pr_warn("spurious interrupt\n" ); |
88 | return IRQ_NONE; |
89 | } |
90 | |
91 | writel_relaxed(1, ce->reg + TIMER_INT); |
92 | |
93 | ce->clkevt.event_handler(&ce->clkevt); |
94 | |
95 | return IRQ_HANDLED; |
96 | } |
97 | |
98 | static int __init mps2_clockevent_init(struct device_node *np) |
99 | { |
100 | void __iomem *base; |
101 | struct clk *clk = NULL; |
102 | struct clockevent_mps2 *ce; |
103 | u32 rate; |
104 | int irq, ret; |
105 | const char *name = "mps2-clkevt" ; |
106 | |
107 | ret = of_property_read_u32(np, propname: "clock-frequency" , out_value: &rate); |
108 | if (ret) { |
109 | clk = of_clk_get(np, index: 0); |
110 | if (IS_ERR(ptr: clk)) { |
111 | ret = PTR_ERR(ptr: clk); |
112 | pr_err("failed to get clock for clockevent: %d\n" , ret); |
113 | goto out; |
114 | } |
115 | |
116 | ret = clk_prepare_enable(clk); |
117 | if (ret) { |
118 | pr_err("failed to enable clock for clockevent: %d\n" , ret); |
119 | goto out_clk_put; |
120 | } |
121 | |
122 | rate = clk_get_rate(clk); |
123 | } |
124 | |
125 | base = of_iomap(node: np, index: 0); |
126 | if (!base) { |
127 | ret = -EADDRNOTAVAIL; |
128 | pr_err("failed to map register for clockevent: %d\n" , ret); |
129 | goto out_clk_disable; |
130 | } |
131 | |
132 | irq = irq_of_parse_and_map(node: np, index: 0); |
133 | if (!irq) { |
134 | ret = -ENOENT; |
135 | pr_err("failed to get irq for clockevent: %d\n" , ret); |
136 | goto out_iounmap; |
137 | } |
138 | |
139 | ce = kzalloc(size: sizeof(*ce), GFP_KERNEL); |
140 | if (!ce) { |
141 | ret = -ENOMEM; |
142 | goto out_iounmap; |
143 | } |
144 | |
145 | ce->reg = base; |
146 | ce->clock_count_per_tick = DIV_ROUND_CLOSEST(rate, HZ); |
147 | ce->clkevt.irq = irq; |
148 | ce->clkevt.name = name; |
149 | ce->clkevt.rating = 200; |
150 | ce->clkevt.features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT; |
151 | ce->clkevt.cpumask = cpu_possible_mask; |
152 | ce->clkevt.set_state_shutdown = mps2_timer_shutdown; |
153 | ce->clkevt.set_state_periodic = mps2_timer_set_periodic; |
154 | ce->clkevt.set_state_oneshot = mps2_timer_shutdown; |
155 | ce->clkevt.set_next_event = mps2_timer_set_next_event; |
156 | |
157 | /* Ensure timer is disabled */ |
158 | writel_relaxed(0, base + TIMER_CTRL); |
159 | |
160 | ret = request_irq(irq, handler: mps2_timer_interrupt, IRQF_TIMER, name, dev: ce); |
161 | if (ret) { |
162 | pr_err("failed to request irq for clockevent: %d\n" , ret); |
163 | goto out_kfree; |
164 | } |
165 | |
166 | clockevents_config_and_register(dev: &ce->clkevt, freq: rate, min_delta: 0xf, max_delta: 0xffffffff); |
167 | |
168 | return 0; |
169 | |
170 | out_kfree: |
171 | kfree(objp: ce); |
172 | out_iounmap: |
173 | iounmap(addr: base); |
174 | out_clk_disable: |
175 | /* clk_{disable, unprepare, put}() can handle NULL as a parameter */ |
176 | clk_disable_unprepare(clk); |
177 | out_clk_put: |
178 | clk_put(clk); |
179 | out: |
180 | return ret; |
181 | } |
182 | |
183 | static int __init mps2_clocksource_init(struct device_node *np) |
184 | { |
185 | void __iomem *base; |
186 | struct clk *clk = NULL; |
187 | u32 rate; |
188 | int ret; |
189 | const char *name = "mps2-clksrc" ; |
190 | |
191 | ret = of_property_read_u32(np, propname: "clock-frequency" , out_value: &rate); |
192 | if (ret) { |
193 | clk = of_clk_get(np, index: 0); |
194 | if (IS_ERR(ptr: clk)) { |
195 | ret = PTR_ERR(ptr: clk); |
196 | pr_err("failed to get clock for clocksource: %d\n" , ret); |
197 | goto out; |
198 | } |
199 | |
200 | ret = clk_prepare_enable(clk); |
201 | if (ret) { |
202 | pr_err("failed to enable clock for clocksource: %d\n" , ret); |
203 | goto out_clk_put; |
204 | } |
205 | |
206 | rate = clk_get_rate(clk); |
207 | } |
208 | |
209 | base = of_iomap(node: np, index: 0); |
210 | if (!base) { |
211 | ret = -EADDRNOTAVAIL; |
212 | pr_err("failed to map register for clocksource: %d\n" , ret); |
213 | goto out_clk_disable; |
214 | } |
215 | |
216 | /* Ensure timer is disabled */ |
217 | writel_relaxed(0, base + TIMER_CTRL); |
218 | |
219 | /* ... and set it up as free-running clocksource */ |
220 | writel_relaxed(0xffffffff, base + TIMER_VALUE); |
221 | writel_relaxed(0xffffffff, base + TIMER_RELOAD); |
222 | |
223 | writel_relaxed(TIMER_CTRL_ENABLE, base + TIMER_CTRL); |
224 | |
225 | ret = clocksource_mmio_init(base + TIMER_VALUE, name, |
226 | rate, 200, 32, |
227 | clocksource_mmio_readl_down); |
228 | if (ret) { |
229 | pr_err("failed to init clocksource: %d\n" , ret); |
230 | goto out_iounmap; |
231 | } |
232 | |
233 | sched_clock_base = base; |
234 | sched_clock_register(read: mps2_sched_read, bits: 32, rate); |
235 | |
236 | return 0; |
237 | |
238 | out_iounmap: |
239 | iounmap(addr: base); |
240 | out_clk_disable: |
241 | /* clk_{disable, unprepare, put}() can handle NULL as a parameter */ |
242 | clk_disable_unprepare(clk); |
243 | out_clk_put: |
244 | clk_put(clk); |
245 | out: |
246 | return ret; |
247 | } |
248 | |
249 | static int __init mps2_timer_init(struct device_node *np) |
250 | { |
251 | static int has_clocksource, has_clockevent; |
252 | int ret; |
253 | |
254 | if (!has_clocksource) { |
255 | ret = mps2_clocksource_init(np); |
256 | if (!ret) { |
257 | has_clocksource = 1; |
258 | return 0; |
259 | } |
260 | } |
261 | |
262 | if (!has_clockevent) { |
263 | ret = mps2_clockevent_init(np); |
264 | if (!ret) { |
265 | has_clockevent = 1; |
266 | return 0; |
267 | } |
268 | } |
269 | |
270 | return 0; |
271 | } |
272 | |
273 | TIMER_OF_DECLARE(mps2_timer, "arm,mps2-timer" , mps2_timer_init); |
274 | |