linux/drivers/watchdog/iop_wdt.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 * drivers/char/watchdog/iop_wdt.c
   4 *
   5 * WDT driver for Intel I/O Processors
   6 * Copyright (C) 2005, Intel Corporation.
   7 *
   8 * Based on ixp4xx driver, Copyright 2004 (c) MontaVista, Software, Inc.
   9 *
  10 *      Curt E Bruns <curt.e.bruns@intel.com>
  11 *      Peter Milne <peter.milne@d-tacq.com>
  12 *      Dan Williams <dan.j.williams@intel.com>
  13 */
  14
  15#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
  16
  17#include <linux/module.h>
  18#include <linux/kernel.h>
  19#include <linux/fs.h>
  20#include <linux/init.h>
  21#include <linux/device.h>
  22#include <linux/miscdevice.h>
  23#include <linux/watchdog.h>
  24#include <linux/uaccess.h>
  25#include <mach/hardware.h>
  26
  27static bool nowayout = WATCHDOG_NOWAYOUT;
  28static unsigned long wdt_status;
  29static unsigned long boot_status;
  30static DEFINE_SPINLOCK(wdt_lock);
  31
  32#define WDT_IN_USE              0
  33#define WDT_OK_TO_CLOSE         1
  34#define WDT_ENABLED             2
  35
  36static unsigned long iop_watchdog_timeout(void)
  37{
  38        return (0xffffffffUL / get_iop_tick_rate());
  39}
  40
  41/**
  42 * wdt_supports_disable - determine if we are accessing a iop13xx watchdog
  43 * or iop3xx by whether it has a disable command
  44 */
  45static int wdt_supports_disable(void)
  46{
  47        int can_disable;
  48
  49        if (IOP_WDTCR_EN_ARM != IOP_WDTCR_DIS_ARM)
  50                can_disable = 1;
  51        else
  52                can_disable = 0;
  53
  54        return can_disable;
  55}
  56
  57static void wdt_enable(void)
  58{
  59        /* Arm and enable the Timer to starting counting down from 0xFFFF.FFFF
  60         * Takes approx. 10.7s to timeout
  61         */
  62        spin_lock(&wdt_lock);
  63        write_wdtcr(IOP_WDTCR_EN_ARM);
  64        write_wdtcr(IOP_WDTCR_EN);
  65        spin_unlock(&wdt_lock);
  66}
  67
  68/* returns 0 if the timer was successfully disabled */
  69static int wdt_disable(void)
  70{
  71        /* Stop Counting */
  72        if (wdt_supports_disable()) {
  73                spin_lock(&wdt_lock);
  74                write_wdtcr(IOP_WDTCR_DIS_ARM);
  75                write_wdtcr(IOP_WDTCR_DIS);
  76                clear_bit(WDT_ENABLED, &wdt_status);
  77                spin_unlock(&wdt_lock);
  78                pr_info("Disabled\n");
  79                return 0;
  80        } else
  81                return 1;
  82}
  83
  84static int iop_wdt_open(struct inode *inode, struct file *file)
  85{
  86        if (test_and_set_bit(WDT_IN_USE, &wdt_status))
  87                return -EBUSY;
  88
  89        clear_bit(WDT_OK_TO_CLOSE, &wdt_status);
  90        wdt_enable();
  91        set_bit(WDT_ENABLED, &wdt_status);
  92        return stream_open(inode, file);
  93}
  94
  95static ssize_t iop_wdt_write(struct file *file, const char *data, size_t len,
  96                  loff_t *ppos)
  97{
  98        if (len) {
  99                if (!nowayout) {
 100                        size_t i;
 101
 102                        clear_bit(WDT_OK_TO_CLOSE, &wdt_status);
 103
 104                        for (i = 0; i != len; i++) {
 105                                char c;
 106
 107                                if (get_user(c, data + i))
 108                                        return -EFAULT;
 109                                if (c == 'V')
 110                                        set_bit(WDT_OK_TO_CLOSE, &wdt_status);
 111                        }
 112                }
 113                wdt_enable();
 114        }
 115        return len;
 116}
 117
 118static const struct watchdog_info ident = {
 119        .options = WDIOF_CARDRESET | WDIOF_MAGICCLOSE | WDIOF_KEEPALIVEPING,
 120        .identity = "iop watchdog",
 121};
 122
 123static long iop_wdt_ioctl(struct file *file,
 124                                unsigned int cmd, unsigned long arg)
 125{
 126        int options;
 127        int ret = -ENOTTY;
 128        int __user *argp = (int __user *)arg;
 129
 130        switch (cmd) {
 131        case WDIOC_GETSUPPORT:
 132                if (copy_to_user(argp, &ident, sizeof(ident)))
 133                        ret = -EFAULT;
 134                else
 135                        ret = 0;
 136                break;
 137
 138        case WDIOC_GETSTATUS:
 139                ret = put_user(0, argp);
 140                break;
 141
 142        case WDIOC_GETBOOTSTATUS:
 143                ret = put_user(boot_status, argp);
 144                break;
 145
 146        case WDIOC_SETOPTIONS:
 147                if (get_user(options, (int *)arg))
 148                        return -EFAULT;
 149
 150                if (options & WDIOS_DISABLECARD) {
 151                        if (!nowayout) {
 152                                if (wdt_disable() == 0) {
 153                                        set_bit(WDT_OK_TO_CLOSE, &wdt_status);
 154                                        ret = 0;
 155                                } else
 156                                        ret = -ENXIO;
 157                        } else
 158                                ret = 0;
 159                }
 160                if (options & WDIOS_ENABLECARD) {
 161                        wdt_enable();
 162                        ret = 0;
 163                }
 164                break;
 165
 166        case WDIOC_KEEPALIVE:
 167                wdt_enable();
 168                ret = 0;
 169                break;
 170
 171        case WDIOC_GETTIMEOUT:
 172                ret = put_user(iop_watchdog_timeout(), argp);
 173                break;
 174        }
 175        return ret;
 176}
 177
 178static int iop_wdt_release(struct inode *inode, struct file *file)
 179{
 180        int state = 1;
 181        if (test_bit(WDT_OK_TO_CLOSE, &wdt_status))
 182                if (test_bit(WDT_ENABLED, &wdt_status))
 183                        state = wdt_disable();
 184
 185        /* if the timer is not disabled reload and notify that we are still
 186         * going down
 187         */
 188        if (state != 0) {
 189                wdt_enable();
 190                pr_crit("Device closed unexpectedly - reset in %lu seconds\n",
 191                        iop_watchdog_timeout());
 192        }
 193
 194        clear_bit(WDT_IN_USE, &wdt_status);
 195        clear_bit(WDT_OK_TO_CLOSE, &wdt_status);
 196
 197        return 0;
 198}
 199
 200static const struct file_operations iop_wdt_fops = {
 201        .owner = THIS_MODULE,
 202        .llseek = no_llseek,
 203        .write = iop_wdt_write,
 204        .unlocked_ioctl = iop_wdt_ioctl,
 205        .compat_ioctl = compat_ptr_ioctl,
 206        .open = iop_wdt_open,
 207        .release = iop_wdt_release,
 208};
 209
 210static struct miscdevice iop_wdt_miscdev = {
 211        .minor = WATCHDOG_MINOR,
 212        .name = "watchdog",
 213        .fops = &iop_wdt_fops,
 214};
 215
 216static int __init iop_wdt_init(void)
 217{
 218        int ret;
 219
 220        /* check if the reset was caused by the watchdog timer */
 221        boot_status = (read_rcsr() & IOP_RCSR_WDT) ? WDIOF_CARDRESET : 0;
 222
 223        /* Configure Watchdog Timeout to cause an Internal Bus (IB) Reset
 224         * NOTE: An IB Reset will Reset both cores in the IOP342
 225         */
 226        write_wdtsr(IOP13XX_WDTCR_IB_RESET);
 227
 228        /* Register after we have the device set up so we cannot race
 229           with an open */
 230        ret = misc_register(&iop_wdt_miscdev);
 231        if (ret == 0)
 232                pr_info("timeout %lu sec\n", iop_watchdog_timeout());
 233
 234        return ret;
 235}
 236
 237static void __exit iop_wdt_exit(void)
 238{
 239        misc_deregister(&iop_wdt_miscdev);
 240}
 241
 242module_init(iop_wdt_init);
 243module_exit(iop_wdt_exit);
 244
 245module_param(nowayout, bool, 0);
 246MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started");
 247
 248MODULE_AUTHOR("Curt E Bruns <curt.e.bruns@intel.com>");
 249MODULE_DESCRIPTION("iop watchdog timer driver");
 250MODULE_LICENSE("GPL");
 251