root/drivers/watchdog/bcm63xx_wdt.c

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

DEFINITIONS

This source file includes following definitions.
  1. bcm63xx_wdt_hw_start
  2. bcm63xx_wdt_hw_stop
  3. bcm63xx_wdt_isr
  4. bcm63xx_timer_tick
  5. bcm63xx_wdt_pet
  6. bcm63xx_wdt_start
  7. bcm63xx_wdt_pause
  8. bcm63xx_wdt_settimeout
  9. bcm63xx_wdt_open
  10. bcm63xx_wdt_release
  11. bcm63xx_wdt_write
  12. bcm63xx_wdt_ioctl
  13. bcm63xx_wdt_probe
  14. bcm63xx_wdt_remove
  15. bcm63xx_wdt_shutdown

   1 // SPDX-License-Identifier: GPL-2.0+
   2 /*
   3  *  Broadcom BCM63xx SoC watchdog driver
   4  *
   5  *  Copyright (C) 2007, Miguel Gaio <miguel.gaio@efixo.com>
   6  *  Copyright (C) 2008, Florian Fainelli <florian@openwrt.org>
   7  *
   8  */
   9 
  10 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
  11 
  12 #include <linux/bitops.h>
  13 #include <linux/errno.h>
  14 #include <linux/fs.h>
  15 #include <linux/io.h>
  16 #include <linux/kernel.h>
  17 #include <linux/miscdevice.h>
  18 #include <linux/module.h>
  19 #include <linux/moduleparam.h>
  20 #include <linux/types.h>
  21 #include <linux/uaccess.h>
  22 #include <linux/watchdog.h>
  23 #include <linux/timer.h>
  24 #include <linux/jiffies.h>
  25 #include <linux/interrupt.h>
  26 #include <linux/ptrace.h>
  27 #include <linux/resource.h>
  28 #include <linux/platform_device.h>
  29 
  30 #include <bcm63xx_cpu.h>
  31 #include <bcm63xx_io.h>
  32 #include <bcm63xx_regs.h>
  33 #include <bcm63xx_timer.h>
  34 
  35 #define PFX KBUILD_MODNAME
  36 
  37 #define WDT_HZ          50000000 /* Fclk */
  38 #define WDT_DEFAULT_TIME        30      /* seconds */
  39 #define WDT_MAX_TIME            256     /* seconds */
  40 
  41 static struct {
  42         void __iomem *regs;
  43         struct timer_list timer;
  44         unsigned long inuse;
  45         atomic_t ticks;
  46 } bcm63xx_wdt_device;
  47 
  48 static int expect_close;
  49 
  50 static int wdt_time = WDT_DEFAULT_TIME;
  51 static bool nowayout = WATCHDOG_NOWAYOUT;
  52 module_param(nowayout, bool, 0);
  53 MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default="
  54         __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
  55 
  56 /* HW functions */
  57 static void bcm63xx_wdt_hw_start(void)
  58 {
  59         bcm_writel(0xfffffffe, bcm63xx_wdt_device.regs + WDT_DEFVAL_REG);
  60         bcm_writel(WDT_START_1, bcm63xx_wdt_device.regs + WDT_CTL_REG);
  61         bcm_writel(WDT_START_2, bcm63xx_wdt_device.regs + WDT_CTL_REG);
  62 }
  63 
  64 static void bcm63xx_wdt_hw_stop(void)
  65 {
  66         bcm_writel(WDT_STOP_1, bcm63xx_wdt_device.regs + WDT_CTL_REG);
  67         bcm_writel(WDT_STOP_2, bcm63xx_wdt_device.regs + WDT_CTL_REG);
  68 }
  69 
  70 static void bcm63xx_wdt_isr(void *data)
  71 {
  72         struct pt_regs *regs = get_irq_regs();
  73 
  74         die(PFX " fire", regs);
  75 }
  76 
  77 static void bcm63xx_timer_tick(struct timer_list *unused)
  78 {
  79         if (!atomic_dec_and_test(&bcm63xx_wdt_device.ticks)) {
  80                 bcm63xx_wdt_hw_start();
  81                 mod_timer(&bcm63xx_wdt_device.timer, jiffies + HZ);
  82         } else
  83                 pr_crit("watchdog will restart system\n");
  84 }
  85 
  86 static void bcm63xx_wdt_pet(void)
  87 {
  88         atomic_set(&bcm63xx_wdt_device.ticks, wdt_time);
  89 }
  90 
  91 static void bcm63xx_wdt_start(void)
  92 {
  93         bcm63xx_wdt_pet();
  94         bcm63xx_timer_tick(0);
  95 }
  96 
  97 static void bcm63xx_wdt_pause(void)
  98 {
  99         del_timer_sync(&bcm63xx_wdt_device.timer);
 100         bcm63xx_wdt_hw_stop();
 101 }
 102 
 103 static int bcm63xx_wdt_settimeout(int new_time)
 104 {
 105         if ((new_time <= 0) || (new_time > WDT_MAX_TIME))
 106                 return -EINVAL;
 107 
 108         wdt_time = new_time;
 109 
 110         return 0;
 111 }
 112 
 113 static int bcm63xx_wdt_open(struct inode *inode, struct file *file)
 114 {
 115         if (test_and_set_bit(0, &bcm63xx_wdt_device.inuse))
 116                 return -EBUSY;
 117 
 118         bcm63xx_wdt_start();
 119         return stream_open(inode, file);
 120 }
 121 
 122 static int bcm63xx_wdt_release(struct inode *inode, struct file *file)
 123 {
 124         if (expect_close == 42)
 125                 bcm63xx_wdt_pause();
 126         else {
 127                 pr_crit("Unexpected close, not stopping watchdog!\n");
 128                 bcm63xx_wdt_start();
 129         }
 130         clear_bit(0, &bcm63xx_wdt_device.inuse);
 131         expect_close = 0;
 132         return 0;
 133 }
 134 
 135 static ssize_t bcm63xx_wdt_write(struct file *file, const char *data,
 136                                 size_t len, loff_t *ppos)
 137 {
 138         if (len) {
 139                 if (!nowayout) {
 140                         size_t i;
 141 
 142                         /* In case it was set long ago */
 143                         expect_close = 0;
 144 
 145                         for (i = 0; i != len; i++) {
 146                                 char c;
 147                                 if (get_user(c, data + i))
 148                                         return -EFAULT;
 149                                 if (c == 'V')
 150                                         expect_close = 42;
 151                         }
 152                 }
 153                 bcm63xx_wdt_pet();
 154         }
 155         return len;
 156 }
 157 
 158 static struct watchdog_info bcm63xx_wdt_info = {
 159         .identity       = PFX,
 160         .options        = WDIOF_SETTIMEOUT |
 161                                 WDIOF_KEEPALIVEPING |
 162                                 WDIOF_MAGICCLOSE,
 163 };
 164 
 165 
 166 static long bcm63xx_wdt_ioctl(struct file *file, unsigned int cmd,
 167                                 unsigned long arg)
 168 {
 169         void __user *argp = (void __user *)arg;
 170         int __user *p = argp;
 171         int new_value, retval = -EINVAL;
 172 
 173         switch (cmd) {
 174         case WDIOC_GETSUPPORT:
 175                 return copy_to_user(argp, &bcm63xx_wdt_info,
 176                         sizeof(bcm63xx_wdt_info)) ? -EFAULT : 0;
 177 
 178         case WDIOC_GETSTATUS:
 179         case WDIOC_GETBOOTSTATUS:
 180                 return put_user(0, p);
 181 
 182         case WDIOC_SETOPTIONS:
 183                 if (get_user(new_value, p))
 184                         return -EFAULT;
 185 
 186                 if (new_value & WDIOS_DISABLECARD) {
 187                         bcm63xx_wdt_pause();
 188                         retval = 0;
 189                 }
 190                 if (new_value & WDIOS_ENABLECARD) {
 191                         bcm63xx_wdt_start();
 192                         retval = 0;
 193                 }
 194 
 195                 return retval;
 196 
 197         case WDIOC_KEEPALIVE:
 198                 bcm63xx_wdt_pet();
 199                 return 0;
 200 
 201         case WDIOC_SETTIMEOUT:
 202                 if (get_user(new_value, p))
 203                         return -EFAULT;
 204 
 205                 if (bcm63xx_wdt_settimeout(new_value))
 206                         return -EINVAL;
 207 
 208                 bcm63xx_wdt_pet();
 209 
 210         case WDIOC_GETTIMEOUT:
 211                 return put_user(wdt_time, p);
 212 
 213         default:
 214                 return -ENOTTY;
 215 
 216         }
 217 }
 218 
 219 static const struct file_operations bcm63xx_wdt_fops = {
 220         .owner          = THIS_MODULE,
 221         .llseek         = no_llseek,
 222         .write          = bcm63xx_wdt_write,
 223         .unlocked_ioctl = bcm63xx_wdt_ioctl,
 224         .open           = bcm63xx_wdt_open,
 225         .release        = bcm63xx_wdt_release,
 226 };
 227 
 228 static struct miscdevice bcm63xx_wdt_miscdev = {
 229         .minor  = WATCHDOG_MINOR,
 230         .name   = "watchdog",
 231         .fops   = &bcm63xx_wdt_fops,
 232 };
 233 
 234 
 235 static int bcm63xx_wdt_probe(struct platform_device *pdev)
 236 {
 237         int ret;
 238         struct resource *r;
 239 
 240         timer_setup(&bcm63xx_wdt_device.timer, bcm63xx_timer_tick, 0);
 241 
 242         r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 243         if (!r) {
 244                 dev_err(&pdev->dev, "failed to get resources\n");
 245                 return -ENODEV;
 246         }
 247 
 248         bcm63xx_wdt_device.regs = devm_ioremap_nocache(&pdev->dev, r->start,
 249                                                         resource_size(r));
 250         if (!bcm63xx_wdt_device.regs) {
 251                 dev_err(&pdev->dev, "failed to remap I/O resources\n");
 252                 return -ENXIO;
 253         }
 254 
 255         ret = bcm63xx_timer_register(TIMER_WDT_ID, bcm63xx_wdt_isr, NULL);
 256         if (ret < 0) {
 257                 dev_err(&pdev->dev, "failed to register wdt timer isr\n");
 258                 return ret;
 259         }
 260 
 261         if (bcm63xx_wdt_settimeout(wdt_time)) {
 262                 bcm63xx_wdt_settimeout(WDT_DEFAULT_TIME);
 263                 dev_info(&pdev->dev,
 264                         ": wdt_time value must be 1 <= wdt_time <= 256, using %d\n",
 265                         wdt_time);
 266         }
 267 
 268         ret = misc_register(&bcm63xx_wdt_miscdev);
 269         if (ret < 0) {
 270                 dev_err(&pdev->dev, "failed to register watchdog device\n");
 271                 goto unregister_timer;
 272         }
 273 
 274         dev_info(&pdev->dev, " started, timer margin: %d sec\n",
 275                                                 WDT_DEFAULT_TIME);
 276 
 277         return 0;
 278 
 279 unregister_timer:
 280         bcm63xx_timer_unregister(TIMER_WDT_ID);
 281         return ret;
 282 }
 283 
 284 static int bcm63xx_wdt_remove(struct platform_device *pdev)
 285 {
 286         if (!nowayout)
 287                 bcm63xx_wdt_pause();
 288 
 289         misc_deregister(&bcm63xx_wdt_miscdev);
 290         bcm63xx_timer_unregister(TIMER_WDT_ID);
 291         return 0;
 292 }
 293 
 294 static void bcm63xx_wdt_shutdown(struct platform_device *pdev)
 295 {
 296         bcm63xx_wdt_pause();
 297 }
 298 
 299 static struct platform_driver bcm63xx_wdt_driver = {
 300         .probe  = bcm63xx_wdt_probe,
 301         .remove = bcm63xx_wdt_remove,
 302         .shutdown = bcm63xx_wdt_shutdown,
 303         .driver = {
 304                 .name = "bcm63xx-wdt",
 305         }
 306 };
 307 
 308 module_platform_driver(bcm63xx_wdt_driver);
 309 
 310 MODULE_AUTHOR("Miguel Gaio <miguel.gaio@efixo.com>");
 311 MODULE_AUTHOR("Florian Fainelli <florian@openwrt.org>");
 312 MODULE_DESCRIPTION("Driver for the Broadcom BCM63xx SoC watchdog");
 313 MODULE_LICENSE("GPL");
 314 MODULE_ALIAS("platform:bcm63xx-wdt");

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