Skip to content

Commit d422555

Browse files
committed
Merge tag 'pm-5.16-rc1-2' of git://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm
Pull more power management updates from Rafael Wysocki: "These fix three intel_pstate driver regressions, fix locking in the core code suspending and resuming devices during system PM transitions, fix the handling of cpuidle drivers based on runtime PM during system-wide suspend, fix two issues in the operating performance points (OPP) framework and resource-managed helpers to it. Specifics: - Fix two intel_pstate driver regressions related to the HWP interrupt handling added recently (Srinivas Pandruvada). - Fix intel_pstate driver regression introduced during the 5.11 cycle and causing HWP desired performance to be mishandled in some cases when switching driver modes and during system suspend and shutdown (Rafael Wysocki). - Fix system-wide device suspend and resume locking to avoid deadlocks when device objects are deleted during a system-wide PM transition (Rafael Wysocki). - Modify system-wide suspend of devices to prevent cpuidle drivers based on runtime PM from misbehaving during the "no IRQ" phase of it (Ulf Hansson). - Fix return value of _opp_add_static_v2() helper (YueHaibing). - Fix required-opp handle count (Pavankumar Kondeti). - Add resource managed OPP helpers, update dev_pm_opp_attach_genpd(), update their devfreq users, and make minor DT binding change (Dmitry Osipenko)" * tag 'pm-5.16-rc1-2' of git://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm: PM: sleep: Avoid calling put_device() under dpm_list_mtx cpufreq: intel_pstate: Clear HWP Status during HWP Interrupt enable cpufreq: intel_pstate: Fix unchecked MSR 0x773 access cpufreq: intel_pstate: Clear HWP desired on suspend/shutdown and offline PM: sleep: Fix runtime PM based cpuidle support dt-bindings: opp: Allow multi-worded OPP entry name opp: Fix return in _opp_add_static_v2() PM / devfreq: tegra30: Check whether clk_round_rate() returns zero rate PM / devfreq: tegra30: Use resource-managed helpers PM / devfreq: Add devm_devfreq_add_governor() opp: Add more resource-managed variants of dev_pm_opp_of_add_table() opp: Change type of dev_pm_opp_attach_genpd(names) argument opp: Fix required-opps phandle array count check
2 parents 285fc3d + dcc0b6f commit d422555

File tree

9 files changed

+229
-108
lines changed

9 files changed

+229
-108
lines changed

Documentation/devicetree/bindings/opp/opp-v2-base.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ properties:
3333
type: boolean
3434

3535
patternProperties:
36-
'^opp-?[0-9]+$':
36+
'^opp(-?[0-9]+)*$':
3737
type: object
3838
description:
3939
One or more OPP nodes describing voltage-current-frequency combinations.

drivers/base/power/main.c

Lines changed: 58 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -710,6 +710,7 @@ static void dpm_noirq_resume_devices(pm_message_t state)
710710
dev = to_device(dpm_noirq_list.next);
711711
get_device(dev);
712712
list_move_tail(&dev->power.entry, &dpm_late_early_list);
713+
713714
mutex_unlock(&dpm_list_mtx);
714715

715716
if (!is_async(dev)) {
@@ -724,8 +725,9 @@ static void dpm_noirq_resume_devices(pm_message_t state)
724725
}
725726
}
726727

727-
mutex_lock(&dpm_list_mtx);
728728
put_device(dev);
729+
730+
mutex_lock(&dpm_list_mtx);
729731
}
730732
mutex_unlock(&dpm_list_mtx);
731733
async_synchronize_full();
@@ -849,6 +851,7 @@ void dpm_resume_early(pm_message_t state)
849851
dev = to_device(dpm_late_early_list.next);
850852
get_device(dev);
851853
list_move_tail(&dev->power.entry, &dpm_suspended_list);
854+
852855
mutex_unlock(&dpm_list_mtx);
853856

