root/drivers/watchdog/ath79_wdt.c

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

DEFINITIONS

This source file includes following definitions.
  1. ath79_wdt_wr
  2. ath79_wdt_rr
  3. ath79_wdt_keepalive
  4. ath79_wdt_enable
  5. ath79_wdt_disable
  6. ath79_wdt_set_timeout
  7. ath79_wdt_open
  8. ath79_wdt_release
  9. ath79_wdt_write
  10. ath79_wdt_ioctl
  11. ath79_wdt_probe
  12. ath79_wdt_remove
  13. ath79_wdt_shutdown

   1 // SPDX-License-Identifier: GPL-2.0-only
   2 /*
   3  * Atheros AR71XX/AR724X/AR913X built-in hardware watchdog timer.
   4  *
   5  * Copyright (C) 2008-2011 Gabor Juhos <juhosg@openwrt.org>
   6  * Copyright (C) 2008 Imre Kaloz <kaloz@openwrt.org>
   7  *
   8  * This driver was based on: drivers/watchdog/ixp4xx_wdt.c
   9  *      Author: Deepak Saxena <dsaxena@plexity.net>
  10  *      Copyright 2004 (c) MontaVista, Software, Inc.
  11  *
  12  * which again was based on sa1100 driver,
  13  *      Copyright (C) 2000 Oleg Drokin <green@crimea.edu>
  14  */
  15 
  16 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
  17 
  18 #include <linux/bitops.h>
  19 #include <linux/delay.h>
  20 #include <linux/errno.h>
  21 #include <linux/fs.h>
  22 #include <linux/io.h>
  23 #include <linux/kernel.h>
  24 #include <linux/miscdevice.h>
  25 #include <linux/module.h>
  26 #include <linux/moduleparam.h>
  27 #include <linux/platform_device.h>
  28 #include <linux/types.h>
  29 #include <linux/watchdog.h>
  30 #include <linux/clk.h>
  31 #include <linux/err.h>
  32 #include <linux/of.h>
  33 #include <linux/of_platform.h>
  34 #include <linux/uaccess.h>
  35 
  36 #define DRIVER_NAME     "ath79-wdt"
  37 
  38 #define WDT_TIMEOUT     15      /* seconds */
  39 
  40 #define WDOG_REG_CTRL           0x00
  41 #define WDOG_REG_TIMER          0x04
  42 
  43 #define WDOG_CTRL_LAST_RESET    BIT(31)
  44 #define WDOG_CTRL_ACTION_MASK   3
  45 #define WDOG_CTRL_ACTION_NONE   0       /* no action */
  46 #define WDOG_CTRL_ACTION_GPI    1       /* general purpose interrupt */
  47 #define WDOG_CTRL_ACTION_NMI    2       /* NMI */
  48 #define WDOG_CTRL_ACTION_FCR    3       /* full chip reset */
  49 
  50 static bool nowayout = WATCHDOG_NOWAYOUT;
  51 module_param(nowayout, bool, 0);
  52 MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started "
  53                            "(default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
  54 
  55 static int timeout = WDT_TIMEOUT;
  56 module_param(timeout, int, 0);
  57 MODULE_PARM_DESC(timeout, "Watchdog timeout in seconds "
  58                           "(default=" __MODULE_STRING(WDT_TIMEOUT) "s)");
  59 
  60 static unsigned long wdt_flags;
  61 
  62 #define WDT_FLAGS_BUSY          0
  63 #define WDT_FLAGS_EXPECT_CLOSE  1
  64 
  65 static struct clk *wdt_clk;
  66 static unsigned long wdt_freq;
  67 static int boot_status;
  68 static int max_timeout;
  69 static void __iomem *wdt_base;
  70 
  71 static inline void ath79_wdt_wr(unsigned reg, u32 val)
  72 {
  73         iowrite32(val, wdt_base + reg);
  74 }
  75 
  76 static inline u32 ath79_wdt_rr(unsigned reg)
  77 {
  78         return ioread32(wdt_base + reg);
  79 }
  80 
  81 static inline void ath79_wdt_keepalive(void)
  82 {
  83         ath79_wdt_wr(WDOG_REG_TIMER, wdt_freq * timeout);
  84         /* flush write */
  85         ath79_wdt_rr(WDOG_REG_TIMER);
  86 }
  87 
  88 static inline void ath79_wdt_enable(void)
  89 {
  90         ath79_wdt_keepalive();
  91 
  92         /*
  93          * Updating the TIMER register requires a few microseconds
  94          * on the AR934x SoCs at least. Use a small delay to ensure
  95          * that the TIMER register is updated within the hardware
  96          * before enabling the watchdog.
  97          */
  98         udelay(2);
  99 
 100         ath79_wdt_wr(WDOG_REG_CTRL, WDOG_CTRL_ACTION_FCR);
 101         /* flush write */
 102         ath79_wdt_rr(WDOG_REG_CTRL);
 103 }
 104 
 105 static inline void ath79_wdt_disable(void)
 106 {
 107         ath79_wdt_wr(WDOG_REG_CTRL, WDOG_CTRL_ACTION_NONE);
 108         /* flush write */
 109         ath79_wdt_rr(WDOG_REG_CTRL);
 110 }
 111 
 112 static int ath79_wdt_set_timeout(int val)
 113 {
 114         if (val < 1 || val > max_timeout)
 115                 return -EINVAL;
 116 
 117         timeout = val;
 118         ath79_wdt_keepalive();
 119 
 120         return 0;
 121 }
 122 
 123 static int ath79_wdt_open(struct inode *inode, struct file *file)
 124 {
 125         if (test_and_set_bit(WDT_FLAGS_BUSY, &wdt_flags))
 126                 return -EBUSY;
 127 
 128         clear_bit(WDT_FLAGS_EXPECT_CLOSE, &wdt_flags);
 129         ath79_wdt_enable();
 130 
 131         return stream_open(inode, file);
 132 }
 133 
 134 static int ath79_wdt_release(struct inode *inode, struct file *file)
 135 {
 136         if (test_bit(WDT_FLAGS_EXPECT_CLOSE, &wdt_flags))
 137                 ath79_wdt_disable();
 138         else {
 139                 pr_crit("device closed unexpectedly, watchdog timer will not stop!\n");
 140                 ath79_wdt_keepalive();
 141         }
 142 
 143         clear_bit(WDT_FLAGS_BUSY, &wdt_flags);
 144         clear_bit(WDT_FLAGS_EXPECT_CLOSE, &wdt_flags);
 145 
 146         return 0;
 147 }
 148 
 149 static ssize_t ath79_wdt_write(struct file *file, const char *data,
 150                                 size_t len, loff_t *ppos)
 151 {
 152         if (len) {
 153                 if (!nowayout) {
 154                         size_t i;
 155 
 156                         clear_bit(WDT_FLAGS_EXPECT_CLOSE, &wdt_flags);
 157 
 158                         for (i = 0; i != len; i++) {
 159                                 char c;
 160 
 161                                 if (get_user(c, data + i))
 162                                         return -EFAULT;
 163 
 164                                 if (c == 'V')
 165                                         set_bit(WDT_FLAGS_EXPECT_CLOSE,
 166                                                 &wdt_flags);
 167                         }
 168                 }
 169 
 170                 ath79_wdt_keepalive();
 171         }
 172 
 173         return len;
 174 }
 175 
 176 static const struct watchdog_info ath79_wdt_info = {
 177         .options                = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING |
 178                                   WDIOF_MAGICCLOSE | WDIOF_CARDRESET,
 179         .firmware_version       = 0,
 180         .identity               = "ATH79 watchdog",
 181 };
 182 
 183 static long ath79_wdt_ioctl(struct file *file, unsigned int cmd,
 184                             unsigned long arg)
 185 {
 186         void __user *argp = (void __user *)arg;
 187         int __user *p = argp;
 188         int err;
 189         int t;
 190 
 191         switch (cmd) {
 192         case WDIOC_GETSUPPORT:
 193                 err = copy_to_user(argp, &ath79_wdt_info,
 194                                    sizeof(ath79_wdt_info)) ? -EFAULT : 0;
 195                 break;
 196 
 197         case WDIOC_GETSTATUS:
 198                 err = put_user(0, p);
 199                 break;
 200 
 201         case WDIOC_GETBOOTSTATUS:
 202                 err = put_user(boot_status, p);
 203                 break;
 204 
 205         case WDIOC_KEEPALIVE:
 206                 ath79_wdt_keepalive();
 207                 err = 0;
 208                 break;
 209 
 210         case WDIOC_SETTIMEOUT:
 211                 err = get_user(t, p);
 212                 if (err)
 213                         break;
 214 
 215                 err = ath79_wdt_set_timeout(t);
 216                 if (err)
 217                         break;
 218 
 219                 /* fallthrough */
 220         case WDIOC_GETTIMEOUT:
 221                 err = put_user(timeout, p);
 222                 break;
 223 
 224         default:
 225                 err = -ENOTTY;
 226                 break;
 227         }
 228 
 229         return err;
 230 }
 231 
 232 static const struct file_operations ath79_wdt_fops = {
 233         .owner          = THIS_MODULE,
 234         .llseek         = no_llseek,
 235         .write          = ath79_wdt_write,
 236         .unlocked_ioctl = ath79_wdt_ioctl,
 237         .open           = ath79_wdt_open,
 238         .release        = ath79_wdt_release,
 239 };
 240 
 241 static struct miscdevice ath79_wdt_miscdev = {
 242         .minor = WATCHDOG_MINOR,
 243         .name = "watchdog",
 244         .fops = &ath79_wdt_fops,
 245 };
 246 
 247 static int ath79_wdt_probe(struct platform_device *pdev)
 248 {
 249         u32 ctrl;
 250         int err;
 251 
 252         if (wdt_base)
 253                 return -EBUSY;
 254 
 255         wdt_base = devm_platform_ioremap_resource(pdev, 0);
 256         if (IS_ERR(wdt_base))
 257                 return PTR_ERR(wdt_base);
 258 
 259         wdt_clk = devm_clk_get(&pdev->dev, "wdt");
 260         if (IS_ERR(wdt_clk))
 261                 return PTR_ERR(wdt_clk);
 262 
 263         err = clk_prepare_enable(wdt_clk);
 264         if (err)
 265                 return err;
 266 
 267         wdt_freq = clk_get_rate(wdt_clk);
 268         if (!wdt_freq) {
 269                 err = -EINVAL;
 270                 goto err_clk_disable;
 271         }
 272 
 273         max_timeout = (0xfffffffful / wdt_freq);
 274         if (timeout < 1 || timeout > max_timeout) {
 275                 timeout = max_timeout;
 276                 dev_info(&pdev->dev,
 277                         "timeout value must be 0 < timeout < %d, using %d\n",
 278                         max_timeout, timeout);
 279         }
 280 
 281         ctrl = ath79_wdt_rr(WDOG_REG_CTRL);
 282         boot_status = (ctrl & WDOG_CTRL_LAST_RESET) ? WDIOF_CARDRESET : 0;
 283 
 284         err = misc_register(&ath79_wdt_miscdev);
 285         if (err) {
 286                 dev_err(&pdev->dev,
 287                         "unable to register misc device, err=%d\n", err);
 288                 goto err_clk_disable;
 289         }
 290 
 291         return 0;
 292 
 293 err_clk_disable:
 294         clk_disable_unprepare(wdt_clk);
 295         return err;
 296 }
 297 
 298 static int ath79_wdt_remove(struct platform_device *pdev)
 299 {
 300         misc_deregister(&ath79_wdt_miscdev);
 301         clk_disable_unprepare(wdt_clk);
 302         return 0;
 303 }
 304 
 305 static void ath79_wdt_shutdown(struct platform_device *pdev)
 306 {
 307         ath79_wdt_disable();
 308 }
 309 
 310 #ifdef CONFIG_OF
 311 static const struct of_device_id ath79_wdt_match[] = {
 312         { .compatible = "qca,ar7130-wdt" },
 313         {},
 314 };
 315 MODULE_DEVICE_TABLE(of, ath79_wdt_match);
 316 #endif
 317 
 318 static struct platform_driver ath79_wdt_driver = {
 319         .probe          = ath79_wdt_probe,
 320         .remove         = ath79_wdt_remove,
 321         .shutdown       = ath79_wdt_shutdown,
 322         .driver         = {
 323                 .name   = DRIVER_NAME,
 324                 .of_match_table = of_match_ptr(ath79_wdt_match),
 325         },
 326 };
 327 
 328 module_platform_driver(ath79_wdt_driver);
 329 
 330 MODULE_DESCRIPTION("Atheros AR71XX/AR724X/AR913X hardware watchdog driver");
 331 MODULE_AUTHOR("Gabor Juhos <juhosg@openwrt.org");
 332 MODULE_AUTHOR("Imre Kaloz <kaloz@openwrt.org");
 333 MODULE_LICENSE("GPL v2");
 334 MODULE_ALIAS("platform:" DRIVER_NAME);

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