* timer interrupts. We use a timer to periodically
* reset 'stopped' watchdogs on affected platforms.
*
- * TODO: DevFS support (/dev/watchdogs/0 ... /dev/watchdogs/2)
- *
* Copyright (c) 2000 Eric Brower (ebrower@usa.net)
*/
#include <linux/interrupt.h>
#include <linux/ioport.h>
#include <linux/timer.h>
+#include <linux/smp_lock.h>
#include <asm/irq.h>
#include <asm/ebus.h>
#include <asm/oplib.h>
* UNKNOWN, MAGICAL MYSTERY REGISTER
*
*/
-struct wd_timer_regblk {
- volatile __u16 dcntr; /* down counter - hw */
- volatile __u16 dcntr_pad;
- volatile __u16 limit; /* limit register - hw */
- volatile __u16 limit_pad;
- volatile __u8 status; /* status register - b */
- volatile __u8 status_pad;
- volatile __u16 status_pad2;
- volatile __u32 pad32; /* yet more padding */
-};
+#define WD_TIMER_REGSZ 16
+#define WD0_OFF 0
+#define WD1_OFF (WD_TIMER_REGSZ * 1)
+#define WD2_OFF (WD_TIMER_REGSZ * 2)
+#define PLD_OFF (WD_TIMER_REGSZ * 3)
-struct wd_pld_regblk {
- volatile __u8 intr_mask; /* interrupt mask - b */
- volatile __u8 intr_mask_pad;
- volatile __u16 intr_mask_pad2;
- volatile __u8 status; /* device status - b */
- volatile __u8 status_pad;
- volatile __u16 status_pad2;
-};
+#define WD_DCNTR 0x00
+#define WD_LIMIT 0x04
+#define WD_STATUS 0x08
-struct wd_regblk {
- volatile struct wd_timer_regblk wd0_regs;
- volatile struct wd_timer_regblk wd1_regs;
- volatile struct wd_timer_regblk wd2_regs;
- volatile struct wd_pld_regblk pld_regs;
-};
+#define PLD_IMASK (PLD_OFF + 0x00)
+#define PLD_STATUS (PLD_OFF + 0x04)
/* Individual timer structure
*/
struct wd_timer {
__u16 timeout;
__u8 intr_mask;
- unsigned char runstatus;
- volatile struct wd_timer_regblk* regs;
+ unsigned char runstatus;
+ void __iomem *regs;
};
/* Device structure
unsigned short opt_timeout;
unsigned char initialized;
struct wd_timer watchdog[WD_NUMDEVS];
- volatile struct wd_regblk* regs;
+ void __iomem *regs;
};
static struct wd_device wd_dev = {
static int wd2_timeout = 0;
#ifdef MODULE
-MODULE_PARM (wd0_timeout, "i");
+module_param (wd0_timeout, int, 0);
MODULE_PARM_DESC(wd0_timeout, "Default watchdog0 timeout in 1/10secs");
-MODULE_PARM (wd1_timeout, "i");
+module_param (wd1_timeout, int, 0);
MODULE_PARM_DESC(wd1_timeout, "Default watchdog1 timeout in 1/10secs");
-MODULE_PARM (wd2_timeout, "i");
+module_param (wd2_timeout, int, 0);
MODULE_PARM_DESC(wd2_timeout, "Default watchdog2 timeout in 1/10secs");
MODULE_AUTHOR
#ifdef WD_DEBUG
static void wd_dumpregs(void);
#endif
-static irqreturn_t wd_interrupt(int irq, void *dev_id, struct pt_regs *regs);
+static irqreturn_t wd_interrupt(int irq, void *dev_id);
static void wd_toggleintr(struct wd_timer* pTimer, int enable);
static void wd_pingtimer(struct wd_timer* pTimer);
static void wd_starttimer(struct wd_timer* pTimer);
{
if (request_irq(wd_dev.irq,
&wd_interrupt,
- SA_SHIRQ,
+ IRQF_SHARED,
WD_OBPNAME,
(void *)wd_dev.regs)) {
- printk("%s: Cannot register IRQ %s\n",
- WD_OBPNAME, __irq_itoa(wd_dev.irq));
+ printk("%s: Cannot register IRQ %d\n",
+ WD_OBPNAME, wd_dev.irq);
return(-EBUSY);
}
wd_dev.initialized = 1;
}
- return(0);
+ return(nonseekable_open(inode, f));
}
static int wd_release(struct inode *inode, struct file *file)
{
int setopt = 0;
struct wd_timer* pTimer = (struct wd_timer*)file->private_data;
+ void __user *argp = (void __user *)arg;
struct watchdog_info info = {
0,
0,
{
/* Generic Linux IOCTLs */
case WDIOC_GETSUPPORT:
- if(copy_to_user((struct watchdog_info *)arg,
- (struct watchdog_info *)&info,
- sizeof(struct watchdog_info))) {
+ if(copy_to_user(argp, &info, sizeof(struct watchdog_info))) {
return(-EFAULT);
}
break;
case WDIOC_GETSTATUS:
case WDIOC_GETBOOTSTATUS:
- if (put_user(0, (int *) arg))
+ if (put_user(0, (int __user *)argp))
return -EFAULT;
break;
case WDIOC_KEEPALIVE:
wd_pingtimer(pTimer);
break;
case WDIOC_SETOPTIONS:
- if(copy_from_user(&setopt, (void*) arg, sizeof(unsigned int))) {
+ if(copy_from_user(&setopt, argp, sizeof(unsigned int))) {
return -EFAULT;
}
if(setopt & WDIOS_DISABLECARD) {
/* Solaris-compatible IOCTLs */
case WIOCGSTAT:
setopt = wd_getstatus(pTimer);
- if(copy_to_user((void*)arg, &setopt, sizeof(unsigned int))) {
+ if(copy_to_user(argp, &setopt, sizeof(unsigned int))) {
return(-EFAULT);
}
break;
return(0);
}
-static ssize_t wd_write( struct file *file,
- const char *buf,
- size_t count,
- loff_t *ppos)
+static long wd_compat_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ int rval = -ENOIOCTLCMD;
+
+ switch (cmd) {
+ /* solaris ioctls are specific to this driver */
+ case WIOCSTART:
+ case WIOCSTOP:
+ case WIOCGSTAT:
+ lock_kernel();
+ rval = wd_ioctl(file->f_path.dentry->d_inode, file, cmd, arg);
+ unlock_kernel();
+ break;
+ /* everything else is handled by the generic compat layer */
+ default:
+ break;
+ }
+
+ return rval;
+}
+
+static ssize_t wd_write(struct file *file,
+ const char __user *buf,
+ size_t count,
+ loff_t *ppos)
{
struct wd_timer* pTimer = (struct wd_timer*)file->private_data;
return(-EINVAL);
}
- if (ppos != &file->f_pos)
- return -ESPIPE;
-
if (count) {
wd_pingtimer(pTimer);
return 1;
return 0;
}
-static ssize_t wd_read(struct file * file, char * buffer,
+static ssize_t wd_read(struct file * file, char __user *buffer,
size_t count, loff_t *ppos)
{
#ifdef WD_DEBUG
#endif /* ifdef WD_DEBUG */
}
-static irqreturn_t wd_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+static irqreturn_t wd_interrupt(int irq, void *dev_id)
{
/* Only WD0 will interrupt-- others are NMI and we won't
* see them here....
static struct file_operations wd_fops = {
.owner = THIS_MODULE,
.ioctl = wd_ioctl,
+ .compat_ioctl = wd_compat_ioctl,
.open = wd_open,
.write = wd_write,
.read = wd_read,
i,
wd_getstatus(&wd_dev.watchdog[i]));
}
- printk("\tintr_mask at 0x%lx: 0x%x\n",
- (unsigned long)(&wd_dev.regs->pld_regs.intr_mask),
- readb(&wd_dev.regs->pld_regs.intr_mask));
- printk("\tpld_status at 0x%lx: 0x%x\n",
- (unsigned long)(&wd_dev.regs->pld_regs.status),
- readb(&wd_dev.regs->pld_regs.status));
+ printk("\tintr_mask at %p: 0x%x\n",
+ wd_dev.regs + PLD_IMASK,
+ readb(wd_dev.regs + PLD_IMASK));
+ printk("\tpld_status at %p: 0x%x\n",
+ wd_dev.regs + PLD_STATUS,
+ readb(wd_dev.regs + PLD_STATUS));
}
#endif
*/
static void wd_toggleintr(struct wd_timer* pTimer, int enable)
{
- unsigned char curregs = wd_readb(&wd_dev.regs->pld_regs.intr_mask);
+ unsigned char curregs = wd_readb(wd_dev.regs + PLD_IMASK);
unsigned char setregs =
(NULL == pTimer) ?
(WD0_INTR_MASK | WD1_INTR_MASK | WD2_INTR_MASK) :
(curregs &= ~setregs):
(curregs |= setregs);
- wd_writeb(curregs, &wd_dev.regs->pld_regs.intr_mask);
+ wd_writeb(curregs, wd_dev.regs + PLD_IMASK);
return;
}
*/
static void wd_pingtimer(struct wd_timer* pTimer)
{
- if(wd_readb(&pTimer->regs->status) & WD_S_RUNNING) {
- wd_readw(&pTimer->regs->dcntr);
+ if (wd_readb(pTimer->regs + WD_STATUS) & WD_S_RUNNING) {
+ wd_readw(pTimer->regs + WD_DCNTR);
}
}
*/
static void wd_stoptimer(struct wd_timer* pTimer)
{
- if(wd_readb(&pTimer->regs->status) & WD_S_RUNNING) {
+ if(wd_readb(pTimer->regs + WD_STATUS) & WD_S_RUNNING) {
wd_toggleintr(pTimer, WD_INTR_OFF);
if(wd_dev.isbaddoggie) {
}
pTimer->runstatus &= ~WD_STAT_SVCD;
- wd_writew(pTimer->timeout, &pTimer->regs->limit);
+ wd_writew(pTimer->timeout, pTimer->regs + WD_LIMIT);
wd_toggleintr(pTimer, WD_INTR_ON);
}
static void wd_resetbrokentimer(struct wd_timer* pTimer)
{
wd_toggleintr(pTimer, WD_INTR_ON);
- wd_writew(WD_BLIMIT, &pTimer->regs->limit);
+ wd_writew(WD_BLIMIT, pTimer->regs + WD_LIMIT);
}
/* Timer device initialization helper.
static int wd_inittimer(int whichdog)
{
struct miscdevice *whichmisc;
- volatile struct wd_timer_regblk *whichregs;
+ void __iomem *whichregs;
char whichident[8];
int whichmask;
__u16 whichlimit;
case WD0_ID:
whichmisc = &wd0_miscdev;
strcpy(whichident, "RIC");
- whichregs = &wd_dev.regs->wd0_regs;
+ whichregs = wd_dev.regs + WD0_OFF;
whichmask = WD0_INTR_MASK;
whichlimit= (0 == wd0_timeout) ?
(wd_dev.opt_timeout):
case WD1_ID:
whichmisc = &wd1_miscdev;
strcpy(whichident, "XIR");
- whichregs = &wd_dev.regs->wd1_regs;
+ whichregs = wd_dev.regs + WD1_OFF;
whichmask = WD1_INTR_MASK;
whichlimit= (0 == wd1_timeout) ?
(wd_dev.opt_timeout):
case WD2_ID:
whichmisc = &wd2_miscdev;
strcpy(whichident, "POR");
- whichregs = &wd_dev.regs->wd2_regs;
+ whichregs = wd_dev.regs + WD2_OFF;
whichmask = WD2_INTR_MASK;
whichlimit= (0 == wd2_timeout) ?
(wd_dev.opt_timeout):
static int wd_getstatus(struct wd_timer* pTimer)
{
- unsigned char stat = wd_readb(&pTimer->regs->status);
- unsigned char intr = wd_readb(&wd_dev.regs->pld_regs.intr_mask);
+ unsigned char stat = wd_readb(pTimer->regs + WD_STATUS);
+ unsigned char intr = wd_readb(wd_dev.regs + PLD_IMASK);
unsigned char ret = WD_STOPPED;
/* determine STOPPED */
for_each_ebus(ebus) {
for_each_ebusdev(edev, ebus) {
- if (!strcmp(edev->prom_name, WD_OBPNAME))
+ if (!strcmp(edev->ofdev.node->name, WD_OBPNAME))
goto ebus_done;
}
}
}
wd_dev.regs =
- ioremap(edev->resource[0].start, sizeof(struct wd_regblk));
+ ioremap(edev->resource[0].start, 4 * WD_TIMER_REGSZ); /* ? */
if(NULL == wd_dev.regs) {
printk("%s: unable to map registers\n", WD_OBPNAME);
* also now eventually trip.
*/
for(id = WD0_ID; id < WD_NUMDEVS; ++id) {
- if(WD_S_RUNNING == wd_readb(&wd_dev.watchdog[id].regs->status)) {
+ if(WD_S_RUNNING == wd_readb(wd_dev.watchdog[id].regs + WD_STATUS)) {
if(wd_dev.opt_enable) {
printk(KERN_WARNING "%s%i: timer not stopped at release\n",
WD_OBPNAME, id);
"%s%i: defect workaround disabled at release, "\
"timer expires in ~%01i sec\n",
WD_OBPNAME, id,
- wd_readw(&wd_dev.watchdog[id].regs->limit) / 10);
+ wd_readw(wd_dev.watchdog[id].regs + WD_LIMIT) / 10);
}
}
}