On 17/07/2020 07:21, Anup Patel wrote: > On Fri, Jul 17, 2020 at 2:57 AM Daniel Lezcano > <daniel.lezcano@xxxxxxxxxx> wrote: >> >> >> Hi Anup, >> >> >> On 15/07/2020 09:15, Anup Patel wrote: >>> The TIME CSR and SBI calls are not available in RISC-V M-mode so we >>> separate add CLINT driver for Linux RISC-V M-mode (i.e. RISC-V NoMMU >>> kernel). >> >> The description is confusing, please reword it and give a bit more >> information about the timer itself, especially, the IPI thing. > > Okay, will update. > >> >>> Signed-off-by: Anup Patel <anup.patel@xxxxxxx> >>> --- >>> drivers/clocksource/Kconfig | 10 ++ >>> drivers/clocksource/Makefile | 1 + >>> drivers/clocksource/timer-clint.c | 229 ++++++++++++++++++++++++++++++ >>> include/linux/cpuhotplug.h | 1 + >>> 4 files changed, 241 insertions(+) >>> create mode 100644 drivers/clocksource/timer-clint.c >>> >>> diff --git a/drivers/clocksource/Kconfig b/drivers/clocksource/Kconfig >>> index 91418381fcd4..eabcf1cfb0c0 100644 >>> --- a/drivers/clocksource/Kconfig >>> +++ b/drivers/clocksource/Kconfig >>> @@ -658,6 +658,16 @@ config RISCV_TIMER >>> is accessed via both the SBI and the rdcycle instruction. This is >>> required for all RISC-V systems. >>> >>> +config CLINT_TIMER >>> + bool "Timer for the RISC-V platform" >>> + depends on GENERIC_SCHED_CLOCK && RISCV_M_MODE >>> + default y >>> + select TIMER_PROBE >>> + select TIMER_OF >>> + help >>> + This option enables the CLINT timer for RISC-V systems. The CLINT >>> + driver is usually used for NoMMU RISC-V systems. >> >> For the timer, we do silent option and let the platform config select >> it. Please refer to other timer option below as reference. > > Okay, I will use "default RISCV" instead of "default y" (just like other > timer Kconfig options). Preferably, select it from the platform's Kconfig. >> >>> config CSKY_MP_TIMER >>> bool "SMP Timer for the C-SKY platform" if COMPILE_TEST >>> depends on CSKY >>> diff --git a/drivers/clocksource/Makefile b/drivers/clocksource/Makefile >>> index bdda1a2e4097..18e700e703a0 100644 >>> --- a/drivers/clocksource/Makefile >>> +++ b/drivers/clocksource/Makefile >>> @@ -87,6 +87,7 @@ obj-$(CONFIG_CLKSRC_ST_LPC) += clksrc_st_lpc.o >>> obj-$(CONFIG_X86_NUMACHIP) += numachip.o >>> obj-$(CONFIG_ATCPIT100_TIMER) += timer-atcpit100.o >>> obj-$(CONFIG_RISCV_TIMER) += timer-riscv.o >>> +obj-$(CONFIG_CLINT_TIMER) += timer-clint.o >>> obj-$(CONFIG_CSKY_MP_TIMER) += timer-mp-csky.o >>> obj-$(CONFIG_GX6605S_TIMER) += timer-gx6605s.o >>> obj-$(CONFIG_HYPERV_TIMER) += hyperv_timer.o >>> diff --git a/drivers/clocksource/timer-clint.c b/drivers/clocksource/timer-clint.c >>> new file mode 100644 >>> index 000000000000..bfc38bb5a589 >>> --- /dev/null >>> +++ b/drivers/clocksource/timer-clint.c >>> @@ -0,0 +1,229 @@ >>> +// SPDX-License-Identifier: GPL-2.0 >>> +/* >>> + * 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. >>> + */ >>> + >>> +#define pr_fmt(fmt) "clint: " fmt >>> +#include <linux/bitops.h> >>> +#include <linux/clocksource.h> >>> +#include <linux/clockchips.h> >>> +#include <linux/cpu.h> >>> +#include <linux/delay.h> >>> +#include <linux/module.h> >>> +#include <linux/of_address.h> >>> +#include <linux/sched_clock.h> >>> +#include <linux/io-64-nonatomic-lo-hi.h> >>> +#include <linux/interrupt.h> >>> +#include <linux/of_irq.h> >>> +#include <linux/smp.h> >>> + >>> +#define CLINT_IPI_OFF 0 >>> +#define CLINT_TIMER_CMP_OFF 0x4000 >>> +#define CLINT_TIMER_VAL_OFF 0xbff8 >>> + >>> +/* CLINT manages IPI and Timer for RISC-V M-mode */ >>> +static u32 __iomem *clint_ipi_base; >>> +static u64 __iomem *clint_timer_cmp; >>> +static u64 __iomem *clint_timer_val; >>> +static unsigned long clint_timer_freq; >>> +static unsigned int clint_timer_irq; >>> + >>> +static void clint_send_ipi(const struct cpumask *target) >>> +{ >>> + unsigned int cpu; >>> + >>> + for_each_cpu(cpu, target) >>> + writel(1, clint_ipi_base + cpuid_to_hartid_map(cpu)); >>> +} >>> + >>> +static void clint_clear_ipi(void) >>> +{ >>> + writel(0, clint_ipi_base + cpuid_to_hartid_map(smp_processor_id())); >>> +} >>> + >>> +static struct riscv_ipi_ops clint_ipi_ops = { >>> + .ipi_inject = clint_send_ipi, >>> + .ipi_clear = clint_clear_ipi, >>> +}; >>> + >>> +#ifdef CONFIG_64BIT >>> +#define clint_get_cycles() readq_relaxed(clint_timer_val) >>> +#else >>> +#define clint_get_cycles() readl_relaxed(clint_timer_val) >>> +#define clint_get_cycles_hi() readl_relaxed(((u32 *)clint_timer_val) + 1) >>> +#endif >>> + >>> +#ifdef CONFIG_64BIT >>> +static u64 clint_get_cycles64(void) >>> +{ >>> + return clint_get_cycles(); >>> +} >>> +#else /* CONFIG_64BIT */ >>> +static u64 clint_get_cycles64(void) >>> +{ >>> + u32 hi, lo; >>> + >>> + do { >>> + hi = clint_get_cycles_hi(); >>> + lo = clint_get_cycles(); >>> + } while (hi != clint_get_cycles_hi()); >>> + >>> + return ((u64)hi << 32) | lo; >>> +} >>> +#endif /* CONFIG_64BIT */ >>> +static int clint_clock_next_event(unsigned long delta, >>> + struct clock_event_device *ce) >>> +{ >>> + void __iomem *r = clint_timer_cmp + >>> + cpuid_to_hartid_map(smp_processor_id()); >>> + >>> + csr_set(CSR_IE, IE_TIE); >>> + writeq_relaxed(clint_get_cycles64() + delta, r); >>> + return 0; >>> +} >>> + >>> +static DEFINE_PER_CPU(struct clock_event_device, clint_clock_event) = { >>> + .name = "clint_clockevent", >>> + .features = CLOCK_EVT_FEAT_ONESHOT, >>> + .rating = 100, >>> + .set_next_event = clint_clock_next_event, >>> +}; >>> + >>> +static u64 clint_rdtime(struct clocksource *cs) >>> +{ >>> + return readq_relaxed(clint_timer_val); >>> +} >>> + >>> +static u64 notrace clint_sched_clock(void) >>> +{ >>> + return readq_relaxed(clint_timer_val); >>> +} >>> + >>> +static struct clocksource clint_clocksource = { >>> + .name = "clint_clocksource", >>> + .rating = 300, >>> + .mask = CLOCKSOURCE_MASK(64), >>> + .flags = CLOCK_SOURCE_IS_CONTINUOUS, >>> + .read = clint_rdtime, >> >> What if !CONFIG_64BIT > > The CLINT counter is 64bit for both 32bit and 64bit systems > but I should have used clint_get_cycles64() in clint_rdtime(). > I will update it. > >> >>> +}; >>> + >>> +static int clint_timer_starting_cpu(unsigned int cpu) >>> +{ >>> + struct clock_event_device *ce = per_cpu_ptr(&clint_clock_event, cpu); >>> + >>> + ce->cpumask = cpumask_of(cpu); >>> + clockevents_config_and_register(ce, clint_timer_freq, 200, ULONG_MAX); >> >> The function is not immune against registering the same clockevents. If >> the CPU is hotplugged several times, this function will be called again >> and again. Why not rely on a for_each_possible_cpu loop in the init >> function ? >> >>> + enable_percpu_irq(clint_timer_irq, >>> + irq_get_trigger_type(clint_timer_irq)); >> >> Why do you want to enable / disable the interrrupts ? The should be >> already handle by the hotplug framework no ? > > The perCPU interrupts are not enabled by default. We have to > explicitly enable/disable perCPU interrupts in CPU hotplug callbacks. > Isn't is possible to do that in the probe/init function ? -- <http://www.linaro.org/> Linaro.org │ Open source software for ARM SoCs Follow Linaro: <http://www.facebook.com/pages/Linaro> Facebook | <http://twitter.com/#!/linaroorg> Twitter | <http://www.linaro.org/linaro-blog/> Blog