From 6d4da19602dbcf68b99347bf6983712be79e397d Mon Sep 17 00:00:00 2001 From: Adam Podogrocki Date: Wed, 21 Jun 2017 10:36:18 +0200 Subject: [PATCH] iwdg: make independent watchdog driver compliant with STM32Cube LL API Appropriate inclusion done for F1/F3/F4/L4 families in SoC tree. JIRA: ZEP-2347 Signed-off-by: Adam Podogrocki --- arch/arm/soc/st_stm32/stm32f1/soc.h | 4 + arch/arm/soc/st_stm32/stm32f3/soc.h | 4 + arch/arm/soc/st_stm32/stm32f4/soc.h | 4 + arch/arm/soc/st_stm32/stm32l4/soc.h | 4 + drivers/watchdog/Kconfig.stm32 | 39 ++---- drivers/watchdog/iwdg_stm32.c | 180 ++++++++++++++++++---------- drivers/watchdog/iwdg_stm32.h | 65 ++-------- 7 files changed, 160 insertions(+), 140 deletions(-) diff --git a/arch/arm/soc/st_stm32/stm32f1/soc.h b/arch/arm/soc/st_stm32/stm32f1/soc.h index 9980e5cf0b5..194b40e99fc 100644 --- a/arch/arm/soc/st_stm32/stm32f1/soc.h +++ b/arch/arm/soc/st_stm32/stm32f1/soc.h @@ -48,6 +48,10 @@ #include #endif +#ifdef CONFIG_IWDG_STM32 +#include +#endif + #endif /* !_ASMLANGUAGE */ #endif /* _STM32F1_SOC_H_ */ diff --git a/arch/arm/soc/st_stm32/stm32f3/soc.h b/arch/arm/soc/st_stm32/stm32f3/soc.h index b4be84b0036..d0d0e8101eb 100644 --- a/arch/arm/soc/st_stm32/stm32f3/soc.h +++ b/arch/arm/soc/st_stm32/stm32f3/soc.h @@ -49,6 +49,10 @@ #include #endif +#ifdef CONFIG_IWDG_STM32 +#include +#endif + #endif /* !_ASMLANGUAGE */ #endif /* _STM32F3_SOC_H_ */ diff --git a/arch/arm/soc/st_stm32/stm32f4/soc.h b/arch/arm/soc/st_stm32/stm32f4/soc.h index 84f437c28ae..e4ab8a76652 100644 --- a/arch/arm/soc/st_stm32/stm32f4/soc.h +++ b/arch/arm/soc/st_stm32/stm32f4/soc.h @@ -52,6 +52,10 @@ #include #endif +#ifdef CONFIG_IWDG_STM32 +#include +#endif + /* For IMG_MANAGER */ #if defined(CONFIG_SOC_FLASH_STM32) #define FLASH_DRIVER_NAME CONFIG_SOC_FLASH_STM32_DEV_NAME diff --git a/arch/arm/soc/st_stm32/stm32l4/soc.h b/arch/arm/soc/st_stm32/stm32l4/soc.h index 3da73d53885..89637d59390 100644 --- a/arch/arm/soc/st_stm32/stm32l4/soc.h +++ b/arch/arm/soc/st_stm32/stm32l4/soc.h @@ -47,6 +47,10 @@ #include #endif +#ifdef CONFIG_IWDG_STM32 +#include +#endif + /* For IMG_MANAGER */ #if defined(CONFIG_SOC_FLASH_STM32) #define FLASH_DRIVER_NAME CONFIG_SOC_FLASH_STM32_DEV_NAME diff --git a/drivers/watchdog/Kconfig.stm32 b/drivers/watchdog/Kconfig.stm32 index 0a5029cd9dd..812e95102e8 100644 --- a/drivers/watchdog/Kconfig.stm32 +++ b/drivers/watchdog/Kconfig.stm32 @@ -1,43 +1,21 @@ # Kconfig - STM32 IWDG configuration # # Copyright (c) 2016 Open-RnD Sp. z o.o. +# Copyright (c) 2017 RnDity Sp. z o.o. # # SPDX-License-Identifier: Apache-2.0 # -if SOC_FAMILY_STM32 - -config IWDG_STM32 +menuconfig IWDG_STM32 bool "Independent Watchdog (IWDG) Driver for STM32 family of MCUs" + depends on SOC_FAMILY_STM32 help Enable IWDG driver for STM32 line of MCUs -config IWDG_STM32_PRESCALER - int "Prescaler divider for clock feeding the IWDG" - depends on IWDG_STM32 - default 4 - range 4 256 - help - Set the prescaler divider for the clock feeding the Independent - Watchdog. Higher values indicate that the watchdog will need to - be reloaded more frequently. Allowed values: 4, 8, 16, 32, 64, - 128, 256. This setting combined with reload counter defines the - watchdog countdown time. - -config IWDG_STM32_RELOAD_COUNTER - int "Value for IWDG counter" - depends on IWDG_STM32 - default 2048 - range 0 4095 - help - Configure the value to be loaded into the watchdog's counter each - time a reload operation is performed. This value combined with - prescaler setting defines the watchdog countdown time. - config IWDG_STM32_START_AT_BOOT bool "Start IWDG during boot" depends on IWDG_STM32 - default n + default y help Enable this setting to allow IWDG to be automatically started during device initialization. Note that once IWDG is started @@ -51,4 +29,11 @@ config IWDG_STM32_DEVICE_NAME help Set the name used by IWDG device during registration. -endif # SOC_FAMILY_STM32 +config IWDG_STM32_TIMEOUT + int "Value for IWDG timeout in [us]" + depends on IWDG_STM32 + default 100 + range 100 26214400 + help + Set timeout value for IWDG in microseconds. + The min timeout supported is 0.1ms, the max timeout is 26214.4ms. diff --git a/drivers/watchdog/iwdg_stm32.c b/drivers/watchdog/iwdg_stm32.c index fc1065ea307..f4b9290e4dc 100644 --- a/drivers/watchdog/iwdg_stm32.c +++ b/drivers/watchdog/iwdg_stm32.c @@ -1,36 +1,71 @@ /* * Copyright (c) 2016 Open-RnD Sp. z o.o. + * Copyright (c) 2017 RnDity Sp. z o.o. * * SPDX-License-Identifier: Apache-2.0 */ -/** - * @brief Driver for Independent Watchdog (IWDG) for STM32 MCUs - * - * Based on reference manual: - * STM32F101xx, STM32F102xx, STM32F103xx, STM32F105xx and STM32F107xx - * advanced ARM ® -based 32-bit MCUs - * - * Chapter 19: Independent watchdog (IWDG) - * - */ - #include #include #include +#include #include "iwdg_stm32.h" -#define AS_IWDG(__base_addr) \ - (struct iwdg_stm32 *)(__base_addr) +/* Minimal timeout in microseconds. */ +#define IWDG_TIMEOUT_MIN 100 +/* Maximal timeout in microseconds. */ +#define IWDG_TIMEOUT_MAX 26214400 + +#define IS_IWDG_TIMEOUT(__TIMEOUT__) \ + (((__TIMEOUT__) >= IWDG_TIMEOUT_MIN) && \ + ((__TIMEOUT__) <= IWDG_TIMEOUT_MAX)) + +/* + * Status register need 5 RC LSI divided by prescaler clock to be updated. + * With higher prescaler (256U), and according to HSI variation, + * we need to wait at least 6 cycles so 48 ms. + */ + +#define IWDG_DEFAULT_TIMEOUT 48u + +/** + * @brief Calculates prescaler & reload values. + * + * @param timeout Timeout value in microseconds. + * @param prescaler Pointer to prescaler value. + * @param reload Pointer to reload value. + */ +static void iwdg_stm32_convert_timeout(u32_t timeout, + u32_t *prescaler, + u32_t *reload) +{ + assert(IS_IWDG_TIMEOUT(timeout)); + + u16_t divider = 0; + u8_t shift = 0; + + /* Convert timeout to seconds. */ + float m_timeout = (float)timeout / 1000000 * LSI_VALUE; + + do { + divider = 4 << shift; + shift++; + } while ((m_timeout / divider) > 0xFFF); + + /* + * Value of the 'shift' variable corresponds to the + * defines of LL_IWDG_PRESCALER_XX type. + */ + *prescaler = --shift; + *reload = (uint32_t)(m_timeout / divider) - 1; +} static void iwdg_stm32_enable(struct device *dev) { - volatile struct iwdg_stm32 *iwdg = AS_IWDG(IWDG_BASE); + IWDG_TypeDef *iwdg = IWDG_STM32_STRUCT(dev); - ARG_UNUSED(dev); - - iwdg->kr.bit.key = STM32_IWDG_KR_START; + LL_IWDG_Enable(iwdg); } static void iwdg_stm32_disable(struct device *dev) @@ -40,30 +75,62 @@ static void iwdg_stm32_disable(struct device *dev) } static int iwdg_stm32_set_config(struct device *dev, - struct wdt_config *config) + struct wdt_config *config) { - ARG_UNUSED(dev); - ARG_UNUSED(config); + IWDG_TypeDef *iwdg = IWDG_STM32_STRUCT(dev); + u32_t timeout = config->timeout; + u32_t prescaler = 0; + u32_t reload = 0; + u32_t tickstart; - /* no configuration */ + assert(IS_IWDG_TIMEOUT(timeout)); - return -ENOTSUP; + iwdg_stm32_convert_timeout(timeout, &prescaler, &reload); + + assert(IS_IWDG_PRESCALER(prescaler)); + assert(IS_IWDG_RELOAD(reload)); + + LL_IWDG_EnableWriteAccess(iwdg); + + LL_IWDG_SetPrescaler(iwdg, prescaler); + LL_IWDG_SetReloadCounter(iwdg, reload); + +#if defined(CONFIG_SOC_SERIES_STM32F3X) || defined(CONFIG_SOC_SERIES_STM32L4X) + /* Neither STM32F1X nor STM32F4 series supports window option. */ + LL_IWDG_SetWindow(iwdg, 0x0FFF); +#endif + + tickstart = k_uptime_get_32(); + + while (LL_IWDG_IsReady(iwdg) == 0) { + if ((k_uptime_get_32() - tickstart) > IWDG_DEFAULT_TIMEOUT) { + return -ENODEV; + } + } + + LL_IWDG_ReloadCounter(iwdg); + + return 0; } static void iwdg_stm32_get_config(struct device *dev, - struct wdt_config *config) + struct wdt_config *config) { - ARG_UNUSED(dev); - ARG_UNUSED(config); + IWDG_TypeDef *iwdg = IWDG_STM32_STRUCT(dev); + + u32_t prescaler = LL_IWDG_GetPrescaler(iwdg); + u32_t reload = LL_IWDG_GetReloadCounter(iwdg); + + /* Timeout given in microseconds. */ + config->timeout = (u32_t)((4 << prescaler) * (reload + 1) + * (1000000 / LSI_VALUE)); } static void iwdg_stm32_reload(struct device *dev) { - volatile struct iwdg_stm32 *iwdg = AS_IWDG(IWDG_BASE); + IWDG_TypeDef *iwdg = IWDG_STM32_STRUCT(dev); - ARG_UNUSED(dev); - - iwdg->kr.bit.key = STM32_IWDG_KR_RELOAD; + LL_IWDG_ReloadCounter(iwdg); } static const struct wdt_driver_api iwdg_stm32_api = { @@ -74,43 +141,36 @@ static const struct wdt_driver_api iwdg_stm32_api = { .reload = iwdg_stm32_reload, }; -static inline int __iwdg_stm32_prescaler(int setting) -{ - int v; - int i = 0; - - /* prescaler range 4 - 256 */ - for (v = 4; v < 256; v *= 2, i++) { - if (v == setting) - return i; - } - return i; -} - static int iwdg_stm32_init(struct device *dev) { - volatile struct iwdg_stm32 *iwdg = AS_IWDG(IWDG_BASE); + IWDG_TypeDef *iwdg = IWDG_STM32_STRUCT(dev); + struct wdt_config config; - /* clock setup is not required, once the watchdog is enabled - * LSI oscillator will be forced on and fed to IWD after - * stabilization period + config.timeout = CONFIG_IWDG_STM32_TIMEOUT; + + LL_IWDG_Enable(iwdg); + + iwdg_stm32_set_config(dev, &config); + + /* + * The ST production value for the option bytes where WDG_SW bit is + * present is 0x00FF55AA, namely the Software watchdog mode is + * enabled by default. + * If the IWDG is started by either hardware option or software access, + * the LSI oscillator is forced ON and cannot be disabled. + * + * t_IWDG(ms) = t_LSI(ms) x 4 x 2^(IWDG_PR[2:0]) x (IWDG_RLR[11:0] + 1) */ - /* unlock access to configuration registers */ - iwdg->kr.bit.key = STM32_IWDG_KR_UNLOCK; - - iwdg->pr.bit.pr = - __iwdg_stm32_prescaler(CONFIG_IWDG_STM32_PRESCALER); - iwdg->rlr.bit.rl = CONFIG_IWDG_STM32_RELOAD_COUNTER; - -#ifdef CONFIG_IWDG_STM32_START_AT_BOOT - iwdg_stm32_enable(dev); -#endif - return 0; } -DEVICE_AND_API_INIT(iwdg_stm32, CONFIG_IWDG_STM32_DEVICE_NAME, iwdg_stm32_init, - NULL, NULL, - PRE_KERNEL_1, CONFIG_KERNEL_INIT_PRIORITY_DEVICE, +static struct iwdg_stm32_data iwdg_stm32_dev_data = { + .Instance = IWDG +}; + +DEVICE_AND_API_INIT(iwdg_stm32, CONFIG_IWDG_STM32_DEVICE_NAME, + iwdg_stm32_init, &iwdg_stm32_dev_data, NULL, + POST_KERNEL, CONFIG_KERNEL_INIT_PRIORITY_DEVICE, &iwdg_stm32_api); + diff --git a/drivers/watchdog/iwdg_stm32.h b/drivers/watchdog/iwdg_stm32.h index 3c17ddfe7b6..b21d58b6ec7 100644 --- a/drivers/watchdog/iwdg_stm32.h +++ b/drivers/watchdog/iwdg_stm32.h @@ -1,5 +1,6 @@ /* * Copyright (c) 2016 Open-RnD Sp. z o.o. + * Copyright (c) 2017 RnDity Sp. z o.o. * * SPDX-License-Identifier: Apache-2.0 */ @@ -12,65 +13,23 @@ /** * @brief Driver for Independent Watchdog (IWDG) for STM32 MCUs * - * Based on reference manual: - * STM32F101xx, STM32F102xx, STM32F103xx, STM32F105xx and STM32F107xx - * advanced ARM(r)-based 32-bit MCUs + * The driver targets all STM32 SoCs. For details please refer to + * an appropriate reference manual and look for chapter called: * - * Chapter 19: Independent watchdog (IWDG) + * Independent watchdog (IWDG) * */ - -/* counter reload trigger */ -#define STM32_IWDG_KR_RELOAD 0xaaaa -/* magic value for unlocking write access to PR and RLR */ -#define STM32_IWDG_KR_UNLOCK 0x5555 -/* watchdog start */ -#define STM32_IWDG_KR_START 0xcccc - -/* 19.4.1 IWDG_KR */ -union __iwdg_kr { - u32_t val; - struct { - u16_t key; - u16_t rsvd; - } bit; +/* driver data */ +struct iwdg_stm32_data { + /* IWDG peripheral instance. */ + IWDG_TypeDef *Instance; }; -/* 19.4.2 IWDG_PR */ -union __iwdg_pr { - u32_t val; - struct { - u32_t pr :3 __packed; - u32_t rsvd__3_31 :29 __packed; - } bit; -}; +#define IWDG_STM32_DATA(dev) \ + ((struct iwdg_stm32_data * const)(dev)->driver_data) -/* 19.4.3 IWDG_RLR */ -union __iwdg_rlr { - u32_t val; - struct { - u32_t rl :12 __packed; - u32_t rsvd__12_31 :20 __packed; - } bit; -}; - -/* 19.4.4 IWDG_SR */ -union __iwdg_sr { - u32_t val; - struct { - u32_t pvu :1 __packed; - u32_t rvu :1 __packed; - u32_t rsvd__2_31 :30 __packed; - } bit; -}; - -/* 19.4.5 IWDG register map */ -struct iwdg_stm32 { - union __iwdg_kr kr; - union __iwdg_pr pr; - union __iwdg_rlr rlr; - union __iwdg_sr sr; -}; +#define IWDG_STM32_STRUCT(dev) \ + ((IWDG_TypeDef *)(IWDG_STM32_DATA(dev))->Instance) #endif /* _STM32_IWDG_H_ */