linux/drivers/watchdog/ixp4xx_wdt.c
<<
>>
Prefs
   1/*
   2 * drivers/char/watchdog/ixp4xx_wdt.c
   3 *
   4 * Watchdog driver for Intel IXP4xx network processors
   5 *
   6 * Author: Deepak Saxena <dsaxena@plexity.net>
   7 *
   8 * Copyright 2004 (c) MontaVista, Software, Inc.
   9 * Based on sa1100 driver, Copyright (C) 2000 Oleg Drokin <green@crimea.edu>
  10 *
  11 * This file is licensed under  the terms of the GNU General Public
  12 * License version 2. This program is licensed "as is" without any
  13 * warranty of any kind, whether express or implied.
  14 */
  15
  16#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
  17
  18#include <linux/module.h>
  19#include <linux/moduleparam.h>
  20#include <linux/types.h>
  21#include <linux/kernel.h>
  22#include <linux/fs.h>
  23#include <linux/miscdevice.h>
  24#include <linux/of.h>
  25#include <linux/watchdog.h>
  26#include <linux/init.h>
  27#include <linux/bitops.h>
  28#include <linux/uaccess.h>
  29#include <mach/hardware.h>
  30
  31static bool nowayout = WATCHDOG_NOWAYOUT;
  32static int heartbeat = 60;      /* (secs) Default is 1 minute */
  33static unsigned long wdt_status;
  34static unsigned long boot_status;
  35static DEFINE_SPINLOCK(wdt_lock);
  36
  37#define WDT_TICK_RATE (IXP4XX_PERIPHERAL_BUS_CLOCK * 1000000UL)
  38
  39#define WDT_IN_USE              0
  40#define WDT_OK_TO_CLOSE         1
  41
  42static void wdt_enable(void)
  43{
  44        spin_lock(&wdt_lock);
  45        *IXP4XX_OSWK = IXP4XX_WDT_KEY;
  46        *IXP4XX_OSWE = 0;
  47        *IXP4XX_OSWT = WDT_TICK_RATE * heartbeat;
  48        *IXP4XX_OSWE = IXP4XX_WDT_COUNT_ENABLE | IXP4XX_WDT_RESET_ENABLE;
  49        *IXP4XX_OSWK = 0;
  50        spin_unlock(&wdt_lock);
  51}
  52
  53static void wdt_disable(void)
  54{
  55        spin_lock(&wdt_lock);
  56        *IXP4XX_OSWK = IXP4XX_WDT_KEY;
  57        *IXP4XX_OSWE = 0;
  58        *IXP4XX_OSWK = 0;
  59        spin_unlock(&wdt_lock);
  60}
  61
  62static int ixp4xx_wdt_open(struct inode *inode, struct file *file)
  63{
  64        if (test_and_set_bit(WDT_IN_USE, &wdt_status))
  65                return -EBUSY;
  66
  67        clear_bit(WDT_OK_TO_CLOSE, &wdt_status);
  68        wdt_enable();
  69        return stream_open(inode, file);
  70}
  71
  72static ssize_t
  73ixp4xx_wdt_write(struct file *file, const char *data, size_t len, loff_t *ppos)
  74{
  75        if (len) {
  76                if (!nowayout) {
  77                        size_t i;
  78
  79                        clear_bit(WDT_OK_TO_CLOSE, &wdt_status);
  80
  81                        for (i = 0; i != len; i++) {
  82                                char c;
  83
  84                                if (get_user(c, data + i))
  85                                        return -EFAULT;
  86                                if (c == 'V')
  87                                        set_bit(WDT_OK_TO_CLOSE, &wdt_status);
  88                        }
  89                }
  90                wdt_enable();
  91        }
  92        return len;
  93}
  94
  95static const struct watchdog_info ident = {
  96        .options        = WDIOF_CARDRESET | WDIOF_MAGICCLOSE |
  97                          WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING,
  98        .identity       = "IXP4xx Watchdog",
  99};
 100
 101
 102static long ixp4xx_wdt_ioctl(struct file *file, unsigned int cmd,
 103                                                        unsigned long arg)
 104{
 105        int ret = -ENOTTY;
 106        int time;
 107
 108        switch (cmd) {
 109        case WDIOC_GETSUPPORT:
 110                ret = copy_to_user((struct watchdog_info *)arg, &ident,
 111                                   sizeof(ident)) ? -EFAULT : 0;
 112                break;
 113
 114        case WDIOC_GETSTATUS:
 115                ret = put_user(0, (int *)arg);
 116                break;
 117
 118        case WDIOC_GETBOOTSTATUS:
 119                ret = put_user(boot_status, (int *)arg);
 120                break;
 121
 122        case WDIOC_KEEPALIVE:
 123                wdt_enable();
 124                ret = 0;
 125                break;
 126
 127        case WDIOC_SETTIMEOUT:
 128                ret = get_user(time, (int *)arg);
 129                if (ret)
 130                        break;
 131
 132                if (time <= 0 || time > 60) {
 133                        ret = -EINVAL;
 134                        break;
 135                }
 136
 137                heartbeat = time;
 138                wdt_enable();
 139                fallthrough;
 140
 141        case WDIOC_GETTIMEOUT:
 142                ret = put_user(heartbeat, (int *)arg);
 143                break;
 144        }
 145        return ret;
 146}
 147
 148static int ixp4xx_wdt_release(struct inode *inode, struct file *file)
 149{
 150        if (test_bit(WDT_OK_TO_CLOSE, &wdt_status))
 151                wdt_disable();
 152        else
 153                pr_crit("Device closed unexpectedly - timer will not stop\n");
 154        clear_bit(WDT_IN_USE, &wdt_status);
 155        clear_bit(WDT_OK_TO_CLOSE, &wdt_status);
 156
 157        return 0;
 158}
 159
 160
 161static const struct file_operations ixp4xx_wdt_fops = {
 162        .owner          = THIS_MODULE,
 163        .llseek         = no_llseek,
 164        .write          = ixp4xx_wdt_write,
 165        .unlocked_ioctl = ixp4xx_wdt_ioctl,
 166        .compat_ioctl   = compat_ptr_ioctl,
 167        .open           = ixp4xx_wdt_open,
 168        .release        = ixp4xx_wdt_release,
 169};
 170
 171static struct miscdevice ixp4xx_wdt_miscdev = {
 172        .minor          = WATCHDOG_MINOR,
 173        .name           = "watchdog",
 174        .fops           = &ixp4xx_wdt_fops,
 175};
 176
 177static int __init ixp4xx_wdt_init(void)
 178{
 179        int ret;
 180
 181        /*
 182         * FIXME: we bail out on device tree boot but this really needs
 183         * to be fixed in a nicer way: this registers the MDIO bus before
 184         * even matching the driver infrastructure, we should only probe
 185         * detected hardware.
 186         */
 187        if (of_have_populated_dt())
 188                return -ENODEV;
 189        if (!(read_cpuid_id() & 0xf) && !cpu_is_ixp46x()) {
 190                pr_err("Rev. A0 IXP42x CPU detected - watchdog disabled\n");
 191
 192                return -ENODEV;
 193        }
 194        boot_status = (*IXP4XX_OSST & IXP4XX_OSST_TIMER_WARM_RESET) ?
 195                        WDIOF_CARDRESET : 0;
 196        ret = misc_register(&ixp4xx_wdt_miscdev);
 197        if (ret == 0)
 198                pr_info("timer heartbeat %d sec\n", heartbeat);
 199        return ret;
 200}
 201
 202static void __exit ixp4xx_wdt_exit(void)
 203{
 204        misc_deregister(&ixp4xx_wdt_miscdev);
 205}
 206
 207
 208module_init(ixp4xx_wdt_init);
 209module_exit(ixp4xx_wdt_exit);
 210
 211MODULE_AUTHOR("Deepak Saxena <dsaxena@plexity.net>");
 212MODULE_DESCRIPTION("IXP4xx Network Processor Watchdog");
 213
 214module_param(heartbeat, int, 0);
 215MODULE_PARM_DESC(heartbeat, "Watchdog heartbeat in seconds (default 60s)");
 216
 217module_param(nowayout, bool, 0);
 218MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started");
 219
 220MODULE_LICENSE("GPL");
 221