root/drivers/watchdog/at91rm9200_wdt.c

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

DEFINITIONS

This source file includes following definitions.
  1. at91rm9200_restart
  2. at91_wdt_stop
  3. at91_wdt_start
  4. at91_wdt_reload
  5. at91_wdt_open
  6. at91_wdt_close
  7. at91_wdt_settimeout
  8. at91_wdt_ioctl
  9. at91_wdt_write
  10. at91wdt_probe
  11. at91wdt_remove
  12. at91wdt_shutdown
  13. at91wdt_suspend
  14. at91wdt_resume
  15. at91_wdt_init
  16. at91_wdt_exit

   1 // SPDX-License-Identifier: GPL-2.0+
   2 /*
   3  * Watchdog driver for Atmel AT91RM9200 (Thunder)
   4  *
   5  *  Copyright (C) 2003 SAN People (Pty) Ltd
   6  *
   7  */
   8 
   9 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
  10 
  11 #include <linux/bitops.h>
  12 #include <linux/delay.h>
  13 #include <linux/errno.h>
  14 #include <linux/fs.h>
  15 #include <linux/init.h>
  16 #include <linux/io.h>
  17 #include <linux/kernel.h>
  18 #include <linux/mfd/syscon.h>
  19 #include <linux/mfd/syscon/atmel-st.h>
  20 #include <linux/miscdevice.h>
  21 #include <linux/module.h>
  22 #include <linux/moduleparam.h>
  23 #include <linux/platform_device.h>
  24 #include <linux/reboot.h>
  25 #include <linux/regmap.h>
  26 #include <linux/types.h>
  27 #include <linux/watchdog.h>
  28 #include <linux/uaccess.h>
  29 #include <linux/of.h>
  30 #include <linux/of_device.h>
  31 
  32 #define WDT_DEFAULT_TIME        5       /* seconds */
  33 #define WDT_MAX_TIME            256     /* seconds */
  34 
  35 static int wdt_time = WDT_DEFAULT_TIME;
  36 static bool nowayout = WATCHDOG_NOWAYOUT;
  37 static struct regmap *regmap_st;
  38 
  39 module_param(wdt_time, int, 0);
  40 MODULE_PARM_DESC(wdt_time, "Watchdog time in seconds. (default="
  41                                 __MODULE_STRING(WDT_DEFAULT_TIME) ")");
  42 
  43 #ifdef CONFIG_WATCHDOG_NOWAYOUT
  44 module_param(nowayout, bool, 0);
  45 MODULE_PARM_DESC(nowayout,
  46                 "Watchdog cannot be stopped once started (default="
  47                                 __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
  48 #endif
  49 
  50 
  51 static unsigned long at91wdt_busy;
  52 
  53 /* ......................................................................... */
  54 
  55 static int at91rm9200_restart(struct notifier_block *this,
  56                                         unsigned long mode, void *cmd)
  57 {
  58         /*
  59          * Perform a hardware reset with the use of the Watchdog timer.
  60          */
  61         regmap_write(regmap_st, AT91_ST_WDMR,
  62                      AT91_ST_RSTEN | AT91_ST_EXTEN | 1);
  63         regmap_write(regmap_st, AT91_ST_CR, AT91_ST_WDRST);
  64 
  65         mdelay(2000);
  66 
  67         pr_emerg("Unable to restart system\n");
  68         return NOTIFY_DONE;
  69 }
  70 
  71 static struct notifier_block at91rm9200_restart_nb = {
  72         .notifier_call = at91rm9200_restart,
  73         .priority = 192,
  74 };
  75 
  76 /*
  77  * Disable the watchdog.
  78  */
  79 static inline void at91_wdt_stop(void)
  80 {
  81         regmap_write(regmap_st, AT91_ST_WDMR, AT91_ST_EXTEN);
  82 }
  83 
  84 /*
  85  * Enable and reset the watchdog.
  86  */
  87 static inline void at91_wdt_start(void)
  88 {
  89         regmap_write(regmap_st, AT91_ST_WDMR, AT91_ST_EXTEN | AT91_ST_RSTEN |
  90                                 (((65536 * wdt_time) >> 8) & AT91_ST_WDV));
  91         regmap_write(regmap_st, AT91_ST_CR, AT91_ST_WDRST);
  92 }
  93 
  94 /*
  95  * Reload the watchdog timer.  (ie, pat the watchdog)
  96  */
  97 static inline void at91_wdt_reload(void)
  98 {
  99         regmap_write(regmap_st, AT91_ST_CR, AT91_ST_WDRST);
 100 }
 101 
 102 /* ......................................................................... */
 103 
 104 /*
 105  * Watchdog device is opened, and watchdog starts running.
 106  */
 107 static int at91_wdt_open(struct inode *inode, struct file *file)
 108 {
 109         if (test_and_set_bit(0, &at91wdt_busy))
 110                 return -EBUSY;
 111 
 112         at91_wdt_start();
 113         return stream_open(inode, file);
 114 }
 115 
 116 /*
 117  * Close the watchdog device.
 118  * If CONFIG_WATCHDOG_NOWAYOUT is NOT defined then the watchdog is also
 119  *  disabled.
 120  */
 121 static int at91_wdt_close(struct inode *inode, struct file *file)
 122 {
 123         /* Disable the watchdog when file is closed */
 124         if (!nowayout)
 125                 at91_wdt_stop();
 126 
 127         clear_bit(0, &at91wdt_busy);
 128         return 0;
 129 }
 130 
 131 /*
 132  * Change the watchdog time interval.
 133  */
 134 static int at91_wdt_settimeout(int new_time)
 135 {
 136         /*
 137          * All counting occurs at SLOW_CLOCK / 128 = 256 Hz
 138          *
 139          * Since WDV is a 16-bit counter, the maximum period is
 140          * 65536 / 256 = 256 seconds.
 141          */
 142         if ((new_time <= 0) || (new_time > WDT_MAX_TIME))
 143                 return -EINVAL;
 144 
 145         /* Set new watchdog time. It will be used when
 146            at91_wdt_start() is called. */
 147         wdt_time = new_time;
 148         return 0;
 149 }
 150 
 151 static const struct watchdog_info at91_wdt_info = {
 152         .identity       = "at91 watchdog",
 153         .options        = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING,
 154 };
 155 
 156 /*
 157  * Handle commands from user-space.
 158  */
 159 static long at91_wdt_ioctl(struct file *file,
 160                                         unsigned int cmd, unsigned long arg)
 161 {
 162         void __user *argp = (void __user *)arg;
 163         int __user *p = argp;
 164         int new_value;
 165 
 166         switch (cmd) {
 167         case WDIOC_GETSUPPORT:
 168                 return copy_to_user(argp, &at91_wdt_info,
 169                                 sizeof(at91_wdt_info)) ? -EFAULT : 0;
 170         case WDIOC_GETSTATUS:
 171         case WDIOC_GETBOOTSTATUS:
 172                 return put_user(0, p);
 173         case WDIOC_SETOPTIONS:
 174                 if (get_user(new_value, p))
 175                         return -EFAULT;
 176                 if (new_value & WDIOS_DISABLECARD)
 177                         at91_wdt_stop();
 178                 if (new_value & WDIOS_ENABLECARD)
 179                         at91_wdt_start();
 180                 return 0;
 181         case WDIOC_KEEPALIVE:
 182                 at91_wdt_reload();      /* pat the watchdog */
 183                 return 0;
 184         case WDIOC_SETTIMEOUT:
 185                 if (get_user(new_value, p))
 186                         return -EFAULT;
 187                 if (at91_wdt_settimeout(new_value))
 188                         return -EINVAL;
 189                 /* Enable new time value */
 190                 at91_wdt_start();
 191                 /* Return current value */
 192                 return put_user(wdt_time, p);
 193         case WDIOC_GETTIMEOUT:
 194                 return put_user(wdt_time, p);
 195         default:
 196                 return -ENOTTY;
 197         }
 198 }
 199 
 200 /*
 201  * Pat the watchdog whenever device is written to.
 202  */
 203 static ssize_t at91_wdt_write(struct file *file, const char *data,
 204                                                 size_t len, loff_t *ppos)
 205 {
 206         at91_wdt_reload();              /* pat the watchdog */
 207         return len;
 208 }
 209 
 210 /* ......................................................................... */
 211 
 212 static const struct file_operations at91wdt_fops = {
 213         .owner          = THIS_MODULE,
 214         .llseek         = no_llseek,
 215         .unlocked_ioctl = at91_wdt_ioctl,
 216         .open           = at91_wdt_open,
 217         .release        = at91_wdt_close,
 218         .write          = at91_wdt_write,
 219 };
 220 
 221 static struct miscdevice at91wdt_miscdev = {
 222         .minor          = WATCHDOG_MINOR,
 223         .name           = "watchdog",
 224         .fops           = &at91wdt_fops,
 225 };
 226 
 227 static int at91wdt_probe(struct platform_device *pdev)
 228 {
 229         struct device *dev = &pdev->dev;
 230         struct device *parent;
 231         int res;
 232 
 233         if (at91wdt_miscdev.parent)
 234                 return -EBUSY;
 235         at91wdt_miscdev.parent = &pdev->dev;
 236 
 237         parent = dev->parent;
 238         if (!parent) {
 239                 dev_err(dev, "no parent\n");
 240                 return -ENODEV;
 241         }
 242 
 243         regmap_st = syscon_node_to_regmap(parent->of_node);
 244         if (IS_ERR(regmap_st))
 245                 return -ENODEV;
 246 
 247         res = misc_register(&at91wdt_miscdev);
 248         if (res)
 249                 return res;
 250 
 251         res = register_restart_handler(&at91rm9200_restart_nb);
 252         if (res)
 253                 dev_warn(dev, "failed to register restart handler\n");
 254 
 255         pr_info("AT91 Watchdog Timer enabled (%d seconds%s)\n",
 256                 wdt_time, nowayout ? ", nowayout" : "");
 257         return 0;
 258 }
 259 
 260 static int at91wdt_remove(struct platform_device *pdev)
 261 {
 262         struct device *dev = &pdev->dev;
 263         int res;
 264 
 265         res = unregister_restart_handler(&at91rm9200_restart_nb);
 266         if (res)
 267                 dev_warn(dev, "failed to unregister restart handler\n");
 268 
 269         misc_deregister(&at91wdt_miscdev);
 270         at91wdt_miscdev.parent = NULL;
 271 
 272         return res;
 273 }
 274 
 275 static void at91wdt_shutdown(struct platform_device *pdev)
 276 {
 277         at91_wdt_stop();
 278 }
 279 
 280 #ifdef CONFIG_PM
 281 
 282 static int at91wdt_suspend(struct platform_device *pdev, pm_message_t message)
 283 {
 284         at91_wdt_stop();
 285         return 0;
 286 }
 287 
 288 static int at91wdt_resume(struct platform_device *pdev)
 289 {
 290         if (at91wdt_busy)
 291                 at91_wdt_start();
 292         return 0;
 293 }
 294 
 295 #else
 296 #define at91wdt_suspend NULL
 297 #define at91wdt_resume  NULL
 298 #endif
 299 
 300 static const struct of_device_id at91_wdt_dt_ids[] = {
 301         { .compatible = "atmel,at91rm9200-wdt" },
 302         { /* sentinel */ }
 303 };
 304 MODULE_DEVICE_TABLE(of, at91_wdt_dt_ids);
 305 
 306 static struct platform_driver at91wdt_driver = {
 307         .probe          = at91wdt_probe,
 308         .remove         = at91wdt_remove,
 309         .shutdown       = at91wdt_shutdown,
 310         .suspend        = at91wdt_suspend,
 311         .resume         = at91wdt_resume,
 312         .driver         = {
 313                 .name   = "atmel_st_watchdog",
 314                 .of_match_table = at91_wdt_dt_ids,
 315         },
 316 };
 317 
 318 static int __init at91_wdt_init(void)
 319 {
 320         /* Check that the heartbeat value is within range;
 321            if not reset to the default */
 322         if (at91_wdt_settimeout(wdt_time)) {
 323                 at91_wdt_settimeout(WDT_DEFAULT_TIME);
 324                 pr_info("wdt_time value must be 1 <= wdt_time <= 256, using %d\n",
 325                         wdt_time);
 326         }
 327 
 328         return platform_driver_register(&at91wdt_driver);
 329 }
 330 
 331 static void __exit at91_wdt_exit(void)
 332 {
 333         platform_driver_unregister(&at91wdt_driver);
 334 }
 335 
 336 module_init(at91_wdt_init);
 337 module_exit(at91_wdt_exit);
 338 
 339 MODULE_AUTHOR("Andrew Victor");
 340 MODULE_DESCRIPTION("Watchdog driver for Atmel AT91RM9200");
 341 MODULE_LICENSE("GPL");
 342 MODULE_ALIAS("platform:atmel_st_watchdog");

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