linux/drivers/clocksource/timer-prima2.c
<<
>>
Prefs
   1/*
   2 * System timer for CSR SiRFprimaII
   3 *
   4 * Copyright (c) 2011 Cambridge Silicon Radio Limited, a CSR plc group company.
   5 *
   6 * Licensed under GPLv2 or later.
   7 */
   8
   9#include <linux/kernel.h>
  10#include <linux/interrupt.h>
  11#include <linux/clockchips.h>
  12#include <linux/clocksource.h>
  13#include <linux/bitops.h>
  14#include <linux/irq.h>
  15#include <linux/clk.h>
  16#include <linux/err.h>
  17#include <linux/slab.h>
  18#include <linux/of.h>
  19#include <linux/of_irq.h>
  20#include <linux/of_address.h>
  21#include <linux/sched_clock.h>
  22#include <asm/mach/time.h>
  23
  24#define SIRFSOC_TIMER_COUNTER_LO        0x0000
  25#define SIRFSOC_TIMER_COUNTER_HI        0x0004
  26#define SIRFSOC_TIMER_MATCH_0           0x0008
  27#define SIRFSOC_TIMER_MATCH_1           0x000C
  28#define SIRFSOC_TIMER_MATCH_2           0x0010
  29#define SIRFSOC_TIMER_MATCH_3           0x0014
  30#define SIRFSOC_TIMER_MATCH_4           0x0018
  31#define SIRFSOC_TIMER_MATCH_5           0x001C
  32#define SIRFSOC_TIMER_STATUS            0x0020
  33#define SIRFSOC_TIMER_INT_EN            0x0024
  34#define SIRFSOC_TIMER_WATCHDOG_EN       0x0028
  35#define SIRFSOC_TIMER_DIV               0x002C
  36#define SIRFSOC_TIMER_LATCH             0x0030
  37#define SIRFSOC_TIMER_LATCHED_LO        0x0034
  38#define SIRFSOC_TIMER_LATCHED_HI        0x0038
  39
  40#define SIRFSOC_TIMER_WDT_INDEX         5
  41
  42#define SIRFSOC_TIMER_LATCH_BIT  BIT(0)
  43
  44#define SIRFSOC_TIMER_REG_CNT 11
  45
  46static const u32 sirfsoc_timer_reg_list[SIRFSOC_TIMER_REG_CNT] = {
  47        SIRFSOC_TIMER_MATCH_0, SIRFSOC_TIMER_MATCH_1, SIRFSOC_TIMER_MATCH_2,
  48        SIRFSOC_TIMER_MATCH_3, SIRFSOC_TIMER_MATCH_4, SIRFSOC_TIMER_MATCH_5,
  49        SIRFSOC_TIMER_INT_EN, SIRFSOC_TIMER_WATCHDOG_EN, SIRFSOC_TIMER_DIV,
  50        SIRFSOC_TIMER_LATCHED_LO, SIRFSOC_TIMER_LATCHED_HI,
  51};
  52
  53static u32 sirfsoc_timer_reg_val[SIRFSOC_TIMER_REG_CNT];
  54
  55static void __iomem *sirfsoc_timer_base;
  56
  57/* timer0 interrupt handler */
  58static irqreturn_t sirfsoc_timer_interrupt(int irq, void *dev_id)
  59{
  60        struct clock_event_device *ce = dev_id;
  61
  62        WARN_ON(!(readl_relaxed(sirfsoc_timer_base + SIRFSOC_TIMER_STATUS) & BIT(0)));
  63
  64        /* clear timer0 interrupt */
  65        writel_relaxed(BIT(0), sirfsoc_timer_base + SIRFSOC_TIMER_STATUS);
  66
  67        ce->event_handler(ce);
  68
  69        return IRQ_HANDLED;
  70}
  71
  72/* read 64-bit timer counter */
  73static cycle_t sirfsoc_timer_read(struct clocksource *cs)
  74{
  75        u64 cycles;
  76
  77        /* latch the 64-bit timer counter */
  78        writel_relaxed(SIRFSOC_TIMER_LATCH_BIT, sirfsoc_timer_base + SIRFSOC_TIMER_LATCH);
  79        cycles = readl_relaxed(sirfsoc_timer_base + SIRFSOC_TIMER_LATCHED_HI);
  80        cycles = (cycles << 32) | readl_relaxed(sirfsoc_timer_base + SIRFSOC_TIMER_LATCHED_LO);
  81
  82        return cycles;
  83}
  84
  85static int sirfsoc_timer_set_next_event(unsigned long delta,
  86        struct clock_event_device *ce)
  87{
  88        unsigned long now, next;
  89
  90        writel_relaxed(SIRFSOC_TIMER_LATCH_BIT, sirfsoc_timer_base + SIRFSOC_TIMER_LATCH);
  91        now = readl_relaxed(sirfsoc_timer_base + SIRFSOC_TIMER_LATCHED_LO);
  92        next = now + delta;
  93        writel_relaxed(next, sirfsoc_timer_base + SIRFSOC_TIMER_MATCH_0);
  94        writel_relaxed(SIRFSOC_TIMER_LATCH_BIT, sirfsoc_timer_base + SIRFSOC_TIMER_LATCH);
  95        now = readl_relaxed(sirfsoc_timer_base + SIRFSOC_TIMER_LATCHED_LO);
  96
  97        return next - now > delta ? -ETIME : 0;
  98}
  99
 100static void sirfsoc_timer_set_mode(enum clock_event_mode mode,
 101        struct clock_event_device *ce)
 102{
 103        u32 val = readl_relaxed(sirfsoc_timer_base + SIRFSOC_TIMER_INT_EN);
 104        switch (mode) {
 105        case CLOCK_EVT_MODE_PERIODIC:
 106                WARN_ON(1);
 107                break;
 108        case CLOCK_EVT_MODE_ONESHOT:
 109                writel_relaxed(val | BIT(0), sirfsoc_timer_base + SIRFSOC_TIMER_INT_EN);
 110                break;
 111        case CLOCK_EVT_MODE_SHUTDOWN:
 112                writel_relaxed(val & ~BIT(0), sirfsoc_timer_base + SIRFSOC_TIMER_INT_EN);
 113                break;
 114        case CLOCK_EVT_MODE_UNUSED:
 115        case CLOCK_EVT_MODE_RESUME:
 116                break;
 117        }
 118}
 119
 120static void sirfsoc_clocksource_suspend(struct clocksource *cs)
 121{
 122        int i;
 123
 124        writel_relaxed(SIRFSOC_TIMER_LATCH_BIT, sirfsoc_timer_base + SIRFSOC_TIMER_LATCH);
 125
 126        for (i = 0; i < SIRFSOC_TIMER_REG_CNT; i++)
 127                sirfsoc_timer_reg_val[i] = readl_relaxed(sirfsoc_timer_base + sirfsoc_timer_reg_list[i]);
 128}
 129
 130static void sirfsoc_clocksource_resume(struct clocksource *cs)
 131{
 132        int i;
 133
 134        for (i = 0; i < SIRFSOC_TIMER_REG_CNT - 2; i++)
 135                writel_relaxed(sirfsoc_timer_reg_val[i], sirfsoc_timer_base + sirfsoc_timer_reg_list[i]);
 136
 137        writel_relaxed(sirfsoc_timer_reg_val[SIRFSOC_TIMER_REG_CNT - 2], sirfsoc_timer_base + SIRFSOC_TIMER_COUNTER_LO);
 138        writel_relaxed(sirfsoc_timer_reg_val[SIRFSOC_TIMER_REG_CNT - 1], sirfsoc_timer_base + SIRFSOC_TIMER_COUNTER_HI);
 139}
 140
 141static struct clock_event_device sirfsoc_clockevent = {
 142        .name = "sirfsoc_clockevent",
 143        .rating = 200,
 144        .features = CLOCK_EVT_FEAT_ONESHOT,
 145        .set_mode = sirfsoc_timer_set_mode,
 146        .set_next_event = sirfsoc_timer_set_next_event,
 147};
 148
 149static struct clocksource sirfsoc_clocksource = {
 150        .name = "sirfsoc_clocksource",
 151        .rating = 200,
 152        .mask = CLOCKSOURCE_MASK(64),
 153        .flags = CLOCK_SOURCE_IS_CONTINUOUS,
 154        .read = sirfsoc_timer_read,
 155        .suspend = sirfsoc_clocksource_suspend,
 156        .resume = sirfsoc_clocksource_resume,
 157};
 158
 159static struct irqaction sirfsoc_timer_irq = {
 160        .name = "sirfsoc_timer0",
 161        .flags = IRQF_TIMER,
 162        .irq = 0,
 163        .handler = sirfsoc_timer_interrupt,
 164        .dev_id = &sirfsoc_clockevent,
 165};
 166
 167/* Overwrite weak default sched_clock with more precise one */
 168static u32 notrace sirfsoc_read_sched_clock(void)
 169{
 170        return (u32)(sirfsoc_timer_read(NULL) & 0xffffffff);
 171}
 172
 173static void __init sirfsoc_clockevent_init(void)
 174{
 175        sirfsoc_clockevent.cpumask = cpumask_of(0);
 176        clockevents_config_and_register(&sirfsoc_clockevent, CLOCK_TICK_RATE,
 177                                        2, -2);
 178}
 179
 180/* initialize the kernel jiffy timer source */
 181static void __init sirfsoc_prima2_timer_init(struct device_node *np)
 182{
 183        unsigned long rate;
 184        struct clk *clk;
 185
 186        /* timer's input clock is io clock */
 187        clk = clk_get_sys("io", NULL);
 188
 189        BUG_ON(IS_ERR(clk));
 190
 191        rate = clk_get_rate(clk);
 192
 193        BUG_ON(rate < CLOCK_TICK_RATE);
 194        BUG_ON(rate % CLOCK_TICK_RATE);
 195
 196        sirfsoc_timer_base = of_iomap(np, 0);
 197        if (!sirfsoc_timer_base)
 198                panic("unable to map timer cpu registers\n");
 199
 200        sirfsoc_timer_irq.irq = irq_of_parse_and_map(np, 0);
 201
 202        writel_relaxed(rate / CLOCK_TICK_RATE / 2 - 1, sirfsoc_timer_base + SIRFSOC_TIMER_DIV);
 203        writel_relaxed(0, sirfsoc_timer_base + SIRFSOC_TIMER_COUNTER_LO);
 204        writel_relaxed(0, sirfsoc_timer_base + SIRFSOC_TIMER_COUNTER_HI);
 205        writel_relaxed(BIT(0), sirfsoc_timer_base + SIRFSOC_TIMER_STATUS);
 206
 207        BUG_ON(clocksource_register_hz(&sirfsoc_clocksource, CLOCK_TICK_RATE));
 208
 209        setup_sched_clock(sirfsoc_read_sched_clock, 32, CLOCK_TICK_RATE);
 210
 211        BUG_ON(setup_irq(sirfsoc_timer_irq.irq, &sirfsoc_timer_irq));
 212
 213        sirfsoc_clockevent_init();
 214}
 215CLOCKSOURCE_OF_DECLARE(sirfsoc_prima2_timer, "sirf,prima2-tick", sirfsoc_prima2_timer_init);
 216
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.