854857
if (!is_async(dev)) {
@@ -862,8 +865,10 @@ void dpm_resume_early(pm_message_t state)
862865
pm_dev_err(dev, state, " early", error);
863866
}
864867
}
865-
mutex_lock(&dpm_list_mtx);
868+
866869
put_device(dev);
870+
871+
mutex_lock(&dpm_list_mtx);
867872
}
868873
mutex_unlock(&dpm_list_mtx);
869874
async_synchronize_full();
@@ -1026,7 +1031,12 @@ void dpm_resume(pm_message_t state)
10261031
}
10271032
if (!list_empty(&dev->power.entry))
10281033
list_move_tail(&dev->power.entry, &dpm_prepared_list);
1034+
1035+
mutex_unlock(&dpm_list_mtx);
1036+
10291037
put_device(dev);
1038+
1039+
mutex_lock(&dpm_list_mtx);
10301040
}
10311041
mutex_unlock(&dpm_list_mtx);
10321042
async_synchronize_full();
@@ -1104,14 +1114,16 @@ void dpm_complete(pm_message_t state)
11041114
get_device(dev);
11051115
dev->power.is_prepared = false;
11061116
list_move(&dev->power.entry, &list);
1117+
11071118
mutex_unlock(&dpm_list_mtx);
11081119

11091120
trace_device_pm_callback_start(dev, "", state.event);
11101121
device_complete(dev, state);
11111122
trace_device_pm_callback_end(dev, 0);
11121123

1113-
mutex_lock(&dpm_list_mtx);
11141124
put_device(dev);
1125+
1126+
mutex_lock(&dpm_list_mtx);
11151127
}
11161128
list_splice(&list, &dpm_list);
11171129
mutex_unlock(&dpm_list_mtx);
@@ -1296,17 +1308,21 @@ static int dpm_noirq_suspend_devices(pm_message_t state)
12961308
error = device_suspend_noirq(dev);
12971309

12981310
mutex_lock(&dpm_list_mtx);
1311+
12991312
if (error) {
13001313
pm_dev_err(dev, state, " noirq", error);
13011314
dpm_save_failed_dev(dev_name(dev));
1302-
put_device(dev);
1303-
break;
1304-
}
1305-
if (!list_empty(&dev->power.entry))
1315+
} else if (!list_empty(&dev->power.entry)) {
13061316
list_move(&dev->power.entry, &dpm_noirq_list);
1317+
}
1318+
1319+
mutex_unlock(&dpm_list_mtx);
1320+
13071321
put_device(dev);
13081322

1309-
if (async_error)
1323+
mutex_lock(&dpm_list_mtx);
1324+
1325+
if (error || async_error)
13101326
break;
13111327
}
13121328
mutex_unlock(&dpm_list_mtx);
@@ -1463,6 +1479,7 @@ int dpm_suspend_late(pm_message_t state)
14631479
int error = 0;
14641480

14651481
trace_suspend_resume(TPS("dpm_suspend_late"), state.event, true);
1482+
wake_up_all_idle_cpus();
14661483
mutex_lock(&dpm_list_mtx);
14671484
pm_transition = state;
14681485
async_error = 0;
@@ -1471,23 +1488,28 @@ int dpm_suspend_late(pm_message_t state)
14711488
struct device *dev = to_device(dpm_suspended_list.prev);
14721489

14731490
get_device(dev);
1491+
14741492
mutex_unlock(&dpm_list_mtx);
14751493

14761494
error = device_suspend_late(dev);
14771495

14781496
mutex_lock(&dpm_list_mtx);
1497+
14791498
if (!list_empty(&dev->power.entry))
14801499
list_move(&dev->power.entry, &dpm_late_early_list);
14811500

14821501
if (error) {
14831502
pm_dev_err(dev, state, " late", error);
14841503
dpm_save_failed_dev(dev_name(dev));
1485-
put_device(dev);
1486-
break;
14871504
}
1505+
1506+
mutex_unlock(&dpm_list_mtx);
1507+
14881508
put_device(dev);
14891509

1490-
if (async_error)
1510+
mutex_lock(&dpm_list_mtx);
1511+
1512+
if (error || async_error)
14911513
break;
14921514
}
14931515
mutex_unlock(&dpm_list_mtx);
@@ -1747,21 +1769,27 @@ int dpm_suspend(pm_message_t state)
17471769
struct device *dev = to_device(dpm_prepared_list.prev);
17481770

17491771
get_device(dev);
1772+
17501773
mutex_unlock(&dpm_list_mtx);
17511774

