1/*
2 *	Watchdog for the 7101 PMU version found in the ALi1535 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/types.h>
12#include <linux/kernel.h>
13#include <linux/fs.h>
14#include <linux/mm.h>
15#include <linux/miscdevice.h>
16#include <linux/watchdog.h>
17#include <linux/reboot.h>
18#include <linux/init.h>
19#include <linux/pci.h>
20#include <linux/ioport.h>
21#include <asm/uaccess.h>
22#include <asm/io.h>
23
24static spinlock_t ali_lock;	/* Guards the hardware */
25
26#ifdef CONFIG_WATCHDOG_NOWAYOUT
27static int nowayout = 1;
28#else
29static int nowayout = 0;
30#endif
31
32MODULE_PARM(nowayout,"i");
33MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=CONFIG_WATCHDOG_NOWAYOUT)");
34
35static unsigned long timer_alive;
36static char ali_expect_close;
37static u32 ali_timeout = 60;			/* 60 seconds */
38static u32 ali_timeout_bits = 1 | (1<<7);	/* 1 count in minutes */
39
40static struct pci_dev *ali_pci;
41
42/**
43 *	ali_timer_start		-	start watchdog countdown
44 *	@dev: PCI device of the PMU
45 *
46 *	Starts the timer running providing the timer has a counter
47 *	configuration set.
48 */
49
50static void ali_timer_start(struct pci_dev *pdev)
51{
52	u32 val;
53
54	spin_lock(&ali_lock);
55
56	pci_read_config_dword(pdev, 0xCC, &val);
57	val &= ~0x3F;	/* Mask count */
58	val |= (1<<25) | ali_timeout_bits;
59	pci_write_config_dword(pdev, 0xCC, val);
60	spin_unlock(&ali_lock);
61}
62
63/**
64 *	ali_timer_stop	-	stop the timer countdown
65 *	@pdev: PCI device of the PMU
66 *
67 *	Stop the ALi watchdog countdown
68 */
69
70static void ali_timer_stop (struct pci_dev *pdev)
71{
72	u32 val;
73
74	spin_lock(&ali_lock);
75	pci_read_config_dword(pdev, 0xCC, &val);
76	val &= ~0x3F;	/* Mask count to zero (disabled) */
77	val &= ~(1<<25);/* and for safety mask the reset enable */
78	pci_write_config_dword(pdev, 0xCC, val);
79	spin_unlock(&ali_lock);
80}
81
82/**
83 *	ali_timer_settimer	-	compute the timer reload value
84 *	@pdev: PCI device of the PMU
85 *	@t: time in seconds
86 *
87 *	Computes the timeout values needed and then restarts the timer
88 *	running with the new timeout values
89 */
90
91static int ali_timer_settimer(struct pci_dev *pdev, unsigned long t)
92{
93	if(t < 60)
94		ali_timeout_bits = t|(1<<6);
95	else if(t < 3600)
96		ali_timeout_bits = (t/60)|(1<<7);
97	else if(t < 18000)
98		ali_timeout_bits = (t/300)|(1<<6)|(1<<7);
99	else return -EINVAL;
100
101	ali_timeout = t;
102	ali_timer_start(pdev);
103	return 0;
104}
105
106/**
107 *	ali_open	-	handle open of ali watchdog
108 *	@inode: inode from VFS
109 *	@file: file from VFS
110 *
111 *	Open the ALi watchdog device. Ensure only one person opens it
112 *	at a time. Also start the watchdog running.
113 */
114
115static int ali_open(struct inode *inode, struct file *file)
116{
117	if (test_and_set_bit(0, &timer_alive))
118		return -EBUSY;
119	ali_timer_start (ali_pci);
120	return 0;
121}
122
123/**
124 *	ali_release	-	close an ALi watchdog
125 *	@inode: inode from VFS
126 *	@file: file from VFS
127 *
128 *	Close the ALi watchdog device. Actual shutdown of the timer
129 *	only occurs if the magic sequence has been set or nowayout is
130 *	disabled
131 */
132
133static int ali_release (struct inode *inode, struct file *file)
134{
135	/*
136	 *      Shut off the timer.
137	 */
138	if (ali_expect_close == 42 && !nowayout) {
139		ali_timer_stop(ali_pci);
140	} else {
141		ali_timer_start(ali_pci);
142		printk(KERN_CRIT  "ali1535_wdt: Unexpected close, not stopping watchdog!\n");
143	}
144	clear_bit(0, &timer_alive);
145	ali_expect_close = 0;
146	return 0;
147}
148
149/**
150 *	ali_write	-	writes to ALi watchdog
151 *	@file: file from VFS
152 *	@data: user address of data
153 *	@len: length of data
154 *	@ppos: pointer to the file offset
155 *
156 *	Handle a write to the ALi watchdog. Writing to the file pings
157 *	the watchdog and resets it. Writing the magic 'V' sequence allows
158 *	the next close to turn off the watchdog.
159 */
160
161static ssize_t ali_write (struct file *file, const char *data,
162			      size_t len, loff_t * ppos)
163{
164	/*  Can't seek (pwrite) on this device  */
165	if (ppos != &file->f_pos)
166		return -ESPIPE;
167
168	/* See if we got the magic character 'V' and reload the timer */
169	if (len) {
170		size_t i;
171
172		ali_expect_close = 0;
173
174		/* scan to see wether or not we got the magic character */
175		for (i = 0; i != len; i++) {
176			u8 c;
177			if(get_user(c, data+i))
178				return -EFAULT;
179			if (c == 'V')
180				ali_expect_close = 42;
181		}
182
183		/* someone wrote to us, we should reload the timer */
184		ali_timer_start(ali_pci);
185		return 1;
186	}
187	return 0;
188}
189
190/**
191 *	ali_ioctl	-	handle watchdog ioctls
192 *	@inode: VFS inode
193 *	@file: VFS file pointer
194 *	@cmd: ioctl number
195 *	@arg: arguments to the ioctl
196 *
197 *	Handle the watchdog ioctls supported by the ALi driver. Really
198 *	we want an extension to enable irq ack monitoring and the like
199 */
200
201static int ali_ioctl (struct inode *inode, struct file *file,
202			  unsigned int cmd, unsigned long arg)
203{
204	int options, retval = -EINVAL;
205	u32 t;
206	static struct watchdog_info ident = {
207		options:		WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING,
208		firmware_version:	0,
209		identity:		"ALi 1535D+ TCO timer",
210	};
211	switch (cmd) {
212		default:
213			return -ENOTTY;
214		case WDIOC_GETSUPPORT:
215			if (copy_to_user((struct watchdog_info *) arg, &ident, sizeof (ident)))
216				return -EFAULT;
217			return 0;
218		case WDIOC_GETSTATUS:
219		case WDIOC_GETBOOTSTATUS:
220			return put_user(0, (int *) arg);
221		case WDIOC_SETOPTIONS:
222			if (get_user (options, (int *) arg))
223				return -EFAULT;
224			if (options & WDIOS_DISABLECARD) {
225				ali_timer_stop(ali_pci);
226				retval = 0;
227			}
228			if (options & WDIOS_ENABLECARD) {
229				ali_timer_start(ali_pci);
230				retval = 0;
231			}
232			return retval;
233		case WDIOC_KEEPALIVE:
234			ali_timer_start(ali_pci);
235			return 0;
236		case WDIOC_SETTIMEOUT:
237			if (get_user(t, (int *) arg))
238				return -EFAULT;
239			if (ali_timer_settimer(ali_pci, t))
240			    return -EINVAL;
241			ali_timer_start(ali_pci);
242			/* Fall */
243		case WDIOC_GETTIMEOUT:
244			return put_user(ali_timeout, (int *)arg);
245	}
246}
247
248/*
249 * Data for PCI driver interface
250 *
251 * This data only exists for exporting the supported
252 * PCI ids via MODULE_DEVICE_TABLE.  We do not actually
253 * register a pci_driver, because someone else might one day
254 * want to register another driver on the same PCI id.
255 */
256
257static struct pci_device_id ali_pci_tbl[] __initdata = {
258	{ PCI_VENDOR_ID_AL, 1535, PCI_ANY_ID, PCI_ANY_ID,},
259	{ 0, },
260};
261MODULE_DEVICE_TABLE (pci, ali_pci_tbl);
262
263
264/**
265 *	ali_find_watchdog	-	find a 1535 and 7101
266 *
267 *	Scans the PCI hardware for a 1535 series bridge and matching 7101
268 *	watchdog device. This may be overtight but it is better to be safe
269 */
270
271static int __init ali_find_watchdog(void)
272{
273	struct pci_dev *pdev;
274	u32 wdog;
275
276	/* Check for a 1535 series bridge */
277	pdev = pci_find_device(PCI_VENDOR_ID_AL, 0x1535, NULL);
278	if(pdev == NULL)
279		return -ENODEV;
280
281	/* Check for the a 7101 PMU */
282	pdev = pci_find_device(PCI_VENDOR_ID_AL, 0x7101, NULL);
283	if(pdev == NULL)
284		return -ENODEV;
285
286	if(pci_enable_device(pdev))
287		return -EIO;
288
289	ali_pci = pdev;
290
291	/*
292	 *	Initialize the timer bits
293	 */
294
295	pci_read_config_dword(pdev, 0xCC, &wdog);
296
297	wdog &= ~0x3F;		/* Timer bits */
298	wdog &= ~((1<<27)|(1<<26)|(1<<25)|(1<<24));	/* Issued events */
299	wdog &= ~((1<<16)|(1<<13)|(1<<12)|(1<<11)|(1<<10)|(1<<9));	/* No monitor bits */
300
301	pci_write_config_dword(pdev, 0xCC, wdog);
302	return 0;
303}
304
305static struct file_operations ali_fops = {
306	owner:		THIS_MODULE,
307	write:		ali_write,
308	ioctl:		ali_ioctl,
309	open:		ali_open,
310	release:	ali_release,
311};
312
313static struct miscdevice ali_miscdev = {
314	minor:		WATCHDOG_MINOR,
315	name:		"watchdog",
316	fops:		&ali_fops,
317};
318
319/**
320 *	watchdog_init	-	module initialiser
321 *
322 *	Scan for a suitable watchdog and if so initialize it. Return an error
323 *	if we cannot, the error causes the module to unload
324 */
325
326static int __init watchdog_init (void)
327{
328	spin_lock_init(&ali_lock);
329	if (!ali_find_watchdog())
330		return -ENODEV;
331	if (misc_register (&ali_miscdev) != 0) {
332		printk (KERN_ERR "alim1535d: cannot register watchdog device node.\n");
333		return -EIO;
334	}
335	return 0;
336}
337
338/**
339 *	watchdog_cleanup	-	unload watchdog
340 *
341 *	Called on the unload of a successfully installed watchdog module.
342 */
343
344static void __exit watchdog_cleanup (void)
345{
346	ali_timer_stop(ali_pci);
347	misc_deregister (&ali_miscdev);
348}
349
350module_init(watchdog_init);
351module_exit(watchdog_cleanup);
352
353MODULE_AUTHOR("Alan Cox");
354MODULE_DESCRIPTION("Watchdog driver for the ALi 1535+ PMU");
355MODULE_LICENSE("GPL");
356EXPORT_NO_SYMBOLS;
357