Add watchdog driver.
[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 #include "cfg/cfg_wdt.h"
43
44 #define USE_FIXED_PLL 1
45
46 #define XTAL_FREQ 18432000UL
47
48 #if USE_FIXED_PLL
49         #if CPU_FREQ != 48054857L
50                 /* Avoid errors on nightly test */
51                 #if !defined(ARCH_NIGHTTEST) || !(ARCH & ARCH_NIGHTTEST)
52                         #warning Clock registers set for 48.055MHz operation, revise following code if you want a different clock.
53                 #endif
54         #endif
55
56         /*
57          * With a 18.432MHz cristal, master clock is:
58          * (((18.432 * (PLL_MUL_VAL + 1)) / PLL_DIV_VAL) / AT91MCK_PRES) = 48.055MHz
59          */
60         #define PLL_MUL_VAL  72  /**< Real multiplier value is PLL_MUL_VAL + 1! */
61         #define PLL_DIV_VAL  14
62         #define AT91MCK_PRES PMC_PRES_CLK_2
63
64 #else /* !USE_FIXED_PLL*/
65
66         #define PLL_IN_MIN  1000000UL
67         #define PLL_IN_MAX  32000000UL
68         #define PLL_OUT_MIN 80000000UL
69         #define PLL_OUT_MAX 160000000UL
70
71         #define DIV_HARD_MIN 1
72         #define DIV_HARD_MAX 255
73
74         #define DIV_MIN  (DIV_ROUND(XTAL_FREQ, PLL_IN_MAX) \
75                 < DIV_HARD_MIN ? DIV_HARD_MIN : DIV_ROUND(XTAL_FREQ, PLL_IN_MAX))
76
77         #define DIV_MAX  (DIV_ROUND(XTAL_FREQ, PLL_IN_MIN) \
78                 > DIV_HARD_MAX ? DIV_HARD_MAX : DIV_ROUND(XTAL_FREQ, PLL_IN_MIN))
79
80         #define MUL_MIN  0
81         #define MUL_MAX  2047
82
83         typedef struct PllRegs
84         {
85                 uint32_t mul;
86                 uint32_t div;
87                 uint32_t pres;
88         } PllRegs;
89
90         /**
91          * Code used to automatically compute the PLL registers.
92          * Since the processor uses the internal 32kHz oscillator
93          * this function takes a lot of time to be executed (~3s!).
94          */
95         static const PllRegs pllCostants(void)
96         {
97                 uint32_t best_err = CPU_FREQ;
98                 PllRegs res;
99
100                 for (uint32_t div = DIV_MIN; div <= DIV_MAX; div++)
101                 {
102                         for (uint32_t pres = 0; pres < 8; pres++)
103                         {
104                                 uint32_t mul = DIV_ROUND((CPU_FREQ * div) << pres, XTAL_FREQ) - 1;
105                                 if (mul <= MUL_MAX)
106                                 {
107                                         uint32_t pll = (XTAL_FREQ * (mul + 1)) / div;
108                                         if (pll >= PLL_OUT_MIN && pll <= PLL_OUT_MAX)
109                                         {
110                                                 uint32_t err = ABS((int32_t)((pll >> pres) - CPU_FREQ));
111                                                 if (err == 0)
112                                                 {
113                                                         res.div = div;
114                                                         res.mul = mul;
115                                                         res.pres = pres;
116                                                         return res;
117                                                 }
118                                                 if (err < best_err)
119                                                 {
120                                                         best_err = err;
121                                                         res.div = div;
122                                                         res.mul = mul;
123                                                         res.pres = pres;
124                                                 }
125                                         }
126                                 }
127                         }
128                 }
129                 return res;
130         }
131 #endif  /* !USE_FIXED_PLL*/
132
133 /*
134  * Override dummy hardware init functions supplied by the ASM startup
135  * routine.
136  */
137
138 void __init1(void);
139 void __init2(void);
140
141 /**
142  * Early hardware initialization routine1.
143  * This will be called by the ASM CRT routine just
144  * *before* clearing .bss and loading .data sections.
145  * Usually only basic tasks are performed here (i.e. setting the PLL).
146  * For more generic tasks, __init2() should be used.
147  *
148  * \note Please keep in mind that since .bss and .data are not yet set, care
149  *       must be taken. No static data can be used inside this funcition.
150  *       Also some libc functions or floating point operations could potentially
151  *       use initialized static data, be aware!
152  */
153 void __init1(void)
154 {
155         /*
156          * Compute number of master clock cycles in 1.5us.
157          * Needed by flash writing functions.
158          * The maximum FMCN value is 0xFF and 0 can be used only if
159          * master clock is less than 33kHz.
160          */
161         #define MCN  DIV_ROUNDUP(CPU_FREQ, 666667UL)
162         #define FMCN (CPU_FREQ <= 33333UL ? 0 : (MCN < 0xFF ? MCN : 0xFF))
163
164         #if CPU_FREQ < 30000000UL
165                 /* Use 1 cycles for flash access. */
166                 MC_FMR = FMCN << MC_FMCN_SHIFT | MC_FWS_1R2W;
167         #else
168                 /* Use 2 cycles for flash access. */
169                 MC_FMR = FMCN << MC_FMCN_SHIFT | MC_FWS_2R3W;
170         #endif
171
172         /* Disable all interrupts. Useful for debugging w/o target reset. */
173         AIC_EOICR = 0xFFFFFFFF;
174         AIC_IDCR =  0xFFFFFFFF;
175
176         #if CONFIG_WATCHDOG == 0
177                 /* The watchdog is enabled after processor reset. Disable it. */
178                 WDT_MR = BV(WDT_WDDIS);
179         #endif
180
181         /*
182          * Enable the main oscillator. Set startup time of 6 * 8 slow
183          * clock cycles and wait until oscillator is stabilized.
184          */
185         CKGR_MOR = (6 << 8) | BV(CKGR_MOSCEN);
186         while (!(PMC_SR & BV(PMC_MOSCS))) ;
187
188         /* Switch to Slow oscillator clock. */
189         PMC_MCKR &= ~PMC_CSS_MASK;
190         while (!(PMC_SR & BV(PMC_MCKRDY))) ;
191
192         /* Switch to prescaler div 1 factor. */
193         PMC_MCKR &= ~PMC_PRES_MASK;
194         while (!(PMC_SR & BV(PMC_MCKRDY))) ;
195
196         uint32_t div, pres, mul;
197         #if USE_FIXED_PLL
198                 div = PLL_DIV_VAL;
199                 mul = PLL_MUL_VAL;
200                 pres = AT91MCK_PRES;
201         #else
202                 PllRegs pll = pllCostants();
203                 div = pll.div;
204                 mul = pll.mul;
205                 pres = pll.pres << PMC_PRES_SHIFT;
206         #endif
207
208         /*
209          * Set PLL:
210          * PLLfreq = crystal / divider * (multiplier + 1)
211          * Wait 28 clock cycles until PLL is locked.
212          */
213         CKGR_PLLR = ((mul << CKGR_MUL_SHIFT)
214                 | (28 << CKGR_PLLCOUNT_SHIFT) | div);
215         while (!(PMC_SR & BV(PMC_LOCK))) ;
216
217         /* Set master clock prescaler.  */
218         PMC_MCKR = pres;
219         while (!(PMC_SR & BV(PMC_MCKRDY))) ;
220
221         /*
222          * Switch to PLL clock. Trying to set this together with the
223          * prescaler fails (see datasheets).
224          */
225         PMC_MCKR |= PMC_CSS_PLL_CLK;
226         while (!(PMC_SR & BV(PMC_MCKRDY))) ;
227 }
228
229 /**
230  * Early hardware initialization routine2.
231  * This will be called by the ASM CRT routine just
232  * *after* clearing .bss and loading .data sections and before calling main().
233  */
234 void __init2(void)
235 {
236         /* Enable external reset key. */
237         RSTC_MR = (RSTC_KEY | BV(RSTC_URSTEN));
238
239         /* Enable clock for PIO(s) */
240         PMC_PCER = BV(PIOA_ID);
241         #if CPU_ARM_SAM7X
242                 PMC_PCER |= BV(PIOB_ID);
243         #endif
244 }