318 lines
11 KiB
C
318 lines
11 KiB
C
/*
|
|
* Copyright (c) 2019-2021 Vestas Wind Systems A/S
|
|
*
|
|
* Based on NXP k6x soc.c, which is:
|
|
* Copyright (c) 2014-2015 Wind River Systems, Inc.
|
|
* Copyright (c) 2016, Freescale Semiconductor, Inc.
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
#include <kernel.h>
|
|
#include <device.h>
|
|
#include <init.h>
|
|
#include <fsl_clock.h>
|
|
#include <fsl_cache.h>
|
|
#include <arch/arm/aarch32/cortex_m/cmsis.h>
|
|
|
|
#define ASSERT_WITHIN_RANGE(val, min, max, str) \
|
|
BUILD_ASSERT(val >= min && val <= max, str)
|
|
|
|
#define ASSERT_ASYNC_CLK_DIV_VALID(val, str) \
|
|
BUILD_ASSERT(val == 0 || val == 1 || val == 2 || val == 4 || \
|
|
val == 8 || val == 16 || val == 2 || val == 64, str)
|
|
|
|
#define TO_SYS_CLK_DIV(val) _DO_CONCAT(kSCG_SysClkDivBy, val)
|
|
|
|
#define kSCG_AsyncClkDivBy0 kSCG_AsyncClkDisable
|
|
#define TO_ASYNC_CLK_DIV(val) _DO_CONCAT(kSCG_AsyncClkDivBy, val)
|
|
|
|
#define SCG_CLOCK_NODE(name) DT_CHILD(DT_INST(0, nxp_kinetis_scg), name)
|
|
#define SCG_CLOCK_DIV(name) DT_PROP(SCG_CLOCK_NODE(name), clock_div)
|
|
#define SCG_CLOCK_MULT(name) DT_PROP(SCG_CLOCK_NODE(name), clock_mult)
|
|
|
|
/* System Clock configuration */
|
|
ASSERT_WITHIN_RANGE(SCG_CLOCK_DIV(slow_clk), 2, 8,
|
|
"Invalid SCG slow clock divider value");
|
|
ASSERT_WITHIN_RANGE(SCG_CLOCK_DIV(bus_clk), 1, 16,
|
|
"Invalid SCG bus clock divider value");
|
|
#if DT_SAME_NODE(DT_CLOCKS_CTLR(SCG_CLOCK_NODE(core_clk)), SCG_CLOCK_NODE(spll_clk))
|
|
/* Core divider range is 1 to 4 with SPLL as clock source */
|
|
ASSERT_WITHIN_RANGE(SCG_CLOCK_DIV(core_clk), 1, 4,
|
|
"Invalid SCG core clock divider value");
|
|
#else
|
|
ASSERT_WITHIN_RANGE(SCG_CLOCK_DIV(core_clk), 1, 16,
|
|
"Invalid SCG core clock divider value");
|
|
#endif
|
|
static const scg_sys_clk_config_t scg_sys_clk_config = {
|
|
.divSlow = TO_SYS_CLK_DIV(SCG_CLOCK_DIV(slow_clk)),
|
|
.divBus = TO_SYS_CLK_DIV(SCG_CLOCK_DIV(bus_clk)),
|
|
.divCore = TO_SYS_CLK_DIV(SCG_CLOCK_DIV(core_clk)),
|
|
#if DT_SAME_NODE(DT_CLOCKS_CTLR(SCG_CLOCK_NODE(core_clk)), SCG_CLOCK_NODE(sosc_clk))
|
|
.src = kSCG_SysClkSrcSysOsc,
|
|
#elif DT_SAME_NODE(DT_CLOCKS_CTLR(SCG_CLOCK_NODE(core_clk)), SCG_CLOCK_NODE(sirc_clk))
|
|
.src = kSCG_SysClkSrcSirc,
|
|
#elif DT_SAME_NODE(DT_CLOCKS_CTLR(SCG_CLOCK_NODE(core_clk)), SCG_CLOCK_NODE(firc_clk))
|
|
.src = kSCG_SysClkSrcFirc,
|
|
#elif DT_SAME_NODE(DT_CLOCKS_CTLR(SCG_CLOCK_NODE(core_clk)), SCG_CLOCK_NODE(spll_clk))
|
|
.src = kSCG_SysClkSrcSysPll,
|
|
#else
|
|
#error Invalid SCG core clock source
|
|
#endif
|
|
};
|
|
|
|
#if DT_NODE_HAS_STATUS(SCG_CLOCK_NODE(sosc_clk), okay)
|
|
/* System Oscillator (SOSC) configuration */
|
|
ASSERT_ASYNC_CLK_DIV_VALID(SCG_CLOCK_DIV(soscdiv1_clk),
|
|
"Invalid SCG SOSC divider 1 value");
|
|
ASSERT_ASYNC_CLK_DIV_VALID(SCG_CLOCK_DIV(soscdiv2_clk),
|
|
"Invalid SCG SOSC divider 2 value");
|
|
static const scg_sosc_config_t scg_sosc_config = {
|
|
.freq = DT_PROP(SCG_CLOCK_NODE(sosc_clk), clock_frequency),
|
|
.monitorMode = kSCG_SysOscMonitorDisable,
|
|
.enableMode = kSCG_SysOscEnable | kSCG_SysOscEnableInLowPower,
|
|
.div1 = TO_ASYNC_CLK_DIV(SCG_CLOCK_DIV(soscdiv1_clk)),
|
|
.div2 = TO_ASYNC_CLK_DIV(SCG_CLOCK_DIV(soscdiv2_clk)),
|
|
.workMode = DT_PROP(DT_INST(0, nxp_kinetis_scg), sosc_mode)
|
|
};
|
|
#endif /* DT_NODE_HAS_PROP(DT_INST(0, nxp_kinetis_scg), sosc_freq) */
|
|
|
|
/* Slow Internal Reference Clock (SIRC) configuration */
|
|
ASSERT_ASYNC_CLK_DIV_VALID(SCG_CLOCK_DIV(sircdiv1_clk),
|
|
"Invalid SCG SIRC divider 1 value");
|
|
ASSERT_ASYNC_CLK_DIV_VALID(SCG_CLOCK_DIV(sircdiv2_clk),
|
|
"Invalid SCG SIRC divider 2 value");
|
|
static const scg_sirc_config_t scg_sirc_config = {
|
|
.enableMode = kSCG_SircEnable | kSCG_SircEnableInLowPower,
|
|
.div1 = TO_ASYNC_CLK_DIV(SCG_CLOCK_DIV(sircdiv1_clk)),
|
|
.div2 = TO_ASYNC_CLK_DIV(SCG_CLOCK_DIV(sircdiv2_clk)),
|
|
#if MHZ(2) == DT_PROP(SCG_CLOCK_NODE(sirc_clk), clock_frequency)
|
|
.range = kSCG_SircRangeLow
|
|
#elif MHZ(8) == DT_PROP(SCG_CLOCK_NODE(sirc_clk), clock_frequency)
|
|
.range = kSCG_SircRangeHigh
|
|
#else
|
|
#error Invalid SCG SIRC clock frequency
|
|
#endif
|
|
};
|
|
|
|
/* Fast Internal Reference Clock (FIRC) configuration */
|
|
ASSERT_ASYNC_CLK_DIV_VALID(SCG_CLOCK_DIV(fircdiv1_clk),
|
|
"Invalid SCG FIRC divider 1 value");
|
|
ASSERT_ASYNC_CLK_DIV_VALID(SCG_CLOCK_DIV(fircdiv2_clk),
|
|
"Invalid SCG FIRC divider 2 value");
|
|
static const scg_firc_config_t scg_firc_config = {
|
|
.enableMode = kSCG_FircEnable,
|
|
.div1 = TO_ASYNC_CLK_DIV(SCG_CLOCK_DIV(fircdiv1_clk)),
|
|
.div2 = TO_ASYNC_CLK_DIV(SCG_CLOCK_DIV(fircdiv2_clk)),
|
|
#if MHZ(48) == DT_PROP(SCG_CLOCK_NODE(firc_clk), clock_frequency)
|
|
.range = kSCG_FircRange48M,
|
|
#elif MHZ(52) == DT_PROP(SCG_CLOCK_NODE(firc_clk), clock_frequency)
|
|
.range = kSCG_FircRange52M,
|
|
#elif MHZ(56) == DT_PROP(SCG_CLOCK_NODE(firc_clk), clock_frequency)
|
|
.range = kSCG_FircRange56M,
|
|
#elif MHZ(60) == DT_PROP(SCG_CLOCK_NODE(firc_clk), clock_frequency)
|
|
.range = kSCG_FircRange60M,
|
|
#else
|
|
#error Invalid SCG FIRC clock frequency
|
|
#endif
|
|
.trimConfig = NULL
|
|
};
|
|
|
|
/* System Phase-Locked Loop (SPLL) configuration */
|
|
ASSERT_WITHIN_RANGE(SCG_CLOCK_DIV(spll_clk), 2, 2,
|
|
"Invalid SCG SPLL fixed divider value");
|
|
ASSERT_ASYNC_CLK_DIV_VALID(SCG_CLOCK_DIV(splldiv1_clk),
|
|
"Invalid SCG SPLL divider 1 value");
|
|
ASSERT_ASYNC_CLK_DIV_VALID(SCG_CLOCK_DIV(splldiv2_clk),
|
|
"Invalid SCG SPLL divider 2 value");
|
|
ASSERT_WITHIN_RANGE(SCG_CLOCK_DIV(pll), 1, 8,
|
|
"Invalid SCG PLL pre divider value");
|
|
ASSERT_WITHIN_RANGE(SCG_CLOCK_MULT(pll), 16, 47,
|
|
"Invalid SCG PLL multiplier value");
|
|
static const scg_spll_config_t scg_spll_config = {
|
|
.enableMode = kSCG_SysPllEnable,
|
|
.monitorMode = kSCG_SysPllMonitorDisable,
|
|
.div1 = TO_ASYNC_CLK_DIV(SCG_CLOCK_DIV(splldiv1_clk)),
|
|
.div2 = TO_ASYNC_CLK_DIV(SCG_CLOCK_DIV(splldiv2_clk)),
|
|
#if DT_SAME_NODE(DT_CLOCKS_CTLR(SCG_CLOCK_NODE(pll)), SCG_CLOCK_NODE(sosc_clk))
|
|
.src = kSCG_SysPllSrcSysOsc,
|
|
#elif DT_SAME_NODE(DT_CLOCKS_CTLR(SCG_CLOCK_NODE(pll)), SCG_CLOCK_NODE(firc_clk))
|
|
.src = kSCG_SysPllSrcFirc,
|
|
#else
|
|
#error Invalid SCG PLL clock source
|
|
#endif
|
|
.prediv = (SCG_CLOCK_DIV(pll) - 1U),
|
|
.mult = (SCG_CLOCK_MULT(pll) - 16U)
|
|
};
|
|
|
|
static ALWAYS_INLINE void clk_init(void)
|
|
{
|
|
const scg_sys_clk_config_t scg_sys_clk_config_safe = {
|
|
.divSlow = kSCG_SysClkDivBy4,
|
|
.divBus = kSCG_SysClkDivBy1,
|
|
.divCore = kSCG_SysClkDivBy1,
|
|
.src = kSCG_SysClkSrcSirc
|
|
};
|
|
scg_sys_clk_config_t current;
|
|
|
|
#if DT_NODE_HAS_STATUS(SCG_CLOCK_NODE(sosc_clk), okay)
|
|
/* Optionally initialize system oscillator */
|
|
CLOCK_InitSysOsc(&scg_sosc_config);
|
|
CLOCK_SetXtal0Freq(scg_sosc_config.freq);
|
|
#endif
|
|
/* Configure SIRC */
|
|
CLOCK_InitSirc(&scg_sirc_config);
|
|
|
|
/* Temporary switch to safe SIRC in order to configure FIRC */
|
|
CLOCK_SetRunModeSysClkConfig(&scg_sys_clk_config_safe);
|
|
do {
|
|
CLOCK_GetCurSysClkConfig(¤t);
|
|
} while (current.src != scg_sys_clk_config_safe.src);
|
|
CLOCK_InitFirc(&scg_firc_config);
|
|
|
|
/* Configure System PLL */
|
|
CLOCK_InitSysPll(&scg_spll_config);
|
|
|
|
/* Only RUN mode supported for now */
|
|
CLOCK_SetRunModeSysClkConfig(&scg_sys_clk_config);
|
|
do {
|
|
CLOCK_GetCurSysClkConfig(¤t);
|
|
} while (current.src != scg_sys_clk_config.src);
|
|
|
|
#if DT_NODE_HAS_STATUS(DT_NODELABEL(lpuart0), okay)
|
|
CLOCK_SetIpSrc(kCLOCK_Lpuart0,
|
|
DT_CLOCKS_CELL(DT_NODELABEL(lpuart0), ip_source));
|
|
#endif
|
|
#if DT_NODE_HAS_STATUS(DT_NODELABEL(lpuart1), okay)
|
|
CLOCK_SetIpSrc(kCLOCK_Lpuart1,
|
|
DT_CLOCKS_CELL(DT_NODELABEL(lpuart1), ip_source));
|
|
#endif
|
|
#if DT_NODE_HAS_STATUS(DT_NODELABEL(lpuart2), okay)
|
|
CLOCK_SetIpSrc(kCLOCK_Lpuart2,
|
|
DT_CLOCKS_CELL(DT_NODELABEL(lpuart2), ip_source));
|
|
#endif
|
|
#if DT_NODE_HAS_STATUS(DT_NODELABEL(lpi2c0), okay)
|
|
CLOCK_SetIpSrc(kCLOCK_Lpi2c0,
|
|
DT_CLOCKS_CELL(DT_NODELABEL(lpi2c0), ip_source));
|
|
#endif
|
|
#if DT_NODE_HAS_STATUS(DT_NODELABEL(lpi2c1), okay)
|
|
CLOCK_SetIpSrc(kCLOCK_Lpi2c1,
|
|
DT_CLOCKS_CELL(DT_NODELABEL(lpi2c1), ip_source));
|
|
#endif
|
|
#if DT_NODE_HAS_STATUS(DT_NODELABEL(lpspi0), okay)
|
|
CLOCK_SetIpSrc(kCLOCK_Lpspi0,
|
|
DT_CLOCKS_CELL(DT_NODELABEL(lpspi0), ip_source));
|
|
#endif
|
|
#if DT_NODE_HAS_STATUS(DT_NODELABEL(lpspi1), okay)
|
|
CLOCK_SetIpSrc(kCLOCK_Lpspi1,
|
|
DT_CLOCKS_CELL(DT_NODELABEL(lpspi1), ip_source));
|
|
#endif
|
|
#if DT_NODE_HAS_STATUS(DT_NODELABEL(adc0), okay)
|
|
CLOCK_SetIpSrc(kCLOCK_Adc0,
|
|
DT_CLOCKS_CELL(DT_NODELABEL(adc0), ip_source));
|
|
#endif
|
|
#if DT_NODE_HAS_STATUS(DT_NODELABEL(adc1), okay)
|
|
CLOCK_SetIpSrc(kCLOCK_Adc1,
|
|
DT_CLOCKS_CELL(DT_NODELABEL(adc1), ip_source));
|
|
#endif
|
|
#if DT_NODE_HAS_STATUS(DT_NODELABEL(adc2), okay)
|
|
CLOCK_SetIpSrc(kCLOCK_Adc2,
|
|
DT_CLOCKS_CELL(DT_NODELABEL(adc2), ip_source));
|
|
#endif
|
|
#if DT_NODE_HAS_STATUS(DT_NODELABEL(ftm0), okay)
|
|
CLOCK_SetIpSrc(kCLOCK_Ftm0,
|
|
DT_CLOCKS_CELL(DT_NODELABEL(ftm0), ip_source));
|
|
#endif
|
|
#if DT_NODE_HAS_STATUS(DT_NODELABEL(ftm1), okay)
|
|
CLOCK_SetIpSrc(kCLOCK_Ftm1,
|
|
DT_CLOCKS_CELL(DT_NODELABEL(ftm1), ip_source));
|
|
#endif
|
|
#if DT_NODE_HAS_STATUS(DT_NODELABEL(ftm2), okay)
|
|
CLOCK_SetIpSrc(kCLOCK_Ftm2,
|
|
DT_CLOCKS_CELL(DT_NODELABEL(ftm2), ip_source));
|
|
#endif
|
|
#if DT_NODE_HAS_STATUS(DT_NODELABEL(ftm3), okay)
|
|
CLOCK_SetIpSrc(kCLOCK_Ftm3,
|
|
DT_CLOCKS_CELL(DT_NODELABEL(ftm3), ip_source));
|
|
#endif
|
|
}
|
|
|
|
static int ke1xf_init(const struct device *arg)
|
|
|
|
{
|
|
ARG_UNUSED(arg);
|
|
|
|
unsigned int old_level; /* old interrupt lock level */
|
|
#if !defined(CONFIG_ARM_MPU)
|
|
uint32_t temp_reg;
|
|
#endif /* !CONFIG_ARM_MPU */
|
|
|
|
/* Disable interrupts */
|
|
old_level = irq_lock();
|
|
|
|
#if !defined(CONFIG_ARM_MPU)
|
|
/*
|
|
* Disable memory protection and clear slave port errors.
|
|
* Note that the KE1xF does not implement the optional ARMv7-M memory
|
|
* protection unit (MPU), specified by the architecture (PMSAv7), in the
|
|
* Cortex-M4 core. Instead, the processor includes its own MPU module.
|
|
*/
|
|
temp_reg = SYSMPU->CESR;
|
|
temp_reg &= ~SYSMPU_CESR_VLD_MASK;
|
|
temp_reg |= SYSMPU_CESR_SPERR_MASK;
|
|
SYSMPU->CESR = temp_reg;
|
|
#endif /* !CONFIG_ARM_MPU */
|
|
|
|
/* Initialize system clocks and PLL */
|
|
clk_init();
|
|
|
|
/*
|
|
* Install default handler that simply resets the CPU if
|
|
* configured in the kernel, NOP otherwise
|
|
*/
|
|
NMI_INIT();
|
|
|
|
#ifdef CONFIG_KINETIS_KE1XF_ENABLE_CODE_CACHE
|
|
L1CACHE_EnableCodeCache();
|
|
#endif
|
|
/* Restore interrupt state */
|
|
irq_unlock(old_level);
|
|
|
|
return 0;
|
|
}
|
|
|
|
void z_arm_watchdog_init(void)
|
|
{
|
|
/*
|
|
* NOTE: DO NOT SINGLE STEP THROUGH THIS FUNCTION!!! Watchdog
|
|
* reconfiguration must take place within 128 bus clocks from
|
|
* unlocking. Single stepping through the code will cause the
|
|
* watchdog to close the unlock window again.
|
|
*/
|
|
|
|
/* Unlock watchdog to enable reconfiguration after bootloader */
|
|
WDOG->CNT = WDOG_UPDATE_KEY;
|
|
while (!(WDOG->CS & WDOG_CS_ULK_MASK)) {
|
|
;
|
|
}
|
|
|
|
/*
|
|
* Watchdog reconfiguration only takes effect after writing to
|
|
* both TOVAL and CS registers.
|
|
*/
|
|
#ifdef CONFIG_WDOG_ENABLE_AT_BOOT
|
|
WDOG->TOVAL = CONFIG_WDOG_INITIAL_TIMEOUT >> 1;
|
|
WDOG->CS = WDOG_CS_PRES(1) | WDOG_CS_CLK(1) | WDOG_CS_WAIT(1) |
|
|
WDOG_CS_EN(1) | WDOG_CS_UPDATE(1);
|
|
#else /* !CONFIG_WDOG_ENABLE_AT_BOOT */
|
|
WDOG->TOVAL = 1024;
|
|
WDOG->CS = WDOG_CS_EN(0) | WDOG_CS_UPDATE(1);
|
|
#endif /* !CONFIG_WDOG_ENABLE_AT_BOOT */
|
|
|
|
while (!(WDOG->CS & WDOG_CS_RCS_MASK)) {
|
|
;
|
|
}
|
|
}
|
|
|
|
SYS_INIT(ke1xf_init, PRE_KERNEL_1, 0);
|