More ACPI and power management updates for 3.16-rc1

- I didn't remember correctly that the Hans de Goede's ACPI video
    patches actually didn't flip the video.use_native_backlight
    default, although we had discussed that and decided to do that.
    Since I said we would do that in the previous PM+ACPI pull
    request, make that change for real now.
 
  - ACPI bus check notifications for PCI host bridges don't cause
    the bus below the host bridge to be checked for changes as they
    should because of a mistake in the ACPI-based PCI hotplug (ACPIPHP)
    subsystem that forgets to add hotplug contexts to PCI host bridge
    ACPI device objects.  Create hotplug contexts for PCI host bridges
    too as appropriate.
 
  - Revert recent cpufreq commit related to the big.LITTLE cpufreq
    driver that breaks arm64 builds.
 
  - Fix for a regression in the ppc-corenet cpufreq driver introduced
    during the 3.15 cycle and causing the driver to use the remainder
    from do_div instead of the quotient.  From Ed Swarthout.
 
  - Resets triggered by panic activate a BUG_ON() in vmalloc.c on
    systems where the ACPI reset register is located in memory address
    space.  Fix from Randy Wright.
 
  - Fix for a problem with cpufreq governors that decisions made by
    them may be suboptimal due to the fact that deferrable timers are
    used by them for CPU load sampling.  From Srivatsa S Bhat.
 
  - Fix for a problem with the Tegra cpufreq driver where the CPU
    frequency is temporarily switched to a "stable" level that
    is different from both the initial and target frequencies
    during transitions which causes udelay() to expire earlier than
    it should sometimes.  From Viresh Kumar.
 
  - New trace points and rework of some existing trace points for
    system suspend/resume profiling from Todd Brandt.
 
  - Assorted cpufreq fixes and cleanups from Stratos Karafotis and
    Viresh Kumar.
 
  - Copyright notice update for suspend-and-cpuhotplug.txt from
    Srivatsa S Bhat.
 
 /
 -----BEGIN PGP SIGNATURE-----
 Version: GnuPG v2.0.22 (GNU/Linux)
 
 iQIcBAABCAAGBQJTmeBNAAoJEILEb/54YlRxFo0QAIfp74wZO9ZPcrR+6IO1AEUb
 1qcVJYMFWvisG2JO9b7DUtxwgWHk8/NMgKv+bYxUAEni95mY7PqDTdJ+Qjk7DinJ
 jVo+mzooaQg+KYGQ503YOtqsGhNFM3lE6Jw01wbLytTCetkNCkTgr//7btBbyRKn
 13Ut3o2vH9n5EMoe1jql96onJH6AfBDEn7jc5Sk4rGL7MtKAMsWNTNSGVyLFA98l
 sghO8ZR0AqnBzoedr1eBxzo6ujUqjfYlIcxowZycpJJVX02eN+KGUbOJao2+6RB+
 J6wu/FoPv2VtJkNwSB8IMgZfqceecSIXeWBG5xC22cYbSQ/IDW2k72V+kLHUqd36
 LhlYLIsIxJQovqOgPdKeP5o6OVFd4EheWBiCfNBrmYU+x2av6I6ZjTscz3Robaxh
 AVG6yU8XR2GOpoVGW/+L7R2jZ1Qse1Io0r93hXvCsSXgMkq9HbueX3mZR605msfe
 liDk+fym357cKQUreSH1XF0Q79C1wpEJ6rTz0Qi6ZxkKB+dAYE3oPA+V0+cWSxbK
 WqaFjQwPtvrrduvLj5Z+qF/zRu4LXdTxiY59utBek/RoN6zUsMMpwsRCCdBfub2O
 alBOHUPRaiUywkQtqu7yP9j7iciNxEn1/tXo97b/1qC3RrOwLWOgd8dhpWe0i0Gp
 EmQkie8qCHXw5vCpaeUK
 =0lht
 -----END PGP SIGNATURE-----

Merge tag 'pm+acpi-3.16-rc1-2' of git://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm

Pull more ACPI and power management updates from Rafael Wysocki:
 "These are fixups on top of the previous PM+ACPI pull request,
  regression fixes (ACPI hotplug, cpufreq ppc-corenet), other bug fixes
  (ACPI reset, cpufreq), new PM trace points for system suspend
  profiling and a copyright notice update.

  Specifics:

   - I didn't remember correctly that the Hans de Goede's ACPI video
     patches actually didn't flip the video.use_native_backlight
     default, although we had discussed that and decided to do that.
     Since I said we would do that in the previous PM+ACPI pull request,
     make that change for real now.

   - ACPI bus check notifications for PCI host bridges don't cause the
     bus below the host bridge to be checked for changes as they should
     because of a mistake in the ACPI-based PCI hotplug (ACPIPHP)
     subsystem that forgets to add hotplug contexts to PCI host bridge
     ACPI device objects.  Create hotplug contexts for PCI host bridges
     too as appropriate.

   - Revert recent cpufreq commit related to the big.LITTLE cpufreq
     driver that breaks arm64 builds.

   - Fix for a regression in the ppc-corenet cpufreq driver introduced
     during the 3.15 cycle and causing the driver to use the remainder
     from do_div instead of the quotient.  From Ed Swarthout.

   - Resets triggered by panic activate a BUG_ON() in vmalloc.c on
     systems where the ACPI reset register is located in memory address
     space.  Fix from Randy Wright.

   - Fix for a problem with cpufreq governors that decisions made by
     them may be suboptimal due to the fact that deferrable timers are
     used by them for CPU load sampling.  From Srivatsa S Bhat.

   - Fix for a problem with the Tegra cpufreq driver where the CPU
     frequency is temporarily switched to a "stable" level that is
     different from both the initial and target frequencies during
     transitions which causes udelay() to expire earlier than it should
     sometimes.  From Viresh Kumar.

   - New trace points and rework of some existing trace points for
     system suspend/resume profiling from Todd Brandt.

   - Assorted cpufreq fixes and cleanups from Stratos Karafotis and
     Viresh Kumar.

   - Copyright notice update for suspend-and-cpuhotplug.txt from
     Srivatsa S Bhat"

* tag 'pm+acpi-3.16-rc1-2' of git://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm:
  ACPI / hotplug / PCI: Add hotplug contexts to PCI host bridges
  PM / sleep: trace events for device PM callbacks
  cpufreq: cpufreq-cpu0: remove dependency on THERMAL and REGULATOR
  cpufreq: tegra: update comment for clarity
  cpufreq: intel_pstate: Remove duplicate CPU ID check
  cpufreq: Mark CPU0 driver with CPUFREQ_NEED_INITIAL_FREQ_CHECK flag
  PM / Documentation: Update copyright in suspend-and-cpuhotplug.txt
  cpufreq: governor: remove copy_prev_load from 'struct cpu_dbs_common_info'
  cpufreq: governor: Be friendly towards latency-sensitive bursty workloads
  PM / sleep: trace events for suspend/resume
  cpufreq: ppc-corenet-cpu-freq: do_div use quotient
  Revert "cpufreq: Enable big.LITTLE cpufreq driver on arm64"
  cpufreq: Tegra: implement intermediate frequency callbacks
  cpufreq: add support for intermediate (stable) frequencies
  ACPI / video: Change the default for video.use_native_backlight to 1
  ACPI: Fix bug when ACPI reset register is implemented in system memory
This commit is contained in:
Linus Torvalds 2014-06-12 13:14:19 -07:00
commit 19c1940fea
24 changed files with 446 additions and 122 deletions

View File

