Forgot that the X5/M5 use a 64/64KB IRAM split, so they can't use the previous optimi...
[Rockbox.git] / firmware / timer.c
blobaeb0ee142b720c9e7f5461c66deb5c4229f1d8a1
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
10 * Copyright (C) 2005 Jens Arnold
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
20 ****************************************************************************/
22 #include <stdbool.h>
23 #include "config.h"
24 #include "cpu.h"
25 #include "system.h"
26 #include "timer.h"
27 #include "logf.h"
29 static int timer_prio = -1;
30 void SHAREDBSS_ATTR (*pfn_timer)(void) = NULL; /* timer callback */
31 void SHAREDBSS_ATTR (*pfn_unregister)(void) = NULL; /* unregister callback */
32 #ifdef CPU_COLDFIRE
33 static int base_prescale;
34 #elif defined CPU_PP || CONFIG_CPU == PNX0101
35 static long SHAREDBSS_ATTR cycles_new = 0;
36 #endif
38 /* interrupt handler */
39 #if CONFIG_CPU == SH7034
40 void IMIA4(void) __attribute__((interrupt_handler));
41 void IMIA4(void)
43 if (pfn_timer != NULL)
44 pfn_timer();
45 and_b(~0x01, &TSR4); /* clear the interrupt */
47 #elif defined CPU_COLDFIRE
48 void TIMER1(void) __attribute__ ((interrupt_handler));
49 void TIMER1(void)
51 if (pfn_timer != NULL)
52 pfn_timer();
53 TER1 = 0xff; /* clear all events */
55 #elif defined(CPU_PP)
56 void TIMER2(void)
58 TIMER2_VAL; /* ACK interrupt */
59 if (cycles_new > 0)
61 TIMER2_CFG = 0xc0000000 | (cycles_new - 1);
62 cycles_new = 0;
64 if (pfn_timer != NULL)
66 cycles_new = -1;
67 /* "lock" the variable, in case timer_set_period()
68 * is called within pfn_timer() */
69 pfn_timer();
70 cycles_new = 0;
73 #elif CONFIG_CPU == PNX0101
74 void TIMER1_ISR(void)
76 if (cycles_new > 0)
78 TIMER1.load = cycles_new - 1;
79 cycles_new = 0;
81 if (pfn_timer != NULL)
83 cycles_new = -1;
84 /* "lock" the variable, in case timer_set_period()
85 * is called within pfn_timer() */
86 pfn_timer();
87 cycles_new = 0;
89 TIMER1.clr = 1; /* clear the interrupt */
91 #endif /* CONFIG_CPU */
93 static bool timer_set(long cycles, bool start)
95 #if (CONFIG_CPU == SH7034) || defined(CPU_COLDFIRE)
96 int phi = 0; /* bits for the prescaler */
97 int prescale = 1;
99 while (cycles > 0x10000)
100 { /* work out the smallest prescaler that makes it fit */
101 #if CONFIG_CPU == SH7034
102 phi++;
103 #endif
104 prescale *= 2;
105 cycles >>= 1;
107 #endif
109 #if CONFIG_CPU == PNX0101
110 if (start)
112 if (pfn_unregister != NULL)
114 pfn_unregister();
115 pfn_unregister = NULL;
117 TIMER1.ctrl &= ~0x80; /* disable the counter */
118 TIMER1.ctrl |= 0x40; /* reload after counting down to zero */
119 TIMER1.ctrl &= ~0xc; /* no prescaler */
120 TIMER1.clr = 1; /* clear an interrupt event */
122 if (start || (cycles_new == -1)) /* within isr, cycles_new is "locked" */
123 { /* enable timer */
124 TIMER1.load = cycles - 1;
125 TIMER1.ctrl |= 0x80; /* enable the counter */
127 else
128 cycles_new = cycles;
130 return true;
131 #elif CONFIG_CPU == SH7034
132 if (prescale > 8)
133 return false;
135 if (start)
137 if (pfn_unregister != NULL)
139 pfn_unregister();
140 pfn_unregister = NULL;
143 and_b(~0x10, &TSTR); /* Stop the timer 4 */
144 and_b(~0x10, &TSNC); /* No synchronization */
145 and_b(~0x10, &TMDR); /* Operate normally */
147 TIER4 = 0xF9; /* Enable GRA match interrupt */
150 TCR4 = 0x20 | phi; /* clear at GRA match, set prescaler */
151 GRA4 = (unsigned short)(cycles - 1);
152 if (start || (TCNT4 >= GRA4))
153 TCNT4 = 0;
154 and_b(~0x01, &TSR4); /* clear an eventual interrupt */
156 return true;
157 #elif defined CPU_COLDFIRE
158 if (prescale > 4096/CPUFREQ_MAX_MULT)
159 return false;
161 if (prescale > 256/CPUFREQ_MAX_MULT)
163 phi = 0x05; /* prescale sysclk/16, timer enabled */
164 prescale >>= 4;
166 else
167 phi = 0x03; /* prescale sysclk, timer enabled */
169 base_prescale = prescale;
170 prescale *= (cpu_frequency / CPU_FREQ);
172 if (start)
174 if (pfn_unregister != NULL)
176 pfn_unregister();
177 pfn_unregister = NULL;
179 phi &= ~1; /* timer disabled at start */
181 /* If it is already enabled, writing a 0 to the RST bit will clear
182 the register, so we clear RST explicitly before writing the real
183 data. */
184 TMR1 = 0;
187 /* We are using timer 1 */
188 TMR1 = 0x0018 | (unsigned short)phi | ((unsigned short)(prescale - 1) << 8);
189 TRR1 = (unsigned short)(cycles - 1);
190 if (start || (TCN1 >= TRR1))
191 TCN1 = 0; /* reset the timer */
192 TER1 = 0xff; /* clear all events */
194 return true;
195 #elif defined(CPU_PP)
196 if (cycles > 0x20000000 || cycles < 2)
197 return false;
199 if (start)
201 if (pfn_unregister != NULL)
203 pfn_unregister();
204 pfn_unregister = NULL;
206 CPU_INT_DIS = TIMER2_MASK;
207 COP_INT_DIS = TIMER2_MASK;
209 if (start || (cycles_new == -1)) /* within isr, cycles_new is "locked" */
210 TIMER2_CFG = 0xc0000000 | (cycles - 1); /* enable timer */
211 else
212 cycles_new = cycles;
214 return true;
215 #elif (CONFIG_CPU == IMX31L)
216 /* TODO */
217 (void)cycles; (void)start;
218 return false;
219 #else
220 return __TIMER_SET(cycles, start);
221 #endif /* CONFIG_CPU */
224 #ifdef CPU_COLDFIRE
225 void timers_adjust_prescale(int multiplier, bool enable_irq)
227 /* tick timer */
228 TMR0 = (TMR0 & 0x00ef)
229 | ((unsigned short)(multiplier - 1) << 8)
230 | (enable_irq ? 0x10 : 0);
232 if (pfn_timer)
234 /* user timer */
235 int prescale = base_prescale * multiplier;
236 TMR1 = (TMR1 & 0x00ef)
237 | ((unsigned short)(prescale - 1) << 8)
238 | (enable_irq ? 0x10 : 0);
241 #endif
243 /* Register a user timer, called every <cycles> TIMER_FREQ cycles */
244 bool timer_register(int reg_prio, void (*unregister_callback)(void),
245 long cycles, int int_prio, void (*timer_callback)(void)
246 IF_COP(, int core))
248 if (reg_prio <= timer_prio || cycles == 0)
249 return false;
251 #if CONFIG_CPU == SH7034
252 if (int_prio < 1 || int_prio > 15)
253 return false;
254 #endif
256 if (!timer_set(cycles, true))
257 return false;
259 pfn_timer = timer_callback;
260 pfn_unregister = unregister_callback;
261 timer_prio = reg_prio;
263 #if CONFIG_CPU == SH7034
264 IPRD = (IPRD & 0xFF0F) | int_prio << 4; /* interrupt priority */
265 or_b(0x10, &TSTR); /* start timer 4 */
266 return true;
267 #elif defined CPU_COLDFIRE
268 ICR2 = 0x90; /* interrupt on level 4.0 */
269 and_l(~(1<<10), &IMR);
270 TMR1 |= 1; /* start timer */
271 return true;
272 #elif defined(CPU_PP)
273 /* unmask interrupt source */
274 #if NUM_CORES > 1
275 if (core == COP)
276 COP_INT_EN = TIMER2_MASK;
277 else
278 #endif
279 CPU_INT_EN = TIMER2_MASK;
280 return true;
281 #elif CONFIG_CPU == PNX0101
282 irq_set_int_handler(IRQ_TIMER1, TIMER1_ISR);
283 irq_enable_int(IRQ_TIMER1);
284 return true;
285 #elif CONFIG_CPU == IMX31L
286 /* TODO */
287 return false;
288 #else
289 return __TIMER_REGISTER(reg_prio, unregister_callback, cycles,
290 int_prio, timer_callback);
291 #endif
292 /* Cover for targets that don't use all these */
293 (void)reg_prio;
294 (void)unregister_callback;
295 (void)cycles;
296 /* TODO: Implement for PortalPlayer and iFP (if possible) */
297 (void)int_prio;
298 (void)timer_callback;
301 bool timer_set_period(long cycles)
303 return timer_set(cycles, false);
306 void timer_unregister(void)
308 #if CONFIG_CPU == SH7034
309 and_b(~0x10, &TSTR); /* stop the timer 4 */
310 IPRD = (IPRD & 0xFF0F); /* disable interrupt */
311 #elif defined CPU_COLDFIRE
312 TMR1 = 0; /* disable timer 1 */
313 or_l((1<<10), &IMR); /* disable interrupt */
314 #elif defined(CPU_PP)
315 TIMER2_CFG = 0; /* stop timer 2 */
316 CPU_INT_DIS = TIMER2_MASK;
317 COP_INT_DIS = TIMER2_MASK;
318 #elif CONFIG_CPU == PNX0101
319 TIMER1.ctrl &= ~0x80; /* disable timer 1 */
320 irq_disable_int(IRQ_TIMER1);
321 #elif CONFIG_CPU == S3C2440 || CONFIG_CPU == DM320
322 __TIMER_UNREGISTER();
323 #endif
324 pfn_timer = NULL;
325 pfn_unregister = NULL;
326 timer_prio = -1;