Ignore SYS_CHARGER_DISCONNECTED event in yesno screen (other events may need to be...
[Rockbox.git] / firmware / timer.c
blobca23cb890c7eab0ff6be9aa7208d56586237e2f5
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
10 * Copyright (C) 2005 Jens Arnold
12 * All files in this archive are subject to the GNU General Public License.
13 * See the file COPYING in the source tree root for full license agreement.
15 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
16 * KIND, either express or implied.
18 ****************************************************************************/
20 #include <stdbool.h>
21 #include "config.h"
22 #include "cpu.h"
23 #include "system.h"
24 #include "timer.h"
26 static int timer_prio = -1;
27 static void (*pfn_timer)(void) = NULL; /* timer callback */
28 static void (*pfn_unregister)(void) = NULL; /* unregister callback */
29 #ifdef CPU_COLDFIRE
30 static int base_prescale;
31 #elif defined CPU_PP || CONFIG_CPU == PNX0101
32 static long cycles_new = 0;
33 #endif
35 /* interrupt handler */
36 #if CONFIG_CPU == SH7034
37 void IMIA4(void) __attribute__((interrupt_handler));
38 void IMIA4(void)
40 if (pfn_timer != NULL)
41 pfn_timer();
42 and_b(~0x01, &TSR4); /* clear the interrupt */
44 #elif defined CPU_COLDFIRE
45 void TIMER1(void) __attribute__ ((interrupt_handler));
46 void TIMER1(void)
48 if (pfn_timer != NULL)
49 pfn_timer();
50 TER1 = 0xff; /* clear all events */
52 #elif defined(CPU_PP)
53 void TIMER2(void)
55 TIMER2_VAL; /* ACK interrupt */
56 if (cycles_new > 0)
58 TIMER2_CFG = 0xc0000000 | (cycles_new - 1);
59 cycles_new = 0;
61 if (pfn_timer != NULL)
63 cycles_new = -1;
64 /* "lock" the variable, in case timer_set_period()
65 * is called within pfn_timer() */
66 pfn_timer();
67 cycles_new = 0;
70 #elif CONFIG_CPU == PNX0101
71 void TIMER1_ISR(void)
73 if (cycles_new > 0)
75 TIMER1.load = cycles_new - 1;
76 cycles_new = 0;
78 if (pfn_timer != NULL)
80 cycles_new = -1;
81 /* "lock" the variable, in case timer_set_period()
82 * is called within pfn_timer() */
83 pfn_timer();
84 cycles_new = 0;
86 TIMER1.clr = 1; /* clear the interrupt */
88 #endif /* CONFIG_CPU */
90 static bool timer_set(long cycles, bool start)
92 #if (CONFIG_CPU == SH7034) || defined(CPU_COLDFIRE)
93 int phi = 0; /* bits for the prescaler */
94 int prescale = 1;
96 while (cycles > 0x10000)
97 { /* work out the smallest prescaler that makes it fit */
98 #if CONFIG_CPU == SH7034
99 phi++;
100 #endif
101 prescale *= 2;
102 cycles >>= 1;
104 #endif
106 #if CONFIG_CPU == PNX0101
107 if (start)
109 if (pfn_unregister != NULL)
111 pfn_unregister();
112 pfn_unregister = NULL;
114 TIMER1.ctrl &= ~0x80; /* disable the counter */
115 TIMER1.ctrl |= 0x40; /* reload after counting down to zero */
116 TIMER1.ctrl &= ~0xc; /* no prescaler */
117 TIMER1.clr = 1; /* clear an interrupt event */
119 if (start || (cycles_new == -1)) /* within isr, cycles_new is "locked" */
120 { /* enable timer */
121 TIMER1.load = cycles - 1;
122 TIMER1.ctrl |= 0x80; /* enable the counter */
124 else
125 cycles_new = cycles;
126 #endif
128 #if CONFIG_CPU == SH7034
129 if (prescale > 8)
130 return false;
132 if (start)
134 if (pfn_unregister != NULL)
136 pfn_unregister();
137 pfn_unregister = NULL;
140 and_b(~0x10, &TSTR); /* Stop the timer 4 */
141 and_b(~0x10, &TSNC); /* No synchronization */
142 and_b(~0x10, &TMDR); /* Operate normally */
144 TIER4 = 0xF9; /* Enable GRA match interrupt */
147 TCR4 = 0x20 | phi; /* clear at GRA match, set prescaler */
148 GRA4 = (unsigned short)(cycles - 1);
149 if (start || (TCNT4 >= GRA4))
150 TCNT4 = 0;
151 and_b(~0x01, &TSR4); /* clear an eventual interrupt */
153 #elif defined CPU_COLDFIRE
154 if (prescale > 4096/CPUFREQ_MAX_MULT)
155 return false;
157 if (prescale > 256/CPUFREQ_MAX_MULT)
159 phi = 0x05; /* prescale sysclk/16, timer enabled */
160 prescale >>= 4;
162 else
163 phi = 0x03; /* prescale sysclk, timer enabled */
165 base_prescale = prescale;
166 prescale *= (cpu_frequency / CPU_FREQ);
168 if (start)
170 if (pfn_unregister != NULL)
172 pfn_unregister();
173 pfn_unregister = NULL;
175 phi &= ~1; /* timer disabled at start */
177 /* If it is already enabled, writing a 0 to the RST bit will clear
178 the register, so we clear RST explicitly before writing the real
179 data. */
180 TMR1 = 0;
183 /* We are using timer 1 */
184 TMR1 = 0x0018 | (unsigned short)phi | ((unsigned short)(prescale - 1) << 8);
185 TRR1 = (unsigned short)(cycles - 1);
186 if (start || (TCN1 >= TRR1))
187 TCN1 = 0; /* reset the timer */
188 TER1 = 0xff; /* clear all events */
189 #elif defined(CPU_PP)
190 if (cycles > 0x20000000 || cycles < 2)
191 return false;
193 if (start)
195 if (pfn_unregister != NULL)
197 pfn_unregister();
198 pfn_unregister = NULL;
201 if (start || (cycles_new == -1)) /* within isr, cycles_new is "locked" */
202 TIMER2_CFG = 0xc0000000 | (cycles - 1); /* enable timer */
203 else
204 cycles_new = cycles;
206 #elif CONFIG_CPU == S3C2440 /* TODO: Implement for the Gigabeat */
207 (void)start;
208 (void)cycles;
209 #endif /* CONFIG_CPU */
210 return true;
213 #ifdef CPU_COLDFIRE
214 void timers_adjust_prescale(int multiplier, bool enable_irq)
216 /* tick timer */
217 TMR0 = (TMR0 & 0x00ef)
218 | ((unsigned short)(multiplier - 1) << 8)
219 | (enable_irq ? 0x10 : 0);
221 if (pfn_timer)
223 /* user timer */
224 int prescale = base_prescale * multiplier;
225 TMR1 = (TMR1 & 0x00ef)
226 | ((unsigned short)(prescale - 1) << 8)
227 | (enable_irq ? 0x10 : 0);
230 #endif
232 /* Register a user timer, called every <cycles> TIMER_FREQ cycles */
233 bool timer_register(int reg_prio, void (*unregister_callback)(void),
234 long cycles, int int_prio, void (*timer_callback)(void))
236 if (reg_prio <= timer_prio || cycles == 0)
237 return false;
239 #if defined(CPU_PP) || (CONFIG_CPU==PNX0101) || (CONFIG_CPU==S3C2440)
240 /* TODO: Implement for PortalPlayer and iFP (if possible) */
241 (void)int_prio;
242 #endif
244 #if CONFIG_CPU == SH7034
245 if (int_prio < 1 || int_prio > 15)
246 return false;
247 #elif defined CPU_COLDFIRE
248 (void)int_prio;
249 #endif
251 if (!timer_set(cycles, true))
252 return false;
254 pfn_timer = timer_callback;
255 pfn_unregister = unregister_callback;
256 timer_prio = reg_prio;
258 #if CONFIG_CPU == SH7034
259 IPRD = (IPRD & 0xFF0F) | int_prio << 4; /* interrupt priority */
260 or_b(0x10, &TSTR); /* start timer 4 */
261 #elif defined CPU_COLDFIRE
262 ICR2 = 0x90; /* interrupt on level 4.0 */
263 and_l(~(1<<10), &IMR);
264 TMR1 |= 1; /* start timer */
265 #elif defined(CPU_PP)
266 /* unmask interrupt source */
267 CPU_INT_EN = TIMER2_MASK;
268 #elif CONFIG_CPU == PNX0101
269 irq_set_int_handler(IRQ_TIMER1, TIMER1_ISR);
270 irq_enable_int(IRQ_TIMER1);
271 #endif
272 return true;
275 bool timer_set_period(long cycles)
277 return timer_set(cycles, false);
280 void timer_unregister(void)
282 #if CONFIG_CPU == SH7034
283 and_b(~0x10, &TSTR); /* stop the timer 4 */
284 IPRD = (IPRD & 0xFF0F); /* disable interrupt */
285 #elif defined CPU_COLDFIRE
286 TMR1 = 0; /* disable timer 1 */
287 or_l((1<<10), &IMR); /* disable interrupt */
288 #elif defined(CPU_PP)
289 TIMER2_CFG = 0; /* stop timer 2 */
290 CPU_INT_CLR = TIMER2_MASK;
291 #elif CONFIG_CPU == PNX0101
292 TIMER1.ctrl &= ~0x80; /* disable timer 1 */
293 irq_disable_int(5);
294 #endif
295 pfn_timer = NULL;
296 pfn_unregister = NULL;
297 timer_prio = -1;