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 int sun4i_clkevt_shutdown(struct clock_event_device *evt)
  85{
  86        sun4i_clkevt_time_stop(0);
  87        return 0;
  88}
  89
  90static int sun4i_clkevt_set_oneshot(struct clock_event_device *evt)
  91{
  92        sun4i_clkevt_time_stop(0);
  93        sun4i_clkevt_time_start(0, false);
  94        return 0;
  95}
  96
  97static int sun4i_clkevt_set_periodic(struct clock_event_device *evt)
  98{
  99        sun4i_clkevt_time_stop(0);
 100        sun4i_clkevt_time_setup(0, ticks_per_jiffy);
 101        sun4i_clkevt_time_start(0, true);
 102        return 0;
 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_state_shutdown = sun4i_clkevt_shutdown,
 120        .set_state_periodic = sun4i_clkevt_set_periodic,
 121        .set_state_oneshot = sun4i_clkevt_set_oneshot,
 122        .tick_resume = sun4i_clkevt_shutdown,
 123        .set_next_event = sun4i_clkevt_next_event,
 124};
 125
 126
 127static irqreturn_t sun4i_timer_interrupt(int irq, void *dev_id)
 128{
 129        struct clock_event_device *evt = (struct clock_event_device *)dev_id;
 130
 131        writel(0x1, timer_base + TIMER_IRQ_ST_REG);
 132        evt->event_handler(evt);
 133
 134        return IRQ_HANDLED;
 135}
 136
 137static struct irqaction sun4i_timer_irq = {
 138        .name = "sun4i_timer0",
 139        .flags = IRQF_TIMER | IRQF_IRQPOLL,
 140        .handler = sun4i_timer_interrupt,
 141        .dev_id = &sun4i_clockevent,
 142};
 143
 144static u64 notrace sun4i_timer_sched_read(void)
 145{
 146        return ~readl(timer_base + TIMER_CNTVAL_REG(1));
 147}
 148
 149static void __init sun4i_timer_init(struct device_node *node)
 150{
 151        unsigned long rate = 0;
 152        struct clk *clk;
 153        int ret, irq;
 154        u32 val;
 155
 156        timer_base = of_iomap(node, 0);
 157        if (!timer_base)
 158                panic("Can't map registers");
 159
 160        irq = irq_of_parse_and_map(node, 0);
 161        if (irq <= 0)
 162                panic("Can't parse IRQ");
 163
 164        clk = of_clk_get(node, 0);
 165        if (IS_ERR(clk))
 166                panic("Can't get timer clock");
 167        clk_prepare_enable(clk);
 168
 169        rate = clk_get_rate(clk);
 170
 171        writel(~0, timer_base + TIMER_INTVAL_REG(1));
 172        writel(TIMER_CTL_ENABLE | TIMER_CTL_RELOAD |
 173               TIMER_CTL_CLK_SRC(TIMER_CTL_CLK_SRC_OSC24M),
 174               timer_base + TIMER_CTL_REG(1));
 175
 176        /*
 177         * sched_clock_register does not have priorities, and on sun6i and
 178         * later there is a better sched_clock registered by arm_arch_timer.c
 179         */
 180        if (of_machine_is_compatible("allwinner,sun4i-a10") ||
 181            of_machine_is_compatible("allwinner,sun5i-a13") ||
 182            of_machine_is_compatible("allwinner,sun5i-a10s"))
 183                sched_clock_register(sun4i_timer_sched_read, 32, rate);
 184
 185        clocksource_mmio_init(timer_base + TIMER_CNTVAL_REG(1), node->name,
 186                              rate, 350, 32, clocksource_mmio_readl_down);
 187
 188        ticks_per_jiffy = DIV_ROUND_UP(rate, HZ);
 189
 190        writel(TIMER_CTL_CLK_SRC(TIMER_CTL_CLK_SRC_OSC24M),
 191               timer_base + TIMER_CTL_REG(0));
 192
 193        /* Make sure timer is stopped before playing with interrupts */
 194        sun4i_clkevt_time_stop(0);
 195
 196        sun4i_clockevent.cpumask = cpu_possible_mask;
 197        sun4i_clockevent.irq = irq;
 198
 199        clockevents_config_and_register(&sun4i_clockevent, rate,
 200                                        TIMER_SYNC_TICKS, 0xffffffff);
 201
 202        ret = setup_irq(irq, &sun4i_timer_irq);
 203        if (ret)
 204                pr_warn("failed to setup irq %d\n", irq);
 205
 206        /* Enable timer0 interrupt */
 207        val = readl(timer_base + TIMER_IRQ_EN_REG);
 208        writel(val | TIMER_IRQ_EN(0), timer_base + TIMER_IRQ_EN_REG);
 209}
 210CLOCKSOURCE_OF_DECLARE(sun4i, "allwinner,sun4i-a10-timer",
 211                       sun4i_timer_init);
 212