Blame view
drivers/watchdog/at91rm9200_wdt.c
7.66 KB
2e62c4988 watchdog: add SPD... |
1 |
// SPDX-License-Identifier: GPL-2.0+ |
853807fb5 [WATCHDOG] at91_w... |
2 3 4 5 6 |
/* * Watchdog driver for Atmel AT91RM9200 (Thunder) * * Copyright (C) 2003 SAN People (Pty) Ltd * |
853807fb5 [WATCHDOG] at91_w... |
7 |
*/ |
27c766aaa watchdog: Use pr_... |
8 |
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt |
1977f0327 remove asm/bitops... |
9 |
#include <linux/bitops.h> |
9ab45f3fb watchdog: at91rm9... |
10 |
#include <linux/delay.h> |
853807fb5 [WATCHDOG] at91_w... |
11 12 13 |
#include <linux/errno.h> #include <linux/fs.h> #include <linux/init.h> |
02e0746ec [ARM] 5370/1: at9... |
14 |
#include <linux/io.h> |
853807fb5 [WATCHDOG] at91_w... |
15 |
#include <linux/kernel.h> |
8432f9e5e watchdog: at91rm9... |
16 17 |
#include <linux/mfd/syscon.h> #include <linux/mfd/syscon/atmel-st.h> |
853807fb5 [WATCHDOG] at91_w... |
18 19 20 |
#include <linux/miscdevice.h> #include <linux/module.h> #include <linux/moduleparam.h> |
dfc7bd9c3 [WATCHDOG] conver... |
21 |
#include <linux/platform_device.h> |
9ab45f3fb watchdog: at91rm9... |
22 |
#include <linux/reboot.h> |
8432f9e5e watchdog: at91rm9... |
23 |
#include <linux/regmap.h> |
853807fb5 [WATCHDOG] at91_w... |
24 25 |
#include <linux/types.h> #include <linux/watchdog.h> |
2760600da [WATCHDOG 06/57] ... |
26 |
#include <linux/uaccess.h> |
a6a1bcd37 watchdog: at91rm9... |
27 28 |
#include <linux/of.h> #include <linux/of_device.h> |
853807fb5 [WATCHDOG] at91_w... |
29 |
|
dfc7bd9c3 [WATCHDOG] conver... |
30 31 |
#define WDT_DEFAULT_TIME 5 /* seconds */ #define WDT_MAX_TIME 256 /* seconds */ |
853807fb5 [WATCHDOG] at91_w... |
32 33 |
static int wdt_time = WDT_DEFAULT_TIME; |
86a1e1896 watchdog: nowayou... |
34 |
static bool nowayout = WATCHDOG_NOWAYOUT; |
8432f9e5e watchdog: at91rm9... |
35 |
static struct regmap *regmap_st; |
853807fb5 [WATCHDOG] at91_w... |
36 37 |
module_param(wdt_time, int, 0); |
2760600da [WATCHDOG 06/57] ... |
38 39 |
MODULE_PARM_DESC(wdt_time, "Watchdog time in seconds. (default=" __MODULE_STRING(WDT_DEFAULT_TIME) ")"); |
853807fb5 [WATCHDOG] at91_w... |
40 |
|
dfc7bd9c3 [WATCHDOG] conver... |
41 |
#ifdef CONFIG_WATCHDOG_NOWAYOUT |
86a1e1896 watchdog: nowayou... |
42 |
module_param(nowayout, bool, 0); |
2760600da [WATCHDOG 06/57] ... |
43 44 45 |
MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); |
dfc7bd9c3 [WATCHDOG] conver... |
46 |
#endif |
853807fb5 [WATCHDOG] at91_w... |
47 48 49 50 51 |
static unsigned long at91wdt_busy; /* ......................................................................... */ |
9ab45f3fb watchdog: at91rm9... |
52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 |
static int at91rm9200_restart(struct notifier_block *this, unsigned long mode, void *cmd) { /* * Perform a hardware reset with the use of the Watchdog timer. */ regmap_write(regmap_st, AT91_ST_WDMR, AT91_ST_RSTEN | AT91_ST_EXTEN | 1); regmap_write(regmap_st, AT91_ST_CR, AT91_ST_WDRST); mdelay(2000); pr_emerg("Unable to restart system "); return NOTIFY_DONE; } static struct notifier_block at91rm9200_restart_nb = { .notifier_call = at91rm9200_restart, .priority = 192, }; |
853807fb5 [WATCHDOG] at91_w... |
73 74 75 |
/* * Disable the watchdog. */ |
2760600da [WATCHDOG 06/57] ... |
76 |
static inline void at91_wdt_stop(void) |
853807fb5 [WATCHDOG] at91_w... |
77 |
{ |
8432f9e5e watchdog: at91rm9... |
78 |
regmap_write(regmap_st, AT91_ST_WDMR, AT91_ST_EXTEN); |
853807fb5 [WATCHDOG] at91_w... |
79 80 81 82 83 |
} /* * Enable and reset the watchdog. */ |
2760600da [WATCHDOG 06/57] ... |
84 |
static inline void at91_wdt_start(void) |
853807fb5 [WATCHDOG] at91_w... |
85 |
{ |
8432f9e5e watchdog: at91rm9... |
86 |
regmap_write(regmap_st, AT91_ST_WDMR, AT91_ST_EXTEN | AT91_ST_RSTEN | |
2760600da [WATCHDOG 06/57] ... |
87 |
(((65536 * wdt_time) >> 8) & AT91_ST_WDV)); |
8432f9e5e watchdog: at91rm9... |
88 |
regmap_write(regmap_st, AT91_ST_CR, AT91_ST_WDRST); |
853807fb5 [WATCHDOG] at91_w... |
89 90 91 92 93 |
} /* * Reload the watchdog timer. (ie, pat the watchdog) */ |
2760600da [WATCHDOG 06/57] ... |
94 |
static inline void at91_wdt_reload(void) |
853807fb5 [WATCHDOG] at91_w... |
95 |
{ |
8432f9e5e watchdog: at91rm9... |
96 |
regmap_write(regmap_st, AT91_ST_CR, AT91_ST_WDRST); |
853807fb5 [WATCHDOG] at91_w... |
97 98 99 100 101 102 103 104 105 106 107 108 109 |
} /* ......................................................................... */ /* * Watchdog device is opened, and watchdog starts running. */ static int at91_wdt_open(struct inode *inode, struct file *file) { if (test_and_set_bit(0, &at91wdt_busy)) return -EBUSY; at91_wdt_start(); |
c5bf68fe0 *: convert stream... |
110 |
return stream_open(inode, file); |
853807fb5 [WATCHDOG] at91_w... |
111 112 113 114 115 116 117 118 119 |
} /* * Close the watchdog device. * If CONFIG_WATCHDOG_NOWAYOUT is NOT defined then the watchdog is also * disabled. */ static int at91_wdt_close(struct inode *inode, struct file *file) { |
2760600da [WATCHDOG 06/57] ... |
120 |
/* Disable the watchdog when file is closed */ |
853807fb5 [WATCHDOG] at91_w... |
121 |
if (!nowayout) |
2760600da [WATCHDOG 06/57] ... |
122 |
at91_wdt_stop(); |
853807fb5 [WATCHDOG] at91_w... |
123 124 125 126 127 128 129 130 131 132 133 |
clear_bit(0, &at91wdt_busy); return 0; } /* * Change the watchdog time interval. */ static int at91_wdt_settimeout(int new_time) { /* |
2af29b786 [ARM] 5390/1: AT9... |
134 |
* All counting occurs at SLOW_CLOCK / 128 = 256 Hz |
853807fb5 [WATCHDOG] at91_w... |
135 136 |
* * Since WDV is a 16-bit counter, the maximum period is |
2af29b786 [ARM] 5390/1: AT9... |
137 |
* 65536 / 256 = 256 seconds. |
853807fb5 [WATCHDOG] at91_w... |
138 139 140 |
*/ if ((new_time <= 0) || (new_time > WDT_MAX_TIME)) return -EINVAL; |
2760600da [WATCHDOG 06/57] ... |
141 142 |
/* Set new watchdog time. It will be used when at91_wdt_start() is called. */ |
853807fb5 [WATCHDOG] at91_w... |
143 144 145 |
wdt_time = new_time; return 0; } |
42747d712 [WATCHDOG] watchd... |
146 |
static const struct watchdog_info at91_wdt_info = { |
853807fb5 [WATCHDOG] at91_w... |
147 148 149 150 151 152 153 |
.identity = "at91 watchdog", .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING, }; /* * Handle commands from user-space. */ |
3c4fafd65 [WATCHDOG] fix wa... |
154 |
static long at91_wdt_ioctl(struct file *file, |
2760600da [WATCHDOG 06/57] ... |
155 |
unsigned int cmd, unsigned long arg) |
853807fb5 [WATCHDOG] at91_w... |
156 157 158 159 |
{ void __user *argp = (void __user *)arg; int __user *p = argp; int new_value; |
2760600da [WATCHDOG 06/57] ... |
160 |
switch (cmd) { |
2760600da [WATCHDOG 06/57] ... |
161 162 163 |
case WDIOC_GETSUPPORT: return copy_to_user(argp, &at91_wdt_info, sizeof(at91_wdt_info)) ? -EFAULT : 0; |
2760600da [WATCHDOG 06/57] ... |
164 165 166 167 168 169 170 171 172 |
case WDIOC_GETSTATUS: case WDIOC_GETBOOTSTATUS: return put_user(0, p); case WDIOC_SETOPTIONS: if (get_user(new_value, p)) return -EFAULT; if (new_value & WDIOS_DISABLECARD) at91_wdt_stop(); if (new_value & WDIOS_ENABLECARD) |
853807fb5 [WATCHDOG] at91_w... |
173 |
at91_wdt_start(); |
2760600da [WATCHDOG 06/57] ... |
174 |
return 0; |
0c06090c9 [WATCHDOG] Coding... |
175 176 177 178 179 180 181 182 183 184 185 186 187 188 |
case WDIOC_KEEPALIVE: at91_wdt_reload(); /* pat the watchdog */ return 0; case WDIOC_SETTIMEOUT: if (get_user(new_value, p)) return -EFAULT; if (at91_wdt_settimeout(new_value)) return -EINVAL; /* Enable new time value */ at91_wdt_start(); /* Return current value */ return put_user(wdt_time, p); case WDIOC_GETTIMEOUT: return put_user(wdt_time, p); |
2760600da [WATCHDOG 06/57] ... |
189 190 |
default: return -ENOTTY; |
853807fb5 [WATCHDOG] at91_w... |
191 192 193 194 195 196 |
} } /* * Pat the watchdog whenever device is written to. */ |
2760600da [WATCHDOG 06/57] ... |
197 198 |
static ssize_t at91_wdt_write(struct file *file, const char *data, size_t len, loff_t *ppos) |
853807fb5 [WATCHDOG] at91_w... |
199 200 201 202 203 204 |
{ at91_wdt_reload(); /* pat the watchdog */ return len; } /* ......................................................................... */ |
62322d255 [PATCH] make more... |
205 |
static const struct file_operations at91wdt_fops = { |
853807fb5 [WATCHDOG] at91_w... |
206 207 |
.owner = THIS_MODULE, .llseek = no_llseek, |
2760600da [WATCHDOG 06/57] ... |
208 |
.unlocked_ioctl = at91_wdt_ioctl, |
853807fb5 [WATCHDOG] at91_w... |
209 210 211 212 213 214 215 216 217 218 |
.open = at91_wdt_open, .release = at91_wdt_close, .write = at91_wdt_write, }; static struct miscdevice at91wdt_miscdev = { .minor = WATCHDOG_MINOR, .name = "watchdog", .fops = &at91wdt_fops, }; |
2d991a164 watchdog: remove ... |
219 |
static int at91wdt_probe(struct platform_device *pdev) |
853807fb5 [WATCHDOG] at91_w... |
220 |
{ |
8432f9e5e watchdog: at91rm9... |
221 222 |
struct device *dev = &pdev->dev; struct device *parent; |
853807fb5 [WATCHDOG] at91_w... |
223 |
int res; |
e0b79e0bc [WATCHDOG] watchd... |
224 |
if (at91wdt_miscdev.parent) |
dfc7bd9c3 [WATCHDOG] conver... |
225 |
return -EBUSY; |
e0b79e0bc [WATCHDOG] watchd... |
226 |
at91wdt_miscdev.parent = &pdev->dev; |
853807fb5 [WATCHDOG] at91_w... |
227 |
|
8432f9e5e watchdog: at91rm9... |
228 229 230 231 232 233 234 235 |
parent = dev->parent; if (!parent) { dev_err(dev, "no parent "); return -ENODEV; } regmap_st = syscon_node_to_regmap(parent->of_node); |
bf5125d5e watchdog: at91rm9... |
236 |
if (IS_ERR(regmap_st)) |
8432f9e5e watchdog: at91rm9... |
237 |
return -ENODEV; |
853807fb5 [WATCHDOG] at91_w... |
238 239 240 |
res = misc_register(&at91wdt_miscdev); if (res) return res; |
9ab45f3fb watchdog: at91rm9... |
241 242 243 244 |
res = register_restart_handler(&at91rm9200_restart_nb); if (res) dev_warn(dev, "failed to register restart handler "); |
27c766aaa watchdog: Use pr_... |
245 246 247 |
pr_info("AT91 Watchdog Timer enabled (%d seconds%s) ", wdt_time, nowayout ? ", nowayout" : ""); |
853807fb5 [WATCHDOG] at91_w... |
248 249 |
return 0; } |
4b12b896c watchdog: remove ... |
250 |
static int at91wdt_remove(struct platform_device *pdev) |
dfc7bd9c3 [WATCHDOG] conver... |
251 |
{ |
9ab45f3fb watchdog: at91rm9... |
252 |
struct device *dev = &pdev->dev; |
dfc7bd9c3 [WATCHDOG] conver... |
253 |
int res; |
9ab45f3fb watchdog: at91rm9... |
254 255 256 257 |
res = unregister_restart_handler(&at91rm9200_restart_nb); if (res) dev_warn(dev, "failed to unregister restart handler "); |
f368ed608 char: make misc_d... |
258 259 |
misc_deregister(&at91wdt_miscdev); at91wdt_miscdev.parent = NULL; |
dfc7bd9c3 [WATCHDOG] conver... |
260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 |
return res; } static void at91wdt_shutdown(struct platform_device *pdev) { at91_wdt_stop(); } #ifdef CONFIG_PM static int at91wdt_suspend(struct platform_device *pdev, pm_message_t message) { at91_wdt_stop(); return 0; } static int at91wdt_resume(struct platform_device *pdev) { if (at91wdt_busy) at91_wdt_start(); |
95f62bdc5 [WATCHDOG] at91rm... |
281 |
return 0; |
dfc7bd9c3 [WATCHDOG] conver... |
282 283 284 285 286 287 |
} #else #define at91wdt_suspend NULL #define at91wdt_resume NULL #endif |
a6a1bcd37 watchdog: at91rm9... |
288 289 290 291 292 |
static const struct of_device_id at91_wdt_dt_ids[] = { { .compatible = "atmel,at91rm9200-wdt" }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, at91_wdt_dt_ids); |
dfc7bd9c3 [WATCHDOG] conver... |
293 294 |
static struct platform_driver at91wdt_driver = { .probe = at91wdt_probe, |
82268714b watchdog: remove ... |
295 |
.remove = at91wdt_remove, |
dfc7bd9c3 [WATCHDOG] conver... |
296 297 298 299 |
.shutdown = at91wdt_shutdown, .suspend = at91wdt_suspend, .resume = at91wdt_resume, .driver = { |
8432f9e5e watchdog: at91rm9... |
300 |
.name = "atmel_st_watchdog", |
85eee8192 watchdog: Remove ... |
301 |
.of_match_table = at91_wdt_dt_ids, |
dfc7bd9c3 [WATCHDOG] conver... |
302 303 304 305 306 |
}, }; static int __init at91_wdt_init(void) { |
2760600da [WATCHDOG 06/57] ... |
307 308 |
/* Check that the heartbeat value is within range; if not reset to the default */ |
dfc7bd9c3 [WATCHDOG] conver... |
309 310 |
if (at91_wdt_settimeout(wdt_time)) { at91_wdt_settimeout(WDT_DEFAULT_TIME); |
27c766aaa watchdog: Use pr_... |
311 312 313 |
pr_info("wdt_time value must be 1 <= wdt_time <= 256, using %d ", wdt_time); |
dfc7bd9c3 [WATCHDOG] conver... |
314 315 316 317 |
} return platform_driver_register(&at91wdt_driver); } |
853807fb5 [WATCHDOG] at91_w... |
318 319 |
static void __exit at91_wdt_exit(void) { |
dfc7bd9c3 [WATCHDOG] conver... |
320 |
platform_driver_unregister(&at91wdt_driver); |
853807fb5 [WATCHDOG] at91_w... |
321 322 323 324 325 326 327 328 |
} module_init(at91_wdt_init); module_exit(at91_wdt_exit); MODULE_AUTHOR("Andrew Victor"); MODULE_DESCRIPTION("Watchdog driver for Atmel AT91RM9200"); MODULE_LICENSE("GPL"); |
8432f9e5e watchdog: at91rm9... |
329 |
MODULE_ALIAS("platform:atmel_st_watchdog"); |