linux/drivers/clocksource/sun4i_timer.c
<<
>>
Prefs
   1/*
   2 * Allwinner A1X SoCs timer handling.
   3 *
   4 * Copyright (C) 2012 Maxime Ripard
   5 *
   6 * Maxime Ripard <maxime.ripard@free-electrons.com>
   7 *
   8 * Based on code from
   9 * Allwinner Technology Co., Ltd. <www.allwinnertech.com>
  10 * Benn Huang <benn@allwinnertech.com>
  11 *
  12 * This file is licensed under the terms of the GNU General Public
  13 * License version 2.  This program is licensed "as is" without any
  14 * warranty of any kind, whether express or implied.
  15 */
  16
  17#include <linux/clk.h>
  18#include <linux/clockchips.h>
  19#include <linux/interrupt.h>
  20#include <linux/irq.h>
  21#include <linux/irqreturn.h>
  22#include <linux/sched_clock.h>
  23#include <linux/of.h>
  24#include <linux/of_address.h>
  25#include <linux/of_irq.h>
  26
  27#define TIMER_IRQ_EN_REG        0x00
  28#define TIMER_IRQ_EN(val)               BIT(val)
  29#define TIMER_IRQ_ST_REG        0x04
  30#define TIMER_CTL_REG(val)      (0x10 * val + 0x10)
  31#define TIMER_CTL_ENABLE                BIT(0)
  32#define TIMER_CTL_RELOAD                BIT(1)
  33#define TIMER_CTL_CLK_SRC(val)          (((val) & 0x3) << 2)
  34#define TIMER_CTL_CLK_SRC_OSC24M                (1)
  35#define TIMER_CTL_CLK_PRES(val)         (((val) & 0x7) << 4)
  36#define TIMER_CTL_ONESHOT               BIT(7)
  37#define TIMER_INTVAL_REG(val)   (0x10 * (val) + 0x14)
  38#define TIMER_CNTVAL_REG(val)   (0x10 * (val) + 0x18)
  39
  40#define TIMER_SYNC_TICKS        3
  41
  42static void __iomem *timer_base;
  43static u32 ticks_per_jiffy;
  44
  45/*
  46 * When we disable a timer, we need to wait at least for 2 cycles of
  47 * the timer source clock. We will use for that the clocksource timer
  48 * that is already setup and runs at the same frequency than the other
  49 * timers, and we never will be disabled.
  50 */
  51static void sun4i_clkevt_sync(void)
  52{
  53        u32 old = readl(timer_base + TIMER_CNTVAL_REG(1));
  54
  55        while ((old - readl(timer_base + TIMER_CNTVAL_REG(1))) < TIMER_SYNC_TICKS)
  56                cpu_relax();
  57}
  58
  59static void sun4i_clkevt_time_stop(u8 timer)
  60{
  61        u32 val = readl(timer_base + TIMER_CTL_REG(timer));
  62        writel(val & ~TIMER_CTL_ENABLE, timer_base + TIMER_CTL_REG(timer));
  63        sun4i_clkevt_sync();
  64}
  65
  66static void sun4i_clkevt_time_setup(u8 timer, unsigned long delay)
  67{
  68        writel(delay, timer_base + TIMER_INTVAL_REG(timer));
  69}
  70
  71static void sun4i_clkevt_time_start(u8 timer, bool periodic)
  72{
  73        u32 val = readl(timer_base + TIMER_CTL_REG(timer));
  74
  75        if (periodic)
  76                val &= ~TIMER_CTL_ONESHOT;
  77        else
  78                val |= TIMER_CTL_ONESHOT;
  79
  80        writel(val | TIMER_CTL_ENABLE | TIMER_CTL_RELOAD,
  81               timer_base + TIMER_CTL_REG(timer));
  82}
  83
  84static void sun4i_clkevt_mode(enum clock_event_mode mode,
  85                              struct clock_event_device *clk)
  86{
  87        switch (mode) {
  88        case CLOCK_EVT_MODE_PERIODIC:
  89                sun4i_clkevt_time_stop(0);
  90                sun4i_clkevt_time_setup(0, ticks_per_jiffy);
  91                sun4i_clkevt_time_start(0, true);
  92                break;
  93        case CLOCK_EVT_MODE_ONESHOT:
  94                sun4i_clkevt_time_stop(0);
  95                sun4i_clkevt_time_start(0, false);
  96                break;
  97        case CLOCK_EVT_MODE_UNUSED:
  98        case CLOCK_EVT_MODE_SHUTDOWN:
  99        default:
 100                sun4i_clkevt_time_stop(0);
 101                break;
 102        }
 103}
 104
 105static int sun4i_clkevt_next_event(unsigned long evt,
 106                                   struct clock_event_device *unused)
 107{
 108        sun4i_clkevt_time_stop(0);
 109        sun4i_clkevt_time_setup(0, evt - TIMER_SYNC_TICKS);
 110        sun4i_clkevt_time_start(0, false);
 111
 112        return 0;
 113}
 114
 115static struct clock_event_device sun4i_clockevent = {
 116        .name = "sun4i_tick",
 117        .rating = 350,
 118        .features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT,
 119        .set_mode = sun4i_clkevt_mode,
 120        .set_next_event = sun4i_clkevt_next_event,
 121};
 122
 123
 124static irqreturn_t sun4i_timer_interrupt(int irq, void *dev_id)
 125{
 126        struct clock_event_device *evt = (struct clock_event_device *)dev_id;
 127
 128        writel(0x1, timer_base + TIMER_IRQ_ST_REG);
 129        evt->event_handler(evt);
 130
 131        return IRQ_HANDLED;
 132}
 133
 134static struct irqaction sun4i_timer_irq = {
 135        .name = "sun4i_timer0",
 136        .flags = IRQF_TIMER | IRQF_IRQPOLL,
 137        .handler = sun4i_timer_interrupt,
 138        .dev_id = &sun4i_clockevent,
 139};
 140
 141static u64 notrace sun4i_timer_sched_read(void)
 142{
 143        return ~readl(timer_base + TIMER_CNTVAL_REG(1));
 144}
 145
 146static void __init sun4i_timer_init(struct device_node *node)
 147{
 148        unsigned long rate = 0;
 149        struct clk *clk;
 150        int ret, irq;
 151        u32 val;
 152
 153        timer_base = of_iomap(node, 0);
 154        if (!timer_base)
 155                panic("Can't map registers");
 156
 157        irq = irq_of_parse_and_map(node, 0);
 158        if (irq <= 0)
 159                panic("Can't parse IRQ");
 160
 161        clk = of_clk_get(node, 0);
 162        if (IS_ERR(clk))
 163                panic("Can't get timer clock");
 164        clk_prepare_enable(clk);
 165
 166        rate = clk_get_rate(clk);
 167
 168        writel(~0, timer_base + TIMER_INTVAL_REG(1));
 169        writel(TIMER_CTL_ENABLE | TIMER_CTL_RELOAD |
 170               TIMER_CTL_CLK_SRC(TIMER_CTL_CLK_SRC_OSC24M),
 171               timer_base + TIMER_CTL_REG(1));
 172
 173        sched_clock_register(sun4i_timer_sched_read, 32, rate);
 174        clocksource_mmio_init(timer_base + TIMER_CNTVAL_REG(1), node->name,
 175                              rate, 350, 32, clocksource_mmio_readl_down);
 176
 177        ticks_per_jiffy = DIV_ROUND_UP(rate, HZ);
 178
 179        writel(TIMER_CTL_CLK_SRC(TIMER_CTL_CLK_SRC_OSC24M),
 180               timer_base + TIMER_CTL_REG(0));
 181
 182        /* Make sure timer is stopped before playing with interrupts */
 183        sun4i_clkevt_time_stop(0);
 184
 185        ret = setup_irq(irq, &sun4i_timer_irq);
 186        if (ret)
 187                pr_warn("failed to setup irq %d\n", irq);
 188
 189        /* Enable timer0 interrupt */
 190        val = readl(timer_base + TIMER_IRQ_EN_REG);
 191        writel(val | TIMER_IRQ_EN(0), timer_base + TIMER_IRQ_EN_REG);
 192
 193        sun4i_clockevent.cpumask = cpu_possible_mask;
 194        sun4i_clockevent.irq = irq;
 195
 196        clockevents_config_and_register(&sun4i_clockevent, rate,
 197                                        TIMER_SYNC_TICKS, 0xffffffff);
 198}
 199CLOCKSOURCE_OF_DECLARE(sun4i, "allwinner,sun4i-timer",
 200                       sun4i_timer_init);
 201