2018-08-04 11:23:19 +03:00
|
|
|
// SPDX-License-Identifier: GPL-2.0
|
|
|
|
/*
|
|
|
|
* Copyright (C) 2012 Regents of the University of California
|
|
|
|
* Copyright (C) 2017 SiFive
|
2019-08-21 17:58:36 +03:00
|
|
|
*
|
2019-10-28 15:10:37 +03:00
|
|
|
* All RISC-V systems have a timer attached to every hart. These timers can
|
|
|
|
* either be read from the "time" and "timeh" CSRs, and can use the SBI to
|
|
|
|
* setup events, or directly accessed using MMIO registers.
|
2018-08-04 11:23:19 +03:00
|
|
|
*/
|
2022-07-22 19:50:46 +03:00
|
|
|
|
|
|
|
#define pr_fmt(fmt) "riscv-timer: " fmt
|
|
|
|
|
2018-08-04 11:23:19 +03:00
|
|
|
#include <linux/clocksource.h>
|
|
|
|
#include <linux/clockchips.h>
|
|
|
|
#include <linux/cpu.h>
|
|
|
|
#include <linux/delay.h>
|
|
|
|
#include <linux/irq.h>
|
2020-06-01 12:15:41 +03:00
|
|
|
#include <linux/irqdomain.h>
|
2021-09-27 14:40:11 +03:00
|
|
|
#include <linux/module.h>
|
2018-12-04 13:29:52 +03:00
|
|
|
#include <linux/sched_clock.h>
|
2019-10-28 15:10:37 +03:00
|
|
|
#include <linux/io-64-nonatomic-lo-hi.h>
|
2020-06-01 12:15:41 +03:00
|
|
|
#include <linux/interrupt.h>
|
|
|
|
#include <linux/of_irq.h>
|
2021-09-27 14:40:11 +03:00
|
|
|
#include <clocksource/timer-riscv.h>
|
2018-10-02 22:15:05 +03:00
|
|
|
#include <asm/smp.h>
|
2022-07-22 19:50:46 +03:00
|
|
|
#include <asm/hwcap.h>
|
2018-08-04 11:23:19 +03:00
|
|
|
#include <asm/sbi.h>
|
2020-08-17 15:42:50 +03:00
|
|
|
#include <asm/timex.h>
|
2019-10-28 15:10:37 +03:00
|
|
|
|
2022-07-22 19:50:46 +03:00
|
|
|
static DEFINE_STATIC_KEY_FALSE(riscv_sstc_available);
|
2023-01-03 17:11:02 +03:00
|
|
|
static bool riscv_timer_cannot_wake_cpu;
|
2022-07-22 19:50:46 +03:00
|
|
|
|
2018-08-04 11:23:19 +03:00
|
|
|
static int riscv_clock_next_event(unsigned long delta,
|
|
|
|
struct clock_event_device *ce)
|
|
|
|
{
|
2022-07-22 19:50:46 +03:00
|
|
|
u64 next_tval = get_cycles64() + delta;
|
|
|
|
|
2019-10-28 15:10:32 +03:00
|
|
|
csr_set(CSR_IE, IE_TIE);
|
2022-07-22 19:50:46 +03:00
|
|
|
if (static_branch_likely(&riscv_sstc_available)) {
|
|
|
|
#if defined(CONFIG_32BIT)
|
|
|
|
csr_write(CSR_STIMECMP, next_tval & 0xFFFFFFFF);
|
|
|
|
csr_write(CSR_STIMECMPH, next_tval >> 32);
|
|
|
|
#else
|
|
|
|
csr_write(CSR_STIMECMP, next_tval);
|
|
|
|
#endif
|
|
|
|
} else
|
|
|
|
sbi_set_timer(next_tval);
|
|
|
|
|
2018-08-04 11:23:19 +03:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2020-06-01 12:15:41 +03:00
|
|
|
static unsigned int riscv_clock_event_irq;
|
2018-08-04 11:23:19 +03:00
|
|
|
static DEFINE_PER_CPU(struct clock_event_device, riscv_clock_event) = {
|
|
|
|
.name = "riscv_timer_clockevent",
|
Revert "clocksource/drivers/riscv: Events are stopped during CPU suspend"
This reverts commit 232ccac1bd9b5bfe73895f527c08623e7fa0752d.
On the subject of suspend, the RISC-V SBI spec states:
This does not cover whether any given events actually reach the hart or
not, just what the hart will do if it receives an event. On PolarFire
SoC, and potentially other SiFive based implementations, events from the
RISC-V timer do reach a hart during suspend. This is not the case for the
implementation on the Allwinner D1 - there timer events are not received
during suspend.
To fix this, the CLOCK_EVT_FEAT_C3STOP (mis)feature was enabled for the
timer driver - but this has broken both RCU stall detection and timers
generally on PolarFire SoC and potentially other SiFive based
implementations.
If an AXI read to the PCIe controller on PolarFire SoC times out, the
system will stall, however, with CLOCK_EVT_FEAT_C3STOP active, the system
just locks up without RCU stalling:
io scheduler mq-deadline registered
io scheduler kyber registered
microchip-pcie 2000000000.pcie: host bridge /soc/pcie@2000000000 ranges:
microchip-pcie 2000000000.pcie: MEM 0x2008000000..0x2087ffffff -> 0x0008000000
microchip-pcie 2000000000.pcie: sec error in pcie2axi buffer
microchip-pcie 2000000000.pcie: ded error in pcie2axi buffer
microchip-pcie 2000000000.pcie: axi read request error
microchip-pcie 2000000000.pcie: axi read timeout
microchip-pcie 2000000000.pcie: sec error in pcie2axi buffer
microchip-pcie 2000000000.pcie: ded error in pcie2axi buffer
microchip-pcie 2000000000.pcie: sec error in pcie2axi buffer
microchip-pcie 2000000000.pcie: ded error in pcie2axi buffer
microchip-pcie 2000000000.pcie: sec error in pcie2axi buffer
microchip-pcie 2000000000.pcie: ded error in pcie2axi buffer
Freeing initrd memory: 7332K
Similarly issues were reported with clock_nanosleep() - with a test app
that sleeps each cpu for 6, 5, 4, 3 ms respectively, HZ=250 & the blamed
commit in place, the sleep times are rounded up to the next jiffy:
== CPU: 1 == == CPU: 2 == == CPU: 3 == == CPU: 4 ==
Mean: 7.974992 Mean: 7.976534 Mean: 7.962591 Mean: 3.952179
Std Dev: 0.154374 Std Dev: 0.156082 Std Dev: 0.171018 Std Dev: 0.076193
Hi: 9.472000 Hi: 10.495000 Hi: 8.864000 Hi: 4.736000
Lo: 6.087000 Lo: 6.380000 Lo: 4.872000 Lo: 3.403000
Samples: 521 Samples: 521 Samples: 521 Samples: 521
Fortunately, the D1 has a second timer, which is "currently used in
preference to the RISC-V/SBI timer driver" so a revert here does not
hurt operation of D1 in its current form.
Ultimately, a DeviceTree property (or node) will be added to encode the
behaviour of the timers, but until then revert the addition of
CLOCK_EVT_FEAT_C3STOP.
Fixes: 232ccac1bd9b ("clocksource/drivers/riscv: Events are stopped during CPU suspend")
Signed-off-by: Conor Dooley <conor.dooley@microchip.com>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Reviewed-by: Palmer Dabbelt <palmer@rivosinc.com>
Acked-by: Palmer Dabbelt <palmer@rivosinc.com>
Acked-by: Samuel Holland <samuel@sholland.org>
Link: https://lore.kernel.org/linux-riscv/YzYTNQRxLr7Q9JR0@spud/
Link: https://github.com/riscv-non-isa/riscv-sbi-doc/issues/98/
Link: https://lore.kernel.org/linux-riscv/bf6d3b1f-f703-4a25-833e-972a44a04114@sholland.org/
Link: https://lore.kernel.org/r/20221122121620.3522431-1-conor.dooley@microchip.com
2022-11-22 15:16:21 +03:00
|
|
|
.features = CLOCK_EVT_FEAT_ONESHOT,
|
2018-08-04 11:23:19 +03:00
|
|
|
.rating = 100,
|
|
|
|
.set_next_event = riscv_clock_next_event,
|
|
|
|
};
|
|
|
|
|
|
|
|
/*
|
|
|
|
* It is guaranteed that all the timers across all the harts are synchronized
|
|
|
|
* within one tick of each other, so while this could technically go
|
|
|
|
* backwards when hopping between CPUs, practically it won't happen.
|
|
|
|
*/
|
|
|
|
static unsigned long long riscv_clocksource_rdtime(struct clocksource *cs)
|
|
|
|
{
|
|
|
|
return get_cycles64();
|
|
|
|
}
|
|
|
|
|
2019-12-23 11:46:14 +03:00
|
|
|
static u64 notrace riscv_sched_clock(void)
|
2018-12-04 13:29:52 +03:00
|
|
|
{
|
|
|
|
return get_cycles64();
|
|
|
|
}
|
|
|
|
|
2019-08-03 07:27:20 +03:00
|
|
|
static struct clocksource riscv_clocksource = {
|
2018-08-04 11:23:19 +03:00
|
|
|
.name = "riscv_clocksource",
|
2022-12-28 03:44:44 +03:00
|
|
|
.rating = 400,
|
2019-03-23 00:54:11 +03:00
|
|
|
.mask = CLOCKSOURCE_MASK(64),
|
2018-08-04 11:23:19 +03:00
|
|
|
.flags = CLOCK_SOURCE_IS_CONTINUOUS,
|
|
|
|
.read = riscv_clocksource_rdtime,
|
2022-12-30 01:46:01 +03:00
|
|
|
#if IS_ENABLED(CONFIG_GENERIC_GETTIMEOFDAY)
|
|
|
|
.vdso_clock_mode = VDSO_CLOCKMODE_ARCHTIMER,
|
|
|
|
#else
|
|
|
|
.vdso_clock_mode = VDSO_CLOCKMODE_NONE,
|
|
|
|
#endif
|
2018-08-04 11:23:19 +03:00
|
|
|
};
|
|
|
|
|
|
|
|
static int riscv_timer_starting_cpu(unsigned int cpu)
|
|
|
|
{
|
|
|
|
struct clock_event_device *ce = per_cpu_ptr(&riscv_clock_event, cpu);
|
|
|
|
|
|
|
|
ce->cpumask = cpumask_of(cpu);
|
2020-06-01 12:15:41 +03:00
|
|
|
ce->irq = riscv_clock_event_irq;
|
2023-01-03 17:11:02 +03:00
|
|
|
if (riscv_timer_cannot_wake_cpu)
|
|
|
|
ce->features |= CLOCK_EVT_FEAT_C3STOP;
|
2018-08-04 11:23:19 +03:00
|
|
|
clockevents_config_and_register(ce, riscv_timebase, 100, 0x7fffffff);
|
|
|
|
|
2020-06-01 12:15:41 +03:00
|
|
|
enable_percpu_irq(riscv_clock_event_irq,
|
|
|
|
irq_get_trigger_type(riscv_clock_event_irq));
|
2018-08-04 11:23:19 +03:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int riscv_timer_dying_cpu(unsigned int cpu)
|
|
|
|
{
|
2020-06-01 12:15:41 +03:00
|
|
|
disable_percpu_irq(riscv_clock_event_irq);
|
2018-08-04 11:23:19 +03:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2021-09-27 14:40:11 +03:00
|
|
|
void riscv_cs_get_mult_shift(u32 *mult, u32 *shift)
|
|
|
|
{
|
|
|
|
*mult = riscv_clocksource.mult;
|
|
|
|
*shift = riscv_clocksource.shift;
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(riscv_cs_get_mult_shift);
|
|
|
|
|
2018-08-04 11:23:19 +03:00
|
|
|
/* called directly from the low-level interrupt handler */
|
2020-06-01 12:15:41 +03:00
|
|
|
static irqreturn_t riscv_timer_interrupt(int irq, void *dev_id)
|
2018-08-04 11:23:19 +03:00
|
|
|
{
|
|
|
|
struct clock_event_device *evdev = this_cpu_ptr(&riscv_clock_event);
|
|
|
|
|
2019-10-28 15:10:32 +03:00
|
|
|
csr_clear(CSR_IE, IE_TIE);
|
2018-08-04 11:23:19 +03:00
|
|
|
evdev->event_handler(evdev);
|
2020-06-01 12:15:41 +03:00
|
|
|
|
|
|
|
return IRQ_HANDLED;
|
2018-08-04 11:23:19 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
static int __init riscv_timer_init_dt(struct device_node *n)
|
|
|
|
{
|
2022-05-27 08:17:42 +03:00
|
|
|
int cpuid, error;
|
|
|
|
unsigned long hartid;
|
2020-06-01 12:15:41 +03:00
|
|
|
struct device_node *child;
|
|
|
|
struct irq_domain *domain;
|
2018-08-04 11:23:19 +03:00
|
|
|
|
2022-05-27 08:17:42 +03:00
|
|
|
error = riscv_of_processor_hartid(n, &hartid);
|
|
|
|
if (error < 0) {
|
|
|
|
pr_warn("Not valid hartid for node [%pOF] error = [%lu]\n",
|
2019-02-13 23:18:10 +03:00
|
|
|
n, hartid);
|
2022-05-27 08:17:42 +03:00
|
|
|
return error;
|
2019-02-13 23:18:10 +03:00
|
|
|
}
|
|
|
|
|
2018-10-02 22:15:05 +03:00
|
|
|
cpuid = riscv_hartid_to_cpuid(hartid);
|
2019-02-13 23:18:10 +03:00
|
|
|
if (cpuid < 0) {
|
2022-05-27 08:17:42 +03:00
|
|
|
pr_warn("Invalid cpuid for hartid [%lu]\n", hartid);
|
2019-02-13 23:18:10 +03:00
|
|
|
return cpuid;
|
|
|
|
}
|
2018-10-02 22:15:05 +03:00
|
|
|
|
|
|
|
if (cpuid != smp_processor_id())
|
2018-08-04 11:23:19 +03:00
|
|
|
return 0;
|
|
|
|
|
2023-01-03 17:11:02 +03:00
|
|
|
child = of_find_compatible_node(NULL, NULL, "riscv,timer");
|
|
|
|
if (child) {
|
|
|
|
riscv_timer_cannot_wake_cpu = of_property_read_bool(child,
|
|
|
|
"riscv,timer-cannot-wake-cpu");
|
|
|
|
of_node_put(child);
|
|
|
|
}
|
|
|
|
|
2020-06-01 12:15:41 +03:00
|
|
|
domain = NULL;
|
|
|
|
child = of_get_compatible_child(n, "riscv,cpu-intc");
|
|
|
|
if (!child) {
|
|
|
|
pr_err("Failed to find INTC node [%pOF]\n", n);
|
|
|
|
return -ENODEV;
|
|
|
|
}
|
|
|
|
domain = irq_find_host(child);
|
|
|
|
of_node_put(child);
|
|
|
|
if (!domain) {
|
|
|
|
pr_err("Failed to find IRQ domain for node [%pOF]\n", n);
|
|
|
|
return -ENODEV;
|
|
|
|
}
|
|
|
|
|
|
|
|
riscv_clock_event_irq = irq_create_mapping(domain, RV_IRQ_TIMER);
|
|
|
|
if (!riscv_clock_event_irq) {
|
|
|
|
pr_err("Failed to map timer interrupt for node [%pOF]\n", n);
|
|
|
|
return -ENODEV;
|
|
|
|
}
|
|
|
|
|
2022-05-27 08:17:42 +03:00
|
|
|
pr_info("%s: Registering clocksource cpuid [%d] hartid [%lu]\n",
|
2019-02-13 23:18:10 +03:00
|
|
|
__func__, cpuid, hartid);
|
2019-08-03 07:27:20 +03:00
|
|
|
error = clocksource_register_hz(&riscv_clocksource, riscv_timebase);
|
2019-02-13 23:18:10 +03:00
|
|
|
if (error) {
|
|
|
|
pr_err("RISCV timer register failed [%d] for cpu = [%d]\n",
|
|
|
|
error, cpuid);
|
|
|
|
return error;
|
|
|
|
}
|
2018-08-04 11:23:19 +03:00
|
|
|
|
2019-03-23 00:54:11 +03:00
|
|
|
sched_clock_register(riscv_sched_clock, 64, riscv_timebase);
|
2018-12-04 13:29:52 +03:00
|
|
|
|
2020-06-01 12:15:41 +03:00
|
|
|
error = request_percpu_irq(riscv_clock_event_irq,
|
|
|
|
riscv_timer_interrupt,
|
|
|
|
"riscv-timer", &riscv_clock_event);
|
|
|
|
if (error) {
|
|
|
|
pr_err("registering percpu irq failed [%d]\n", error);
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
|
2023-02-01 22:49:42 +03:00
|
|
|
if (riscv_isa_extension_available(NULL, SSTC)) {
|
|
|
|
pr_info("Timer interrupt in S-mode is available via sstc extension\n");
|
|
|
|
static_branch_enable(&riscv_sstc_available);
|
|
|
|
}
|
|
|
|
|
2018-08-04 11:23:19 +03:00
|
|
|
error = cpuhp_setup_state(CPUHP_AP_RISCV_TIMER_STARTING,
|
|
|
|
"clockevents/riscv/timer:starting",
|
|
|
|
riscv_timer_starting_cpu, riscv_timer_dying_cpu);
|
|
|
|
if (error)
|
2019-02-13 23:18:10 +03:00
|
|
|
pr_err("cpu hp setup state failed for RISCV timer [%d]\n",
|
|
|
|
error);
|
2022-07-22 19:50:46 +03:00
|
|
|
|
2018-08-04 11:23:19 +03:00
|
|
|
return error;
|
|
|
|
}
|
|
|
|
|
|
|
|
TIMER_OF_DECLARE(riscv_timer, "riscv", riscv_timer_init_dt);
|