root/drivers/watchdog/cpu5wdt.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. cpu5wdt_trigger
  2. cpu5wdt_reset
  3. cpu5wdt_start
  4. cpu5wdt_stop
  5. cpu5wdt_open
  6. cpu5wdt_release
  7. cpu5wdt_ioctl
  8. cpu5wdt_write
  9. cpu5wdt_init
  10. cpu5wdt_init_module
  11. cpu5wdt_exit
  12. cpu5wdt_exit_module

   1 // SPDX-License-Identifier: GPL-2.0-or-later
   2 /*
   3  * sma cpu5 watchdog driver
   4  *
   5  * Copyright (C) 2003 Heiko Ronsdorf <hero@ihg.uni-duisburg.de>
   6  */
   7 
   8 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
   9 
  10 #include <linux/module.h>
  11 #include <linux/moduleparam.h>
  12 #include <linux/types.h>
  13 #include <linux/errno.h>
  14 #include <linux/miscdevice.h>
  15 #include <linux/fs.h>
  16 #include <linux/ioport.h>
  17 #include <linux/timer.h>
  18 #include <linux/completion.h>
  19 #include <linux/jiffies.h>
  20 #include <linux/io.h>
  21 #include <linux/uaccess.h>
  22 #include <linux/watchdog.h>
  23 
  24 /* adjustable parameters */
  25 
  26 static int verbose;
  27 static int port = 0x91;
  28 static int ticks = 10000;
  29 static DEFINE_SPINLOCK(cpu5wdt_lock);
  30 
  31 #define PFX                     "cpu5wdt: "
  32 
  33 #define CPU5WDT_EXTENT          0x0A
  34 
  35 #define CPU5WDT_STATUS_REG      0x00
  36 #define CPU5WDT_TIME_A_REG      0x02
  37 #define CPU5WDT_TIME_B_REG      0x03
  38 #define CPU5WDT_MODE_REG        0x04
  39 #define CPU5WDT_TRIGGER_REG     0x07
  40 #define CPU5WDT_ENABLE_REG      0x08
  41 #define CPU5WDT_RESET_REG       0x09
  42 
  43 #define CPU5WDT_INTERVAL        (HZ/10+1)
  44 
  45 /* some device data */
  46 
  47 static struct {
  48         struct completion stop;
  49         int running;
  50         struct timer_list timer;
  51         int queue;
  52         int default_ticks;
  53         unsigned long inuse;
  54 } cpu5wdt_device;
  55 
  56 /* generic helper functions */
  57 
  58 static void cpu5wdt_trigger(struct timer_list *unused)
  59 {
  60         if (verbose > 2)
  61                 pr_debug("trigger at %i ticks\n", ticks);
  62 
  63         if (cpu5wdt_device.running)
  64                 ticks--;
  65 
  66         spin_lock(&cpu5wdt_lock);
  67         /* keep watchdog alive */
  68         outb(1, port + CPU5WDT_TRIGGER_REG);
  69 
  70         /* requeue?? */
  71         if (cpu5wdt_device.queue && ticks)
  72                 mod_timer(&cpu5wdt_device.timer, jiffies + CPU5WDT_INTERVAL);
  73         else {
  74                 /* ticks doesn't matter anyway */
  75                 complete(&cpu5wdt_device.stop);
  76         }
  77         spin_unlock(&cpu5wdt_lock);
  78 
  79 }
  80 
  81 static void cpu5wdt_reset(void)
  82 {
  83         ticks = cpu5wdt_device.default_ticks;
  84 
  85         if (verbose)
  86                 pr_debug("reset (%i ticks)\n", (int) ticks);
  87 
  88 }
  89 
  90 static void cpu5wdt_start(void)
  91 {
  92         unsigned long flags;
  93 
  94         spin_lock_irqsave(&cpu5wdt_lock, flags);
  95         if (!cpu5wdt_device.queue) {
  96                 cpu5wdt_device.queue = 1;
  97                 outb(0, port + CPU5WDT_TIME_A_REG);
  98                 outb(0, port + CPU5WDT_TIME_B_REG);
  99                 outb(1, port + CPU5WDT_MODE_REG);
 100                 outb(0, port + CPU5WDT_RESET_REG);
 101                 outb(0, port + CPU5WDT_ENABLE_REG);
 102                 mod_timer(&cpu5wdt_device.timer, jiffies + CPU5WDT_INTERVAL);
 103         }
 104         /* if process dies, counter is not decremented */
 105         cpu5wdt_device.running++;
 106         spin_unlock_irqrestore(&cpu5wdt_lock, flags);
 107 }
 108 
 109 static int cpu5wdt_stop(void)
 110 {
 111         unsigned long flags;
 112 
 113         spin_lock_irqsave(&cpu5wdt_lock, flags);
 114         if (cpu5wdt_device.running)
 115                 cpu5wdt_device.running = 0;
 116         ticks = cpu5wdt_device.default_ticks;
 117         spin_unlock_irqrestore(&cpu5wdt_lock, flags);
 118         if (verbose)
 119                 pr_crit("stop not possible\n");
 120         return -EIO;
 121 }
 122 
 123 /* filesystem operations */
 124 
 125 static int cpu5wdt_open(struct inode *inode, struct file *file)
 126 {
 127         if (test_and_set_bit(0, &cpu5wdt_device.inuse))
 128                 return -EBUSY;
 129         return stream_open(inode, file);
 130 }
 131 
 132 static int cpu5wdt_release(struct inode *inode, struct file *file)
 133 {
 134         clear_bit(0, &cpu5wdt_device.inuse);
 135         return 0;
 136 }
 137 
 138 static long cpu5wdt_ioctl(struct file *file, unsigned int cmd,
 139                                                 unsigned long arg)
 140 {
 141         void __user *argp = (void __user *)arg;
 142         int __user *p = argp;
 143         unsigned int value;
 144         static const struct watchdog_info ident = {
 145                 .options = WDIOF_CARDRESET,
 146                 .identity = "CPU5 WDT",
 147         };
 148 
 149         switch (cmd) {
 150         case WDIOC_GETSUPPORT:
 151                 if (copy_to_user(argp, &ident, sizeof(ident)))
 152                         return -EFAULT;
 153                 break;
 154         case WDIOC_GETSTATUS:
 155                 value = inb(port + CPU5WDT_STATUS_REG);
 156                 value = (value >> 2) & 1;
 157                 return put_user(value, p);
 158         case WDIOC_GETBOOTSTATUS:
 159                 return put_user(0, p);
 160         case WDIOC_SETOPTIONS:
 161                 if (get_user(value, p))
 162                         return -EFAULT;
 163                 if (value & WDIOS_ENABLECARD)
 164                         cpu5wdt_start();
 165                 if (value & WDIOS_DISABLECARD)
 166                         cpu5wdt_stop();
 167                 break;
 168         case WDIOC_KEEPALIVE:
 169                 cpu5wdt_reset();
 170                 break;
 171         default:
 172                 return -ENOTTY;
 173         }
 174         return 0;
 175 }
 176 
 177 static ssize_t cpu5wdt_write(struct file *file, const char __user *buf,
 178                                                 size_t count, loff_t *ppos)
 179 {
 180         if (!count)
 181                 return -EIO;
 182         cpu5wdt_reset();
 183         return count;
 184 }
 185 
 186 static const struct file_operations cpu5wdt_fops = {
 187         .owner          = THIS_MODULE,
 188         .llseek         = no_llseek,
 189         .unlocked_ioctl = cpu5wdt_ioctl,
 190         .open           = cpu5wdt_open,
 191         .write          = cpu5wdt_write,
 192         .release        = cpu5wdt_release,
 193 };
 194 
 195 static struct miscdevice cpu5wdt_misc = {
 196         .minor  = WATCHDOG_MINOR,
 197         .name   = "watchdog",
 198         .fops   = &cpu5wdt_fops,
 199 };
 200 
 201 /* init/exit function */
 202 
 203 static int cpu5wdt_init(void)
 204 {
 205         unsigned int val;
 206         int err;
 207 
 208         if (verbose)
 209                 pr_debug("port=0x%x, verbose=%i\n", port, verbose);
 210 
 211         init_completion(&cpu5wdt_device.stop);
 212         cpu5wdt_device.queue = 0;
 213         timer_setup(&cpu5wdt_device.timer, cpu5wdt_trigger, 0);
 214         cpu5wdt_device.default_ticks = ticks;
 215 
 216         if (!request_region(port, CPU5WDT_EXTENT, PFX)) {
 217                 pr_err("request_region failed\n");
 218                 err = -EBUSY;
 219                 goto no_port;
 220         }
 221 
 222         /* watchdog reboot? */
 223         val = inb(port + CPU5WDT_STATUS_REG);
 224         val = (val >> 2) & 1;
 225         if (!val)
 226                 pr_info("sorry, was my fault\n");
 227 
 228         err = misc_register(&cpu5wdt_misc);
 229         if (err < 0) {
 230                 pr_err("misc_register failed\n");
 231                 goto no_misc;
 232         }
 233 
 234 
 235         pr_info("init success\n");
 236         return 0;
 237 
 238 no_misc:
 239         release_region(port, CPU5WDT_EXTENT);
 240 no_port:
 241         return err;
 242 }
 243 
 244 static int cpu5wdt_init_module(void)
 245 {
 246         return cpu5wdt_init();
 247 }
 248 
 249 static void cpu5wdt_exit(void)
 250 {
 251         if (cpu5wdt_device.queue) {
 252                 cpu5wdt_device.queue = 0;
 253                 wait_for_completion(&cpu5wdt_device.stop);
 254                 del_timer(&cpu5wdt_device.timer);
 255         }
 256 
 257         misc_deregister(&cpu5wdt_misc);
 258 
 259         release_region(port, CPU5WDT_EXTENT);
 260 
 261 }
 262 
 263 static void cpu5wdt_exit_module(void)
 264 {
 265         cpu5wdt_exit();
 266 }
 267 
 268 /* module entry points */
 269 
 270 module_init(cpu5wdt_init_module);
 271 module_exit(cpu5wdt_exit_module);
 272 
 273 MODULE_AUTHOR("Heiko Ronsdorf <hero@ihg.uni-duisburg.de>");
 274 MODULE_DESCRIPTION("sma cpu5 watchdog driver");
 275 MODULE_SUPPORTED_DEVICE("sma cpu5 watchdog");
 276 MODULE_LICENSE("GPL");
 277 
 278 module_param_hw(port, int, ioport, 0);
 279 MODULE_PARM_DESC(port, "base address of watchdog card, default is 0x91");
 280 
 281 module_param(verbose, int, 0);
 282 MODULE_PARM_DESC(verbose, "be verbose, default is 0 (no)");
 283 
 284 module_param(ticks, int, 0);
 285 MODULE_PARM_DESC(ticks, "count down ticks, default is 10000");

/* [<][>][^][v][top][bottom][index][help] */