diff --git a/arch/arm/mach-omap2/omap_hwmod.c b/arch/arm/mach-omap2/omap_hwmod.c index bf86f7e8f91f5837869cb1b799f2b59ab4a81395..6d6c31a10a1b43933b6e7854b89ba85f1fc82e0d 100644 --- a/arch/arm/mach-omap2/omap_hwmod.c +++ b/arch/arm/mach-omap2/omap_hwmod.c @@ -153,6 +153,7 @@ #include "prm44xx.h" #include "prminst44xx.h" #include "mux.h" +#include "pm.h" /* Maximum microseconds to wait for OMAP module to softreset */ #define MAX_MODULE_SOFTRESET_WAIT 10000 @@ -172,6 +173,9 @@ static LIST_HEAD(omap_hwmod_list); /* mpu_oh: used to add/remove MPU initiator from sleepdep list */ static struct omap_hwmod *mpu_oh; +/* io_chain_lock: used to serialize reconfigurations of the I/O chain */ +static DEFINE_SPINLOCK(io_chain_lock); + /* * linkspace: ptr to a buffer that struct omap_hwmod_link records are * allocated from - used to reduce the number of small memory @@ -1737,6 +1741,32 @@ static int _reset(struct omap_hwmod *oh) return r; } +/** + * _reconfigure_io_chain - clear any I/O chain wakeups and reconfigure chain + * + * Call the appropriate PRM function to clear any logged I/O chain + * wakeups and to reconfigure the chain. This apparently needs to be + * done upon every mux change. Since hwmods can be concurrently + * enabled and idled, hold a spinlock around the I/O chain + * reconfiguration sequence. No return value. + * + * XXX When the PRM code is moved to drivers, this function can be removed, + * as the PRM infrastructure should abstract this. + */ +static void _reconfigure_io_chain(void) +{ + unsigned long flags; + + spin_lock_irqsave(&io_chain_lock, flags); + + if (cpu_is_omap34xx() && omap3_has_io_chain_ctrl()) + omap3xxx_prm_reconfigure_io_chain(); + else if (cpu_is_omap44xx()) + omap44xx_prm_reconfigure_io_chain(); + + spin_unlock_irqrestore(&io_chain_lock, flags); +} + /** * _enable - enable an omap_hwmod * @oh: struct omap_hwmod * @@ -1793,8 +1823,10 @@ static int _enable(struct omap_hwmod *oh) /* Mux pins for device runtime if populated */ if (oh->mux && (!oh->mux->enabled || ((oh->_state == _HWMOD_STATE_IDLE) && - oh->mux->pads_dynamic))) + oh->mux->pads_dynamic))) { omap_hwmod_mux(oh->mux, _HWMOD_STATE_ENABLED); + _reconfigure_io_chain(); + } _add_initiator_dep(oh, mpu_oh); @@ -1883,8 +1915,10 @@ static int _idle(struct omap_hwmod *oh) clkdm_hwmod_disable(oh->clkdm, oh); /* Mux pins for device idle if populated */ - if (oh->mux && oh->mux->pads_dynamic) + if (oh->mux && oh->mux->pads_dynamic) { omap_hwmod_mux(oh->mux, _HWMOD_STATE_IDLE); + _reconfigure_io_chain(); + } oh->_state = _HWMOD_STATE_IDLE;