diff options
author | Kamal Kannan Balagopalan <kbalagopalan@nvidia.com> | 2012-06-23 00:24:33 -0700 |
---|---|---|
committer | Varun Colbert <vcolbert@nvidia.com> | 2012-07-09 15:32:27 -0700 |
commit | 8b7839cb926c7ee360ec811788d683b4c1123ab9 (patch) | |
tree | 5df667cab4865fb0535acb2185e9761a913103f7 /drivers/watchdog | |
parent | 3f510643870b06d31c24cdd07dd48a7e461ae849 (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.c | 62 |
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; |