Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

watchdog: Add STM32 IWDG driver

This patch adds IWDG (Independent WatchDoG) support for STM32 platform.

Signed-off-by: Yannick FERTRE <yannick.fertre@st.com>
Reviewed-by: Guenter Roeck <linux@roeck-us.net>
Signed-off-by: Guenter Roeck <linux@roeck-us.net>
Signed-off-by: Wim Van Sebroeck <wim@iguana.be>

authored by

Yannick Fertre and committed by
Wim Van Sebroeck
4332d113 5df11427

+266
+12
drivers/watchdog/Kconfig
··· 752 752 To compile this driver as a module, choose M here: the 753 753 module will be called zx2967_wdt. 754 754 755 + config STM32_WATCHDOG 756 + tristate "STM32 Independent WatchDoG (IWDG) support" 757 + depends on ARCH_STM32 758 + select WATCHDOG_CORE 759 + default y 760 + help 761 + Say Y here to include support for the watchdog timer 762 + in stm32 SoCs. 763 + 764 + To compile this driver as a module, choose M here: the 765 + module will be called stm32_iwdg. 766 + 755 767 # AVR32 Architecture 756 768 757 769 config AT32AP700X_WDT
+1
drivers/watchdog/Makefile
··· 85 85 obj-$(CONFIG_RENESAS_RZAWDT) += rza_wdt.o 86 86 obj-$(CONFIG_ASPEED_WATCHDOG) += aspeed_wdt.o 87 87 obj-$(CONFIG_ZX2967_WATCHDOG) += zx2967_wdt.o 88 + obj-$(CONFIG_STM32_WATCHDOG) += stm32_iwdg.o 88 89 89 90 # AVR32 Architecture 90 91 obj-$(CONFIG_AT32AP700X_WDT) += at32ap700x_wdt.o
+253
drivers/watchdog/stm32_iwdg.c
··· 1 + /* 2 + * Driver for STM32 Independent Watchdog 3 + * 4 + * Copyright (C) Yannick Fertre 2017 5 + * Author: Yannick Fertre <yannick.fertre@st.com> 6 + * 7 + * This driver is based on tegra_wdt.c 8 + * 9 + * License terms: GNU General Public License (GPL), version 2 10 + */ 11 + 12 + #include <linux/clk.h> 13 + #include <linux/delay.h> 14 + #include <linux/kernel.h> 15 + #include <linux/module.h> 16 + #include <linux/interrupt.h> 17 + #include <linux/io.h> 18 + #include <linux/iopoll.h> 19 + #include <linux/of.h> 20 + #include <linux/platform_device.h> 21 + #include <linux/watchdog.h> 22 + 23 + /* IWDG registers */ 24 + #define IWDG_KR 0x00 /* Key register */ 25 + #define IWDG_PR 0x04 /* Prescaler Register */ 26 + #define IWDG_RLR 0x08 /* ReLoad Register */ 27 + #define IWDG_SR 0x0C /* Status Register */ 28 + #define IWDG_WINR 0x10 /* Windows Register */ 29 + 30 + /* IWDG_KR register bit mask */ 31 + #define KR_KEY_RELOAD 0xAAAA /* reload counter enable */ 32 + #define KR_KEY_ENABLE 0xCCCC /* peripheral enable */ 33 + #define KR_KEY_EWA 0x5555 /* write access enable */ 34 + #define KR_KEY_DWA 0x0000 /* write access disable */ 35 + 36 + /* IWDG_PR register bit values */ 37 + #define PR_4 0x00 /* prescaler set to 4 */ 38 + #define PR_8 0x01 /* prescaler set to 8 */ 39 + #define PR_16 0x02 /* prescaler set to 16 */ 40 + #define PR_32 0x03 /* prescaler set to 32 */ 41 + #define PR_64 0x04 /* prescaler set to 64 */ 42 + #define PR_128 0x05 /* prescaler set to 128 */ 43 + #define PR_256 0x06 /* prescaler set to 256 */ 44 + 45 + /* IWDG_RLR register values */ 46 + #define RLR_MIN 0x07C /* min value supported by reload register */ 47 + #define RLR_MAX 0xFFF /* max value supported by reload register */ 48 + 49 + /* IWDG_SR register bit mask */ 50 + #define FLAG_PVU BIT(0) /* Watchdog prescaler value update */ 51 + #define FLAG_RVU BIT(1) /* Watchdog counter reload value update */ 52 + 53 + /* set timeout to 100000 us */ 54 + #define TIMEOUT_US 100000 55 + #define SLEEP_US 1000 56 + 57 + struct stm32_iwdg { 58 + struct watchdog_device wdd; 59 + void __iomem *regs; 60 + struct clk *clk; 61 + unsigned int rate; 62 + }; 63 + 64 + static inline u32 reg_read(void __iomem *base, u32 reg) 65 + { 66 + return readl_relaxed(base + reg); 67 + } 68 + 69 + static inline void reg_write(void __iomem *base, u32 reg, u32 val) 70 + { 71 + writel_relaxed(val, base + reg); 72 + } 73 + 74 + static int stm32_iwdg_start(struct watchdog_device *wdd) 75 + { 76 + struct stm32_iwdg *wdt = watchdog_get_drvdata(wdd); 77 + u32 val = FLAG_PVU | FLAG_RVU; 78 + u32 reload; 79 + int ret; 80 + 81 + dev_dbg(wdd->parent, "%s\n", __func__); 82 + 83 + /* prescaler fixed to 256 */ 84 + reload = clamp_t(unsigned int, ((wdd->timeout * wdt->rate) / 256) - 1, 85 + RLR_MIN, RLR_MAX); 86 + 87 + /* enable write access */ 88 + reg_write(wdt->regs, IWDG_KR, KR_KEY_EWA); 89 + 90 + /* set prescaler & reload registers */ 91 + reg_write(wdt->regs, IWDG_PR, PR_256); /* prescaler fix to 256 */ 92 + reg_write(wdt->regs, IWDG_RLR, reload); 93 + reg_write(wdt->regs, IWDG_KR, KR_KEY_ENABLE); 94 + 95 + /* wait for the registers to be updated (max 100ms) */ 96 + ret = readl_relaxed_poll_timeout(wdt->regs + IWDG_SR, val, 97 + !(val & (FLAG_PVU | FLAG_RVU)), 98 + SLEEP_US, TIMEOUT_US); 99 + if (ret) { 100 + dev_err(wdd->parent, 101 + "Fail to set prescaler or reload registers\n"); 102 + return ret; 103 + } 104 + 105 + /* reload watchdog */ 106 + reg_write(wdt->regs, IWDG_KR, KR_KEY_RELOAD); 107 + 108 + return 0; 109 + } 110 + 111 + static int stm32_iwdg_ping(struct watchdog_device *wdd) 112 + { 113 + struct stm32_iwdg *wdt = watchdog_get_drvdata(wdd); 114 + 115 + dev_dbg(wdd->parent, "%s\n", __func__); 116 + 117 + /* reload watchdog */ 118 + reg_write(wdt->regs, IWDG_KR, KR_KEY_RELOAD); 119 + 120 + return 0; 121 + } 122 + 123 + static int stm32_iwdg_set_timeout(struct watchdog_device *wdd, 124 + unsigned int timeout) 125 + { 126 + dev_dbg(wdd->parent, "%s timeout: %d sec\n", __func__, timeout); 127 + 128 + wdd->timeout = timeout; 129 + 130 + if (watchdog_active(wdd)) 131 + return stm32_iwdg_start(wdd); 132 + 133 + return 0; 134 + } 135 + 136 + static const struct watchdog_info stm32_iwdg_info = { 137 + .options = WDIOF_SETTIMEOUT | 138 + WDIOF_MAGICCLOSE | 139 + WDIOF_KEEPALIVEPING, 140 + .identity = "STM32 Independent Watchdog", 141 + }; 142 + 143 + static struct watchdog_ops stm32_iwdg_ops = { 144 + .owner = THIS_MODULE, 145 + .start = stm32_iwdg_start, 146 + .ping = stm32_iwdg_ping, 147 + .set_timeout = stm32_iwdg_set_timeout, 148 + }; 149 + 150 + static int stm32_iwdg_probe(struct platform_device *pdev) 151 + { 152 + struct watchdog_device *wdd; 153 + struct stm32_iwdg *wdt; 154 + struct resource *res; 155 + void __iomem *regs; 156 + struct clk *clk; 157 + int ret; 158 + 159 + /* This is the timer base. */ 160 + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 161 + regs = devm_ioremap_resource(&pdev->dev, res); 162 + if (IS_ERR(regs)) { 163 + dev_err(&pdev->dev, "Could not get resource\n"); 164 + return PTR_ERR(regs); 165 + } 166 + 167 + clk = devm_clk_get(&pdev->dev, NULL); 168 + if (IS_ERR(clk)) { 169 + dev_err(&pdev->dev, "Unable to get clock\n"); 170 + return PTR_ERR(clk); 171 + } 172 + 173 + ret = clk_prepare_enable(clk); 174 + if (ret) { 175 + dev_err(&pdev->dev, "Unable to prepare clock %p\n", clk); 176 + return ret; 177 + } 178 + 179 + /* 180 + * Allocate our watchdog driver data, which has the 181 + * struct watchdog_device nested within it. 182 + */ 183 + wdt = devm_kzalloc(&pdev->dev, sizeof(*wdt), GFP_KERNEL); 184 + if (!wdt) { 185 + ret = -ENOMEM; 186 + goto err; 187 + } 188 + 189 + /* Initialize struct stm32_iwdg. */ 190 + wdt->regs = regs; 191 + wdt->clk = clk; 192 + wdt->rate = clk_get_rate(clk); 193 + 194 + /* Initialize struct watchdog_device. */ 195 + wdd = &wdt->wdd; 196 + wdd->info = &stm32_iwdg_info; 197 + wdd->ops = &stm32_iwdg_ops; 198 + wdd->min_timeout = ((RLR_MIN + 1) * 256) / wdt->rate; 199 + wdd->max_hw_heartbeat_ms = ((RLR_MAX + 1) * 256 * 1000) / wdt->rate; 200 + wdd->parent = &pdev->dev; 201 + 202 + watchdog_set_drvdata(wdd, wdt); 203 + watchdog_set_nowayout(wdd, WATCHDOG_NOWAYOUT); 204 + 205 + ret = watchdog_init_timeout(wdd, 0, &pdev->dev); 206 + if (ret) 207 + dev_warn(&pdev->dev, 208 + "unable to set timeout value, using default\n"); 209 + 210 + ret = watchdog_register_device(wdd); 211 + if (ret) { 212 + dev_err(&pdev->dev, "failed to register watchdog device\n"); 213 + goto err; 214 + } 215 + 216 + platform_set_drvdata(pdev, wdt); 217 + 218 + return 0; 219 + err: 220 + clk_disable_unprepare(clk); 221 + 222 + return ret; 223 + } 224 + 225 + static int stm32_iwdg_remove(struct platform_device *pdev) 226 + { 227 + struct stm32_iwdg *wdt = platform_get_drvdata(pdev); 228 + 229 + watchdog_unregister_device(&wdt->wdd); 230 + clk_disable_unprepare(wdt->clk); 231 + 232 + return 0; 233 + } 234 + 235 + static const struct of_device_id stm32_iwdg_of_match[] = { 236 + { .compatible = "st,stm32-iwdg" }, 237 + { /* end node */ } 238 + }; 239 + MODULE_DEVICE_TABLE(of, stm32_iwdg_of_match); 240 + 241 + static struct platform_driver stm32_iwdg_driver = { 242 + .probe = stm32_iwdg_probe, 243 + .remove = stm32_iwdg_remove, 244 + .driver = { 245 + .name = "iwdg", 246 + .of_match_table = stm32_iwdg_of_match, 247 + }, 248 + }; 249 + module_platform_driver(stm32_iwdg_driver); 250 + 251 + MODULE_AUTHOR("Yannick Fertre <yannick.fertre@st.com>"); 252 + MODULE_DESCRIPTION("STMicroelectronics STM32 Independent Watchdog Driver"); 253 + MODULE_LICENSE("GPL v2");