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$ 00036 * Revision 1.23 2009/01/17 11:26:37 haraldkipp 00037 * Getting rid of two remaining BSD types in favor of stdint. 00038 * Replaced 'u_int' by 'unsinged int' and 'uptr_t' by 'uintptr_t'. 00039 * 00040 * Revision 1.22 2008/08/22 09:25:33 haraldkipp 00041 * Clock value caching and new functions NutArchClockGet, NutClockGet and 00042 * NutClockSet added. 00043 * 00044 * Revision 1.21 2008/08/11 06:59:12 haraldkipp 00045 * BSD types replaced by stdint types (feature request #1282721). 00046 * 00047 * Revision 1.20 2008/08/06 12:51:01 haraldkipp 00048 * Added support for Ethernut 5 (AT91SAM9XE reference design). 00049 * 00050 * Revision 1.19 2008/07/08 08:25:04 haraldkipp 00051 * NutDelay is no more architecture specific. 00052 * Number of loops per millisecond is configurable or will be automatically 00053 * determined. 00054 * A new function NutMicroDelay provides shorter delays. 00055 * 00056 * Revision 1.18 2008/02/15 16:58:41 haraldkipp 00057 * Spport for AT91SAM7SE512 added. 00058 * 00059 * Revision 1.17 2007/10/04 19:59:47 olereinhardt 00060 * Support for SAM7S256 added 00061 * 00062 * Revision 1.16 2007/08/17 10:44:37 haraldkipp 00063 * Timer enable/disable macro replaces previous global interrupt 00064 * enable/disable or function calling. 00065 * 00066 * Revision 1.15 2007/04/12 09:03:48 haraldkipp 00067 * Miserable delay routine will now honor milliseconds on a 73 MHz ARM. 00068 * 00069 * Revision 1.14 2007/02/15 16:14:39 haraldkipp 00070 * Periodic interrupt timer can be used as a system clock. 00071 * 00072 * Revision 1.13 2006/10/08 16:48:07 haraldkipp 00073 * Documentation fixed 00074 * 00075 * Revision 1.12 2006/09/29 12:37:36 haraldkipp 00076 * Now working correctly, if the CPU is running on the second PLL. 00077 * 00078 * Revision 1.11 2006/09/05 12:27:25 haraldkipp 00079 * PLL clock calculation re-arranged to prevent 32-bit overflow. 00080 * NutTimerMillisToTicks() returned wrong result. Shane Buckham reported 00081 * this long time ago. Many thanks. Needs to be fixed for other platforms too. 00082 * 00083 * Revision 1.10 2006/08/31 18:59:50 haraldkipp 00084 * Added support for the AT91SAM9260. We now determine between processor and 00085 * master clock. A new API function At91GetMasterClock() had been added to 00086 * query the latter. 00087 * 00088 * Revision 1.9 2006/08/05 12:00:01 haraldkipp 00089 * NUT_CPU_FREQ did not override AT91_PLL_MAINCK or NUT_PLL_CPUCLK. Fixed. 00090 * 00091 * Revision 1.8 2006/07/26 11:17:16 haraldkipp 00092 * Defining AT91_PLL_MAINCK will automatically determine SAM7X clock by 00093 * reading PLL settings. 00094 * 00095 * Revision 1.7 2006/07/05 07:59:41 haraldkipp 00096 * Daidai's support for AT91SAM7X added. 00097 * 00098 * Revision 1.6 2006/06/28 17:10:35 haraldkipp 00099 * Include more general header file for ARM. 00100 * 00101 * Revision 1.5 2006/03/02 19:53:01 haraldkipp 00102 * Bugfix. The system timer configuration was based on a fixed MCU clock 00103 * of 66.6 MHz. Now it uses the actual frequency. 00104 * 00105 * Revision 1.4 2006/01/05 16:46:25 haraldkipp 00106 * Added support for CY22393 programmable clock chip. 00107 * 00108 * Revision 1.3 2005/10/24 08:34:13 haraldkipp 00109 * Moved AT91 family specific header files to sbudir arm. 00110 * Use new IRQ API. 00111 * 00112 * Revision 1.2 2005/08/02 17:46:45 haraldkipp 00113 * Major API documentation update. 00114 * 00115 * Revision 1.1 2005/07/26 18:02:26 haraldkipp 00116 * Moved from dev. 00117 * 00118 * Revision 1.2 2005/07/20 09:17:26 haraldkipp 00119 * Default NUT_CPU_FREQ and NUT_TICK_FREQ added. 00120 * NutTimerIntr() removed, because we can use the hardware independent code. 00121 * 00122 * Revision 1.1 2005/05/27 17:16:40 drsung 00123 * Moved the file. 00124 * 00125 * Revision 1.5 2005/04/05 17:50:46 haraldkipp 00126 * Use register names in gba.h. 00127 * 00128 * Revision 1.4 2004/11/08 19:16:37 haraldkipp 00129 * Hacked in Gameboy timer support 00130 * 00131 * Revision 1.3 2004/10/03 18:42:21 haraldkipp 00132 * No GBA support yet, but let the compiler run through 00133 * 00134 * Revision 1.2 2004/09/08 10:19:39 haraldkipp 00135 * Running on AT91 and S3C, thanks to James Tyou 00136 * 00137 */ 00138 00139 #define NUT_DEPRECATED 00140 00141 #include <cfg/os.h> 00142 #include <cfg/clock.h> 00143 #include <arch/arm.h> 00144 #include <dev/irqreg.h> 00145 #include <sys/timer.h> 00146 00147 #ifndef NUT_CPU_FREQ 00148 #ifdef NUT_PLL_CPUCLK 00149 #include <dev/cy2239x.h> 00150 #elif !defined(AT91_PLL_MAINCK) 00151 #define NUT_CPU_FREQ 73728000UL 00152 #endif /* !AT91_PLL_MAINCK */ 00153 #endif /* !NUT_CPU_FREQ */ 00154 00155 00160 00161 #ifndef NUT_TICK_FREQ 00162 #define NUT_TICK_FREQ 1000UL 00163 #endif 00164 00176 void NutRegisterTimer(void (*handler) (void *)) 00177 { 00178 #if defined(NUT_TICK_AT91PIT) 00179 /* Set compare value for the specified tick frequency. */ 00180 #if defined(AT91_PLL_MAINCK) 00181 outr(PIT_MR, (At91GetMasterClock() / (16 * NUT_TICK_FREQ) - 1) << PIT_PIV_LSB); 00182 #else 00183 outr(PIT_MR, (NutGetCpuClock() / (16 * NUT_TICK_FREQ) - 1) << PIT_PIV_LSB); 00184 #endif 00185 00186 /* Register system interrupt handler. */ 00187 NutRegisterSysIrqHandler(&syssig_PIT, handler, NULL); 00188 /* Enable interval timer and interval timer interrupts */ 00189 outr(PIT_MR, inr(PIT_MR) | PIT_PITEN | PIT_PITIEN); 00190 NutSysIrqEnable(&syssig_PIT); 00191 inr(PIT_PIVR); 00192 00193 #else /* NUT_TICK_AT91PIT */ 00194 00195 int dummy; 00196 00197 #if defined(MCU_AT91SAM7X) || defined(MCU_AT91SAM7S256) || defined(MCU_AT91SAM9260) || defined(MCU_AT91SAM9G45) || defined (MCU_AT91SAM7SE512) || defined(MCU_AT91SAM9XE512) 00198 /* Enable TC0 clock. */ 00199 outr(PMC_PCER, _BV(TC0_ID)); 00200 #endif 00201 00202 /* Disable the Clock Counter */ 00203 outr(TC0_CCR, TC_CLKDIS); 00204 /* Disable all interrupts */ 00205 outr(TC0_IDR, 0xFFFFFFFF); 00206 /* Clear the status register. */ 00207 dummy = inr(TC0_SR); 00208 /* Select divider and compare trigger */ 00209 outr(TC0_CMR, TC_CLKS_MCK32 | TC_CPCTRG); 00210 /* Enable the Clock counter */ 00211 outr(TC0_CCR, TC_CLKEN); 00212 /* Validate the RC compare interrupt */ 00213 outr(TC0_IER, TC_CPCS); 00214 00215 /* Register timer interrupt handler. */ 00216 NutRegisterIrqHandler(&sig_TC0, handler, 0); 00217 /* Set to lowest priority. */ 00218 NutIrqSetPriority(&sig_TC0, 0); 00219 00220 /* Enable timer 0 interrupts */ 00221 NutIrqEnable(&sig_TC0); 00222 //outr(AIC_IECR, _BV(TC0_ID)); 00223 00224 /* Set compare value for 1 ms. */ 00225 #if defined(AT91_PLL_MAINCK) 00226 outr(TC0_RC, At91GetMasterClock() / (32 * NUT_TICK_FREQ)); 00227 #else 00228 outr(TC0_RC, NutGetCpuClock() / (32 * NUT_TICK_FREQ)); 00229 #endif 00230 00231 /* Software trigger starts the clock. */ 00232 outr(TC0_CCR, TC_SWTRG); 00233 00234 #endif /* NUT_TICK_AT91PIT */ 00235 } 00236 00237 #if defined(AT91_PLL_MAINCK) 00238 00239 #if !defined(AT91_SLOW_CLOCK) 00240 /* This is just a guess and may be completely wrong. */ 00241 #define AT91_SLOW_CLOCK 32000 00242 #endif 00243 00251 static unsigned int At91GetPllClock(int plla) 00252 { 00253 unsigned int rc; 00254 unsigned int pllr; 00255 unsigned int divider; 00256 00257 /* 00258 * The main oscillator clock frequency is specified by the 00259 * configuration. It's usually equal to the on-board crystal. 00260 */ 00261 rc = AT91_PLL_MAINCK; 00262 00263 /* Retrieve the clock generator register of the selected PLL. */ 00264 #if defined(CKGR_PLLAR) && defined(CKGR_PLLBR) 00265 pllr = plla ? inr(CKGR_PLLAR) : inr(CKGR_PLLBR); 00266 #else 00267 pllr = inr(CKGR_PLLR); 00268 #endif 00269 00270 /* Extract the divider value. */ 00271 divider = (pllr & CKGR_DIV) >> CKGR_DIV_LSB; 00272 00273 if (divider) { 00274 rc /= divider; 00275 rc *= ((pllr & CKGR_MUL) >> CKGR_MUL_LSB) + 1; 00276 } 00277 return rc; 00278 } 00279 00285 static uint32_t At91GetProcessorClock(void) 00286 { 00287 unsigned int rc = 0; 00288 unsigned int mckr = inr(PMC_MCKR); 00289 00290 /* Determine the clock source. */ 00291 switch(mckr & PMC_CSS) { 00292 case PMC_CSS_SLOW_CLK: 00293 /* Slow clock selected. */ 00294 rc = AT91_SLOW_CLOCK; 00295 break; 00296 case PMC_CSS_MAIN_CLK: 00297 /* Main clock selected. */ 00298 rc = AT91_PLL_MAINCK; 00299 break; 00300 #if defined(PMC_CSS_PLLA_CLK) 00301 case PMC_CSS_PLLA_CLK: 00302 /* PLL A clock selected. */ 00303 rc = At91GetPllClock(1); 00304 break; 00305 #endif 00306 #if defined(PMC_CSS_PLLB_CLK) 00307 case PMC_CSS_PLLB_CLK: 00308 /* PLL (B) clock selected. */ 00309 rc = At91GetPllClock(0); 00310 break; 00311 #elif defined(PMC_CSS_PLL_CLK) 00312 case PMC_CSS_PLL_CLK: 00313 /* PLL (B) clock selected. */ 00314 rc = At91GetPllClock(0); 00315 break; 00316 #endif 00317 } 00318 00319 /* Handle pre-scaling. */ 00320 mckr &= PMC_PRES; 00321 mckr >>= PMC_PRES_LSB; 00322 if (mckr < 7) { 00323 rc /= _BV(mckr); 00324 } 00325 else { 00326 rc = 0; 00327 } 00328 return rc; 00329 } 00330 00338 uint32_t At91GetMasterClock(void) 00339 { 00340 return NutArchClockGet(NUT_HWCLK_PERIPHERAL); 00341 } 00342 00343 #endif /* AT91_PLL_MAINCK */ 00344 00345 #ifndef NUT_CPU_FREQ 00346 00355 uint32_t NutArchClockGet(int idx) 00356 { 00357 uint32_t rc = 0; 00358 00359 if (idx == NUT_HWCLK_CPU) { 00360 #if defined(AT91_PLL_MAINCK) 00361 rc = At91GetProcessorClock(); 00362 #elif defined(NUT_PLL_CPUCLK) 00363 rc = Cy2239xGetFreq(NUT_PLL_CPUCLK, 7); 00364 #else 00365 #warning "No CPU Clock defined" 00366 #endif 00367 } 00368 #if defined(AT91_PLL_MAINCK) 00369 if (idx == NUT_HWCLK_PERIPHERAL) { 00370 rc = At91GetProcessorClock(); 00371 #if defined(PMC_MDIV) 00372 switch(inr(PMC_MCKR) & PMC_MDIV) { 00373 case PMC_MDIV_2: 00374 rc /= 2; 00375 break; 00376 case PMC_MDIV_4: 00377 rc /= 4; 00378 break; 00379 case PMC_MDIV_3: 00380 rc /= 3; 00381 break; 00382 } 00383 #endif 00384 } 00385 #ifdef PMC_PLLADIV2 00386 if (inr(PMC_MCKR) & PMC_PLLADIV2) 00387 { 00388 rc /= 2; 00389 } 00390 #endif 00391 #endif 00392 return rc; 00393 } 00394 #endif 00395 00401 uint32_t NutGetTickClock(void) 00402 { 00403 unsigned int rc; 00404 00405 #if defined(NUT_TICK_AT91PIT) 00406 rc = ((inr(PIT_MR) & PIT_PIV) + 1) * 16; 00407 #else 00408 rc = inr(TC0_RC) * 32; 00409 #endif 00410 00411 if (rc) { 00412 #if defined(AT91_PLL_MAINCK) 00413 return At91GetMasterClock() / rc; 00414 #else 00415 return NutGetCpuClock() / rc; 00416 #endif 00417 } 00418 return NUT_TICK_FREQ; 00419 } 00420 00424 uint32_t NutTimerMillisToTicks(uint32_t ms) 00425 { 00426 return (ms * NutGetTickClock()) / 1000; 00427 } 00428