Fix FS#10174 by correcting an oversimplification of the AAC window switching code...
[kugel-rb.git] / firmware / timer.c
blobe9f11b6ae7392cdb9136727615808e6c39d81b02
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 #ifndef __TIMER_SET
39 /* Define these if not defined by target to make the #else cases compile
40 * even if the target doesn't have them implemented. */
41 #define __TIMER_SET(cycles, set) false
42 #define __TIMER_REGISTER(reg_prio, unregister_callback, cycles, \
43 int_prio, timer_callback) false
44 #define __TIMER_UNREGISTER(...)
45 #endif
47 /* interrupt handler */
48 #if CONFIG_CPU == SH7034
49 void IMIA4(void) __attribute__((interrupt_handler));
50 void IMIA4(void)
52 if (pfn_timer != NULL)
53 pfn_timer();
54 and_b(~0x01, &TSR4); /* clear the interrupt */
56 #elif defined CPU_COLDFIRE
57 void TIMER1(void) __attribute__ ((interrupt_handler));
58 void TIMER1(void)
60 if (pfn_timer != NULL)
61 pfn_timer();
62 TER1 = 0xff; /* clear all events */
64 #elif CONFIG_CPU == AS3525
65 void INT_TIMER1(void)
67 if (pfn_timer != NULL)
68 pfn_timer();
70 TIMER1_INTCLR = 0; /* clear interrupt */
72 #elif defined(CPU_PP)
73 void TIMER2(void)
75 TIMER2_VAL; /* ACK interrupt */
76 if (cycles_new > 0)
78 TIMER2_CFG = 0xc0000000 | (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;
90 #elif CONFIG_CPU == PNX0101
91 void TIMER1_ISR(void)
93 if (cycles_new > 0)
95 TIMER1.load = cycles_new - 1;
96 cycles_new = 0;
98 if (pfn_timer != NULL)
100 cycles_new = -1;
101 /* "lock" the variable, in case timer_set_period()
102 * is called within pfn_timer() */
103 pfn_timer();
104 cycles_new = 0;
106 TIMER1.clr = 1; /* clear the interrupt */
108 #endif /* CONFIG_CPU */
110 static bool timer_set(long cycles, bool start)
112 #if CONFIG_CPU == SH7034 || defined(CPU_COLDFIRE) || CONFIG_CPU == AS3525
113 int phi = 0; /* bits for the prescaler */
114 int prescale = 1;
116 #if CONFIG_CPU == SH7034 || defined(CPU_COLDFIRE)
117 #define PRESCALE_STEP 1
118 #else /* CONFIG_CPU == AS3525 */
119 #define PRESCALE_STEP 4
120 #endif
122 while (cycles > 0x10000)
123 { /* work out the smallest prescaler that makes it fit */
124 #if CONFIG_CPU == SH7034 || CONFIG_CPU == AS3525
125 phi++;
126 #endif
127 prescale <<= PRESCALE_STEP;
128 cycles >>= PRESCALE_STEP;
130 #endif
132 #if CONFIG_CPU == PNX0101
133 if (start)
135 if (pfn_unregister != NULL)
137 pfn_unregister();
138 pfn_unregister = NULL;
140 TIMER1.ctrl &= ~0x80; /* disable the counter */
141 TIMER1.ctrl |= 0x40; /* reload after counting down to zero */
142 TIMER1.ctrl &= ~0xc; /* no prescaler */
143 TIMER1.clr = 1; /* clear an interrupt event */
145 if (start || (cycles_new == -1)) /* within isr, cycles_new is "locked" */
146 { /* enable timer */
147 TIMER1.load = cycles - 1;
148 TIMER1.ctrl |= 0x80; /* enable the counter */
150 else
151 cycles_new = cycles;
153 return true;
154 #elif CONFIG_CPU == SH7034
155 if (prescale > 8)
156 return false;
158 if (start)
160 if (pfn_unregister != NULL)
162 pfn_unregister();
163 pfn_unregister = NULL;
166 and_b(~0x10, &TSTR); /* Stop the timer 4 */
167 and_b(~0x10, &TSNC); /* No synchronization */
168 and_b(~0x10, &TMDR); /* Operate normally */
170 TIER4 = 0xF9; /* Enable GRA match interrupt */
173 TCR4 = 0x20 | phi; /* clear at GRA match, set prescaler */
174 GRA4 = (unsigned short)(cycles - 1);
175 if (start || (TCNT4 >= GRA4))
176 TCNT4 = 0;
177 and_b(~0x01, &TSR4); /* clear an eventual interrupt */
179 return true;
180 #elif CONFIG_CPU == AS3525
181 /* XXX: 32 bits cycles could be used */
182 if (prescale > 256 || cycles > 0x10000)
183 return false;
185 if (start)
187 if (pfn_unregister != NULL)
189 pfn_unregister();
190 pfn_unregister = NULL;
194 TIMER1_LOAD = TIMER1_BGLOAD = cycles;
195 /* /!\ bit 4 (reserved) must not be modified
196 * periodic mode, interrupt enabled, 16 bits counter */
197 TIMER1_CONTROL = (TIMER1_CONTROL & (1<<4)) | 0xe0 | (phi<<2);
198 return true;
199 #elif defined CPU_COLDFIRE
200 if (prescale > 4096/CPUFREQ_MAX_MULT)
201 return false;
203 if (prescale > 256/CPUFREQ_MAX_MULT)
205 phi = 0x05; /* prescale sysclk/16, timer enabled */
206 prescale >>= 4;
208 else
209 phi = 0x03; /* prescale sysclk, timer enabled */
211 base_prescale = prescale;
212 prescale *= (cpu_frequency / CPU_FREQ);
214 if (start)
216 if (pfn_unregister != NULL)
218 pfn_unregister();
219 pfn_unregister = NULL;
221 phi &= ~1; /* timer disabled at start */
223 /* If it is already enabled, writing a 0 to the RST bit will clear
224 the register, so we clear RST explicitly before writing the real
225 data. */
226 TMR1 = 0;
229 /* We are using timer 1 */
230 TMR1 = 0x0018 | (unsigned short)phi | ((unsigned short)(prescale - 1) << 8);
231 TRR1 = (unsigned short)(cycles - 1);
232 if (start || (TCN1 >= TRR1))
233 TCN1 = 0; /* reset the timer */
234 TER1 = 0xff; /* clear all events */
236 return true;
237 #elif defined(CPU_PP)
238 if (cycles > 0x20000000 || cycles < 2)
239 return false;
241 if (start)
243 if (pfn_unregister != NULL)
245 pfn_unregister();
246 pfn_unregister = NULL;
248 CPU_INT_DIS = TIMER2_MASK;
249 COP_INT_DIS = TIMER2_MASK;
251 if (start || (cycles_new == -1)) /* within isr, cycles_new is "locked" */
252 TIMER2_CFG = 0xc0000000 | (cycles - 1); /* enable timer */
253 else
254 cycles_new = cycles;
256 return true;
257 #else
258 return __TIMER_SET(cycles, start);
259 #endif /* CONFIG_CPU */
262 #ifdef CPU_COLDFIRE
263 void timers_adjust_prescale(int multiplier, bool enable_irq)
265 /* tick timer */
266 TMR0 = (TMR0 & 0x00ef)
267 | ((unsigned short)(multiplier - 1) << 8)
268 | (enable_irq ? 0x10 : 0);
270 if (pfn_timer)
272 /* user timer */
273 int prescale = base_prescale * multiplier;
274 TMR1 = (TMR1 & 0x00ef)
275 | ((unsigned short)(prescale - 1) << 8)
276 | (enable_irq ? 0x10 : 0);
279 #endif
281 /* Register a user timer, called every <cycles> TIMER_FREQ cycles */
282 bool timer_register(int reg_prio, void (*unregister_callback)(void),
283 long cycles, int int_prio, void (*timer_callback)(void)
284 IF_COP(, int core))
286 if (reg_prio <= timer_prio || cycles == 0)
287 return false;
289 #if CONFIG_CPU == SH7034
290 if (int_prio < 1 || int_prio > 15)
291 return false;
292 #endif
294 if (!timer_set(cycles, true))
295 return false;
297 pfn_timer = timer_callback;
298 pfn_unregister = unregister_callback;
299 timer_prio = reg_prio;
301 #if CONFIG_CPU == SH7034
302 IPRD = (IPRD & 0xFF0F) | int_prio << 4; /* interrupt priority */
303 or_b(0x10, &TSTR); /* start timer 4 */
304 return true;
305 #elif defined CPU_COLDFIRE
306 ICR2 = 0x90; /* interrupt on level 4.0 */
307 and_l(~(1<<10), &IMR);
308 TMR1 |= 1; /* start timer */
309 return true;
310 #elif defined(CPU_PP)
311 /* unmask interrupt source */
312 #if NUM_CORES > 1
313 if (core == COP)
314 COP_INT_EN = TIMER2_MASK;
315 else
316 #endif
317 CPU_INT_EN = TIMER2_MASK;
318 return true;
319 #elif CONFIG_CPU == PNX0101
320 irq_set_int_handler(IRQ_TIMER1, TIMER1_ISR);
321 irq_enable_int(IRQ_TIMER1);
322 return true;
323 #elif CONFIG_CPU == AS3525
324 CGU_PERI |= CGU_TIMER1_CLOCK_ENABLE; /* enable peripheral */
325 VIC_INT_ENABLE |= INTERRUPT_TIMER1;
326 return true;
327 #else
328 return __TIMER_REGISTER(reg_prio, unregister_callback, cycles,
329 int_prio, timer_callback);
330 #endif
331 /* Cover for targets that don't use all these */
332 (void)reg_prio;
333 (void)unregister_callback;
334 (void)cycles;
335 /* TODO: Implement for PortalPlayer and iFP (if possible) */
336 (void)int_prio;
337 (void)timer_callback;
340 bool timer_set_period(long cycles)
342 return timer_set(cycles, false);
345 void timer_unregister(void)
347 #if CONFIG_CPU == SH7034
348 and_b(~0x10, &TSTR); /* stop the timer 4 */
349 IPRD = (IPRD & 0xFF0F); /* disable interrupt */
350 #elif defined CPU_COLDFIRE
351 TMR1 = 0; /* disable timer 1 */
352 or_l((1<<10), &IMR); /* disable interrupt */
353 #elif defined(CPU_PP)
354 TIMER2_CFG = 0; /* stop timer 2 */
355 CPU_INT_DIS = TIMER2_MASK;
356 COP_INT_DIS = TIMER2_MASK;
357 #elif CONFIG_CPU == PNX0101
358 TIMER1.ctrl &= ~0x80; /* disable timer 1 */
359 irq_disable_int(IRQ_TIMER1);
360 #elif CONFIG_CPU == AS3525
361 TIMER1_CONTROL &= 0x10; /* disable timer 1 (don't modify bit 4) */
362 VIC_INT_EN_CLEAR = INTERRUPT_TIMER1; /* disable interrupt */
363 CGU_PERI &= ~CGU_TIMER1_CLOCK_ENABLE; /* disable peripheral */
364 #else
365 __TIMER_UNREGISTER();
366 #endif
367 pfn_timer = NULL;
368 pfn_unregister = NULL;
369 timer_prio = -1;