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