linux/drivers/clocksource/timer-gx6605s.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2// Copyright (C) 2018 Hangzhou C-SKY Microsystems co.,ltd.
   3
   4#include <linux/init.h>
   5#include <linux/interrupt.h>
   6#include <linux/sched_clock.h>
   7
   8#include "timer-of.h"
   9
  10#define CLKSRC_OFFSET   0x40
  11
  12#define TIMER_STATUS    0x00
  13#define TIMER_VALUE     0x04
  14#define TIMER_CONTRL    0x10
  15#define TIMER_CONFIG    0x20
  16#define TIMER_DIV       0x24
  17#define TIMER_INI       0x28
  18
  19#define GX6605S_STATUS_CLR      BIT(0)
  20#define GX6605S_CONTRL_RST      BIT(0)
  21#define GX6605S_CONTRL_START    BIT(1)
  22#define GX6605S_CONFIG_EN       BIT(0)
  23#define GX6605S_CONFIG_IRQ_EN   BIT(1)
  24
  25static irqreturn_t gx6605s_timer_interrupt(int irq, void *dev)
  26{
  27        struct clock_event_device *ce = dev;
  28        void __iomem *base = timer_of_base(to_timer_of(ce));
  29
  30        writel_relaxed(GX6605S_STATUS_CLR, base + TIMER_STATUS);
  31        writel_relaxed(0, base + TIMER_INI);
  32
  33        ce->event_handler(ce);
  34
  35        return IRQ_HANDLED;
  36}
  37
  38static int gx6605s_timer_set_oneshot(struct clock_event_device *ce)
  39{
  40        void __iomem *base = timer_of_base(to_timer_of(ce));
  41
  42        /* reset and stop counter */
  43        writel_relaxed(GX6605S_CONTRL_RST, base + TIMER_CONTRL);
  44
  45        /* enable with irq and start */
  46        writel_relaxed(GX6605S_CONFIG_EN | GX6605S_CONFIG_IRQ_EN,
  47                       base + TIMER_CONFIG);
  48
  49        return 0;
  50}
  51
  52static int gx6605s_timer_set_next_event(unsigned long delta,
  53                                        struct clock_event_device *ce)
  54{
  55        void __iomem *base = timer_of_base(to_timer_of(ce));
  56
  57        /* use reset to pause timer */
  58        writel_relaxed(GX6605S_CONTRL_RST, base + TIMER_CONTRL);
  59
  60        /* config next timeout value */
  61        writel_relaxed(ULONG_MAX - delta, base + TIMER_INI);
  62        writel_relaxed(GX6605S_CONTRL_START, base + TIMER_CONTRL);
  63
  64        return 0;
  65}
  66
  67static int gx6605s_timer_shutdown(struct clock_event_device *ce)
  68{
  69        void __iomem *base = timer_of_base(to_timer_of(ce));
  70
  71        writel_relaxed(0, base + TIMER_CONTRL);
  72        writel_relaxed(0, base + TIMER_CONFIG);
  73
  74        return 0;
  75}
  76
  77static struct timer_of to = {
  78        .flags = TIMER_OF_IRQ | TIMER_OF_BASE | TIMER_OF_CLOCK,
  79        .clkevt = {
  80                .rating                 = 300,
  81                .features               = CLOCK_EVT_FEAT_DYNIRQ |
  82                                          CLOCK_EVT_FEAT_ONESHOT,
  83                .set_state_shutdown     = gx6605s_timer_shutdown,
  84                .set_state_oneshot      = gx6605s_timer_set_oneshot,
  85                .set_next_event         = gx6605s_timer_set_next_event,
  86                .cpumask                = cpu_possible_mask,
  87        },
  88        .of_irq = {
  89                .handler                = gx6605s_timer_interrupt,
  90                .flags                  = IRQF_TIMER | IRQF_IRQPOLL,
  91        },
  92};
  93
  94static u64 notrace gx6605s_sched_clock_read(void)
  95{
  96        void __iomem *base;
  97
  98        base = timer_of_base(&to) + CLKSRC_OFFSET;
  99
 100        return (u64)readl_relaxed(base + TIMER_VALUE);
 101}
 102
 103static void gx6605s_clkevt_init(void __iomem *base)
 104{
 105        writel_relaxed(0, base + TIMER_DIV);
 106        writel_relaxed(0, base + TIMER_CONFIG);
 107
 108        clockevents_config_and_register(&to.clkevt, timer_of_rate(&to), 2,
 109                                        ULONG_MAX);
 110}
 111
 112static int gx6605s_clksrc_init(void __iomem *base)
 113{
 114        writel_relaxed(0, base + TIMER_DIV);
 115        writel_relaxed(0, base + TIMER_INI);
 116
 117        writel_relaxed(GX6605S_CONTRL_RST, base + TIMER_CONTRL);
 118
 119        writel_relaxed(GX6605S_CONFIG_EN, base + TIMER_CONFIG);
 120
 121        writel_relaxed(GX6605S_CONTRL_START, base + TIMER_CONTRL);
 122
 123        sched_clock_register(gx6605s_sched_clock_read, 32, timer_of_rate(&to));
 124
 125        return clocksource_mmio_init(base + TIMER_VALUE, "gx6605s",
 126                        timer_of_rate(&to), 200, 32, clocksource_mmio_readl_up);
 127}
 128
 129static int __init gx6605s_timer_init(struct device_node *np)
 130{
 131        int ret;
 132
 133        /*
 134         * The timer driver is for nationalchip gx6605s SOC and there are two
 135         * same timer in gx6605s. We use one for clkevt and another for clksrc.
 136         *
 137         * The timer is mmio map to access, so we need give mmio address in dts.
 138         *
 139         * It provides a 32bit countup timer and interrupt will be caused by
 140         * count-overflow.
 141         * So we need set-next-event by ULONG_MAX - delta in TIMER_INI reg.
 142         *
 143         * The counter at 0x0  offset is clock event.
 144         * The counter at 0x40 offset is clock source.
 145         * They are the same in hardware, just different used by driver.
 146         */
 147        ret = timer_of_init(np, &to);
 148        if (ret)
 149                return ret;
 150
 151        gx6605s_clkevt_init(timer_of_base(&to));
 152
 153        return gx6605s_clksrc_init(timer_of_base(&to) + CLKSRC_OFFSET);
 154}
 155TIMER_OF_DECLARE(csky_gx6605s_timer, "csky,gx6605s-timer", gx6605s_timer_init);
 156