From 8279e5427209ca09e76f0ace43b9e81e51523cf6 Mon Sep 17 00:00:00 2001 From: Gilles DOFFE Date: Mon, 3 Aug 2020 00:21:58 +0200 Subject: [PATCH] cpu/stm32: add clock configuration for stm32mp1 Configure stm32mp1 Cortex-M4 MCU core clock according to board configuration. Signed-off-by: Gilles DOFFE --- cpu/stm32/stmclk/stmclk_mp1.c | 220 ++++++++++++++++++++++++++++++++++ 1 file changed, 220 insertions(+) create mode 100644 cpu/stm32/stmclk/stmclk_mp1.c diff --git a/cpu/stm32/stmclk/stmclk_mp1.c b/cpu/stm32/stmclk/stmclk_mp1.c new file mode 100644 index 0000000000..1b28d628ec --- /dev/null +++ b/cpu/stm32/stmclk/stmclk_mp1.c @@ -0,0 +1,220 @@ +/* + * Copyright (C) 2020 Savoir-faire Linux + * + * This file is subject to the terms and conditions of the GNU Lesser General + * Public License v2.1. See the file LICENSE in the top level directory for more + * details. + */ + +/** + * @ingroup cpu_stm32 + * @{ + * + * @file + * @brief Implementation of STM32 clock configuration for STM32MP1 + * + * @author Gilles DOFFE + * + * @} + */ + +#include "cpu.h" +#include "stmclk.h" +#include "periph_conf.h" + +/* PLL configuration */ +#if IS_ACTIVE(CONFIG_BOARD_HAS_HSE) +#define PLL_SRC RCC_RCK3SELR_PLL3SRC_1 +#else +#define PLL_SRC RCC_RCK3SELR_PLL3SRC_0 +#endif + +/* Compute the bitfields for the PLL configuration */ +#define PLL_P (((CONFIG_CLOCK_PLL_P / 2)) \ + << RCC_PLL3CFGR2_DIVP_Pos) +#define PLL_M ((CONFIG_CLOCK_PLL_M) \ + << RCC_PLL3CFGR1_DIVM3_Pos) +#define PLL_N ((CONFIG_CLOCK_PLL_N) \ + << RCC_PLL3CFGR1_DIVN_Pos) +#define PLL_Q ((CONFIG_CLOCK_PLL_Q) \ + << RCC_PLL3CFGR2_DIVQ_Pos) +#if defined(RCC_PLL3CFGR2_DIVR) && defined(CONFIG_CLOCK_PLL_R) +#define PLL_R ((CONFIG_CLOCK_PLL_R) \ + << RCC_PLL3CFGR2_DIVR_Pos) +#else +#define PLL_R (0) +#endif + +/* Configure HLCK and PCLK prescalers */ +#if CONFIG_CLOCK_MCU_DIV == 1 +#define CLOCK_MCU_DIV (RCC_MCUDIVR_MCUDIV_0) +#elif CONFIG_CLOCK_MCU_DIV == 2 +#define CLOCK_MCU_DIV (RCC_MCUDIVR_MCUDIV_1) +#elif CONFIG_CLOCK_MCU_DIV == 4 +#define CLOCK_MCU_DIV (RCC_MCUDIVR_MCUDIV_2) +#elif CONFIG_CLOCK_MCU_DIV == 8 +#define CLOCK_MCU_DIV (RCC_MCUDIVR_MCUDIV_3) +#elif CONFIG_CLOCK_MCU_DIV == 16 +#define CLOCK_MCU_DIV (RCC_MCUDIVR_MCUDIV_4) +#elif CONFIG_CLOCK_MCU_DIV == 32 +#define CLOCK_MCU_DIV (RCC_MCUDIVR_MCUDIV_5) +#elif CONFIG_CLOCK_MCU_DIV == 64 +#define CLOCK_MCU_DIV (RCC_MCUDIVR_MCUDIV_6) +#elif CONFIG_CLOCK_MCU_DIV == 128 +#define CLOCK_MCU_DIV (RCC_MCUDIVR_MCUDIV_7) +#elif CONFIG_CLOCK_MCU_DIV == 256 +#define CLOCK_MCU_DIV (RCC_MCUDIVR_MCUDIV_8) +#elif CONFIG_CLOCK_MCU_DIV == 512 +#define CLOCK_MCU_DIV (RCC_MCUDIVR_MCUDIV_9) +#else +#error "Invalid MCU prescaler value (only 1, 2, 4, 8, 16, 32, 64, 128, 256 \ +and 512 allowed)" +#endif + +#if CONFIG_CLOCK_APB1_DIV == 1 +#define CLOCK_APB1_DIV (RCC_APB1DIVR_APB1DIV_0) +#elif CONFIG_CLOCK_APB1_DIV == 2 +#define CLOCK_APB1_DIV (RCC_APB1DIVR_APB1DIV_1) +#elif CONFIG_CLOCK_APB1_DIV == 4 +#define CLOCK_APB1_DIV (RCC_APB1DIVR_APB1DIV_2) +#elif CONFIG_CLOCK_APB1_DIV == 8 +#define CLOCK_APB1_DIV (RCC_APB1DIVR_APB1DIV_3) +#elif CONFIG_CLOCK_APB1_DIV == 16 +#define CLOCK_APB1_DIV (RCC_APB1DIVR_APB1DIV_4) +#else +#error "Invalid APB1 prescaler value (only 1, 2, 4, 8 and 16 allowed)" +#endif + +#if CONFIG_CLOCK_APB2_DIV == 1 +#define CLOCK_APB2_DIV (RCC_APB2DIVR_APB2DIV_0) +#elif CONFIG_CLOCK_APB2_DIV == 2 +#define CLOCK_APB2_DIV (RCC_APB2DIVR_APB2DIV_1) +#elif CONFIG_CLOCK_APB2_DIV == 4 +#define CLOCK_APB2_DIV (RCC_APB2DIVR_APB2DIV_2) +#elif CONFIG_CLOCK_APB2_DIV == 8 +#define CLOCK_APB2_DIV (RCC_APB2DIVR_APB2DIV_3) +#elif CONFIG_CLOCK_APB2_DIV == 16 +#define CLOCK_APB2_DIV (RCC_APB2DIVR_APB2DIV_4) +#else +#error "Invalid APB2 prescaler value (only 1, 2, 4, 8 and 16 allowed)" +#endif + +#if CONFIG_CLOCK_APB3_DIV == 1 +#define CLOCK_APB3_DIV (RCC_APB3DIVR_APB3DIV_0) +#elif CONFIG_CLOCK_APB3_DIV == 2 +#define CLOCK_APB3_DIV (RCC_APB3DIVR_APB3DIV_1) +#elif CONFIG_CLOCK_APB3_DIV == 4 +#define CLOCK_APB3_DIV (RCC_APB3DIVR_APB3DIV_2) +#elif CONFIG_CLOCK_APB3_DIV == 8 +#define CLOCK_APB3_DIV (RCC_APB3DIVR_APB3DIV_3) +#elif CONFIG_CLOCK_APB3_DIV == 16 +#define CLOCK_APB3_DIV (RCC_APB3DIVR_APB3DIV_4) +#else +#error "Invalid APB3 prescaler value (only 1, 2, 4, 8 and 16 allowed)" +#endif + +/* Check whether PLL must be enabled: + - When PLL is used as SYSCLK + - When PLLQ is required +*/ +#if IS_ACTIVE(CONFIG_USE_CLOCK_PLL) || IS_ACTIVE(CONFIG_CLOCK_ENABLE_PLLQ) +#define CONFIG_CLOCK_USE_PLL 1 +#else +#define CONFIG_CLOCK_USE_PLL 0 +#endif + +/* Check whether HSE must be enabled: + - When HSE is used as SYSCLK + - When PLL is used as SYSCLK and the board provides HSE (since HSE will be + used as PLL input clock) +*/ +#if IS_ACTIVE(CONFIG_USE_CLOCK_HSE) || \ + (IS_ACTIVE(CONFIG_BOARD_HAS_HSE) && IS_ACTIVE(CONFIG_CLOCK_USE_PLL)) +#define CONFIG_CLOCK_USE_HSE 1 +#else +#define CONFIG_CLOCK_USE_HSE 0 +#endif + +void stmclk_enable_hsi(void) +{ + RCC->OCENSETR |= RCC_OCENSETR_HSION; + while (!(RCC->OCRDYR & RCC_OCRDYR_HSIRDY)) {} +} + +static void stmclk_enable_hse(void) +{ + RCC->OCENSETR |= RCC_OCENSETR_HSEON; + while (!(RCC->OCRDYR & RCC_OCRDYR_HSERDY)) {} +} + +void stmclk_init_sysclk(void) +{ + /* disable any interrupts. Global interrupts could be enabled if this is + * called from some kind of bootloader... */ + unsigned is = irq_disable(); + RCC->MC_CIFR = 0; + + /* enable HSI clock for the duration of initialization */ + stmclk_enable_hsi(); + + /* use HSI as system clock while we do any further configuration and + * configure the AHB and APB clock dividers as configured by the board */ + /* MCUDIV */ + RCC->MCUDIVR = CLOCK_MCU_DIV; + while ((RCC->MCUDIVR & RCC_MCUDIVR_MCUDIVRDY) + != RCC_MCUDIVR_MCUDIVRDY) {} + /* APB1DIV */ + RCC->APB1DIVR = CLOCK_APB1_DIV; + while ((RCC->APB1DIVR & RCC_APB1DIVR_APB1DIVRDY) + != RCC_APB1DIVR_APB1DIVRDY) {} + /* APB2DIV */ + RCC->APB2DIVR = CLOCK_APB2_DIV; + while ((RCC->APB2DIVR & RCC_APB2DIVR_APB2DIVRDY) + != RCC_APB2DIVR_APB2DIVRDY) {} + /* APB3DIV */ + RCC->APB3DIVR = CLOCK_APB3_DIV; + while ((RCC->APB3DIVR & RCC_APB3DIVR_APB3DIVRDY) + != RCC_APB3DIVR_APB3DIVRDY) {} + + /* MCU clock source */ + RCC->MSSCKSELR = RCC_MSSCKSELR_MCUSSRC_0; /* HSI */ + while ((RCC->MSSCKSELR & RCC_MSSCKSELR_MCUSSRCRDY) + != RCC_MSSCKSELR_MCUSSRCRDY) {} + + /* disable all active clocks except HSI -> resets the clk configuration */ + RCC->OCENCLRR = ~(RCC_OCENSETR_HSION); + + /* if configured, we need to enable the HSE clock now */ + if (IS_ACTIVE(CONFIG_CLOCK_USE_HSE)) { + stmclk_enable_hse(); + } + + if (IS_ACTIVE(CONFIG_CLOCK_USE_PLL)) { + /* now we can safely configure the PLL */ + RCC->PLL3CFGR1 = (PLL_M | PLL_N); + RCC->PLL3CFGR2 = (PLL_P | PLL_Q | PLL_R); + + RCC->RCK3SELR |= PLL_SRC; + while (!(RCC->RCK3SELR & RCC_RCK3SELR_PLL3SRCRDY)) {} + + /* and start the PLL */ + RCC->PLL3CR |= (RCC_PLL3CR_DIVPEN | RCC_PLL3CR_DIVQEN + | RCC_PLL3CR_DIVREN | RCC_PLL3CR_PLLON); + while (!(RCC->PLL3CR & RCC_PLL3CR_PLL3RDY)) {} + } + + /* Configure SYSCLK */ + if (IS_ACTIVE(CONFIG_CLOCK_USE_PLL)) { + RCC->MSSCKSELR = RCC_MSSCKSELR_MCUSSRC_3; /* PLL3 */ + } + else if (IS_ACTIVE(CONFIG_CLOCK_USE_HSE)) { + RCC->MSSCKSELR = RCC_MSSCKSELR_MCUSSRC_1; /* HSE */ + } + else { + RCC->MSSCKSELR = RCC_MSSCKSELR_MCUSSRC_0; /* HSI by default */ + } + /* Wait SYSCLK to be ready */ + while (!(RCC->MSSCKSELR & RCC_MSSCKSELR_MCUSSRCRDY)) {} + + irq_restore(is); +}