0001
0002
0003
0004
0005
0006
0007
0008 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
0009
0010 #include <linux/module.h>
0011 #include <linux/moduleparam.h>
0012 #include <linux/types.h>
0013 #include <linux/errno.h>
0014 #include <linux/miscdevice.h>
0015 #include <linux/fs.h>
0016 #include <linux/ioport.h>
0017 #include <linux/timer.h>
0018 #include <linux/completion.h>
0019 #include <linux/jiffies.h>
0020 #include <linux/io.h>
0021 #include <linux/uaccess.h>
0022 #include <linux/watchdog.h>
0023
0024
0025
0026 static int verbose;
0027 static int port = 0x91;
0028 static int ticks = 10000;
0029 static DEFINE_SPINLOCK(cpu5wdt_lock);
0030
0031 #define PFX "cpu5wdt: "
0032
0033 #define CPU5WDT_EXTENT 0x0A
0034
0035 #define CPU5WDT_STATUS_REG 0x00
0036 #define CPU5WDT_TIME_A_REG 0x02
0037 #define CPU5WDT_TIME_B_REG 0x03
0038 #define CPU5WDT_MODE_REG 0x04
0039 #define CPU5WDT_TRIGGER_REG 0x07
0040 #define CPU5WDT_ENABLE_REG 0x08
0041 #define CPU5WDT_RESET_REG 0x09
0042
0043 #define CPU5WDT_INTERVAL (HZ/10+1)
0044
0045
0046
0047 static struct {
0048 struct completion stop;
0049 int running;
0050 struct timer_list timer;
0051 int queue;
0052 int default_ticks;
0053 unsigned long inuse;
0054 } cpu5wdt_device;
0055
0056
0057
0058 static void cpu5wdt_trigger(struct timer_list *unused)
0059 {
0060 if (verbose > 2)
0061 pr_debug("trigger at %i ticks\n", ticks);
0062
0063 if (cpu5wdt_device.running)
0064 ticks--;
0065
0066 spin_lock(&cpu5wdt_lock);
0067
0068 outb(1, port + CPU5WDT_TRIGGER_REG);
0069
0070
0071 if (cpu5wdt_device.queue && ticks)
0072 mod_timer(&cpu5wdt_device.timer, jiffies + CPU5WDT_INTERVAL);
0073 else {
0074
0075 complete(&cpu5wdt_device.stop);
0076 }
0077 spin_unlock(&cpu5wdt_lock);
0078
0079 }
0080
0081 static void cpu5wdt_reset(void)
0082 {
0083 ticks = cpu5wdt_device.default_ticks;
0084
0085 if (verbose)
0086 pr_debug("reset (%i ticks)\n", (int) ticks);
0087
0088 }
0089
0090 static void cpu5wdt_start(void)
0091 {
0092 unsigned long flags;
0093
0094 spin_lock_irqsave(&cpu5wdt_lock, flags);
0095 if (!cpu5wdt_device.queue) {
0096 cpu5wdt_device.queue = 1;
0097 outb(0, port + CPU5WDT_TIME_A_REG);
0098 outb(0, port + CPU5WDT_TIME_B_REG);
0099 outb(1, port + CPU5WDT_MODE_REG);
0100 outb(0, port + CPU5WDT_RESET_REG);
0101 outb(0, port + CPU5WDT_ENABLE_REG);
0102 mod_timer(&cpu5wdt_device.timer, jiffies + CPU5WDT_INTERVAL);
0103 }
0104
0105 cpu5wdt_device.running++;
0106 spin_unlock_irqrestore(&cpu5wdt_lock, flags);
0107 }
0108
0109 static int cpu5wdt_stop(void)
0110 {
0111 unsigned long flags;
0112
0113 spin_lock_irqsave(&cpu5wdt_lock, flags);
0114 if (cpu5wdt_device.running)
0115 cpu5wdt_device.running = 0;
0116 ticks = cpu5wdt_device.default_ticks;
0117 spin_unlock_irqrestore(&cpu5wdt_lock, flags);
0118 if (verbose)
0119 pr_crit("stop not possible\n");
0120 return -EIO;
0121 }
0122
0123
0124
0125 static int cpu5wdt_open(struct inode *inode, struct file *file)
0126 {
0127 if (test_and_set_bit(0, &cpu5wdt_device.inuse))
0128 return -EBUSY;
0129 return stream_open(inode, file);
0130 }
0131
0132 static int cpu5wdt_release(struct inode *inode, struct file *file)
0133 {
0134 clear_bit(0, &cpu5wdt_device.inuse);
0135 return 0;
0136 }
0137
0138 static long cpu5wdt_ioctl(struct file *file, unsigned int cmd,
0139 unsigned long arg)
0140 {
0141 void __user *argp = (void __user *)arg;
0142 int __user *p = argp;
0143 unsigned int value;
0144 static const struct watchdog_info ident = {
0145 .options = WDIOF_CARDRESET,
0146 .identity = "CPU5 WDT",
0147 };
0148
0149 switch (cmd) {
0150 case WDIOC_GETSUPPORT:
0151 if (copy_to_user(argp, &ident, sizeof(ident)))
0152 return -EFAULT;
0153 break;
0154 case WDIOC_GETSTATUS:
0155 value = inb(port + CPU5WDT_STATUS_REG);
0156 value = (value >> 2) & 1;
0157 return put_user(value, p);
0158 case WDIOC_GETBOOTSTATUS:
0159 return put_user(0, p);
0160 case WDIOC_SETOPTIONS:
0161 if (get_user(value, p))
0162 return -EFAULT;
0163 if (value & WDIOS_ENABLECARD)
0164 cpu5wdt_start();
0165 if (value & WDIOS_DISABLECARD)
0166 cpu5wdt_stop();
0167 break;
0168 case WDIOC_KEEPALIVE:
0169 cpu5wdt_reset();
0170 break;
0171 default:
0172 return -ENOTTY;
0173 }
0174 return 0;
0175 }
0176
0177 static ssize_t cpu5wdt_write(struct file *file, const char __user *buf,
0178 size_t count, loff_t *ppos)
0179 {
0180 if (!count)
0181 return -EIO;
0182 cpu5wdt_reset();
0183 return count;
0184 }
0185
0186 static const struct file_operations cpu5wdt_fops = {
0187 .owner = THIS_MODULE,
0188 .llseek = no_llseek,
0189 .unlocked_ioctl = cpu5wdt_ioctl,
0190 .compat_ioctl = compat_ptr_ioctl,
0191 .open = cpu5wdt_open,
0192 .write = cpu5wdt_write,
0193 .release = cpu5wdt_release,
0194 };
0195
0196 static struct miscdevice cpu5wdt_misc = {
0197 .minor = WATCHDOG_MINOR,
0198 .name = "watchdog",
0199 .fops = &cpu5wdt_fops,
0200 };
0201
0202
0203
0204 static int cpu5wdt_init(void)
0205 {
0206 unsigned int val;
0207 int err;
0208
0209 if (verbose)
0210 pr_debug("port=0x%x, verbose=%i\n", port, verbose);
0211
0212 init_completion(&cpu5wdt_device.stop);
0213 cpu5wdt_device.queue = 0;
0214 timer_setup(&cpu5wdt_device.timer, cpu5wdt_trigger, 0);
0215 cpu5wdt_device.default_ticks = ticks;
0216
0217 if (!request_region(port, CPU5WDT_EXTENT, PFX)) {
0218 pr_err("request_region failed\n");
0219 err = -EBUSY;
0220 goto no_port;
0221 }
0222
0223
0224 val = inb(port + CPU5WDT_STATUS_REG);
0225 val = (val >> 2) & 1;
0226 if (!val)
0227 pr_info("sorry, was my fault\n");
0228
0229 err = misc_register(&cpu5wdt_misc);
0230 if (err < 0) {
0231 pr_err("misc_register failed\n");
0232 goto no_misc;
0233 }
0234
0235
0236 pr_info("init success\n");
0237 return 0;
0238
0239 no_misc:
0240 release_region(port, CPU5WDT_EXTENT);
0241 no_port:
0242 return err;
0243 }
0244
0245 static int cpu5wdt_init_module(void)
0246 {
0247 return cpu5wdt_init();
0248 }
0249
0250 static void cpu5wdt_exit(void)
0251 {
0252 if (cpu5wdt_device.queue) {
0253 cpu5wdt_device.queue = 0;
0254 wait_for_completion(&cpu5wdt_device.stop);
0255 del_timer(&cpu5wdt_device.timer);
0256 }
0257
0258 misc_deregister(&cpu5wdt_misc);
0259
0260 release_region(port, CPU5WDT_EXTENT);
0261
0262 }
0263
0264 static void cpu5wdt_exit_module(void)
0265 {
0266 cpu5wdt_exit();
0267 }
0268
0269
0270
0271 module_init(cpu5wdt_init_module);
0272 module_exit(cpu5wdt_exit_module);
0273
0274 MODULE_AUTHOR("Heiko Ronsdorf <hero@ihg.uni-duisburg.de>");
0275 MODULE_DESCRIPTION("sma cpu5 watchdog driver");
0276 MODULE_LICENSE("GPL");
0277
0278 module_param_hw(port, int, ioport, 0);
0279 MODULE_PARM_DESC(port, "base address of watchdog card, default is 0x91");
0280
0281 module_param(verbose, int, 0);
0282 MODULE_PARM_DESC(verbose, "be verbose, default is 0 (no)");
0283
0284 module_param(ticks, int, 0);
0285 MODULE_PARM_DESC(ticks, "count down ticks, default is 10000");