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