Boot Linux faster!

Check our new training course

Boot Linux faster!

Check our new training course
and Creative Commons CC-BY-SA
lecture and lab materials

Bootlin logo

Elixir Cross Referencer

/*
 * Copyright (c) 2016 Nordic Semiconductor ASA
 *
 * SPDX-License-Identifier: Apache-2.0
 */

/**
 * @file
 * @brief System/hardware module for Nordic Semiconductor nRF52 family processor
 *
 * This module provides routines to initialize and support board-level hardware
 * for the Nordic Semiconductor nRF52 family processor.
 */

#include <kernel.h>
#include <device.h>
#include <init.h>
#include <soc.h>
#include <cortex_m/exc.h>

#ifdef CONFIG_RUNTIME_NMI
extern void _NmiInit(void);
#define NMI_INIT() _NmiInit()
#else
#define NMI_INIT()
#endif

#include "nrf.h"

#define __SYSTEM_CLOCK_64M (64000000UL)

#ifdef CONFIG_SOC_NRF52832
static bool ftpan_32(void)
{
	if ((((*(uint32_t *)0xF0000FE0) & 0x000000FF) == 0x6) &&
		(((*(uint32_t *)0xF0000FE4) & 0x0000000F) == 0x0)) {
		if ((((*(uint32_t *)0xF0000FE8) & 0x000000F0) == 0x30) &&
			(((*(uint32_t *)0xF0000FEC) & 0x000000F0) == 0x0)) {
			return true;
		}
	}

	return false;
}

static bool ftpan_37(void)
{
	if ((((*(uint32_t *)0xF0000FE0) & 0x000000FF) == 0x6) &&
		(((*(uint32_t *)0xF0000FE4) & 0x0000000F) == 0x0)) {
		if ((((*(uint32_t *)0xF0000FE8) & 0x000000F0) == 0x30) &&
			(((*(uint32_t *)0xF0000FEC) & 0x000000F0) == 0x0)) {
			return true;
		}
	}

	return false;
}

static bool ftpan_36(void)
{
	if ((((*(uint32_t *)0xF0000FE0) & 0x000000FF) == 0x6) &&
		(((*(uint32_t *)0xF0000FE4) & 0x0000000F) == 0x0)) {
		if ((((*(uint32_t *)0xF0000FE8) & 0x000000F0) == 0x30) &&
			(((*(uint32_t *)0xF0000FEC) & 0x000000F0) == 0x0)) {
			return true;
		}
	}

	return false;
}

