1 /*
2  * Copyright (c) 2018, Christian Taedcke
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 /**
8  * @file
9  * @brief Common SoC initialization for the Silabs products
10  */
11 
12 #include <zephyr/init.h>
13 #include <zephyr/kernel.h>
14 #include <zephyr/logging/log.h>
15 
16 #include <em_chip.h>
17 #include <em_cmu.h>
18 #include <em_emu.h>
19 #include <soc.h>
20 #include <cmsis_core.h>
21 
22 LOG_MODULE_REGISTER(soc, CONFIG_SOC_LOG_LEVEL);
23 
24 #ifdef CONFIG_CMU_HFCLK_HFXO
25 /**
26  * @brief Initialization parameters for the external high frequency oscillator
27  */
28 static CMU_HFXOInit_TypeDef hfxoInit = CMU_HFXOINIT_DEFAULT;
29 #endif
30 
31 #ifdef CONFIG_CMU_NEED_LFXO
32 /**
33  * @brief Initialization parameters for the external low frequency oscillator
34  */
35 static CMU_LFXOInit_TypeDef lfxoInit = CMU_LFXOINIT_DEFAULT;
36 
init_lfxo(void)37 static void init_lfxo(void)
38 {
39 	/*
40 	 * Configuring LFXO disables it, so we can do that only if it's not
41 	 * used as a SYSCLK/HFCLK source.
42 	 */
43 #if defined(_SILICON_LABS_32B_SERIES_2)
44 	if (CMU_ClockSelectGet(cmuClock_SYSCLK) != cmuSelect_LFXO) {
45 		/*
46 		 * Check if device has LFXO configuration info in DEVINFO
47 		 * See AN0016.2
48 		 */
49 		if ((DEVINFO->MODULEINFO & DEVINFO_MODULEINFO_LFXOCALVAL) ==
50 		    DEVINFO_MODULEINFO_LFXOCALVAL_VALID) {
51 			lfxoInit.capTune =
52 				(DEVINFO->MODXOCAL & _DEVINFO_MODXOCAL_LFXOCAPTUNE_MASK) >>
53 				_DEVINFO_MODXOCAL_LFXOCAPTUNE_SHIFT;
54 		}
55 		CMU_LFXOInit(&lfxoInit);
56 	}
57 #else
58 	if (CMU_ClockSelectGet(cmuClock_HF) != cmuSelect_LFXO) {
59 		CMU_LFXOInit(&lfxoInit);
60 		CMU_OscillatorEnable(cmuOsc_LFXO, true, true);
61 	}
62 #endif /* _SILICON_LABS_32B_SERIES_2 */
63 	SystemLFXOClockSet(CONFIG_CMU_LFXO_FREQ);
64 }
65 
66 #endif /* CONFIG_CMU_NEED_LFXO */
67 
68 /**
69  * @brief Initialize the system clock
70  */
clock_init(void)71 static ALWAYS_INLINE void clock_init(void)
72 {
73 #ifdef CONFIG_CMU_HFCLK_HFXO
74 #if defined(_SILICON_LABS_32B_SERIES_2)
75 	if (CMU_ClockSelectGet(cmuClock_SYSCLK) != cmuSelect_HFXO) {
76 		/*
77 		 * Check if device has HFXO configuration info in DEVINFO
78 		 * See AN0016.2
79 		 */
80 		if ((DEVINFO->MODULEINFO & DEVINFO_MODULEINFO_HFXOCALVAL) ==
81 		    DEVINFO_MODULEINFO_HFXOCALVAL_VALID) {
82 			hfxoInit.ctuneXoAna =
83 				(DEVINFO->MODXOCAL & _DEVINFO_MODXOCAL_HFXOCTUNEXOANA_MASK) >>
84 				_DEVINFO_MODXOCAL_HFXOCTUNEXOANA_SHIFT;
85 			hfxoInit.ctuneXiAna =
86 				(DEVINFO->MODXOCAL & _DEVINFO_MODXOCAL_HFXOCTUNEXIANA_MASK) >>
87 				_DEVINFO_MODXOCAL_HFXOCTUNEXIANA_SHIFT;
88 		}
89 
90 		CMU_HFXOInit(&hfxoInit);
91 		CMU_ClockSelectSet(cmuClock_SYSCLK, cmuSelect_HFXO);
92 	}
93 
94 	SystemHFXOClockSet(CONFIG_CMU_HFXO_FREQ);
95 #else
96 	if (CMU_ClockSelectGet(cmuClock_HF) != cmuSelect_HFXO) {
97 		CMU_HFXOInit(&hfxoInit);
98 		CMU_OscillatorEnable(cmuOsc_HFXO, true, true);
99 		CMU_ClockSelectSet(cmuClock_HF, cmuSelect_HFXO);
100 	}
101 	SystemHFXOClockSet(CONFIG_CMU_HFXO_FREQ);
102 	CMU_OscillatorEnable(cmuOsc_HFRCO, false, false);
103 #endif /* _SILICON_LABS_32B_SERIES_2 */
104 #elif (defined CONFIG_CMU_HFCLK_LFXO)
105 	/* LFXO should've been already brought up by init_lfxo() */
106 #if defined(_SILICON_LABS_32B_SERIES_2)
107 	CMU_ClockSelectSet(cmuClock_SYSCLK, cmuSelect_LFXO);
108 #else
109 	CMU_ClockSelectSet(cmuClock_HF, cmuSelect_LFXO);
110 	CMU_OscillatorEnable(cmuOsc_HFRCO, false, false);
111 #endif /* _SILICON_LABS_32B_SERIES_2 */
112 #elif (defined CONFIG_CMU_HFCLK_HFRCO)
113 	/*
114 	 * This is the default clock, the controller starts with
115 	 */
116 
117 #ifdef CONFIG_SOC_GECKO_HAS_HFRCO_FREQRANGE
118 	if (CONFIG_CMU_HFRCO_FREQ) {
119 		/* Setting system HFRCO frequency */
120 		CMU_HFRCOBandSet(CONFIG_CMU_HFRCO_FREQ);
121 
122 		/* Using HFRCO as high frequency clock, HFCLK */
123 		CMU_ClockSelectSet(cmuClock_HF, cmuSelect_HFRCO);
124 	}
125 #endif
126 #else
127 #error "Unsupported clock source for HFCLK selected"
128 #endif
129 
130 #if defined(_SILICON_LABS_32B_SERIES_2)
131 	/* Enable the High Frequency Peripheral Clock */
132 	CMU_ClockEnable(cmuClock_PCLK, true);
133 #else
134 	/* Enable the High Frequency Peripheral Clock */
135 	CMU_ClockEnable(cmuClock_HFPER, true);
136 #endif /* _SILICON_LABS_32B_SERIES_2 */
137 
138 #if defined(CONFIG_GPIO_GECKO) || defined(CONFIG_LOG_BACKEND_SWO)
139 	CMU_ClockEnable(cmuClock_GPIO, true);
140 #endif
141 }
142 
143 #ifdef CONFIG_SOC_GECKO_EMU_DCDC
dcdc_init(void)144 static ALWAYS_INLINE void dcdc_init(void)
145 {
146 #if defined(CONFIG_SOC_GECKO_EMU_DCDC_MODE_UNCONFIGURED)
147 	/* Nothing to do, leave DC/DC converter in unconfigured, safe state. */
148 #elif defined(CONFIG_SOC_GECKO_EMU_DCDC_MODE_ON) || defined(CONFIG_SOC_GECKO_EMU_DCDC_MODE_BYPASS)
149 	EMU_DCDCInit_TypeDef init_cfg = EMU_DCDCINIT_DEFAULT;
150 #if defined(CONFIG_SOC_GECKO_EMU_DCDC_MODE_BYPASS)
151 	init_cfg.dcdcMode = emuDcdcMode_Bypass;
152 #endif
153 	EMU_DCDCInit(&init_cfg);
154 #elif defined(CONFIG_SOC_GECKO_EMU_DCDC_MODE_OFF)
155 	EMU_DCDCPowerOff();
156 #else
157 #error "Unsupported power configuration mode of the on chip DC/DC converter."
158 #endif
159 }
160 #endif
161 
162 #ifdef CONFIG_LOG_BACKEND_SWO
swo_init(void)163 static void swo_init(void)
164 {
165 	struct soc_gpio_pin pin_swo = PIN_SWO;
166 
167 #if defined(_SILICON_LABS_32B_SERIES_2)
168 	GPIO->TRACEROUTEPEN = GPIO_TRACEROUTEPEN_SWVPEN;
169 #else
170 	/* Select HFCLK as the debug trace clock */
171 	CMU->DBGCLKSEL = CMU_DBGCLKSEL_DBG_HFCLK;
172 
173 #if defined(_GPIO_ROUTEPEN_MASK)
174 	/* Enable Serial wire output pin */
175 	GPIO->ROUTEPEN |= GPIO_ROUTEPEN_SWVPEN;
176 	/* Set SWO location */
177 	GPIO->ROUTELOC0 = SWO_LOCATION << _GPIO_ROUTELOC0_SWVLOC_SHIFT;
178 #else
179 	GPIO->ROUTE = GPIO_ROUTE_SWOPEN | (SWO_LOCATION << 8);
180 #endif
181 #endif /* _SILICON_LABS_32B_SERIES_2 */
182 
183 	GPIO_PinModeSet(pin_swo.port, pin_swo.pin, pin_swo.mode, pin_swo.out);
184 }
185 #endif /* CONFIG_LOG_BACKEND_SWO */
186 
187 /**
188  * @brief Perform basic hardware initialization
189  *
190  * Initialize the interrupt controller device drivers.
191  * Also initialize the timer device driver, if required.
192  *
193  * @return 0
194  */
soc_early_init_hook(void)195 void soc_early_init_hook(void)
196 {
197 	/* handle chip errata */
198 	CHIP_Init();
199 
200 #ifdef CONFIG_CMU_NEED_LFXO
201 	init_lfxo();
202 #endif
203 
204 #ifdef CONFIG_SOC_GECKO_EMU_DCDC
205 	dcdc_init();
206 #endif
207 
208 	/* Initialize system clock according to CONFIG_CMU settings */
209 	clock_init();
210 
211 #ifdef CONFIG_LOG_BACKEND_SWO
212 	/* Configure SWO debug output */
213 	swo_init();
214 #endif
215 }
216