Merge branch 'release' of git://git.kernel.org/pub/scm/linux/kernel/git/lenb/linux
[firefly-linux-kernel-4.4.55.git] / drivers / acpi / processor_idle.c
index 9b88f9828d8ca2ce98dc69be2606ffcc069d323e..73b2909dddfe8ebbd2e41ac2db2daeceb48b29b7 100644 (file)
@@ -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;
 }