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