1 /*
2  * Copyright (c) 2024 Nordic Semiconductor ASA
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 /**
8  * @file
9  * @brief System/hardware module for Nordic Semiconductor nRF54L family processor
10  *
11  * This module provides routines to initialize and support board-level hardware
12  * for the Nordic Semiconductor nRF54L family processor.
13  */
14 
15 /* Include autoconf for cases when this file is used in special build (e.g. TFM) */
16 #include <zephyr/autoconf.h>
17 
18 #include <zephyr/devicetree.h>
19 #include <zephyr/kernel.h>
20 #include <zephyr/devicetree.h>
21 #include <zephyr/init.h>
22 #include <zephyr/logging/log.h>
23 #include <zephyr/cache.h>
24 #include <soc/nrfx_coredep.h>
25 #include <system_nrf54l.h>
26 #include <soc.h>
27 LOG_MODULE_REGISTER(soc, CONFIG_SOC_LOG_LEVEL);
28 
29 #if (defined(NRF_APPLICATION) && !defined(CONFIG_TRUSTED_EXECUTION_NONSECURE)) || \
30 	!defined(__ZEPHYR__)
31 
32 #include <nrf_erratas.h>
33 #include <hal/nrf_glitchdet.h>
34 #include <hal/nrf_oscillators.h>
35 #include <hal/nrf_power.h>
36 #include <hal/nrf_regulators.h>
37 #include <zephyr/dt-bindings/regulator/nrf5x.h>
38 
39 #define LFXO_NODE DT_NODELABEL(lfxo)
40 #define HFXO_NODE DT_NODELABEL(hfxo)
41 
power_and_clock_configuration(void)42 static inline void power_and_clock_configuration(void)
43 {
44 /* NRF_REGULATORS and NRF_OSCILLATORS are configured to be secure
45  * as NRF_REGULATORS.POFCON is needed by the secure image to
46  * prevent glitches when the power supply is attacked.
47  *
48  * NRF_OSCILLATORS is also configured as secure because of a HW limitation
49  * that requires them to be configured with the same security property.
50  */
51 #if DT_ENUM_HAS_VALUE(LFXO_NODE, load_capacitors, internal)
52 	uint32_t xosc32ktrim = NRF_FICR->XOSC32KTRIM;
53 
54 	uint32_t offset_k =
55 		(xosc32ktrim & FICR_XOSC32KTRIM_OFFSET_Msk) >> FICR_XOSC32KTRIM_OFFSET_Pos;
56 
57 	uint32_t slope_field_k =
58 		(xosc32ktrim & FICR_XOSC32KTRIM_SLOPE_Msk) >> FICR_XOSC32KTRIM_SLOPE_Pos;
59 	uint32_t slope_mask_k = FICR_XOSC32KTRIM_SLOPE_Msk >> FICR_XOSC32KTRIM_SLOPE_Pos;
60 	uint32_t slope_sign_k = (slope_mask_k - (slope_mask_k >> 1));
61 	int32_t slope_k = (int32_t)(slope_field_k ^ slope_sign_k) - (int32_t)slope_sign_k;
62 
63 	/* As specified in the nRF54L15 PS:
64 	 * CAPVALUE = round( (2*CAPACITANCE - 12) * (FICR->XOSC32KTRIM.SLOPE + 0.765625 * 2^9)/(2^9)
65 	 *            + FICR->XOSC32KTRIM.OFFSET/(2^6) );
66 	 * where CAPACITANCE is the desired capacitor value in pF, holding any
67 	 * value between 4 pF and 18 pF in 0.5 pF steps.
68 	 */
69 
70 	/* Encoding of desired capacitance (single ended) to value required for INTCAP core
71 	 * calculation: (CAP_VAL - 4 pF)* 0.5
72 	 * That translate to ((CAP_VAL_FEMTO_F - 4000fF) * 2UL) / 1000UL
73 	 *
74 	 * NOTE: The desired capacitance value is used in encoded from in INTCAP calculation formula
75 	 *       That is different than in case of HFXO.
76 	 */
77 	uint32_t cap_val_encoded = (((DT_PROP(LFXO_NODE, load_capacitance_femtofarad) - 4000UL)
78 				     * 2UL) / 1000UL);
79 
80 	/* Calculation of INTCAP code before rounding. Min that calculations here are done on
81 	 * values multiplied by 2^9, e.g. 0.765625 * 2^9 = 392.
82 	 * offset_k should be divided by 2^6, but to add it to value shifted by 2^9 we have to
83 	 * multiply it be 2^3.
84 	 */
85 	uint32_t mid_val = (2UL * cap_val_encoded - 12UL) * (uint32_t)(slope_k + 392UL)
86 			   + (offset_k << 3UL);
87 
88 	/* Get integer part of the INTCAP code */
89 	uint32_t lfxo_intcap = mid_val >> 9UL;
90 
91 	/* Round based on fractional part */
92 	if ((mid_val & BIT_MASK(9)) > (BIT_MASK(9) / 2)) {
93 		lfxo_intcap++;
94 	}
95 
96 	nrf_oscillators_lfxo_cap_set(NRF_OSCILLATORS, lfxo_intcap);
97 #elif DT_ENUM_HAS_VALUE(LFXO_NODE, load_capacitors, external)
98 	nrf_oscillators_lfxo_cap_set(NRF_OSCILLATORS, (nrf_oscillators_lfxo_cap_t)0);
99 #endif
100 
101 #if DT_ENUM_HAS_VALUE(HFXO_NODE, load_capacitors, internal)
102 	uint32_t xosc32mtrim = NRF_FICR->XOSC32MTRIM;
103 	/* The SLOPE field is in the two's complement form, hence this special
104 	 * handling. Ideally, it would result in just one SBFX instruction for
105 	 * extracting the slope value, at least gcc is capable of producing such
106 	 * output, but since the compiler apparently tries first to optimize
107 	 * additions and subtractions, it generates slightly less than optimal
108 	 * code.
109 	 */
110 	uint32_t slope_field =
111 		(xosc32mtrim & FICR_XOSC32MTRIM_SLOPE_Msk) >> FICR_XOSC32MTRIM_SLOPE_Pos;
112 	uint32_t slope_mask = FICR_XOSC32MTRIM_SLOPE_Msk >> FICR_XOSC32MTRIM_SLOPE_Pos;
113 	uint32_t slope_sign = (slope_mask - (slope_mask >> 1));
114 	int32_t slope_m = (int32_t)(slope_field ^ slope_sign) - (int32_t)slope_sign;
115 	uint32_t offset_m =
116 		(xosc32mtrim & FICR_XOSC32MTRIM_OFFSET_Msk) >> FICR_XOSC32MTRIM_OFFSET_Pos;
117 	/* As specified in the nRF54L15 PS:
118 	 * CAPVALUE = (((CAPACITANCE-5.5)*(FICR->XOSC32MTRIM.SLOPE+791)) +
119 	 *              FICR->XOSC32MTRIM.OFFSET<<2)>>8;
120 	 * where CAPACITANCE is the desired total load capacitance value in pF,
121 	 * holding any value between 4.0 pF and 17.0 pF in 0.25 pF steps.
122 	 */
123 
124 	/* NOTE 1: Requested HFXO internal capacitance in femto Faradas is used directly in formula
125 	 *         to calculate INTCAP code. That is different than in case of LFXO.
126 	 *
127 	 * NOTE 2: PS formula uses piko Farads, the implementation of the formula uses femto Farads
128 	 *         to avoid use of floating point data type.
129 	 */
130 	uint32_t cap_val_femto_f = DT_PROP(HFXO_NODE, load_capacitance_femtofarad);
131 
132 	uint32_t mid_val_intcap = (((cap_val_femto_f - 5500UL) * (uint32_t)(slope_m + 791UL))
133 		 + (offset_m << 2UL) * 1000UL) >> 8UL;
134 
135 	/* Convert the calculated value to piko Farads */
136 	uint32_t hfxo_intcap = mid_val_intcap / 1000;
137 
138 	/* Round based on fractional part */
139 	if (mid_val_intcap % 1000 >= 500) {
140 		hfxo_intcap++;
141 	}
142 
143 	nrf_oscillators_hfxo_cap_set(NRF_OSCILLATORS, true, hfxo_intcap);
144 
145 #elif DT_ENUM_HAS_VALUE(HFXO_NODE, load_capacitors, external)
146 	nrf_oscillators_hfxo_cap_set(NRF_OSCILLATORS, false, 0);
147 #endif
148 
149 	if (IS_ENABLED(CONFIG_SOC_NRF_FORCE_CONSTLAT)) {
150 		nrf_power_task_trigger(NRF_POWER, NRF_POWER_TASK_CONSTLAT);
151 	}
152 
153 #if (DT_PROP(DT_NODELABEL(vregmain), regulator_initial_mode) == NRF5X_REG_MODE_DCDC)
154 #if NRF54L_ERRATA_31_ENABLE_WORKAROUND
155 	/* Workaround for Errata 31 */
156 	if (nrf54l_errata_31()) {
157 		*((volatile uint32_t *)0x50120624ul) = 20 | 1<<5;
158 		*((volatile uint32_t *)0x5012063Cul) &= ~(1<<19);
159 	}
160 #endif
161 	nrf_regulators_vreg_enable_set(NRF_REGULATORS, NRF_REGULATORS_VREG_MAIN, true);
162 #endif
163 
164 }
165 #endif /* NRF_APPLICATION && !CONFIG_TRUSTED_EXECUTION_NONSECURE */
166 
nordicsemi_nrf54l_init(void)167 int nordicsemi_nrf54l_init(void)
168 {
169 	/* Update the SystemCoreClock global variable with current core clock
170 	 * retrieved from the DT.
171 	 */
172 	SystemCoreClock = NRF_PERIPH_GET_FREQUENCY(DT_NODELABEL(cpu));
173 
174 	sys_cache_instr_enable();
175 
176 #if (defined(NRF_APPLICATION) && !defined(CONFIG_TRUSTED_EXECUTION_NONSECURE)) || \
177 	!defined(__ZEPHYR__)
178 	power_and_clock_configuration();
179 #endif
180 
181 	return 0;
182 }
183 
arch_busy_wait(uint32_t time_us)184 void arch_busy_wait(uint32_t time_us)
185 {
186 	nrfx_coredep_delay_us(time_us);
187 }
188 
189 SYS_INIT(nordicsemi_nrf54l_init, PRE_KERNEL_1, 0);
190