diff options
Diffstat (limited to 'drivers/watchdog/ast_wdt.c')
-rw-r--r-- | drivers/watchdog/ast_wdt.c | 189 |
1 files changed, 141 insertions, 48 deletions
diff --git a/drivers/watchdog/ast_wdt.c b/drivers/watchdog/ast_wdt.c index 845f1db3d66d..9e7e84fb8740 100644 --- a/drivers/watchdog/ast_wdt.c +++ b/drivers/watchdog/ast_wdt.c @@ -78,6 +78,17 @@ typedef unsigned char bool_T; #define WDT_Clr (WDT_BASE_VA+0x14) #define WDT_RstWd (WDT_BASE_VA+0x18) +#define WDT_CTRL_B_SECOND_BOOT (0x1 << 7) +#define WDT_CTRL_B_RESET_SOC (0x00 << 5) /* yes, 0x00 */ +#define WDT_CTRL_B_RESET_FULL (0x01 << 5) +#define WDT_CTRL_B_RESET_ARM (0x2 << 5) +#define WDT_CTRL_B_RESET_MASK (0x3 << 5) +#define WDT_CTRL_B_1MCLK (0x1 << 4) +#define WDT_CTRL_B_EXT (0x1 << 3) +#define WDT_CTRL_B_INTR (0x1 << 2) +#define WDT_CTRL_B_CLEAR_AFTER (0x1 << 1) +#define WDT_CTRL_B_ENABLE (0x1 << 0) + #define AST_READ_REG(r) (*((volatile unsigned int *) (r))) #define AST_WRITE_REG(r,v) (*((volatile unsigned int *) (r)) = ((unsigned int) (v))) @@ -87,17 +98,24 @@ typedef unsigned char bool_T; #define WDT_CLK_SRC_PCLK 1 //Global Variables -#define WD_TIMO 6 /* Default heartbeat = 6 seconds */ +#define WDT_TIMO 30 /* Default heartbeat = 30 seconds */ + +#define WDT_INITIAL_TIMO (8*60) /* Initial timeout, 8m */ +#define WDT_TIMO2TICKS(t) (TICKS_PER_uSEC * 1000000 * (t)) -static int heartbeat = WD_TIMO; +static int heartbeat = WDT_TIMO; module_param(heartbeat, int, 0); -MODULE_PARM_DESC(heartbeat, "Watchdog heartbeat in seconds. (0<heartbeat<65536, default=" __MODULE_STRING(WD_TIMO) ")"); +MODULE_PARM_DESC(heartbeat, "Watchdog heartbeat in seconds. (0<heartbeat<65536, default=" __MODULE_STRING(WDT_TIMO) ")"); static int nowayout = WATCHDOG_NOWAYOUT; module_param(nowayout, int, 0); MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=CONFIG_WATCHDOG_NOWAYOUT)"); -static unsigned long wdt_is_open; +static int force_disable = 0; // setting this to 1 will disable the wdt timer +module_param(force_disable, int, 0); +MODULE_PARM_DESC(force_disable, "Disable watchdog by default " + "(default=0, enable watchdog)"); + static char expect_close; //Function Declaration @@ -117,7 +135,7 @@ void wdt_disable(void) /* reset WDT_Ctrl[0] as 0 */ regVal = AST_READ_REG(WDT_Ctrl); - regVal &= 0xFFFFFFFE; + regVal &= ~(WDT_CTRL_B_ENABLE); AST_WRITE_REG(WDT_Ctrl, regVal); } @@ -129,17 +147,18 @@ void wdt_sel_clk_src(unsigned char sourceClk) if (sourceClk == WDT_CLK_SRC_PCLK) { /* reset WDT_Ctrl[4] as 0 */ - regVal &= 0xFFFFFFEF; + regVal &= ~(WDT_CTRL_B_1MCLK); } else { /* set WDT_Ctrl[4] as 1 */ - regVal |= 0x00000010; + regVal |= WDT_CTRL_B_1MCLK; } AST_WRITE_REG(WDT_Ctrl, regVal); } -void wdt_set_timeout_action(bool_T bResetOut, bool_T bIntrSys, bool_T bResetSys) +void wdt_set_timeout_action(bool_T bResetOut, bool_T bIntrSys, + bool_T bClrAfter, bool_T bResetARMOnly) { register unsigned int regVal; @@ -148,70 +167,102 @@ void wdt_set_timeout_action(bool_T bResetOut, bool_T bIntrSys, bool_T bResetSys) if (bResetOut) { /* set WDT_Ctrl[3] = 1 */ - regVal |= 0x00000008; + regVal |= WDT_CTRL_B_EXT; } else { /* reset WDT_Ctrl[3] = 0 */ - regVal &= 0xFFFFFFF7; + regVal &= ~WDT_CTRL_B_EXT; } if (bIntrSys) { /* set WDT_Ctrl[2] = 1 */ - regVal |= 0x00000004; + regVal |= WDT_CTRL_B_INTR; } else { /* reset WDT_Ctrl[2] = 0 */ - regVal &= 0xFFFFFFFB; + regVal &= ~WDT_CTRL_B_INTR; } - if (bResetSys) + if (bClrAfter) { /* set WDT_Ctrl[1] = 1 */ - regVal |= 0x00000002; + regVal |= WDT_CTRL_B_CLEAR_AFTER; } else { /* reset WDT_Ctrl[1] = 0 */ - regVal &= 0xFFFFFFFD; + regVal &= ~WDT_CTRL_B_CLEAR_AFTER; + } + + if (bResetARMOnly) + { + /* set WDT_Ctrl[6..5] = 10 ie, reset ARM only */ + regVal &= ~WDT_CTRL_B_RESET_MASK; + regVal |= WDT_CTRL_B_RESET_ARM; + } + else + { + /* reset WDT_CTrl[6..5] = 01, full chip */ + regVal &= ~WDT_CTRL_B_RESET_MASK; + regVal |= WDT_CTRL_B_RESET_FULL; } + AST_WRITE_REG(WDT_Ctrl, regVal); } void wdt_enable(void) { - register unsigned int regVal; + if (!force_disable) { + register unsigned int regVal; + + /* set WDT_Ctrl[0] as 1 */ + regVal = AST_READ_REG(WDT_Ctrl); + regVal |= WDT_CTRL_B_ENABLE; + AST_WRITE_REG(WDT_Ctrl, regVal); + } +} - /* set WDT_Ctrl[0] as 1 */ - regVal = AST_READ_REG(WDT_Ctrl); - regVal |= 1; - AST_WRITE_REG(WDT_Ctrl, regVal); +bool_T wdt_is_enabled(void) +{ + unsigned int reg; + reg = AST_READ_REG(WDT_Ctrl); + return reg & WDT_CTRL_B_ENABLE; } -void wdt_restart_new(unsigned int nPeriod, int sourceClk, bool_T bResetOut, bool_T bIntrSys, bool_T bResetSys, bool_T bUpdated) + +void wdt_restart_new(unsigned int nPeriod, int sourceClk, bool_T bResetOut, + bool_T bIntrSys, bool_T bClrAfter, bool_T bResetARMOnly) { - wdt_disable(); + bool_T enabled = wdt_is_enabled(); + + if (enabled) { + wdt_disable(); + } AST_WRITE_REG(WDT_Reload, nPeriod); wdt_sel_clk_src(sourceClk); - wdt_set_timeout_action(bResetOut, bIntrSys, bResetSys); + wdt_set_timeout_action(bResetOut, bIntrSys, bClrAfter, bResetARMOnly); AST_WRITE_REG(WDT_Restart, 0x4755); /* reload! */ - if (!bUpdated) + if (enabled) { wdt_enable(); + } } void wdt_restart(void) { - wdt_disable(); - AST_WRITE_REG(WDT_Restart, 0x4755); /* reload! */ - wdt_enable(); + if (!force_disable) { + wdt_disable(); + AST_WRITE_REG(WDT_Restart, 0x4755); /* reload! */ + wdt_enable(); + } } @@ -230,7 +281,9 @@ static int wdt_set_heartbeat(int t) heartbeat=t; - wdt_restart_new(TICKS_PER_uSEC*1000000*t, WDT_CLK_SRC_EXT, FALSE, TRUE, FALSE, FALSE); + wdt_restart_new(WDT_TIMO2TICKS(t), WDT_CLK_SRC_EXT, + /* No Ext, No intr, Self clear, Full chip reset */ + FALSE, FALSE, TRUE, FALSE); return 0; } @@ -245,8 +298,11 @@ static int wdt_set_heartbeat(int t) * @count: count of bytes * @ppos: pointer to the position to write. No seeks allowed * - * A write to a watchdog device is defined as a keepalive signal. Any - * write of data will do, as we we don't define content meaning. + * A write to a watchdog device is defined as a keepalive signal. + * Any data will do, except for the reserved letters 'V' (to enable + * magic close), the letter 'X' (to override the current watchdog + * settings and disable it), or the letter 'x' (to turn off override + * and restore its old settings). */ static ssize_t ast_wdt_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos) @@ -265,8 +321,20 @@ static int wdt_set_heartbeat(int t) char c; if (get_user(c, buf + i)) return -EFAULT; - if (c == 'V') - expect_close = 42; + switch(c) { + case 'V': + expect_close = 42; + break; + case 'X': + force_disable = 1; + wdt_disable(); + break; + case 'x': + force_disable = 0; + break; + default: + break; + } } } wdt_restart(); @@ -338,8 +406,6 @@ static int ast_wdt_ioctl(struct inode *inode, struct file *file, unsigned int cm static int ast_wdt_open(struct inode *inode, struct file *file) { - if(test_and_set_bit(0, &wdt_is_open)) - return -EBUSY; /* * Activate */ @@ -362,14 +428,23 @@ static int ast_wdt_open(struct inode *inode, struct file *file) static int ast_wdt_release(struct inode *inode, struct file *file) { - if (expect_close == 42 || !nowayout) + if (expect_close != 42 || !nowayout) { + /* handles the case where the device is closed without the "magic + * close" character (anything that is not 'V' qualifies -- see the + * original Linux watchdog spec for more about this). closing the + * device in this case must disable the timer too, so automatic + * restarts are inhibited. + */ + wdt_disable(); - clear_bit(0, &wdt_is_open); } else { - printk(KERN_CRIT "wdt: WDT device closed unexpectedly. WDT will not stop!\n"); + /* handles the case where the kernel is compiled with nowayout, or + * if the user specifies that the watchdog should continue ticking + * after device closure (by writing a 'V' before closing the device) + */ wdt_restart(); } expect_close = 0; @@ -398,14 +473,23 @@ static int ast_wdt_notify_sys(struct notifier_block *this, unsigned long code, v return NOTIFY_DONE; } -extern void ast_soc_wdt_reset(void) +extern void ast_soc_reset_soc(void) { - writel(0x10 , WDT_BASE_VA+0x04); - writel(0x4755, WDT_BASE_VA+0x08); - writel(0x3, WDT_BASE_VA+0x0c); + writel(0x10 , WDT_Reload); + writel(0x4755, WDT_Restart); + writel(WDT_CTRL_B_RESET_SOC|WDT_CTRL_B_CLEAR_AFTER|WDT_CTRL_B_ENABLE, + WDT_Ctrl); } +EXPORT_SYMBOL(ast_soc_reset_soc); -EXPORT_SYMBOL(ast_soc_wdt_reset); +extern void ast_wdt_reset_full(void) +{ + writel(0x10 , WDT_Reload); + writel(0x4755, WDT_Restart); + writel(WDT_CTRL_B_RESET_FULL|WDT_CTRL_B_CLEAR_AFTER|WDT_CTRL_B_ENABLE, + WDT_Ctrl); +} +EXPORT_SYMBOL(ast_wdt_reset_full); static struct file_operations ast_wdt_fops = { @@ -433,10 +517,6 @@ static int ast_wdt_probe(struct platform_device *pdev) { int ret; - wdt_disable(); - wdt_sel_clk_src(WDT_CLK_SRC_EXT); - wdt_set_timeout_action(FALSE, FALSE, FALSE); - /* register ISR */ if (request_irq(IRQ_WDT, (void *)wdt_isr, IRQF_DISABLED, "WDT", NULL)) { @@ -463,9 +543,22 @@ static int ast_wdt_probe(struct platform_device *pdev) } /* interrupt the system while WDT timeout */ - wdt_restart_new(TICKS_PER_uSEC*1000000*heartbeat, WDT_CLK_SRC_EXT, FALSE, TRUE, FALSE, TRUE); + wdt_restart_new(WDT_TIMO2TICKS(WDT_INITIAL_TIMO), WDT_CLK_SRC_EXT, + /* No Ext, No intr, Self clear, Full chip reset */ + FALSE, FALSE, TRUE, FALSE); - printk(KERN_INFO "AST WDT is installed.(irq = %d, heartbeat = %d secs, nowayout = %d)\n",IRQ_WDT,heartbeat,nowayout); + /* enable it by default */ + if (!force_disable) { + wdt_enable(); + } + + /* change the reload value back to regular */ + AST_WRITE_REG(WDT_Reload, WDT_TIMO2TICKS(heartbeat)); + + printk(KERN_INFO "UMVP2500 WDT is installed. (irq:%d, initial timeout:%ds, " + "timeout:%ds nowayout:%d enabled:%s)\n", + IRQ_WDT, WDT_INITIAL_TIMO, heartbeat, nowayout, + wdt_is_enabled() ? "yes" : "no"); return (0); } |