blob: aeb0ee142b720c9e7f5461c66deb5c4229f1d8a1 [file] [log] [blame]
Jens Arnolde44372e2005-07-26 20:01:11 +00001/***************************************************************************
2* __________ __ ___.
3* Open \______ \ ____ ____ | | _\_ |__ _______ ___
4* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7* \/ \/ \/ \/ \/
8* $Id$
9*
10* Copyright (C) 2005 Jens Arnold
11*
Daniel Stenberg2acc0ac2008-06-28 18:10:04 +000012* 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.
Jens Arnolde44372e2005-07-26 20:01:11 +000016*
17* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18* KIND, either express or implied.
19*
20****************************************************************************/
21
22#include <stdbool.h>
23#include "config.h"
24#include "cpu.h"
25#include "system.h"
26#include "timer.h"
Michael Sevakis4ae87c82007-07-06 21:36:32 +000027#include "logf.h"
Jens Arnolde44372e2005-07-26 20:01:11 +000028
Jens Arnolde44372e2005-07-26 20:01:11 +000029static int timer_prio = -1;
Michael Sevakis05099142008-04-06 04:34:57 +000030void SHAREDBSS_ATTR (*pfn_timer)(void) = NULL; /* timer callback */
31void SHAREDBSS_ATTR (*pfn_unregister)(void) = NULL; /* unregister callback */
Jens Arnoldcfb073c2005-10-03 09:24:36 +000032#ifdef CPU_COLDFIRE
33static int base_prescale;
Tomasz Malesinskicd630c92007-03-24 19:26:13 +000034#elif defined CPU_PP || CONFIG_CPU == PNX0101
Michael Sevakis05099142008-04-06 04:34:57 +000035static long SHAREDBSS_ATTR cycles_new = 0;
Jens Arnoldcfb073c2005-10-03 09:24:36 +000036#endif
Jens Arnolde44372e2005-07-26 20:01:11 +000037
38/* interrupt handler */
39#if CONFIG_CPU == SH7034
40void IMIA4(void) __attribute__((interrupt_handler));
41void IMIA4(void)
42{
43 if (pfn_timer != NULL)
44 pfn_timer();
45 and_b(~0x01, &TSR4); /* clear the interrupt */
46}
47#elif defined CPU_COLDFIRE
48void TIMER1(void) __attribute__ ((interrupt_handler));
49void TIMER1(void)
50{
51 if (pfn_timer != NULL)
52 pfn_timer();
53 TER1 = 0xff; /* clear all events */
54}
Daniel Ankers242cbd52006-11-22 00:41:30 +000055#elif defined(CPU_PP)
Thom Johansen46de4c22006-03-17 00:08:39 +000056void TIMER2(void)
57{
58 TIMER2_VAL; /* ACK interrupt */
Jens Arnolde9086e02006-08-27 23:43:04 +000059 if (cycles_new > 0)
60 {
Jens Arnold1bb86572006-09-01 06:13:33 +000061 TIMER2_CFG = 0xc0000000 | (cycles_new - 1);
Jens Arnolde9086e02006-08-27 23:43:04 +000062 cycles_new = 0;
63 }
Thom Johansen46de4c22006-03-17 00:08:39 +000064 if (pfn_timer != NULL)
Jens Arnoldbb5fbf72006-08-28 06:47:26 +000065 {
Jens Arnold7d8c5aa2006-09-01 22:03:14 +000066 cycles_new = -1;
Jens Arnoldbb5fbf72006-08-28 06:47:26 +000067 /* "lock" the variable, in case timer_set_period()
68 * is called within pfn_timer() */
Thom Johansen46de4c22006-03-17 00:08:39 +000069 pfn_timer();
Jens Arnoldbb5fbf72006-08-28 06:47:26 +000070 cycles_new = 0;
71 }
Thom Johansen46de4c22006-03-17 00:08:39 +000072}
Tomasz Malesinskicd630c92007-03-24 19:26:13 +000073#elif CONFIG_CPU == PNX0101
74void TIMER1_ISR(void)
75{
76 if (cycles_new > 0)
77 {
78 TIMER1.load = cycles_new - 1;
79 cycles_new = 0;
80 }
81 if (pfn_timer != NULL)
82 {
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;
88 }
89 TIMER1.clr = 1; /* clear the interrupt */
90}
Jens Arnolde44372e2005-07-26 20:01:11 +000091#endif /* CONFIG_CPU */
92
93static bool timer_set(long cycles, bool start)
94{
Jens Arnolde9086e02006-08-27 23:43:04 +000095#if (CONFIG_CPU == SH7034) || defined(CPU_COLDFIRE)
Jens Arnolde44372e2005-07-26 20:01:11 +000096 int phi = 0; /* bits for the prescaler */
97 int prescale = 1;
Dave Chapmand31a32c2005-11-11 17:51:35 +000098
Jens Arnolde44372e2005-07-26 20:01:11 +000099 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;
106 }
Thom Johansen46de4c22006-03-17 00:08:39 +0000107#endif
Jens Arnolde44372e2005-07-26 20:01:11 +0000108
Tomasz Malesinskicd630c92007-03-24 19:26:13 +0000109#if CONFIG_CPU == PNX0101
110 if (start)
111 {
112 if (pfn_unregister != NULL)
113 {
114 pfn_unregister();
115 pfn_unregister = NULL;
116 }
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 */
121 }
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 */
126 }
127 else
128 cycles_new = cycles;
Jens Arnolde9086e02006-08-27 23:43:04 +0000129
Michael Sevakis4ae87c82007-07-06 21:36:32 +0000130 return true;
131#elif CONFIG_CPU == SH7034
Jens Arnolde44372e2005-07-26 20:01:11 +0000132 if (prescale > 8)
133 return false;
134
135 if (start)
136 {
137 if (pfn_unregister != NULL)
138 {
139 pfn_unregister();
140 pfn_unregister = NULL;
141 }
142
143 and_b(~0x10, &TSTR); /* Stop the timer 4 */
144 and_b(~0x10, &TSNC); /* No synchronization */
145 and_b(~0x10, &TMDR); /* Operate normally */
146
147 TIER4 = 0xF9; /* Enable GRA match interrupt */
148 }
149
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 */
155
Michael Sevakis4ae87c82007-07-06 21:36:32 +0000156 return true;
Jens Arnolde44372e2005-07-26 20:01:11 +0000157#elif defined CPU_COLDFIRE
Jens Arnoldcfb073c2005-10-03 09:24:36 +0000158 if (prescale > 4096/CPUFREQ_MAX_MULT)
Jens Arnolde44372e2005-07-26 20:01:11 +0000159 return false;
160
Jens Arnoldcfb073c2005-10-03 09:24:36 +0000161 if (prescale > 256/CPUFREQ_MAX_MULT)
Jens Arnolde44372e2005-07-26 20:01:11 +0000162 {
163 phi = 0x05; /* prescale sysclk/16, timer enabled */
164 prescale >>= 4;
165 }
166 else
167 phi = 0x03; /* prescale sysclk, timer enabled */
Jens Arnoldcfb073c2005-10-03 09:24:36 +0000168
169 base_prescale = prescale;
170 prescale *= (cpu_frequency / CPU_FREQ);
Jens Arnolde44372e2005-07-26 20:01:11 +0000171
172 if (start)
173 {
174 if (pfn_unregister != NULL)
175 {
176 pfn_unregister();
177 pfn_unregister = NULL;
178 }
179 phi &= ~1; /* timer disabled at start */
Linus Nielsen Feltzing07eea492006-08-25 11:46:04 +0000180
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;
Jens Arnolde44372e2005-07-26 20:01:11 +0000185 }
186
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 */
Michael Sevakis4ae87c82007-07-06 21:36:32 +0000193
194 return true;
Daniel Ankers242cbd52006-11-22 00:41:30 +0000195#elif defined(CPU_PP)
Jens Arnold7d8c5aa2006-09-01 22:03:14 +0000196 if (cycles > 0x20000000 || cycles < 2)
Jens Arnolde9086e02006-08-27 23:43:04 +0000197 return false;
198
Thom Johansen46de4c22006-03-17 00:08:39 +0000199 if (start)
200 {
201 if (pfn_unregister != NULL)
202 {
203 pfn_unregister();
204 pfn_unregister = NULL;
205 }
Michael Sevakis191320c2008-06-03 05:08:24 +0000206 CPU_INT_DIS = TIMER2_MASK;
207 COP_INT_DIS = TIMER2_MASK;
Thom Johansen46de4c22006-03-17 00:08:39 +0000208 }
Jens Arnoldbb5fbf72006-08-28 06:47:26 +0000209 if (start || (cycles_new == -1)) /* within isr, cycles_new is "locked" */
Jens Arnold1bb86572006-09-01 06:13:33 +0000210 TIMER2_CFG = 0xc0000000 | (cycles - 1); /* enable timer */
Jens Arnolde9086e02006-08-27 23:43:04 +0000211 else
212 cycles_new = cycles;
213
Jens Arnolde44372e2005-07-26 20:01:11 +0000214 return true;
Will Robertson26a05af2007-09-22 02:17:08 +0000215#elif (CONFIG_CPU == IMX31L)
216 /* TODO */
Michael Sevakis040b6cc2008-04-15 14:44:32 +0000217 (void)cycles; (void)start;
218 return false;
Karl Kurbjun7b97fe22007-09-20 04:46:41 +0000219#else
Michael Sevakis4ae87c82007-07-06 21:36:32 +0000220 return __TIMER_SET(cycles, start);
221#endif /* CONFIG_CPU */
Jens Arnolde44372e2005-07-26 20:01:11 +0000222}
223
Jens Arnoldcfb073c2005-10-03 09:24:36 +0000224#ifdef CPU_COLDFIRE
225void timers_adjust_prescale(int multiplier, bool enable_irq)
226{
227 /* tick timer */
228 TMR0 = (TMR0 & 0x00ef)
229 | ((unsigned short)(multiplier - 1) << 8)
230 | (enable_irq ? 0x10 : 0);
231
232 if (pfn_timer)
233 {
234 /* user timer */
235 int prescale = base_prescale * multiplier;
236 TMR1 = (TMR1 & 0x00ef)
237 | ((unsigned short)(prescale - 1) << 8)
238 | (enable_irq ? 0x10 : 0);
239 }
240}
241#endif
242
Thom Johansen46de4c22006-03-17 00:08:39 +0000243/* Register a user timer, called every <cycles> TIMER_FREQ cycles */
Jens Arnolde44372e2005-07-26 20:01:11 +0000244bool timer_register(int reg_prio, void (*unregister_callback)(void),
Jens Arnoldac9b9272008-04-04 19:38:46 +0000245 long cycles, int int_prio, void (*timer_callback)(void)
246 IF_COP(, int core))
Jens Arnolde44372e2005-07-26 20:01:11 +0000247{
248 if (reg_prio <= timer_prio || cycles == 0)
249 return false;
250
251#if CONFIG_CPU == SH7034
252 if (int_prio < 1 || int_prio > 15)
253 return false;
Jens Arnolde44372e2005-07-26 20:01:11 +0000254#endif
255
256 if (!timer_set(cycles, true))
257 return false;
258
259 pfn_timer = timer_callback;
260 pfn_unregister = unregister_callback;
261 timer_prio = reg_prio;
262
263#if CONFIG_CPU == SH7034
264 IPRD = (IPRD & 0xFF0F) | int_prio << 4; /* interrupt priority */
265 or_b(0x10, &TSTR); /* start timer 4 */
Michael Sevakis4ae87c82007-07-06 21:36:32 +0000266 return true;
Jens Arnolde44372e2005-07-26 20:01:11 +0000267#elif defined CPU_COLDFIRE
Jens Arnold72f98782005-11-05 03:28:20 +0000268 ICR2 = 0x90; /* interrupt on level 4.0 */
Jens Arnolde44372e2005-07-26 20:01:11 +0000269 and_l(~(1<<10), &IMR);
270 TMR1 |= 1; /* start timer */
Michael Sevakis4ae87c82007-07-06 21:36:32 +0000271 return true;
Daniel Ankers242cbd52006-11-22 00:41:30 +0000272#elif defined(CPU_PP)
Thom Johansen46de4c22006-03-17 00:08:39 +0000273 /* unmask interrupt source */
Jens Arnoldac9b9272008-04-04 19:38:46 +0000274#if NUM_CORES > 1
275 if (core == COP)
276 COP_INT_EN = TIMER2_MASK;
277 else
278#endif
279 CPU_INT_EN = TIMER2_MASK;
Michael Sevakis4ae87c82007-07-06 21:36:32 +0000280 return true;
Tomasz Malesinskicd630c92007-03-24 19:26:13 +0000281#elif CONFIG_CPU == PNX0101
282 irq_set_int_handler(IRQ_TIMER1, TIMER1_ISR);
283 irq_enable_int(IRQ_TIMER1);
Jens Arnolde44372e2005-07-26 20:01:11 +0000284 return true;
Will Robertson26a05af2007-09-22 02:17:08 +0000285#elif CONFIG_CPU == IMX31L
286 /* TODO */
Michael Sevakis040b6cc2008-04-15 14:44:32 +0000287 return false;
Karl Kurbjun7b97fe22007-09-20 04:46:41 +0000288#else
Michael Sevakis4ae87c82007-07-06 21:36:32 +0000289 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;
Jens Arnolde44372e2005-07-26 20:01:11 +0000299}
300
301bool timer_set_period(long cycles)
302{
303 return timer_set(cycles, false);
304}
305
306void timer_unregister(void)
307{
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 */
Daniel Ankers242cbd52006-11-22 00:41:30 +0000314#elif defined(CPU_PP)
Jens Arnolde9086e02006-08-27 23:43:04 +0000315 TIMER2_CFG = 0; /* stop timer 2 */
Michael Sevakis191320c2008-06-03 05:08:24 +0000316 CPU_INT_DIS = TIMER2_MASK;
317 COP_INT_DIS = TIMER2_MASK;
Tomasz Malesinskicd630c92007-03-24 19:26:13 +0000318#elif CONFIG_CPU == PNX0101
319 TIMER1.ctrl &= ~0x80; /* disable timer 1 */
Tomasz Malesinski4fc77ac2007-09-22 23:37:58 +0000320 irq_disable_int(IRQ_TIMER1);
Maurus Cuelenaere95167e02008-04-24 20:08:28 +0000321#elif CONFIG_CPU == S3C2440 || CONFIG_CPU == DM320
Michael Sevakis4ae87c82007-07-06 21:36:32 +0000322 __TIMER_UNREGISTER();
Jens Arnolde44372e2005-07-26 20:01:11 +0000323#endif
324 pfn_timer = NULL;
325 pfn_unregister = NULL;
326 timer_prio = -1;
327}
328