Merge branches 'release', 'asus', 'sony-laptop' and 'thinkpad' into release
[pandora-kernel.git] / arch / arm / mach-orion / time.c
1 /*
2  * arch/arm/mach-orion/time.c
3  *
4  * Core time functions for Marvell Orion System On Chip
5  *
6  * Maintainer: Tzachi Perelstein <tzachi@marvell.com>
7  *
8  * This file is licensed under the terms of the GNU General Public
9  * License version 2. This program is licensed "as is" without any
10  * warranty of any kind, whether express or implied.
11  */
12
13 #include <linux/kernel.h>
14 #include <linux/clockchips.h>
15 #include <linux/interrupt.h>
16 #include <linux/irq.h>
17 #include <asm/mach/time.h>
18 #include <asm/arch/orion.h>
19 #include "common.h"
20
21 /*
22  * Timer0: clock_event_device, Tick.
23  * Timer1: clocksource, Free running.
24  * WatchDog: Not used.
25  *
26  * Timers are counting down.
27  */
28 #define CLOCKEVENT      0
29 #define CLOCKSOURCE     1
30
31 /*
32  * Timers bits
33  */
34 #define BRIDGE_INT_TIMER(x)     (1 << ((x) + 1))
35 #define TIMER_EN(x)             (1 << ((x) * 2))
36 #define TIMER_RELOAD_EN(x)      (1 << (((x) * 2) + 1))
37 #define BRIDGE_INT_TIMER_WD     (1 << 3)
38 #define TIMER_WD_EN             (1 << 4)
39 #define TIMER_WD_RELOAD_EN      (1 << 5)
40
41 static cycle_t orion_clksrc_read(void)
42 {
43         return (0xffffffff - orion_read(TIMER_VAL(CLOCKSOURCE)));
44 }
45
46 static struct clocksource orion_clksrc = {
47         .name           = "orion_clocksource",
48         .shift          = 20,
49         .rating         = 300,
50         .read           = orion_clksrc_read,
51         .mask           = CLOCKSOURCE_MASK(32),
52         .flags          = CLOCK_SOURCE_IS_CONTINUOUS,
53 };
54
55 static int
56 orion_clkevt_next_event(unsigned long delta, struct clock_event_device *dev)
57 {
58         unsigned long flags;
59
60         if (delta == 0)
61                 return -ETIME;
62
63         local_irq_save(flags);
64
65         /*
66          * Clear and enable timer interrupt bit
67          */
68         orion_write(BRIDGE_CAUSE, ~BRIDGE_INT_TIMER(CLOCKEVENT));
69         orion_setbits(BRIDGE_MASK, BRIDGE_INT_TIMER(CLOCKEVENT));
70
71         /*
72          * Setup new timer value
73          */
74         orion_write(TIMER_VAL(CLOCKEVENT), delta);
75
76         /*
77          * Disable auto reload and kickoff the timer
78          */
79         orion_clrbits(TIMER_CTRL, TIMER_RELOAD_EN(CLOCKEVENT));
80         orion_setbits(TIMER_CTRL, TIMER_EN(CLOCKEVENT));
81
82         local_irq_restore(flags);
83
84         return 0;
85 }
86
87 static void
88 orion_clkevt_mode(enum clock_event_mode mode, struct clock_event_device *dev)
89 {
90         unsigned long flags;
91
92         local_irq_save(flags);
93
94         if (mode == CLOCK_EVT_MODE_PERIODIC) {
95                 /*
96                  * Setup latch cycles in timer and enable reload interrupt.
97                  */
98                 orion_write(TIMER_VAL_RELOAD(CLOCKEVENT), LATCH);
99                 orion_write(TIMER_VAL(CLOCKEVENT), LATCH);
100                 orion_setbits(BRIDGE_MASK, BRIDGE_INT_TIMER(CLOCKEVENT));
101                 orion_setbits(TIMER_CTRL, TIMER_RELOAD_EN(CLOCKEVENT) |
102                                           TIMER_EN(CLOCKEVENT));
103         } else {
104                 /*
105                  * Disable timer and interrupt
106                  */
107                 orion_clrbits(BRIDGE_MASK, BRIDGE_INT_TIMER(CLOCKEVENT));
108                 orion_write(BRIDGE_CAUSE, ~BRIDGE_INT_TIMER(CLOCKEVENT));
109                 orion_clrbits(TIMER_CTRL, TIMER_RELOAD_EN(CLOCKEVENT) |
110                                           TIMER_EN(CLOCKEVENT));
111         }
112
113         local_irq_restore(flags);
114 }
115
116 static struct clock_event_device orion_clkevt = {
117         .name           = "orion_tick",
118         .features       = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT,
119         .shift          = 32,
120         .rating         = 300,
121         .cpumask        = CPU_MASK_CPU0,
122         .set_next_event = orion_clkevt_next_event,
123         .set_mode       = orion_clkevt_mode,
124 };
125
126 static irqreturn_t orion_timer_interrupt(int irq, void *dev_id)
127 {
128         /*
129          * Clear cause bit and do event
130          */
131         orion_write(BRIDGE_CAUSE, ~BRIDGE_INT_TIMER(CLOCKEVENT));
132         orion_clkevt.event_handler(&orion_clkevt);
133         return IRQ_HANDLED;
134 }
135
136 static struct irqaction orion_timer_irq = {
137         .name           = "orion_tick",
138         .flags          = IRQF_DISABLED | IRQF_TIMER,
139         .handler        = orion_timer_interrupt
140 };
141
142 static void orion_timer_init(void)
143 {
144         /*
145          * Setup clocksource free running timer (no interrupt on reload)
146          */
147         orion_write(TIMER_VAL(CLOCKSOURCE), 0xffffffff);
148         orion_write(TIMER_VAL_RELOAD(CLOCKSOURCE), 0xffffffff);
149         orion_clrbits(BRIDGE_MASK, BRIDGE_INT_TIMER(CLOCKSOURCE));
150         orion_setbits(TIMER_CTRL, TIMER_RELOAD_EN(CLOCKSOURCE) |
151                                   TIMER_EN(CLOCKSOURCE));
152
153         /*
154          * Register clocksource
155          */
156         orion_clksrc.mult =
157                 clocksource_hz2mult(CLOCK_TICK_RATE, orion_clksrc.shift);
158
159         clocksource_register(&orion_clksrc);
160
161         /*
162          * Connect and enable tick handler
163          */
164         setup_irq(IRQ_ORION_BRIDGE, &orion_timer_irq);
165
166         /*
167          * Register clockevent
168          */
169         orion_clkevt.mult =
170                 div_sc(CLOCK_TICK_RATE, NSEC_PER_SEC, orion_clkevt.shift);
171         orion_clkevt.max_delta_ns =
172                 clockevent_delta2ns(0xfffffffe, &orion_clkevt);
173         orion_clkevt.min_delta_ns =
174                 clockevent_delta2ns(1, &orion_clkevt);
175
176         clockevents_register_device(&orion_clkevt);
177 }
178
179 struct sys_timer orion_timer = {
180         .init = orion_timer_init,
181 };