linux/drivers/clocksource/timer-keystone.c
<<
>>
Prefs
   1/*
   2 * Keystone broadcast clock-event
   3 *
   4 * Copyright 2013 Texas Instruments, Inc.
   5 *
   6 * Author: Ivan Khoronzhuk <ivan.khoronzhuk@ti.com>
   7 *
   8 * This program is free software; you can redistribute it and/or modify
   9 * it under the terms of the GNU General Public License version 2 as
  10 * published by the Free Software Foundation.
  11 *
  12 */
  13
  14#include <linux/clk.h>
  15#include <linux/clockchips.h>
  16#include <linux/clocksource.h>
  17#include <linux/interrupt.h>
  18#include <linux/of_address.h>
  19#include <linux/of_irq.h>
  20
  21#define TIMER_NAME                      "timer-keystone"
  22
  23/* Timer register offsets */
  24#define TIM12                           0x10
  25#define TIM34                           0x14
  26#define PRD12                           0x18
  27#define PRD34                           0x1c
  28#define TCR                             0x20
  29#define TGCR                            0x24
  30#define INTCTLSTAT                      0x44
  31
  32/* Timer register bitfields */
  33#define TCR_ENAMODE_MASK                0xC0
  34#define TCR_ENAMODE_ONESHOT_MASK        0x40
  35#define TCR_ENAMODE_PERIODIC_MASK       0x80
  36
  37#define TGCR_TIM_UNRESET_MASK           0x03
  38#define INTCTLSTAT_ENINT_MASK           0x01
  39
  40/**
  41 * struct keystone_timer: holds timer's data
  42 * @base: timer memory base address
  43 * @hz_period: cycles per HZ period
  44 * @event_dev: event device based on timer
  45 */
  46static struct keystone_timer {
  47        void __iomem *base;
  48        unsigned long hz_period;
  49        struct clock_event_device event_dev;
  50} timer;
  51
  52static inline u32 keystone_timer_readl(unsigned long rg)
  53{
  54        return readl_relaxed(timer.base + rg);
  55}
  56
  57static inline void keystone_timer_writel(u32 val, unsigned long rg)
  58{
  59        writel_relaxed(val, timer.base + rg);
  60}
  61
  62/**
  63 * keystone_timer_barrier: write memory barrier
  64 * use explicit barrier to avoid using readl/writel non relaxed function
  65 * variants, because in our case non relaxed variants hide the true places
  66 * where barrier is needed.
  67 */
  68static inline void keystone_timer_barrier(void)
  69{
  70        __iowmb();
  71}
  72
  73/**
  74 * keystone_timer_config: configures timer to work in oneshot/periodic modes.
  75 * @ mode: mode to configure
  76 * @ period: cycles number to configure for
  77 */
  78static int keystone_timer_config(u64 period, enum clock_event_mode mode)
  79{
  80        u32 tcr;
  81        u32 off;
  82
  83        tcr = keystone_timer_readl(TCR);
  84        off = tcr & ~(TCR_ENAMODE_MASK);
  85
  86        /* set enable mode */
  87        switch (mode) {
  88        case CLOCK_EVT_MODE_ONESHOT:
  89                tcr |= TCR_ENAMODE_ONESHOT_MASK;
  90                break;
  91        case CLOCK_EVT_MODE_PERIODIC:
  92                tcr |= TCR_ENAMODE_PERIODIC_MASK;
  93                break;
  94        default:
  95                return -1;
  96        }
  97
  98        /* disable timer */
  99        keystone_timer_writel(off, TCR);
 100        /* here we have to be sure the timer has been disabled */
 101        keystone_timer_barrier();
 102
 103        /* reset counter to zero, set new period */
 104        keystone_timer_writel(0, TIM12);
 105        keystone_timer_writel(0, TIM34);
 106        keystone_timer_writel(period & 0xffffffff, PRD12);
 107        keystone_timer_writel(period >> 32, PRD34);
 108
 109        /*
 110         * enable timer
 111         * here we have to be sure that CNTLO, CNTHI, PRDLO, PRDHI registers
 112         * have been written.
 113         */
 114        keystone_timer_barrier();
 115        keystone_timer_writel(tcr, TCR);
 116        return 0;
 117}
 118
 119static void keystone_timer_disable(void)
 120{
 121        u32 tcr;
 122
 123        tcr = keystone_timer_readl(TCR);
 124
 125        /* disable timer */
 126        tcr &= ~(TCR_ENAMODE_MASK);
 127        keystone_timer_writel(tcr, TCR);
 128}
 129
 130static irqreturn_t keystone_timer_interrupt(int irq, void *dev_id)
 131{
 132        struct clock_event_device *evt = dev_id;
 133
 134        evt->event_handler(evt);
 135        return IRQ_HANDLED;
 136}
 137
 138static int keystone_set_next_event(unsigned long cycles,
 139                                  struct clock_event_device *evt)
 140{
 141        return keystone_timer_config(cycles, evt->mode);
 142}
 143
 144static void keystone_set_mode(enum clock_event_mode mode,
 145                             struct clock_event_device *evt)
 146{
 147        switch (mode) {
 148        case CLOCK_EVT_MODE_PERIODIC:
 149                keystone_timer_config(timer.hz_period, CLOCK_EVT_MODE_PERIODIC);
 150                break;
 151        case CLOCK_EVT_MODE_UNUSED:
 152        case CLOCK_EVT_MODE_SHUTDOWN:
 153        case CLOCK_EVT_MODE_ONESHOT:
 154                keystone_timer_disable();
 155                break;
 156        default:
 157                break;
 158        }
 159}
 160
 161static void __init keystone_timer_init(struct device_node *np)
 162{
 163        struct clock_event_device *event_dev = &timer.event_dev;
 164        unsigned long rate;
 165        struct clk *clk;
 166        int irq, error;
 167
 168        irq  = irq_of_parse_and_map(np, 0);
 169        if (irq == NO_IRQ) {
 170                pr_err("%s: failed to map interrupts\n", __func__);
 171                return;
 172        }
 173
 174        timer.base = of_iomap(np, 0);
 175        if (!timer.base) {
 176                pr_err("%s: failed to map registers\n", __func__);
 177                return;
 178        }
 179
 180        clk = of_clk_get(np, 0);
 181        if (IS_ERR(clk)) {
 182                pr_err("%s: failed to get clock\n", __func__);
 183                iounmap(timer.base);
 184                return;
 185        }
 186
 187        error = clk_prepare_enable(clk);
 188        if (error) {
 189                pr_err("%s: failed to enable clock\n", __func__);
 190                goto err;
 191        }
 192
 193        rate = clk_get_rate(clk);
 194
 195        /* disable, use internal clock source */
 196        keystone_timer_writel(0, TCR);
 197        /* here we have to be sure the timer has been disabled */
 198        keystone_timer_barrier();
 199
 200        /* reset timer as 64-bit, no pre-scaler, plus features are disabled */
 201        keystone_timer_writel(0, TGCR);
 202
 203        /* unreset timer */
 204        keystone_timer_writel(TGCR_TIM_UNRESET_MASK, TGCR);
 205
 206        /* init counter to zero */
 207        keystone_timer_writel(0, TIM12);
 208        keystone_timer_writel(0, TIM34);
 209
 210        timer.hz_period = DIV_ROUND_UP(rate, HZ);
 211
 212        /* enable timer interrupts */
 213        keystone_timer_writel(INTCTLSTAT_ENINT_MASK, INTCTLSTAT);
 214
 215        error = request_irq(irq, keystone_timer_interrupt, IRQF_TIMER,
 216                            TIMER_NAME, event_dev);
 217        if (error) {
 218                pr_err("%s: failed to setup irq\n", __func__);
 219                goto err;
 220        }
 221
 222        /* setup clockevent */
 223        event_dev->features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT;
 224        event_dev->set_next_event = keystone_set_next_event;
 225        event_dev->set_mode = keystone_set_mode;
 226        event_dev->cpumask = cpu_all_mask;
 227        event_dev->owner = THIS_MODULE;
 228        event_dev->name = TIMER_NAME;
 229        event_dev->irq = irq;
 230
 231        clockevents_config_and_register(event_dev, rate, 1, ULONG_MAX);
 232
 233        pr_info("keystone timer clock @%lu Hz\n", rate);
 234        return;
 235err:
 236        clk_put(clk);
 237        iounmap(timer.base);
 238}
 239
 240CLOCKSOURCE_OF_DECLARE(keystone_timer, "ti,keystone-timer",
 241                                        keystone_timer_init);
 242