linux/drivers/watchdog/sp805_wdt.c
<<
>>
Prefs
   1/*
   2 * drivers/char/watchdog/sp805-wdt.c
   3 *
   4 * Watchdog driver for ARM SP805 watchdog module
   5 *
   6 * Copyright (C) 2010 ST Microelectronics
   7 * Viresh Kumar<viresh.kumar@st.com>
   8 *
   9 * This file is licensed under the terms of the GNU General Public
  10 * License version 2 or later. This program is licensed "as is" without any
  11 * warranty of any kind, whether express or implied.
  12 */
  13
  14#include <linux/device.h>
  15#include <linux/resource.h>
  16#include <linux/amba/bus.h>
  17#include <linux/bitops.h>
  18#include <linux/clk.h>
  19#include <linux/fs.h>
  20#include <linux/init.h>
  21#include <linux/io.h>
  22#include <linux/ioport.h>
  23#include <linux/kernel.h>
  24#include <linux/math64.h>
  25#include <linux/miscdevice.h>
  26#include <linux/module.h>
  27#include <linux/moduleparam.h>
  28#include <linux/pm.h>
  29#include <linux/slab.h>
  30#include <linux/spinlock.h>
  31#include <linux/types.h>
  32#include <linux/uaccess.h>
  33#include <linux/watchdog.h>
  34
  35/* default timeout in seconds */
  36#define DEFAULT_TIMEOUT         60
  37
  38#define MODULE_NAME             "sp805-wdt"
  39
  40/* watchdog register offsets and masks */
  41#define WDTLOAD                 0x000
  42        #define LOAD_MIN        0x00000001
  43        #define LOAD_MAX        0xFFFFFFFF
  44#define WDTVALUE                0x004
  45#define WDTCONTROL              0x008
  46        /* control register masks */
  47        #define INT_ENABLE      (1 << 0)
  48        #define RESET_ENABLE    (1 << 1)
  49#define WDTINTCLR               0x00C
  50#define WDTRIS                  0x010
  51#define WDTMIS                  0x014
  52        #define INT_MASK        (1 << 0)
  53#define WDTLOCK                 0xC00
  54        #define UNLOCK          0x1ACCE551
  55        #define LOCK            0x00000001
  56
  57/**
  58 * struct sp805_wdt: sp805 wdt device structure
  59 * @lock: spin lock protecting dev structure and io access
  60 * @base: base address of wdt
  61 * @clk: clock structure of wdt
  62 * @adev: amba device structure of wdt
  63 * @status: current status of wdt
  64 * @load_val: load value to be set for current timeout
  65 * @timeout: current programmed timeout
  66 */
  67struct sp805_wdt {
  68        spinlock_t                      lock;
  69        void __iomem                    *base;
  70        struct clk                      *clk;
  71        struct amba_device              *adev;
  72        unsigned long                   status;
  73        #define WDT_BUSY                0
  74        #define WDT_CAN_BE_CLOSED       1
  75        unsigned int                    load_val;
  76        unsigned int                    timeout;
  77};
  78
  79/* local variables */
  80static struct sp805_wdt *wdt;
  81static bool nowayout = WATCHDOG_NOWAYOUT;
  82
  83/* This routine finds load value that will reset system in required timout */
  84static void wdt_setload(unsigned int timeout)
  85{
  86        u64 load, rate;
  87
  88        rate = clk_get_rate(wdt->clk);
  89
  90        /*
  91         * sp805 runs counter with given value twice, after the end of first
  92         * counter it gives an interrupt and then starts counter again. If
  93         * interrupt already occurred then it resets the system. This is why
  94         * load is half of what should be required.
  95         */
  96        load = div_u64(rate, 2) * timeout - 1;
  97
  98        load = (load > LOAD_MAX) ? LOAD_MAX : load;
  99        load = (load < LOAD_MIN) ? LOAD_MIN : load;
 100
 101        spin_lock(&wdt->lock);
 102        wdt->load_val = load;
 103        /* roundup timeout to closest positive integer value */
 104        wdt->timeout = div_u64((load + 1) * 2 + (rate / 2), rate);
 105        spin_unlock(&wdt->lock);
 106}
 107
 108/* returns number of seconds left for reset to occur */
 109static u32 wdt_timeleft(void)
 110{
 111        u64 load, rate;
 112
 113        rate = clk_get_rate(wdt->clk);
 114
 115        spin_lock(&wdt->lock);
 116        load = readl_relaxed(wdt->base + WDTVALUE);
 117
 118        /*If the interrupt is inactive then time left is WDTValue + WDTLoad. */
 119        if (!(readl_relaxed(wdt->base + WDTRIS) & INT_MASK))
 120                load += wdt->load_val + 1;
 121        spin_unlock(&wdt->lock);
 122
 123        return div_u64(load, rate);
 124}
 125
 126/* enables watchdog timers reset */
 127static void wdt_enable(void)
 128{
 129        spin_lock(&wdt->lock);
 130
 131        writel_relaxed(UNLOCK, wdt->base + WDTLOCK);
 132        writel_relaxed(wdt->load_val, wdt->base + WDTLOAD);
 133        writel_relaxed(INT_MASK, wdt->base + WDTINTCLR);
 134        writel_relaxed(INT_ENABLE | RESET_ENABLE, wdt->base + WDTCONTROL);
 135        writel_relaxed(LOCK, wdt->base + WDTLOCK);
 136
 137        /* Flush posted writes. */
 138        readl_relaxed(wdt->base + WDTLOCK);
 139        spin_unlock(&wdt->lock);
 140}
 141
 142/* disables watchdog timers reset */
 143static void wdt_disable(void)
 144{
 145        spin_lock(&wdt->lock);
 146
 147        writel_relaxed(UNLOCK, wdt->base + WDTLOCK);
 148        writel_relaxed(0, wdt->base + WDTCONTROL);
 149        writel_relaxed(LOCK, wdt->base + WDTLOCK);
 150
 151        /* Flush posted writes. */
 152        readl_relaxed(wdt->base + WDTLOCK);
 153        spin_unlock(&wdt->lock);
 154}
 155
 156static ssize_t sp805_wdt_write(struct file *file, const char *data,
 157                size_t len, loff_t *ppos)
 158{
 159        if (len) {
 160                if (!nowayout) {
 161                        size_t i;
 162
 163                        clear_bit(WDT_CAN_BE_CLOSED, &wdt->status);
 164
 165                        for (i = 0; i != len; i++) {
 166                                char c;
 167
 168                                if (get_user(c, data + i))
 169                                        return -EFAULT;
 170                                /* Check for Magic Close character */
 171                                if (c == 'V') {
 172                                        set_bit(WDT_CAN_BE_CLOSED,
 173                                                        &wdt->status);
 174                                        break;
 175                                }
 176                        }
 177                }
 178                wdt_enable();
 179        }
 180        return len;
 181}
 182
 183static const struct watchdog_info ident = {
 184        .options = WDIOF_MAGICCLOSE | WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING,
 185        .identity = MODULE_NAME,
 186};
 187
 188static long sp805_wdt_ioctl(struct file *file, unsigned int cmd,
 189                unsigned long arg)
 190{
 191        int ret = -ENOTTY;
 192        unsigned int timeout;
 193
 194        switch (cmd) {
 195        case WDIOC_GETSUPPORT:
 196                ret = copy_to_user((struct watchdog_info *)arg, &ident,
 197                                sizeof(ident)) ? -EFAULT : 0;
 198                break;
 199
 200        case WDIOC_GETSTATUS:
 201                ret = put_user(0, (int *)arg);
 202                break;
 203
 204        case WDIOC_KEEPALIVE:
 205                wdt_enable();
 206                ret = 0;
 207                break;
 208
 209        case WDIOC_SETTIMEOUT:
 210                ret = get_user(timeout, (unsigned int *)arg);
 211                if (ret)
 212                        break;
 213
 214                wdt_setload(timeout);
 215
 216                wdt_enable();
 217                /* Fall through */
 218
 219        case WDIOC_GETTIMEOUT:
 220                ret = put_user(wdt->timeout, (unsigned int *)arg);
 221                break;
 222        case WDIOC_GETTIMELEFT:
 223                ret = put_user(wdt_timeleft(), (unsigned int *)arg);
 224                break;
 225        }
 226        return ret;
 227}
 228
 229static int sp805_wdt_open(struct inode *inode, struct file *file)
 230{
 231        int ret = 0;
 232
 233        if (test_and_set_bit(WDT_BUSY, &wdt->status))
 234                return -EBUSY;
 235
 236        ret = clk_enable(wdt->clk);
 237        if (ret) {
 238                dev_err(&wdt->adev->dev, "clock enable fail");
 239                goto err;
 240        }
 241
 242        wdt_enable();
 243
 244        /* can not be closed, once enabled */
 245        clear_bit(WDT_CAN_BE_CLOSED, &wdt->status);
 246        return nonseekable_open(inode, file);
 247
 248err:
 249        clear_bit(WDT_BUSY, &wdt->status);
 250        return ret;
 251}
 252
 253static int sp805_wdt_release(struct inode *inode, struct file *file)
 254{
 255        if (!test_bit(WDT_CAN_BE_CLOSED, &wdt->status)) {
 256                clear_bit(WDT_BUSY, &wdt->status);
 257                dev_warn(&wdt->adev->dev, "Device closed unexpectedly\n");
 258                return 0;
 259        }
 260
 261        wdt_disable();
 262        clk_disable(wdt->clk);
 263        clear_bit(WDT_BUSY, &wdt->status);
 264
 265        return 0;
 266}
 267
 268static const struct file_operations sp805_wdt_fops = {
 269        .owner = THIS_MODULE,
 270        .llseek = no_llseek,
 271        .write = sp805_wdt_write,
 272        .unlocked_ioctl = sp805_wdt_ioctl,
 273        .open = sp805_wdt_open,
 274        .release = sp805_wdt_release,
 275};
 276
 277static struct miscdevice sp805_wdt_miscdev = {
 278        .minor = WATCHDOG_MINOR,
 279        .name = "watchdog",
 280        .fops = &sp805_wdt_fops,
 281};
 282
 283static int __devinit
 284sp805_wdt_probe(struct amba_device *adev, const struct amba_id *id)
 285{
 286        int ret = 0;
 287
 288        if (!devm_request_mem_region(&adev->dev, adev->res.start,
 289                                resource_size(&adev->res), "sp805_wdt")) {
 290                dev_warn(&adev->dev, "Failed to get memory region resource\n");
 291                ret = -ENOENT;
 292                goto err;
 293        }
 294
 295        wdt = devm_kzalloc(&adev->dev, sizeof(*wdt), GFP_KERNEL);
 296        if (!wdt) {
 297                dev_warn(&adev->dev, "Kzalloc failed\n");
 298                ret = -ENOMEM;
 299                goto err;
 300        }
 301
 302        wdt->base = devm_ioremap(&adev->dev, adev->res.start,
 303                        resource_size(&adev->res));
 304        if (!wdt->base) {
 305                ret = -ENOMEM;
 306                dev_warn(&adev->dev, "ioremap fail\n");
 307                goto err;
 308        }
 309
 310        wdt->clk = clk_get(&adev->dev, NULL);
 311        if (IS_ERR(wdt->clk)) {
 312                dev_warn(&adev->dev, "Clock not found\n");
 313                ret = PTR_ERR(wdt->clk);
 314                goto err;
 315        }
 316
 317        wdt->adev = adev;
 318        spin_lock_init(&wdt->lock);
 319        wdt_setload(DEFAULT_TIMEOUT);
 320
 321        ret = misc_register(&sp805_wdt_miscdev);
 322        if (ret < 0) {
 323                dev_warn(&adev->dev, "cannot register misc device\n");
 324                goto err_misc_register;
 325        }
 326
 327        dev_info(&adev->dev, "registration successful\n");
 328        return 0;
 329
 330err_misc_register:
 331        clk_put(wdt->clk);
 332err:
 333        dev_err(&adev->dev, "Probe Failed!!!\n");
 334        return ret;
 335}
 336
 337static int __devexit sp805_wdt_remove(struct amba_device *adev)
 338{
 339        misc_deregister(&sp805_wdt_miscdev);
 340        clk_put(wdt->clk);
 341
 342        return 0;
 343}
 344
 345#ifdef CONFIG_PM
 346static int sp805_wdt_suspend(struct device *dev)
 347{
 348        if (test_bit(WDT_BUSY, &wdt->status)) {
 349                wdt_disable();
 350                clk_disable(wdt->clk);
 351        }
 352
 353        return 0;
 354}
 355
 356static int sp805_wdt_resume(struct device *dev)
 357{
 358        int ret = 0;
 359
 360        if (test_bit(WDT_BUSY, &wdt->status)) {
 361                ret = clk_enable(wdt->clk);
 362                if (ret) {
 363                        dev_err(dev, "clock enable fail");
 364                        return ret;
 365                }
 366                wdt_enable();
 367        }
 368
 369        return ret;
 370}
 371#endif /* CONFIG_PM */
 372
 373static SIMPLE_DEV_PM_OPS(sp805_wdt_dev_pm_ops, sp805_wdt_suspend,
 374                sp805_wdt_resume);
 375
 376static struct amba_id sp805_wdt_ids[] = {
 377        {
 378                .id     = 0x00141805,
 379                .mask   = 0x00ffffff,
 380        },
 381        { 0, 0 },
 382};
 383
 384MODULE_DEVICE_TABLE(amba, sp805_wdt_ids);
 385
 386static struct amba_driver sp805_wdt_driver = {
 387        .drv = {
 388                .name   = MODULE_NAME,
 389                .pm     = &sp805_wdt_dev_pm_ops,
 390        },
 391        .id_table       = sp805_wdt_ids,
 392        .probe          = sp805_wdt_probe,
 393        .remove = __devexit_p(sp805_wdt_remove),
 394};
 395
 396module_amba_driver(sp805_wdt_driver);
 397
 398module_param(nowayout, bool, 0);
 399MODULE_PARM_DESC(nowayout,
 400                "Set to 1 to keep watchdog running after device release");
 401
 402MODULE_AUTHOR("Viresh Kumar <viresh.kumar@st.com>");
 403MODULE_DESCRIPTION("ARM SP805 Watchdog Driver");
 404MODULE_LICENSE("GPL");
 405MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
 406