static void nordicsemi_nrf52832_init(void)
{
	/* Workaround for FTPAN-32 "DIF: Debug session automatically
	* enables TracePort pins" found at Product Anomaly document
	* for your device located at https://www.nordicsemi.com/
	*/
	if (ftpan_32()) {
		CoreDebug->DEMCR &= ~CoreDebug_DEMCR_TRCENA_Msk;
	}

	/* Workaround for FTPAN-37 "AMLI: EasyDMA is slow with Radio,
	* ECB, AAR and CCM." found at Product Anomaly document
	* for your device located at https://www.nordicsemi.com/
	*/
	if (ftpan_37()) {
		*(volatile uint32_t *)0x400005A0 = 0x3;
	}

	/* Workaround for FTPAN-36 "CLOCK: Some registers are not
	* reset when expected." found at Product Anomaly document
	* for your device located at https://www.nordicsemi.com/
	*/
	if (ftpan_36()) {
		NRF_CLOCK->EVENTS_DONE = 0;
		NRF_CLOCK->EVENTS_CTTO = 0;
	}
	/* Configure GPIO pads as pPin Reset pin if Pin Reset
	* capabilities desired. If CONFIG_GPIO_AS_PINRESET is not
	* defined, pin reset will not be available. One GPIO (see
	* Product Specification to see which one) will then be
	* reserved for PinReset and not available as normal GPIO.
	*/
#if defined(CONFIG_GPIO_AS_PINRESET)
	if (((NRF_UICR->PSELRESET[0] & UICR_PSELRESET_CONNECT_Msk) !=
	     (UICR_PSELRESET_CONNECT_Connected << UICR_PSELRESET_CONNECT_Pos)) ||
	    ((NRF_UICR->PSELRESET[0] & UICR_PSELRESET_CONNECT_Msk) !=
	     (UICR_PSELRESET_CONNECT_Connected << UICR_PSELRESET_CONNECT_Pos))) {

		NRF_NVMC->CONFIG = NVMC_CONFIG_WEN_Wen << NVMC_CONFIG_WEN_Pos;
		while (NRF_NVMC->READY == NVMC_READY_READY_Busy) {
			;
		}
		NRF_UICR->PSELRESET[0] = 21;
		while (NRF_NVMC->READY == NVMC_READY_READY_Busy) {
			;
		}
		NRF_UICR->PSELRESET[1] = 21;
		while (NRF_NVMC->READY == NVMC_READY_READY_Busy) {
			;
		}
		NRF_NVMC->CONFIG = NVMC_CONFIG_WEN_Ren << NVMC_CONFIG_WEN_Pos;
		while (NRF_NVMC->READY == NVMC_READY_READY_Busy) {
			;
		}
		NVIC_SystemReset();
	}
#endif

	/* Enable SWO trace functionality. If ENABLE_SWO is not
	* defined, SWO pin will be used as GPIO (see Product
	* Specification to see which one).
	*/
#if defined(ENABLE_SWO)
	CoreDebug->DEMCR |= CoreDebug_DEMCR_TRCENA_Msk;
	NRF_CLOCK->TRACECONFIG |= CLOCK_TRACECONFIG_TRACEMUX_Serial <<
				  CLOCK_TRACECONFIG_TRACEMUX_Pos;
#endif

	/* Enable Trace functionality. If ENABLE_TRACE is not
	* defined, TRACE pins will be used as GPIOs (see Product
	* Specification to see which ones).
	*/
#if defined(ENABLE_TRACE)
	CoreDebug->DEMCR |= CoreDebug_DEMCR_TRCENA_Msk;
	NRF_CLOCK->TRACECONFIG |= CLOCK_TRACECONFIG_TRACEMUX_Parallel <<
				  CLOCK_TRACECONFIG_TRACEMUX_Pos;
#endif
}
#endif /* CONFIG_SOC_NRF52832 */

#ifdef CONFIG_SOC_NRF52840
static bool errata_36(void)
{
	if ((*(uint32_t *)0x10000130ul == 0x8ul) &&
	    (*(uint32_t *)0x10000134ul == 0x0ul)) {
		return true;
	}

	return false;
}


static bool errata_98(void)
{
	if ((*(uint32_t *)0x10000130ul == 0x8ul) &&
	    (*(uint32_t *)0x10000134ul == 0x0ul)) {
		return true;
	}

	return false;
}


static bool errata_103(void)
{
	if ((*(uint32_t *)0x10000130ul == 0x8ul) &&
	    (*(uint32_t *)0x10000134ul == 0x0ul)) {
		return true;
	}

	return false;
}


static bool errata_115(void)
{
	if ((*(uint32_t *)0x10000130ul == 0x8ul) &&
	    (*(uint32_t *)0x10000134ul == 0x0ul)) {
		return true;
	}

	return false;
}


static bool errata_120(void)
{
	if ((*(uint32_t *)0x10000130ul == 0x8ul) &&
	    (*(uint32_t *)0x10000134ul == 0x0ul)) {
		return true;
	}

	return false;
}

