diff options
Diffstat (limited to 'drivers/clocksource/timer-riscv.c')
-rw-r--r-- | drivers/clocksource/timer-riscv.c | 118 |
1 files changed, 118 insertions, 0 deletions
diff --git a/drivers/clocksource/timer-riscv.c b/drivers/clocksource/timer-riscv.c new file mode 100644 index 000000000000..431892200a08 --- /dev/null +++ b/drivers/clocksource/timer-riscv.c | |||
@@ -0,0 +1,118 @@ | |||
1 | // SPDX-License-Identifier: GPL-2.0 | ||
2 | /* | ||
3 | * Copyright (C) 2012 Regents of the University of California | ||
4 | * Copyright (C) 2017 SiFive | ||
5 | */ | ||
6 | #include <linux/clocksource.h> | ||
7 | #include <linux/clockchips.h> | ||
8 | #include <linux/cpu.h> | ||
9 | #include <linux/delay.h> | ||
10 | #include <linux/irq.h> | ||
11 | #include <linux/sched_clock.h> | ||
12 | #include <asm/smp.h> | ||
13 | #include <asm/sbi.h> | ||
14 | |||
15 | /* | ||
16 | * All RISC-V systems have a timer attached to every hart. These timers can be | ||
17 | * read by the 'rdcycle' pseudo instruction, and can use the SBI to setup | ||
18 | * events. In order to abstract the architecture-specific timer reading and | ||
19 | * setting functions away from the clock event insertion code, we provide | ||
20 | * function pointers to the clockevent subsystem that perform two basic | ||
21 | * operations: rdtime() reads the timer on the current CPU, and | ||
22 | * next_event(delta) sets the next timer event to 'delta' cycles in the future. | ||
23 | * As the timers are inherently a per-cpu resource, these callbacks perform | ||
24 | * operations on the current hart. There is guaranteed to be exactly one timer | ||
25 | * per hart on all RISC-V systems. | ||
26 | */ | ||
27 | |||
28 | static int riscv_clock_next_event(unsigned long delta, | ||
29 | struct clock_event_device *ce) | ||
30 | { | ||
31 | csr_set(sie, SIE_STIE); | ||
32 | sbi_set_timer(get_cycles64() + delta); | ||
33 | return 0; | ||
34 | } | ||
35 | |||
36 | static DEFINE_PER_CPU(struct clock_event_device, riscv_clock_event) = { | ||
37 | .name = "riscv_timer_clockevent", | ||
38 | .features = CLOCK_EVT_FEAT_ONESHOT, | ||
39 | .rating = 100, | ||
40 | .set_next_event = riscv_clock_next_event, | ||
41 | }; | ||
42 | |||
43 | /* | ||
44 | * It is guaranteed that all the timers across all the harts are synchronized | ||
45 | * within one tick of each other, so while this could technically go | ||
46 | * backwards when hopping between CPUs, practically it won't happen. | ||
47 | */ | ||
48 | static unsigned long long riscv_clocksource_rdtime(struct clocksource *cs) | ||
49 | { | ||
50 | return get_cycles64(); | ||
51 | } | ||
52 | |||
53 | static u64 riscv_sched_clock(void) | ||
54 | { | ||
55 | return get_cycles64(); | ||
56 | } | ||
57 | |||
58 | static DEFINE_PER_CPU(struct clocksource, riscv_clocksource) = { | ||
59 | .name = "riscv_clocksource", | ||
60 | .rating = 300, | ||
61 | .mask = CLOCKSOURCE_MASK(BITS_PER_LONG), | ||
62 | .flags = CLOCK_SOURCE_IS_CONTINUOUS, | ||
63 | .read = riscv_clocksource_rdtime, | ||
64 | }; | ||
65 | |||
66 | static int riscv_timer_starting_cpu(unsigned int cpu) | ||
67 | { | ||
68 | struct clock_event_device *ce = per_cpu_ptr(&riscv_clock_event, cpu); | ||
69 | |||
70 | ce->cpumask = cpumask_of(cpu); | ||
71 | clockevents_config_and_register(ce, riscv_timebase, 100, 0x7fffffff); | ||
72 | |||
73 | csr_set(sie, SIE_STIE); | ||
74 | return 0; | ||
75 | } | ||
76 | |||
77 | static int riscv_timer_dying_cpu(unsigned int cpu) | ||
78 | { | ||
79 | csr_clear(sie, SIE_STIE); | ||
80 | return 0; | ||
81 | } | ||
82 | |||
83 | /* called directly from the low-level interrupt handler */ | ||
84 | void riscv_timer_interrupt(void) | ||
85 | { | ||
86 | struct clock_event_device *evdev = this_cpu_ptr(&riscv_clock_event); | ||
87 | |||
88 | csr_clear(sie, SIE_STIE); | ||
89 | evdev->event_handler(evdev); | ||
90 | } | ||
91 | |||
92 | static int __init riscv_timer_init_dt(struct device_node *n) | ||
93 | { | ||
94 | int cpuid, hartid, error; | ||
95 | struct clocksource *cs; | ||
96 | |||
97 | hartid = riscv_of_processor_hartid(n); | ||
98 | cpuid = riscv_hartid_to_cpuid(hartid); | ||
99 | |||
100 | if (cpuid != smp_processor_id()) | ||
101 | return 0; | ||
102 | |||
103 | cs = per_cpu_ptr(&riscv_clocksource, cpuid); | ||
104 | clocksource_register_hz(cs, riscv_timebase); | ||
105 | |||
106 | sched_clock_register(riscv_sched_clock, | ||
107 | BITS_PER_LONG, riscv_timebase); | ||
108 | |||
109 | error = cpuhp_setup_state(CPUHP_AP_RISCV_TIMER_STARTING, | ||
110 | "clockevents/riscv/timer:starting", | ||
111 | riscv_timer_starting_cpu, riscv_timer_dying_cpu); | ||
112 | if (error) | ||
113 | pr_err("RISCV timer register failed [%d] for cpu = [%d]\n", | ||
114 | error, cpuid); | ||
115 | return error; | ||
116 | } | ||
117 | |||
118 | TIMER_OF_DECLARE(riscv_timer, "riscv", riscv_timer_init_dt); | ||