summaryrefslogtreecommitdiff
path: root/drivers/watchdog/ast_wdt.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/watchdog/ast_wdt.c')
-rw-r--r--drivers/watchdog/ast_wdt.c189
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);
}