linux/drivers/watchdog/booke_wdt.c
<<
>>
Prefs
   1/*
   2 * Watchdog timer for PowerPC Book-E systems
   3 *
   4 * Author: Matthew McClintock
   5 * Maintainer: Kumar Gala <galak@kernel.crashing.org>
   6 *
   7 * Copyright 2005, 2008 Freescale Semiconductor Inc.
   8 *
   9 * This program is free software; you can redistribute  it and/or modify it
  10 * under  the terms of  the GNU General  Public License as published by the
  11 * Free Software Foundation;  either version 2 of the  License, or (at your
  12 * option) any later version.
  13 */
  14
  15#include <linux/module.h>
  16#include <linux/fs.h>
  17#include <linux/smp.h>
  18#include <linux/miscdevice.h>
  19#include <linux/notifier.h>
  20#include <linux/watchdog.h>
  21#include <linux/uaccess.h>
  22
  23#include <asm/reg_booke.h>
  24#include <asm/system.h>
  25
  26/* If the kernel parameter wdt=1, the watchdog will be enabled at boot.
  27 * Also, the wdt_period sets the watchdog timer period timeout.
  28 * For E500 cpus the wdt_period sets which bit changing from 0->1 will
  29 * trigger a watchog timeout. This watchdog timeout will occur 3 times, the
  30 * first time nothing will happen, the second time a watchdog exception will
  31 * occur, and the final time the board will reset.
  32 */
  33
  34#ifdef  CONFIG_FSL_BOOKE
  35#define WDT_PERIOD_DEFAULT 63   /* Ex. wdt_period=28 bus=333Mhz,reset=~40sec */
  36#else
  37#define WDT_PERIOD_DEFAULT 3    /* Refer to the PPC40x and PPC4xx manuals */
  38#endif                          /* for timing information */
  39
  40u32 booke_wdt_enabled;
  41u32 booke_wdt_period = WDT_PERIOD_DEFAULT;
  42
  43#ifdef  CONFIG_FSL_BOOKE
  44#define WDTP(x)         ((((63-x)&0x3)<<30)|(((63-x)&0x3c)<<15))
  45#define WDTP_MASK       (WDTP(0))
  46#else
  47#define WDTP(x)         (TCR_WP(x))
  48#define WDTP_MASK       (TCR_WP_MASK)
  49#endif
  50
  51static DEFINE_SPINLOCK(booke_wdt_lock);
  52
  53static void __booke_wdt_ping(void *data)
  54{
  55        mtspr(SPRN_TSR, TSR_ENW|TSR_WIS);
  56}
  57
  58static void booke_wdt_ping(void)
  59{
  60        on_each_cpu(__booke_wdt_ping, NULL, 0);
  61}
  62
  63static void __booke_wdt_enable(void *data)
  64{
  65        u32 val;
  66
  67        /* clear status before enabling watchdog */
  68        __booke_wdt_ping(NULL);
  69        val = mfspr(SPRN_TCR);
  70        val &= ~WDTP_MASK;
  71        val |= (TCR_WIE|TCR_WRC(WRC_CHIP)|WDTP(booke_wdt_period));
  72
  73        mtspr(SPRN_TCR, val);
  74}
  75
  76static ssize_t booke_wdt_write(struct file *file, const char __user *buf,
  77                                size_t count, loff_t *ppos)
  78{
  79        booke_wdt_ping();
  80        return count;
  81}
  82
  83static struct watchdog_info ident = {
  84        .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING,
  85        .identity = "PowerPC Book-E Watchdog",
  86};
  87
  88static long booke_wdt_ioctl(struct file *file,
  89                                unsigned int cmd, unsigned long arg)
  90{
  91        u32 tmp = 0;
  92        u32 __user *p = (u32 __user *)arg;
  93
  94        switch (cmd) {
  95        case WDIOC_GETSUPPORT:
  96                if (copy_to_user(arg, &ident, sizeof(struct watchdog_info)))
  97                        return -EFAULT;
  98        case WDIOC_GETSTATUS:
  99                return put_user(ident.options, p);
 100        case WDIOC_GETBOOTSTATUS:
 101                /* XXX: something is clearing TSR */
 102                tmp = mfspr(SPRN_TSR) & TSR_WRS(3);
 103                /* returns 1 if last reset was caused by the WDT */
 104                return (tmp ? 1 : 0);
 105        case WDIOC_SETOPTIONS:
 106                if (get_user(tmp, p))
 107                        return -EINVAL;
 108                if (tmp == WDIOS_ENABLECARD) {
 109                        booke_wdt_ping();
 110                        break;
 111                } else
 112                        return -EINVAL;
 113                return 0;
 114        case WDIOC_KEEPALIVE:
 115                booke_wdt_ping();
 116                return 0;
 117        case WDIOC_SETTIMEOUT:
 118                if (get_user(booke_wdt_period, p))
 119                        return -EFAULT;
 120                mtspr(SPRN_TCR, (mfspr(SPRN_TCR) & ~WDTP_MASK) |
 121                                                WDTP(booke_wdt_period));
 122                return 0;
 123        case WDIOC_GETTIMEOUT:
 124                return put_user(booke_wdt_period, p);
 125        default:
 126                return -ENOTTY;
 127        }
 128
 129        return 0;
 130}
 131
 132static int booke_wdt_open(struct inode *inode, struct file *file)
 133{
 134        spin_lock(&booke_wdt_lock);
 135        if (booke_wdt_enabled == 0) {
 136                booke_wdt_enabled = 1;
 137                on_each_cpu(__booke_wdt_enable, NULL, 0);
 138                printk(KERN_INFO
 139                      "PowerPC Book-E Watchdog Timer Enabled (wdt_period=%d)\n",
 140                                booke_wdt_period);
 141        }
 142        spin_unlock(&booke_wdt_lock);
 143
 144        return nonseekable_open(inode, file);
 145}
 146
 147static const struct file_operations booke_wdt_fops = {
 148        .owner = THIS_MODULE,
 149        .llseek = no_llseek,
 150        .write = booke_wdt_write,
 151        .unlocked_ioctl = booke_wdt_ioctl,
 152        .open = booke_wdt_open,
 153};
 154
 155static struct miscdevice booke_wdt_miscdev = {
 156        .minor = WATCHDOG_MINOR,
 157        .name = "watchdog",
 158        .fops = &booke_wdt_fops,
 159};
 160
 161static void __exit booke_wdt_exit(void)
 162{
 163        misc_deregister(&booke_wdt_miscdev);
 164}
 165
 166static int __init booke_wdt_init(void)
 167{
 168        int ret = 0;
 169
 170        printk(KERN_INFO "PowerPC Book-E Watchdog Timer Loaded\n");
 171        ident.firmware_version = cur_cpu_spec->pvr_value;
 172
 173        ret = misc_register(&booke_wdt_miscdev);
 174        if (ret) {
 175                printk(KERN_CRIT "Cannot register miscdev on minor=%d: %d\n",
 176                                WATCHDOG_MINOR, ret);
 177                return ret;
 178        }
 179
 180        spin_lock(&booke_wdt_lock);
 181        if (booke_wdt_enabled == 1) {
 182                printk(KERN_INFO
 183                      "PowerPC Book-E Watchdog Timer Enabled (wdt_period=%d)\n",
 184                                booke_wdt_period);
 185                on_each_cpu(__booke_wdt_enable, NULL, 0);
 186        }
 187        spin_unlock(&booke_wdt_lock);
 188
 189        return ret;
 190}
 191device_initcall(booke_wdt_init);
 192