1 /*
2 * Copyright (c) 2020 Gerson Fernando Budke <nandojve@gmail.com>
3 * SPDX-License-Identifier: Apache-2.0
4 */
5
6 /**
7 * @file
8 * @brief Atmel SAM4L MCU series initialization code
9 *
10 * This module provides routines to initialize and support board-level hardware
11 * for the Atmel SAM4L series processor.
12 */
13
14 #include <device.h>
15 #include <init.h>
16 #include <soc.h>
17 #include <arch/cpu.h>
18
19 /** Watchdog control register first write keys */
20 #define WDT_FIRST_KEY 0x55ul
21 /** Watchdog control register second write keys */
22 #define WDT_SECOND_KEY 0xAAul
23
24 /**
25 * @brief Calculate \f$ \left\lceil \frac{a}{b} \right\rceil \f$ using
26 * integer arithmetic.
27 *
28 * @param a An integer
29 * @param b Another integer
30 *
31 * @return (\a a / \a b) rounded up to the nearest integer.
32 */
33 #define div_ceil(a, b) (((a) + (b) - 1) / (b))
34
35 /**
36 * @brief Sets the WatchDog Timer Control register to the \a ctrl value thanks
37 * to the WatchDog Timer key.
38 *
39 * @param ctrl Value to set the WatchDog Timer Control register to.
40 */
wdt_set_ctrl(uint32_t ctrl)41 static ALWAYS_INLINE void wdt_set_ctrl(uint32_t ctrl)
42 {
43 volatile uint32_t dly;
44
45 /** Calculate delay for internal synchronization
46 * see 45.1.3 WDT errata
47 */
48 dly = div_ceil(48000000 * 2, 115000);
49 dly >>= 3; /* ~8 cycles for one while loop */
50 while (dly--) {
51 ;
52 }
53 WDT->CTRL = ctrl | WDT_CTRL_KEY(WDT_FIRST_KEY);
54 WDT->CTRL = ctrl | WDT_CTRL_KEY(WDT_SECOND_KEY);
55 }
56
57 #define XTAL_FREQ 12000000
58 #define NR_PLLS 1
59 #define PLL_MAX_STARTUP_CYCLES (SCIF_PLL_PLLCOUNT_Msk >> SCIF_PLL_PLLCOUNT_Pos)
60
61 /**
62 * Fcpu = 48MHz
63 * Fpll = (Fclk * PLL_mul) / PLL_div
64 */
65 #define PLL0_MUL (192000000 / XTAL_FREQ)
66 #define PLL0_DIV 4
67
pll_is_locked(uint32_t pll_id)68 static inline bool pll_is_locked(uint32_t pll_id)
69 {
70 return !!(SCIF->PCLKSR & (1U << (6 + pll_id)));
71 }
72
osc_is_ready(uint8_t id)73 static inline bool osc_is_ready(uint8_t id)
74 {
75 switch (id) {
76 case OSC_ID_OSC0:
77 return !!(SCIF->PCLKSR & SCIF_PCLKSR_OSC0RDY);
78 case OSC_ID_OSC32:
79 return !!(BSCIF->PCLKSR & BSCIF_PCLKSR_OSC32RDY);
80 case OSC_ID_RC32K:
81 return !!(BSCIF->RC32KCR & (BSCIF_RC32KCR_EN));
82 case OSC_ID_RC80M:
83 return !!(SCIF->RC80MCR & (SCIF_RC80MCR_EN));
84 case OSC_ID_RCFAST:
85 return !!(SCIF->RCFASTCFG & (SCIF_RCFASTCFG_EN));
86 case OSC_ID_RC1M:
87 return !!(BSCIF->RC1MCR & (BSCIF_RC1MCR_CLKOE));
88 case OSC_ID_RCSYS:
89 /* RCSYS is always ready */
90 return true;
91 default:
92 /* unhandled_case(id); */
93 return false;
94 }
95 }
96
97 /**
98 * The PLL options #PLL_OPT_VCO_RANGE_HIGH and #PLL_OPT_OUTPUT_DIV will
99 * be set automatically based on the calculated target frequency.
100 */
pll_config_init(uint32_t divide,uint32_t mul)101 static inline uint32_t pll_config_init(uint32_t divide, uint32_t mul)
102 {
103 #define SCIF0_PLL_VCO_RANGE1_MAX_FREQ 240000000
104 #define SCIF_PLL0_VCO_RANGE1_MIN_FREQ 160000000
105 #define SCIF_PLL0_VCO_RANGE0_MAX_FREQ 180000000
106 #define SCIF_PLL0_VCO_RANGE0_MIN_FREQ 80000000
107 /* VCO frequency range is 160-240 MHz (80-180 MHz if unset) */
108 #define PLL_OPT_VCO_RANGE_HIGH 0
109 /* Divide output frequency by two */
110 #define PLL_OPT_OUTPUT_DIV 1
111 /* The threshold above which to set the #PLL_OPT_VCO_RANGE_HIGH option */
112 #define PLL_VCO_LOW_THRESHOLD ((SCIF_PLL0_VCO_RANGE1_MIN_FREQ \
113 + SCIF_PLL0_VCO_RANGE0_MAX_FREQ) / 2)
114 #define PLL_MIN_HZ 40000000
115 #define PLL_MAX_HZ 240000000
116 #define MUL_MIN 2
117 #define MUL_MAX 16
118 #define DIV_MIN 0
119 #define DIV_MAX 15
120
121 uint32_t pll_value;
122 uint32_t vco_hz;
123
124 /* Calculate internal VCO frequency */
125 vco_hz = XTAL_FREQ * mul;
126 vco_hz /= divide;
127
128 pll_value = 0;
129
130 /* Bring the internal VCO frequency up to the minimum value */
131 if ((vco_hz < PLL_MIN_HZ * 2) && (mul <= 8)) {
132 mul *= 2;
133 vco_hz *= 2;
134 pll_value |= (1U << (SCIF_PLL_PLLOPT_Pos +
135 PLL_OPT_OUTPUT_DIV));
136 }
137
138 /* Set VCO frequency range according to calculated value */
139 if (vco_hz >= PLL_VCO_LOW_THRESHOLD) {
140 pll_value |= 1U << (SCIF_PLL_PLLOPT_Pos +
141 PLL_OPT_VCO_RANGE_HIGH);
142 }
143
144 pll_value |= ((mul - 1) << SCIF_PLL_PLLMUL_Pos) |
145 (divide << SCIF_PLL_PLLDIV_Pos) |
146 (PLL_MAX_STARTUP_CYCLES << SCIF_PLL_PLLCOUNT_Pos);
147
148 return pll_value;
149 }
150
flashcalw_set_wait_state(uint32_t wait_state)151 static inline void flashcalw_set_wait_state(uint32_t wait_state)
152 {
153 HFLASHC->FCR = (HFLASHC->FCR & ~FLASHCALW_FCR_FWS) |
154 (wait_state ?
155 FLASHCALW_FCR_FWS_1 :
156 FLASHCALW_FCR_FWS_0);
157 }
158
flashcalw_is_ready(void)159 static inline bool flashcalw_is_ready(void)
160 {
161 return ((HFLASHC->FSR & FLASHCALW_FSR_FRDY) != 0);
162 }
163
flashcalw_issue_command(uint32_t command,int page_number)164 static inline void flashcalw_issue_command(uint32_t command, int page_number)
165 {
166 uint32_t time;
167
168 flashcalw_is_ready();
169 time = HFLASHC->FCMD;
170
171 /* Clear the command bitfield. */
172 time &= ~FLASHCALW_FCMD_CMD_Msk;
173 if (page_number >= 0) {
174 time = (FLASHCALW_FCMD_KEY_KEY |
175 FLASHCALW_FCMD_PAGEN(page_number) | command);
176 } else {
177 time |= (FLASHCALW_FCMD_KEY_KEY | command);
178 }
179
180 HFLASHC->FCMD = time;
181 flashcalw_is_ready();
182 }
183
184 /**
185 * @brief Setup various clock on SoC at boot time.
186 *
187 * Setup the SoC clocks according to section 28.12 in datasheet.
188 *
189 * Setup Slow, Main, PLLA, Processor and Master clocks during the device boot.
190 * It is assumed that the relevant registers are at their reset value.
191 */
clock_init(void)192 static ALWAYS_INLINE void clock_init(void)
193 {
194 /* Disable PicoCache and Enable HRAMC1 as extended RAM */
195 soc_pmc_peripheral_enable(
196 PM_CLOCK_MASK(PM_CLK_GRP_HSB, SYSCLK_HRAMC1_DATA));
197 soc_pmc_peripheral_enable(
198 PM_CLOCK_MASK(PM_CLK_GRP_PBB, SYSCLK_HRAMC1_REGS));
199
200 HCACHE->CTRL = HCACHE_CTRL_CEN_NO;
201
202 while (HCACHE->SR & HCACHE_SR_CSTS_EN) {
203 ;
204 }
205
206 /* Enable PLL */
207 if (!pll_is_locked(0)) {
208 /* This assumes external 12MHz Crystal */
209 SCIF->UNLOCK = SCIF_UNLOCK_KEY(0xAAu) |
210 SCIF_UNLOCK_ADDR((uint32_t)&SCIF->OSCCTRL0 -
211 (uint32_t)SCIF);
212 SCIF->OSCCTRL0 = SCIF_OSCCTRL0_STARTUP(2) |
213 SCIF_OSCCTRL0_GAIN(3) |
214 SCIF_OSCCTRL0_MODE |
215 SCIF_OSCCTRL0_OSCEN;
216
217 while (!osc_is_ready(OSC_ID_OSC0)) {
218 ;
219 }
220 uint32_t pll_config = pll_config_init(PLL0_DIV,
221 PLL0_MUL);
222
223 SCIF->UNLOCK = SCIF_UNLOCK_KEY(0xAAu) |
224 SCIF_UNLOCK_ADDR((uint32_t)&SCIF->PLL[0] -
225 (uint32_t)SCIF);
226 SCIF->PLL[0] = pll_config | SCIF_PLL_PLLEN;
227
228 while (!pll_is_locked(0)) {
229 ;
230 }
231 }
232
233 /** Set a flash wait state depending on the new cpu frequency.
234 */
235 flashcalw_set_wait_state(1);
236 flashcalw_issue_command(FLASHCALW_FCMD_CMD_HSEN, -1);
237
238 /** Set Clock CPU/BUS dividers
239 */
240 PM->UNLOCK = PM_UNLOCK_KEY(0xAAu) |
241 PM_UNLOCK_ADDR((uint32_t)&PM->CPUSEL - (uint32_t)PM);
242 PM->CPUSEL = PM_CPUSEL_CPUSEL(0);
243
244 PM->UNLOCK = PM_UNLOCK_KEY(0xAAu) |
245 PM_UNLOCK_ADDR((uint32_t)&PM->PBASEL - (uint32_t)PM);
246 PM->PBASEL = PM_PBASEL_PBSEL(0);
247
248 PM->UNLOCK = PM_UNLOCK_KEY(0xAAu) |
249 PM_UNLOCK_ADDR((uint32_t)&PM->PBBSEL - (uint32_t)PM);
250 PM->PBBSEL = PM_PBBSEL_PBSEL(0);
251
252 PM->UNLOCK = PM_UNLOCK_KEY(0xAAu) |
253 PM_UNLOCK_ADDR((uint32_t)&PM->PBCSEL - (uint32_t)PM);
254 PM->PBCSEL = PM_PBCSEL_PBSEL(0);
255
256 PM->UNLOCK = PM_UNLOCK_KEY(0xAAu) |
257 PM_UNLOCK_ADDR((uint32_t)&PM->PBDSEL - (uint32_t)PM);
258 PM->PBDSEL = PM_PBDSEL_PBSEL(0);
259
260 /** Set PLL0 as source clock
261 */
262 PM->UNLOCK = PM_UNLOCK_KEY(0xAAu) |
263 PM_UNLOCK_ADDR((uint32_t)&PM->MCCTRL - (uint32_t)PM);
264 PM->MCCTRL = OSC_SRC_PLL0;
265 }
266
267 /**
268 * @brief Perform basic hardware initialization at boot.
269 *
270 * This needs to be run from the very beginning.
271 * So the init priority has to be 0 (zero).
272 *
273 * @return 0
274 */
atmel_sam4l_init(const struct device * arg)275 static int atmel_sam4l_init(const struct device *arg)
276 {
277 uint32_t key;
278
279 ARG_UNUSED(arg);
280
281 key = irq_lock();
282
283 #if defined(CONFIG_WDT_DISABLE_AT_BOOT)
284 wdt_set_ctrl(WDT->CTRL & ~WDT_CTRL_EN);
285 while (WDT->CTRL & WDT_CTRL_EN) {
286 ;
287 }
288 #endif
289
290 /* Setup system clocks. */
291 clock_init();
292
293 /*
294 * Install default handler that simply resets the CPU
295 * if configured in the kernel, NOP otherwise.
296 */
297 NMI_INIT();
298
299 irq_unlock(key);
300
301 return 0;
302 }
303
304 SYS_INIT(atmel_sam4l_init, PRE_KERNEL_1, 0);
305