00001 /* 00002 * Copyright (C) 2001-2007 by egnite Software GmbH. All rights reserved. 00003 * 00004 * Redistribution and use in source and binary forms, with or without 00005 * modification, are permitted provided that the following conditions 00006 * are met: 00007 * 00008 * 1. Redistributions of source code must retain the above copyright 00009 * notice, this list of conditions and the following disclaimer. 00010 * 2. Redistributions in binary form must reproduce the above copyright 00011 * notice, this list of conditions and the following disclaimer in the 00012 * documentation and/or other materials provided with the distribution. 00013 * 3. Neither the name of the copyright holders nor the names of 00014 * contributors may be used to endorse or promote products derived 00015 * from this software without specific prior written permission. 00016 * 00017 * THIS SOFTWARE IS PROVIDED BY EGNITE SOFTWARE GMBH AND CONTRIBUTORS 00018 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 00019 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 00020 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL EGNITE 00021 * SOFTWARE GMBH OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 00022 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 00023 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS 00024 * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 00025 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 00026 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF 00027 * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 00028 * SUCH DAMAGE. 00029 * 00030 * For additional information see http://www.ethernut.de/ 00031 * 00032 */ 00033 00034 /* 00035 * $Log: ostimer_at91.c,v $ 00036 * Revision 1.16 2007/08/17 10:44:37 haraldkipp 00037 * Timer enable/disable macro replaces previous global interrupt 00038 * enable/disable or function calling. 00039 * 00040 * Revision 1.15 2007/04/12 09:03:48 haraldkipp 00041 * Miserable delay routine will now honor milliseconds on a 73 MHz ARM. 00042 * 00043 * Revision 1.14 2007/02/15 16:14:39 haraldkipp 00044 * Periodic interrupt timer can be used as a system clock. 00045 * 00046 * Revision 1.13 2006/10/08 16:48:07 haraldkipp 00047 * Documentation fixed 00048 * 00049 * Revision 1.12 2006/09/29 12:37:36 haraldkipp 00050 * Now working correctly, if the CPU is running on the second PLL. 00051 * 00052 * Revision 1.11 2006/09/05 12:27:25 haraldkipp 00053 * PLL clock calculation re-arranged to prevent 32-bit overflow. 00054 * NutTimerMillisToTicks() returned wrong result. Shane Buckham reported 00055 * this long time ago. Many thanks. Needs to be fixed for other platforms too. 00056 * 00057 * Revision 1.10 2006/08/31 18:59:50 haraldkipp 00058 * Added support for the AT91SAM9260. We now determine between processor and 00059 * master clock. A new API function At91GetMasterClock() had been added to 00060 * query the latter. 00061 * 00062 * Revision 1.9 2006/08/05 12:00:01 haraldkipp 00063 * NUT_CPU_FREQ did not override AT91_PLL_MAINCK or NUT_PLL_CPUCLK. Fixed. 00064 * 00065 * Revision 1.8 2006/07/26 11:17:16 haraldkipp 00066 * Defining AT91_PLL_MAINCK will automatically determine SAM7X clock by 00067 * reading PLL settings. 00068 * 00069 * Revision 1.7 2006/07/05 07:59:41 haraldkipp 00070 * Daidai's support for AT91SAM7X added. 00071 * 00072 * Revision 1.6 2006/06/28 17:10:35 haraldkipp 00073 * Include more general header file for ARM. 00074 * 00075 * Revision 1.5 2006/03/02 19:53:01 haraldkipp 00076 * Bugfix. The system timer configuration was based on a fixed MCU clock 00077 * of 66.6 MHz. Now it uses the actual frequency. 00078 * 00079 * Revision 1.4 2006/01/05 16:46:25 haraldkipp 00080 * Added support for CY22393 programmable clock chip. 00081 * 00082 * Revision 1.3 2005/10/24 08:34:13 haraldkipp 00083 * Moved AT91 family specific header files to sbudir arm. 00084 * Use new IRQ API. 00085 * 00086 * Revision 1.2 2005/08/02 17:46:45 haraldkipp 00087 * Major API documentation update. 00088 * 00089 * Revision 1.1 2005/07/26 18:02:26 haraldkipp 00090 * Moved from dev. 00091 * 00092 * Revision 1.2 2005/07/20 09:17:26 haraldkipp 00093 * Default NUT_CPU_FREQ and NUT_TICK_FREQ added. 00094 * NutTimerIntr() removed, because we can use the hardware independent code. 00095 * 00096 * Revision 1.1 2005/05/27 17:16:40 drsung 00097 * Moved the file. 00098 * 00099 * Revision 1.5 2005/04/05 17:50:46 haraldkipp 00100 * Use register names in gba.h. 00101 * 00102 * Revision 1.4 2004/11/08 19:16:37 haraldkipp 00103 * Hacked in Gameboy timer support 00104 * 00105 * Revision 1.3 2004/10/03 18:42:21 haraldkipp 00106 * No GBA support yet, but let the compiler run through 00107 * 00108 * Revision 1.2 2004/09/08 10:19:39 haraldkipp 00109 * Running on AT91 and S3C, thanks to James Tyou 00110 * 00111 */ 00112 00113 #include <cfg/os.h> 00114 #include <cfg/clock.h> 00115 #include <arch/arm.h> 00116 #include <dev/irqreg.h> 00117 #include <sys/timer.h> 00118 00119 #ifndef NUT_CPU_FREQ 00120 #ifdef NUT_PLL_CPUCLK 00121 #include <dev/cy2239x.h> 00122 #elif !defined(AT91_PLL_MAINCK) 00123 #define NUT_CPU_FREQ 73728000UL 00124 #endif /* !AT91_PLL_MAINCK */ 00125 #endif /* !NUT_CPU_FREQ */ 00126 00127 00132 00133 #ifndef NUT_TICK_FREQ 00134 #define NUT_TICK_FREQ 1000UL 00135 #endif 00136 00152 void NutDelay(u_char ms) 00153 { 00154 int i; 00155 00156 while (ms--) { 00157 for (i = 14600; i--; ) { 00158 _NOP(); 00159 } 00160 } 00161 } 00162 00174 void NutRegisterTimer(void (*handler) (void *)) 00175 { 00176 #if defined(NUT_TICK_AT91PIT) 00177 00178 /* Set compare value for the specified tick frequency. */ 00179 #if defined(AT91_PLL_MAINCK) 00180 outr(PIT_MR, (At91GetMasterClock() / (16 * NUT_TICK_FREQ) - 1) << PIT_PIV_LSB); 00181 #else 00182 outr(PIT_MR, (NutGetCpuClock() / (16 * NUT_TICK_FREQ) - 1) << PIT_PIV_LSB); 00183 #endif 00184 00185 /* Register system interrupt handler. */ 00186 NutRegisterSysIrqHandler(&syssig_PIT, handler, NULL); 00187 /* Enable interval timer and interval timer interrupts */ 00188 outr(PIT_MR, inr(PIT_MR) | PIT_PITEN | PIT_PITIEN); 00189 NutSysIrqEnable(&syssig_PIT); 00190 inr(PIT_PIVR); 00191 00192 #else /* NUT_TICK_AT91PIT */ 00193 00194 int dummy; 00195 00196 #if defined(MCU_AT91SAM7X256) || defined(MCU_AT91SAM9260) 00197 /* Enable TC0 clock. */ 00198 outr(PMC_PCER, _BV(TC0_ID)); 00199 #endif 00200 00201 /* Disable the Clock Counter */ 00202 outr(TC0_CCR, TC_CLKDIS); 00203 /* Disable all interrupts */ 00204 outr(TC0_IDR, 0xFFFFFFFF); 00205 /* Clear the status register. */ 00206 dummy = inr(TC0_SR); 00207 /* Select divider and compare trigger */ 00208 outr(TC0_CMR, TC_CLKS_MCK32 | TC_CPCTRG); 00209 /* Enable the Clock counter */ 00210 outr(TC0_CCR, TC_CLKEN); 00211 /* Validate the RC compare interrupt */ 00212 outr(TC0_IER, TC_CPCS); 00213 00214 /* Register timer interrupt handler. */ 00215 NutRegisterIrqHandler(&sig_TC0, handler, 0); 00216 /* Set to lowest priority. */ 00217 NutIrqSetPriority(&sig_TC0, 0); 00218 00219 /* Enable timer 0 interrupts */ 00220 NutIrqEnable(&sig_TC0); 00221 //outr(AIC_IECR, _BV(TC0_ID)); 00222 00223 /* Set compare value for 1 ms. */ 00224 #if defined(AT91_PLL_MAINCK) 00225 outr(TC0_RC, At91GetMasterClock() / (32 * NUT_TICK_FREQ)); 00226 #else 00227 outr(TC0_RC, NutGetCpuClock() / (32 * NUT_TICK_FREQ)); 00228 #endif 00229 00230 /* Software trigger starts the clock. */ 00231 outr(TC0_CCR, TC_SWTRG); 00232 00233 #endif /* NUT_TICK_AT91PIT */ 00234 } 00235 00236 #if defined(AT91_PLL_MAINCK) 00237 00238 #if !defined(AT91_SLOW_CLOCK) 00239 /* This is just a guess and may be completely wrong. */ 00240 #define AT91_SLOW_CLOCK 32000 00241 #endif 00242 00250 static u_int At91GetPllClock(int plla) 00251 { 00252 u_int rc; 00253 u_int pllr; 00254 u_int divider; 00255 00256 /* 00257 * The main oscillator clock frequency is specified by the 00258 * configuration. It's usually equal to the on-board crystal. 00259 */ 00260 rc = AT91_PLL_MAINCK; 00261 00262 /* Retrieve the clock generator register of the selected PLL. */ 00263 #if defined(CKGR_PLLAR) && defined(CKGR_PLLBR) 00264 pllr = plla ? inr(CKGR_PLLAR) : inr(CKGR_PLLBR); 00265 #else 00266 pllr = inr(CKGR_PLLR); 00267 #endif 00268 00269 /* Extract the divider value. */ 00270 divider = (pllr & CKGR_DIV) >> CKGR_DIV_LSB; 00271 00272 if (divider) { 00273 rc /= divider; 00274 rc *= ((pllr & CKGR_MUL) >> CKGR_MUL_LSB) + 1; 00275 } 00276 return rc; 00277 } 00278 00284 static u_long At91GetProcessorClock(void) 00285 { 00286 u_int rc = 0; 00287 u_int mckr = inr(PMC_MCKR); 00288 00289 /* Determine the clock source. */ 00290 switch(mckr & PMC_CSS) { 00291 case PMC_CSS_SLOW_CLK: 00292 /* Slow clock selected. */ 00293 rc = AT91_SLOW_CLOCK; 00294 break; 00295 case PMC_CSS_MAIN_CLK: 00296 /* Main clock selected. */ 00297 rc = AT91_PLL_MAINCK; 00298 break; 00299 #if defined(PMC_CSS_PLLA_CLK) 00300 case PMC_CSS_PLLA_CLK: 00301 /* PLL A clock selected. */ 00302 rc = At91GetPllClock(1); 00303 break; 00304 #endif 00305 #if defined(PMC_CSS_PLLB_CLK) 00306 case PMC_CSS_PLLB_CLK: 00307 /* PLL (B) clock selected. */ 00308 rc = At91GetPllClock(0); 00309 break; 00310 #elif defined(PMC_CSS_PLL_CLK) 00311 case PMC_CSS_PLL_CLK: 00312 /* PLL (B) clock selected. */ 00313 rc = At91GetPllClock(0); 00314 break; 00315 #endif 00316 } 00317 00318 /* Handle pre-scaling. */ 00319 mckr &= PMC_PRES; 00320 mckr >>= PMC_PRES_LSB; 00321 if (mckr < 7) { 00322 rc /= _BV(mckr); 00323 } 00324 else { 00325 rc = 0; 00326 } 00327 return rc; 00328 } 00329 00335 u_long At91GetMasterClock(void) 00336 { 00337 u_long rc = At91GetProcessorClock(); 00338 00339 #if defined(MCU_AT91SAM9260) 00340 switch(inr(PMC_MCKR) & PMC_MDIV) { 00341 case PMC_MDIV_2: 00342 rc /= 2; 00343 break; 00344 case PMC_MDIV_4: 00345 rc /= 4; 00346 break; 00347 } 00348 #endif 00349 return rc; 00350 } 00351 00352 #endif /* AT91_PLL_MAINCK */ 00353 00363 u_long NutGetCpuClock(void) 00364 { 00365 #if defined(NUT_CPU_FREQ) 00366 return NUT_CPU_FREQ; 00367 #elif defined(AT91_PLL_MAINCK) 00368 return At91GetProcessorClock(); 00369 #elif defined(NUT_PLL_CPUCLK) 00370 return Cy2239xGetFreq(NUT_PLL_CPUCLK, 7); 00371 #else 00372 #warning "No CPU Clock defined" 00373 return 0; 00374 #endif 00375 } 00376 00382 u_long NutGetTickClock(void) 00383 { 00384 u_int rc; 00385 00386 #if defined(NUT_TICK_AT91PIT) 00387 rc = ((inr(PIT_MR) & PIT_PIV) + 1) * 16; 00388 #else 00389 rc = inr(TC0_RC) * 32; 00390 #endif 00391 00392 if (rc) { 00393 #if defined(AT91_PLL_MAINCK) 00394 return At91GetMasterClock() / rc; 00395 #else 00396 return NutGetCpuClock() / rc; 00397 #endif 00398 } 00399 return NUT_TICK_FREQ; 00400 } 00401 00405 u_long NutTimerMillisToTicks(u_long ms) 00406 { 00407 return (ms * NutGetTickClock()) / 1000; 00408 } 00409