Paul, > -----Original Message----- > From: Paul Walmsley [mailto:paul@xxxxxxxxx] > Sent: Friday, October 15, 2010 12:08 AM > To: Varadarajan, Charulatha > Cc: Kevin Hilman; Cousson, Benoit; linux-omap@xxxxxxxxxxxxxxx > Subject: RE: [PATCH] omap2plus: wdt: Fix boot warn when > CONFIG_PM_RUNTIME=n > > Hello Charu, Kevin, Benoît, > > On Thu, 14 Oct 2010, Varadarajan, Charulatha wrote: > > > Please provide your input on this. > > Thinking about this problem -- and the earlier problem of how to > determine what state to leave the watchdog in -- what occurs to me is > that we should probably: Thanks for providing detailed input on this. > > 1. add a function pointer in the struct omap_hwmod_class for a custom > pre-shutdown function, called before clocks are disabled, etc.; that > can do the appropriate register writes needed during an > omap_hwmod_shutdown() while the device is still considered to be > enabled; > > 2. add a function to allow OMAP core code or board-*.c code to tell > the hwmod code to leave a hwmod disabled at the end of _setup(), > rather than idled or left enabled -- something like > omap_hwmod_set_postsetup_state(HWMOD_STATE_DISABLED); > > 3. look to see if we can detect if the watchdog is enabled before the > hwmod code starts -- perhaps by reading WSPR? -- and if it is not > enabled, calling > omap_hwmod_set_postsetup_state(HWMOD_STATE_DISABLED) prior to > calling omap_hwmod_late_init(); > > 4. disable the MPU watchdog by default if #3 is not possible, and then > allow specific board files to indicate that it should be enabled if > they want full kernel boot watchdog coverage with something like > omap_hwmod_set_postsetup_state(HWMOD_STATE_IDLE); and possibly also > add a kernel command line parameter like "enable_omapwdt_boot" that > can specify that the watchdog should be left enabled upon boot. > > This arrangement is a little more complicated, but it ensures that > omap_hwmod_shutdown() really will shut down the watchdog. It also > makes it possible for individual board files or bootloader > configurations to ensure full watchdog coverage of the kernel > initialization process. If I were designing a commercial OMAP-based > device, I'd probably want full watchdog coverage of the entire boot > process from the bootloader onwards. But simultaneously we don't want > to change the existing kernel behavior too much -- most of our > developer users would probably be shocked if, upon upgrading to a new > kernel, suddenly the watchdog started rebooting their machines -- so > it makes sense to me to disable the watchdog unless instructed > otherwise. > > Enclosed below is a work-in-progress patch to illustrate the proposal. > It clearly needs some more work and should be split into at least two > patches, but it might help clarify what I am proposing. > > We also should, IMHO, move the struct omap_hwmod_class definitions out of > the main hwmod data files, since they may contain function pointers. (We > will probably also need a .reset function pointer there also to deal > with complicated IPs like the IVA2, as we discussed a few months ago.) > We should try to restrict the main omap_hwmod data files to be relatively > OS-independent data structures. > > Regarding the omap_hwmod_is_enabled() proposal: it seems racy to me. In > theory the system state can change after the result of > omap_hwmod_is_enabled() is tested, but before the consequence of the > conditional statement runs. There used to be a similar problem in the > clock framework with a clk_is_enabled() function (a similar race still > exists with the clk_get_rate() function). If possible, I think we > should try to avoid creating APIs that are explicitly racy. > > Charu, I think that functions like omap2_disable_wdt() and any related > watchdog device manipulation functions should go into their own file > -- something like arch/arm/mach-omap2/wd_timer.c ? -- and not live in > devices.c. Yes, you are right. I can do this in a new file arch/arm/mach-omap2/wd_timer.c. After doing this, I can also move the omap_device_build of wdt device from devices.c to mach-omap2/wd_timer.c. -V Charulatha > > One last thing: Benoît, in the hwmod data, I think we should separate > watchdog IPs that are automatically enabled upon reset (like the MPU > watchdog), from watchdogs that are disabled after reset (like the IVA2 > watchdog). It doesn't matter to me whether we create a second class, > or whether we just use a dev_attr flag. If we separate these, then > code can use omap_hwmod_for_each_class() to ensure that all watchdogs > of this type are disabled upon boot, rather than hardcoding a specific > hwmod. > > > Thoughts? > > ... > > As an aside, we seem to again be skirting issues of what it means for > a hwmod to be "enabled" and "idle." If we read "enabled" as "fully > functional and registers accessible," that seems to make sense; but if > we consider idle as meaning "configured but nonfunctional," that does > not match up to the state that modules like WDT and GPIO are left in. > The issue with WDT is that it probably does not IdleAck when enabled > and its clocks are turned off, as Benoît mentioned; and the issue with > GPIO is that it can generate asynchronous interrupts, even when all > its clocks are off. It would be good if we could clarify the > semantics for these states in some formal way. Perhaps more states > are needed at the hwmod level? Or perhaps we just need to clarify > what "idle" really means... > > > - Paul > > > From 2e05f1c33b2c5b081146079a2908fe5bbb4350d6 Mon Sep 17 00:00:00 2001 > From: Paul Walmsley <paul@xxxxxxxxx> > Date: Thu, 14 Oct 2010 11:47:46 -0600 > Subject: [RFC PATCH] OMAP2+: hwmod: add postsetup state suppotr > > Experimental, didactic patch to add postsetup state, selected at runtime. > This patch needs major work before merging; it's simply intended to > illustrate a proposal. > > Not-yet-signed-off-by: Paul Walmsley <paul@xxxxxxxxx> > --- > arch/arm/mach-omap2/io.c | 53 ++++++++++++++++++++- > -- > arch/arm/mach-omap2/omap_hwmod.c | 59 +++++++++++++++++++-- > ----- > arch/arm/plat-omap/include/plat/omap_hwmod.h | 16 +++++++- > 3 files changed, 105 insertions(+), 23 deletions(-) > > diff --git a/arch/arm/mach-omap2/io.c b/arch/arm/mach-omap2/io.c > index 40562dd..e3a423d 100644 > --- a/arch/arm/mach-omap2/io.c > +++ b/arch/arm/mach-omap2/io.c > @@ -311,10 +311,15 @@ static int __init _omap2_init_reprogram_sdrc(void) > return v; > } > > -void __init omap2_init_common_hw(struct omap_sdrc_params *sdrc_cs0, > - struct omap_sdrc_params *sdrc_cs1) > +/* XXX document */ > +static int _set_hwmod_postsetup_state(struct omap_hwmod *oh, void *data) > +{ > + return omap_hwmod_set_postsetup_state(oh, *(u8 *)data); > +} > + > +static void __init omap2_init_common_infra(void) > { > - u8 skip_setup_idle = 0; > + u8 postsetup_state; > > pwrdm_init(powerdomains_omap); > clkdm_init(clockdomains_omap, clkdm_autodeps); > @@ -327,6 +332,24 @@ void __init omap2_init_common_hw(struct > omap_sdrc_params *sdrc_cs0, > else if (cpu_is_omap44xx()) > omap44xx_hwmod_init(); > > + /* Set the default postsetup state for all hwmods ... */ > +#ifdef CONFIG_PM_RUNTIME > + postsetup_state = _HWMOD_STATE_IDLE; > +#else > + postsetup_state = _HWMOD_STATE_ENABLED; > +#endif > + omap_hwmod_for_each(_set_hwmod_postsetup_state, &postsetup_state); > + > + /* ... then handle the exceptions for unusual modules (like MPU WDT) > */ > + /* > + * XXX ideally we could detect whether the MPU WDT was currently > + * enabled here and make this conditional > + */ > + postsetup_state = _HWMOD_STATE_DISABLED; > + omap_hwmod_for_each_by_class("wd_timer_enabled_on_reset", > + _set_hwmod_postsetup_state, > + &postsetup_state); > + > /* The OPP tables have to be registered before a clk init */ > omap_pm_if_early_init(mpu_opps, dsp_opps, l3_opps); > > @@ -340,16 +363,32 @@ void __init omap2_init_common_hw(struct > omap_sdrc_params *sdrc_cs0, > omap4xxx_clk_init(); > else > pr_err("Could not init clock framework - unknown CPU\n"); > +} > > +static void __init omap2_init_common_devices(struct omap_sdrc_params > *sdrc_cs0, > + struct omap_sdrc_params *sdrc_cs1) > +{ > omap_serial_early_init(); > > -#ifndef CONFIG_PM_RUNTIME > - skip_setup_idle = 1; > -#endif > - omap_hwmod_late_init(skip_setup_idle); > + omap_hwmod_late_init(); > + > if (cpu_is_omap24xx() || cpu_is_omap34xx()) { > omap2_sdrc_init(sdrc_cs0, sdrc_cs1); > _omap2_init_reprogram_sdrc(); > } > gpmc_init(); > } > + > +/* > + * XXX this function should go away and the board-*.c files should call > + * both omap2_init_common_infra() and omap2_init_common_devices(), > + * so the board-*.c files can override some default hwmod > postsetup_states > + * (e.g., for watchdog) between the two functions. > + */ > +void __init omap2_init_common_hw(struct omap_sdrc_params *sdrc_cs0, > + struct omap_sdrc_params *sdrc_cs1) > +{ > + omap2_init_common_infra(); > + omap2_init_common_devices(sdrc_cs0, sdrc_cs1); > +} > + > diff --git a/arch/arm/mach-omap2/omap_hwmod.c b/arch/arm/mach- > omap2/omap_hwmod.c > index 5a30658..618e135 100644 > --- a/arch/arm/mach-omap2/omap_hwmod.c > +++ b/arch/arm/mach-omap2/omap_hwmod.c > @@ -1298,24 +1298,19 @@ static int _shutdown(struct omap_hwmod *oh) > /** > * _setup - do initial configuration of omap_hwmod > * @oh: struct omap_hwmod * > - * @skip_setup_idle_p: do not idle hwmods at the end of the fn if 1 > * > * Writes the CLOCKACTIVITY bits @clockact to the hwmod @oh > - * OCP_SYSCONFIG register. @skip_setup_idle is intended to be used on > - * a system that will not call omap_hwmod_enable() to enable devices > - * (e.g., a system without PM runtime). Returns -EINVAL if the hwmod > - * is in the wrong state or returns 0. > + * OCP_SYSCONFIG register. Returns -EINVAL if the hwmod is in the > + * wrong state or returns 0. > */ > static int _setup(struct omap_hwmod *oh, void *data) > { > int i, r; > - u8 skip_setup_idle; > + u8 postsetup_state; > > if (!oh || !data) > return -EINVAL; > > - skip_setup_idle = *(u8 *)data; > - > /* Set iclk autoidle mode */ > if (oh->slaves_cnt > 0) { > for (i = 0; i < oh->slaves_cnt; i++) { > @@ -1368,8 +1363,24 @@ static int _setup(struct omap_hwmod *oh, void > *data) > } > } > > - if (!(oh->flags & HWMOD_INIT_NO_IDLE) && !skip_setup_idle) > + postsetup_state = oh->_postsetup_state; > + if (postsetup_state == _HWMOD_STATE_UNKNOWN) > + postsetup_state = _HWMOD_STATE_ENABLED; > + > + /* > + * XXX HWMOD_INIT_NO_IDLE does not belong in hwmod data - > + * it should be set by the core code as a runtime flag during > startup > + */ > + if (!(oh->flags & HWMOD_INIT_NO_IDLE)) > + postsetup_state = _HWMOD_STATE_IDLE; > + > + if (postsetup_state == _HWMOD_STATE_IDLE) > _omap_hwmod_idle(oh); > + else if (postsetup_state == _HWMOD_STATE_DISABLED) > + _shutdown(oh); > + else > + WARN(1, "hwmod: %s: unknown postsetup state %d! defaulting to > enabled\n", > + oh->name, postsetup_state); > > return 0; > } > @@ -1570,13 +1581,12 @@ int omap_hwmod_init(struct omap_hwmod **ohs) > > /** > * omap_hwmod_late_init - do some post-clock framework initialization > - * @skip_setup_idle: if 1, do not idle hwmods in _setup() > * > * Must be called after omap2_clk_init(). Resolves the struct clk names > * to struct clk pointers for each registered omap_hwmod. Also calls > * _setup() on each hwmod. Returns 0. > */ > -int omap_hwmod_late_init(u8 skip_setup_idle) > +int omap_hwmod_late_init(void) > { > int r; > > @@ -1588,10 +1598,7 @@ int omap_hwmod_late_init(u8 skip_setup_idle) > WARN(!mpu_oh, "omap_hwmod: could not find MPU initiator hwmod %s\n", > MPU_INITIATOR_NAME); > > - if (skip_setup_idle) > - pr_debug("omap_hwmod: will leave hwmods enabled during > setup\n"); > - > - omap_hwmod_for_each(_setup, &skip_setup_idle); > + omap_hwmod_for_each(_setup, NULL); > > return 0; > } > @@ -2117,3 +2124,25 @@ int omap_hwmod_for_each_by_class(const char > *classname, > return ret; > } > > + > +/** > + * omap_hwmod_set_postsetup_state - set the post-_setup() state for this > hwmod > + * @oh: struct omap_hwmod * > + * @state: state that _setup() should leave the hwmod in > + * > + * XXX document > + * > + */ > +int omap_hwmod_set_postsetup_state(struct omap_hwmod *oh, u8 state) > +{ > + if (!oh) > + return -EINVAL; > + > + /* XXX insure valid @state passed in */ > + > + mutex_lock(&oh->_mutex); > + oh->_postsetup_state = state; > + mutex_unlock(&oh->_mutex); > + > + return 0; > +} > diff --git a/arch/arm/plat-omap/include/plat/omap_hwmod.h b/arch/arm/plat- > omap/include/plat/omap_hwmod.h > index 7eaa8ed..a566efc 100644 > --- a/arch/arm/plat-omap/include/plat/omap_hwmod.h > +++ b/arch/arm/plat-omap/include/plat/omap_hwmod.h > @@ -415,14 +415,24 @@ struct omap_hwmod_omap4_prcm { > * @name: name of the hwmod_class > * @sysc: device SYSCONFIG/SYSSTATUS register data > * @rev: revision of the IP class > + * @pre_shutdown: fn ptr to be executed immediately prior to device > shutdown > * > * Represent the class of a OMAP hardware "modules" (e.g. timer, > * smartreflex, gpio, uart...) > + * > + * @pre_shutdown is a function that will be run immediately before > + * hwmod clocks are disabled, etc. It is intended for use for hwmods > + * like the MPU watchdog, which cannot be disabled with the standard > + * omap_hwmod_shutdown(). The function should return 0 upon success, > + * or some negative error upon failure. Returning an error will cause > + * omap_hwmod_shutdown() to abort the device shutdown and return an > + * error. > */ > struct omap_hwmod_class { > const char *name; > struct omap_hwmod_class_sysconfig *sysc; > u32 rev; > + int (*pre_shutdown)(struct omap_hwmod > *oh); > }; > > /** > @@ -452,6 +462,7 @@ struct omap_hwmod_class { > * @response_lat: device OCP response latency (in interface clock cycles) > * @_int_flags: internal-use hwmod flags > * @_state: internal-use hwmod state > + * @_postsetup_state: internal-use state to leave the hwmod in after > _setup() > * @flags: hwmod flags (documented below) > * @omap_chip: OMAP chips this hwmod is present on > * @_mutex: mutex serializing operations on this hwmod > @@ -500,6 +511,7 @@ struct omap_hwmod { > u8 hwmods_cnt; > u8 _int_flags; > u8 _state; > + u8 _postsetup_state; > const struct omap_chip_id omap_chip; > }; > > @@ -509,7 +521,7 @@ int omap_hwmod_unregister(struct omap_hwmod *oh); > struct omap_hwmod *omap_hwmod_lookup(const char *name); > int omap_hwmod_for_each(int (*fn)(struct omap_hwmod *oh, void *data), > void *data); > -int omap_hwmod_late_init(u8 skip_setup_idle); > +int omap_hwmod_late_init(void); > > int omap_hwmod_enable(struct omap_hwmod *oh); > int _omap_hwmod_enable(struct omap_hwmod *oh); > @@ -556,6 +568,8 @@ int omap_hwmod_for_each_by_class(const char *classname, > void *user), > void *user); > > +int omap_hwmod_set_postsetup_state(struct omap_hwmod *oh, u8 state); > + > /* > * Chip variant-specific hwmod init routines - XXX should be converted > * to use initcalls once the initial boot ordering is straightened out > -- > 1.7.1 -- To unsubscribe from this list: send the line "unsubscribe linux-omap" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html