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