summaryrefslogtreecommitdiff
path: root/drivers/watchdog
diff options
context:
space:
mode:
authorKamal Kannan Balagopalan <kbalagopalan@nvidia.com>2012-06-23 00:24:33 -0700
committerVarun Colbert <vcolbert@nvidia.com>2012-07-09 15:32:27 -0700
commit8b7839cb926c7ee360ec811788d683b4c1123ab9 (patch)
tree5df667cab4865fb0535acb2185e9761a913103f7 /drivers/watchdog
parent3f510643870b06d31c24cdd07dd48a7e461ae849 (diff)
watchdog: tegra: Add support for FIQ debugger
Enable FIQ on 2nd watchdog expiry to make use of FIQ debugger. FIQ debugger can be used to dump call-stack and registers when WDT logic triggers FIQ on 2nd timeout. Bug 970018 Change-Id: Ia2e7f8b136499974ff51f2f0c2ef55704dfe37e1 Signed-off-by: Kamal Kannan Balagopalan <kbalagopalan@nvidia.com> Reviewed-on: http://git-master/r/110953 Reviewed-by: Automatic_Commit_Validation_User Reviewed-by: Xin Xie <xxie@nvidia.com> Reviewed-by: Steve Kuo <stevek@nvidia.com> Reviewed-by: Bharat Nihalani <bnihalani@nvidia.com>
Diffstat (limited to 'drivers/watchdog')
-rw-r--r--drivers/watchdog/tegra_wdt.c62
1 files changed, 61 insertions, 1 deletions
diff --git a/drivers/watchdog/tegra_wdt.c b/drivers/watchdog/tegra_wdt.c
index f6f21fcde7a8..0d8373efd3f1 100644
--- a/drivers/watchdog/tegra_wdt.c
+++ b/drivers/watchdog/tegra_wdt.c
@@ -35,6 +35,9 @@
#include <linux/spinlock.h>
#include <linux/uaccess.h>
#include <linux/watchdog.h>
+#ifdef CONFIG_TEGRA_FIQ_DEBUGGER
+#include <mach/irqs.h>
+#endif
/* minimum and maximum watchdog trigger periods, in seconds */
#define MIN_WDT_PERIOD 5
@@ -53,9 +56,11 @@ struct tegra_wdt {
struct notifier_block notifier;
struct resource *res_src;
struct resource *res_wdt;
+ struct resource *res_int_base;
unsigned long users;
void __iomem *wdt_source;
void __iomem *wdt_timer;
+ void __iomem *int_base;
int irq;
int tmrsrc;
int timeout;
@@ -122,6 +127,7 @@ static irqreturn_t tegra_wdt_interrupt(int irq, void *dev_id)
#define WDT_CFG (0)
#define WDT_CFG_PERIOD (1 << 4)
#define WDT_CFG_INT_EN (1 << 12)
+ #define WDT_CFG_FIQ_INT_EN (1 << 13)
#define WDT_CFG_SYS_RST_EN (1 << 14)
#define WDT_CFG_PMC2CAR_RST_EN (1 << 15)
#define WDT_STATUS (4)
@@ -131,6 +137,7 @@ static irqreturn_t tegra_wdt_interrupt(int irq, void *dev_id)
#define WDT_CMD_DISABLE_COUNTER (1 << 1)
#define WDT_UNLOCK (0xC)
#define WDT_UNLOCK_PATTERN (0xC45A << 0)
+#define ICTLR_IEP_CLASS 0x2C
#define MAX_NR_CPU_WDT 0x4
struct tegra_wdt *tegra_wdt[MAX_NR_CPU_WDT];
@@ -140,6 +147,19 @@ static inline void tegra_wdt_ping(struct tegra_wdt *wdt)
writel(WDT_CMD_START_COUNTER, wdt->wdt_source + WDT_CMD);
}
+#ifdef CONFIG_TEGRA_FIQ_DEBUGGER
+static void tegra_wdt_int_priority(struct tegra_wdt *wdt)
+{
+ unsigned val = 0;
+
+ if (!wdt->int_base)
+ return;
+ val = readl(wdt->int_base + ICTLR_IEP_CLASS);
+ val &= ~(1 << (INT_WDT_CPU & 31));
+ writel(val, wdt->int_base + ICTLR_IEP_CLASS);
+}
+#endif
+
static void tegra_wdt_enable(struct tegra_wdt *wdt)
{
u32 val;
@@ -157,6 +177,9 @@ static void tegra_wdt_enable(struct tegra_wdt *wdt)
*/
val = wdt->tmrsrc | WDT_CFG_PERIOD | /*WDT_CFG_INT_EN |*/
/*WDT_CFG_SYS_RST_EN |*/ WDT_CFG_PMC2CAR_RST_EN;
+#ifdef CONFIG_TEGRA_FIQ_DEBUGGER
+ val |= WDT_CFG_FIQ_INT_EN;
+#endif
writel(val, wdt->wdt_source + WDT_CFG);
writel(WDT_CMD_START_COUNTER, wdt->wdt_source + WDT_CMD);
}
@@ -307,7 +330,7 @@ static const struct file_operations tegra_wdt_fops = {
static int tegra_wdt_probe(struct platform_device *pdev)
{
- struct resource *res_src, *res_wdt, *res_irq;
+ struct resource *res_src, *res_wdt, *res_irq, *res_int_base;
struct tegra_wdt *wdt;
u32 src;
int ret = 0;
@@ -327,6 +350,14 @@ static int tegra_wdt_probe(struct platform_device *pdev)
return -ENOENT;
}
+#ifdef CONFIG_TEGRA_FIQ_DEBUGGER
+ res_int_base = platform_get_resource(pdev, IORESOURCE_MEM, 2);
+ if (!pdev->id && !res_int_base) {
+ dev_err(&pdev->dev, "FIQ_DBG: INT base not defined\n");
+ return -ENOENT;
+ }
+#endif
+
if (pdev->id == -1 && !res_irq) {
dev_err(&pdev->dev, "incorrect irq\n");
return -ENOENT;
@@ -387,6 +418,24 @@ static int tegra_wdt_probe(struct platform_device *pdev)
writel(TIMER_PCR_INTR, wdt->wdt_timer + TIMER_PCR);
if (res_irq != NULL) {
+#ifdef CONFIG_TEGRA_FIQ_DEBUGGER
+ /* FIQ debugger enables FIQ priority for INT_WDT_CPU.
+ * But that will disable IRQ on WDT expiration.
+ * Reset the priority back to IRQ on INT_WDT_CPU so
+ * that tegra_wdt_interrupt gets its chance to restart the
+ * counter before expiration.
+ */
+ res_int_base = request_mem_region(res_int_base->start,
+ resource_size(res_int_base),
+ pdev->name);
+ if (!res_int_base)
+ goto fail;
+ wdt->int_base = ioremap(res_int_base->start,
+ resource_size(res_int_base));
+ if (!wdt->int_base)
+ goto fail;
+ tegra_wdt_int_priority(wdt);
+#endif
ret = request_irq(res_irq->start, tegra_wdt_interrupt,
IRQF_DISABLED, dev_name(&pdev->dev), wdt);
if (ret) {
@@ -398,6 +447,7 @@ static int tegra_wdt_probe(struct platform_device *pdev)
wdt->res_src = res_src;
wdt->res_wdt = res_wdt;
+ wdt->res_int_base = res_int_base;
wdt->status = WDT_DISABLED;
ret = register_reboot_notifier(&wdt->notifier);
@@ -439,10 +489,15 @@ fail:
iounmap(wdt->wdt_source);
if (wdt->wdt_timer)
iounmap(wdt->wdt_timer);
+ if (wdt->int_base)
+ iounmap(wdt->int_base);
if (res_src)
release_mem_region(res_src->start, resource_size(res_src));
if (res_wdt)
release_mem_region(res_wdt->start, resource_size(res_wdt));
+ if (res_int_base)
+ release_mem_region(res_int_base->start,
+ resource_size(res_int_base));
kfree(wdt);
return ret;
}
@@ -459,8 +514,13 @@ static int tegra_wdt_remove(struct platform_device *pdev)
free_irq(wdt->irq, wdt);
iounmap(wdt->wdt_source);
iounmap(wdt->wdt_timer);
+ if (wdt->int_base)
+ iounmap(wdt->int_base);
release_mem_region(wdt->res_src->start, resource_size(wdt->res_src));
release_mem_region(wdt->res_wdt->start, resource_size(wdt->res_wdt));
+ if (wdt->res_int_base)
+ release_mem_region(wdt->res_int_base->start,
+ resource_size(wdt->res_int_base));
kfree(wdt);
platform_set_drvdata(pdev, NULL);
return 0;