2 * linux/arch/arm/mach-p2001/time.c
4 * Copyright (C) 2004-2005 Tobias Lorenz
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>
31 #include <asm/hardware.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; \
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 /**************************************************************************
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
);
69 /* clear interrupt pending bit */
70 P2001_TIMER
->TIMER_INT
&= ~(1<<0); // Timer1_Int
72 write_sequnlock(&xtime_lock
);
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);
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
);
138 /**************************************************************************
139 * Timer 2: LED Frequency Indicator
140 **************************************************************************/
141 #ifdef CONFIG_P2001_TIMER2_LED_FREQ_INDICATOR
143 #define TIMER2_FACTOR 20
145 static irqreturn_t
p2001_timer2_interrupt(int irq
, void *dev_id
, struct pt_regs
*regs
)
150 gpio2
= P2001_GPIO
->GPIO2_Out
;
151 if (gpio2
& 0x0040) {
160 P2001_GPIO
->GPIO2_Out
= gpio2
;
162 /* clear interrupt pending bit */
163 P2001_TIMER
->TIMER_INT
&= ~(1<<1); // Timer2_Int
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
202 /**************************************************************************
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);
211 P2001_TIMER
->Timer12_PreDiv
|= (1<<31); // WatchDog_Reset
213 /* clear interrupt pending bit */
214 P2001_TIMER
->TIMER_INT
&= ~(1<<2); // WatchDog_Int
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
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
248 /**************************************************************************
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;
262 #ifdef CONFIG_P2001_TIMER2_LED_FREQ_INDICATOR
265 #ifdef CONFIG_P2001_WATCHDOG
270 struct sys_timer p2001_timer
= {
271 .init
= p2001_init_time
,
272 .offset
= p2001_gettimeoffset
,