The RISC-V ACLINT specification is a modular specification and the ACLINT MTIMER device is backward compatible with the M-mode timer functionality of the CLINT device. This patch extends the CLINT timer driver to support both CLINT device and ACLINT MTIMER device. Signed-off-by: Anup Patel <anup.patel@xxxxxxx> Reviewed-by: Bin Meng <bmeng.cn@xxxxxxxxx> --- drivers/clocksource/timer-clint.c | 59 ++++++++++++++++++++++++------- 1 file changed, 47 insertions(+), 12 deletions(-) diff --git a/drivers/clocksource/timer-clint.c b/drivers/clocksource/timer-clint.c index 3b68ed53fe4a..9d38dfce374e 100644 --- a/drivers/clocksource/timer-clint.c +++ b/drivers/clocksource/timer-clint.c @@ -2,8 +2,16 @@ /* * Copyright (C) 2020 Western Digital Corporation or its affiliates. * - * Most of the M-mode (i.e. NoMMU) RISC-V systems usually have a - * CLINT MMIO timer device. + * Most of the M-mode (i.e. NoMMU) RISC-V systems usually have a CLINT + * MMIO device which is a composite device capable of injecting M-mode + * software interrupts and M-mode timer interrupts. + * + * The RISC-V ACLINT specification is modular in nature and defines + * separate devices for M-mode software interrupt (MSWI), M-mode timer + * (MTIMER) and S-mode software interrupt (SSWI). + * + * This is a common timer driver for the CLINT device and the ACLINT + * MTIMER device. */ #define pr_fmt(fmt) "clint: " fmt @@ -17,12 +25,16 @@ #include <linux/sched_clock.h> #include <linux/io-64-nonatomic-lo-hi.h> #include <linux/interrupt.h> +#include <linux/irqchip/irq-riscv-aclint-swi.h> #include <linux/of_irq.h> #include <linux/smp.h> #include <linux/timex.h> -#ifndef CONFIG_RISCV_M_MODE +#ifdef CONFIG_RISCV_M_MODE #include <asm/clint.h> + +u64 __iomem *clint_time_val; +EXPORT_SYMBOL(clint_time_val); #endif #define CLINT_IPI_OFF 0 @@ -35,11 +47,6 @@ static u64 __iomem *clint_timer_val; static unsigned long clint_timer_freq; static unsigned int clint_timer_irq; -#ifdef CONFIG_RISCV_M_MODE -u64 __iomem *clint_time_val; -EXPORT_SYMBOL(clint_time_val); -#endif - #ifdef CONFIG_64BIT #define clint_get_cycles() readq_relaxed(clint_timer_val) #else @@ -129,8 +136,10 @@ static int __init clint_timer_init_dt(struct device_node *np) { int rc; u32 i, nr_irqs; - void __iomem *base; + void __iomem *base = NULL; + void __iomem *base1 = NULL; struct of_phandle_args oirq; + bool is_aclint = of_device_is_compatible(np, "riscv,aclint-mtimer"); /* * Ensure that CLINT device interrupts are either RV_IRQ_TIMER or @@ -170,8 +179,19 @@ static int __init clint_timer_init_dt(struct device_node *np) return -ENODEV; } - clint_timer_cmp = base + CLINT_TIMER_CMP_OFF; - clint_timer_val = base + CLINT_TIMER_VAL_OFF; + if (is_aclint) { + clint_timer_val = base; + base1 = of_iomap(np, 1); + if (!base1) { + rc = -ENODEV; + pr_err("%pOFP: could not map registers\n", np); + goto fail_iounmap; + } + clint_timer_cmp = base1; + } else { + clint_timer_cmp = base + CLINT_TIMER_CMP_OFF; + clint_timer_val = base + CLINT_TIMER_VAL_OFF; + } clint_timer_freq = riscv_timebase; #ifdef CONFIG_RISCV_M_MODE @@ -208,14 +228,29 @@ static int __init clint_timer_init_dt(struct device_node *np) goto fail_free_irq; } + if (!is_aclint) { + rc = aclint_swi_init(np, base + CLINT_IPI_OFF); + if (rc) { + pr_err("%pOFP: aclint swi init failed [%d]\n", + np, rc); + goto fail_remove_cpuhp; + } + } + return 0; +fail_remove_cpuhp: + cpuhp_remove_state(CPUHP_AP_CLINT_TIMER_STARTING); fail_free_irq: free_irq(clint_timer_irq, &clint_clock_event); fail_iounmap: - iounmap(base); + if (base1) + iounmap(base1); + if (base) + iounmap(base); return rc; } TIMER_OF_DECLARE(clint_timer, "riscv,clint0", clint_timer_init_dt); TIMER_OF_DECLARE(clint_timer1, "sifive,clint0", clint_timer_init_dt); +TIMER_OF_DECLARE(clint_timer2, "riscv,aclint-mtimer", clint_timer_init_dt); -- 2.25.1