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 <erwan.gouriou@linaro.org>
This commit is contained in:
Erwan Gouriou 2019-07-01 14:12:48 +02:00 committed by Anas Nashif
commit 2805ea9193
5 changed files with 383 additions and 0 deletions

View file

@ -12,6 +12,8 @@ zephyr_sources_ifdef(CONFIG_CLOCK_CONTROL_RV32M1_PCC clock_control_rv32
if(CONFIG_CLOCK_CONTROL_STM32_CUBE) if(CONFIG_CLOCK_CONTROL_STM32_CUBE)
if(CONFIG_SOC_SERIES_STM32MP1X) if(CONFIG_SOC_SERIES_STM32MP1X)
zephyr_sources(clock_stm32_ll_mp1.c) zephyr_sources(clock_stm32_ll_mp1.c)
elseif(CONFIG_SOC_SERIES_STM32H7X)
zephyr_sources(clock_stm32_ll_h7.c)
else() else()
zephyr_sources(clock_stm32_ll_common.c) zephyr_sources(clock_stm32_ll_common.c)
zephyr_sources_ifdef(CONFIG_SOC_SERIES_STM32F0X clock_stm32f0_f3.c) zephyr_sources_ifdef(CONFIG_SOC_SERIES_STM32F0X clock_stm32f0_f3.c)

View file

@ -122,12 +122,15 @@ endchoice
source "drivers/clock_control/Kconfig.stm32f0_f3" source "drivers/clock_control/Kconfig.stm32f0_f3"
source "drivers/clock_control/Kconfig.stm32f1" source "drivers/clock_control/Kconfig.stm32f1"
source "drivers/clock_control/Kconfig.stm32f2_f4_f7" 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.stm32l0_l1"
source "drivers/clock_control/Kconfig.stm32l4_wb" source "drivers/clock_control/Kconfig.stm32l4_wb"
# Bus clocks configuration options # Bus clocks configuration options
if !SOC_SERIES_STM32H7X
config CLOCK_STM32_AHB_PRESCALER config CLOCK_STM32_AHB_PRESCALER
int "AHB prescaler" int "AHB prescaler"
default 1 default 1
@ -181,6 +184,7 @@ config CLOCK_STM32_AHB4_PRESCALER
HCLK4 prescaler, allowed values: 1, 2, 4, 8, 16, 64, 128, HCLK4 prescaler, allowed values: 1, 2, 4, 8, 16, 64, 128,
256, 512. 256, 512.
endif # !SOC_SERIES_STM32H7X
# Micro-controller Clock output configuration options # Micro-controller Clock output configuration options

View file

@ -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

View file

@ -0,0 +1,272 @@
/*
*
* Copyright (c) 2019 Linaro Limited.
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <soc.h>
#include <clock_control.h>
#include <misc/util.h>
#include <clock_control/stm32_clock_control.h>
/* 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);

View file

@ -17,6 +17,13 @@
*/ */
#include <kernel_includes.h> #include <kernel_includes.h>
#ifdef CONFIG_CLOCK_CONTROL_STM32_CUBE
#include <stm32h7xx_ll_bus.h>
#include <stm32h7xx_ll_rcc.h>
#include <stm32h7xx_ll_pwr.h>
#include <stm32h7xx_ll_system.h>
#endif /* CONFIG_CLOCK_CONTROL_STM32_CUBE */
#endif /* !_ASMLANGUAGE */ #endif /* !_ASMLANGUAGE */
#endif /* _STM32F7_SOC_H7_ */ #endif /* _STM32F7_SOC_H7_ */