|  | @@ -1820,3 +1820,176 @@ static int _omap4_disable_module(struct omap_hwmod *oh)
 | 
	
		
			
				|  |  |  		pr_warn("omap_hwmod: %s: _wait_target_disable failed\n",
 | 
	
		
			
				|  |  |  			oh->name);
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +	return 0;
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +/**
 | 
	
		
			
				|  |  | + * _am33xx_disable_module - enable CLKCTRL modulemode on AM33XX
 | 
	
		
			
				|  |  | + * @oh: struct omap_hwmod *
 | 
	
		
			
				|  |  | + *
 | 
	
		
			
				|  |  | + * Disable the PRCM module mode related to the hwmod @oh.
 | 
	
		
			
				|  |  | + * Return EINVAL if the modulemode is not supported and 0 in case of success.
 | 
	
		
			
				|  |  | + */
 | 
	
		
			
				|  |  | +static int _am33xx_disable_module(struct omap_hwmod *oh)
 | 
	
		
			
				|  |  | +{
 | 
	
		
			
				|  |  | +	int v;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	if (!oh->clkdm || !oh->prcm.omap4.modulemode)
 | 
	
		
			
				|  |  | +		return -EINVAL;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	pr_debug("omap_hwmod: %s: %s\n", oh->name, __func__);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	if (_are_any_hardreset_lines_asserted(oh))
 | 
	
		
			
				|  |  | +		return 0;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	am33xx_cm_module_disable(oh->clkdm->cm_inst, oh->clkdm->clkdm_offs,
 | 
	
		
			
				|  |  | +				 oh->prcm.omap4.clkctrl_offs);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	v = _am33xx_wait_target_disable(oh);
 | 
	
		
			
				|  |  | +	if (v)
 | 
	
		
			
				|  |  | +		pr_warn("omap_hwmod: %s: _wait_target_disable failed\n",
 | 
	
		
			
				|  |  | +			oh->name);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	return 0;
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +/**
 | 
	
		
			
				|  |  | + * _ocp_softreset - reset an omap_hwmod via the OCP_SYSCONFIG bit
 | 
	
		
			
				|  |  | + * @oh: struct omap_hwmod *
 | 
	
		
			
				|  |  | + *
 | 
	
		
			
				|  |  | + * Resets an omap_hwmod @oh via the OCP_SYSCONFIG bit.  hwmod must be
 | 
	
		
			
				|  |  | + * enabled for this to work.  Returns -ENOENT if the hwmod cannot be
 | 
	
		
			
				|  |  | + * reset this way, -EINVAL if the hwmod is in the wrong state,
 | 
	
		
			
				|  |  | + * -ETIMEDOUT if the module did not reset in time, or 0 upon success.
 | 
	
		
			
				|  |  | + *
 | 
	
		
			
				|  |  | + * In OMAP3 a specific SYSSTATUS register is used to get the reset status.
 | 
	
		
			
				|  |  | + * Starting in OMAP4, some IPs do not have SYSSTATUS registers and instead
 | 
	
		
			
				|  |  | + * use the SYSCONFIG softreset bit to provide the status.
 | 
	
		
			
				|  |  | + *
 | 
	
		
			
				|  |  | + * Note that some IP like McBSP do have reset control but don't have
 | 
	
		
			
				|  |  | + * reset status.
 | 
	
		
			
				|  |  | + */
 | 
	
		
			
				|  |  | +static int _ocp_softreset(struct omap_hwmod *oh)
 | 
	
		
			
				|  |  | +{
 | 
	
		
			
				|  |  | +	u32 v;
 | 
	
		
			
				|  |  | +	int c = 0;
 | 
	
		
			
				|  |  | +	int ret = 0;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	if (!oh->class->sysc ||
 | 
	
		
			
				|  |  | +	    !(oh->class->sysc->sysc_flags & SYSC_HAS_SOFTRESET))
 | 
	
		
			
				|  |  | +		return -ENOENT;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	/* clocks must be on for this operation */
 | 
	
		
			
				|  |  | +	if (oh->_state != _HWMOD_STATE_ENABLED) {
 | 
	
		
			
				|  |  | +		pr_warn("omap_hwmod: %s: reset can only be entered from enabled state\n",
 | 
	
		
			
				|  |  | +			oh->name);
 | 
	
		
			
				|  |  | +		return -EINVAL;
 | 
	
		
			
				|  |  | +	}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	/* For some modules, all optionnal clocks need to be enabled as well */
 | 
	
		
			
				|  |  | +	if (oh->flags & HWMOD_CONTROL_OPT_CLKS_IN_RESET)
 | 
	
		
			
				|  |  | +		_enable_optional_clocks(oh);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	pr_debug("omap_hwmod: %s: resetting via OCP SOFTRESET\n", oh->name);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	v = oh->_sysc_cache;
 | 
	
		
			
				|  |  | +	ret = _set_softreset(oh, &v);
 | 
	
		
			
				|  |  | +	if (ret)
 | 
	
		
			
				|  |  | +		goto dis_opt_clks;
 | 
	
		
			
				|  |  | +	_write_sysconfig(v, oh);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	if (oh->class->sysc->srst_udelay)
 | 
	
		
			
				|  |  | +		udelay(oh->class->sysc->srst_udelay);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	c = _wait_softreset_complete(oh);
 | 
	
		
			
				|  |  | +	if (c == MAX_MODULE_SOFTRESET_WAIT)
 | 
	
		
			
				|  |  | +		pr_warning("omap_hwmod: %s: softreset failed (waited %d usec)\n",
 | 
	
		
			
				|  |  | +			   oh->name, MAX_MODULE_SOFTRESET_WAIT);
 | 
	
		
			
				|  |  | +	else
 | 
	
		
			
				|  |  | +		pr_debug("omap_hwmod: %s: softreset in %d usec\n", oh->name, c);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	/*
 | 
	
		
			
				|  |  | +	 * XXX add _HWMOD_STATE_WEDGED for modules that don't come back from
 | 
	
		
			
				|  |  | +	 * _wait_target_ready() or _reset()
 | 
	
		
			
				|  |  | +	 */
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	ret = (c == MAX_MODULE_SOFTRESET_WAIT) ? -ETIMEDOUT : 0;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +dis_opt_clks:
 | 
	
		
			
				|  |  | +	if (oh->flags & HWMOD_CONTROL_OPT_CLKS_IN_RESET)
 | 
	
		
			
				|  |  | +		_disable_optional_clocks(oh);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	return ret;
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +/**
 | 
	
		
			
				|  |  | + * _reset - reset an omap_hwmod
 | 
	
		
			
				|  |  | + * @oh: struct omap_hwmod *
 | 
	
		
			
				|  |  | + *
 | 
	
		
			
				|  |  | + * Resets an omap_hwmod @oh.  If the module has a custom reset
 | 
	
		
			
				|  |  | + * function pointer defined, then call it to reset the IP block, and
 | 
	
		
			
				|  |  | + * pass along its return value to the caller.  Otherwise, if the IP
 | 
	
		
			
				|  |  | + * block has an OCP_SYSCONFIG register with a SOFTRESET bitfield
 | 
	
		
			
				|  |  | + * associated with it, call a function to reset the IP block via that
 | 
	
		
			
				|  |  | + * method, and pass along the return value to the caller.  Finally, if
 | 
	
		
			
				|  |  | + * the IP block has some hardreset lines associated with it, assert
 | 
	
		
			
				|  |  | + * all of those, but do _not_ deassert them. (This is because driver
 | 
	
		
			
				|  |  | + * authors have expressed an apparent requirement to control the
 | 
	
		
			
				|  |  | + * deassertion of the hardreset lines themselves.)
 | 
	
		
			
				|  |  | + *
 | 
	
		
			
				|  |  | + * The default software reset mechanism for most OMAP IP blocks is
 | 
	
		
			
				|  |  | + * triggered via the OCP_SYSCONFIG.SOFTRESET bit.  However, some
 | 
	
		
			
				|  |  | + * hwmods cannot be reset via this method.  Some are not targets and
 | 
	
		
			
				|  |  | + * therefore have no OCP header registers to access.  Others (like the
 | 
	
		
			
				|  |  | + * IVA) have idiosyncratic reset sequences.  So for these relatively
 | 
	
		
			
				|  |  | + * rare cases, custom reset code can be supplied in the struct
 | 
	
		
			
				|  |  | + * omap_hwmod_class .reset function pointer.
 | 
	
		
			
				|  |  | + *
 | 
	
		
			
				|  |  | + * _set_dmadisable() is called to set the DMADISABLE bit so that it
 | 
	
		
			
				|  |  | + * does not prevent idling of the system. This is necessary for cases
 | 
	
		
			
				|  |  | + * where ROMCODE/BOOTLOADER uses dma and transfers control to the
 | 
	
		
			
				|  |  | + * kernel without disabling dma.
 | 
	
		
			
				|  |  | + *
 | 
	
		
			
				|  |  | + * Passes along the return value from either _ocp_softreset() or the
 | 
	
		
			
				|  |  | + * custom reset function - these must return -EINVAL if the hwmod
 | 
	
		
			
				|  |  | + * cannot be reset this way or if the hwmod is in the wrong state,
 | 
	
		
			
				|  |  | + * -ETIMEDOUT if the module did not reset in time, or 0 upon success.
 | 
	
		
			
				|  |  | + */
 | 
	
		
			
				|  |  | +static int _reset(struct omap_hwmod *oh)
 | 
	
		
			
				|  |  | +{
 | 
	
		
			
				|  |  | +	int i, r;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	pr_debug("omap_hwmod: %s: resetting\n", oh->name);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	if (oh->class->reset) {
 | 
	
		
			
				|  |  | +		r = oh->class->reset(oh);
 | 
	
		
			
				|  |  | +	} else {
 | 
	
		
			
				|  |  | +		if (oh->rst_lines_cnt > 0) {
 | 
	
		
			
				|  |  | +			for (i = 0; i < oh->rst_lines_cnt; i++)
 | 
	
		
			
				|  |  | +				_assert_hardreset(oh, oh->rst_lines[i].name);
 | 
	
		
			
				|  |  | +			return 0;
 | 
	
		
			
				|  |  | +		} else {
 | 
	
		
			
				|  |  | +			r = _ocp_softreset(oh);
 | 
	
		
			
				|  |  | +			if (r == -ENOENT)
 | 
	
		
			
				|  |  | +				r = 0;
 | 
	
		
			
				|  |  | +		}
 | 
	
		
			
				|  |  | +	}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	_set_dmadisable(oh);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	/*
 | 
	
		
			
				|  |  | +	 * OCP_SYSCONFIG bits need to be reprogrammed after a
 | 
	
		
			
				|  |  | +	 * softreset.  The _enable() function should be split to avoid
 | 
	
		
			
				|  |  | +	 * the rewrite of the OCP_SYSCONFIG register.
 | 
	
		
			
				|  |  | +	 */
 | 
	
		
			
				|  |  | +	if (oh->class->sysc) {
 | 
	
		
			
				|  |  | +		_update_sysc_cache(oh);
 | 
	
		
			
				|  |  | +		_enable_sysc(oh);
 | 
	
		
			
				|  |  | +	}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	return r;
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +/**
 | 
	
		
			
				|  |  | + * _reconfigure_io_chain - clear any I/O chain wakeups and reconfigure chain
 | 
	
		
			
				|  |  | + *
 |