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