Commit fe3b97a8 authored by Andrzej Głąbek's avatar Andrzej Głąbek Committed by Carles Cufi
Browse files

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: default avatarAndrzej Głąbek <andrzej.glabek@nordicsemi.no>
parent 39115837
Loading
Loading
Loading
Loading
+7 −0
Original line number Diff line number Diff line
@@ -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.

+83 −0
Original line number Diff line number Diff line
@@ -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