Commit de1c2722 authored by Rafael J. Wysocki's avatar Rafael J. Wysocki

Merge branches 'pm-em' and 'pm-docs'

Merge Enery Model update and a power management documentation update for
6.10:

 - Make the Samsung exynos-asv driver update the Energy Model after
   adjusting voltage on top of some preliminary changes of the OPP and
   Enery Model generic code (Lukasz Luba).

 - Remove a reference to a function that has been dropped from the power
   management documentation (Bjorn Helgaas).

* pm-em:
  soc: samsung: exynos-asv: Update Energy Model after adjusting voltage
  PM: EM: Add em_dev_update_chip_binning()
  PM: EM: Refactor em_adjust_new_capacity()
  OPP: OF: Export dev_opp_pm_calc_power() for usage from EM

* pm-docs:
  Documentation: PM: Update platform_pci_wakeup_init() reference
...@@ -333,7 +333,7 @@ struct pci_dev. ...@@ -333,7 +333,7 @@ struct pci_dev.
The PCI subsystem's first task related to device power management is to The PCI subsystem's first task related to device power management is to
prepare the device for power management and initialize the fields of struct prepare the device for power management and initialize the fields of struct
pci_dev used for this purpose. This happens in two functions defined in pci_dev used for this purpose. This happens in two functions defined in
drivers/pci/pci.c, pci_pm_init() and platform_pci_wakeup_init(). drivers/pci/, pci_pm_init() and pci_acpi_setup().
The first of these functions checks if the device supports native PCI PM The first of these functions checks if the device supports native PCI PM
and if that's the case the offset of its power management capability structure and if that's the case the offset of its power management capability structure
......
...@@ -1494,20 +1494,26 @@ _get_dt_power(struct device *dev, unsigned long *uW, unsigned long *kHz) ...@@ -1494,20 +1494,26 @@ _get_dt_power(struct device *dev, unsigned long *uW, unsigned long *kHz)
return 0; return 0;
} }
/* /**
* Callback function provided to the Energy Model framework upon registration. * dev_pm_opp_calc_power() - Calculate power value for device with EM
* @dev : Device for which an Energy Model has to be registered
* @uW : New power value that is calculated
* @kHz : Frequency for which the new power is calculated
*
* This computes the power estimated by @dev at @kHz if it is the frequency * This computes the power estimated by @dev at @kHz if it is the frequency
* of an existing OPP, or at the frequency of the first OPP above @kHz otherwise * of an existing OPP, or at the frequency of the first OPP above @kHz otherwise
* (see dev_pm_opp_find_freq_ceil()). This function updates @kHz to the ceiled * (see dev_pm_opp_find_freq_ceil()). This function updates @kHz to the ceiled
* frequency and @uW to the associated power. The power is estimated as * frequency and @uW to the associated power. The power is estimated as
* P = C * V^2 * f with C being the device's capacitance and V and f * P = C * V^2 * f with C being the device's capacitance and V and f
* respectively the voltage and frequency of the OPP. * respectively the voltage and frequency of the OPP.
* It is also used as a callback function provided to the Energy Model
* framework upon registration.
* *
* Returns -EINVAL if the power calculation failed because of missing * Returns -EINVAL if the power calculation failed because of missing
* parameters, 0 otherwise. * parameters, 0 otherwise.
*/ */
static int __maybe_unused _get_power(struct device *dev, unsigned long *uW, int dev_pm_opp_calc_power(struct device *dev, unsigned long *uW,
unsigned long *kHz) unsigned long *kHz)
{ {
struct dev_pm_opp *opp; struct dev_pm_opp *opp;
struct device_node *np; struct device_node *np;
...@@ -1544,6 +1550,7 @@ static int __maybe_unused _get_power(struct device *dev, unsigned long *uW, ...@@ -1544,6 +1550,7 @@ static int __maybe_unused _get_power(struct device *dev, unsigned long *uW,
return 0; return 0;
} }
EXPORT_SYMBOL_GPL(dev_pm_opp_calc_power);
static bool _of_has_opp_microwatt_property(struct device *dev) static bool _of_has_opp_microwatt_property(struct device *dev)
{ {
...@@ -1619,7 +1626,7 @@ int dev_pm_opp_of_register_em(struct device *dev, struct cpumask *cpus) ...@@ -1619,7 +1626,7 @@ int dev_pm_opp_of_register_em(struct device *dev, struct cpumask *cpus)
goto failed; goto failed;
} }
EM_SET_ACTIVE_POWER_CB(em_cb, _get_power); EM_SET_ACTIVE_POWER_CB(em_cb, dev_pm_opp_calc_power);
register_em: register_em:
ret = em_dev_register_perf_domain(dev, nr_opp, &em_cb, cpus, true); ret = em_dev_register_perf_domain(dev, nr_opp, &em_cb, cpus, true);
......
...@@ -11,6 +11,7 @@ ...@@ -11,6 +11,7 @@
#include <linux/cpu.h> #include <linux/cpu.h>
#include <linux/device.h> #include <linux/device.h>
#include <linux/energy_model.h>
#include <linux/errno.h> #include <linux/errno.h>
#include <linux/of.h> #include <linux/of.h>
#include <linux/pm_opp.h> #include <linux/pm_opp.h>
...@@ -97,9 +98,16 @@ static int exynos_asv_update_opps(struct exynos_asv *asv) ...@@ -97,9 +98,16 @@ static int exynos_asv_update_opps(struct exynos_asv *asv)
last_opp_table = opp_table; last_opp_table = opp_table;
ret = exynos_asv_update_cpu_opps(asv, cpu); ret = exynos_asv_update_cpu_opps(asv, cpu);
if (ret < 0) if (!ret) {
/*
* Update EM power values since OPP
* voltage values may have changed.
*/
em_dev_update_chip_binning(cpu);
} else {
dev_err(asv->dev, "Couldn't udate OPPs for cpu%d\n", dev_err(asv->dev, "Couldn't udate OPPs for cpu%d\n",
cpuid); cpuid);
}
} }
dev_pm_opp_put_opp_table(opp_table); dev_pm_opp_put_opp_table(opp_table);
......
...@@ -172,6 +172,7 @@ struct em_perf_table __rcu *em_table_alloc(struct em_perf_domain *pd); ...@@ -172,6 +172,7 @@ struct em_perf_table __rcu *em_table_alloc(struct em_perf_domain *pd);
void em_table_free(struct em_perf_table __rcu *table); void em_table_free(struct em_perf_table __rcu *table);
int em_dev_compute_costs(struct device *dev, struct em_perf_state *table, int em_dev_compute_costs(struct device *dev, struct em_perf_state *table,
int nr_states); int nr_states);
int em_dev_update_chip_binning(struct device *dev);
/** /**
* em_pd_get_efficient_state() - Get an efficient performance state from the EM * em_pd_get_efficient_state() - Get an efficient performance state from the EM
...@@ -386,6 +387,10 @@ int em_dev_compute_costs(struct device *dev, struct em_perf_state *table, ...@@ -386,6 +387,10 @@ int em_dev_compute_costs(struct device *dev, struct em_perf_state *table,
{ {
return -EINVAL; return -EINVAL;
} }
static inline int em_dev_update_chip_binning(struct device *dev)
{
return -EINVAL;
}
#endif #endif
#endif #endif
...@@ -476,6 +476,8 @@ struct device_node *dev_pm_opp_get_of_node(struct dev_pm_opp *opp); ...@@ -476,6 +476,8 @@ struct device_node *dev_pm_opp_get_of_node(struct dev_pm_opp *opp);
int of_get_required_opp_performance_state(struct device_node *np, int index); int of_get_required_opp_performance_state(struct device_node *np, int index);
int dev_pm_opp_of_find_icc_paths(struct device *dev, struct opp_table *opp_table); int dev_pm_opp_of_find_icc_paths(struct device *dev, struct opp_table *opp_table);
int dev_pm_opp_of_register_em(struct device *dev, struct cpumask *cpus); int dev_pm_opp_of_register_em(struct device *dev, struct cpumask *cpus);
int dev_pm_opp_calc_power(struct device *dev, unsigned long *uW,
unsigned long *kHz);
static inline void dev_pm_opp_of_unregister_em(struct device *dev) static inline void dev_pm_opp_of_unregister_em(struct device *dev)
{ {
em_dev_unregister_perf_domain(dev); em_dev_unregister_perf_domain(dev);
...@@ -539,6 +541,12 @@ static inline void dev_pm_opp_of_unregister_em(struct device *dev) ...@@ -539,6 +541,12 @@ static inline void dev_pm_opp_of_unregister_em(struct device *dev)
{ {
} }
static inline int dev_pm_opp_calc_power(struct device *dev, unsigned long *uW,
unsigned long *kHz)
{
return -EOPNOTSUPP;
}
static inline int of_get_required_opp_performance_state(struct device_node *np, int index) static inline int of_get_required_opp_performance_state(struct device_node *np, int index)
{ {
return -EOPNOTSUPP; return -EOPNOTSUPP;
......
...@@ -674,23 +674,15 @@ void em_dev_unregister_perf_domain(struct device *dev) ...@@ -674,23 +674,15 @@ void em_dev_unregister_perf_domain(struct device *dev)
} }
EXPORT_SYMBOL_GPL(em_dev_unregister_perf_domain); EXPORT_SYMBOL_GPL(em_dev_unregister_perf_domain);
/* static struct em_perf_table __rcu *em_table_dup(struct em_perf_domain *pd)
* Adjustment of CPU performance values after boot, when all CPUs capacites
* are correctly calculated.
*/
static void em_adjust_new_capacity(struct device *dev,
struct em_perf_domain *pd,
u64 max_cap)
{ {
struct em_perf_table __rcu *em_table; struct em_perf_table __rcu *em_table;
struct em_perf_state *ps, *new_ps; struct em_perf_state *ps, *new_ps;
int ret, ps_size; int ps_size;
em_table = em_table_alloc(pd); em_table = em_table_alloc(pd);
if (!em_table) { if (!em_table)
dev_warn(dev, "EM: allocation failed\n"); return NULL;
return;
}
new_ps = em_table->state; new_ps = em_table->state;
...@@ -702,24 +694,52 @@ static void em_adjust_new_capacity(struct device *dev, ...@@ -702,24 +694,52 @@ static void em_adjust_new_capacity(struct device *dev,
rcu_read_unlock(); rcu_read_unlock();
em_init_performance(dev, pd, new_ps, pd->nr_perf_states); return em_table;
ret = em_compute_costs(dev, new_ps, NULL, pd->nr_perf_states, }
static int em_recalc_and_update(struct device *dev, struct em_perf_domain *pd,
struct em_perf_table __rcu *em_table)
{
int ret;
ret = em_compute_costs(dev, em_table->state, NULL, pd->nr_perf_states,
pd->flags); pd->flags);
if (ret) { if (ret)
dev_warn(dev, "EM: compute costs failed\n"); goto free_em_table;
return;
}
ret = em_dev_update_perf_domain(dev, em_table); ret = em_dev_update_perf_domain(dev, em_table);
if (ret) if (ret)
dev_warn(dev, "EM: update failed %d\n", ret); goto free_em_table;
/* /*
* This is one-time-update, so give up the ownership in this updater. * This is one-time-update, so give up the ownership in this updater.
* The EM framework has incremented the usage counter and from now * The EM framework has incremented the usage counter and from now
* will keep the reference (then free the memory when needed). * will keep the reference (then free the memory when needed).
*/ */
free_em_table:
em_table_free(em_table); em_table_free(em_table);
return ret;
}
/*
* Adjustment of CPU performance values after boot, when all CPUs capacites
* are correctly calculated.
*/
static void em_adjust_new_capacity(struct device *dev,
struct em_perf_domain *pd,
u64 max_cap)
{
struct em_perf_table __rcu *em_table;
em_table = em_table_dup(pd);
if (!em_table) {
dev_warn(dev, "EM: allocation failed\n");
return;
}
em_init_performance(dev, pd, em_table->state, pd->nr_perf_states);
em_recalc_and_update(dev, pd, em_table);
} }
static void em_check_capacity_update(void) static void em_check_capacity_update(void)
...@@ -788,3 +808,51 @@ static void em_update_workfn(struct work_struct *work) ...@@ -788,3 +808,51 @@ static void em_update_workfn(struct work_struct *work)
{ {
em_check_capacity_update(); em_check_capacity_update();
} }
/**
* em_dev_update_chip_binning() - Update Energy Model after the new voltage
* information is present in the OPPs.
* @dev : Device for which the Energy Model has to be updated.
*
* This function allows to update easily the EM with new values available in
* the OPP framework and DT. It can be used after the chip has been properly
* verified by device drivers and the voltages adjusted for the 'chip binning'.
*/
int em_dev_update_chip_binning(struct device *dev)
{
struct em_perf_table __rcu *em_table;
struct em_perf_domain *pd;
int i, ret;
if (IS_ERR_OR_NULL(dev))
return -EINVAL;
pd = em_pd_get(dev);
if (!pd) {
dev_warn(dev, "Couldn't find Energy Model\n");
return -EINVAL;
}
em_table = em_table_dup(pd);
if (!em_table) {
dev_warn(dev, "EM: allocation failed\n");
return -ENOMEM;
}
/* Update power values which might change due to new voltage in OPPs */
for (i = 0; i < pd->nr_perf_states; i++) {
unsigned long freq = em_table->state[i].frequency;
unsigned long power;
ret = dev_pm_opp_calc_power(dev, &power, &freq);
if (ret) {
em_table_free(em_table);
return ret;
}
em_table->state[i].power = power;
}
return em_recalc_and_update(dev, pd, em_table);
}
EXPORT_SYMBOL_GPL(em_dev_update_chip_binning);
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment