From 2805ea9193f89c365ac4cc23aaae6062632b9523 Mon Sep 17 00:00:00 2001 From: Erwan Gouriou Date: Mon, 1 Jul 2019 14:12:48 +0200 Subject: [PATCH] drivers/clock_control: STM32H7 support Provide basic clock control driver for STM32H7. Bus clock activation is done through CM7 and CM4 common registers so we don't have to care to the CPU Id before accessing. Accesses are not protected for now. Only possible configuration is system clock source set to HSE driven PLL. Signed-off-by: Erwan Gouriou --- drivers/clock_control/CMakeLists.txt | 2 + drivers/clock_control/Kconfig.stm32 | 4 + drivers/clock_control/Kconfig.stm32h7 | 98 ++++++++ drivers/clock_control/clock_stm32_ll_h7.c | 272 ++++++++++++++++++++++ soc/arm/st_stm32/stm32h7/soc.h | 7 + 5 files changed, 383 insertions(+) create mode 100644 drivers/clock_control/Kconfig.stm32h7 create mode 100644 drivers/clock_control/clock_stm32_ll_h7.c 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_ */