riowd.c 5.8 KB
Newer Older
1
/* riowd.c - driver for hw watchdog inside Super I/O of RIO
L
Linus Torvalds 已提交
2
 *
3
 * Copyright (C) 2001, 2008 David S. Miller (davem@davemloft.net)
L
Linus Torvalds 已提交
4 5 6 7 8 9 10 11 12
 */

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/types.h>
#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/init.h>
#include <linux/miscdevice.h>
13 14 15
#include <linux/watchdog.h>
#include <linux/of.h>
#include <linux/of_device.h>
16 17
#include <linux/io.h>
#include <linux/uaccess.h>
L
Linus Torvalds 已提交
18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43


/* RIO uses the NatSemi Super I/O power management logical device
 * as its' watchdog.
 *
 * When the watchdog triggers, it asserts a line to the BBC (Boot Bus
 * Controller) of the machine.  The BBC can only be configured to
 * trigger a power-on reset when the signal is asserted.  The BBC
 * can be configured to ignore the signal entirely as well.
 *
 * The only Super I/O device register we care about is at index
 * 0x05 (WDTO_INDEX) which is the watchdog time-out in minutes (1-255).
 * If set to zero, this disables the watchdog.  When set, the system
 * must periodically (before watchdog expires) clear (set to zero) and
 * re-set the watchdog else it will trigger.
 *
 * There are two other indexed watchdog registers inside this Super I/O
 * logical device, but they are unused.  The first, at index 0x06 is
 * the watchdog control and can be used to make the watchdog timer re-set
 * when the PS/2 mouse or serial lines show activity.  The second, at
 * index 0x07 is merely a sampling of the line from the watchdog to the
 * BBC.
 *
 * The watchdog device generates no interrupts.
 */

44
MODULE_AUTHOR("David S. Miller <davem@davemloft.net>");
L
Linus Torvalds 已提交
45 46 47 48
MODULE_DESCRIPTION("Hardware watchdog driver for Sun RIO");
MODULE_SUPPORTED_DEVICE("watchdog");
MODULE_LICENSE("GPL");

49 50
#define DRIVER_NAME	"riowd"
#define PFX		DRIVER_NAME ": "
L
Linus Torvalds 已提交
51

52 53 54 55 56 57
struct riowd {
	void __iomem		*regs;
	spinlock_t		lock;
};

static struct riowd *riowd_device;
L
Linus Torvalds 已提交
58 59 60 61 62 63 64

#define WDTO_INDEX	0x05

static int riowd_timeout = 1;		/* in minutes */
module_param(riowd_timeout, int, 0);
MODULE_PARM_DESC(riowd_timeout, "Watchdog timeout in minutes");

65
static void riowd_writereg(struct riowd *p, u8 val, int index)
L
Linus Torvalds 已提交
66 67 68
{
	unsigned long flags;

69 70 71 72
	spin_lock_irqsave(&p->lock, flags);
	writeb(index, p->regs + 0);
	writeb(val, p->regs + 1);
	spin_unlock_irqrestore(&p->lock, flags);
L
Linus Torvalds 已提交
73 74 75 76 77 78 79 80 81 82 83 84 85
}

static int riowd_open(struct inode *inode, struct file *filp)
{
	nonseekable_open(inode, filp);
	return 0;
}

static int riowd_release(struct inode *inode, struct file *filp)
{
	return 0;
}

86
static long riowd_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
L
Linus Torvalds 已提交
87
{
88
	static const struct watchdog_info info = {
89 90
		.options		= WDIOF_SETTIMEOUT,
		.firmware_version	= 1,
91
		.identity		= DRIVER_NAME,
L
Linus Torvalds 已提交
92 93
	};
	void __user *argp = (void __user *)arg;
94
	struct riowd *p = riowd_device;
L
Linus Torvalds 已提交
95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110
	unsigned int options;
	int new_margin;

	switch (cmd) {
	case WDIOC_GETSUPPORT:
		if (copy_to_user(argp, &info, sizeof(info)))
			return -EFAULT;
		break;

	case WDIOC_GETSTATUS:
	case WDIOC_GETBOOTSTATUS:
		if (put_user(0, (int __user *)argp))
			return -EFAULT;
		break;

	case WDIOC_KEEPALIVE:
111
		riowd_writereg(p, riowd_timeout, WDTO_INDEX);
L
Linus Torvalds 已提交
112 113 114 115 116 117 118
		break;

	case WDIOC_SETOPTIONS:
		if (copy_from_user(&options, argp, sizeof(options)))
			return -EFAULT;

		if (options & WDIOS_DISABLECARD)
119
			riowd_writereg(p, 0, WDTO_INDEX);
L
Linus Torvalds 已提交
120
		else if (options & WDIOS_ENABLECARD)
121
			riowd_writereg(p, riowd_timeout, WDTO_INDEX);
L
Linus Torvalds 已提交
122 123 124 125 126 127 128 129 130
		else
			return -EINVAL;

		break;

	case WDIOC_SETTIMEOUT:
		if (get_user(new_margin, (int __user *)argp))
			return -EFAULT;
		if ((new_margin < 60) || (new_margin > (255 * 60)))
131
			return -EINVAL;
L
Linus Torvalds 已提交
132
		riowd_timeout = (new_margin + 59) / 60;
133
		riowd_writereg(p, riowd_timeout, WDTO_INDEX);
L
Linus Torvalds 已提交
134 135 136 137 138 139 140 141 142 143 144 145
		/* Fall */

	case WDIOC_GETTIMEOUT:
		return put_user(riowd_timeout * 60, (int __user *)argp);

	default:
		return -EINVAL;
	};

	return 0;
}

