aboutsummaryrefslogtreecommitdiffstats
path: root/arch/arm/mach-omap2
diff options
context:
space:
mode:
authorKevin Hilman <khilman@ti.com>2012-05-08 13:34:30 -0400
committerPaul Walmsley <paul@pwsan.com>2012-05-08 19:25:37 -0400
commit414e41286e3aeb87de140ef4c75100f9344c32b2 (patch)
tree31c47972416865b37d317f65b8dd5c0162ab080a /arch/arm/mach-omap2
parentc8d82ff68fb6873691536cf33021977efbf5593c (diff)
ARM: OMAP2+: WDTIMER integration: fix !PM boot crash, disarm timer after hwmod reset
Without runtime PM enabled, hwmod needs to leave all IP blocks in an enabled state by default so any driver access to the HW will succeed. This is accomplished by seting the postsetup_state to enabled for all hwmods during init when runtime PM is disabled. Currently, we have a special case for WDT in that its postsetup_state is always set to disabled. This is done so that the WDT is disabled and the timer is disarmed at boot in case there is no WDT driver. This also means that when runtime PM is disabled, if a WDT driver *is* built in the kernel, the kernel will crash on the first access to the WDT hardware. We can't simply leave the WDT module enabled, because the timer is armed by default after reset. That means that if there is no WDT driver initialzed or loaded before the timer expires, the kernel will reboot. To fix this, a custom reset method is added to the watchdog class of omap_hwmod. This method will *always* disarm the timer after hwmod reset. The WDT timer then will only be rearmed when/if the driver is loaded for the WDT. With the timer disarmed by default, we no longer need a special-case for the postsetup_state of WDT during init, so it is removed. Any platforms wishing to ensure the watchdog remains armed across the entire boot boot can simply disable the reset-on-init feature of the watchdog hwmod using omap_hwmod_no_setup_reset(). Tested on 3530/Overo, 4430/Panda. NOTE: on 4430, the hwmod OCP reset does not seem to rearm the timer as documented in the TRM (and what happens on OMAP3.) I noticed this because testing the HWMOD_INIT_NO_RESET feature with no driver loaded, I expected a reboot part way through the boot, but did not see a reboot. Adding some debug to read the counter, I verified that right after OCP softreset, the counter is not firing. After writing the magic start sequence, the timer starts counting. This means that the timer disarm sequence added here does not seem to be needed for 4430, but is technically the correct way to ensure the timer is disarmed, so it is left in for OMAP4. Special thanks to Paul Walmsley for helping brainstorm ideas to fix this problem. Cc: Paul Walmsley <paul@pwsan.com> Signed-off-by: Kevin Hilman <khilman@ti.com> Cc: Santosh Shilimkar <santosh.shilimkar@ti.com> Acked-by: Santosh Shilimkar <santosh.shilimkar@ti.com> [paul@pwsan.com: updated the omap2_wd_timer_reset() function in the wake of commit 3c55c1baffa5f719eb2ae9729088bc867f972f53 ("ARM: OMAP2+: hwmod: Revert "ARM: OMAP2+: hwmod: Make omap_hwmod_softreset wait for reset status""); added kerneldoc; rolled in warning fix from Kevin] Signed-off-by: Paul Walmsley <paul@pwsan.com>
Diffstat (limited to 'arch/arm/mach-omap2')
-rw-r--r--arch/arm/mach-omap2/io.c18
-rw-r--r--arch/arm/mach-omap2/omap_hwmod_2xxx_ipblock_data.c3
-rw-r--r--arch/arm/mach-omap2/omap_hwmod_3xxx_data.c3
-rw-r--r--arch/arm/mach-omap2/omap_hwmod_44xx_data.c1
-rw-r--r--arch/arm/mach-omap2/wd_timer.c45
-rw-r--r--arch/arm/mach-omap2/wd_timer.h1
6 files changed, 51 insertions, 20 deletions
diff --git a/arch/arm/mach-omap2/io.c b/arch/arm/mach-omap2/io.c
index 065bd768987c..fafcc35b970c 100644
--- a/arch/arm/mach-omap2/io.c
+++ b/arch/arm/mach-omap2/io.c
@@ -363,24 +363,6 @@ static void __init omap_hwmod_init_postsetup(void)
363#endif 363#endif
364 omap_hwmod_for_each(_set_hwmod_postsetup_state, &postsetup_state); 364 omap_hwmod_for_each(_set_hwmod_postsetup_state, &postsetup_state);
365 365
366 /*
367 * Set the default postsetup state for unusual modules (like
368 * MPU WDT).
369 *
370 * The postsetup_state is not actually used until
371 * omap_hwmod_late_init(), so boards that desire full watchdog
372 * coverage of kernel initialization can reprogram the
373 * postsetup_state between the calls to
374 * omap2_init_common_infra() and omap_sdrc_init().
375 *
376 * XXX ideally we could detect whether the MPU WDT was currently
377 * enabled here and make this conditional
378 */
379 postsetup_state = _HWMOD_STATE_DISABLED;
380 omap_hwmod_for_each_by_class("wd_timer",
381 _set_hwmod_postsetup_state,
382 &postsetup_state);
383
384 omap_pm_if_early_init(); 366 omap_pm_if_early_init();
385} 367}
386 368
diff --git a/arch/arm/mach-omap2/omap_hwmod_2xxx_ipblock_data.c b/arch/arm/mach-omap2/omap_hwmod_2xxx_ipblock_data.c
index 5941f14130a1..83eafd96ecaa 100644
--- a/arch/arm/mach-omap2/omap_hwmod_2xxx_ipblock_data.c
+++ b/arch/arm/mach-omap2/omap_hwmod_2xxx_ipblock_data.c
@@ -89,7 +89,8 @@ static struct omap_hwmod_class_sysconfig omap2xxx_wd_timer_sysc = {
89struct omap_hwmod_class omap2xxx_wd_timer_hwmod_class = { 89struct omap_hwmod_class omap2xxx_wd_timer_hwmod_class = {
90 .name = "wd_timer", 90 .name = "wd_timer",
91 .sysc = &omap2xxx_wd_timer_sysc, 91 .sysc = &omap2xxx_wd_timer_sysc,
92 .pre_shutdown = &omap2_wd_timer_disable 92 .pre_shutdown = &omap2_wd_timer_disable,
93 .reset = &omap2_wd_timer_reset,
93}; 94};
94 95
95/* 96/*
diff --git a/arch/arm/mach-omap2/omap_hwmod_3xxx_data.c b/arch/arm/mach-omap2/omap_hwmod_3xxx_data.c
index 2432574123c6..fd48797fa95a 100644
--- a/arch/arm/mach-omap2/omap_hwmod_3xxx_data.c
+++ b/arch/arm/mach-omap2/omap_hwmod_3xxx_data.c
@@ -418,7 +418,8 @@ static struct omap_hwmod_class_sysconfig i2c_sysc = {
418static struct omap_hwmod_class omap3xxx_wd_timer_hwmod_class = { 418static struct omap_hwmod_class omap3xxx_wd_timer_hwmod_class = {
419 .name = "wd_timer", 419 .name = "wd_timer",
420 .sysc = &omap3xxx_wd_timer_sysc, 420 .sysc = &omap3xxx_wd_timer_sysc,
421 .pre_shutdown = &omap2_wd_timer_disable 421 .pre_shutdown = &omap2_wd_timer_disable,
422 .reset = &omap2_wd_timer_reset,
422}; 423};
423 424
424static struct omap_hwmod omap3xxx_wd_timer2_hwmod = { 425static struct omap_hwmod omap3xxx_wd_timer2_hwmod = {
diff --git a/arch/arm/mach-omap2/omap_hwmod_44xx_data.c b/arch/arm/mach-omap2/omap_hwmod_44xx_data.c
index c997c97c08b7..950454a3fa31 100644
--- a/arch/arm/mach-omap2/omap_hwmod_44xx_data.c
+++ b/arch/arm/mach-omap2/omap_hwmod_44xx_data.c
@@ -3535,6 +3535,7 @@ static struct omap_hwmod_class omap44xx_wd_timer_hwmod_class = {
3535 .name = "wd_timer", 3535 .name = "wd_timer",
3536 .sysc = &omap44xx_wd_timer_sysc, 3536 .sysc = &omap44xx_wd_timer_sysc,
3537 .pre_shutdown = &omap2_wd_timer_disable, 3537 .pre_shutdown = &omap2_wd_timer_disable,
3538 .reset = &omap2_wd_timer_reset,
3538}; 3539};
3539 3540
3540/* wd_timer2 */ 3541/* wd_timer2 */
diff --git a/arch/arm/mach-omap2/wd_timer.c b/arch/arm/mach-omap2/wd_timer.c
index 4067669d96c4..b2f1c67043a2 100644
--- a/arch/arm/mach-omap2/wd_timer.c
+++ b/arch/arm/mach-omap2/wd_timer.c
@@ -14,6 +14,7 @@
14#include <plat/omap_hwmod.h> 14#include <plat/omap_hwmod.h>
15 15
16#include "wd_timer.h" 16#include "wd_timer.h"
17#include "common.h"
17 18
18/* 19/*
19 * In order to avoid any assumptions from bootloader regarding WDT 20 * In order to avoid any assumptions from bootloader regarding WDT
@@ -25,6 +26,8 @@
25#define OMAP_WDT_WPS 0x34 26#define OMAP_WDT_WPS 0x34
26#define OMAP_WDT_SPR 0x48 27#define OMAP_WDT_SPR 0x48
27 28
29/* Maximum microseconds to wait for OMAP module to softreset */
30#define MAX_MODULE_SOFTRESET_WAIT 10000
28 31
29int omap2_wd_timer_disable(struct omap_hwmod *oh) 32int omap2_wd_timer_disable(struct omap_hwmod *oh)
30{ 33{
@@ -54,3 +57,45 @@ int omap2_wd_timer_disable(struct omap_hwmod *oh)
54 return 0; 57 return 0;
55} 58}
56 59
60/**
61 * omap2_wdtimer_reset - reset and disable the WDTIMER IP block
62 * @oh: struct omap_hwmod *
63 *
64 * After the WDTIMER IP blocks are reset on OMAP2/3, we must also take
65 * care to execute the special watchdog disable sequence. This is
66 * because the watchdog is re-armed upon OCP softreset. (On OMAP4,
67 * this behavior was apparently changed and the watchdog is no longer
68 * re-armed after an OCP soft-reset.) Returns -ETIMEDOUT if the reset
69 * did not complete, or 0 upon success.
70 *
71 * XXX Most of this code should be moved to the omap_hwmod.c layer
72 * during a normal merge window. omap_hwmod_softreset() should be
73 * renamed to omap_hwmod_set_ocp_softreset(), and omap_hwmod_softreset()
74 * should call the hwmod _ocp_softreset() code.
75 */
76int omap2_wd_timer_reset(struct omap_hwmod *oh)
77{
78 int c = 0;
79
80 /* Write to the SOFTRESET bit */
81 omap_hwmod_softreset(oh);
82
83 /* Poll on RESETDONE bit */
84 omap_test_timeout((omap_hwmod_read(oh,
85 oh->class->sysc->syss_offs)
86 & SYSS_RESETDONE_MASK),
87 MAX_MODULE_SOFTRESET_WAIT, c);
88
89 if (oh->class->sysc->srst_udelay)
90 udelay(oh->class->sysc->srst_udelay);
91
92 if (c == MAX_MODULE_SOFTRESET_WAIT)
93 pr_warning("%s: %s: softreset failed (waited %d usec)\n",
94 __func__, oh->name, MAX_MODULE_SOFTRESET_WAIT);
95 else
96 pr_debug("%s: %s: softreset in %d usec\n", __func__,
97 oh->name, c);
98
99 return (c == MAX_MODULE_SOFTRESET_WAIT) ? -ETIMEDOUT :
100 omap2_wd_timer_disable(oh);
101}
diff --git a/arch/arm/mach-omap2/wd_timer.h b/arch/arm/mach-omap2/wd_timer.h
index e0054a2d5505..f6bbba73b535 100644
--- a/arch/arm/mach-omap2/wd_timer.h
+++ b/arch/arm/mach-omap2/wd_timer.h
@@ -13,5 +13,6 @@
13#include <plat/omap_hwmod.h> 13#include <plat/omap_hwmod.h>
14 14
15extern int omap2_wd_timer_disable(struct omap_hwmod *oh); 15extern int omap2_wd_timer_disable(struct omap_hwmod *oh);
16extern int omap2_wd_timer_reset(struct omap_hwmod *oh);
16 17
17#endif 18#endif