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;
  60
  61static void __iomem *timer_base;
  62
  63static inline void epit_irq_disable(void)
  64{
  65        u32 val;
  66
  67        val = imx_readl(timer_base + EPITCR);
  68        val &= ~EPITCR_OCIEN;
  69        imx_writel(val, timer_base + EPITCR);
  70}
  71
  72static inline void epit_irq_enable(void)
  73{
  74        u32 val;
  75
  76        val = imx_readl(timer_base + EPITCR);
  77        val |= EPITCR_OCIEN;
  78        imx_writel(val, timer_base + EPITCR);
  79}
  80
  81static void epit_irq_acknowledge(void)
  82{
  83        imx_writel(EPITSR_OCIF, timer_base + EPITSR);
  84}
  85
  86static int __init epit_clocksource_init(struct clk *timer_clk)
  87{
  88        unsigned int c = clk_get_rate(timer_clk);
  89
  90        return clocksource_mmio_init(timer_base + EPITCNR, "epit", c, 200, 32,
  91                        clocksource_mmio_readl_down);
  92}
  93
  94/* clock event */
  95
  96static int epit_set_next_event(unsigned long evt,
  97                              struct clock_event_device *unused)
  98{
  99        unsigned long tcmp;
 100
 101        tcmp = imx_readl(timer_base + EPITCNR);
 102
 103        imx_writel(tcmp - evt, timer_base + EPITCMPR);
 104
 105        return 0;
 106}
 107
 108/* Left event sources disabled, no more interrupts appear */
 109static int epit_shutdown(struct clock_event_device *evt)
 110{
 111        unsigned long flags;
 112
 113        /*
 114         * The timer interrupt generation is disabled at least
 115         * for enough time to call epit_set_next_event()
 116         */
 117        local_irq_save(flags);
 118
 119        /* Disable interrupt in GPT module */
 120        epit_irq_disable();
 121
 122        /* Clear pending interrupt */
 123        epit_irq_acknowledge();
 124
 125        local_irq_restore(flags);
 126
 127        return 0;
 128}
 129
 130static int epit_set_oneshot(struct clock_event_device *evt)
 131{
 132        unsigned long flags;
 133
 134        /*
 135         * The timer interrupt generation is disabled at least
 136         * for enough time to call epit_set_next_event()
 137         */
 138        local_irq_save(flags);
 139
 140        /* Disable interrupt in GPT module */
 141        epit_irq_disable();
 142
 143        /* Clear pending interrupt, only while switching mode */
 144        if (!clockevent_state_oneshot(evt))
 145                epit_irq_acknowledge();
 146
 147        /*
 148         * Do not put overhead of interrupt enable/disable into
 149         * epit_set_next_event(), the core has about 4 minutes
 150         * to call epit_set_next_event() or shutdown clock after
 151         * mode switching
 152         */
 153        epit_irq_enable();
 154        local_irq_restore(flags);
 155
 156        return 0;
 157}
 158
 159/*
 160 * IRQ handler for the timer
 161 */
 162static irqreturn_t epit_timer_interrupt(int irq, void *dev_id)
 163{
 164        struct clock_event_device *evt = &clockevent_epit;
 165
 166        epit_irq_acknowledge();
 167
 168        evt->event_handler(evt);
 169
 170        return IRQ_HANDLED;
 171}
 172
 173static struct irqaction epit_timer_irq = {
 174        .name           = "i.MX EPIT Timer Tick",
 175        .flags          = IRQF_TIMER | IRQF_IRQPOLL,
 176        .handler        = epit_timer_interrupt,
 177};
 178
 179static struct clock_event_device clockevent_epit = {
 180        .name                   = "epit",
 181        .features               = CLOCK_EVT_FEAT_ONESHOT,
 182        .set_state_shutdown     = epit_shutdown,
 183        .tick_resume            = epit_shutdown,
 184        .set_state_oneshot      = epit_set_oneshot,
 185        .set_next_event         = epit_set_next_event,
 186        .rating                 = 200,
 187};
 188
 189static int __init epit_clockevent_init(struct clk *timer_clk)
 190{
 191        clockevent_epit.cpumask = cpumask_of(0);
 192        clockevents_config_and_register(&clockevent_epit,
 193                                        clk_get_rate(timer_clk),
 194                                        0x800, 0xfffffffe);
 195
 196        return 0;
 197}
 198
 199void __init epit_timer_init(void __iomem *base, int irq)
 200{
 201        struct clk *timer_clk;
 202
 203        timer_clk = clk_get_sys("imx-epit.0", NULL);
 204        if (IS_ERR(timer_clk)) {
 205                pr_err("i.MX epit: unable to get clk\n");
 206                return;
 207        }
 208
 209        clk_prepare_enable(timer_clk);
 210
 211        timer_base = base;
 212
 213        /*
 214         * Initialise to a known state (all timers off, and timing reset)
 215         */
 216        imx_writel(0x0, timer_base + EPITCR);
 217
 218        imx_writel(0xffffffff, timer_base + EPITLR);
 219        imx_writel(EPITCR_EN | EPITCR_CLKSRC_REF_HIGH | EPITCR_WAITEN,
 220                   timer_base + EPITCR);
 221
 222        /* init and register the timer to the framework */
 223        epit_clocksource_init(timer_clk);
 224        epit_clockevent_init(timer_clk);
 225
 226        /* Make irqs happen */
 227        setup_irq(irq, &epit_timer_irq);
 228}
 229