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:
parent
3911583728
commit
fe3b97a87f
|
@ -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.
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue