summaryrefslogtreecommitdiff
path: root/drivers/watchdog
diff options
context:
space:
mode:
authorXin Xie <xxie@nvidia.com>2013-02-20 13:52:59 -0800
committerDan Willemsen <dwillemsen@nvidia.com>2013-09-14 13:00:57 -0700
commiteeb81888c41ebce8f6822fd241934e582c13a773 (patch)
treec205234f3ac81a9c046238506a9c278ccf4f87c8 /drivers/watchdog
parent16d65c456be545d1e6d44c8a96962681d640573a (diff)
watchdog: tegra: fix reboot reason
bug 1239765 Change-Id: Ia2c5c83c0489b21d3fb5dcc005a9b7c07fb9b98c Signed-off-by: Xin Xie <xxie@nvidia.com> Reviewed-on: http://git-master/r/202662 Reviewed-by: Simone Willett <swillett@nvidia.com> Tested-by: Simone Willett <swillett@nvidia.com>
Diffstat (limited to 'drivers/watchdog')
-rw-r--r--drivers/watchdog/tegra_wdt.c63
1 files changed, 59 insertions, 4 deletions
diff --git a/drivers/watchdog/tegra_wdt.c b/drivers/watchdog/tegra_wdt.c
index fcefb9bac880..6306e351f744 100644
--- a/drivers/watchdog/tegra_wdt.c
+++ b/drivers/watchdog/tegra_wdt.c
@@ -39,6 +39,7 @@
#ifdef CONFIG_TEGRA_FIQ_DEBUGGER
#include <mach/irqs.h>
#endif
+#include <mach/iomap.h>
/* minimum and maximum watchdog trigger periods, in seconds */
#define MIN_WDT_PERIOD 5
@@ -140,6 +141,7 @@ static irqreturn_t tegra_wdt_interrupt(int irq, void *dev_id)
#define WDT_UNLOCK_PATTERN (0xC45A << 0)
#define ICTLR_IEP_CLASS 0x2C
#define MAX_NR_CPU_WDT 0x4
+#define PMC_RST_STATUS 0x1b4
struct tegra_wdt *tegra_wdt[MAX_NR_CPU_WDT];
@@ -320,6 +322,62 @@ static ssize_t tegra_wdt_write(struct file *file, const char __user *data,
return len;
}
+static void tegra_wdt_log_reset_reason(struct platform_device *pdev,
+ struct tegra_wdt *wdt)
+{
+
+#if defined(CONFIG_ARCH_TEGRA_3x_SOC) || defined(CONFIG_ARCH_TEGRA_11x_SOC)
+ /*
+ * There are two pathes to make the WDT reset:
+ * (a) WDT -> PMC -> CAR
+ * ^
+ * |
+ * v
+ * PMIC
+ *
+ * (b) WDT -> CAR
+ *
+ * Path (a) is enabled by WDT_CFG_PMC2CAR_RST_EN bit in the WDT
+ * configuration register, as it will reset the CAR module, and we
+ * cannot read back the reset reason from the CAR module. However, we
+ * can read back the reaset reason from the PMC module.
+ *
+ * Path (b) is enabled by the WDT_CFG_SYS_RST_EN bit, and we can
+ * read back the reset reason from the CAR moudle. However, this reset
+ * path will not reset the peripherals which might be the hard hang
+ * source. We will not use this path.
+ */
+ u32 val;
+ void __iomem *pmc_base;
+#define RESET_STR(REASON) "last reset is due to "#REASON"\n"
+ char *reset_reason[] = {
+ RESET_STR(power on reset),
+ RESET_STR(watchdog timeout),
+ RESET_STR(sensor),
+ RESET_STR(software reset),
+ RESET_STR(deep sleep reset),
+ };
+
+ /* report reset reason only once */
+ if (pdev->id > 0)
+ return;
+
+ pmc_base = IO_ADDRESS(TEGRA_PMC_BASE);
+ val = readl(pmc_base + PMC_RST_STATUS) & 0x7;
+ if (val >= ARRAY_SIZE(reset_reason))
+ dev_info(&pdev->dev, "last reset value is invalid 0x%x\n", val);
+ else
+ dev_info(&pdev->dev, reset_reason[val]);
+
+#else
+ u32 val;
+
+ val = readl(wdt->wdt_source);
+ if (val & BIT(12))
+ dev_info(&pdev->dev, "last reset due to watchdog timeout\n");
+#endif
+}
+
static const struct file_operations tegra_wdt_fops = {
.owner = THIS_MODULE,
.llseek = no_llseek,
@@ -334,7 +392,6 @@ static int tegra_wdt_probe(struct platform_device *pdev)
struct resource *res_src, *res_wdt, *res_irq;
struct resource *res_int_base = NULL;
struct tegra_wdt *wdt;
- u32 src;
int ret = 0;
if (pdev->id < -1 && pdev->id > 3) {
@@ -411,9 +468,7 @@ static int tegra_wdt_probe(struct platform_device *pdev)
goto fail;
}
- src = readl(wdt->wdt_source);
- if (src & BIT(12))
- dev_info(&pdev->dev, "last reset due to watchdog timeout\n");
+ tegra_wdt_log_reset_reason(pdev, wdt);
tegra_wdt_disable(wdt);
writel(TIMER_PCR_INTR, wdt->wdt_timer + TIMER_PCR);