17521775
error = device_suspend(dev);
17531776

17541777
mutex_lock(&dpm_list_mtx);
1778+
17551779
if (error) {
17561780
pm_dev_err(dev, state, "", error);
17571781
dpm_save_failed_dev(dev_name(dev));
1758-
put_device(dev);
1759-
break;
1760-
}
1761-
if (!list_empty(&dev->power.entry))
1782+
} else if (!list_empty(&dev->power.entry)) {
17621783
list_move(&dev->power.entry, &dpm_suspended_list);
1784+
}
1785+
1786+
mutex_unlock(&dpm_list_mtx);
1787+
17631788
put_device(dev);
1764-
if (async_error)
1789+
1790+
mutex_lock(&dpm_list_mtx);
1791+
1792+
if (error || async_error)
17651793
break;
17661794
}
17671795
mutex_unlock(&dpm_list_mtx);
@@ -1878,28 +1906,31 @@ int dpm_prepare(pm_message_t state)
18781906
struct device *dev = to_device(dpm_list.next);
18791907

18801908
get_device(dev);
1909+
18811910
mutex_unlock(&dpm_list_mtx);
18821911

18831912
trace_device_pm_callback_start(dev, "", state.event);
18841913
error = device_prepare(dev, state);
18851914
trace_device_pm_callback_end(dev, error);
18861915

18871916
mutex_lock(&dpm_list_mtx);
1888-
if (error) {
1889-
if (error == -EAGAIN) {
1890-
put_device(dev);
1891-
error = 0;
1892-
continue;
1893-
}
1917+
1918+
if (!error) {
1919+
dev->power.is_prepared = true;
1920+
if (!list_empty(&dev->power.entry))
1921+
list_move_tail(&dev->power.entry, &dpm_prepared_list);
1922+
} else if (error == -EAGAIN) {
1923+
error = 0;
1924+
} else {
18941925
dev_info(dev, "not prepared for power transition: code %d\n",
18951926
error);
1896-
put_device(dev);
1897-
break;
18981927
}
1899-
dev->power.is_prepared = true;
1900-
if (!list_empty(&dev->power.entry))
1901-
list_move_tail(&dev->power.entry, &dpm_prepared_list);
1928+
1929+
mutex_unlock(&dpm_list_mtx);
1930+
19021931
put_device(dev);
1932+
1933+
mutex_lock(&dpm_list_mtx);
19031934
}
19041935
mutex_unlock(&dpm_list_mtx);
19051936
trace_suspend_resume(TPS("dpm_prepare"), state.event, false);

drivers/cpufreq/intel_pstate.c

Lines changed: 34 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1006,9 +1006,16 @@ static void intel_pstate_hwp_offline(struct cpudata *cpu)
10061006
*/
10071007
value &= ~GENMASK_ULL(31, 24);
10081008
value |= HWP_ENERGY_PERF_PREFERENCE(cpu->epp_cached);
1009-
WRITE_ONCE(cpu->hwp_req_cached, value);
10101009
}
10111010

1011+
/*
1012+
* Clear the desired perf field in the cached HWP request value to
1013+
* prevent nonzero desired values from being leaked into the active
1014+
* mode.
1015+
*/
1016+
value &= ~HWP_DESIRED_PERF(~0L);
1017+
WRITE_ONCE(cpu->hwp_req_cached, value);
1018+
10121019
value &= ~GENMASK_ULL(31, 0);
10131020
min_perf = HWP_LOWEST_PERF(READ_ONCE(cpu->hwp_cap_cached));
10141021

@@ -1620,6 +1627,9 @@ static void intel_pstate_disable_hwp_interrupt(struct cpudata *cpudata)
16201627
{
16211628
unsigned long flags;
16221629

1630+
if (!boot_cpu_has(X86_FEATURE_HWP_NOTIFY))
1631+
return;
1632+
16231633
/* wrmsrl_on_cpu has to be outside spinlock as this can result in IPC */
16241634
wrmsrl_on_cpu(cpudata->cpu, MSR_HWP_INTERRUPT, 0x00);
16251635

@@ -1642,6 +1652,7 @@ static void intel_pstate_enable_hwp_interrupt(struct cpudata *cpudata)
16421652

16431653
/* wrmsrl_on_cpu has to be outside spinlock as this can result in IPC */
16441654
wrmsrl_on_cpu(cpudata->cpu, MSR_HWP_INTERRUPT, 0x01);
1655+
wrmsrl_on_cpu(cpudata->cpu, MSR_HWP_STATUS, 0);
16451656
}
16461657
}
16471658