static void nordicsemi_nrf52840_init(void)
{
	/* Workaround for Errata 36 "CLOCK: Some registers are not reset when
	 * expected" found at the Errata document for your device located at
	 * https://infocenter.nordicsemi.com/
	 */
	if (errata_36()) {
		NRF_CLOCK->EVENTS_DONE = 0;
		NRF_CLOCK->EVENTS_CTTO = 0;
		NRF_CLOCK->CTIV = 0;
	}

	/* Workaround for Errata 98 "NFCT: Not able to communicate with the
	 * peer" found at the Errata document for your device located at
	 * https://infocenter.nordicsemi.com/
	 */
	if (errata_98()) {
		*(volatile uint32_t *)0x4000568Cul = 0x00038148ul;
	}

	/* Workaround for Errata 103 "CCM: Wrong reset value of CCM
	 * MAXPACKETSIZE" found at the Errata document for your device
	 * located at https://infocenter.nordicsemi.com/
	 */
	if (errata_103()) {
		NRF_CCM->MAXPACKETSIZE = 0xFBul;
	}

	/* Workaround for Errata 115 "RAM: RAM content cannot be trusted upon
	 * waking up from System ON Idle or System OFF mode" found at the
	 * Errata document for your device located at
	 * https://infocenter.nordicsemi.com/
	 */
	if (errata_115()) {
		*(volatile uint32_t *)0x40000EE4 =
			(*(volatile uint32_t *) 0x40000EE4 & 0xFFFFFFF0) |
			(*(uint32_t *)0x10000258 & 0x0000000F);
	}

	/* Workaround for Errata 120 "QSPI: Data read or written is corrupted"
	 * found at the Errata document for your device located at
	 * https://infocenter.nordicsemi.com/
	 */
	if (errata_120()) {
		*(volatile uint32_t *)0x40029640ul = 0x200ul;
	}

	/* Configure GPIO pads as pPin Reset pin if Pin Reset capabilities
	 * desired.
	 * If CONFIG_GPIO_AS_PINRESET is not defined, pin reset will not be
	 * available. One GPIO (see Product Specification to see which one) will
	 * then be reserved for PinReset and not available as normal GPIO.
	 */
#if defined(CONFIG_GPIO_AS_PINRESET)
	if (((NRF_UICR->PSELRESET[0] & UICR_PSELRESET_CONNECT_Msk) !=
	     (UICR_PSELRESET_CONNECT_Connected << UICR_PSELRESET_CONNECT_Pos)) ||
	    ((NRF_UICR->PSELRESET[1] & UICR_PSELRESET_CONNECT_Msk) !=
	     (UICR_PSELRESET_CONNECT_Connected << UICR_PSELRESET_CONNECT_Pos))) {
		NRF_NVMC->CONFIG = NVMC_CONFIG_WEN_Wen << NVMC_CONFIG_WEN_Pos;
		while (NRF_NVMC->READY == NVMC_READY_READY_Busy) {
		}
		NRF_UICR->PSELRESET[0] = 18;
		while (NRF_NVMC->READY == NVMC_READY_READY_Busy) {
		}
		NRF_UICR->PSELRESET[1] = 18;
		while (NRF_NVMC->READY == NVMC_READY_READY_Busy) {
		}
		NRF_NVMC->CONFIG = NVMC_CONFIG_WEN_Ren << NVMC_CONFIG_WEN_Pos;
		while (NRF_NVMC->READY == NVMC_READY_READY_Busy) {
		}
		NVIC_SystemReset();
	}
#endif
	/* Enable SWO trace functionality. If ENABLE_SWO is not defined, SWO pin
	 * will be used as GPIO (see Product Specification to see which one).
	 */
#if defined(ENABLE_SWO)
	CoreDebug->DEMCR |= CoreDebug_DEMCR_TRCENA_Msk;
	NRF_CLOCK->TRACECONFIG |= CLOCK_TRACECONFIG_TRACEMUX_Serial <<
		CLOCK_TRACECONFIG_TRACEMUX_Pos;
	NRF_P1->PIN_CNF[0] = (GPIO_PIN_CNF_DRIVE_H0H1 << GPIO_PIN_CNF_DRIVE_Pos)
		| (GPIO_PIN_CNF_INPUT_Connect << GPIO_PIN_CNF_INPUT_Pos) |
		(GPIO_PIN_CNF_DIR_Output << GPIO_PIN_CNF_DIR_Pos);
#endif
	/* Enable Trace functionality. If ENABLE_TRACE is not defined, TRACE
	 * pins will be used as GPIOs (see Product Specification to see which
	 * ones).
	 */
#if defined(ENABLE_TRACE)
	CoreDebug->DEMCR |= CoreDebug_DEMCR_TRCENA_Msk;
	NRF_CLOCK->TRACECONFIG |= CLOCK_TRACECONFIG_TRACEMUX_Parallel <<
		CLOCK_TRACECONFIG_TRACEMUX_Pos;
	NRF_P0->PIN_CNF[7]	= (GPIO_PIN_CNF_DRIVE_H0H1 <<
			GPIO_PIN_CNF_DRIVE_Pos) | (GPIO_PIN_CNF_INPUT_Connect <<
				GPIO_PIN_CNF_INPUT_Pos) |
			(GPIO_PIN_CNF_DIR_Output << GPIO_PIN_CNF_DIR_Pos);
	NRF_P1->PIN_CNF[0]	= (GPIO_PIN_CNF_DRIVE_H0H1 <<
			GPIO_PIN_CNF_DRIVE_Pos) | (GPIO_PIN_CNF_INPUT_Connect <<
				GPIO_PIN_CNF_INPUT_Pos) |
			(GPIO_PIN_CNF_DIR_Output << GPIO_PIN_CNF_DIR_Pos);
	NRF_P0->PIN_CNF[12] = (GPIO_PIN_CNF_DRIVE_H0H1 <<
			GPIO_PIN_CNF_DRIVE_Pos) | (GPIO_PIN_CNF_INPUT_Connect <<
				GPIO_PIN_CNF_INPUT_Pos) |
			(GPIO_PIN_CNF_DIR_Output << GPIO_PIN_CNF_DIR_Pos);
	NRF_P0->PIN_CNF[11] = (GPIO_PIN_CNF_DRIVE_H0H1 <<
			GPIO_PIN_CNF_DRIVE_Pos) | (GPIO_PIN_CNF_INPUT_Connect <<
				GPIO_PIN_CNF_INPUT_Pos) |
			(GPIO_PIN_CNF_DIR_Output << GPIO_PIN_CNF_DIR_Pos);
	NRF_P1->PIN_CNF[9]	= (GPIO_PIN_CNF_DRIVE_H0H1 <<
			GPIO_PIN_CNF_DRIVE_Pos) | (GPIO_PIN_CNF_INPUT_Connect <<
				GPIO_PIN_CNF_INPUT_Pos) |
			(GPIO_PIN_CNF_DIR_Output << GPIO_PIN_CNF_DIR_Pos);
#endif
}
#endif /* CONFIG_SOC_NRF52840 */

