linux/drivers/clocksource/timer-atcpit100.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2// Copyright (C) 2005-2017 Andes Technology Corporation
   3/*
   4 *  Andestech ATCPIT100 Timer Device Driver Implementation
   5 * Rick Chen, Andes Technology Corporation <rick@andestech.com>
   6 *
   7 */
   8
   9#include <linux/irq.h>
  10#include <linux/clocksource.h>
  11#include <linux/clockchips.h>
  12#include <linux/interrupt.h>
  13#include <linux/ioport.h>
  14#include <linux/cpufreq.h>
  15#include <linux/sched.h>
  16#include <linux/sched_clock.h>
  17#include <linux/of_address.h>
  18#include <linux/of_irq.h>
  19#include <linux/of_platform.h>
  20#include "timer-of.h"
  21#ifdef CONFIG_NDS32
  22#include <asm/vdso_timer_info.h>
  23#endif
  24
  25/*
  26 * Definition of register offsets
  27 */
  28
  29/* ID and Revision Register */
  30#define ID_REV          0x0
  31
  32/* Configuration Register */
  33#define CFG             0x10
  34
  35/* Interrupt Enable Register */
  36#define INT_EN          0x14
  37#define CH_INT_EN(c, i) ((1<<i)<<(4*c))
  38#define CH0INT0EN       0x01
  39
  40/* Interrupt Status Register */
  41#define INT_STA         0x18
  42#define CH0INT0         0x01
  43
  44/* Channel Enable Register */
  45#define CH_EN           0x1C
  46#define CH0TMR0EN       0x1
  47#define CH1TMR0EN       0x10
  48
  49/* Channel 0 , 1 Control Register */
  50#define CH0_CTL         (0x20)
  51#define CH1_CTL         (0x20 + 0x10)
  52
  53/* Channel clock source , bit 3 , 0:External clock , 1:APB clock */
  54#define APB_CLK         BIT(3)
  55
  56/* Channel mode , bit 0~2 */
  57#define TMR_32          0x1
  58#define TMR_16          0x2
  59#define TMR_8           0x3
  60
  61/* Channel 0 , 1 Reload Register */
  62#define CH0_REL         (0x24)
  63#define CH1_REL         (0x24 + 0x10)
  64
  65/* Channel 0 , 1 Counter Register */
  66#define CH0_CNT         (0x28)
  67#define CH1_CNT         (0x28 + 0x10)
  68
  69#define TIMER_SYNC_TICKS        3
  70
  71static void atcpit100_ch1_tmr0_en(void __iomem *base)
  72{
  73        writel(~0, base + CH1_REL);
  74        writel(APB_CLK|TMR_32, base + CH1_CTL);
  75}
  76
  77static void atcpit100_ch0_tmr0_en(void __iomem *base)
  78{
  79        writel(APB_CLK|TMR_32, base + CH0_CTL);
  80}
  81
  82static void atcpit100_clkevt_time_setup(void __iomem *base, unsigned long delay)
  83{
  84        writel(delay, base + CH0_CNT);
  85        writel(delay, base + CH0_REL);
  86}
  87
  88static void atcpit100_timer_clear_interrupt(void __iomem *base)
  89{
  90        u32 val;
  91
  92        val = readl(base + INT_STA);
  93        writel(val | CH0INT0, base + INT_STA);
  94}
  95
  96static void atcpit100_clocksource_start(void __iomem *base)
  97{
  98        u32 val;
  99
 100        val = readl(base + CH_EN);
 101        writel(val | CH1TMR0EN, base + CH_EN);
 102}
 103
 104static void atcpit100_clkevt_time_start(void __iomem *base)
 105{
 106        u32 val;
 107
 108        val = readl(base + CH_EN);
 109        writel(val | CH0TMR0EN, base + CH_EN);
 110}
 111
 112static void atcpit100_clkevt_time_stop(void __iomem *base)
 113{
 114        u32 val;
 115
 116        atcpit100_timer_clear_interrupt(base);
 117        val = readl(base + CH_EN);
 118        writel(val & ~CH0TMR0EN, base + CH_EN);
 119}
 120
 121static int atcpit100_clkevt_next_event(unsigned long evt,
 122        struct clock_event_device *clkevt)
 123{
 124        u32 val;
 125        struct timer_of *to = to_timer_of(clkevt);
 126
 127        val = readl(timer_of_base(to) + CH_EN);
 128        writel(val & ~CH0TMR0EN, timer_of_base(to) + CH_EN);
 129        writel(evt, timer_of_base(to) + CH0_REL);
 130        writel(val | CH0TMR0EN, timer_of_base(to) + CH_EN);
 131
 132        return 0;
 133}
 134
 135static int atcpit100_clkevt_set_periodic(struct clock_event_device *evt)
 136{
 137        struct timer_of *to = to_timer_of(evt);
 138
 139        atcpit100_clkevt_time_setup(timer_of_base(to), timer_of_period(to));
 140        atcpit100_clkevt_time_start(timer_of_base(to));
 141
 142        return 0;
 143}
 144static int atcpit100_clkevt_shutdown(struct clock_event_device *evt)
 145{
 146        struct timer_of *to = to_timer_of(evt);
 147
 148        atcpit100_clkevt_time_stop(timer_of_base(to));
 149
 150        return 0;
 151}
 152static int atcpit100_clkevt_set_oneshot(struct clock_event_device *evt)
 153{
 154        struct timer_of *to = to_timer_of(evt);
 155        u32 val;
 156
 157        writel(~0x0, timer_of_base(to) + CH0_REL);
 158        val = readl(timer_of_base(to) + CH_EN);
 159        writel(val | CH0TMR0EN, timer_of_base(to) + CH_EN);
 160
 161        return 0;
 162}
 163
 164static irqreturn_t atcpit100_timer_interrupt(int irq, void *dev_id)
 165{
 166        struct clock_event_device *evt = (struct clock_event_device *)dev_id;
 167        struct timer_of *to = to_timer_of(evt);
 168
 169        atcpit100_timer_clear_interrupt(timer_of_base(to));
 170
 171        evt->event_handler(evt);
 172
 173        return IRQ_HANDLED;
 174}
 175
 176static struct timer_of to = {
 177        .flags = TIMER_OF_IRQ | TIMER_OF_CLOCK | TIMER_OF_BASE,
 178
 179        .clkevt = {
 180                .name = "atcpit100_tick",
 181                .rating = 300,
 182                .features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT,
 183                .set_state_shutdown = atcpit100_clkevt_shutdown,
 184                .set_state_periodic = atcpit100_clkevt_set_periodic,
 185                .set_state_oneshot = atcpit100_clkevt_set_oneshot,
 186                .tick_resume = atcpit100_clkevt_shutdown,
 187                .set_next_event = atcpit100_clkevt_next_event,
 188                .cpumask = cpu_possible_mask,
 189        },
 190
 191        .of_irq = {
 192                .handler = atcpit100_timer_interrupt,
 193                .flags = IRQF_TIMER | IRQF_IRQPOLL,
 194        },
 195
 196        /*
 197         * FIXME: we currently only support clocking using PCLK
 198         * and using EXTCLK is not supported in the driver.
 199         */
 200        .of_clk = {
 201                .name = "PCLK",
 202        }
 203};
 204
 205static u64 notrace atcpit100_timer_sched_read(void)
 206{
 207        return ~readl(timer_of_base(&to) + CH1_CNT);
 208}
 209
 210#ifdef CONFIG_NDS32
 211static void fill_vdso_need_info(struct device_node *node)
 212{
 213        struct resource timer_res;
 214        of_address_to_resource(node, 0, &timer_res);
 215        timer_info.mapping_base = (unsigned long)timer_res.start;
 216        timer_info.cycle_count_down = true;
 217        timer_info.cycle_count_reg_offset = CH1_CNT;
 218}
 219#endif
 220
 221static int __init atcpit100_timer_init(struct device_node *node)
 222{
 223        int ret;
 224        u32 val;
 225        void __iomem *base;
 226
 227        ret = timer_of_init(node, &to);
 228        if (ret)
 229                return ret;
 230
 231        base = timer_of_base(&to);
 232
 233        sched_clock_register(atcpit100_timer_sched_read, 32,
 234                timer_of_rate(&to));
 235
 236        ret = clocksource_mmio_init(base + CH1_CNT,
 237                node->name, timer_of_rate(&to), 300, 32,
 238                clocksource_mmio_readl_down);
 239
 240        if (ret) {
 241                pr_err("Failed to register clocksource\n");
 242                return ret;
 243        }
 244
 245        /* clear channel 0 timer0 interrupt */
 246        atcpit100_timer_clear_interrupt(base);
 247
 248        clockevents_config_and_register(&to.clkevt, timer_of_rate(&to),
 249                                        TIMER_SYNC_TICKS, 0xffffffff);
 250        atcpit100_ch0_tmr0_en(base);
 251        atcpit100_ch1_tmr0_en(base);
 252        atcpit100_clocksource_start(base);
 253        atcpit100_clkevt_time_start(base);
 254
 255        /* Enable channel 0 timer0 interrupt */
 256        val = readl(base + INT_EN);
 257        writel(val | CH0INT0EN, base + INT_EN);
 258
 259#ifdef CONFIG_NDS32
 260        fill_vdso_need_info(node);
 261#endif
 262
 263        return ret;
 264}
 265
 266TIMER_OF_DECLARE(atcpit100, "andestech,atcpit100", atcpit100_timer_init);
 267