@ -26,6 +26,7 @@ Contents:
1.4 target/target_index or setpolicy? 1.4 target/target_index or setpolicy?
1.5 target/target_index 1.5 target/target_index
1.6 setpolicy 1.6 setpolicy
1.7 get_intermediate and target_intermediate
2. Frequency Table Helpers 2. Frequency Table Helpers
@ -79,6 +80,10 @@ cpufreq_driver.attr - A pointer to a NULL-terminated list of
"struct freq_attr" which allow to "struct freq_attr" which allow to
export values to sysfs. export values to sysfs.
cpufreq_driver.get_intermediate
and target_intermediate Used to switch to stable frequency while
changing CPU frequency.
1.2 Per-CPU Initialization 1.2 Per-CPU Initialization
-------------------------- --------------------------
@ -151,7 +156,7 @@ Some cpufreq-capable processors switch the frequency between certain
limits on their own. These shall use the ->setpolicy call limits on their own. These shall use the ->setpolicy call
1.4. target/target_index 1.5. target/target_index
------------- -------------
The target_index call has two arguments: struct cpufreq_policy *policy, The target_index call has two arguments: struct cpufreq_policy *policy,
@ -160,6 +165,9 @@ and unsigned int index (into the exposed frequency table).
The CPUfreq driver must set the new frequency when called here. The The CPUfreq driver must set the new frequency when called here. The
actual frequency must be determined by freq_table[index].frequency. actual frequency must be determined by freq_table[index].frequency.
It should always restore to earlier frequency (i.e. policy->restore_freq) in
case of errors, even if we switched to intermediate frequency earlier.
Deprecated: Deprecated:
---------- ----------
The target call has three arguments: struct cpufreq_policy *policy, The target call has three arguments: struct cpufreq_policy *policy,
@ -179,7 +187,7 @@ Here again the frequency table helper might assist you - see section 2
for details. for details.
1.5 setpolicy 1.6 setpolicy
--------------- ---------------
The setpolicy call only takes a struct cpufreq_policy *policy as The setpolicy call only takes a struct cpufreq_policy *policy as
@ -190,6 +198,23 @@ setting when policy->policy is CPUFREQ_POLICY_PERFORMANCE, and a
powersaving-oriented setting when CPUFREQ_POLICY_POWERSAVE. Also check powersaving-oriented setting when CPUFREQ_POLICY_POWERSAVE. Also check
the reference implementation in drivers/cpufreq/longrun.c the reference implementation in drivers/cpufreq/longrun.c
1.7 get_intermediate and target_intermediate
--------------------------------------------
Only for drivers with target_index() and CPUFREQ_ASYNC_NOTIFICATION unset.
get_intermediate should return a stable intermediate frequency platform wants to
switch to, and target_intermediate() should set CPU to to that frequency, before
jumping to the frequency corresponding to 'index'. Core will take care of
sending notifications and driver doesn't have to handle them in
target_intermediate() or target_index().
Drivers can return '0' from get_intermediate() in case they don't wish to switch
to intermediate frequency for some target frequency. In that case core will
directly call ->target_index().
NOTE: ->target_index() should restore to policy->restore_freq in case of
failures as core would send notifications for that.
2. Frequency Table Helpers 2. Frequency Table Helpers

View File

@ -1,6 +1,6 @@
Interaction of Suspend code (S3) with the CPU hotplug infrastructure Interaction of Suspend code (S3) with the CPU hotplug infrastructure
(C) 2011 Srivatsa S. Bhat <srivatsa.bhat@linux.vnet.ibm.com> (C) 2011 - 2014 Srivatsa S. Bhat <srivatsa.bhat@linux.vnet.ibm.com>
I. How does the regular CPU hotplug code differ from how the Suspend-to-RAM I. How does the regular CPU hotplug code differ from how the Suspend-to-RAM

View File

@ -1810,6 +1810,16 @@ acpi_status __init acpi_os_initialize(void)
acpi_os_map_generic_address(&acpi_gbl_FADT.xpm1b_event_block); acpi_os_map_generic_address(&acpi_gbl_FADT.xpm1b_event_block);
acpi_os_map_generic_address(&acpi_gbl_FADT.xgpe0_block); acpi_os_map_generic_address(&acpi_gbl_FADT.xgpe0_block);
acpi_os_map_generic_address(&acpi_gbl_FADT.xgpe1_block); acpi_os_map_generic_address(&acpi_gbl_FADT.xgpe1_block);
if (acpi_gbl_FADT.flags & ACPI_FADT_RESET_REGISTER) {
/*
* Use acpi_os_map_generic_address to pre-map the reset
* register if it's in system memory.
*/
int rv;
rv = acpi_os_map_generic_address(&acpi_gbl_FADT.reset_register);
pr_debug(PREFIX "%s: map reset_reg status %d\n", __func__, rv);
}
return AE_OK; return AE_OK;
} }
@ -1838,6 +1848,8 @@ acpi_status acpi_os_terminate(void)
acpi_os_unmap_generic_address(&acpi_gbl_FADT.xgpe0_block); acpi_os_unmap_generic_address(&acpi_gbl_FADT.xgpe0_block);
acpi_os_unmap_generic_address(&acpi_gbl_FADT.xpm1b_event_block); acpi_os_unmap_generic_address(&acpi_gbl_FADT.xpm1b_event_block);
acpi_os_unmap_generic_address(&acpi_gbl_FADT.xpm1a_event_block); acpi_os_unmap_generic_address(&acpi_gbl_FADT.xpm1a_event_block);
if (acpi_gbl_FADT.flags & ACPI_FADT_RESET_REGISTER)
acpi_os_unmap_generic_address(&acpi_gbl_FADT.reset_register);
destroy_workqueue(kacpid_wq); destroy_workqueue(kacpid_wq);
destroy_workqueue(kacpi_notify_wq); destroy_workqueue(kacpi_notify_wq);

View File

@ -19,6 +19,7 @@
#include <linux/acpi.h> #include <linux/acpi.h>
#include <linux/module.h> #include <linux/module.h>
#include <asm/io.h> #include <asm/io.h>
#include <trace/events/power.h>
#include "internal.h" #include "internal.h"
#include "sleep.h" #include "sleep.h"
@ -501,6 +502,7 @@ static int acpi_suspend_enter(suspend_state_t pm_state)
ACPI_FLUSH_CPU_CACHE(); ACPI_FLUSH_CPU_CACHE();
trace_suspend_resume(TPS("acpi_suspend"), acpi_state, true);
switch (acpi_state) { switch (acpi_state) {
case ACPI_STATE_S1: case ACPI_STATE_S1:
barrier(); barrier();
@ -516,6 +518,7 @@ static int acpi_suspend_enter(suspend_state_t pm_state)
pr_info(PREFIX "Low-level resume complete\n"); pr_info(PREFIX "Low-level resume complete\n");
break; break;
} }
trace_suspend_resume(TPS("acpi_suspend"), acpi_state, false);
/* This violates the spec but is required for bug compatibility. */ /* This violates the spec but is required for bug compatibility. */
acpi_write_bit_register(ACPI_BITREG_SCI_ENABLE, 1); acpi_write_bit_register(ACPI_BITREG_SCI_ENABLE, 1);

View File

@ -82,7 +82,7 @@ module_param(allow_duplicates, bool, 0644);
* For Windows 8 systems: used to decide if video module * For Windows 8 systems: used to decide if video module
* should skip registering backlight interface of its own. * should skip registering backlight interface of its own.
*/ */
static int use_native_backlight_param = -1; static int use_native_backlight_param = 1;
module_param_named(use_native_backlight, use_native_backlight_param, int, 0444); module_param_named(use_native_backlight, use_native_backlight_param, int, 0444);
static bool use_native_backlight_dmi = false; static bool use_native_backlight_dmi = false;

View File

