MOXA linux-2.6.x / linux-2.6.19-uc1 from UC-7110-LX-BOOTLOADER-1.9_VERSION-4.2.tgz
[linux-2.6.19-moxart.git] / arch / arm / mach-p2001 / time.c
bloba331b544a4bee10f8b12d21db525ce18367a636d
1 /*
2 * linux/arch/arm/mach-p2001/time.c
4 * Copyright (C) 2004-2005 Tobias Lorenz
6 * Timer handling code
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License version 2 as
10 * published by the Free Software Foundation.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21 #include <linux/timex.h>
22 #include <linux/init.h>
23 #include <linux/interrupt.h>
24 #include <linux/sched.h>
26 #ifdef CONFIG_CPU_FREQ
27 #include <linux/notifier.h>
28 #include <linux/cpufreq.h>
29 #endif
31 #include <asm/hardware.h>
32 #include <asm/irq.h>
33 #include <asm/leds.h>
34 #include <asm/io.h>
36 #include <asm/mach/time.h>
38 #define P2001_TIMER_VALUE(reg, mask, shift, value) { \
39 unsigned int i = (P2001_TIMER->reg); \
40 i &= ~((mask) << (shift)); \
41 i |= (((value) & (mask)) << (shift)); \
42 (P2001_TIMER->reg) = i; \
46 * short calculation
47 * ---------------------------------------------------------------
48 * prescaler = factor * (SYSCLK / 12288000) max: 255
49 * period = SYSCLK/prescaler/HZ max: 65535
50 * clocks_per_usec = SYSCLK/prescaler / 1000000 min: 1
51 * = 12288000 / factor / 1000000
52 * ---------------------------------------------------------------
53 * IMPORTANT: recalculate factor when HZ changes, so that limits
54 * are kept within SYSCLK range (12288000-73728000)
57 /**************************************************************************
58 * Timer 1: Scheduler
59 **************************************************************************/
60 #define TIMER1_HZ HZ /* 100-1000 HZ */
61 #define TIMER1_FACTOR 2
63 static irqreturn_t p2001_timer1_interrupt(int irq, void *dev_id)
65 write_seqlock(&xtime_lock);
67 timer_tick();
69 /* clear interrupt pending bit */
70 P2001_TIMER->TIMER_INT &= ~(1<<0); // Timer1_Int
72 write_sequnlock(&xtime_lock);
74 return IRQ_HANDLED;
77 static struct irqaction p2001_timer1_irq = {
78 .name = "P2001 timer1",
79 .flags = IRQF_DISABLED | IRQF_TIMER,
80 .handler = p2001_timer1_interrupt,
83 /* Return number of microseconds since last interrupt */
84 #define TIMER1_CLOCKS_PER_USEC (12288000/TIMER1_FACTOR/1000000)
85 static unsigned long p2001_gettimeoffset(void)
87 return ((0xffff - P2001_TIMER->Timer1) & 0xffff) / TIMER1_CLOCKS_PER_USEC;
90 #ifdef CONFIG_CPU_FREQ
92 * Transistion notifier
94 static int p2001_timer1_notifier(struct notifier_block *self, unsigned long phase, void *data)
96 struct cpufreq_freqs *cf = data;
97 unsigned int prescaler, period;
99 if ((phase == CPUFREQ_POSTCHANGE) ||
100 (phase == CPUFREQ_RESUMECHANGE)) {
101 prescaler = TIMER1_FACTOR*cf->new/12288;
102 period = (1000*cf->new/prescaler)/TIMER1_HZ;
103 P2001_TIMER_VALUE(TIMER_PRELOAD, 0xffff, 0, period);
104 P2001_TIMER_VALUE(Timer12_PreDiv, 0xff, 0, prescaler - 1);
107 return NOTIFY_OK;
110 static struct notifier_block p2001_timer1_nb = { &p2001_timer1_notifier, NULL, 0 };
111 #endif /* CONFIG_CPU_FREQ */
113 static void p2001_timer1_init(void)
115 unsigned int prescaler, period;
117 /* initialize the timer period and prescaler */
118 prescaler = TIMER1_FACTOR*(CONFIG_SYSCLK/12288000);
119 period = (CONFIG_SYSCLK/prescaler)/TIMER1_HZ;
120 P2001_TIMER_VALUE(TIMER_PRELOAD, 0xffff, 0, period);
121 P2001_TIMER_VALUE(Timer12_PreDiv, 0xff, 0, prescaler - 1);
123 /* set up the interrupt vector for timer 1 match */
124 setup_irq(IRQ_TIMER1, &p2001_timer1_irq);
126 /* enable the timer IRQ */
127 P2001_TIMER->TIMER_INT |= (1<<4); // Timer1_Int_En
129 /* let timer 1 run... */
130 P2001_TIMER->Timer12_PreDiv &= ~(1<<28); // Timer_1_Disable
132 #ifdef CONFIG_CPU_FREQ
133 cpufreq_register_notifier(&p2001_timer1_nb, CPUFREQ_TRANSITION_NOTIFIER);
134 #endif
138 /**************************************************************************
139 * Timer 2: LED Frequency Indicator
140 **************************************************************************/
141 #ifdef CONFIG_P2001_TIMER2_LED_FREQ_INDICATOR
142 #define TIMER2_HZ 10
143 #define TIMER2_FACTOR 20
145 static irqreturn_t p2001_timer2_interrupt(int irq, void *dev_id, struct pt_regs *regs)
147 unsigned int gpio2;
149 /* switch leds */
150 gpio2 = P2001_GPIO->GPIO2_Out;
151 if (gpio2 & 0x0040) {
152 /* gpio23_v4 on */
153 gpio2 &= ~0x00c0;
154 gpio2 |= 0x0080;
155 } else {
156 /* gpio22_v5 on */
157 gpio2 &= ~0x00c0;
158 gpio2 |= 0x0040;
160 P2001_GPIO->GPIO2_Out = gpio2;
162 /* clear interrupt pending bit */
163 P2001_TIMER->TIMER_INT &= ~(1<<1); // Timer2_Int
165 return IRQ_HANDLED;
168 static struct irqaction p2001_timer2_irq = {
169 .name = "P2001 timer2",
170 .flags = SA_INTERRUPT,
171 .handler = p2001_timer2_interrupt,
174 static void p2001_timer2_init(void)
176 unsigned int prescaler, period;
178 /* initialize the timer period and prescaler */
179 prescaler = TIMER2_FACTOR*(CONFIG_SYSCLK/12288000);
180 period = (CONFIG_SYSCLK/prescaler)/TIMER2_HZ;
181 P2001_TIMER_VALUE(TIMER_PRELOAD, 0xffff, 16, period);
182 P2001_TIMER_VALUE(Timer12_PreDiv, 0xff, 8, prescaler - 1);
184 /* Activate Leds Frequency Indicator */
185 /* Schematics say that: SDO_2/GPIO_22=V5, SDI_2/GPIO_23=V4 */
186 P2001_GPIO->PIN_MUX |= (1<<2); // set MUX to GPIOs
187 P2001_GPIO->GPIO2_En |= 0xC0; // Enable GPIO driver
188 P2001_GPIO->GPIO2_Out |= 0x00C00000; // Mask bits
190 /* set up the interrupt vector for timer 2 match */
191 setup_irq(IRQ_TIMER2, &p2001_timer2_irq);
193 /* enable the timer IRQ */
194 P2001_TIMER->TIMER_INT |= (1<<5); // Timer2_Int_En
196 /* let timer 2 run... */
197 P2001_TIMER->Timer12_PreDiv &= ~(1<<29); // Timer_2_Disable
199 #endif
202 /**************************************************************************
203 * Watchdog
204 **************************************************************************/
205 #ifdef CONFIG_P2001_WATCHDOG
206 static irqreturn_t p2001_wdt_interrupt(int irq, void *dev_id, struct pt_regs *regs)
208 // printk(KERN_CRIT "Critical watchdog value reached: %d!\n", P2001_TIMER->WatchDog_Timer);
210 /* Reset watchdog */
211 P2001_TIMER->Timer12_PreDiv |= (1<<31); // WatchDog_Reset
213 /* clear interrupt pending bit */
214 P2001_TIMER->TIMER_INT &= ~(1<<2); // WatchDog_Int
216 return IRQ_HANDLED;
219 static struct irqaction p2001_wdt_irq = {
220 .name = "P2001 watchdog",
221 .flags = SA_INTERRUPT,
222 .handler = p2001_wdt_interrupt,
225 static void p2001_wdt_init(void)
227 /* Set predivider, so that watchdog runs at 3000 Hz */
228 /* Reset after 65536/3000 = 21.85 secs (75 MHz) */
229 P2001_TIMER->Timer12_PreDiv |= (0xfff << 16); // PreDiv_WatchDog
231 /* Reset watchdog */
232 P2001_TIMER->Timer12_PreDiv |= (1<<31); // WatchDog_Reset
234 /* Warning after 30000/3000 = 10 secs passed */
235 P2001_TIMER->TIMER_INT |= (1<<6); // WatchDog_Int_En
236 P2001_TIMER->TIMER_INT &= 0xff; // WatchDog_Int_Level
237 P2001_TIMER->TIMER_INT |= (30000 << 8); // WatchDog_Int_Level
239 /* Activate watchdog warning interrupt */
240 setup_irq(IRQ_WATCHDOG, &p2001_wdt_irq);
242 /* Activate watchdog */
243 P2001_TIMER->Timer12_PreDiv &= ~(1<<30); // WatchDog_Disable
245 #endif
248 /**************************************************************************
249 * Main init
250 **************************************************************************/
251 static void __init p2001_init_time(void)
254 * disable and clear timer 0, set to
255 * internal clock and interval mode
257 P2001_TIMER->Timer12_PreDiv = 0x70bb0000;
258 P2001_TIMER->Timer1 = 0;
259 P2001_TIMER->Timer2 = 0;
261 p2001_timer1_init();
262 #ifdef CONFIG_P2001_TIMER2_LED_FREQ_INDICATOR
263 p2001_timer2_init();
264 #endif
265 #ifdef CONFIG_P2001_WATCHDOG
266 p2001_wdt_init();
267 #endif
270 struct sys_timer p2001_timer = {
271 .init = p2001_init_time,
272 .offset = p2001_gettimeoffset,