1 /* 2 * Copyright 2010-2011 Picochip Ltd., Jamie Iles 3 * http://www.picochip.com 4 * 5 * This program is free software; you can redistribute it and/or 6 * modify it under the terms of the GNU General Public License 7 * as published by the Free Software Foundation; either version 8 * 2 of the License, or (at your option) any later version. 9 * 10 * This file implements a driver for the Synopsys DesignWare watchdog device 11 * in the many subsystems. The watchdog has 16 different timeout periods 12 * and these are a function of the input clock frequency. 13 * 14 * The DesignWare watchdog cannot be stopped once it has been started so we 15 * do not implement a stop function. The watchdog core will continue to send 16 * heartbeat requests after the watchdog device has been closed. 17 */ 18 19 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 20 21 #include <linux/bitops.h> 22 #include <linux/clk.h> 23 #include <linux/delay.h> 24 #include <linux/err.h> 25 #include <linux/io.h> 26 #include <linux/kernel.h> 27 #include <linux/module.h> 28 #include <linux/moduleparam.h> 29 #include <linux/of.h> 30 #include <linux/pm.h> 31 #include <linux/platform_device.h> 32 #include <linux/reset.h> 33 #include <linux/watchdog.h> 34 35 #define WDOG_CONTROL_REG_OFFSET 0x00 36 #define WDOG_CONTROL_REG_WDT_EN_MASK 0x01 37 #define WDOG_TIMEOUT_RANGE_REG_OFFSET 0x04 38 #define WDOG_TIMEOUT_RANGE_TOPINIT_SHIFT 4 39 #define WDOG_CURRENT_COUNT_REG_OFFSET 0x08 40 #define WDOG_COUNTER_RESTART_REG_OFFSET 0x0c 41 #define WDOG_COUNTER_RESTART_KICK_VALUE 0x76 42 43 /* The maximum TOP (timeout period) value that can be set in the watchdog. */ 44 #define DW_WDT_MAX_TOP 15 45 46 #define DW_WDT_DEFAULT_SECONDS 30 47 48 static bool nowayout = WATCHDOG_NOWAYOUT; 49 module_param(nowayout, bool, 0); 50 MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started " 51 "(default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); 52 53 struct dw_wdt { 54 void __iomem *regs; 55 struct clk *clk; 56 unsigned long rate; 57 struct watchdog_device wdd; 58 struct reset_control *rst; 59 }; 60 61 #define to_dw_wdt(wdd) container_of(wdd, struct dw_wdt, wdd) 62 63 static inline int dw_wdt_is_enabled(struct dw_wdt *dw_wdt) 64 { 65 return readl(dw_wdt->regs + WDOG_CONTROL_REG_OFFSET) & 66 WDOG_CONTROL_REG_WDT_EN_MASK; 67 } 68 69 static inline int dw_wdt_top_in_seconds(struct dw_wdt *dw_wdt, unsigned top) 70 { 71 /* 72 * There are 16 possible timeout values in 0..15 where the number of 73 * cycles is 2 ^ (16 + i) and the watchdog counts down. 74 */ 75 return (1U << (16 + top)) / dw_wdt->rate; 76 } 77 78 static int dw_wdt_get_top(struct dw_wdt *dw_wdt) 79 { 80 int top = readl(dw_wdt->regs + WDOG_TIMEOUT_RANGE_REG_OFFSET) & 0xF; 81 82 return dw_wdt_top_in_seconds(dw_wdt, top); 83 } 84 85 static int dw_wdt_ping(struct watchdog_device *wdd) 86 { 87 struct dw_wdt *dw_wdt = to_dw_wdt(wdd); 88 89 writel(WDOG_COUNTER_RESTART_KICK_VALUE, dw_wdt->regs + 90 WDOG_COUNTER_RESTART_REG_OFFSET); 91 92 return 0; 93 } 94 95 static int dw_wdt_set_timeout(struct watchdog_device *wdd, unsigned int top_s) 96 { 97 struct dw_wdt *dw_wdt = to_dw_wdt(wdd); 98 int i, top_val = DW_WDT_MAX_TOP; 99 100 /* 101 * Iterate over the timeout values until we find the closest match. We 102 * always look for >=. 103 */ 104 for (i = 0; i <= DW_WDT_MAX_TOP; ++i) 105 if (dw_wdt_top_in_seconds(dw_wdt, i) >= top_s) { 106 top_val = i; 107 break; 108 } 109 110 /* 111 * Set the new value in the watchdog. Some versions of dw_wdt 112 * have have TOPINIT in the TIMEOUT_RANGE register (as per 113 * CP_WDT_DUAL_TOP in WDT_COMP_PARAMS_1). On those we 114 * effectively get a pat of the watchdog right here. 115 */ 116 writel(top_val | top_val << WDOG_TIMEOUT_RANGE_TOPINIT_SHIFT, 117 dw_wdt->regs + WDOG_TIMEOUT_RANGE_REG_OFFSET); 118 119 wdd->timeout = dw_wdt_top_in_seconds(dw_wdt, top_val); 120 121 return 0; 122 } 123 124 static int dw_wdt_start(struct watchdog_device *wdd) 125 { 126 struct dw_wdt *dw_wdt = to_dw_wdt(wdd); 127 128 dw_wdt_set_timeout(wdd, wdd->timeout); 129 130 writel(WDOG_CONTROL_REG_WDT_EN_MASK, 131 dw_wdt->regs + WDOG_CONTROL_REG_OFFSET); 132 133 return 0; 134 } 135 136 static int dw_wdt_stop(struct watchdog_device *wdd) 137 { 138 struct dw_wdt *dw_wdt = to_dw_wdt(wdd); 139 140 if (!dw_wdt->rst) { 141 set_bit(WDOG_HW_RUNNING, &wdd->status); 142 return 0; 143 } 144 145 reset_control_assert(dw_wdt->rst); 146 reset_control_deassert(dw_wdt->rst); 147 148 return 0; 149 } 150 151 static int dw_wdt_restart(struct watchdog_device *wdd, 152 unsigned long action, void *data) 153 { 154 struct dw_wdt *dw_wdt = to_dw_wdt(wdd); 155 u32 val; 156 157 writel(0, dw_wdt->regs + WDOG_TIMEOUT_RANGE_REG_OFFSET); 158 val = readl(dw_wdt->regs + WDOG_CONTROL_REG_OFFSET); 159 if (val & WDOG_CONTROL_REG_WDT_EN_MASK) 160 writel(WDOG_COUNTER_RESTART_KICK_VALUE, 161 dw_wdt->regs + WDOG_COUNTER_RESTART_REG_OFFSET); 162 else 163 writel(WDOG_CONTROL_REG_WDT_EN_MASK, 164 dw_wdt->regs + WDOG_CONTROL_REG_OFFSET); 165 166 /* wait for reset to assert... */ 167 mdelay(500); 168 169 return 0; 170 } 171 172 static unsigned int dw_wdt_get_timeleft(struct watchdog_device *wdd) 173 { 174 struct dw_wdt *dw_wdt = to_dw_wdt(wdd); 175 176 return readl(dw_wdt->regs + WDOG_CURRENT_COUNT_REG_OFFSET) / 177 dw_wdt->rate; 178 } 179 180 static const struct watchdog_info dw_wdt_ident = { 181 .options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT | 182 WDIOF_MAGICCLOSE, 183 .identity = "Synopsys DesignWare Watchdog", 184 }; 185 186 static const struct watchdog_ops dw_wdt_ops = { 187 .owner = THIS_MODULE, 188 .start = dw_wdt_start, 189 .stop = dw_wdt_stop, 190 .ping = dw_wdt_ping, 191 .set_timeout = dw_wdt_set_timeout, 192 .get_timeleft = dw_wdt_get_timeleft, 193 .restart = dw_wdt_restart, 194 }; 195 196 #ifdef CONFIG_PM_SLEEP 197 static int dw_wdt_suspend(struct device *dev) 198 { 199 struct dw_wdt *dw_wdt = dev_get_drvdata(dev); 200 201 clk_disable_unprepare(dw_wdt->clk); 202 203 return 0; 204 } 205 206 static int dw_wdt_resume(struct device *dev) 207 { 208 struct dw_wdt *dw_wdt = dev_get_drvdata(dev); 209 int err = clk_prepare_enable(dw_wdt->clk); 210 211 if (err) 212 return err; 213 214 dw_wdt_ping(&dw_wdt->wdd); 215 216 return 0; 217 } 218 #endif /* CONFIG_PM_SLEEP */ 219 220 static SIMPLE_DEV_PM_OPS(dw_wdt_pm_ops, dw_wdt_suspend, dw_wdt_resume); 221 222 static int dw_wdt_drv_probe(struct platform_device *pdev) 223 { 224 struct device *dev = &pdev->dev; 225 struct watchdog_device *wdd; 226 struct dw_wdt *dw_wdt; 227 struct resource *mem; 228 int ret; 229 230 dw_wdt = devm_kzalloc(dev, sizeof(*dw_wdt), GFP_KERNEL); 231 if (!dw_wdt) 232 return -ENOMEM; 233 234 mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); 235 dw_wdt->regs = devm_ioremap_resource(dev, mem); 236 if (IS_ERR(dw_wdt->regs)) 237 return PTR_ERR(dw_wdt->regs); 238 239 dw_wdt->clk = devm_clk_get(dev, NULL); 240 if (IS_ERR(dw_wdt->clk)) 241 return PTR_ERR(dw_wdt->clk); 242 243 ret = clk_prepare_enable(dw_wdt->clk); 244 if (ret) 245 return ret; 246 247 dw_wdt->rate = clk_get_rate(dw_wdt->clk); 248 if (dw_wdt->rate == 0) { 249 ret = -EINVAL; 250 goto out_disable_clk; 251 } 252 253 dw_wdt->rst = devm_reset_control_get_optional_shared(&pdev->dev, NULL); 254 if (IS_ERR(dw_wdt->rst)) { 255 ret = PTR_ERR(dw_wdt->rst); 256 goto out_disable_clk; 257 } 258 259 reset_control_deassert(dw_wdt->rst); 260 261 wdd = &dw_wdt->wdd; 262 wdd->info = &dw_wdt_ident; 263 wdd->ops = &dw_wdt_ops; 264 wdd->min_timeout = 1; 265 wdd->max_hw_heartbeat_ms = 266 dw_wdt_top_in_seconds(dw_wdt, DW_WDT_MAX_TOP) * 1000; 267 wdd->parent = dev; 268 269 watchdog_set_drvdata(wdd, dw_wdt); 270 watchdog_set_nowayout(wdd, nowayout); 271 watchdog_init_timeout(wdd, 0, dev); 272 273 /* 274 * If the watchdog is already running, use its already configured 275 * timeout. Otherwise use the default or the value provided through 276 * devicetree. 277 */ 278 if (dw_wdt_is_enabled(dw_wdt)) { 279 wdd->timeout = dw_wdt_get_top(dw_wdt); 280 set_bit(WDOG_HW_RUNNING, &wdd->status); 281 } else { 282 wdd->timeout = DW_WDT_DEFAULT_SECONDS; 283 watchdog_init_timeout(wdd, 0, dev); 284 } 285 286 platform_set_drvdata(pdev, dw_wdt); 287 288 watchdog_set_restart_priority(wdd, 128); 289 290 ret = watchdog_register_device(wdd); 291 if (ret) 292 goto out_disable_clk; 293 294 return 0; 295 296 out_disable_clk: 297 clk_disable_unprepare(dw_wdt->clk); 298 return ret; 299 } 300 301 static int dw_wdt_drv_remove(struct platform_device *pdev) 302 { 303 struct dw_wdt *dw_wdt = platform_get_drvdata(pdev); 304 305 watchdog_unregister_device(&dw_wdt->wdd); 306 reset_control_assert(dw_wdt->rst); 307 clk_disable_unprepare(dw_wdt->clk); 308 309 return 0; 310 } 311 312 #ifdef CONFIG_OF 313 static const struct of_device_id dw_wdt_of_match[] = { 314 { .compatible = "snps,dw-wdt", }, 315 { /* sentinel */ } 316 }; 317 MODULE_DEVICE_TABLE(of, dw_wdt_of_match); 318 #endif 319 320 static struct platform_driver dw_wdt_driver = { 321 .probe = dw_wdt_drv_probe, 322 .remove = dw_wdt_drv_remove, 323 .driver = { 324 .name = "dw_wdt", 325 .of_match_table = of_match_ptr(dw_wdt_of_match), 326 .pm = &dw_wdt_pm_ops, 327 }, 328 }; 329 330 module_platform_driver(dw_wdt_driver); 331 332 MODULE_AUTHOR("Jamie Iles"); 333 MODULE_DESCRIPTION("Synopsys DesignWare Watchdog Driver"); 334 MODULE_LICENSE("GPL"); 335