BeRTOS
init_at91.c
Go to the documentation of this file.
00001 
00039 #include <io/arm.h>
00040 #include <cfg/macros.h>
00041 
00042 #define USE_FIXED_PLL 1
00043 
00044 #define XTAL_FREQ 18432000UL
00045 
00046 #if USE_FIXED_PLL
00047     #if CPU_FREQ != 48054857L
00048         /* Avoid errors on nightly test */
00049         #if !defined(ARCH_NIGHTTEST) || !(ARCH & ARCH_NIGHTTEST)
00050             #warning Clock registers set for 48.055MHz operation, revise following code if you want a different clock.
00051         #endif
00052     #endif
00053 
00054     /*
00055      * With a 18.432MHz cristal, master clock is:
00056      * (((18.432 * (PLL_MUL_VAL + 1)) / PLL_DIV_VAL) / AT91MCK_PRES) = 48.055MHz
00057      */
00058     #define PLL_MUL_VAL  72  
00059     #define PLL_DIV_VAL  14
00060     #define AT91MCK_PRES PMC_PRES_CLK_2
00061 
00062 #else /* !USE_FIXED_PLL*/
00063 
00064     #define PLL_IN_MIN  1000000UL
00065     #define PLL_IN_MAX  32000000UL
00066     #define PLL_OUT_MIN 80000000UL
00067     #define PLL_OUT_MAX 160000000UL
00068 
00069     #define DIV_HARD_MIN 1
00070     #define DIV_HARD_MAX 255
00071 
00072     #define DIV_MIN  (DIV_ROUND(XTAL_FREQ, PLL_IN_MAX) \
00073         < DIV_HARD_MIN ? DIV_HARD_MIN : DIV_ROUND(XTAL_FREQ, PLL_IN_MAX))
00074 
00075     #define DIV_MAX  (DIV_ROUND(XTAL_FREQ, PLL_IN_MIN) \
00076         > DIV_HARD_MAX ? DIV_HARD_MAX : DIV_ROUND(XTAL_FREQ, PLL_IN_MIN))
00077 
00078     #define MUL_MIN  0
00079     #define MUL_MAX  2047
00080 
00081     typedef struct PllRegs
00082     {
00083         uint32_t mul;
00084         uint32_t div;
00085         uint32_t pres;
00086     } PllRegs;
00087 
00093     static const PllRegs pllCostants(void)
00094     {
00095         uint32_t best_err = CPU_FREQ;
00096         PllRegs res;
00097 
00098         for (uint32_t div = DIV_MIN; div <= DIV_MAX; div++)
00099         {
00100             for (uint32_t pres = 0; pres < 8; pres++)
00101             {
00102                 uint32_t mul = DIV_ROUND((CPU_FREQ * div) << pres, XTAL_FREQ) - 1;
00103                 if (mul <= MUL_MAX)
00104                 {
00105                     uint32_t pll = (XTAL_FREQ * (mul + 1)) / div;
00106                     if (pll >= PLL_OUT_MIN && pll <= PLL_OUT_MAX)
00107                     {
00108                         uint32_t err = ABS((int32_t)((pll >> pres) - CPU_FREQ));
00109                         if (err == 0)
00110                         {
00111                             res.div = div;
00112                             res.mul = mul;
00113                             res.pres = pres;
00114                             return res;
00115                         }
00116                         if (err < best_err)
00117                         {
00118                             best_err = err;
00119                             res.div = div;
00120                             res.mul = mul;
00121                             res.pres = pres;
00122                         }
00123                     }
00124                 }
00125             }
00126         }
00127         return res;
00128     }
00129 #endif  /* !USE_FIXED_PLL*/
00130 
00131 /*
00132  * Override dummy hardware init functions supplied by the ASM startup
00133  * routine.
00134  */
00135 
00136 void __init1(void);
00137 void __init2(void);
00138 
00151 void __init1(void)
00152 {
00153     /*
00154      * Compute number of master clock cycles in 1.5us.
00155      * Needed by flash writing functions.
00156      * The maximum FMCN value is 0xFF and 0 can be used only if
00157      * master clock is less than 33kHz.
00158      */
00159     #define MCN  DIV_ROUNDUP(CPU_FREQ, 666667UL)
00160     #define FMCN (CPU_FREQ <= 33333UL ? 0 : (MCN < 0xFF ? MCN : 0xFF))
00161 
00162     #if CPU_FREQ < 30000000UL
00163         /* Use 1 cycles for flash access. */
00164         MC_FMR = FMCN << MC_FMCN_SHIFT | MC_FWS_1R2W;
00165     #else
00166         /* Use 2 cycles for flash access. */
00167         MC_FMR = FMCN << MC_FMCN_SHIFT | MC_FWS_2R3W;
00168     #endif
00169 
00170         /* Disable all interrupts. Useful for debugging w/o target reset. */
00171     AIC_EOICR = 0xFFFFFFFF;
00172     AIC_IDCR =  0xFFFFFFFF;
00173 
00174         /* The watchdog is enabled after processor reset. Disable it. */
00175     WDT_MR = BV(WDT_WDDIS);
00176 
00177         /*
00178          * Enable the main oscillator. Set startup time of 6 * 8 slow
00179          * clock cycles and wait until oscillator is stabilized.
00180          */
00181     CKGR_MOR = (6 << 8) | BV(CKGR_MOSCEN);
00182     while (!(PMC_SR & BV(PMC_MOSCS))) ;
00183 
00184         /* Switch to Slow oscillator clock. */
00185     PMC_MCKR &= ~PMC_CSS_MASK;
00186     while (!(PMC_SR & BV(PMC_MCKRDY))) ;
00187 
00188         /* Switch to prescaler div 1 factor. */
00189     PMC_MCKR &= ~PMC_PRES_MASK;
00190     while (!(PMC_SR & BV(PMC_MCKRDY))) ;
00191 
00192     uint32_t div, pres, mul;
00193     #if USE_FIXED_PLL
00194         div = PLL_DIV_VAL;
00195         mul = PLL_MUL_VAL;
00196         pres = AT91MCK_PRES;
00197     #else
00198         PllRegs pll = pllCostants();
00199         div = pll.div;
00200         mul = pll.mul;
00201         pres = pll.pres << PMC_PRES_SHIFT;
00202     #endif
00203 
00204         /*
00205          * Set PLL:
00206          * PLLfreq = crystal / divider * (multiplier + 1)
00207          * Wait 28 clock cycles until PLL is locked.
00208          */
00209     CKGR_PLLR = ((mul << CKGR_MUL_SHIFT)
00210         | (28 << CKGR_PLLCOUNT_SHIFT) | div);
00211     while (!(PMC_SR & BV(PMC_LOCK))) ;
00212 
00213     /* Set master clock prescaler.  */
00214     PMC_MCKR = pres;
00215     while (!(PMC_SR & BV(PMC_MCKRDY))) ;
00216 
00217         /*
00218          * Switch to PLL clock. Trying to set this together with the
00219          * prescaler fails (see datasheets).
00220          */
00221     PMC_MCKR |= PMC_CSS_PLL_CLK;
00222     while (!(PMC_SR & BV(PMC_MCKRDY))) ;
00223 }
00224 
00230 void __init2(void)
00231 {
00232     /* Enable external reset key. */
00233     RSTC_MR = (RSTC_KEY | BV(RSTC_URSTEN));
00234 
00235     /* Enable clock for PIO(s) */
00236     PMC_PCER = BV(PIOA_ID);
00237     #if CPU_ARM_SAM7X
00238         PMC_PCER |= BV(PIOB_ID);
00239     #endif
00240 }