root/drivers/watchdog/ar7_wdt.c

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

DEFINITIONS

This source file includes following definitions.
  1. ar7_wdt_kick
  2. ar7_wdt_prescale
  3. ar7_wdt_change
  4. ar7_wdt_disable
  5. ar7_wdt_update_margin
  6. ar7_wdt_enable_wdt
  7. ar7_wdt_disable_wdt
  8. ar7_wdt_open
  9. ar7_wdt_release
  10. ar7_wdt_write
  11. ar7_wdt_ioctl
  12. ar7_wdt_probe
  13. ar7_wdt_remove
  14. ar7_wdt_shutdown

   1 // SPDX-License-Identifier: GPL-2.0+
   2 /*
   3  * drivers/watchdog/ar7_wdt.c
   4  *
   5  * Copyright (C) 2007 Nicolas Thill <nico@openwrt.org>
   6  * Copyright (c) 2005 Enrik Berkhan <Enrik.Berkhan@akk.org>
   7  *
   8  * Some code taken from:
   9  * National Semiconductor SCx200 Watchdog support
  10  * Copyright (c) 2001,2002 Christer Weinigel <wingel@nano-system.com>
  11  *
  12  */
  13 
  14 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
  15 
  16 #include <linux/module.h>
  17 #include <linux/moduleparam.h>
  18 #include <linux/errno.h>
  19 #include <linux/miscdevice.h>
  20 #include <linux/platform_device.h>
  21 #include <linux/watchdog.h>
  22 #include <linux/fs.h>
  23 #include <linux/ioport.h>
  24 #include <linux/io.h>
  25 #include <linux/uaccess.h>
  26 #include <linux/clk.h>
  27 
  28 #include <asm/addrspace.h>
  29 #include <asm/mach-ar7/ar7.h>
  30 
  31 #define LONGNAME "TI AR7 Watchdog Timer"
  32 
  33 MODULE_AUTHOR("Nicolas Thill <nico@openwrt.org>");
  34 MODULE_DESCRIPTION(LONGNAME);
  35 MODULE_LICENSE("GPL");
  36 
  37 static int margin = 60;
  38 module_param(margin, int, 0);
  39 MODULE_PARM_DESC(margin, "Watchdog margin in seconds");
  40 
  41 static bool nowayout = WATCHDOG_NOWAYOUT;
  42 module_param(nowayout, bool, 0);
  43 MODULE_PARM_DESC(nowayout, "Disable watchdog shutdown on close");
  44 
  45 #define READ_REG(x) readl((void __iomem *)&(x))
  46 #define WRITE_REG(x, v) writel((v), (void __iomem *)&(x))
  47 
  48 struct ar7_wdt {
  49         u32 kick_lock;
  50         u32 kick;
  51         u32 change_lock;
  52         u32 change;
  53         u32 disable_lock;
  54         u32 disable;
  55         u32 prescale_lock;
  56         u32 prescale;
  57 };
  58 
  59 static unsigned long wdt_is_open;
  60 static unsigned expect_close;
  61 static DEFINE_SPINLOCK(wdt_lock);
  62 
  63 /* XXX currently fixed, allows max margin ~68.72 secs */
  64 #define prescale_value 0xffff
  65 
  66 /* Resource of the WDT registers */
  67 static struct resource *ar7_regs_wdt;
  68 /* Pointer to the remapped WDT IO space */
  69 static struct ar7_wdt *ar7_wdt;
  70 
  71 static struct clk *vbus_clk;
  72 
  73 static void ar7_wdt_kick(u32 value)
  74 {
  75         WRITE_REG(ar7_wdt->kick_lock, 0x5555);
  76         if ((READ_REG(ar7_wdt->kick_lock) & 3) == 1) {
  77                 WRITE_REG(ar7_wdt->kick_lock, 0xaaaa);
  78                 if ((READ_REG(ar7_wdt->kick_lock) & 3) == 3) {
  79                         WRITE_REG(ar7_wdt->kick, value);
  80                         return;
  81                 }
  82         }
  83         pr_err("failed to unlock WDT kick reg\n");
  84 }
  85 
  86 static void ar7_wdt_prescale(u32 value)
  87 {
  88         WRITE_REG(ar7_wdt->prescale_lock, 0x5a5a);
  89         if ((READ_REG(ar7_wdt->prescale_lock) & 3) == 1) {
  90                 WRITE_REG(ar7_wdt->prescale_lock, 0xa5a5);
  91                 if ((READ_REG(ar7_wdt->prescale_lock) & 3) == 3) {
  92                         WRITE_REG(ar7_wdt->prescale, value);
  93                         return;
  94                 }
  95         }
  96         pr_err("failed to unlock WDT prescale reg\n");
  97 }
  98 
  99 static void ar7_wdt_change(u32 value)
 100 {
 101         WRITE_REG(ar7_wdt->change_lock, 0x6666);
 102         if ((READ_REG(ar7_wdt->change_lock) & 3) == 1) {
 103                 WRITE_REG(ar7_wdt->change_lock, 0xbbbb);
 104                 if ((READ_REG(ar7_wdt->change_lock) & 3) == 3) {
 105                         WRITE_REG(ar7_wdt->change, value);
 106                         return;
 107                 }
 108         }
 109         pr_err("failed to unlock WDT change reg\n");
 110 }
 111 
 112 static void ar7_wdt_disable(u32 value)
 113 {
 114         WRITE_REG(ar7_wdt->disable_lock, 0x7777);
 115         if ((READ_REG(ar7_wdt->disable_lock) & 3) == 1) {
 116                 WRITE_REG(ar7_wdt->disable_lock, 0xcccc);
 117                 if ((READ_REG(ar7_wdt->disable_lock) & 3) == 2) {
 118                         WRITE_REG(ar7_wdt->disable_lock, 0xdddd);
 119                         if ((READ_REG(ar7_wdt->disable_lock) & 3) == 3) {
 120                                 WRITE_REG(ar7_wdt->disable, value);
 121                                 return;
 122                         }
 123                 }
 124         }
 125         pr_err("failed to unlock WDT disable reg\n");
 126 }
 127 
 128 static void ar7_wdt_update_margin(int new_margin)
 129 {
 130         u32 change;
 131         u32 vbus_rate;
 132 
 133         vbus_rate = clk_get_rate(vbus_clk);
 134         change = new_margin * (vbus_rate / prescale_value);
 135         if (change < 1)
 136                 change = 1;
 137         if (change > 0xffff)
 138                 change = 0xffff;
 139         ar7_wdt_change(change);
 140         margin = change * prescale_value / vbus_rate;
 141         pr_info("timer margin %d seconds (prescale %d, change %d, freq %d)\n",
 142                 margin, prescale_value, change, vbus_rate);
 143 }
 144 
 145 static void ar7_wdt_enable_wdt(void)
 146 {
 147         pr_debug("enabling watchdog timer\n");
 148         ar7_wdt_disable(1);
 149         ar7_wdt_kick(1);
 150 }
 151 
 152 static void ar7_wdt_disable_wdt(void)
 153 {
 154         pr_debug("disabling watchdog timer\n");
 155         ar7_wdt_disable(0);
 156 }
 157 
 158 static int ar7_wdt_open(struct inode *inode, struct file *file)
 159 {
 160         /* only allow one at a time */
 161         if (test_and_set_bit(0, &wdt_is_open))
 162                 return -EBUSY;
 163         ar7_wdt_enable_wdt();
 164         expect_close = 0;
 165 
 166         return stream_open(inode, file);
 167 }
 168 
 169 static int ar7_wdt_release(struct inode *inode, struct file *file)
 170 {
 171         if (!expect_close)
 172                 pr_warn("watchdog device closed unexpectedly, will not disable the watchdog timer\n");
 173         else if (!nowayout)
 174                 ar7_wdt_disable_wdt();
 175         clear_bit(0, &wdt_is_open);
 176         return 0;
 177 }
 178 
 179 static ssize_t ar7_wdt_write(struct file *file, const char *data,
 180                              size_t len, loff_t *ppos)
 181 {
 182         /* check for a magic close character */
 183         if (len) {
 184                 size_t i;
 185 
 186                 spin_lock(&wdt_lock);
 187                 ar7_wdt_kick(1);
 188                 spin_unlock(&wdt_lock);
 189 
 190                 expect_close = 0;
 191                 for (i = 0; i < len; ++i) {
 192                         char c;
 193                         if (get_user(c, data + i))
 194                                 return -EFAULT;
 195                         if (c == 'V')
 196                                 expect_close = 1;
 197                 }
 198 
 199         }
 200         return len;
 201 }
 202 
 203 static long ar7_wdt_ioctl(struct file *file,
 204                                         unsigned int cmd, unsigned long arg)
 205 {
 206         static const struct watchdog_info ident = {
 207                 .identity = LONGNAME,
 208                 .firmware_version = 1,
 209                 .options = (WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING |
 210                                                 WDIOF_MAGICCLOSE),
 211         };
 212         int new_margin;
 213 
 214         switch (cmd) {
 215         case WDIOC_GETSUPPORT:
 216                 if (copy_to_user((struct watchdog_info *)arg, &ident,
 217                                 sizeof(ident)))
 218                         return -EFAULT;
 219                 return 0;
 220         case WDIOC_GETSTATUS:
 221         case WDIOC_GETBOOTSTATUS:
 222                 if (put_user(0, (int *)arg))
 223                         return -EFAULT;
 224                 return 0;
 225         case WDIOC_KEEPALIVE:
 226                 ar7_wdt_kick(1);
 227                 return 0;
 228         case WDIOC_SETTIMEOUT:
 229                 if (get_user(new_margin, (int *)arg))
 230                         return -EFAULT;
 231                 if (new_margin < 1)
 232                         return -EINVAL;
 233 
 234                 spin_lock(&wdt_lock);
 235                 ar7_wdt_update_margin(new_margin);
 236                 ar7_wdt_kick(1);
 237                 spin_unlock(&wdt_lock);
 238                 /* Fall through */
 239 
 240         case WDIOC_GETTIMEOUT:
 241                 if (put_user(margin, (int *)arg))
 242                         return -EFAULT;
 243                 return 0;
 244         default:
 245                 return -ENOTTY;
 246         }
 247 }
 248 
 249 static const struct file_operations ar7_wdt_fops = {
 250         .owner          = THIS_MODULE,
 251         .write          = ar7_wdt_write,
 252         .unlocked_ioctl = ar7_wdt_ioctl,
 253         .open           = ar7_wdt_open,
 254         .release        = ar7_wdt_release,
 255         .llseek         = no_llseek,
 256 };
 257 
 258 static struct miscdevice ar7_wdt_miscdev = {
 259         .minor          = WATCHDOG_MINOR,
 260         .name           = "watchdog",
 261         .fops           = &ar7_wdt_fops,
 262 };
 263 
 264 static int ar7_wdt_probe(struct platform_device *pdev)
 265 {
 266         int rc;
 267 
 268         ar7_regs_wdt =
 269                 platform_get_resource_byname(pdev, IORESOURCE_MEM, "regs");
 270         ar7_wdt = devm_ioremap_resource(&pdev->dev, ar7_regs_wdt);
 271         if (IS_ERR(ar7_wdt))
 272                 return PTR_ERR(ar7_wdt);
 273 
 274         vbus_clk = clk_get(NULL, "vbus");
 275         if (IS_ERR(vbus_clk)) {
 276                 pr_err("could not get vbus clock\n");
 277                 return PTR_ERR(vbus_clk);
 278         }
 279 
 280         ar7_wdt_disable_wdt();
 281         ar7_wdt_prescale(prescale_value);
 282         ar7_wdt_update_margin(margin);
 283 
 284         rc = misc_register(&ar7_wdt_miscdev);
 285         if (rc) {
 286                 pr_err("unable to register misc device\n");
 287                 goto out;
 288         }
 289         return 0;
 290 
 291 out:
 292         clk_put(vbus_clk);
 293         vbus_clk = NULL;
 294         return rc;
 295 }
 296 
 297 static int ar7_wdt_remove(struct platform_device *pdev)
 298 {
 299         misc_deregister(&ar7_wdt_miscdev);
 300         clk_put(vbus_clk);
 301         vbus_clk = NULL;
 302         return 0;
 303 }
 304 
 305 static void ar7_wdt_shutdown(struct platform_device *pdev)
 306 {
 307         if (!nowayout)
 308                 ar7_wdt_disable_wdt();
 309 }
 310 
 311 static struct platform_driver ar7_wdt_driver = {
 312         .probe = ar7_wdt_probe,
 313         .remove = ar7_wdt_remove,
 314         .shutdown = ar7_wdt_shutdown,
 315         .driver = {
 316                 .name = "ar7_wdt",
 317         },
 318 };
 319 
 320 module_platform_driver(ar7_wdt_driver);

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