Merge branch 'pm-opp'
* pm-opp: opp: Don't parse icc paths unnecessarily opp: Remove bandwidth votes when target_freq is zero opp: core: add regulators enable and disable opp: Reorder the code for !target_freq case opp: Expose bandwidth information via debugfs cpufreq: dt: Add support for interconnect bandwidth scaling opp: Update the bandwidth on OPP frequency changes opp: Add sanity checks in _read_opp_key() opp: Add support for parsing interconnect bandwidth interconnect: Remove unused module exit code from core interconnect: Disallow interconnect core to be built as a module interconnect: Add of_icc_get_by_index() helper function OPP: Add helpers for reading the binding properties dt-bindings: opp: Introduce opp-peak-kBps and opp-avg-kBps bindings
This commit is contained in:
commit
4c277e2f85
@ -83,9 +83,14 @@ properties.
|
|||||||
|
|
||||||
Required properties:
|
Required properties:
|
||||||
- opp-hz: Frequency in Hz, expressed as a 64-bit big-endian integer. This is a
|
- opp-hz: Frequency in Hz, expressed as a 64-bit big-endian integer. This is a
|
||||||
required property for all device nodes but devices like power domains. The
|
required property for all device nodes, unless another "required" property to
|
||||||
power domain nodes must have another (implementation dependent) property which
|
uniquely identify the OPP nodes exists. Devices like power domains must have
|
||||||
uniquely identifies the OPP nodes.
|
another (implementation dependent) property.
|
||||||
|
|
||||||
|
- opp-peak-kBps: Peak bandwidth in kilobytes per second, expressed as an array
|
||||||
|
of 32-bit big-endian integers. Each element of the array represents the
|
||||||
|
peak bandwidth value of each interconnect path. The number of elements should
|
||||||
|
match the number of interconnect paths.
|
||||||
|
|
||||||
Optional properties:
|
Optional properties:
|
||||||
- opp-microvolt: voltage in micro Volts.
|
- opp-microvolt: voltage in micro Volts.
|
||||||
@ -132,6 +137,12 @@ Optional properties:
|
|||||||
- opp-level: A value representing the performance level of the device,
|
- opp-level: A value representing the performance level of the device,
|
||||||
expressed as a 32-bit integer.
|
expressed as a 32-bit integer.
|
||||||
|
|
||||||
|
- opp-avg-kBps: Average bandwidth in kilobytes per second, expressed as an array
|
||||||
|
of 32-bit big-endian integers. Each element of the array represents the
|
||||||
|
average bandwidth value of each interconnect path. The number of elements
|
||||||
|
should match the number of interconnect paths. This property is only
|
||||||
|
meaningful in OPP tables where opp-peak-kBps is present.
|
||||||
|
|
||||||
- clock-latency-ns: Specifies the maximum possible transition latency (in
|
- clock-latency-ns: Specifies the maximum possible transition latency (in
|
||||||
nanoseconds) for switching to this OPP from any other OPP.
|
nanoseconds) for switching to this OPP from any other OPP.
|
||||||
|
|
||||||
|
@ -41,3 +41,7 @@ Temperature
|
|||||||
Pressure
|
Pressure
|
||||||
----------------------------------------
|
----------------------------------------
|
||||||
-kpascal : kilopascal
|
-kpascal : kilopascal
|
||||||
|
|
||||||
|
Throughput
|
||||||
|
----------------------------------------
|
||||||
|
-kBps : kilobytes per second
|
||||||
|
@ -121,6 +121,10 @@ static int resources_available(void)
|
|||||||
|
|
||||||
clk_put(cpu_clk);
|
clk_put(cpu_clk);
|
||||||
|
|
||||||
|
ret = dev_pm_opp_of_find_icc_paths(cpu_dev, NULL);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
name = find_supply_name(cpu_dev);
|
name = find_supply_name(cpu_dev);
|
||||||
/* Platform doesn't require regulator */
|
/* Platform doesn't require regulator */
|
||||||
if (!name)
|
if (!name)
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
# SPDX-License-Identifier: GPL-2.0-only
|
# SPDX-License-Identifier: GPL-2.0-only
|
||||||
menuconfig INTERCONNECT
|
menuconfig INTERCONNECT
|
||||||
tristate "On-Chip Interconnect management support"
|
bool "On-Chip Interconnect management support"
|
||||||
help
|
help
|
||||||
Support for management of the on-chip interconnects.
|
Support for management of the on-chip interconnects.
|
||||||
|
|
||||||
|
@ -351,9 +351,9 @@ static struct icc_node *of_icc_get_from_provider(struct of_phandle_args *spec)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* of_icc_get() - get a path handle from a DT node based on name
|
* of_icc_get_by_index() - get a path handle from a DT node based on index
|
||||||
* @dev: device pointer for the consumer device
|
* @dev: device pointer for the consumer device
|
||||||
* @name: interconnect path name
|
* @idx: interconnect path index
|
||||||
*
|
*
|
||||||
* This function will search for a path between two endpoints and return an
|
* This function will search for a path between two endpoints and return an
|
||||||
* icc_path handle on success. Use icc_put() to release constraints when they
|
* icc_path handle on success. Use icc_put() to release constraints when they
|
||||||
@ -365,13 +365,12 @@ static struct icc_node *of_icc_get_from_provider(struct of_phandle_args *spec)
|
|||||||
* Return: icc_path pointer on success or ERR_PTR() on error. NULL is returned
|
* Return: icc_path pointer on success or ERR_PTR() on error. NULL is returned
|
||||||
* when the API is disabled or the "interconnects" DT property is missing.
|
* when the API is disabled or the "interconnects" DT property is missing.
|
||||||
*/
|
*/
|
||||||
struct icc_path *of_icc_get(struct device *dev, const char *name)
|
struct icc_path *of_icc_get_by_index(struct device *dev, int idx)
|
||||||
{
|
{
|
||||||
struct icc_path *path = ERR_PTR(-EPROBE_DEFER);
|
struct icc_path *path;
|
||||||
struct icc_node *src_node, *dst_node;
|
struct icc_node *src_node, *dst_node;
|
||||||
struct device_node *np = NULL;
|
struct device_node *np;
|
||||||
struct of_phandle_args src_args, dst_args;
|
struct of_phandle_args src_args, dst_args;
|
||||||
int idx = 0;
|
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
if (!dev || !dev->of_node)
|
if (!dev || !dev->of_node)
|
||||||
@ -391,12 +390,6 @@ struct icc_path *of_icc_get(struct device *dev, const char *name)
|
|||||||
* lets support only global ids and extend this in the future if needed
|
* lets support only global ids and extend this in the future if needed
|
||||||
* without breaking DT compatibility.
|
* without breaking DT compatibility.
|
||||||
*/
|
*/
|
||||||
if (name) {
|
|
||||||
idx = of_property_match_string(np, "interconnect-names", name);
|
|
||||||
if (idx < 0)
|
|
||||||
return ERR_PTR(idx);
|
|
||||||
}
|
|
||||||
|
|
||||||
ret = of_parse_phandle_with_args(np, "interconnects",
|
ret = of_parse_phandle_with_args(np, "interconnects",
|
||||||
"#interconnect-cells", idx * 2,
|
"#interconnect-cells", idx * 2,
|
||||||
&src_args);
|
&src_args);
|
||||||
@ -439,12 +432,8 @@ struct icc_path *of_icc_get(struct device *dev, const char *name)
|
|||||||
return path;
|
return path;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (name)
|
path->name = kasprintf(GFP_KERNEL, "%s-%s",
|
||||||
path->name = kstrdup_const(name, GFP_KERNEL);
|
src_node->name, dst_node->name);
|
||||||
else
|
|
||||||
path->name = kasprintf(GFP_KERNEL, "%s-%s",
|
|
||||||
src_node->name, dst_node->name);
|
|
||||||
|
|
||||||
if (!path->name) {
|
if (!path->name) {
|
||||||
kfree(path);
|
kfree(path);
|
||||||
return ERR_PTR(-ENOMEM);
|
return ERR_PTR(-ENOMEM);
|
||||||
@ -452,6 +441,53 @@ struct icc_path *of_icc_get(struct device *dev, const char *name)
|
|||||||
|
|
||||||
return path;
|
return path;
|
||||||
}
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(of_icc_get_by_index);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* of_icc_get() - get a path handle from a DT node based on name
|
||||||
|
* @dev: device pointer for the consumer device
|
||||||
|
* @name: interconnect path name
|
||||||
|
*
|
||||||
|
* This function will search for a path between two endpoints and return an
|
||||||
|
* icc_path handle on success. Use icc_put() to release constraints when they
|
||||||
|
* are not needed anymore.
|
||||||
|
* If the interconnect API is disabled, NULL is returned and the consumer
|
||||||
|
* drivers will still build. Drivers are free to handle this specifically,
|
||||||
|
* but they don't have to.
|
||||||
|
*
|
||||||
|
* Return: icc_path pointer on success or ERR_PTR() on error. NULL is returned
|
||||||
|
* when the API is disabled or the "interconnects" DT property is missing.
|
||||||
|
*/
|
||||||
|
struct icc_path *of_icc_get(struct device *dev, const char *name)
|
||||||
|
{
|
||||||
|
struct device_node *np;
|
||||||
|
int idx = 0;
|
||||||
|
|
||||||
|
if (!dev || !dev->of_node)
|
||||||
|
return ERR_PTR(-ENODEV);
|
||||||
|
|
||||||
|
np = dev->of_node;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* When the consumer DT node do not have "interconnects" property
|
||||||
|
* return a NULL path to skip setting constraints.
|
||||||
|
*/
|
||||||
|
if (!of_find_property(np, "interconnects", NULL))
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We use a combination of phandle and specifier for endpoint. For now
|
||||||
|
* lets support only global ids and extend this in the future if needed
|
||||||
|
* without breaking DT compatibility.
|
||||||
|
*/
|
||||||
|
if (name) {
|
||||||
|
idx = of_property_match_string(np, "interconnect-names", name);
|
||||||
|
if (idx < 0)
|
||||||
|
return ERR_PTR(idx);
|
||||||
|
}
|
||||||
|
|
||||||
|
return of_icc_get_by_index(dev, idx);
|
||||||
|
}
|
||||||
EXPORT_SYMBOL_GPL(of_icc_get);
|
EXPORT_SYMBOL_GPL(of_icc_get);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -478,6 +514,24 @@ void icc_set_tag(struct icc_path *path, u32 tag)
|
|||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(icc_set_tag);
|
EXPORT_SYMBOL_GPL(icc_set_tag);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* icc_get_name() - Get name of the icc path
|
||||||
|
* @path: reference to the path returned by icc_get()
|
||||||
|
*
|
||||||
|
* This function is used by an interconnect consumer to get the name of the icc
|
||||||
|
* path.
|
||||||
|
*
|
||||||
|
* Returns a valid pointer on success, or NULL otherwise.
|
||||||
|
*/
|
||||||
|
const char *icc_get_name(struct icc_path *path)
|
||||||
|
{
|
||||||
|
if (!path)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
return path->name;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(icc_get_name);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* icc_set_bw() - set bandwidth constraints on an interconnect path
|
* icc_set_bw() - set bandwidth constraints on an interconnect path
|
||||||
* @path: reference to the path returned by icc_get()
|
* @path: reference to the path returned by icc_get()
|
||||||
@ -908,12 +962,7 @@ static int __init icc_init(void)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void __exit icc_exit(void)
|
device_initcall(icc_init);
|
||||||
{
|
|
||||||
debugfs_remove_recursive(icc_debugfs_dir);
|
|
||||||
}
|
|
||||||
module_init(icc_init);
|
|
||||||
module_exit(icc_exit);
|
|
||||||
|
|
||||||
MODULE_AUTHOR("Georgi Djakov <georgi.djakov@linaro.org>");
|
MODULE_AUTHOR("Georgi Djakov <georgi.djakov@linaro.org>");
|
||||||
MODULE_DESCRIPTION("Interconnect Driver Core");
|
MODULE_DESCRIPTION("Interconnect Driver Core");
|
||||||
|
@ -664,7 +664,7 @@ static inline int _generic_set_opp_clk_only(struct device *dev, struct clk *clk,
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int _generic_set_opp_regulator(const struct opp_table *opp_table,
|
static int _generic_set_opp_regulator(struct opp_table *opp_table,
|
||||||
struct device *dev,
|
struct device *dev,
|
||||||
unsigned long old_freq,
|
unsigned long old_freq,
|
||||||
unsigned long freq,
|
unsigned long freq,
|
||||||
@ -699,6 +699,18 @@ static int _generic_set_opp_regulator(const struct opp_table *opp_table,
|
|||||||
goto restore_freq;
|
goto restore_freq;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Enable the regulator after setting its voltages, otherwise it breaks
|
||||||
|
* some boot-enabled regulators.
|
||||||
|
*/
|
||||||
|
if (unlikely(!opp_table->regulator_enabled)) {
|
||||||
|
ret = regulator_enable(reg);
|
||||||
|
if (ret < 0)
|
||||||
|
dev_warn(dev, "Failed to enable regulator: %d", ret);
|
||||||
|
else
|
||||||
|
opp_table->regulator_enabled = true;
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
restore_freq:
|
restore_freq:
|
||||||
@ -713,6 +725,34 @@ restore_voltage:
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int _set_opp_bw(const struct opp_table *opp_table,
|
||||||
|
struct dev_pm_opp *opp, struct device *dev, bool remove)
|
||||||
|
{
|
||||||
|
u32 avg, peak;
|
||||||
|
int i, ret;
|
||||||
|
|
||||||
|
if (!opp_table->paths)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
for (i = 0; i < opp_table->path_count; i++) {
|
||||||
|
if (remove) {
|
||||||
|
avg = 0;
|
||||||
|
peak = 0;
|
||||||
|
} else {
|
||||||
|
avg = opp->bandwidth[i].avg;
|
||||||
|
peak = opp->bandwidth[i].peak;
|
||||||
|
}
|
||||||
|
ret = icc_set_bw(opp_table->paths[i], avg, peak);
|
||||||
|
if (ret) {
|
||||||
|
dev_err(dev, "Failed to %s bandwidth[%d]: %d\n",
|
||||||
|
remove ? "remove" : "set", i, ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static int _set_opp_custom(const struct opp_table *opp_table,
|
static int _set_opp_custom(const struct opp_table *opp_table,
|
||||||
struct device *dev, unsigned long old_freq,
|
struct device *dev, unsigned long old_freq,
|
||||||
unsigned long freq,
|
unsigned long freq,
|
||||||
@ -817,15 +857,31 @@ int dev_pm_opp_set_rate(struct device *dev, unsigned long target_freq)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (unlikely(!target_freq)) {
|
if (unlikely(!target_freq)) {
|
||||||
if (opp_table->required_opp_tables) {
|
/*
|
||||||
ret = _set_required_opps(dev, opp_table, NULL);
|
* Some drivers need to support cases where some platforms may
|
||||||
} else if (!_get_opp_count(opp_table)) {
|
* have OPP table for the device, while others don't and
|
||||||
|
* opp_set_rate() just needs to behave like clk_set_rate().
|
||||||
|
*/
|
||||||
|
if (!_get_opp_count(opp_table))
|
||||||
return 0;
|
return 0;
|
||||||
} else {
|
|
||||||
|
if (!opp_table->required_opp_tables && !opp_table->regulators &&
|
||||||
|
!opp_table->paths) {
|
||||||
dev_err(dev, "target frequency can't be 0\n");
|
dev_err(dev, "target frequency can't be 0\n");
|
||||||
ret = -EINVAL;
|
ret = -EINVAL;
|
||||||
|
goto put_opp_table;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ret = _set_opp_bw(opp_table, NULL, dev, true);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
if (opp_table->regulator_enabled) {
|
||||||
|
regulator_disable(opp_table->regulators[0]);
|
||||||
|
opp_table->regulator_enabled = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = _set_required_opps(dev, opp_table, NULL);
|
||||||
goto put_opp_table;
|
goto put_opp_table;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -909,6 +965,9 @@ int dev_pm_opp_set_rate(struct device *dev, unsigned long target_freq)
|
|||||||
dev_err(dev, "Failed to set required opps: %d\n", ret);
|
dev_err(dev, "Failed to set required opps: %d\n", ret);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!ret)
|
||||||
|
ret = _set_opp_bw(opp_table, opp, dev, false);
|
||||||
|
|
||||||
put_opp:
|
put_opp:
|
||||||
dev_pm_opp_put(opp);
|
dev_pm_opp_put(opp);
|
||||||
put_old_opp:
|
put_old_opp:
|
||||||
@ -999,6 +1058,12 @@ static struct opp_table *_allocate_opp_table(struct device *dev, int index)
|
|||||||
ret);
|
ret);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Find interconnect path(s) for the device */
|
||||||
|
ret = dev_pm_opp_of_find_icc_paths(dev, opp_table);
|
||||||
|
if (ret)
|
||||||
|
dev_warn(dev, "%s: Error finding interconnect paths: %d\n",
|
||||||
|
__func__, ret);
|
||||||
|
|
||||||
BLOCKING_INIT_NOTIFIER_HEAD(&opp_table->head);
|
BLOCKING_INIT_NOTIFIER_HEAD(&opp_table->head);
|
||||||
INIT_LIST_HEAD(&opp_table->opp_list);
|
INIT_LIST_HEAD(&opp_table->opp_list);
|
||||||
kref_init(&opp_table->kref);
|
kref_init(&opp_table->kref);
|
||||||
@ -1057,6 +1122,7 @@ static void _opp_table_kref_release(struct kref *kref)
|
|||||||
{
|
{
|
||||||
struct opp_table *opp_table = container_of(kref, struct opp_table, kref);
|
struct opp_table *opp_table = container_of(kref, struct opp_table, kref);
|
||||||
struct opp_device *opp_dev, *temp;
|
struct opp_device *opp_dev, *temp;
|
||||||
|
int i;
|
||||||
|
|
||||||
_of_clear_opp_table(opp_table);
|
_of_clear_opp_table(opp_table);
|
||||||
|
|
||||||
@ -1064,6 +1130,12 @@ static void _opp_table_kref_release(struct kref *kref)
|
|||||||
if (!IS_ERR(opp_table->clk))
|
if (!IS_ERR(opp_table->clk))
|
||||||
clk_put(opp_table->clk);
|
clk_put(opp_table->clk);
|
||||||
|
|
||||||
|
if (opp_table->paths) {
|
||||||
|
for (i = 0; i < opp_table->path_count; i++)
|
||||||
|
icc_put(opp_table->paths[i]);
|
||||||
|
kfree(opp_table->paths);
|
||||||
|
}
|
||||||
|
|
||||||
WARN_ON(!list_empty(&opp_table->opp_list));
|
WARN_ON(!list_empty(&opp_table->opp_list));
|
||||||
|
|
||||||
list_for_each_entry_safe(opp_dev, temp, &opp_table->dev_list, node) {
|
list_for_each_entry_safe(opp_dev, temp, &opp_table->dev_list, node) {
|
||||||
@ -1243,19 +1315,23 @@ EXPORT_SYMBOL_GPL(dev_pm_opp_remove_all_dynamic);
|
|||||||
struct dev_pm_opp *_opp_allocate(struct opp_table *table)
|
struct dev_pm_opp *_opp_allocate(struct opp_table *table)
|
||||||
{
|
{
|
||||||
struct dev_pm_opp *opp;
|
struct dev_pm_opp *opp;
|
||||||
int count, supply_size;
|
int supply_count, supply_size, icc_size;
|
||||||
|
|
||||||
/* Allocate space for at least one supply */
|
/* Allocate space for at least one supply */
|
||||||
count = table->regulator_count > 0 ? table->regulator_count : 1;
|
supply_count = table->regulator_count > 0 ? table->regulator_count : 1;
|
||||||
supply_size = sizeof(*opp->supplies) * count;
|
supply_size = sizeof(*opp->supplies) * supply_count;
|
||||||
|
icc_size = sizeof(*opp->bandwidth) * table->path_count;
|
||||||
|
|
||||||
/* allocate new OPP node and supplies structures */
|
/* allocate new OPP node and supplies structures */
|
||||||
opp = kzalloc(sizeof(*opp) + supply_size, GFP_KERNEL);
|
opp = kzalloc(sizeof(*opp) + supply_size + icc_size, GFP_KERNEL);
|
||||||
|
|
||||||
if (!opp)
|
if (!opp)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
/* Put the supplies at the end of the OPP structure as an empty array */
|
/* Put the supplies at the end of the OPP structure as an empty array */
|
||||||
opp->supplies = (struct dev_pm_opp_supply *)(opp + 1);
|
opp->supplies = (struct dev_pm_opp_supply *)(opp + 1);
|
||||||
|
if (icc_size)
|
||||||
|
opp->bandwidth = (struct dev_pm_opp_icc_bw *)(opp->supplies + supply_count);
|
||||||
INIT_LIST_HEAD(&opp->node);
|
INIT_LIST_HEAD(&opp->node);
|
||||||
|
|
||||||
return opp;
|
return opp;
|
||||||
@ -1286,11 +1362,24 @@ static bool _opp_supported_by_regulators(struct dev_pm_opp *opp,
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int _opp_compare_key(struct dev_pm_opp *opp1, struct dev_pm_opp *opp2)
|
||||||
|
{
|
||||||
|
if (opp1->rate != opp2->rate)
|
||||||
|
return opp1->rate < opp2->rate ? -1 : 1;
|
||||||
|
if (opp1->bandwidth && opp2->bandwidth &&
|
||||||
|
opp1->bandwidth[0].peak != opp2->bandwidth[0].peak)
|
||||||
|
return opp1->bandwidth[0].peak < opp2->bandwidth[0].peak ? -1 : 1;
|
||||||
|
if (opp1->level != opp2->level)
|
||||||
|
return opp1->level < opp2->level ? -1 : 1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static int _opp_is_duplicate(struct device *dev, struct dev_pm_opp *new_opp,
|
static int _opp_is_duplicate(struct device *dev, struct dev_pm_opp *new_opp,
|
||||||
struct opp_table *opp_table,
|
struct opp_table *opp_table,
|
||||||
struct list_head **head)
|
struct list_head **head)
|
||||||
{
|
{
|
||||||
struct dev_pm_opp *opp;
|
struct dev_pm_opp *opp;
|
||||||
|
int opp_cmp;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Insert new OPP in order of increasing frequency and discard if
|
* Insert new OPP in order of increasing frequency and discard if
|
||||||
@ -1301,12 +1390,13 @@ static int _opp_is_duplicate(struct device *dev, struct dev_pm_opp *new_opp,
|
|||||||
* loop.
|
* loop.
|
||||||
*/
|
*/
|
||||||
list_for_each_entry(opp, &opp_table->opp_list, node) {
|
list_for_each_entry(opp, &opp_table->opp_list, node) {
|
||||||
if (new_opp->rate > opp->rate) {
|
opp_cmp = _opp_compare_key(new_opp, opp);
|
||||||
|
if (opp_cmp > 0) {
|
||||||
*head = &opp->node;
|
*head = &opp->node;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (new_opp->rate < opp->rate)
|
if (opp_cmp < 0)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
/* Duplicate OPPs */
|
/* Duplicate OPPs */
|
||||||
@ -1670,6 +1760,13 @@ void dev_pm_opp_put_regulators(struct opp_table *opp_table)
|
|||||||
/* Make sure there are no concurrent readers while updating opp_table */
|
/* Make sure there are no concurrent readers while updating opp_table */
|
||||||
WARN_ON(!list_empty(&opp_table->opp_list));
|
WARN_ON(!list_empty(&opp_table->opp_list));
|
||||||
|
|
||||||
|
if (opp_table->regulator_enabled) {
|
||||||
|
for (i = opp_table->regulator_count - 1; i >= 0; i--)
|
||||||
|
regulator_disable(opp_table->regulators[i]);
|
||||||
|
|
||||||
|
opp_table->regulator_enabled = false;
|
||||||
|
}
|
||||||
|
|
||||||
for (i = opp_table->regulator_count - 1; i >= 0; i--)
|
for (i = opp_table->regulator_count - 1; i >= 0; i--)
|
||||||
regulator_put(opp_table->regulators[i]);
|
regulator_put(opp_table->regulators[i]);
|
||||||
|
|
||||||
|
@ -32,6 +32,47 @@ void opp_debug_remove_one(struct dev_pm_opp *opp)
|
|||||||
debugfs_remove_recursive(opp->dentry);
|
debugfs_remove_recursive(opp->dentry);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static ssize_t bw_name_read(struct file *fp, char __user *userbuf,
|
||||||
|
size_t count, loff_t *ppos)
|
||||||
|
{
|
||||||
|
struct icc_path *path = fp->private_data;
|
||||||
|
char buf[64];
|
||||||
|
int i;
|
||||||
|
|
||||||
|
i = scnprintf(buf, sizeof(buf), "%.62s\n", icc_get_name(path));
|
||||||
|
|
||||||
|
return simple_read_from_buffer(userbuf, count, ppos, buf, i);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct file_operations bw_name_fops = {
|
||||||
|
.open = simple_open,
|
||||||
|
.read = bw_name_read,
|
||||||
|
.llseek = default_llseek,
|
||||||
|
};
|
||||||
|
|
||||||
|
static void opp_debug_create_bw(struct dev_pm_opp *opp,
|
||||||
|
struct opp_table *opp_table,
|
||||||
|
struct dentry *pdentry)
|
||||||
|
{
|
||||||
|
struct dentry *d;
|
||||||
|
char name[11];
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < opp_table->path_count; i++) {
|
||||||
|
snprintf(name, sizeof(name), "icc-path-%.1d", i);
|
||||||
|
|
||||||
|
/* Create per-path directory */
|
||||||
|
d = debugfs_create_dir(name, pdentry);
|
||||||
|
|
||||||
|
debugfs_create_file("name", S_IRUGO, d, opp_table->paths[i],
|
||||||
|
&bw_name_fops);
|
||||||
|
debugfs_create_u32("peak_bw", S_IRUGO, d,
|
||||||
|
&opp->bandwidth[i].peak);
|
||||||
|
debugfs_create_u32("avg_bw", S_IRUGO, d,
|
||||||
|
&opp->bandwidth[i].avg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void opp_debug_create_supplies(struct dev_pm_opp *opp,
|
static void opp_debug_create_supplies(struct dev_pm_opp *opp,
|
||||||
struct opp_table *opp_table,
|
struct opp_table *opp_table,
|
||||||
struct dentry *pdentry)
|
struct dentry *pdentry)
|
||||||
@ -94,6 +135,7 @@ void opp_debug_create_one(struct dev_pm_opp *opp, struct opp_table *opp_table)
|
|||||||
&opp->clock_latency_ns);
|
&opp->clock_latency_ns);
|
||||||
|
|
||||||
opp_debug_create_supplies(opp, opp_table, d);
|
opp_debug_create_supplies(opp, opp_table, d);
|
||||||
|
opp_debug_create_bw(opp, opp_table, d);
|
||||||
|
|
||||||
opp->dentry = d;
|
opp->dentry = d;
|
||||||
}
|
}
|
||||||
|
205
drivers/opp/of.c
205
drivers/opp/of.c
@ -332,6 +332,105 @@ free_required_opps:
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int _bandwidth_supported(struct device *dev, struct opp_table *opp_table)
|
||||||
|
{
|
||||||
|
struct device_node *np, *opp_np;
|
||||||
|
struct property *prop;
|
||||||
|
|
||||||
|
if (!opp_table) {
|
||||||
|
np = of_node_get(dev->of_node);
|
||||||
|
if (!np)
|
||||||
|
return -ENODEV;
|
||||||
|
|
||||||
|
opp_np = _opp_of_get_opp_desc_node(np, 0);
|
||||||
|
of_node_put(np);
|
||||||
|
} else {
|
||||||
|
opp_np = of_node_get(opp_table->np);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Lets not fail in case we are parsing opp-v1 bindings */
|
||||||
|
if (!opp_np)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
/* Checking only first OPP is sufficient */
|
||||||
|
np = of_get_next_available_child(opp_np, NULL);
|
||||||
|
if (!np) {
|
||||||
|
dev_err(dev, "OPP table empty\n");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
of_node_put(opp_np);
|
||||||
|
|
||||||
|
prop = of_find_property(np, "opp-peak-kBps", NULL);
|
||||||
|
of_node_put(np);
|
||||||
|
|
||||||
|
if (!prop || !prop->length)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int dev_pm_opp_of_find_icc_paths(struct device *dev,
|
||||||
|
struct opp_table *opp_table)
|
||||||
|
{
|
||||||
|
struct device_node *np;
|
||||||
|
int ret, i, count, num_paths;
|
||||||
|
struct icc_path **paths;
|
||||||
|
|
||||||
|
ret = _bandwidth_supported(dev, opp_table);
|
||||||
|
if (ret <= 0)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
ret = 0;
|
||||||
|
|
||||||
|
np = of_node_get(dev->of_node);
|
||||||
|
if (!np)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
count = of_count_phandle_with_args(np, "interconnects",
|
||||||
|
"#interconnect-cells");
|
||||||
|
of_node_put(np);
|
||||||
|
if (count < 0)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
/* two phandles when #interconnect-cells = <1> */
|
||||||
|
if (count % 2) {
|
||||||
|
dev_err(dev, "%s: Invalid interconnects values\n", __func__);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
num_paths = count / 2;
|
||||||
|
paths = kcalloc(num_paths, sizeof(*paths), GFP_KERNEL);
|
||||||
|
if (!paths)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
for (i = 0; i < num_paths; i++) {
|
||||||
|
paths[i] = of_icc_get_by_index(dev, i);
|
||||||
|
if (IS_ERR(paths[i])) {
|
||||||
|
ret = PTR_ERR(paths[i]);
|
||||||
|
if (ret != -EPROBE_DEFER) {
|
||||||
|
dev_err(dev, "%s: Unable to get path%d: %d\n",
|
||||||
|
__func__, i, ret);
|
||||||
|
}
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (opp_table) {
|
||||||
|
opp_table->paths = paths;
|
||||||
|
opp_table->path_count = num_paths;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
err:
|
||||||
|
while (i--)
|
||||||
|
icc_put(paths[i]);
|
||||||
|
|
||||||
|
kfree(paths);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(dev_pm_opp_of_find_icc_paths);
|
||||||
|
|
||||||
static bool _opp_is_supported(struct device *dev, struct opp_table *opp_table,
|
static bool _opp_is_supported(struct device *dev, struct opp_table *opp_table,
|
||||||
struct device_node *np)
|
struct device_node *np)
|
||||||
{
|
{
|
||||||
@ -521,6 +620,90 @@ void dev_pm_opp_of_remove_table(struct device *dev)
|
|||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(dev_pm_opp_of_remove_table);
|
EXPORT_SYMBOL_GPL(dev_pm_opp_of_remove_table);
|
||||||
|
|
||||||
|
static int _read_bw(struct dev_pm_opp *new_opp, struct opp_table *table,
|
||||||
|
struct device_node *np, bool peak)
|
||||||
|
{
|
||||||
|
const char *name = peak ? "opp-peak-kBps" : "opp-avg-kBps";
|
||||||
|
struct property *prop;
|
||||||
|
int i, count, ret;
|
||||||
|
u32 *bw;
|
||||||
|
|
||||||
|
prop = of_find_property(np, name, NULL);
|
||||||
|
if (!prop)
|
||||||
|
return -ENODEV;
|
||||||
|
|
||||||
|
count = prop->length / sizeof(u32);
|
||||||
|
if (table->path_count != count) {
|
||||||
|
pr_err("%s: Mismatch between %s and paths (%d %d)\n",
|
||||||
|
__func__, name, count, table->path_count);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
bw = kmalloc_array(count, sizeof(*bw), GFP_KERNEL);
|
||||||
|
if (!bw)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
ret = of_property_read_u32_array(np, name, bw, count);
|
||||||
|
if (ret) {
|
||||||
|
pr_err("%s: Error parsing %s: %d\n", __func__, name, ret);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < count; i++) {
|
||||||
|
if (peak)
|
||||||
|
new_opp->bandwidth[i].peak = kBps_to_icc(bw[i]);
|
||||||
|
else
|
||||||
|
new_opp->bandwidth[i].avg = kBps_to_icc(bw[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
out:
|
||||||
|
kfree(bw);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int _read_opp_key(struct dev_pm_opp *new_opp, struct opp_table *table,
|
||||||
|
struct device_node *np, bool *rate_not_available)
|
||||||
|
{
|
||||||
|
bool found = false;
|
||||||
|
u64 rate;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = of_property_read_u64(np, "opp-hz", &rate);
|
||||||
|
if (!ret) {
|
||||||
|
/*
|
||||||
|
* Rate is defined as an unsigned long in clk API, and so
|
||||||
|
* casting explicitly to its type. Must be fixed once rate is 64
|
||||||
|
* bit guaranteed in clk API.
|
||||||
|
*/
|
||||||
|
new_opp->rate = (unsigned long)rate;
|
||||||
|
found = true;
|
||||||
|
}
|
||||||
|
*rate_not_available = !!ret;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Bandwidth consists of peak and average (optional) values:
|
||||||
|
* opp-peak-kBps = <path1_value path2_value>;
|
||||||
|
* opp-avg-kBps = <path1_value path2_value>;
|
||||||
|
*/
|
||||||
|
ret = _read_bw(new_opp, table, np, true);
|
||||||
|
if (!ret) {
|
||||||
|
found = true;
|
||||||
|
ret = _read_bw(new_opp, table, np, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* The properties were found but we failed to parse them */
|
||||||
|
if (ret && ret != -ENODEV)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
if (!of_property_read_u32(np, "opp-level", &new_opp->level))
|
||||||
|
found = true;
|
||||||
|
|
||||||
|
if (found)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* _opp_add_static_v2() - Allocate static OPPs (As per 'v2' DT bindings)
|
* _opp_add_static_v2() - Allocate static OPPs (As per 'v2' DT bindings)
|
||||||
* @opp_table: OPP table
|
* @opp_table: OPP table
|
||||||
@ -558,26 +741,12 @@ static struct dev_pm_opp *_opp_add_static_v2(struct opp_table *opp_table,
|
|||||||
if (!new_opp)
|
if (!new_opp)
|
||||||
return ERR_PTR(-ENOMEM);
|
return ERR_PTR(-ENOMEM);
|
||||||
|
|
||||||
ret = of_property_read_u64(np, "opp-hz", &rate);
|
ret = _read_opp_key(new_opp, opp_table, np, &rate_not_available);
|
||||||
if (ret < 0) {
|
if (ret < 0 && !opp_table->is_genpd) {
|
||||||
/* "opp-hz" is optional for devices like power domains. */
|
dev_err(dev, "%s: opp key field not found\n", __func__);
|
||||||
if (!opp_table->is_genpd) {
|
goto free_opp;
|
||||||
dev_err(dev, "%s: opp-hz not found\n", __func__);
|
|
||||||
goto free_opp;
|
|
||||||
}
|
|
||||||
|
|
||||||
rate_not_available = true;
|
|
||||||
} else {
|
|
||||||
/*
|
|
||||||
* Rate is defined as an unsigned long in clk API, and so
|
|
||||||
* casting explicitly to its type. Must be fixed once rate is 64
|
|
||||||
* bit guaranteed in clk API.
|
|
||||||
*/
|
|
||||||
new_opp->rate = (unsigned long)rate;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
of_property_read_u32(np, "opp-level", &new_opp->level);
|
|
||||||
|
|
||||||
/* Check if the OPP supports hardware's hierarchy of versions or not */
|
/* Check if the OPP supports hardware's hierarchy of versions or not */
|
||||||
if (!_opp_is_supported(dev, opp_table, np)) {
|
if (!_opp_is_supported(dev, opp_table, np)) {
|
||||||
dev_dbg(dev, "OPP not supported by hardware: %llu\n", rate);
|
dev_dbg(dev, "OPP not supported by hardware: %llu\n", rate);
|
||||||
|
@ -12,6 +12,7 @@
|
|||||||
#define __DRIVER_OPP_H__
|
#define __DRIVER_OPP_H__
|
||||||
|
|
||||||
#include <linux/device.h>
|
#include <linux/device.h>
|
||||||
|
#include <linux/interconnect.h>
|
||||||
#include <linux/kernel.h>
|
#include <linux/kernel.h>
|
||||||
#include <linux/kref.h>
|
#include <linux/kref.h>
|
||||||
#include <linux/list.h>
|
#include <linux/list.h>
|
||||||
@ -59,6 +60,7 @@ extern struct list_head opp_tables;
|
|||||||
* @rate: Frequency in hertz
|
* @rate: Frequency in hertz
|
||||||
* @level: Performance level
|
* @level: Performance level
|
||||||
* @supplies: Power supplies voltage/current values
|
* @supplies: Power supplies voltage/current values
|
||||||
|
* @bandwidth: Interconnect bandwidth values
|
||||||
* @clock_latency_ns: Latency (in nanoseconds) of switching to this OPP's
|
* @clock_latency_ns: Latency (in nanoseconds) of switching to this OPP's
|
||||||
* frequency from any other OPP's frequency.
|
* frequency from any other OPP's frequency.
|
||||||
* @required_opps: List of OPPs that are required by this OPP.
|
* @required_opps: List of OPPs that are required by this OPP.
|
||||||
@ -81,6 +83,7 @@ struct dev_pm_opp {
|
|||||||
unsigned int level;
|
unsigned int level;
|
||||||
|
|
||||||
struct dev_pm_opp_supply *supplies;
|
struct dev_pm_opp_supply *supplies;
|
||||||
|
struct dev_pm_opp_icc_bw *bandwidth;
|
||||||
|
|
||||||
unsigned long clock_latency_ns;
|
unsigned long clock_latency_ns;
|
||||||
|
|
||||||
@ -144,8 +147,11 @@ enum opp_table_access {
|
|||||||
* @clk: Device's clock handle
|
* @clk: Device's clock handle
|
||||||
* @regulators: Supply regulators
|
* @regulators: Supply regulators
|
||||||
* @regulator_count: Number of power supply regulators. Its value can be -1
|
* @regulator_count: Number of power supply regulators. Its value can be -1
|
||||||
|
* @regulator_enabled: Set to true if regulators were previously enabled.
|
||||||
* (uninitialized), 0 (no opp-microvolt property) or > 0 (has opp-microvolt
|
* (uninitialized), 0 (no opp-microvolt property) or > 0 (has opp-microvolt
|
||||||
* property).
|
* property).
|
||||||
|
* @paths: Interconnect path handles
|
||||||
|
* @path_count: Number of interconnect paths
|
||||||
* @genpd_performance_state: Device's power domain support performance state.
|
* @genpd_performance_state: Device's power domain support performance state.
|
||||||
* @is_genpd: Marks if the OPP table belongs to a genpd.
|
* @is_genpd: Marks if the OPP table belongs to a genpd.
|
||||||
* @set_opp: Platform specific set_opp callback
|
* @set_opp: Platform specific set_opp callback
|
||||||
@ -189,6 +195,9 @@ struct opp_table {
|
|||||||
struct clk *clk;
|
struct clk *clk;
|
||||||
struct regulator **regulators;
|
struct regulator **regulators;
|
||||||
int regulator_count;
|
int regulator_count;
|
||||||
|
bool regulator_enabled;
|
||||||
|
struct icc_path **paths;
|
||||||
|
unsigned int path_count;
|
||||||
bool genpd_performance_state;
|
bool genpd_performance_state;
|
||||||
bool is_genpd;
|
bool is_genpd;
|
||||||
|
|
||||||
@ -211,6 +220,7 @@ struct opp_device *_add_opp_dev(const struct device *dev, struct opp_table *opp_
|
|||||||
void _dev_pm_opp_find_and_remove_table(struct device *dev);
|
void _dev_pm_opp_find_and_remove_table(struct device *dev);
|
||||||
struct dev_pm_opp *_opp_allocate(struct opp_table *opp_table);
|
struct dev_pm_opp *_opp_allocate(struct opp_table *opp_table);
|
||||||
void _opp_free(struct dev_pm_opp *opp);
|
void _opp_free(struct dev_pm_opp *opp);
|
||||||
|
int _opp_compare_key(struct dev_pm_opp *opp1, struct dev_pm_opp *opp2);
|
||||||
int _opp_add(struct device *dev, struct dev_pm_opp *new_opp, struct opp_table *opp_table, bool rate_not_available);
|
int _opp_add(struct device *dev, struct dev_pm_opp *new_opp, struct opp_table *opp_table, bool rate_not_available);
|
||||||
int _opp_add_v1(struct opp_table *opp_table, struct device *dev, unsigned long freq, long u_volt, bool dynamic);
|
int _opp_add_v1(struct opp_table *opp_table, struct device *dev, unsigned long freq, long u_volt, bool dynamic);
|
||||||
void _dev_pm_opp_cpumask_remove_table(const struct cpumask *cpumask, int last_cpu);
|
void _dev_pm_opp_cpumask_remove_table(const struct cpumask *cpumask, int last_cpu);
|
||||||
|
@ -28,9 +28,11 @@ struct device;
|
|||||||
struct icc_path *icc_get(struct device *dev, const int src_id,
|
struct icc_path *icc_get(struct device *dev, const int src_id,
|
||||||
const int dst_id);
|
const int dst_id);
|
||||||
struct icc_path *of_icc_get(struct device *dev, const char *name);
|
struct icc_path *of_icc_get(struct device *dev, const char *name);
|
||||||
|
struct icc_path *of_icc_get_by_index(struct device *dev, int idx);
|
||||||
void icc_put(struct icc_path *path);
|
void icc_put(struct icc_path *path);
|
||||||
int icc_set_bw(struct icc_path *path, u32 avg_bw, u32 peak_bw);
|
int icc_set_bw(struct icc_path *path, u32 avg_bw, u32 peak_bw);
|
||||||
void icc_set_tag(struct icc_path *path, u32 tag);
|
void icc_set_tag(struct icc_path *path, u32 tag);
|
||||||
|
const char *icc_get_name(struct icc_path *path);
|
||||||
|
|
||||||
#else
|
#else
|
||||||
|
|
||||||
@ -46,6 +48,11 @@ static inline struct icc_path *of_icc_get(struct device *dev,
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline struct icc_path *of_icc_get_by_index(struct device *dev, int idx)
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
static inline void icc_put(struct icc_path *path)
|
static inline void icc_put(struct icc_path *path)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
@ -59,6 +66,11 @@ static inline void icc_set_tag(struct icc_path *path, u32 tag)
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline const char *icc_get_name(struct icc_path *path)
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
#endif /* CONFIG_INTERCONNECT */
|
#endif /* CONFIG_INTERCONNECT */
|
||||||
|
|
||||||
#endif /* __LINUX_INTERCONNECT_H */
|
#endif /* __LINUX_INTERCONNECT_H */
|
||||||
|
@ -41,6 +41,18 @@ struct dev_pm_opp_supply {
|
|||||||
unsigned long u_amp;
|
unsigned long u_amp;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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;
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* struct dev_pm_opp_info - OPP freq/voltage/current values
|
* struct dev_pm_opp_info - OPP freq/voltage/current values
|
||||||
* @rate: Target clk rate in hz
|
* @rate: Target clk rate in hz
|
||||||
@ -360,6 +372,7 @@ int dev_pm_opp_of_get_sharing_cpus(struct device *cpu_dev, struct cpumask *cpuma
|
|||||||
struct device_node *dev_pm_opp_of_get_opp_desc_node(struct device *dev);
|
struct device_node *dev_pm_opp_of_get_opp_desc_node(struct device *dev);
|
||||||
struct device_node *dev_pm_opp_get_of_node(struct dev_pm_opp *opp);
|
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);
|
||||||
void dev_pm_opp_of_register_em(struct cpumask *cpus);
|
void dev_pm_opp_of_register_em(struct cpumask *cpus);
|
||||||
#else
|
#else
|
||||||
static inline int dev_pm_opp_of_add_table(struct device *dev)
|
static inline int dev_pm_opp_of_add_table(struct device *dev)
|
||||||
@ -408,6 +421,11 @@ static inline int of_get_required_opp_performance_state(struct device_node *np,
|
|||||||
{
|
{
|
||||||
return -ENOTSUPP;
|
return -ENOTSUPP;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline int dev_pm_opp_of_find_icc_paths(struct device *dev, struct opp_table *opp_table)
|
||||||
|
{
|
||||||
|
return -ENOTSUPP;
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#endif /* __LINUX_OPP_H__ */
|
#endif /* __LINUX_OPP_H__ */
|
||||||
|
Loading…
Reference in New Issue
Block a user