|  | @@ -73,3 +73,185 @@ static struct powerdomain *_pwrdm_lookup(const char *name)
 | 
	
		
			
				|  |  |   * Adds a powerdomain to the internal powerdomain list.  Returns
 | 
	
		
			
				|  |  |   * -EINVAL if given a null pointer, -EEXIST if a powerdomain is
 | 
	
		
			
				|  |  |   * already registered by the provided name, or 0 upon success.
 | 
	
		
			
				|  |  | + */
 | 
	
		
			
				|  |  | +static int _pwrdm_register(struct powerdomain *pwrdm)
 | 
	
		
			
				|  |  | +{
 | 
	
		
			
				|  |  | +	int i;
 | 
	
		
			
				|  |  | +	struct voltagedomain *voltdm;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	if (!pwrdm || !pwrdm->name)
 | 
	
		
			
				|  |  | +		return -EINVAL;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	if (cpu_is_omap44xx() &&
 | 
	
		
			
				|  |  | +	    pwrdm->prcm_partition == OMAP4430_INVALID_PRCM_PARTITION) {
 | 
	
		
			
				|  |  | +		pr_err("powerdomain: %s: missing OMAP4 PRCM partition ID\n",
 | 
	
		
			
				|  |  | +		       pwrdm->name);
 | 
	
		
			
				|  |  | +		return -EINVAL;
 | 
	
		
			
				|  |  | +	}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	if (_pwrdm_lookup(pwrdm->name))
 | 
	
		
			
				|  |  | +		return -EEXIST;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	voltdm = voltdm_lookup(pwrdm->voltdm.name);
 | 
	
		
			
				|  |  | +	if (!voltdm) {
 | 
	
		
			
				|  |  | +		pr_err("powerdomain: %s: voltagedomain %s does not exist\n",
 | 
	
		
			
				|  |  | +		       pwrdm->name, pwrdm->voltdm.name);
 | 
	
		
			
				|  |  | +		return -EINVAL;
 | 
	
		
			
				|  |  | +	}
 | 
	
		
			
				|  |  | +	pwrdm->voltdm.ptr = voltdm;
 | 
	
		
			
				|  |  | +	INIT_LIST_HEAD(&pwrdm->voltdm_node);
 | 
	
		
			
				|  |  | +	voltdm_add_pwrdm(voltdm, pwrdm);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	list_add(&pwrdm->node, &pwrdm_list);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	/* Initialize the powerdomain's state counter */
 | 
	
		
			
				|  |  | +	for (i = 0; i < PWRDM_MAX_PWRSTS; i++)
 | 
	
		
			
				|  |  | +		pwrdm->state_counter[i] = 0;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	pwrdm->ret_logic_off_counter = 0;
 | 
	
		
			
				|  |  | +	for (i = 0; i < pwrdm->banks; i++)
 | 
	
		
			
				|  |  | +		pwrdm->ret_mem_off_counter[i] = 0;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	pwrdm_wait_transition(pwrdm);
 | 
	
		
			
				|  |  | +	pwrdm->state = pwrdm_read_pwrst(pwrdm);
 | 
	
		
			
				|  |  | +	pwrdm->state_counter[pwrdm->state] = 1;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	pr_debug("powerdomain: registered %s\n", pwrdm->name);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	return 0;
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +static void _update_logic_membank_counters(struct powerdomain *pwrdm)
 | 
	
		
			
				|  |  | +{
 | 
	
		
			
				|  |  | +	int i;
 | 
	
		
			
				|  |  | +	u8 prev_logic_pwrst, prev_mem_pwrst;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	prev_logic_pwrst = pwrdm_read_prev_logic_pwrst(pwrdm);
 | 
	
		
			
				|  |  | +	if ((pwrdm->pwrsts_logic_ret == PWRSTS_OFF_RET) &&
 | 
	
		
			
				|  |  | +	    (prev_logic_pwrst == PWRDM_POWER_OFF))
 | 
	
		
			
				|  |  | +		pwrdm->ret_logic_off_counter++;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	for (i = 0; i < pwrdm->banks; i++) {
 | 
	
		
			
				|  |  | +		prev_mem_pwrst = pwrdm_read_prev_mem_pwrst(pwrdm, i);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +		if ((pwrdm->pwrsts_mem_ret[i] == PWRSTS_OFF_RET) &&
 | 
	
		
			
				|  |  | +		    (prev_mem_pwrst == PWRDM_POWER_OFF))
 | 
	
		
			
				|  |  | +			pwrdm->ret_mem_off_counter[i]++;
 | 
	
		
			
				|  |  | +	}
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +static int _pwrdm_state_switch(struct powerdomain *pwrdm, int flag)
 | 
	
		
			
				|  |  | +{
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	int prev, state, trace_state = 0;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	if (pwrdm == NULL)
 | 
	
		
			
				|  |  | +		return -EINVAL;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	state = pwrdm_read_pwrst(pwrdm);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	switch (flag) {
 | 
	
		
			
				|  |  | +	case PWRDM_STATE_NOW:
 | 
	
		
			
				|  |  | +		prev = pwrdm->state;
 | 
	
		
			
				|  |  | +		break;
 | 
	
		
			
				|  |  | +	case PWRDM_STATE_PREV:
 | 
	
		
			
				|  |  | +		prev = pwrdm_read_prev_pwrst(pwrdm);
 | 
	
		
			
				|  |  | +		if (pwrdm->state != prev)
 | 
	
		
			
				|  |  | +			pwrdm->state_counter[prev]++;
 | 
	
		
			
				|  |  | +		if (prev == PWRDM_POWER_RET)
 | 
	
		
			
				|  |  | +			_update_logic_membank_counters(pwrdm);
 | 
	
		
			
				|  |  | +		/*
 | 
	
		
			
				|  |  | +		 * If the power domain did not hit the desired state,
 | 
	
		
			
				|  |  | +		 * generate a trace event with both the desired and hit states
 | 
	
		
			
				|  |  | +		 */
 | 
	
		
			
				|  |  | +		if (state != prev) {
 | 
	
		
			
				|  |  | +			trace_state = (PWRDM_TRACE_STATES_FLAG |
 | 
	
		
			
				|  |  | +				       ((state & OMAP_POWERSTATE_MASK) << 8) |
 | 
	
		
			
				|  |  | +				       ((prev & OMAP_POWERSTATE_MASK) << 0));
 | 
	
		
			
				|  |  | +			trace_power_domain_target(pwrdm->name, trace_state,
 | 
	
		
			
				|  |  | +						  smp_processor_id());
 | 
	
		
			
				|  |  | +		}
 | 
	
		
			
				|  |  | +		break;
 | 
	
		
			
				|  |  | +	default:
 | 
	
		
			
				|  |  | +		return -EINVAL;
 | 
	
		
			
				|  |  | +	}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	if (state != prev)
 | 
	
		
			
				|  |  | +		pwrdm->state_counter[state]++;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	pm_dbg_update_time(pwrdm, prev);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	pwrdm->state = state;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	return 0;
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +static int _pwrdm_pre_transition_cb(struct powerdomain *pwrdm, void *unused)
 | 
	
		
			
				|  |  | +{
 | 
	
		
			
				|  |  | +	pwrdm_clear_all_prev_pwrst(pwrdm);
 | 
	
		
			
				|  |  | +	_pwrdm_state_switch(pwrdm, PWRDM_STATE_NOW);
 | 
	
		
			
				|  |  | +	return 0;
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +static int _pwrdm_post_transition_cb(struct powerdomain *pwrdm, void *unused)
 | 
	
		
			
				|  |  | +{
 | 
	
		
			
				|  |  | +	_pwrdm_state_switch(pwrdm, PWRDM_STATE_PREV);
 | 
	
		
			
				|  |  | +	return 0;
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +/* Public functions */
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +/**
 | 
	
		
			
				|  |  | + * pwrdm_register_platform_funcs - register powerdomain implementation fns
 | 
	
		
			
				|  |  | + * @po: func pointers for arch specific implementations
 | 
	
		
			
				|  |  | + *
 | 
	
		
			
				|  |  | + * Register the list of function pointers used to implement the
 | 
	
		
			
				|  |  | + * powerdomain functions on different OMAP SoCs.  Should be called
 | 
	
		
			
				|  |  | + * before any other pwrdm_register*() function.  Returns -EINVAL if
 | 
	
		
			
				|  |  | + * @po is null, -EEXIST if platform functions have already been
 | 
	
		
			
				|  |  | + * registered, or 0 upon success.
 | 
	
		
			
				|  |  | + */
 | 
	
		
			
				|  |  | +int pwrdm_register_platform_funcs(struct pwrdm_ops *po)
 | 
	
		
			
				|  |  | +{
 | 
	
		
			
				|  |  | +	if (!po)
 | 
	
		
			
				|  |  | +		return -EINVAL;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	if (arch_pwrdm)
 | 
	
		
			
				|  |  | +		return -EEXIST;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	arch_pwrdm = po;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	return 0;
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +/**
 | 
	
		
			
				|  |  | + * pwrdm_register_pwrdms - register SoC powerdomains
 | 
	
		
			
				|  |  | + * @ps: pointer to an array of struct powerdomain to register
 | 
	
		
			
				|  |  | + *
 | 
	
		
			
				|  |  | + * Register the powerdomains available on a particular OMAP SoC.  Must
 | 
	
		
			
				|  |  | + * be called after pwrdm_register_platform_funcs().  May be called
 | 
	
		
			
				|  |  | + * multiple times.  Returns -EACCES if called before
 | 
	
		
			
				|  |  | + * pwrdm_register_platform_funcs(); -EINVAL if the argument @ps is
 | 
	
		
			
				|  |  | + * null; or 0 upon success.
 | 
	
		
			
				|  |  | + */
 | 
	
		
			
				|  |  | +int pwrdm_register_pwrdms(struct powerdomain **ps)
 | 
	
		
			
				|  |  | +{
 | 
	
		
			
				|  |  | +	struct powerdomain **p = NULL;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	if (!arch_pwrdm)
 | 
	
		
			
				|  |  | +		return -EEXIST;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	if (!ps)
 | 
	
		
			
				|  |  | +		return -EINVAL;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	for (p = ps; *p; p++)
 | 
	
		
			
				|  |  | +		_pwrdm_register(*p);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	return 0;
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +/**
 | 
	
		
			
				|  |  | + * pwrdm_complete_init - set up the powerdomain layer
 | 
	
		
			
				|  |  | + *
 | 
	
		
			
				|  |  | + * Do whatever is necessary to initialize registered powerdomains and
 | 
	
		
			
				|  |  | + * powerdomain code.  Currently, this programs the next power state
 |