Skip to content

Commit

Permalink
Merge branches 'pm-core', 'pm-sleep', 'pm-opp' and 'pm-tools'
Browse files Browse the repository at this point in the history
Merge PM core changes, updates related to system sleep support,
operating performance points (OPP) changes and power management
utilities changes for 6.4-rc1:

 - Drop unnecessary (void *) conversions from the PM core (Li zeming).

 - Add sysfs files to represent time spent in a platform sleep state
   during suspend-to-idle and make AMD and Intel PMC drivers use them
   (Mario Limonciello).

 - Use of_property_present() for testing DT property presence (Rob
   Herring).

 - Add set_required_opps() callback to the 'struct opp_table', to make
   the code paths cleaner (Viresh Kumar).

 - Update the pm-graph siute of utilities to v5.11 with the following
   changes:
   * New script which allows users to install the latest pm-graph
     from the upstream github repo.
   * Update all the dmesg suspend/resume PM print formats to be able to
     process recent timelines using dmesg only.
   * Add ethtool output to the log for the system's ethernet device if
     ethtool exists.
   * Make the tool more robustly handle events where mangled dmesg or
     ftrace outputs do not include all the requisite data.

 - Make the sleepgraph utility recognize "CPU killed" messages (Xueqin
   Luo).

* pm-core:
  PM: core: Remove unnecessary (void *) conversions

* pm-sleep:
  platform/x86/intel/pmc: core: Report duration of time in HW sleep state
  platform/x86/intel/pmc: core: Always capture counters on suspend
  platform/x86/amd: pmc: Report duration of time in hw sleep state
  PM: Add sysfs files to represent time spent in hardware sleep state

* pm-opp:
  OPP: Move required opps configuration to specialized callback
  OPP: Handle all genpd cases together in _set_required_opps()
  opp: Use of_property_present() for testing DT property presence

* pm-tools:
  PM: tools: sleepgraph: Recognize "CPU killed" messages
  pm-graph: Update to v5.11
  • Loading branch information
Rafael J. Wysocki committed Apr 24, 2023
5 parents 21def61 + 73d73f5 + ddd66d6 + 0ba4962 + 34ea427 commit d3f2c40
Showing 13 changed files with 236 additions and 86 deletions.
29 changes: 29 additions & 0 deletions Documentation/ABI/testing/sysfs-power
Original file line number Diff line number Diff line change
@@ -413,6 +413,35 @@ Description:
The /sys/power/suspend_stats/last_failed_step file contains
the last failed step in the suspend/resume path.

What: /sys/power/suspend_stats/last_hw_sleep
Date: June 2023
Contact: Mario Limonciello <mario.limonciello@amd.com>
Description:
The /sys/power/suspend_stats/last_hw_sleep file
contains the duration of time spent in a hardware sleep
state in the most recent system suspend-resume cycle.
This number is measured in microseconds.

What: /sys/power/suspend_stats/total_hw_sleep
Date: June 2023
Contact: Mario Limonciello <mario.limonciello@amd.com>
Description:
The /sys/power/suspend_stats/total_hw_sleep file
contains the aggregate of time spent in a hardware sleep
state since the kernel was booted. This number
is measured in microseconds.

What: /sys/power/suspend_stats/max_hw_sleep
Date: June 2023
Contact: Mario Limonciello <mario.limonciello@amd.com>
Description:
The /sys/power/suspend_stats/max_hw_sleep file
contains the maximum amount of time that the hardware can
report for time spent in a hardware sleep state. When sleep
cycles are longer than this time, the values for
'total_hw_sleep' and 'last_hw_sleep' may not be accurate.
This number is measured in microseconds.

What: /sys/power/sync_on_suspend
Date: October 2019
Contact: Jonas Meurer <jonas@freesources.org>
12 changes: 6 additions & 6 deletions drivers/base/power/main.c
Original file line number Diff line number Diff line change
@@ -679,7 +679,7 @@ static bool dpm_async_fn(struct device *dev, async_func_t func)