@@ -3003,6 +3014,27 @@ static int intel_cpufreq_cpu_exit(struct cpufreq_policy *policy)
30033014
return intel_pstate_cpu_exit(policy);
30043015
}
30053016

3017+
static int intel_cpufreq_suspend(struct cpufreq_policy *policy)
3018+
{
3019+
intel_pstate_suspend(policy);
3020+
3021+
if (hwp_active) {
3022+
struct cpudata *cpu = all_cpu_data[policy->cpu];
3023+
u64 value = READ_ONCE(cpu->hwp_req_cached);
3024+
3025+
/*
3026+
* Clear the desired perf field in MSR_HWP_REQUEST in case
3027+
* intel_cpufreq_adjust_perf() is in use and the last value
3028+
* written by it may not be suitable.
3029+
*/
3030+
value &= ~HWP_DESIRED_PERF(~0L);
3031+
wrmsrl_on_cpu(cpu->cpu, MSR_HWP_REQUEST, value);
3032+
WRITE_ONCE(cpu->hwp_req_cached, value);
3033+
}
3034+
3035+
return 0;
3036+
}
3037+
30063038
static struct cpufreq_driver intel_cpufreq = {
30073039
.flags = CPUFREQ_CONST_LOOPS,
30083040
.verify = intel_cpufreq_verify_policy,
@@ -3012,7 +3044,7 @@ static struct cpufreq_driver intel_cpufreq = {
30123044
.exit = intel_cpufreq_cpu_exit,
30133045
.offline = intel_cpufreq_cpu_offline,
30143046
.online = intel_pstate_cpu_online,
3015-
.suspend = intel_pstate_suspend,
3047+
.suspend = intel_cpufreq_suspend,
30163048
.resume = intel_pstate_resume,
30173049
.update_limits = intel_pstate_update_limits,
30183050
.name = "intel_cpufreq",

drivers/devfreq/devfreq.c

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1301,6 +1301,32 @@ int devfreq_add_governor(struct devfreq_governor *governor)
13011301
}
13021302
EXPORT_SYMBOL(devfreq_add_governor);
13031303

1304+
static void devm_devfreq_remove_governor(void *governor)
1305+
{
1306+
WARN_ON(devfreq_remove_governor(governor));
1307+
}
1308+
1309+
/**
1310+
* devm_devfreq_add_governor() - Add devfreq governor
1311+
* @dev: device which adds devfreq governor
1312+
* @governor: the devfreq governor to be added
1313+
*
1314+
* This is a resource-managed variant of devfreq_add_governor().
1315+
*/
1316+
int devm_devfreq_add_governor(struct device *dev,
1317+
struct devfreq_governor *governor)
1318+
{
1319+
int err;
1320+
1321+
err = devfreq_add_governor(governor);
1322+
if (err)
1323+
return err;
1324+
1325+
return devm_add_action_or_reset(dev, devm_devfreq_remove_governor,
1326+
governor);
1327+
}
1328+
EXPORT_SYMBOL(devm_devfreq_add_governor);
1329+
13041330
/**
13051331
* devfreq_remove_governor() - Remove devfreq feature from a device.
13061332
* @governor: the devfreq governor to be removed

drivers/devfreq/governor.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,9 @@ void devfreq_update_interval(struct devfreq *devfreq, unsigned int *delay);
8484
int devfreq_add_governor(struct devfreq_governor *governor);
8585
int devfreq_remove_governor(struct devfreq_governor *governor);
8686

87+
int devm_devfreq_add_governor(struct device *dev,
88+
struct devfreq_governor *governor);
89+
8790
int devfreq_update_status(struct devfreq *devfreq, unsigned long freq);
8891
int devfreq_update_target(struct devfreq *devfreq, unsigned long freq);
8992

0 commit comments

Comments
 (0)