Linux的WDT(watchdog)驱动(3)

static unsigned long open_lock;
static struct device *wdt_dev; /* platform device attached to */
static struct resource *wdt_mem;
static struct resource *wdt_irq;
static struct clk *wdt_clock;
static void __iomem *wdt_base;
static unsigned int wdt_count;
static char expect_close;
static DEFINE_SPINLOCK(wdt_lock);

/* watchdog control routines */

#define DBG(msg...) do { \
    if (debug) \
        printk(KERN_INFO msg); \
    } while (0)
/* functions */

static void s3c2410wdt_keepalive(void)
{
    spin_lock(&wdt_lock);
    writel(wdt_count, wdt_base + S3C2410_WTCNT);
    spin_unlock(&wdt_lock);
}

static void __s3c2410wdt_stop(void)
{
    unsigned long wtcon;

wtcon = readl(wdt_base + S3C2410_WTCON);
    wtcon &= ~(S3C2410_WTCON_ENABLE | S3C2410_WTCON_RSTEN);
    writel(wtcon, wdt_base + S3C2410_WTCON);
}

static void s3c2410wdt_stop(void)
{
    spin_lock(&wdt_lock);
    __s3c2410wdt_stop();
    spin_unlock(&wdt_lock);
}

static void s3c2410wdt_start(void)
{
    unsigned long wtcon;

spin_lock(&wdt_lock);

__s3c2410wdt_stop();

wtcon = readl(wdt_base + S3C2410_WTCON);
    wtcon |= S3C2410_WTCON_ENABLE | S3C2410_WTCON_DIV128;

if (soft_noboot) {
        wtcon |= S3C2410_WTCON_INTEN;
        wtcon &= ~S3C2410_WTCON_RSTEN;
    } else {
        wtcon &= ~S3C2410_WTCON_INTEN;
        wtcon |= S3C2410_WTCON_RSTEN;
    }

DBG("%s: wdt_count=0x%08x, wtcon=%08lx\n",
        __func__, wdt_count, wtcon);

writel(wdt_count, wdt_base + S3C2410_WTDAT);
    writel(wdt_count, wdt_base + S3C2410_WTCNT);
    writel(wtcon, wdt_base + S3C2410_WTCON);
    spin_unlock(&wdt_lock);
}

static int s3c2410wdt_set_heartbeat(int timeout)
{
    unsigned int freq = clk_get_rate(wdt_clock);
    unsigned int count;
    unsigned int divisor = 1;
    unsigned long wtcon;
    if (timeout < 1)
        return -EINVAL;

freq /= 128;
    count = timeout * freq;

DBG("%s: count=%d, timeout=%d, freq=%d\n",
        __func__, count, timeout, freq);

/* if the count is bigger than the watchdog register,
      then work out what we need to do (and if) we can
      actually make this value
    */

if (count >= 0x10000) {
        for (divisor = 1; divisor <= 0x100; divisor++) {
            if ((count / divisor) < 0x10000)
                break;
        }

if ((count / divisor) >= 0x10000) {
            dev_err(wdt_dev, "timeout %d too big\n", timeout);
            return -EINVAL;
        }
    }

tmr_margin = timeout;

DBG("%s: timeout=%d, divisor=%d, count=%d (%08x)\n",
        __func__, timeout, divisor, count, count/divisor);

count /= divisor;
    wdt_count = count;

/* update the pre-scaler */
    wtcon = readl(wdt_base + S3C2410_WTCON);
    wtcon &= ~S3C2410_WTCON_PRESCALE_MASK;
    wtcon |= S3C2410_WTCON_PRESCALE(divisor-1);

writel(count, wdt_base + S3C2410_WTDAT);
    writel(wtcon, wdt_base + S3C2410_WTCON);

return 0;
}

/*
 * /dev/watchdog handling
 */

static int s3c2410wdt_open(struct inode *inode, struct file *file)
{
    if (test_and_set_bit(0, &open_lock))
        return -EBUSY;

if (nowayout)
        __module_get(THIS_MODULE);

expect_close = 0;

/* start the timer */
    s3c2410wdt_start();
    return nonseekable_open(inode, file);
}

static int s3c2410wdt_release(struct inode *inode, struct file *file)
{
    /*
    * Shut off the timer.
    * Lock it in if it's a module and we set nowayout
    */

if (expect_close == 42)
        s3c2410wdt_stop();
    else {
        dev_err(wdt_dev, "Unexpected close, not stopping watchdog\n");
        s3c2410wdt_keepalive();
    }
    expect_close = 0;
    clear_bit(0, &open_lock);
    return 0;
}

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/16180.html