18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (C) 2012 Regents of the University of California 48c2ecf20Sopenharmony_ci * Copyright (C) 2017 SiFive 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * All RISC-V systems have a timer attached to every hart. These timers can 78c2ecf20Sopenharmony_ci * either be read from the "time" and "timeh" CSRs, and can use the SBI to 88c2ecf20Sopenharmony_ci * setup events, or directly accessed using MMIO registers. 98c2ecf20Sopenharmony_ci */ 108c2ecf20Sopenharmony_ci#include <linux/clocksource.h> 118c2ecf20Sopenharmony_ci#include <linux/clockchips.h> 128c2ecf20Sopenharmony_ci#include <linux/cpu.h> 138c2ecf20Sopenharmony_ci#include <linux/delay.h> 148c2ecf20Sopenharmony_ci#include <linux/irq.h> 158c2ecf20Sopenharmony_ci#include <linux/irqdomain.h> 168c2ecf20Sopenharmony_ci#include <linux/sched_clock.h> 178c2ecf20Sopenharmony_ci#include <linux/io-64-nonatomic-lo-hi.h> 188c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 198c2ecf20Sopenharmony_ci#include <linux/of_irq.h> 208c2ecf20Sopenharmony_ci#include <asm/smp.h> 218c2ecf20Sopenharmony_ci#include <asm/sbi.h> 228c2ecf20Sopenharmony_ci#include <asm/timex.h> 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_cistatic int riscv_clock_next_event(unsigned long delta, 258c2ecf20Sopenharmony_ci struct clock_event_device *ce) 268c2ecf20Sopenharmony_ci{ 278c2ecf20Sopenharmony_ci csr_set(CSR_IE, IE_TIE); 288c2ecf20Sopenharmony_ci sbi_set_timer(get_cycles64() + delta); 298c2ecf20Sopenharmony_ci return 0; 308c2ecf20Sopenharmony_ci} 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_cistatic unsigned int riscv_clock_event_irq; 338c2ecf20Sopenharmony_cistatic DEFINE_PER_CPU(struct clock_event_device, riscv_clock_event) = { 348c2ecf20Sopenharmony_ci .name = "riscv_timer_clockevent", 358c2ecf20Sopenharmony_ci .features = CLOCK_EVT_FEAT_ONESHOT, 368c2ecf20Sopenharmony_ci .rating = 100, 378c2ecf20Sopenharmony_ci .set_next_event = riscv_clock_next_event, 388c2ecf20Sopenharmony_ci}; 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_ci/* 418c2ecf20Sopenharmony_ci * It is guaranteed that all the timers across all the harts are synchronized 428c2ecf20Sopenharmony_ci * within one tick of each other, so while this could technically go 438c2ecf20Sopenharmony_ci * backwards when hopping between CPUs, practically it won't happen. 448c2ecf20Sopenharmony_ci */ 458c2ecf20Sopenharmony_cistatic unsigned long long riscv_clocksource_rdtime(struct clocksource *cs) 468c2ecf20Sopenharmony_ci{ 478c2ecf20Sopenharmony_ci return get_cycles64(); 488c2ecf20Sopenharmony_ci} 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_cistatic u64 notrace riscv_sched_clock(void) 518c2ecf20Sopenharmony_ci{ 528c2ecf20Sopenharmony_ci return get_cycles64(); 538c2ecf20Sopenharmony_ci} 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_cistatic struct clocksource riscv_clocksource = { 568c2ecf20Sopenharmony_ci .name = "riscv_clocksource", 578c2ecf20Sopenharmony_ci .rating = 300, 588c2ecf20Sopenharmony_ci .mask = CLOCKSOURCE_MASK(64), 598c2ecf20Sopenharmony_ci .flags = CLOCK_SOURCE_IS_CONTINUOUS, 608c2ecf20Sopenharmony_ci .read = riscv_clocksource_rdtime, 618c2ecf20Sopenharmony_ci}; 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_cistatic int riscv_timer_starting_cpu(unsigned int cpu) 648c2ecf20Sopenharmony_ci{ 658c2ecf20Sopenharmony_ci struct clock_event_device *ce = per_cpu_ptr(&riscv_clock_event, cpu); 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci ce->cpumask = cpumask_of(cpu); 688c2ecf20Sopenharmony_ci ce->irq = riscv_clock_event_irq; 698c2ecf20Sopenharmony_ci clockevents_config_and_register(ce, riscv_timebase, 100, 0x7fffffff); 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci enable_percpu_irq(riscv_clock_event_irq, 728c2ecf20Sopenharmony_ci irq_get_trigger_type(riscv_clock_event_irq)); 738c2ecf20Sopenharmony_ci return 0; 748c2ecf20Sopenharmony_ci} 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_cistatic int riscv_timer_dying_cpu(unsigned int cpu) 778c2ecf20Sopenharmony_ci{ 788c2ecf20Sopenharmony_ci disable_percpu_irq(riscv_clock_event_irq); 798c2ecf20Sopenharmony_ci return 0; 808c2ecf20Sopenharmony_ci} 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci/* called directly from the low-level interrupt handler */ 838c2ecf20Sopenharmony_cistatic irqreturn_t riscv_timer_interrupt(int irq, void *dev_id) 848c2ecf20Sopenharmony_ci{ 858c2ecf20Sopenharmony_ci struct clock_event_device *evdev = this_cpu_ptr(&riscv_clock_event); 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci csr_clear(CSR_IE, IE_TIE); 888c2ecf20Sopenharmony_ci evdev->event_handler(evdev); 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci return IRQ_HANDLED; 918c2ecf20Sopenharmony_ci} 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_cistatic int __init riscv_timer_init_dt(struct device_node *n) 948c2ecf20Sopenharmony_ci{ 958c2ecf20Sopenharmony_ci int cpuid, hartid, error; 968c2ecf20Sopenharmony_ci struct device_node *child; 978c2ecf20Sopenharmony_ci struct irq_domain *domain; 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci hartid = riscv_of_processor_hartid(n); 1008c2ecf20Sopenharmony_ci if (hartid < 0) { 1018c2ecf20Sopenharmony_ci pr_warn("Not valid hartid for node [%pOF] error = [%d]\n", 1028c2ecf20Sopenharmony_ci n, hartid); 1038c2ecf20Sopenharmony_ci return hartid; 1048c2ecf20Sopenharmony_ci } 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci cpuid = riscv_hartid_to_cpuid(hartid); 1078c2ecf20Sopenharmony_ci if (cpuid < 0) { 1088c2ecf20Sopenharmony_ci pr_warn("Invalid cpuid for hartid [%d]\n", hartid); 1098c2ecf20Sopenharmony_ci return cpuid; 1108c2ecf20Sopenharmony_ci } 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci if (cpuid != smp_processor_id()) 1138c2ecf20Sopenharmony_ci return 0; 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci domain = NULL; 1168c2ecf20Sopenharmony_ci child = of_get_compatible_child(n, "riscv,cpu-intc"); 1178c2ecf20Sopenharmony_ci if (!child) { 1188c2ecf20Sopenharmony_ci pr_err("Failed to find INTC node [%pOF]\n", n); 1198c2ecf20Sopenharmony_ci return -ENODEV; 1208c2ecf20Sopenharmony_ci } 1218c2ecf20Sopenharmony_ci domain = irq_find_host(child); 1228c2ecf20Sopenharmony_ci of_node_put(child); 1238c2ecf20Sopenharmony_ci if (!domain) { 1248c2ecf20Sopenharmony_ci pr_err("Failed to find IRQ domain for node [%pOF]\n", n); 1258c2ecf20Sopenharmony_ci return -ENODEV; 1268c2ecf20Sopenharmony_ci } 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci riscv_clock_event_irq = irq_create_mapping(domain, RV_IRQ_TIMER); 1298c2ecf20Sopenharmony_ci if (!riscv_clock_event_irq) { 1308c2ecf20Sopenharmony_ci pr_err("Failed to map timer interrupt for node [%pOF]\n", n); 1318c2ecf20Sopenharmony_ci return -ENODEV; 1328c2ecf20Sopenharmony_ci } 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci pr_info("%s: Registering clocksource cpuid [%d] hartid [%d]\n", 1358c2ecf20Sopenharmony_ci __func__, cpuid, hartid); 1368c2ecf20Sopenharmony_ci error = clocksource_register_hz(&riscv_clocksource, riscv_timebase); 1378c2ecf20Sopenharmony_ci if (error) { 1388c2ecf20Sopenharmony_ci pr_err("RISCV timer register failed [%d] for cpu = [%d]\n", 1398c2ecf20Sopenharmony_ci error, cpuid); 1408c2ecf20Sopenharmony_ci return error; 1418c2ecf20Sopenharmony_ci } 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci sched_clock_register(riscv_sched_clock, 64, riscv_timebase); 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci error = request_percpu_irq(riscv_clock_event_irq, 1468c2ecf20Sopenharmony_ci riscv_timer_interrupt, 1478c2ecf20Sopenharmony_ci "riscv-timer", &riscv_clock_event); 1488c2ecf20Sopenharmony_ci if (error) { 1498c2ecf20Sopenharmony_ci pr_err("registering percpu irq failed [%d]\n", error); 1508c2ecf20Sopenharmony_ci return error; 1518c2ecf20Sopenharmony_ci } 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci error = cpuhp_setup_state(CPUHP_AP_RISCV_TIMER_STARTING, 1548c2ecf20Sopenharmony_ci "clockevents/riscv/timer:starting", 1558c2ecf20Sopenharmony_ci riscv_timer_starting_cpu, riscv_timer_dying_cpu); 1568c2ecf20Sopenharmony_ci if (error) 1578c2ecf20Sopenharmony_ci pr_err("cpu hp setup state failed for RISCV timer [%d]\n", 1588c2ecf20Sopenharmony_ci error); 1598c2ecf20Sopenharmony_ci return error; 1608c2ecf20Sopenharmony_ci} 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ciTIMER_OF_DECLARE(riscv_timer, "riscv", riscv_timer_init_dt); 163