Skip to content

Commit 2ab8899

Browse files
committed
riscv: Introduce per cpu based local timer interrupt
Currently, ther timer interrupt handleri(riscv_timer_interrupt()) is invoked directly from the arch handler. This approach creates interwinding of driver and architecture code which is not ideal. Register timer interrupt as a per cpu based irq behind generic Linux IRQ infrastructure. As the local timer node is directly connected to individual harts, HLIC acts as the parent irq domain. This patch requires necessary bbl changes that adds timer node in device tree which can be found here. riscv-software-src/riscv-pk#112 Before the patch --------------------------------------------------- CPU1 CPU2 CPU3 CPU4 21: 12 8 6 5 riscv,plic0,c000000 53 eth0 37: 0 0 0 0 riscv,plic0,c000000 32 xilinx-pcie 43: 0 0 0 0 riscv,plic0,c000000 4 10010000.serial 44: 0 0 0 0 riscv,plic0,c000000 5 10011000.serial 45: 0 0 0 0 riscv,plic0,c000000 51 10040000.spi 46: 0 0 0 0 riscv,plic0,c000000 52 10041000.spi 47: 25 1 26 50 riscv,plic0,c000000 6 10050000.spi After the patch --------------------------------------------------- CPU1 CPU2 CPU3 CPU4 5: 271 0 0 0 riscv,cpu_intc,1 5 local_timer 6: 0 307 0 0 riscv,cpu_intc,2 5 local_timer 7: 0 0 303 0 riscv,cpu_intc,3 5 local_timer 8: 0 0 0 223 riscv,cpu_intc,4 5 local_timer 47: 337 489 389 35 riscv,plic0,c000000 4 10010000.serial 48: 0 0 0 0 riscv,plic0,c000000 5 10011000.serial 49: 0 0 0 0 riscv,plic0,c000000 51 10040000.spi 50: 0 0 0 0 riscv,plic0,c000000 52 10041000.spi 51: 2 14 47 39 riscv,plic0,c000000 6 10050000.spi Signed-off-by: Atish Patra <[email protected]>
1 parent 1edeb50 commit 2ab8899

File tree

4 files changed

+64
-31
lines changed

4 files changed

+64
-31
lines changed

arch/riscv/include/asm/irq.h

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,6 @@
2121
#define INTERRUPT_CAUSE_TIMER 5
2222
#define INTERRUPT_CAUSE_EXTERNAL 9
2323

24-
void riscv_timer_interrupt(void);
25-
2624
#include <asm-generic/irq.h>
2725

2826
#endif /* _ASM_RISCV_IRQ_H */

arch/riscv/kernel/time.c

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

2525
unsigned long riscv_timebase;
2626

