162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci// Copyright (C) 2018 Hangzhou C-SKY Microsystems co.,ltd. 362306a36Sopenharmony_ci 462306a36Sopenharmony_ci#include <linux/init.h> 562306a36Sopenharmony_ci#include <linux/interrupt.h> 662306a36Sopenharmony_ci#include <linux/sched_clock.h> 762306a36Sopenharmony_ci#include <linux/cpu.h> 862306a36Sopenharmony_ci#include <linux/of_irq.h> 962306a36Sopenharmony_ci#include <asm/reg_ops.h> 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ci#include "timer-of.h" 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_ci#define PTIM_CCVR "cr<3, 14>" 1462306a36Sopenharmony_ci#define PTIM_CTLR "cr<0, 14>" 1562306a36Sopenharmony_ci#define PTIM_LVR "cr<6, 14>" 1662306a36Sopenharmony_ci#define PTIM_TSR "cr<1, 14>" 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_cistatic int csky_mptimer_irq; 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_cistatic int csky_mptimer_set_next_event(unsigned long delta, 2162306a36Sopenharmony_ci struct clock_event_device *ce) 2262306a36Sopenharmony_ci{ 2362306a36Sopenharmony_ci mtcr(PTIM_LVR, delta); 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci return 0; 2662306a36Sopenharmony_ci} 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_cistatic int csky_mptimer_shutdown(struct clock_event_device *ce) 2962306a36Sopenharmony_ci{ 3062306a36Sopenharmony_ci mtcr(PTIM_CTLR, 0); 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci return 0; 3362306a36Sopenharmony_ci} 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_cistatic int csky_mptimer_oneshot(struct clock_event_device *ce) 3662306a36Sopenharmony_ci{ 3762306a36Sopenharmony_ci mtcr(PTIM_CTLR, 1); 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_ci return 0; 4062306a36Sopenharmony_ci} 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_cistatic int csky_mptimer_oneshot_stopped(struct clock_event_device *ce) 4362306a36Sopenharmony_ci{ 4462306a36Sopenharmony_ci mtcr(PTIM_CTLR, 0); 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci return 0; 4762306a36Sopenharmony_ci} 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_cistatic DEFINE_PER_CPU(struct timer_of, csky_to) = { 5062306a36Sopenharmony_ci .flags = TIMER_OF_CLOCK, 5162306a36Sopenharmony_ci .clkevt = { 5262306a36Sopenharmony_ci .rating = 300, 5362306a36Sopenharmony_ci .features = CLOCK_EVT_FEAT_PERCPU | 5462306a36Sopenharmony_ci CLOCK_EVT_FEAT_ONESHOT, 5562306a36Sopenharmony_ci .set_state_shutdown = csky_mptimer_shutdown, 5662306a36Sopenharmony_ci .set_state_oneshot = csky_mptimer_oneshot, 5762306a36Sopenharmony_ci .set_state_oneshot_stopped = csky_mptimer_oneshot_stopped, 5862306a36Sopenharmony_ci .set_next_event = csky_mptimer_set_next_event, 5962306a36Sopenharmony_ci }, 6062306a36Sopenharmony_ci}; 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_cistatic irqreturn_t csky_timer_interrupt(int irq, void *dev) 6362306a36Sopenharmony_ci{ 6462306a36Sopenharmony_ci struct timer_of *to = this_cpu_ptr(&csky_to); 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci mtcr(PTIM_TSR, 0); 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci to->clkevt.event_handler(&to->clkevt); 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci return IRQ_HANDLED; 7162306a36Sopenharmony_ci} 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci/* 7462306a36Sopenharmony_ci * clock event for percpu 7562306a36Sopenharmony_ci */ 7662306a36Sopenharmony_cistatic int csky_mptimer_starting_cpu(unsigned int cpu) 7762306a36Sopenharmony_ci{ 7862306a36Sopenharmony_ci struct timer_of *to = per_cpu_ptr(&csky_to, cpu); 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ci to->clkevt.cpumask = cpumask_of(cpu); 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci enable_percpu_irq(csky_mptimer_irq, 0); 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci clockevents_config_and_register(&to->clkevt, timer_of_rate(to), 8562306a36Sopenharmony_ci 2, ULONG_MAX); 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci return 0; 8862306a36Sopenharmony_ci} 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_cistatic int csky_mptimer_dying_cpu(unsigned int cpu) 9162306a36Sopenharmony_ci{ 9262306a36Sopenharmony_ci disable_percpu_irq(csky_mptimer_irq); 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci return 0; 9562306a36Sopenharmony_ci} 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci/* 9862306a36Sopenharmony_ci * clock source 9962306a36Sopenharmony_ci */ 10062306a36Sopenharmony_cistatic u64 notrace sched_clock_read(void) 10162306a36Sopenharmony_ci{ 10262306a36Sopenharmony_ci return (u64)mfcr(PTIM_CCVR); 10362306a36Sopenharmony_ci} 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_cistatic u64 clksrc_read(struct clocksource *c) 10662306a36Sopenharmony_ci{ 10762306a36Sopenharmony_ci return (u64)mfcr(PTIM_CCVR); 10862306a36Sopenharmony_ci} 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_cistruct clocksource csky_clocksource = { 11162306a36Sopenharmony_ci .name = "csky", 11262306a36Sopenharmony_ci .rating = 400, 11362306a36Sopenharmony_ci .mask = CLOCKSOURCE_MASK(32), 11462306a36Sopenharmony_ci .flags = CLOCK_SOURCE_IS_CONTINUOUS, 11562306a36Sopenharmony_ci .read = clksrc_read, 11662306a36Sopenharmony_ci}; 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_cistatic int __init csky_mptimer_init(struct device_node *np) 11962306a36Sopenharmony_ci{ 12062306a36Sopenharmony_ci int ret, cpu, cpu_rollback; 12162306a36Sopenharmony_ci struct timer_of *to = NULL; 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci /* 12462306a36Sopenharmony_ci * Csky_mptimer is designed for C-SKY SMP multi-processors and 12562306a36Sopenharmony_ci * every core has it's own private irq and regs for clkevt and 12662306a36Sopenharmony_ci * clksrc. 12762306a36Sopenharmony_ci * 12862306a36Sopenharmony_ci * The regs is accessed by cpu instruction: mfcr/mtcr instead of 12962306a36Sopenharmony_ci * mmio map style. So we needn't mmio-address in dts, but we still 13062306a36Sopenharmony_ci * need to give clk and irq number. 13162306a36Sopenharmony_ci * 13262306a36Sopenharmony_ci * We use private irq for the mptimer and irq number is the same 13362306a36Sopenharmony_ci * for every core. So we use request_percpu_irq() in timer_of_init. 13462306a36Sopenharmony_ci */ 13562306a36Sopenharmony_ci csky_mptimer_irq = irq_of_parse_and_map(np, 0); 13662306a36Sopenharmony_ci if (csky_mptimer_irq <= 0) 13762306a36Sopenharmony_ci return -EINVAL; 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci ret = request_percpu_irq(csky_mptimer_irq, csky_timer_interrupt, 14062306a36Sopenharmony_ci "csky_mp_timer", &csky_to); 14162306a36Sopenharmony_ci if (ret) 14262306a36Sopenharmony_ci return -EINVAL; 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci for_each_possible_cpu(cpu) { 14562306a36Sopenharmony_ci to = per_cpu_ptr(&csky_to, cpu); 14662306a36Sopenharmony_ci ret = timer_of_init(np, to); 14762306a36Sopenharmony_ci if (ret) 14862306a36Sopenharmony_ci goto rollback; 14962306a36Sopenharmony_ci } 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci clocksource_register_hz(&csky_clocksource, timer_of_rate(to)); 15262306a36Sopenharmony_ci sched_clock_register(sched_clock_read, 32, timer_of_rate(to)); 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci ret = cpuhp_setup_state(CPUHP_AP_CSKY_TIMER_STARTING, 15562306a36Sopenharmony_ci "clockevents/csky/timer:starting", 15662306a36Sopenharmony_ci csky_mptimer_starting_cpu, 15762306a36Sopenharmony_ci csky_mptimer_dying_cpu); 15862306a36Sopenharmony_ci if (ret) 15962306a36Sopenharmony_ci return -EINVAL; 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci return 0; 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_cirollback: 16462306a36Sopenharmony_ci for_each_possible_cpu(cpu_rollback) { 16562306a36Sopenharmony_ci if (cpu_rollback == cpu) 16662306a36Sopenharmony_ci break; 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci to = per_cpu_ptr(&csky_to, cpu_rollback); 16962306a36Sopenharmony_ci timer_of_cleanup(to); 17062306a36Sopenharmony_ci } 17162306a36Sopenharmony_ci return -EINVAL; 17262306a36Sopenharmony_ci} 17362306a36Sopenharmony_ciTIMER_OF_DECLARE(csky_mptimer, "csky,mptimer", csky_mptimer_init); 174