162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 262306a36Sopenharmony_ci// 362306a36Sopenharmony_ci// Copyright 2017-2019 NXP 462306a36Sopenharmony_ci 562306a36Sopenharmony_ci#include <linux/interrupt.h> 662306a36Sopenharmony_ci#include <linux/clockchips.h> 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include "timer-of.h" 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#define CMP_OFFSET 0x10000 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci#define CNTCV_LO 0x8 1362306a36Sopenharmony_ci#define CNTCV_HI 0xc 1462306a36Sopenharmony_ci#define CMPCV_LO (CMP_OFFSET + 0x20) 1562306a36Sopenharmony_ci#define CMPCV_HI (CMP_OFFSET + 0x24) 1662306a36Sopenharmony_ci#define CMPCR (CMP_OFFSET + 0x2c) 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ci#define SYS_CTR_EN 0x1 1962306a36Sopenharmony_ci#define SYS_CTR_IRQ_MASK 0x2 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci#define SYS_CTR_CLK_DIV 0x3 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_cistatic void __iomem *sys_ctr_base __ro_after_init; 2462306a36Sopenharmony_cistatic u32 cmpcr __ro_after_init; 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_cistatic void sysctr_timer_enable(bool enable) 2762306a36Sopenharmony_ci{ 2862306a36Sopenharmony_ci writel(enable ? cmpcr | SYS_CTR_EN : cmpcr, sys_ctr_base + CMPCR); 2962306a36Sopenharmony_ci} 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_cistatic void sysctr_irq_acknowledge(void) 3262306a36Sopenharmony_ci{ 3362306a36Sopenharmony_ci /* 3462306a36Sopenharmony_ci * clear the enable bit(EN =0) will clear 3562306a36Sopenharmony_ci * the status bit(ISTAT = 0), then the interrupt 3662306a36Sopenharmony_ci * signal will be negated(acknowledged). 3762306a36Sopenharmony_ci */ 3862306a36Sopenharmony_ci sysctr_timer_enable(false); 3962306a36Sopenharmony_ci} 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_cistatic inline u64 sysctr_read_counter(void) 4262306a36Sopenharmony_ci{ 4362306a36Sopenharmony_ci u32 cnt_hi, tmp_hi, cnt_lo; 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci do { 4662306a36Sopenharmony_ci cnt_hi = readl_relaxed(sys_ctr_base + CNTCV_HI); 4762306a36Sopenharmony_ci cnt_lo = readl_relaxed(sys_ctr_base + CNTCV_LO); 4862306a36Sopenharmony_ci tmp_hi = readl_relaxed(sys_ctr_base + CNTCV_HI); 4962306a36Sopenharmony_ci } while (tmp_hi != cnt_hi); 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ci return ((u64) cnt_hi << 32) | cnt_lo; 5262306a36Sopenharmony_ci} 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_cistatic int sysctr_set_next_event(unsigned long delta, 5562306a36Sopenharmony_ci struct clock_event_device *evt) 5662306a36Sopenharmony_ci{ 5762306a36Sopenharmony_ci u32 cmp_hi, cmp_lo; 5862306a36Sopenharmony_ci u64 next; 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_ci sysctr_timer_enable(false); 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci next = sysctr_read_counter(); 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci next += delta; 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci cmp_hi = (next >> 32) & 0x00fffff; 6762306a36Sopenharmony_ci cmp_lo = next & 0xffffffff; 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci writel_relaxed(cmp_hi, sys_ctr_base + CMPCV_HI); 7062306a36Sopenharmony_ci writel_relaxed(cmp_lo, sys_ctr_base + CMPCV_LO); 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci sysctr_timer_enable(true); 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci return 0; 7562306a36Sopenharmony_ci} 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_cistatic int sysctr_set_state_oneshot(struct clock_event_device *evt) 7862306a36Sopenharmony_ci{ 7962306a36Sopenharmony_ci return 0; 8062306a36Sopenharmony_ci} 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_cistatic int sysctr_set_state_shutdown(struct clock_event_device *evt) 8362306a36Sopenharmony_ci{ 8462306a36Sopenharmony_ci sysctr_timer_enable(false); 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci return 0; 8762306a36Sopenharmony_ci} 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_cistatic irqreturn_t sysctr_timer_interrupt(int irq, void *dev_id) 9062306a36Sopenharmony_ci{ 9162306a36Sopenharmony_ci struct clock_event_device *evt = dev_id; 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci sysctr_irq_acknowledge(); 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci evt->event_handler(evt); 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci return IRQ_HANDLED; 9862306a36Sopenharmony_ci} 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_cistatic struct timer_of to_sysctr = { 10162306a36Sopenharmony_ci .flags = TIMER_OF_IRQ | TIMER_OF_CLOCK | TIMER_OF_BASE, 10262306a36Sopenharmony_ci .clkevt = { 10362306a36Sopenharmony_ci .name = "i.MX system counter timer", 10462306a36Sopenharmony_ci .features = CLOCK_EVT_FEAT_ONESHOT | 10562306a36Sopenharmony_ci CLOCK_EVT_FEAT_DYNIRQ, 10662306a36Sopenharmony_ci .set_state_oneshot = sysctr_set_state_oneshot, 10762306a36Sopenharmony_ci .set_next_event = sysctr_set_next_event, 10862306a36Sopenharmony_ci .set_state_shutdown = sysctr_set_state_shutdown, 10962306a36Sopenharmony_ci .rating = 200, 11062306a36Sopenharmony_ci }, 11162306a36Sopenharmony_ci .of_irq = { 11262306a36Sopenharmony_ci .handler = sysctr_timer_interrupt, 11362306a36Sopenharmony_ci .flags = IRQF_TIMER, 11462306a36Sopenharmony_ci }, 11562306a36Sopenharmony_ci .of_clk = { 11662306a36Sopenharmony_ci .name = "per", 11762306a36Sopenharmony_ci }, 11862306a36Sopenharmony_ci}; 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_cistatic void __init sysctr_clockevent_init(void) 12162306a36Sopenharmony_ci{ 12262306a36Sopenharmony_ci to_sysctr.clkevt.cpumask = cpu_possible_mask; 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci clockevents_config_and_register(&to_sysctr.clkevt, 12562306a36Sopenharmony_ci timer_of_rate(&to_sysctr), 12662306a36Sopenharmony_ci 0xff, 0x7fffffff); 12762306a36Sopenharmony_ci} 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_cistatic int __init sysctr_timer_init(struct device_node *np) 13062306a36Sopenharmony_ci{ 13162306a36Sopenharmony_ci int ret = 0; 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci ret = timer_of_init(np, &to_sysctr); 13462306a36Sopenharmony_ci if (ret) 13562306a36Sopenharmony_ci return ret; 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci if (!of_property_read_bool(np, "nxp,no-divider")) { 13862306a36Sopenharmony_ci /* system counter clock is divided by 3 internally */ 13962306a36Sopenharmony_ci to_sysctr.of_clk.rate /= SYS_CTR_CLK_DIV; 14062306a36Sopenharmony_ci } 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci sys_ctr_base = timer_of_base(&to_sysctr); 14362306a36Sopenharmony_ci cmpcr = readl(sys_ctr_base + CMPCR); 14462306a36Sopenharmony_ci cmpcr &= ~SYS_CTR_EN; 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci sysctr_clockevent_init(); 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci return 0; 14962306a36Sopenharmony_ci} 15062306a36Sopenharmony_ciTIMER_OF_DECLARE(sysctr_timer, "nxp,sysctr-timer", sysctr_timer_init); 151