linux/drivers/clocksource/nomadik-mtu.c
<<
>>
Prefs
   1/*
   2 * Copyright (C) 2008 STMicroelectronics
   3 * Copyright (C) 2010 Alessandro Rubini
   4 * Copyright (C) 2010 Linus Walleij for ST-Ericsson
   5 *
   6 * This program is free software; you can redistribute it and/or modify
   7 * it under the terms of the GNU General Public License version 2, as
   8 * published by the Free Software Foundation.
   9 */
  10#include <linux/init.h>
  11#include <linux/interrupt.h>
  12#include <linux/irq.h>
  13#include <linux/io.h>
  14#include <linux/clockchips.h>
  15#include <linux/clocksource.h>
  16#include <linux/clk.h>
  17#include <linux/jiffies.h>
  18#include <linux/delay.h>
  19#include <linux/err.h>
  20#include <linux/platform_data/clocksource-nomadik-mtu.h>
  21#include <asm/mach/time.h>
  22#include <asm/sched_clock.h>
  23
  24/*
  25 * The MTU device hosts four different counters, with 4 set of
  26 * registers. These are register names.
  27 */
  28
  29#define MTU_IMSC        0x00    /* Interrupt mask set/clear */
  30#define MTU_RIS         0x04    /* Raw interrupt status */
  31#define MTU_MIS         0x08    /* Masked interrupt status */
  32#define MTU_ICR         0x0C    /* Interrupt clear register */
  33
  34/* per-timer registers take 0..3 as argument */
  35#define MTU_LR(x)       (0x10 + 0x10 * (x) + 0x00)      /* Load value */
  36#define MTU_VAL(x)      (0x10 + 0x10 * (x) + 0x04)      /* Current value */
  37#define MTU_CR(x)       (0x10 + 0x10 * (x) + 0x08)      /* Control reg */
  38#define MTU_BGLR(x)     (0x10 + 0x10 * (x) + 0x0c)      /* At next overflow */
  39
  40/* bits for the control register */
  41#define MTU_CRn_ENA             0x80
  42#define MTU_CRn_PERIODIC        0x40    /* if 0 = free-running */
  43#define MTU_CRn_PRESCALE_MASK   0x0c
  44#define MTU_CRn_PRESCALE_1              0x00
  45#define MTU_CRn_PRESCALE_16             0x04
  46#define MTU_CRn_PRESCALE_256            0x08
  47#define MTU_CRn_32BITS          0x02
  48#define MTU_CRn_ONESHOT         0x01    /* if 0 = wraps reloading from BGLR*/
  49
  50/* Other registers are usual amba/primecell registers, currently not used */
  51#define MTU_ITCR        0xff0
  52#define MTU_ITOP        0xff4
  53
  54#define MTU_PERIPH_ID0  0xfe0
  55#define MTU_PERIPH_ID1  0xfe4
  56#define MTU_PERIPH_ID2  0xfe8
  57#define MTU_PERIPH_ID3  0xfeC
  58
  59#define MTU_PCELL0      0xff0
  60#define MTU_PCELL1      0xff4
  61#define MTU_PCELL2      0xff8
  62#define MTU_PCELL3      0xffC
  63
  64static void __iomem *mtu_base;
  65static bool clkevt_periodic;
  66static u32 clk_prescale;
  67static u32 nmdk_cycle;          /* write-once */
  68static struct delay_timer mtu_delay_timer;
  69
  70#ifdef CONFIG_CLKSRC_NOMADIK_MTU_SCHED_CLOCK
  71/*
  72 * Override the global weak sched_clock symbol with this
  73 * local implementation which uses the clocksource to get some
  74 * better resolution when scheduling the kernel.
  75 */
  76static u32 notrace nomadik_read_sched_clock(void)
  77{
  78        if (unlikely(!mtu_base))
  79                return 0;
  80
  81        return -readl(mtu_base + MTU_VAL(0));
  82}
  83#endif
  84
  85static unsigned long nmdk_timer_read_current_timer(void)
  86{
  87        return ~readl_relaxed(mtu_base + MTU_VAL(0));
  88}
  89
  90/* Clockevent device: use one-shot mode */
  91static int nmdk_clkevt_next(unsigned long evt, struct clock_event_device *ev)
  92{
  93        writel(1 << 1, mtu_base + MTU_IMSC);
  94        writel(evt, mtu_base + MTU_LR(1));
  95        /* Load highest value, enable device, enable interrupts */
  96        writel(MTU_CRn_ONESHOT | clk_prescale |
  97               MTU_CRn_32BITS | MTU_CRn_ENA,
  98               mtu_base + MTU_CR(1));
  99
 100        return 0;
 101}
 102
 103void nmdk_clkevt_reset(void)
 104{
 105        if (clkevt_periodic) {
 106                /* Timer: configure load and background-load, and fire it up */
 107                writel(nmdk_cycle, mtu_base + MTU_LR(1));
 108                writel(nmdk_cycle, mtu_base + MTU_BGLR(1));
 109
 110                writel(MTU_CRn_PERIODIC | clk_prescale |
 111                       MTU_CRn_32BITS | MTU_CRn_ENA,
 112                       mtu_base + MTU_CR(1));
 113                writel(1 << 1, mtu_base + MTU_IMSC);
 114        } else {
 115                /* Generate an interrupt to start the clockevent again */
 116                (void) nmdk_clkevt_next(nmdk_cycle, NULL);
 117        }
 118}
 119
 120static void nmdk_clkevt_mode(enum clock_event_mode mode,
 121                             struct clock_event_device *dev)
 122{
 123        switch (mode) {
 124        case CLOCK_EVT_MODE_PERIODIC:
 125                clkevt_periodic = true;
 126                nmdk_clkevt_reset();
 127                break;
 128        case CLOCK_EVT_MODE_ONESHOT:
 129                clkevt_periodic = false;
 130                break;
 131        case CLOCK_EVT_MODE_SHUTDOWN:
 132        case CLOCK_EVT_MODE_UNUSED:
 133                writel(0, mtu_base + MTU_IMSC);
 134                /* disable timer */
 135                writel(0, mtu_base + MTU_CR(1));
 136                /* load some high default value */
 137                writel(0xffffffff, mtu_base + MTU_LR(1));
 138                break;
 139        case CLOCK_EVT_MODE_RESUME:
 140                break;
 141        }
 142}
 143
 144void nmdk_clksrc_reset(void)
 145{
 146        /* Disable */
 147        writel(0, mtu_base + MTU_CR(0));
 148
 149        /* ClockSource: configure load and background-load, and fire it up */
 150        writel(nmdk_cycle, mtu_base + MTU_LR(0));
 151        writel(nmdk_cycle, mtu_base + MTU_BGLR(0));
 152
 153        writel(clk_prescale | MTU_CRn_32BITS | MTU_CRn_ENA,
 154               mtu_base + MTU_CR(0));
 155}
 156
 157static void nmdk_clkevt_resume(struct clock_event_device *cedev)
 158{
 159        nmdk_clkevt_reset();
 160        nmdk_clksrc_reset();
 161}
 162
 163static struct clock_event_device nmdk_clkevt = {
 164        .name           = "mtu_1",
 165        .features       = CLOCK_EVT_FEAT_ONESHOT | CLOCK_EVT_FEAT_PERIODIC,
 166        .rating         = 200,
 167        .set_mode       = nmdk_clkevt_mode,
 168        .set_next_event = nmdk_clkevt_next,
 169        .resume         = nmdk_clkevt_resume,
 170};
 171
 172/*
 173 * IRQ Handler for timer 1 of the MTU block.
 174 */
 175static irqreturn_t nmdk_timer_interrupt(int irq, void *dev_id)
 176{
 177        struct clock_event_device *evdev = dev_id;
 178
 179        writel(1 << 1, mtu_base + MTU_ICR); /* Interrupt clear reg */
 180        evdev->event_handler(evdev);
 181        return IRQ_HANDLED;
 182}
 183
 184static struct irqaction nmdk_timer_irq = {
 185        .name           = "Nomadik Timer Tick",
 186        .flags          = IRQF_DISABLED | IRQF_TIMER,
 187        .handler        = nmdk_timer_interrupt,
 188        .dev_id         = &nmdk_clkevt,
 189};
 190
 191void __init nmdk_timer_init(void __iomem *base, int irq)
 192{
 193        unsigned long rate;
 194        struct clk *clk0, *pclk0;
 195
 196        mtu_base = base;
 197
 198        pclk0 = clk_get_sys("mtu0", "apb_pclk");
 199        BUG_ON(IS_ERR(pclk0));
 200        BUG_ON(clk_prepare(pclk0) < 0);
 201        BUG_ON(clk_enable(pclk0) < 0);
 202
 203        clk0 = clk_get_sys("mtu0", NULL);
 204        BUG_ON(IS_ERR(clk0));
 205        BUG_ON(clk_prepare(clk0) < 0);
 206        BUG_ON(clk_enable(clk0) < 0);
 207
 208        /*
 209         * Tick rate is 2.4MHz for Nomadik and 2.4Mhz, 100MHz or 133 MHz
 210         * for ux500.
 211         * Use a divide-by-16 counter if the tick rate is more than 32MHz.
 212         * At 32 MHz, the timer (with 32 bit counter) can be programmed
 213         * to wake-up at a max 127s a head in time. Dividing a 2.4 MHz timer
 214         * with 16 gives too low timer resolution.
 215         */
 216        rate = clk_get_rate(clk0);
 217        if (rate > 32000000) {
 218                rate /= 16;
 219                clk_prescale = MTU_CRn_PRESCALE_16;
 220        } else {
 221                clk_prescale = MTU_CRn_PRESCALE_1;
 222        }
 223
 224        /* Cycles for periodic mode */
 225        nmdk_cycle = DIV_ROUND_CLOSEST(rate, HZ);
 226
 227
 228        /* Timer 0 is the free running clocksource */
 229        nmdk_clksrc_reset();
 230
 231        if (clocksource_mmio_init(mtu_base + MTU_VAL(0), "mtu_0",
 232                        rate, 200, 32, clocksource_mmio_readl_down))
 233                pr_err("timer: failed to initialize clock source %s\n",
 234                       "mtu_0");
 235
 236#ifdef CONFIG_CLKSRC_NOMADIK_MTU_SCHED_CLOCK
 237        setup_sched_clock(nomadik_read_sched_clock, 32, rate);
 238#endif
 239
 240        /* Timer 1 is used for events, register irq and clockevents */
 241        setup_irq(irq, &nmdk_timer_irq);
 242        nmdk_clkevt.cpumask = cpumask_of(0);
 243        nmdk_clkevt.irq = irq;
 244        clockevents_config_and_register(&nmdk_clkevt, rate, 2, 0xffffffffU);
 245
 246        mtu_delay_timer.read_current_timer = &nmdk_timer_read_current_timer;
 247        mtu_delay_timer.freq = rate;
 248        register_current_timer_delay(&mtu_delay_timer);
 249}
 250
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.