summaryrefslogtreecommitdiff
path: root/arch/arm/mach-tegra/wdt-recovery.c
diff options
context:
space:
mode:
authorKamal Kannan Balagopalan <kbalagopalan@nvidia.com>2011-12-12 14:15:02 -0800
committerVarun Wadekar <vwadekar@nvidia.com>2011-12-22 11:12:35 +0530
commitf0fa117c3fef2df54c2151c0d2c6ec7278cd45ee (patch)
tree90a4809963d40404df8fbc6c3271983e0e6eb1ac /arch/arm/mach-tegra/wdt-recovery.c
parent964834f1203e375c13be8d3fb58b3fada083c579 (diff)
arm: tegra: power: add watchdog recovery function
Add watchdog recovery mechanism to protect against hangs during driver suspend/resume sequence Bug 857748 Change-Id: I03d540b38318a5a953b1a697af123291b48991e9 Signed-off-by: Kamal Kannan Balagopalan <kbalagopalan@nvidia.com> Reviewed-on: http://git-master/r/65986 Reviewed-by: Automatic_Commit_Validation_User Reviewed-by: Krishna Reddy <vdumpa@nvidia.com>
Diffstat (limited to 'arch/arm/mach-tegra/wdt-recovery.c')
-rw-r--r--arch/arm/mach-tegra/wdt-recovery.c131
1 files changed, 131 insertions, 0 deletions
diff --git a/arch/arm/mach-tegra/wdt-recovery.c b/arch/arm/mach-tegra/wdt-recovery.c
new file mode 100644
index 000000000000..537b1c0db853
--- /dev/null
+++ b/arch/arm/mach-tegra/wdt-recovery.c
@@ -0,0 +1,131 @@
+/*
+ * arch/arm/mach-tegra/wdt-recovery.c
+ *
+ * Copyright (c) 2011, NVIDIA Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/suspend.h>
+#include <linux/resource.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/syscore_ops.h>
+#include <linux/io.h>
+
+#include <asm/mach-types.h>
+#include <asm/mach/time.h>
+#include <asm/localtimer.h>
+
+#include <mach/nvmap.h>
+#include <mach/irqs.h>
+#include <mach/iomap.h>
+#include <mach/clk.h>
+#include <mach/io.h>
+
+static int wdt_heartbeat = 30;
+
+#if defined(CONFIG_ARCH_TEGRA_3x_SOC)
+#define TIMER_PTV 0
+ #define TIMER_EN (1 << 31)
+ #define TIMER_PERIODIC (1 << 30)
+#define TIMER_PCR 0x4
+ #define TIMER_PCR_INTR (1 << 30)
+#define WDT_CFG (0)
+ #define WDT_CFG_TMR_SRC (7 << 0) /* for TMR7. */
+ #define WDT_CFG_PERIOD (1 << 4)
+ #define WDT_CFG_INT_EN (1 << 12)
+ #define WDT_CFG_SYS_RST_EN (1 << 14)
+ #define WDT_CFG_PMC2CAR_RST_EN (1 << 15)
+#define WDT_CMD (8)
+ #define WDT_CMD_START_COUNTER (1 << 0)
+ #define WDT_CMD_DISABLE_COUNTER (1 << 1)
+#define WDT_UNLOCK (0xC)
+ #define WDT_UNLOCK_PATTERN (0xC45A << 0)
+
+static void __iomem *wdt_timer = IO_ADDRESS(TEGRA_TMR7_BASE);
+static void __iomem *wdt_source = IO_ADDRESS(TEGRA_WDT0_BASE);
+
+static void tegra_wdt_reset_enable(void)
+{
+ u32 val;
+
+ writel(TIMER_PCR_INTR, wdt_timer + TIMER_PCR);
+ val = (wdt_heartbeat * 1000000ul) / 4;
+ val |= (TIMER_EN | TIMER_PERIODIC);
+ writel(val, wdt_timer + TIMER_PTV);
+
+ val = WDT_CFG_TMR_SRC | WDT_CFG_PERIOD | /*WDT_CFG_INT_EN |*/
+ /*WDT_CFG_SYS_RST_EN |*/ WDT_CFG_PMC2CAR_RST_EN;
+ writel(val, wdt_source + WDT_CFG);
+ writel(WDT_CMD_START_COUNTER, wdt_source + WDT_CMD);
+ pr_info("%s: WDT Recovery Enabled\n", __func__);
+}
+
+static int tegra_wdt_reset_disable(void)
+{
+ writel(TIMER_PCR_INTR, wdt_timer + TIMER_PCR);
+ writel(WDT_UNLOCK_PATTERN, wdt_source + WDT_UNLOCK);
+ writel(WDT_CMD_DISABLE_COUNTER, wdt_source + WDT_CMD);
+
+ writel(0, wdt_timer + TIMER_PTV);
+ pr_info("%s: WDT Recovery Disabled\n", __func__);
+
+ return 0;
+}
+#elif defined(CONFIG_ARCH_TEGRA_2x_SOC)
+
+static void tegra_wdt_reset_enable(void)
+{
+}
+static int tegra_wdt_reset_disable(void)
+{
+ return 0;
+}
+#endif
+
+static int tegra_pm_notify(struct notifier_block *nb,
+ unsigned long event, void *nouse)
+{
+ switch (event) {
+ case PM_SUSPEND_PREPARE:
+ tegra_wdt_reset_enable();
+ break;
+ case PM_POST_SUSPEND:
+ tegra_wdt_reset_disable();
+ break;
+ }
+
+ return NOTIFY_OK;
+}
+
+static struct notifier_block tegra_wdt_notify = {
+ .notifier_call = tegra_pm_notify,
+};
+
+static struct syscore_ops tegra_wdt_syscore_ops = {
+ .suspend = tegra_wdt_reset_disable,
+ .resume = tegra_wdt_reset_enable,
+};
+
+void __init tegra_wdt_recovery_init(void)
+{
+#ifdef CONFIG_PM
+ /* Register PM notifier. */
+ register_pm_notifier(&tegra_wdt_notify);
+#endif
+ register_syscore_ops(&tegra_wdt_syscore_ops);
+}