1/*
2 * linux/arch/arm/mach-sa1100/time.c
3 *
4 * Copyright (C) 1998 Deborah Wallach.
5 * Twiddles  (C) 1999 	Hugo Fiennes <hugo@empeg.com>
6 *
7 * 2000/03/29 (C) Nicolas Pitre <nico@cam.org>
8 *	Rewritten: big cleanup, much simpler, better HZ accuracy.
9 *
10 */
11#include <linux/init.h>
12#include <linux/errno.h>
13#include <linux/interrupt.h>
14#include <linux/irq.h>
15#include <linux/timex.h>
16#include <linux/signal.h>
17
18#include <asm/mach/time.h>
19#include <asm/hardware.h>
20
21#define RTC_DEF_DIVIDER		(32768 - 1)
22#define RTC_DEF_TRIM            0
23
24static unsigned long __init sa1100_get_rtc_time(void)
25{
26	/*
27	 * According to the manual we should be able to let RTTR be zero
28	 * and then a default divisor for a 32.768KHz clock is used.
29	 * Apparently this doesn't work, at least for my SA1110 rev 5.
30	 * If the clock divider is uninitialized then reset it to the
31	 * default value to get the 1Hz clock.
32	 */
33	if (RTTR == 0) {
34		RTTR = RTC_DEF_DIVIDER + (RTC_DEF_TRIM << 16);
35		printk(KERN_WARNING "Warning: uninitialized Real Time Clock\n");
36		/* The current RTC value probably doesn't make sense either */
37		RCNR = 0;
38		return 0;
39	}
40	return RCNR;
41}
42
43static int sa1100_set_rtc(void)
44{
45	unsigned long current_time = xtime.tv_sec;
46
47	if (RTSR & RTSR_ALE) {
48		/* make sure not to forward the clock over an alarm */
49		unsigned long alarm = RTAR;
50		if (current_time >= alarm && alarm >= RCNR)
51			return -ERESTARTSYS;
52	}
53	RCNR = current_time;
54	return 0;
55}
56
57/* IRQs are disabled before entering here from do_gettimeofday() */
58static unsigned long sa1100_gettimeoffset (void)
59{
60	unsigned long ticks_to_match, elapsed, usec;
61
62	/* Get ticks before next timer match */
63	ticks_to_match = OSMR0 - OSCR;
64
65	/* We need elapsed ticks since last match */
66	elapsed = LATCH - ticks_to_match;
67
68	/* Now convert them to usec */
69	usec = (unsigned long)(elapsed * (tick_nsec / 1000))/LATCH;
70
71	return usec;
72}
73
74#ifdef CONFIG_NO_IDLE_HZ
75static unsigned long initial_match;
76static int match_posponed;
77#endif
78
79static irqreturn_t
80sa1100_timer_interrupt(int irq, void *dev_id)
81{
82	unsigned int next_match;
83
84	write_seqlock(&xtime_lock);
85
86#ifdef CONFIG_NO_IDLE_HZ
87	if (match_posponed) {
88		match_posponed = 0;
89		OSMR0 = initial_match;
90	}
91#endif
92
93	/*
94	 * Loop until we get ahead of the free running timer.
95	 * This ensures an exact clock tick count and time accuracy.
96	 * Since IRQs are disabled at this point, coherence between
97	 * lost_ticks(updated in do_timer()) and the match reg value is
98	 * ensured, hence we can use do_gettimeofday() from interrupt
99	 * handlers.
100	 */
101	do {
102		timer_tick();
103		OSSR = OSSR_M0;  /* Clear match on timer 0 */
104		next_match = (OSMR0 += LATCH);
105	} while ((signed long)(next_match - OSCR) <= 0);
106
107	write_sequnlock(&xtime_lock);
108
109	return IRQ_HANDLED;
110}
111
112static struct irqaction sa1100_timer_irq = {
113	.name		= "SA11xx Timer Tick",
114	.flags		= IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL,
115	.handler	= sa1100_timer_interrupt,
116};
117
118static void __init sa1100_timer_init(void)
119{
120	struct timespec tv;
121	unsigned long flags;
122
123	set_rtc = sa1100_set_rtc;
124
125	tv.tv_nsec = 0;
126	tv.tv_sec = sa1100_get_rtc_time();
127	do_settimeofday(&tv);
128
129	OIER = 0;		/* disable any timer interrupts */
130	OSSR = 0xf;		/* clear status on all timers */
131	setup_irq(IRQ_OST0, &sa1100_timer_irq);
132	local_irq_save(flags);
133	OIER = OIER_E0;		/* enable match on timer 0 to cause interrupts */
134	OSMR0 = OSCR + LATCH;	/* set initial match */
135	local_irq_restore(flags);
136}
137
138#ifdef CONFIG_NO_IDLE_HZ
139static int sa1100_dyn_tick_enable_disable(void)
140{
141	/* nothing to do */
142	return 0;
143}
144
145static void sa1100_dyn_tick_reprogram(unsigned long ticks)
146{
147	if (ticks > 1) {
148		initial_match = OSMR0;
149		OSMR0 = initial_match + ticks * LATCH;
150		match_posponed = 1;
151	}
152}
153
154static irqreturn_t
155sa1100_dyn_tick_handler(int irq, void *dev_id)
156{
157	if (match_posponed) {
158		match_posponed = 0;
159		OSMR0 = initial_match;
160		if ((signed long)(initial_match - OSCR) <= 0)
161			return sa1100_timer_interrupt(irq, dev_id);
162	}
163	return IRQ_NONE;
164}
165
166static struct dyn_tick_timer sa1100_dyn_tick = {
167	.enable		= sa1100_dyn_tick_enable_disable,
168	.disable	= sa1100_dyn_tick_enable_disable,
169	.reprogram	= sa1100_dyn_tick_reprogram,
170	.handler	= sa1100_dyn_tick_handler,
171};
172#endif
173
174#ifdef CONFIG_PM
175unsigned long osmr[4], oier;
176
177static void sa1100_timer_suspend(void)
178{
179	osmr[0] = OSMR0;
180	osmr[1] = OSMR1;
181	osmr[2] = OSMR2;
182	osmr[3] = OSMR3;
183	oier = OIER;
184}
185
186static void sa1100_timer_resume(void)
187{
188	OSSR = 0x0f;
189	OSMR0 = osmr[0];
190	OSMR1 = osmr[1];
191	OSMR2 = osmr[2];
192	OSMR3 = osmr[3];
193	OIER = oier;
194
195	/*
196	 * OSMR0 is the system timer: make sure OSCR is sufficiently behind
197	 */
198	OSCR = OSMR0 - LATCH;
199}
200#else
201#define sa1100_timer_suspend NULL
202#define sa1100_timer_resume NULL
203#endif
204
205struct sys_timer sa1100_timer = {
206	.init		= sa1100_timer_init,
207	.suspend	= sa1100_timer_suspend,
208	.resume		= sa1100_timer_resume,
209	.offset		= sa1100_gettimeoffset,
210#ifdef CONFIG_NO_IDLE_HZ
211	.dyn_tick	= &sa1100_dyn_tick,
212#endif
213};
214