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
  40static void __iomem *timer_base;
  41static u32 ticks_per_jiffy;
  42
  43/*
  44 * When we disable a timer, we need to wait at least for 2 cycles of
  45 * the timer source clock. We will use for that the clocksource timer
  46 * that is already setup and runs at the same frequency than the other
  47 * timers, and we never will be disabled.
  48 */
  49static void sun4i_clkevt_sync(void)
  50{
  51        u32 old = readl(timer_base + TIMER_CNTVAL_REG(1));
  52
  53        while ((old - readl(timer_base + TIMER_CNTVAL_REG(1))) < 3)
  54                cpu_relax();
  55}
  56
  57static void sun4i_clkevt_time_stop(u8 timer)
  58{
  59        u32 val = readl(timer_base + TIMER_CTL_REG(timer));
  60        writel(val & ~TIMER_CTL_ENABLE, timer_base + TIMER_CTL_REG(timer));
  61        sun4i_clkevt_sync();
  62}
  63
  64static void sun4i_clkevt_time_setup(u8 timer, unsigned long delay)
  65{
  66        writel(delay, timer_base + TIMER_INTVAL_REG(timer));
  67}
  68
  69static void sun4i_clkevt_time_start(u8 timer, bool periodic)
  70{
  71        u32 val = readl(timer_base + TIMER_CTL_REG(timer));
  72
  73        if (periodic)
  74                val &= ~TIMER_CTL_ONESHOT;
  75        else
  76                val |= TIMER_CTL_ONESHOT;
  77
  78        writel(val | TIMER_CTL_ENABLE | TIMER_CTL_RELOAD,
  79               timer_base + TIMER_CTL_REG(timer));
  80}
  81
  82static void sun4i_clkevt_mode(enum clock_event_mode mode,
  83                              struct clock_event_device *clk)
  84{
  85        switch (mode) {
  86        case CLOCK_EVT_MODE_PERIODIC:
  87                sun4i_clkevt_time_stop(0);
  88                sun4i_clkevt_time_setup(0, ticks_per_jiffy);
  89                sun4i_clkevt_time_start(0, true);
  90                break;
  91        case CLOCK_EVT_MODE_ONESHOT:
  92                sun4i_clkevt_time_stop(0);
  93                sun4i_clkevt_time_start(0, false);
  94                break;
  95        case CLOCK_EVT_MODE_UNUSED:
  96        case CLOCK_EVT_MODE_SHUTDOWN:
  97        default:
  98                sun4i_clkevt_time_stop(0);
  99                break;
 100        }
 101}
 102
 103static int sun4i_clkevt_next_event(unsigned long evt,
 104                                   struct clock_event_device *unused)
 105{
 106        sun4i_clkevt_time_stop(0);
 107        sun4i_clkevt_time_setup(0, evt);
 108        sun4i_clkevt_time_start(0, false);
 109
 110        return 0;
 111}
 112
 113static struct clock_event_device sun4i_clockevent = {
 114        .name = "sun4i_tick",
 115        .rating = 300,
 116        .features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT,
 117        .set_mode = sun4i_clkevt_mode,
 118        .set_next_event = sun4i_clkevt_next_event,
 119};
 120
 121
 122static irqreturn_t sun4i_timer_interrupt(int irq, void *dev_id)
 123{
 124        struct clock_event_device *evt = (struct clock_event_device *)dev_id;
 125
 126        writel(0x1, timer_base + TIMER_IRQ_ST_REG);
 127        evt->event_handler(evt);
 128
 129        return IRQ_HANDLED;
 130}
 131
 132static struct irqaction sun4i_timer_irq = {
 133        .name = "sun4i_timer0",
 134        .flags = IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL,
 135        .handler = sun4i_timer_interrupt,
 136        .dev_id = &sun4i_clockevent,
 137};
 138
 139static u32 sun4i_timer_sched_read(void)
 140{
 141        return ~readl(timer_base + TIMER_CNTVAL_REG(1));
 142}
 143
 144static void __init sun4i_timer_init(struct device_node *node)
 145{
 146        unsigned long rate = 0;
 147        struct clk *clk;
 148        int ret, irq;
 149        u32 val;
 150
 151        timer_base = of_iomap(node, 0);
 152        if (!timer_base)
 153                panic("Can't map registers");
 154
 155        irq = irq_of_parse_and_map(node, 0);
 156        if (irq <= 0)
 157                panic("Can't parse IRQ");
 158
 159        clk = of_clk_get(node, 0);
 160        if (IS_ERR(clk))
 161                panic("Can't get timer clock");
 162        clk_prepare_enable(clk);
 163
 164        rate = clk_get_rate(clk);
 165
 166        writel(~0, timer_base + TIMER_INTVAL_REG(1));
 167        writel(TIMER_CTL_ENABLE | TIMER_CTL_RELOAD |
 168               TIMER_CTL_CLK_SRC(TIMER_CTL_CLK_SRC_OSC24M),
 169               timer_base + TIMER_CTL_REG(1));
 170
 171        setup_sched_clock(sun4i_timer_sched_read, 32, rate);
 172        clocksource_mmio_init(timer_base + TIMER_CNTVAL_REG(1), node->name,
 173                              rate, 300, 32, clocksource_mmio_readl_down);
 174
 175        ticks_per_jiffy = DIV_ROUND_UP(rate, HZ);
 176
 177        writel(TIMER_CTL_CLK_SRC(TIMER_CTL_CLK_SRC_OSC24M),
 178               timer_base + TIMER_CTL_REG(0));
 179
 180        ret = setup_irq(irq, &sun4i_timer_irq);
 181        if (ret)
 182                pr_warn("failed to setup irq %d\n", irq);
 183
 184        /* Enable timer0 interrupt */
 185        val = readl(timer_base + TIMER_IRQ_EN_REG);
 186        writel(val | TIMER_IRQ_EN(0), timer_base + TIMER_IRQ_EN_REG);
 187
 188        sun4i_clockevent.cpumask = cpumask_of(0);
 189
 190        clockevents_config_and_register(&sun4i_clockevent, rate, 0x1,
 191                                        0xffffffff);
 192}
 193CLOCKSOURCE_OF_DECLARE(sun4i, "allwinner,sun4i-timer",
 194                       sun4i_timer_init);
 195
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.