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