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