From 3892cccbc5137b4a150eb4db9d143fc589367c86 Mon Sep 17 00:00:00 2001 From: robert-hh Date: Mon, 21 Aug 2023 08:14:27 +0200 Subject: [PATCH] mimxrt: Add a machine.freq(x) method. For i.MX RT 101x, 102x, 105x, 106x: Just for three selected frequencies base_freq/1, /2 and /4. For i.MX RT 117x: Any frequency between 156 MHZ and 996 MHz. At /4 the power consumption is like 30% decreased for teensy boards and way less for other boards, since bus & peripherals still run at full speed. Signed-off-by: robert-hh --- ports/mimxrt/Makefile | 6 +- .../boards/ADAFRUIT_METRO_M7/mpconfigboard.mk | 1 + .../boards/MIMXRT1010_EVK/mpconfigboard.mk | 1 + .../boards/MIMXRT1015_EVK/mpconfigboard.mk | 1 + .../boards/MIMXRT1020_EVK/mpconfigboard.mk | 1 + .../boards/MIMXRT1050_EVK/mpconfigboard.mk | 1 + .../boards/MIMXRT1060_EVK/mpconfigboard.mk | 1 + .../boards/MIMXRT1064_EVK/mpconfigboard.mk | 1 + .../boards/OLIMEX_RT1010/mpconfigboard.mk | 1 + .../boards/SEEED_ARCH_MIX/mpconfigboard.mk | 1 + ports/mimxrt/boards/TEENSY40/mpconfigboard.mk | 1 + ports/mimxrt/boards/TEENSY41/mpconfigboard.mk | 1 + ports/mimxrt/hal/fsl_clock_MIMXRT1176.c | 1904 +++++++++++++++++ ports/mimxrt/modmachine.c | 81 +- ports/mimxrt/mphalport.h | 11 + 15 files changed, 2009 insertions(+), 4 deletions(-) create mode 100644 ports/mimxrt/hal/fsl_clock_MIMXRT1176.c diff --git a/ports/mimxrt/Makefile b/ports/mimxrt/Makefile index 8cb46245aec97..d471a4af999c4 100644 --- a/ports/mimxrt/Makefile +++ b/ports/mimxrt/Makefile @@ -122,8 +122,8 @@ endif # NXP SDK sources SRC_HAL_IMX_C += \ - $(MCU_DIR)/drivers/fsl_clock.c \ $(MCU_DIR)/drivers/fsl_common.c \ + $(MCU_DIR)/drivers/fsl_dcdc.c \ $(MCU_DIR)/drivers/fsl_dmamux.c \ $(MCU_DIR)/drivers/fsl_edma.c \ $(MCU_DIR)/drivers/fsl_flexram.c \ @@ -165,6 +165,9 @@ endif ifeq ($(MCU_SERIES), MIMXRT1176) INC += -I$(TOP)/$(MCU_DIR)/drivers/cm7 +SRC_C += \ + hal/fsl_clock_MIMXRT1176.c + SRC_HAL_IMX_C += \ $(MCU_DIR)/drivers/cm7/fsl_cache.c \ $(MCU_DIR)/drivers/fsl_dcdc.c \ @@ -177,6 +180,7 @@ else SRC_HAL_IMX_C += \ $(MCU_DIR)/drivers/fsl_adc.c \ $(MCU_DIR)/drivers/fsl_cache.c \ + $(MCU_DIR)/drivers/fsl_clock.c \ $(MCU_DIR)/drivers/fsl_trng.c endif diff --git a/ports/mimxrt/boards/ADAFRUIT_METRO_M7/mpconfigboard.mk b/ports/mimxrt/boards/ADAFRUIT_METRO_M7/mpconfigboard.mk index 236db27c87db3..7e3ee970cb308 100644 --- a/ports/mimxrt/boards/ADAFRUIT_METRO_M7/mpconfigboard.mk +++ b/ports/mimxrt/boards/ADAFRUIT_METRO_M7/mpconfigboard.mk @@ -1,5 +1,6 @@ MCU_SERIES = MIMXRT1011 MCU_VARIANT = MIMXRT1011DAE5A +CFLAGS += -DMIMXRT101x_SERIES MICROPY_FLOAT_IMPL = single MICROPY_PY_MACHINE_SDCARD = 0 diff --git a/ports/mimxrt/boards/MIMXRT1010_EVK/mpconfigboard.mk b/ports/mimxrt/boards/MIMXRT1010_EVK/mpconfigboard.mk index 19db53c3f8710..32fed4b620598 100644 --- a/ports/mimxrt/boards/MIMXRT1010_EVK/mpconfigboard.mk +++ b/ports/mimxrt/boards/MIMXRT1010_EVK/mpconfigboard.mk @@ -1,5 +1,6 @@ MCU_SERIES = MIMXRT1011 MCU_VARIANT = MIMXRT1011DAE5A +CFLAGS += -DMIMXRT101x_SERIES MICROPY_FLOAT_IMPL = single MICROPY_PY_MACHINE_SDCARD = 0 diff --git a/ports/mimxrt/boards/MIMXRT1015_EVK/mpconfigboard.mk b/ports/mimxrt/boards/MIMXRT1015_EVK/mpconfigboard.mk index ba7d61f6d4f0f..81e928ed914b3 100644 --- a/ports/mimxrt/boards/MIMXRT1015_EVK/mpconfigboard.mk +++ b/ports/mimxrt/boards/MIMXRT1015_EVK/mpconfigboard.mk @@ -1,5 +1,6 @@ MCU_SERIES = MIMXRT1015 MCU_VARIANT = MIMXRT1015DAF5A +CFLAGS += -DMIMXRT101x_SERIES MICROPY_FLOAT_IMPL = single MICROPY_PY_MACHINE_SDCARD = 0 diff --git a/ports/mimxrt/boards/MIMXRT1020_EVK/mpconfigboard.mk b/ports/mimxrt/boards/MIMXRT1020_EVK/mpconfigboard.mk index 547b88d57de43..5750c77db6480 100644 --- a/ports/mimxrt/boards/MIMXRT1020_EVK/mpconfigboard.mk +++ b/ports/mimxrt/boards/MIMXRT1020_EVK/mpconfigboard.mk @@ -1,5 +1,6 @@ MCU_SERIES = MIMXRT1021 MCU_VARIANT = MIMXRT1021DAG5A +CFLAGS += -DMIMXRT102x_SERIES MICROPY_FLOAT_IMPL = double MICROPY_PY_MACHINE_SDCARD = 1 diff --git a/ports/mimxrt/boards/MIMXRT1050_EVK/mpconfigboard.mk b/ports/mimxrt/boards/MIMXRT1050_EVK/mpconfigboard.mk index 0cf2a348046e2..9bdcb86066a68 100644 --- a/ports/mimxrt/boards/MIMXRT1050_EVK/mpconfigboard.mk +++ b/ports/mimxrt/boards/MIMXRT1050_EVK/mpconfigboard.mk @@ -1,5 +1,6 @@ MCU_SERIES = MIMXRT1052 MCU_VARIANT = MIMXRT1052DVL6B +CFLAGS += -DMIMXRT105x_SERIES MICROPY_FLOAT_IMPL = double MICROPY_PY_MACHINE_SDCARD = 1 diff --git a/ports/mimxrt/boards/MIMXRT1060_EVK/mpconfigboard.mk b/ports/mimxrt/boards/MIMXRT1060_EVK/mpconfigboard.mk index c2556a27248ac..93679c3094f75 100644 --- a/ports/mimxrt/boards/MIMXRT1060_EVK/mpconfigboard.mk +++ b/ports/mimxrt/boards/MIMXRT1060_EVK/mpconfigboard.mk @@ -1,5 +1,6 @@ MCU_SERIES = MIMXRT1062 MCU_VARIANT = MIMXRT1062DVJ6A +CFLAGS += -DMIMXRT106x_SERIES MICROPY_FLOAT_IMPL = double MICROPY_PY_MACHINE_SDCARD = 1 diff --git a/ports/mimxrt/boards/MIMXRT1064_EVK/mpconfigboard.mk b/ports/mimxrt/boards/MIMXRT1064_EVK/mpconfigboard.mk index d3ba752419e0a..a0a2ef019457f 100644 --- a/ports/mimxrt/boards/MIMXRT1064_EVK/mpconfigboard.mk +++ b/ports/mimxrt/boards/MIMXRT1064_EVK/mpconfigboard.mk @@ -1,5 +1,6 @@ MCU_SERIES = MIMXRT1064 MCU_VARIANT = MIMXRT1064DVL6A +CFLAGS += -DMIMXRT106x_SERIES MICROPY_FLOAT_IMPL = double MICROPY_PY_MACHINE_SDCARD = 1 diff --git a/ports/mimxrt/boards/OLIMEX_RT1010/mpconfigboard.mk b/ports/mimxrt/boards/OLIMEX_RT1010/mpconfigboard.mk index 135c43257c95e..620f7d4d4ebde 100644 --- a/ports/mimxrt/boards/OLIMEX_RT1010/mpconfigboard.mk +++ b/ports/mimxrt/boards/OLIMEX_RT1010/mpconfigboard.mk @@ -1,5 +1,6 @@ MCU_SERIES = MIMXRT1011 MCU_VARIANT = MIMXRT1011DAE5A +CFLAGS += -DMIMXRT101x_SERIES MICROPY_FLOAT_IMPL = single MICROPY_PY_MACHINE_SDCARD ?= 0 diff --git a/ports/mimxrt/boards/SEEED_ARCH_MIX/mpconfigboard.mk b/ports/mimxrt/boards/SEEED_ARCH_MIX/mpconfigboard.mk index ca27dff55f3a2..2b14cffd9720d 100644 --- a/ports/mimxrt/boards/SEEED_ARCH_MIX/mpconfigboard.mk +++ b/ports/mimxrt/boards/SEEED_ARCH_MIX/mpconfigboard.mk @@ -1,5 +1,6 @@ MCU_SERIES = MIMXRT1052 MCU_VARIANT = MIMXRT1052DVL6B +CFLAGS += -DMIMXRT105x_SERIES MICROPY_FLOAT_IMPL = double MICROPY_PY_MACHINE_SDCARD = 1 diff --git a/ports/mimxrt/boards/TEENSY40/mpconfigboard.mk b/ports/mimxrt/boards/TEENSY40/mpconfigboard.mk index 4482c629f7dcc..2fcafc912ac95 100644 --- a/ports/mimxrt/boards/TEENSY40/mpconfigboard.mk +++ b/ports/mimxrt/boards/TEENSY40/mpconfigboard.mk @@ -1,5 +1,6 @@ MCU_SERIES = MIMXRT1062 MCU_VARIANT = MIMXRT1062DVJ6A +CFLAGS += -DMIMXRT106x_SERIES MICROPY_FLOAT_IMPL = double MICROPY_PY_MACHINE_SDCARD = 1 diff --git a/ports/mimxrt/boards/TEENSY41/mpconfigboard.mk b/ports/mimxrt/boards/TEENSY41/mpconfigboard.mk index cf07144668e15..3dacae6c753b3 100755 --- a/ports/mimxrt/boards/TEENSY41/mpconfigboard.mk +++ b/ports/mimxrt/boards/TEENSY41/mpconfigboard.mk @@ -1,5 +1,6 @@ MCU_SERIES = MIMXRT1062 MCU_VARIANT = MIMXRT1062DVJ6A +CFLAGS += -DMIMXRT106x_SERIES MICROPY_FLOAT_IMPL = double MICROPY_PY_MACHINE_SDCARD = 1 diff --git a/ports/mimxrt/hal/fsl_clock_MIMXRT1176.c b/ports/mimxrt/hal/fsl_clock_MIMXRT1176.c new file mode 100644 index 0000000000000..da60442c94572 --- /dev/null +++ b/ports/mimxrt/hal/fsl_clock_MIMXRT1176.c @@ -0,0 +1,1904 @@ +/* + * Copyright 2019 - 2021 NXP + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include "fsl_clock.h" +#include "fsl_pmu.h" +#include "fsl_anatop_ai.h" +/* Component ID definition, used by tools. */ +#ifndef FSL_COMPONENT_ID +#define FSL_COMPONENT_ID "platform.drivers.clock" +#endif + +/******************************************************************************* + * Definitions + ******************************************************************************/ +/* To make full use of CM7 hardware FPU, use double instead of uint64_t in clock driver to +achieve better performance, it is depend on the IDE Floating point settings, if double precision is selected +in IDE, clock_64b_t will switch to double type automatically. only support IAR and MDK here */ +#if __FPU_USED + +#if (defined(__ICCARM__)) + +#if (__ARMVFP__ >= __ARMFPV5__) && \ + (__ARM_FP == 0xE) /*0xe implies support for half, single and double precision operations*/ +typedef double clock_64b_t; +#else +typedef uint64_t clock_64b_t; +#endif + +#elif (defined(__GNUC__)) + +#if (__ARM_FP == 0xE) /*0xe implies support for half, single and double precision operations*/ +typedef double clock_64b_t; +#else +typedef uint64_t clock_64b_t; +#endif + +#elif defined(__CC_ARM) || defined(__ARMCC_VERSION) + +#if defined __TARGET_FPU_FPV5_D16 +typedef double clock_64b_t; +#else +typedef uint64_t clock_64b_t; +#endif + +#else +typedef uint64_t clock_64b_t; +#endif + +#else +typedef uint64_t clock_64b_t; +#endif + +#define PLL_AI_CTRL0_REG kAI_PLL1G_CTRL0 +#define PLL_AI_CTRL0_SET_REG kAI_PLL1G_CTRL0_SET +#define PLL_AI_CTRL0_CLR_REG kAI_PLL1G_CTRL0_CLR +#define PLL_AI_CTRL1_REG kAI_PLL1G_CTRL1 +#define PLL_AI_CTRL1_SET_REG kAI_PLL1G_CTRL1_SET +#define PLL_AI_CTRL1_CLR_REG kAI_PLL1G_CTRL1_CLR +#define PLL_AI_CTRL2_REG kAI_PLL1G_CTRL2 +#define PLL_AI_CTRL2_SET_REG kAI_PLL1G_CTRL2_SET +#define PLL_AI_CTRL2_CLR_REG kAI_PLL1G_CTRL2_CLR +#define PLL_AI_CTRL3_REG kAI_PLL1G_CTRL3 +#define PLL_AI_CTRL3_SET_REG kAI_PLL1G_CTRL3_SET +#define PLL_AI_CTRL3_CLR_REG kAI_PLL1G_CTRL3_CLR + +#define PLL_AI_CTRL0_HOLD_RING_OFF_MASK AI_PLL1G_CTRL0_HOLD_RING_OFF_MASK + +#define PLL_AI_CTRL0_POWER_UP_MASK AI_PLL1G_CTRL0_POWER_UP_MASK + +#define PLL_AI_CTRL0_ENABLE_MASK AI_PLL1G_CTRL0_ENABLE_MASK + +#define PLL_AI_CTRL0_BYPASS_MASK AI_PLL1G_CTRL0_BYPASS_MASK + +#define PLL_AI_CTRL0_PLL_REG_EN_MASK AI_PLL1G_CTRL0_PLL_REG_EN_MASK +/******************************************************************************* + * Variables + ******************************************************************************/ + +/******************************************************************************* + * Prototypes + ******************************************************************************/ +static void ANATOP_PllSetPower(anatop_ai_itf_t itf, bool enable); +static void ANATOP_PllBypass(anatop_ai_itf_t itf, bool bypass); +static void ANATOP_PllEnablePllReg(anatop_ai_itf_t itf, bool enable); +static void ANATOP_PllHoldRingOff(anatop_ai_itf_t itf, bool off); +static void ANATOP_PllToggleHoldRingOff(anatop_ai_itf_t itf, uint32_t delay_us); +static void ANATOP_PllEnableClk(anatop_ai_itf_t itf, bool enable); +static void ANATOP_PllConfigure(anatop_ai_itf_t itf, + uint8_t div, + uint32_t numer, + uint8_t post_div, + uint32_t denom, + const clock_pll_ss_config_t *ss); +static void ANATOP_AudioPllGate(bool enable); +static void ANATOP_AudioPllSwEnClk(bool enable); +static void ANATOP_VideoPllGate(bool enable); +static void ANATOP_VideoPllSwEnClk(bool enable); +static void ANATOP_SysPll1Gate(bool enable); +static void ANATOP_SysPll1Div2En(bool enable); +static void ANATOP_SysPll1Div5En(bool enable); +static void ANATOP_SysPll1SwEnClk(bool enable); +static void ANATOP_SysPll1WaitStable(void); +static void ANATOP_PllEnableSs(anatop_ai_itf_t itf, bool enable); +#ifndef GET_FREQ_FROM_OBS +static uint32_t CLOCK_GetAvPllFreq(clock_pll_t pll); +#endif +/******************************************************************************* + * Code + ******************************************************************************/ + +#define ARM_PLL_DIV_SEL_MIN 104 +#define ARM_PLL_DIV_SEL_MAX 208 + +void CLOCK_InitArmPll(const clock_arm_pll_config_t *config) +{ + assert((config->loopDivider <= (uint32_t)ARM_PLL_DIV_SEL_MAX) && + (config->loopDivider >= (uint32_t)ARM_PLL_DIV_SEL_MIN)); + + uint32_t reg; + + if (((ANADIG_PLL->ARM_PLL_CTRL & ANADIG_PLL_ARM_PLL_CTRL_POWERUP_MASK) != 0UL) && + ((ANADIG_PLL->ARM_PLL_CTRL & ANADIG_PLL_ARM_PLL_CTRL_DIV_SELECT_MASK) == + ANADIG_PLL_ARM_PLL_CTRL_DIV_SELECT(config->loopDivider)) && + ((ANADIG_PLL->ARM_PLL_CTRL & ANADIG_PLL_ARM_PLL_CTRL_POST_DIV_SEL_MASK) == + ANADIG_PLL_ARM_PLL_CTRL_POST_DIV_SEL(config->postDivider))) + { + /* no need to reconfigure the PLL if all the configuration is the same */ + if ((ANADIG_PLL->ARM_PLL_CTRL & ANADIG_PLL_ARM_PLL_CTRL_ENABLE_CLK_MASK) == 0UL) + { + ANADIG_PLL->ARM_PLL_CTRL |= ANADIG_PLL_ARM_PLL_CTRL_ENABLE_CLK_MASK; + } + if ((ANADIG_PLL->ARM_PLL_CTRL & ANADIG_PLL_ARM_PLL_CTRL_ARM_PLL_GATE_MASK) != 0UL) + { + ANADIG_PLL->ARM_PLL_CTRL &= ~ANADIG_PLL_ARM_PLL_CTRL_ARM_PLL_GATE_MASK; + } + return; + } + + PMU_StaticEnablePllLdo(ANADIG_PMU); + + reg = ANADIG_PLL->ARM_PLL_CTRL & (~ANADIG_PLL_ARM_PLL_CTRL_ARM_PLL_STABLE_MASK); + if ((reg & (ANADIG_PLL_ARM_PLL_CTRL_POWERUP_MASK | ANADIG_PLL_ARM_PLL_CTRL_ENABLE_CLK_MASK)) != 0UL) + { + /* Power down the PLL. */ + reg &= ~(ANADIG_PLL_ARM_PLL_CTRL_POWERUP_MASK | ANADIG_PLL_ARM_PLL_CTRL_ENABLE_CLK_MASK); + reg |= ANADIG_PLL_ARM_PLL_CTRL_ARM_PLL_GATE_MASK; + ANADIG_PLL->ARM_PLL_CTRL = reg; + } + + /* Set the configuration. */ + reg &= ~(ANADIG_PLL_ARM_PLL_CTRL_DIV_SELECT_MASK | ANADIG_PLL_ARM_PLL_CTRL_POST_DIV_SEL_MASK); + reg |= (ANADIG_PLL_ARM_PLL_CTRL_DIV_SELECT(config->loopDivider) | + ANADIG_PLL_ARM_PLL_CTRL_POST_DIV_SEL(config->postDivider)) | + ANADIG_PLL_ARM_PLL_CTRL_ARM_PLL_GATE_MASK | ANADIG_PLL_ARM_PLL_CTRL_POWERUP_MASK; + ANADIG_PLL->ARM_PLL_CTRL = reg; + __DSB(); + __ISB(); + SDK_DelayAtLeastUs(30, SDK_DEVICE_MAXIMUM_CPU_CLOCK_FREQUENCY); + + /* Wait for the PLL stable, */ + while (0U == (ANADIG_PLL->ARM_PLL_CTRL & ANADIG_PLL_ARM_PLL_CTRL_ARM_PLL_STABLE_MASK)) + { + } + + /* Enable and ungate the clock. */ + reg |= ANADIG_PLL_ARM_PLL_CTRL_ENABLE_CLK_MASK; + reg &= ~ANADIG_PLL_ARM_PLL_CTRL_ARM_PLL_GATE_MASK; + ANADIG_PLL->ARM_PLL_CTRL = reg; +} + +void CLOCK_DeinitArmPll(void) +{ + uint32_t reg = ANADIG_PLL->ARM_PLL_CTRL & (~ANADIG_PLL_ARM_PLL_CTRL_ARM_PLL_STABLE_MASK); + + reg &= ~(ANADIG_PLL_ARM_PLL_CTRL_POWERUP_MASK | ANADIG_PLL_ARM_PLL_CTRL_ENABLE_CLK_MASK); + reg |= ANADIG_PLL_ARM_PLL_CTRL_ARM_PLL_GATE_MASK; + ANADIG_PLL->ARM_PLL_CTRL = reg; +} + +void CLOCK_CalcPllSpreadSpectrum(uint32_t factor, uint32_t range, uint32_t mod, clock_pll_ss_config_t *ss) +{ + assert(ss != NULL); + + ss->stop = (uint16_t)(factor * range / XTAL_FREQ); + ss->step = (uint16_t)((mod << 1UL) * (uint32_t)(ss->stop) / XTAL_FREQ); +} + +/* 528PLL */ +void CLOCK_InitSysPll2(const clock_sys_pll2_config_t *config) +{ + uint32_t reg; + + if ((ANADIG_PLL->SYS_PLL2_CTRL & ANADIG_PLL_SYS_PLL2_CTRL_POWERUP_MASK) != 0UL) + { + if ((config == NULL) || + ((0UL == (ANADIG_PLL->SYS_PLL2_SS & ANADIG_PLL_SYS_PLL2_SS_ENABLE_MASK)) && (!config->ssEnable)) || + ((ANADIG_PLL_SYS_PLL2_SS_ENABLE(config->ssEnable) | ANADIG_PLL_SYS_PLL2_SS_STOP(config->ss->stop) | + ANADIG_PLL_SYS_PLL2_SS_STEP(config->ss->step)) == ANADIG_PLL->SYS_PLL2_SS)) + { + /* no need to reconfigure the PLL if all the configuration is the same */ + if (0UL == (ANADIG_PLL->SYS_PLL2_CTRL & ANADIG_PLL_SYS_PLL2_CTRL_ENABLE_CLK_MASK)) + { + ANADIG_PLL->SYS_PLL2_CTRL |= ANADIG_PLL_SYS_PLL2_CTRL_ENABLE_CLK_MASK; + } + + if ((ANADIG_PLL->SYS_PLL2_CTRL & ANADIG_PLL_SYS_PLL2_CTRL_SYS_PLL2_GATE_MASK) != 0UL) + { + ANADIG_PLL->SYS_PLL2_CTRL &= ~ANADIG_PLL_SYS_PLL2_CTRL_SYS_PLL2_GATE_MASK; + } + return; + } + } + + PMU_StaticEnablePllLdo(ANADIG_PMU); + /* Gate all PFDs */ + ANADIG_PLL->SYS_PLL2_PFD |= + ANADIG_PLL_SYS_PLL2_PFD_PFD0_DIV1_CLKGATE(1) | ANADIG_PLL_SYS_PLL2_PFD_PFD1_DIV1_CLKGATE(1) | + ANADIG_PLL_SYS_PLL2_PFD_PFD2_DIV1_CLKGATE(1) | ANADIG_PLL_SYS_PLL2_PFD_PFD3_DIV1_CLKGATE(1); + + reg = ANADIG_PLL->SYS_PLL2_CTRL; + if ((reg & (ANADIG_PLL_SYS_PLL2_CTRL_POWERUP_MASK | ANADIG_PLL_SYS_PLL2_CTRL_ENABLE_CLK_MASK)) != 0UL) + { + /* Power down the PLL. */ + reg &= ~(ANADIG_PLL_SYS_PLL2_CTRL_POWERUP_MASK | ANADIG_PLL_SYS_PLL2_CTRL_ENABLE_CLK_MASK); + reg |= ANADIG_PLL_SYS_PLL2_CTRL_SYS_PLL2_GATE_MASK; + ANADIG_PLL->SYS_PLL2_CTRL = reg; + } + + /* Config PLL */ + if ((config != NULL) && (config->ssEnable) && (config->ss != NULL)) + { + ANADIG_PLL->SYS_PLL2_MFD = ANADIG_PLL_SYS_PLL2_MFD_MFD(config->mfd); + ANADIG_PLL->SYS_PLL2_SS = (ANADIG_PLL_SYS_PLL2_SS_ENABLE_MASK | ANADIG_PLL_SYS_PLL2_SS_STOP(config->ss->stop) | + ANADIG_PLL_SYS_PLL2_SS_STEP(config->ss->step)); + } + + /* REG_EN = 1, GATE = 1, DIV_SEL = 0, POWERUP = 0 */ + reg = ANADIG_PLL_SYS_PLL2_CTRL_PLL_REG_EN(1) | ANADIG_PLL_SYS_PLL2_CTRL_SYS_PLL2_GATE(1); + ANADIG_PLL->SYS_PLL2_CTRL = reg; + /* Wait until LDO is stable */ + SDK_DelayAtLeastUs(30, SDK_DEVICE_MAXIMUM_CPU_CLOCK_FREQUENCY); + + /* REG_EN = 1, GATE = 1, DIV_SEL = 0, POWERUP = 1, HOLDRING_OFF = 1 */ + reg |= ANADIG_PLL_SYS_PLL2_CTRL_POWERUP(1) | ANADIG_PLL_SYS_PLL2_CTRL_HOLD_RING_OFF_MASK; + ANADIG_PLL->SYS_PLL2_CTRL = reg; + SDK_DelayAtLeastUs(250, SDK_DEVICE_MAXIMUM_CPU_CLOCK_FREQUENCY); + + /* REG_EN = 1, GATE = 1, DIV_SEL = 0, POWERUP = 1, HOLDRING_OFF = 0 */ + reg &= ~ANADIG_PLL_SYS_PLL2_CTRL_HOLD_RING_OFF_MASK; + ANADIG_PLL->SYS_PLL2_CTRL = reg; + /* Wait for PLL stable */ + while (ANADIG_PLL_SYS_PLL2_CTRL_SYS_PLL2_STABLE_MASK != + (ANADIG_PLL->SYS_PLL2_CTRL & ANADIG_PLL_SYS_PLL2_CTRL_SYS_PLL2_STABLE_MASK)) + { + } + + /* REG_EN = 1, GATE = 1, DIV_SEL = 0, POWERUP = 1, HOLDRING_OFF = 0, CLK = 1*/ + reg |= ANADIG_PLL_SYS_PLL2_CTRL_ENABLE_CLK_MASK; + ANADIG_PLL->SYS_PLL2_CTRL = reg; + + /* REG_EN = 1, GATE = 0, DIV_SEL = 0, POWERUP = 1, HOLDRING_OFF = 0, CLK = 1*/ + reg &= ~ANADIG_PLL_SYS_PLL2_CTRL_SYS_PLL2_GATE_MASK; + ANADIG_PLL->SYS_PLL2_CTRL = reg; + ANADIG_PLL->SYS_PLL2_PFD &= + ~(ANADIG_PLL_SYS_PLL2_PFD_PFD0_DIV1_CLKGATE(1) | ANADIG_PLL_SYS_PLL2_PFD_PFD1_DIV1_CLKGATE(1) | + ANADIG_PLL_SYS_PLL2_PFD_PFD2_DIV1_CLKGATE(1) | ANADIG_PLL_SYS_PLL2_PFD_PFD3_DIV1_CLKGATE(1)); +} + +void CLOCK_DeinitSysPll2(void) +{ + ANADIG_PLL->SYS_PLL2_PFD |= + ANADIG_PLL_SYS_PLL2_PFD_PFD0_DIV1_CLKGATE(1) | ANADIG_PLL_SYS_PLL2_PFD_PFD1_DIV1_CLKGATE(1) | + ANADIG_PLL_SYS_PLL2_PFD_PFD2_DIV1_CLKGATE(1) | ANADIG_PLL_SYS_PLL2_PFD_PFD3_DIV1_CLKGATE(1); + + ANADIG_PLL->SYS_PLL2_CTRL |= ANADIG_PLL_SYS_PLL2_CTRL_SYS_PLL2_GATE_MASK; + ANADIG_PLL->SYS_PLL2_CTRL &= ~(ANADIG_PLL_SYS_PLL2_CTRL_ENABLE_CLK_MASK | ANADIG_PLL_SYS_PLL2_CTRL_POWERUP_MASK | + ANADIG_PLL_SYS_PLL2_CTRL_PLL_REG_EN_MASK); + + ANADIG_PLL->SYS_PLL2_SS &= ~ANADIG_PLL_SYS_PLL2_SS_ENABLE_MASK; +} + +/*! + * brief Check if Sys PLL2 PFD is enabled + * + * param pfd PFD control name + * return PFD bypass status. + * - true: power on. + * - false: power off. + */ +bool CLOCK_IsSysPll2PfdEnabled(clock_pfd_t pfd) +{ + return ((ANADIG_PLL->SYS_PLL2_PFD & (uint32_t)ANADIG_PLL_SYS_PLL2_PFD_PFD0_DIV1_CLKGATE_MASK + << (8UL * (uint8_t)pfd)) == 0U); +} + +#define PFD_FRAC_MIN 12U +#define PFD_FRAC_MAX 35U +void CLOCK_InitPfd(clock_pll_t pll, clock_pfd_t pfd, uint8_t frac) +{ + volatile uint32_t *pfdCtrl = NULL, *pfdUpdate = NULL, stable; + uint32_t regFracVal; + bool pfdGated; + + assert(frac <= (uint8_t)PFD_FRAC_MAX); + assert(frac >= (uint8_t)PFD_FRAC_MIN); + + switch (pll) + { + case kCLOCK_PllSys2: + pfdCtrl = &ANADIG_PLL->SYS_PLL2_PFD; + pfdUpdate = &ANADIG_PLL->SYS_PLL2_UPDATE; + break; + case kCLOCK_PllSys3: + pfdCtrl = &ANADIG_PLL->SYS_PLL3_PFD; + pfdUpdate = &ANADIG_PLL->SYS_PLL3_UPDATE; + break; + default: + /* Wrong input parameter pll. */ + assert(false); + break; + } + regFracVal = ((*pfdCtrl) & ((uint32_t)ANADIG_PLL_SYS_PLL2_PFD_PFD0_FRAC_MASK << (8UL * (uint32_t)pfd))) >> + (8UL * (uint32_t)pfd); + pfdGated = + (bool)(((*pfdCtrl) & ((uint32_t)ANADIG_PLL_SYS_PLL2_PFD_PFD0_DIV1_CLKGATE_MASK << (8UL * (uint32_t)pfd))) >> + (8UL >> (uint32_t)pfd)); + if ((regFracVal == (uint32_t)frac) && (!pfdGated)) + { + return; + } + + stable = *pfdCtrl & ((uint32_t)ANADIG_PLL_SYS_PLL2_PFD_PFD0_STABLE_MASK << (8UL * (uint32_t)pfd)); + *pfdCtrl |= ((uint32_t)ANADIG_PLL_SYS_PLL2_PFD_PFD0_DIV1_CLKGATE_MASK << (8UL * (uint32_t)pfd)); + + /* all pfds support to be updated on-the-fly after corresponding PLL is stable */ + *pfdCtrl &= ~((uint32_t)ANADIG_PLL_SYS_PLL2_PFD_PFD0_FRAC_MASK << (8UL * (uint32_t)pfd)); + *pfdCtrl |= (ANADIG_PLL_SYS_PLL2_PFD_PFD0_FRAC(frac) << (8UL * (uint32_t)pfd)); + + *pfdUpdate ^= ((uint32_t)ANADIG_PLL_SYS_PLL2_UPDATE_PFD0_UPDATE_MASK << (uint32_t)pfd); + *pfdCtrl &= ~((uint32_t)ANADIG_PLL_SYS_PLL2_PFD_PFD0_DIV1_CLKGATE_MASK << (8UL * (uint32_t)pfd)); + /* Wait for stablizing */ + while (stable == (*pfdCtrl & ((uint32_t)ANADIG_PLL_SYS_PLL2_PFD_PFD0_STABLE_MASK << (8UL * (uint32_t)pfd)))) + { + } +} + +#ifndef GET_FREQ_FROM_OBS +uint32_t CLOCK_GetPfdFreq(clock_pll_t pll, clock_pfd_t pfd) +{ + uint32_t pllFreq = 0UL, frac = 0UL; + assert((pll == kCLOCK_PllSys2) || (pll == kCLOCK_PllSys3)); + + switch (pll) + { + case kCLOCK_PllSys2: + frac = (ANADIG_PLL->SYS_PLL2_PFD & + ((uint32_t)ANADIG_PLL_SYS_PLL2_PFD_PFD0_FRAC_MASK << (8UL * (uint32_t)pfd))); + pllFreq = (uint32_t)SYS_PLL2_FREQ; + break; + case kCLOCK_PllSys3: + frac = (ANADIG_PLL->SYS_PLL3_PFD & + ((uint32_t)ANADIG_PLL_SYS_PLL3_PFD_PFD0_FRAC_MASK << (8UL * (uint32_t)pfd))); + pllFreq = (uint32_t)SYS_PLL3_FREQ; + break; + default: + /* Wrong input parameter pll. */ + assert(false); + break; + } + + frac = frac >> (8UL * (uint32_t)pfd); + assert(frac >= (uint32_t)PFD_FRAC_MIN); + assert(frac <= (uint32_t)PFD_FRAC_MAX); + return ((frac != 0UL) ? (pllFreq / frac * 18UL) : 0UL); +} +#else +uint32_t CLOCK_GetPfdFreq(clock_pll_t pll, clock_pfd_t pfd) +{ + uint32_t freq = 0; + assert((pll == kCLOCK_PllSys2) || (pll == kCLOCK_PllSys3)); + + switch (pll) + { + case kCLOCK_PllSys2: + /* SYS_PLL2_PFD0 OBS index starts from 234 */ + freq = CLOCK_GetFreqFromObs(pfd + 234, 2); + break; + case kCLOCK_PllSys3: + /* SYS_PLL3_PFD0 OBS index starts from 241 */ + freq = CLOCK_GetFreqFromObs(pfd + 241, 2); + break; + default: + assert(false); + } + return freq; +} +#endif + +/* 480PLL */ +void CLOCK_InitSysPll3(void) +{ + uint32_t reg; + + if (0UL != (ANADIG_PLL->SYS_PLL3_CTRL & ANADIG_PLL_SYS_PLL3_CTRL_POWERUP_MASK)) + { + /* no need to reconfigure the PLL if all the configuration is the same */ + if (0UL == (ANADIG_PLL->SYS_PLL3_CTRL & ANADIG_PLL_SYS_PLL3_CTRL_ENABLE_CLK_MASK)) + { + ANADIG_PLL->SYS_PLL3_CTRL |= ANADIG_PLL_SYS_PLL3_CTRL_ENABLE_CLK_MASK; + } + + if (0UL != (ANADIG_PLL->SYS_PLL3_CTRL & ANADIG_PLL_SYS_PLL3_CTRL_SYS_PLL3_GATE_MASK)) + { + ANADIG_PLL->SYS_PLL3_CTRL &= ~ANADIG_PLL_SYS_PLL3_CTRL_SYS_PLL3_GATE_MASK; + } + return; + } + + PMU_StaticEnablePllLdo(ANADIG_PMU); + + /* Gate all PFDs */ + ANADIG_PLL->SYS_PLL3_PFD |= + ANADIG_PLL_SYS_PLL3_PFD_PFD0_DIV1_CLKGATE(1) | ANADIG_PLL_SYS_PLL3_PFD_PFD1_DIV1_CLKGATE(1) | + ANADIG_PLL_SYS_PLL3_PFD_PFD2_DIV1_CLKGATE(1) | ANADIG_PLL_SYS_PLL3_PFD_PFD3_DIV1_CLKGATE(1); + /* + * 1. configure PLL registres + * 2. Enable internal LDO + * 3. Wati LDO stable + * 4. Power up PLL, assert hold_ring_off (only needed for avpll) + * 5. At half lock time, de-asserted hold_ring_off (only needed for avpll) + * 6. Wait PLL lock + * 7. Enable clock output, release pfd_gate + */ + reg = ANADIG_PLL_SYS_PLL3_CTRL_PLL_REG_EN(1) | ANADIG_PLL_SYS_PLL3_CTRL_SYS_PLL3_GATE(1); + ANADIG_PLL->SYS_PLL3_CTRL = reg; + SDK_DelayAtLeastUs(30, SDK_DEVICE_MAXIMUM_CPU_CLOCK_FREQUENCY); + + reg |= ANADIG_PLL_SYS_PLL3_CTRL_POWERUP(1) | ANADIG_PLL_SYS_PLL3_CTRL_HOLD_RING_OFF_MASK; + ANADIG_PLL->SYS_PLL3_CTRL = reg; + SDK_DelayAtLeastUs(30, SDK_DEVICE_MAXIMUM_CPU_CLOCK_FREQUENCY); + + reg &= ~ANADIG_PLL_SYS_PLL3_CTRL_HOLD_RING_OFF_MASK; + ANADIG_PLL->SYS_PLL3_CTRL = reg; + + /* Wait for PLL stable */ + while (ANADIG_PLL_SYS_PLL3_CTRL_SYS_PLL3_STABLE_MASK != + (ANADIG_PLL->SYS_PLL3_CTRL & ANADIG_PLL_SYS_PLL3_CTRL_SYS_PLL3_STABLE_MASK)) + { + } + + reg |= ANADIG_PLL_SYS_PLL3_CTRL_ENABLE_CLK(1) | ANADIG_PLL_SYS_PLL3_CTRL_SYS_PLL3_DIV2(1); + ANADIG_PLL->SYS_PLL3_CTRL = reg; + + reg &= ~ANADIG_PLL_SYS_PLL3_CTRL_SYS_PLL3_GATE_MASK; + ANADIG_PLL->SYS_PLL3_CTRL = reg; +} + +void CLOCK_DeinitSysPll3(void) +{ + ANADIG_PLL->SYS_PLL3_PFD |= + ANADIG_PLL_SYS_PLL3_PFD_PFD0_DIV1_CLKGATE(1) | ANADIG_PLL_SYS_PLL3_PFD_PFD1_DIV1_CLKGATE(1) | + ANADIG_PLL_SYS_PLL3_PFD_PFD2_DIV1_CLKGATE(1) | ANADIG_PLL_SYS_PLL3_PFD_PFD3_DIV1_CLKGATE(1); + + ANADIG_PLL->SYS_PLL3_CTRL |= ANADIG_PLL_SYS_PLL3_CTRL_SYS_PLL3_GATE_MASK; + ANADIG_PLL->SYS_PLL3_CTRL &= ~(ANADIG_PLL_SYS_PLL3_CTRL_ENABLE_CLK_MASK | ANADIG_PLL_SYS_PLL3_CTRL_POWERUP_MASK | + ANADIG_PLL_SYS_PLL3_CTRL_PLL_REG_EN_MASK); +} + +/*! + * brief Check if Sys PLL3 PFD is enabled + * + * param pfd PFD control name + * return PFD bypass status. + * - true: power on. + * - false: power off. + */ +bool CLOCK_IsSysPll3PfdEnabled(clock_pfd_t pfd) +{ + return ((ANADIG_PLL->SYS_PLL3_PFD & (uint32_t)ANADIG_PLL_SYS_PLL3_PFD_PFD0_DIV1_CLKGATE_MASK + << (8UL * (uint8_t)pfd)) == 0U); +} + +void CLOCK_SetPllBypass(clock_pll_t pll, bool bypass) +{ + switch (pll) + { + case kCLOCK_PllArm: + ANADIG_PLL->ARM_PLL_CTRL = bypass ? (ANADIG_PLL->ARM_PLL_CTRL | ANADIG_PLL_ARM_PLL_CTRL_BYPASS_MASK) : + (ANADIG_PLL->ARM_PLL_CTRL & ~ANADIG_PLL_ARM_PLL_CTRL_BYPASS_MASK); + break; + case kCLOCK_PllSys1: + ANATOP_PllBypass(kAI_Itf_1g, bypass); + break; + case kCLOCK_PllSys2: + ANADIG_PLL->SYS_PLL2_CTRL = bypass ? (ANADIG_PLL->SYS_PLL2_CTRL | ANADIG_PLL_SYS_PLL2_CTRL_BYPASS_MASK) : + (ANADIG_PLL->SYS_PLL2_CTRL & ~ANADIG_PLL_SYS_PLL2_CTRL_BYPASS_MASK); + break; + case kCLOCK_PllSys3: + ANADIG_PLL->SYS_PLL3_CTRL = bypass ? (ANADIG_PLL->SYS_PLL3_CTRL | ANADIG_PLL_SYS_PLL3_CTRL_BYPASS_MASK) : + (ANADIG_PLL->SYS_PLL3_CTRL & ~ANADIG_PLL_SYS_PLL3_CTRL_BYPASS_MASK); + break; + case kCLOCK_PllAudio: + ANATOP_PllBypass(kAI_Itf_Audio, bypass); + break; + case kCLOCK_PllVideo: + ANATOP_PllBypass(kAI_Itf_Video, bypass); + break; + default: + assert(false); + break; + } +} + +static void ANATOP_PllSetPower(anatop_ai_itf_t itf, bool enable) +{ + ANATOP_AI_Write(itf, enable ? PLL_AI_CTRL0_SET_REG : PLL_AI_CTRL0_CLR_REG, + PLL_AI_CTRL0_POWER_UP_MASK | (enable ? PLL_AI_CTRL0_HOLD_RING_OFF_MASK : 0UL)); +} + +static void ANATOP_PllBypass(anatop_ai_itf_t itf, bool bypass) +{ + ANATOP_AI_Write(itf, bypass ? PLL_AI_CTRL0_SET_REG : PLL_AI_CTRL0_CLR_REG, PLL_AI_CTRL0_BYPASS_MASK); +} + +static void ANATOP_PllEnablePllReg(anatop_ai_itf_t itf, bool enable) +{ + ANATOP_AI_Write(itf, enable ? PLL_AI_CTRL0_SET_REG : PLL_AI_CTRL0_CLR_REG, PLL_AI_CTRL0_PLL_REG_EN_MASK); +} + +static void ANATOP_PllHoldRingOff(anatop_ai_itf_t itf, bool off) +{ + ANATOP_AI_Write(itf, off ? PLL_AI_CTRL0_SET_REG : PLL_AI_CTRL0_CLR_REG, PLL_AI_CTRL0_HOLD_RING_OFF_MASK); +} + +static void ANATOP_PllToggleHoldRingOff(anatop_ai_itf_t itf, uint32_t delay_us) +{ + ANATOP_PllHoldRingOff(itf, true); + SDK_DelayAtLeastUs(delay_us, SDK_DEVICE_MAXIMUM_CPU_CLOCK_FREQUENCY); + ANATOP_PllHoldRingOff(itf, false); +} + +static void ANATOP_PllEnableSs(anatop_ai_itf_t itf, bool enable) +{ + ANATOP_AI_Write(itf, enable ? PLL_AI_CTRL0_SET_REG : PLL_AI_CTRL0_CLR_REG, PLL_AI_CTRL0_ENABLE_MASK); +} + +static void ANATOP_PllEnableClk(anatop_ai_itf_t itf, bool enable) +{ + ANATOP_AI_Write(itf, enable ? PLL_AI_CTRL0_SET_REG : PLL_AI_CTRL0_CLR_REG, PLL_AI_CTRL0_ENABLE_MASK); +} + +static void ANATOP_PllConfigure( + anatop_ai_itf_t itf, uint8_t div, uint32_t numer, uint8_t post_div, uint32_t denom, const clock_pll_ss_config_t *ss) +{ + if (itf != kAI_Itf_1g) + { + ANATOP_PllSetPower(itf, false); + } + if (ss != NULL) + { + ANATOP_AI_Write(itf, PLL_AI_CTRL1_REG, + AUDIO_PLL_SPREAD_SPECTRUM_STEP(ss->step) | AUDIO_PLL_SPREAD_SPECTRUM_STOP(ss->stop) | + AUDIO_PLL_SPREAD_SPECTRUM_ENABLE_MASK); + } + ANATOP_AI_Write(itf, PLL_AI_CTRL3_REG, denom); + ANATOP_AI_Write(itf, PLL_AI_CTRL2_REG, numer); + ANATOP_AI_WriteWithMaskShift(itf, PLL_AI_CTRL0_REG, div, 0x7f, 0); + if (itf != kAI_Itf_1g) + { + ANATOP_AI_WriteWithMaskShift(itf, PLL_AI_CTRL0_REG, post_div, 0xE000000UL, 25UL); + } + ANATOP_PllEnablePllReg(itf, true); + SDK_DelayAtLeastUs(100, SDK_DEVICE_MAXIMUM_CPU_CLOCK_FREQUENCY); + ANATOP_PllSetPower(itf, true); +} + +static void ANATOP_AudioPllGate(bool enable) +{ + if (!enable) + { + ANADIG_PLL->PLL_AUDIO_CTRL &= ~ANADIG_PLL_PLL_AUDIO_CTRL_PLL_AUDIO_GATE_MASK; + } + else + { + ANADIG_PLL->PLL_AUDIO_CTRL |= ANADIG_PLL_PLL_AUDIO_CTRL_PLL_AUDIO_GATE_MASK; + } +} + +static void ANATOP_AudioPllSwEnClk(bool enable) +{ + if (!enable) + { + ANADIG_PLL->PLL_AUDIO_CTRL &= ~ANADIG_PLL_PLL_AUDIO_CTRL_ENABLE_CLK_MASK; + } + else + { + ANADIG_PLL->PLL_AUDIO_CTRL |= ANADIG_PLL_PLL_AUDIO_CTRL_ENABLE_CLK_MASK; + } +} + +status_t CLOCK_InitAudioPllWithFreq(uint32_t freqInMhz, bool ssEnable, uint32_t ssRange, uint32_t ssMod) +{ + clock_audio_pll_config_t config = {0}; + config.ssEnable = ssEnable; + + if (kStatus_Success == CLOCK_CalcAvPllFreq(&config, freqInMhz)) + { + if (config.ssEnable) + { + clock_pll_ss_config_t ss = {0}; + CLOCK_CalcPllSpreadSpectrum(config.denominator, ssRange, ssMod, &ss); + config.ss = &ss; + } + CLOCK_InitAudioPll(&config); + return kStatus_Success; + } + return kStatus_Fail; +} + +void CLOCK_InitAudioPll(const clock_audio_pll_config_t *config) +{ + uint32_t reg; + bool pllStable = false; + + PMU_StaticEnablePllLdo(ANADIG_PMU); + + reg = ANADIG_PLL->PLL_AUDIO_CTRL; + if ((reg & (ANADIG_PLL_PLL_AUDIO_CTRL_PLL_AUDIO_STABLE_MASK | ANADIG_PLL_PLL_AUDIO_CTRL_ENABLE_CLK_MASK)) == + (ANADIG_PLL_PLL_AUDIO_CTRL_PLL_AUDIO_STABLE_MASK | ANADIG_PLL_PLL_AUDIO_CTRL_ENABLE_CLK_MASK)) + { + pllStable = true; + } + /* bypass pll */ + ANATOP_PllBypass(kAI_Itf_Audio, true); + + if (pllStable) + { + /* sw enable clock */ + ANATOP_AudioPllSwEnClk(false); + /* gate clock */ + ANATOP_AudioPllGate(true); + ANATOP_PllEnableClk(kAI_Itf_Audio, false); + ANATOP_PllSetPower(kAI_Itf_Audio, false); + ANATOP_PllEnablePllReg(kAI_Itf_Audio, false); + } + + ANATOP_AudioPllSwEnClk(true); + /* configure pll */ + ANATOP_PllConfigure(kAI_Itf_Audio, config->loopDivider, config->numerator, config->postDivider, config->denominator, + (config->ssEnable && (config->ss != NULL)) ? config->ss : NULL); + + /* toggle hold ring off */ + ANATOP_PllToggleHoldRingOff(kAI_Itf_Audio, 225); + + /* wait pll stable */ + do + { + reg = ANADIG_PLL->PLL_AUDIO_CTRL; + } while ((reg & ANADIG_PLL_PLL_AUDIO_CTRL_PLL_AUDIO_STABLE_MASK) != + ANADIG_PLL_PLL_AUDIO_CTRL_PLL_AUDIO_STABLE_MASK); /* wait for PLL locked */ + + /* enabled clock */ + ANATOP_PllEnableClk(kAI_Itf_Audio, true); + + /* ungate clock */ + ANATOP_AudioPllGate(false); + + ANATOP_PllBypass(kAI_Itf_Audio, false); +} + +void CLOCK_DeinitAudioPll(void) +{ + ANATOP_AudioPllSwEnClk(false); + ANATOP_AudioPllGate(true); + ANATOP_PllEnableClk(kAI_Itf_Audio, false); + ANATOP_PllSetPower(kAI_Itf_Audio, false); + ANATOP_PllEnableSs(kAI_Itf_Audio, false); + ANATOP_PllEnablePllReg(kAI_Itf_Audio, false); +} + +void CLOCK_GPC_SetAudioPllOutputFreq(const clock_audio_pll_gpc_config_t *config) +{ + assert(config != NULL); + + ANADIG_PLL->PLL_AUDIO_DIV_SELECT = config->loopDivider; + ANADIG_PLL->PLL_AUDIO_NUMERATOR = config->numerator; + ANADIG_PLL->PLL_AUDIO_DENOMINATOR = config->denominator; + if ((config->ss != NULL) && config->ssEnable) + { + ANADIG_PLL->PLL_AUDIO_SS = ANADIG_PLL_PLL_AUDIO_SS_STEP(config->ss->step) | + ANADIG_PLL_PLL_AUDIO_SS_STOP(config->ss->stop) | ANADIG_PLL_PLL_AUDIO_SS_ENABLE_MASK; + } + + ANADIG_PLL->PLL_AUDIO_CTRL |= ANADIG_PLL_PLL_AUDIO_CTRL_PLL_AUDIO_CONTROL_MODE_MASK; +} + +static void ANATOP_VideoPllGate(bool enable) +{ + if (!enable) + { + ANADIG_PLL->PLL_VIDEO_CTRL &= ~ANADIG_PLL_PLL_VIDEO_CTRL_PLL_VIDEO_GATE_MASK; + } + else + { + ANADIG_PLL->PLL_VIDEO_CTRL |= ANADIG_PLL_PLL_VIDEO_CTRL_PLL_VIDEO_GATE_MASK; + } +} + +static void ANATOP_VideoPllSwEnClk(bool enable) +{ + if (!enable) + { + ANADIG_PLL->PLL_VIDEO_CTRL &= ~ANADIG_PLL_PLL_VIDEO_CTRL_ENABLE_CLK_MASK; + } + else + { + ANADIG_PLL->PLL_VIDEO_CTRL |= ANADIG_PLL_PLL_VIDEO_CTRL_ENABLE_CLK_MASK; + } +} + +status_t CLOCK_CalcArmPllFreq(clock_arm_pll_config_t *config, uint32_t freqInMhz) +{ + assert(config != NULL); + uint32_t refFreq = (XTAL_FREQ / 1000000UL) * 104UL; /* MHz */ + assert((freqInMhz <= refFreq) && (freqInMhz >= 156UL)); + + /* + * ARM_PLL (156Mhz - 2496Mhz configureable ) + * freqInMhz = osc_freq * loopDivider / (2 * postDivider) + * - loopDivider: 104 - 208 + * - postDivider: 0 - 3 + * postDivider: + * 0 - divide by 2; 1 - divide by 4; 2 - divide by 8; 3 - divide by 1 + */ + + if (freqInMhz >= refFreq) + { + config->postDivider = kCLOCK_PllPostDiv1; + config->loopDivider = 208; + } + else if (freqInMhz >= (refFreq >> 1)) + { + config->postDivider = kCLOCK_PllPostDiv1; + config->loopDivider = freqInMhz / 12UL; + } + else if (freqInMhz >= (refFreq >> 2)) + { + config->postDivider = kCLOCK_PllPostDiv2; + config->loopDivider = freqInMhz / 6UL; + } + else if (freqInMhz >= (refFreq >> 3)) + { + config->postDivider = kCLOCK_PllPostDiv4; + config->loopDivider = freqInMhz / 3UL; + } + else if (freqInMhz > (refFreq >> 4)) + { + config->postDivider = kCLOCK_PllPostDiv8; + config->loopDivider = 2UL * freqInMhz / 3UL; + } + else + { + config->postDivider = kCLOCK_PllPostDiv8; + config->loopDivider = 104; + } + return kStatus_Success; +} + +status_t CLOCK_InitArmPllWithFreq(uint32_t freqInMhz) +{ + clock_arm_pll_config_t config; + if (kStatus_Success == CLOCK_CalcArmPllFreq(&config, freqInMhz)) + { + CLOCK_InitArmPll(&config); + return kStatus_Success; + } + return kStatus_Fail; +} + +status_t CLOCK_CalcAvPllFreq(clock_av_pll_config_t *config, uint32_t freqInMhz) +{ + assert(config != NULL); + + uint32_t refFreq = (XTAL_FREQ / 1000000UL) * 54UL; /* MHz */ + + assert((freqInMhz <= refFreq) && (freqInMhz > 20UL)); + + /* + * AUDIO_PLL/VIDEO_PLL (20.3125MHZ--- 1300MHZ configureable ) + * freqInMhz = osc_freq * (loopDivider + numerator / (2^28 - 1) ) / 2^postDivider + * - loopDivider: 27---54 + * - postDivider: 0---5 + * - numerator is a signed number, 30 bit, numer[29] is the sign bit, such as +1--->0x00000001; -1---> 0x20000001 + * such as: div_sel = 27, numer = 0x026EEEEF, post_div =0, fref = 24.0MHZ, output_fre =24.0*(27 + 40824559/(2^28 - + * 1))/2^0 = 651.65M such as: div_sel = 33, numer = 0x0F0AAAAA, post_div =0, fref = 19.2MHZ, output_fre =19.2*(33 + + * 252357290/(2^28 - 1))/2^0= 651.65M + */ + + config->denominator = 0x0FFFFFFF; + + if (freqInMhz >= refFreq) + { + config->postDivider = 0; + config->loopDivider = 54; + config->numerator = 0; + } + else if (freqInMhz >= (refFreq >> 1)) + { + config->postDivider = 0; + config->loopDivider = (uint8_t)(freqInMhz / 24UL); + config->numerator = (config->denominator / 24UL) * (freqInMhz % 24UL); + } + else if (freqInMhz >= (refFreq >> 2)) + { + config->postDivider = 1; + config->loopDivider = (uint8_t)(freqInMhz / 12UL); + config->numerator = (config->denominator / 12UL) * (freqInMhz % 12UL); + } + else if (freqInMhz >= (refFreq >> 3)) + { + config->postDivider = 2; + config->loopDivider = (uint8_t)(freqInMhz / 6UL); + config->numerator = (config->denominator / 6UL) * (freqInMhz % 6UL); + } + else if (freqInMhz >= (refFreq >> 4)) + { + config->postDivider = 3; + config->loopDivider = (uint8_t)(freqInMhz / 3UL); + config->numerator = (config->denominator / 3UL) * (freqInMhz % 3UL); + } + else if (freqInMhz >= (refFreq >> 5)) + { + config->postDivider = 4; + config->loopDivider = (uint8_t)(freqInMhz * 2UL / 3UL); + config->numerator = (config->denominator * 2UL / 3UL) * (freqInMhz * 2UL % 3UL); + } + else if (freqInMhz > (refFreq >> 6)) + { + config->postDivider = 5; + config->loopDivider = (uint8_t)(freqInMhz * 4UL / 3UL); + config->numerator = (config->denominator * 4UL / 3UL) * (freqInMhz * 4UL % 3UL); + } + else + { + return kStatus_Fail; + } + return kStatus_Success; +} + +status_t CLOCK_InitVideoPllWithFreq(uint32_t freqInMhz, bool ssEnable, uint32_t ssRange, uint32_t ssMod) +{ + clock_video_pll_config_t config = {0}; + config.ssEnable = ssEnable; + + if (kStatus_Success == CLOCK_CalcAvPllFreq(&config, freqInMhz)) + { + if (config.ssEnable) + { + clock_pll_ss_config_t ss = {0}; + CLOCK_CalcPllSpreadSpectrum(config.denominator, ssRange, ssMod, &ss); + config.ss = &ss; + } + CLOCK_InitVideoPll(&config); + return kStatus_Success; + } + return kStatus_Fail; +} + +void CLOCK_InitVideoPll(const clock_video_pll_config_t *config) +{ + uint32_t reg; + bool pllStable = false; + + PMU_StaticEnablePllLdo(ANADIG_PMU); + + reg = ANADIG_PLL->PLL_VIDEO_CTRL; + if ((reg & (ANADIG_PLL_PLL_VIDEO_CTRL_PLL_VIDEO_STABLE_MASK | ANADIG_PLL_PLL_VIDEO_CTRL_ENABLE_CLK_MASK)) == + (ANADIG_PLL_PLL_VIDEO_CTRL_PLL_VIDEO_STABLE_MASK | ANADIG_PLL_PLL_VIDEO_CTRL_ENABLE_CLK_MASK)) + { + pllStable = true; + } + /* bypass pll */ + ANATOP_PllBypass(kAI_Itf_Video, true); + + if (pllStable) + { + /* sw enable clock */ + ANATOP_VideoPllSwEnClk(false); + /* gate clock */ + ANATOP_VideoPllGate(true); + ANATOP_PllEnableClk(kAI_Itf_Video, false); + ANATOP_PllSetPower(kAI_Itf_Video, false); + ANATOP_PllEnablePllReg(kAI_Itf_Video, false); + } + + ANATOP_VideoPllSwEnClk(true); + /* configure pll */ + ANATOP_PllConfigure(kAI_Itf_Video, config->loopDivider, config->numerator, config->postDivider, config->denominator, + (config->ssEnable && (config->ss != NULL)) ? config->ss : NULL); + + /* toggle hold ring off */ + ANATOP_PllToggleHoldRingOff(kAI_Itf_Video, 225); + + /* wait pll stable */ + do + { + reg = ANADIG_PLL->PLL_VIDEO_CTRL; + } while ((reg & ANADIG_PLL_PLL_VIDEO_CTRL_PLL_VIDEO_STABLE_MASK) != + ANADIG_PLL_PLL_VIDEO_CTRL_PLL_VIDEO_STABLE_MASK); /* wait for PLL locked */ + + /* enabled clock */ + ANATOP_PllEnableClk(kAI_Itf_Video, true); + + /* ungate clock */ + ANATOP_VideoPllGate(false); + + ANATOP_PllBypass(kAI_Itf_Video, false); +} + +void CLOCK_DeinitVideoPll(void) +{ + ANATOP_VideoPllSwEnClk(false); + ANATOP_VideoPllGate(true); + ANATOP_PllEnableClk(kAI_Itf_Video, false); + ANATOP_PllSetPower(kAI_Itf_Video, false); + ANATOP_PllEnableSs(kAI_Itf_Video, false); + ANATOP_PllEnablePllReg(kAI_Itf_Video, false); +} + +void CLOCK_GPC_SetVideoPllOutputFreq(const clock_video_pll_gpc_config_t *config) +{ + assert(config != NULL); + + ANADIG_PLL->PLL_VIDEO_DIV_SELECT = config->loopDivider; + ANADIG_PLL->PLL_VIDEO_NUMERATOR = config->numerator; + ANADIG_PLL->PLL_VIDEO_DENOMINATOR = config->denominator; + + if ((config->ss != NULL) && config->ssEnable) + { + ANADIG_PLL->PLL_VIDEO_SS = ANADIG_PLL_PLL_VIDEO_SS_STEP(config->ss->step) | + ANADIG_PLL_PLL_VIDEO_SS_STOP(config->ss->stop) | ANADIG_PLL_PLL_VIDEO_SS_ENABLE_MASK; + } + + ANADIG_PLL->PLL_VIDEO_CTRL |= ANADIG_PLL_PLL_VIDEO_CTRL_PLL_VIDEO_CONTROL_MODE_MASK; +} + +static void ANATOP_SysPll1Gate(bool enable) +{ + if (!enable) + { + ANADIG_PLL->SYS_PLL1_CTRL &= ~ANADIG_PLL_SYS_PLL1_CTRL_SYS_PLL1_GATE_MASK; + } + else + { + ANADIG_PLL->SYS_PLL1_CTRL |= ANADIG_PLL_SYS_PLL1_CTRL_SYS_PLL1_GATE_MASK; + } +} + +static void ANATOP_SysPll1Div2En(bool enable) +{ + if (!enable) + { + ANADIG_PLL->SYS_PLL1_CTRL &= ~ANADIG_PLL_SYS_PLL1_CTRL_SYS_PLL1_DIV2_MASK; + } + else + { + ANADIG_PLL->SYS_PLL1_CTRL |= ANADIG_PLL_SYS_PLL1_CTRL_SYS_PLL1_DIV2_MASK; + } +} + +static void ANATOP_SysPll1Div5En(bool enable) +{ + if (!enable) + { + ANADIG_PLL->SYS_PLL1_CTRL &= ~ANADIG_PLL_SYS_PLL1_CTRL_SYS_PLL1_DIV5_MASK; + } + else + { + ANADIG_PLL->SYS_PLL1_CTRL |= ANADIG_PLL_SYS_PLL1_CTRL_SYS_PLL1_DIV5_MASK; + } +} + +static void ANATOP_SysPll1SwEnClk(bool enable) +{ + if (!enable) + { + ANADIG_PLL->SYS_PLL1_CTRL &= ~ANADIG_PLL_SYS_PLL1_CTRL_ENABLE_CLK_MASK; + } + else + { + ANADIG_PLL->SYS_PLL1_CTRL |= ANADIG_PLL_SYS_PLL1_CTRL_ENABLE_CLK_MASK; + } +} + +static void ANATOP_SysPll1WaitStable(void) +{ + uint32_t reg; + do + { + reg = ANADIG_PLL->SYS_PLL1_CTRL; + } while ((reg & ANADIG_PLL_SYS_PLL1_CTRL_SYS_PLL1_STABLE_MASK) != + ANADIG_PLL_SYS_PLL1_CTRL_SYS_PLL1_STABLE_MASK); /* wait for PLL locked */ +} + +/* 1GPLL */ +void CLOCK_InitSysPll1(const clock_sys_pll1_config_t *config) +{ + uint8_t div; + uint32_t numerator, denominator; + + PMU_StaticEnablePllLdo(ANADIG_PMU); + /* bypass pll */ + ANATOP_PllBypass(kAI_Itf_1g, true); + + /* sw enable clock */ + ANATOP_SysPll1SwEnClk(true); + + denominator = 0x0FFFFFFF; + div = 41U; + numerator = 178956970UL; + + if (config->ssEnable && (config->ss != NULL)) + { + return; + } + + /* configure pll */ + ANATOP_PllConfigure(kAI_Itf_1g, div, numerator, 0U, denominator, + (config->ssEnable && (config->ss != NULL)) ? config->ss : NULL); + + /* toggle hold ring off */ + ANATOP_PllToggleHoldRingOff(kAI_Itf_1g, 225); + + /* wait pll stable */ + ANATOP_SysPll1WaitStable(); + + /* enabled clock */ + ANATOP_PllEnableClk(kAI_Itf_1g, true); + + /* ungate clock */ + ANATOP_SysPll1Gate(false); + + ANATOP_SysPll1Div2En(config->pllDiv2En); + ANATOP_SysPll1Div5En(config->pllDiv5En); + + /* bypass pll */ + ANATOP_PllBypass(kAI_Itf_1g, false); +} + +void CLOCK_DeinitSysPll1(void) +{ + ANATOP_SysPll1SwEnClk(false); + ANATOP_SysPll1Div2En(false); + ANATOP_SysPll1Div5En(false); + ANATOP_SysPll1Gate(true); + ANATOP_PllEnableClk(kAI_Itf_1g, false); + ANATOP_PllSetPower(kAI_Itf_1g, false); + ANATOP_PllEnableSs(kAI_Itf_1g, false); + ANATOP_PllEnablePllReg(kAI_Itf_1g, false); +} + +void CLOCK_GPC_SetSysPll1OutputFreq(const clock_sys_pll1_gpc_config_t *config) +{ + assert(config != NULL); + + ANADIG_PLL->SYS_PLL1_DIV_SELECT = config->loopDivider; + ANADIG_PLL->SYS_PLL1_NUMERATOR = config->numerator; + ANADIG_PLL->SYS_PLL1_DENOMINATOR = config->denominator; + + if ((config->ss != NULL) && config->ssEnable) + { + ANADIG_PLL->SYS_PLL1_SS = ANADIG_PLL_SYS_PLL1_SS_STEP(config->ss->step) | + ANADIG_PLL_SYS_PLL1_SS_STOP(config->ss->stop) | ANADIG_PLL_SYS_PLL1_SS_ENABLE_MASK; + } + + ANADIG_PLL->SYS_PLL1_CTRL |= ANADIG_PLL_SYS_PLL1_CTRL_SYS_PLL1_CONTROL_MODE_MASK; +} + +void CLOCK_OSC_EnableOsc24M(void) +{ + if (0UL == (ANADIG_OSC->OSC_24M_CTRL & ANADIG_OSC_OSC_24M_CTRL_OSC_EN_MASK)) + { + ANADIG_OSC->OSC_24M_CTRL |= ANADIG_OSC_OSC_24M_CTRL_OSC_EN_MASK; + while (ANADIG_OSC_OSC_24M_CTRL_OSC_24M_STABLE_MASK != + (ANADIG_OSC->OSC_24M_CTRL & ANADIG_OSC_OSC_24M_CTRL_OSC_24M_STABLE_MASK)) + { + } + } +} + +/*! + * brief Set the work mode of 24MHz crystal oscillator, the available modes are high gian mode, low power mode, and + * bypass mode. + * + * param workMode The work mode of 24MHz crystal oscillator, please refer to @ref clock_24MOsc_mode_t for details. + */ +void CLOCK_OSC_SetOsc24MWorkMode(clock_24MOsc_mode_t workMode) +{ + uint32_t tmp32; + + tmp32 = ANADIG_OSC->OSC_24M_CTRL; + tmp32 &= ~(ANADIG_OSC_OSC_24M_CTRL_LP_EN_MASK | ANADIG_OSC_OSC_24M_CTRL_BYPASS_EN_MASK); + tmp32 |= (((uint32_t)workMode << ANADIG_OSC_OSC_24M_CTRL_BYPASS_EN_SHIFT) & + (ANADIG_OSC_OSC_24M_CTRL_LP_EN_MASK | ANADIG_OSC_OSC_24M_CTRL_BYPASS_EN_MASK)); + ANADIG_OSC->OSC_24M_CTRL = tmp32; +} + +/*! + * brief Configure the 16MHz oscillator. + * + * param source Used to select the source for 16MHz RC oscillator, please refer to clock_16MOsc_source_t. + * param enablePowerSave Enable/disable power save mode function at 16MHz OSC. + * - \b true Enable power save mode function at 16MHz osc. + * - \b false Disable power save mode function at 16MHz osc. + * param enableClockOut Enable/Disable clock output for 16MHz RCOSC. + * - \b true Enable clock output for 16MHz RCOSC. + * - \b false Disable clock output for 16MHz RCOSC. + */ +void CLOCK_OSC_SetOsc16MConfig(clock_16MOsc_source_t source, bool enablePowerSave, bool enableClockOut) +{ + uint32_t tmp32; + + tmp32 = ANADIG_OSC->OSC_16M_CTRL; + tmp32 &= ~(ANADIG_OSC_OSC_16M_CTRL_EN_IRC4M16M_MASK | ANADIG_OSC_OSC_16M_CTRL_EN_POWER_SAVE_MASK | + ANADIG_OSC_OSC_16M_CTRL_SOURCE_SEL_16M_MASK); + tmp32 |= ANADIG_OSC_OSC_16M_CTRL_EN_IRC4M16M(enableClockOut) | + ANADIG_OSC_OSC_16M_CTRL_EN_POWER_SAVE(enablePowerSave) | ANADIG_OSC_OSC_16M_CTRL_SOURCE_SEL_16M(source); + ANADIG_OSC->OSC_16M_CTRL = tmp32; +} + +/*! + * brief Set the divide value for ref_clk to generate slow clock. + * + * note slow_clk = ref_clk / (divValue + 1), and the recommand divide value is 24. + * + * param divValue The divide value to be set, the available range is 0~63. + */ +void CLOCK_OSC_SetOscRc400MRefClkDiv(uint8_t divValue) +{ + uint32_t tmp32; + + tmp32 = ANATOP_AI_Read(kAI_Itf_400m, kAI_RCOSC400M_CTRL0); + tmp32 = ((tmp32 & ~AI_RCOSC400M_CTRL0_REF_CLK_DIV_MASK) | AI_RCOSC400M_CTRL0_REF_CLK_DIV(divValue)); + ANATOP_AI_Write(kAI_Itf_400m, kAI_RCOSC400M_CTRL0, tmp32); +} + +/*! + * brief Set the target count for the fast clock. + * + * param targetCount The desired target for the fast clock, should be the number of clock cycles of the fast_clk per + * divided ref_clk. + */ +void CLOCK_OSC_SetOscRc400MFastClkCount(uint16_t targetCount) +{ + uint32_t tmp32; + + tmp32 = ANATOP_AI_Read(kAI_Itf_400m, kAI_RCOSC400M_CTRL1); + tmp32 = ((tmp32 & ~AI_RCOSC400M_CTRL1_TARGET_COUNT_MASK) | AI_RCOSC400M_CTRL1_TARGET_COUNT(targetCount)); + ANATOP_AI_Write(kAI_Itf_400m, kAI_RCOSC400M_CTRL1, tmp32); +} + +/*! + * brief Set the negative and positive hysteresis value for the tuned clock. + * + * note The hysteresis value should be set after the clock is tuned. + * + * param negHysteresis The negative hysteresis value for the turned clock, this value in number of clock cycles of the + * fast clock + * param posHysteresis The positive hysteresis value for the turned clock, this value in number of clock cycles of the + * fast clock + */ +void CLOCK_OSC_SetOscRc400MHysteresisValue(uint8_t negHysteresis, uint8_t posHysteresis) +{ + uint32_t tmp32; + + tmp32 = ANATOP_AI_Read(kAI_Itf_400m, kAI_RCOSC400M_CTRL1); + tmp32 = ((tmp32 & ~(AI_RCOSC400M_CTRL1_HYST_PLUS_MASK | AI_RCOSC400M_CTRL1_HYST_MINUS_MASK)) | + (AI_RCOSC400M_CTRL1_HYST_PLUS(posHysteresis) | AI_RCOSC400M_CTRL1_HYST_MINUS(negHysteresis))); + ANATOP_AI_Write(kAI_Itf_400m, kAI_RCOSC400M_CTRL1, tmp32); +} + +/*! + * brief Bypass/un-bypass the tune logic + * + * param enableBypass Used to control whether to bypass the turn logic. + * - \b true Bypass the tune logic and use the programmed oscillator frequency to run the oscillator. + * Function CLOCK_OSC_SetOscRc400MTuneValue() can be used to set oscillator frequency. + * - \b false Use the output of tune logic to run the oscillator. + */ +void CLOCK_OSC_BypassOscRc400MTuneLogic(bool enableBypass) +{ + if (enableBypass) + { + ANATOP_AI_Write(kAI_Itf_400m, kAI_RCOSC400M_CTRL2_SET, AI_RCOSC400M_CTRL2_TUNE_BYP_MASK); + } + else + { + ANATOP_AI_Write(kAI_Itf_400m, kAI_RCOSC400M_CTRL2_CLR, AI_RCOSC400M_CTRL2_TUNE_BYP_MASK); + } +} + +/*! + * brief Start/Stop the tune logic. + * + * param enable Used to start or stop the tune logic. + * - \b true Start tuning + * - \b false Stop tuning and reset the tuning logic. + */ +void CLOCK_OSC_EnableOscRc400MTuneLogic(bool enable) +{ + if (enable) + { + ANATOP_AI_Write(kAI_Itf_400m, kAI_RCOSC400M_CTRL2_SET, AI_RCOSC400M_CTRL2_TUNE_START_MASK); + } + else + { + ANATOP_AI_Write(kAI_Itf_400m, kAI_RCOSC400M_CTRL2_CLR, AI_RCOSC400M_CTRL2_TUNE_START_MASK); + } +} + +/*! + * brief Freeze/Unfreeze the tuning value. + * + * param enableFreeze Used to control whether to freeze the tune value. + * - \b true Freeze the tune at the current tuned value and the oscillator runs at tje frozen tune value. + * - \b false Unfreezes and continues the tune operation. + */ +void CLOCK_OSC_FreezeOscRc400MTuneValue(bool enableFreeze) +{ + if (enableFreeze) + { + ANATOP_AI_Write(kAI_Itf_400m, kAI_RCOSC400M_CTRL2_SET, AI_RCOSC400M_CTRL2_TUNE_EN_MASK); + } + else + { + ANATOP_AI_Write(kAI_Itf_400m, kAI_RCOSC400M_CTRL2_CLR, AI_RCOSC400M_CTRL2_TUNE_EN_MASK); + } +} + +/*! + * @brief Set the 400MHz RC oscillator tune value when the tune logic is disabled. + * + * @param tuneValue The tune value to determine the frequency of Oscillator. + */ +void CLOCK_OSC_SetOscRc400MTuneValue(uint8_t tuneValue) +{ + uint32_t tmp32; + + tmp32 = ANATOP_AI_Read(kAI_Itf_400m, kAI_RCOSC400M_CTRL2); + tmp32 &= ~AI_RCOSC400M_CTRL2_OSC_TUNE_VAL_MASK; + tmp32 |= AI_RCOSC400M_CTRL2_OSC_TUNE_VAL(tuneValue); + ANATOP_AI_Write(kAI_Itf_400m, kAI_RCOSC400M_CTRL2, tmp32); +} + +/*! + * brief Set the behavior of the 1MHz output clock, such as disable the 1MHz clock output, + * enable the free-running 1MHz clock output, enable the locked 1MHz clock output. + * + * note The 1MHz clock is divided from 400M RC Oscillator. + * + * param behavior The behavior of 1MHz output clock, please refer to clock_1MHzOut_behavior_t for details. + */ +void CLOCK_OSC_Set1MHzOutputBehavior(clock_1MHzOut_behavior_t behavior) +{ + uint32_t tmp32; + + tmp32 = ANATOP_AI_Read(kAI_Itf_400m, kAI_RCOSC400M_CTRL3); + tmp32 &= ~(AI_RCOSC400M_CTRL3_EN_1M_CLK_MASK | AI_RCOSC400M_CTRL3_MUX_1M_CLK_MASK); + + if (behavior == kCLOCK_1MHzOutDisable) + { + tmp32 |= AI_RCOSC400M_CTRL3_EN_1M_CLK_MASK; + } + else + { + if (behavior == kCLOCK_1MHzOutEnableLocked1Mhz) + { + tmp32 |= AI_RCOSC400M_CTRL3_MUX_1M_CLK_MASK; + } + } + ANATOP_AI_Write(kAI_Itf_400m, kAI_RCOSC400M_CTRL3, tmp32); +} + +/*! + * brief Set the count for the locked 1MHz clock out. + * + * param count Used to set the desired target for the locked 1MHz clock out, the value in number of clock cycles of the + * osc_out_400M per divided ref_clk. + */ +void CLOCK_OSC_SetLocked1MHzCount(uint16_t count) +{ + uint32_t tmp32; + uint16_t targetCount; + uint16_t hystMinus; + uint16_t diffCount; + + tmp32 = ANATOP_AI_Read(kAI_Itf_400m, kAI_RCOSC400M_CTRL1); + targetCount = (uint16_t)((tmp32 & AI_RCOSC400M_CTRL1_TARGET_COUNT_MASK) >> AI_RCOSC400M_CTRL1_TARGET_COUNT_SHIFT); + hystMinus = (uint16_t)((tmp32 & AI_RCOSC400M_CTRL1_HYST_MINUS_MASK) >> AI_RCOSC400M_CTRL1_HYST_MINUS_SHIFT); + diffCount = targetCount - hystMinus - count; + + /* The count for the locked 1MHz clock should be 4 to 8 counts less than CTRL[TARGET_COUNT] - CTRL1[HYST_MINUS]. */ + if ((diffCount >= 4U) && (diffCount <= 8U)) + { + tmp32 = (tmp32 & ~AI_RCOSC400M_CTRL3_COUNT_1M_CLK_MASK) | AI_RCOSC400M_CTRL3_COUNT_1M_CLK(count); + ANATOP_AI_Write(kAI_Itf_400m, kAI_RCOSC400M_CTRL3, tmp32); + } +} + +/*! + * brief Check the error flag for locked 1MHz clock out. + * + * return The error flag for locked 1MHz clock out. + * - \b true The count value has been reached within one diviced ref clock period + * - \b false No effect. + */ +bool CLOCK_OSC_CheckLocked1MHzErrorFlag(void) +{ + uint32_t tmp32; + + tmp32 = ANATOP_AI_Read(kAI_Itf_400m, kAI_RCOSC400M_STAT0); + + return ((tmp32 & AI_RCOSC400M_STAT0_CLK1M_ERR_MASK) == AI_RCOSC400M_STAT0_CLK1M_ERR_MASK); +} + +/*! + * brief Clear the error flag for locked 1MHz clock out. + */ +void CLOCK_OSC_ClearLocked1MHzErrorFlag(void) +{ + ANATOP_AI_Write(kAI_Itf_400m, kAI_RCOSC400M_CTRL3_SET, AI_RCOSC400M_CTRL3_CLR_ERR_MASK); +} + +/*! + * brief Get current count for the fast clock during the tune process. + * + * return The current count for the fast clock. + */ +uint16_t CLOCK_OSC_GetCurrentOscRc400MFastClockCount(void) +{ + uint32_t tmp32; + + tmp32 = ANATOP_AI_Read(kAI_Itf_400m, kAI_RCOSC400M_STAT1); + + return (uint16_t)((tmp32 & AI_RCOSC400M_STAT1_CURR_COUNT_VAL_MASK) >> AI_RCOSC400M_STAT1_CURR_COUNT_VAL_SHIFT); +} + +/*! + * brief Get current tune value used by oscillator during tune process. + * + * return The current tune value. + */ +uint8_t CLOCK_OSC_GetCurrentOscRc400MTuneValue(void) +{ + uint32_t tmp32; + + tmp32 = ANATOP_AI_Read(kAI_Itf_400m, kAI_RCOSC400M_STAT2); + + return (uint8_t)((tmp32 & AI_RCOSC400M_STAT2_CURR_OSC_TUNE_VAL_MASK) >> AI_RCOSC400M_STAT2_CURR_OSC_TUNE_VAL_SHIFT); +} + +#ifndef GET_FREQ_FROM_OBS +static uint32_t CLOCK_GetAvPllFreq(clock_pll_t pll) +{ + uint32_t freq = 0; + uint32_t div; + uint32_t post_div; + double tmpDouble; + double denom; + double numer; + + assert((pll == kCLOCK_PllAudio) || (pll == kCLOCK_PllVideo)); + + div = ANATOP_AI_Read(pll == kCLOCK_PllAudio ? kAI_Itf_Audio : kAI_Itf_Video, PLL_AI_CTRL0_REG); + post_div = (div & (0xE000000UL)) >> 25UL; + div &= 0x7fUL; + denom = (double)ANATOP_AI_Read(pll == kCLOCK_PllAudio ? kAI_Itf_Audio : kAI_Itf_Video, PLL_AI_CTRL3_REG); + numer = (double)ANATOP_AI_Read(pll == kCLOCK_PllAudio ? kAI_Itf_Audio : kAI_Itf_Video, PLL_AI_CTRL2_REG); + + tmpDouble = ((double)XTAL_FREQ * ((double)div + (numer / denom)) / (double)(uint32_t)(1UL << post_div)); + freq = (uint32_t)tmpDouble; + + return freq; +} +#endif + +uint32_t CLOCK_GetPllFreq(clock_pll_t pll) +{ + uint32_t freq = 0; +#ifndef GET_FREQ_FROM_OBS + uint32_t divSelect, postDiv; +#endif + + switch (pll) + { + case kCLOCK_PllArm: +#ifndef GET_FREQ_FROM_OBS + divSelect = (ANADIG_PLL->ARM_PLL_CTRL & ANADIG_PLL_ARM_PLL_CTRL_DIV_SELECT_MASK) >> + ANADIG_PLL_ARM_PLL_CTRL_DIV_SELECT_SHIFT; + postDiv = (ANADIG_PLL->ARM_PLL_CTRL & ANADIG_PLL_ARM_PLL_CTRL_POST_DIV_SEL_MASK) >> + ANADIG_PLL_ARM_PLL_CTRL_POST_DIV_SEL_SHIFT; + postDiv = (1UL << (postDiv + 1UL)); + freq = XTAL_FREQ / (2UL * postDiv); + freq *= divSelect; +#else + freq = CLOCK_GetFreqFromObs(CCM_OBS_ARM_PLL_OUT); +#endif + break; + case kCLOCK_PllSys1: +#ifndef GET_FREQ_FROM_OBS + freq = SYS_PLL1_FREQ; +#else + freq = CLOCK_GetFreqFromObs(CCM_OBS_SYS_PLL1_OUT); +#endif + break; + case kCLOCK_PllSys2: +#ifndef GET_FREQ_FROM_OBS + freq = SYS_PLL2_FREQ; +#else + freq = CLOCK_GetFreqFromObs(CCM_OBS_SYS_PLL2_OUT); +#endif + break; + case kCLOCK_PllSys3: +#ifndef GET_FREQ_FROM_OBS + freq = SYS_PLL3_FREQ; +#else + freq = CLOCK_GetFreqFromObs(CCM_OBS_SYS_PLL3_OUT); +#endif + break; + case kCLOCK_PllAudio: +#ifndef GET_FREQ_FROM_OBS + freq = CLOCK_GetAvPllFreq(kCLOCK_PllAudio); +#else + freq = CLOCK_GetFreqFromObs(CCM_OBS_PLL_AUDIO_OUT); +#endif + break; + case kCLOCK_PllVideo: +#ifndef GET_FREQ_FROM_OBS + freq = CLOCK_GetAvPllFreq(kCLOCK_PllVideo); +#else + freq = CLOCK_GetFreqFromObs(CCM_OBS_PLL_VIDEO_OUT); +#endif + break; + default: + /* Wrong input parameter pll. */ + assert(false); + break; + } + assert(freq != 0UL); + return freq; +} + +uint32_t CLOCK_GetFreq(clock_name_t name) +{ + uint32_t freq = 0; + switch (name) + { + case kCLOCK_OscRc16M: +#ifndef GET_FREQ_FROM_OBS + freq = 16000000U; +#else + freq = CLOCK_GetFreqFromObs(CCM_OBS_OSC_RC_16M); +#endif + break; + case kCLOCK_OscRc48M: +#ifndef GET_FREQ_FROM_OBS + freq = 48000000U; +#else + freq = CLOCK_GetFreqFromObs(CCM_OBS_OSC_RC_48M); +#endif + break; + case kCLOCK_OscRc48MDiv2: +#ifndef GET_FREQ_FROM_OBS + freq = 24000000U; +#else + freq = CLOCK_GetFreqFromObs(CCM_OBS_OSC_RC_48M_DIV2); +#endif + break; + case kCLOCK_OscRc400M: +#ifndef GET_FREQ_FROM_OBS + freq = 400000000U; +#else + freq = CLOCK_GetFreqFromObs(CCM_OBS_OSC_RC_400M); +#endif + break; + case kCLOCK_Osc24MOut: + case kCLOCK_Osc24M: +#ifndef GET_FREQ_FROM_OBS + freq = 24000000U; +#else + freq = CLOCK_GetFreqFromObs(CCM_OBS_OSC_24M_OUT); +#endif + break; + case kCLOCK_ArmPllOut: + case kCLOCK_ArmPll: + freq = CLOCK_GetPllFreq(kCLOCK_PllArm); + break; + case kCLOCK_SysPll2: + case kCLOCK_SysPll2Out: + freq = CLOCK_GetPllFreq(kCLOCK_PllSys2); + break; + case kCLOCK_SysPll2Pfd0: + freq = CLOCK_GetPfdFreq(kCLOCK_PllSys2, kCLOCK_Pfd0); + break; + case kCLOCK_SysPll2Pfd1: + freq = CLOCK_GetPfdFreq(kCLOCK_PllSys2, kCLOCK_Pfd1); + break; + case kCLOCK_SysPll2Pfd2: + freq = CLOCK_GetPfdFreq(kCLOCK_PllSys2, kCLOCK_Pfd2); + break; + case kCLOCK_SysPll2Pfd3: + freq = CLOCK_GetPfdFreq(kCLOCK_PllSys2, kCLOCK_Pfd3); + break; + case kCLOCK_SysPll3Out: + case kCLOCK_SysPll3: + freq = CLOCK_GetPllFreq(kCLOCK_PllSys3); + break; + case kCLOCK_SysPll3Div2: + freq = (CLOCK_GetPllFreq(kCLOCK_PllSys3) / 2UL); + break; + case kCLOCK_SysPll3Pfd0: + freq = CLOCK_GetPfdFreq(kCLOCK_PllSys3, kCLOCK_Pfd0); + break; + case kCLOCK_SysPll3Pfd1: + freq = CLOCK_GetPfdFreq(kCLOCK_PllSys3, kCLOCK_Pfd1); + break; + case kCLOCK_SysPll3Pfd2: + freq = CLOCK_GetPfdFreq(kCLOCK_PllSys3, kCLOCK_Pfd2); + break; + case kCLOCK_SysPll3Pfd3: + freq = CLOCK_GetPfdFreq(kCLOCK_PllSys3, kCLOCK_Pfd3); + break; + case kCLOCK_SysPll1: + case kCLOCK_SysPll1Out: + freq = CLOCK_GetPllFreq(kCLOCK_PllSys1); + break; + case kCLOCK_SysPll1Div2: + freq = CLOCK_GetPllFreq(kCLOCK_PllSys1) / 2UL; + break; + case kCLOCK_SysPll1Div5: + freq = CLOCK_GetPllFreq(kCLOCK_PllSys1) / 5UL; + break; + case kCLOCK_AudioPll: + case kCLOCK_AudioPllOut: + freq = CLOCK_GetPllFreq(kCLOCK_PllAudio); + break; + case kCLOCK_VideoPll: + case kCLOCK_VideoPllOut: + freq = CLOCK_GetPllFreq(kCLOCK_PllVideo); + break; + case kCLOCK_CpuClk: + case kCLOCK_CoreSysClk: + freq = CLOCK_GetCpuClkFreq(); + break; + default: + /* Wrong input parameter name. */ + assert(false); + break; + } + assert(freq != 0UL); + return freq; +} + +void CLOCK_SetGroupConfig(clock_group_t group, const clock_group_config_t *config) +{ + assert(group < kCLOCK_Group_Last); + + CCM->CLOCK_GROUP[group].CONTROL = ((config->clockOff ? CCM_CLOCK_GROUP_CONTROL_OFF_MASK : 0UL) | + ((uint32_t)config->resetDiv << CCM_CLOCK_GROUP_CONTROL_RSTDIV_SHIFT) | + (config->div0 << CCM_CLOCK_GROUP_CONTROL_DIV0_SHIFT)); +} + +void CLOCK_OSC_TrimOscRc400M(bool enable, bool bypass, uint16_t trim) +{ + if (enable) + { + ANADIG_MISC->VDDLPSR_AI400M_CTRL = 0x20UL; + ANADIG_MISC->VDDLPSR_AI400M_WDATA = ((uint32_t)bypass << 10UL) | ((uint32_t)trim << 24UL); + ANADIG_MISC->VDDLPSR_AI400M_CTRL |= 0x100UL; + SDK_DelayAtLeastUs(1, SDK_DEVICE_MAXIMUM_CPU_CLOCK_FREQUENCY); + ANADIG_MISC->VDDLPSR_AI400M_CTRL &= ~0x100UL; + } +} + +void CLOCK_OSC_EnableOscRc400M(void) +{ + ANADIG_OSC->OSC_400M_CTRL1 &= ~ANADIG_OSC_OSC_400M_CTRL1_PWD_MASK; + ANADIG_OSC->OSC_400M_CTRL2 |= ANADIG_OSC_OSC_400M_CTRL2_ENABLE_CLK_MASK; +} + +uint32_t CLOCK_GetFreqFromObs(uint32_t obsSigIndex, uint32_t obsIndex) +{ + CCM_OBS->OBSERVE[obsIndex].CONTROL = CCM_OBS_OBSERVE_CONTROL_OFF_MASK; /* turn off detect */ + CCM_OBS->OBSERVE[obsIndex].CONTROL_SET = CCM_OBS_OBSERVE_CONTROL_RESET_MASK; /* reset slice */ + CCM_OBS->OBSERVE[obsIndex].CONTROL_CLR = CCM_OBS_OBSERVE_CONTROL_RAW_MASK; /* select raw obsSigIndex */ + CCM_OBS->OBSERVE[obsIndex].CONTROL &= ~CCM_OBS_OBSERVE_CONTROL_SELECT_MASK; /* Select observed obsSigIndex */ + CCM_OBS->OBSERVE[obsIndex].CONTROL |= CCM_OBS_OBSERVE_CONTROL_SELECT(obsSigIndex) | + CCM_OBS_OBSERVE_CONTROL_DIVIDE(CCM_OBS_DIV); /* turn on detection */ + CCM_OBS->OBSERVE[obsIndex].CONTROL_CLR = + CCM_OBS_OBSERVE_CONTROL_RESET_MASK | CCM_OBS_OBSERVE_CONTROL_OFF_MASK; /* unreset and turn on detect */ + + while (CCM_OBS->OBSERVE[obsIndex].FREQUENCY_CURRENT == 0UL) + { + } + + return (CCM_OBS->OBSERVE[obsIndex].FREQUENCY_CURRENT * ((uint32_t)CCM_OBS_DIV + 1UL)); +} + +/*! brief Enable USB HS clock. + * + * This function only enables the access to USB HS prepheral, upper layer + * should first call the ref CLOCK_EnableUsbhs0PhyPllClock to enable the PHY + * clock to use USB HS. + * + * param src USB HS does not care about the clock source, here must be ref kCLOCK_UsbSrcUnused. + * param freq USB HS does not care about the clock source, so this parameter is ignored. + * retval true The clock is set successfully. + * retval false The clock source is invalid to get proper USB HS clock. + */ +bool CLOCK_EnableUsbhs0Clock(clock_usb_src_t src, uint32_t freq) +{ + return true; +} +/*! brief Enable USB HS PHY PLL clock. + * + * This function enables the internal 480MHz USB PHY PLL clock. + * + * param src USB HS PHY PLL clock source. + * param freq The frequency specified by src. + * retval true The clock is set successfully. + * retval false The clock source is invalid to get proper USB HS clock. + */ +bool CLOCK_EnableUsbhs0PhyPllClock(clock_usb_phy_src_t src, uint32_t freq) +{ + uint32_t phyPllDiv = 0U; + uint16_t multiplier = 0U; + bool err = false; + CLOCK_EnableClock(kCLOCK_Usb); + + USBPHY1->CTRL_CLR = USBPHY_CTRL_SFTRST_MASK; + + USBPHY1->PLL_SIC_SET = (USBPHY_PLL_SIC_PLL_POWER(1) | USBPHY_PLL_SIC_PLL_REG_ENABLE_MASK); + if ((480000000UL % freq) != 0UL) + { + return false; + } + multiplier = (uint16_t)(480000000UL / freq); + + switch (multiplier) + { + case 13: + { + phyPllDiv = USBPHY_PLL_SIC_PLL_DIV_SEL(0U); + break; + } + case 15: + { + phyPllDiv = USBPHY_PLL_SIC_PLL_DIV_SEL(1U); + break; + } + case 16: + { + phyPllDiv = USBPHY_PLL_SIC_PLL_DIV_SEL(2U); + break; + } + case 20: + { + phyPllDiv = USBPHY_PLL_SIC_PLL_DIV_SEL(3U); + break; + } + case 22: + { + phyPllDiv = USBPHY_PLL_SIC_PLL_DIV_SEL(4U); + break; + } + case 25: + { + phyPllDiv = USBPHY_PLL_SIC_PLL_DIV_SEL(5U); + break; + } + case 30: + { + phyPllDiv = USBPHY_PLL_SIC_PLL_DIV_SEL(6U); + break; + } + case 240: + { + phyPllDiv = USBPHY_PLL_SIC_PLL_DIV_SEL(7U); + break; + } + default: + { + err = true; + break; + } + } + + if (err) + { + return false; + } + + USBPHY1->PLL_SIC = (USBPHY1->PLL_SIC & ~(USBPHY_PLL_SIC_PLL_DIV_SEL_MASK)) | phyPllDiv; + + USBPHY1->PLL_SIC_CLR = USBPHY_PLL_SIC_PLL_BYPASS_MASK; + USBPHY1->PLL_SIC_SET = (USBPHY_PLL_SIC_PLL_EN_USB_CLKS_MASK); + + USBPHY1->CTRL_CLR = USBPHY_CTRL_CLR_CLKGATE_MASK; + USBPHY1->PWD_SET = 0x0; + + while (0UL == (USBPHY1->PLL_SIC & USBPHY_PLL_SIC_PLL_LOCK_MASK)) + { + } + return true; +} +/*! brief Enable USB HS clock. + * + * This function only enables the access to USB HS prepheral, upper layer + * should first call the ref CLOCK_EnableUsbhs0PhyPllClock to enable the PHY + * clock to use USB HS. + * + * param src USB HS does not care about the clock source, here must be ref kCLOCK_UsbSrcUnused. + * param freq USB HS does not care about the clock source, so this parameter is ignored. + * retval true The clock is set successfully. + * retval false The clock source is invalid to get proper USB HS clock. + */ +/*! brief Disable USB HS PHY PLL clock. + * + * This function disables USB HS PHY PLL clock. + */ +void CLOCK_DisableUsbhs0PhyPllClock(void) +{ + USBPHY1->PLL_SIC_CLR = (USBPHY_PLL_SIC_PLL_EN_USB_CLKS_MASK); + USBPHY1->CTRL |= USBPHY_CTRL_CLKGATE_MASK; /* Set to 1U to gate clocks */ +} +bool CLOCK_EnableUsbhs1Clock(clock_usb_src_t src, uint32_t freq) +{ + return true; +} +bool CLOCK_EnableUsbhs1PhyPllClock(clock_usb_phy_src_t src, uint32_t freq) +{ + uint32_t phyPllDiv = 0U; + uint16_t multiplier = 0U; + bool err = false; + CLOCK_EnableClock(kCLOCK_Usb); + + USBPHY2->CTRL_CLR = USBPHY_CTRL_SFTRST_MASK; + + USBPHY2->PLL_SIC_SET = (USBPHY_PLL_SIC_PLL_POWER(1) | USBPHY_PLL_SIC_PLL_REG_ENABLE_MASK); + if ((480000000UL % freq) != 0UL) + { + return false; + } + multiplier = (uint16_t)(uint32_t)(480000000UL / freq); + + switch (multiplier) + { + case 13: + { + phyPllDiv = USBPHY_PLL_SIC_PLL_DIV_SEL(0U); + break; + } + case 15: + { + phyPllDiv = USBPHY_PLL_SIC_PLL_DIV_SEL(1U); + break; + } + case 16: + { + phyPllDiv = USBPHY_PLL_SIC_PLL_DIV_SEL(2U); + break; + } + case 20: + { + phyPllDiv = USBPHY_PLL_SIC_PLL_DIV_SEL(3U); + break; + } + case 22: + { + phyPllDiv = USBPHY_PLL_SIC_PLL_DIV_SEL(4U); + break; + } + case 25: + { + phyPllDiv = USBPHY_PLL_SIC_PLL_DIV_SEL(5U); + break; + } + case 30: + { + phyPllDiv = USBPHY_PLL_SIC_PLL_DIV_SEL(6U); + break; + } + case 240: + { + phyPllDiv = USBPHY_PLL_SIC_PLL_DIV_SEL(7U); + break; + } + default: + { + err = true; + break; + } + } + + if (err) + { + return false; + } + USBPHY2->PLL_SIC = (USBPHY2->PLL_SIC & ~(USBPHY_PLL_SIC_PLL_DIV_SEL_MASK)) | phyPllDiv; + + USBPHY2->PLL_SIC_CLR = USBPHY_PLL_SIC_PLL_BYPASS_MASK; + USBPHY2->PLL_SIC_SET = (USBPHY_PLL_SIC_PLL_EN_USB_CLKS_MASK); + + USBPHY2->CTRL_CLR = USBPHY_CTRL_CLR_CLKGATE_MASK; + USBPHY2->PWD_SET = 0x0; + + while (0UL == (USBPHY2->PLL_SIC & USBPHY_PLL_SIC_PLL_LOCK_MASK)) + { + } + return true; +} +/*! brief Disable USB HS PHY PLL clock. + * + * This function disables USB HS PHY PLL clock. + */ +void CLOCK_DisableUsbhs1PhyPllClock(void) +{ + USBPHY2->PLL_SIC_CLR = (USBPHY_PLL_SIC_PLL_EN_USB_CLKS_MASK); + USBPHY2->CTRL |= USBPHY_CTRL_CLKGATE_MASK; /* Set to 1U to gate clocks */ +} + +void CLOCK_OSCPLL_ControlBySetPointMode(clock_name_t name, uint16_t spValue, uint16_t stbyValue) +{ + /* Set control mode to unassigned mode. */ + CCM->OSCPLL[name].AUTHEN &= + ~(CCM_OSCPLL_AUTHEN_CPULPM_MASK | CCM_OSCPLL_AUTHEN_DOMAIN_MODE_MASK | CCM_OSCPLL_AUTHEN_SETPOINT_MODE_MASK); + /* Change SetPoint value in unassigned mode. */ + CCM->OSCPLL[name].SETPOINT = CCM_OSCPLL_SETPOINT_STANDBY(stbyValue) | CCM_OSCPLL_SETPOINT_SETPOINT(spValue); + /* Set control mode to SetPoint mode. */ + CCM->OSCPLL[name].AUTHEN |= CCM_OSCPLL_AUTHEN_SETPOINT_MODE_MASK; +} + +void CLOCK_OSCPLL_ControlByCpuLowPowerMode(clock_name_t name, + uint8_t domainId, + clock_level_t level0, + clock_level_t level1) +{ + /* Set control mode to unassigned mode. */ + CCM->OSCPLL[name].AUTHEN &= + ~(CCM_OSCPLL_AUTHEN_SETPOINT_MODE_MASK | CCM_OSCPLL_AUTHEN_DOMAIN_MODE_MASK | CCM_OSCPLL_AUTHEN_CPULPM_MASK); + /* Change clock depend level for each domain in unassigned mode. */ + CCM->OSCPLL[name].DOMAINr = + ((((uint32_t)domainId & (1UL << 1UL)) != 0UL) ? CCM_OSCPLL_DOMAIN_LEVEL1(level1) : 0UL) | + (((domainId & (1UL << 0UL)) != 0UL) ? CCM_OSCPLL_DOMAIN_LEVEL0(level0) : 0UL); + /* Set control mode to CPU low power mode and update whitelist. */ + CCM->OSCPLL[name].AUTHEN = (CCM->OSCPLL[name].AUTHEN & ~CCM_OSCPLL_AUTHEN_WHITE_LIST_MASK) | + CCM_OSCPLL_AUTHEN_CPULPM_MASK | CCM_OSCPLL_AUTHEN_WHITE_LIST(domainId); +} + +void CLOCK_ROOT_ControlBySetPointMode(clock_root_t name, const clock_root_setpoint_config_t *spTable) +{ + uint8_t i; + + /* Set control mode to unassigned mode. */ + CLOCK_ROOT_ControlByUnassignedMode(name); + /* Change SetPoint value in unassigned mode. */ + for (i = 0U; i < 16U; i++) + { + CLOCK_ROOT_ConfigSetPoint(name, i, &spTable[i]); + } + /* Set control mode to SetPoint mode. */ + CLOCK_ROOT_EnableSetPointControl(name); +} + +void CLOCK_LPCG_ControlBySetPointMode(clock_lpcg_t name, uint16_t spValue, uint16_t stbyValue) +{ + /* Set control mode to unassigned mode. */ + CCM->LPCG[name].AUTHEN &= + ~(CCM_LPCG_AUTHEN_CPULPM_MASK | CCM_LPCG_AUTHEN_DOMAIN_MODE_MASK | CCM_LPCG_AUTHEN_SETPOINT_MODE_MASK); + /* Change SetPoint value in unassigned mode. */ + CCM->LPCG[name].SETPOINT = CCM_LPCG_SETPOINT_STANDBY(stbyValue) | CCM_LPCG_SETPOINT_SETPOINT(spValue); + /* Set control mode to SetPoint mode. */ + CCM->LPCG[name].AUTHEN |= CCM_LPCG_AUTHEN_SETPOINT_MODE_MASK; +} + +void CLOCK_LPCG_ControlByCpuLowPowerMode(clock_lpcg_t name, + uint8_t domainId, + clock_level_t level0, + clock_level_t level1) +{ + /* Set control mode to unassigned mode. */ + CCM->LPCG[name].AUTHEN &= + ~(CCM_LPCG_AUTHEN_SETPOINT_MODE_MASK | CCM_LPCG_AUTHEN_DOMAIN_MODE_MASK | CCM_LPCG_AUTHEN_CPULPM_MASK); + /* Change clock depend level for each domain in unassigned mode. */ + CCM->LPCG[name].DOMAINr = ((((uint32_t)domainId & (1UL << 1UL)) != 0UL) ? CCM_LPCG_DOMAIN_LEVEL1(level1) : 0UL) | + ((((uint32_t)domainId & (1UL << 0UL)) != 0UL) ? CCM_LPCG_DOMAIN_LEVEL0(level0) : 0UL); + /* Set control mode to CPU low power mode and update whitelist. */ + CCM->LPCG[name].AUTHEN = (CCM->LPCG[name].AUTHEN & ~CCM_LPCG_AUTHEN_WHITE_LIST_MASK) | CCM_LPCG_AUTHEN_CPULPM_MASK | + CCM_LPCG_AUTHEN_WHITE_LIST(domainId); +} diff --git a/ports/mimxrt/modmachine.c b/ports/mimxrt/modmachine.c index 423a67d6095f2..b6076264031e9 100644 --- a/ports/mimxrt/modmachine.c +++ b/ports/mimxrt/modmachine.c @@ -5,6 +5,8 @@ * * Copyright (c) 2019 Damien P. George * Copyright (c) 2020 Jim Mussared + * Copyright (c) 2021 ctd. Robert Hammelrath + * Copyright (c) 2023 David Casier * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -41,10 +43,12 @@ #if FSL_FEATURE_BOOT_ROM_HAS_ROMAPI #include "fsl_romapi.h" #endif +#include "fsl_dcdc.h" #if MICROPY_PY_MACHINE #include CPU_HEADER_H +#include CLOCK_CONFIG_H typedef enum { MP_PWRON_RESET = 1, @@ -87,10 +91,81 @@ STATIC mp_obj_t machine_reset_cause(void) { } STATIC MP_DEFINE_CONST_FUN_OBJ_0(machine_reset_cause_obj, machine_reset_cause); -STATIC mp_obj_t machine_freq(void) { - return MP_OBJ_NEW_SMALL_INT(mp_hal_get_cpu_freq()); +STATIC mp_obj_t machine_freq(size_t n_args, const mp_obj_t *args) { + if (n_args == 0) { + return MP_OBJ_NEW_SMALL_INT(mp_hal_get_cpu_freq()); + } else { + uint32_t freq = mp_obj_get_int(args[0]); + #if defined(MIMXRT106x_SERIES) || defined(MIMXRT105x_SERIES) + clock_arm_pll_config_t armPllConfig_BOARD_BootClockRUN = { + .loopDivider = 100, // PLL loop divider, Fout = Fin * loopDivider / 2 + .src = 0, // Bypass clock source, 0 - OSC 24M, 1 - CLK1_P and CLK1_N + }; + if (freq == (BOARD_BOOTCLOCKRUN_CORE_CLOCK / 4)) { + armPllConfig_BOARD_BootClockRUN.loopDivider = 75; + CLOCK_InitArmPll(&armPllConfig_BOARD_BootClockRUN); + CLOCK_SetDiv(kCLOCK_ArmDiv, 5); + CLOCK_SetDiv(kCLOCK_IpgDiv, 0); + // Setting the VDD_SOC to 1.15V (< 528Mhz) + DCDC_AdjustRunTargetVoltage(DCDC, 0x0e); + } else if (freq == (BOARD_BOOTCLOCKRUN_CORE_CLOCK / 2)) { + armPllConfig_BOARD_BootClockRUN.loopDivider = 75; + CLOCK_InitArmPll(&armPllConfig_BOARD_BootClockRUN); + CLOCK_SetDiv(kCLOCK_ArmDiv, 2); + CLOCK_SetDiv(kCLOCK_IpgDiv, 1); + // Setting the VDD_SOC to 1.15V (< 528Mhz) + DCDC_AdjustRunTargetVoltage(DCDC, 0x0e); + } else if (freq == BOARD_BOOTCLOCKRUN_CORE_CLOCK) { + // Setting the VDD_SOC to 1.275V. for full speed. + DCDC_AdjustRunTargetVoltage(DCDC, 0x13); + armPllConfig_BOARD_BootClockRUN.loopDivider = 100; + CLOCK_InitArmPll(&armPllConfig_BOARD_BootClockRUN); + CLOCK_SetDiv(kCLOCK_IpgDiv, 3); + CLOCK_SetDiv(kCLOCK_ArmDiv, 1); + } else { + mp_raise_ValueError(MP_ERROR_TEXT("invalid frequency")); + } + #elif defined(MIMXRT102x_SERIES) || defined(MIMXRT101x_SERIES) + if (freq == (BOARD_BOOTCLOCKRUN_CORE_CLOCK / 4)) { + CLOCK_SetDiv(kCLOCK_AhbDiv, 3); + CLOCK_SetDiv(kCLOCK_IpgDiv, 0); + // Setting the VDD_SOC to 1.15V (< 528Mhz) + DCDC_AdjustRunTargetVoltage(DCDC, 0x0e); + } else if (freq == (BOARD_BOOTCLOCKRUN_CORE_CLOCK / 2)) { + CLOCK_SetDiv(kCLOCK_AhbDiv, 1); + CLOCK_SetDiv(kCLOCK_IpgDiv, 1); + DCDC_AdjustRunTargetVoltage(DCDC, 0x0e); + } else if (freq == BOARD_BOOTCLOCKRUN_CORE_CLOCK) { + // Setting the VDD_SOC to 1.275V + DCDC_AdjustRunTargetVoltage(DCDC, 0x13); + CLOCK_SetDiv(kCLOCK_IpgDiv, 3); + CLOCK_SetDiv(kCLOCK_AhbDiv, 0); + } else { + mp_raise_ValueError(MP_ERROR_TEXT("invalid frequency")); + } + #elif defined(MIMXRT117x_SERIES) + if (freq > 100000000) { + freq /= 1000000; + } + if (freq >= 156 && freq <= 996) { + clock_root_config_t rootCfg = {0}; + // First set the CPU to a safe source + rootCfg.mux = kCLOCK_M7_ClockRoot_MuxOscRc400M; + rootCfg.div = 1; + CLOCK_SetRootClock(kCLOCK_Root_M7, &rootCfg); + // Change the ARMPLL freq + CLOCK_InitArmPllWithFreq(freq); + // Enable it + rootCfg.mux = kCLOCK_M7_ClockRoot_MuxArmPllOut; + CLOCK_SetRootClock(kCLOCK_Root_M7, &rootCfg); + } else { + mp_raise_ValueError(MP_ERROR_TEXT("invalid frequency")); + } + #endif + return mp_const_none; + } } -MP_DEFINE_CONST_FUN_OBJ_0(machine_freq_obj, machine_freq); +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(machine_freq_obj, 0, 1, machine_freq); STATIC mp_obj_t machine_idle(void) { MICROPY_EVENT_POLL_HOOK; diff --git a/ports/mimxrt/mphalport.h b/ports/mimxrt/mphalport.h index ba7e1cfa94165..8dee49a66e527 100644 --- a/ports/mimxrt/mphalport.h +++ b/ports/mimxrt/mphalport.h @@ -94,9 +94,20 @@ static inline mp_uint_t mp_hal_ticks_cpu(void) { return DWT->CYCCNT; } +#if defined(MIMXRT101x_SERIES) +static inline mp_uint_t mp_hal_get_cpu_freq(void) { + return CLOCK_GetCoreFreq(); +} +#elif defined(MIMXRT102x_SERIES) +static inline mp_uint_t mp_hal_get_cpu_freq(void) { + return CLOCK_GetAhbFreq(); +} +#else static inline mp_uint_t mp_hal_get_cpu_freq(void) { return CLOCK_GetCpuClkFreq(); } +#endif + enum { MP_HAL_MAC_WLAN0 = 0,