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.
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
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
and if that's the case the offset of its power management capability structure
......
......@@ -1494,19 +1494,25 @@ _get_dt_power(struct device *dev, unsigned long *uW, unsigned long *kHz)
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
* 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
* 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
* 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
* 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)
{
struct dev_pm_opp *opp;
......@@ -1544,6 +1550,7 @@ static int __maybe_unused _get_power(struct device *dev, unsigned long *uW,
return 0;
}
EXPORT_SYMBOL_GPL(dev_pm_opp_calc_power);
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)
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:
ret = em_dev_register_perf_domain(dev, nr_opp, &em_cb, cpus, true);
......
......@@ -11,6 +11,7 @@
#include <linux/cpu.h>
#include <linux/device.h>
#include <linux/energy_model.h>
#include <linux/errno.h>
#include <linux/of.h>
#include <linux/pm_opp.h>
......@@ -97,10 +98,17 @@ static int exynos_asv_update_opps(struct exynos_asv *asv)
last_opp_table = opp_table;
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",
cpuid);
}
}
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);
void em_table_free(struct em_perf_table __rcu *table);
int em_dev_compute_costs(struct device *dev, struct em_perf_state *table,
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
......@@ -386,6 +387,10 @@ int em_dev_compute_costs(struct device *dev, struct em_perf_state *table,
{
return -EINVAL;
}
static inline int em_dev_update_chip_binning(struct device *dev)
{
return -EINVAL;
}
#endif
#endif
......@@ -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 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_calc_power(struct device *dev, unsigned long *uW,
unsigned long *kHz);
static inline void dev_pm_opp_of_unregister_em(struct device *dev)
{
em_dev_unregister_perf_domain(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)
{
return -EOPNOTSUPP;
......
......@@ -674,23 +674,15 @@ void em_dev_unregister_perf_domain(struct device *dev)
}
EXPORT_SYMBOL_GPL(em_dev_unregister_perf_domain);
/*
* 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)
static struct em_perf_table __rcu *em_table_dup(struct em_perf_domain *pd)
{
struct em_perf_table __rcu *em_table;
struct em_perf_state *ps, *new_ps;
int ret, ps_size;
int ps_size;
em_table = em_table_alloc(pd);
if (!em_table) {
dev_warn(dev, "EM: allocation failed\n");
return;
}
if (!em_table)
return NULL;
new_ps = em_table->state;
......@@ -702,24 +694,52 @@ static void em_adjust_new_capacity(struct device *dev,
rcu_read_unlock();
em_init_performance(dev, pd, new_ps, pd->nr_perf_states);
ret = em_compute_costs(dev, new_ps, NULL, pd->nr_perf_states,
return em_table;
}
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);
if (ret) {
dev_warn(dev, "EM: compute costs failed\n");
return;
}
if (ret)
goto free_em_table;
ret = em_dev_update_perf_domain(dev, em_table);
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.
* The EM framework has incremented the usage counter and from now
* will keep the reference (then free the memory when needed).
*/
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)
......@@ -788,3 +808,51 @@ static void em_update_workfn(struct work_struct *work)
{
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