/* * Copyright (C) 2014 Freie Universität Berlin * 2017-2020 Inria * 2018 Kaspar Schleiser * * 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 L0 and L1 families * * @author Hauke Petersen * @author Alexandre Abadie * * @} */ #include "cpu.h" #include "stmclk.h" #include "periph_conf.h" #if defined(CPU_FAM_STM32L1) #define REG_CIR (RCC->CIR) #else /* CPU_FAM_STM32L0 */ #define REG_CIR (RCC->CICR) #endif /* configuration of flash access cycles */ #define CLOCK_FLASH_LATENCY (FLASH_ACR_LATENCY) /* Configure the prescalers */ #define CLOCK_AHB_DIV (RCC_CFGR_HPRE_DIV1) /* HCLK = SYSCLK */ #if CONFIG_CLOCK_APB1_DIV == 1 #define CLOCK_APB1_DIV (RCC_CFGR_PPRE1_DIV1) #elif CONFIG_CLOCK_APB1_DIV == 2 #define CLOCK_APB1_DIV (RCC_CFGR_PPRE1_DIV2) #elif CONFIG_CLOCK_APB1_DIV == 4 #define CLOCK_APB1_DIV (RCC_CFGR_PPRE1_DIV4) #elif CONFIG_CLOCK_APB1_DIV == 8 #define CLOCK_APB1_DIV (RCC_CFGR_PPRE1_DIV8) #elif CONFIG_CLOCK_APB1_DIV == 16 #define CLOCK_APB1_DIV (RCC_CFGR_PPRE1_DIV16) #endif #if CONFIG_CLOCK_APB2_DIV == 1 #define CLOCK_APB2_DIV (RCC_CFGR_PPRE2_DIV1) #elif CONFIG_CLOCK_APB2_DIV == 2 #define CLOCK_APB2_DIV (RCC_CFGR_PPRE2_DIV2) #elif CONFIG_CLOCK_APB2_DIV == 4 #define CLOCK_APB2_DIV (RCC_CFGR_PPRE2_DIV4) #elif CONFIG_CLOCK_APB2_DIV == 8 #define CLOCK_APB2_DIV (RCC_CFGR_PPRE2_DIV8) #elif CONFIG_CLOCK_APB2_DIV == 16 #define CLOCK_APB2_DIV (RCC_CFGR_PPRE2_DIV16) #endif /* Check the source to be used for the PLL */ #if IS_ACTIVE(CONFIG_BOARD_HAS_HSE) #define CLOCK_PLL_SOURCE (RCC_CFGR_PLLSRC_HSE) #else /* Use HSI as PLL input */ #define CLOCK_PLL_SOURCE (RCC_CFGR_PLLSRC_HSI) #endif #if CONFIG_CLOCK_PLL_DIV == 2 #define CLOCK_PLL_DIV (RCC_CFGR_PLLDIV2) #elif CONFIG_CLOCK_PLL_DIV == 3 #define CLOCK_PLL_DIV (RCC_CFGR_PLLDIV3) #elif CONFIG_CLOCK_PLL_DIV == 4 #define CLOCK_PLL_DIV (RCC_CFGR_PLLDIV4) #else #error "Invalid PLL DIV value, only 2, 3, and 4 values are allowed." #endif #if CONFIG_CLOCK_PLL_MUL == 3 #define CLOCK_PLL_MUL (RCC_CFGR_PLLMUL3) #elif CONFIG_CLOCK_PLL_MUL == 4 #define CLOCK_PLL_MUL (RCC_CFGR_PLLMUL4) #elif CONFIG_CLOCK_PLL_MUL == 6 #define CLOCK_PLL_MUL (RCC_CFGR_PLLMUL6) #elif CONFIG_CLOCK_PLL_MUL == 8 #define CLOCK_PLL_MUL (RCC_CFGR_PLLMUL8) #elif CONFIG_CLOCK_PLL_MUL == 12 #define CLOCK_PLL_MUL (RCC_CFGR_PLLMUL12) #elif CONFIG_CLOCK_PLL_MUL == 16 #define CLOCK_PLL_MUL (RCC_CFGR_PLLMUL16) #elif CONFIG_CLOCK_PLL_MUL == 24 #define CLOCK_PLL_MUL (RCC_CFGR_PLLMUL24) #elif CONFIG_CLOCK_PLL_MUL == 32 #define CLOCK_PLL_MUL (RCC_CFGR_PLLMUL32) #elif CONFIG_CLOCK_PLL_MUL == 48 #define CLOCK_PLL_MUL (RCC_CFGR_PLLMUL48) #else #error "Invalid PLL MUL value, only 3, 4, 6, 8, 12, 16, 24, 32 and 48 values are allowed." #endif #if CONFIG_CLOCK_MSI == 65536UL #define CLOCK_MSIRANGE (RCC_ICSCR_MSIRANGE_0) #elif CONFIG_CLOCK_MSI == 131072UL #define CLOCK_MSIRANGE (RCC_ICSCR_MSIRANGE_1) #elif CONFIG_CLOCK_MSI == 262144UL #define CLOCK_MSIRANGE (RCC_ICSCR_MSIRANGE_2) #elif CONFIG_CLOCK_MSI == 524288UL #define CLOCK_MSIRANGE (RCC_ICSCR_MSIRANGE_3) #elif CONFIG_CLOCK_MSI == KHZ(1048) #define CLOCK_MSIRANGE (RCC_ICSCR_MSIRANGE_4) #elif CONFIG_CLOCK_MSI == KHZ(2097) #define CLOCK_MSIRANGE (RCC_ICSCR_MSIRANGE_5) #elif CONFIG_CLOCK_MSI == KHZ(4194) #define CLOCK_MSIRANGE (RCC_ICSCR_MSIRANGE_6) #else #error "Invalid MSI clock value" #endif /** * @brief Configure the controllers clock system * * NOTE: currently there is no timeout for initialization of PLL and other locks * -> when wrong values are chosen, the initialization could stall */ 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(); /* Disable all interrupts */ REG_CIR = 0x0; /* enable HSI clock for the duration of initialization */ stmclk_enable_hsi(); /* Reset the RCC clock configuration to the default reset state(for debug purpose) */ /* Reset MSION, HSEON, CSSON and PLLON bits */ RCC->CR &= ~(RCC_CR_MSION | RCC_CR_HSEON | RCC_CR_HSEBYP | RCC_CR_CSSON | RCC_CR_PLLON); /* use HSI as system clock while we do any further configuration and * configure the AHB and APB clock dividers as configured by the board */ RCC->CFGR = (RCC_CFGR_SW_HSI | CLOCK_AHB_DIV | CLOCK_APB1_DIV | CLOCK_APB2_DIV); #if defined(CPU_FAM_STM32L1) FLASH->ACR |= FLASH_ACR_ACC64; #endif /* Enable Prefetch Buffer */ FLASH->ACR |= FLASH_ACR_PRFTEN; /* Flash 1 wait state */ FLASH->ACR |= CLOCK_FLASH_LATENCY; /* Select the Voltage Range 1 (1.8 V) */ PWR->CR = PWR_CR_VOS_0; /* Wait Until the Voltage Regulator is ready */ while((PWR->CSR & PWR_CSR_VOSF) != 0) {} /* Only enable the HSE clock when it's provided by the board and required: - when HSE is used as system clock - when PLL is used as system clock (because HSE is used automatically as PLL input if it's available) */ if (IS_ACTIVE(CONFIG_BOARD_HAS_HSE) && (IS_ACTIVE(CONFIG_USE_CLOCK_HSE) || IS_ACTIVE(CONFIG_USE_CLOCK_PLL))) { RCC->CR |= (RCC_CR_HSEON); while (!(RCC->CR & RCC_CR_HSERDY)) {} } if (IS_ACTIVE(CONFIG_USE_CLOCK_HSE)) { /* Select HSE as system clock and configure the different prescalers */ RCC->CFGR &= ~(RCC_CFGR_SW); RCC->CFGR |= RCC_CFGR_SW_HSE; } else if (IS_ACTIVE(CONFIG_USE_CLOCK_MSI)) { /* Configure MSI range and enable it */ RCC->ICSCR |= CLOCK_MSIRANGE; RCC->CR |= (RCC_CR_MSION); while (!(RCC->CR & RCC_CR_MSIRDY)) {} /* Select MSI as system clock and configure the different prescalers */ RCC->CFGR &= ~(RCC_CFGR_SW); RCC->CFGR |= RCC_CFGR_SW_MSI; } else if (IS_ACTIVE(CONFIG_USE_CLOCK_PLL)) { /* Configure PLL clock source and configure the different prescalers */ RCC->CFGR &= ~(RCC_CFGR_PLLSRC | RCC_CFGR_PLLDIV | RCC_CFGR_PLLMUL); RCC->CFGR |= (CLOCK_PLL_SOURCE | CLOCK_PLL_DIV | CLOCK_PLL_MUL); /* Enable PLL */ RCC->CR |= RCC_CR_PLLON; /* Wait till PLL is ready */ while (!(RCC->CR & RCC_CR_PLLRDY)) {} /* Select PLL as system clock source */ RCC->CFGR &= ~((uint32_t)(RCC_CFGR_SW)); RCC->CFGR |= RCC_CFGR_SW_PLL; /* Wait till PLL is used as system clock source */ while ((RCC->CFGR & RCC_CFGR_SWS) != RCC_CFGR_SWS_PLL) {} } if (!IS_ACTIVE(CONFIG_USE_CLOCK_HSI) || (IS_ACTIVE(CONFIG_USE_CLOCK_PLL) && IS_ACTIVE(CONFIG_BOARD_HAS_HSE))) { /* Disable HSI only if not used */ stmclk_disable_hsi(); } irq_restore(is); }