|  | @@ -1993,3 +1993,200 @@ static int _reset(struct omap_hwmod *oh)
 | 
	
		
			
				|  |  |  /**
 | 
	
		
			
				|  |  |   * _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);
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +/**
 | 
	
		
			
				|  |  | + * _omap4_update_context_lost - increment hwmod context loss counter if
 | 
	
		
			
				|  |  | + * hwmod context was lost, and clear hardware context loss reg
 | 
	
		
			
				|  |  | + * @oh: hwmod to check for context loss
 | 
	
		
			
				|  |  | + *
 | 
	
		
			
				|  |  | + * If the PRCM indicates that the hwmod @oh lost context, increment
 | 
	
		
			
				|  |  | + * our in-memory context loss counter, and clear the RM_*_CONTEXT
 | 
	
		
			
				|  |  | + * bits. No return value.
 | 
	
		
			
				|  |  | + */
 | 
	
		
			
				|  |  | +static void _omap4_update_context_lost(struct omap_hwmod *oh)
 | 
	
		
			
				|  |  | +{
 | 
	
		
			
				|  |  | +	if (oh->prcm.omap4.flags & HWMOD_OMAP4_NO_CONTEXT_LOSS_BIT)
 | 
	
		
			
				|  |  | +		return;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	if (!prm_was_any_context_lost_old(oh->clkdm->pwrdm.ptr->prcm_partition,
 | 
	
		
			
				|  |  | +					  oh->clkdm->pwrdm.ptr->prcm_offs,
 | 
	
		
			
				|  |  | +					  oh->prcm.omap4.context_offs))
 | 
	
		
			
				|  |  | +		return;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	oh->prcm.omap4.context_lost_counter++;
 | 
	
		
			
				|  |  | +	prm_clear_context_loss_flags_old(oh->clkdm->pwrdm.ptr->prcm_partition,
 | 
	
		
			
				|  |  | +					 oh->clkdm->pwrdm.ptr->prcm_offs,
 | 
	
		
			
				|  |  | +					 oh->prcm.omap4.context_offs);
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +/**
 | 
	
		
			
				|  |  | + * _omap4_get_context_lost - get context loss counter for a hwmod
 | 
	
		
			
				|  |  | + * @oh: hwmod to get context loss counter for
 | 
	
		
			
				|  |  | + *
 | 
	
		
			
				|  |  | + * Returns the in-memory context loss counter for a hwmod.
 | 
	
		
			
				|  |  | + */
 | 
	
		
			
				|  |  | +static int _omap4_get_context_lost(struct omap_hwmod *oh)
 | 
	
		
			
				|  |  | +{
 | 
	
		
			
				|  |  | +	return oh->prcm.omap4.context_lost_counter;
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +/**
 | 
	
		
			
				|  |  | + * _enable - enable an omap_hwmod
 | 
	
		
			
				|  |  | + * @oh: struct omap_hwmod *
 | 
	
		
			
				|  |  | + *
 | 
	
		
			
				|  |  | + * Enables an omap_hwmod @oh such that the MPU can access the hwmod's
 | 
	
		
			
				|  |  | + * register target.  Returns -EINVAL if the hwmod is in the wrong
 | 
	
		
			
				|  |  | + * state or passes along the return value of _wait_target_ready().
 | 
	
		
			
				|  |  | + */
 | 
	
		
			
				|  |  | +static int _enable(struct omap_hwmod *oh)
 | 
	
		
			
				|  |  | +{
 | 
	
		
			
				|  |  | +	int r;
 | 
	
		
			
				|  |  | +	int hwsup = 0;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	pr_debug("omap_hwmod: %s: enabling\n", oh->name);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	/*
 | 
	
		
			
				|  |  | +	 * hwmods with HWMOD_INIT_NO_IDLE flag set are left in enabled
 | 
	
		
			
				|  |  | +	 * state at init.  Now that someone is really trying to enable
 | 
	
		
			
				|  |  | +	 * them, just ensure that the hwmod mux is set.
 | 
	
		
			
				|  |  | +	 */
 | 
	
		
			
				|  |  | +	if (oh->_int_flags & _HWMOD_SKIP_ENABLE) {
 | 
	
		
			
				|  |  | +		/*
 | 
	
		
			
				|  |  | +		 * If the caller has mux data populated, do the mux'ing
 | 
	
		
			
				|  |  | +		 * which wouldn't have been done as part of the _enable()
 | 
	
		
			
				|  |  | +		 * done during setup.
 | 
	
		
			
				|  |  | +		 */
 | 
	
		
			
				|  |  | +		if (oh->mux)
 | 
	
		
			
				|  |  | +			omap_hwmod_mux(oh->mux, _HWMOD_STATE_ENABLED);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +		oh->_int_flags &= ~_HWMOD_SKIP_ENABLE;
 | 
	
		
			
				|  |  | +		return 0;
 | 
	
		
			
				|  |  | +	}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	if (oh->_state != _HWMOD_STATE_INITIALIZED &&
 | 
	
		
			
				|  |  | +	    oh->_state != _HWMOD_STATE_IDLE &&
 | 
	
		
			
				|  |  | +	    oh->_state != _HWMOD_STATE_DISABLED) {
 | 
	
		
			
				|  |  | +		WARN(1, "omap_hwmod: %s: enabled state can only be entered from initialized, idle, or disabled state\n",
 | 
	
		
			
				|  |  | +			oh->name);
 | 
	
		
			
				|  |  | +		return -EINVAL;
 | 
	
		
			
				|  |  | +	}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	/*
 | 
	
		
			
				|  |  | +	 * If an IP block contains HW reset lines and all of them are
 | 
	
		
			
				|  |  | +	 * asserted, we let integration code associated with that
 | 
	
		
			
				|  |  | +	 * block handle the enable.  We've received very little
 | 
	
		
			
				|  |  | +	 * information on what those driver authors need, and until
 | 
	
		
			
				|  |  | +	 * detailed information is provided and the driver code is
 | 
	
		
			
				|  |  | +	 * posted to the public lists, this is probably the best we
 | 
	
		
			
				|  |  | +	 * can do.
 | 
	
		
			
				|  |  | +	 */
 | 
	
		
			
				|  |  | +	if (_are_all_hardreset_lines_asserted(oh))
 | 
	
		
			
				|  |  | +		return 0;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	/* Mux pins for device runtime if populated */
 | 
	
		
			
				|  |  | +	if (oh->mux && (!oh->mux->enabled ||
 | 
	
		
			
				|  |  | +			((oh->_state == _HWMOD_STATE_IDLE) &&
 | 
	
		
			
				|  |  | +			 oh->mux->pads_dynamic))) {
 | 
	
		
			
				|  |  | +		omap_hwmod_mux(oh->mux, _HWMOD_STATE_ENABLED);
 | 
	
		
			
				|  |  | +		_reconfigure_io_chain();
 | 
	
		
			
				|  |  | +	}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	_add_initiator_dep(oh, mpu_oh);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	if (oh->clkdm) {
 | 
	
		
			
				|  |  | +		/*
 | 
	
		
			
				|  |  | +		 * A clockdomain must be in SW_SUP before enabling
 | 
	
		
			
				|  |  | +		 * completely the module. The clockdomain can be set
 | 
	
		
			
				|  |  | +		 * in HW_AUTO only when the module become ready.
 | 
	
		
			
				|  |  | +		 */
 | 
	
		
			
				|  |  | +		hwsup = clkdm_in_hwsup(oh->clkdm) &&
 | 
	
		
			
				|  |  | +			!clkdm_missing_idle_reporting(oh->clkdm);
 | 
	
		
			
				|  |  | +		r = clkdm_hwmod_enable(oh->clkdm, oh);
 | 
	
		
			
				|  |  | +		if (r) {
 | 
	
		
			
				|  |  | +			WARN(1, "omap_hwmod: %s: could not enable clockdomain %s: %d\n",
 | 
	
		
			
				|  |  | +			     oh->name, oh->clkdm->name, r);
 | 
	
		
			
				|  |  | +			return r;
 | 
	
		
			
				|  |  | +		}
 | 
	
		
			
				|  |  | +	}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	_enable_clocks(oh);
 | 
	
		
			
				|  |  | +	if (soc_ops.enable_module)
 | 
	
		
			
				|  |  | +		soc_ops.enable_module(oh);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	if (soc_ops.update_context_lost)
 | 
	
		
			
				|  |  | +		soc_ops.update_context_lost(oh);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	r = (soc_ops.wait_target_ready) ? soc_ops.wait_target_ready(oh) :
 | 
	
		
			
				|  |  | +		-EINVAL;
 | 
	
		
			
				|  |  | +	if (!r) {
 | 
	
		
			
				|  |  | +		/*
 | 
	
		
			
				|  |  | +		 * Set the clockdomain to HW_AUTO only if the target is ready,
 | 
	
		
			
				|  |  | +		 * assuming that the previous state was HW_AUTO
 | 
	
		
			
				|  |  | +		 */
 | 
	
		
			
				|  |  | +		if (oh->clkdm && hwsup)
 | 
	
		
			
				|  |  | +			clkdm_allow_idle(oh->clkdm);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +		oh->_state = _HWMOD_STATE_ENABLED;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +		/* Access the sysconfig only if the target is ready */
 | 
	
		
			
				|  |  | +		if (oh->class->sysc) {
 | 
	
		
			
				|  |  | +			if (!(oh->_int_flags & _HWMOD_SYSCONFIG_LOADED))
 | 
	
		
			
				|  |  | +				_update_sysc_cache(oh);
 | 
	
		
			
				|  |  | +			_enable_sysc(oh);
 | 
	
		
			
				|  |  | +		}
 | 
	
		
			
				|  |  | +	} else {
 | 
	
		
			
				|  |  | +		if (soc_ops.disable_module)
 | 
	
		
			
				|  |  | +			soc_ops.disable_module(oh);
 | 
	
		
			
				|  |  | +		_disable_clocks(oh);
 | 
	
		
			
				|  |  | +		pr_debug("omap_hwmod: %s: _wait_target_ready: %d\n",
 | 
	
		
			
				|  |  | +			 oh->name, r);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +		if (oh->clkdm)
 | 
	
		
			
				|  |  | +			clkdm_hwmod_disable(oh->clkdm, oh);
 | 
	
		
			
				|  |  | +	}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	return r;
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +/**
 | 
	
		
			
				|  |  | + * _idle - idle an omap_hwmod
 | 
	
		
			
				|  |  | + * @oh: struct omap_hwmod *
 | 
	
		
			
				|  |  | + *
 | 
	
		
			
				|  |  | + * Idles an omap_hwmod @oh.  This should be called once the hwmod has
 | 
	
		
			
				|  |  | + * no further work.  Returns -EINVAL if the hwmod is in the wrong
 | 
	
		
			
				|  |  | + * state or returns 0.
 | 
	
		
			
				|  |  | + */
 | 
	
		
			
				|  |  | +static int _idle(struct omap_hwmod *oh)
 | 
	
		
			
				|  |  | +{
 | 
	
		
			
				|  |  | +	pr_debug("omap_hwmod: %s: idling\n", oh->name);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	if (oh->_state != _HWMOD_STATE_ENABLED) {
 | 
	
		
			
				|  |  | +		WARN(1, "omap_hwmod: %s: idle state can only be entered from enabled state\n",
 | 
	
		
			
				|  |  | +			oh->name);
 | 
	
		
			
				|  |  | +		return -EINVAL;
 | 
	
		
			
				|  |  | +	}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	if (_are_all_hardreset_lines_asserted(oh))
 | 
	
		
			
				|  |  | +		return 0;
 |