146 147
static ssize_t riowd_write(struct file *file, const char __user *buf,
						size_t count, loff_t *ppos)
L
Linus Torvalds 已提交
148
{
149 150
	struct riowd *p = riowd_device;

L
Linus Torvalds 已提交
151
	if (count) {
152
		riowd_writereg(p, riowd_timeout, WDTO_INDEX);
L
Linus Torvalds 已提交
153 154 155 156 157 158
		return 1;
	}

	return 0;
}

159
static const struct file_operations riowd_fops = {
160 161 162 163 164 165
	.owner =		THIS_MODULE,
	.llseek =		no_llseek,
	.unlocked_ioctl =	riowd_ioctl,
	.open =			riowd_open,
	.write =		riowd_write,
	.release =		riowd_release,
L
Linus Torvalds 已提交
166 167
};

168 169 170 171 172
static struct miscdevice riowd_miscdev = {
	.minor	= WATCHDOG_MINOR,
	.name	= "watchdog",
	.fops	= &riowd_fops
};
L
Linus Torvalds 已提交
173

174 175
static int __devinit riowd_probe(struct of_device *op,
				 const struct of_device_id *match)
L
Linus Torvalds 已提交
176
{
177 178
	struct riowd *p;
	int err = -EINVAL;
L
Linus Torvalds 已提交
179

180 181
	if (riowd_device)
		goto out;
L
Linus Torvalds 已提交
182

183 184 185 186
	err = -ENOMEM;
	p = kzalloc(sizeof(*p), GFP_KERNEL);
	if (!p)
		goto out;
L
Linus Torvalds 已提交
187

188
	spin_lock_init(&p->lock);
L
Linus Torvalds 已提交
189

190
	p->regs = of_ioremap(&op->resource[0], 0, 2, DRIVER_NAME);
191 192 193
	if (!p->regs) {
		printk(KERN_ERR PFX "Cannot map registers.\n");
		goto out_free;
L
Linus Torvalds 已提交
194
	}
195 196
	/* Make miscdev useable right away */
	riowd_device = p;
L
Linus Torvalds 已提交
197

198 199 200 201
	err = misc_register(&riowd_miscdev);
	if (err) {
		printk(KERN_ERR PFX "Cannot register watchdog misc device.\n");
		goto out_iounmap;
L
Linus Torvalds 已提交
202 203
	}

204 205
	printk(KERN_INFO PFX "Hardware watchdog [%i minutes], "
	       "regs at %p\n", riowd_timeout, p->regs);
L
Linus Torvalds 已提交
206

207
	dev_set_drvdata(&op->dev, p);
208
	return 0;
L
Linus Torvalds 已提交
209

210
out_iounmap:
211
	riowd_device = NULL;
212
	of_iounmap(&op->resource[0], p->regs, 2);
L
Linus Torvalds 已提交
213

214 215
out_free:
	kfree(p);
L
Linus Torvalds 已提交
216

217 218
out:
	return err;
L
Linus Torvalds 已提交
219 220
}

221
static int __devexit riowd_remove(struct of_device *op)
L
Linus Torvalds 已提交
222
{
223 224
	struct riowd *p = dev_get_drvdata(&op->dev);

L
Linus Torvalds 已提交
225
	misc_deregister(&riowd_miscdev);
226 227 228 229 230 231
	of_iounmap(&op->resource[0], p->regs, 2);
	kfree(p);

	return 0;
}

232
static const struct of_device_id riowd_match[] = {
233 234 235 236 237 238 239 240
	{
		.name = "pmc",
	},
	{},
};
MODULE_DEVICE_TABLE(of, riowd_match);

static struct of_platform_driver riowd_driver = {
241
	.name		= DRIVER_NAME,
242 243 244 245 246 247 248 249 250 251 252 253 254
	.match_table	= riowd_match,
	.probe		= riowd_probe,
	.remove		= __devexit_p(riowd_remove),
};

static int __init riowd_init(void)
{
	return of_register_driver(&riowd_driver, &of_bus_type);
}

static void __exit riowd_exit(void)
{
	of_unregister_driver(&riowd_driver);
L
Linus Torvalds 已提交
255 256 257
}

module_init(riowd_init);
258
module_exit(riowd_exit);