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