Linux preempt-rt

Check our new training course

Real-Time Linux with PREEMPT_RT

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

Bootlin logo

Elixir Cross Referencer

/*
 * Copyright (c) 2020, NXP
 *
 * SPDX-License-Identifier: Apache-2.0
 */

/**
 * @file
 * @brief System/hardware module for nxp_lpc55s69 platform
 *
 * This module provides routines to initialize and support board-level
 * hardware for the nxp_lpc55s69 platform.
 */

#include <kernel.h>
#include <device.h>
#include <init.h>
#include <soc.h>
#include <drivers/uart.h>
#include <linker/sections.h>
#include <arch/cpu.h>
#include <aarch32/cortex_m/exc.h>
#include <fsl_power.h>
#include <fsl_clock.h>
#include <fsl_common.h>
#include <fsl_device_registers.h>

#if CONFIG_USB_DC_NXP_LPCIP3511
#include "usb_phy.h"
#include "usb_dc_mcux.h"
#endif


#define SYSTEM_IS_XIP_FLEXSPI() \
	((((uint32_t)nxp_rt600_init >= 0x08000000U) &&		\
	  ((uint32_t)nxp_rt600_init < 0x10000000U)) ||		\
	 (((uint32_t)nxp_rt600_init >= 0x18000000U) &&		\
	  ((uint32_t)nxp_rt600_init < 0x20000000U)))

#define CTIMER_CLOCK_SOURCE(node_id) \
	TO_CTIMER_CLOCK_SOURCE(DT_CLOCKS_CELL(node_id, name), DT_PROP(node_id, clk_source))
#define TO_CTIMER_CLOCK_SOURCE(inst, val) TO_CLOCK_ATTACH_ID(inst, val)
#define TO_CLOCK_ATTACH_ID(inst, val) CLKCTL1_TUPLE_MUXA(CT32BIT##inst##FCLKSEL_OFFSET, val)
#define CTIMER_CLOCK_SETUP(node_id) CLOCK_AttachClk(CTIMER_CLOCK_SOURCE(node_id));

#ifdef CONFIG_INIT_SYS_PLL
const clock_sys_pll_config_t g_sysPllConfig = {
	.sys_pll_src  = kCLOCK_SysPllXtalIn,
	.numerator	  = 0,
	.denominator  = 1,
	.sys_pll_mult = kCLOCK_SysPllMult22
};
#endif

#ifdef CONFIG_INIT_AUDIO_PLL
const clock_audio_pll_config_t g_audioPllConfig = {
	.audio_pll_src	= kCLOCK_AudioPllXtalIn,
	.numerator		= 5040,
	.denominator	= 27000,
	.audio_pll_mult = kCLOCK_AudioPllMult22
};
#endif

#if CONFIG_USB_DC_NXP_LPCIP3511
/* USB PHY condfiguration */
#define BOARD_USB_PHY_D_CAL (0x0CU)
#define BOARD_USB_PHY_TXCAL45DP (0x06U)
#define BOARD_USB_PHY_TXCAL45DM (0x06U)
#endif

#ifdef CONFIG_NXP_IMX_RT6XX_BOOT_HEADER
extern char z_main_stack[];
extern char _flash_used[];

extern void z_arm_reset(void);
extern void z_arm_nmi(void);
extern void z_arm_hard_fault(void);
extern void z_arm_mpu_fault(void);
extern void z_arm_bus_fault(void);
extern void z_arm_usage_fault(void);
extern void z_arm_secure_fault(void);
extern void z_arm_svc(void);
extern void z_arm_debug_monitor(void);
extern void z_arm_pendsv(void);
extern void sys_clock_isr(void);
extern void z_arm_exc_spurious(void);

