Re: [PATCH v6 2/4] clocksource/drivers: Add CLINT timer driver

2020-08-04 Thread Palmer Dabbelt

On Fri, 24 Jul 2020 00:18:20 PDT (-0700), Anup Patel wrote:

We add a separate CLINT timer driver for Linux RISC-V M-mode (i.e.
RISC-V NoMMU kernel).

The CLINT MMIO device provides three things:
1. 64bit free running counter register
2. 64bit per-CPU time compare registers
3. 32bit per-CPU inter-processor interrupt registers

Unlike other timer devices, CLINT provides IPI registers along with
timer registers. To use CLINT IPI registers, the CLINT timer driver
provides IPI related callbacks to arch/riscv.

Signed-off-by: Anup Patel 
Tested-by: Emil Renner Berhing 
Acked-by: Daniel Lezcano 
---
 drivers/clocksource/Kconfig   |   9 ++
 drivers/clocksource/Makefile  |   1 +
 drivers/clocksource/timer-clint.c | 226 ++
 include/linux/cpuhotplug.h|   1 +
 4 files changed, 237 insertions(+)
 create mode 100644 drivers/clocksource/timer-clint.c

diff --git a/drivers/clocksource/Kconfig b/drivers/clocksource/Kconfig
index 91418381fcd4..41f1c147c178 100644
--- a/drivers/clocksource/Kconfig
+++ b/drivers/clocksource/Kconfig
@@ -658,6 +658,15 @@ 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 "CLINT Timer for the RISC-V platform" if COMPILE_TEST
+   depends on GENERIC_SCHED_CLOCK && RISCV
+   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.
+
 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 ..8eeafa82c03d
