xref: /linux/drivers/watchdog/alim1535_wdt.c (revision 3fd6c59042dbba50391e30862beac979491145fe)
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3  *	Watchdog for the 7101 PMU version found in the ALi M1535 chipsets
4  */
5 
6 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
7 
8 #include <linux/module.h>
9 #include <linux/moduleparam.h>
10 #include <linux/types.h>
11 #include <linux/miscdevice.h>
12 #include <linux/watchdog.h>
13 #include <linux/ioport.h>
14 #include <linux/notifier.h>
15 #include <linux/reboot.h>
16 #include <linux/init.h>
17 #include <linux/fs.h>
18 #include <linux/pci.h>
19 #include <linux/uaccess.h>
20 #include <linux/io.h>
21 
22 #define WATCHDOG_NAME "ALi_M1535"
23 #define WATCHDOG_TIMEOUT 60	/* 60 sec default timeout */
24 
25 /* internal variables */
26 static unsigned long ali_is_open;
27 static char ali_expect_release;
28 static struct pci_dev *ali_pci;
29 static u32 ali_timeout_bits;		/* stores the computed timeout */
30 static DEFINE_SPINLOCK(ali_lock);	/* Guards the hardware */
31 
32 /* module parameters */
33 static int timeout = WATCHDOG_TIMEOUT;
34 module_param(timeout, int, 0);
35 MODULE_PARM_DESC(timeout,
36 		"Watchdog timeout in seconds. (0 < timeout < 18000, default="
37 				__MODULE_STRING(WATCHDOG_TIMEOUT) ")");
38 
39 static bool nowayout = WATCHDOG_NOWAYOUT;
40 module_param(nowayout, bool, 0);
41 MODULE_PARM_DESC(nowayout,
42 		"Watchdog cannot be stopped once started (default="
43 				__MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
44 
45 /*
46  *	ali_start	-	start watchdog countdown
47  *
48  *	Starts the timer running providing the timer has a counter
49  *	configuration set.
50  */
51 
ali_start(void)52 static void ali_start(void)
53 {
54 	u32 val;
55 
56 	spin_lock(&ali_lock);
57 
58 	pci_read_config_dword(ali_pci, 0xCC, &val);
59 	val &= ~0x3F;	/* Mask count */
60 	val |= (1 << 25) | ali_timeout_bits;
61 	pci_write_config_dword(ali_pci, 0xCC, val);
62 
63 	spin_unlock(&ali_lock);
64 }
65 
66 /*
67  *	ali_stop	-	stop the timer countdown
68  *
69  *	Stop the ALi watchdog countdown
70  */
71 
ali_stop(void)72 static void ali_stop(void)
73 {
74 	u32 val;
75 
76 	spin_lock(&ali_lock);
77 
78 	pci_read_config_dword(ali_pci, 0xCC, &val);
79 	val &= ~0x3F;		/* Mask count to zero (disabled) */
80 	val &= ~(1 << 25);	/* and for safety mask the reset enable */
81 	pci_write_config_dword(ali_pci, 0xCC, val);
82 
83 	spin_unlock(&ali_lock);
84 }
85 
86 /*
87  *	ali_keepalive	-	send a keepalive to the watchdog
88  *
89  *	Send a keepalive to the timer (actually we restart the timer).
90  */
91 
ali_keepalive(void)92 static void ali_keepalive(void)
93 {
94 	ali_start();
95 }
96 
97 /*
98  *	ali_settimer	-	compute the timer reload value
99  *	@t: time in seconds
100  *
101  *	Computes the timeout values needed
102  */
103 
ali_settimer(int t)104 static int ali_settimer(int t)
105 {
106 	if (t < 0)
107 		return -EINVAL;
108 	else if (t < 60)
109 		ali_timeout_bits = t|(1 << 6);
110 	else if (t < 3600)
111 		ali_timeout_bits = (t / 60)|(1 << 7);
112 	else if (t < 18000)
113 		ali_timeout_bits = (t / 300)|(1 << 6)|(1 << 7);
114 	else
115 		return -EINVAL;
116 
117 	timeout = t;
118 	return 0;
119 }
120 
121 /*
122  *	/dev/watchdog handling
123  */
124 
125 /*
126  *	ali_write	-	writes to ALi watchdog
127  *	@file: file from VFS
128  *	@data: user address of data
129  *	@len: length of data
130  *	@ppos: pointer to the file offset
131  *
132  *	Handle a write to the ALi watchdog. Writing to the file pings
133  *	the watchdog and resets it. Writing the magic 'V' sequence allows
134  *	the next close to turn off the watchdog.
135  */
136 
ali_write(struct file * file,const char __user * data,size_t len,loff_t * ppos)137 static ssize_t ali_write(struct file *file, const char __user *data,
138 						size_t len, loff_t *ppos)
139 {
140 	/* See if we got the magic character 'V' and reload the timer */
141 	if (len) {
142 		if (!nowayout) {
143 			size_t i;
144 
145 			/* note: just in case someone wrote the
146 			   magic character five months ago... */
147 			ali_expect_release = 0;
148 
149 			/* scan to see whether or not we got
150 			   the magic character */
151 			for (i = 0; i != len; i++) {
152 				char c;
153 				if (get_user(c, data + i))
154 					return -EFAULT;
155 				if (c == 'V')
156 					ali_expect_release = 42;
157 			}
158 		}
159 
160 		/* someone wrote to us, we should reload the timer */
161 		ali_start();
162 	}
163 	return len;
164 }
165 
166 /*
167  *	ali_ioctl	-	handle watchdog ioctls
168  *	@file: VFS file pointer
169  *	@cmd: ioctl number
170  *	@arg: arguments to the ioctl
171  *
172  *	Handle the watchdog ioctls supported by the ALi driver. Really
173  *	we want an extension to enable irq ack monitoring and the like
174  */
175 
ali_ioctl(struct file * file,unsigned int cmd,unsigned long arg)176 static long ali_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
177 {
178 	void __user *argp = (void __user *)arg;
179 	int __user *p = argp;
180 	static const struct watchdog_info ident = {
181 		.options =		WDIOF_KEEPALIVEPING |
182 					WDIOF_SETTIMEOUT |
183 					WDIOF_MAGICCLOSE,
184 		.firmware_version =	0,
185 		.identity =		"ALi M1535 WatchDog Timer",
186 	};
187 
188 	switch (cmd) {
189 	case WDIOC_GETSUPPORT:
190 		return copy_to_user(argp, &ident, sizeof(ident)) ? -EFAULT : 0;
191 
192 	case WDIOC_GETSTATUS:
193 	case WDIOC_GETBOOTSTATUS:
194 		return put_user(0, p);
195 	case WDIOC_SETOPTIONS:
196 	{
197 		int new_options, retval = -EINVAL;
198 
199 		if (get_user(new_options, p))
200 			return -EFAULT;
201 		if (new_options & WDIOS_DISABLECARD) {
202 			ali_stop();
203 			retval = 0;
204 		}
205 		if (new_options & WDIOS_ENABLECARD) {
206 			ali_start();
207 			retval = 0;
208 		}
209 		return retval;
210 	}
211 	case WDIOC_KEEPALIVE:
212 		ali_keepalive();
213 		return 0;
214 	case WDIOC_SETTIMEOUT:
215 	{
216 		int new_timeout;
217 		if (get_user(new_timeout, p))
218 			return -EFAULT;
219 		if (ali_settimer(new_timeout))
220 			return -EINVAL;
221 		ali_keepalive();
222 	}
223 		fallthrough;
224 	case WDIOC_GETTIMEOUT:
225 		return put_user(timeout, p);
226 	default:
227 		return -ENOTTY;
228 	}
229 }
230 
231 /*
232  *	ali_open	-	handle open of ali watchdog
233  *	@inode: inode from VFS
234  *	@file: file from VFS
235  *
236  *	Open the ALi watchdog device. Ensure only one person opens it
237  *	at a time. Also start the watchdog running.
238  */
239 
ali_open(struct inode * inode,struct file * file)240 static int ali_open(struct inode *inode, struct file *file)
241 {
242 	/* /dev/watchdog can only be opened once */
243 	if (test_and_set_bit(0, &ali_is_open))
244 		return -EBUSY;
245 
246 	/* Activate */
247 	ali_start();
248 	return stream_open(inode, file);
249 }
250 
251 /*
252  *	ali_release	-	close an ALi watchdog
253  *	@inode: inode from VFS
254  *	@file: file from VFS
255  *
256  *	Close the ALi watchdog device. Actual shutdown of the timer
257  *	only occurs if the magic sequence has been set.
258  */
259 
ali_release(struct inode * inode,struct file * file)260 static int ali_release(struct inode *inode, struct file *file)
261 {
262 	/*
263 	 *      Shut off the timer.
264 	 */
265 	if (ali_expect_release == 42)
266 		ali_stop();
267 	else {
268 		pr_crit("Unexpected close, not stopping watchdog!\n");
269 		ali_keepalive();
270 	}
271 	clear_bit(0, &ali_is_open);
272 	ali_expect_release = 0;
273 	return 0;
274 }
275 
276 /*
277  *	ali_notify_sys	-	System down notifier
278  *
279  *	Notifier for system down
280  */
281 
282 
ali_notify_sys(struct notifier_block * this,unsigned long code,void * unused)283 static int ali_notify_sys(struct notifier_block *this,
284 					unsigned long code, void *unused)
285 {
286 	if (code == SYS_DOWN || code == SYS_HALT)
287 		ali_stop();		/* Turn the WDT off */
288 	return NOTIFY_DONE;
289 }
290 
291 /*
292  *	Data for PCI driver interface
293  *
294  *	This data only exists for exporting the supported
295  *	PCI ids via MODULE_DEVICE_TABLE.  We do not actually
296  *	register a pci_driver, because someone else might one day
297  *	want to register another driver on the same PCI id.
298  */
299 
300 static const struct pci_device_id ali_pci_tbl[] __used = {
301 	{ PCI_VENDOR_ID_AL, 0x1533, PCI_ANY_ID, PCI_ANY_ID,},
302 	{ PCI_VENDOR_ID_AL, 0x1535, PCI_ANY_ID, PCI_ANY_ID,},
303 	{ 0, },
304 };
305 MODULE_DEVICE_TABLE(pci, ali_pci_tbl);
306 
307 /*
308  *	ali_find_watchdog	-	find a 1535 and 7101
309  *
310  *	Scans the PCI hardware for a 1535 series bridge and matching 7101
311  *	watchdog device. This may be overtight but it is better to be safe
312  */
313 
ali_find_watchdog(void)314 static int __init ali_find_watchdog(void)
315 {
316 	struct pci_dev *pdev;
317 	u32 wdog;
318 
319 	/* Check for a 1533/1535 series bridge */
320 	pdev = pci_get_device(PCI_VENDOR_ID_AL, 0x1535, NULL);
321 	if (pdev == NULL)
322 		pdev = pci_get_device(PCI_VENDOR_ID_AL, 0x1533, NULL);
323 	if (pdev == NULL)
324 		return -ENODEV;
325 	pci_dev_put(pdev);
326 
327 	/* Check for the a 7101 PMU */
328 	pdev = pci_get_device(PCI_VENDOR_ID_AL, 0x7101, NULL);
329 	if (pdev == NULL)
330 		return -ENODEV;
331 
332 	if (pci_enable_device(pdev)) {
333 		pci_dev_put(pdev);
334 		return -EIO;
335 	}
336 
337 	ali_pci = pdev;
338 
339 	/*
340 	 *	Initialize the timer bits
341 	 */
342 	pci_read_config_dword(pdev, 0xCC, &wdog);
343 
344 	/* Timer bits */
345 	wdog &= ~0x3F;
346 	/* Issued events */
347 	wdog &= ~((1 << 27)|(1 << 26)|(1 << 25)|(1 << 24));
348 	/* No monitor bits */
349 	wdog &= ~((1 << 16)|(1 << 13)|(1 << 12)|(1 << 11)|(1 << 10)|(1 << 9));
350 
351 	pci_write_config_dword(pdev, 0xCC, wdog);
352 
353 	return 0;
354 }
355 
356 /*
357  *	Kernel Interfaces
358  */
359 
360 static const struct file_operations ali_fops = {
361 	.owner		=	THIS_MODULE,
362 	.write		=	ali_write,
363 	.unlocked_ioctl =	ali_ioctl,
364 	.compat_ioctl	= 	compat_ptr_ioctl,
365 	.open		=	ali_open,
366 	.release	=	ali_release,
367 };
368 
369 static struct miscdevice ali_miscdev = {
370 	.minor =	WATCHDOG_MINOR,
371 	.name =		"watchdog",
372 	.fops =		&ali_fops,
373 };
374 
375 static struct notifier_block ali_notifier = {
376 	.notifier_call =	ali_notify_sys,
377 };
378 
379 /*
380  *	watchdog_init	-	module initialiser
381  *
382  *	Scan for a suitable watchdog and if so initialize it. Return an error
383  *	if we cannot, the error causes the module to unload
384  */
385 
watchdog_init(void)386 static int __init watchdog_init(void)
387 {
388 	int ret;
389 
390 	/* Check whether or not the hardware watchdog is there */
391 	if (ali_find_watchdog() != 0)
392 		return -ENODEV;
393 
394 	/* Check that the timeout value is within it's range;
395 	   if not reset to the default */
396 	if (timeout < 1 || timeout >= 18000) {
397 		timeout = WATCHDOG_TIMEOUT;
398 		pr_info("timeout value must be 0 < timeout < 18000, using %d\n",
399 			timeout);
400 	}
401 
402 	/* Calculate the watchdog's timeout */
403 	ali_settimer(timeout);
404 
405 	ret = register_reboot_notifier(&ali_notifier);
406 	if (ret != 0) {
407 		pr_err("cannot register reboot notifier (err=%d)\n", ret);
408 		goto out;
409 	}
410 
411 	ret = misc_register(&ali_miscdev);
412 	if (ret != 0) {
413 		pr_err("cannot register miscdev on minor=%d (err=%d)\n",
414 		       WATCHDOG_MINOR, ret);
415 		goto unreg_reboot;
416 	}
417 
418 	pr_info("initialized. timeout=%d sec (nowayout=%d)\n",
419 		timeout, nowayout);
420 
421 out:
422 	return ret;
423 unreg_reboot:
424 	unregister_reboot_notifier(&ali_notifier);
425 	goto out;
426 }
427 
428 /*
429  *	watchdog_exit	-	module de-initialiser
430  *
431  *	Called while unloading a successfully installed watchdog module.
432  */
433 
watchdog_exit(void)434 static void __exit watchdog_exit(void)
435 {
436 	/* Stop the timer before we leave */
437 	ali_stop();
438 
439 	/* Deregister */
440 	misc_deregister(&ali_miscdev);
441 	unregister_reboot_notifier(&ali_notifier);
442 	pci_dev_put(ali_pci);
443 }
444 
445 module_init(watchdog_init);
446 module_exit(watchdog_exit);
447 
448 MODULE_AUTHOR("Alan Cox");
449 MODULE_DESCRIPTION("ALi M1535 PMU Watchdog Timer driver");
450 MODULE_LICENSE("GPL");
451