Merge branches 'pm-opp' and 'pm-cpufreq'

Merge operating performance points (OPP) framework updates for and
intel_pstate driver fixes for 5.16-rc1.

* pm-opp:
  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

* pm-cpufreq:
  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
This commit is contained in:
Rafael J. Wysocki 2021-11-10 14:06:51 +01:00
commit dcc0b6f2e6
8 changed files with 171 additions and 81 deletions

View File

@ -33,7 +33,7 @@ properties:
type: boolean type: boolean
patternProperties: patternProperties:
'^opp-?[0-9]+$': '^opp(-?[0-9]+)*$':
type: object type: object
description: description:
One or more OPP nodes describing voltage-current-frequency combinations. One or more OPP nodes describing voltage-current-frequency combinations.

View File

@ -1006,9 +1006,16 @@ static void intel_pstate_hwp_offline(struct cpudata *cpu)
*/ */
value &= ~GENMASK_ULL(31, 24); value &= ~GENMASK_ULL(31, 24);
value |= HWP_ENERGY_PERF_PREFERENCE(cpu->epp_cached); value |= HWP_ENERGY_PERF_PREFERENCE(cpu->epp_cached);
WRITE_ONCE(cpu->hwp_req_cached, value);
} }
/*
* Clear the desired perf field in the cached HWP request value to
* prevent nonzero desired values from being leaked into the active
* mode.
*/
value &= ~HWP_DESIRED_PERF(~0L);
WRITE_ONCE(cpu->hwp_req_cached, value);
value &= ~GENMASK_ULL(31, 0); value &= ~GENMASK_ULL(31, 0);
min_perf = HWP_LOWEST_PERF(READ_ONCE(cpu->hwp_cap_cached)); min_perf = HWP_LOWEST_PERF(READ_ONCE(cpu->hwp_cap_cached));
@ -1620,6 +1627,9 @@ static void intel_pstate_disable_hwp_interrupt(struct cpudata *cpudata)
{ {
unsigned long flags; unsigned long flags;
if (!boot_cpu_has(X86_FEATURE_HWP_NOTIFY))
return;
/* wrmsrl_on_cpu has to be outside spinlock as this can result in IPC */ /* wrmsrl_on_cpu has to be outside spinlock as this can result in IPC */
wrmsrl_on_cpu(cpudata->cpu, MSR_HWP_INTERRUPT, 0x00); wrmsrl_on_cpu(cpudata->cpu, MSR_HWP_INTERRUPT, 0x00);
@ -1642,6 +1652,7 @@ static void intel_pstate_enable_hwp_interrupt(struct cpudata *cpudata)
/* wrmsrl_on_cpu has to be outside spinlock as this can result in IPC */ /* wrmsrl_on_cpu has to be outside spinlock as this can result in IPC */
wrmsrl_on_cpu(cpudata->cpu, MSR_HWP_INTERRUPT, 0x01); wrmsrl_on_cpu(cpudata->cpu, MSR_HWP_INTERRUPT, 0x01);
wrmsrl_on_cpu(cpudata->cpu, MSR_HWP_STATUS, 0);
} }
} }
@ -3003,6 +3014,27 @@ static int intel_cpufreq_cpu_exit(struct cpufreq_policy *policy)
return intel_pstate_cpu_exit(policy); return intel_pstate_cpu_exit(policy);
} }
static int intel_cpufreq_suspend(struct cpufreq_policy *policy)
{
intel_pstate_suspend(policy);
if (hwp_active) {
struct cpudata *cpu = all_cpu_data[policy->cpu];
u64 value = READ_ONCE(cpu->hwp_req_cached);
/*
* Clear the desired perf field in MSR_HWP_REQUEST in case
* intel_cpufreq_adjust_perf() is in use and the last value
* written by it may not be suitable.
*/
value &= ~HWP_DESIRED_PERF(~0L);
wrmsrl_on_cpu(cpu->cpu, MSR_HWP_REQUEST, value);
WRITE_ONCE(cpu->hwp_req_cached, value);
}
return 0;
}
static struct cpufreq_driver intel_cpufreq = { static struct cpufreq_driver intel_cpufreq = {
.flags = CPUFREQ_CONST_LOOPS, .flags = CPUFREQ_CONST_LOOPS,
.verify = intel_cpufreq_verify_policy, .verify = intel_cpufreq_verify_policy,
@ -3012,7 +3044,7 @@ static struct cpufreq_driver intel_cpufreq = {
.exit = intel_cpufreq_cpu_exit, .exit = intel_cpufreq_cpu_exit,
.offline = intel_cpufreq_cpu_offline, .offline = intel_cpufreq_cpu_offline,
.online = intel_pstate_cpu_online, .online = intel_pstate_cpu_online,
.suspend = intel_pstate_suspend, .suspend = intel_cpufreq_suspend,
.resume = intel_pstate_resume, .resume = intel_pstate_resume,
.update_limits = intel_pstate_update_limits, .update_limits = intel_pstate_update_limits,
.name = "intel_cpufreq", .name = "intel_cpufreq",

View File

@ -1301,6 +1301,32 @@ err_out:
} }
EXPORT_SYMBOL(devfreq_add_governor); EXPORT_SYMBOL(devfreq_add_governor);
static void devm_devfreq_remove_governor(void *governor)
{
WARN_ON(devfreq_remove_governor(governor));
}
/**
* devm_devfreq_add_governor() - Add devfreq governor
* @dev: device which adds devfreq governor
* @governor: the devfreq governor to be added
*
* This is a resource-managed variant of devfreq_add_governor().
*/
int devm_devfreq_add_governor(struct device *dev,
struct devfreq_governor *governor)
{
int err;
err = devfreq_add_governor(governor);
if (err)
return err;
return devm_add_action_or_reset(dev, devm_devfreq_remove_governor,
governor);
}
EXPORT_SYMBOL(devm_devfreq_add_governor);
/** /**
* devfreq_remove_governor() - Remove devfreq feature from a device. * devfreq_remove_governor() - Remove devfreq feature from a device.
* @governor: the devfreq governor to be removed * @governor: the devfreq governor to be removed

View File

@ -84,6 +84,9 @@ void devfreq_update_interval(struct devfreq *devfreq, unsigned int *delay);
int devfreq_add_governor(struct devfreq_governor *governor); int devfreq_add_governor(struct devfreq_governor *governor);
int devfreq_remove_governor(struct devfreq_governor *governor); int devfreq_remove_governor(struct devfreq_governor *governor);
int devm_devfreq_add_governor(struct device *dev,
struct devfreq_governor *governor);
int devfreq_update_status(struct devfreq *devfreq, unsigned long freq); int devfreq_update_status(struct devfreq *devfreq, unsigned long freq);
int devfreq_update_target(struct devfreq *devfreq, unsigned long freq); int devfreq_update_target(struct devfreq *devfreq, unsigned long freq);

View File

@ -178,7 +178,6 @@ struct tegra_devfreq_soc_data {
struct tegra_devfreq { struct tegra_devfreq {
struct devfreq *devfreq; struct devfreq *devfreq;
struct opp_table *opp_table;
struct reset_control *reset; struct reset_control *reset;
struct clk *clock; struct clk *clock;
@ -789,6 +788,39 @@ static struct devfreq_governor tegra_devfreq_governor = {
.event_handler = tegra_governor_event_handler, .event_handler = tegra_governor_event_handler,
}; };
static void devm_tegra_devfreq_deinit_hw(void *data)
{
struct tegra_devfreq *tegra = data;
reset_control_reset(tegra->reset);
clk_disable_unprepare(tegra->clock);
}
static int devm_tegra_devfreq_init_hw(struct device *dev,
struct tegra_devfreq *tegra)
{
int err;
err = clk_prepare_enable(tegra->clock);
if (err) {
dev_err(dev, "Failed to prepare and enable ACTMON clock\n");
return err;
}
err = devm_add_action_or_reset(dev, devm_tegra_devfreq_deinit_hw,
tegra);
if (err)
return err;
err = reset_control_reset(tegra->reset);
if (err) {
dev_err(dev, "Failed to reset hardware: %d\n", err);
return err;
}
return err;
}
static int tegra_devfreq_probe(struct platform_device *pdev) static int tegra_devfreq_probe(struct platform_device *pdev)
{ {
u32 hw_version = BIT(tegra_sku_info.soc_speedo_id); u32 hw_version = BIT(tegra_sku_info.soc_speedo_id);
@ -842,38 +874,26 @@ static int tegra_devfreq_probe(struct platform_device *pdev)
return err; return err;
} }
tegra->opp_table = dev_pm_opp_set_supported_hw(&pdev->dev, err = devm_pm_opp_set_supported_hw(&pdev->dev, &hw_version, 1);
&hw_version, 1);
err = PTR_ERR_OR_ZERO(tegra->opp_table);
if (err) { if (err) {
dev_err(&pdev->dev, "Failed to set supported HW: %d\n", err); dev_err(&pdev->dev, "Failed to set supported HW: %d\n", err);
return err; return err;
} }
err = dev_pm_opp_of_add_table_noclk(&pdev->dev, 0); err = devm_pm_opp_of_add_table_noclk(&pdev->dev, 0);
if (err) { if (err) {
dev_err(&pdev->dev, "Failed to add OPP table: %d\n", err); dev_err(&pdev->dev, "Failed to add OPP table: %d\n", err);
goto put_hw; return err;
} }
err = clk_prepare_enable(tegra->clock); err = devm_tegra_devfreq_init_hw(&pdev->dev, tegra);
if (err) { if (err)
dev_err(&pdev->dev, return err;
"Failed to prepare and enable ACTMON clock\n");
goto remove_table;
}
err = reset_control_reset(tegra->reset);
if (err) {
dev_err(&pdev->dev, "Failed to reset hardware: %d\n", err);
goto disable_clk;
}
rate = clk_round_rate(tegra->emc_clock, ULONG_MAX); rate = clk_round_rate(tegra->emc_clock, ULONG_MAX);
if (rate < 0) { if (rate <= 0) {
dev_err(&pdev->dev, "Failed to round clock rate: %ld\n", rate); dev_err(&pdev->dev, "Failed to round clock rate: %ld\n", rate);
err = rate; return rate ?: -EINVAL;
goto disable_clk;
} }
tegra->max_freq = rate / KHZ; tegra->max_freq = rate / KHZ;
@ -892,52 +912,18 @@ static int tegra_devfreq_probe(struct platform_device *pdev)
INIT_DELAYED_WORK(&tegra->cpufreq_update_work, INIT_DELAYED_WORK(&tegra->cpufreq_update_work,
tegra_actmon_delayed_update); tegra_actmon_delayed_update);
err = devfreq_add_governor(&tegra_devfreq_governor); err = devm_devfreq_add_governor(&pdev->dev, &tegra_devfreq_governor);
if (err) { if (err) {
dev_err(&pdev->dev, "Failed to add governor: %d\n", err); dev_err(&pdev->dev, "Failed to add governor: %d\n", err);
goto remove_opps; return err;
} }
tegra_devfreq_profile.initial_freq = clk_get_rate(tegra->emc_clock); tegra_devfreq_profile.initial_freq = clk_get_rate(tegra->emc_clock);
devfreq = devfreq_add_device(&pdev->dev, &tegra_devfreq_profile, devfreq = devm_devfreq_add_device(&pdev->dev, &tegra_devfreq_profile,
"tegra_actmon", NULL); "tegra_actmon", NULL);
if (IS_ERR(devfreq)) { if (IS_ERR(devfreq))
err = PTR_ERR(devfreq); return PTR_ERR(devfreq);
goto remove_governor;
}
return 0;
remove_governor:
devfreq_remove_governor(&tegra_devfreq_governor);
remove_opps:
dev_pm_opp_remove_all_dynamic(&pdev->dev);
reset_control_reset(tegra->reset);
disable_clk:
clk_disable_unprepare(tegra->clock);
remove_table:
dev_pm_opp_of_remove_table(&pdev->dev);
put_hw:
dev_pm_opp_put_supported_hw(tegra->opp_table);
return err;
}
static int tegra_devfreq_remove(struct platform_device *pdev)
{
struct tegra_devfreq *tegra = platform_get_drvdata(pdev);
devfreq_remove_device(tegra->devfreq);
devfreq_remove_governor(&tegra_devfreq_governor);
reset_control_reset(tegra->reset);
clk_disable_unprepare(tegra->clock);
dev_pm_opp_of_remove_table(&pdev->dev);
dev_pm_opp_put_supported_hw(tegra->opp_table);
return 0; return 0;
} }
@ -967,7 +953,6 @@ MODULE_DEVICE_TABLE(of, tegra_devfreq_of_match);
static struct platform_driver tegra_devfreq_driver = { static struct platform_driver tegra_devfreq_driver = {
.probe = tegra_devfreq_probe, .probe = tegra_devfreq_probe,
.remove = tegra_devfreq_remove,
.driver = { .driver = {
.name = "tegra-devfreq", .name = "tegra-devfreq",
.of_match_table = tegra_devfreq_of_match, .of_match_table = tegra_devfreq_of_match,

View File

@ -2348,12 +2348,12 @@ static void _opp_detach_genpd(struct opp_table *opp_table)
* "required-opps" are added in DT. * "required-opps" are added in DT.
*/ */
struct opp_table *dev_pm_opp_attach_genpd(struct device *dev, struct opp_table *dev_pm_opp_attach_genpd(struct device *dev,
const char **names, struct device ***virt_devs) const char * const *names, struct device ***virt_devs)
{ {
struct opp_table *opp_table; struct opp_table *opp_table;
struct device *virt_dev; struct device *virt_dev;
int index = 0, ret = -EINVAL; int index = 0, ret = -EINVAL;
const char **name = names; const char * const *name = names;
opp_table = _add_opp_table(dev, false); opp_table = _add_opp_table(dev, false);
if (IS_ERR(opp_table)) if (IS_ERR(opp_table))
@ -2457,7 +2457,7 @@ static void devm_pm_opp_detach_genpd(void *data)
* *
* Return: 0 on success and errorno otherwise. * Return: 0 on success and errorno otherwise.
*/ */
int devm_pm_opp_attach_genpd(struct device *dev, const char **names, int devm_pm_opp_attach_genpd(struct device *dev, const char * const *names,
struct device ***virt_devs) struct device ***virt_devs)
{ {
struct opp_table *opp_table; struct opp_table *opp_table;

View File

@ -170,7 +170,7 @@ static void _opp_table_alloc_required_tables(struct opp_table *opp_table,
} }
count = of_count_phandle_with_args(np, "required-opps", NULL); count = of_count_phandle_with_args(np, "required-opps", NULL);
if (!count) if (count <= 0)
goto put_np; goto put_np;
required_opp_tables = kcalloc(count, sizeof(*required_opp_tables), required_opp_tables = kcalloc(count, sizeof(*required_opp_tables),
@ -921,7 +921,7 @@ free_required_opps:
free_opp: free_opp:
_opp_free(new_opp); _opp_free(new_opp);
return ERR_PTR(ret); return ret ? ERR_PTR(ret) : NULL;
} }
/* Initializes OPP tables based on new bindings */ /* Initializes OPP tables based on new bindings */
@ -1081,6 +1081,17 @@ static void devm_pm_opp_of_table_release(void *data)
dev_pm_opp_of_remove_table(data); dev_pm_opp_of_remove_table(data);
} }
static int _devm_of_add_table_indexed(struct device *dev, int index, bool getclk)
{
int ret;
ret = _of_add_table_indexed(dev, index, getclk);
if (ret)
return ret;
return devm_add_action_or_reset(dev, devm_pm_opp_of_table_release, dev);
}
/** /**
* devm_pm_opp_of_add_table() - Initialize opp table from device tree * devm_pm_opp_of_add_table() - Initialize opp table from device tree
* @dev: device pointer used to lookup OPP table. * @dev: device pointer used to lookup OPP table.
@ -1102,13 +1113,7 @@ static void devm_pm_opp_of_table_release(void *data)
*/ */
int devm_pm_opp_of_add_table(struct device *dev) int devm_pm_opp_of_add_table(struct device *dev)
{ {
int ret; return _devm_of_add_table_indexed(dev, 0, true);
ret = dev_pm_opp_of_add_table(dev);
if (ret)
return ret;
return devm_add_action_or_reset(dev, devm_pm_opp_of_table_release, dev);
} }
EXPORT_SYMBOL_GPL(devm_pm_opp_of_add_table); EXPORT_SYMBOL_GPL(devm_pm_opp_of_add_table);
@ -1151,6 +1156,19 @@ int dev_pm_opp_of_add_table_indexed(struct device *dev, int index)
} }
EXPORT_SYMBOL_GPL(dev_pm_opp_of_add_table_indexed); EXPORT_SYMBOL_GPL(dev_pm_opp_of_add_table_indexed);
/**
* devm_pm_opp_of_add_table_indexed() - Initialize indexed opp table from device tree
* @dev: device pointer used to lookup OPP table.
* @index: Index number.
*
* This is a resource-managed variant of dev_pm_opp_of_add_table_indexed().
*/
int devm_pm_opp_of_add_table_indexed(struct device *dev, int index)
{
return _devm_of_add_table_indexed(dev, index, true);
}
EXPORT_SYMBOL_GPL(devm_pm_opp_of_add_table_indexed);
/** /**
* dev_pm_opp_of_add_table_noclk() - Initialize indexed opp table from device * dev_pm_opp_of_add_table_noclk() - Initialize indexed opp table from device
* tree without getting clk for device. * tree without getting clk for device.
@ -1169,6 +1187,20 @@ int dev_pm_opp_of_add_table_noclk(struct device *dev, int index)
} }
EXPORT_SYMBOL_GPL(dev_pm_opp_of_add_table_noclk); EXPORT_SYMBOL_GPL(dev_pm_opp_of_add_table_noclk);
/**
* devm_pm_opp_of_add_table_noclk() - Initialize indexed opp table from device
* tree without getting clk for device.
* @dev: device pointer used to lookup OPP table.
* @index: Index number.
*
* This is a resource-managed variant of dev_pm_opp_of_add_table_noclk().
*/
int devm_pm_opp_of_add_table_noclk(struct device *dev, int index)
{
return _devm_of_add_table_indexed(dev, index, false);
}
EXPORT_SYMBOL_GPL(devm_pm_opp_of_add_table_noclk);
/* CPU device specific helpers */ /* CPU device specific helpers */
/** /**

View File

@ -156,9 +156,9 @@ int devm_pm_opp_set_clkname(struct device *dev, const char *name);
struct opp_table *dev_pm_opp_register_set_opp_helper(struct device *dev, int (*set_opp)(struct dev_pm_set_opp_data *data)); struct opp_table *dev_pm_opp_register_set_opp_helper(struct device *dev, int (*set_opp)(struct dev_pm_set_opp_data *data));
void dev_pm_opp_unregister_set_opp_helper(struct opp_table *opp_table); void dev_pm_opp_unregister_set_opp_helper(struct opp_table *opp_table);
int devm_pm_opp_register_set_opp_helper(struct device *dev, int (*set_opp)(struct dev_pm_set_opp_data *data)); int devm_pm_opp_register_set_opp_helper(struct device *dev, int (*set_opp)(struct dev_pm_set_opp_data *data));
struct opp_table *dev_pm_opp_attach_genpd(struct device *dev, const char **names, struct device ***virt_devs); struct opp_table *dev_pm_opp_attach_genpd(struct device *dev, const char * const *names, struct device ***virt_devs);
void dev_pm_opp_detach_genpd(struct opp_table *opp_table); void dev_pm_opp_detach_genpd(struct opp_table *opp_table);
int devm_pm_opp_attach_genpd(struct device *dev, const char **names, struct device ***virt_devs); int devm_pm_opp_attach_genpd(struct device *dev, const char * const *names, struct device ***virt_devs);
struct dev_pm_opp *dev_pm_opp_xlate_required_opp(struct opp_table *src_table, struct opp_table *dst_table, struct dev_pm_opp *src_opp); struct dev_pm_opp *dev_pm_opp_xlate_required_opp(struct opp_table *src_table, struct opp_table *dst_table, struct dev_pm_opp *src_opp);
int dev_pm_opp_xlate_performance_state(struct opp_table *src_table, struct opp_table *dst_table, unsigned int pstate); int dev_pm_opp_xlate_performance_state(struct opp_table *src_table, struct opp_table *dst_table, unsigned int pstate);
int dev_pm_opp_set_rate(struct device *dev, unsigned long target_freq); int dev_pm_opp_set_rate(struct device *dev, unsigned long target_freq);
@ -376,7 +376,7 @@ static inline int devm_pm_opp_set_clkname(struct device *dev, const char *name)
return -EOPNOTSUPP; return -EOPNOTSUPP;
} }
static inline struct opp_table *dev_pm_opp_attach_genpd(struct device *dev, const char **names, struct device ***virt_devs) static inline struct opp_table *dev_pm_opp_attach_genpd(struct device *dev, const char * const *names, struct device ***virt_devs)
{ {
return ERR_PTR(-EOPNOTSUPP); return ERR_PTR(-EOPNOTSUPP);
} }
@ -384,7 +384,7 @@ static inline struct opp_table *dev_pm_opp_attach_genpd(struct device *dev, cons
static inline void dev_pm_opp_detach_genpd(struct opp_table *opp_table) {} static inline void dev_pm_opp_detach_genpd(struct opp_table *opp_table) {}
static inline int devm_pm_opp_attach_genpd(struct device *dev, static inline int devm_pm_opp_attach_genpd(struct device *dev,
const char **names, const char * const *names,
struct device ***virt_devs) struct device ***virt_devs)
{ {
return -EOPNOTSUPP; return -EOPNOTSUPP;
@ -439,7 +439,9 @@ static inline int dev_pm_opp_sync_regulators(struct device *dev)
#if defined(CONFIG_PM_OPP) && defined(CONFIG_OF) #if defined(CONFIG_PM_OPP) && defined(CONFIG_OF)
int dev_pm_opp_of_add_table(struct device *dev); int dev_pm_opp_of_add_table(struct device *dev);
int dev_pm_opp_of_add_table_indexed(struct device *dev, int index); int dev_pm_opp_of_add_table_indexed(struct device *dev, int index);
int devm_pm_opp_of_add_table_indexed(struct device *dev, int index);
int dev_pm_opp_of_add_table_noclk(struct device *dev, int index); int dev_pm_opp_of_add_table_noclk(struct device *dev, int index);
int devm_pm_opp_of_add_table_noclk(struct device *dev, int index);
void dev_pm_opp_of_remove_table(struct device *dev); void dev_pm_opp_of_remove_table(struct device *dev);
int devm_pm_opp_of_add_table(struct device *dev); int devm_pm_opp_of_add_table(struct device *dev);
int dev_pm_opp_of_cpumask_add_table(const struct cpumask *cpumask); int dev_pm_opp_of_cpumask_add_table(const struct cpumask *cpumask);
@ -465,11 +467,21 @@ static inline int dev_pm_opp_of_add_table_indexed(struct device *dev, int index)
return -EOPNOTSUPP; return -EOPNOTSUPP;
} }
static inline int devm_pm_opp_of_add_table_indexed(struct device *dev, int index)
{
return -EOPNOTSUPP;
}
static inline int dev_pm_opp_of_add_table_noclk(struct device *dev, int index) static inline int dev_pm_opp_of_add_table_noclk(struct device *dev, int index)
{ {
return -EOPNOTSUPP; return -EOPNOTSUPP;
} }
static inline int devm_pm_opp_of_add_table_noclk(struct device *dev, int index)
{
return -EOPNOTSUPP;
}
static inline void dev_pm_opp_of_remove_table(struct device *dev) static inline void dev_pm_opp_of_remove_table(struct device *dev)
{ {
} }