--- /dev/null
+++ b/drivers/clocksource/timer-clint.c
@@ -0,0 +1,226 @@
+// 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 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+
+#define CLINT_IPI_OFF  0
+#define CLINT_TIMER_CMP_OFF0x4000
+#define CLINT_TIMER_VAL_OFF0xbff8
+
+/* 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 notrace clint_get_cycles64(void)
+{
+   return clint_get_cycles();
+}
+#else /* CONFIG_64BIT */
+static u64 notrace 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 u64 clint_rdtime(struct clocksource *cs)
+{
+   return clint_get_cycles64();
+}
+
+static struct clocksource clint_clocksource = {
+   .name   = "clint_clocksource",
+   .rating = 300,
+   .mask   = CLOCKSOURCE_MASK(64),
+   .flags  = CLOCK_SOURCE_IS_CONTINUOUS,
+   .read   = clint_rdtime,
+};
+
+static int clint_clock_next_event(unsigned long delta,
+  struct clock_event_device *ce)
+{
+   void __iomem *r = clint_timer_cmp +
+ 

Re: [PATCH v6 2/4] clocksource/drivers: Add CLINT timer driver

2020-07-24 Thread Atish Patra
On Fri, Jul 24, 2020 at 12:19 AM Anup Patel  wrote:
>
> We add a separate CLINT timer driver for Linux RISC-V M-mode (i.e.
> RISC-V NoMMU kernel).
>
> The CLINT MMIO device provides three things:
> 1. 64bit free running counter register
> 2. 64bit per-CPU time compare registers
> 3. 32bit per-CPU inter-processor interrupt registers
>
> Unlike other timer devices, CLINT provides IPI registers along with
> timer registers. To use CLINT IPI registers, the CLINT timer driver
> provides IPI related callbacks to arch/riscv.
>
> Signed-off-by: Anup Patel 
> Tested-by: Emil Renner Berhing 
> Acked-by: Daniel Lezcano 
> ---
>  drivers/clocksource/Kconfig   |   9 ++
>  drivers/clocksource/Makefile  |   1 +
>  drivers/clocksource/timer-clint.c | 226 ++
>  include/linux/cpuhotplug.h|   1 +
>  4 files changed, 237 insertions(+)
>  create mode 100644 drivers/clocksource/timer-clint.c
>
> diff --git a/drivers/clocksource/Kconfig b/drivers/clocksource/Kconfig
> index 91418381fcd4..41f1c147c178 100644
> --- a/drivers/clocksource/Kconfig
> +++ b/drivers/clocksource/Kconfig
> @@ -658,6 +658,15 @@ 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 "CLINT Timer for the RISC-V platform" if COMPILE_TEST
> +   depends on GENERIC_SCHED_CLOCK && RISCV
> +   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.
> +
>  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 ..8eeafa82c03d
> --- /dev/null
> +++ b/drivers/clocksource/timer-clint.c
> @@ -0,0 +1,226 @@
> +// 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 
> +#include 
> +#include 
> +#include 
> +#include 
> +#include 
> +#include 
> +#include 
> +#include 
> +#include 
> +#include 
> +#include 
> +
> +#define CLINT_IPI_OFF  0
> +#define CLINT_TIMER_CMP_OFF0x4000
> +#define CLINT_TIMER_VAL_OFF0xbff8
> +
> +/* 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 notrace clint_get_cycles64(void)
> +{
> +   return clint_get_cycles();
> +}
> +#else /* CONFIG_64BIT */
> +static u64 notrace 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 u64 clint_rdtime(struct clocksource *cs)
> +{
> +   return clint_get_cycles64();
> +}
> +
> +static struct clocksource clint_clocksource = {
> +   .name   = "clint_clocksource",
> +   .rating = 300,
> +   .mask   = CLOCKSOURCE_MASK(64),
> +   .flags  = 

[PATCH v6 2/4] clocksource/drivers: Add CLINT timer driver

2020-07-24 Thread Anup Patel
We add a separate CLINT timer driver for Linux RISC-V M-mode (i.e.
RISC-V NoMMU kernel).

The CLINT MMIO device provides three things:
1. 64bit free running counter register
2. 64bit per-CPU time compare registers
3. 32bit per-CPU inter-processor interrupt registers

Unlike other timer devices, CLINT provides IPI registers along with
timer registers. To use CLINT IPI registers, the CLINT timer driver
provides IPI related callbacks to arch/riscv.

Signed-off-by: Anup Patel 
Tested-by: Emil Renner Berhing 
Acked-by: Daniel Lezcano 
---
 drivers/clocksource/Kconfig   |   9 ++
 drivers/clocksource/Makefile  |   1 +
 drivers/clocksource/timer-clint.c | 226 ++
 include/linux/cpuhotplug.h|   1 +
 4 files changed, 237 insertions(+)
 create mode 100644 drivers/clocksource/timer-clint.c

diff --git a/drivers/clocksource/Kconfig b/drivers/clocksource/Kconfig
index 91418381fcd4..41f1c147c178 100644
--- a/drivers/clocksource/Kconfig
+++ b/drivers/clocksource/Kconfig
@@ -658,6 +658,15 @@ 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 "CLINT Timer for the RISC-V platform" if COMPILE_TEST
+   depends on GENERIC_SCHED_CLOCK && RISCV
+   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.
+
 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 ..8eeafa82c03d
--- /dev/null
+++ b/drivers/clocksource/timer-clint.c
@@ -0,0 +1,226 @@
+// 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 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+
+#define CLINT_IPI_OFF  0
+#define CLINT_TIMER_CMP_OFF0x4000
+#define CLINT_TIMER_VAL_OFF0xbff8
+
+/* 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 notrace clint_get_cycles64(void)
+{
+   return clint_get_cycles();
+}
+#else /* CONFIG_64BIT */
+static u64 notrace 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 u64 clint_rdtime(struct clocksource *cs)
+{
+   return clint_get_cycles64();
+}
+
+static struct clocksource clint_clocksource = {
+   .name   = "clint_clocksource",
+   .rating = 300,
+   .mask   = CLOCKSOURCE_MASK(64),
+   .flags  = CLOCK_SOURCE_IS_CONTINUOUS,
+   .read   = clint_rdtime,
+};
+
+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);
+