1 /*
2 * Copyright (c) 2020-2023 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 <zephyr/device.h>
15 #include <zephyr/init.h>
16 #include <soc.h>
17 #include <zephyr/sys/util.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 Sets the WatchDog Timer Control register to the \a ctrl value thanks
26 * to the WatchDog Timer key.
27 *
28 * @param ctrl Value to set the WatchDog Timer Control register to.
29 */
wdt_set_ctrl(uint32_t ctrl)30 static ALWAYS_INLINE void wdt_set_ctrl(uint32_t ctrl)
31 {
32 volatile uint32_t dly;
33
34 /** Calculate delay for internal synchronization
35 * see 45.1.3 WDT errata
36 */
37 dly = DIV_ROUND_UP(48000000 * 2, 115000);
38 dly >>= 3; /* ~8 cycles for one while loop */
39 while (dly--) {
40 ;
41 }
42 WDT->CTRL = ctrl | WDT_CTRL_KEY(WDT_FIRST_KEY);
43 WDT->CTRL = ctrl | WDT_CTRL_KEY(WDT_SECOND_KEY);
44 }
45
46 #define XTAL_FREQ 12000000
47 #define NR_PLLS 1
48 #define PLL_MAX_STARTUP_CYCLES (SCIF_PLL_PLLCOUNT_Msk >> SCIF_PLL_PLLCOUNT_Pos)
49
50 /**
51 * Fcpu = 48MHz
52 * Fpll = (Fclk * PLL_mul) / PLL_div
53 */
54 #define PLL0_MUL (192000000 / XTAL_FREQ)
55 #define PLL0_DIV 4
56
pll_is_locked(uint32_t pll_id)57 static inline bool pll_is_locked(uint32_t pll_id)
58 {
59 return !!(SCIF->PCLKSR & (1U << (6 + pll_id)));
60 }
61
osc_is_ready(uint8_t id)62 static inline bool osc_is_ready(uint8_t id)
63 {
64 switch (id) {
65 case OSC_ID_OSC0:
66 return !!(SCIF->PCLKSR & SCIF_PCLKSR_OSC0RDY);
67 case OSC_ID_OSC32:
68 return !!(BSCIF->PCLKSR & BSCIF_PCLKSR_OSC32RDY);
69 case OSC_ID_RC32K:
70 return !!(BSCIF->RC32KCR & (BSCIF_RC32KCR_EN));
71 case OSC_ID_RC80M:
72 return !!(SCIF->RC80MCR & (SCIF_RC80MCR_EN));
73 case OSC_ID_RCFAST:
74 return !!(SCIF->RCFASTCFG & (SCIF_RCFASTCFG_EN));
75 case OSC_ID_RC1M:
76 return !!(BSCIF->RC1MCR & (BSCIF_RC1MCR_CLKOE));
77 case OSC_ID_RCSYS:
78 /* RCSYS is always ready */
79 return true;
80 default:
81 /* unhandled_case(id); */
82 return false;
83 }
84 }
85
86 /**
87 * The PLL options #PLL_OPT_VCO_RANGE_HIGH and #PLL_OPT_OUTPUT_DIV will
88 * be set automatically based on the calculated target frequency.
89 */
pll_config_init(uint32_t divide,uint32_t mul)90 static inline uint32_t pll_config_init(uint32_t divide, uint32_t mul)
91 {
92 #define SCIF0_PLL_VCO_RANGE1_MAX_FREQ 240000000
93 #define SCIF_PLL0_VCO_RANGE1_MIN_FREQ 160000000
94 #define SCIF_PLL0_VCO_RANGE0_MAX_FREQ 180000000
95 #define SCIF_PLL0_VCO_RANGE0_MIN_FREQ 80000000
96 /* VCO frequency range is 160-240 MHz (80-180 MHz if unset) */
97 #define PLL_OPT_VCO_RANGE_HIGH 0
98 /* Divide output frequency by two */
99 #define PLL_OPT_OUTPUT_DIV 1
100 /* The threshold above which to set the #PLL_OPT_VCO_RANGE_HIGH option */
101 #define PLL_VCO_LOW_THRESHOLD ((SCIF_PLL0_VCO_RANGE1_MIN_FREQ \
102 + SCIF_PLL0_VCO_RANGE0_MAX_FREQ) / 2)
103 #define PLL_MIN_HZ 40000000
104 #define PLL_MAX_HZ 240000000
105 #define MUL_MIN 2
106 #define MUL_MAX 16
107 #define DIV_MIN 0
108 #define DIV_MAX 15
109
110 uint32_t pll_value;
111 uint32_t vco_hz;
112
113 /* Calculate internal VCO frequency */
114 vco_hz = XTAL_FREQ * mul;
115 vco_hz /= divide;
116
117 pll_value = 0;
118
119 /* Bring the internal VCO frequency up to the minimum value */
120 if ((vco_hz < PLL_MIN_HZ * 2) && (mul <= 8)) {
121 mul *= 2;
122 vco_hz *= 2;
123 pll_value |= (1U << (SCIF_PLL_PLLOPT_Pos +
124 PLL_OPT_OUTPUT_DIV));
125 }
126
127 /* Set VCO frequency range according to calculated value */
128 if (vco_hz >= PLL_VCO_LOW_THRESHOLD) {
129 pll_value |= 1U << (SCIF_PLL_PLLOPT_Pos +
130 PLL_OPT_VCO_RANGE_HIGH);
131 }
132
133 pll_value |= ((mul - 1) << SCIF_PLL_PLLMUL_Pos) |
134 (divide << SCIF_PLL_PLLDIV_Pos) |
135 (PLL_MAX_STARTUP_CYCLES << SCIF_PLL_PLLCOUNT_Pos);
136
137 return pll_value;
138 }
139
flashcalw_set_wait_state(uint32_t wait_state)140 static inline void flashcalw_set_wait_state(uint32_t wait_state)
141 {
142 HFLASHC->FCR = (HFLASHC->FCR & ~FLASHCALW_FCR_FWS) |
143 (wait_state ?
144 FLASHCALW_FCR_FWS_1 :
145 FLASHCALW_FCR_FWS_0);
146 }
147
flashcalw_is_ready(void)148 static inline bool flashcalw_is_ready(void)
149 {
150 return ((HFLASHC->FSR & FLASHCALW_FSR_FRDY) != 0);
151 }
152
flashcalw_issue_command(uint32_t command,int page_number)153 static inline void flashcalw_issue_command(uint32_t command, int page_number)
154 {
155 uint32_t time;
156
157 flashcalw_is_ready();
158 time = HFLASHC->FCMD;
159
160 /* Clear the command bitfield. */
161 time &= ~FLASHCALW_FCMD_CMD_Msk;
162 if (page_number >= 0) {
163 time = (FLASHCALW_FCMD_KEY_KEY |
164 FLASHCALW_FCMD_PAGEN(page_number) | command);
165 } else {
166 time |= (FLASHCALW_FCMD_KEY_KEY | command);
167 }
168
169 HFLASHC->FCMD = time;
170 flashcalw_is_ready();
171 }
172
173 /**
174 * @brief Setup various clock on SoC at boot time.
175 *
176 * Setup the SoC clocks according to section 28.12 in datasheet.
177 *
178 * Setup Slow, Main, PLLA, Processor and Master clocks during the device boot.
179 * It is assumed that the relevant registers are at their reset value.
180 */
clock_init(void)181 static ALWAYS_INLINE void clock_init(void)
182 {
183 /* Disable PicoCache and Enable HRAMC1 as extended RAM */
184 soc_pmc_peripheral_enable(
185 PM_CLOCK_MASK(PM_CLK_GRP_HSB, SYSCLK_HRAMC1_DATA));
186 soc_pmc_peripheral_enable(
187 PM_CLOCK_MASK(PM_CLK_GRP_PBB, SYSCLK_HRAMC1_REGS));
188
189 HCACHE->CTRL = HCACHE_CTRL_CEN_NO;
190
191 while (HCACHE->SR & HCACHE_SR_CSTS_EN) {
192 ;
193 }
194
195 /* Enable PLL */
196 if (!pll_is_locked(0)) {
197 /* This assumes external 12MHz Crystal */
198 SCIF->UNLOCK = SCIF_UNLOCK_KEY(0xAAu) |
199 SCIF_UNLOCK_ADDR((uint32_t)&SCIF->OSCCTRL0 -
200 (uint32_t)SCIF);
201 SCIF->OSCCTRL0 = SCIF_OSCCTRL0_STARTUP(2) |
202 SCIF_OSCCTRL0_GAIN(3) |
203 SCIF_OSCCTRL0_MODE |
204 SCIF_OSCCTRL0_OSCEN;
205
206 while (!osc_is_ready(OSC_ID_OSC0)) {
207 ;
208 }
209 uint32_t pll_config = pll_config_init(PLL0_DIV,
210 PLL0_MUL);
211
212 SCIF->UNLOCK = SCIF_UNLOCK_KEY(0xAAu) |
213 SCIF_UNLOCK_ADDR((uint32_t)&SCIF->PLL[0] -
214 (uint32_t)SCIF);
215 SCIF->PLL[0] = pll_config | SCIF_PLL_PLLEN;
216
217 while (!pll_is_locked(0)) {
218 ;
219 }
220 }
221
222 /** Set a flash wait state depending on the new cpu frequency.
223 */
224 flashcalw_set_wait_state(1);
225 flashcalw_issue_command(FLASHCALW_FCMD_CMD_HSEN, -1);
226
227 /** Set Clock CPU/BUS dividers
228 */
229 PM->UNLOCK = PM_UNLOCK_KEY(0xAAu) |
230 PM_UNLOCK_ADDR((uint32_t)&PM->CPUSEL - (uint32_t)PM);
231 PM->CPUSEL = PM_CPUSEL_CPUSEL(0);
232
233 PM->UNLOCK = PM_UNLOCK_KEY(0xAAu) |
234 PM_UNLOCK_ADDR((uint32_t)&PM->PBASEL - (uint32_t)PM);
235 PM->PBASEL = PM_PBASEL_PBSEL(0);
236
237 PM->UNLOCK = PM_UNLOCK_KEY(0xAAu) |
238 PM_UNLOCK_ADDR((uint32_t)&PM->PBBSEL - (uint32_t)PM);
239 PM->PBBSEL = PM_PBBSEL_PBSEL(0);
240
241 PM->UNLOCK = PM_UNLOCK_KEY(0xAAu) |
242 PM_UNLOCK_ADDR((uint32_t)&PM->PBCSEL - (uint32_t)PM);
243 PM->PBCSEL = PM_PBCSEL_PBSEL(0);
244
245 PM->UNLOCK = PM_UNLOCK_KEY(0xAAu) |
246 PM_UNLOCK_ADDR((uint32_t)&PM->PBDSEL - (uint32_t)PM);
247 PM->PBDSEL = PM_PBDSEL_PBSEL(0);
248
249 /** Set PLL0 as source clock
250 */
251 PM->UNLOCK = PM_UNLOCK_KEY(0xAAu) |
252 PM_UNLOCK_ADDR((uint32_t)&PM->MCCTRL - (uint32_t)PM);
253 PM->MCCTRL = OSC_SRC_PLL0;
254 }
255
z_arm_platform_init(void)256 void z_arm_platform_init(void)
257 {
258 #if defined(CONFIG_WDT_DISABLE_AT_BOOT)
259 wdt_set_ctrl(WDT->CTRL & ~WDT_CTRL_EN);
260 while (WDT->CTRL & WDT_CTRL_EN) {
261 ;
262 }
263 #endif
264
265 /* Setup system clocks. */
266 clock_init();
267 }
268