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