uint32_t SystemCoreClock __used = __SYSTEM_CLOCK_64M;

static void clock_init(void)
{
	SystemCoreClock = __SYSTEM_CLOCK_64M;
}

static int nordicsemi_nrf52_init(struct device *arg)
{
	uint32_t key;

	ARG_UNUSED(arg);

	key = irq_lock();

#ifdef CONFIG_SOC_NRF52832
	nordicsemi_nrf52832_init();
#endif
#ifdef CONFIG_SOC_NRF52840
	nordicsemi_nrf52840_init();
#endif
	/* Enable the FPU if the compiler used floating point unit
	 * instructions. Since the FPU consumes energy, remember to
	 * disable FPU use in the compiler if floating point unit
	 * operations are not used in your code.
	 */
#if defined(CONFIG_FLOAT)
	SCB->CPACR |= (3UL << 20) | (3UL << 22);
	__DSB();
	__ISB();
#endif

	/* Configure NFCT pins as GPIOs if NFCT is not to be used in
	 * your code. If CONFIG_NFCT_PINS_AS_GPIOS is not defined,
	 * two GPIOs (see Product Specification to see which ones)
	 * will be reserved for NFC and will not be available as
	 * normal GPIOs.
	 */
#if defined(CONFIG_NFCT_PINS_AS_GPIOS)
	if ((NRF_UICR->NFCPINS & UICR_NFCPINS_PROTECT_Msk) ==
	    (UICR_NFCPINS_PROTECT_NFC << UICR_NFCPINS_PROTECT_Pos)) {

		NRF_NVMC->CONFIG = NVMC_CONFIG_WEN_Wen << NVMC_CONFIG_WEN_Pos;
		while (NRF_NVMC->READY == NVMC_READY_READY_Busy) {
			;
		}
		NRF_UICR->NFCPINS &= ~UICR_NFCPINS_PROTECT_Msk;
		while (NRF_NVMC->READY == NVMC_READY_READY_Busy) {
			;
		}
		NRF_NVMC->CONFIG = NVMC_CONFIG_WEN_Ren << NVMC_CONFIG_WEN_Pos;
		while (NRF_NVMC->READY == NVMC_READY_READY_Busy) {
			;
		}
		NVIC_SystemReset();
	}
#endif

	_ClearFaults();

	/* Setup master clock */
	clock_init();

	/* Install default handler that simply resets the CPU
	* if configured in the kernel, NOP otherwise
	*/
	NMI_INIT();

	irq_unlock(key);

	return 0;
}

SYS_INIT(nordicsemi_nrf52_init, PRE_KERNEL_1, 0);