|
@@ -741,22 +741,25 @@ static inline void acpi_idle_do_entry(struct acpi_processor_cx *cx)
|
|
|
/**
|
|
|
* acpi_idle_enter_c1 - enters an ACPI C1 state-type
|
|
|
* @dev: the target CPU
|
|
|
- * @state: the state data
|
|
|
+ * @drv: cpuidle driver containing cpuidle state info
|
|
|
+ * @index: index of target state
|
|
|
*
|
|
|
* This is equivalent to the HALT instruction.
|
|
|
*/
|
|
|
static int acpi_idle_enter_c1(struct cpuidle_device *dev,
|
|
|
- struct cpuidle_state *state)
|
|
|
+ struct cpuidle_driver *drv, int index)
|
|
|
{
|
|
|
ktime_t kt1, kt2;
|
|
|
s64 idle_time;
|
|
|
struct acpi_processor *pr;
|
|
|
- struct acpi_processor_cx *cx = cpuidle_get_statedata(state);
|
|
|
+ struct cpuidle_state_usage *state_usage = &dev->states_usage[index];
|
|
|
+ struct acpi_processor_cx *cx = cpuidle_get_statedata(state_usage);
|
|
|
|
|
|
pr = __this_cpu_read(processors);
|
|
|
+ dev->last_residency = 0;
|
|
|
|
|
|
if (unlikely(!pr))
|
|
|
- return 0;
|
|
|
+ return -EINVAL;
|
|
|
|
|
|
local_irq_disable();
|
|
|
|
|
@@ -764,7 +767,7 @@ static int acpi_idle_enter_c1(struct cpuidle_device *dev,
|
|
|
if (acpi_idle_suspend) {
|
|
|
local_irq_enable();
|
|
|
cpu_relax();
|
|
|
- return 0;
|
|
|
+ return -EINVAL;
|
|
|
}
|
|
|
|
|
|
lapic_timer_state_broadcast(pr, cx, 1);
|
|
@@ -773,37 +776,47 @@ static int acpi_idle_enter_c1(struct cpuidle_device *dev,
|
|
|
kt2 = ktime_get_real();
|
|
|
idle_time = ktime_to_us(ktime_sub(kt2, kt1));
|
|
|
|
|
|
+ /* Update device last_residency*/
|
|
|
+ dev->last_residency = (int)idle_time;
|
|
|
+
|
|
|
local_irq_enable();
|
|
|
cx->usage++;
|
|
|
lapic_timer_state_broadcast(pr, cx, 0);
|
|
|
|
|
|
- return idle_time;
|
|
|
+ return index;
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* acpi_idle_enter_simple - enters an ACPI state without BM handling
|
|
|
* @dev: the target CPU
|
|
|
- * @state: the state data
|
|
|
+ * @drv: cpuidle driver with cpuidle state information
|
|
|
+ * @index: the index of suggested state
|
|
|
*/
|
|
|
static int acpi_idle_enter_simple(struct cpuidle_device *dev,
|
|
|
- struct cpuidle_state *state)
|
|
|
+ struct cpuidle_driver *drv, int index)
|
|
|
{
|
|
|
struct acpi_processor *pr;
|
|
|
- struct acpi_processor_cx *cx = cpuidle_get_statedata(state);
|
|
|
+ struct cpuidle_state_usage *state_usage = &dev->states_usage[index];
|
|
|
+ struct acpi_processor_cx *cx = cpuidle_get_statedata(state_usage);
|
|
|
ktime_t kt1, kt2;
|
|
|
s64 idle_time_ns;
|
|
|
s64 idle_time;
|
|
|
|
|
|
pr = __this_cpu_read(processors);
|
|
|
+ dev->last_residency = 0;
|
|
|
|
|
|
if (unlikely(!pr))
|
|
|
- return 0;
|
|
|
-
|
|
|
- if (acpi_idle_suspend)
|
|
|
- return(acpi_idle_enter_c1(dev, state));
|
|
|
+ return -EINVAL;
|
|
|
|
|
|
local_irq_disable();
|
|
|
|
|
|
+ if (acpi_idle_suspend) {
|
|
|
+ local_irq_enable();
|
|
|
+ cpu_relax();
|
|
|
+ return -EINVAL;
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
if (cx->entry_method != ACPI_CSTATE_FFH) {
|
|
|
current_thread_info()->status &= ~TS_POLLING;
|
|
|
/*
|
|
@@ -815,7 +828,7 @@ static int acpi_idle_enter_simple(struct cpuidle_device *dev,
|
|
|
if (unlikely(need_resched())) {
|
|
|
current_thread_info()->status |= TS_POLLING;
|
|
|
local_irq_enable();
|
|
|
- return 0;
|
|
|
+ return -EINVAL;
|
|
|
}
|
|
|
}
|
|
|
|
|
@@ -837,6 +850,9 @@ static int acpi_idle_enter_simple(struct cpuidle_device *dev,
|
|
|
idle_time = idle_time_ns;
|
|
|
do_div(idle_time, NSEC_PER_USEC);
|
|
|
|
|
|
+ /* Update device last_residency*/
|
|
|
+ dev->last_residency = (int)idle_time;
|
|
|
+
|
|
|
/* Tell the scheduler how much we idled: */
|
|
|
sched_clock_idle_wakeup_event(idle_time_ns);
|
|
|
|
|
@@ -848,7 +864,7 @@ static int acpi_idle_enter_simple(struct cpuidle_device *dev,
|
|
|
|
|
|
lapic_timer_state_broadcast(pr, cx, 0);
|
|
|
cx->time += idle_time;
|
|
|
- return idle_time;
|
|
|
+ return index;
|
|
|
}
|
|
|
|
|
|
static int c3_cpu_count;
|
|
@@ -857,37 +873,43 @@ static DEFINE_RAW_SPINLOCK(c3_lock);
|
|
|
/**
|
|
|
* acpi_idle_enter_bm - enters C3 with proper BM handling
|
|
|
* @dev: the target CPU
|
|
|
- * @state: the state data
|
|
|
+ * @drv: cpuidle driver containing state data
|
|
|
+ * @index: the index of suggested state
|
|
|
*
|
|
|
* If BM is detected, the deepest non-C3 idle state is entered instead.
|
|
|
*/
|
|
|
static int acpi_idle_enter_bm(struct cpuidle_device *dev,
|
|
|
- struct cpuidle_state *state)
|
|
|
+ struct cpuidle_driver *drv, int index)
|
|
|
{
|
|
|
struct acpi_processor *pr;
|
|
|
- struct acpi_processor_cx *cx = cpuidle_get_statedata(state);
|
|
|
+ struct cpuidle_state_usage *state_usage = &dev->states_usage[index];
|
|
|
+ struct acpi_processor_cx *cx = cpuidle_get_statedata(state_usage);
|
|
|
ktime_t kt1, kt2;
|
|
|
s64 idle_time_ns;
|
|
|
s64 idle_time;
|
|
|
|
|
|
|
|
|
pr = __this_cpu_read(processors);
|
|
|
+ dev->last_residency = 0;
|
|
|
|
|
|
if (unlikely(!pr))
|
|
|
- return 0;
|
|
|
+ return -EINVAL;
|
|
|
+
|
|
|
|
|
|
- if (acpi_idle_suspend)
|
|
|
- return(acpi_idle_enter_c1(dev, state));
|
|
|
+ if (acpi_idle_suspend) {
|
|
|
+ cpu_relax();
|
|
|
+ return -EINVAL;
|
|
|
+ }
|
|
|
|
|
|
if (!cx->bm_sts_skip && acpi_idle_bm_check()) {
|
|
|
- if (dev->safe_state) {
|
|
|
- dev->last_state = dev->safe_state;
|
|
|
- return dev->safe_state->enter(dev, dev->safe_state);
|
|
|
+ if (drv->safe_state_index >= 0) {
|
|
|
+ return drv->states[drv->safe_state_index].enter(dev,
|
|
|
+ drv, drv->safe_state_index);
|
|
|
} else {
|
|
|
local_irq_disable();
|
|
|
acpi_safe_halt();
|
|
|
local_irq_enable();
|
|
|
- return 0;
|
|
|
+ return -EINVAL;
|
|
|
}
|
|
|
}
|
|
|
|
|
@@ -904,7 +926,7 @@ static int acpi_idle_enter_bm(struct cpuidle_device *dev,
|
|
|
if (unlikely(need_resched())) {
|
|
|
current_thread_info()->status |= TS_POLLING;
|
|
|
local_irq_enable();
|
|
|
- return 0;
|
|
|
+ return -EINVAL;
|
|
|
}
|
|
|
}
|
|
|
|
|
@@ -954,6 +976,9 @@ static int acpi_idle_enter_bm(struct cpuidle_device *dev,
|
|
|
idle_time = idle_time_ns;
|
|
|
do_div(idle_time, NSEC_PER_USEC);
|
|
|
|
|
|
+ /* Update device last_residency*/
|
|
|
+ dev->last_residency = (int)idle_time;
|
|
|
+
|
|
|
/* Tell the scheduler how much we idled: */
|
|
|
sched_clock_idle_wakeup_event(idle_time_ns);
|
|
|
|
|
@@ -965,7 +990,7 @@ static int acpi_idle_enter_bm(struct cpuidle_device *dev,
|
|
|
|
|
|
lapic_timer_state_broadcast(pr, cx, 0);
|
|
|
cx->time += idle_time;
|
|
|
- return idle_time;
|
|
|
+ return index;
|
|
|
}
|
|
|
|
|
|
struct cpuidle_driver acpi_idle_driver = {
|
|
@@ -974,14 +999,16 @@ struct cpuidle_driver acpi_idle_driver = {
|
|
|
};
|
|
|
|
|
|
/**
|
|
|
- * acpi_processor_setup_cpuidle - prepares and configures CPUIDLE
|
|
|
+ * acpi_processor_setup_cpuidle_cx - prepares and configures CPUIDLE
|
|
|
+ * device i.e. per-cpu data
|
|
|
+ *
|
|
|
* @pr: the ACPI processor
|
|
|
*/
|
|
|
-static int acpi_processor_setup_cpuidle(struct acpi_processor *pr)
|
|
|
+static int acpi_processor_setup_cpuidle_cx(struct acpi_processor *pr)
|
|
|
{
|
|
|
int i, count = CPUIDLE_DRIVER_STATE_START;
|
|
|
struct acpi_processor_cx *cx;
|
|
|
- struct cpuidle_state *state;
|
|
|
+ struct cpuidle_state_usage *state_usage;
|
|
|
struct cpuidle_device *dev = &pr->power.dev;
|
|
|
|
|
|
if (!pr->flags.power_setup_done)
|
|
@@ -992,9 +1019,62 @@ static int acpi_processor_setup_cpuidle(struct acpi_processor *pr)
|
|
|
}
|
|
|
|
|
|
dev->cpu = pr->id;
|
|
|
+
|
|
|
+ if (max_cstate == 0)
|
|
|
+ max_cstate = 1;
|
|
|
+
|
|
|
+ for (i = 1; i < ACPI_PROCESSOR_MAX_POWER && i <= max_cstate; i++) {
|
|
|
+ cx = &pr->power.states[i];
|
|
|
+ state_usage = &dev->states_usage[count];
|
|
|
+
|
|
|
+ if (!cx->valid)
|
|
|
+ continue;
|
|
|
+
|
|
|
+#ifdef CONFIG_HOTPLUG_CPU
|
|
|
+ if ((cx->type != ACPI_STATE_C1) && (num_online_cpus() > 1) &&
|
|
|
+ !pr->flags.has_cst &&
|
|
|
+ !(acpi_gbl_FADT.flags & ACPI_FADT_C2_MP_SUPPORTED))
|
|
|
+ continue;
|
|
|
+#endif
|
|
|
+
|
|
|
+ cpuidle_set_statedata(state_usage, cx);
|
|
|
+
|
|
|
+ count++;
|
|
|
+ if (count == CPUIDLE_STATE_MAX)
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ dev->state_count = count;
|
|
|
+
|
|
|
+ if (!count)
|
|
|
+ return -EINVAL;
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * acpi_processor_setup_cpuidle states- prepares and configures cpuidle
|
|
|
+ * global state data i.e. idle routines
|
|
|
+ *
|
|
|
+ * @pr: the ACPI processor
|
|
|
+ */
|
|
|
+static int acpi_processor_setup_cpuidle_states(struct acpi_processor *pr)
|
|
|
+{
|
|
|
+ int i, count = CPUIDLE_DRIVER_STATE_START;
|
|
|
+ struct acpi_processor_cx *cx;
|
|
|
+ struct cpuidle_state *state;
|
|
|
+ struct cpuidle_driver *drv = &acpi_idle_driver;
|
|
|
+
|
|
|
+ if (!pr->flags.power_setup_done)
|
|
|
+ return -EINVAL;
|
|
|
+
|
|
|
+ if (pr->flags.power == 0)
|
|
|
+ return -EINVAL;
|
|
|
+
|
|
|
+ drv->safe_state_index = -1;
|
|
|
for (i = 0; i < CPUIDLE_STATE_MAX; i++) {
|
|
|
- dev->states[i].name[0] = '\0';
|
|
|
- dev->states[i].desc[0] = '\0';
|
|
|
+ drv->states[i].name[0] = '\0';
|
|
|
+ drv->states[i].desc[0] = '\0';
|
|
|
}
|
|
|
|
|
|
if (max_cstate == 0)
|
|
@@ -1002,7 +1082,6 @@ static int acpi_processor_setup_cpuidle(struct acpi_processor *pr)
|
|
|
|
|
|
for (i = 1; i < ACPI_PROCESSOR_MAX_POWER && i <= max_cstate; i++) {
|
|
|
cx = &pr->power.states[i];
|
|
|
- state = &dev->states[count];
|
|
|
|
|
|
if (!cx->valid)
|
|
|
continue;
|
|
@@ -1013,8 +1092,8 @@ static int acpi_processor_setup_cpuidle(struct acpi_processor *pr)
|
|
|
!(acpi_gbl_FADT.flags & ACPI_FADT_C2_MP_SUPPORTED))
|
|
|
continue;
|
|
|
#endif
|
|
|
- cpuidle_set_statedata(state, cx);
|
|
|
|
|
|
+ state = &drv->states[count];
|
|
|
snprintf(state->name, CPUIDLE_NAME_LEN, "C%d", i);
|
|
|
strncpy(state->desc, cx->desc, CPUIDLE_DESC_LEN);
|
|
|
state->exit_latency = cx->latency;
|
|
@@ -1027,13 +1106,13 @@ static int acpi_processor_setup_cpuidle(struct acpi_processor *pr)
|
|
|
state->flags |= CPUIDLE_FLAG_TIME_VALID;
|
|
|
|
|
|
state->enter = acpi_idle_enter_c1;
|
|
|
- dev->safe_state = state;
|
|
|
+ drv->safe_state_index = count;
|
|
|
break;
|
|
|
|
|
|
case ACPI_STATE_C2:
|
|
|
state->flags |= CPUIDLE_FLAG_TIME_VALID;
|
|
|
state->enter = acpi_idle_enter_simple;
|
|
|
- dev->safe_state = state;
|
|
|
+ drv->safe_state_index = count;
|
|
|
break;
|
|
|
|
|
|
case ACPI_STATE_C3:
|
|
@@ -1049,7 +1128,7 @@ static int acpi_processor_setup_cpuidle(struct acpi_processor *pr)
|
|
|
break;
|
|
|
}
|
|
|
|
|
|
- dev->state_count = count;
|
|
|
+ drv->state_count = count;
|
|
|
|
|
|
if (!count)
|
|
|
return -EINVAL;
|
|
@@ -1057,7 +1136,7 @@ static int acpi_processor_setup_cpuidle(struct acpi_processor *pr)
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
-int acpi_processor_cst_has_changed(struct acpi_processor *pr)
|
|
|
+int acpi_processor_hotplug(struct acpi_processor *pr)
|
|
|
{
|
|
|
int ret = 0;
|
|
|
|
|
@@ -1078,7 +1157,7 @@ int acpi_processor_cst_has_changed(struct acpi_processor *pr)
|
|
|
cpuidle_disable_device(&pr->power.dev);
|
|
|
acpi_processor_get_power_info(pr);
|
|
|
if (pr->flags.power) {
|
|
|
- acpi_processor_setup_cpuidle(pr);
|
|
|
+ acpi_processor_setup_cpuidle_cx(pr);
|
|
|
ret = cpuidle_enable_device(&pr->power.dev);
|
|
|
}
|
|
|
cpuidle_resume_and_unlock();
|
|
@@ -1086,10 +1165,72 @@ int acpi_processor_cst_has_changed(struct acpi_processor *pr)
|
|
|
return ret;
|
|
|
}
|
|
|
|
|
|
+int acpi_processor_cst_has_changed(struct acpi_processor *pr)
|
|
|
+{
|
|
|
+ int cpu;
|
|
|
+ struct acpi_processor *_pr;
|
|
|
+
|
|
|
+ if (disabled_by_idle_boot_param())
|
|
|
+ return 0;
|
|
|
+
|
|
|
+ if (!pr)
|
|
|
+ return -EINVAL;
|
|
|
+
|
|
|
+ if (nocst)
|
|
|
+ return -ENODEV;
|
|
|
+
|
|
|
+ if (!pr->flags.power_setup_done)
|
|
|
+ return -ENODEV;
|
|
|
+
|
|
|
+ /*
|
|
|
+ * FIXME: Design the ACPI notification to make it once per
|
|
|
+ * system instead of once per-cpu. This condition is a hack
|
|
|
+ * to make the code that updates C-States be called once.
|
|
|
+ */
|
|
|
+
|
|
|
+ if (smp_processor_id() == 0 &&
|
|
|
+ cpuidle_get_driver() == &acpi_idle_driver) {
|
|
|
+
|
|
|
+ cpuidle_pause_and_lock();
|
|
|
+ /* Protect against cpu-hotplug */
|
|
|
+ get_online_cpus();
|
|
|
+
|
|
|
+ /* Disable all cpuidle devices */
|
|
|
+ for_each_online_cpu(cpu) {
|
|
|
+ _pr = per_cpu(processors, cpu);
|
|
|
+ if (!_pr || !_pr->flags.power_setup_done)
|
|
|
+ continue;
|
|
|
+ cpuidle_disable_device(&_pr->power.dev);
|
|
|
+ }
|
|
|
+
|
|
|
+ /* Populate Updated C-state information */
|
|
|
+ acpi_processor_setup_cpuidle_states(pr);
|
|
|
+
|
|
|
+ /* Enable all cpuidle devices */
|
|
|
+ for_each_online_cpu(cpu) {
|
|
|
+ _pr = per_cpu(processors, cpu);
|
|
|
+ if (!_pr || !_pr->flags.power_setup_done)
|
|
|
+ continue;
|
|
|
+ acpi_processor_get_power_info(_pr);
|
|
|
+ if (_pr->flags.power) {
|
|
|
+ acpi_processor_setup_cpuidle_cx(_pr);
|
|
|
+ cpuidle_enable_device(&_pr->power.dev);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ put_online_cpus();
|
|
|
+ cpuidle_resume_and_unlock();
|
|
|
+ }
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static int acpi_processor_registered;
|
|
|
+
|
|
|
int __cpuinit acpi_processor_power_init(struct acpi_processor *pr,
|
|
|
struct acpi_device *device)
|
|
|
{
|
|
|
acpi_status status = 0;
|
|
|
+ int retval;
|
|
|
static int first_run;
|
|
|
|
|
|
if (disabled_by_idle_boot_param())
|
|
@@ -1126,9 +1267,26 @@ int __cpuinit acpi_processor_power_init(struct acpi_processor *pr,
|
|
|
* platforms that only support C1.
|
|
|
*/
|
|
|
if (pr->flags.power) {
|
|
|
- acpi_processor_setup_cpuidle(pr);
|
|
|
- if (cpuidle_register_device(&pr->power.dev))
|
|
|
- return -EIO;
|
|
|
+ /* Register acpi_idle_driver if not already registered */
|
|
|
+ if (!acpi_processor_registered) {
|
|
|
+ acpi_processor_setup_cpuidle_states(pr);
|
|
|
+ retval = cpuidle_register_driver(&acpi_idle_driver);
|
|
|
+ if (retval)
|
|
|
+ return retval;
|
|
|
+ printk(KERN_DEBUG "ACPI: %s registered with cpuidle\n",
|
|
|
+ acpi_idle_driver.name);
|
|
|
+ }
|
|
|
+ /* Register per-cpu cpuidle_device. Cpuidle driver
|
|
|
+ * must already be registered before registering device
|
|
|
+ */
|
|
|
+ acpi_processor_setup_cpuidle_cx(pr);
|
|
|
+ retval = cpuidle_register_device(&pr->power.dev);
|
|
|
+ if (retval) {
|
|
|
+ if (acpi_processor_registered == 0)
|
|
|
+ cpuidle_unregister_driver(&acpi_idle_driver);
|
|
|
+ return retval;
|
|
|
+ }
|
|
|
+ acpi_processor_registered++;
|
|
|
}
|
|
|
return 0;
|
|
|
}
|
|
@@ -1139,8 +1297,13 @@ int acpi_processor_power_exit(struct acpi_processor *pr,
|
|
|
if (disabled_by_idle_boot_param())
|
|
|
return 0;
|
|
|
|
|
|
- cpuidle_unregister_device(&pr->power.dev);
|
|
|
- pr->flags.power_setup_done = 0;
|
|
|
+ if (pr->flags.power) {
|
|
|
+ cpuidle_unregister_device(&pr->power.dev);
|
|
|
+ acpi_processor_registered--;
|
|
|
+ if (acpi_processor_registered == 0)
|
|
|
+ cpuidle_unregister_driver(&acpi_idle_driver);
|
|
|
+ }
|
|
|
|
|
|
+ pr->flags.power_setup_done = 0;
|
|
|
return 0;
|
|
|
}
|