blob: bd4262da4f405ed0ad1f65f2cddf4e411fc32765 [file] [log] [blame]
Tzachi Perelstein51cbff12007-10-23 15:14:42 -04001/*
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
41static cycle_t orion_clksrc_read(void)
42{
43 return (0xffffffff - orion_read(TIMER_VAL(CLOCKSOURCE)));
44}
45
46static 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
55static int
56orion_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
87static void
88orion_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
116static 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
126static 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
136static struct irqaction orion_timer_irq = {
137 .name = "orion_tick",
138 .flags = IRQF_DISABLED | IRQF_TIMER,
139 .handler = orion_timer_interrupt
140};
141
142static 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
179struct sys_timer orion_timer = {
180 .init = orion_timer_init,
181};