aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--drivers/watchdog/Kconfig1
-rw-r--r--drivers/watchdog/hpwdt.c216
2 files changed, 48 insertions, 169 deletions
diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig
index 37460cd6cabb..e873522fca2d 100644
--- a/drivers/watchdog/Kconfig
+++ b/drivers/watchdog/Kconfig
@@ -1118,6 +1118,7 @@ config IT87_WDT
1118 1118
1119config HP_WATCHDOG 1119config HP_WATCHDOG
1120 tristate "HP ProLiant iLO2+ Hardware Watchdog Timer" 1120 tristate "HP ProLiant iLO2+ Hardware Watchdog Timer"
1121 select WATCHDOG_CORE
1121 depends on X86 && PCI 1122 depends on X86 && PCI
1122 help 1123 help
1123 A software monitoring watchdog and NMI sourcing driver. This driver 1124 A software monitoring watchdog and NMI sourcing driver. This driver
diff --git a/drivers/watchdog/hpwdt.c b/drivers/watchdog/hpwdt.c
index 44c3038cc531..0e35bb735d8e 100644
--- a/drivers/watchdog/hpwdt.c
+++ b/drivers/watchdog/hpwdt.c
@@ -16,17 +16,13 @@
16#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 16#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
17 17
18#include <linux/device.h> 18#include <linux/device.h>
19#include <linux/fs.h>
20#include <linux/io.h> 19#include <linux/io.h>
21#include <linux/bitops.h>
22#include <linux/kernel.h> 20#include <linux/kernel.h>
23#include <linux/miscdevice.h>
24#include <linux/module.h> 21#include <linux/module.h>
25#include <linux/moduleparam.h> 22#include <linux/moduleparam.h>
26#include <linux/pci.h> 23#include <linux/pci.h>
27#include <linux/pci_ids.h> 24#include <linux/pci_ids.h>
28#include <linux/types.h> 25#include <linux/types.h>
29#include <linux/uaccess.h>
30#include <linux/watchdog.h> 26#include <linux/watchdog.h>
31#include <asm/nmi.h> 27#include <asm/nmi.h>
32 28
@@ -42,8 +38,6 @@ static bool nowayout = WATCHDOG_NOWAYOUT;
42#ifdef CONFIG_HPWDT_NMI_DECODING 38#ifdef CONFIG_HPWDT_NMI_DECODING
43static unsigned int allow_kdump = 1; 39static unsigned int allow_kdump = 1;
44#endif 40#endif
45static char expect_release;
46static unsigned long hpwdt_is_open;
47 41
48static void __iomem *pci_mem_addr; /* the PCI-memory address */ 42static void __iomem *pci_mem_addr; /* the PCI-memory address */
49static unsigned long __iomem *hpwdt_nmistat; 43static unsigned long __iomem *hpwdt_nmistat;
@@ -61,11 +55,14 @@ MODULE_DEVICE_TABLE(pci, hpwdt_devices);
61/* 55/*
62 * Watchdog operations 56 * Watchdog operations
63 */ 57 */
64static void hpwdt_start(void) 58static int hpwdt_start(struct watchdog_device *wdd)
65{ 59{
66 reload = SECS_TO_TICKS(soft_margin); 60 reload = SECS_TO_TICKS(wdd->timeout);
61
67 iowrite16(reload, hpwdt_timer_reg); 62 iowrite16(reload, hpwdt_timer_reg);
68 iowrite8(0x85, hpwdt_timer_con); 63 iowrite8(0x85, hpwdt_timer_con);
64
65 return 0;
69} 66}
70 67
71static void hpwdt_stop(void) 68static void hpwdt_stop(void)
@@ -77,31 +74,32 @@ static void hpwdt_stop(void)
77 iowrite8(data, hpwdt_timer_con); 74 iowrite8(data, hpwdt_timer_con);
78} 75}
79 76
80static void hpwdt_ping(void) 77static int hpwdt_stop_core(struct watchdog_device *wdd)
81{ 78{
82 iowrite16(reload, hpwdt_timer_reg); 79 hpwdt_stop();
80
81 return 0;
83} 82}
84 83
85static int hpwdt_change_timer(int new_margin) 84static int hpwdt_ping(struct watchdog_device *wdd)
86{ 85{
87 if (new_margin < 1 || new_margin > HPWDT_MAX_TIMER) { 86 iowrite16(reload, hpwdt_timer_reg);
88 pr_warn("New value passed in is invalid: %d seconds\n",
89 new_margin);
90 return -EINVAL;
91 }
92
93 soft_margin = new_margin;
94 pr_debug("New timer passed in is %d seconds\n", new_margin);
95 reload = SECS_TO_TICKS(soft_margin);
96
97 return 0; 87 return 0;
98} 88}
99 89
100static int hpwdt_time_left(void) 90static unsigned int hpwdt_gettimeleft(struct watchdog_device *wdd)
101{ 91{
102 return TICKS_TO_SECS(ioread16(hpwdt_timer_reg)); 92 return TICKS_TO_SECS(ioread16(hpwdt_timer_reg));
103} 93}
104 94
95static int hpwdt_settimeout(struct watchdog_device *wdd, unsigned int val)
96{
97 wdd->timeout = val;
98 hpwdt_ping(wdd);
99
100 return 0;
101}
102
105#ifdef CONFIG_HPWDT_NMI_DECODING 103#ifdef CONFIG_HPWDT_NMI_DECODING
106static int hpwdt_my_nmi(void) 104static int hpwdt_my_nmi(void)
107{ 105{
@@ -135,68 +133,6 @@ static int hpwdt_pretimeout(unsigned int ulReason, struct pt_regs *regs)
135} 133}
136#endif /* CONFIG_HPWDT_NMI_DECODING */ 134#endif /* CONFIG_HPWDT_NMI_DECODING */
137 135
138/*
139 * /dev/watchdog handling
140 */
141static int hpwdt_open(struct inode *inode, struct file *file)
142{
143 /* /dev/watchdog can only be opened once */
144 if (test_and_set_bit(0, &hpwdt_is_open))
145 return -EBUSY;
146
147 /* Start the watchdog */
148 hpwdt_start();
149 hpwdt_ping();
150
151 return nonseekable_open(inode, file);
152}
153
154static int hpwdt_release(struct inode *inode, struct file *file)
155{
156 /* Stop the watchdog */
157 if (expect_release == 42) {
158 hpwdt_stop();
159 } else {
160 pr_crit("Unexpected close, not stopping watchdog!\n");
161 hpwdt_ping();
162 }
163
164 expect_release = 0;
165
166 /* /dev/watchdog is being closed, make sure it can be re-opened */
167 clear_bit(0, &hpwdt_is_open);
168
169 return 0;
170}
171
172static ssize_t hpwdt_write(struct file *file, const char __user *data,
173 size_t len, loff_t *ppos)
174{
175 /* See if we got the magic character 'V' and reload the timer */
176 if (len) {
177 if (!nowayout) {
178 size_t i;
179
180 /* note: just in case someone wrote the magic character
181 * five months ago... */
182 expect_release = 0;
183
184 /* scan to see whether or not we got the magic char. */
185 for (i = 0; i != len; i++) {
186 char c;
187 if (get_user(c, data + i))
188 return -EFAULT;
189 if (c == 'V')
190 expect_release = 42;
191 }
192 }
193
194 /* someone wrote to us, we should reload the timer */
195 hpwdt_ping();
196 }
197
198 return len;
199}
200 136
201static const struct watchdog_info ident = { 137static const struct watchdog_info ident = {
202 .options = WDIOF_SETTIMEOUT | 138 .options = WDIOF_SETTIMEOUT |
@@ -205,90 +141,32 @@ static const struct watchdog_info ident = {
205 .identity = "HPE iLO2+ HW Watchdog Timer", 141 .identity = "HPE iLO2+ HW Watchdog Timer",
206}; 142};
207 143
208static long hpwdt_ioctl(struct file *file, unsigned int cmd,
209 unsigned long arg)
210{
211 void __user *argp = (void __user *)arg;
212 int __user *p = argp;
213 int new_margin, options;
214 int ret = -ENOTTY;
215
216 switch (cmd) {
217 case WDIOC_GETSUPPORT:
218 ret = 0;
219 if (copy_to_user(argp, &ident, sizeof(ident)))
220 ret = -EFAULT;
221 break;
222
223 case WDIOC_GETSTATUS:
224 case WDIOC_GETBOOTSTATUS:
225 ret = put_user(0, p);
226 break;
227
228 case WDIOC_KEEPALIVE:
229 hpwdt_ping();
230 ret = 0;
231 break;
232
233 case WDIOC_SETOPTIONS:
234 ret = get_user(options, p);
235 if (ret)
236 break;
237
238 if (options & WDIOS_DISABLECARD)
239 hpwdt_stop();
240
241 if (options & WDIOS_ENABLECARD) {
242 hpwdt_start();
243 hpwdt_ping();
244 }
245 break;
246
247 case WDIOC_SETTIMEOUT:
248 ret = get_user(new_margin, p);
249 if (ret)
250 break;
251
252 ret = hpwdt_change_timer(new_margin);
253 if (ret)
254 break;
255
256 hpwdt_ping();
257 /* Fall */
258 case WDIOC_GETTIMEOUT:
259 ret = put_user(soft_margin, p);
260 break;
261
262 case WDIOC_GETTIMELEFT:
263 ret = put_user(hpwdt_time_left(), p);
264 break;
265 }
266 return ret;
267}
268
269/* 144/*
270 * Kernel interfaces 145 * Kernel interfaces
271 */ 146 */
272static const struct file_operations hpwdt_fops = { 147
273 .owner = THIS_MODULE, 148static const struct watchdog_ops hpwdt_ops = {
274 .llseek = no_llseek, 149 .owner = THIS_MODULE,
275 .write = hpwdt_write, 150 .start = hpwdt_start,
276 .unlocked_ioctl = hpwdt_ioctl, 151 .stop = hpwdt_stop_core,
277 .open = hpwdt_open, 152 .ping = hpwdt_ping,
278 .release = hpwdt_release, 153 .set_timeout = hpwdt_settimeout,
154 .get_timeleft = hpwdt_gettimeleft,
279}; 155};
280 156
281static struct miscdevice hpwdt_miscdev = { 157static struct watchdog_device hpwdt_dev = {
282 .minor = WATCHDOG_MINOR, 158 .info = &ident,
283 .name = "watchdog", 159 .ops = &hpwdt_ops,
284 .fops = &hpwdt_fops, 160 .min_timeout = 1,
161 .max_timeout = HPWDT_MAX_TIMER,
162 .timeout = DEFAULT_MARGIN,
285}; 163};
286 164
165
287/* 166/*
288 * Init & Exit 167 * Init & Exit
289 */ 168 */
290 169
291
292static int hpwdt_init_nmi_decoding(struct pci_dev *dev) 170static int hpwdt_init_nmi_decoding(struct pci_dev *dev)
293{ 171{
294#ifdef CONFIG_HPWDT_NMI_DECODING 172#ifdef CONFIG_HPWDT_NMI_DECODING
@@ -379,29 +257,29 @@ static int hpwdt_init_one(struct pci_dev *dev,
379 /* Make sure that timer is disabled until /dev/watchdog is opened */ 257 /* Make sure that timer is disabled until /dev/watchdog is opened */
380 hpwdt_stop(); 258 hpwdt_stop();
381 259
382 /* Make sure that we have a valid soft_margin */
383 if (hpwdt_change_timer(soft_margin))
384 hpwdt_change_timer(DEFAULT_MARGIN);
385
386 /* Initialize NMI Decoding functionality */ 260 /* Initialize NMI Decoding functionality */
387 retval = hpwdt_init_nmi_decoding(dev); 261 retval = hpwdt_init_nmi_decoding(dev);
388 if (retval != 0) 262 if (retval != 0)
389 goto error_init_nmi_decoding; 263 goto error_init_nmi_decoding;
390 264
391 retval = misc_register(&hpwdt_miscdev); 265 watchdog_set_nowayout(&hpwdt_dev, nowayout);
266 if (watchdog_init_timeout(&hpwdt_dev, soft_margin, NULL))
267 dev_warn(&dev->dev, "Invalid soft_margin: %d.\n", soft_margin);
268
269 hpwdt_dev.parent = &dev->dev;
270 retval = watchdog_register_device(&hpwdt_dev);
392 if (retval < 0) { 271 if (retval < 0) {
393 dev_warn(&dev->dev, 272 dev_err(&dev->dev, "watchdog register failed: %d.\n", retval);
394 "Unable to register miscdev on minor=%d (err=%d).\n", 273 goto error_wd_register;
395 WATCHDOG_MINOR, retval);
396 goto error_misc_register;
397 } 274 }
398 275
399 dev_info(&dev->dev, "HPE Watchdog Timer Driver: %s" 276 dev_info(&dev->dev, "HPE Watchdog Timer Driver: %s"
400 ", timer margin: %d seconds (nowayout=%d).\n", 277 ", timer margin: %d seconds (nowayout=%d).\n",
401 HPWDT_VERSION, soft_margin, nowayout); 278 HPWDT_VERSION, hpwdt_dev.timeout, nowayout);
279
402 return 0; 280 return 0;
403 281
404error_misc_register: 282error_wd_register:
405 hpwdt_exit_nmi_decoding(); 283 hpwdt_exit_nmi_decoding();
406error_init_nmi_decoding: 284error_init_nmi_decoding:
407 pci_iounmap(dev, pci_mem_addr); 285 pci_iounmap(dev, pci_mem_addr);
@@ -415,7 +293,7 @@ static void hpwdt_exit(struct pci_dev *dev)
415 if (!nowayout) 293 if (!nowayout)
416 hpwdt_stop(); 294 hpwdt_stop();
417 295
418 misc_deregister(&hpwdt_miscdev); 296 watchdog_unregister_device(&hpwdt_dev);
419 hpwdt_exit_nmi_decoding(); 297 hpwdt_exit_nmi_decoding();
420 pci_iounmap(dev, pci_mem_addr); 298 pci_iounmap(dev, pci_mem_addr);
421 pci_disable_device(dev); 299 pci_disable_device(dev);