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>
A
Arnd Bergmann 已提交
13
#include <linux/smp_lock.h>
14 15 16
#include <linux/watchdog.h>
#include <linux/of.h>
#include <linux/of_device.h>
L
Linus Torvalds 已提交
17 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 44 45

#include <asm/io.h>
#include <asm/uaccess.h>


/* 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.
 */

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

51 52
#define DRIVER_NAME	"riowd"
#define PFX		DRIVER_NAME ": "
L
Linus Torvalds 已提交
53

54 55 56 57 58 59
struct riowd {
	void __iomem		*regs;
	spinlock_t		lock;
};

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

#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");

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

71 72 73 74
	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 已提交
75 76 77 78
}

static int riowd_open(struct inode *inode, struct file *filp)
{
A
Arnd Bergmann 已提交
79
	cycle_kernel_lock();
L
Linus Torvalds 已提交
80 81 82 83 84 85 86 87 88
	nonseekable_open(inode, filp);
	return 0;
}

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

89
static long riowd_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
L
Linus Torvalds 已提交
90 91
{
	static struct watchdog_info info = {
92 93
		.options		= WDIOF_SETTIMEOUT,
		.firmware_version	= 1,
94
		.identity		= DRIVER_NAME,
L
Linus Torvalds 已提交
95 96
	};
	void __user *argp = (void __user *)arg;
97
	struct riowd *p = riowd_device;
L
Linus Torvalds 已提交
98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113
	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:
114
		riowd_writereg(p, riowd_timeout, WDTO_INDEX);
L
Linus Torvalds 已提交
115 116 117 118 119 120 121
		break;

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

		if (options & WDIOS_DISABLECARD)
122
			riowd_writereg(p, 0, WDTO_INDEX);
L
Linus Torvalds 已提交
123
		else if (options & WDIOS_ENABLECARD)
124
			riowd_writereg(p, riowd_timeout, WDTO_INDEX);
L
Linus Torvalds 已提交
125 126 127 128 129 130 131 132 133
		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)))
134
			return -EINVAL;
L
Linus Torvalds 已提交
135
		riowd_timeout = (new_margin + 59) / 60;
136
		riowd_writereg(p, riowd_timeout, WDTO_INDEX);
L
Linus Torvalds 已提交
137 138 139 140 141 142 143 144 145 146 147 148 149 150
		/* Fall */

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

	default:
		return -EINVAL;
	};

	return 0;
}

static ssize_t riowd_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos)
{
151 152
	struct riowd *p = riowd_device;

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

	return 0;
}

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

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

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

182 183
	if (riowd_device)
		goto out;
L
Linus Torvalds 已提交
184

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

190
	spin_lock_init(&p->lock);
L
Linus Torvalds 已提交
191

192
	p->regs = of_ioremap(&op->resource[0], 0, 2, DRIVER_NAME);
193 194 195
	if (!p->regs) {
		printk(KERN_ERR PFX "Cannot map registers.\n");
		goto out_free;
L
Linus Torvalds 已提交
196 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 208 209
	dev_set_drvdata(&op->dev, p);
	riowd_device = p;
	err = 0;
L
Linus Torvalds 已提交
210

211 212
out_iounmap:
	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);