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