__imx_boot_ivt_section void (* const image_vector_table[])(void)  = {
	(void (*)())(z_main_stack + CONFIG_MAIN_STACK_SIZE),  /* 0x00 */
	z_arm_reset,				/* 0x04 */
	z_arm_nmi,					/* 0x08 */
	z_arm_hard_fault,			/* 0x0C */
	z_arm_mpu_fault,			/* 0x10 */
	z_arm_bus_fault,			/* 0x14 */
	z_arm_usage_fault,			/* 0x18 */
#if defined(CONFIG_ARM_SECURE_FIRMWARE)
	z_arm_secure_fault,			/* 0x1C */
#else
	z_arm_exc_spurious,
#endif /* CONFIG_ARM_SECURE_FIRMWARE */
	(void (*)())_flash_used,	/* 0x20, imageLength. */
	0,				/* 0x24, imageType (Plain Image) */
	0,				/* 0x28, authBlockOffset/crcChecksum */
	z_arm_svc,		/* 0x2C */
	z_arm_debug_monitor,	/* 0x30 */
	(void (*)())image_vector_table,		/* 0x34, imageLoadAddress. */
	z_arm_pendsv,						/* 0x38 */
#if defined(CONFIG_SYS_CLOCK_EXISTS)
	sys_clock_isr,						/* 0x3C */
#else
	z_arm_exc_spurious,
#endif
};
#endif /* CONFIG_NXP_IMX_RT6XX_BOOT_HEADER */

#if CONFIG_USB_DC_NXP_LPCIP3511

static void usb_device_clock_init(void)
{
	uint8_t usbClockDiv = 1;
	uint32_t usbClockFreq;
	usb_phy_config_struct_t phyConfig = {
		BOARD_USB_PHY_D_CAL,
		BOARD_USB_PHY_TXCAL45DP,
		BOARD_USB_PHY_TXCAL45DM,
	};

	/* enable USB IP clock */
	CLOCK_SetClkDiv(kCLOCK_DivPfc1Clk, 5);
	CLOCK_AttachClk(kXTALIN_CLK_to_USB_CLK);
	CLOCK_SetClkDiv(kCLOCK_DivUsbHsFclk, usbClockDiv);
	CLOCK_EnableUsbhsDeviceClock();
	RESET_PeripheralReset(kUSBHS_PHY_RST_SHIFT_RSTn);
	RESET_PeripheralReset(kUSBHS_DEVICE_RST_SHIFT_RSTn);
	RESET_PeripheralReset(kUSBHS_HOST_RST_SHIFT_RSTn);
	RESET_PeripheralReset(kUSBHS_SRAM_RST_SHIFT_RSTn);
	/*Make sure USDHC ram buffer has power up*/
	POWER_DisablePD(kPDRUNCFG_APD_USBHS_SRAM);
	POWER_DisablePD(kPDRUNCFG_PPD_USBHS_SRAM);
	POWER_ApplyPD();

	/* save usb ip clock freq*/
	usbClockFreq = g_xtalFreq / usbClockDiv;
	/* enable USB PHY PLL clock, the phy bus clock (480MHz) source is same with USB IP */
	CLOCK_EnableUsbHs0PhyPllClock(kXTALIN_CLK_to_USB_CLK, usbClockFreq);

#if defined(FSL_FEATURE_USBHSD_USB_RAM) && (FSL_FEATURE_USBHSD_USB_RAM)
	for (int i = 0; i < FSL_FEATURE_USBHSD_USB_RAM; i++) {
		((uint8_t *)FSL_FEATURE_USBHSD_USB_RAM_BASE_ADDRESS)[i] = 0x00U;
	}
#endif
	USB_EhciPhyInit(kUSB_ControllerLpcIp3511Hs0, CLK_XTAL_OSC_CLK, &phyConfig);

	/* the following code should run after phy initialization and
	 * should wait some microseconds to make sure utmi clock valid
	 */
	/* enable usb1 host clock */
	CLOCK_EnableClock(kCLOCK_UsbhsHost);
	/* Wait until host_needclk de-asserts */
	while (SYSCTL0->USBCLKSTAT & SYSCTL0_USBCLKSTAT_HOST_NEED_CLKST_MASK) {
		__ASM("nop");
	}
	/* According to reference mannual, device mode setting has to be set by
	 * access usb host register
	 */
	USBHSH->PORTMODE |= USBHSH_PORTMODE_DEV_ENABLE_MASK;
	/* disable usb1 host clock */
	CLOCK_DisableClock(kCLOCK_UsbhsHost);
}