@ -214,9 +214,6 @@ static void initcall_debug_report(struct device *dev, ktime_t calltime,
pr_info("call %s+ returned %d after %Ld usecs\n", dev_name(dev), pr_info("call %s+ returned %d after %Ld usecs\n", dev_name(dev),
error, (unsigned long long)nsecs >> 10); error, (unsigned long long)nsecs >> 10);
} }
trace_device_pm_report_time(dev, info, nsecs, pm_verb(state.event),
error);
} }
/** /**
@ -387,7 +384,9 @@ static int dpm_run_callback(pm_callback_t cb, struct device *dev,
calltime = initcall_debug_start(dev); calltime = initcall_debug_start(dev);
pm_dev_dbg(dev, state, info); pm_dev_dbg(dev, state, info);
trace_device_pm_callback_start(dev, info, state.event);
error = cb(dev); error = cb(dev);
trace_device_pm_callback_end(dev, error);
suspend_report_result(cb, error); suspend_report_result(cb, error);
initcall_debug_report(dev, calltime, error, state, info); initcall_debug_report(dev, calltime, error, state, info);
@ -545,6 +544,7 @@ static void dpm_resume_noirq(pm_message_t state)
struct device *dev; struct device *dev;
ktime_t starttime = ktime_get(); ktime_t starttime = ktime_get();
trace_suspend_resume(TPS("dpm_resume_noirq"), state.event, true);
mutex_lock(&dpm_list_mtx); mutex_lock(&dpm_list_mtx);
pm_transition = state; pm_transition = state;
@ -587,6 +587,7 @@ static void dpm_resume_noirq(pm_message_t state)
dpm_show_time(starttime, state, "noirq"); dpm_show_time(starttime, state, "noirq");
resume_device_irqs(); resume_device_irqs();
cpuidle_resume(); cpuidle_resume();
trace_suspend_resume(TPS("dpm_resume_noirq"), state.event, false);
} }
/** /**
@ -664,6 +665,7 @@ static void dpm_resume_early(pm_message_t state)
struct device *dev; struct device *dev;
ktime_t starttime = ktime_get(); ktime_t starttime = ktime_get();
trace_suspend_resume(TPS("dpm_resume_early"), state.event, true);
mutex_lock(&dpm_list_mtx); mutex_lock(&dpm_list_mtx);
pm_transition = state; pm_transition = state;
@ -703,6 +705,7 @@ static void dpm_resume_early(pm_message_t state)
mutex_unlock(&dpm_list_mtx); mutex_unlock(&dpm_list_mtx);
async_synchronize_full(); async_synchronize_full();
dpm_show_time(starttime, state, "early"); dpm_show_time(starttime, state, "early");
trace_suspend_resume(TPS("dpm_resume_early"), state.event, false);
} }
/** /**
@ -834,6 +837,7 @@ void dpm_resume(pm_message_t state)
struct device *dev; struct device *dev;
ktime_t starttime = ktime_get(); ktime_t starttime = ktime_get();
trace_suspend_resume(TPS("dpm_resume"), state.event, true);
might_sleep(); might_sleep();
mutex_lock(&dpm_list_mtx); mutex_lock(&dpm_list_mtx);
@ -875,6 +879,7 @@ void dpm_resume(pm_message_t state)
dpm_show_time(starttime, state, NULL); dpm_show_time(starttime, state, NULL);
cpufreq_resume(); cpufreq_resume();
trace_suspend_resume(TPS("dpm_resume"), state.event, false);
} }
/** /**
@ -913,7 +918,9 @@ static void device_complete(struct device *dev, pm_message_t state)
if (callback) { if (callback) {
pm_dev_dbg(dev, state, info); pm_dev_dbg(dev, state, info);
trace_device_pm_callback_start(dev, info, state.event);
callback(dev); callback(dev);
trace_device_pm_callback_end(dev, 0);
} }
device_unlock(dev); device_unlock(dev);
@ -932,6 +939,7 @@ void dpm_complete(pm_message_t state)
{ {
struct list_head list; struct list_head list;
trace_suspend_resume(TPS("dpm_complete"), state.event, true);
might_sleep(); might_sleep();
INIT_LIST_HEAD(&list); INIT_LIST_HEAD(&list);
@ -951,6 +959,7 @@ void dpm_complete(pm_message_t state)
} }
list_splice(&list, &dpm_list); list_splice(&list, &dpm_list);
mutex_unlock(&dpm_list_mtx); mutex_unlock(&dpm_list_mtx);
trace_suspend_resume(TPS("dpm_complete"), state.event, false);
} }
/** /**
@ -1086,6 +1095,7 @@ static int dpm_suspend_noirq(pm_message_t state)
ktime_t starttime = ktime_get(); ktime_t starttime = ktime_get();
int error = 0; int error = 0;
trace_suspend_resume(TPS("dpm_suspend_noirq"), state.event, true);
cpuidle_pause(); cpuidle_pause();
suspend_device_irqs(); suspend_device_irqs();
mutex_lock(&dpm_list_mtx); mutex_lock(&dpm_list_mtx);
@ -1126,6 +1136,7 @@ static int dpm_suspend_noirq(pm_message_t state)
} else { } else {
dpm_show_time(starttime, state, "noirq"); dpm_show_time(starttime, state, "noirq");
} }
trace_suspend_resume(TPS("dpm_suspend_noirq"), state.event, false);
return error; return error;
} }
@ -1222,6 +1233,7 @@ static int dpm_suspend_late(pm_message_t state)
ktime_t starttime = ktime_get(); ktime_t starttime = ktime_get();
int error = 0; int error = 0;
trace_suspend_resume(TPS("dpm_suspend_late"), state.event, true);
mutex_lock(&dpm_list_mtx); mutex_lock(&dpm_list_mtx);
pm_transition = state; pm_transition = state;
async_error = 0; async_error = 0;
@ -1257,6 +1269,7 @@ static int dpm_suspend_late(pm_message_t state)
} else { } else {
dpm_show_time(starttime, state, "late"); dpm_show_time(starttime, state, "late");
} }
trace_suspend_resume(TPS("dpm_suspend_late"), state.event, false);
return error; return error;
} }
@ -1295,7 +1308,9 @@ static int legacy_suspend(struct device *dev, pm_message_t state,
calltime = initcall_debug_start(dev); calltime = initcall_debug_start(dev);
trace_device_pm_callback_start(dev, info, state.event);
error = cb(dev, state); error = cb(dev, state);
trace_device_pm_callback_end(dev, error);
suspend_report_result(cb, error); suspend_report_result(cb, error);
initcall_debug_report(dev, calltime, error, state, info); initcall_debug_report(dev, calltime, error, state, info);
@ -1461,6 +1476,7 @@ int dpm_suspend(pm_message_t state)
ktime_t starttime = ktime_get(); ktime_t starttime = ktime_get();
int error = 0; int error = 0;
trace_suspend_resume(TPS("dpm_suspend"), state.event, true);
might_sleep(); might_sleep();
cpufreq_suspend(); cpufreq_suspend();
@ -1498,6 +1514,7 @@ int dpm_suspend(pm_message_t state)
dpm_save_failed_step(SUSPEND_SUSPEND); dpm_save_failed_step(SUSPEND_SUSPEND);
} else } else
dpm_show_time(starttime, state, NULL); dpm_show_time(starttime, state, NULL);
trace_suspend_resume(TPS("dpm_suspend"), state.event, false);
return error; return error;
} }
@ -1549,8 +1566,11 @@ static int device_prepare(struct device *dev, pm_message_t state)
callback = dev->driver->pm->prepare; callback = dev->driver->pm->prepare;
} }
if (callback) if (callback) {
trace_device_pm_callback_start(dev, info, state.event);
ret = callback(dev); ret = callback(dev);
trace_device_pm_callback_end(dev, ret);
}
device_unlock(dev); device_unlock(dev);
@ -1582,6 +1602,7 @@ int dpm_prepare(pm_message_t state)
{ {
int error = 0; int error = 0;
trace_suspend_resume(TPS("dpm_prepare"), state.event, true);
might_sleep(); might_sleep();
mutex_lock(&dpm_list_mtx); mutex_lock(&dpm_list_mtx);
@ -1612,6 +1633,7 @@ int dpm_prepare(pm_message_t state)
put_device(dev); put_device(dev);
} }
mutex_unlock(&dpm_list_mtx); mutex_unlock(&dpm_list_mtx);
trace_suspend_resume(TPS("dpm_prepare"), state.event, false);
return error; return error;
} }

View File

@ -10,6 +10,7 @@
#include <linux/mutex.h> #include <linux/mutex.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <trace/events/power.h>
static LIST_HEAD(syscore_ops_list); static LIST_HEAD(syscore_ops_list);
static DEFINE_MUTEX(syscore_ops_lock); static DEFINE_MUTEX(syscore_ops_lock);
@ -49,6 +50,7 @@ int syscore_suspend(void)
struct syscore_ops *ops; struct syscore_ops *ops;
int ret = 0; int ret = 0;
trace_suspend_resume(TPS("syscore_suspend"), 0, true);
pr_debug("Checking wakeup interrupts\n"); pr_debug("Checking wakeup interrupts\n");
/* Return error code if there are any wakeup interrupts pending. */ /* Return error code if there are any wakeup interrupts pending. */
@ -70,6 +72,7 @@ int syscore_suspend(void)
"Interrupts enabled after %pF\n", ops->suspend); "Interrupts enabled after %pF\n", ops->suspend);
} }
trace_suspend_resume(TPS("syscore_suspend"), 0, false);
return 0; return 0;
err_out: err_out:
@ -92,6 +95,7 @@ void syscore_resume(void)
{ {
struct syscore_ops *ops; struct syscore_ops *ops;
trace_suspend_resume(TPS("syscore_resume"), 0, true);
WARN_ONCE(!irqs_disabled(), WARN_ONCE(!irqs_disabled(),
"Interrupts enabled before system core resume.\n"); "Interrupts enabled before system core resume.\n");
@ -103,6 +107,7 @@ void syscore_resume(void)
WARN_ONCE(!irqs_disabled(), WARN_ONCE(!irqs_disabled(),
"Interrupts enabled after %pF\n", ops->resume); "Interrupts enabled after %pF\n", ops->resume);
} }
trace_suspend_resume(TPS("syscore_resume"), 0, false);
} }
EXPORT_SYMBOL_GPL(syscore_resume); EXPORT_SYMBOL_GPL(syscore_resume);
#endif /* CONFIG_PM_SLEEP */ #endif /* CONFIG_PM_SLEEP */

