From e6632cda77367d2fbace7daae2080685aacae808 Mon Sep 17 00:00:00 2001 From: Mateusz Myalski Date: Fri, 11 Oct 2024 11:51:07 +0200 Subject: [PATCH] Added support for USARTs and clock setup Tested: - USART2 Rx/Tx with: - In 8N1 115200 - With sysclk set as HSI and default setup - With all clk input types for USART2 --- .../stm32/common/gpio_common_f234.h | 2 +- include/libopencm3/stm32/u5/rcc.h | 300 +++++++++++- include/libopencm3/stm32/u5/usart.h | 70 +++ include/libopencm3/stm32/usart.h | 2 + lib/stm32/u5/Makefile | 3 +- lib/stm32/u5/rcc.c | 427 ++++++++++++++++++ 6 files changed, 797 insertions(+), 7 deletions(-) create mode 100644 include/libopencm3/stm32/u5/usart.h create mode 100644 lib/stm32/u5/rcc.c diff --git a/include/libopencm3/stm32/common/gpio_common_f234.h b/include/libopencm3/stm32/common/gpio_common_f234.h index d635dd4d..a89eb010 100644 --- a/include/libopencm3/stm32/common/gpio_common_f234.h +++ b/include/libopencm3/stm32/common/gpio_common_f234.h @@ -270,7 +270,7 @@ specific memorymap.h header before including this header file.*/ BEGIN_DECLS /* - * Note: The F2 and F4 series have a completely new GPIO peripheral with + * Note: The F2, U5 and F4 series have a completely new GPIO peripheral with * different configuration options. Here we implement a different API partly to * more closely match the peripheral capabilities and also to deliberately * break compatibility with old F1 code so there is no confusion with similar diff --git a/include/libopencm3/stm32/u5/rcc.h b/include/libopencm3/stm32/u5/rcc.h index 485f8511..f109ccc5 100644 --- a/include/libopencm3/stm32/u5/rcc.h +++ b/include/libopencm3/stm32/u5/rcc.h @@ -43,6 +43,18 @@ /* Module definitions */ /*****************************************************************************/ +struct rcc_clock_scale { + uint8_t hpre; /* AHB prescaler */ + uint8_t ppre1; /* APB1 low-speed prescaler */ + uint8_t ppre2; /* APB2 high-speed prescaler */ + uint32_t ahb_frequency; /* AHB clock frequency */ + uint32_t apb1_frequency; /* APB1 clock frequency */ + uint32_t apb2_frequency; /* APB2 clock frequency */ + // enum pwr_vos_scale vos_scale; /* Dynamic voltage scale */ TODO +}; + +extern const struct rcc_clock_scale rcc_hsi16mhz_configs; + /*****************************************************************************/ /* Register definitions */ /*****************************************************************************/ @@ -51,46 +63,165 @@ #define RCC_CFGR MMIO32(RCC_BASE + 0x1c) #define RCC_CFGR2 MMIO32(RCC_BASE + 0x20) #define RCC_CFGR3 MMIO32(RCC_BASE + 0x24) +#define RCC_CCIPR1 MMIO32(RCC_BASE + 0xE0) +#define RCC_CCIPR2 MMIO32(RCC_BASE + 0xE4) + +#define RCC_CFGR_SWS_SHIFT 2 +#define RCC_CFGR_SWS_MASK 0x3 +#define RCC_CFGR_SWS (0x3 << RCC_CFGR_SWS_SHIFT) +#define RCC_CFGR_SWS_MSIS 0x0 +#define RCC_CFGR_SWS_HSI16 0x1 +#define RCC_CFGR_SWS_HE 0x2 +#define RCC_CFGR_SWS_PLL 0x3 + +#define RCC_CFGR_SW_SYSCLKSEL_SHIFT 0 +#define RCC_CFGR_SW_SYSCLKSEL_MASK 0x3 +#define RCC_CFGR_SW_SYSCLKSEL_MSIS 0x0 +#define RCC_CFGR_SW_SYSCLKSEL_HSI16 0x1 +#define RCC_CFGR_SW_SYSCLKSEL_HSE 0x2 +#define RCC_CFGR_SW_SYSCLKSEL_PLL 0x3 + /*****************************************************************************/ /* Register values */ /*****************************************************************************/ /* --- RCC_CR values ------------------------------------------------------- */ -#define RCC_CR_HSEBYP (1 << 18) +#define RCC_CR_PLL3RDY (1 << 29) +#define RCC_CR_PLL3ON (1 << 28) +#define RCC_CR_PLL2RDY (1 << 27) +#define RCC_CR_PLL2ON (1 << 26) +#define RCC_CR_PLL1RDY (1 << 25) +#define RCC_CR_PLL1ON (1 << 24) +#define RCC_CR_HSEEXT (1 << 20) +#define RCC_CR_CSSON (1 << 19) +#define RCC_CR_HSEBYP (1 << 18) +#define RCC_CR_HSERDY (1 << 17) +#define RCC_CR_HSEON (1 << 16) +#define RCC_CR_SHSIRDY (1 << 15) +#define RCC_CR_SHSION (1 << 14) +#define RCC_CR_HSI48RDY (1 << 13) +#define RCC_CR_HSI48ON (1 << 12) +#define RCC_CR_HSIRDY (1 << 10) +#define RCC_CR_HSIKERON (1 << 9) +#define RCC_CR_HSION (1 << 8) +#define RCC_CR_MSIPLFAST (1 << 7) +#define RCC_CR_MSIKRDY (1 << 5) +#define RCC_CR_MSIKON (1 << 4) +#define RCC_CR_MSIPLLEN (1 << 3) +#define RCC_CR_MSISRDY (1 << 2) +#define RCC_CR_MSIKERON (1 << 1) +#define RCC_CR_MSISON (1 << 0) /* --- RCC_CFGR values ----------------------------------------------------- */ #define RCC_CFGR_MCO_SHIFT 24 #define RCC_CFGR_MCO_MASK 0xf +#define RCC_CFGR_HPRE_SHIFT 0 +#define RCC_CFGR_HPRE (0xf << RCC_CFGR_HPRE_SHIFT) +#define RCC_CFGR_HPRE_MASK 0xf + +/** @defgroup rcc_cfgr_ahbpre RCC_CFGR AHB prescale factors +@{*/ +#define RCC_CFGR_HPRE_NODIV 0x0 +#define RCC_CFGR_HPRE_DIV2 0x8 +#define RCC_CFGR_HPRE_DIV4 0x9 +#define RCC_CFGR_HPRE_DIV8 0xA +#define RCC_CFGR_HPRE_DIV16 0xB +#define RCC_CFGR_HPRE_DIV64 0xC +#define RCC_CFGR_HPRE_DIV128 0xD +#define RCC_CFGR_HPRE_DIV256 0xE +#define RCC_CFGR_HPRE_DIV512 0xF +/**@}*/ + +#define RCC_CFGR_PPRE1_SHIFT 4 +#define RCC_CFGR_PPRE1 (0x7 << RCC_CFGR_PPRE1_SHIFT) +#define RCC_CFGR_PPRE1_MASK 0x7 +#define RCC_CFGR_PPRE2_SHIFT 8 +#define RCC_CFGR_PPRE2 (0x7 << RCC_CFGR_PPRE2_SHIFT) +#define RCC_CFGR_PPRE2_MASK 0x7 + +/** @defgroup rcc_cfgr_apbxpre RCC_CFGR APBx prescale factors + * These can be used for both APB1 and APB2 prescaling + * @{ + */ +#define RCC_CFGR_PPRE_NODIV 0x0 +#define RCC_CFGR_PPRE_DIV2 0x4 +#define RCC_CFGR_PPRE_DIV4 0x5 +#define RCC_CFGR_PPRE_DIV8 0x6 +#define RCC_CFGR_PPRE_DIV16 0x7 +/**@}*/ + + /* --- RCC_BDCR values ----------------------------------------------------- */ #define RCC_BDCR MMIO32(RCC_BASE + 0xF0) #define RCC_BDCR_LSEBYP (1 << 2) +/* --- RCC_CCIPR1 values ---------------------------------------------------- */ + +#define RCC_CCIPR_USARTxSEL_MASK 0x3 + +#define RCC_CCIPR1_USART5SEL_SHIFT 8 +#define RCC_CCIPR1_USART4SEL_SHIFT 6 +#define RCC_CCIPR1_USART3SEL_SHIFT 4 +#define RCC_CCIPR1_USART2SEL_SHIFT 2 +#define RCC_CCIPR1_USART1SEL_SHIFT 0 + +#define RCC_CCIPR_USARTxSEL_PCLKx 0x0 +#define RCC_CCIPR_USARTxSEL_SYSCLK 0x1 +#define RCC_CCIPR_USARTxSEL_HSI16 0x2 +#define RCC_CCIPR_USARTxSEL_LSE 0x3 + +/* --- RCC_CCIPR2 values ---------------------------------------------------- */ + +#define RCC_CCIPR2_USART6SEL_SHIFT 16 + /*****************************************************************************/ /* API definitions */ /*****************************************************************************/ +/* --- Variable definitions ------------------------------------------------ */ + +extern uint32_t rcc_ahb_frequency; +extern uint32_t rcc_apb1_frequency; +extern uint32_t rcc_apb2_frequency; + enum rcc_osc { - RCC_PLL, + RCC_PLL3, + RCC_PLL2, + RCC_PLL1, + RCC_SHSI, RCC_HSE, RCC_HSI, RCC_HSI16, RCC_MSIS, - RCC_MSIK, + RCC_MSI, RCC_LSI, RCC_LSE, RCC_HSI48, - RCC_SHSI, }; #define _REG_BIT(base, bit) (((base) << 5) + (bit)) enum rcc_periph_rst { + /* AHB1 peripherals */ + RST_GPDMA = _REG_BIT(0x60, 0), + RST_CORDIC = _REG_BIT(0x60, 1), + RST_FMAC = _REG_BIT(0x60, 2), + RST_MDF1 = _REG_BIT(0x60, 3), + RST_CRC = _REG_BIT(0x60, 12), + RST_JPEG = _REG_BIT(0x60, 15), + RST_TSCRS = _REG_BIT(0x60, 16), + RST_RAMCFG = _REG_BIT(0x60, 17), + RST_DMA2D = _REG_BIT(0x60, 18), + RST_GFXMMU = _REG_BIT(0x60, 19), + RST_GPU2D = _REG_BIT(0x60, 20), + + /* AHB2 peripherals */ RST_GPIOA = _REG_BIT(0x64, 0), RST_GPIOB = _REG_BIT(0x64, 1), @@ -100,10 +231,105 @@ enum rcc_periph_rst { RST_GPIOF = _REG_BIT(0x64, 5), RST_GPIOG = _REG_BIT(0x64, 6), RST_GPIOH = _REG_BIT(0x64, 7), + RST_GPIOI = _REG_BIT(0x64, 8), + RST_GPIOJ = _REG_BIT(0x64, 9), + RST_ADC12 = _REG_BIT(0x64, 10), + RST_DCMI = _REG_BIT(0x64, 12), + RST_OTG = _REG_BIT(0x64, 14), + RST_AES = _REG_BIT(0x64, 16), + RST_HASH = _REG_BIT(0x64, 17), + RST_RNG = _REG_BIT(0x64, 18), + RST_PKA = _REG_BIT(0x64, 19), + RST_SAES = _REG_BIT(0x64, 20), + RST_OTFDEC1 = _REG_BIT(0x64, 23), + RST_OTFDEC2 = _REG_BIT(0x64, 24), + RST_SDMMC1 = _REG_BIT(0x64, 27), + RST_SDMMC2 = _REG_BIT(0x64, 28), + + RST_FSMC = _REG_BIT(0x68, 0), + RST_OCTOSPI1 = _REG_BIT(0x68, 4), + RST_OCTOSPI2 = _REG_BIT(0x68, 8), + RST_HSPI1 = _REG_BIT(0x68, 12), + + /* AHB3 peripherals */ + RST_LPGIO1 = _REG_BIT(0x6C, 0), + RST_ADC4 = _REG_BIT(0x6C, 5), + RST_LPDMA1 = _REG_BIT(0x6C, 9), + RST_ADF1 = _REG_BIT(0x6C, 10), + + /* APB1 peripherals */ + RST_TIM2 = _REG_BIT(0x74, 0), + RST_TIM3 = _REG_BIT(0x74, 1), + RST_TIM4 = _REG_BIT(0x74, 2), + RST_TIM5 = _REG_BIT(0x74, 3), + RST_TIM6 = _REG_BIT(0x74, 4), + RST_TIM7 = _REG_BIT(0x74, 5), + RST_SPI2 = _REG_BIT(0x74, 14), + RST_USART2 = _REG_BIT(0x74, 17), + RST_USART3 = _REG_BIT(0x74, 18), + RST_USART4 = _REG_BIT(0x74, 19), + RST_USART5 = _REG_BIT(0x74, 20), + RST_I2C1 = _REG_BIT(0x74, 21), + RST_I2C2 = _REG_BIT(0x74, 22), + RST_CRS = _REG_BIT(0x74, 24), + RST_USART6 = _REG_BIT(0x74, 25), + RST_I2C4 = _REG_BIT(0x78, 1), + RST_LPTIM2 = _REG_BIT(0x78, 5), + RST_I2C5 = _REG_BIT(0x78, 6), + RST_I2C6 = _REG_BIT(0x78, 7), + RST_FDCAN1 = _REG_BIT(0x78, 9), + RST_UCPD1 = _REG_BIT(0x78, 23), + + /* APB2 peripherals*/ + RST_TIM1 = _REG_BIT(0x7C, 11), + RST_SPI1 = _REG_BIT(0x7C, 12), + RST_TIM8 = _REG_BIT(0x7C, 13), + RST_USART1 = _REG_BIT(0x7C, 14), + RST_TIM15 = _REG_BIT(0x7C, 16), + RST_TIM16 = _REG_BIT(0x7C, 17), + RST_TIM17 = _REG_BIT(0x7C, 18), + RST_SAI1 = _REG_BIT(0x7C, 21), + RST_SAI2 = _REG_BIT(0x7C, 22), + RST_USB = _REG_BIT(0x7C, 24), + RST_GFXTIM = _REG_BIT(0x7C, 25), + RST_LTDCR = _REG_BIT(0x7C, 26), + RST_DSI = _REG_BIT(0x7C, 27), + + /* APB3 peripherals */ + RST_SYSCFG = _REG_BIT(0x80, 1), + RST_SPI3 = _REG_BIT(0x80, 5), + RST_LPUART1 = _REG_BIT(0x80, 6), + RST_I2C3 = _REG_BIT(0x80, 7), + RST_LPTIM1 = _REG_BIT(0x80, 11), + RST_LPTIM3 = _REG_BIT(0x80, 12), + RST_LPTIM4 = _REG_BIT(0x80, 13), + RST_OPAMP = _REG_BIT(0x80, 14), + RST_COMP = _REG_BIT(0x80, 15), + RST_VREF = _REG_BIT(0x80, 20), + }; enum rcc_periph_clken { + /* AHB1 peripherals */ + RCC_GPDMA = _REG_BIT(0x88, 0), + RCC_CORDIC = _REG_BIT(0x88, 1), + RCC_FMAC = _REG_BIT(0x88, 2), + RCC_MDF1 = _REG_BIT(0x88, 3), + RCC_FLASH = _REG_BIT(0x88, 8), + RCC_CRCS = _REG_BIT(0x88, 12), + RCC_JPEG = _REG_BIT(0x88, 15), + RCC_TSCR = _REG_BIT(0x88, 16), + RCC_RAMCFG = _REG_BIT(0x88, 17), + RCC_DMA2D = _REG_BIT(0x88, 18), + RCC_GFXMMU = _REG_BIT(0x88, 19), + RCC_GPU2D = _REG_BIT(0x88, 20), + RCC_DCACHE2 = _REG_BIT(0x88, 21), + RCC_GTZC1 = _REG_BIT(0x88, 24), + RCC_BKPSRAM = _REG_BIT(0x88, 28), + RCC_DCACHE1 = _REG_BIT(0x88, 30), + RCC_SRAM1 = _REG_BIT(0x88, 31), + /* AHB2 peripherals */ RCC_GPIOA = _REG_BIT(0x8C, 0), RCC_GPIOB = _REG_BIT(0x8C, 1), @@ -113,6 +339,59 @@ enum rcc_periph_clken { RCC_GPIOF = _REG_BIT(0x8C, 5), RCC_GPIOG = _REG_BIT(0x8C, 6), RCC_GPIOH = _REG_BIT(0x8C, 7), + RCC_GPIOI = _REG_BIT(0x8C, 8), + RCC_GPIOJ = _REG_BIT(0x8C, 9), + + /* APB1 peripherals */ + RCC_TIM2 = _REG_BIT(0x9C, 0), + RCC_TIM3 = _REG_BIT(0x9C, 1), + RCC_TIM4 = _REG_BIT(0x9C, 2), + RCC_TIM5 = _REG_BIT(0x9C, 3), + RCC_TIM6 = _REG_BIT(0x9C, 4), + RCC_TIM7 = _REG_BIT(0x9C, 5), + RCC_WWDG = _REG_BIT(0x9C, 11), + RCC_SPI2 = _REG_BIT(0x9C, 14), + RCC_USART2 = _REG_BIT(0x9C, 17), + RCC_USART3 = _REG_BIT(0x9C, 18), + RCC_USART4 = _REG_BIT(0x9C, 19), + RCC_USART5 = _REG_BIT(0x9C, 20), + RCC_I2C1 = _REG_BIT(0x9C, 21), + RCC_I2C2 = _REG_BIT(0x9C, 22), + RCC_CRS = _REG_BIT(0x9C, 24), + RCC_USART6 = _REG_BIT(0x9C, 25), + RCC_I2C4 = _REG_BIT(0x9C, 1), + RCC_LPTIM2 = _REG_BIT(0x9C, 5), + RCC_I2C5 = _REG_BIT(0x9C, 6), + RCC_I2C6 = _REG_BIT(0x9C, 7), + RCC_FDCAN1 = _REG_BIT(0x9C, 9), + RCC_UCPD1 = _REG_BIT(0x9C, 23), + + /* APB2 peripherals */ + RCC_TIM1 = _REG_BIT(0xA4, 11), + RCC_SPI1 = _REG_BIT(0xA4, 12), + RCC_TIM8 = _REG_BIT(0xA4, 13), + RCC_USART1 = _REG_BIT(0xA4, 14), + RCC_TIM15 = _REG_BIT(0xA4, 16), + RCC_TIM16 = _REG_BIT(0xA4, 17), + RCC_TIM17 = _REG_BIT(0xA4, 18), + RCC_SAI1 = _REG_BIT(0xA4, 21), + RCC_SAI2 = _REG_BIT(0xA4, 22), + RCC_USB = _REG_BIT(0xA4, 24), + RCC_GFXTIM = _REG_BIT(0xA4, 25), + RCC_LTDCR = _REG_BIT(0xA4, 26), + RCC_DSI = _REG_BIT(0xA4, 27), + + /* APB3 peripherals */ + RCC_SYSCFG = _REG_BIT(0xA8, 1), + RCC_SPI3 = _REG_BIT(0xA8, 5), + RCC_LPUART1 = _REG_BIT(0xA8, 6), + RCC_I2C3 = _REG_BIT(0xA8, 7), + RCC_LPTIM1 = _REG_BIT(0xA8, 11), + RCC_LPTIM3 = _REG_BIT(0xA8, 12), + RCC_LPTIM4 = _REG_BIT(0xA8, 13), + RCC_OPAMP = _REG_BIT(0xA8, 14), + RCC_COMP = _REG_BIT(0xA8, 15), + RCC_VREF = _REG_BIT(0xA8, 20), }; #undef _REG_BIT @@ -123,7 +402,18 @@ enum rcc_periph_clken { #include BEGIN_DECLS - +uint32_t rcc_get_usart_clk_freq(uint32_t usart); +void rcc_osc_on(enum rcc_osc osc); +void rcc_osc_off(enum rcc_osc osc); +void rcc_css_enable(void); +void rcc_css_disable(void); +void rcc_set_sysclk_source(enum rcc_osc clk); +uint32_t rcc_system_clock_source(void); +void rcc_set_peripheral_clk_sel(uint32_t periph, uint32_t sel); +void rcc_clock_setup_hsi(const struct rcc_clock_scale *clock); +void rcc_set_ppre2(uint32_t ppre1); +void rcc_set_ppre1(uint32_t ppre1); +void rcc_set_hpre(uint32_t hpre); END_DECLS /**@}*/ diff --git a/include/libopencm3/stm32/u5/usart.h b/include/libopencm3/stm32/u5/usart.h new file mode 100644 index 00000000..21319292 --- /dev/null +++ b/include/libopencm3/stm32/u5/usart.h @@ -0,0 +1,70 @@ +/** @defgroup usart_defines USART Defines + * + * @brief Defined Constants and Types for the STM32U5xxx USART + * + * @ingroup STM32U5xxx_defines + * + * @version 1.0.0 + * + * @date 8 October 2024 + * + * LGPL License Terms @ref lgpl_license + */ + +/* + * This file is part of the libopencm3 project. + * + * This library is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library. If not, see . + */ + +#ifndef LIBOPENCM3_USART_H +#define LIBOPENCM3_USART_H + +#include +#include + +/**@{*/ + +/*****************************************************************************/ +/* Module definitions */ +/*****************************************************************************/ + +/** @defgroup usart_reg_base USART register base addresses + * Holds all the U(S)ART peripherals supported. + * @{ + */ +#define USART1 USART1_BASE +#define USART2 USART2_BASE +#define USART3 USART3_BASE +#define USART4 USART4_BASE +#define USART5 USART5_BASE +#define USART6 USART6_BASE +/**@}*/ + +/*****************************************************************************/ +/* Register values */ +/*****************************************************************************/ + +/*****************************************************************************/ +/* API Functions */ +/*****************************************************************************/ + +BEGIN_DECLS + +END_DECLS + +/**@}*/ + +#endif + diff --git a/include/libopencm3/stm32/usart.h b/include/libopencm3/stm32/usart.h index 3e835598..bb67c6d3 100644 --- a/include/libopencm3/stm32/usart.h +++ b/include/libopencm3/stm32/usart.h @@ -38,6 +38,8 @@ # include #elif defined(STM32L4) # include +#elif defined(STM32U5) +# include #elif defined(STM32G0) # include #elif defined(STM32G4) diff --git a/lib/stm32/u5/Makefile b/lib/stm32/u5/Makefile index 621873b9..537402f1 100644 --- a/lib/stm32/u5/Makefile +++ b/lib/stm32/u5/Makefile @@ -35,8 +35,9 @@ TGT_CFLAGS += $(STANDARD_FLAGS) ARFLAGS = rcs +OBJS += usart_common_all.o usart_common_v2.o OBJS += gpio_common_all.o gpio_common_f0234.o -OBJS += rcc_common_all.o +OBJS += rcc.o rcc_common_all.o VPATH += ../:../../cm3:../common diff --git a/lib/stm32/u5/rcc.c b/lib/stm32/u5/rcc.c new file mode 100644 index 00000000..7f0d9fd9 --- /dev/null +++ b/lib/stm32/u5/rcc.c @@ -0,0 +1,427 @@ +/** @defgroup rcc_file RCC peripheral API + * + * @ingroup peripheral_apis + * + * @brief libopencm3 STM32U5xx Reset and Clock Control + * + * @version 1.0.0 + * + * @date 09 Oct 2024 + * + * This library supports the Reset and Clock Control System in the STM32U5xx + * series of ARM Cortex Microcontrollers by ST Microelectronics. + * + * LGPL License Terms @ref lgpl_license + */ +/* + * This file is part of the libopencm3 project. + * + * Copyright (C) 2009 Federico Ruiz-Ugalde + * Copyright (C) 2009 Uwe Hermann + * Copyright (C) 2010 Thomas Otto + * + * This library is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library. If not, see . + */ + +/**@{*/ + +#include +#include + +/* Set the default clock frequencies */ +#define RCC_DEFAULT_MSIS_FREQUENCY 4000000U +#define RCC_DEFAULT_HSI48_FREQUENCY 48000000U +#define RCC_DEFAULT_SHSI_FREQUENCY 48000000U +#define RCC_DEFAULT_HSI16_FREQUENCY 16000000U +#define RCC_DEFAULT_LSI_FREQUENCY 32000U +#define RCC_DEFAULT_LSE_FREQUENCY 32768U + +uint32_t rcc_ahb_frequency = RCC_DEFAULT_MSIS_FREQUENCY; +uint32_t rcc_apb1_frequency = RCC_DEFAULT_MSIS_FREQUENCY; +uint32_t rcc_apb2_frequency = RCC_DEFAULT_MSIS_FREQUENCY; + + +const struct rcc_clock_scale rcc_hsi16mhz_configs = { + .hpre = RCC_CFGR_HPRE_NODIV, + .ppre1 = RCC_CFGR_PPRE_NODIV, + .ppre2 = RCC_CFGR_PPRE_NODIV, + .ahb_frequency = RCC_DEFAULT_HSI16_FREQUENCY, + .apb1_frequency = RCC_DEFAULT_HSI16_FREQUENCY, + .apb2_frequency = RCC_DEFAULT_HSI16_FREQUENCY, +}; + +void rcc_set_ppre2(uint32_t ppre2) +{ + uint32_t reg32; + + reg32 = RCC_CFGR; + reg32 &= ~RCC_CFGR_PPRE2; + RCC_CFGR = (reg32 | (ppre2 << RCC_CFGR_PPRE2_SHIFT)); +} + +void rcc_set_ppre1(uint32_t ppre1) +{ + uint32_t reg32; + + reg32 = RCC_CFGR; + reg32 &= ~RCC_CFGR_PPRE1; + RCC_CFGR = (reg32 | (ppre1 << RCC_CFGR_PPRE1_SHIFT)); +} + +void rcc_set_hpre(uint32_t hpre) +{ + uint32_t reg32; + + reg32 = RCC_CFGR; + reg32 &= ~RCC_CFGR_HPRE; + RCC_CFGR = (reg32 | (hpre << RCC_CFGR_HPRE_SHIFT)); +} + +/** + * Switch sysclock to HSI with the given parameters. + * This should be usable from any point in time, but only if you have used + * library functions to manage clocks. It relies on the global + * @ref rcc_ahb_frequency to ensure that it reliably scales voltage up or down + * as appropriate. + * @param clock full struct with desired parameters + */ +void rcc_clock_setup_hsi(const struct rcc_clock_scale *clock) +{ + /* Enable internal high-speed oscillator. */ + rcc_osc_on(RCC_HSI); + /* Don't try and go to fast for a voltage range! */ + if (clock->ahb_frequency > rcc_ahb_frequency) { + /* Going up, power up first */ + // pwr_set_vos_scale(clock->voltage_scale); TODO + rcc_set_hpre(clock->hpre); + rcc_set_ppre1(clock->ppre1); + rcc_set_ppre2(clock->ppre2); + } else { + /* going down, slow down before cutting power */ + rcc_set_hpre(clock->hpre); + rcc_set_ppre1(clock->ppre1); + rcc_set_ppre2(clock->ppre2); + // pwr_set_vos_scale(clock->voltage_scale); TODO + } + + rcc_wait_for_osc_ready(RCC_HSI); + rcc_set_sysclk_source(RCC_HSI16); + + /* Set the peripheral clock frequencies used. */ + rcc_ahb_frequency = clock->ahb_frequency; + rcc_apb1_frequency = clock->apb1_frequency; + rcc_apb2_frequency = clock->apb2_frequency; +} + +/*---------------------------------------------------------------------------*/ +/** @brief RCC Get the System Clock Source. + * @returns Unsigned int32. System clock source: + * @li 00 indicates MSIS + * @li 01 indicates HSI16 + * @li 02 indicates HSE + * @li 03 indicates PLL + */ +uint32_t rcc_system_clock_source(void) +{ + return (RCC_CFGR & RCC_CFGR_SWS) >> RCC_CFGR_SWS_SHIFT; +} + +void rcc_wait_for_osc_ready(enum rcc_osc osc) +{ + while (!rcc_is_osc_ready(osc)); +} + +void rcc_css_enable(void) +{ + RCC_CR |= RCC_CR_CSSON; +} + +void rcc_css_disable(void) +{ + RCC_CR &= ~RCC_CR_CSSON; +} + +/*---------------------------------------------------------------------------*/ +/** @brief RCC Turn off an Oscillator. + +Disable an oscillator and power off. + +@note An oscillator cannot be turned off if it is selected as the system clock. +@note The LSE clock is in the backup domain and cannot be disabled until the +backup domain write protection has been removed (see +@ref pwr_disable_backup_domain_write_protect) or the backup domain has been +(see reset @ref rcc_backupdomain_reset). + +@param[in] osc Oscillator ID +*/ + +void rcc_osc_off(enum rcc_osc osc) +{ + switch (osc) { + case RCC_PLL3: + RCC_CR &= ~RCC_CR_PLL3ON; + break; + case RCC_PLL2: + RCC_CR &= ~RCC_CR_PLL2ON; + break; + case RCC_PLL1: + RCC_CR &= ~RCC_CR_PLL1ON; + break; + case RCC_HSE: + RCC_CR &= ~RCC_CR_HSEON; + break; + case RCC_SHSI: + RCC_CR &= ~RCC_CR_SHSION; + break; + case RCC_HSI48: + RCC_CR &= ~RCC_CR_HSI48ON; + break; + case RCC_HSI: + RCC_CR &= ~RCC_CR_HSION; + break; + case RCC_MSI: + RCC_CR &= ~RCC_CR_MSIKON; + break; + // TODO: Should we add MSIKER? + case RCC_MSIS: + RCC_CR &= ~RCC_CR_MSISON; + break; + default: + cm3_assert_not_reached(); + break; + } +} + +/*---------------------------------------------------------------------------*/ +/** @brief RCC Turn on an Oscillator. + * + * Enable an oscillator and power on. Each oscillator requires an amount of + * time to settle to a usable state. Refer to datasheets for time delay + * information. A status flag is available to indicate when the oscillator + * becomes ready (see @ref rcc_osc_ready_int_flag and @ref + * rcc_wait_for_osc_ready). + * + * @param osc Oscillator ID + */ + +void rcc_osc_on(enum rcc_osc osc) +{ + switch (osc) { + case RCC_PLL3: + RCC_CR |= RCC_CR_PLL3ON; + break; + case RCC_PLL2: + RCC_CR |= RCC_CR_PLL2ON; + break; + case RCC_PLL1: + RCC_CR |= RCC_CR_PLL1ON; + break; + case RCC_HSE: + RCC_CR |= RCC_CR_HSEON; + break; + case RCC_SHSI: + RCC_CR |= RCC_CR_SHSION; + break; + case RCC_HSI48: + RCC_CR |= RCC_CR_HSI48ON; + break; + case RCC_HSI: + RCC_CR |= RCC_CR_HSION; + break; + case RCC_MSI: + RCC_CR |= RCC_CR_MSIKON; + break; + // TODO: Should we add MSIKER? + case RCC_MSIS: + RCC_CR |= RCC_CR_MSISON; + break; + default: + cm3_assert_not_reached(); + break; + } +} + +/*---------------------------------------------------------------------------*/ +/** @brief Returns if the oscillator is ready. + * + * @param osc Oscillator ID + */ +bool rcc_is_osc_ready(enum rcc_osc osc) +{ + switch (osc) { + case RCC_PLL3: + return RCC_CR & RCC_CR_PLL3RDY; + case RCC_PLL2: + return RCC_CR & RCC_CR_PLL2RDY; + case RCC_PLL1: + return RCC_CR & RCC_CR_PLL1RDY; + case RCC_HSE: + return RCC_CR & RCC_CR_HSERDY; + case RCC_SHSI: + return RCC_CR & RCC_CR_SHSIRDY; + case RCC_HSI48: + return RCC_CR & RCC_CR_HSI48RDY; + case RCC_HSI: + return RCC_CR & RCC_CR_HSIRDY; + case RCC_MSI: + return RCC_CR & RCC_CR_MSIKRDY; + case RCC_MSIS: + return RCC_CR & RCC_CR_MSISRDY; + default: + break; + } + + return false; +} + +/*---------------------------------------------------------------------------*/ +/** @brief RCC Set the Source for the System Clock. + * + * @param clk Oscillator ID. + */ +void rcc_set_sysclk_source(enum rcc_osc clk) +{ + uint32_t sw = 0x0; + + switch (clk) { + case RCC_MSIS: + sw = RCC_CFGR_SW_SYSCLKSEL_MSIS; + break; + case RCC_HSI16: + sw = RCC_CFGR_SW_SYSCLKSEL_HSI16; + break; + case RCC_HSE: + sw = RCC_CFGR_SW_SYSCLKSEL_HSE; + break; + case RCC_PLL1: + sw = RCC_CFGR_SW_SYSCLKSEL_PLL; + break; + default: + cm3_assert_not_reached(); + break; + } + + sw <<= RCC_CFGR_SW_SYSCLKSEL_SHIFT; + RCC_CFGR = (RCC_CFGR & ~(RCC_CFGR_SW_SYSCLKSEL_MASK << RCC_CFGR_SW_SYSCLKSEL_SHIFT)) | sw; +} + +/** + * @brief Set the peripheral clock source + * @param periph peripheral of choice, eg XXX_BASE + * @param sel periphral clock source + */ +void rcc_set_peripheral_clk_sel(uint32_t periph, uint32_t sel) { + + volatile uint32_t *reg32; + uint32_t shift; + uint32_t mask; + + switch (periph) { + case USART1_BASE: + reg32 = &RCC_CCIPR1; + shift = RCC_CCIPR1_USART1SEL_SHIFT; + mask = RCC_CCIPR_USARTxSEL_MASK; + break; + case USART2_BASE: + reg32 = &RCC_CCIPR1; + shift = RCC_CCIPR1_USART2SEL_SHIFT; + mask = RCC_CCIPR_USARTxSEL_MASK; + break; + case USART3_BASE: + reg32 = &RCC_CCIPR1; + shift = RCC_CCIPR1_USART3SEL_SHIFT; + mask = RCC_CCIPR_USARTxSEL_MASK; + break; + case USART4_BASE: + reg32 = &RCC_CCIPR1; + shift = RCC_CCIPR1_USART4SEL_SHIFT; + mask = RCC_CCIPR_USARTxSEL_MASK; + break; + case USART5_BASE: + reg32 = &RCC_CCIPR1; + shift = RCC_CCIPR1_USART5SEL_SHIFT; + mask = RCC_CCIPR_USARTxSEL_MASK; + break; + case USART6_BASE: + reg32 = &RCC_CCIPR2; + shift = RCC_CCIPR2_USART6SEL_SHIFT; + mask = RCC_CCIPR_USARTxSEL_MASK; + break; + default: + cm3_assert_not_reached(); + break; + } + + mask <<= shift; + sel <<= shift; + (*reg32) = ((*reg32) & ~mask) | sel; +} + +static uint32_t rcc_get_usart_clksel_freq(uint32_t usart, uint8_t shift) { + uint8_t clksel; + if(usart == USART6_BASE) { + clksel = (RCC_CCIPR2 >> shift) & RCC_CCIPR_USARTxSEL_MASK; + } else { + clksel = (RCC_CCIPR1 >> shift) & RCC_CCIPR_USARTxSEL_MASK; + } + + switch (clksel) { + case RCC_CCIPR_USARTxSEL_PCLKx: + if(usart == USART1_BASE) { + return rcc_apb2_frequency; + } + return rcc_apb1_frequency; + case RCC_CCIPR_USARTxSEL_SYSCLK: + return rcc_ahb_frequency; + case RCC_CCIPR_USARTxSEL_HSI16: + return RCC_DEFAULT_HSI16_FREQUENCY; + case RCC_CCIPR_USARTxSEL_LSE: + return RCC_DEFAULT_LSE_FREQUENCY; + default: + cm3_assert_not_reached(); + break; + } + return 0; +} + + +/*---------------------------------------------------------------------------*/ +/** @brief Get the peripheral clock speed for the USART at base specified. + * @param usart Base address of USART to get clock frequency for. + */ +uint32_t rcc_get_usart_clk_freq(uint32_t usart) +{ + switch (usart) + { + case USART1_BASE: + return rcc_get_usart_clksel_freq(usart, RCC_CCIPR1_USART1SEL_SHIFT); + case USART2_BASE: + return rcc_get_usart_clksel_freq(usart, RCC_CCIPR1_USART2SEL_SHIFT); + case USART3_BASE: + return rcc_get_usart_clksel_freq(usart, RCC_CCIPR1_USART3SEL_SHIFT); + case USART4_BASE: + return rcc_get_usart_clksel_freq(usart, RCC_CCIPR1_USART4SEL_SHIFT); + case USART5_BASE: + return rcc_get_usart_clksel_freq(usart, RCC_CCIPR1_USART5SEL_SHIFT); + case USART6_BASE: + return rcc_get_usart_clksel_freq(usart, RCC_CCIPR2_USART6SEL_SHIFT); + default: + break; + } + cm3_assert_not_reached(); + return 0; +} + +/**@}*/ +