root/drivers/watchdog/geodewdt.c

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

DEFINITIONS

This source file includes following definitions.
  1. geodewdt_ping
  2. geodewdt_disable
  3. geodewdt_set_heartbeat
  4. geodewdt_open
  5. geodewdt_release
  6. geodewdt_write
  7. geodewdt_ioctl
  8. geodewdt_probe
  9. geodewdt_remove
  10. geodewdt_shutdown
  11. geodewdt_init
  12. geodewdt_exit

   1 // SPDX-License-Identifier: GPL-2.0-or-later
   2 /* Watchdog timer for machines with the CS5535/CS5536 companion chip
   3  *
   4  * Copyright (C) 2006-2007, Advanced Micro Devices, Inc.
   5  * Copyright (C) 2009  Andres Salomon <dilinger@collabora.co.uk>
   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/miscdevice.h>
  14 #include <linux/watchdog.h>
  15 #include <linux/fs.h>
  16 #include <linux/platform_device.h>
  17 #include <linux/reboot.h>
  18 #include <linux/uaccess.h>
  19 
  20 #include <linux/cs5535.h>
  21 
  22 #define GEODEWDT_HZ 500
  23 #define GEODEWDT_SCALE 6
  24 #define GEODEWDT_MAX_SECONDS 131
  25 
  26 #define WDT_FLAGS_OPEN 1
  27 #define WDT_FLAGS_ORPHAN 2
  28 
  29 #define DRV_NAME "geodewdt"
  30 #define WATCHDOG_NAME "Geode GX/LX WDT"
  31 #define WATCHDOG_TIMEOUT 60
  32 
  33 static int timeout = WATCHDOG_TIMEOUT;
  34 module_param(timeout, int, 0);
  35 MODULE_PARM_DESC(timeout,
  36         "Watchdog timeout in seconds. 1<= timeout <=131, default="
  37                                 __MODULE_STRING(WATCHDOG_TIMEOUT) ".");
  38 
  39 static bool nowayout = WATCHDOG_NOWAYOUT;
  40 module_param(nowayout, bool, 0);
  41 MODULE_PARM_DESC(nowayout,
  42         "Watchdog cannot be stopped once started (default="
  43                                 __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
  44 
  45 static struct platform_device *geodewdt_platform_device;
  46 static unsigned long wdt_flags;
  47 static struct cs5535_mfgpt_timer *wdt_timer;
  48 static int safe_close;
  49 
  50 static void geodewdt_ping(void)
  51 {
  52         /* Stop the counter */
  53         cs5535_mfgpt_write(wdt_timer, MFGPT_REG_SETUP, 0);
  54 
  55         /* Reset the counter */
  56         cs5535_mfgpt_write(wdt_timer, MFGPT_REG_COUNTER, 0);
  57 
  58         /* Enable the counter */
  59         cs5535_mfgpt_write(wdt_timer, MFGPT_REG_SETUP, MFGPT_SETUP_CNTEN);
  60 }
  61 
  62 static void geodewdt_disable(void)
  63 {
  64         cs5535_mfgpt_write(wdt_timer, MFGPT_REG_SETUP, 0);
  65         cs5535_mfgpt_write(wdt_timer, MFGPT_REG_COUNTER, 0);
  66 }
  67 
  68 static int geodewdt_set_heartbeat(int val)
  69 {
  70         if (val < 1 || val > GEODEWDT_MAX_SECONDS)
  71                 return -EINVAL;
  72 
  73         cs5535_mfgpt_write(wdt_timer, MFGPT_REG_SETUP, 0);
  74         cs5535_mfgpt_write(wdt_timer, MFGPT_REG_CMP2, val * GEODEWDT_HZ);
  75         cs5535_mfgpt_write(wdt_timer, MFGPT_REG_COUNTER, 0);
  76         cs5535_mfgpt_write(wdt_timer, MFGPT_REG_SETUP, MFGPT_SETUP_CNTEN);
  77 
  78         timeout = val;
  79         return 0;
  80 }
  81 
  82 static int geodewdt_open(struct inode *inode, struct file *file)
  83 {
  84         if (test_and_set_bit(WDT_FLAGS_OPEN, &wdt_flags))
  85                 return -EBUSY;
  86 
  87         if (!test_and_clear_bit(WDT_FLAGS_ORPHAN, &wdt_flags))
  88                 __module_get(THIS_MODULE);
  89 
  90         geodewdt_ping();
  91         return stream_open(inode, file);
  92 }
  93 
  94 static int geodewdt_release(struct inode *inode, struct file *file)
  95 {
  96         if (safe_close) {
  97                 geodewdt_disable();
  98                 module_put(THIS_MODULE);
  99         } else {
 100                 pr_crit("Unexpected close - watchdog is not stopping\n");
 101                 geodewdt_ping();
 102 
 103                 set_bit(WDT_FLAGS_ORPHAN, &wdt_flags);
 104         }
 105 
 106         clear_bit(WDT_FLAGS_OPEN, &wdt_flags);
 107         safe_close = 0;
 108         return 0;
 109 }
 110 
 111 static ssize_t geodewdt_write(struct file *file, const char __user *data,
 112                                 size_t len, loff_t *ppos)
 113 {
 114         if (len) {
 115                 if (!nowayout) {
 116                         size_t i;
 117                         safe_close = 0;
 118 
 119                         for (i = 0; i != len; i++) {
 120                                 char c;
 121 
 122                                 if (get_user(c, data + i))
 123                                         return -EFAULT;
 124 
 125                                 if (c == 'V')
 126                                         safe_close = 1;
 127                         }
 128                 }
 129 
 130                 geodewdt_ping();
 131         }
 132         return len;
 133 }
 134 
 135 static long geodewdt_ioctl(struct file *file, unsigned int cmd,
 136                                 unsigned long arg)
 137 {
 138         void __user *argp = (void __user *)arg;
 139         int __user *p = argp;
 140         int interval;
 141 
 142         static const struct watchdog_info ident = {
 143                 .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING
 144                 | WDIOF_MAGICCLOSE,
 145                 .firmware_version =     1,
 146                 .identity =             WATCHDOG_NAME,
 147         };
 148 
 149         switch (cmd) {
 150         case WDIOC_GETSUPPORT:
 151                 return copy_to_user(argp, &ident,
 152                                     sizeof(ident)) ? -EFAULT : 0;
 153                 break;
 154 
 155         case WDIOC_GETSTATUS:
 156         case WDIOC_GETBOOTSTATUS:
 157                 return put_user(0, p);
 158 
 159         case WDIOC_SETOPTIONS:
 160         {
 161                 int options, ret = -EINVAL;
 162 
 163                 if (get_user(options, p))
 164                         return -EFAULT;
 165 
 166                 if (options & WDIOS_DISABLECARD) {
 167                         geodewdt_disable();
 168                         ret = 0;
 169                 }
 170 
 171                 if (options & WDIOS_ENABLECARD) {
 172                         geodewdt_ping();
 173                         ret = 0;
 174                 }
 175 
 176                 return ret;
 177         }
 178         case WDIOC_KEEPALIVE:
 179                 geodewdt_ping();
 180                 return 0;
 181 
 182         case WDIOC_SETTIMEOUT:
 183                 if (get_user(interval, p))
 184                         return -EFAULT;
 185 
 186                 if (geodewdt_set_heartbeat(interval))
 187                         return -EINVAL;
 188         /* Fall through */
 189         case WDIOC_GETTIMEOUT:
 190                 return put_user(timeout, p);
 191 
 192         default:
 193                 return -ENOTTY;
 194         }
 195 
 196         return 0;
 197 }
 198 
 199 static const struct file_operations geodewdt_fops = {
 200         .owner          = THIS_MODULE,
 201         .llseek         = no_llseek,
 202         .write          = geodewdt_write,
 203         .unlocked_ioctl = geodewdt_ioctl,
 204         .open           = geodewdt_open,
 205         .release        = geodewdt_release,
 206 };
 207 
 208 static struct miscdevice geodewdt_miscdev = {
 209         .minor = WATCHDOG_MINOR,
 210         .name = "watchdog",
 211         .fops = &geodewdt_fops,
 212 };
 213 
 214 static int __init geodewdt_probe(struct platform_device *dev)
 215 {
 216         int ret;
 217 
 218         wdt_timer = cs5535_mfgpt_alloc_timer(MFGPT_TIMER_ANY, MFGPT_DOMAIN_WORKING);
 219         if (!wdt_timer) {
 220                 pr_err("No timers were available\n");
 221                 return -ENODEV;
 222         }
 223 
 224         /* Set up the timer */
 225 
 226         cs5535_mfgpt_write(wdt_timer, MFGPT_REG_SETUP,
 227                           GEODEWDT_SCALE | (3 << 8));
 228 
 229         /* Set up comparator 2 to reset when the event fires */
 230         cs5535_mfgpt_toggle_event(wdt_timer, MFGPT_CMP2, MFGPT_EVENT_RESET, 1);
 231 
 232         /* Set up the initial timeout */
 233 
 234         cs5535_mfgpt_write(wdt_timer, MFGPT_REG_CMP2,
 235                 timeout * GEODEWDT_HZ);
 236 
 237         ret = misc_register(&geodewdt_miscdev);
 238 
 239         return ret;
 240 }
 241 
 242 static int geodewdt_remove(struct platform_device *dev)
 243 {
 244         misc_deregister(&geodewdt_miscdev);
 245         return 0;
 246 }
 247 
 248 static void geodewdt_shutdown(struct platform_device *dev)
 249 {
 250         geodewdt_disable();
 251 }
 252 
 253 static struct platform_driver geodewdt_driver = {
 254         .remove         = geodewdt_remove,
 255         .shutdown       = geodewdt_shutdown,
 256         .driver         = {
 257                 .name   = DRV_NAME,
 258         },
 259 };
 260 
 261 static int __init geodewdt_init(void)
 262 {
 263         int ret;
 264 
 265         geodewdt_platform_device = platform_device_register_simple(DRV_NAME,
 266                                                                 -1, NULL, 0);
 267         if (IS_ERR(geodewdt_platform_device))
 268                 return PTR_ERR(geodewdt_platform_device);
 269 
 270         ret = platform_driver_probe(&geodewdt_driver, geodewdt_probe);
 271         if (ret)
 272                 goto err;
 273 
 274         return 0;
 275 err:
 276         platform_device_unregister(geodewdt_platform_device);
 277         return ret;
 278 }
 279 
 280 static void __exit geodewdt_exit(void)
 281 {
 282         platform_device_unregister(geodewdt_platform_device);
 283         platform_driver_unregister(&geodewdt_driver);
 284 }
 285 
 286 module_init(geodewdt_init);
 287 module_exit(geodewdt_exit);
 288 
 289 MODULE_AUTHOR("Advanced Micro Devices, Inc");
 290 MODULE_DESCRIPTION("Geode GX/LX Watchdog Driver");
 291 MODULE_LICENSE("GPL");

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