|  | /* | 
|  | * Copyright (c) 2016 RnDity Sp. z o.o. | 
|  | * | 
|  | * SPDX-License-Identifier: Apache-2.0 | 
|  | */ | 
|  |  | 
|  | /** | 
|  | * @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 <soc.h> | 
|  | #include <soc_registers.h> | 
|  | #include <clock_control.h> | 
|  | #include <misc/util.h> | 
|  | #include <clock_control/stm32_clock_control.h> | 
|  |  | 
|  | struct stm32f10x_rcc_data { | 
|  | u8_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); | 
|  | u32_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); | 
|  | u32_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 u32_t get_ahb_clock(u32_t sysclk) | 
|  | { | 
|  | /* AHB clock is generated based on SYSCLK  */ | 
|  | u32_t sysclk_div = | 
|  | CONFIG_CLOCK_STM32F10X_CONN_LINE_AHB_PRESCALER; | 
|  |  | 
|  | if (sysclk_div == 0) { | 
|  | sysclk_div = 1; | 
|  | } | 
|  |  | 
|  | return sysclk / sysclk_div; | 
|  | } | 
|  |  | 
|  | static u32_t get_apb_clock(u32_t ahb_clock, u32_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, | 
|  | u32_t *rate) | 
|  | { | 
|  | ARG_UNUSED(clock); | 
|  |  | 
|  | u32_t subsys = POINTER_TO_UINT(sub_system); | 
|  | u32_t prescaler = | 
|  | CONFIG_CLOCK_STM32F10X_CONN_LINE_APB1_PRESCALER; | 
|  | /* assumes SYSCLK is SYS_CLOCK_HW_CYCLES_PER_SEC */ | 
|  | u32_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; | 
|  | u32_t hpre = | 
|  | ahb_prescaler(CONFIG_CLOCK_STM32F10X_CONN_LINE_AHB_PRESCALER); | 
|  | u32_t ppre1 = | 
|  | apb_prescaler(CONFIG_CLOCK_STM32F10X_CONN_LINE_APB1_PRESCALER); | 
|  | u32_t ppre2 = | 
|  | apb_prescaler(CONFIG_CLOCK_STM32F10X_CONN_LINE_APB2_PRESCALER); | 
|  | #ifdef CONFIG_CLOCK_STM32F10X_CONN_LINE_PLL_MULTIPLIER | 
|  | u32_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 | 
|  | u32_t pll2_mul = | 
|  | pll2mul(CONFIG_CLOCK_STM32F10X_CONN_LINE_PLL2_MULTIPLIER); | 
|  | #endif /* CONFIG_CLOCK_STM32F10X_CONN_LINE_PLL2_MULTIPLIER */ | 
|  | #ifdef CONFIG_CLOCK_STM32F10X_CONN_LINE_PREDIV1 | 
|  | u32_t prediv1 = | 
|  | prediv_prescaler(CONFIG_CLOCK_STM32F10X_CONN_LINE_PREDIV1); | 
|  | #endif /* CONFIG_CLOCK_STM32F10X_CONN_LINE_PREDIV1 */ | 
|  | #ifdef CONFIG_CLOCK_STM32F10X_CONN_LINE_PREDIV2 | 
|  | u32_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 = pll2_mul; | 
|  |  | 
|  | /* enable PLL2 */ | 
|  | rcc->cr.bit.pll2on = 1; | 
|  |  | 
|  | /* wait for PLL to become ready */ | 
|  | while (rcc->cr.bit.pll2rdy != 1) { | 
|  | } | 
|  |  | 
|  | #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 = (u8_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); |