#endif

/**
 *
 * @brief Initialize the system clock
 *
 * @return N/A
 *
 */
static ALWAYS_INLINE void clock_init(void)
{
#ifdef CONFIG_SOC_MIMXRT685S_CM33
	/* Configure LPOSC clock*/
	POWER_DisablePD(kPDRUNCFG_PD_LPOSC);
	/* Configure FFRO clock */
	POWER_DisablePD(kPDRUNCFG_PD_FFRO);
	CLOCK_EnableFfroClk(kCLOCK_Ffro48M);
	/* Configure SFRO clock */
	POWER_DisablePD(kPDRUNCFG_PD_SFRO);
	CLOCK_EnableSfroClk();

	/* Let CPU run on FFRO for safe switching. */
	CLOCK_AttachClk(kFFRO_to_MAIN_CLK);

	/* Configure SYSOSC clock source */
	POWER_DisablePD(kPDRUNCFG_PD_SYSXTAL);
	POWER_UpdateOscSettlingTime(CONFIG_SYSOSC_SETTLING_US);
	CLOCK_EnableSysOscClk(true, true, CONFIG_SYSOSC_SETTLING_US);
	CLOCK_SetXtalFreq(CONFIG_XTAL_SYS_CLK_HZ);

#ifdef CONFIG_INIT_SYS_PLL
	/* Configure SysPLL0 clock source */
	CLOCK_InitSysPll(&g_sysPllConfig);
	CLOCK_InitSysPfd(kCLOCK_Pfd0, 19);
	CLOCK_InitSysPfd(kCLOCK_Pfd2, 24);
#endif

#ifdef CONFIG_INIT_AUDIO_PLL
	/* Configure Audio PLL clock source */
	CLOCK_InitAudioPll(&g_audioPllConfig);
	CLOCK_InitAudioPfd(kCLOCK_Pfd0, 26);
	CLOCK_SetClkDiv(kCLOCK_DivAudioPllClk, 15U);
#endif

	/* Set SYSCPUAHBCLKDIV divider to value 2 */
	CLOCK_SetClkDiv(kCLOCK_DivSysCpuAhbClk, 2U);

	/* Set up clock selectors - Attach clocks to the peripheries */
	CLOCK_AttachClk(kMAIN_PLL_to_MAIN_CLK);

	/* Set up dividers */
	/* Set PFC0DIV divider to value 2 */
	CLOCK_SetClkDiv(kCLOCK_DivPfc0Clk, 2U);
	/* Set FRGPLLCLKDIV divider to value 12 */
	CLOCK_SetClkDiv(kCLOCK_DivPllFrgClk, 12U);

	CLOCK_AttachClk(kSFRO_to_FLEXCOMM0);

#if CONFIG_USB_DC_NXP_LPCIP3511
	usb_device_clock_init();
#endif

#if DT_NODE_HAS_COMPAT_STATUS(DT_NODELABEL(flexcomm2), nxp_lpc_i2c, okay)
	CLOCK_AttachClk(kSFRO_to_FLEXCOMM2);
#endif

#if DT_NODE_HAS_COMPAT_STATUS(DT_NODELABEL(flexcomm4), nxp_lpc_usart, okay)
	CLOCK_AttachClk(kSFRO_to_FLEXCOMM4);
#endif

#if DT_NODE_HAS_COMPAT_STATUS(DT_NODELABEL(flexcomm5), nxp_lpc_spi, okay)
	CLOCK_AttachClk(kFFRO_to_FLEXCOMM5);
#endif

#if (DT_NODE_HAS_COMPAT_STATUS(DT_NODELABEL(flexcomm1), nxp_lpc_i2s, okay))
	/* attach AUDIO PLL clock to FLEXCOMM1 (I2S1) */
	CLOCK_AttachClk(kAUDIO_PLL_to_FLEXCOMM1);
#endif
#if (DT_NODE_HAS_COMPAT_STATUS(DT_NODELABEL(flexcomm3), nxp_lpc_i2s, okay))
	/* attach AUDIO PLL clock to FLEXCOMM3 (I2S3) */
	CLOCK_AttachClk(kAUDIO_PLL_to_FLEXCOMM3);
#endif

#if (DT_NODE_HAS_COMPAT_STATUS(DT_NODELABEL(wwdt0), nxp_lpc_wwdt, okay))
	CLOCK_AttachClk(kLPOSC_to_WDT0_CLK);
#else
	/* Allowed to select none if not being used for watchdog to
	 * reduce power
	 */
	CLOCK_AttachClk(kNONE_to_WDT0_CLK);
#endif

#if DT_NODE_HAS_STATUS(DT_NODELABEL(usdhc1), okay) && CONFIG_DISK_DRIVER_SDMMC
	/* Make sure USDHC ram buffer has been power up*/
	POWER_DisablePD(kPDRUNCFG_APD_USDHC0_SRAM);
	POWER_DisablePD(kPDRUNCFG_PPD_USDHC0_SRAM);
	POWER_DisablePD(kPDRUNCFG_PD_LPOSC);
	POWER_ApplyPD();

	/* usdhc depend on 32K clock also */
	CLOCK_AttachClk(kLPOSC_DIV32_to_32KHZWAKE_CLK);
	CLOCK_AttachClk(kAUX0_PLL_to_SDIO0_CLK);
	CLOCK_SetClkDiv(kCLOCK_DivSdio0Clk, 1);
	CLOCK_EnableClock(kCLOCK_Sdio0);
	RESET_PeripheralReset(kSDIO0_RST_SHIFT_RSTn);
#endif

	DT_FOREACH_STATUS_OKAY(nxp_lpc_ctimer, CTIMER_CLOCK_SETUP)

#endif /* CONFIG_SOC_MIMXRT685S_CM33 */
}

