linux/drivers/watchdog/davinci_wdt.c
<<
>>
Prefs
   1/*
   2 * drivers/char/watchdog/davinci_wdt.c
   3 *
   4 * Watchdog driver for DaVinci DM644x/DM646x processors
   5 *
   6 * Copyright (C) 2006 Texas Instruments.
   7 *
   8 * 2007 (c) MontaVista Software, Inc. This file is licensed under
   9 * the terms of the GNU General Public License version 2. This program
  10 * is licensed "as is" without any warranty of any kind, whether express
  11 * or implied.
  12 */
  13
  14#include <linux/module.h>
  15#include <linux/moduleparam.h>
  16#include <linux/types.h>
  17#include <linux/kernel.h>
  18#include <linux/fs.h>
  19#include <linux/miscdevice.h>
  20#include <linux/watchdog.h>
  21#include <linux/init.h>
  22#include <linux/bitops.h>
  23#include <linux/platform_device.h>
  24#include <linux/spinlock.h>
  25
  26#include <asm/hardware.h>
  27#include <asm/uaccess.h>
  28#include <asm/io.h>
  29
  30#define MODULE_NAME "DAVINCI-WDT: "
  31
  32#define DEFAULT_HEARTBEAT 60
  33#define MAX_HEARTBEAT     600   /* really the max margin is 264/27MHz*/
  34
  35/* Timer register set definition */
  36#define PID12   (0x0)
  37#define EMUMGT  (0x4)
  38#define TIM12   (0x10)
  39#define TIM34   (0x14)
  40#define PRD12   (0x18)
  41#define PRD34   (0x1C)
  42#define TCR     (0x20)
  43#define TGCR    (0x24)
  44#define WDTCR   (0x28)
  45
  46/* TCR bit definitions */
  47#define ENAMODE12_DISABLED      (0 << 6)
  48#define ENAMODE12_ONESHOT       (1 << 6)
  49#define ENAMODE12_PERIODIC      (2 << 6)
  50
  51/* TGCR bit definitions */
  52#define TIM12RS_UNRESET         (1 << 0)
  53#define TIM34RS_UNRESET         (1 << 1)
  54#define TIMMODE_64BIT_WDOG      (2 << 2)
  55
  56/* WDTCR bit definitions */
  57#define WDEN                    (1 << 14)
  58#define WDFLAG                  (1 << 15)
  59#define WDKEY_SEQ0              (0xa5c6 << 16)
  60#define WDKEY_SEQ1              (0xda7e << 16)
  61
  62static int heartbeat = DEFAULT_HEARTBEAT;
  63
  64static DEFINE_SPINLOCK(io_lock);
  65static unsigned long wdt_status;
  66#define WDT_IN_USE        0
  67#define WDT_OK_TO_CLOSE   1
  68#define WDT_REGION_INITED 2
  69#define WDT_DEVICE_INITED 3
  70
  71static struct resource  *wdt_mem;
  72static void __iomem     *wdt_base;
  73
  74static void wdt_service(void)
  75{
  76        spin_lock(&io_lock);
  77
  78        /* put watchdog in service state */
  79        davinci_writel(WDKEY_SEQ0, wdt_base + WDTCR);
  80        /* put watchdog in active state */
  81        davinci_writel(WDKEY_SEQ1, wdt_base + WDTCR);
  82
  83        spin_unlock(&io_lock);
  84}
  85
  86static void wdt_enable(void)
  87{
  88        u32 tgcr;
  89        u32 timer_margin;
  90
  91        spin_lock(&io_lock);
  92
  93        /* disable, internal clock source */
  94        davinci_writel(0, wdt_base + TCR);
  95        /* reset timer, set mode to 64-bit watchdog, and unreset */
  96        davinci_writel(0, wdt_base + TGCR);
  97        tgcr = TIMMODE_64BIT_WDOG | TIM12RS_UNRESET | TIM34RS_UNRESET;
  98        davinci_writel(tgcr, wdt_base + TGCR);
  99        /* clear counter regs */
 100        davinci_writel(0, wdt_base + TIM12);
 101        davinci_writel(0, wdt_base + TIM34);
 102        /* set timeout period */
 103        timer_margin = (((u64)heartbeat * CLOCK_TICK_RATE) & 0xffffffff);
 104        davinci_writel(timer_margin, wdt_base + PRD12);
 105        timer_margin = (((u64)heartbeat * CLOCK_TICK_RATE) >> 32);
 106        davinci_writel(timer_margin, wdt_base + PRD34);
 107        /* enable run continuously */
 108        davinci_writel(ENAMODE12_PERIODIC, wdt_base + TCR);
 109        /* Once the WDT is in pre-active state write to
 110         * TIM12, TIM34, PRD12, PRD34, TCR, TGCR, WDTCR are
 111         * write protected (except for the WDKEY field)
 112         */
 113        /* put watchdog in pre-active state */
 114        davinci_writel(WDKEY_SEQ0 | WDEN, wdt_base + WDTCR);
 115        /* put watchdog in active state */
 116        davinci_writel(WDKEY_SEQ1 | WDEN, wdt_base + WDTCR);
 117
 118        spin_unlock(&io_lock);
 119}
 120
 121static int davinci_wdt_open(struct inode *inode, struct file *file)
 122{
 123        if (test_and_set_bit(WDT_IN_USE, &wdt_status))
 124                return -EBUSY;
 125
 126        wdt_enable();
 127
 128        return nonseekable_open(inode, file);
 129}
 130
 131static ssize_t
 132davinci_wdt_write(struct file *file, const char *data, size_t len,
 133                  loff_t *ppos)
 134{
 135        if (len)
 136                wdt_service();
 137
 138        return len;
 139}
 140
 141static struct watchdog_info ident = {
 142        .options = WDIOF_KEEPALIVEPING,
 143        .identity = "DaVinci Watchdog",
 144};
 145
 146static int
 147davinci_wdt_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
 148                  unsigned long arg)
 149{
 150        int ret = -ENOTTY;
 151
 152        switch (cmd) {
 153        case WDIOC_GETSUPPORT:
 154                ret = copy_to_user((struct watchdog_info *)arg, &ident,
 155                                   sizeof(ident)) ? -EFAULT : 0;
 156                break;
 157
 158        case WDIOC_GETSTATUS:
 159        case WDIOC_GETBOOTSTATUS:
 160                ret = put_user(0, (int *)arg);
 161                break;
 162
 163        case WDIOC_GETTIMEOUT:
 164                ret = put_user(heartbeat, (int *)arg);
 165                break;
 166
 167        case WDIOC_KEEPALIVE:
 168                wdt_service();
 169                ret = 0;
 170                break;
 171        }
 172        return ret;
 173}
 174
 175static int davinci_wdt_release(struct inode *inode, struct file *file)
 176{
 177        wdt_service();
 178        clear_bit(WDT_IN_USE, &wdt_status);
 179
 180        return 0;
 181}
 182
 183static const struct file_operations davinci_wdt_fops = {
 184        .owner = THIS_MODULE,
 185        .llseek = no_llseek,
 186        .write = davinci_wdt_write,
 187        .ioctl = davinci_wdt_ioctl,
 188        .open = davinci_wdt_open,
 189        .release = davinci_wdt_release,
 190};
 191
 192static struct miscdevice davinci_wdt_miscdev = {
 193        .minor = WATCHDOG_MINOR,
 194        .name = "watchdog",
 195        .fops = &davinci_wdt_fops,
 196};
 197
 198static int davinci_wdt_probe(struct platform_device *pdev)
 199{
 200        int ret = 0, size;
 201        struct resource *res;
 202
 203        if (heartbeat < 1 || heartbeat > MAX_HEARTBEAT)
 204                heartbeat = DEFAULT_HEARTBEAT;
 205
 206        printk(KERN_INFO MODULE_NAME
 207                "DaVinci Watchdog Timer: heartbeat %d sec\n", heartbeat);
 208
 209        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 210        if (res == NULL) {
 211                printk(KERN_INFO MODULE_NAME
 212                        "failed to get memory region resource\n");
 213                return -ENOENT;
 214        }
 215
 216        size = res->end - res->start + 1;
 217        wdt_mem = request_mem_region(res->start, size, pdev->name);
 218
 219        if (wdt_mem == NULL) {
 220                printk(KERN_INFO MODULE_NAME "failed to get memory region\n");
 221                return -ENOENT;
 222        }
 223        wdt_base = (void __iomem *)(res->start);
 224
 225        ret = misc_register(&davinci_wdt_miscdev);
 226        if (ret < 0) {
 227                printk(KERN_ERR MODULE_NAME "cannot register misc device\n");
 228                release_resource(wdt_mem);
 229                kfree(wdt_mem);
 230        } else {
 231                set_bit(WDT_DEVICE_INITED, &wdt_status);
 232        }
 233
 234        return ret;
 235}
 236
 237static int davinci_wdt_remove(struct platform_device *pdev)
 238{
 239        misc_deregister(&davinci_wdt_miscdev);
 240        if (wdt_mem) {
 241                release_resource(wdt_mem);
 242                kfree(wdt_mem);
 243                wdt_mem = NULL;
 244        }
 245        return 0;
 246}
 247
 248static struct platform_driver platform_wdt_driver = {
 249        .driver = {
 250                .name = "watchdog",
 251        },
 252        .probe = davinci_wdt_probe,
 253        .remove = davinci_wdt_remove,
 254};
 255
 256static int __init davinci_wdt_init(void)
 257{
 258        return platform_driver_register(&platform_wdt_driver);
 259}
 260
 261static void __exit davinci_wdt_exit(void)
 262{
 263        platform_driver_unregister(&platform_wdt_driver);
 264}
 265
 266module_init(davinci_wdt_init);
 267module_exit(davinci_wdt_exit);
 268
 269MODULE_AUTHOR("Texas Instruments");
 270MODULE_DESCRIPTION("DaVinci Watchdog Driver");
 271
 272module_param(heartbeat, int, 0);
 273MODULE_PARM_DESC(heartbeat,
 274                 "Watchdog heartbeat period in seconds from 1 to "
 275                 __MODULE_STRING(MAX_HEARTBEAT) ", default "
 276                 __MODULE_STRING(DEFAULT_HEARTBEAT));
 277
 278MODULE_LICENSE("GPL");
 279MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
 280