root/drivers/watchdog/mv64x60_wdt.c

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

DEFINITIONS

This source file includes following definitions.
  1. mv64x60_wdt_toggle_wdc
  2. mv64x60_wdt_service
  3. mv64x60_wdt_handler_enable
  4. mv64x60_wdt_handler_disable
  5. mv64x60_wdt_set_timeout
  6. mv64x60_wdt_open
  7. mv64x60_wdt_release
  8. mv64x60_wdt_write
  9. mv64x60_wdt_ioctl
  10. mv64x60_wdt_probe
  11. mv64x60_wdt_remove
  12. mv64x60_wdt_init
  13. mv64x60_wdt_exit

   1 // SPDX-License-Identifier: GPL-2.0
   2 /*
   3  * mv64x60_wdt.c - MV64X60 (Marvell Discovery) watchdog userspace interface
   4  *
   5  * Author: James Chapman <jchapman@katalix.com>
   6  *
   7  * Platform-specific setup code should configure the dog to generate
   8  * interrupt or reset as required.  This code only enables/disables
   9  * and services the watchdog.
  10  *
  11  * Derived from mpc8xx_wdt.c, with the following copyright.
  12  *
  13  * 2002 (c) Florian Schirmer <jolt@tuxbox.org>
  14  */
  15 
  16 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
  17 
  18 #include <linux/fs.h>
  19 #include <linux/init.h>
  20 #include <linux/kernel.h>
  21 #include <linux/miscdevice.h>
  22 #include <linux/module.h>
  23 #include <linux/watchdog.h>
  24 #include <linux/platform_device.h>
  25 #include <linux/mv643xx.h>
  26 #include <linux/uaccess.h>
  27 #include <linux/io.h>
  28 
  29 #define MV64x60_WDT_WDC_OFFSET  0
  30 
  31 /*
  32  * The watchdog configuration register contains a pair of 2-bit fields,
  33  *   1.  a reload field, bits 27-26, which triggers a reload of
  34  *       the countdown register, and
  35  *   2.  an enable field, bits 25-24, which toggles between
  36  *       enabling and disabling the watchdog timer.
  37  * Bit 31 is a read-only field which indicates whether the
  38  * watchdog timer is currently enabled.
  39  *
  40  * The low 24 bits contain the timer reload value.
  41  */
  42 #define MV64x60_WDC_ENABLE_SHIFT        24
  43 #define MV64x60_WDC_SERVICE_SHIFT       26
  44 #define MV64x60_WDC_ENABLED_SHIFT       31
  45 
  46 #define MV64x60_WDC_ENABLED_TRUE        1
  47 #define MV64x60_WDC_ENABLED_FALSE       0
  48 
  49 /* Flags bits */
  50 #define MV64x60_WDOG_FLAG_OPENED        0
  51 
  52 static unsigned long wdt_flags;
  53 static int wdt_status;
  54 static void __iomem *mv64x60_wdt_regs;
  55 static int mv64x60_wdt_timeout;
  56 static int mv64x60_wdt_count;
  57 static unsigned int bus_clk;
  58 static char expect_close;
  59 static DEFINE_SPINLOCK(mv64x60_wdt_spinlock);
  60 
  61 static bool nowayout = WATCHDOG_NOWAYOUT;
  62 module_param(nowayout, bool, 0);
  63 MODULE_PARM_DESC(nowayout,
  64                 "Watchdog cannot be stopped once started (default="
  65                                 __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
  66 
  67 static int mv64x60_wdt_toggle_wdc(int enabled_predicate, int field_shift)
  68 {
  69         u32 data;
  70         u32 enabled;
  71         int ret = 0;
  72 
  73         spin_lock(&mv64x60_wdt_spinlock);
  74         data = readl(mv64x60_wdt_regs + MV64x60_WDT_WDC_OFFSET);
  75         enabled = (data >> MV64x60_WDC_ENABLED_SHIFT) & 1;
  76 
  77         /* only toggle the requested field if enabled state matches predicate */
  78         if ((enabled ^ enabled_predicate) == 0) {
  79                 /* We write a 1, then a 2 -- to the appropriate field */
  80                 data = (1 << field_shift) | mv64x60_wdt_count;
  81                 writel(data, mv64x60_wdt_regs + MV64x60_WDT_WDC_OFFSET);
  82 
  83                 data = (2 << field_shift) | mv64x60_wdt_count;
  84                 writel(data, mv64x60_wdt_regs + MV64x60_WDT_WDC_OFFSET);
  85                 ret = 1;
  86         }
  87         spin_unlock(&mv64x60_wdt_spinlock);
  88 
  89         return ret;
  90 }
  91 
  92 static void mv64x60_wdt_service(void)
  93 {
  94         mv64x60_wdt_toggle_wdc(MV64x60_WDC_ENABLED_TRUE,
  95                                MV64x60_WDC_SERVICE_SHIFT);
  96 }
  97 
  98 static void mv64x60_wdt_handler_enable(void)
  99 {
 100         if (mv64x60_wdt_toggle_wdc(MV64x60_WDC_ENABLED_FALSE,
 101                                    MV64x60_WDC_ENABLE_SHIFT)) {
 102                 mv64x60_wdt_service();
 103                 pr_notice("watchdog activated\n");
 104         }
 105 }
 106 
 107 static void mv64x60_wdt_handler_disable(void)
 108 {
 109         if (mv64x60_wdt_toggle_wdc(MV64x60_WDC_ENABLED_TRUE,
 110                                    MV64x60_WDC_ENABLE_SHIFT))
 111                 pr_notice("watchdog deactivated\n");
 112 }
 113 
 114 static void mv64x60_wdt_set_timeout(unsigned int timeout)
 115 {
 116         /* maximum bus cycle count is 0xFFFFFFFF */
 117         if (timeout > 0xFFFFFFFF / bus_clk)
 118                 timeout = 0xFFFFFFFF / bus_clk;
 119 
 120         mv64x60_wdt_count = timeout * bus_clk >> 8;
 121         mv64x60_wdt_timeout = timeout;
 122 }
 123 
 124 static int mv64x60_wdt_open(struct inode *inode, struct file *file)
 125 {
 126         if (test_and_set_bit(MV64x60_WDOG_FLAG_OPENED, &wdt_flags))
 127                 return -EBUSY;
 128 
 129         if (nowayout)
 130                 __module_get(THIS_MODULE);
 131 
 132         mv64x60_wdt_handler_enable();
 133 
 134         return stream_open(inode, file);
 135 }
 136 
 137 static int mv64x60_wdt_release(struct inode *inode, struct file *file)
 138 {
 139         if (expect_close == 42)
 140                 mv64x60_wdt_handler_disable();
 141         else {
 142                 pr_crit("unexpected close, not stopping timer!\n");
 143                 mv64x60_wdt_service();
 144         }
 145         expect_close = 0;
 146 
 147         clear_bit(MV64x60_WDOG_FLAG_OPENED, &wdt_flags);
 148 
 149         return 0;
 150 }
 151 
 152 static ssize_t mv64x60_wdt_write(struct file *file, const char __user *data,
 153                                  size_t len, loff_t *ppos)
 154 {
 155         if (len) {
 156                 if (!nowayout) {
 157                         size_t i;
 158 
 159                         expect_close = 0;
 160 
 161                         for (i = 0; i != len; i++) {
 162                                 char c;
 163                                 if (get_user(c, data + i))
 164                                         return -EFAULT;
 165                                 if (c == 'V')
 166                                         expect_close = 42;
 167                         }
 168                 }
 169                 mv64x60_wdt_service();
 170         }
 171 
 172         return len;
 173 }
 174 
 175 static long mv64x60_wdt_ioctl(struct file *file,
 176                                         unsigned int cmd, unsigned long arg)
 177 {
 178         int timeout;
 179         int options;
 180         void __user *argp = (void __user *)arg;
 181         static const struct watchdog_info info = {
 182                 .options =      WDIOF_SETTIMEOUT        |
 183                                 WDIOF_MAGICCLOSE        |
 184                                 WDIOF_KEEPALIVEPING,
 185                 .firmware_version = 0,
 186                 .identity = "MV64x60 watchdog",
 187         };
 188 
 189         switch (cmd) {
 190         case WDIOC_GETSUPPORT:
 191                 if (copy_to_user(argp, &info, sizeof(info)))
 192                         return -EFAULT;
 193                 break;
 194 
 195         case WDIOC_GETSTATUS:
 196         case WDIOC_GETBOOTSTATUS:
 197                 if (put_user(wdt_status, (int __user *)argp))
 198                         return -EFAULT;
 199                 wdt_status &= ~WDIOF_KEEPALIVEPING;
 200                 break;
 201 
 202         case WDIOC_GETTEMP:
 203                 return -EOPNOTSUPP;
 204 
 205         case WDIOC_SETOPTIONS:
 206                 if (get_user(options, (int __user *)argp))
 207                         return -EFAULT;
 208 
 209                 if (options & WDIOS_DISABLECARD)
 210                         mv64x60_wdt_handler_disable();
 211 
 212                 if (options & WDIOS_ENABLECARD)
 213                         mv64x60_wdt_handler_enable();
 214                 break;
 215 
 216         case WDIOC_KEEPALIVE:
 217                 mv64x60_wdt_service();
 218                 wdt_status |= WDIOF_KEEPALIVEPING;
 219                 break;
 220 
 221         case WDIOC_SETTIMEOUT:
 222                 if (get_user(timeout, (int __user *)argp))
 223                         return -EFAULT;
 224                 mv64x60_wdt_set_timeout(timeout);
 225                 /* Fall through */
 226 
 227         case WDIOC_GETTIMEOUT:
 228                 if (put_user(mv64x60_wdt_timeout, (int __user *)argp))
 229                         return -EFAULT;
 230                 break;
 231 
 232         default:
 233                 return -ENOTTY;
 234         }
 235 
 236         return 0;
 237 }
 238 
 239 static const struct file_operations mv64x60_wdt_fops = {
 240         .owner = THIS_MODULE,
 241         .llseek = no_llseek,
 242         .write = mv64x60_wdt_write,
 243         .unlocked_ioctl = mv64x60_wdt_ioctl,
 244         .open = mv64x60_wdt_open,
 245         .release = mv64x60_wdt_release,
 246 };
 247 
 248 static struct miscdevice mv64x60_wdt_miscdev = {
 249         .minor = WATCHDOG_MINOR,
 250         .name = "watchdog",
 251         .fops = &mv64x60_wdt_fops,
 252 };
 253 
 254 static int mv64x60_wdt_probe(struct platform_device *dev)
 255 {
 256         struct mv64x60_wdt_pdata *pdata = dev_get_platdata(&dev->dev);
 257         struct resource *r;
 258         int timeout = 10;
 259 
 260         bus_clk = 133;                  /* in MHz */
 261         if (pdata) {
 262                 timeout = pdata->timeout;
 263                 bus_clk = pdata->bus_clk;
 264         }
 265 
 266         /* Since bus_clk is truncated MHz, actual frequency could be
 267          * up to 1MHz higher.  Round up, since it's better to time out
 268          * too late than too soon.
 269          */
 270         bus_clk++;
 271         bus_clk *= 1000000;             /* convert to Hz */
 272 
 273         r = platform_get_resource(dev, IORESOURCE_MEM, 0);
 274         if (!r)
 275                 return -ENODEV;
 276 
 277         mv64x60_wdt_regs = devm_ioremap(&dev->dev, r->start, resource_size(r));
 278         if (mv64x60_wdt_regs == NULL)
 279                 return -ENOMEM;
 280 
 281         mv64x60_wdt_set_timeout(timeout);
 282 
 283         mv64x60_wdt_handler_disable();  /* in case timer was already running */
 284 
 285         return misc_register(&mv64x60_wdt_miscdev);
 286 }
 287 
 288 static int mv64x60_wdt_remove(struct platform_device *dev)
 289 {
 290         misc_deregister(&mv64x60_wdt_miscdev);
 291 
 292         mv64x60_wdt_handler_disable();
 293 
 294         return 0;
 295 }
 296 
 297 static struct platform_driver mv64x60_wdt_driver = {
 298         .probe = mv64x60_wdt_probe,
 299         .remove = mv64x60_wdt_remove,
 300         .driver = {
 301                 .name = MV64x60_WDT_NAME,
 302         },
 303 };
 304 
 305 static int __init mv64x60_wdt_init(void)
 306 {
 307         pr_info("MV64x60 watchdog driver\n");
 308 
 309         return platform_driver_register(&mv64x60_wdt_driver);
 310 }
 311 
 312 static void __exit mv64x60_wdt_exit(void)
 313 {
 314         platform_driver_unregister(&mv64x60_wdt_driver);
 315 }
 316 
 317 module_init(mv64x60_wdt_init);
 318 module_exit(mv64x60_wdt_exit);
 319 
 320 MODULE_AUTHOR("James Chapman <jchapman@katalix.com>");
 321 MODULE_DESCRIPTION("MV64x60 watchdog driver");
 322 MODULE_LICENSE("GPL");
 323 MODULE_ALIAS("platform:" MV64x60_WDT_NAME);

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