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