View File

@ -185,7 +185,7 @@ config CPU_FREQ_GOV_CONSERVATIVE
config GENERIC_CPUFREQ_CPU0 config GENERIC_CPUFREQ_CPU0
tristate "Generic CPU0 cpufreq driver" tristate "Generic CPU0 cpufreq driver"
depends on HAVE_CLK && REGULATOR && OF && THERMAL && CPU_THERMAL depends on HAVE_CLK && OF
select PM_OPP select PM_OPP
help help
This adds a generic cpufreq driver for CPU0 frequency management. This adds a generic cpufreq driver for CPU0 frequency management.

View File

@ -5,8 +5,7 @@
# big LITTLE core layer and glue drivers # big LITTLE core layer and glue drivers
config ARM_BIG_LITTLE_CPUFREQ config ARM_BIG_LITTLE_CPUFREQ
tristate "Generic ARM big LITTLE CPUfreq driver" tristate "Generic ARM big LITTLE CPUfreq driver"
depends on (BIG_LITTLE && ARM_CPU_TOPOLOGY) || (ARM64 && SMP) depends on ARM && BIG_LITTLE && ARM_CPU_TOPOLOGY && HAVE_CLK
depends on HAVE_CLK
select PM_OPP select PM_OPP
help help
This enables the Generic CPUfreq driver for ARM big.LITTLE platforms. This enables the Generic CPUfreq driver for ARM big.LITTLE platforms.

View File

