linux/arch/arm/mach-imx/epit.c
<<
>>
Prefs
   1/*
   2 *  linux/arch/arm/plat-mxc/epit.c
   3 *
   4 *  Copyright (C) 2010 Sascha Hauer <s.hauer@pengutronix.de>
   5 *
   6 * This program is free software; you can redistribute it and/or
   7 * modify it under the terms of the GNU General Public License
   8 * as published by the Free Software Foundation; either version 2
   9 * of the License, or (at your option) any later version.
  10 * This program is distributed in the hope that it will be useful,
  11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  13 * GNU General Public License for more details.
  14 *
  15 * You should have received a copy of the GNU General Public License
  16 * along with this program; if not, write to the Free Software
  17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
  18 * MA 02110-1301, USA.
  19 */
  20
  21#define EPITCR          0x00
  22#define EPITSR          0x04
  23#define EPITLR          0x08
  24#define EPITCMPR        0x0c
  25#define EPITCNR         0x10
  26
  27#define EPITCR_EN                       (1 << 0)
  28#define EPITCR_ENMOD                    (1 << 1)
  29#define EPITCR_OCIEN                    (1 << 2)
  30#define EPITCR_RLD                      (1 << 3)
  31#define EPITCR_PRESC(x)                 (((x) & 0xfff) << 4)
  32#define EPITCR_SWR                      (1 << 16)
  33#define EPITCR_IOVW                     (1 << 17)
  34#define EPITCR_DBGEN                    (1 << 18)
  35#define EPITCR_WAITEN                   (1 << 19)
  36#define EPITCR_RES                      (1 << 20)
  37#define EPITCR_STOPEN                   (1 << 21)
  38#define EPITCR_OM_DISCON                (0 << 22)
  39#define EPITCR_OM_TOGGLE                (1 << 22)
  40#define EPITCR_OM_CLEAR                 (2 << 22)
  41#define EPITCR_OM_SET                   (3 << 22)
  42#define EPITCR_CLKSRC_OFF               (0 << 24)
  43#define EPITCR_CLKSRC_PERIPHERAL        (1 << 24)
  44#define EPITCR_CLKSRC_REF_HIGH          (1 << 24)
  45#define EPITCR_CLKSRC_REF_LOW           (3 << 24)
  46
  47#define EPITSR_OCIF                     (1 << 0)
  48
  49#include <linux/interrupt.h>
  50#include <linux/irq.h>
  51#include <linux/clockchips.h>
  52#include <linux/clk.h>
  53#include <linux/err.h>
  54#include <asm/mach/time.h>
  55
  56#include "common.h"
  57#include "hardware.h"
  58
  59static struct clock_event_device clockevent_epit;
  60static enum clock_event_mode clockevent_mode = CLOCK_EVT_MODE_UNUSED;
  61
  62static void __iomem *timer_base;
  63
  64static inline void epit_irq_disable(void)
  65{
  66        u32 val;
  67
  68        val = __raw_readl(timer_base + EPITCR);
  69        val &= ~EPITCR_OCIEN;
  70        __raw_writel(val, timer_base + EPITCR);
  71}
  72
  73static inline void epit_irq_enable(void)
  74{
  75        u32 val;
  76
  77        val = __raw_readl(timer_base + EPITCR);
  78        val |= EPITCR_OCIEN;
  79        __raw_writel(val, timer_base + EPITCR);
  80}
  81
  82static void epit_irq_acknowledge(void)
  83{
  84        __raw_writel(EPITSR_OCIF, timer_base + EPITSR);
  85}
  86
  87static int __init epit_clocksource_init(struct clk *timer_clk)
  88{
  89        unsigned int c = clk_get_rate(timer_clk);
  90
  91        return clocksource_mmio_init(timer_base + EPITCNR, "epit", c, 200, 32,
  92                        clocksource_mmio_readl_down);
  93}
  94
  95/* clock event */
  96
  97static int epit_set_next_event(unsigned long evt,
  98                              struct clock_event_device *unused)
  99{
 100        unsigned long tcmp;
 101
 102        tcmp = __raw_readl(timer_base + EPITCNR);
 103
 104        __raw_writel(tcmp - evt, timer_base + EPITCMPR);
 105
 106        return 0;
 107}
 108
 109static void epit_set_mode(enum clock_event_mode mode,
 110                                struct clock_event_device *evt)
 111{
 112        unsigned long flags;
 113
 114        /*
 115         * The timer interrupt generation is disabled at least
 116         * for enough time to call epit_set_next_event()
 117         */
 118        local_irq_save(flags);
 119
 120        /* Disable interrupt in GPT module */
 121        epit_irq_disable();
 122
 123        if (mode != clockevent_mode) {
 124                /* Set event time into far-far future */
 125
 126                /* Clear pending interrupt */
 127                epit_irq_acknowledge();
 128        }
 129
 130        /* Remember timer mode */
 131        clockevent_mode = mode;
 132        local_irq_restore(flags);
 133
 134        switch (mode) {
 135        case CLOCK_EVT_MODE_PERIODIC:
 136                printk(KERN_ERR "epit_set_mode: Periodic mode is not "
 137                                "supported for i.MX EPIT\n");
 138                break;
 139        case CLOCK_EVT_MODE_ONESHOT:
 140        /*
 141         * Do not put overhead of interrupt enable/disable into
 142         * epit_set_next_event(), the core has about 4 minutes
 143         * to call epit_set_next_event() or shutdown clock after
 144         * mode switching
 145         */
 146                local_irq_save(flags);
 147                epit_irq_enable();
 148                local_irq_restore(flags);
 149                break;
 150        case CLOCK_EVT_MODE_SHUTDOWN:
 151        case CLOCK_EVT_MODE_UNUSED:
 152        case CLOCK_EVT_MODE_RESUME:
 153                /* Left event sources disabled, no more interrupts appear */
 154                break;
 155        }
 156}
 157
 158/*
 159 * IRQ handler for the timer
 160 */
 161static irqreturn_t epit_timer_interrupt(int irq, void *dev_id)
 162{
 163        struct clock_event_device *evt = &clockevent_epit;
 164
 165        epit_irq_acknowledge();
 166
 167        evt->event_handler(evt);
 168
 169        return IRQ_HANDLED;
 170}
 171
 172static struct irqaction epit_timer_irq = {
 173        .name           = "i.MX EPIT Timer Tick",
 174        .flags          = IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL,
 175        .handler        = epit_timer_interrupt,
 176};
 177
 178static struct clock_event_device clockevent_epit = {
 179        .name           = "epit",
 180        .features       = CLOCK_EVT_FEAT_ONESHOT,
 181        .set_mode       = epit_set_mode,
 182        .set_next_event = epit_set_next_event,
 183        .rating         = 200,
 184};
 185
 186static int __init epit_clockevent_init(struct clk *timer_clk)
 187{
 188        clockevent_epit.cpumask = cpumask_of(0);
 189        clockevents_config_and_register(&clockevent_epit,
 190                                        clk_get_rate(timer_clk),
 191                                        0x800, 0xfffffffe);
 192
 193        return 0;
 194}
 195
 196void __init epit_timer_init(void __iomem *base, int irq)
 197{
 198        struct clk *timer_clk;
 199
 200        timer_clk = clk_get_sys("imx-epit.0", NULL);
 201        if (IS_ERR(timer_clk)) {
 202                pr_err("i.MX epit: unable to get clk\n");
 203                return;
 204        }
 205
 206        clk_prepare_enable(timer_clk);
 207
 208        timer_base = base;
 209
 210        /*
 211         * Initialise to a known state (all timers off, and timing reset)
 212         */
 213        __raw_writel(0x0, timer_base + EPITCR);
 214
 215        __raw_writel(0xffffffff, timer_base + EPITLR);
 216        __raw_writel(EPITCR_EN | EPITCR_CLKSRC_REF_HIGH | EPITCR_WAITEN,
 217                        timer_base + EPITCR);
 218
 219        /* init and register the timer to the framework */
 220        epit_clocksource_init(timer_clk);
 221        epit_clockevent_init(timer_clk);
 222
 223        /* Make irqs happen */
 224        setup_irq(irq, &epit_timer_irq);
 225}
 226