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 <andrzej.glabek@nordicsemi.no>
This commit is contained in:
Andrzej Głąbek 2023-02-20 12:10:57 +01:00 committed by Carles Cufí
parent 3911583728
commit fe3b97a87f
2 changed files with 90 additions and 0 deletions

View file

@ -13,6 +13,7 @@ config SOC_NRF5340_CPUAPP
config SOC_NRF5340_CPUNET
bool
select HAS_NO_PM
imply SOC_NRF53_ANOMALY_160_WORKAROUND
choice
prompt "nRF53x MCU Selection"
@ -27,16 +28,22 @@ config SOC_NRF5340_CPUNET_QKAA
endchoice
config SOC_NRF53_ANOMALY_160_WORKAROUND
bool
depends on SYS_CLOCK_EXISTS
select ARM_ON_ENTER_CPU_IDLE_HOOK
if SOC_NRF5340_CPUAPP
config SOC_DCDC_NRF53X_APP
bool
imply SOC_NRF53_ANOMALY_160_WORKAROUND
help
Enable nRF53 series System on Chip Application MCU DC/DC converter.
config SOC_DCDC_NRF53X_NET
bool
imply SOC_NRF53_ANOMALY_160_WORKAROUND
help
Enable nRF53 series System on Chip Network MCU DC/DC converter.

View file

@ -18,6 +18,7 @@
#include <soc/nrfx_coredep.h>
#include <zephyr/logging/log.h>
#include <nrf_erratas.h>
#include <hal/nrf_power.h>
#if defined(CONFIG_SOC_NRF5340_CPUAPP)
#include <zephyr/drivers/gpio.h>
#include <zephyr/devicetree.h>
@ -92,6 +93,83 @@ static void enable_ram_retention(void)
}
#endif /* CONFIG_PM_S2RAM */
#if defined(CONFIG_SOC_NRF53_ANOMALY_160_WORKAROUND)
static void nrf53_anomaly_160_workaround(void)
{
/* This part is supposed to be removed once the writes are available
* in hal_nordic/nrfx/MDK.
*/
#if defined(CONFIG_SOC_NRF5340_CPUAPP) && !defined(CONFIG_TRUSTED_EXECUTION_NONSECURE)
*((volatile uint32_t *)0x5000470C) = 0x7Eul;
*((volatile uint32_t *)0x5000493C) = 0x7Eul;
*((volatile uint32_t *)0x50002118) = 0x7Ful;
*((volatile uint32_t *)0x50039E04) = 0x0ul;
*((volatile uint32_t *)0x50039E08) = 0x0ul;
*((volatile uint32_t *)0x50101110) = 0x0ul;
*((volatile uint32_t *)0x50002124) = 0x0ul;
*((volatile uint32_t *)0x5000212C) = 0x0ul;
*((volatile uint32_t *)0x502012A0) = 0x0ul;
#elif defined(CONFIG_SOC_NRF5340_CPUNET)
*((volatile uint32_t *)0x41002118) = 0x7Ful;
*((volatile uint32_t *)0x41080E04) = 0x0ul;
*((volatile uint32_t *)0x41080E08) = 0x0ul;
*((volatile uint32_t *)0x41002124) = 0x0ul;
*((volatile uint32_t *)0x4100212C) = 0x0ul;
*((volatile uint32_t *)0x41101110) = 0x0ul;
#endif
}
bool z_arm_on_enter_cpu_idle(void)
{
/* This code prevents the CPU from entering sleep again if it already
* entered sleep 5 times within last 200 us.
*/
/* System clock cycles needed to cover 200 us window. */
const uint32_t window_cycles =
ceiling_fraction(200 * CONFIG_SYS_CLOCK_HW_CYCLES_PER_SEC,
1000000);
static uint32_t timestamps[5];
static bool timestamps_filled;
static bool suppress_warning;
static uint8_t current;
uint8_t oldest = (current + 1) % ARRAY_SIZE(timestamps);
uint32_t now = k_cycle_get_32();
if (timestamps_filled &&
/* + 1 because only fully elapsed cycles need to be counted. */
(now - timestamps[oldest]) < (window_cycles + 1)) {
if (!suppress_warning) {
LOG_WRN("Anomaly 160 trigger conditions detected.");
suppress_warning = true;
}
return false;
}
suppress_warning = false;
/* Check if the CPU actually entered sleep since the last visit here
* (WFE/WFI could return immediately if the wake-up event was already
* registered).
*/
if (nrf_power_event_check(NRF_POWER, NRF_POWER_EVENT_SLEEPENTER)) {
nrf_power_event_clear(NRF_POWER, NRF_POWER_EVENT_SLEEPENTER);
/* If so, update the index at which the current timestamp is
* to be stored so that it replaces the oldest one, otherwise
* (when the CPU did not sleep), the recently stored timestamp
* is updated.
*/
current = oldest;
if (current == 0) {
timestamps_filled = true;
}
}
timestamps[current] = k_cycle_get_32();
return true;
}
#endif /* CONFIG_SOC_NRF53_ANOMALY_160_WORKAROUND */
static int nordicsemi_nrf53_init(const struct device *arg)
{
uint32_t key;
@ -164,6 +242,11 @@ static int nordicsemi_nrf53_init(const struct device *arg)
nrf_oscillators_hfxo_cap_set(NRF_OSCILLATORS, false, 0);
#endif
#if defined(CONFIG_SOC_NRF53_ANOMALY_160_WORKAROUND)
/* This needs to be done before DC/DC operation is enabled. */
nrf53_anomaly_160_workaround();
#endif
#if defined(CONFIG_SOC_DCDC_NRF53X_APP)
nrf_regulators_dcdcen_set(NRF_REGULATORS, true);
#endif