blob: 62f18ad43a282b9019e97b5b2a974d5ef8a9802d [file] [log] [blame]
Alessandro Rubini28ad94e2009-07-02 19:06:47 +01001/*
2 * linux/arch/arm/mach-nomadik/timer.c
3 *
4 * Copyright (C) 2008 STMicroelectronics
5 * Copyright (C) 2009 Alessandro Rubini, somewhat based on at91sam926x
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License version 2, as
9 * published by the Free Software Foundation.
10 */
11#include <linux/init.h>
12#include <linux/interrupt.h>
13#include <linux/irq.h>
14#include <linux/io.h>
15#include <linux/clockchips.h>
16#include <linux/jiffies.h>
17#include <asm/mach/time.h>
Alessandro Rubini28ad94e2009-07-02 19:06:47 +010018
Srinidhi Kasagar59b559d2009-11-12 06:20:54 +010019#include <plat/mtu.h>
Alessandro Rubini28ad94e2009-07-02 19:06:47 +010020
21static u32 nmdk_count; /* accumulated count */
22static u32 nmdk_cycle; /* write-once */
Srinidhi Kasagar59b559d2009-11-12 06:20:54 +010023
24/* setup by the platform code */
25void __iomem *mtu_base;
Alessandro Rubini28ad94e2009-07-02 19:06:47 +010026
27/*
28 * clocksource: the MTU device is a decrementing counters, so we negate
29 * the value being read.
30 */
31static cycle_t nmdk_read_timer(struct clocksource *cs)
32{
33 u32 count = readl(mtu_base + MTU_VAL(0));
34 return nmdk_count + nmdk_cycle - count;
35
36}
37
38static struct clocksource nmdk_clksrc = {
39 .name = "mtu_0",
40 .rating = 120,
41 .read = nmdk_read_timer,
42 .shift = 20,
43 .flags = CLOCK_SOURCE_IS_CONTINUOUS,
44};
45
46/*
47 * Clockevent device: currently only periodic mode is supported
48 */
49static void nmdk_clkevt_mode(enum clock_event_mode mode,
50 struct clock_event_device *dev)
51{
52 unsigned long flags;
53
54 switch (mode) {
55 case CLOCK_EVT_MODE_PERIODIC:
56 /* enable interrupts -- and count current value? */
57 raw_local_irq_save(flags);
58 writel(readl(mtu_base + MTU_IMSC) | 1, mtu_base + MTU_IMSC);
59 raw_local_irq_restore(flags);
60 break;
61 case CLOCK_EVT_MODE_ONESHOT:
62 BUG(); /* Not supported, yet */
63 /* FALLTHROUGH */
64 case CLOCK_EVT_MODE_SHUTDOWN:
65 case CLOCK_EVT_MODE_UNUSED:
66 /* disable irq */
67 raw_local_irq_save(flags);
68 writel(readl(mtu_base + MTU_IMSC) & ~1, mtu_base + MTU_IMSC);
69 raw_local_irq_restore(flags);
70 break;
71 case CLOCK_EVT_MODE_RESUME:
72 break;
73 }
74}
75
76static struct clock_event_device nmdk_clkevt = {
77 .name = "mtu_0",
78 .features = CLOCK_EVT_FEAT_PERIODIC,
79 .shift = 32,
80 .rating = 100,
81 .set_mode = nmdk_clkevt_mode,
82};
83
84/*
85 * IRQ Handler for the timer 0 of the MTU block. The irq is not shared
86 * as we are the only users of mtu0 by now.
87 */
88static irqreturn_t nmdk_timer_interrupt(int irq, void *dev_id)
89{
90 /* ack: "interrupt clear register" */
Srinidhi Kasagar59b559d2009-11-12 06:20:54 +010091 writel(1 << 0, mtu_base + MTU_ICR);
Alessandro Rubini28ad94e2009-07-02 19:06:47 +010092
93 /* we can't count lost ticks, unfortunately */
94 nmdk_count += nmdk_cycle;
95 nmdk_clkevt.event_handler(&nmdk_clkevt);
96
97 return IRQ_HANDLED;
98}
99
100/*
101 * Set up timer interrupt, and return the current time in seconds.
102 */
103static struct irqaction nmdk_timer_irq = {
104 .name = "Nomadik Timer Tick",
105 .flags = IRQF_DISABLED | IRQF_TIMER,
106 .handler = nmdk_timer_interrupt,
107};
108
109static void nmdk_timer_reset(void)
110{
111 u32 cr;
112
113 writel(0, mtu_base + MTU_CR(0)); /* off */
114
115 /* configure load and background-load, and fire it up */
116 writel(nmdk_cycle, mtu_base + MTU_LR(0));
117 writel(nmdk_cycle, mtu_base + MTU_BGLR(0));
118 cr = MTU_CRn_PERIODIC | MTU_CRn_PRESCALE_1 | MTU_CRn_32BITS;
119 writel(cr, mtu_base + MTU_CR(0));
120 writel(cr | MTU_CRn_ENA, mtu_base + MTU_CR(0));
121}
122
Srinidhi Kasagar59b559d2009-11-12 06:20:54 +0100123void __init nmdk_timer_init(void)
Alessandro Rubini28ad94e2009-07-02 19:06:47 +0100124{
Alessandro Rubini28ad94e2009-07-02 19:06:47 +0100125 unsigned long rate;
126 int bits;
127
128 rate = CLOCK_TICK_RATE; /* 2.4MHz */
129 nmdk_cycle = (rate + HZ/2) / HZ;
130
Alessandro Rubini28ad94e2009-07-02 19:06:47 +0100131 /* Init the timer and register clocksource */
132 nmdk_timer_reset();
133
134 nmdk_clksrc.mult = clocksource_hz2mult(rate, nmdk_clksrc.shift);
135 bits = 8*sizeof(nmdk_count);
136 nmdk_clksrc.mask = CLOCKSOURCE_MASK(bits);
137
Srinidhi Kasagar59b559d2009-11-12 06:20:54 +0100138 if (clocksource_register(&nmdk_clksrc))
139 printk(KERN_ERR "timer: failed to initialize clock "
140 "source %s\n", nmdk_clksrc.name);
Alessandro Rubini28ad94e2009-07-02 19:06:47 +0100141
142 /* Register irq and clockevents */
143 setup_irq(IRQ_MTU0, &nmdk_timer_irq);
144 nmdk_clkevt.mult = div_sc(rate, NSEC_PER_SEC, nmdk_clkevt.shift);
145 nmdk_clkevt.cpumask = cpumask_of(0);
146 clockevents_register_device(&nmdk_clkevt);
147}