nuttx/arch/arm/src/stm32/stm32g4xxxx_rcc.c
2022-11-17 19:53:53 +08:00

1006 lines
29 KiB
C

/****************************************************************************
* 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 <nuttx/config.h>
#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
****************************************************************************/