diff options
Diffstat (limited to 'drivers/acpi/processor_idle.c')
| -rw-r--r-- | drivers/acpi/processor_idle.c | 109 | 
1 files changed, 61 insertions, 48 deletions
| diff --git a/drivers/acpi/processor_idle.c b/drivers/acpi/processor_idle.c index 71a30b0d0f05..f66236cff69b 100644 --- a/drivers/acpi/processor_idle.c +++ b/drivers/acpi/processor_idle.c @@ -161,18 +161,10 @@ static void lapic_timer_propagate_broadcast(struct acpi_processor *pr)  }  /* Power(C) State timer broadcast control */ -static void lapic_timer_state_broadcast(struct acpi_processor *pr, -				       struct acpi_processor_cx *cx, -				       int broadcast) +static bool lapic_timer_needs_broadcast(struct acpi_processor *pr, +					struct acpi_processor_cx *cx)  { -	int state = cx - pr->power.states; - -	if (state >= pr->power.timer_broadcast_on_state) { -		if (broadcast) -			tick_broadcast_enter(); -		else -			tick_broadcast_exit(); -	} +	return cx - pr->power.states >= pr->power.timer_broadcast_on_state;  }  #else @@ -180,10 +172,11 @@ static void lapic_timer_state_broadcast(struct acpi_processor *pr,  static void lapic_timer_check_state(int state, struct acpi_processor *pr,  				   struct acpi_processor_cx *cstate) { }  static void lapic_timer_propagate_broadcast(struct acpi_processor *pr) { } -static void lapic_timer_state_broadcast(struct acpi_processor *pr, -				       struct acpi_processor_cx *cx, -				       int broadcast) + +static bool lapic_timer_needs_broadcast(struct acpi_processor *pr, +					struct acpi_processor_cx *cx)  { +	return false;  }  #endif @@ -566,32 +559,43 @@ static DEFINE_RAW_SPINLOCK(c3_lock);  /**   * acpi_idle_enter_bm - enters C3 with proper BM handling + * @drv: cpuidle driver   * @pr: Target processor   * @cx: Target state context - * @timer_bc: Whether or not to change timer mode to broadcast + * @index: index of target state   */ -static void acpi_idle_enter_bm(struct acpi_processor *pr, -			       struct acpi_processor_cx *cx, bool timer_bc) +static int acpi_idle_enter_bm(struct cpuidle_driver *drv, +			       struct acpi_processor *pr, +			       struct acpi_processor_cx *cx, +			       int index)  { -	acpi_unlazy_tlb(smp_processor_id()); - -	/* -	 * Must be done before busmaster disable as we might need to -	 * access HPET ! -	 */ -	if (timer_bc) -		lapic_timer_state_broadcast(pr, cx, 1); +	static struct acpi_processor_cx safe_cx = { +		.entry_method = ACPI_CSTATE_HALT, +	};  	/*  	 * disable bus master  	 * bm_check implies we need ARB_DIS  	 * bm_control implies whether we can do ARB_DIS  	 * -	 * That leaves a case where bm_check is set and bm_control is -	 * not set. In that case we cannot do much, we enter C3 -	 * without doing anything. +	 * That leaves a case where bm_check is set and bm_control is not set. +	 * In that case we cannot do much, we enter C3 without doing anything.  	 */ -	if (pr->flags.bm_control) { +	bool dis_bm = pr->flags.bm_control; + +	/* If we can skip BM, demote to a safe state. */ +	if (!cx->bm_sts_skip && acpi_idle_bm_check()) { +		dis_bm = false; +		index = drv->safe_state_index; +		if (index >= 0) { +			cx = this_cpu_read(acpi_cstate[index]); +		} else { +			cx = &safe_cx; +			index = -EBUSY; +		} +	} + +	if (dis_bm) {  		raw_spin_lock(&c3_lock);  		c3_cpu_count++;  		/* Disable bus master arbitration when all CPUs are in C3 */ @@ -600,18 +604,21 @@ static void acpi_idle_enter_bm(struct acpi_processor *pr,  		raw_spin_unlock(&c3_lock);  	} +	rcu_idle_enter(); +  	acpi_idle_do_entry(cx); +	rcu_idle_exit(); +  	/* Re-enable bus master arbitration */ -	if (pr->flags.bm_control) { +	if (dis_bm) {  		raw_spin_lock(&c3_lock);  		acpi_write_bit_register(ACPI_BITREG_ARB_DISABLE, 0);  		c3_cpu_count--;  		raw_spin_unlock(&c3_lock);  	} -	if (timer_bc) -		lapic_timer_state_broadcast(pr, cx, 0); +	return index;  }  static int acpi_idle_enter(struct cpuidle_device *dev, @@ -625,32 +632,21 @@ static int acpi_idle_enter(struct cpuidle_device *dev,  		return -EINVAL;  	if (cx->type != ACPI_STATE_C1) { +		if (cx->type == ACPI_STATE_C3 && pr->flags.bm_check) +			return acpi_idle_enter_bm(drv, pr, cx, index); + +		/* C2 to C1 demotion. */  		if (acpi_idle_fallback_to_c1(pr) && num_online_cpus() > 1) {  			index = ACPI_IDLE_STATE_START;  			cx = per_cpu(acpi_cstate[index], dev->cpu); -		} else if (cx->type == ACPI_STATE_C3 && pr->flags.bm_check) { -			if (cx->bm_sts_skip || !acpi_idle_bm_check()) { -				acpi_idle_enter_bm(pr, cx, true); -				return index; -			} else if (drv->safe_state_index >= 0) { -				index = drv->safe_state_index; -				cx = per_cpu(acpi_cstate[index], dev->cpu); -			} else { -				acpi_safe_halt(); -				return -EBUSY; -			}  		}  	} -	lapic_timer_state_broadcast(pr, cx, 1); -  	if (cx->type == ACPI_STATE_C3)  		ACPI_FLUSH_CPU_CACHE();  	acpi_idle_do_entry(cx); -	lapic_timer_state_broadcast(pr, cx, 0); -  	return index;  } @@ -666,7 +662,13 @@ static int acpi_idle_enter_s2idle(struct cpuidle_device *dev,  			return 0;  		if (pr->flags.bm_check) { -			acpi_idle_enter_bm(pr, cx, false); +			u8 bm_sts_skip = cx->bm_sts_skip; + +			/* Don't check BM_STS, do an unconditional ARB_DIS for S2IDLE */ +			cx->bm_sts_skip = 1; +			acpi_idle_enter_bm(drv, pr, cx, index); +			cx->bm_sts_skip = bm_sts_skip; +  			return 0;  		} else {  			ACPI_FLUSH_CPU_CACHE(); @@ -682,11 +684,13 @@ static int acpi_processor_setup_cpuidle_cx(struct acpi_processor *pr,  {  	int i, count = ACPI_IDLE_STATE_START;  	struct acpi_processor_cx *cx; +	struct cpuidle_state *state;  	if (max_cstate == 0)  		max_cstate = 1;  	for (i = 1; i < ACPI_PROCESSOR_MAX_POWER && i <= max_cstate; i++) { +		state = &acpi_idle_driver.states[count];  		cx = &pr->power.states[i];  		if (!cx->valid) @@ -694,6 +698,15 @@ static int acpi_processor_setup_cpuidle_cx(struct acpi_processor *pr,  		per_cpu(acpi_cstate[count], dev->cpu) = cx; +		if (lapic_timer_needs_broadcast(pr, cx)) +			state->flags |= CPUIDLE_FLAG_TIMER_STOP; + +		if (cx->type == ACPI_STATE_C3) { +			state->flags |= CPUIDLE_FLAG_TLB_FLUSHED; +			if (pr->flags.bm_check) +				state->flags |= CPUIDLE_FLAG_RCU_IDLE; +		} +  		count++;  		if (count == CPUIDLE_STATE_MAX)  			break; |