static void async_resume_noirq(void *data, async_cookie_t cookie)
{
struct device *dev = (struct device *)data;
struct device *dev = data;
int error;

error = device_resume_noirq(dev, pm_transition, true);
@@ -816,7 +816,7 @@ static int device_resume_early(struct device *dev, pm_message_t state, bool asyn

static void async_resume_early(void *data, async_cookie_t cookie)
{
struct device *dev = (struct device *)data;
struct device *dev = data;
int error;

error = device_resume_early(dev, pm_transition, true);
@@ -980,7 +980,7 @@ static int device_resume(struct device *dev, pm_message_t state, bool async)

static void async_resume(void *data, async_cookie_t cookie)
{
struct device *dev = (struct device *)data;
struct device *dev = data;
int error;

error = device_resume(dev, pm_transition, true);
@@ -1269,7 +1269,7 @@ static int __device_suspend_noirq(struct device *dev, pm_message_t state, bool a

static void async_suspend_noirq(void *data, async_cookie_t cookie)
{
struct device *dev = (struct device *)data;
struct device *dev = data;
int error;

error = __device_suspend_noirq(dev, pm_transition, true);
@@ -1450,7 +1450,7 @@ static int __device_suspend_late(struct device *dev, pm_message_t state, bool as

static void async_suspend_late(void *data, async_cookie_t cookie)
{
struct device *dev = (struct device *)data;
struct device *dev = data;
int error;

error = __device_suspend_late(dev, pm_transition, true);
@@ -1727,7 +1727,7 @@ static int __device_suspend(struct device *dev, pm_message_t state, bool async)

static void async_suspend(void *data, async_cookie_t cookie)
{
struct device *dev = (struct device *)data;
struct device *dev = data;
int error;

error = __device_suspend(dev, pm_transition, true);
78 changes: 44 additions & 34 deletions drivers/opp/core.c
Original file line number Diff line number Diff line change
@@ -935,8 +935,8 @@ static int _set_opp_bw(const struct opp_table *opp_table,
return 0;
}

static int _set_required_opp(struct device *dev, struct device *pd_dev,
struct dev_pm_opp *opp, int i)
static int _set_performance_state(struct device *dev, struct device *pd_dev,
struct dev_pm_opp *opp, int i)
{
unsigned int pstate = likely(opp) ? opp->required_opps[i]->pstate : 0;
int ret;
@@ -953,37 +953,19 @@ static int _set_required_opp(struct device *dev, struct device *pd_dev,
return ret;
}

/* This is only called for PM domain for now */
static int _set_required_opps(struct device *dev,
struct opp_table *opp_table,
struct dev_pm_opp *opp, bool up)
static int _opp_set_required_opps_generic(struct device *dev,
struct opp_table *opp_table, struct dev_pm_opp *opp, bool scaling_down)
{
struct opp_table **required_opp_tables = opp_table->required_opp_tables;
struct device **genpd_virt_devs = opp_table->genpd_virt_devs;
int i, ret = 0;

if (!required_opp_tables)
return 0;

/* required-opps not fully initialized yet */
if (lazy_linking_pending(opp_table))
return -EBUSY;

/*
* We only support genpd's OPPs in the "required-opps" for now, as we
* don't know much about other use cases. Error out if the required OPP
* doesn't belong to a genpd.
*/
if (unlikely(!required_opp_tables[0]->is_genpd)) {
dev_err(dev, "required-opps don't belong to a genpd\n");
return -ENOENT;
}

/* Single genpd case */
if (!genpd_virt_devs)
return _set_required_opp(dev, dev, opp, 0);
dev_err(dev, "setting required-opps isn't supported for non-genpd devices\n");
return -ENOENT;
}

/* Multiple genpd case */
static int _opp_set_required_opps_genpd(struct device *dev,
struct opp_table *opp_table, struct dev_pm_opp *opp, bool scaling_down)
{
struct device **genpd_virt_devs =
opp_table->genpd_virt_devs ? opp_table->genpd_virt_devs : &dev;
int i, ret = 0;

/*
* Acquire genpd_virt_dev_lock to make sure we don't use a genpd_dev
@@ -992,15 +974,15 @@ static int _set_required_opps(struct device *dev,
mutex_lock(&opp_table->genpd_virt_dev_lock);

/* Scaling up? Set required OPPs in normal order, else reverse */
if (up) {
if (!scaling_down) {
for (i = 0; i < opp_table->required_opp_count; i++) {
ret = _set_required_opp(dev, genpd_virt_devs[i], opp, i);
ret = _set_performance_state(dev, genpd_virt_devs[i], opp, i);
if (ret)
break;
}
} else {
for (i = opp_table->required_opp_count - 1; i >= 0; i--) {
ret = _set_required_opp(dev, genpd_virt_devs[i], opp, i);
ret = _set_performance_state(dev, genpd_virt_devs[i], opp, i);
if (ret)
break;
}
@@ -1011,6 +993,34 @@ static int _set_required_opps(struct device *dev,
return ret;
}

/* This is only called for PM domain for now */
static int _set_required_opps(struct device *dev, struct opp_table *opp_table,
struct dev_pm_opp *opp, bool up)
{
/* required-opps not fully initialized yet */
if (lazy_linking_pending(opp_table))
return -EBUSY;

if (opp_table->set_required_opps)
return opp_table->set_required_opps(dev, opp_table, opp, up);

return 0;
}

/* Update set_required_opps handler */
void _update_set_required_opps(struct opp_table *opp_table)
{
/* Already set */
if (opp_table->set_required_opps)
return;

/* All required OPPs will belong to genpd or none */
if (opp_table->required_opp_tables[0]->is_genpd)
opp_table->set_required_opps = _opp_set_required_opps_genpd;
else
opp_table->set_required_opps = _opp_set_required_opps_generic;
}

static void _find_current_opp(struct device *dev, struct opp_table *opp_table)
{
struct dev_pm_opp *opp = ERR_PTR(-ENODEV);
7 changes: 5 additions & 2 deletions drivers/opp/of.c
Original file line number Diff line number Diff line change
@@ -196,6 +196,8 @@ static void _opp_table_alloc_required_tables(struct opp_table *opp_table,
/* Let's do the linking later on */
if (lazy)
list_add(&opp_table->lazy, &lazy_opp_tables);
else
_update_set_required_opps(opp_table);

goto put_np;

@@ -224,7 +226,7 @@ void _of_init_opp_table(struct opp_table *opp_table, struct device *dev,
of_property_read_u32(np, "voltage-tolerance",
&opp_table->voltage_tolerance_v1);

if (of_find_property(np, "#power-domain-cells", NULL))
if (of_property_present(np, "#power-domain-cells"))
opp_table->is_genpd = true;

/* Get OPP table node */
@@ -411,6 +413,7 @@ static void lazy_link_required_opp_table(struct opp_table *new_table)

/* All required opp-tables found, remove from lazy list */
if (!lazy) {
_update_set_required_opps(opp_table);
list_del_init(&opp_table->lazy);

list_for_each_entry(opp, &opp_table->opp_list, node)
@@ -536,7 +539,7 @@ static bool _opp_is_supported(struct device *dev, struct opp_table *opp_table,
* an OPP then the OPP should not be enabled as there is
* no way to see if the hardware supports it.
*/
if (of_find_property(np, "opp-supported-hw", NULL))
if (of_property_present(np, "opp-supported-hw"))
return false;
else
return true;
4 changes: 4 additions & 0 deletions drivers/opp/opp.h
Original file line number Diff line number Diff line change
@@ -184,6 +184,7 @@ enum opp_table_access {
* @enabled: Set to true if the device's resources are enabled/configured.
* @genpd_performance_state: Device's power domain support performance state.
* @is_genpd: Marks if the OPP table belongs to a genpd.
* @set_required_opps: Helper responsible to set required OPPs.
* @dentry: debugfs dentry pointer of the real device directory (not links).
* @dentry_name: Name of the real dentry.
*
@@ -234,6 +235,8 @@ struct opp_table {
bool enabled;
bool genpd_performance_state;
bool is_genpd;
int (*set_required_opps)(struct device *dev,
struct opp_table *opp_table, struct dev_pm_opp *opp, bool scaling_down);

#ifdef CONFIG_DEBUG_FS
struct dentry *dentry;
@@ -257,6 +260,7 @@ void _dev_pm_opp_cpumask_remove_table(const struct cpumask *cpumask, int last_cp
struct opp_table *_add_opp_table_indexed(struct device *dev, int index, bool getclk);
void _put_opp_list_kref(struct opp_table *opp_table);
void _required_opps_available(struct dev_pm_opp *opp, int count);
void _update_set_required_opps(struct opp_table *opp_table);

static inline bool lazy_linking_pending(struct opp_table *opp_table)
{
6 changes: 3 additions & 3 deletions drivers/platform/x86/amd/pmc.c
Original file line number Diff line number Diff line change
@@ -393,9 +393,8 @@ static void amd_pmc_validate_deepest(struct amd_pmc_dev *pdev)

if (!table.s0i3_last_entry_status)
dev_warn(pdev->dev, "Last suspend didn't reach deepest state\n");
else
dev_dbg(pdev->dev, "Last suspend in deepest state for %lluus\n",
table.timein_s0i3_lastcapture);
pm_report_hw_sleep_time(table.s0i3_last_entry_status ?
table.timein_s0i3_lastcapture : 0);
}

static int amd_pmc_get_smu_version(struct amd_pmc_dev *dev)
@@ -1015,6 +1014,7 @@ static int amd_pmc_probe(struct platform_device *pdev)
}

amd_pmc_dbgfs_register(dev);
pm_report_max_hw_sleep(U64_MAX);
return 0;

err_pci_dev_put:
17 changes: 9 additions & 8 deletions drivers/platform/x86/intel/pmc/core.c
Original file line number Diff line number Diff line change
@@ -1153,6 +1153,8 @@ static int pmc_core_probe(struct platform_device *pdev)
pmc_core_do_dmi_quirks(pmcdev);

pmc_core_dbgfs_register(pmcdev);
pm_report_max_hw_sleep(FIELD_MAX(SLP_S0_RES_COUNTER_MASK) *
pmc_core_adjust_slp_s0_step(pmcdev, 1));

device_initialized = true;
dev_info(&pdev->dev, " initialized\n");
@@ -1179,12 +1181,6 @@ static __maybe_unused int pmc_core_suspend(struct device *dev)
{
struct pmc_dev *pmcdev = dev_get_drvdata(dev);

pmcdev->check_counters = false;

/* No warnings on S0ix failures */
if (!warn_on_s0ix_failures)
return 0;

/* Check if the syspend will actually use S0ix */
if (pm_suspend_via_firmware())
return 0;
@@ -1197,7 +1193,6 @@ static __maybe_unused int pmc_core_suspend(struct device *dev)
if (pmc_core_dev_state_get(pmcdev, &pmcdev->s0ix_counter))
return -EIO;

pmcdev->check_counters = true;
return 0;
}

@@ -1221,6 +1216,8 @@ static inline bool pmc_core_is_s0ix_failed(struct pmc_dev *pmcdev)
if (pmc_core_dev_state_get(pmcdev, &s0ix_counter))
return false;

pm_report_hw_sleep_time((u32)(s0ix_counter - pmcdev->s0ix_counter));

if (s0ix_counter == pmcdev->s0ix_counter)
return true;

@@ -1233,12 +1230,16 @@ static __maybe_unused int pmc_core_resume(struct device *dev)
const struct pmc_bit_map **maps = pmcdev->map->lpm_sts;
int offset = pmcdev->map->lpm_status_offset;

if (!pmcdev->check_counters)
/* Check if the syspend used S0ix */
if (pm_suspend_via_firmware())
return 0;

if (!pmc_core_is_s0ix_failed(pmcdev))
return 0;

if (!warn_on_s0ix_failures)
return 0;

if (pmc_core_is_pc10_failed(pmcdev)) {
/* S0ix failed because of PC10 entry failure */
dev_info(dev, "CPU did not enter PC10!!! (PC10 cnt=0x%llx)\n",
4 changes: 2 additions & 2 deletions drivers/platform/x86/intel/pmc/core.h
Original file line number Diff line number Diff line change
@@ -16,6 +16,8 @@
#include <linux/bits.h>
#include <linux/platform_device.h>

#define SLP_S0_RES_COUNTER_MASK GENMASK(31, 0)

#define PMC_BASE_ADDR_DEFAULT 0xFE000000

/* Sunrise Point Power Management Controller PCI Device ID */
@@ -319,7 +321,6 @@ struct pmc_reg_map {
* @pmc_xram_read_bit: flag to indicate whether PMC XRAM shadow registers
* used to read MPHY PG and PLL status are available
* @mutex_lock: mutex to complete one transcation
* @check_counters: On resume, check if counters are getting incremented
* @pc10_counter: PC10 residency counter
* @s0ix_counter: S0ix residency (step adjusted)
* @num_lpm_modes: Count of enabled modes
@@ -338,7 +339,6 @@ struct pmc_dev {
int pmc_xram_read_bit;
struct mutex lock; /* generic mutex lock for PMC Core */

bool check_counters; /* Check for counter increments on resume */
u64 pc10_counter;
u64 s0ix_counter;
int num_lpm_modes;
8 changes: 8 additions & 0 deletions include/linux/suspend.h
Original file line number Diff line number Diff line change
@@ -68,6 +68,9 @@ struct suspend_stats {
int last_failed_errno;
int errno[REC_FAILED_NUM];
int last_failed_step;
u64 last_hw_sleep;
u64 total_hw_sleep;
u64 max_hw_sleep;
enum suspend_stat_step failed_steps[REC_FAILED_NUM];
};

@@ -489,6 +492,8 @@ void restore_processor_state(void);
extern int register_pm_notifier(struct notifier_block *nb);
extern int unregister_pm_notifier(struct notifier_block *nb);
extern void ksys_sync_helper(void);
extern void pm_report_hw_sleep_time(u64 t);
extern void pm_report_max_hw_sleep(u64 t);

#define pm_notifier(fn, pri) { \
static struct notifier_block fn##_nb = \
@@ -526,6 +531,9 @@ static inline int unregister_pm_notifier(struct notifier_block *nb)
return 0;
}

static inline void pm_report_hw_sleep_time(u64 t) {};
static inline void pm_report_max_hw_sleep(u64 t) {};

static inline void ksys_sync_helper(void) {}

#define pm_notifier(fn, pri) do { (void)(fn); } while (0)
Loading

0 comments on commit d3f2c40

Please sign in to comment.