Skip to content

Commit 62b0194

Browse files
clocksource: new RISC-V SBI timer driver
The RISC-V ISA defines a per-hart real-time clock and timer, which is present on all systems. The clock is accessed via the 'rdtime' pseudo-instruction (which reads a CSR), and the timer is set via an SBI call. Contains various improvements from Atish Patra <atish.patra@wdc.com>. Signed-off-by: Dmitriy Cherkasov <dmitriy@oss-tech.org> Signed-off-by: Palmer Dabbelt <palmer@dabbelt.com> [hch: remove dead code, add SPDX tags, used riscv_of_processor_hart(), minor cleanups, merged hotplug cpu support and other improvements from Atish] Signed-off-by: Christoph Hellwig <hch@lst.de> Acked-by: Thomas Gleixner <tglx@linutronix.de> Reviewed-by: Atish Patra <atish.patra@wdc.com> Signed-off-by: Palmer Dabbelt <palmer@sifive.com>
1 parent 6ea0f26 commit 62b0194

File tree

8 files changed

+122
-12
lines changed

8 files changed

+122
-12
lines changed

arch/riscv/include/asm/smp.h

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,6 @@
2424

2525
#ifdef CONFIG_SMP
2626

27-
/* SMP initialization hook for setup_arch */
28-
void __init init_clockevent(void);
29-
3027
/* SMP initialization hook for setup_arch */
3128
void __init setup_smp(void);
3229

arch/riscv/kernel/irq.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,9 @@ asmlinkage void __irq_entry do_IRQ(struct pt_regs *regs, unsigned long cause)
3030

