1 /* 2 * sunxi Watchdog Driver 3 * 4 * Copyright (c) 2013 Carlo Caione 5 * 2012 Henrik Nordstrom 6 * 7 * This program is free software; you can redistribute it and/or 8 * modify it under the terms of the GNU General Public License 9 * as published by the Free Software Foundation; either version 10 * 2 of the License, or (at your option) any later version. 11 * 12 * Based on xen_wdt.c 13 * (c) Copyright 2010 Novell, Inc. 14 */ 15 16 #include <linux/clk.h> 17 #include <linux/delay.h> 18 #include <linux/err.h> 19 #include <linux/init.h> 20 #include <linux/io.h> 21 #include <linux/kernel.h> 22 #include <linux/module.h> 23 #include <linux/moduleparam.h> 24 #include <linux/notifier.h> 25 #include <linux/of.h> 26 #include <linux/platform_device.h> 27 #include <linux/reboot.h> 28 #include <linux/types.h> 29 #include <linux/watchdog.h> 30 31 #define WDT_MAX_TIMEOUT 16 32 #define WDT_MIN_TIMEOUT 1 33 #define WDT_MODE_TIMEOUT(n) ((n) << 3) 34 #define WDT_TIMEOUT_MASK WDT_MODE_TIMEOUT(0x0F) 35 36 #define WDT_CTRL 0x00 37 #define WDT_CTRL_RELOAD ((1 << 0) | (0x0a57 << 1)) 38 39 #define WDT_MODE 0x04 40 #define WDT_MODE_EN (1 << 0) 41 #define WDT_MODE_RST_EN (1 << 1) 42 43 #define DRV_NAME "sunxi-wdt" 44 #define DRV_VERSION "1.0" 45 46 static bool nowayout = WATCHDOG_NOWAYOUT; 47 static unsigned int timeout = WDT_MAX_TIMEOUT; 48 49 struct sunxi_wdt_dev { 50 struct watchdog_device wdt_dev; 51 void __iomem *wdt_base; 52 struct notifier_block restart_handler; 53 }; 54 55 /* 56 * wdt_timeout_map maps the watchdog timer interval value in seconds to 57 * the value of the register WDT_MODE bit 3:6 58 * 59 * [timeout seconds] = register value 60 * 61 */ 62 63 static const int wdt_timeout_map[] = { 64 [1] = 0x1, /* 1s */ 65 [2] = 0x2, /* 2s */ 66 [3] = 0x3, /* 3s */ 67 [4] = 0x4, /* 4s */ 68 [5] = 0x5, /* 5s */ 69 [6] = 0x6, /* 6s */ 70 [8] = 0x7, /* 8s */ 71 [10] = 0x8, /* 10s */ 72 [12] = 0x9, /* 12s */ 73 [14] = 0xA, /* 14s */ 74 [16] = 0xB, /* 16s */ 75 }; 76 77 78 static int sunxi_restart_handle(struct notifier_block *this, unsigned long mode, 79 void *cmd) 80 { 81 struct sunxi_wdt_dev *sunxi_wdt = container_of(this, 82 struct sunxi_wdt_dev, 83 restart_handler); 84 void __iomem *wdt_base = sunxi_wdt->wdt_base; 85 86 /* Enable timer and set reset bit in the watchdog */ 87 writel(WDT_MODE_EN | WDT_MODE_RST_EN, wdt_base + WDT_MODE); 88 89 /* 90 * Restart the watchdog. The default (and lowest) interval 91 * value for the watchdog is 0.5s. 92 */ 93 writel(WDT_CTRL_RELOAD, wdt_base + WDT_CTRL); 94 95 while (1) { 96 mdelay(5); 97 writel(WDT_MODE_EN | WDT_MODE_RST_EN, wdt_base + WDT_MODE); 98 } 99 return NOTIFY_DONE; 100 } 101 102 static int sunxi_wdt_ping(struct watchdog_device *wdt_dev) 103 { 104 struct sunxi_wdt_dev *sunxi_wdt = watchdog_get_drvdata(wdt_dev); 105 void __iomem *wdt_base = sunxi_wdt->wdt_base; 106 107 iowrite32(WDT_CTRL_RELOAD, wdt_base + WDT_CTRL); 108 109 return 0; 110 } 111 112 static int sunxi_wdt_set_timeout(struct watchdog_device *wdt_dev, 113 unsigned int timeout) 114 { 115 struct sunxi_wdt_dev *sunxi_wdt = watchdog_get_drvdata(wdt_dev); 116 void __iomem *wdt_base = sunxi_wdt->wdt_base; 117 u32 reg; 118 119 if (wdt_timeout_map[timeout] == 0) 120 timeout++; 121 122 sunxi_wdt->wdt_dev.timeout = timeout; 123 124 reg = ioread32(wdt_base + WDT_MODE); 125 reg &= ~WDT_TIMEOUT_MASK; 126 reg |= WDT_MODE_TIMEOUT(wdt_timeout_map[timeout]); 127 iowrite32(reg, wdt_base + WDT_MODE); 128 129 sunxi_wdt_ping(wdt_dev); 130 131 return 0; 132 } 133 134 static int sunxi_wdt_stop(struct watchdog_device *wdt_dev) 135 { 136 struct sunxi_wdt_dev *sunxi_wdt = watchdog_get_drvdata(wdt_dev); 137 void __iomem *wdt_base = sunxi_wdt->wdt_base; 138 139 iowrite32(0, wdt_base + WDT_MODE); 140 141 return 0; 142 } 143 144 static int sunxi_wdt_start(struct watchdog_device *wdt_dev) 145 { 146 u32 reg; 147 struct sunxi_wdt_dev *sunxi_wdt = watchdog_get_drvdata(wdt_dev); 148 void __iomem *wdt_base = sunxi_wdt->wdt_base; 149 int ret; 150 151 ret = sunxi_wdt_set_timeout(&sunxi_wdt->wdt_dev, 152 sunxi_wdt->wdt_dev.timeout); 153 if (ret < 0) 154 return ret; 155 156 reg = ioread32(wdt_base + WDT_MODE); 157 reg |= (WDT_MODE_RST_EN | WDT_MODE_EN); 158 iowrite32(reg, wdt_base + WDT_MODE); 159 160 return 0; 161 } 162 163 static const struct watchdog_info sunxi_wdt_info = { 164 .identity = DRV_NAME, 165 .options = WDIOF_SETTIMEOUT | 166 WDIOF_KEEPALIVEPING | 167 WDIOF_MAGICCLOSE, 168 }; 169 170 static const struct watchdog_ops sunxi_wdt_ops = { 171 .owner = THIS_MODULE, 172 .start = sunxi_wdt_start, 173 .stop = sunxi_wdt_stop, 174 .ping = sunxi_wdt_ping, 175 .set_timeout = sunxi_wdt_set_timeout, 176 }; 177 178 static int sunxi_wdt_probe(struct platform_device *pdev) 179 { 180 struct sunxi_wdt_dev *sunxi_wdt; 181 struct resource *res; 182 int err; 183 184 sunxi_wdt = devm_kzalloc(&pdev->dev, sizeof(*sunxi_wdt), GFP_KERNEL); 185 if (!sunxi_wdt) 186 return -EINVAL; 187 188 platform_set_drvdata(pdev, sunxi_wdt); 189 190 res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 191 sunxi_wdt->wdt_base = devm_ioremap_resource(&pdev->dev, res); 192 if (IS_ERR(sunxi_wdt->wdt_base)) 193 return PTR_ERR(sunxi_wdt->wdt_base); 194 195 sunxi_wdt->wdt_dev.info = &sunxi_wdt_info; 196 sunxi_wdt->wdt_dev.ops = &sunxi_wdt_ops; 197 sunxi_wdt->wdt_dev.timeout = WDT_MAX_TIMEOUT; 198 sunxi_wdt->wdt_dev.max_timeout = WDT_MAX_TIMEOUT; 199 sunxi_wdt->wdt_dev.min_timeout = WDT_MIN_TIMEOUT; 200 sunxi_wdt->wdt_dev.parent = &pdev->dev; 201 202 watchdog_init_timeout(&sunxi_wdt->wdt_dev, timeout, &pdev->dev); 203 watchdog_set_nowayout(&sunxi_wdt->wdt_dev, nowayout); 204 205 watchdog_set_drvdata(&sunxi_wdt->wdt_dev, sunxi_wdt); 206 207 sunxi_wdt_stop(&sunxi_wdt->wdt_dev); 208 209 err = watchdog_register_device(&sunxi_wdt->wdt_dev); 210 if (unlikely(err)) 211 return err; 212 213 sunxi_wdt->restart_handler.notifier_call = sunxi_restart_handle; 214 sunxi_wdt->restart_handler.priority = 128; 215 err = register_restart_handler(&sunxi_wdt->restart_handler); 216 if (err) 217 dev_err(&pdev->dev, 218 "cannot register restart handler (err=%d)\n", err); 219 220 dev_info(&pdev->dev, "Watchdog enabled (timeout=%d sec, nowayout=%d)", 221 sunxi_wdt->wdt_dev.timeout, nowayout); 222 223 return 0; 224 } 225 226 static int sunxi_wdt_remove(struct platform_device *pdev) 227 { 228 struct sunxi_wdt_dev *sunxi_wdt = platform_get_drvdata(pdev); 229 230 unregister_restart_handler(&sunxi_wdt->restart_handler); 231 232 watchdog_unregister_device(&sunxi_wdt->wdt_dev); 233 watchdog_set_drvdata(&sunxi_wdt->wdt_dev, NULL); 234 235 return 0; 236 } 237 238 static void sunxi_wdt_shutdown(struct platform_device *pdev) 239 { 240 struct sunxi_wdt_dev *sunxi_wdt = platform_get_drvdata(pdev); 241 242 sunxi_wdt_stop(&sunxi_wdt->wdt_dev); 243 } 244 245 static const struct of_device_id sunxi_wdt_dt_ids[] = { 246 { .compatible = "allwinner,sun4i-a10-wdt" }, 247 { /* sentinel */ } 248 }; 249 MODULE_DEVICE_TABLE(of, sunxi_wdt_dt_ids); 250 251 static struct platform_driver sunxi_wdt_driver = { 252 .probe = sunxi_wdt_probe, 253 .remove = sunxi_wdt_remove, 254 .shutdown = sunxi_wdt_shutdown, 255 .driver = { 256 .owner = THIS_MODULE, 257 .name = DRV_NAME, 258 .of_match_table = sunxi_wdt_dt_ids, 259 }, 260 }; 261 262 module_platform_driver(sunxi_wdt_driver); 263 264 module_param(timeout, uint, 0); 265 MODULE_PARM_DESC(timeout, "Watchdog heartbeat in seconds"); 266 267 module_param(nowayout, bool, 0); 268 MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started " 269 "(default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); 270 271 MODULE_LICENSE("GPL"); 272 MODULE_AUTHOR("Carlo Caione <carlo.caione@gmail.com>"); 273 MODULE_AUTHOR("Henrik Nordstrom <henrik@henriknordstrom.net>"); 274 MODULE_DESCRIPTION("sunxi WatchDog Timer Driver"); 275 MODULE_VERSION(DRV_VERSION); 276