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 <linux/device.h>
  28#include <linux/clk.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;
  73struct clk              *wdt_clk;
  74
  75static void wdt_service(void)
  76{
  77        spin_lock(&io_lock);
  78
  79        /* put watchdog in service state */
  80        iowrite32(WDKEY_SEQ0, wdt_base + WDTCR);
  81        /* put watchdog in active state */
  82        iowrite32(WDKEY_SEQ1, wdt_base + WDTCR);
  83
  84        spin_unlock(&io_lock);
  85}
  86
  87static void wdt_enable(void)
  88{
  89        u32 tgcr;
  90        u32 timer_margin;
  91        unsigned long wdt_freq;
  92
  93        wdt_freq = clk_get_rate(wdt_clk);
  94
  95        spin_lock(&io_lock);
  96
  97        /* disable, internal clock source */
  98        iowrite32(0, wdt_base + TCR);
  99        /* reset timer, set mode to 64-bit watchdog, and unreset */
 100        iowrite32(0, wdt_base + TGCR);
 101        tgcr = TIMMODE_64BIT_WDOG | TIM12RS_UNRESET | TIM34RS_UNRESET;
 102        iowrite32(tgcr, wdt_base + TGCR);
 103        /* clear counter regs */
 104        iowrite32(0, wdt_base + TIM12);
 105        iowrite32(0, wdt_base + TIM34);
 106        /* set timeout period */
 107        timer_margin = (((u64)heartbeat * wdt_freq) & 0xffffffff);
 108        iowrite32(timer_margin, wdt_base + PRD12);
 109        timer_margin = (((u64)heartbeat * wdt_freq) >> 32);
 110        iowrite32(timer_margin, wdt_base + PRD34);
 111        /* enable run continuously */
 112        iowrite32(ENAMODE12_PERIODIC, wdt_base + TCR);
 113        /* Once the WDT is in pre-active state write to
 114         * TIM12, TIM34, PRD12, PRD34, TCR, TGCR, WDTCR are
 115         * write protected (except for the WDKEY field)
 116         */
 117        /* put watchdog in pre-active state */
 118        iowrite32(WDKEY_SEQ0 | WDEN, wdt_base + WDTCR);
 119        /* put watchdog in active state */
 120        iowrite32(WDKEY_SEQ1 | WDEN, wdt_base + WDTCR);
 121
 122        spin_unlock(&io_lock);
 123}
 124
 125static int davinci_wdt_open(struct inode *inode, struct file *file)
 126{
 127        if (test_and_set_bit(WDT_IN_USE, &wdt_status))
 128                return -EBUSY;
 129
 130        wdt_enable();
 131
 132        return nonseekable_open(inode, file);
 133}
 134
 135static ssize_t
 136davinci_wdt_write(struct file *file, const char *data, size_t len,
 137                  loff_t *ppos)
 138{
 139        if (len)
 140                wdt_service();
 141
 142        return len;
 143}
 144
 145static struct watchdog_info ident = {
 146        .options = WDIOF_KEEPALIVEPING,
 147        .identity = "DaVinci Watchdog",
 148};
 149
 150static long davinci_wdt_ioctl(struct file *file,
 151                                        unsigned int cmd, unsigned long arg)
 152{
 153        int ret = -ENOTTY;
 154
 155        switch (cmd) {
 156        case WDIOC_GETSUPPORT:
 157                ret = copy_to_user((struct watchdog_info *)arg, &ident,
 158                                   sizeof(ident)) ? -EFAULT : 0;
 159                break;
 160
 161        case WDIOC_GETSTATUS:
 162        case WDIOC_GETBOOTSTATUS:
 163                ret = put_user(0, (int *)arg);
 164                break;
 165
 166        case WDIOC_KEEPALIVE:
 167                wdt_service();
 168                ret = 0;
 169                break;
 170
 171        case WDIOC_GETTIMEOUT:
 172                ret = put_user(heartbeat, (int *)arg);
 173                break;
 174        }
 175        return ret;
 176}
 177
 178static int davinci_wdt_release(struct inode *inode, struct file *file)
 179{
 180        wdt_service();
 181        clear_bit(WDT_IN_USE, &wdt_status);
 182
 183        return 0;
 184}
 185
 186static const struct file_operations davinci_wdt_fops = {
 187        .owner = THIS_MODULE,
 188        .llseek = no_llseek,
 189        .write = davinci_wdt_write,
 190        .unlocked_ioctl = davinci_wdt_ioctl,
 191        .open = davinci_wdt_open,
 192        .release = davinci_wdt_release,
 193};
 194
 195static struct miscdevice davinci_wdt_miscdev = {
 196        .minor = WATCHDOG_MINOR,
 197        .name = "watchdog",
 198        .fops = &davinci_wdt_fops,
 199};
 200
 201static int __devinit davinci_wdt_probe(struct platform_device *pdev)
 202{
 203        int ret = 0, size;
 204        struct resource *res;
 205        struct device *dev = &pdev->dev;
 206
 207        wdt_clk = clk_get(dev, NULL);
 208        if (WARN_ON(IS_ERR(wdt_clk)))
 209                return PTR_ERR(wdt_clk);
 210
 211        clk_enable(wdt_clk);
 212
 213        if (heartbeat < 1 || heartbeat > MAX_HEARTBEAT)
 214                heartbeat = DEFAULT_HEARTBEAT;
 215
 216        dev_info(dev, "heartbeat %d sec\n", heartbeat);
 217
 218        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 219        if (res == NULL) {
 220                dev_err(dev, "failed to get memory region resource\n");
 221                return -ENOENT;
 222        }
 223
 224        size = res->end - res->start + 1;
 225        wdt_mem = request_mem_region(res->start, size, pdev->name);
 226
 227        if (wdt_mem == NULL) {
 228                dev_err(dev, "failed to get memory region\n");
 229                return -ENOENT;
 230        }
 231
 232        wdt_base = ioremap(res->start, size);
 233        if (!wdt_base) {
 234                dev_err(dev, "failed to map memory region\n");
 235                return -ENOMEM;
 236        }
 237
 238        ret = misc_register(&davinci_wdt_miscdev);
 239        if (ret < 0) {
 240                dev_err(dev, "cannot register misc device\n");
 241                release_resource(wdt_mem);
 242                kfree(wdt_mem);
 243        } else {
 244                set_bit(WDT_DEVICE_INITED, &wdt_status);
 245        }
 246
 247        iounmap(wdt_base);
 248        return ret;
 249}
 250
 251static int __devexit davinci_wdt_remove(struct platform_device *pdev)
 252{
 253        misc_deregister(&davinci_wdt_miscdev);
 254        if (wdt_mem) {
 255                release_resource(wdt_mem);
 256                kfree(wdt_mem);
 257                wdt_mem = NULL;
 258        }
 259
 260        clk_disable(wdt_clk);
 261        clk_put(wdt_clk);
 262
 263        return 0;
 264}
 265
 266static struct platform_driver platform_wdt_driver = {
 267        .driver = {
 268                .name = "watchdog",
 269                .owner  = THIS_MODULE,
 270        },
 271        .probe = davinci_wdt_probe,
 272        .remove = __devexit_p(davinci_wdt_remove),
 273};
 274
 275static int __init davinci_wdt_init(void)
 276{
 277        return platform_driver_register(&platform_wdt_driver);
 278}
 279
 280static void __exit davinci_wdt_exit(void)
 281{
 282        platform_driver_unregister(&platform_wdt_driver);
 283}
 284
 285module_init(davinci_wdt_init);
 286module_exit(davinci_wdt_exit);
 287
 288MODULE_AUTHOR("Texas Instruments");
 289MODULE_DESCRIPTION("DaVinci Watchdog Driver");
 290
 291module_param(heartbeat, int, 0);
 292MODULE_PARM_DESC(heartbeat,
 293                 "Watchdog heartbeat period in seconds from 1 to "
 294                 __MODULE_STRING(MAX_HEARTBEAT) ", default "
 295                 __MODULE_STRING(DEFAULT_HEARTBEAT));
 296
 297MODULE_LICENSE("GPL");
 298MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
 299MODULE_ALIAS("platform:watchdog");
 300