Jens Arnold | e44372e | 2005-07-26 20:01:11 +0000 | [diff] [blame] | 1 | /*************************************************************************** |
| 2 | * __________ __ ___. |
| 3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ |
| 4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / |
| 5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < |
| 6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ |
| 7 | * \/ \/ \/ \/ \/ |
| 8 | * $Id$ |
| 9 | * |
| 10 | * Copyright (C) 2005 Jens Arnold |
| 11 | * |
Daniel Stenberg | 2acc0ac | 2008-06-28 18:10:04 +0000 | [diff] [blame^] | 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. |
Jens Arnold | e44372e | 2005-07-26 20:01:11 +0000 | [diff] [blame] | 16 | * |
| 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 Sevakis | 4ae87c8 | 2007-07-06 21:36:32 +0000 | [diff] [blame] | 27 | #include "logf.h" |
Jens Arnold | e44372e | 2005-07-26 20:01:11 +0000 | [diff] [blame] | 28 | |
Jens Arnold | e44372e | 2005-07-26 20:01:11 +0000 | [diff] [blame] | 29 | static int timer_prio = -1; |
Michael Sevakis | 0509914 | 2008-04-06 04:34:57 +0000 | [diff] [blame] | 30 | void SHAREDBSS_ATTR (*pfn_timer)(void) = NULL; /* timer callback */ |
| 31 | void SHAREDBSS_ATTR (*pfn_unregister)(void) = NULL; /* unregister callback */ |
Jens Arnold | cfb073c | 2005-10-03 09:24:36 +0000 | [diff] [blame] | 32 | #ifdef CPU_COLDFIRE |
| 33 | static int base_prescale; |
Tomasz Malesinski | cd630c9 | 2007-03-24 19:26:13 +0000 | [diff] [blame] | 34 | #elif defined CPU_PP || CONFIG_CPU == PNX0101 |
Michael Sevakis | 0509914 | 2008-04-06 04:34:57 +0000 | [diff] [blame] | 35 | static long SHAREDBSS_ATTR cycles_new = 0; |
Jens Arnold | cfb073c | 2005-10-03 09:24:36 +0000 | [diff] [blame] | 36 | #endif |
Jens Arnold | e44372e | 2005-07-26 20:01:11 +0000 | [diff] [blame] | 37 | |
| 38 | /* interrupt handler */ |
| 39 | #if CONFIG_CPU == SH7034 |
| 40 | void IMIA4(void) __attribute__((interrupt_handler)); |
| 41 | void 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 |
| 48 | void TIMER1(void) __attribute__ ((interrupt_handler)); |
| 49 | void TIMER1(void) |
| 50 | { |
| 51 | if (pfn_timer != NULL) |
| 52 | pfn_timer(); |
| 53 | TER1 = 0xff; /* clear all events */ |
| 54 | } |
Daniel Ankers | 242cbd5 | 2006-11-22 00:41:30 +0000 | [diff] [blame] | 55 | #elif defined(CPU_PP) |
Thom Johansen | 46de4c2 | 2006-03-17 00:08:39 +0000 | [diff] [blame] | 56 | void TIMER2(void) |
| 57 | { |
| 58 | TIMER2_VAL; /* ACK interrupt */ |
Jens Arnold | e9086e0 | 2006-08-27 23:43:04 +0000 | [diff] [blame] | 59 | if (cycles_new > 0) |
| 60 | { |
Jens Arnold | 1bb8657 | 2006-09-01 06:13:33 +0000 | [diff] [blame] | 61 | TIMER2_CFG = 0xc0000000 | (cycles_new - 1); |
Jens Arnold | e9086e0 | 2006-08-27 23:43:04 +0000 | [diff] [blame] | 62 | cycles_new = 0; |
| 63 | } |
Thom Johansen | 46de4c2 | 2006-03-17 00:08:39 +0000 | [diff] [blame] | 64 | if (pfn_timer != NULL) |
Jens Arnold | bb5fbf7 | 2006-08-28 06:47:26 +0000 | [diff] [blame] | 65 | { |
Jens Arnold | 7d8c5aa | 2006-09-01 22:03:14 +0000 | [diff] [blame] | 66 | cycles_new = -1; |
Jens Arnold | bb5fbf7 | 2006-08-28 06:47:26 +0000 | [diff] [blame] | 67 | /* "lock" the variable, in case timer_set_period() |
| 68 | * is called within pfn_timer() */ |
Thom Johansen | 46de4c2 | 2006-03-17 00:08:39 +0000 | [diff] [blame] | 69 | pfn_timer(); |
Jens Arnold | bb5fbf7 | 2006-08-28 06:47:26 +0000 | [diff] [blame] | 70 | cycles_new = 0; |
| 71 | } |
Thom Johansen | 46de4c2 | 2006-03-17 00:08:39 +0000 | [diff] [blame] | 72 | } |
Tomasz Malesinski | cd630c9 | 2007-03-24 19:26:13 +0000 | [diff] [blame] | 73 | #elif CONFIG_CPU == PNX0101 |
| 74 | void 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 Arnold | e44372e | 2005-07-26 20:01:11 +0000 | [diff] [blame] | 91 | #endif /* CONFIG_CPU */ |
| 92 | |
| 93 | static bool timer_set(long cycles, bool start) |
| 94 | { |
Jens Arnold | e9086e0 | 2006-08-27 23:43:04 +0000 | [diff] [blame] | 95 | #if (CONFIG_CPU == SH7034) || defined(CPU_COLDFIRE) |
Jens Arnold | e44372e | 2005-07-26 20:01:11 +0000 | [diff] [blame] | 96 | int phi = 0; /* bits for the prescaler */ |
| 97 | int prescale = 1; |
Dave Chapman | d31a32c | 2005-11-11 17:51:35 +0000 | [diff] [blame] | 98 | |
Jens Arnold | e44372e | 2005-07-26 20:01:11 +0000 | [diff] [blame] | 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; |
| 106 | } |
Thom Johansen | 46de4c2 | 2006-03-17 00:08:39 +0000 | [diff] [blame] | 107 | #endif |
Jens Arnold | e44372e | 2005-07-26 20:01:11 +0000 | [diff] [blame] | 108 | |
Tomasz Malesinski | cd630c9 | 2007-03-24 19:26:13 +0000 | [diff] [blame] | 109 | #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 Arnold | e9086e0 | 2006-08-27 23:43:04 +0000 | [diff] [blame] | 129 | |
Michael Sevakis | 4ae87c8 | 2007-07-06 21:36:32 +0000 | [diff] [blame] | 130 | return true; |
| 131 | #elif CONFIG_CPU == SH7034 |
Jens Arnold | e44372e | 2005-07-26 20:01:11 +0000 | [diff] [blame] | 132 | 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 Sevakis | 4ae87c8 | 2007-07-06 21:36:32 +0000 | [diff] [blame] | 156 | return true; |
Jens Arnold | e44372e | 2005-07-26 20:01:11 +0000 | [diff] [blame] | 157 | #elif defined CPU_COLDFIRE |
Jens Arnold | cfb073c | 2005-10-03 09:24:36 +0000 | [diff] [blame] | 158 | if (prescale > 4096/CPUFREQ_MAX_MULT) |
Jens Arnold | e44372e | 2005-07-26 20:01:11 +0000 | [diff] [blame] | 159 | return false; |
| 160 | |
Jens Arnold | cfb073c | 2005-10-03 09:24:36 +0000 | [diff] [blame] | 161 | if (prescale > 256/CPUFREQ_MAX_MULT) |
Jens Arnold | e44372e | 2005-07-26 20:01:11 +0000 | [diff] [blame] | 162 | { |
| 163 | phi = 0x05; /* prescale sysclk/16, timer enabled */ |
| 164 | prescale >>= 4; |
| 165 | } |
| 166 | else |
| 167 | phi = 0x03; /* prescale sysclk, timer enabled */ |
Jens Arnold | cfb073c | 2005-10-03 09:24:36 +0000 | [diff] [blame] | 168 | |
| 169 | base_prescale = prescale; |
| 170 | prescale *= (cpu_frequency / CPU_FREQ); |
Jens Arnold | e44372e | 2005-07-26 20:01:11 +0000 | [diff] [blame] | 171 | |
| 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 Feltzing | 07eea49 | 2006-08-25 11:46:04 +0000 | [diff] [blame] | 180 | |
| 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 Arnold | e44372e | 2005-07-26 20:01:11 +0000 | [diff] [blame] | 185 | } |
| 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 Sevakis | 4ae87c8 | 2007-07-06 21:36:32 +0000 | [diff] [blame] | 193 | |
| 194 | return true; |
Daniel Ankers | 242cbd5 | 2006-11-22 00:41:30 +0000 | [diff] [blame] | 195 | #elif defined(CPU_PP) |
Jens Arnold | 7d8c5aa | 2006-09-01 22:03:14 +0000 | [diff] [blame] | 196 | if (cycles > 0x20000000 || cycles < 2) |
Jens Arnold | e9086e0 | 2006-08-27 23:43:04 +0000 | [diff] [blame] | 197 | return false; |
| 198 | |
Thom Johansen | 46de4c2 | 2006-03-17 00:08:39 +0000 | [diff] [blame] | 199 | if (start) |
| 200 | { |
| 201 | if (pfn_unregister != NULL) |
| 202 | { |
| 203 | pfn_unregister(); |
| 204 | pfn_unregister = NULL; |
| 205 | } |
Michael Sevakis | 191320c | 2008-06-03 05:08:24 +0000 | [diff] [blame] | 206 | CPU_INT_DIS = TIMER2_MASK; |
| 207 | COP_INT_DIS = TIMER2_MASK; |
Thom Johansen | 46de4c2 | 2006-03-17 00:08:39 +0000 | [diff] [blame] | 208 | } |
Jens Arnold | bb5fbf7 | 2006-08-28 06:47:26 +0000 | [diff] [blame] | 209 | if (start || (cycles_new == -1)) /* within isr, cycles_new is "locked" */ |
Jens Arnold | 1bb8657 | 2006-09-01 06:13:33 +0000 | [diff] [blame] | 210 | TIMER2_CFG = 0xc0000000 | (cycles - 1); /* enable timer */ |
Jens Arnold | e9086e0 | 2006-08-27 23:43:04 +0000 | [diff] [blame] | 211 | else |
| 212 | cycles_new = cycles; |
| 213 | |
Jens Arnold | e44372e | 2005-07-26 20:01:11 +0000 | [diff] [blame] | 214 | return true; |
Will Robertson | 26a05af | 2007-09-22 02:17:08 +0000 | [diff] [blame] | 215 | #elif (CONFIG_CPU == IMX31L) |
| 216 | /* TODO */ |
Michael Sevakis | 040b6cc | 2008-04-15 14:44:32 +0000 | [diff] [blame] | 217 | (void)cycles; (void)start; |
| 218 | return false; |
Karl Kurbjun | 7b97fe2 | 2007-09-20 04:46:41 +0000 | [diff] [blame] | 219 | #else |
Michael Sevakis | 4ae87c8 | 2007-07-06 21:36:32 +0000 | [diff] [blame] | 220 | return __TIMER_SET(cycles, start); |
| 221 | #endif /* CONFIG_CPU */ |
Jens Arnold | e44372e | 2005-07-26 20:01:11 +0000 | [diff] [blame] | 222 | } |
| 223 | |
Jens Arnold | cfb073c | 2005-10-03 09:24:36 +0000 | [diff] [blame] | 224 | #ifdef CPU_COLDFIRE |
| 225 | void 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 Johansen | 46de4c2 | 2006-03-17 00:08:39 +0000 | [diff] [blame] | 243 | /* Register a user timer, called every <cycles> TIMER_FREQ cycles */ |
Jens Arnold | e44372e | 2005-07-26 20:01:11 +0000 | [diff] [blame] | 244 | bool timer_register(int reg_prio, void (*unregister_callback)(void), |
Jens Arnold | ac9b927 | 2008-04-04 19:38:46 +0000 | [diff] [blame] | 245 | long cycles, int int_prio, void (*timer_callback)(void) |
| 246 | IF_COP(, int core)) |
Jens Arnold | e44372e | 2005-07-26 20:01:11 +0000 | [diff] [blame] | 247 | { |
| 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 Arnold | e44372e | 2005-07-26 20:01:11 +0000 | [diff] [blame] | 254 | #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 Sevakis | 4ae87c8 | 2007-07-06 21:36:32 +0000 | [diff] [blame] | 266 | return true; |
Jens Arnold | e44372e | 2005-07-26 20:01:11 +0000 | [diff] [blame] | 267 | #elif defined CPU_COLDFIRE |
Jens Arnold | 72f9878 | 2005-11-05 03:28:20 +0000 | [diff] [blame] | 268 | ICR2 = 0x90; /* interrupt on level 4.0 */ |
Jens Arnold | e44372e | 2005-07-26 20:01:11 +0000 | [diff] [blame] | 269 | and_l(~(1<<10), &IMR); |
| 270 | TMR1 |= 1; /* start timer */ |
Michael Sevakis | 4ae87c8 | 2007-07-06 21:36:32 +0000 | [diff] [blame] | 271 | return true; |
Daniel Ankers | 242cbd5 | 2006-11-22 00:41:30 +0000 | [diff] [blame] | 272 | #elif defined(CPU_PP) |
Thom Johansen | 46de4c2 | 2006-03-17 00:08:39 +0000 | [diff] [blame] | 273 | /* unmask interrupt source */ |
Jens Arnold | ac9b927 | 2008-04-04 19:38:46 +0000 | [diff] [blame] | 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; |
Michael Sevakis | 4ae87c8 | 2007-07-06 21:36:32 +0000 | [diff] [blame] | 280 | return true; |
Tomasz Malesinski | cd630c9 | 2007-03-24 19:26:13 +0000 | [diff] [blame] | 281 | #elif CONFIG_CPU == PNX0101 |
| 282 | irq_set_int_handler(IRQ_TIMER1, TIMER1_ISR); |
| 283 | irq_enable_int(IRQ_TIMER1); |
Jens Arnold | e44372e | 2005-07-26 20:01:11 +0000 | [diff] [blame] | 284 | return true; |
Will Robertson | 26a05af | 2007-09-22 02:17:08 +0000 | [diff] [blame] | 285 | #elif CONFIG_CPU == IMX31L |
| 286 | /* TODO */ |
Michael Sevakis | 040b6cc | 2008-04-15 14:44:32 +0000 | [diff] [blame] | 287 | return false; |
Karl Kurbjun | 7b97fe2 | 2007-09-20 04:46:41 +0000 | [diff] [blame] | 288 | #else |
Michael Sevakis | 4ae87c8 | 2007-07-06 21:36:32 +0000 | [diff] [blame] | 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; |
Jens Arnold | e44372e | 2005-07-26 20:01:11 +0000 | [diff] [blame] | 299 | } |
| 300 | |
| 301 | bool timer_set_period(long cycles) |
| 302 | { |
| 303 | return timer_set(cycles, false); |
| 304 | } |
| 305 | |
| 306 | void 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 Ankers | 242cbd5 | 2006-11-22 00:41:30 +0000 | [diff] [blame] | 314 | #elif defined(CPU_PP) |
Jens Arnold | e9086e0 | 2006-08-27 23:43:04 +0000 | [diff] [blame] | 315 | TIMER2_CFG = 0; /* stop timer 2 */ |
Michael Sevakis | 191320c | 2008-06-03 05:08:24 +0000 | [diff] [blame] | 316 | CPU_INT_DIS = TIMER2_MASK; |
| 317 | COP_INT_DIS = TIMER2_MASK; |
Tomasz Malesinski | cd630c9 | 2007-03-24 19:26:13 +0000 | [diff] [blame] | 318 | #elif CONFIG_CPU == PNX0101 |
| 319 | TIMER1.ctrl &= ~0x80; /* disable timer 1 */ |
Tomasz Malesinski | 4fc77ac | 2007-09-22 23:37:58 +0000 | [diff] [blame] | 320 | irq_disable_int(IRQ_TIMER1); |
Maurus Cuelenaere | 95167e0 | 2008-04-24 20:08:28 +0000 | [diff] [blame] | 321 | #elif CONFIG_CPU == S3C2440 || CONFIG_CPU == DM320 |
Michael Sevakis | 4ae87c8 | 2007-07-06 21:36:32 +0000 | [diff] [blame] | 322 | __TIMER_UNREGISTER(); |
Jens Arnold | e44372e | 2005-07-26 20:01:11 +0000 | [diff] [blame] | 323 | #endif |
| 324 | pfn_timer = NULL; |
| 325 | pfn_unregister = NULL; |
| 326 | timer_prio = -1; |
| 327 | } |
| 328 | |