blob: e0ab416c678b6b08f5ada295e7b0326375e2bb14 [file] [log] [blame]
/*
* <h2><center>&copy; COPYRIGHT 2016 STMicroelectronics</center></h2>
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* 3. Neither the name of STMicroelectronics nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <assert.h>
#include <stm32u5xx_hal_pwr_ex.h>
#include <stm32u5xx_hal_rcc.h>
#include <stm32u5xx_hal.h>
#define IS_RCC_HCLK(__HCLK__) (((__HCLK__) == RCC_SYSCLK_DIV1) || ((__HCLK__) == RCC_SYSCLK_DIV2) || \
((__HCLK__) == RCC_SYSCLK_DIV4) || ((__HCLK__) == RCC_SYSCLK_DIV8) || \
((__HCLK__) == RCC_SYSCLK_DIV16) || ((__HCLK__) == RCC_SYSCLK_DIV64) || \
((__HCLK__) == RCC_SYSCLK_DIV128) || ((__HCLK__) == RCC_SYSCLK_DIV256) || \
((__HCLK__) == RCC_SYSCLK_DIV512))
#define IS_RCC_PCLK(__PCLK__) (((__PCLK__) == RCC_HCLK_DIV1) || ((__PCLK__) == RCC_HCLK_DIV2) || \
((__PCLK__) == RCC_HCLK_DIV4) || ((__PCLK__) == RCC_HCLK_DIV8) || \
((__PCLK__) == RCC_HCLK_DIV16))
/*
* This allows an user to have a custom clock configuration by zeroing
* every possible clock source in the syscfg.
*/
#if MYNEWT_VAL(STM32_CLOCK_MSI) || MYNEWT_VAL(STM32_CLOCK_HSE) || \
MYNEWT_VAL(STM32_CLOCK_LSE) || MYNEWT_VAL(STM32_CLOCK_HSI) || \
MYNEWT_VAL(STM32_CLOCK_HSI48) || MYNEWT_VAL(STM32_CLOCK_LSI)
#define TRNG_ENABLED (MYNEWT_VAL(TRNG) != 0)
/*
* HSI is turned on by default, but can be turned off and use HSE/HSI48 instead.
*/
#if ((MYNEWT_VAL(STM32_CLOCK_MSI) != 0) + \
(MYNEWT_VAL(STM32_CLOCK_HSE) != 0) + \
(MYNEWT_VAL(STM32_CLOCK_HSI) != 0) + \
(MYNEWT_VAL(STM32_CLOCK_HSI48) != 0)) < 1
#error "At least one of MSI, HSE, HSI or HSI48 clock sources must be enabled"
#endif
void
SystemClock_Config(void)
{
RCC_OscInitTypeDef osc_init = {};
RCC_ClkInitTypeDef clk_init = {};
HAL_StatusTypeDef status;
#if TRNG_ENABLED
RCC_PeriphCLKInitTypeDef pclk_init;
#endif
/*
* The voltage scaling allows optimizing the power consumption when the
* device is clocked below the maximum system frequency, to update the
* voltage scaling value regarding system frequency refer to product
* datasheet.
*/
__HAL_RCC_PWR_CLK_ENABLE();
HAL_PWREx_ControlVoltageScaling(MYNEWT_VAL(STM32_CLOCK_VOLTAGESCALING_CONFIG));
osc_init.OscillatorType = RCC_OSCILLATORTYPE_NONE;
/*
* LSI is used to clock the independent watchdog and optionally the RTC.
* It can be disabled per user request, but will be automatically enabled
* again when the IWDG is started.
*
* XXX currently the watchdog is not optional, so there's no point in
* disabling LSI through syscfg.
*/
osc_init.OscillatorType |= RCC_OSCILLATORTYPE_LSI;
#if (MYNEWT_VAL(STM32_CLOCK_LSI) == 0)
osc_init.LSIState = RCC_LSI_OFF;
#else
osc_init.LSIState = RCC_LSI_ON;
#endif
/*
* LSE is only used to clock the RTC.
*/
osc_init.OscillatorType |= RCC_OSCILLATORTYPE_LSE;
#if (MYNEWT_VAL(STM32_CLOCK_LSE) == 0)
osc_init.LSEState = RCC_LSE_OFF;
#elif MYNEWT_VAL(STM32_CLOCK_LSE_BYPASS)
osc_init.LSEState = RCC_LSE_BYPASS;
#else
osc_init.LSEState = RCC_LSE_ON;
#endif
/*
* MSI Oscillator
*/
#if MYNEWT_VAL(STM32_CLOCK_MSI)
#if (MYNEWT_VAL(STM32_CLOCK_MSI_CALIBRATION) > 255)
#error "Invalid MSI calibration value"
#endif
#if !IS_RCC_MSI_CLOCK_RANGE(MYNEWT_VAL(STM32_CLOCK_MSI_CLOCK_RANGE))
#error "Invalid MSI clock range"
#endif
/* NOTE: MSI can't be disabled if it's the current PLL or SYSCLK source;
* leave it untouched in those cases, and disable later after a new source
* has been configured.
*/
osc_init.OscillatorType |= RCC_OSCILLATORTYPE_MSI;
osc_init.MSIState = RCC_MSI_ON;
osc_init.MSICalibrationValue = MYNEWT_VAL(STM32_CLOCK_MSI_CALIBRATION);
osc_init.MSIClockRange = MYNEWT_VAL(STM32_CLOCK_MSI_CLOCK_RANGE);
#endif
/*
* HSE Oscillator (can be used as PLL, SYSCLK and RTC clock source)
*/
#if MYNEWT_VAL(STM32_CLOCK_HSE)
osc_init.OscillatorType |= RCC_OSCILLATORTYPE_HSE;
#if MYNEWT_VAL(STM32_CLOCK_HSE_BYPASS)
osc_init.HSEState = RCC_HSE_BYPASS;
#else
osc_init.HSEState = RCC_HSE_ON;
#endif
#endif
/*
* HSI Oscillator (can be used as PLL and SYSCLK clock source). It is
* already turned on by default but a new calibration setting might be
* used. If the user chooses to turn it off, it must be turned off after
* SYSCLK was updated to use HSE/PLL.
*/
#if MYNEWT_VAL(STM32_CLOCK_HSI)
osc_init.OscillatorType |= RCC_OSCILLATORTYPE_HSI;
osc_init.HSIState = RCC_HSI_ON;
/* HSI calibration is not optional when HSI is enabled */
osc_init.HSICalibrationValue = MYNEWT_VAL(STM32_CLOCK_HSI_CALIBRATION);
#if (MYNEWT_VAL(STM32_CLOCK_HSI_CALIBRATION) > 127)
#error "Invalid HSI calibration value"
#endif
#endif
/*
* HSI48 can be used to drive USB/SDMMC/RNG
*/
#if MYNEWT_VAL(STM32_CLOCK_HSI48)
osc_init.OscillatorType |= RCC_OSCILLATORTYPE_HSI48;
osc_init.HSI48State = RCC_HSI48_ON;
#endif
/*
* Default to MSI, HSE or HSI48 as PLL source when multiple high-speed
* sources are enabled.
*/
osc_init.PLL.PLLState = RCC_PLL_ON;
#if MYNEWT_VAL(STM32_CLOCK_MSI)
osc_init.PLL.PLLSource = RCC_PLLSOURCE_MSI;
#elif MYNEWT_VAL(STM32_CLOCK_HSE)
osc_init.PLL.PLLSource = RCC_PLLSOURCE_HSE;
#else
osc_init.PLL.PLLSource = RCC_PLLSOURCE_HSI;
#endif
#if !IS_RCC_PLLM_VALUE(MYNEWT_VAL(STM32_CLOCK_PLL_PLLM))
#error "PLLM value is invalid"
#endif
#if !IS_RCC_PLLN_VALUE(MYNEWT_VAL(STM32_CLOCK_PLL_PLLN))
#error "PLLN value is invalid"
#endif
#if !IS_RCC_PLLP_VALUE(MYNEWT_VAL(STM32_CLOCK_PLL_PLLP))
#error "PLLP value is invalid"
#endif
#if !IS_RCC_PLLQ_VALUE(MYNEWT_VAL(STM32_CLOCK_PLL_PLLQ))
#error "PLLQ value is invalid"
#endif
#if !IS_RCC_PLLR_VALUE(MYNEWT_VAL(STM32_CLOCK_PLL_PLLR))
#error "PLLR value is invalid"
#endif
osc_init.PLL.PLLM = MYNEWT_VAL(STM32_CLOCK_PLL_PLLM);
osc_init.PLL.PLLN = MYNEWT_VAL(STM32_CLOCK_PLL_PLLN);
osc_init.PLL.PLLP = MYNEWT_VAL(STM32_CLOCK_PLL_PLLP);
osc_init.PLL.PLLQ = MYNEWT_VAL(STM32_CLOCK_PLL_PLLQ);
osc_init.PLL.PLLR = MYNEWT_VAL(STM32_CLOCK_PLL_PLLR);
osc_init.PLL.PLLRGE = MYNEWT_VAL(STM32_CLOCK_PLL_PLLRGE);
if (MYNEWT_VAL(STM32_CLOCK_PLL_BOOST_DIV) >= 0) {
osc_init.PLL.PLLMBOOST = MYNEWT_VAL(STM32_CLOCK_PLL_BOOST_DIV);
}
status = HAL_RCC_OscConfig(&osc_init);
if (status != HAL_OK) {
assert(0);
}
/*
* Select PLL as system clock source and configure the HCLK*, PCLK* and
* SYSCLK clocks dividers. HSI, HSE and MSI are also valid system clock
* sources, although there is no much point in supporting them now.
*/
clk_init.ClockType = RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_HCLK |
RCC_CLOCKTYPE_PCLK1 | RCC_CLOCKTYPE_PCLK2;
clk_init.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
#if !IS_RCC_HCLK(MYNEWT_VAL(STM32_CLOCK_AHB_DIVIDER))
#error "AHB clock divider is invalid"
#endif
#if !IS_RCC_PCLK(MYNEWT_VAL(STM32_CLOCK_APB1_DIVIDER))
#error "APB1 clock divider is invalid"
#endif
#if !IS_RCC_PCLK(MYNEWT_VAL(STM32_CLOCK_APB2_DIVIDER))
#error "APB2 clock divider is invalid"
#endif
#if !IS_RCC_PCLK(MYNEWT_VAL(STM32_CLOCK_APB3_DIVIDER))
#error "APB2 clock divider is invalid"
#endif
clk_init.AHBCLKDivider = MYNEWT_VAL(STM32_CLOCK_AHB_DIVIDER);
clk_init.APB1CLKDivider = MYNEWT_VAL(STM32_CLOCK_APB1_DIVIDER);
clk_init.APB2CLKDivider = MYNEWT_VAL(STM32_CLOCK_APB2_DIVIDER);
clk_init.APB3CLKDivider = MYNEWT_VAL(STM32_CLOCK_APB3_DIVIDER);
#if !IS_FLASH_LATENCY(MYNEWT_VAL(STM32_FLASH_LATENCY))
#error "Flash latency value is invalid"
#endif
status = HAL_RCC_ClockConfig(&clk_init, MYNEWT_VAL(STM32_FLASH_LATENCY));
if (status != HAL_OK) {
assert(0);
}
#if (MYNEWT_VAL(STM32_CLOCK_HSI) == 0) || (MYNEWT_VAL(STM32_CLOCK_HSE) == 0) || \
(MYNEWT_VAL(STM32_CLOCK_HSI48) == 0) || (MYNEWT_VAL(STM32_CLOCK_MSI) == 0)
/*
* Turn off HSE/HSI/HSI48 oscillator; this must be done at the end because
* SYSCLK source has to be updated first.
*/
osc_init.OscillatorType = RCC_OSCILLATORTYPE_NONE;
#if (MYNEWT_VAL(STM32_CLOCK_HSE) == 0)
osc_init.OscillatorType |= RCC_OSCILLATORTYPE_HSE;
osc_init.HSEState = RCC_HSE_OFF;
#endif
#if (MYNEWT_VAL(STM32_CLOCK_HSI) == 0)
osc_init.OscillatorType |= RCC_OSCILLATORTYPE_HSI;
osc_init.HSIState = RCC_HSI_OFF;
#endif
#if (MYNEWT_VAL(STM32_CLOCK_HSI48) == 0) && defined(RCC_HSI48_SUPPORT)
osc_init.OscillatorType |= RCC_OSCILLATORTYPE_HSI48;
osc_init.HSI48State = RCC_HSI48_OFF;
#endif
#if (MYNEWT_VAL(STM32_CLOCK_MSI) == 0)
osc_init.OscillatorType |= RCC_OSCILLATORTYPE_MSI;
osc_init.MSIState = RCC_MSI_OFF;
#endif
#endif
osc_init.PLL.PLLState = RCC_PLL_NONE;
status = HAL_RCC_OscConfig(&osc_init);
if (status != HAL_OK) {
assert(0);
}
#if TRNG_ENABLED
pclk_init.PeriphClockSelection = RCC_PERIPHCLK_RNG;
/* Other clock sources are possible, but since right now we always
* configure the PLL, this should be ok
*/
pclk_init.RngClockSelection = RCC_RNGCLKSOURCE_PLL;
status = HAL_RCCEx_PeriphCLKConfig(&pclk_init);
if (status != HAL_OK) {
assert(0);
}
#endif
#if PREFETCH_ENABLE
__HAL_FLASH_PREFETCH_BUFFER_ENABLE();
#endif
#if MYNEWT_VAL(STM32_ENABLE_ICACHE)
HAL_ICACHE_Enable();
#endif
#if MYNEWT_VAL(STM32_ENABLE_DCACHE)
DCACHE_HandleTypeDef cache = {
.Instance = DCACHE1_S,
.Init.ReadBurstType = DCACHE_READ_BURST_WRAP,
};
HAL_DCACHE_Init(&cache);
#endif
}
#endif