edcfff23f943acd988c696f1947200037a606f2f
[bertos.git] / bertos / cpu / arm / hw / init_at91.c
1 /**
2  * \file
3  * <!--
4  * This file is part of BeRTOS.
5  *
6  * Bertos is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
19  *
20  * As a special exception, you may use this file as part of a free software
21  * library without restriction.  Specifically, if other files instantiate
22  * templates or use macros or inline functions from this file, or you compile
23  * this file and link it with other files to produce an executable, this
24  * file does not by itself cause the resulting executable to be covered by
25  * the GNU General Public License.  This exception does not however
26  * invalidate any other reasons why the executable file might be covered by
27  * the GNU General Public License.
28  *
29  * Copyright 2010 Develer S.r.l. (http://www.develer.com/)
30  *
31  * -->
32  *
33  *
34  * \author Francesco Sacchi <batt@develer.com>
35  *
36  * \brief AT91SAM7S256 CRT.
37  */
38
39 #include <io/arm.h>
40 #include <cfg/macros.h>
41
42 #define USE_FIXED_PLL 1
43
44 #define XTAL_FREQ 18420000UL
45
46 #if USE_FIXED_PLL
47         #if CPU_FREQ != 48023000L
48                 /* Avoid errors on nightly test */
49                 #if !defined(ARCH_NIGHTTEST) || !(ARCH & ARCH_NIGHTTEST)
50                         #warning Clock registers set for 48.023MHz operation, revise following code if you want a different clock.
51                 #endif
52         #endif
53
54         /*
55          * With a 18.420MHz cristal, master clock is:
56          * (((18.420 * PLL_MUL_VAL + 1) / PLL_DIV_VAL) / AT91MCK_PRES) = 48.023MHz
57          */
58         #define PLL_MUL_VAL  72  /**< Real multiplier value is PLL_MUL_VAL + 1! */
59         #define PLL_DIV_VAL  14
60         #define AT91MCK_PRES PMC_PRES_CLK_2
61
62 #else /* !USE_FIXED_PLL*/
63
64         #define PLL_IN_MIN  1000000UL
65         #define PLL_IN_MAX  32000000UL
66         #define PLL_OUT_MIN 80000000UL
67         #define PLL_OUT_MAX 160000000UL
68
69         #define DIV_HARD_MIN 1
70         #define DIV_HARD_MAX 255
71
72         #define DIV_MIN  (DIV_ROUND(XTAL_FREQ, PLL_IN_MAX) \
73                 < DIV_HARD_MIN ? DIV_HARD_MIN : DIV_ROUND(XTAL_FREQ, PLL_IN_MAX))
74
75         #define DIV_MAX  (DIV_ROUND(XTAL_FREQ, PLL_IN_MIN) \
76                 > DIV_HARD_MAX ? DIV_HARD_MAX : DIV_ROUND(XTAL_FREQ, PLL_IN_MIN))
77
78         #define MUL_MIN  0
79         #define MUL_MAX  2047
80
81         typedef struct PllRegs
82         {
83                 uint32_t mul;
84                 uint32_t div;
85                 uint32_t pres;
86         } PllRegs;
87
88         /**
89          * Code used to automatically compute the PLL registers.
90          * Since the processor uses the internal 32kHz oscillator
91          * this function takes a lot of time to be executed (~3s!).
92          */
93         static const PllRegs pllCostants(void)
94         {
95                 uint32_t best_err = CPU_FREQ;
96                 PllRegs res;
97
98                 for (uint32_t div = DIV_MIN; div <= DIV_MAX; div++)
99                 {
100                         for (uint32_t pres = 0; pres < 8; pres++)
101                         {
102                                 uint32_t mul = DIV_ROUND((CPU_FREQ * div) << pres, XTAL_FREQ) - 1;
103                                 if (mul <= MUL_MAX)
104                                 {
105                                         uint32_t pll = (XTAL_FREQ * (mul + 1)) / div;
106                                         if (pll >= PLL_OUT_MIN && pll <= PLL_OUT_MAX)
107                                         {
108                                                 uint32_t err = ABS((int32_t)((pll >> pres) - CPU_FREQ));
109                                                 if (err == 0)
110                                                 {
111                                                         res.div = div;
112                                                         res.mul = mul;
113                                                         res.pres = pres;
114                                                         return res;
115                                                 }
116                                                 if (err < best_err)
117                                                 {
118                                                         best_err = err;
119                                                         res.div = div;
120                                                         res.mul = mul;
121                                                         res.pres = pres;
122                                                 }
123                                         }
124                                 }
125                         }
126                 }
127                 return res;
128         }
129 #endif  /* !USE_FIXED_PLL*/
130
131 /*
132  * Override dummy hardware init functions supplied by the ASM startup
133  * routine.
134  */
135
136 void __init1(void);
137 void __init2(void);
138
139 /**
140  * Early hardware initialization routine1.
141  * This will be called by the ASM CRT routine just
142  * *before* clearing .bss and loading .data sections.
143  * Usually only basic tasks are performed here (i.e. setting the PLL).
144  * For more generic tasks, __init2() should be used.
145  *
146  * \note Please keep in mind that since .bss and .data are not yet set, care
147  *       must be taken. No static data can be used inside this funcition.
148  *       Also some libc functions or floating point operations could potentially
149  *       use initialized static data, be aware!
150  */
151 void __init1(void)
152 {
153         /* Disable all interrupts. Useful for debugging w/o target reset. */
154         AIC_EOICR = 0xFFFFFFFF;
155         AIC_IDCR =  0xFFFFFFFF;
156
157         /* The watchdog is enabled after processor reset. Disable it. */
158         WDT_MR = BV(WDT_WDDIS);
159
160         /*
161          * Enable the main oscillator. Set startup time of 6 * 8 slow
162          * clock cycles and wait until oscillator is stabilized.
163          */
164         CKGR_MOR = (6 << 8) | BV(CKGR_MOSCEN);
165         while (!(PMC_SR & BV(PMC_MOSCS))) ;
166
167         /* Switch to Slow oscillator clock. */
168         PMC_MCKR &= ~PMC_CSS_MASK;
169         while (!(PMC_SR & BV(PMC_MCKRDY))) ;
170
171         /* Switch to prescaler div 1 factor. */
172         PMC_MCKR &= ~PMC_PRES_MASK;
173         while (!(PMC_SR & BV(PMC_MCKRDY))) ;
174
175         uint32_t div, pres, mul;
176         #if USE_FIXED_PLL
177                 div = PLL_DIV_VAL;
178                 mul = PLL_MUL_VAL;
179                 pres = AT91MCK_PRES;
180         #else
181                 PllRegs pll = pllCostants();
182                 div = pll.div;
183                 mul = pll.mul;
184                 pres = pll.pres << PMC_PRES_SHIFT;
185         #endif
186
187         /*
188          * Set PLL:
189          * PLLfreq = crystal / divider * (multiplier + 1)
190          * Wait 28 clock cycles until PLL is locked.
191          */
192         CKGR_PLLR = ((mul << CKGR_MUL_SHIFT)
193                 | (28 << CKGR_PLLCOUNT_SHIFT) | div);
194         while (!(PMC_SR & BV(PMC_LOCK))) ;
195
196         /* Set master clock prescaler.  */
197         PMC_MCKR = pres;
198         while (!(PMC_SR & BV(PMC_MCKRDY))) ;
199
200         /*
201          * Switch to PLL clock. Trying to set this together with the
202          * prescaler fails (see datasheets).
203          */
204         PMC_MCKR |= PMC_CSS_PLL_CLK;
205         while (!(PMC_SR & BV(PMC_MCKRDY))) ;
206 }
207
208 /**
209  * Early hardware initialization routine2.
210  * This will be called by the ASM CRT routine just
211  * *after* clearing .bss and loading .data sections and before calling main().
212  */
213 void __init2(void)
214 {
215         /* Enable external reset key. */
216         RSTC_MR = (RSTC_KEY | BV(RSTC_URSTEN));
217
218         /* Enable clock for PIO(s) */
219         PMC_PCER = BV(PIOA_ID);
220         #if CPU_ARM_SAM7X
221                 PMC_PCER |= BV(PIOB_ID);
222         #endif
223 }