1/* $Id: riowatchdog.c,v 1.1.1.1 2007/08/03 18:52:55 Exp $
2 * riowatchdog.c - driver for hw watchdog inside Super I/O of RIO
3 *
4 * Copyright (C) 2001 David S. Miller (davem@redhat.com)
5 */
6
7#include <linux/kernel.h>
8#include <linux/module.h>
9#include <linux/types.h>
10#include <linux/fs.h>
11#include <linux/errno.h>
12#include <linux/init.h>
13#include <linux/miscdevice.h>
14
15#include <asm/io.h>
16#include <asm/ebus.h>
17#include <asm/bbc.h>
18#include <asm/oplib.h>
19#include <asm/uaccess.h>
20
21#include <asm/watchdog.h>
22
23/* RIO uses the NatSemi Super I/O power management logical device
24 * as its' watchdog.
25 *
26 * When the watchdog triggers, it asserts a line to the BBC (Boot Bus
27 * Controller) of the machine.  The BBC can only be configured to
28 * trigger a power-on reset when the signal is asserted.  The BBC
29 * can be configured to ignore the signal entirely as well.
30 *
31 * The only Super I/O device register we care about is at index
32 * 0x05 (WDTO_INDEX) which is the watchdog time-out in minutes (1-255).
33 * If set to zero, this disables the watchdog.  When set, the system
34 * must periodically (before watchdog expires) clear (set to zero) and
35 * re-set the watchdog else it will trigger.
36 *
37 * There are two other indexed watchdog registers inside this Super I/O
38 * logical device, but they are unused.  The first, at index 0x06 is
39 * the watchdog control and can be used to make the watchdog timer re-set
40 * when the PS/2 mouse or serial lines show activity.  The second, at
41 * index 0x07 is merely a sampling of the line from the watchdog to the
42 * BBC.
43 *
44 * The watchdog device generates no interrupts.
45 */
46
47MODULE_AUTHOR("David S. Miller <davem@redhat.com>");
48MODULE_DESCRIPTION("Hardware watchdog driver for Sun RIO");
49MODULE_SUPPORTED_DEVICE("watchdog");
50MODULE_LICENSE("GPL");
51
52#define RIOWD_NAME	"pmc"
53#define RIOWD_MINOR	215
54
55static DEFINE_SPINLOCK(riowd_lock);
56
57static void __iomem *bbc_regs;
58static void __iomem *riowd_regs;
59#define WDTO_INDEX	0x05
60
61static int riowd_timeout = 1;		/* in minutes */
62module_param(riowd_timeout, int, 0);
63MODULE_PARM_DESC(riowd_timeout, "Watchdog timeout in minutes");
64
65
66static void riowd_writereg(u8 val, int index)
67{
68	unsigned long flags;
69
70	spin_lock_irqsave(&riowd_lock, flags);
71	writeb(index, riowd_regs + 0);
72	writeb(val, riowd_regs + 1);
73	spin_unlock_irqrestore(&riowd_lock, flags);
74}
75
76static void riowd_pingtimer(void)
77{
78	riowd_writereg(riowd_timeout, WDTO_INDEX);
79}
80
81static void riowd_stoptimer(void)
82{
83	u8 val;
84
85	riowd_writereg(0, WDTO_INDEX);
86
87	val = readb(bbc_regs + BBC_WDACTION);
88	val &= ~BBC_WDACTION_RST;
89	writeb(val, bbc_regs + BBC_WDACTION);
90}
91
92static void riowd_starttimer(void)
93{
94	u8 val;
95
96	riowd_writereg(riowd_timeout, WDTO_INDEX);
97
98	val = readb(bbc_regs + BBC_WDACTION);
99	val |= BBC_WDACTION_RST;
100	writeb(val, bbc_regs + BBC_WDACTION);
101}
102
103static int riowd_open(struct inode *inode, struct file *filp)
104{
105	nonseekable_open(inode, filp);
106	return 0;
107}
108
109static int riowd_release(struct inode *inode, struct file *filp)
110{
111	return 0;
112}
113
114static int riowd_ioctl(struct inode *inode, struct file *filp,
115		       unsigned int cmd, unsigned long arg)
116{
117	static struct watchdog_info info = {
118	       	WDIOF_SETTIMEOUT, 0, "Natl. Semiconductor PC97317"
119	};
120	void __user *argp = (void __user *)arg;
121	unsigned int options;
122	int new_margin;
123
124	switch (cmd) {
125	case WDIOC_GETSUPPORT:
126		if (copy_to_user(argp, &info, sizeof(info)))
127			return -EFAULT;
128		break;
129
130	case WDIOC_GETSTATUS:
131	case WDIOC_GETBOOTSTATUS:
132		if (put_user(0, (int __user *)argp))
133			return -EFAULT;
134		break;
135
136	case WDIOC_KEEPALIVE:
137		riowd_pingtimer();
138		break;
139
140	case WDIOC_SETOPTIONS:
141		if (copy_from_user(&options, argp, sizeof(options)))
142			return -EFAULT;
143
144		if (options & WDIOS_DISABLECARD)
145			riowd_stoptimer();
146		else if (options & WDIOS_ENABLECARD)
147			riowd_starttimer();
148		else
149			return -EINVAL;
150
151		break;
152
153	case WDIOC_SETTIMEOUT:
154		if (get_user(new_margin, (int __user *)argp))
155			return -EFAULT;
156		if ((new_margin < 60) || (new_margin > (255 * 60)))
157		    return -EINVAL;
158		riowd_timeout = (new_margin + 59) / 60;
159		riowd_pingtimer();
160		/* Fall */
161
162	case WDIOC_GETTIMEOUT:
163		return put_user(riowd_timeout * 60, (int __user *)argp);
164
165	default:
166		return -EINVAL;
167	};
168
169	return 0;
170}
171
172static ssize_t riowd_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos)
173{
174	if (count) {
175		riowd_pingtimer();
176		return 1;
177	}
178
179	return 0;
180}
181
182static const struct file_operations riowd_fops = {
183	.owner =	THIS_MODULE,
184	.ioctl =	riowd_ioctl,
185	.open =		riowd_open,
186	.write =	riowd_write,
187	.release =	riowd_release,
188};
189
190static struct miscdevice riowd_miscdev = { RIOWD_MINOR, RIOWD_NAME, &riowd_fops };
191
192static int __init riowd_bbc_init(void)
193{
194	struct 	linux_ebus *ebus = NULL;
195	struct 	linux_ebus_device *edev = NULL;
196	u8 val;
197
198	for_each_ebus(ebus) {
199		for_each_ebusdev(edev, ebus) {
200			if (!strcmp(edev->ofdev.node->name, "bbc"))
201				goto found_bbc;
202		}
203	}
204
205found_bbc:
206	if (!edev)
207		return -ENODEV;
208	bbc_regs = ioremap(edev->resource[0].start, BBC_REGS_SIZE);
209	if (!bbc_regs)
210		return -ENODEV;
211
212	/* Turn it off. */
213	val = readb(bbc_regs + BBC_WDACTION);
214	val &= ~BBC_WDACTION_RST;
215	writeb(val, bbc_regs + BBC_WDACTION);
216
217	return 0;
218}
219
220static int __init riowd_init(void)
221{
222	struct 	linux_ebus *ebus = NULL;
223	struct 	linux_ebus_device *edev = NULL;
224
225	for_each_ebus(ebus) {
226		for_each_ebusdev(edev, ebus) {
227			if (!strcmp(edev->ofdev.node->name, RIOWD_NAME))
228				goto ebus_done;
229		}
230	}
231
232ebus_done:
233	if (!edev)
234		goto fail;
235
236	riowd_regs = ioremap(edev->resource[0].start, 2);
237	if (riowd_regs == NULL) {
238		printk(KERN_ERR "pmc: Cannot map registers.\n");
239		return -ENODEV;
240	}
241
242	if (riowd_bbc_init()) {
243		printk(KERN_ERR "pmc: Failure initializing BBC config.\n");
244		goto fail;
245	}
246
247	if (misc_register(&riowd_miscdev)) {
248		printk(KERN_ERR "pmc: Cannot register watchdog misc device.\n");
249		goto fail;
250	}
251
252	printk(KERN_INFO "pmc: Hardware watchdog [%i minutes], "
253	       "regs at %p\n", riowd_timeout, riowd_regs);
254
255	return 0;
256
257fail:
258	if (riowd_regs) {
259		iounmap(riowd_regs);
260		riowd_regs = NULL;
261	}
262	if (bbc_regs) {
263		iounmap(bbc_regs);
264		bbc_regs = NULL;
265	}
266	return -ENODEV;
267}
268
269static void __exit riowd_cleanup(void)
270{
271	misc_deregister(&riowd_miscdev);
272	iounmap(riowd_regs);
273	riowd_regs = NULL;
274	iounmap(bbc_regs);
275	bbc_regs = NULL;
276}
277
278module_init(riowd_init);
279module_exit(riowd_cleanup);
280