Skip to content

Commit ee41e58

Browse files
ananglrlubos
authored andcommitted
[nrf fromtree] soc: nrf53: Add workaround for anomaly 160
Implement a workaround for the nRF53 anomaly 160. This consist of a set of writes to certain hardware registers that is done at boot and a piece of code that is executed when the CPU is made idle and that prevents the CPU from switching between active and sleep modes more than five times within a 200 us period. Signed-off-by: Andrzej Głąbek <[email protected]> (cherry picked from commit fe3b97a)
1 parent 523b567 commit ee41e58

File tree

2 files changed

+90
-0
lines changed

2 files changed

+90
-0
lines changed

soc/arm/nordic_nrf/nrf53/Kconfig.soc

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ config SOC_NRF5340_CPUAPP
1313
config SOC_NRF5340_CPUNET
1414
bool
1515
select HAS_NO_PM
16+
imply SOC_NRF53_ANOMALY_160_WORKAROUND
1617

1718
choice
1819
prompt "nRF53x MCU Selection"
@@ -27,16 +28,22 @@ config SOC_NRF5340_CPUNET_QKAA
2728

2829
endchoice
2930

31+
config SOC_NRF53_ANOMALY_160_WORKAROUND
32+
bool
33+
depends on SYS_CLOCK_EXISTS
34+
select ARM_ON_ENTER_CPU_IDLE_HOOK
3035

3136
if SOC_NRF5340_CPUAPP
3237

3338
config SOC_DCDC_NRF53X_APP
3439
bool
40+
imply SOC_NRF53_ANOMALY_160_WORKAROUND
3541
help
3642
Enable nRF53 series System on Chip Application MCU DC/DC converter.
3743

3844
config SOC_DCDC_NRF53X_NET
3945
bool
46+
imply SOC_NRF53_ANOMALY_160_WORKAROUND
4047
help
4148
Enable nRF53 series System on Chip Network MCU DC/DC converter.
4249

soc/arm/nordic_nrf/nrf53/soc.c

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
#include <soc/nrfx_coredep.h>
1919
#include <zephyr/logging/log.h>
2020
#include <nrf_erratas.h>
21+
#include <hal/nrf_power.h>
2122
#if defined(CONFIG_SOC_NRF5340_CPUAPP)
2223
#include <zephyr/drivers/gpio.h>
2324
#include <zephyr/devicetree.h>
@@ -98,6 +99,83 @@ static void enable_ram_retention(void)
9899
}
99100
#endif /* CONFIG_PM_S2RAM */
100101

102+
#if defined(CONFIG_SOC_NRF53_ANOMALY_160_WORKAROUND)
103+
static void nrf53_anomaly_160_workaround(void)
104+
{
105+
/* This part is supposed to be removed once the writes are available
106+
* in hal_nordic/nrfx/MDK.
107+
*/
108+
#if defined(CONFIG_SOC_NRF5340_CPUAPP) && !defined(CONFIG_TRUSTED_EXECUTION_NONSECURE)
109+
*((volatile uint32_t *)0x5000470C) = 0x7Eul;
110+
*((volatile uint32_t *)0x5000493C) = 0x7Eul;
111+
*((volatile uint32_t *)0x50002118) = 0x7Ful;
112+
*((volatile uint32_t *)0x50039E04) = 0x0ul;
113+
*((volatile uint32_t *)0x50039E08) = 0x0ul;
114+
*((volatile uint32_t *)0x50101110) = 0x0ul;
115+
*((volatile uint32_t *)0x50002124) = 0x0ul;
116+
*((volatile uint32_t *)0x5000212C) = 0x0ul;
117+
*((volatile uint32_t *)0x502012A0) = 0x0ul;
118+
#elif defined(CONFIG_SOC_NRF5340_CPUNET)
119+
*((volatile uint32_t *)0x41002118) = 0x7Ful;
120+
*((volatile uint32_t *)0x41080E04) = 0x0ul;
121+
*((volatile uint32_t *)0x41080E08) = 0x0ul;
122+
*((volatile uint32_t *)0x41002124) = 0x0ul;
123+
*((volatile uint32_t *)0x4100212C) = 0x0ul;
124+
*((volatile uint32_t *)0x41101110) = 0x0ul;
125+
#endif
126+
}
127+
128+
bool z_arm_on_enter_cpu_idle(void)
129+
{
130+
/* This code prevents the CPU from entering sleep again if it already
131+
* entered sleep 5 times within last 200 us.
132+
*/
133+
134+
/* System clock cycles needed to cover 200 us window. */
135+
const uint32_t window_cycles =
136+
ceiling_fraction(200 * CONFIG_SYS_CLOCK_HW_CYCLES_PER_SEC,
137+
1000000);
138+
static uint32_t timestamps[5];
139+
static bool timestamps_filled;
140+
static bool suppress_warning;
141+
static uint8_t current;
142+
uint8_t oldest = (current + 1) % ARRAY_SIZE(timestamps);
143+
uint32_t now = k_cycle_get_32();
144+
145+
if (timestamps_filled &&
146+
/* + 1 because only fully elapsed cycles need to be counted. */
147+
(now - timestamps[oldest]) < (window_cycles + 1)) {
148+
if (!suppress_warning) {
149+
LOG_WRN("Anomaly 160 trigger conditions detected.");
150+
suppress_warning = true;
151+
}
152+
return false;
153+
}
154+
suppress_warning = false;
155+
156+
/* Check if the CPU actually entered sleep since the last visit here
157+
* (WFE/WFI could return immediately if the wake-up event was already
158+
* registered).
159+
*/
160+
if (nrf_power_event_check(NRF_POWER, NRF_POWER_EVENT_SLEEPENTER)) {
161+
nrf_power_event_clear(NRF_POWER, NRF_POWER_EVENT_SLEEPENTER);
162+
/* If so, update the index at which the current timestamp is
163+
* to be stored so that it replaces the oldest one, otherwise
164+
* (when the CPU did not sleep), the recently stored timestamp
165+
* is updated.
166+
*/
167+
current = oldest;
168+
if (current == 0) {
169+
timestamps_filled = true;
170+
}
171+
}
172+
173+
timestamps[current] = k_cycle_get_32();
174+
175+
return true;
176+
}
177+
#endif /* CONFIG_SOC_NRF53_ANOMALY_160_WORKAROUND */
178+
101179
static int nordicsemi_nrf53_init(const struct device *arg)
102180
{
103181
uint32_t key;
@@ -159,6 +237,11 @@ static int nordicsemi_nrf53_init(const struct device *arg)
159237
nrf_oscillators_hfxo_cap_set(NRF_OSCILLATORS, false, 0);
160238
#endif
161239

240+
#if defined(CONFIG_SOC_NRF53_ANOMALY_160_WORKAROUND)
241+
/* This needs to be done before DC/DC operation is enabled. */
242+
nrf53_anomaly_160_workaround();
243+
#endif
244+
162245
#if defined(CONFIG_SOC_DCDC_NRF53X_APP)
163246
nrf_regulators_dcdcen_set(NRF_REGULATORS, true);
164247
#endif

0 commit comments

Comments
 (0)