linux/drivers/clocksource/timer-imx-sysctr.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0+
   2//
   3// Copyright 2017-2019 NXP
   4
   5#include <linux/interrupt.h>
   6#include <linux/clockchips.h>
   7
   8#include "timer-of.h"
   9
  10#define CMP_OFFSET      0x10000
  11
  12#define CNTCV_LO        0x8
  13#define CNTCV_HI        0xc
  14#define CMPCV_LO        (CMP_OFFSET + 0x20)
  15#define CMPCV_HI        (CMP_OFFSET + 0x24)
  16#define CMPCR           (CMP_OFFSET + 0x2c)
  17
  18#define SYS_CTR_EN              0x1
  19#define SYS_CTR_IRQ_MASK        0x2
  20
  21#define SYS_CTR_CLK_DIV         0x3
  22
  23static void __iomem *sys_ctr_base;
  24static u32 cmpcr;
  25
  26static void sysctr_timer_enable(bool enable)
  27{
  28        writel(enable ? cmpcr | SYS_CTR_EN : cmpcr, sys_ctr_base + CMPCR);
  29}
  30
  31static void sysctr_irq_acknowledge(void)
  32{
  33        /*
  34         * clear the enable bit(EN =0) will clear
  35         * the status bit(ISTAT = 0), then the interrupt
  36         * signal will be negated(acknowledged).
  37         */
  38        sysctr_timer_enable(false);
  39}
  40
  41static inline u64 sysctr_read_counter(void)
  42{
  43        u32 cnt_hi, tmp_hi, cnt_lo;
  44
  45        do {
  46                cnt_hi = readl_relaxed(sys_ctr_base + CNTCV_HI);
  47                cnt_lo = readl_relaxed(sys_ctr_base + CNTCV_LO);
  48                tmp_hi = readl_relaxed(sys_ctr_base + CNTCV_HI);
  49        } while (tmp_hi != cnt_hi);
  50
  51        return  ((u64) cnt_hi << 32) | cnt_lo;
  52}
  53
  54static int sysctr_set_next_event(unsigned long delta,
  55                                 struct clock_event_device *evt)
  56{
  57        u32 cmp_hi, cmp_lo;
  58        u64 next;
  59
  60        sysctr_timer_enable(false);
  61
  62        next = sysctr_read_counter();
  63
  64        next += delta;
  65
  66        cmp_hi = (next >> 32) & 0x00fffff;
  67        cmp_lo = next & 0xffffffff;
  68
  69        writel_relaxed(cmp_hi, sys_ctr_base + CMPCV_HI);
  70        writel_relaxed(cmp_lo, sys_ctr_base + CMPCV_LO);
  71
  72        sysctr_timer_enable(true);
  73
  74        return 0;
  75}
  76
  77static int sysctr_set_state_oneshot(struct clock_event_device *evt)
  78{
  79        return 0;
  80}
  81
  82static int sysctr_set_state_shutdown(struct clock_event_device *evt)
  83{
  84        sysctr_timer_enable(false);
  85
  86        return 0;
  87}
  88
  89static irqreturn_t sysctr_timer_interrupt(int irq, void *dev_id)
  90{
  91        struct clock_event_device *evt = dev_id;
  92
  93        sysctr_irq_acknowledge();
  94
  95        evt->event_handler(evt);
  96
  97        return IRQ_HANDLED;
  98}
  99
 100static struct timer_of to_sysctr = {
 101        .flags = TIMER_OF_IRQ | TIMER_OF_CLOCK | TIMER_OF_BASE,
 102        .clkevt = {
 103                .name                   = "i.MX system counter timer",
 104                .features               = CLOCK_EVT_FEAT_ONESHOT |
 105                                                CLOCK_EVT_FEAT_DYNIRQ,
 106                .set_state_oneshot      = sysctr_set_state_oneshot,
 107                .set_next_event         = sysctr_set_next_event,
 108                .set_state_shutdown     = sysctr_set_state_shutdown,
 109                .rating                 = 200,
 110        },
 111        .of_irq = {
 112                .handler                = sysctr_timer_interrupt,
 113                .flags                  = IRQF_TIMER | IRQF_IRQPOLL,
 114        },
 115        .of_clk = {
 116                .name = "per",
 117        },
 118};
 119
 120static void __init sysctr_clockevent_init(void)
 121{
 122        to_sysctr.clkevt.cpumask = cpumask_of(0);
 123
 124        clockevents_config_and_register(&to_sysctr.clkevt,
 125                                        timer_of_rate(&to_sysctr),
 126                                        0xff, 0x7fffffff);
 127}
 128
 129static int __init sysctr_timer_init(struct device_node *np)
 130{
 131        int ret = 0;
 132
 133        ret = timer_of_init(np, &to_sysctr);
 134        if (ret)
 135                return ret;
 136
 137        /* system counter clock is divided by 3 internally */
 138        to_sysctr.of_clk.rate /= SYS_CTR_CLK_DIV;
 139
 140        sys_ctr_base = timer_of_base(&to_sysctr);
 141        cmpcr = readl(sys_ctr_base + CMPCR);
 142        cmpcr &= ~SYS_CTR_EN;
 143
 144        sysctr_clockevent_init();
 145
 146        return 0;
 147}
 148TIMER_OF_DECLARE(sysctr_timer, "nxp,sysctr-timer", sysctr_timer_init);
 149