diff options
Diffstat (limited to 'arch/arc/kernel/time.c')
-rw-r--r-- | arch/arc/kernel/time.c | 238 |
1 files changed, 163 insertions, 75 deletions
diff --git a/arch/arc/kernel/time.c b/arch/arc/kernel/time.c index 7d9a736fc7e5..4549ab255dd1 100644 --- a/arch/arc/kernel/time.c +++ b/arch/arc/kernel/time.c | |||
@@ -29,21 +29,16 @@ | |||
29 | * which however is currently broken | 29 | * which however is currently broken |
30 | */ | 30 | */ |
31 | 31 | ||
32 | #include <linux/spinlock.h> | ||
33 | #include <linux/interrupt.h> | 32 | #include <linux/interrupt.h> |
34 | #include <linux/module.h> | 33 | #include <linux/clk.h> |
35 | #include <linux/sched.h> | 34 | #include <linux/clk-provider.h> |
36 | #include <linux/kernel.h> | ||
37 | #include <linux/time.h> | ||
38 | #include <linux/init.h> | ||
39 | #include <linux/timex.h> | ||
40 | #include <linux/profile.h> | ||
41 | #include <linux/clocksource.h> | 35 | #include <linux/clocksource.h> |
42 | #include <linux/clockchips.h> | 36 | #include <linux/clockchips.h> |
37 | #include <linux/cpu.h> | ||
38 | #include <linux/of.h> | ||
39 | #include <linux/of_irq.h> | ||
43 | #include <asm/irq.h> | 40 | #include <asm/irq.h> |
44 | #include <asm/arcregs.h> | 41 | #include <asm/arcregs.h> |
45 | #include <asm/clk.h> | ||
46 | #include <asm/mach_desc.h> | ||
47 | 42 | ||
48 | #include <asm/mcip.h> | 43 | #include <asm/mcip.h> |
49 | 44 | ||
@@ -60,16 +55,35 @@ | |||
60 | 55 | ||
61 | #define ARC_TIMER_MAX 0xFFFFFFFF | 56 | #define ARC_TIMER_MAX 0xFFFFFFFF |
62 | 57 | ||
63 | /********** Clock Source Device *********/ | 58 | static unsigned long arc_timer_freq; |
64 | |||
65 | #ifdef CONFIG_ARC_HAS_GFRC | ||
66 | 59 | ||
67 | static int arc_counter_setup(void) | 60 | static int noinline arc_get_timer_clk(struct device_node *node) |
68 | { | 61 | { |
69 | return 1; | 62 | struct clk *clk; |
63 | int ret; | ||
64 | |||
65 | clk = of_clk_get(node, 0); | ||
66 | if (IS_ERR(clk)) { | ||
67 | pr_err("timer missing clk"); | ||
68 | return PTR_ERR(clk); | ||
69 | } | ||
70 | |||
71 | ret = clk_prepare_enable(clk); | ||
72 | if (ret) { | ||
73 | pr_err("Couldn't enable parent clk\n"); | ||
74 | return ret; | ||
75 | } | ||
76 | |||
77 | arc_timer_freq = clk_get_rate(clk); | ||
78 | |||
79 | return 0; | ||
70 | } | 80 | } |
71 | 81 | ||
72 | static cycle_t arc_counter_read(struct clocksource *cs) | 82 | /********** Clock Source Device *********/ |
83 | |||
84 | #ifdef CONFIG_ARC_HAS_GFRC | ||
85 | |||
86 | static cycle_t arc_read_gfrc(struct clocksource *cs) | ||
73 | { | 87 | { |
74 | unsigned long flags; | 88 | unsigned long flags; |
75 | union { | 89 | union { |
@@ -94,15 +108,31 @@ static cycle_t arc_counter_read(struct clocksource *cs) | |||
94 | return stamp.full; | 108 | return stamp.full; |
95 | } | 109 | } |
96 | 110 | ||
97 | static struct clocksource arc_counter = { | 111 | static struct clocksource arc_counter_gfrc = { |
98 | .name = "ARConnect GFRC", | 112 | .name = "ARConnect GFRC", |
99 | .rating = 400, | 113 | .rating = 400, |
100 | .read = arc_counter_read, | 114 | .read = arc_read_gfrc, |
101 | .mask = CLOCKSOURCE_MASK(64), | 115 | .mask = CLOCKSOURCE_MASK(64), |
102 | .flags = CLOCK_SOURCE_IS_CONTINUOUS, | 116 | .flags = CLOCK_SOURCE_IS_CONTINUOUS, |
103 | }; | 117 | }; |
104 | 118 | ||
105 | #else | 119 | static void __init arc_cs_setup_gfrc(struct device_node *node) |
120 | { | ||
121 | int exists = cpuinfo_arc700[0].extn.gfrc; | ||
122 | int ret; | ||
123 | |||
124 | if (WARN(!exists, "Global-64-bit-Ctr clocksource not detected")) | ||
125 | return; | ||
126 | |||
127 | ret = arc_get_timer_clk(node); | ||
128 | if (ret) | ||
129 | return; | ||
130 | |||
131 | clocksource_register_hz(&arc_counter_gfrc, arc_timer_freq); | ||
132 | } | ||
133 | CLOCKSOURCE_OF_DECLARE(arc_gfrc, "snps,archs-timer-gfrc", arc_cs_setup_gfrc); | ||
134 | |||
135 | #endif | ||
106 | 136 | ||
107 | #ifdef CONFIG_ARC_HAS_RTC | 137 | #ifdef CONFIG_ARC_HAS_RTC |
108 | 138 | ||
@@ -110,15 +140,7 @@ static struct clocksource arc_counter = { | |||
110 | #define AUX_RTC_LOW 0x104 | 140 | #define AUX_RTC_LOW 0x104 |
111 | #define AUX_RTC_HIGH 0x105 | 141 | #define AUX_RTC_HIGH 0x105 |
112 | 142 | ||
113 | int arc_counter_setup(void) | 143 | static cycle_t arc_read_rtc(struct clocksource *cs) |
114 | { | ||
115 | write_aux_reg(AUX_RTC_CTRL, 1); | ||
116 | |||
117 | /* Not usable in SMP */ | ||
118 | return !IS_ENABLED(CONFIG_SMP); | ||
119 | } | ||
120 | |||
121 | static cycle_t arc_counter_read(struct clocksource *cs) | ||
122 | { | 144 | { |
123 | unsigned long status; | 145 | unsigned long status; |
124 | union { | 146 | union { |
@@ -142,47 +164,78 @@ static cycle_t arc_counter_read(struct clocksource *cs) | |||
142 | return stamp.full; | 164 | return stamp.full; |
143 | } | 165 | } |
144 | 166 | ||
145 | static struct clocksource arc_counter = { | 167 | static struct clocksource arc_counter_rtc = { |
146 | .name = "ARCv2 RTC", | 168 | .name = "ARCv2 RTC", |
147 | .rating = 350, | 169 | .rating = 350, |
148 | .read = arc_counter_read, | 170 | .read = arc_read_rtc, |
149 | .mask = CLOCKSOURCE_MASK(64), | 171 | .mask = CLOCKSOURCE_MASK(64), |
150 | .flags = CLOCK_SOURCE_IS_CONTINUOUS, | 172 | .flags = CLOCK_SOURCE_IS_CONTINUOUS, |
151 | }; | 173 | }; |
152 | 174 | ||
153 | #else /* !CONFIG_ARC_HAS_RTC */ | 175 | static void __init arc_cs_setup_rtc(struct device_node *node) |
154 | |||
155 | /* | ||
156 | * set 32bit TIMER1 to keep counting monotonically and wraparound | ||
157 | */ | ||
158 | int arc_counter_setup(void) | ||
159 | { | 176 | { |
160 | write_aux_reg(ARC_REG_TIMER1_LIMIT, ARC_TIMER_MAX); | 177 | int exists = cpuinfo_arc700[smp_processor_id()].extn.rtc; |
161 | write_aux_reg(ARC_REG_TIMER1_CNT, 0); | 178 | int ret; |
162 | write_aux_reg(ARC_REG_TIMER1_CTRL, TIMER_CTRL_NH); | 179 | |
180 | if (WARN(!exists, "Local-64-bit-Ctr clocksource not detected")) | ||
181 | return; | ||
182 | |||
183 | /* Local to CPU hence not usable in SMP */ | ||
184 | if (WARN(IS_ENABLED(CONFIG_SMP), "Local-64-bit-Ctr not usable in SMP")) | ||
185 | return; | ||
186 | |||
187 | ret = arc_get_timer_clk(node); | ||
188 | if (ret) | ||
189 | return; | ||
190 | |||
191 | write_aux_reg(AUX_RTC_CTRL, 1); | ||
163 | 192 | ||
164 | /* Not usable in SMP */ | 193 | clocksource_register_hz(&arc_counter_rtc, arc_timer_freq); |
165 | return !IS_ENABLED(CONFIG_SMP); | ||
166 | } | 194 | } |
195 | CLOCKSOURCE_OF_DECLARE(arc_rtc, "snps,archs-timer-rtc", arc_cs_setup_rtc); | ||
167 | 196 | ||
168 | static cycle_t arc_counter_read(struct clocksource *cs) | 197 | #endif |
198 | |||
199 | /* | ||
200 | * 32bit TIMER1 to keep counting monotonically and wraparound | ||
201 | */ | ||
202 | |||
203 | static cycle_t arc_read_timer1(struct clocksource *cs) | ||
169 | { | 204 | { |
170 | return (cycle_t) read_aux_reg(ARC_REG_TIMER1_CNT); | 205 | return (cycle_t) read_aux_reg(ARC_REG_TIMER1_CNT); |
171 | } | 206 | } |
172 | 207 | ||
173 | static struct clocksource arc_counter = { | 208 | static struct clocksource arc_counter_timer1 = { |
174 | .name = "ARC Timer1", | 209 | .name = "ARC Timer1", |
175 | .rating = 300, | 210 | .rating = 300, |
176 | .read = arc_counter_read, | 211 | .read = arc_read_timer1, |
177 | .mask = CLOCKSOURCE_MASK(32), | 212 | .mask = CLOCKSOURCE_MASK(32), |
178 | .flags = CLOCK_SOURCE_IS_CONTINUOUS, | 213 | .flags = CLOCK_SOURCE_IS_CONTINUOUS, |
179 | }; | 214 | }; |
180 | 215 | ||
181 | #endif | 216 | static void __init arc_cs_setup_timer1(struct device_node *node) |
182 | #endif | 217 | { |
218 | int ret; | ||
219 | |||
220 | /* Local to CPU hence not usable in SMP */ | ||
221 | if (IS_ENABLED(CONFIG_SMP)) | ||
222 | return; | ||
223 | |||
224 | ret = arc_get_timer_clk(node); | ||
225 | if (ret) | ||
226 | return; | ||
227 | |||
228 | write_aux_reg(ARC_REG_TIMER1_LIMIT, ARC_TIMER_MAX); | ||
229 | write_aux_reg(ARC_REG_TIMER1_CNT, 0); | ||
230 | write_aux_reg(ARC_REG_TIMER1_CTRL, TIMER_CTRL_NH); | ||
231 | |||
232 | clocksource_register_hz(&arc_counter_timer1, arc_timer_freq); | ||
233 | } | ||
183 | 234 | ||
184 | /********** Clock Event Device *********/ | 235 | /********** Clock Event Device *********/ |
185 | 236 | ||
237 | static int arc_timer_irq; | ||
238 | |||
186 | /* | 239 | /* |
187 | * Arm the timer to interrupt after @cycles | 240 | * Arm the timer to interrupt after @cycles |
188 | * The distinction for oneshot/periodic is done in arc_event_timer_ack() below | 241 | * The distinction for oneshot/periodic is done in arc_event_timer_ack() below |
@@ -209,7 +262,7 @@ static int arc_clkevent_set_periodic(struct clock_event_device *dev) | |||
209 | * At X Hz, 1 sec = 1000ms -> X cycles; | 262 | * At X Hz, 1 sec = 1000ms -> X cycles; |
210 | * 10ms -> X / 100 cycles | 263 | * 10ms -> X / 100 cycles |
211 | */ | 264 | */ |
212 | arc_timer_event_setup(arc_get_core_freq() / HZ); | 265 | arc_timer_event_setup(arc_timer_freq / HZ); |
213 | return 0; | 266 | return 0; |
214 | } | 267 | } |
215 | 268 | ||
@@ -218,7 +271,6 @@ static DEFINE_PER_CPU(struct clock_event_device, arc_clockevent_device) = { | |||
218 | .features = CLOCK_EVT_FEAT_ONESHOT | | 271 | .features = CLOCK_EVT_FEAT_ONESHOT | |
219 | CLOCK_EVT_FEAT_PERIODIC, | 272 | CLOCK_EVT_FEAT_PERIODIC, |
220 | .rating = 300, | 273 | .rating = 300, |
221 | .irq = TIMER0_IRQ, /* hardwired, no need for resources */ | ||
222 | .set_next_event = arc_clkevent_set_next_event, | 274 | .set_next_event = arc_clkevent_set_next_event, |
223 | .set_state_periodic = arc_clkevent_set_periodic, | 275 | .set_state_periodic = arc_clkevent_set_periodic, |
224 | }; | 276 | }; |
@@ -244,45 +296,81 @@ static irqreturn_t timer_irq_handler(int irq, void *dev_id) | |||
244 | return IRQ_HANDLED; | 296 | return IRQ_HANDLED; |
245 | } | 297 | } |
246 | 298 | ||
299 | static int arc_timer_cpu_notify(struct notifier_block *self, | ||
300 | unsigned long action, void *hcpu) | ||
301 | { | ||
302 | struct clock_event_device *evt = this_cpu_ptr(&arc_clockevent_device); | ||
303 | |||
304 | evt->cpumask = cpumask_of(smp_processor_id()); | ||
305 | |||
306 | switch (action & ~CPU_TASKS_FROZEN) { | ||
307 | case CPU_STARTING: | ||
308 | clockevents_config_and_register(evt, arc_timer_freq, | ||
309 | 0, ULONG_MAX); | ||
310 | enable_percpu_irq(arc_timer_irq, 0); | ||
311 | break; | ||
312 | case CPU_DYING: | ||
313 | disable_percpu_irq(arc_timer_irq); | ||
314 | break; | ||
315 | } | ||
316 | |||
317 | return NOTIFY_OK; | ||
318 | } | ||
319 | |||
320 | static struct notifier_block arc_timer_cpu_nb = { | ||
321 | .notifier_call = arc_timer_cpu_notify, | ||
322 | }; | ||
323 | |||
247 | /* | 324 | /* |
248 | * Setup the local event timer for @cpu | 325 | * clockevent setup for boot CPU |
249 | */ | 326 | */ |
250 | void arc_local_timer_setup() | 327 | static void __init arc_clockevent_setup(struct device_node *node) |
251 | { | 328 | { |
252 | struct clock_event_device *evt = this_cpu_ptr(&arc_clockevent_device); | 329 | struct clock_event_device *evt = this_cpu_ptr(&arc_clockevent_device); |
253 | int cpu = smp_processor_id(); | 330 | int ret; |
331 | |||
332 | register_cpu_notifier(&arc_timer_cpu_nb); | ||
254 | 333 | ||
255 | evt->cpumask = cpumask_of(cpu); | 334 | arc_timer_irq = irq_of_parse_and_map(node, 0); |
256 | clockevents_config_and_register(evt, arc_get_core_freq(), | 335 | if (arc_timer_irq <= 0) |
336 | panic("clockevent: missing irq"); | ||
337 | |||
338 | ret = arc_get_timer_clk(node); | ||
339 | if (ret) | ||
340 | panic("clockevent: missing clk"); | ||
341 | |||
342 | evt->irq = arc_timer_irq; | ||
343 | evt->cpumask = cpumask_of(smp_processor_id()); | ||
344 | clockevents_config_and_register(evt, arc_timer_freq, | ||
257 | 0, ARC_TIMER_MAX); | 345 | 0, ARC_TIMER_MAX); |
258 | 346 | ||
259 | /* setup the per-cpu timer IRQ handler - for all cpus */ | 347 | /* Needs apriori irq_set_percpu_devid() done in intc map function */ |
260 | arc_request_percpu_irq(TIMER0_IRQ, cpu, timer_irq_handler, | 348 | ret = request_percpu_irq(arc_timer_irq, timer_irq_handler, |
261 | "Timer0 (per-cpu-tick)", evt); | 349 | "Timer0 (per-cpu-tick)", evt); |
350 | if (ret) | ||
351 | panic("clockevent: unable to request irq\n"); | ||
352 | |||
353 | enable_percpu_irq(arc_timer_irq, 0); | ||
262 | } | 354 | } |
263 | 355 | ||
356 | static void __init arc_of_timer_init(struct device_node *np) | ||
357 | { | ||
358 | static int init_count = 0; | ||
359 | |||
360 | if (!init_count) { | ||
361 | init_count = 1; | ||
362 | arc_clockevent_setup(np); | ||
363 | } else { | ||
364 | arc_cs_setup_timer1(np); | ||
365 | } | ||
366 | } | ||
367 | CLOCKSOURCE_OF_DECLARE(arc_clkevt, "snps,arc-timer", arc_of_timer_init); | ||
368 | |||
264 | /* | 369 | /* |
265 | * Called from start_kernel() - boot CPU only | 370 | * Called from start_kernel() - boot CPU only |
266 | * | ||
267 | * -Sets up h/w timers as applicable on boot cpu | ||
268 | * -Also sets up any global state needed for timer subsystem: | ||
269 | * - for "counting" timer, registers a clocksource, usable across CPUs | ||
270 | * (provided that underlying counter h/w is synchronized across cores) | ||
271 | * - for "event" timer, sets up TIMER0 IRQ (as that is platform agnostic) | ||
272 | */ | 371 | */ |
273 | void __init time_init(void) | 372 | void __init time_init(void) |
274 | { | 373 | { |
275 | /* | 374 | of_clk_init(NULL); |
276 | * sets up the timekeeping free-flowing counter which also returns | 375 | clocksource_probe(); |
277 | * whether the counter is usable as clocksource | ||
278 | */ | ||
279 | if (arc_counter_setup()) | ||
280 | /* | ||
281 | * CLK upto 4.29 GHz can be safely represented in 32 bits | ||
282 | * because Max 32 bit number is 4,294,967,295 | ||
283 | */ | ||
284 | clocksource_register_hz(&arc_counter, arc_get_core_freq()); | ||
285 | |||
286 | /* sets up the periodic event timer */ | ||
287 | arc_local_timer_setup(); | ||
288 | } | 376 | } |