root/drivers/watchdog/pnx833x_wdt.c

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

DEFINITIONS

This source file includes following definitions.
  1. pnx833x_wdt_start
  2. pnx833x_wdt_stop
  3. pnx833x_wdt_ping
  4. pnx833x_wdt_open
  5. pnx833x_wdt_release
  6. pnx833x_wdt_write
  7. pnx833x_wdt_ioctl
  8. pnx833x_wdt_notify_sys
  9. watchdog_init
  10. watchdog_exit

   1 // SPDX-License-Identifier: GPL-2.0-or-later
   2 /*
   3  *  PNX833x Hardware Watchdog Driver
   4  *  Copyright 2008 NXP Semiconductors
   5  *  Daniel Laird <daniel.j.laird@nxp.com>
   6  *  Andre McCurdy <andre.mccurdy@nxp.com>
   7  *
   8  *  Heavily based upon - IndyDog        0.3
   9  *  A Hardware Watchdog Device for SGI IP22
  10  *
  11  * (c) Copyright 2002 Guido Guenther <agx@sigxcpu.org>, All Rights Reserved.
  12  *
  13  * based on softdog.c by Alan Cox <alan@redhat.com>
  14  */
  15 
  16 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
  17 
  18 #include <linux/module.h>
  19 #include <linux/moduleparam.h>
  20 #include <linux/types.h>
  21 #include <linux/kernel.h>
  22 #include <linux/fs.h>
  23 #include <linux/mm.h>
  24 #include <linux/miscdevice.h>
  25 #include <linux/watchdog.h>
  26 #include <linux/notifier.h>
  27 #include <linux/reboot.h>
  28 #include <linux/init.h>
  29 #include <asm/mach-pnx833x/pnx833x.h>
  30 
  31 #define WATCHDOG_TIMEOUT 30             /* 30 sec Maximum timeout */
  32 #define WATCHDOG_COUNT_FREQUENCY 68000000U /* Watchdog counts at 68MHZ. */
  33 #define PNX_WATCHDOG_TIMEOUT    (WATCHDOG_TIMEOUT * WATCHDOG_COUNT_FREQUENCY)
  34 #define PNX_TIMEOUT_VALUE       2040000000U
  35 
  36 /** CONFIG block */
  37 #define PNX833X_CONFIG                      (0x07000U)
  38 #define PNX833X_CONFIG_CPU_WATCHDOG         (0x54)
  39 #define PNX833X_CONFIG_CPU_WATCHDOG_COMPARE (0x58)
  40 #define PNX833X_CONFIG_CPU_COUNTERS_CONTROL (0x1c)
  41 
  42 /** RESET block */
  43 #define PNX833X_RESET                       (0x08000U)
  44 #define PNX833X_RESET_CONFIG                (0x08)
  45 
  46 static int pnx833x_wdt_alive;
  47 
  48 /* Set default timeout in MHZ.*/
  49 static int pnx833x_wdt_timeout = PNX_WATCHDOG_TIMEOUT;
  50 module_param(pnx833x_wdt_timeout, int, 0);
  51 MODULE_PARM_DESC(timeout, "Watchdog timeout in Mhz. (68Mhz clock), default="
  52                         __MODULE_STRING(PNX_TIMEOUT_VALUE) "(30 seconds).");
  53 
  54 static bool nowayout = WATCHDOG_NOWAYOUT;
  55 module_param(nowayout, bool, 0);
  56 MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default="
  57                                         __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
  58 
  59 #define START_DEFAULT   1
  60 static int start_enabled = START_DEFAULT;
  61 module_param(start_enabled, int, 0);
  62 MODULE_PARM_DESC(start_enabled, "Watchdog is started on module insertion "
  63                                 "(default=" __MODULE_STRING(START_DEFAULT) ")");
  64 
  65 static void pnx833x_wdt_start(void)
  66 {
  67         /* Enable watchdog causing reset. */
  68         PNX833X_REG(PNX833X_RESET + PNX833X_RESET_CONFIG) |= 0x1;
  69         /* Set timeout.*/
  70         PNX833X_REG(PNX833X_CONFIG +
  71                 PNX833X_CONFIG_CPU_WATCHDOG_COMPARE) = pnx833x_wdt_timeout;
  72         /* Enable watchdog. */
  73         PNX833X_REG(PNX833X_CONFIG +
  74                                 PNX833X_CONFIG_CPU_COUNTERS_CONTROL) |= 0x1;
  75 
  76         pr_info("Started watchdog timer\n");
  77 }
  78 
  79 static void pnx833x_wdt_stop(void)
  80 {
  81         /* Disable watchdog causing reset. */
  82         PNX833X_REG(PNX833X_RESET + PNX833X_CONFIG) &= 0xFFFFFFFE;
  83         /* Disable watchdog.*/
  84         PNX833X_REG(PNX833X_CONFIG +
  85                         PNX833X_CONFIG_CPU_COUNTERS_CONTROL) &= 0xFFFFFFFE;
  86 
  87         pr_info("Stopped watchdog timer\n");
  88 }
  89 
  90 static void pnx833x_wdt_ping(void)
  91 {
  92         PNX833X_REG(PNX833X_CONFIG +
  93                 PNX833X_CONFIG_CPU_WATCHDOG_COMPARE) = pnx833x_wdt_timeout;
  94 }
  95 
  96 /*
  97  *      Allow only one person to hold it open
  98  */
  99 static int pnx833x_wdt_open(struct inode *inode, struct file *file)
 100 {
 101         if (test_and_set_bit(0, &pnx833x_wdt_alive))
 102                 return -EBUSY;
 103 
 104         if (nowayout)
 105                 __module_get(THIS_MODULE);
 106 
 107         /* Activate timer */
 108         if (!start_enabled)
 109                 pnx833x_wdt_start();
 110 
 111         pnx833x_wdt_ping();
 112 
 113         pr_info("Started watchdog timer\n");
 114 
 115         return stream_open(inode, file);
 116 }
 117 
 118 static int pnx833x_wdt_release(struct inode *inode, struct file *file)
 119 {
 120         /* Shut off the timer.
 121          * Lock it in if it's a module and we defined ...NOWAYOUT */
 122         if (!nowayout)
 123                 pnx833x_wdt_stop(); /* Turn the WDT off */
 124 
 125         clear_bit(0, &pnx833x_wdt_alive);
 126         return 0;
 127 }
 128 
 129 static ssize_t pnx833x_wdt_write(struct file *file, const char *data, size_t len, loff_t *ppos)
 130 {
 131         /* Refresh the timer. */
 132         if (len)
 133                 pnx833x_wdt_ping();
 134 
 135         return len;
 136 }
 137 
 138 static long pnx833x_wdt_ioctl(struct file *file, unsigned int cmd,
 139                                                         unsigned long arg)
 140 {
 141         int options, new_timeout = 0;
 142         uint32_t timeout, timeout_left = 0;
 143 
 144         static const struct watchdog_info ident = {
 145                 .options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT,
 146                 .firmware_version = 0,
 147                 .identity = "Hardware Watchdog for PNX833x",
 148         };
 149 
 150         switch (cmd) {
 151         default:
 152                 return -ENOTTY;
 153 
 154         case WDIOC_GETSUPPORT:
 155                 if (copy_to_user((struct watchdog_info *)arg,
 156                                  &ident, sizeof(ident)))
 157                         return -EFAULT;
 158                 return 0;
 159 
 160         case WDIOC_GETSTATUS:
 161         case WDIOC_GETBOOTSTATUS:
 162                 return put_user(0, (int *)arg);
 163 
 164         case WDIOC_SETOPTIONS:
 165                 if (get_user(options, (int *)arg))
 166                         return -EFAULT;
 167 
 168                 if (options & WDIOS_DISABLECARD)
 169                         pnx833x_wdt_stop();
 170 
 171                 if (options & WDIOS_ENABLECARD)
 172                         pnx833x_wdt_start();
 173 
 174                 return 0;
 175 
 176         case WDIOC_KEEPALIVE:
 177                 pnx833x_wdt_ping();
 178                 return 0;
 179 
 180         case WDIOC_SETTIMEOUT:
 181         {
 182                 if (get_user(new_timeout, (int *)arg))
 183                         return -EFAULT;
 184 
 185                 pnx833x_wdt_timeout = new_timeout;
 186                 PNX833X_REG(PNX833X_CONFIG +
 187                         PNX833X_CONFIG_CPU_WATCHDOG_COMPARE) = new_timeout;
 188                 return put_user(new_timeout, (int *)arg);
 189         }
 190 
 191         case WDIOC_GETTIMEOUT:
 192                 timeout = PNX833X_REG(PNX833X_CONFIG +
 193                                         PNX833X_CONFIG_CPU_WATCHDOG_COMPARE);
 194                 return put_user(timeout, (int *)arg);
 195 
 196         case WDIOC_GETTIMELEFT:
 197                 timeout_left = PNX833X_REG(PNX833X_CONFIG +
 198                                                 PNX833X_CONFIG_CPU_WATCHDOG);
 199                 return put_user(timeout_left, (int *)arg);
 200 
 201         }
 202 }
 203 
 204 static int pnx833x_wdt_notify_sys(struct notifier_block *this,
 205                                         unsigned long code, void *unused)
 206 {
 207         if (code == SYS_DOWN || code == SYS_HALT)
 208                 pnx833x_wdt_stop(); /* Turn the WDT off */
 209 
 210         return NOTIFY_DONE;
 211 }
 212 
 213 static const struct file_operations pnx833x_wdt_fops = {
 214         .owner          = THIS_MODULE,
 215         .llseek         = no_llseek,
 216         .write          = pnx833x_wdt_write,
 217         .unlocked_ioctl = pnx833x_wdt_ioctl,
 218         .open           = pnx833x_wdt_open,
 219         .release        = pnx833x_wdt_release,
 220 };
 221 
 222 static struct miscdevice pnx833x_wdt_miscdev = {
 223         .minor          = WATCHDOG_MINOR,
 224         .name           = "watchdog",
 225         .fops           = &pnx833x_wdt_fops,
 226 };
 227 
 228 static struct notifier_block pnx833x_wdt_notifier = {
 229         .notifier_call = pnx833x_wdt_notify_sys,
 230 };
 231 
 232 static int __init watchdog_init(void)
 233 {
 234         int ret, cause;
 235 
 236         /* Lets check the reason for the reset.*/
 237         cause = PNX833X_REG(PNX833X_RESET);
 238         /*If bit 31 is set then watchdog was cause of reset.*/
 239         if (cause & 0x80000000) {
 240                 pr_info("The system was previously reset due to the watchdog firing - please investigate...\n");
 241         }
 242 
 243         ret = register_reboot_notifier(&pnx833x_wdt_notifier);
 244         if (ret) {
 245                 pr_err("cannot register reboot notifier (err=%d)\n", ret);
 246                 return ret;
 247         }
 248 
 249         ret = misc_register(&pnx833x_wdt_miscdev);
 250         if (ret) {
 251                 pr_err("cannot register miscdev on minor=%d (err=%d)\n",
 252                        WATCHDOG_MINOR, ret);
 253                 unregister_reboot_notifier(&pnx833x_wdt_notifier);
 254                 return ret;
 255         }
 256 
 257         pr_info("Hardware Watchdog Timer for PNX833x: Version 0.1\n");
 258 
 259         if (start_enabled)
 260                 pnx833x_wdt_start();
 261 
 262         return 0;
 263 }
 264 
 265 static void __exit watchdog_exit(void)
 266 {
 267         misc_deregister(&pnx833x_wdt_miscdev);
 268         unregister_reboot_notifier(&pnx833x_wdt_notifier);
 269 }
 270 
 271 module_init(watchdog_init);
 272 module_exit(watchdog_exit);
 273 
 274 MODULE_AUTHOR("Daniel Laird/Andre McCurdy");
 275 MODULE_DESCRIPTION("Hardware Watchdog Device for PNX833x");
 276 MODULE_LICENSE("GPL");

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