From 3842c60ce3c6ed2bae303590f435958f767b125b Mon Sep 17 00:00:00 2001 From: Neil Armstrong Date: Mon, 3 Oct 2016 15:46:48 +0200 Subject: [PATCH] stm32l4: add clock control driver Add the clock driver for the STM32L4 series. Change-Id: Icdf79061f163d8d00187b382d1564422fb875c5b Signed-off-by: Fabien Parent Signed-off-by: Kumar Gala --- .../soc/st_stm32/stm32l4/flash_registers.h | 73 ++++ arch/arm/soc/st_stm32/stm32l4/rcc_registers.h | 186 +++++++++ arch/arm/soc/st_stm32/stm32l4/soc_registers.h | 2 + drivers/clock_control/Kconfig | 2 + drivers/clock_control/Kconfig.stm32l4x | 150 +++++++ drivers/clock_control/Makefile | 1 + drivers/clock_control/stm32l4x_clock.c | 393 ++++++++++++++++++ .../clock_control/stm32_clock_control.h | 5 + .../clock_control/stm32l4x_clock_control.h | 111 +++++ 9 files changed, 923 insertions(+) create mode 100644 arch/arm/soc/st_stm32/stm32l4/flash_registers.h create mode 100644 arch/arm/soc/st_stm32/stm32l4/rcc_registers.h create mode 100644 drivers/clock_control/Kconfig.stm32l4x create mode 100644 drivers/clock_control/stm32l4x_clock.c create mode 100644 include/drivers/clock_control/stm32l4x_clock_control.h diff --git a/arch/arm/soc/st_stm32/stm32l4/flash_registers.h b/arch/arm/soc/st_stm32/stm32l4/flash_registers.h new file mode 100644 index 00000000000..653ed658826 --- /dev/null +++ b/arch/arm/soc/st_stm32/stm32l4/flash_registers.h @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2016 Open-RnD Sp. z o.o. + * Copyright (c) 2016 BayLibre, SAS + * + * 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. + */ + +#ifndef _STM32L4X_FLASH_REGISTERS_H_ +#define _STM32L4X_FLASH_REGISTERS_H_ + +enum { + STM32L4X_FLASH_LATENCY_0 = 0x0, + STM32L4X_FLASH_LATENCY_1 = 0x1, + STM32L4X_FLASH_LATENCY_2 = 0x2, + STM32L4X_FLASH_LATENCY_3 = 0x3, + STM32L4X_FLASH_LATENCY_4 = 0x4, +}; + +/* 3.7.1 FLASH_ACR */ +union __ef_acr { + uint32_t val; + struct { + uint32_t latency :3 __packed; + uint32_t rsvd__3_7 :5 __packed; + uint32_t prften :1 __packed; + uint32_t icen :1 __packed; + uint32_t dcen :1 __packed; + uint32_t icrst :1 __packed; + uint32_t dcrst :1 __packed; + uint32_t run_pd :1 __packed; + uint32_t sleep_pd :1 __packed; + uint32_t rsvd__16_31 :17 __packed; + } bit; +}; + +/* FLASH register map */ +struct stm32l4x_flash { + union __ef_acr acr; + uint32_t pdkeyr; + uint32_t keyr; + uint32_t optkeyr; + uint32_t sr; + uint32_t cr; + uint32_t eccr; + uint32_t rsvd_0; + uint32_t optr; + uint32_t pcrop1sr; + uint32_t pcrop1er; + uint32_t wrp1ar; + uint32_t wrp1br; + uint32_t rsvd_2[4]; + + /* + * The registers below are only present on STM32L4x2, STM32L4x5, + * STM32L4x6. + */ + uint32_t pcrop2sr; + uint32_t pcrop2er; + uint32_t wrp2ar; + uint32_t wrp2br; +}; + +#endif /* _STM32L4X_FLASH_REGISTERS_H_ */ diff --git a/arch/arm/soc/st_stm32/stm32l4/rcc_registers.h b/arch/arm/soc/st_stm32/stm32l4/rcc_registers.h new file mode 100644 index 00000000000..582a88525ef --- /dev/null +++ b/arch/arm/soc/st_stm32/stm32l4/rcc_registers.h @@ -0,0 +1,186 @@ +/* + * Copyright (c) 2016 Open-RnD Sp. z o.o. + * Copyright (c) 2016 BayLibre, SAS + * + * 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. + */ + +#ifndef _STM32L4X_CLOCK_H_ +#define _STM32L4X_CLOCK_H_ + +/** + * @brief Driver for Reset & Clock Control of STM32L4x6 family processor. + */ + +enum { + STM32L4X_RCC_CFG_PLL_SRC_MSI = 0x1, + STM32L4X_RCC_CFG_PLL_SRC_HSI = 0x2, + STM32L4X_RCC_CFG_PLL_SRC_HSE = 0x3, +}; + +enum { + STM32L4X_RCC_CFG_PLL_Q_R_0 = 0x1, + STM32L4X_RCC_CFG_PLL_Q_R_2 = 0x2, +}; + +enum { + STM32L4X_RCC_CFG_SYSCLK_SRC_MSI = 0x0, + STM32L4X_RCC_CFG_SYSCLK_SRC_HSI = 0x1, + STM32L4X_RCC_CFG_SYSCLK_SRC_HSE = 0x2, + STM32L4X_RCC_CFG_SYSCLK_SRC_PLL = 0x3, +}; + +enum { + STM32L4X_RCC_CFG_HCLK_DIV_0 = 0x0, + STM32L4X_RCC_CFG_HCLK_DIV_2 = 0x4, + STM32L4X_RCC_CFG_HCLK_DIV_4 = 0x5, + STM32L4X_RCC_CFG_HCLK_DIV_8 = 0x6, + STM32L4X_RCC_CFG_HCLK_DIV_16 = 0x7, +}; + +enum { + STM32L4X_RCC_CFG_SYSCLK_DIV_0 = 0x0, + STM32L4X_RCC_CFG_SYSCLK_DIV_2 = 0x8, + STM32L4X_RCC_CFG_SYSCLK_DIV_4 = 0x9, + STM32L4X_RCC_CFG_SYSCLK_DIV_8 = 0xa, + STM32L4X_RCC_CFG_SYSCLK_DIV_16 = 0xb, + STM32L4X_RCC_CFG_SYSCLK_DIV_64 = 0xc, + STM32L4X_RCC_CFG_SYSCLK_DIV_128 = 0xd, + STM32L4X_RCC_CFG_SYSCLK_DIV_256 = 0xe, + STM32L4X_RCC_CFG_SYSCLK_DIV_512 = 0xf, +}; + +enum { + STM32L4X_RCC_CFG_MCO_DIV_0 = 0x0, + STM32L4X_RCC_CFG_MCO_DIV_2 = 0x1, + STM32L4X_RCC_CFG_MCO_DIV_4 = 0x2, + STM32L4X_RCC_CFG_MCO_DIV_8 = 0x3, + STM32L4X_RCC_CFG_MCO_DIV_16 = 0x4, +}; + +/** + * @brief Reset and Clock Control + */ + +union __rcc_cr { + uint32_t val; + struct { + uint32_t msion :1 __packed; + uint32_t msirdy :1 __packed; + uint32_t msipllen :1 __packed; + uint32_t msirgsel :1 __packed; + uint32_t msirange :4 __packed; + uint32_t hsion :1 __packed; + uint32_t hsikeron :1 __packed; + uint32_t hsirdy :1 __packed; + uint32_t hsiasfs :1 __packed; + uint32_t rsvd__12_15 :4 __packed; + uint32_t hseon :1 __packed; + uint32_t hserdy :1 __packed; + uint32_t hsebyp :1 __packed; + uint32_t csson :1 __packed; + uint32_t rsvd__20_23 :4 __packed; + uint32_t pllon :1 __packed; + uint32_t pllrdy :1 __packed; + uint32_t pllsai1on :1 __packed; + uint32_t pllsai1rdy :1 __packed; + + /* + * SAI2 not present on L4x2, L431xx, STM32L433xx, + * and STM32L443xx. + */ + uint32_t pllsai2on :1 __packed; + uint32_t pllsai2rdy :1 __packed; + uint32_t rsvd__30_31 :2 __packed; + } bit; +}; + +union __rcc_cfgr { + uint32_t val; + struct { + uint32_t sw :2 __packed; + uint32_t sws :2 __packed; + uint32_t hpre :4 __packed; + uint32_t ppre1 :3 __packed; + uint32_t ppre2 :3 __packed; + uint32_t stopwuck :1 __packed; + uint32_t rsvd__16_23 :8 __packed; + uint32_t mcosel :3 __packed; /* 2 bits long on L4x{1,5,6} */ + uint32_t mcopre :3 __packed; + uint32_t rsvd__31 :1 __packed; + } bit; +}; + +union __rcc_pllcfgr { + uint32_t val; + struct { + uint32_t pllsrc :2 __packed; + uint32_t rsvd__2_3 :2 __packed; + uint32_t pllm :3 __packed; + uint32_t rsvd__7 :1 __packed; + uint32_t plln :7 __packed; + uint32_t rsvd__15 :1 __packed; + uint32_t pllpen :1 __packed; + uint32_t pllp :1 __packed; + uint32_t rsvd__18_19 :2 __packed; + uint32_t pllqen :1 __packed; + uint32_t pllq :2 __packed; + uint32_t rsvd__23 :1 __packed; + uint32_t pllren :1 __packed; + uint32_t pllr :2 __packed; + uint32_t pllpdiv :5 __packed; /* Not present on L4x{1,5,6} */ + } bit; +}; + +struct stm32l4x_rcc { + union __rcc_cr cr; + uint32_t icscr; + union __rcc_cfgr cfgr; + union __rcc_pllcfgr pllcfgr; + uint32_t pllsai1cfgr; + uint32_t pllsai2cfgr; + uint32_t cier; + uint32_t cifr; + uint32_t cicr; + uint32_t rsvd_0; + uint32_t ahb1rstr; + uint32_t ahb2rstr; + uint32_t ahb3rstr; + uint32_t rsvd_1; + uint32_t apb1rstr1; + uint32_t apb1rstr2; + uint32_t apb2rstr; + uint32_t rsvd_2; + uint32_t ahb1enr; + uint32_t ahb2enr; + uint32_t ahb3enr; + uint32_t rsvd_3; + uint32_t apb1enr1; + uint32_t apb1enr2; + uint32_t apb2enr; + uint32_t rsvd_4; + uint32_t ahb1smenr; + uint32_t ahb2smenr; + uint32_t ahb3smenr; + uint32_t rsvd_5; + uint32_t apb1smenr1; + uint32_t apb1smenr2; + uint32_t apb2smenr; + uint32_t rsvd_6; + uint32_t ccipr; + uint32_t rsvd_7; + uint32_t bdcr; + uint32_t csr; +}; + +#endif /* _STM32L4X_CLOCK_H_ */ diff --git a/arch/arm/soc/st_stm32/stm32l4/soc_registers.h b/arch/arm/soc/st_stm32/stm32l4/soc_registers.h index 48bfc2d17ee..ee5c26c153f 100644 --- a/arch/arm/soc/st_stm32/stm32l4/soc_registers.h +++ b/arch/arm/soc/st_stm32/stm32l4/soc_registers.h @@ -19,6 +19,8 @@ #define _STM32L4X_SOC_REGISTERS_H_ /* include register mapping headers */ +#include "rcc_registers.h" +#include "flash_registers.h" #include "syscfg_registers.h" #endif /* _STM32L4X_SOC_REGISTERS_H_ */ diff --git a/drivers/clock_control/Kconfig b/drivers/clock_control/Kconfig index eafba8bd577..509efef0e6c 100644 --- a/drivers/clock_control/Kconfig +++ b/drivers/clock_control/Kconfig @@ -58,4 +58,6 @@ source "drivers/clock_control/Kconfig.stm32f4x" source "drivers/clock_control/Kconfig.nrf5" +source "drivers/clock_control/Kconfig.stm32l4x" + endif # CLOCK_CONTROL diff --git a/drivers/clock_control/Kconfig.stm32l4x b/drivers/clock_control/Kconfig.stm32l4x new file mode 100644 index 00000000000..d59150cacba --- /dev/null +++ b/drivers/clock_control/Kconfig.stm32l4x @@ -0,0 +1,150 @@ +# Kconfig - STM32L4 MCU clock control driver config +# +# Copyright (c) 2016 Open-RnD Sp. z o.o. +# Copyright (c) 2016 BayLibre, SAS +# +# 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_SERIES_STM32L4X + +menuconfig CLOCK_CONTROL_STM32L4X + bool + prompt "STM32L4x Reset & Clock Control" + depends on CLOCK_CONTROL && SOC_SERIES_STM32L4X + default y if SOC_SERIES_STM32L4X + help + Enable driver for Reset & Clock Control subsystem found + in STM32L4 family of MCUs + +config CLOCK_CONTROL_STM32L4X_DEVICE_INIT_PRIORITY + int "Clock Control Device Priority" + default 1 + depends on CLOCK_CONTROL_STM32L4X + 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 "STM32L4X System Clock Source" +depends on CLOCK_CONTROL_STM32L4X +default CLOCK_STM32L4X_SYSCLK_SRC_PLL + +config CLOCK_STM32L4X_SYSCLK_SRC_HSE + bool "HSE" + help + Use HSE as source of SYSCLK + +config CLOCK_STM32L4X_SYSCLK_SRC_PLL + bool "PLL" + help + Use PLL as source of SYSCLK + +endchoice + +choice +prompt "STM32L4X PLL Clock Source" +depends on CLOCK_CONTROL_STM32L4X && CLOCK_STM32L4X_SYSCLK_SRC_PLL +default CLOCK_STM32L4X_PLL_SRC_HSI + +config CLOCK_STM32L4X_PLL_SRC_MSI + bool "MSI" + help + Use MSI as source of PLL + +config CLOCK_STM32L4X_PLL_SRC_HSI + bool "HSI" + help + Use HSI as source of PLL + +endchoice + +config CLOCK_STM32L4X_HSE_BYPASS + bool "HSE bypass" + depends on CLOCK_CONTROL_STM32L4X && CLOCK_STM32L4X_SYSCLK_SRC_HSE + help + Enable this option to bypass external high-speed clock (HSE). + +config CLOCK_STM32L4X_PLL_DIVISOR + int "PLL divisor" + depends on CLOCK_CONTROL_STM32L4X && CLOCK_STM32L4X_SYSCLK_SRC_PLL + default 1 + range 1 8 + help + PLL divisor, allowed values: 1-8. With this ensure that the PLL + VCO input frequency ranges from 4 to 16MHz. + +config CLOCK_STM32L4X_PLL_MULTIPLIER + int "PLL multiplier" + depends on CLOCK_CONTROL_STM32L4X && CLOCK_STM32L4X_SYSCLK_SRC_PLL + default 20 + range 8 86 + help + PLL multiplier, allowed values: 2-16. PLL output must not + exceed 344MHz. + +config CLOCK_STM32L4X_PLL_P_DIVISOR + int "PLL P Divisor" + depends on CLOCK_CONTROL_STM32L4X + default 7 + range 0 17 + help + PLL P Output divisor, allowed values: 0, 7, 17. + +config CLOCK_STM32L4X_PLL_Q_DIVISOR + int "PLL Q Divisor" + depends on CLOCK_CONTROL_STM32L4X + default 2 + range 0 8 + help + PLL Q Output divisor, allowed values: 0, 2, 4, 6, 8. + +config CLOCK_STM32L4X_PLL_R_DIVISOR + int "PLL R Divisor" + depends on CLOCK_CONTROL_STM32L4X + default 4 + range 0 8 + help + PLL R Output divisor, allowed values: 0, 2, 4, 6, 8. + +config CLOCK_STM32L4X_AHB_PRESCALER + int "AHB prescaler" + depends on CLOCK_CONTROL_STM32L4X + default 0 + range 0 512 + help + AHB prescaler, allowed values: 0, 2, 4, 8, 16, 64, 128, + 256, 512. + +config CLOCK_STM32L4X_APB1_PRESCALER + int "APB1 prescaler" + depends on CLOCK_CONTROL_STM32L4X + default 0 + range 0 16 + help + APB1 Low speed clock (PCLK1) prescaler, allowed values: + 0, 2, 4, 8, 16 + +config CLOCK_STM32L4X_APB2_PRESCALER + int "APB2 prescaler" + depends on CLOCK_CONTROL_STM32L4X + 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/Makefile b/drivers/clock_control/Makefile index b23147eb936..c4f7f4f589d 100644 --- a/drivers/clock_control/Makefile +++ b/drivers/clock_control/Makefile @@ -2,3 +2,4 @@ obj-$(CONFIG_CLOCK_CONTROL_QUARK_SE) += quark_se_clock_control.o obj-$(CONFIG_CLOCK_CONTROL_STM32F10X) += stm32f10x_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 diff --git a/drivers/clock_control/stm32l4x_clock.c b/drivers/clock_control/stm32l4x_clock.c new file mode 100644 index 00000000000..c4ae845dc40 --- /dev/null +++ b/drivers/clock_control/stm32l4x_clock.c @@ -0,0 +1,393 @@ +/* + * Copyright (c) 2016 Open-RnD Sp. z o.o. + * Copyright (c) 2016 BayLibre, SAS + * + * 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: + * STM32L4x1, STM32L4x2, STM32L431xx STM32L443xx STM32L433xx, STM32L4x5, + * STM32l4x6 + * advanced ARM ® -based 32-bit MCUs + * + * Chapter 7: Low-, medium-, high- and XL-density reset and + * clock control + */ + +#include +#include +#include +#include +#include +#include + +struct stm32l4x_rcc_data { + uint8_t *base; +}; + +static inline int stm32l4x_clock_control_on(struct device *dev, + clock_control_subsys_t sub_system) +{ + struct stm32l4x_rcc_data *data = dev->driver_data; + volatile struct stm32l4x_rcc *rcc = (struct stm32l4x_rcc *)(data->base); + uint32_t subsys = POINTER_TO_UINT(sub_system); + uint32_t base = STM32L4X_CLOCK_BASE(subsys); + uint32_t bit = 1 << STM32L4X_CLOCK_BIT(subsys); + + switch (base) { + case STM32L4X_CLOCK_AHB1_BASE: + rcc->ahb1enr |= bit; + break; + case STM32L4X_CLOCK_AHB2_BASE: + rcc->ahb2enr |= bit; + break; + case STM32L4X_CLOCK_AHB3_BASE: + rcc->ahb3enr |= bit; + break; + case STM32L4X_CLOCK_APB1_1_BASE: + rcc->apb1enr1 |= bit; + break; + case STM32L4X_CLOCK_APB1_2_BASE: + rcc->apb1enr2 |= bit; + break; + case STM32L4X_CLOCK_APB2_BASE: + rcc->apb2enr |= bit; + break; + default: + return -EINVAL; + } + + return 0; +} + +static inline int stm32l4x_clock_control_off(struct device *dev, + clock_control_subsys_t sub_system) +{ + struct stm32l4x_rcc_data *data = dev->driver_data; + volatile struct stm32l4x_rcc *rcc = + (struct stm32l4x_rcc *)(data->base); + uint32_t subsys = POINTER_TO_UINT(sub_system); + uint32_t base = STM32L4X_CLOCK_BASE(subsys); + uint32_t bit = 1 << STM32L4X_CLOCK_BIT(subsys); + + switch (base) { + case STM32L4X_CLOCK_AHB1_BASE: + rcc->ahb1enr &= bit; + break; + case STM32L4X_CLOCK_AHB2_BASE: + rcc->ahb2enr &= bit; + break; + case STM32L4X_CLOCK_AHB3_BASE: + rcc->ahb3enr &= bit; + break; + case STM32L4X_CLOCK_APB1_1_BASE: + rcc->apb1enr1 &= bit; + break; + case STM32L4X_CLOCK_APB1_2_BASE: + rcc->apb1enr2 &= bit; + break; + case STM32L4X_CLOCK_APB2_BASE: + rcc->apb2enr &= bit; + break; + default: + return -EINVAL; + } + + 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) +{ + size_t i; + + for (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 STM32L4X_RCC_CFG_HCLK_DIV_0; + } + + const struct regval_map map[] = { + {0, STM32L4X_RCC_CFG_HCLK_DIV_0}, + {2, STM32L4X_RCC_CFG_HCLK_DIV_2}, + {4, STM32L4X_RCC_CFG_HCLK_DIV_4}, + {8, STM32L4X_RCC_CFG_HCLK_DIV_8}, + {16, STM32L4X_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 STM32L4X_RCC_CFG_SYSCLK_DIV_0; + + const struct regval_map map[] = { + {0, STM32L4X_RCC_CFG_SYSCLK_DIV_0}, + {2, STM32L4X_RCC_CFG_SYSCLK_DIV_2}, + {4, STM32L4X_RCC_CFG_SYSCLK_DIV_4}, + {8, STM32L4X_RCC_CFG_SYSCLK_DIV_8}, + {16, STM32L4X_RCC_CFG_SYSCLK_DIV_16}, + {64, STM32L4X_RCC_CFG_SYSCLK_DIV_64}, + {128, STM32L4X_RCC_CFG_SYSCLK_DIV_128}, + {256, STM32L4X_RCC_CFG_SYSCLK_DIV_256}, + {512, STM32L4X_RCC_CFG_SYSCLK_DIV_512}, + }; + return map_reg_val(map, ARRAY_SIZE(map), prescaler); +} + +static uint32_t get_ahb_clock(uint32_t sysclk) +{ + /* AHB clock is generated based on SYSCLK */ + uint32_t sysclk_div = CONFIG_CLOCK_STM32L4X_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 stm32l4x_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 base = STM32L4X_CLOCK_BASE(subsys); + + /* assumes SYSCLK is SYS_CLOCK_HW_CYCLES_PER_SEC */ + uint32_t ahb_clock = + get_ahb_clock(CONFIG_SYS_CLOCK_HW_CYCLES_PER_SEC); + + switch (base) { + case STM32L4X_CLOCK_AHB1_BASE: + case STM32L4X_CLOCK_AHB2_BASE: + case STM32L4X_CLOCK_AHB3_BASE: + *rate = ahb_clock; + break; + case STM32L4X_CLOCK_APB1_1_BASE: + case STM32L4X_CLOCK_APB1_2_BASE: + *rate = get_apb_clock(ahb_clock, + CONFIG_CLOCK_STM32L4X_APB1_PRESCALER); + break; + case STM32L4X_CLOCK_APB2_BASE: + *rate = get_apb_clock(ahb_clock, + CONFIG_CLOCK_STM32L4X_APB2_PRESCALER); + break; + default: + return -EINVAL; + } + + return 0; +} + +static const struct clock_control_driver_api stm32l4x_clock_control_api = { + .on = stm32l4x_clock_control_on, + .off = stm32l4x_clock_control_off, + .get_rate = stm32l4x_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 stm32l4x_flash *flash = + (struct stm32l4x_flash *)(FLASH_R_BASE); + + if (CONFIG_SYS_CLOCK_HW_CYCLES_PER_SEC <= 16000000) { + flash->acr.bit.latency = STM32L4X_FLASH_LATENCY_0; + } else if (CONFIG_SYS_CLOCK_HW_CYCLES_PER_SEC <= 32000000) { + flash->acr.bit.latency = STM32L4X_FLASH_LATENCY_1; + } else if (CONFIG_SYS_CLOCK_HW_CYCLES_PER_SEC <= 48000000) { + flash->acr.bit.latency = STM32L4X_FLASH_LATENCY_2; + } else if (CONFIG_SYS_CLOCK_HW_CYCLES_PER_SEC <= 64000000) { + flash->acr.bit.latency = STM32L4X_FLASH_LATENCY_3; + } else if (CONFIG_SYS_CLOCK_HW_CYCLES_PER_SEC <= 80000000) { + flash->acr.bit.latency = STM32L4X_FLASH_LATENCY_4; + } +} + +static int pllqrdiv(int val) +{ + switch (val) { + case 2: + return 0; + case 4: + return 1; + case 6: + return 2; + case 8: + return 3; + } + + return 0; +} + +static int stm32l4x_clock_control_init(struct device *dev) +{ + struct stm32l4x_rcc_data *data = dev->driver_data; + volatile struct stm32l4x_rcc *rcc; + /* SYSCLK source defaults to MSI */ + int sysclk_src = STM32L4X_RCC_CFG_SYSCLK_SRC_MSI; + uint32_t hpre = ahb_prescaler(CONFIG_CLOCK_STM32L4X_AHB_PRESCALER); + uint32_t ppre1 = apb_prescaler(CONFIG_CLOCK_STM32L4X_APB1_PRESCALER); + uint32_t ppre2 = apb_prescaler(CONFIG_CLOCK_STM32L4X_APB2_PRESCALER); +#ifdef CONFIG_CLOCK_STM32L4X_SYSCLK_SRC_PLL + uint32_t pllm = CONFIG_CLOCK_STM32L4X_PLL_DIVISOR-1; + uint32_t plln = CONFIG_CLOCK_STM32L4X_PLL_MULTIPLIER; + uint32_t pllpdiv = CONFIG_CLOCK_STM32L4X_PLL_P_DIVISOR; + uint32_t pllqdiv = pllqrdiv(CONFIG_CLOCK_STM32L4X_PLL_Q_DIVISOR); + uint32_t pllrdiv = pllqrdiv(CONFIG_CLOCK_STM32L4X_PLL_R_DIVISOR); +#endif /* CONFIG_CLOCK_STM32L4X_PLL_MULTIPLIER */ + + + rcc = (struct stm32l4x_rcc *)(data->base); + /* disable PLL */ + rcc->cr.bit.pllon = 0; + /* disable HSE */ + rcc->cr.bit.hseon = 0; + +#ifdef CONFIG_CLOCK_STM32L4X_HSE_BYPASS + /* HSE is disabled, HSE bypass can be enabled*/ + rcc->cr.bit.hsebyp = 1; +#endif + +#ifdef CONFIG_CLOCK_STM32L4X_PLL_SRC_MSI + /* enable MSI clock */ + rcc->cr.bit.msion = 1; + /* this should end after one test */ + while (rcc->cr.bit.msirdy != 1) { + } + + /* PLL input from HSI/2 = 4MHz */ + rcc->pllcfgr.bit.pllsrc = STM32L4X_RCC_CFG_PLL_SRC_MSI; +#endif /* CONFIG_CLOCK_STM32L4X_PLL_SRC_MSI */ + +#ifdef CONFIG_CLOCK_STM32L4X_PLL_SRC_HSI + + /* wait for to become ready */ + rcc->cr.bit.hsion = 1; + while (rcc->cr.bit.hsirdy != 1) { + } + + rcc->pllcfgr.bit.pllsrc = STM32L4X_RCC_CFG_PLL_SRC_HSI; +#endif /* CONFIG_CLOCK_STM32L4X_PLL_SRC_HSI */ + + /* 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_STM32L4X_SYSCLK_SRC_PLL + /* setup PLL multiplication and divisor (PLL must be disabled) */ + rcc->pllcfgr.bit.pllm = pllm; + rcc->pllcfgr.bit.plln = plln; + + /* Setup PLL output divisors */ + rcc->pllcfgr.bit.pllp = pllpdiv == 17 ? 1 : 0; + rcc->pllcfgr.bit.pllpen = !!pllpdiv; + rcc->pllcfgr.bit.pllq = pllqdiv; + rcc->pllcfgr.bit.pllqen = !!CONFIG_CLOCK_STM32L4X_PLL_Q_DIVISOR; + rcc->pllcfgr.bit.pllr = pllrdiv; + rcc->pllcfgr.bit.pllren = !!CONFIG_CLOCK_STM32L4X_PLL_R_DIVISOR; + + /* enable PLL */ + rcc->cr.bit.pllon = 1; + + /* wait for PLL to become ready */ + while (rcc->cr.bit.pllrdy != 1) { + } + + sysclk_src = STM32L4X_RCC_CFG_SYSCLK_SRC_PLL; +#elif defined(CONFIG_CLOCK_STM32L4X_SYSCLK_SRC_HSE) + /* wait for to become ready */ + rcc->cr.bit.hseon = 1; + while (rcc->cr.bit.hserdy != 1) { + } + + sysclk_src = STM32L4X_RCC_CFG_SYSCLK_SRC_HSE; +#else +#error "Need to select or implement support for this STM32L4X SYSCLK source" +#endif + + /* 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 stm32l4x_rcc_data stm32l4x_rcc_data = { + .base = (uint8_t *)RCC_BASE, +}; + +/** + * @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_stm32l4x, STM32_CLOCK_CONTROL_NAME, + &stm32l4x_clock_control_init, + &stm32l4x_rcc_data, NULL, + PRE_KERNEL_1, + CONFIG_CLOCK_CONTROL_STM32L4X_DEVICE_INIT_PRIORITY, + &stm32l4x_clock_control_api); diff --git a/include/drivers/clock_control/stm32_clock_control.h b/include/drivers/clock_control/stm32_clock_control.h index 2d6084c1596..d8d67256342 100644 --- a/include/drivers/clock_control/stm32_clock_control.h +++ b/include/drivers/clock_control/stm32_clock_control.h @@ -1,5 +1,6 @@ /* * Copyright (c) 2016 Open-RnD Sp. z o.o. + * Copyright (c) 2016 BayLibre, SAS * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -27,4 +28,8 @@ #include "stm32f4_clock_control.h" #endif +#ifdef CONFIG_SOC_SERIES_STM32L4X +#include "stm32l4x_clock_control.h" +#endif + #endif /* _STM32_CLOCK_CONTROL_H_ */ diff --git a/include/drivers/clock_control/stm32l4x_clock_control.h b/include/drivers/clock_control/stm32l4x_clock_control.h new file mode 100644 index 00000000000..107e3e07041 --- /dev/null +++ b/include/drivers/clock_control/stm32l4x_clock_control.h @@ -0,0 +1,111 @@ +/* + * Copyright (c) 2016 Open-RnD Sp. z o.o. + * Copyright (c) 2016 BayLibre, SAS + * + * 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. + */ +#ifndef _STM32L4X_CLOCK_CONTROL_H_ +#define _STM32L4X_CLOCK_CONTROL_H_ + +/** + * @file + * + * @brief Clock subsystem IDs for STM32L4 family + */ + +enum { + STM32L4X_CLOCK_AHB1_BASE = 0, + STM32L4X_CLOCK_AHB2_BASE, + STM32L4X_CLOCK_AHB3_BASE, + STM32L4X_CLOCK_APB1_1_BASE, + STM32L4X_CLOCK_APB1_2_BASE, + STM32L4X_CLOCK_APB2_BASE, + STM32L4X_CLOCK_BASE_COUNT, +}; + +#define STM32L4X_CLOCK_ID(_base, id) \ + ((STM32L4X_CLOCK_##_base##_BASE << 16) + id) + +#define STM32L4X_CLOCK_BASE(num) ((num) >> 16) +#define STM32L4X_CLOCK_BIT(num) ((num) & 0xFFFF) + +enum { + /* AHB1 */ + STM32L4X_CLOCK_SUBSYS_DMA1 = STM32L4X_CLOCK_ID(AHB1, 0), + STM32L4X_CLOCK_SUBSYS_DMA2 = STM32L4X_CLOCK_ID(AHB1, 1), + STM32L4X_CLOCK_SUBSYS_FLASH = STM32L4X_CLOCK_ID(AHB1, 8), + STM32L4X_CLOCK_SUBSYS_CRC = STM32L4X_CLOCK_ID(AHB1, 12), + STM32L4X_CLOCK_SUBSYS_TSC = STM32L4X_CLOCK_ID(AHB1, 16), + + /* AHB2 */ + STM32L4X_CLOCK_SUBSYS_GPIOA = STM32L4X_CLOCK_ID(AHB2, 0), + STM32L4X_CLOCK_SUBSYS_GPIOB = STM32L4X_CLOCK_ID(AHB2, 1), + STM32L4X_CLOCK_SUBSYS_GPIOC = STM32L4X_CLOCK_ID(AHB2, 2), + STM32L4X_CLOCK_SUBSYS_GPIOD = STM32L4X_CLOCK_ID(AHB2, 3), + STM32L4X_CLOCK_SUBSYS_GPIOE = STM32L4X_CLOCK_ID(AHB2, 4), + STM32L4X_CLOCK_SUBSYS_GPIOF = STM32L4X_CLOCK_ID(AHB2, 5), + STM32L4X_CLOCK_SUBSYS_GPIOG = STM32L4X_CLOCK_ID(AHB2, 6), + STM32L4X_CLOCK_SUBSYS_GPIOH = STM32L4X_CLOCK_ID(AHB2, 7), + STM32L4X_CLOCK_SUBSYS_OTGFS = STM32L4X_CLOCK_ID(AHB2, 12), + STM32L4X_CLOCK_SUBSYS_ADC = STM32L4X_CLOCK_ID(AHB2, 13), + STM32L4X_CLOCK_SUBSYS_AES = STM32L4X_CLOCK_ID(AHB2, 16), + STM32L4X_CLOCK_SUBSYS_RNG = STM32L4X_CLOCK_ID(AHB2, 18), + + /* AHB3 */ + STM32L4X_CLOCK_SUBSYS_FMC = STM32L4X_CLOCK_ID(AHB3, 0), + STM32L4X_CLOCK_SUBSYS_QSPI = STM32L4X_CLOCK_ID(AHB3, 0), + + /* APB1 */ + STM32L4X_CLOCK_SUBSYS_TIM2 = STM32L4X_CLOCK_ID(APB1_1, 0), + STM32L4X_CLOCK_SUBSYS_TIM3 = STM32L4X_CLOCK_ID(APB1_1, 1), + STM32L4X_CLOCK_SUBSYS_TIM4 = STM32L4X_CLOCK_ID(APB1_1, 2), + STM32L4X_CLOCK_SUBSYS_TIM5 = STM32L4X_CLOCK_ID(APB1_1, 3), + STM32L4X_CLOCK_SUBSYS_TIM6 = STM32L4X_CLOCK_ID(APB1_1, 4), + STM32L4X_CLOCK_SUBSYS_TIM7 = STM32L4X_CLOCK_ID(APB1_1, 5), + STM32L4X_CLOCK_SUBSYS_LCD = STM32L4X_CLOCK_ID(APB1_1, 9), + STM32L4X_CLOCK_SUBSYS_WWDG = STM32L4X_CLOCK_ID(APB1_1, 11), + STM32L4X_CLOCK_SUBSYS_SPI2 = STM32L4X_CLOCK_ID(APB1_1, 14), + STM32L4X_CLOCK_SUBSYS_SPI3 = STM32L4X_CLOCK_ID(APB1_1, 15), + STM32L4X_CLOCK_SUBSYS_USART2 = STM32L4X_CLOCK_ID(APB1_1, 17), + STM32L4X_CLOCK_SUBSYS_USART3 = STM32L4X_CLOCK_ID(APB1_1, 18), + STM32L4X_CLOCK_SUBSYS_UART4 = STM32L4X_CLOCK_ID(APB1_1, 19), + STM32L4X_CLOCK_SUBSYS_UART5 = STM32L4X_CLOCK_ID(APB1_1, 20), + STM32L4X_CLOCK_SUBSYS_I2C1 = STM32L4X_CLOCK_ID(APB1_1, 21), + STM32L4X_CLOCK_SUBSYS_I2C2 = STM32L4X_CLOCK_ID(APB1_1, 22), + STM32L4X_CLOCK_SUBSYS_I2C3 = STM32L4X_CLOCK_ID(APB1_1, 23), + STM32L4X_CLOCK_SUBSYS_PWR = STM32L4X_CLOCK_ID(APB1_1, 28), + STM32L4X_CLOCK_SUBSYS_DAC = STM32L4X_CLOCK_ID(APB1_1, 29), + STM32L4X_CLOCK_SUBSYS_OPAMP = STM32L4X_CLOCK_ID(APB1_1, 30), + STM32L4X_CLOCK_SUBSYS_LPTIM1 = STM32L4X_CLOCK_ID(APB1_1, 31), + STM32L4X_CLOCK_SUBSYS_LPUART1 = STM32L4X_CLOCK_ID(APB1_2, 0), + STM32L4X_CLOCK_SUBSYS_SWPMI1 = STM32L4X_CLOCK_ID(APB1_2, 2), + STM32L4X_CLOCK_SUBSYS_LPTIM2 = STM32L4X_CLOCK_ID(APB1_2, 5), + + /* APB2 */ + STM32L4X_CLOCK_SUBSYS_SYSCFG = STM32L4X_CLOCK_ID(APB2, 0), + STM32L4X_CLOCK_SUBSYS_FW = STM32L4X_CLOCK_ID(APB2, 7), + STM32L4X_CLOCK_SUBSYS_SDMMC1 = STM32L4X_CLOCK_ID(APB2, 10), + STM32L4X_CLOCK_SUBSYS_TIM1 = STM32L4X_CLOCK_ID(APB2, 11), + STM32L4X_CLOCK_SUBSYS_SPI1 = STM32L4X_CLOCK_ID(APB2, 12), + STM32L4X_CLOCK_SUBSYS_TIM8 = STM32L4X_CLOCK_ID(APB2, 13), + STM32L4X_CLOCK_SUBSYS_USART1 = STM32L4X_CLOCK_ID(APB2, 14), + STM32L4X_CLOCK_SUBSYS_TIM15 = STM32L4X_CLOCK_ID(APB2, 16), + STM32L4X_CLOCK_SUBSYS_TIM16 = STM32L4X_CLOCK_ID(APB2, 17), + STM32L4X_CLOCK_SUBSYS_TIM17 = STM32L4X_CLOCK_ID(APB2, 18), + STM32L4X_CLOCK_SUBSYS_SAI1 = STM32L4X_CLOCK_ID(APB2, 21), + STM32L4X_CLOCK_SUBSYS_SAI2 = STM32L4X_CLOCK_ID(APB2, 22), + STM32L4X_CLOCK_SUBSYS_DFSDM1 = STM32L4X_CLOCK_ID(APB2, 24), + +}; + +#endif /* _STM32L4_CLOCK_CONTROL_H_ */