From 72782f542c1eb0c44dac7e539fc7caf9d4350c34 Mon Sep 17 00:00:00 2001 From: Adam Podogrocki Date: Fri, 28 Oct 2016 12:46:50 +0200 Subject: [PATCH] clock/stm32: add STM32F107 reset and clock control Change-Id: If2280187c866c130212ea22c3d406501f37133b2 Signed-off-by: Adam Podogrocki --- arch/arm/soc/st_stm32/stm32f1/rcc_registers.h | 61 +++ drivers/clock_control/Kconfig | 6 +- drivers/clock_control/Kconfig.stm32f107xx | 157 +++++++ drivers/clock_control/Kconfig.stm32f10x | 4 +- drivers/clock_control/Makefile | 3 +- drivers/clock_control/stm32f107xx_clock.c | 426 ++++++++++++++++++ .../clock_control/stm32_clock_control.h | 4 +- 7 files changed, 653 insertions(+), 8 deletions(-) create mode 100644 drivers/clock_control/Kconfig.stm32f107xx create mode 100644 drivers/clock_control/stm32f107xx_clock.c diff --git a/arch/arm/soc/st_stm32/stm32f1/rcc_registers.h b/arch/arm/soc/st_stm32/stm32f1/rcc_registers.h index d2a7b459420..90f2d81ba1a 100644 --- a/arch/arm/soc/st_stm32/stm32f1/rcc_registers.h +++ b/arch/arm/soc/st_stm32/stm32f1/rcc_registers.h @@ -1,5 +1,6 @@ /* * Copyright (c) 2016 Open-RnD Sp. z o.o. + * Copyright (c) 2016 RnDity Sp. z o.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -26,9 +27,11 @@ * * Chapter 7: Low-, medium-, high- and XL-density reset and * clock control + * Chapter 8: Connectivity line devices: reset and clock control (RCC) */ /* 8.3.1 Clock control register (RCC_CR) */ +#ifdef CONFIG_SOC_STM32F10X_DENSITY_DEVICE enum { STM32F10X_RCC_CFG_PLL_SRC_HSI = 0x0, STM32F10X_RCC_CFG_PLL_SRC_HSE = 0x1, @@ -38,6 +41,7 @@ enum { STM32F10X_RCC_CFG_PLL_XTPRE_DIV_0 = 0x0, STM32F10X_RCC_CFG_PLL_XTPRE_DIV_2 = 0x1, }; +#endif enum { STM32F10X_RCC_CFG_SYSCLK_SRC_HSI = 0x0, @@ -65,6 +69,37 @@ enum { STM32F10X_RCC_CFG_SYSCLK_DIV_512 = 0xf, }; +#ifdef CONFIG_SOC_STM32F10X_CONNECTIVITY_LINE_DEVICE +enum { + STM32F10X_RCC_CFG_PLL_SRC_HSI = 0x0, + STM32F10X_RCC_CFG_PLL_SRC_PREDIV1 = 0x1, +}; + +enum { + STM32F10X_RCC_CFG2_PREDIV1_SRC_HSE = 0x0, + STM32F10X_RCC_CFG2_PREDIV1_SRC_PLL2 = 0x1, +}; + +enum { + STM32F10X_CONN_LINE_RCC_CFGR2_PREDIV_DIV_0 = 0x0, + STM32F10X_CONN_LINE_RCC_CFGR2_PREDIV_DIV_2 = 0x1, + STM32F10X_CONN_LINE_RCC_CFGR2_PREDIV_DIV_3 = 0x2, + STM32F10X_CONN_LINE_RCC_CFGR2_PREDIV_DIV_4 = 0x3, + STM32F10X_CONN_LINE_RCC_CFGR2_PREDIV_DIV_5 = 0x4, + STM32F10X_CONN_LINE_RCC_CFGR2_PREDIV_DIV_6 = 0x5, + STM32F10X_CONN_LINE_RCC_CFGR2_PREDIV_DIV_7 = 0x6, + STM32F10X_CONN_LINE_RCC_CFGR2_PREDIV_DIV_8 = 0x7, + STM32F10X_CONN_LINE_RCC_CFGR2_PREDIV_DIV_9 = 0x8, + STM32F10X_CONN_LINE_RCC_CFGR2_PREDIV_DIV_10 = 0x9, + STM32F10X_CONN_LINE_RCC_CFGR2_PREDIV_DIV_11 = 0xa, + STM32F10X_CONN_LINE_RCC_CFGR2_PREDIV_DIV_12 = 0xb, + STM32F10X_CONN_LINE_RCC_CFGR2_PREDIV_DIV_13 = 0xc, + STM32F10X_CONN_LINE_RCC_CFGR2_PREDIV_DIV_14 = 0xd, + STM32F10X_CONN_LINE_RCC_CFGR2_PREDIV_DIV_15 = 0xe, + STM32F10X_CONN_LINE_RCC_CFGR2_PREDIV_DIV_16 = 0xf +}; +#endif + /** * @brief Reset and Clock Control */ @@ -84,7 +119,15 @@ union __rcc_cr { uint32_t rsvd__20_23 :4 __packed; uint32_t pllon :1 __packed; uint32_t pllrdy :1 __packed; +#if CONFIG_SOC_STM32F10X_DENSITY_DEVICE uint32_t rsvd__26_31 :6 __packed; +#elif CONFIG_SOC_STM32F10X_CONNECTIVITY_LINE_DEVICE + uint32_t pll2on :1 __packed; + uint32_t pll2rdy :1 __packed; + uint32_t pll3on :1 __packed; + uint32_t pll3rdy :1 __packed; + uint32_t rsvd__30_31 :2 __packed; +#endif } bit; }; @@ -107,6 +150,20 @@ union __rcc_cfgr { } bit; }; +union __rcc_cfgr2 { + uint32_t val; + struct { + uint32_t prediv1 :4 __packed; + uint32_t prediv2 :4 __packed; + uint32_t pll2mul :4 __packed; + uint32_t pll3mul :4 __packed; + uint32_t prediv1src :1 __packed; + uint32_t i2s2sr :1 __packed; + uint32_t i2s3sr :1 __packed; + uint32_t rsvd__19_31 :13 __packed; + } bit; +}; + struct stm32f10x_rcc { union __rcc_cr cr; union __rcc_cfgr cfgr; @@ -118,6 +175,10 @@ struct stm32f10x_rcc { uint32_t apb1enr; uint32_t bdcr; uint32_t csr; +#ifdef CONFIG_SOC_STM32F10X_CONNECTIVITY_LINE_DEVICE + uint32_t ahbrstr; + union __rcc_cfgr2 cfgr2; +#endif }; #endif /* _STM32F10X_CLOCK_H_ */ diff --git a/drivers/clock_control/Kconfig b/drivers/clock_control/Kconfig index 764813a69e5..a223da7e8e3 100644 --- a/drivers/clock_control/Kconfig +++ b/drivers/clock_control/Kconfig @@ -50,13 +50,15 @@ config SYS_LOG_CLOCK_CONTROL_LEVEL - 4 DEBUG, write SYS_LOG_DBG in adition to previous levels +source "drivers/clock_control/Kconfig.nrf5" + source "drivers/clock_control/Kconfig.quark_se" source "drivers/clock_control/Kconfig.stm32f10x" -source "drivers/clock_control/Kconfig.stm32f4x" +source "drivers/clock_control/Kconfig.stm32f107xx" -source "drivers/clock_control/Kconfig.nrf5" +source "drivers/clock_control/Kconfig.stm32f4x" source "drivers/clock_control/Kconfig.stm32l4x" diff --git a/drivers/clock_control/Kconfig.stm32f107xx b/drivers/clock_control/Kconfig.stm32f107xx new file mode 100644 index 00000000000..5d25b12b67a --- /dev/null +++ b/drivers/clock_control/Kconfig.stm32f107xx @@ -0,0 +1,157 @@ +# Kconfig - STM32F1X Connectivity Line MCU clock control driver config +# +# Copyright (c) 2016 RnDity Sp. z o.o. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +if SOC_STM32F10X_CONNECTIVITY_LINE_DEVICE + +menuconfig CLOCK_CONTROL_STM32F10X_CONN_LINE + bool + prompt "STM32F107x Reset & Clock Control" + default y if SOC_STM32F10X_CONNECTIVITY_LINE_DEVICE + help + Enable driver for Reset & Clock Control subsystem found + in STM32F105/STM32F107 family of MCUs + +config CLOCK_CONTROL_STM32F10X_CONN_LINE_DEVICE_INIT_PRIORITY + int "Clock Control Device Priority" + default 1 + depends on CLOCK_CONTROL_STM32F10X_CONN_LINE + help + This option controls the priority of clock control + device initialization. Higher priority ensures that the device + is initialized earlier in the startup cycle. If unsure, leave + at default value 1 + +choice +prompt "STM32F10x Connectivity Line System Clock Source" +depends on CLOCK_CONTROL_STM32F10X_CONN_LINE + +config CLOCK_STM32F10X_CONN_LINE_SYSCLK_SRC_HSI + bool "HSI" + help + Use HSI as source of SYSCLK + +config CLOCK_STM32F10X_CONN_LINE_SYSCLK_SRC_HSE + bool "HSE" + help + Use HSE as source of SYSCLK + +config CLOCK_STM32F10X_CONN_LINE_SYSCLK_SRC_PLLCLK + bool "PLLCLK" + help + Use PLLCLK as source of SYSCLK + +endchoice + +choice +prompt "STM32F10x Connectivity Line PLL Clock Source" +depends on CLOCK_CONTROL_STM32F10X_CONN_LINE && CLOCK_STM32F10X_CONN_LINE_SYSCLK_SRC_PLLCLK + +config CLOCK_STM32F10X_CONN_LINE_PLL_SRC_HSI + bool "HSI" + help + Use HSI divided by 2 as source of PLL + +config CLOCK_STM32F10X_CONN_LINE_PLL_SRC_PREDIV1 + bool "PREDIV1" + help + Use clock from PREDIV1 as source of PLL + +endchoice + +choice +prompt "STM32F10x Connectivity Line PREDIV1 entry clock source" +depends on CLOCK_CONTROL_STM32F10X_CONN_LINE && CLOCK_STM32F10X_CONN_LINE_PLL_SRC_PREDIV1 + +config CLOCK_STM32F10X_CONN_LINE_PREDIV1_SRC_HSE + bool "HSE" + help + Use HSE as source of PREDIV1 + +config CLOCK_STM32F10X_CONN_LINE_PREDIV1_SRC_PLL2CLK + bool "PLL2CLK" + help + Use clock from PLL2CLK as source of PLL + +endchoice + +config CLOCK_STM32F10X_CONN_LINE_HSE_BYPASS + bool "HSE bypass" + depends on CLOCK_CONTROL_STM32F10X_CONN_LINE && (CLOCK_STM32F10X_CONN_LINE_SYSCLK_SRC_HSE || CLOCK_STM32F10X_CONN_LINE_PREDIV1_SRC_HSE) + help + Enable this option to bypass external high-speed clock (HSE). + +config CLOCK_STM32F10X_CONN_LINE_PREDIV1 + int "PREDIV1 Prescler" + depends on CLOCK_CONTROL_STM32F10X_CONN_LINE && CLOCK_STM32F10X_CONN_LINE_PLL_SRC_PREDIV1 + default 0 + range 0 16 + help + PREDIV1 is PREDIV1SCR clock signal prescaler, allowed values: 0 - 16. + +config CLOCK_STM32F10X_CONN_LINE_PLL_MULTIPLIER + int "PLL multiplier" + depends on CLOCK_CONTROL_STM32F10X_CONN_LINE && CLOCK_STM32F10X_CONN_LINE_SYSCLK_SRC_PLLCLK + default 9 + range 4 13 + help + PLL multiplier, allowed values: 4 - 9 and 13 (in fact the multiplication factor is 6.5). + Values in range 10-12 are reserved. PLL output must not exceed 72MHz. + +config CLOCK_STM32F10X_CONN_LINE_PLL2_MULTIPLIER + int "PLL2 multiplier" + depends on CLOCK_CONTROL_STM32F10X_CONN_LINE && CLOCK_STM32F10X_CONN_LINE_PREDIV1_SRC_PLL2CLK + default 8 + range 8 20 + help + PLL2 multiplier, allowed values: 8 - 20. PLL2 output must not exceed 72MHz. + +config CLOCK_STM32F10X_CONN_LINE_PREDIV2 + int "PREDIV2 Prescler" + depends on CLOCK_CONTROL_STM32F10X_CONN_LINE && CLOCK_STM32F10X_CONN_LINE_PREDIV1_SRC_PLL2CLK + default 0 + range 0 16 + help + PREDIV2 is HSE prescaler, allowed values: 0 - 16. + +config CLOCK_STM32F10X_CONN_LINE_AHB_PRESCALER + int "AHB prescaler" + depends on CLOCK_CONTROL_STM32F10X_CONN_LINE + default 0 + range 0 512 + help + AHB prescaler, allowed values: 0, 2, 4, 8, 16, 64, 128, + 256, 512. + +config CLOCK_STM32F10X_CONN_LINE_APB1_PRESCALER + int "APB1 prescaler" + depends on CLOCK_CONTROL_STM32F10X_CONN_LINE + default 0 + range 0 16 + help + APB1 Low speed clock (PCLK1) prescaler, allowed values: + 0, 2, 4, 8, 16. The APB1 clock must not exceed 36MHz. + +config CLOCK_STM32F10X_CONN_LINE_APB2_PRESCALER + int "APB2 prescaler" + depends on CLOCK_CONTROL_STM32F10X_CONN_LINE + default 0 + range 0 16 + help + APB2 High speed clock (PCLK2) prescaler, allowed values: + 0, 2, 4, 8, 16 + +endif diff --git a/drivers/clock_control/Kconfig.stm32f10x b/drivers/clock_control/Kconfig.stm32f10x index 7276cda158a..baa69b52635 100644 --- a/drivers/clock_control/Kconfig.stm32f10x +++ b/drivers/clock_control/Kconfig.stm32f10x @@ -15,12 +15,12 @@ # limitations under the License. # -if SOC_SERIES_STM32F1X +if SOC_STM32F10X_DENSITY_DEVICE menuconfig CLOCK_CONTROL_STM32F10X bool prompt "STM32F10x Reset & Clock Control" - default y if SOC_SERIES_STM32F1X + default y if SOC_STM32F10X_DENSITY_DEVICE help Enable driver for Reset & Clock Control subsystem found in STM32F1 family of MCUs diff --git a/drivers/clock_control/Makefile b/drivers/clock_control/Makefile index 83ffdc99ceb..5d7b93b4418 100644 --- a/drivers/clock_control/Makefile +++ b/drivers/clock_control/Makefile @@ -1,6 +1,7 @@ +obj-$(CONFIG_CLOCK_CONTROL_NRF5) += nrf5_power_clock.o obj-$(CONFIG_CLOCK_CONTROL_QUARK_SE) += quark_se_clock_control.o obj-$(CONFIG_CLOCK_CONTROL_STM32F10X) += stm32f10x_clock.o +obj-$(CONFIG_CLOCK_CONTROL_STM32F10X_CONN_LINE) += stm32f107xx_clock.o obj-$(CONFIG_CLOCK_CONTROL_STM32F4X) += stm32f4x_clock.o -obj-$(CONFIG_CLOCK_CONTROL_NRF5) += nrf5_power_clock.o obj-$(CONFIG_CLOCK_CONTROL_STM32L4X) += stm32l4x_clock.o obj-$(CONFIG_CLOCK_CONTROL_BEETLE) += beetle_clock_control.o diff --git a/drivers/clock_control/stm32f107xx_clock.c b/drivers/clock_control/stm32f107xx_clock.c new file mode 100644 index 00000000000..02ad047cd97 --- /dev/null +++ b/drivers/clock_control/stm32f107xx_clock.c @@ -0,0 +1,426 @@ +/* + * Copyright (c) 2016 RnDity Sp. z o.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @brief Driver for Reset & Clock Control of STM32F10x family processor. + * + * Based on reference manual: + * STM32F101xx, STM32F102xx, STM32F103xx, STM32F105xx and STM32F107xx + * advanced ARM ® -based 32-bit MCUs + * + * Chapter 8: Connectivity line devices: reset and clock control (RCC) + */ + +#include +#include +#include +#include +#include + +struct stm32f10x_rcc_data { + uint8_t *base; +}; + +static inline int stm32f10x_clock_control_on(struct device *dev, + clock_control_subsys_t sub_system) +{ + struct stm32f10x_rcc_data *data = dev->driver_data; + volatile struct stm32f10x_rcc *rcc = + (struct stm32f10x_rcc *)(data->base); + uint32_t subsys = POINTER_TO_UINT(sub_system); + + if (subsys > STM32F10X_CLOCK_APB2_BASE) { + subsys &= ~(STM32F10X_CLOCK_APB2_BASE); + rcc->apb2enr |= subsys; + } else { + rcc->apb1enr |= subsys; + } + + return 0; +} + +static inline int stm32f10x_clock_control_off(struct device *dev, + clock_control_subsys_t sub_system) +{ + struct stm32f10x_rcc_data *data = dev->driver_data; + volatile struct stm32f10x_rcc *rcc = + (struct stm32f10x_rcc *)(data->base); + uint32_t subsys = POINTER_TO_UINT(sub_system); + + if (subsys > STM32F10X_CLOCK_APB2_BASE) { + subsys &= ~(STM32F10X_CLOCK_APB2_BASE); + rcc->apb2enr &= ~subsys; + } else { + rcc->apb1enr &= ~subsys; + } + + return 0; +} + +/** + * @brief helper for mapping a setting to register value + */ +struct regval_map { + int val; + int reg; +}; + +static int map_reg_val(const struct regval_map *map, size_t cnt, int val) +{ + for (int i = 0; i < cnt; i++) { + if (map[i].val == val) { + return map[i].reg; + } + } + + return 0; +} + +/** + * @brief map APB prescaler setting to register value + */ +static int apb_prescaler(int prescaler) +{ + if (prescaler == 0) { + return STM32F10X_RCC_CFG_HCLK_DIV_0; + } + + const struct regval_map map[] = { + {0, STM32F10X_RCC_CFG_HCLK_DIV_0}, + {2, STM32F10X_RCC_CFG_HCLK_DIV_2}, + {4, STM32F10X_RCC_CFG_HCLK_DIV_4}, + {8, STM32F10X_RCC_CFG_HCLK_DIV_8}, + {16, STM32F10X_RCC_CFG_HCLK_DIV_16}, + }; + + return map_reg_val(map, ARRAY_SIZE(map), prescaler); +} + +/** + * @brief map AHB prescaler setting to register value + */ +static int ahb_prescaler(int prescaler) +{ + if (prescaler == 0) { + return STM32F10X_RCC_CFG_SYSCLK_DIV_0; + } + + const struct regval_map map[] = { + {0, STM32F10X_RCC_CFG_SYSCLK_DIV_0}, + {2, STM32F10X_RCC_CFG_SYSCLK_DIV_2}, + {4, STM32F10X_RCC_CFG_SYSCLK_DIV_4}, + {8, STM32F10X_RCC_CFG_SYSCLK_DIV_8}, + {16, STM32F10X_RCC_CFG_SYSCLK_DIV_16}, + {64, STM32F10X_RCC_CFG_SYSCLK_DIV_64}, + {128, STM32F10X_RCC_CFG_SYSCLK_DIV_128}, + {256, STM32F10X_RCC_CFG_SYSCLK_DIV_256}, + {512, STM32F10X_RCC_CFG_SYSCLK_DIV_512}, + }; + + return map_reg_val(map, ARRAY_SIZE(map), prescaler); +} + +/** + * @brief select PREDIV division factor + */ +static int prediv_prescaler(int prescaler) +{ + if (prescaler == 0) { + return STM32F10X_CONN_LINE_RCC_CFGR2_PREDIV_DIV_0; + } + + const struct regval_map map[] = { + {0, STM32F10X_CONN_LINE_RCC_CFGR2_PREDIV_DIV_0}, + {2, STM32F10X_CONN_LINE_RCC_CFGR2_PREDIV_DIV_2}, + {3, STM32F10X_CONN_LINE_RCC_CFGR2_PREDIV_DIV_3}, + {4, STM32F10X_CONN_LINE_RCC_CFGR2_PREDIV_DIV_4}, + {5, STM32F10X_CONN_LINE_RCC_CFGR2_PREDIV_DIV_5}, + {6, STM32F10X_CONN_LINE_RCC_CFGR2_PREDIV_DIV_6}, + {7, STM32F10X_CONN_LINE_RCC_CFGR2_PREDIV_DIV_7}, + {8, STM32F10X_CONN_LINE_RCC_CFGR2_PREDIV_DIV_8}, + {9, STM32F10X_CONN_LINE_RCC_CFGR2_PREDIV_DIV_9}, + {10, STM32F10X_CONN_LINE_RCC_CFGR2_PREDIV_DIV_10}, + {11, STM32F10X_CONN_LINE_RCC_CFGR2_PREDIV_DIV_11}, + {12, STM32F10X_CONN_LINE_RCC_CFGR2_PREDIV_DIV_12}, + {13, STM32F10X_CONN_LINE_RCC_CFGR2_PREDIV_DIV_13}, + {14, STM32F10X_CONN_LINE_RCC_CFGR2_PREDIV_DIV_14}, + {15, STM32F10X_CONN_LINE_RCC_CFGR2_PREDIV_DIV_15}, + {16, STM32F10X_CONN_LINE_RCC_CFGR2_PREDIV_DIV_16}, + }; + + return map_reg_val(map, ARRAY_SIZE(map), prescaler); +} + +#ifdef CONFIG_CLOCK_STM32F10X_CONN_LINE_PLL_MULTIPLIER +/** + * @brief map PLL multiplier setting to register value + */ +static int pllmul(int mul) +{ + /* x4 -> 0x2 + * x5 -> 0x3 + * x6 -> 0x4 + * x7 -> 0x5 + * x8 -> 0x6 + * x9 -> 0x7 + * x6.5 -> 0xd + */ + if (mul == 13) { + /* ToDo: do something with 6.5 multiplication */ + return 0xd; + } else { + return mul - 2; + } +} +#endif /* CONFIG_CLOCK_STM32F10X_CONN_LINE_PLL_MULTIPLIER */ +#ifdef CONFIG_CLOCK_STM32F10X_CONN_LINE_PLL2_MULTIPLIER +static int pll2mul(int mul) +{ + /* x8 -> 0x6 + * x9 -> 0x7 + * x10 -> 0x8 + * x11 -> 0x9 + * x12 -> 0xa + * x13 -> 0xb + * x14 -> 0xc + * x16 -> 0xe + * x20 -> 0xf + */ + if (mul == 20) { + return 0xf; + } else { + return mul - 2; + } +} +#endif /* CONFIG_CLOCK_STM32F10X_CONN_LINE_PLL2_MULTIPLIER */ + +static uint32_t get_ahb_clock(uint32_t sysclk) +{ + /* AHB clock is generated based on SYSCLK */ + uint32_t sysclk_div = + CONFIG_CLOCK_STM32F10X_CONN_LINE_AHB_PRESCALER; + + if (sysclk_div == 0) { + sysclk_div = 1; + } + + return sysclk / sysclk_div; +} + +static uint32_t get_apb_clock(uint32_t ahb_clock, uint32_t prescaler) +{ + if (prescaler == 0) { + prescaler = 1; + } + + return ahb_clock / prescaler; +} + +static +int stm32f10x_clock_control_get_subsys_rate(struct device *clock, + clock_control_subsys_t sub_system, + uint32_t *rate) +{ + ARG_UNUSED(clock); + + uint32_t subsys = POINTER_TO_UINT(sub_system); + uint32_t prescaler = + CONFIG_CLOCK_STM32F10X_CONN_LINE_APB1_PRESCALER; + /* assumes SYSCLK is SYS_CLOCK_HW_CYCLES_PER_SEC */ + uint32_t ahb_clock = + get_ahb_clock(CONFIG_SYS_CLOCK_HW_CYCLES_PER_SEC); + + if (subsys > STM32F10X_CLOCK_APB2_BASE) { + prescaler = + CONFIG_CLOCK_STM32F10X_CONN_LINE_APB2_PRESCALER; + } + + *rate = get_apb_clock(ahb_clock, prescaler); + + return 0; +} + +static const struct clock_control_driver_api stm32f10x_clock_control_api = { + .on = stm32f10x_clock_control_on, + .off = stm32f10x_clock_control_off, + .get_rate = stm32f10x_clock_control_get_subsys_rate, +}; + +/** + * @brief setup embedded flash controller + * + * Configure flash access time latency depending on SYSCLK. + */ +static inline void setup_flash(void) +{ + volatile struct stm32f10x_flash *flash = + (struct stm32f10x_flash *)(FLASH_R_BASE); + + if (CONFIG_SYS_CLOCK_HW_CYCLES_PER_SEC <= 24000000) { + flash->acr.bit.latency = STM32F10X_FLASH_LATENCY_0; + } else if (CONFIG_SYS_CLOCK_HW_CYCLES_PER_SEC <= 48000000) { + flash->acr.bit.latency = STM32F10X_FLASH_LATENCY_1; + } else if (CONFIG_SYS_CLOCK_HW_CYCLES_PER_SEC <= 72000000) { + flash->acr.bit.latency = STM32F10X_FLASH_LATENCY_2; + } +} + +static int stm32f10x_clock_control_init(struct device *dev) +{ + struct stm32f10x_rcc_data *data = dev->driver_data; + volatile struct stm32f10x_rcc *rcc = + (struct stm32f10x_rcc *)(data->base); + /* SYSCLK source defaults to HSI */ + int sysclk_src = STM32F10X_RCC_CFG_SYSCLK_SRC_HSI; + uint32_t hpre = + ahb_prescaler(CONFIG_CLOCK_STM32F10X_CONN_LINE_AHB_PRESCALER); + uint32_t ppre1 = + apb_prescaler(CONFIG_CLOCK_STM32F10X_CONN_LINE_APB1_PRESCALER); + uint32_t ppre2 = + apb_prescaler(CONFIG_CLOCK_STM32F10X_CONN_LINE_APB2_PRESCALER); +#ifdef CONFIG_CLOCK_STM32F10X_CONN_LINE_PLL_MULTIPLIER + uint32_t pll_mul = + pllmul(CONFIG_CLOCK_STM32F10X_CONN_LINE_PLL_MULTIPLIER); +#endif /* CONFIG_CLOCK_STM32F10X_CONN_LINE_PLL_MULTIPLIER */ +#ifdef CONFIG_CLOCK_STM32F10X_CONN_LINE_PLL2_MULTIPLIER + uint32_t pll2mul = + pllmul(CLOCK_STM32F10X_CONN_LINE_PLL2_MULTIPLIER); +#endif /* CONFIG_CLOCK_STM32F10X_CONN_LINE_PLL2_MULTIPLIER */ +#ifdef CONFIG_CLOCK_STM32F10X_CONN_LINE_PREDIV1 + uint32_t prediv1 = + prediv_prescaler(CONFIG_CLOCK_STM32F10X_CONN_LINE_PREDIV1); +#endif /* CONFIG_CLOCK_STM32F10X_CONN_LINE_PREDIV1 */ +#ifdef CONFIG_CLOCK_STM32F10X_CONN_LINE_PREDIV2 + uint32_t prediv2 = + prediv_prescaler(CONFIG_CLOCK_STM32F10X_CONN_LINE_PREDIV2); +#endif /* CLOCK_STM32F10X_CONN_LINE_PREDIV2 */ + + /* disable PLLs */ + rcc->cr.bit.pllon = 0; + rcc->cr.bit.pll2on = 0; + rcc->cr.bit.pll3on = 0; + /* disable HSE */ + rcc->cr.bit.hseon = 0; + +#ifdef CONFIG_CLOCK_STM32F10X_CONN_LINE_HSE_BYPASS + /* HSE is disabled, HSE bypass can be enabled*/ + rcc->cr.bit.hsebyp = 1; +#endif /* CONFIG_CLOCK_STM32F10X_CONN_LINE_HSE_BYPASS */ + +#ifdef CONFIG_CLOCK_STM32F10X_CONN_LINE_PLL_SRC_HSI + /* enable HSI clock */ + rcc->cr.bit.hsion = 1; + /* this should end after one test */ + while (rcc->cr.bit.hsirdy != 1) { + } + + /* HSI oscillator clock / 2 selected as PLL input clock */ + rcc->cfgr.bit.pllsrc = STM32F10X_RCC_CFG_PLL_SRC_HSI; +#endif /* CONFIG_CLOCK_STM32F10X_PLL_SRC_HSI */ + +#ifdef CONFIG_CLOCK_STM32F10X_CONN_LINE_PLL_SRC_PREDIV1 + + /* wait for to become ready */ + rcc->cr.bit.hseon = 1; + while (rcc->cr.bit.hserdy != 1) { + } + + rcc->cfgr2.bit.prediv1 = prediv1; + + /* Clock from PREDIV1 selected as PLL input clock */ + rcc->cfgr.bit.pllsrc = STM32F10X_RCC_CFG_PLL_SRC_PREDIV1; + +#ifdef CONFIG_CLOCK_STM32F10X_CONN_LINE_PREDIV1_SRC_HSE + /* HSE oscillator clock selected as PREDIV1 clock entry */ + rcc->cfgr2.bit.prediv1src = STM32F10X_RCC_CFG2_PREDIV1_SRC_HSE; +#else + /* PLL2 selected as PREDIV1 clock entry */ + rcc->cfgr2.bit.prediv1src = STM32F10X_RCC_CFG2_PREDIV1_SRC_PLL2; + + rcc->cfgr2.bit.prediv2 = prediv2; + rcc->cfgr2.bit.pll2mul = pll2mul; + +#endif /* CONFIG_CLOCK_STM32F10X_CONN_LINE_PREDIV1_SRC_HSE */ +#endif /* CONFIG_CLOCK_STM32F10X_CONN_LINE_PLL_SRC_PREDIV1 */ + + /* setup AHB prescaler */ + rcc->cfgr.bit.hpre = hpre; + + /* setup APB1, must not exceed 36MHz */ + rcc->cfgr.bit.ppre1 = ppre1; + + /* setup APB2 */ + rcc->cfgr.bit.ppre2 = ppre2; + +#ifdef CONFIG_CLOCK_STM32F10X_CONN_LINE_SYSCLK_SRC_HSI + /* enable HSI clock */ + rcc->cr.bit.hsion = 1; + /* this should end after one test */ + while (rcc->cr.bit.hsirdy != 1) { + } + sysclk_src = STM32F10X_RCC_CFG_SYSCLK_SRC_HSI; +#elif defined(CONFIG_CLOCK_STM32F10X_SYSCLK_SRC_HSE) + /* enable HSE clock */ + rcc->cr.bit.hseon = 1; + /* wait for to become ready */ + while (rcc->cr.bit.hserdy != 1) { + } + sysclk_src = STM32F10X_RCC_CFG_SYSCLK_SRC_HSE; +#elif defined(CONFIG_CLOCK_STM32F10X_CONN_LINE_SYSCLK_SRC_PLLCLK) + /* setup PLL multiplication (PLL must be disabled) */ + rcc->cfgr.bit.pllmul = pll_mul; + + /* enable PLL */ + rcc->cr.bit.pllon = 1; + + /* wait for PLL to become ready */ + while (rcc->cr.bit.pllrdy != 1) { + } + + sysclk_src = STM32F10X_RCC_CFG_SYSCLK_SRC_PLL; +#endif /* CONFIG_CLOCK_STM32F10X_CONN_LINE_SYSCLK_SRC_HSI */ + + /* configure flash access latency before SYSCLK source + * switch + */ + setup_flash(); + + /* set SYSCLK clock value */ + rcc->cfgr.bit.sw = sysclk_src; + + /* wait for SYSCLK to switch the source */ + while (rcc->cfgr.bit.sws != sysclk_src) { + } + + return 0; +} + +static struct stm32f10x_rcc_data stm32f10x_rcc_data = { + .base = (uint8_t *)RCC_BASE, +}; + +/* FIXME: move prescaler/multiplier defines into device config */ + +/** + * @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_stm32f10x, STM32_CLOCK_CONTROL_NAME, + &stm32f10x_clock_control_init, + &stm32f10x_rcc_data, NULL, + PRE_KERNEL_1, + CONFIG_CLOCK_CONTROL_STM32F10X_CONN_LINE_DEVICE_INIT_PRIORITY, + &stm32f10x_clock_control_api); diff --git a/include/drivers/clock_control/stm32_clock_control.h b/include/drivers/clock_control/stm32_clock_control.h index d8d67256342..0fa42d831b7 100644 --- a/include/drivers/clock_control/stm32_clock_control.h +++ b/include/drivers/clock_control/stm32_clock_control.h @@ -26,9 +26,7 @@ #include "stm32f1_clock_control.h" #elif CONFIG_SOC_SERIES_STM32F4X #include "stm32f4_clock_control.h" -#endif - -#ifdef CONFIG_SOC_SERIES_STM32L4X +#elif CONFIG_SOC_SERIES_STM32L4X #include "stm32l4x_clock_control.h" #endif