diff --git a/drivers/clock_control/CMakeLists.txt b/drivers/clock_control/CMakeLists.txt index b34df8574e2..edae619f2d8 100644 --- a/drivers/clock_control/CMakeLists.txt +++ b/drivers/clock_control/CMakeLists.txt @@ -12,6 +12,8 @@ zephyr_sources_ifdef(CONFIG_CLOCK_CONTROL_RV32M1_PCC clock_control_rv32 if(CONFIG_CLOCK_CONTROL_STM32_CUBE) if(CONFIG_SOC_SERIES_STM32MP1X) zephyr_sources(clock_stm32_ll_mp1.c) +elseif(CONFIG_SOC_SERIES_STM32H7X) + zephyr_sources(clock_stm32_ll_h7.c) else() zephyr_sources(clock_stm32_ll_common.c) zephyr_sources_ifdef(CONFIG_SOC_SERIES_STM32F0X clock_stm32f0_f3.c) diff --git a/drivers/clock_control/Kconfig.stm32 b/drivers/clock_control/Kconfig.stm32 index df7ca3cf9bf..29218783185 100644 --- a/drivers/clock_control/Kconfig.stm32 +++ b/drivers/clock_control/Kconfig.stm32 @@ -122,12 +122,15 @@ endchoice source "drivers/clock_control/Kconfig.stm32f0_f3" source "drivers/clock_control/Kconfig.stm32f1" source "drivers/clock_control/Kconfig.stm32f2_f4_f7" +source "drivers/clock_control/Kconfig.stm32h7" source "drivers/clock_control/Kconfig.stm32l0_l1" source "drivers/clock_control/Kconfig.stm32l4_wb" # Bus clocks configuration options +if !SOC_SERIES_STM32H7X + config CLOCK_STM32_AHB_PRESCALER int "AHB prescaler" default 1 @@ -181,6 +184,7 @@ config CLOCK_STM32_AHB4_PRESCALER HCLK4 prescaler, allowed values: 1, 2, 4, 8, 16, 64, 128, 256, 512. +endif # !SOC_SERIES_STM32H7X # Micro-controller Clock output configuration options diff --git a/drivers/clock_control/Kconfig.stm32h7 b/drivers/clock_control/Kconfig.stm32h7 new file mode 100644 index 00000000000..d2d3077b1ad --- /dev/null +++ b/drivers/clock_control/Kconfig.stm32h7 @@ -0,0 +1,98 @@ +# Kconfig - STM32H7 PLL configuration options +# +# Copyright (c) 2019 Linaro +# +# SPDX-License-Identifier: Apache-2.0 +# + +if SOC_SERIES_STM32H7X + +# Bus clocks configuration options + +config CLOCK_STM32_D1CPRE + int "D1 Domain, CPU1 clock prescaler" + default 1 + range 1 512 + help + D1 Domain, CPU1 clock (sys_d1cpre_ck prescaler), + allowed values: 1, 2, 4, 8, 16, 64, 128, 256, 512. + +config CLOCK_STM32_HPRE + int "hclk prescaler, D2 domain (CPU2) Clock prescaler" + default 1 + range 1 512 + help + hclk prescaler, allowed values: 1, 2, 4, 8, 16, 64, 128, 256, 512. + +config CLOCK_STM32_D2PPRE1 + int "APB1 prescaler" + default 1 + range 1 16 + help + APB1 clock (rcc_pclk1) prescaler, allowed values: 1, 2, 4, 8, 16 + +config CLOCK_STM32_D2PPRE2 + int "D2 DOMAIN, APB2 prescaler" + default 1 + range 1 16 + help + APB2 clock (rcc_pclk2) prescaler, allowed values: 1, 2, 4, 8, 16 + +config CLOCK_STM32_D1PPRE + int "D1 DOMAIN, APB3 prescaler" + default 1 + range 1 16 + help + APB3 clock (rcc_pclk3) prescaler, allowed values: 1, 2, 4, 8, 16 + +config CLOCK_STM32_D3PPRE + int "APB4 prescaler" + default 1 + range 1 16 + help + APB4 clock (rcc_pclk4) prescaler, allowed values: 1, 2, 4, 8, 16 + + +# PLL settings + +config CLOCK_STM32_PLL_M_DIVISOR + int "PLL divisor" + depends on CLOCK_STM32_SYSCLK_SRC_PLL + default 32 + range 0 63 + help + PLL divisor, allowed values: 0-63. + +config CLOCK_STM32_PLL_N_MULTIPLIER + int "PLL1 VCO multiplier" + depends on CLOCK_STM32_SYSCLK_SRC_PLL + default 129 + range 4 512 + help + PLL multiplier, allowed values: 4-512. + +config CLOCK_STM32_PLL_P_DIVISOR + int "PLL P Divisor" + depends on CLOCK_STM32_SYSCLK_SRC_PLL + default 2 + range 1 128 + help + PLL P Output divisor, allowed values: 1-128. + +config CLOCK_STM32_PLL_Q_DIVISOR + int "PLL Q Divisor" + depends on CLOCK_STM32_SYSCLK_SRC_PLL + default 2 + range 1 128 + help + PLL Q Output divisor, allowed values: 1-128. + +config CLOCK_STM32_PLL_R_DIVISOR + int "PLL R Divisor" + depends on CLOCK_STM32_SYSCLK_SRC_PLL + default 2 + range 1 128 + help + PLL R Output divisor, allowed values: 1-128. + +endif # SOC_SERIES_STM32H7X diff --git a/drivers/clock_control/clock_stm32_ll_h7.c b/drivers/clock_control/clock_stm32_ll_h7.c new file mode 100644 index 00000000000..4ed253587ca --- /dev/null +++ b/drivers/clock_control/clock_stm32_ll_h7.c @@ -0,0 +1,272 @@ +/* + * + * Copyright (c) 2019 Linaro Limited. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include + +/* Macros to fill up prescaler values */ +#define z_sysclk_prescaler(v) LL_RCC_SYSCLK_DIV_ ## v +#define sysclk_prescaler(v) z_sysclk_prescaler(v) + +#define z_ahb_prescaler(v) LL_RCC_AHB_DIV_ ## v +#define ahb_prescaler(v) z_ahb_prescaler(v) + +#define z_apb1_prescaler(v) LL_RCC_APB1_DIV_ ## v +#define apb1_prescaler(v) z_apb1_prescaler(v) + +#define z_apb2_prescaler(v) LL_RCC_APB2_DIV_ ## v +#define apb2_prescaler(v) z_apb2_prescaler(v) + +#define z_apb3_prescaler(v) LL_RCC_APB3_DIV_ ## v +#define apb3_prescaler(v) z_apb3_prescaler(v) + +#define z_apb4_prescaler(v) LL_RCC_APB4_DIV_ ## v +#define apb4_prescaler(v) z_apb4_prescaler(v) + +/** + * @brief fill in AHB/APB buses configuration structure + */ +static void config_bus_prescalers(void) +{ + LL_RCC_SetSysPrescaler(sysclk_prescaler(CONFIG_CLOCK_STM32_D1CPRE)); + LL_RCC_SetAHBPrescaler(ahb_prescaler(CONFIG_CLOCK_STM32_HPRE)); + LL_RCC_SetAPB1Prescaler(apb1_prescaler(CONFIG_CLOCK_STM32_D2PPRE1)); + LL_RCC_SetAPB2Prescaler(apb2_prescaler(CONFIG_CLOCK_STM32_D2PPRE2)); + LL_RCC_SetAPB3Prescaler(apb3_prescaler(CONFIG_CLOCK_STM32_D1PPRE)); + LL_RCC_SetAPB4Prescaler(apb4_prescaler(CONFIG_CLOCK_STM32_D3PPRE)); +} + +static u32_t get_bus_clock(u32_t clock, u32_t prescaler) +{ + return clock / prescaler; +} + +static inline int stm32_clock_control_on(struct device *dev, + clock_control_subsys_t sub_system) +{ + struct stm32_pclken *pclken = (struct stm32_pclken *)(sub_system); + + ARG_UNUSED(dev); + + switch (pclken->bus) { + case STM32_CLOCK_BUS_AHB1: + LL_AHB1_GRP1_EnableClock(pclken->enr); + break; + case STM32_CLOCK_BUS_AHB2: + LL_AHB2_GRP1_EnableClock(pclken->enr); + break; + case STM32_CLOCK_BUS_AHB3: + LL_AHB3_GRP1_EnableClock(pclken->enr); + break; + case STM32_CLOCK_BUS_AHB4: + LL_AHB4_GRP1_EnableClock(pclken->enr); + break; + case STM32_CLOCK_BUS_APB1: + LL_APB1_GRP1_EnableClock(pclken->enr); + break; + case STM32_CLOCK_BUS_APB1_2: + LL_APB1_GRP2_EnableClock(pclken->enr); + break; + case STM32_CLOCK_BUS_APB2: + LL_APB2_GRP1_EnableClock(pclken->enr); + break; + case STM32_CLOCK_BUS_APB3: + LL_APB3_GRP1_EnableClock(pclken->enr); + break; + case STM32_CLOCK_BUS_APB4: + LL_APB4_GRP1_EnableClock(pclken->enr); + break; + default: + return -ENOTSUP; + } + + return 0; +} + +static inline int stm32_clock_control_off(struct device *dev, + clock_control_subsys_t sub_system) +{ + struct stm32_pclken *pclken = (struct stm32_pclken *)(sub_system); + + ARG_UNUSED(dev); + + switch (pclken->bus) { + case STM32_CLOCK_BUS_AHB1: + LL_AHB1_GRP1_DisableClock(pclken->enr); + break; + case STM32_CLOCK_BUS_AHB2: + LL_AHB2_GRP1_DisableClock(pclken->enr); + break; + case STM32_CLOCK_BUS_AHB3: + LL_AHB3_GRP1_DisableClock(pclken->enr); + break; + case STM32_CLOCK_BUS_AHB4: + LL_AHB4_GRP1_DisableClock(pclken->enr); + break; + case STM32_CLOCK_BUS_APB1: + LL_APB1_GRP1_DisableClock(pclken->enr); + break; + case STM32_CLOCK_BUS_APB1_2: + LL_APB1_GRP2_DisableClock(pclken->enr); + break; + case STM32_CLOCK_BUS_APB2: + LL_APB2_GRP1_DisableClock(pclken->enr); + break; + case STM32_CLOCK_BUS_APB3: + LL_APB3_GRP1_DisableClock(pclken->enr); + break; + case STM32_CLOCK_BUS_APB4: + LL_APB4_GRP1_DisableClock(pclken->enr); + break; + default: + return -ENOTSUP; + } + + return 0; +} + +static int stm32_clock_control_get_subsys_rate(struct device *clock, + clock_control_subsys_t sub_system, + u32_t *rate) +{ + struct stm32_pclken *pclken = (struct stm32_pclken *)(sub_system); + /* + * Get AHB Clock (= SystemCoreClock = SYSCLK/prescaler) + * SystemCoreClock is preferred to CONFIG_SYS_CLOCK_HW_CYCLES_PER_SEC + * since it will be updated after clock configuration and hence + * more likely to contain actual clock speed + */ + u32_t sys_d1cpre_ck = get_bus_clock(SystemCoreClock, + CONFIG_CLOCK_STM32_D1CPRE); + u32_t ahb_clock = get_bus_clock(sys_d1cpre_ck, + CONFIG_CLOCK_STM32_HPRE); + u32_t apb1_clock = get_bus_clock(ahb_clock, + CONFIG_CLOCK_STM32_D2PPRE1); + u32_t apb2_clock = get_bus_clock(ahb_clock, + CONFIG_CLOCK_STM32_D2PPRE2); + u32_t apb3_clock = get_bus_clock(ahb_clock, + CONFIG_CLOCK_STM32_D1PPRE); + u32_t apb4_clock = get_bus_clock(ahb_clock, + CONFIG_CLOCK_STM32_D3PPRE); + + ARG_UNUSED(clock); + + switch (pclken->bus) { + case STM32_CLOCK_BUS_AHB1: + case STM32_CLOCK_BUS_AHB2: + case STM32_CLOCK_BUS_AHB3: + case STM32_CLOCK_BUS_AHB4: + *rate = ahb_clock; + break; + case STM32_CLOCK_BUS_APB1: + case STM32_CLOCK_BUS_APB1_2: + *rate = apb1_clock; + break; + case STM32_CLOCK_BUS_APB2: + *rate = apb2_clock; + break; + case STM32_CLOCK_BUS_APB3: + *rate = apb3_clock; + break; + case STM32_CLOCK_BUS_APB4: + *rate = apb4_clock; + break; + default: + return -ENOTSUP; + } + + return 0; +} + +static struct clock_control_driver_api stm32_clock_control_api = { + .on = stm32_clock_control_on, + .off = stm32_clock_control_off, + .get_rate = stm32_clock_control_get_subsys_rate, +}; + +static int stm32_clock_control_init(struct device *dev) +{ + ARG_UNUSED(dev); + +#ifdef CONFIG_CLOCK_STM32_SYSCLK_SRC_PLL + /* Power Configuration */ + LL_PWR_ConfigSupply(LL_PWR_DIRECT_SMPS_SUPPLY); + LL_PWR_SetRegulVoltageScaling(LL_PWR_REGU_VOLTAGE_SCALE1); + while (LL_PWR_IsActiveFlag_VOS() == 0) { + } + +#ifdef CONFIG_CLOCK_STM32_PLL_SRC_HSE + +#ifdef CONFIG_CLOCK_STM32_HSE_BYPASS + LL_RCC_HSE_EnableBypass(); +#else + LL_RCC_HSE_DisableBypass(); +#endif /* CONFIG_CLOCK_STM32_HSE_BYPASS */ + + /* Enable HSE oscillator */ + LL_RCC_HSE_Enable(); + while (LL_RCC_HSE_IsReady() != 1) { + } + + /* Set FLASH latency */ + LL_FLASH_SetLatency(LL_FLASH_LATENCY_4); + + /* Main PLL configuration and activation */ + LL_RCC_PLL_SetSource(LL_RCC_PLLSOURCE_HSE); +#else + #error "CONFIG_CLOCK_STM32_PLL_SRC_HSE not selected" +#endif /* CONFIG_CLOCK_STM32_PLL_SRC_HSE */ + + /* Configure PLL1 */ + LL_RCC_PLL1P_Enable(); + LL_RCC_PLL1Q_Enable(); + LL_RCC_PLL1R_Enable(); + LL_RCC_PLL1FRACN_Disable(); + LL_RCC_PLL1_SetVCOInputRange(LL_RCC_PLLINPUTRANGE_2_4); + LL_RCC_PLL1_SetVCOOutputRange(LL_RCC_PLLVCORANGE_WIDE); + LL_RCC_PLL1_SetM(CONFIG_CLOCK_STM32_PLL_M_DIVISOR); + LL_RCC_PLL1_SetN(CONFIG_CLOCK_STM32_PLL_N_MULTIPLIER); + LL_RCC_PLL1_SetP(CONFIG_CLOCK_STM32_PLL_P_DIVISOR); + LL_RCC_PLL1_SetQ(CONFIG_CLOCK_STM32_PLL_Q_DIVISOR); + LL_RCC_PLL1_SetR(CONFIG_CLOCK_STM32_PLL_R_DIVISOR); + + LL_RCC_PLL1_Enable(); + while (LL_RCC_PLL1_IsReady() != 1) { + } + + /* Set buses (Sys,AHB, APB1, APB2 & APB4) prescalers */ + config_bus_prescalers(); + + /* Set PLL1 as System Clock Source */ + LL_RCC_SetSysClkSource(LL_RCC_SYS_CLKSOURCE_PLL1); + while (LL_RCC_GetSysClkSource() != LL_RCC_SYS_CLKSOURCE_STATUS_PLL1) { + } + +#else + #error "CONFIG_CLOCK_STM32_SYSCLK_SRC_PLL not selected" +#endif /* CLOCK_STM32_SYSCLK_SRC_PLL */ + + /* Set systick to 1ms */ + SysTick_Config(CONFIG_SYS_CLOCK_HW_CYCLES_PER_SEC / 1000); + /* Update CMSIS variable */ + SystemCoreClock = CONFIG_SYS_CLOCK_HW_CYCLES_PER_SEC; + + return 0; +} + +/** + * @brief RCC device, note that priority is intentionally set to 1 so + * that the device init runs just after SOC init + */ +DEVICE_AND_API_INIT(rcc_stm32, STM32_CLOCK_CONTROL_NAME, + &stm32_clock_control_init, + NULL, NULL, + PRE_KERNEL_1, + CONFIG_CLOCK_CONTROL_STM32_DEVICE_INIT_PRIORITY, + &stm32_clock_control_api); diff --git a/soc/arm/st_stm32/stm32h7/soc.h b/soc/arm/st_stm32/stm32h7/soc.h index 22ad4f5ae6d..8f9fefc7a31 100644 --- a/soc/arm/st_stm32/stm32h7/soc.h +++ b/soc/arm/st_stm32/stm32h7/soc.h @@ -17,6 +17,13 @@ */ #include +#ifdef CONFIG_CLOCK_CONTROL_STM32_CUBE +#include +#include +#include +#include +#endif /* CONFIG_CLOCK_CONTROL_STM32_CUBE */ + #endif /* !_ASMLANGUAGE */ #endif /* _STM32F7_SOC_H7_ */