27-
DECLARE_PER_CPU(struct clock_event_device, riscv_clock_event);
28-
29-
void riscv_timer_interrupt(void)
30-
{
31-
#ifdef CONFIG_RISCV_TIMER
32-
/*
33-
* FIXME: This needs to be cleaned up along with the rest of the IRQ
34-
* handling cleanup. See irq.c for more details.
35-
*/
36-
struct clock_event_device *evdev = this_cpu_ptr(&riscv_clock_event);
37-
38-
/*
39-
* There are no direct SBI calls to clear pending timer interrupt bit.
40-
* Disable timer interrupt to ignore pending interrupt until next
41-
* interrupt.
42-
*/
43-
csr_clear(sie, SIE_STIE);
44-
evdev->event_handler(evdev);
45-
#endif
46-
}
47-
4827
static long __init timebase_frequency(void)
4928
{
5029
struct device_node *cpu;

drivers/clocksource/riscv_timer.c

Lines changed: 57 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,12 @@
1515
#include <linux/clocksource.h>
1616
#include <linux/clockchips.h>
1717
#include <linux/delay.h>
18+
#include <linux/interrupt.h>
1819
#include <linux/timer_riscv.h>
1920
#include <linux/sched_clock.h>
2021
#include <linux/cpu.h>
22+
#include <linux/of.h>
23+
#include <linux/of_irq.h>
2124
#include <asm/sbi.h>
2225

2326
#define MINDELTA 100
@@ -73,6 +76,23 @@ DEFINE_PER_CPU(struct clocksource, riscv_clocksource) = {
7376
.read = rdtime,
7477
};
7578

79+
static irqreturn_t riscv_timer_interrupt(int irq, void *dev_id)
80+
{
81+
struct clock_event_device *evdev = dev_id;
82+
#ifdef CONFIG_RISCV_TIMER
83+
84+
/*
85+
* There are no direct SBI calls to clear pending timer interrupt bit.
86+
* Disable timer interrupt to ignore pending interrupt until next
87+
* interrupt.
88+
*/
89+
csr_clear(sie, SIE_STIE);
90+
evdev->event_handler(evdev);
91+
#endif
92+
return IRQ_HANDLED;
93+
}
94+
95+
7696
static int hart_of_timer(struct device_node *dev)
7797
{
7898
u32 hart;
@@ -100,36 +120,69 @@ static int timer_riscv_starting_cpu(unsigned int cpu)
100120
clockevents_config_and_register(ce, riscv_timebase, MINDELTA, MAXDELTA);
101121
/* Enable timer interrupt for this cpu */
102122
csr_set(sie, SIE_STIE);
123+
enable_percpu_irq(ce->irq, IRQ_TYPE_NONE);
103124

104125
return 0;
105126
}
106127

107128
static int timer_riscv_dying_cpu(unsigned int cpu)
108129
{
130+
struct clock_event_device *ce = per_cpu_ptr(&riscv_clock_event, cpu);
109131
/* Disable timer interrupt for this cpu */
110132
csr_clear(sie, SIE_STIE);
133+
disable_percpu_irq(ce->irq);
111134

112135
return 0;
113136
}
114137

115138
static int __init timer_riscv_init_dt(struct device_node *n)
116139
{
117140
int err = 0;
118-
int cpu_id = hart_of_timer(n);
119-
struct clocksource *cs = per_cpu_ptr(&riscv_clocksource, cpu_id);
141+
int cpu_id, timer_int;
142+
struct device_node *parent;
143+
struct clocksource *cs;
144+
struct clock_event_device *ce;
145+
146+
timer_int = irq_of_parse_and_map(n, 0);
147+
if (!timer_int) {
148+
pr_err("Unable to find local timer irq\n");
149+
return -EINVAL;
150+
}
120151

152+
parent = of_get_parent(n);
153+
if (!parent) {
154+
pr_err("Parent of timer node doesn't exist\n");
155+
return -EINVAL;
156+
}
157+
cpu_id = hart_of_timer(parent);
158+
159+
cs = per_cpu_ptr(&riscv_clocksource, cpu_id);
160+
ce = per_cpu_ptr(&riscv_clock_event, cpu_id);
161+
ce->irq = timer_int;
162+
163+
err = request_percpu_irq(ce->irq, riscv_timer_interrupt,
164+
"local_timer", &riscv_clock_event);
165+
if (err) {
166+
pr_err("local timer can't register for interrupt [%d] [%d]\n",
167+
timer_int, err);
168+
free_percpu_irq(ce->irq, ce);
169+
return err;
170+
}
121171
if (cpu_id == smp_processor_id()) {
122172
clocksource_register_hz(cs, riscv_timebase);
123173
sched_clock_register(timer_riscv_sched_read, 64, riscv_timebase);
124174

125175
err = cpuhp_setup_state(CPUHP_AP_RISCV_TIMER_STARTING,
126176
"clockevents/riscv/timer:starting",
127177
timer_riscv_starting_cpu, timer_riscv_dying_cpu);
128-
if (err)
178+
if (err) {
129179
pr_err("RISCV timer register failed [%d] for cpu = [%d]\n",
130180
err, cpu_id);
181+
free_percpu_irq(ce->irq, ce);
182+
return err;
183+
}
131184
}
132185
return err;
133186
}
134187

135-
TIMER_OF_DECLARE(riscv_timer, "riscv", timer_riscv_init_dt);
188+
TIMER_OF_DECLARE(riscv_timer, "riscv,local-timer", timer_riscv_init_dt);

drivers/irqchip/irq-riscv-intc.c

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -75,9 +75,6 @@ void riscv_intc_irq(struct pt_regs *regs)
7575
* device interrupts use the generic IRQ mechanisms.
7676
*/
7777
switch (cause) {
78-
case INTERRUPT_CAUSE_TIMER:
79-
riscv_timer_interrupt();
80-
break;
8178
case INTERRUPT_CAUSE_SOFTWARE:
8279
riscv_software_interrupt();
8380
break;
@@ -96,7 +93,13 @@ static int riscv_irqdomain_map(struct irq_domain *d, unsigned int irq,
9693
{
9794
struct riscv_irq_data *data = d->host_data;
9895

99-
irq_set_chip_and_handler(irq, &data->chip, handle_simple_irq);
96+
if (hwirq == INTERRUPT_CAUSE_TIMER) {
97+
irq_set_percpu_devid(irq);
98+
irq_set_chip_and_handler(irq, &data->chip,
99+
handle_percpu_devid_irq);
100+
irq_set_status_flags(irq, IRQ_NOAUTOEN);
101+
} else
102+
irq_set_chip_and_handler(irq, &data->chip, handle_simple_irq);
100103
irq_set_chip_data(irq, data);
101104
irq_set_noprobe(irq);
102105
irq_set_affinity(irq, cpumask_of(data->hart));

0 commit comments

Comments
 (0)