#if (DT_NODE_HAS_STATUS(DT_NODELABEL(usdhc1), okay) && CONFIG_DISK_DRIVER_SDMMC)

void imxrt_usdhc_pinmux(uint16_t nusdhc, bool init,
	uint32_t speed, uint32_t strength)
{

}
#endif

/**
 *
 * @brief Perform basic hardware initialization
 *
 * Initialize the interrupt controller device drivers.
 * Also initialize the timer device driver, if required.
 *
 * @return 0
 */

static int nxp_rt600_init(const struct device *arg)
{
	ARG_UNUSED(arg);

	/* old interrupt lock level */
	unsigned int oldLevel;

	/* disable interrupts */
	oldLevel = irq_lock();

	/* Enable cache to accelerate boot. */
	if (SYSTEM_IS_XIP_FLEXSPI() && (CACHE64_POLSEL->POLSEL == 0)) {
		/*
		 * Set command to invalidate all ways and write GO bit
		 * to initiate command
		 */
		CACHE64->CCR = (CACHE64_CTRL_CCR_INVW1_MASK |
					CACHE64_CTRL_CCR_INVW0_MASK);
		CACHE64->CCR |= CACHE64_CTRL_CCR_GO_MASK;
		/* Wait until the command completes */
		while (CACHE64->CCR & CACHE64_CTRL_CCR_GO_MASK) {
		}
		/* Enable cache, enable write buffer */
		CACHE64->CCR = (CACHE64_CTRL_CCR_ENWRBUF_MASK |
						CACHE64_CTRL_CCR_ENCACHE_MASK);

		/* Set whole FlexSPI0 space to write through. */
		CACHE64_POLSEL->REG0_TOP = 0x07FFFC00U;
		CACHE64_POLSEL->REG1_TOP = 0x0U;
		CACHE64_POLSEL->POLSEL = 0x1U;

		__ISB();
		__DSB();
	}

	/* Initialize clock */
	clock_init();

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

	/* restore interrupt state */
	irq_unlock(oldLevel);

	return 0;
}

SYS_INIT(nxp_rt600_init, PRE_KERNEL_1, 0);