3131
irq_enter();
3232
switch (cause & ~INTERRUPT_CAUSE_FLAG) {
33+
case INTERRUPT_CAUSE_TIMER:
34+
riscv_timer_interrupt();
35+
break;
3336
#ifdef CONFIG_SMP
3437
case INTERRUPT_CAUSE_SOFTWARE:
3538
/*

arch/riscv/kernel/smpboot.c

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,6 @@ asmlinkage void __init smp_callin(void)
104104
current->active_mm = mm;
105105

106106
trap_init();
107-
init_clockevent();
108107
notify_cpu_starting(smp_processor_id());
109108
set_cpu_online(smp_processor_id(), 1);
110109
local_flush_tlb_all();

arch/riscv/kernel/time.c

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,6 @@
1818

1919
unsigned long riscv_timebase;
2020

21-
void __init init_clockevent(void)
22-
{
23-
timer_probe();
24-
csr_set(sie, SIE_STIE);
25-
}
26-
2721
void __init time_init(void)
2822
{
2923
struct device_node *cpu;
@@ -35,6 +29,5 @@ void __init time_init(void)
3529
riscv_timebase = prop;
3630

3731
lpj_fine = riscv_timebase / HZ;
38-
39-
init_clockevent();
32+
timer_probe();
4033
}

drivers/clocksource/Kconfig

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -609,4 +609,15 @@ config ATCPIT100_TIMER
609609
help
610610
This option enables support for the Andestech ATCPIT100 timers.
611611

612+
config RISCV_TIMER
613+
bool "Timer for the RISC-V platform"
614+
depends on RISCV
615+
default y
616+
select TIMER_PROBE
617+
select TIMER_OF
618+
help
619+
This enables the per-hart timer built into all RISC-V systems, which
620+
is accessed via both the SBI and the rdcycle instruction. This is
621+
required for all RISC-V systems.
622+
612623
endmenu

drivers/clocksource/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,3 +78,4 @@ obj-$(CONFIG_H8300_TPU) += h8300_tpu.o
7878
obj-$(CONFIG_CLKSRC_ST_LPC) += clksrc_st_lpc.o
7979
obj-$(CONFIG_X86_NUMACHIP) += numachip.o
8080
obj-$(CONFIG_ATCPIT100_TIMER) += timer-atcpit100.o
81+
obj-$(CONFIG_RISCV_TIMER) += riscv_timer.o

drivers/clocksource/riscv_timer.c

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
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 <asm/sbi.h>
12+
13+
/*
14+
* All RISC-V systems have a timer attached to every hart. These timers can be
15+
* read by the 'rdcycle' pseudo instruction, and can use the SBI to setup
16+
* events. In order to abstract the architecture-specific timer reading and
17+
* setting functions away from the clock event insertion code, we provide
18+
* function pointers to the clockevent subsystem that perform two basic
19+
* operations: rdtime() reads the timer on the current CPU, and
20+
* next_event(delta) sets the next timer event to 'delta' cycles in the future.
21+
* As the timers are inherently a per-cpu resource, these callbacks perform
22+
* operations on the current hart. There is guaranteed to be exactly one timer
23+
* per hart on all RISC-V systems.
24+
*/
25+
26+
static int riscv_clock_next_event(unsigned long delta,
27+
struct clock_event_device *ce)
28+
{
29+
csr_set(sie, SIE_STIE);
30+
sbi_set_timer(get_cycles64() + delta);
31+
return 0;
32+
}
33+
34+
static DEFINE_PER_CPU(struct clock_event_device, riscv_clock_event) = {
35+
.name = "riscv_timer_clockevent",
36+
.features = CLOCK_EVT_FEAT_ONESHOT,
37+
.rating = 100,
38+
.set_next_event = riscv_clock_next_event,
39+
};
40+
41+
/*
42+
* It is guaranteed that all the timers across all the harts are synchronized
43+
* within one tick of each other, so while this could technically go
44+
* backwards when hopping between CPUs, practically it won't happen.
45+
*/
46+
static unsigned long long riscv_clocksource_rdtime(struct clocksource *cs)
47+
{
48+
return get_cycles64();
49+
}
50+
51+
static DEFINE_PER_CPU(struct clocksource, riscv_clocksource) = {
52+
.name = "riscv_clocksource",
53+
.rating = 300,
54+
.mask = CLOCKSOURCE_MASK(BITS_PER_LONG),
55+
.flags = CLOCK_SOURCE_IS_CONTINUOUS,
56+
.read = riscv_clocksource_rdtime,
57+
};
58+
59+
static int riscv_timer_starting_cpu(unsigned int cpu)
60+
{
61+
struct clock_event_device *ce = per_cpu_ptr(&riscv_clock_event, cpu);
62+
63+
ce->cpumask = cpumask_of(cpu);
64+
clockevents_config_and_register(ce, riscv_timebase, 100, 0x7fffffff);
65+
66+
csr_set(sie, SIE_STIE);
67+
return 0;
68+
}
69+
70+
static int riscv_timer_dying_cpu(unsigned int cpu)
71+
{
72+
csr_clear(sie, SIE_STIE);
73+
return 0;
74+
}
75+
76+
/* called directly from the low-level interrupt handler */
77+
void riscv_timer_interrupt(void)
78+
{
79+
struct clock_event_device *evdev = this_cpu_ptr(&riscv_clock_event);
80+
81+
csr_clear(sie, SIE_STIE);
82+
evdev->event_handler(evdev);
83+
}
84+
85+
static int __init riscv_timer_init_dt(struct device_node *n)
86+
{
87+
int cpu_id = riscv_of_processor_hart(n), error;
88+
struct clocksource *cs;
89+
90+
if (cpu_id != smp_processor_id())
91+
return 0;
92+
93+
cs = per_cpu_ptr(&riscv_clocksource, cpu_id);
94+
clocksource_register_hz(cs, riscv_timebase);
95+
96+
error = cpuhp_setup_state(CPUHP_AP_RISCV_TIMER_STARTING,
97+
"clockevents/riscv/timer:starting",
98+
riscv_timer_starting_cpu, riscv_timer_dying_cpu);
99+
if (error)
100+
pr_err("RISCV timer register failed [%d] for cpu = [%d]\n",
101+
error, cpu_id);
102+
return error;
103+
}
104+
105+
TIMER_OF_DECLARE(riscv_timer, "riscv", riscv_timer_init_dt);

include/linux/cpuhotplug.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,7 @@ enum cpuhp_state {
125125
CPUHP_AP_MARCO_TIMER_STARTING,
126126
CPUHP_AP_MIPS_GIC_TIMER_STARTING,
127127
CPUHP_AP_ARC_TIMER_STARTING,
128+
CPUHP_AP_RISCV_TIMER_STARTING,
128129
CPUHP_AP_KVM_STARTING,
129130
CPUHP_AP_KVM_ARM_VGIC_INIT_STARTING,
130131
CPUHP_AP_KVM_ARM_VGIC_STARTING,

0 commit comments

Comments
 (0)