Merge tag 'opp-updates-6.8' of git://git.kernel.org/pub/scm/linux/kernel/git/vireshk/pm into pm-opp
Merge OPP (Operating Performance Points) updates for 6.8 from Viresh Kumar: "- Fix _set_required_opps when opp is NULL (Bryan O'Donoghue). - ti: Use device_get_match_data() (Rob Herring). - Minor cleanups around OPP level and other parts and call dev_pm_opp_set_opp() recursively for required OPPs (Viresh Kumar)." * tag 'opp-updates-6.8' of git://git.kernel.org/pub/scm/linux/kernel/git/vireshk/pm: OPP: Rename 'rate_clk_single' OPP: Pass rounded rate to _set_opp() OPP: Relocate dev_pm_opp_sync_regulators() OPP: Move dev_pm_opp_icc_bw to internal opp.h OPP: Fix _set_required_opps when opp is NULL OPP: The level field is always of unsigned int type OPP: Check for invalid OPP in dev_pm_opp_find_level_ceil() OPP: Don't set OPP recursively for a parent genpd OPP: Call dev_pm_opp_set_opp() for required OPPs OPP: Use _set_opp_level() for single genpd case OPP: Level zero is valid opp: ti: Use device_get_match_data()
This commit is contained in:
@@ -201,7 +201,7 @@ EXPORT_SYMBOL_GPL(dev_pm_opp_get_freq_indexed);
|
||||
* @opp: opp for which level value has to be returned for
|
||||
*
|
||||
* Return: level read from device tree corresponding to the opp, else
|
||||
* return 0.
|
||||
* return U32_MAX.
|
||||
*/
|
||||
unsigned int dev_pm_opp_get_level(struct dev_pm_opp *opp)
|
||||
{
|
||||
@@ -221,7 +221,7 @@ EXPORT_SYMBOL_GPL(dev_pm_opp_get_level);
|
||||
* @index: index of the required opp
|
||||
*
|
||||
* Return: performance state read from device tree corresponding to the
|
||||
* required opp, else return 0.
|
||||
* required opp, else return U32_MAX.
|
||||
*/
|
||||
unsigned int dev_pm_opp_get_required_pstate(struct dev_pm_opp *opp,
|
||||
unsigned int index)
|
||||
@@ -808,6 +808,16 @@ struct dev_pm_opp *dev_pm_opp_find_level_ceil(struct device *dev,
|
||||
struct dev_pm_opp *opp;
|
||||
|
||||
opp = _find_key_ceil(dev, &temp, 0, true, _read_level, NULL);
|
||||
if (IS_ERR(opp))
|
||||
return opp;
|
||||
|
||||
/* False match */
|
||||
if (temp == OPP_LEVEL_UNSET) {
|
||||
dev_err(dev, "%s: OPP levels aren't available\n", __func__);
|
||||
dev_pm_opp_put(opp);
|
||||
return ERR_PTR(-ENODEV);
|
||||
}
|
||||
|
||||
*level = temp;
|
||||
return opp;
|
||||
}
|
||||
@@ -832,9 +842,14 @@ EXPORT_SYMBOL_GPL(dev_pm_opp_find_level_ceil);
|
||||
* use.
|
||||
*/
|
||||
struct dev_pm_opp *dev_pm_opp_find_level_floor(struct device *dev,
|
||||
unsigned long *level)
|
||||
unsigned int *level)
|
||||
{
|
||||
return _find_key_floor(dev, level, 0, true, _read_level, NULL);
|
||||
unsigned long temp = *level;
|
||||
struct dev_pm_opp *opp;
|
||||
|
||||
opp = _find_key_floor(dev, &temp, 0, true, _read_level, NULL);
|
||||
*level = temp;
|
||||
return opp;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(dev_pm_opp_find_level_floor);
|
||||
|
||||
@@ -948,7 +963,7 @@ _opp_config_clk_single(struct device *dev, struct opp_table *opp_table,
|
||||
dev_err(dev, "%s: failed to set clock rate: %d\n", __func__,
|
||||
ret);
|
||||
} else {
|
||||
opp_table->rate_clk_single = freq;
|
||||
opp_table->current_rate_single_clk = freq;
|
||||
}
|
||||
|
||||
return ret;
|
||||
@@ -1046,40 +1061,23 @@ static int _set_opp_bw(const struct opp_table *opp_table,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int _set_performance_state(struct device *dev, struct device *pd_dev,
|
||||
struct dev_pm_opp *opp, int i)
|
||||
/* 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)
|
||||
{
|
||||
unsigned int pstate = likely(opp) ? opp->required_opps[i]->level: 0;
|
||||
int ret;
|
||||
|
||||
if (!pd_dev)
|
||||
return 0;
|
||||
|
||||
ret = dev_pm_domain_set_performance_state(pd_dev, pstate);
|
||||
if (ret) {
|
||||
dev_err(dev, "Failed to set performance state of %s: %d (%d)\n",
|
||||
dev_name(pd_dev), pstate, ret);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int _opp_set_required_opps_generic(struct device *dev,
|
||||
struct opp_table *opp_table, struct dev_pm_opp *opp, bool scaling_down)
|
||||
{
|
||||
dev_err(dev, "setting required-opps isn't supported for non-genpd devices\n");
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
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;
|
||||
struct device **devs = opp_table->required_devs;
|
||||
struct dev_pm_opp *required_opp;
|
||||
int index, target, delta, ret;
|
||||
|
||||
if (!devs)
|
||||
return 0;
|
||||
|
||||
/* required-opps not fully initialized yet */
|
||||
if (lazy_linking_pending(opp_table))
|
||||
return -EBUSY;
|
||||
|
||||
/* Scaling up? Set required OPPs in normal order, else reverse */
|
||||
if (!scaling_down) {
|
||||
if (up) {
|
||||
index = 0;
|
||||
target = opp_table->required_opp_count;
|
||||
delta = 1;
|
||||
@@ -1090,9 +1088,13 @@ static int _opp_set_required_opps_genpd(struct device *dev,
|
||||
}
|
||||
|
||||
while (index != target) {
|
||||
ret = _set_performance_state(dev, genpd_virt_devs[index], opp, index);
|
||||
if (ret)
|
||||
return ret;
|
||||
if (devs[index]) {
|
||||
required_opp = opp ? opp->required_opps[index] : NULL;
|
||||
|
||||
ret = dev_pm_opp_set_opp(devs[index], required_opp);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
index += delta;
|
||||
}
|
||||
@@ -1100,34 +1102,6 @@ static int _opp_set_required_opps_genpd(struct device *dev,
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* 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 int _set_opp_level(struct device *dev, struct opp_table *opp_table,
|
||||
struct dev_pm_opp *opp)
|
||||
{
|
||||
@@ -1135,7 +1109,7 @@ static int _set_opp_level(struct device *dev, struct opp_table *opp_table,
|
||||
int ret = 0;
|
||||
|
||||
if (opp) {
|
||||
if (!opp->level)
|
||||
if (opp->level == OPP_LEVEL_UNSET)
|
||||
return 0;
|
||||
|
||||
level = opp->level;
|
||||
@@ -1378,12 +1352,12 @@ int dev_pm_opp_set_rate(struct device *dev, unsigned long target_freq)
|
||||
* value of the frequency. In such a case, do not abort but
|
||||
* configure the hardware to the desired frequency forcefully.
|
||||
*/
|
||||
forced = opp_table->rate_clk_single != target_freq;
|
||||
forced = opp_table->current_rate_single_clk != freq;
|
||||
}
|
||||
|
||||
ret = _set_opp(dev, opp_table, opp, &target_freq, forced);
|
||||
ret = _set_opp(dev, opp_table, opp, &freq, forced);
|
||||
|
||||
if (target_freq)
|
||||
if (freq)
|
||||
dev_pm_opp_put(opp);
|
||||
|
||||
put_opp_table:
|
||||
@@ -1867,6 +1841,8 @@ struct dev_pm_opp *_opp_allocate(struct opp_table *opp_table)
|
||||
|
||||
INIT_LIST_HEAD(&opp->node);
|
||||
|
||||
opp->level = OPP_LEVEL_UNSET;
|
||||
|
||||
return opp;
|
||||
}
|
||||
|
||||
@@ -2388,19 +2364,13 @@ static void _opp_detach_genpd(struct opp_table *opp_table)
|
||||
{
|
||||
int index;
|
||||
|
||||
if (!opp_table->genpd_virt_devs)
|
||||
return;
|
||||
|
||||
for (index = 0; index < opp_table->required_opp_count; index++) {
|
||||
if (!opp_table->genpd_virt_devs[index])
|
||||
if (!opp_table->required_devs[index])
|
||||
continue;
|
||||
|
||||
dev_pm_domain_detach(opp_table->genpd_virt_devs[index], false);
|
||||
opp_table->genpd_virt_devs[index] = NULL;
|
||||
dev_pm_domain_detach(opp_table->required_devs[index], false);
|
||||
opp_table->required_devs[index] = NULL;
|
||||
}
|
||||
|
||||
kfree(opp_table->genpd_virt_devs);
|
||||
opp_table->genpd_virt_devs = NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -2427,14 +2397,20 @@ static int _opp_attach_genpd(struct opp_table *opp_table, struct device *dev,
|
||||
int index = 0, ret = -EINVAL;
|
||||
const char * const *name = names;
|
||||
|
||||
if (opp_table->genpd_virt_devs)
|
||||
return 0;
|
||||
if (!opp_table->required_devs) {
|
||||
dev_err(dev, "Required OPPs not available, can't attach genpd\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
opp_table->genpd_virt_devs = kcalloc(opp_table->required_opp_count,
|
||||
sizeof(*opp_table->genpd_virt_devs),
|
||||
GFP_KERNEL);
|
||||
if (!opp_table->genpd_virt_devs)
|
||||
return -ENOMEM;
|
||||
/* Genpd core takes care of propagation to parent genpd */
|
||||
if (opp_table->is_genpd) {
|
||||
dev_err(dev, "%s: Operation not supported for genpds\n", __func__);
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
/* Checking only the first one is enough ? */
|
||||
if (opp_table->required_devs[0])
|
||||
return 0;
|
||||
|
||||
while (*name) {
|
||||
if (index >= opp_table->required_opp_count) {
|
||||
@@ -2450,13 +2426,25 @@ static int _opp_attach_genpd(struct opp_table *opp_table, struct device *dev,
|
||||
goto err;
|
||||
}
|
||||
|
||||
opp_table->genpd_virt_devs[index] = virt_dev;
|
||||
/*
|
||||
* Add the virtual genpd device as a user of the OPP table, so
|
||||
* we can call dev_pm_opp_set_opp() on it directly.
|
||||
*
|
||||
* This will be automatically removed when the OPP table is
|
||||
* removed, don't need to handle that here.
|
||||
*/
|
||||
if (!_add_opp_dev(virt_dev, opp_table->required_opp_tables[index])) {
|
||||
ret = -ENOMEM;
|
||||
goto err;
|
||||
}
|
||||
|
||||
opp_table->required_devs[index] = virt_dev;
|
||||
index++;
|
||||
name++;
|
||||
}
|
||||
|
||||
if (virt_devs)
|
||||
*virt_devs = opp_table->genpd_virt_devs;
|
||||
*virt_devs = opp_table->required_devs;
|
||||
|
||||
return 0;
|
||||
|
||||
@@ -2466,10 +2454,50 @@ err:
|
||||
|
||||
}
|
||||
|
||||
static int _opp_set_required_devs(struct opp_table *opp_table,
|
||||
struct device *dev,
|
||||
struct device **required_devs)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (!opp_table->required_devs) {
|
||||
dev_err(dev, "Required OPPs not available, can't set required devs\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Another device that shares the OPP table has set the required devs ? */
|
||||
if (opp_table->required_devs[0])
|
||||
return 0;
|
||||
|
||||
for (i = 0; i < opp_table->required_opp_count; i++) {
|
||||
/* Genpd core takes care of propagation to parent genpd */
|
||||
if (required_devs[i] && opp_table->is_genpd &&
|
||||
opp_table->required_opp_tables[i]->is_genpd) {
|
||||
dev_err(dev, "%s: Operation not supported for genpds\n", __func__);
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
opp_table->required_devs[i] = required_devs[i];
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void _opp_put_required_devs(struct opp_table *opp_table)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < opp_table->required_opp_count; i++)
|
||||
opp_table->required_devs[i] = NULL;
|
||||
}
|
||||
|
||||
static void _opp_clear_config(struct opp_config_data *data)
|
||||
{
|
||||
if (data->flags & OPP_CONFIG_GENPD)
|
||||
if (data->flags & OPP_CONFIG_REQUIRED_DEVS)
|
||||
_opp_put_required_devs(data->opp_table);
|
||||
else if (data->flags & OPP_CONFIG_GENPD)
|
||||
_opp_detach_genpd(data->opp_table);
|
||||
|
||||
if (data->flags & OPP_CONFIG_REGULATOR)
|
||||
_opp_put_regulators(data->opp_table);
|
||||
if (data->flags & OPP_CONFIG_SUPPORTED_HW)
|
||||
@@ -2583,12 +2611,22 @@ int dev_pm_opp_set_config(struct device *dev, struct dev_pm_opp_config *config)
|
||||
|
||||
/* Attach genpds */
|
||||
if (config->genpd_names) {
|
||||
if (config->required_devs)
|
||||
goto err;
|
||||
|
||||
ret = _opp_attach_genpd(opp_table, dev, config->genpd_names,
|
||||
config->virt_devs);
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
data->flags |= OPP_CONFIG_GENPD;
|
||||
} else if (config->required_devs) {
|
||||
ret = _opp_set_required_devs(opp_table, dev,
|
||||
config->required_devs);
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
data->flags |= OPP_CONFIG_REQUIRED_DEVS;
|
||||
}
|
||||
|
||||
ret = xa_alloc(&opp_configs, &id, data, XA_LIMIT(1, INT_MAX),
|
||||
@@ -2974,6 +3012,47 @@ put_table:
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(dev_pm_opp_adjust_voltage);
|
||||
|
||||
/**
|
||||
* dev_pm_opp_sync_regulators() - Sync state of voltage regulators
|
||||
* @dev: device for which we do this operation
|
||||
*
|
||||
* Sync voltage state of the OPP table regulators.
|
||||
*
|
||||
* Return: 0 on success or a negative error value.
|
||||
*/
|
||||
int dev_pm_opp_sync_regulators(struct device *dev)
|
||||
{
|
||||
struct opp_table *opp_table;
|
||||
struct regulator *reg;
|
||||
int i, ret = 0;
|
||||
|
||||
/* Device may not have OPP table */
|
||||
opp_table = _find_opp_table(dev);
|
||||
if (IS_ERR(opp_table))
|
||||
return 0;
|
||||
|
||||
/* Regulator may not be required for the device */
|
||||
if (unlikely(!opp_table->regulators))
|
||||
goto put_table;
|
||||
|
||||
/* Nothing to sync if voltage wasn't changed */
|
||||
if (!opp_table->enabled)
|
||||
goto put_table;
|
||||
|
||||
for (i = 0; i < opp_table->regulator_count; i++) {
|
||||
reg = opp_table->regulators[i];
|
||||
ret = regulator_sync_voltage(reg);
|
||||
if (ret)
|
||||
break;
|
||||
}
|
||||
put_table:
|
||||
/* Drop reference taken by _find_opp_table() */
|
||||
dev_pm_opp_put_opp_table(opp_table);
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(dev_pm_opp_sync_regulators);
|
||||
|
||||
/**
|
||||
* dev_pm_opp_enable() - Enable a specific OPP
|
||||
* @dev: device for which we do this operation
|
||||
@@ -3097,44 +3176,3 @@ void dev_pm_opp_remove_table(struct device *dev)
|
||||
dev_pm_opp_put_opp_table(opp_table);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(dev_pm_opp_remove_table);
|
||||
|
||||
/**
|
||||
* dev_pm_opp_sync_regulators() - Sync state of voltage regulators
|
||||
* @dev: device for which we do this operation
|
||||
*
|
||||
* Sync voltage state of the OPP table regulators.
|
||||
*
|
||||
* Return: 0 on success or a negative error value.
|
||||
*/
|
||||
int dev_pm_opp_sync_regulators(struct device *dev)
|
||||
{
|
||||
struct opp_table *opp_table;
|
||||
struct regulator *reg;
|
||||
int i, ret = 0;
|
||||
|
||||
/* Device may not have OPP table */
|
||||
opp_table = _find_opp_table(dev);
|
||||
if (IS_ERR(opp_table))
|
||||
return 0;
|
||||
|
||||
/* Regulator may not be required for the device */
|
||||
if (unlikely(!opp_table->regulators))
|
||||
goto put_table;
|
||||
|
||||
/* Nothing to sync if voltage wasn't changed */
|
||||
if (!opp_table->enabled)
|
||||
goto put_table;
|
||||
|
||||
for (i = 0; i < opp_table->regulator_count; i++) {
|
||||
reg = opp_table->regulators[i];
|
||||
ret = regulator_sync_voltage(reg);
|
||||
if (ret)
|
||||
break;
|
||||
}
|
||||
put_table:
|
||||
/* Drop reference taken by _find_opp_table() */
|
||||
dev_pm_opp_put_opp_table(opp_table);
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(dev_pm_opp_sync_regulators);
|
||||
|
||||
@@ -165,7 +165,7 @@ static void _opp_table_alloc_required_tables(struct opp_table *opp_table,
|
||||
struct opp_table **required_opp_tables;
|
||||
struct device_node *required_np, *np;
|
||||
bool lazy = false;
|
||||
int count, i;
|
||||
int count, i, size;
|
||||
|
||||
/* Traversing the first OPP node is all we need */
|
||||
np = of_get_next_available_child(opp_np, NULL);
|
||||
@@ -179,12 +179,13 @@ static void _opp_table_alloc_required_tables(struct opp_table *opp_table,
|
||||
if (count <= 0)
|
||||
goto put_np;
|
||||
|
||||
required_opp_tables = kcalloc(count, sizeof(*required_opp_tables),
|
||||
GFP_KERNEL);
|
||||
size = sizeof(*required_opp_tables) + sizeof(*opp_table->required_devs);
|
||||
required_opp_tables = kcalloc(count, size, GFP_KERNEL);
|
||||
if (!required_opp_tables)
|
||||
goto put_np;
|
||||
|
||||
opp_table->required_opp_tables = required_opp_tables;
|
||||
opp_table->required_devs = (void *)(required_opp_tables + count);
|
||||
opp_table->required_opp_count = count;
|
||||
|
||||
for (i = 0; i < count; i++) {
|
||||
@@ -208,8 +209,6 @@ static void _opp_table_alloc_required_tables(struct opp_table *opp_table,
|
||||
mutex_lock(&opp_table_lock);
|
||||
list_add(&opp_table->lazy, &lazy_opp_tables);
|
||||
mutex_unlock(&opp_table_lock);
|
||||
} else {
|
||||
_update_set_required_opps(opp_table);
|
||||
}
|
||||
|
||||
goto put_np;
|
||||
@@ -296,7 +295,7 @@ void _of_clear_opp(struct opp_table *opp_table, struct dev_pm_opp *opp)
|
||||
of_node_put(opp->np);
|
||||
}
|
||||
|
||||
static int _link_required_opps(struct dev_pm_opp *opp,
|
||||
static int _link_required_opps(struct dev_pm_opp *opp, struct opp_table *opp_table,
|
||||
struct opp_table *required_table, int index)
|
||||
{
|
||||
struct device_node *np;
|
||||
@@ -314,6 +313,39 @@ static int _link_required_opps(struct dev_pm_opp *opp,
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
/*
|
||||
* There are two genpd (as required-opp) cases that we need to handle,
|
||||
* devices with a single genpd and ones with multiple genpds.
|
||||
*
|
||||
* The single genpd case requires special handling as we need to use the
|
||||
* same `dev` structure (instead of a virtual one provided by genpd
|
||||
* core) for setting the performance state.
|
||||
*
|
||||
* It doesn't make sense for a device's DT entry to have both
|
||||
* "opp-level" and single "required-opps" entry pointing to a genpd's
|
||||
* OPP, as that would make the OPP core call
|
||||
* dev_pm_domain_set_performance_state() for two different values for
|
||||
* the same device structure. Lets treat single genpd configuration as a
|
||||
* case where the OPP's level is directly available without required-opp
|
||||
* link in the DT.
|
||||
*
|
||||
* Just update the `level` with the right value, which
|
||||
* dev_pm_opp_set_opp() will take care of in the normal path itself.
|
||||
*
|
||||
* There is another case though, where a genpd's OPP table has
|
||||
* required-opps set to a parent genpd. The OPP core expects the user to
|
||||
* set the respective required `struct device` pointer via
|
||||
* dev_pm_opp_set_config().
|
||||
*/
|
||||
if (required_table->is_genpd && opp_table->required_opp_count == 1 &&
|
||||
!opp_table->required_devs[0]) {
|
||||
/* Genpd core takes care of propagation to parent genpd */
|
||||
if (!opp_table->is_genpd) {
|
||||
if (!WARN_ON(opp->level != OPP_LEVEL_UNSET))
|
||||
opp->level = opp->required_opps[0]->level;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -338,7 +370,7 @@ static int _of_opp_alloc_required_opps(struct opp_table *opp_table,
|
||||
if (IS_ERR_OR_NULL(required_table))
|
||||
continue;
|
||||
|
||||
ret = _link_required_opps(opp, required_table, i);
|
||||
ret = _link_required_opps(opp, opp_table, required_table, i);
|
||||
if (ret)
|
||||
goto free_required_opps;
|
||||
}
|
||||
@@ -359,7 +391,7 @@ static int lazy_link_required_opps(struct opp_table *opp_table,
|
||||
int ret;
|
||||
|
||||
list_for_each_entry(opp, &opp_table->opp_list, node) {
|
||||
ret = _link_required_opps(opp, new_table, index);
|
||||
ret = _link_required_opps(opp, opp_table, new_table, index);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
@@ -422,7 +454,6 @@ 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)
|
||||
@@ -1393,8 +1424,14 @@ int of_get_required_opp_performance_state(struct device_node *np, int index)
|
||||
|
||||
opp = _find_opp_of_np(opp_table, required_np);
|
||||
if (opp) {
|
||||
pstate = opp->level;
|
||||
if (opp->level == OPP_LEVEL_UNSET) {
|
||||
pr_err("%s: OPP levels aren't available for %pOF\n",
|
||||
__func__, np);
|
||||
} else {
|
||||
pstate = opp->level;
|
||||
}
|
||||
dev_pm_opp_put(opp);
|
||||
|
||||
}
|
||||
|
||||
dev_pm_opp_put_opp_table(opp_table);
|
||||
|
||||
@@ -35,6 +35,7 @@ extern struct list_head opp_tables;
|
||||
#define OPP_CONFIG_PROP_NAME BIT(3)
|
||||
#define OPP_CONFIG_SUPPORTED_HW BIT(4)
|
||||
#define OPP_CONFIG_GENPD BIT(5)
|
||||
#define OPP_CONFIG_REQUIRED_DEVS BIT(6)
|
||||
|
||||
/**
|
||||
* struct opp_config_data - data for set config operations
|
||||
@@ -49,6 +50,18 @@ struct opp_config_data {
|
||||
unsigned int flags;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct dev_pm_opp_icc_bw - Interconnect bandwidth values
|
||||
* @avg: Average bandwidth corresponding to this OPP (in icc units)
|
||||
* @peak: Peak bandwidth corresponding to this OPP (in icc units)
|
||||
*
|
||||
* This structure stores the bandwidth values for a single interconnect path.
|
||||
*/
|
||||
struct dev_pm_opp_icc_bw {
|
||||
u32 avg;
|
||||
u32 peak;
|
||||
};
|
||||
|
||||
/*
|
||||
* Internal data structure organization with the OPP layer library is as
|
||||
* follows:
|
||||
@@ -157,12 +170,12 @@ enum opp_table_access {
|
||||
* @clock_latency_ns_max: Max clock latency in nanoseconds.
|
||||
* @parsed_static_opps: Count of devices for which OPPs are initialized from DT.
|
||||
* @shared_opp: OPP is shared between multiple devices.
|
||||
* @rate_clk_single: Currently configured frequency for single clk.
|
||||
* @current_rate_single_clk: Currently configured frequency for single clk.
|
||||
* @current_opp: Currently configured OPP for the table.
|
||||
* @suspend_opp: Pointer to OPP to be used during device suspend.
|
||||
* @genpd_virt_devs: List of virtual devices for multiple genpd support.
|
||||
* @required_opp_tables: List of device OPP tables that are required by OPPs in
|
||||
* this table.
|
||||
* @required_devs: List of devices for required OPP tables.
|
||||
* @required_opp_count: Number of required devices.
|
||||
* @supported_hw: Array of version number to support.
|
||||
* @supported_hw_count: Number of elements in supported_hw array.
|
||||
@@ -180,7 +193,6 @@ enum opp_table_access {
|
||||
* @path_count: Number of interconnect paths
|
||||
* @enabled: Set to true if the device's resources are enabled/configured.
|
||||
* @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.
|
||||
*
|
||||
@@ -207,12 +219,12 @@ struct opp_table {
|
||||
|
||||
unsigned int parsed_static_opps;
|
||||
enum opp_table_access shared_opp;
|
||||
unsigned long rate_clk_single;
|
||||
unsigned long current_rate_single_clk;
|
||||
struct dev_pm_opp *current_opp;
|
||||
struct dev_pm_opp *suspend_opp;
|
||||
|
||||
struct device **genpd_virt_devs;
|
||||
struct opp_table **required_opp_tables;
|
||||
struct device **required_devs;
|
||||
unsigned int required_opp_count;
|
||||
|
||||
unsigned int *supported_hw;
|
||||
@@ -229,8 +241,6 @@ struct opp_table {
|
||||
unsigned int path_count;
|
||||
bool enabled;
|
||||
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;
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
#include <linux/of.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/pm_opp.h>
|
||||
#include <linux/property.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
@@ -373,23 +374,15 @@ static int ti_opp_supply_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct device *cpu_dev = get_cpu_device(0);
|
||||
const struct of_device_id *match;
|
||||
const struct ti_opp_supply_of_data *of_data;
|
||||
int ret = 0;
|
||||
|
||||
match = of_match_device(ti_opp_supply_of_match, dev);
|
||||
if (!match) {
|
||||
/* We do not expect this to happen */
|
||||
dev_err(dev, "%s: Unable to match device\n", __func__);
|
||||
return -ENODEV;
|
||||
}
|
||||
if (!match->data) {
|
||||
of_data = device_get_match_data(dev);
|
||||
if (!of_data) {
|
||||
/* Again, unlikely.. but mistakes do happen */
|
||||
dev_err(dev, "%s: Bad data in match\n", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
of_data = match->data;
|
||||
|
||||
dev_set_drvdata(dev, (void *)of_data);
|
||||
|
||||
/* If we need optimized voltage */
|
||||
|
||||
@@ -45,18 +45,6 @@ struct dev_pm_opp_supply {
|
||||
unsigned long u_watt;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct dev_pm_opp_icc_bw - Interconnect bandwidth values
|
||||
* @avg: Average bandwidth corresponding to this OPP (in icc units)
|
||||
* @peak: Peak bandwidth corresponding to this OPP (in icc units)
|
||||
*
|
||||
* This structure stores the bandwidth values for a single interconnect path.
|
||||
*/
|
||||
struct dev_pm_opp_icc_bw {
|
||||
u32 avg;
|
||||
u32 peak;
|
||||
};
|
||||
|
||||
typedef int (*config_regulators_t)(struct device *dev,
|
||||
struct dev_pm_opp *old_opp, struct dev_pm_opp *new_opp,
|
||||
struct regulator **regulators, unsigned int count);
|
||||
@@ -74,8 +62,10 @@ typedef int (*config_clks_t)(struct device *dev, struct opp_table *opp_table,
|
||||
* @supported_hw_count: Number of elements in the array.
|
||||
* @regulator_names: Array of pointers to the names of the regulator, NULL terminated.
|
||||
* @genpd_names: Null terminated array of pointers containing names of genpd to
|
||||
* attach.
|
||||
* @virt_devs: Pointer to return the array of virtual devices.
|
||||
* attach. Mutually exclusive with required_devs.
|
||||
* @virt_devs: Pointer to return the array of genpd virtual devices. Mutually
|
||||
* exclusive with required_devs.
|
||||
* @required_devs: Required OPP devices. Mutually exclusive with genpd_names/virt_devs.
|
||||
*
|
||||
* This structure contains platform specific OPP configurations for the device.
|
||||
*/
|
||||
@@ -90,11 +80,15 @@ struct dev_pm_opp_config {
|
||||
const char * const *regulator_names;
|
||||
const char * const *genpd_names;
|
||||
struct device ***virt_devs;
|
||||
struct device **required_devs;
|
||||
};
|
||||
|
||||
#define OPP_LEVEL_UNSET U32_MAX
|
||||
|
||||
/**
|
||||
* struct dev_pm_opp_data - The data to use to initialize an OPP.
|
||||
* @level: The performance level for the OPP.
|
||||
* @level: The performance level for the OPP. Set level to OPP_LEVEL_UNSET if
|
||||
* level field isn't used.
|
||||
* @freq: The clock rate in Hz for the OPP.
|
||||
* @u_volt: The voltage in uV for the OPP.
|
||||
*/
|
||||
@@ -157,7 +151,7 @@ struct dev_pm_opp *dev_pm_opp_find_level_ceil(struct device *dev,
|
||||
unsigned int *level);
|
||||
|
||||
struct dev_pm_opp *dev_pm_opp_find_level_floor(struct device *dev,
|
||||
unsigned long *level);
|
||||
unsigned int *level);
|
||||
|
||||
struct dev_pm_opp *dev_pm_opp_find_bw_ceil(struct device *dev,
|
||||
unsigned int *bw, int index);
|
||||
@@ -324,7 +318,7 @@ static inline struct dev_pm_opp *dev_pm_opp_find_level_ceil(struct device *dev,
|
||||
}
|
||||
|
||||
static inline struct dev_pm_opp *dev_pm_opp_find_level_floor(struct device *dev,
|
||||
unsigned long *level)
|
||||
unsigned int *level)
|
||||
{
|
||||
return ERR_PTR(-EOPNOTSUPP);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user