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