/**************************************************************************** * arch/arm/src/stm32/stm32g4xxxx_rcc.c * * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. The * ASF licenses this file to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance with the * License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations * under the License. * ****************************************************************************/ /* Unless otherwise specified, when comments in this file refer to the * reference manual, that is the STM32G4 Reference Manual (RM0440 Rev 5). * * This file requires a clocking configuration, which is set in board.h, * consisting of some or all of the following defines: * * STM32_HSI_FREQUENCY should be defined to the frequency of the MCU's * high speed internal (HSI) oscillator in Hz. * * STM32_LSI_FREQUENCY should be defined to the frequency of the MCU's low * speed internal (LSI) oscillator in Hz. * * If the board has an external crystal or oscillator, STM32_BOARD_XTAL * should be defined its frequency in Hz. * * If the board has a high speed external (HSE) crystal/oscillator, * STM32_HSE_FREQUENCY should be defined to its frequency in Hz. * * If the board has a low speed external (LSE) crystal/oscillator, * STM32_LSE_FREQUENCY should be defined to its frequency in Hz. * * If the PLL is used, its source must be set by defining * STM32_PLLCFGR_PLLSRC to one of the following: RCC_PLLCFGR_PLLSRC_HSI or * RCC_PLLCFGR_PLLSRC_HSE, and each of its output clock(s) should be * enabled, as needed, by setting STM32_PLLCFGR_PLLCFG to the bitwise OR * of RCC_PLLCFGR_PLLPEN, RCC_PLLCFGR_PLLQEN, and/or RCC_PLLCFGR_PLLREN. * Its prescale division and VCO multiplication factors must be set by * defining STM32_PLLCFGR_PLLM and STM32_PLLCFGR_PLLN, using the * RCC_PLLCFGR_PLLM() and RCC_PLLCFGR_PLLN() macros, respectively. The * division factors for each enabled PLL output must be set by defining * STM32_PLLCFGR_PLLP, STM32_PLLCFGR_PLLQ, and STM32_PLLCFGR_PLLR, using * the RCC_PLLCFGR_PLLP(), RCC_PLLCFGR_PLLQ(), and RCC_PLLCFGR_PLLR() * macros, respectively. The resulting frequencies must be specified by * defining STM32_VCO_FREQUENCY, STM32_PLLP_FREQUENCY, * STM32_PLLQ_FREQUENCY, and STM32_PLLR_FREQUENCY to those frequency in * Hz, which can be calculated in terms of above-defined frequencies. * * The SYSCLK source must be given by defining STM32_SYSCLK_SW to one of * RCC_CFGR_SW_HSI, RCC_CFGR_SW_HSE, or RCC_CFGR_SW_PLL, defining * STM32_SYSCLK_SWS to one of RCC_CFGR_SWS_HSI, RCC_CFGR_SWS_HSE, or * RCC_CFGR_SWS_PLL, and defining STM32_SYSCLK_FREQUENCY to the resulting * SYSCLK frequency in Hz. For example, if SYSCLK is driven by the PLL "R" * clock, STM32_SYSCLK_FREQUENCY can be defined to STM32_PLLR_FREQUENCY, * which was defined earlier. * * The AHB clock (HCLK) must be setup by defining STM32_RCC_CFGR_HPRE to * one of RCC_CFGR_HPRE_SYSCLK, RCC_CFGR_HPRE_SYSCLKd2, * RCC_CFGR_HPRE_SYSCLKd4, RCC_CFGR_HPRE_SYSCLKd8, * RCC_CFGR_HPRE_SYSCLKd16, RCC_CFGR_HPRE_SYSCLKd64, * RCC_CFGR_HPRE_SYSCLKd128, RCC_CFGR_HPRE_SYSCLKd256, or * RCC_CFGR_HPRE_SYSCLKd512. Also, STM32_HCLK_FREQUENCY must be defined to * its resulting frequency in Hz. For example, if HCLK is driven by * SYSCLK, STM32_HCLK_FREQUENCY can be defined to STM32_SYSCLK_FREQUENCY. * * The APB1 clock (PCLK1) must be setup by defining STM32_RCC_CFGR_PPRE1 * to one of RCC_CFGR_PPRE1_HCLK, RCC_CFGR_PPRE1_HCLKd2, * RCC_CFGR_PPRE1_HCLKd4, RCC_CFGR_PPRE1_HCLKd8, RCC_CFGR_PPRE1_HCLKd16 * and defining STM32_PCLK1_FREQUENCY to its frequency in Hz. * * The APB2 clock (PCLK2) must be setup by defining STM32_RCC_CFGR_PPRE2 * to one of RCC_CFGR_PPRE2_HCLK, RCC_CFGR_PPRE2_HCLKd2, * RCC_CFGR_PPRE2_HCLKd4, RCC_CFGR_PPRE2_HCLKd8, RCC_CFGR_PPRE2_HCLKd16 * and defining STM32_PCLK2_FREQUENCY to its frequency in Hz. */ /**************************************************************************** * Included Files ****************************************************************************/ #include #include "hardware/stm32g4xxxx_pwr.h" /**************************************************************************** * Pre-processor Definitions ****************************************************************************/ #if (STM32_SYSCLK_SW == RCC_CFGR_SW_HSE) # define USE_HSE #endif #if (STM32_SYSCLK_SW == RCC_CFGR_SW_HSI) # define USE_HSI #endif #if (STM32_SYSCLK_SW == RCC_CFGR_SW_PLL) # define USE_PLL # define PLLRDY_TIMEOUT (100 * CONFIG_BOARD_LOOPSPERMSEC) # if (STM32_PLLCFGR_PLLSRC == RCC_PLLCFGR_PLLSRC_HSE) # define USE_HSE # elif (STM32_PLLCFGR_PLLSRC == RCC_PLLCFGR_PLLSRC_HSI) # define USE_HSI # else # error "STM32_SYSCLK_SW is RCC_CFGR_SW_PLL but STM32_PLLCFGR_PLLSRC is not recognized!" # endif #endif #if defined(USE_HSI) # define HSIRDY_TIMEOUT (100 * CONFIG_BOARD_LOOPSPERMSEC) #endif #if defined(USE_HSE) # define HSERDY_TIMEOUT (100 * CONFIG_BOARD_LOOPSPERMSEC) #endif /* Per the reference manual: * * Choose PWR VOS range setting and R1MODE based on SYSCLK frequency (see * section 6.1.5, Dynamic voltage scaling management). * * Choose number of FLASH wait states according to CPU clock (HCLK) * frequency (see section 3.3.3, Read access latency, and Table 9 in that * section. * * This will define FLASH_ACR_LATENCY_SETTING, PWR_CR1_VOS_RANGE_SETTING, * and PWR_CR5_R1MODE_SETTING to the appropriate settings. */ #if (STM32_SYSCLK_FREQUENCY > 26000000) # define PWR_CR1_VOS_RANGE_SETTING PWR_CR1_VOS_RANGE_1 # if (STM32_SYSCLK_FREQUENCY > 150000000) /* Table 9, left column, "Vcore Range 1 boost mode" */ # define PWR_CR5_R1MODE_SETTING 0 # if (STM32_HCLK_FREQUENCY <= 34000000) # define FLASH_ACR_LATENCY_SETTING FLASH_ACR_LATENCY_0 # elif (STM32_HCLK_FREQUENCY <= 68000000) # define FLASH_ACR_LATENCY_SETTING FLASH_ACR_LATENCY_1 # elif (STM32_HCLK_FREQUENCY <= 102000000) # define FLASH_ACR_LATENCY_SETTING FLASH_ACR_LATENCY_2 # elif (STM32_HCLK_FREQUENCY <= 136000000) # define FLASH_ACR_LATENCY_SETTING FLASH_ACR_LATENCY_3 # elif (STM32_HCLK_FREQUENCY <= 170000000) # define FLASH_ACR_LATENCY_SETTING FLASH_ACR_LATENCY_4 # else # error "Incorrect STM32_HCLK_FREQUENCY (Vcore range 1 boost mode)!" # endif # else /* Table 9, middle column, "Vcore Range 1 normal mode" */ # define PWR_CR5_R1MODE_SETTING PWR_CR5_R1MODE # if (STM32_HCLK_FREQUENCY <= 30000000) # define FLASH_ACR_LATENCY_SETTING FLASH_ACR_LATENCY_0 # elif (STM32_HCLK_FREQUENCY <= 60000000) # define FLASH_ACR_LATENCY_SETTING FLASH_ACR_LATENCY_1 # elif (STM32_HCLK_FREQUENCY <= 90000000) # define FLASH_ACR_LATENCY_SETTING FLASH_ACR_LATENCY_2 # elif (STM32_HCLK_FREQUENCY <= 120000000) # define FLASH_ACR_LATENCY_SETTING FLASH_ACR_LATENCY_3 # elif (STM32_HCLK_FREQUENCY <= 150000000) # define FLASH_ACR_LATENCY_SETTING FLASH_ACR_LATENCY_4 # else # error "Incorrect STM32_HCLK_FREQUENCY (Vcore range 1 normal mode)!" # endif # endif #else /* Table 9, right column, "Vcore Range 2" */ # define PWR_CR1_VOS_RANGE_SETTING PWR_CR1_VOS_RANGE_2; # define PWR_CR5_R1MODE_SETTING 0 # if (STM32_HCLK_FREQUENCY <= 12000000) # define FLASH_ACR_LATENCY_SETTING FLASH_ACR_LATENCY_0 # elif (STM32_HCLK_FREQUENCY <= 24000000) # define FLASH_ACR_LATENCY_SETTING FLASH_ACR_LATENCY_1 # elif (STM32_HCLK_FREQUENCY <= 26000000) # define FLASH_ACR_LATENCY_SETTING FLASH_ACR_LATENCY_2 # else # error "Incorrect STM32_HCLK_FREQUENCY! (Vcore range 2)" # endif #endif /**************************************************************************** * Private Types ****************************************************************************/ /**************************************************************************** * Private Function Prototypes ****************************************************************************/ /**************************************************************************** * Private Data ****************************************************************************/ /**************************************************************************** * Public Data ****************************************************************************/ /**************************************************************************** * Private Functions ****************************************************************************/ /**************************************************************************** * Name: rcc_reset * * Description: * Put all RCC registers in reset state. * ****************************************************************************/ static inline void rcc_reset(void) { uint32_t regval; /* Set HSION bit to the reset value and wait until HSI ready */ regval = getreg32(STM32_RCC_CR); regval |= RCC_CR_HSION; putreg32(regval, STM32_RCC_CR); while ((getreg32(STM32_RCC_CR) & RCC_CR_HSIRDY) != RCC_CR_HSIRDY) { } /* Reset HSI trimming */ regval = getreg32(STM32_RCC_ICSCR); regval &= ~(RCC_ICSCR_HSITRIM_MASK); regval |= RCC_ICSCR_HSITRIM_RESET; putreg32(regval, STM32_RCC_ICSCR); /* Reset CFGR register */ regval = getreg32(STM32_RCC_CFGR); regval &= RCC_CFGR_RESERVED_MASK; regval |= RCC_CFGR_RESET; putreg32(regval, STM32_RCC_CFGR); /* Wait until HSI is being used as the system clock, else we cannot clear * the HSEON, HSEBYP, and PLLON bits. */ while ((getreg32(STM32_RCC_CFGR) & RCC_CFGR_SWS_MASK) != RCC_CFGR_SWS_HSI) { } /* Clear the HSEON, HSEBYP, and PLLON bits */ regval = getreg32(STM32_RCC_CR); regval &= ~(RCC_CR_HSEON | RCC_CR_HSEBYP | RCC_CR_PLLON); putreg32(regval, STM32_RCC_CR); /* Wait until PLL is OFF, else we cannot change some of the bits in the * PLLCFGR register. */ while ((getreg32(STM32_RCC_CR) & RCC_CR_PLLRDY) != 0) { } /* Reset PLLCFGR register */ regval = getreg32(STM32_RCC_PLLCFGR); regval &= RCC_PLLCFGR_RESERVED_MASK; regval |= RCC_PLLCFGR_RESET; putreg32(RCC_PLLCFGR_RESET, STM32_RCC_PLLCFGR); /* Disable all RCC interrupts and clear any previously pended ones */ putreg32(0, STM32_RCC_CIER); putreg32(0xffffffff, STM32_RCC_CICR); } /**************************************************************************** * Name: rcc_enableahb1 * * Description: * Enable selected AHB1 peripherals. * ****************************************************************************/ static inline void rcc_enableahb1(void) { uint32_t regval; regval = getreg32(STM32_RCC_AHB1ENR); regval |= RCC_AHB1ENR_FLASHEN; #if defined(CONFIG_STM32_DMA1) regval |= RCC_AHB1ENR_DMA1EN; #endif #if defined(CONFIG_STM32_DMA2) regval |= RCC_AHB1ENR_DMA2EN; #endif #if defined(CONFIG_STM32_DMA1) || defined(CONFIG_STM32_DMA2) regval |= RCC_AHB1ENR_DMAMUX1EN; #endif #if defined(CONFIG_STM32_CORDIC) regval |= RCC_AHB1ENR_CORDICEN; #endif #if defined(CONFIG_STM32_FMAC) regval |= RCC_AHB1ENR_FMACEN; #endif #if defined(CONFIG_STM32_CRC) regval |= RCC_AHB1ENR_CRCEN; #endif putreg32(regval, STM32_RCC_AHB1ENR); } /**************************************************************************** * Name: rcc_enableahb2 * * Description: * Enable selected AHB2 peripherals. * ****************************************************************************/ static inline void rcc_enableahb2(void) { uint32_t regval; regval = getreg32(STM32_RCC_AHB2ENR); #if (STM32_NGPIO_PORTS > 0) regval |= (RCC_AHB2ENR_GPIOAEN # if (STM32_NGPIO_PORTS > 1) | RCC_AHB2ENR_GPIOBEN # endif # if (STM32_NGPIO_PORTS > 2) | RCC_AHB2ENR_GPIOCEN # endif # if (STM32_NGPIO_PORTS > 3) | RCC_AHB2ENR_GPIODEN # endif # if (STM32_NGPIO_PORTS > 4) | RCC_AHB2ENR_GPIOEEN # endif # if (STM32_NGPIO_PORTS > 5) | RCC_AHB2ENR_GPIOFEN # endif # if (STM32_NGPIO_PORTS > 6) | RCC_AHB2ENR_GPIOGEN # endif ); #endif #if defined(CONFIG_STM32_ADC1) || defined(CONFIG_STM32_ADC2) regval |= RCC_AHB2ENR_ADC12EN; #endif #if defined(CONFIG_STM32_ADC3) || defined(CONFIG_STM32_ADC4) || \ defined(CONFIG_STM32_ADC5) regval |= RCC_AHB2ENR_ADC345EN; #endif #if defined(CONFIG_STM32_DAC1) regval |= RCC_AHB2ENR_DAC1EN; #endif #if defined(CONFIG_STM32_DAC2) regval |= RCC_AHB2ENR_DAC2EN; #endif #if defined(CONFIG_STM32_DAC3) regval |= RCC_AHB2ENR_DAC3EN; #endif #if defined(CONFIG_STM32_DAC4) regval |= RCC_AHB2ENR_DAC4EN; #endif #if defined(CONFIG_STM32_RNG) regval |= RCC_AHB2ENR_RNGEN; #endif putreg32(regval, STM32_RCC_AHB2ENR); } /**************************************************************************** * Name: rcc_enableahb3 * * Description: * Enable selected AHB3 peripherals. * ****************************************************************************/ static inline void rcc_enableahb3(void) { uint32_t regval; regval = getreg32(STM32_RCC_AHB3ENR); #if defined(CONFIG_STM32_FMC) regval |= RCC_AHB3ENR_FMCEN; #endif #if defined(CONFIG_STM32_QSPI) regval |= RCC_AHB3ENR_QSPIEN; #endif putreg32(regval, STM32_RCC_AHB3ENR); } /**************************************************************************** * Name: rcc_enableapb1 * * Description: * Enable selected APB1 peripherals. * ****************************************************************************/ static inline void rcc_enableapb1(void) { uint32_t regval; /* Careful: There are two separate registers for enabling APB1 * peripherals. Configure the first register: */ regval = getreg32(STM32_RCC_APB1ENR1); regval |= RCC_APB1ENR1_PWREN; #if defined(CONFIG_STM32_TIM2) regval |= RCC_APB1ENR1_TIM2EN; #endif #if defined(CONFIG_STM32_TIM3) regval |= RCC_APB1ENR1_TIM3EN; #endif #if defined(CONFIG_STM32_TIM4) regval |= RCC_APB1ENR1_TIM4EN; #endif #if defined(CONFIG_STM32_TIM5) regval |= RCC_APB1ENR1_TIM5EN; #endif #if defined(CONFIG_STM32_TIM6) regval |= RCC_APB1ENR1_TIM6EN; #endif #if defined(CONFIG_STM32_TIM7) regval |= RCC_APB1ENR1_TIM7EN; #endif #if defined(CONFIG_STM32_CRS) regval |= RCC_APB1ENR1_CRSEN; #endif #if defined(CONFIG_STM32_RTC) regval |= RCC_APB1ENR1_RTCAPBEN; #endif #if defined(CONFIG_STM32_WWDG) regval |= RCC_APB1ENR1_WWDGEN; #endif #if defined(CONFIG_STM32_SPI2) regval |= RCC_APB1ENR1_SPI2EN; #endif #if defined(CONFIG_STM32_SPI3) regval |= RCC_APB1ENR1_SPI3EN; #endif #if defined(CONFIG_STM32_USART2) regval |= RCC_APB1ENR1_USART2EN; #endif #if defined(CONFIG_STM32_USART3) regval |= RCC_APB1ENR1_USART3EN; #endif #if defined(CONFIG_STM32_UART4) regval |= RCC_APB1ENR1_UART4EN; #endif #if defined(CONFIG_STM32_UART5) regval |= RCC_APB1ENR1_UART5EN; #endif #if defined(CONFIG_STM32_I2C1) regval |= RCC_APB1ENR1_I2C1EN; #endif #if defined(CONFIG_STM32_I2C2) regval |= RCC_APB1ENR1_I2C2EN; #endif #if defined(CONFIG_STM32_USB) || defined(CONFIG_STM32_USBFS) regval |= RCC_APB1ENR1_USBEN; #endif #if defined(CONFIG_STM32_FDCAN) regval |= RCC_APB1ENR1_FDCANEN; #endif #if defined(CONFIG_STM32_I2C3) regval |= RCC_APB1ENR1_I2C3EN; #endif #if defined(CONFIG_STM32_LPTIM1) regval |= RCC_APB1ENR1_LPTIM1EN; #endif putreg32(regval, STM32_RCC_APB1ENR1); /* Now configure the second register: */ regval = getreg32(STM32_RCC_APB1ENR2); #if defined(CONFIG_STM32_LPUART1) regval |= RCC_APB1ENR2_LPUART1EN; #endif #if defined(CONFIG_STM32_I2C4) regval |= RCC_APB1ENR2_I2C4EN; #endif #if defined(CONFIG_STM32_UCPD) regval |= RCC_APB1ENR2_UCPD1EN; #endif putreg32(regval, STM32_RCC_APB1ENR2); } /**************************************************************************** * Name: rcc_enableapb2 * * Description: * Enable selected APB2 peripherals. * ****************************************************************************/ static inline void rcc_enableapb2(void) { uint32_t regval; regval = getreg32(STM32_RCC_APB2ENR); #if defined(CONFIG_STM32_SYSCFG) regval |= RCC_APB2ENR_SYSCFGEN; #endif #if defined(CONFIG_STM32_TIM1) regval |= RCC_APB2ENR_TIM1EN; #endif #if defined(CONFIG_STM32_SPI1) regval |= RCC_APB2ENR_SPI1EN; #endif #if defined(CONFIG_STM32_TIM8) regval |= RCC_APB2ENR_TIM8EN; #endif #if defined(CONFIG_STM32_USART1) regval |= RCC_APB2ENR_USART1EN; #endif #if defined(CONFIG_STM32_SPI4) regval |= RCC_APB2ENR_SPI4EN; #endif #if defined(CONFIG_STM32_TIM15) regval |= RCC_APB2ENR_TIM15EN; #endif #if defined(CONFIG_STM32_TIM16) regval |= RCC_APB2ENR_TIM16EN; #endif #if defined(CONFIG_STM32_TIM17) regval |= RCC_APB2ENR_TIM17EN; #endif #if defined(CONFIG_STM32_TIM20) regval |= RCC_APB2ENR_TIM20EN; #endif #if defined(CONFIG_STM32_SAI1) regval |= RCC_APB2ENR_SAI1EN; #endif #if defined(CONFIG_STM32_HRTIM1) regval |= RCC_APB2ENR_HRTIM1EN; #endif putreg32(regval, STM32_RCC_APB2ENR); } /**************************************************************************** * Name: stm32_rcc_enablehse * * Description: * Enable the High-Speed External (HSE) Oscillator. * ****************************************************************************/ #if defined (USE_HSE) static inline bool stm32_rcc_enablehse(void) { uint32_t regval; uint32_t timeout; /* Enable External High-Speed Clock (HSE) */ regval = getreg32(STM32_RCC_CR); #if defined(STM32_HSEBYP_ENABLE) /* May be defined in board.h header file */ regval |= RCC_CR_HSEBYP; /* Enable HSE clock bypass */ #else regval &= ~RCC_CR_HSEBYP; /* Disable HSE clock bypass */ #endif regval |= RCC_CR_HSEON; /* Enable HSE */ putreg32(regval, STM32_RCC_CR); /* Wait until the HSE is ready (or until a timeout elapsed) */ for (timeout = HSERDY_TIMEOUT; timeout > 0; timeout--) { if ((getreg32(STM32_RCC_CR) & RCC_CR_HSERDY) != 0) { /* HSE has been enabled successfully and is ready */ return true; } } /* HSE was not enabled successfully or timed out; this could * mean that the external crystal or oscillator is missing or * not working. */ return false; } #endif /* USE_HSE */ /**************************************************************************** * Name: stm32_rcc_enablehsi * * Description: * Enable the High-Speed Internal (HSI) Oscillator. * ****************************************************************************/ #if defined (USE_HSI) static inline bool stm32_rcc_enablehsi(void) { uint32_t regval; uint32_t timeout; /* Enable Internal High-Speed Clock (HSI) */ regval = getreg32(STM32_RCC_CR); regval |= RCC_CR_HSION; putreg32(regval, STM32_RCC_CR); /* Wait until the HSI is ready (or until a timeout elapsed) */ for (timeout = HSIRDY_TIMEOUT; timeout > 0; timeout--) { if ((getreg32(STM32_RCC_CR) & RCC_CR_HSIRDY) != 0) { /* HSI has been enabled successfully and is ready */ return true; } } /* HSI was not enabled successfully or timed out */ return false; } #endif /* USE_HSI */ /**************************************************************************** * Name: stm32_rcc_enablepll * * Description: * Enable the Phase Locked Loop (PLL). * ****************************************************************************/ #if defined (USE_PLL) static inline bool stm32_rcc_enablepll(void) { uint32_t regval; uint32_t timeout; /* Preserve reserved bits when altering the PLLCFGR register */ regval = getreg32(STM32_RCC_PLLCFGR); regval &= ~(RCC_PLLCFGR_RESERVED_MASK); /* Configure PLL source and enables */ regval |= STM32_PLLCFGR_PLLSRC | STM32_PLLCFGR_PLLCFG; /* Configure PLL multiplication and division factors */ regval |= STM32_PLLCFGR_PLLM | STM32_PLLCFGR_PLLN; /* Configure PLL clock outputs division factors */ regval |= STM32_PLLCFGR_PLLP | STM32_PLLCFGR_PLLQ | STM32_PLLCFGR_PLLR; /* Write PLLCFG register */ putreg32(regval, STM32_RCC_PLLCFGR); /* Enable PLL */ regval = getreg32(STM32_RCC_CR); regval |= RCC_CR_PLLON; putreg32(regval, STM32_RCC_CR); /* Wait until PLL ready (or timeout) */ for (timeout = PLLRDY_TIMEOUT; timeout > 0; timeout--) { if ((getreg32(STM32_RCC_CR) & RCC_CR_PLLRDY) != 0) { /* PLL has been enabled successfully and is ready */ return true; } } /* PLL was not enabled successfully or timed out */ return false; } #endif /* USE_PLL */ /**************************************************************************** * Name: stm32_stdclockconfig * * Description: * Called to change to new clock based on settings in board.h. * * Pre-conditions: * rcc_reset() and rcc_resetbkp() have been called and the HSI is * the MCU's SYSCLK. ****************************************************************************/ static void stm32_stdclockconfig(void) { uint32_t regval; /* REVISIT: * * (1) We only support SYSCLK from HSE, HSI, or PLL, with PLL * clocked by HSE or HSI. There is no configuration here for * LSI, LSE, or HSI48. * * (2) We do not yet explicitly disable any clocks to save power. * * (3) Do we need to enable clock(s) to any bus(es) before we can * access the PWR registers? And if so, do we need to disable * any such clock(s) before programming the clock tree? * * (4) Do we need to enable write access or unlock anything * before we can program any of the following things? */ /* Set up the power regulator per configured SYSCLK frequency. * * Before we begin, make sure voltage regulator is ready to receive * any changes by waiting until VOSF bit is cleared by hardware. * * REVISIT: This should use the implementation in stm32_pwr.c, but * it appears to be too different than this family and will need * to be refactored accordingly. It is implemented here directly as * a stopgap. */ /* REVISIT: Do we need to activate RCC_APB1ENR1_PWREN bit in * RCC_APB1ENR1? */ while ((getreg32(STM32_PWR_SR2) & PWR_SR2_VOSF) != 0) { } /* Choose the appropriate PWR VOS range and R1MODE based on the * SYSCLK we're going to configure: * * R1MODE: Enable range 1 boost mode for 150MHz < SYSCLK <= 170MHz, * disable range 1 boost mode for 26MHz < SYSCLK <= 150MHz. * * VOS: Range 1 for SYSCLK > 26MHz; range 2 for SYSCLK <= 26MHz. */ regval = getreg32(STM32_PWR_CR5); regval &= ~(PWR_CR5_R1MODE); regval |= PWR_CR5_R1MODE_SETTING; putreg32(regval, STM32_PWR_CR5); regval = getreg32(STM32_PWR_CR1); regval &= ~(PWR_CR1_VOS_MASK); regval |= PWR_CR1_VOS_RANGE_SETTING; putreg32(regval, STM32_PWR_CR1); /* Now we have to wait until VOSF bit is cleared by hardware * again */ while ((getreg32(STM32_PWR_SR2) & PWR_SR2_VOSF) != 0) { } /* Now we can program the clock tree */ #if defined(USE_HSE) /* The HSE is being used, either as input to the PLL or as SYSCLK * itself. Enable the HSE. */ if (stm32_rcc_enablehse() != true) { /* REVISIT: If we get here, timeout occurred waiting for HSE ready. * We should have some sort of mechanism by which the application * software can query whether the MCU has started up properly, so * that it could possibly report an error or at least not attempt * to work with wrong timing. Currently, as there is no mechanism * in place to do that, we do not configure the clock any further. */ return; } #endif #if defined (USE_HSI) /* The HSI is being used, either as input to the PLL or as SYSCLK * itself. Enable the HSI. */ if (stm32_rcc_enablehsi() != true) { /* REVISIT: If we get here, timeout occurred waiting for HSI ready. * We should have some sort of mechanism by which the application * software can query whether the MCU has started up properly, so * that it could possibly report an error or at least not attempt * to work with wrong timing. Currently, as there is no mechanism * in place to do that, we do not configure the clock any further. */ return; } #endif #if defined(USE_PLL) if (stm32_rcc_enablepll() != true) { /* REVISIT: If we get here, timeout occurred waiting for HSI ready. * We should have some sort of mechanism by which the application * software can query whether the MCU has started up properly, so * that it could possibly report an error or at least not attempt * to work with wrong timing. Currently, as there is no mechanism * in place to do that, we do not configure the clock any further. */ return; } #endif /* Configure FLASH wait states per the SYSCLK frequency that is about * to go into effect and enable prefetch to reduce latency due to * these wait states (ART accelerator). * * REVISIT: Should we also enable I-Cache and D-Cache? Also, the * reference manual suggests that we must read the ACR register to * make sure the latency setting has taken effect. Are we doing that * correctly? */ regval = getreg32(STM32_FLASH_ACR); regval &= ~FLASH_ACR_LATENCY_MASK; regval |= FLASH_ACR_LATENCY_SETTING; regval |= FLASH_ACR_PRFTEN; putreg32(regval, STM32_FLASH_ACR); while ((getreg32(STM32_FLASH_ACR) & FLASH_ACR_LATENCY_MASK) != FLASH_ACR_LATENCY_SETTING) { } /* Before selecting the SYSCLK source, set the HPRE, PPRE1, and PPRE2 * dividers. */ #if (STM32_SYSCLK_FREQUENCY > 150000000) && (STM32_RCC_CFGR_HPRE == RCC_CFGR_HPRE_SYSCLK) /* If SYSCLK > 150MHz, temporarily set the HCLK prescaler (RCC_CFGR_HPRE) * to divide by 2 (RCC_CFGR_HPRE_SYSCLKd2) before changing SYSCLK source * to PLL. Afterwards, (after waiting at least 1us) change back to no * division (RCC_CFGR_HPRE_SYSCLK). See reference manual, section 6.1.5. */ regval = getreg32(STM32_RCC_CFGR); regval &= ~(RCC_CFGR_HPRE_MASK); regval |= RCC_CFGR_HPRE_SYSCLKd2; putreg32(regval, STM32_RCC_CFGR); #endif /* Select the system clock source as defined in board.h. This could * be the HSI, HSE, or PLL (most likely the PLL). */ regval = getreg32(STM32_RCC_CFGR); regval &= ~(RCC_CFGR_SW_MASK); regval |= STM32_SYSCLK_SW; putreg32(regval, STM32_RCC_CFGR); /* Wait until the selected source is used as the system clock source */ while ((getreg32(STM32_RCC_CFGR) & RCC_CFGR_SWS_MASK) != STM32_SYSCLK_SWS) { } /* Before we set HCLK prescaler to the correct value, temporarily * divide APB1 and APB2 clocks by 16 to avoid problems. */ regval = getreg32(STM32_RCC_CFGR); regval &= ~(RCC_CFGR_PPRE1_MASK | RCC_CFGR_PPRE2_MASK); regval |= (RCC_CFGR_PPRE1_HCLKd16 | RCC_CFGR_PPRE2_HCLKd16); putreg32(regval, STM32_RCC_CFGR); /* Now set HCLK prescaler to the correct value */ regval = getreg32(STM32_RCC_CFGR); regval &= ~(RCC_CFGR_HPRE_MASK); regval |= STM32_RCC_CFGR_HPRE; putreg32(regval, STM32_RCC_CFGR); /* Now set APB1 and APB2 prescalers to the correct value */ regval = getreg32(STM32_RCC_CFGR); regval &= ~(RCC_CFGR_PPRE1_MASK | RCC_CFGR_PPRE2_MASK); regval |= (STM32_RCC_CFGR_PPRE1 | STM32_RCC_CFGR_PPRE2); putreg32(regval, STM32_RCC_CFGR); /* Configure FDCAN source clock */ #if defined(STM32_CCIPR_FDCANSRC) regval = getreg32(STM32_RCC_CCIPR); regval &= ~RCC_CCIPR_FDCANSEL_MASK; regval |= STM32_CCIPR_FDCANSRC; putreg32(regval, STM32_RCC_CCIPR); #endif #if defined(CONFIG_STM32_ADC1) || defined(CONFIG_STM32_ADC2) /* Configure ADC12 clock */ regval = getreg32(STM32_RCC_CCIPR); regval &= ~RCC_CCIPR_ADC12SEL_MASK; regval |= RCC_CCIPR_ADC12SEL_PLLP; putreg32(regval, STM32_RCC_CCIPR); #endif #if defined(CONFIG_STM32_ADC3) || defined(CONFIG_STM32_ADC4) || \ defined(CONFIG_STM32_ADC5) /* Configure ADC345 clock */ regval = getreg32(STM32_RCC_CCIPR); regval &= ~RCC_CCIPR_ADC345SEL_MASK; regval |= RCC_CCIPR_ADC345SEL_PLLP; putreg32(regval, STM32_RCC_CCIPR); #endif } /**************************************************************************** * Name: rcc_enableperipherals * * Description: * Enable all peripheral buses and all configured peripherals. * ****************************************************************************/ static inline void rcc_enableperipherals(void) { rcc_enableahb1(); rcc_enableahb2(); rcc_enableahb3(); rcc_enableapb1(); rcc_enableapb2(); } /**************************************************************************** * Public Functions ****************************************************************************/