@ -104,7 +104,7 @@ static int cpu0_cpufreq_init(struct cpufreq_policy *policy)
} }
static struct cpufreq_driver cpu0_cpufreq_driver = { static struct cpufreq_driver cpu0_cpufreq_driver = {
.flags = CPUFREQ_STICKY, .flags = CPUFREQ_STICKY | CPUFREQ_NEED_INITIAL_FREQ_CHECK,
.verify = cpufreq_generic_frequency_table_verify, .verify = cpufreq_generic_frequency_table_verify,
.target_index = cpu0_set_target, .target_index = cpu0_set_target,
.get = cpufreq_generic_get, .get = cpufreq_generic_get,

View File

@ -1816,20 +1816,55 @@ EXPORT_SYMBOL(cpufreq_unregister_notifier);
* GOVERNORS * * GOVERNORS *
*********************************************************************/ *********************************************************************/
/* Must set freqs->new to intermediate frequency */
static int __target_intermediate(struct cpufreq_policy *policy,
struct cpufreq_freqs *freqs, int index)
{
int ret;
freqs->new = cpufreq_driver->get_intermediate(policy, index);
/* We don't need to switch to intermediate freq */
if (!freqs->new)
return 0;
pr_debug("%s: cpu: %d, switching to intermediate freq: oldfreq: %u, intermediate freq: %u\n",
__func__, policy->cpu, freqs->old, freqs->new);
cpufreq_freq_transition_begin(policy, freqs);
ret = cpufreq_driver->target_intermediate(policy, index);
cpufreq_freq_transition_end(policy, freqs, ret);
if (ret)
pr_err("%s: Failed to change to intermediate frequency: %d\n",
__func__, ret);
return ret;
}
static int __target_index(struct cpufreq_policy *policy, static int __target_index(struct cpufreq_policy *policy,
struct cpufreq_frequency_table *freq_table, int index) struct cpufreq_frequency_table *freq_table, int index)
{ {
struct cpufreq_freqs freqs; struct cpufreq_freqs freqs = {.old = policy->cur, .flags = 0};
unsigned int intermediate_freq = 0;
int retval = -EINVAL; int retval = -EINVAL;
bool notify; bool notify;
notify = !(cpufreq_driver->flags & CPUFREQ_ASYNC_NOTIFICATION); notify = !(cpufreq_driver->flags & CPUFREQ_ASYNC_NOTIFICATION);
if (notify) { if (notify) {
freqs.old = policy->cur; /* Handle switching to intermediate frequency */
freqs.new = freq_table[index].frequency; if (cpufreq_driver->get_intermediate) {
freqs.flags = 0; retval = __target_intermediate(policy, &freqs, index);
if (retval)
return retval;
intermediate_freq = freqs.new;
/* Set old freq to intermediate */
if (intermediate_freq)
freqs.old = freqs.new;
}
freqs.new = freq_table[index].frequency;
pr_debug("%s: cpu: %d, oldfreq: %u, new freq: %u\n", pr_debug("%s: cpu: %d, oldfreq: %u, new freq: %u\n",
__func__, policy->cpu, freqs.old, freqs.new); __func__, policy->cpu, freqs.old, freqs.new);
@ -1841,9 +1876,23 @@ static int __target_index(struct cpufreq_policy *policy,
pr_err("%s: Failed to change cpu frequency: %d\n", __func__, pr_err("%s: Failed to change cpu frequency: %d\n", __func__,
retval); retval);
if (notify) if (notify) {
cpufreq_freq_transition_end(policy, &freqs, retval); cpufreq_freq_transition_end(policy, &freqs, retval);
/*
* Failed after setting to intermediate freq? Driver should have
* reverted back to initial frequency and so should we. Check
* here for intermediate_freq instead of get_intermediate, in
* case we have't switched to intermediate freq at all.
*/
if (unlikely(retval && intermediate_freq)) {
freqs.old = intermediate_freq;
freqs.new = policy->restore_freq;
cpufreq_freq_transition_begin(policy, &freqs);
cpufreq_freq_transition_end(policy, &freqs, 0);
}
}
return retval; return retval;
} }
@ -1875,6 +1924,9 @@ int __cpufreq_driver_target(struct cpufreq_policy *policy,
if (target_freq == policy->cur) if (target_freq == policy->cur)
return 0; return 0;
/* Save last value to restore later on errors */
policy->restore_freq = policy->cur;
if (cpufreq_driver->target) if (cpufreq_driver->target)
retval = cpufreq_driver->target(policy, target_freq, relation); retval = cpufreq_driver->target(policy, target_freq, relation);
else if (cpufreq_driver->target_index) { else if (cpufreq_driver->target_index) {
@ -2361,7 +2413,8 @@ int cpufreq_register_driver(struct cpufreq_driver *driver_data)
!(driver_data->setpolicy || driver_data->target_index || !(driver_data->setpolicy || driver_data->target_index ||
driver_data->target) || driver_data->target) ||
(driver_data->setpolicy && (driver_data->target_index || (driver_data->setpolicy && (driver_data->target_index ||
driver_data->target))) driver_data->target)) ||
(!!driver_data->get_intermediate != !!driver_data->target_intermediate))
return -EINVAL; return -EINVAL;
pr_debug("trying to register driver %s\n", driver_data->name); pr_debug("trying to register driver %s\n", driver_data->name);

View File

@ -36,14 +36,29 @@ void dbs_check_cpu(struct dbs_data *dbs_data, int cpu)
struct od_dbs_tuners *od_tuners = dbs_data->tuners; struct od_dbs_tuners *od_tuners = dbs_data->tuners;
struct cs_dbs_tuners *cs_tuners = dbs_data->tuners; struct cs_dbs_tuners *cs_tuners = dbs_data->tuners;
struct cpufreq_policy *policy; struct cpufreq_policy *policy;
unsigned int sampling_rate;
unsigned int max_load = 0; unsigned int max_load = 0;
unsigned int ignore_nice; unsigned int ignore_nice;
unsigned int j; unsigned int j;
if (dbs_data->cdata->governor == GOV_ONDEMAND) if (dbs_data->cdata->governor == GOV_ONDEMAND) {
struct od_cpu_dbs_info_s *od_dbs_info =
dbs_data->cdata->get_cpu_dbs_info_s(cpu);
/*
* Sometimes, the ondemand governor uses an additional
* multiplier to give long delays. So apply this multiplier to
* the 'sampling_rate', so as to keep the wake-up-from-idle
* detection logic a bit conservative.
*/
sampling_rate = od_tuners->sampling_rate;
sampling_rate *= od_dbs_info->rate_mult;
ignore_nice = od_tuners->ignore_nice_load; ignore_nice = od_tuners->ignore_nice_load;
else } else {
sampling_rate = cs_tuners->sampling_rate;
ignore_nice = cs_tuners->ignore_nice_load; ignore_nice = cs_tuners->ignore_nice_load;
}
policy = cdbs->cur_policy; policy = cdbs->cur_policy;
@ -96,7 +111,46 @@ void dbs_check_cpu(struct dbs_data *dbs_data, int cpu)
if (unlikely(!wall_time || wall_time < idle_time)) if (unlikely(!wall_time || wall_time < idle_time))
continue; continue;
load = 100 * (wall_time - idle_time) / wall_time; /*
* If the CPU had gone completely idle, and a task just woke up
* on this CPU now, it would be unfair to calculate 'load' the
* usual way for this elapsed time-window, because it will show
* near-zero load, irrespective of how CPU intensive that task
* actually is. This is undesirable for latency-sensitive bursty
* workloads.
*
* To avoid this, we reuse the 'load' from the previous
* time-window and give this task a chance to start with a
* reasonably high CPU frequency. (However, we shouldn't over-do
* this copy, lest we get stuck at a high load (high frequency)
* for too long, even when the current system load has actually
* dropped down. So we perform the copy only once, upon the
* first wake-up from idle.)
*
* Detecting this situation is easy: the governor's deferrable
* timer would not have fired during CPU-idle periods. Hence
* an unusually large 'wall_time' (as compared to the sampling
* rate) indicates this scenario.
*
* prev_load can be zero in two cases and we must recalculate it
* for both cases:
* - during long idle intervals
* - explicitly set to zero
*/
if (unlikely(wall_time > (2 * sampling_rate) &&
j_cdbs->prev_load)) {
load = j_cdbs->prev_load;
/*
* Perform a destructive copy, to ensure that we copy
* the previous load only once, upon the first wake-up
* from idle.
*/
j_cdbs->prev_load = 0;
} else {
load = 100 * (wall_time - idle_time) / wall_time;
j_cdbs->prev_load = load;
}
if (load > max_load) if (load > max_load)
max_load = load; max_load = load;
@ -318,11 +372,18 @@ int cpufreq_governor_dbs(struct cpufreq_policy *policy,
for_each_cpu(j, policy->cpus) { for_each_cpu(j, policy->cpus) {
struct cpu_dbs_common_info *j_cdbs = struct cpu_dbs_common_info *j_cdbs =
dbs_data->cdata->get_cpu_cdbs(j); dbs_data->cdata->get_cpu_cdbs(j);
unsigned int prev_load;
j_cdbs->cpu = j; j_cdbs->cpu = j;
j_cdbs->cur_policy = policy; j_cdbs->cur_policy = policy;
j_cdbs->prev_cpu_idle = get_cpu_idle_time(j, j_cdbs->prev_cpu_idle = get_cpu_idle_time(j,
&j_cdbs->prev_cpu_wall, io_busy); &j_cdbs->prev_cpu_wall, io_busy);
prev_load = (unsigned int)
(j_cdbs->prev_cpu_wall - j_cdbs->prev_cpu_idle);
j_cdbs->prev_load = 100 * prev_load /
(unsigned int) j_cdbs->prev_cpu_wall;
if (ignore_nice) if (ignore_nice)
j_cdbs->prev_cpu_nice = j_cdbs->prev_cpu_nice =
kcpustat_cpu(j).cpustat[CPUTIME_NICE]; kcpustat_cpu(j).cpustat[CPUTIME_NICE];

View File

@ -134,6 +134,13 @@ struct cpu_dbs_common_info {
u64 prev_cpu_idle; u64 prev_cpu_idle;
u64 prev_cpu_wall; u64 prev_cpu_wall;
u64 prev_cpu_nice; u64 prev_cpu_nice;
/*
* Used to keep track of load in the previous interval. However, when
* explicitly set to zero, it is used as a flag to ensure that we copy
* the previous load to the current interval only once, upon the first
* wake-up from idle.
*/
unsigned int prev_load;
struct cpufreq_policy *cur_policy; struct cpufreq_policy *cur_policy;
struct delayed_work work; struct delayed_work work;
/* /*

View File

@ -691,14 +691,8 @@ MODULE_DEVICE_TABLE(x86cpu, intel_pstate_cpu_ids);
static int intel_pstate_init_cpu(unsigned int cpunum) static int intel_pstate_init_cpu(unsigned int cpunum)
{ {
const struct x86_cpu_id *id;
struct cpudata *cpu; struct cpudata *cpu;
id = x86_match_cpu(intel_pstate_cpu_ids);
if (!id)
return -ENODEV;
all_cpu_data[cpunum] = kzalloc(sizeof(struct cpudata), GFP_KERNEL); all_cpu_data[cpunum] = kzalloc(sizeof(struct cpudata), GFP_KERNEL);
if (!all_cpu_data[cpunum]) if (!all_cpu_data[cpunum])
return -ENOMEM; return -ENOMEM;

View File

@ -138,7 +138,7 @@ static int corenet_cpufreq_cpu_init(struct cpufreq_policy *policy)
struct cpufreq_frequency_table *table; struct cpufreq_frequency_table *table;
struct cpu_data *data; struct cpu_data *data;
unsigned int cpu = policy->cpu; unsigned int cpu = policy->cpu;
u64 transition_latency_hz; u64 u64temp;
np = of_get_cpu_node(cpu, NULL); np = of_get_cpu_node(cpu, NULL);
if (!np) if (!np)
@ -206,9 +206,10 @@ static int corenet_cpufreq_cpu_init(struct cpufreq_policy *policy)
for_each_cpu(i, per_cpu(cpu_mask, cpu)) for_each_cpu(i, per_cpu(cpu_mask, cpu))
per_cpu(cpu_data, i) = data; per_cpu(cpu_data, i) = data;
transition_latency_hz = 12ULL * NSEC_PER_SEC; /* Minimum transition latency is 12 platform clocks */
policy->cpuinfo.transition_latency = u64temp = 12ULL * NSEC_PER_SEC;
do_div(transition_latency_hz, fsl_get_sys_freq()); do_div(u64temp, fsl_get_sys_freq());
policy->cpuinfo.transition_latency = u64temp + 1;
of_node_put(np); of_node_put(np);

View File

@ -45,46 +45,54 @@ static struct clk *cpu_clk;
static struct clk *pll_x_clk; static struct clk *pll_x_clk;
static struct clk *pll_p_clk; static struct clk *pll_p_clk;
static struct clk *emc_clk; static struct clk *emc_clk;
static bool pll_x_prepared;
static int tegra_cpu_clk_set_rate(unsigned long rate) static unsigned int tegra_get_intermediate(struct cpufreq_policy *policy,
unsigned int index)
{
unsigned int ifreq = clk_get_rate(pll_p_clk) / 1000;
/*
* Don't switch to intermediate freq if:
* - we are already at it, i.e. policy->cur == ifreq
* - index corresponds to ifreq
*/
if ((freq_table[index].frequency == ifreq) || (policy->cur == ifreq))
return 0;
return ifreq;
}
static int tegra_target_intermediate(struct cpufreq_policy *policy,
unsigned int index)
{ {
int ret; int ret;
/* /*
* Take an extra reference to the main pll so it doesn't turn * Take an extra reference to the main pll so it doesn't turn
* off when we move the cpu off of it * off when we move the cpu off of it as enabling it again while we
* switch to it from tegra_target() would take additional time.
*
* When target-freq is equal to intermediate freq we don't need to
* switch to an intermediate freq and so this routine isn't called.
* Also, we wouldn't be using pll_x anymore and must not take extra
* reference to it, as it can be disabled now to save some power.
*/ */
clk_prepare_enable(pll_x_clk); clk_prepare_enable(pll_x_clk);
ret = clk_set_parent(cpu_clk, pll_p_clk); ret = clk_set_parent(cpu_clk, pll_p_clk);
if (ret) { if (ret)
pr_err("Failed to switch cpu to clock pll_p\n"); clk_disable_unprepare(pll_x_clk);
goto out; else
} pll_x_prepared = true;
if (rate == clk_get_rate(pll_p_clk))
goto out;
ret = clk_set_rate(pll_x_clk, rate);
if (ret) {
pr_err("Failed to change pll_x to %lu\n", rate);
goto out;
}
ret = clk_set_parent(cpu_clk, pll_x_clk);
if (ret) {
pr_err("Failed to switch cpu to clock pll_x\n");
goto out;
}
out:
clk_disable_unprepare(pll_x_clk);
return ret; return ret;
} }
static int tegra_target(struct cpufreq_policy *policy, unsigned int index) static int tegra_target(struct cpufreq_policy *policy, unsigned int index)
{ {
unsigned long rate = freq_table[index].frequency; unsigned long rate = freq_table[index].frequency;
unsigned int ifreq = clk_get_rate(pll_p_clk) / 1000;
int ret = 0; int ret = 0;
/* /*
@ -98,10 +106,30 @@ static int tegra_target(struct cpufreq_policy *policy, unsigned int index)
else else
clk_set_rate(emc_clk, 100000000); /* emc 50Mhz */ clk_set_rate(emc_clk, 100000000); /* emc 50Mhz */
ret = tegra_cpu_clk_set_rate(rate * 1000); /*
* target freq == pll_p, don't need to take extra reference to pll_x_clk
* as it isn't used anymore.
*/
if (rate == ifreq)
return clk_set_parent(cpu_clk, pll_p_clk);
ret = clk_set_rate(pll_x_clk, rate * 1000);
/* Restore to earlier frequency on error, i.e. pll_x */
if (ret) if (ret)
pr_err("cpu-tegra: Failed to set cpu frequency to %lu kHz\n", pr_err("Failed to change pll_x to %lu\n", rate);
rate);
ret = clk_set_parent(cpu_clk, pll_x_clk);
/* This shouldn't fail while changing or restoring */
WARN_ON(ret);
/*
* Drop count to pll_x clock only if we switched to intermediate freq
* earlier while transitioning to a target frequency.
*/
if (pll_x_prepared) {
clk_disable_unprepare(pll_x_clk);
pll_x_prepared = false;
}
return ret; return ret;
} }
@ -137,16 +165,18 @@ static int tegra_cpu_exit(struct cpufreq_policy *policy)
} }
static struct cpufreq_driver tegra_cpufreq_driver = { static struct cpufreq_driver tegra_cpufreq_driver = {
.flags = CPUFREQ_NEED_INITIAL_FREQ_CHECK, .flags = CPUFREQ_NEED_INITIAL_FREQ_CHECK,
.verify = cpufreq_generic_frequency_table_verify, .verify = cpufreq_generic_frequency_table_verify,
.target_index = tegra_target, .get_intermediate = tegra_get_intermediate,
.get = cpufreq_generic_get, .target_intermediate = tegra_target_intermediate,
.init = tegra_cpu_init, .target_index = tegra_target,
.exit = tegra_cpu_exit, .get = cpufreq_generic_get,
.name = "tegra", .init = tegra_cpu_init,
.attr = cpufreq_generic_attr, .exit = tegra_cpu_exit,
.name = "tegra",
.attr = cpufreq_generic_attr,
#ifdef CONFIG_PM #ifdef CONFIG_PM
.suspend = cpufreq_generic_suspend, .suspend = cpufreq_generic_suspend,
#endif #endif
}; };

View File

@ -142,6 +142,16 @@ static inline acpi_handle func_to_handle(struct acpiphp_func *func)
return func_to_acpi_device(func)->handle; return func_to_acpi_device(func)->handle;
} }
struct acpiphp_root_context {
struct acpi_hotplug_context hp;
struct acpiphp_bridge *root_bridge;
};
static inline struct acpiphp_root_context *to_acpiphp_root_context(struct acpi_hotplug_context *hp)
{
return container_of(hp, struct acpiphp_root_context, hp);
}
/* /*
* struct acpiphp_attention_info - device specific attention registration * struct acpiphp_attention_info - device specific attention registration
* *

View File

@ -373,17 +373,13 @@ static acpi_status acpiphp_add_context(acpi_handle handle, u32 lvl, void *data,
static struct acpiphp_bridge *acpiphp_dev_to_bridge(struct acpi_device *adev) static struct acpiphp_bridge *acpiphp_dev_to_bridge(struct acpi_device *adev)
{ {
struct acpiphp_context *context;
struct acpiphp_bridge *bridge = NULL; struct acpiphp_bridge *bridge = NULL;
acpi_lock_hp_context(); acpi_lock_hp_context();
context = acpiphp_get_context(adev); if (adev->hp) {
if (context) { bridge = to_acpiphp_root_context(adev->hp)->root_bridge;
bridge = context->bridge;
if (bridge) if (bridge)
get_bridge(bridge); get_bridge(bridge);
acpiphp_put_context(context);
} }
acpi_unlock_hp_context(); acpi_unlock_hp_context();
return bridge; return bridge;
@ -881,7 +877,17 @@ void acpiphp_enumerate_slots(struct pci_bus *bus)
*/ */
get_device(&bus->dev); get_device(&bus->dev);
if (!pci_is_root_bus(bridge->pci_bus)) { acpi_lock_hp_context();
if (pci_is_root_bus(bridge->pci_bus)) {
struct acpiphp_root_context *root_context;
root_context = kzalloc(sizeof(*root_context), GFP_KERNEL);
if (!root_context)
goto err;
root_context->root_bridge = bridge;
acpi_set_hp_context(adev, &root_context->hp, NULL, NULL, NULL);
} else {
struct acpiphp_context *context; struct acpiphp_context *context;
/* /*
@ -890,21 +896,16 @@ void acpiphp_enumerate_slots(struct pci_bus *bus)
* parent is going to be handled by pciehp, in which case this * parent is going to be handled by pciehp, in which case this
* bridge is not interesting to us either. * bridge is not interesting to us either.
*/ */
acpi_lock_hp_context();
context = acpiphp_get_context(adev); context = acpiphp_get_context(adev);
if (!context) { if (!context)
acpi_unlock_hp_context(); goto err;
put_device(&bus->dev);
pci_dev_put(bridge->pci_dev);
kfree(bridge);
return;
}
bridge->context = context; bridge->context = context;
context->bridge = bridge; context->bridge = bridge;
/* Get a reference to the parent bridge. */ /* Get a reference to the parent bridge. */
get_bridge(context->func.parent); get_bridge(context->func.parent);
acpi_unlock_hp_context();
} }
acpi_unlock_hp_context();
/* Must be added to the list prior to calling acpiphp_add_context(). */ /* Must be added to the list prior to calling acpiphp_add_context(). */
mutex_lock(&bridge_mutex); mutex_lock(&bridge_mutex);
@ -919,6 +920,30 @@ void acpiphp_enumerate_slots(struct pci_bus *bus)
cleanup_bridge(bridge); cleanup_bridge(bridge);
put_bridge(bridge); put_bridge(bridge);
} }
return;
err:
acpi_unlock_hp_context();
put_device(&bus->dev);
pci_dev_put(bridge->pci_dev);
kfree(bridge);
}
void acpiphp_drop_bridge(struct acpiphp_bridge *bridge)
{
if (pci_is_root_bus(bridge->pci_bus)) {
struct acpiphp_root_context *root_context;
struct acpi_device *adev;
acpi_lock_hp_context();
adev = ACPI_COMPANION(bridge->pci_bus->bridge);
root_context = to_acpiphp_root_context(adev->hp);
adev->hp = NULL;
acpi_unlock_hp_context();
kfree(root_context);
}
cleanup_bridge(bridge);
put_bridge(bridge);
} }
/** /**
@ -936,8 +961,7 @@ void acpiphp_remove_slots(struct pci_bus *bus)
list_for_each_entry(bridge, &bridge_list, list) list_for_each_entry(bridge, &bridge_list, list)
if (bridge->pci_bus == bus) { if (bridge->pci_bus == bus) {
mutex_unlock(&bridge_mutex); mutex_unlock(&bridge_mutex);
cleanup_bridge(bridge); acpiphp_drop_bridge(bridge);
put_bridge(bridge);
return; return;
} }

View File

@ -75,6 +75,7 @@ struct cpufreq_policy {
unsigned int max; /* in kHz */ unsigned int max; /* in kHz */
unsigned int cur; /* in kHz, only needed if cpufreq unsigned int cur; /* in kHz, only needed if cpufreq
* governors are used */ * governors are used */
unsigned int restore_freq; /* = policy->cur before transition */
unsigned int suspend_freq; /* freq to set during suspend */ unsigned int suspend_freq; /* freq to set during suspend */
unsigned int policy; /* see above */ unsigned int policy; /* see above */
@ -221,11 +222,35 @@ struct cpufreq_driver {
/* define one out of two */ /* define one out of two */
int (*setpolicy) (struct cpufreq_policy *policy); int (*setpolicy) (struct cpufreq_policy *policy);
/*
* On failure, should always restore frequency to policy->restore_freq
* (i.e. old freq).
*/
int (*target) (struct cpufreq_policy *policy, /* Deprecated */ int (*target) (struct cpufreq_policy *policy, /* Deprecated */
unsigned int target_freq, unsigned int target_freq,
unsigned int relation); unsigned int relation);
int (*target_index) (struct cpufreq_policy *policy, int (*target_index) (struct cpufreq_policy *policy,
unsigned int index); unsigned int index);
/*
* Only for drivers with target_index() and CPUFREQ_ASYNC_NOTIFICATION
* unset.
*
* get_intermediate should return a stable intermediate frequency
* platform wants to switch to and target_intermediate() should set CPU
* to to that frequency, before jumping to the frequency corresponding
* to 'index'. Core will take care of sending notifications and driver
* doesn't have to handle them in target_intermediate() or
* target_index().
*
* Drivers can return '0' from get_intermediate() in case they don't
* wish to switch to intermediate frequency for some target frequency.
* In that case core will directly call ->target_index().
*/
unsigned int (*get_intermediate)(struct cpufreq_policy *policy,
unsigned int index);
int (*target_intermediate)(struct cpufreq_policy *policy,
unsigned int index);
/* should be defined, if possible */ /* should be defined, if possible */
unsigned int (*get) (unsigned int cpu); unsigned int (*get) (unsigned int cpu);

View File

@ -7,6 +7,9 @@
#include <linux/ktime.h> #include <linux/ktime.h>
#include <linux/pm_qos.h> #include <linux/pm_qos.h>
#include <linux/tracepoint.h> #include <linux/tracepoint.h>
#include <linux/ftrace_event.h>
#define TPS(x) tracepoint_string(x)
DECLARE_EVENT_CLASS(cpu, DECLARE_EVENT_CLASS(cpu,
@ -90,6 +93,17 @@ TRACE_EVENT(pstate_sample,
#define PWR_EVENT_EXIT -1 #define PWR_EVENT_EXIT -1
#endif #endif
#define pm_verb_symbolic(event) \
__print_symbolic(event, \
{ PM_EVENT_SUSPEND, "suspend" }, \
{ PM_EVENT_RESUME, "resume" }, \
{ PM_EVENT_FREEZE, "freeze" }, \
{ PM_EVENT_QUIESCE, "quiesce" }, \
{ PM_EVENT_HIBERNATE, "hibernate" }, \
{ PM_EVENT_THAW, "thaw" }, \
{ PM_EVENT_RESTORE, "restore" }, \
{ PM_EVENT_RECOVER, "recover" })
DEFINE_EVENT(cpu, cpu_frequency, DEFINE_EVENT(cpu, cpu_frequency,
TP_PROTO(unsigned int frequency, unsigned int cpu_id), TP_PROTO(unsigned int frequency, unsigned int cpu_id),
@ -97,58 +111,76 @@ DEFINE_EVENT(cpu, cpu_frequency,
TP_ARGS(frequency, cpu_id) TP_ARGS(frequency, cpu_id)
); );
TRACE_EVENT(machine_suspend, TRACE_EVENT(device_pm_callback_start,
TP_PROTO(unsigned int state), TP_PROTO(struct device *dev, const char *pm_ops, int event),
TP_ARGS(state), TP_ARGS(dev, pm_ops, event),
TP_STRUCT__entry(
__field( u32, state )
),
TP_fast_assign(
__entry->state = state;
),
TP_printk("state=%lu", (unsigned long)__entry->state)
);
TRACE_EVENT(device_pm_report_time,
TP_PROTO(struct device *dev, const char *pm_ops, s64 ops_time,
char *pm_event_str, int error),
TP_ARGS(dev, pm_ops, ops_time, pm_event_str, error),
TP_STRUCT__entry( TP_STRUCT__entry(
__string(device, dev_name(dev)) __string(device, dev_name(dev))
__string(driver, dev_driver_string(dev)) __string(driver, dev_driver_string(dev))
__string(parent, dev->parent ? dev_name(dev->parent) : "none") __string(parent, dev->parent ? dev_name(dev->parent) : "none")
__string(pm_ops, pm_ops ? pm_ops : "none ") __string(pm_ops, pm_ops ? pm_ops : "none ")
__string(pm_event_str, pm_event_str) __field(int, event)
__field(s64, ops_time) ),
TP_fast_assign(
__assign_str(device, dev_name(dev));
__assign_str(driver, dev_driver_string(dev));
__assign_str(parent,
dev->parent ? dev_name(dev->parent) : "none");
__assign_str(pm_ops, pm_ops ? pm_ops : "none ");
__entry->event = event;
),
TP_printk("%s %s, parent: %s, %s[%s]", __get_str(driver),
__get_str(device), __get_str(parent), __get_str(pm_ops),
pm_verb_symbolic(__entry->event))
);
TRACE_EVENT(device_pm_callback_end,
TP_PROTO(struct device *dev, int error),
TP_ARGS(dev, error),
TP_STRUCT__entry(
__string(device, dev_name(dev))
__string(driver, dev_driver_string(dev))
__field(int, error) __field(int, error)
), ),
TP_fast_assign( TP_fast_assign(
const char *tmp = dev->parent ? dev_name(dev->parent) : "none";
const char *tmp_i = pm_ops ? pm_ops : "none ";
__assign_str(device, dev_name(dev)); __assign_str(device, dev_name(dev));
__assign_str(driver, dev_driver_string(dev)); __assign_str(driver, dev_driver_string(dev));
__assign_str(parent, tmp);
__assign_str(pm_ops, tmp_i);
__assign_str(pm_event_str, pm_event_str);
__entry->ops_time = ops_time;
__entry->error = error; __entry->error = error;
), ),
/* ops_str has an extra space at the end */ TP_printk("%s %s, err=%d",
TP_printk("%s %s parent=%s state=%s ops=%snsecs=%lld err=%d", __get_str(driver), __get_str(device), __entry->error)
__get_str(driver), __get_str(device), __get_str(parent), );
__get_str(pm_event_str), __get_str(pm_ops),
__entry->ops_time, __entry->error) TRACE_EVENT(suspend_resume,
TP_PROTO(const char *action, int val, bool start),
TP_ARGS(action, val, start),
TP_STRUCT__entry(
__field(const char *, action)
__field(int, val)
__field(bool, start)
),
TP_fast_assign(
__entry->action = action;
__entry->val = val;
__entry->start = start;
),
TP_printk("%s[%u] %s", __entry->action, (unsigned int)__entry->val,
(__entry->start)?"begin":"end")
); );
DECLARE_EVENT_CLASS(wakeup_source, DECLARE_EVENT_CLASS(wakeup_source,

View File

@ -20,6 +20,7 @@
#include <linux/gfp.h> #include <linux/gfp.h>
#include <linux/suspend.h> #include <linux/suspend.h>
#include <linux/lockdep.h> #include <linux/lockdep.h>
#include <trace/events/power.h>
#include "smpboot.h" #include "smpboot.h"
@ -520,7 +521,9 @@ int disable_nonboot_cpus(void)
for_each_online_cpu(cpu) { for_each_online_cpu(cpu) {
if (cpu == first_cpu) if (cpu == first_cpu)
continue; continue;
trace_suspend_resume(TPS("CPU_OFF"), cpu, true);
error = _cpu_down(cpu, 1); error = _cpu_down(cpu, 1);
trace_suspend_resume(TPS("CPU_OFF"), cpu, false);
if (!error) if (!error)
cpumask_set_cpu(cpu, frozen_cpus); cpumask_set_cpu(cpu, frozen_cpus);
else { else {
@ -563,7 +566,9 @@ void __ref enable_nonboot_cpus(void)
arch_enable_nonboot_cpus_begin(); arch_enable_nonboot_cpus_begin();
for_each_cpu(cpu, frozen_cpus) { for_each_cpu(cpu, frozen_cpus) {
trace_suspend_resume(TPS("CPU_ON"), cpu, true);
error = _cpu_up(cpu, 1); error = _cpu_up(cpu, 1);
trace_suspend_resume(TPS("CPU_ON"), cpu, false);
if (!error) { if (!error) {
pr_info("CPU%d is up\n", cpu); pr_info("CPU%d is up\n", cpu);
continue; continue;

View File

@ -28,6 +28,7 @@
#include <linux/syscore_ops.h> #include <linux/syscore_ops.h>
#include <linux/ctype.h> #include <linux/ctype.h>
#include <linux/genhd.h> #include <linux/genhd.h>
#include <trace/events/power.h>
#include "power.h" #include "power.h"
@ -292,7 +293,9 @@ static int create_image(int platform_mode)
in_suspend = 1; in_suspend = 1;
save_processor_state(); save_processor_state();
trace_suspend_resume(TPS("machine_suspend"), PM_EVENT_HIBERNATE, true);
error = swsusp_arch_suspend(); error = swsusp_arch_suspend();
trace_suspend_resume(TPS("machine_suspend"), PM_EVENT_HIBERNATE, false);
if (error) if (error)
printk(KERN_ERR "PM: Error %d creating hibernation image\n", printk(KERN_ERR "PM: Error %d creating hibernation image\n",
error); error);

View File

@ -17,6 +17,7 @@
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/workqueue.h> #include <linux/workqueue.h>
#include <linux/kmod.h> #include <linux/kmod.h>
#include <trace/events/power.h>
/* /*
* Timeout for stopping processes * Timeout for stopping processes
@ -175,6 +176,7 @@ void thaw_processes(void)
struct task_struct *g, *p; struct task_struct *g, *p;
struct task_struct *curr = current; struct task_struct *curr = current;
trace_suspend_resume(TPS("thaw_processes"), 0, true);
if (pm_freezing) if (pm_freezing)
atomic_dec(&system_freezing_cnt); atomic_dec(&system_freezing_cnt);
pm_freezing = false; pm_freezing = false;
@ -201,6 +203,7 @@ void thaw_processes(void)
schedule(); schedule();
printk("done.\n"); printk("done.\n");
trace_suspend_resume(TPS("thaw_processes"), 0, false);
} }
void thaw_kernel_threads(void) void thaw_kernel_threads(void)

View File

@ -177,7 +177,9 @@ static int suspend_prepare(suspend_state_t state)
if (error) if (error)
goto Finish; goto Finish;
trace_suspend_resume(TPS("freeze_processes"), 0, true);
error = suspend_freeze_processes(); error = suspend_freeze_processes();
trace_suspend_resume(TPS("freeze_processes"), 0, false);
if (!error) if (!error)
return 0; return 0;
@ -240,7 +242,9 @@ static int suspend_enter(suspend_state_t state, bool *wakeup)
* all the devices are suspended. * all the devices are suspended.
*/ */
if (state == PM_SUSPEND_FREEZE) { if (state == PM_SUSPEND_FREEZE) {
trace_suspend_resume(TPS("machine_suspend"), state, true);
freeze_enter(); freeze_enter();
trace_suspend_resume(TPS("machine_suspend"), state, false);
goto Platform_wake; goto Platform_wake;
} }
@ -256,7 +260,11 @@ static int suspend_enter(suspend_state_t state, bool *wakeup)
if (!error) { if (!error) {
*wakeup = pm_wakeup_pending(); *wakeup = pm_wakeup_pending();
if (!(suspend_test(TEST_CORE) || *wakeup)) { if (!(suspend_test(TEST_CORE) || *wakeup)) {
trace_suspend_resume(TPS("machine_suspend"),
state, true);
error = suspend_ops->enter(state); error = suspend_ops->enter(state);
trace_suspend_resume(TPS("machine_suspend"),
state, false);
events_check_enabled = false; events_check_enabled = false;
} }
syscore_resume(); syscore_resume();
@ -294,7 +302,6 @@ int suspend_devices_and_enter(suspend_state_t state)
if (need_suspend_ops(state) && !suspend_ops) if (need_suspend_ops(state) && !suspend_ops)
return -ENOSYS; return -ENOSYS;
trace_machine_suspend(state);
if (need_suspend_ops(state) && suspend_ops->begin) { if (need_suspend_ops(state) && suspend_ops->begin) {
error = suspend_ops->begin(state); error = suspend_ops->begin(state);
if (error) if (error)
@ -331,7 +338,6 @@ int suspend_devices_and_enter(suspend_state_t state)
else if (state == PM_SUSPEND_FREEZE && freeze_ops->end) else if (state == PM_SUSPEND_FREEZE && freeze_ops->end)
freeze_ops->end(); freeze_ops->end();
trace_machine_suspend(PWR_EVENT_EXIT);
return error; return error;
Recover_platform: Recover_platform:
@ -365,6 +371,7 @@ static int enter_state(suspend_state_t state)
{ {
int error; int error;
trace_suspend_resume(TPS("suspend_enter"), state, true);
if (state == PM_SUSPEND_FREEZE) { if (state == PM_SUSPEND_FREEZE) {
#ifdef CONFIG_PM_DEBUG #ifdef CONFIG_PM_DEBUG
if (pm_test_level != TEST_NONE && pm_test_level <= TEST_CPUS) { if (pm_test_level != TEST_NONE && pm_test_level <= TEST_CPUS) {
@ -382,9 +389,11 @@ static int enter_state(suspend_state_t state)
if (state == PM_SUSPEND_FREEZE) if (state == PM_SUSPEND_FREEZE)
freeze_begin(); freeze_begin();
trace_suspend_resume(TPS("sync_filesystems"), 0, true);
printk(KERN_INFO "PM: Syncing filesystems ... "); printk(KERN_INFO "PM: Syncing filesystems ... ");
sys_sync(); sys_sync();
printk("done.\n"); printk("done.\n");
trace_suspend_resume(TPS("sync_filesystems"), 0, false);
pr_debug("PM: Preparing system for %s sleep\n", pm_states[state].label); pr_debug("PM: Preparing system for %s sleep\n", pm_states[state].label);
error = suspend_prepare(state); error = suspend_prepare(state);
@ -394,6 +403,7 @@ static int enter_state(suspend_state_t state)
if (suspend_test(TEST_FREEZER)) if (suspend_test(TEST_FREEZER))
goto Finish; goto Finish;
trace_suspend_resume(TPS("suspend_enter"), state, false);
pr_debug("PM: Entering %s sleep\n", pm_states[state].label); pr_debug("PM: Entering %s sleep\n", pm_states[state].label);
pm_restrict_gfp_mask(); pm_restrict_gfp_mask();
error = suspend_devices_and_enter(state); error = suspend_devices_and_enter(state);