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

Merge branch 'pm-opp'

* pm-opp:
  PM / OPP: Rename structures for clarity
  PM / OPP: Fix incorrect comments
  PM / OPP: Initialize regulator pointer to an error value
  PM / OPP: Initialize u_volt_min/max to a valid value
  PM / OPP: Fix NULL pointer dereference crash when disabling OPPs
  PM / OPP: Add dev_pm_opp_set_rate()
  PM / OPP: Manage device clk
  PM / OPP: Parse clock-latency and voltage-tolerance for v1 bindings
  PM / OPP: Introduce dev_pm_opp_get_max_transition_latency()
  PM / OPP: Introduce dev_pm_opp_get_max_volt_latency()
  PM / OPP: Disable OPPs that aren't supported by the regulator
  PM / OPP: get/put regulators from OPP core
parents 07cc77e0 2c2709dc
...@@ -13,50 +13,52 @@ ...@@ -13,50 +13,52 @@
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/clk.h>
#include <linux/errno.h> #include <linux/errno.h>
#include <linux/err.h> #include <linux/err.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/device.h> #include <linux/device.h>
#include <linux/of.h> #include <linux/of.h>
#include <linux/export.h> #include <linux/export.h>
#include <linux/regulator/consumer.h>
#include "opp.h" #include "opp.h"
/* /*
* The root of the list of all devices. All device_opp structures branch off * The root of the list of all opp-tables. All opp_table structures branch off
* from here, with each device_opp containing the list of opp it supports in * from here, with each opp_table containing the list of opps it supports in
* various states of availability. * various states of availability.
*/ */
static LIST_HEAD(dev_opp_list); static LIST_HEAD(opp_tables);
/* Lock to allow exclusive modification to the device and opp lists */ /* Lock to allow exclusive modification to the device and opp lists */
DEFINE_MUTEX(dev_opp_list_lock); DEFINE_MUTEX(opp_table_lock);
#define opp_rcu_lockdep_assert() \ #define opp_rcu_lockdep_assert() \
do { \ do { \
RCU_LOCKDEP_WARN(!rcu_read_lock_held() && \ RCU_LOCKDEP_WARN(!rcu_read_lock_held() && \
!lockdep_is_held(&dev_opp_list_lock), \ !lockdep_is_held(&opp_table_lock), \
"Missing rcu_read_lock() or " \ "Missing rcu_read_lock() or " \
"dev_opp_list_lock protection"); \ "opp_table_lock protection"); \
} while (0) } while (0)
static struct device_list_opp *_find_list_dev(const struct device *dev, static struct opp_device *_find_opp_dev(const struct device *dev,
struct device_opp *dev_opp) struct opp_table *opp_table)
{ {
struct device_list_opp *list_dev; struct opp_device *opp_dev;
list_for_each_entry(list_dev, &dev_opp->dev_list, node) list_for_each_entry(opp_dev, &opp_table->dev_list, node)
if (list_dev->dev == dev) if (opp_dev->dev == dev)
return list_dev; return opp_dev;
return NULL; return NULL;
} }
static struct device_opp *_managed_opp(const struct device_node *np) static struct opp_table *_managed_opp(const struct device_node *np)
{ {
struct device_opp *dev_opp; struct opp_table *opp_table;
list_for_each_entry_rcu(dev_opp, &dev_opp_list, node) { list_for_each_entry_rcu(opp_table, &opp_tables, node) {
if (dev_opp->np == np) { if (opp_table->np == np) {
/* /*
* Multiple devices can point to the same OPP table and * Multiple devices can point to the same OPP table and
* so will have same node-pointer, np. * so will have same node-pointer, np.
...@@ -64,7 +66,7 @@ static struct device_opp *_managed_opp(const struct device_node *np) ...@@ -64,7 +66,7 @@ static struct device_opp *_managed_opp(const struct device_node *np)
* But the OPPs will be considered as shared only if the * But the OPPs will be considered as shared only if the
* OPP table contains a "opp-shared" property. * OPP table contains a "opp-shared" property.
*/ */
return dev_opp->shared_opp ? dev_opp : NULL; return opp_table->shared_opp ? opp_table : NULL;
} }
} }
...@@ -72,24 +74,24 @@ static struct device_opp *_managed_opp(const struct device_node *np) ...@@ -72,24 +74,24 @@ static struct device_opp *_managed_opp(const struct device_node *np)
} }
/** /**
* _find_device_opp() - find device_opp struct using device pointer * _find_opp_table() - find opp_table struct using device pointer
* @dev: device pointer used to lookup device OPPs * @dev: device pointer used to lookup OPP table
* *
* Search list of device OPPs for one containing matching device. Does a RCU * Search OPP table for one containing matching device. Does a RCU reader
* reader operation to grab the pointer needed. * operation to grab the pointer needed.
* *
* Return: pointer to 'struct device_opp' if found, otherwise -ENODEV or * Return: pointer to 'struct opp_table' if found, otherwise -ENODEV or
* -EINVAL based on type of error. * -EINVAL based on type of error.
* *
* Locking: For readers, this function must be called under rcu_read_lock(). * Locking: For readers, this function must be called under rcu_read_lock().
* device_opp is a RCU protected pointer, which means that device_opp is valid * opp_table is a RCU protected pointer, which means that opp_table is valid
* as long as we are under RCU lock. * as long as we are under RCU lock.
* *
* For Writers, this function must be called with dev_opp_list_lock held. * For Writers, this function must be called with opp_table_lock held.
*/ */
struct device_opp *_find_device_opp(struct device *dev) struct opp_table *_find_opp_table(struct device *dev)
{ {
struct device_opp *dev_opp; struct opp_table *opp_table;
opp_rcu_lockdep_assert(); opp_rcu_lockdep_assert();
...@@ -98,9 +100,9 @@ struct device_opp *_find_device_opp(struct device *dev) ...@@ -98,9 +100,9 @@ struct device_opp *_find_device_opp(struct device *dev)
return ERR_PTR(-EINVAL); return ERR_PTR(-EINVAL);
} }
list_for_each_entry_rcu(dev_opp, &dev_opp_list, node) list_for_each_entry_rcu(opp_table, &opp_tables, node)
if (_find_list_dev(dev, dev_opp)) if (_find_opp_dev(dev, opp_table))
return dev_opp; return opp_table;
return ERR_PTR(-ENODEV); return ERR_PTR(-ENODEV);
} }
...@@ -213,22 +215,98 @@ EXPORT_SYMBOL_GPL(dev_pm_opp_is_turbo); ...@@ -213,22 +215,98 @@ EXPORT_SYMBOL_GPL(dev_pm_opp_is_turbo);
*/ */
unsigned long dev_pm_opp_get_max_clock_latency(struct device *dev) unsigned long dev_pm_opp_get_max_clock_latency(struct device *dev)
{ {
struct device_opp *dev_opp; struct opp_table *opp_table;
unsigned long clock_latency_ns; unsigned long clock_latency_ns;
rcu_read_lock(); rcu_read_lock();
dev_opp = _find_device_opp(dev); opp_table = _find_opp_table(dev);
if (IS_ERR(dev_opp)) if (IS_ERR(opp_table))
clock_latency_ns = 0; clock_latency_ns = 0;
else else
clock_latency_ns = dev_opp->clock_latency_ns_max; clock_latency_ns = opp_table->clock_latency_ns_max;
rcu_read_unlock(); rcu_read_unlock();
return clock_latency_ns; return clock_latency_ns;
} }
EXPORT_SYMBOL_GPL(dev_pm_opp_get_max_clock_latency); EXPORT_SYMBOL_GPL(dev_pm_opp_get_max_clock_latency);
/**
* dev_pm_opp_get_max_volt_latency() - Get max voltage latency in nanoseconds
* @dev: device for which we do this operation
*
* Return: This function returns the max voltage latency in nanoseconds.
*
* Locking: This function takes rcu_read_lock().
*/
unsigned long dev_pm_opp_get_max_volt_latency(struct device *dev)
{
struct opp_table *opp_table;
struct dev_pm_opp *opp;
struct regulator *reg;
unsigned long latency_ns = 0;
unsigned long min_uV = ~0, max_uV = 0;
int ret;
rcu_read_lock();
opp_table = _find_opp_table(dev);
if (IS_ERR(opp_table)) {
rcu_read_unlock();
return 0;
}
reg = opp_table->regulator;
if (IS_ERR(reg)) {
/* Regulator may not be required for device */
if (reg)
dev_err(dev, "%s: Invalid regulator (%ld)\n", __func__,
PTR_ERR(reg));
rcu_read_unlock();
return 0;
}
list_for_each_entry_rcu(opp, &opp_table->opp_list, node) {
if (!opp->available)
continue;
if (opp->u_volt_min < min_uV)
min_uV = opp->u_volt_min;
if (opp->u_volt_max > max_uV)
max_uV = opp->u_volt_max;
}
rcu_read_unlock();
/*
* The caller needs to ensure that opp_table (and hence the regulator)
* isn't freed, while we are executing this routine.
*/
ret = regulator_set_voltage_time(reg, min_uV, max_uV);
if (ret > 0)
latency_ns = ret * 1000;
return latency_ns;
}
EXPORT_SYMBOL_GPL(dev_pm_opp_get_max_volt_latency);
/**
* dev_pm_opp_get_max_transition_latency() - Get max transition latency in
* nanoseconds
* @dev: device for which we do this operation
*
* Return: This function returns the max transition latency, in nanoseconds, to
* switch from one OPP to other.
*
* Locking: This function takes rcu_read_lock().
*/
unsigned long dev_pm_opp_get_max_transition_latency(struct device *dev)
{
return dev_pm_opp_get_max_volt_latency(dev) +
dev_pm_opp_get_max_clock_latency(dev);
}
EXPORT_SYMBOL_GPL(dev_pm_opp_get_max_transition_latency);
/** /**
* dev_pm_opp_get_suspend_opp() - Get suspend opp * dev_pm_opp_get_suspend_opp() - Get suspend opp
* @dev: device for which we do this operation * @dev: device for which we do this operation
...@@ -244,21 +322,21 @@ EXPORT_SYMBOL_GPL(dev_pm_opp_get_max_clock_latency); ...@@ -244,21 +322,21 @@ EXPORT_SYMBOL_GPL(dev_pm_opp_get_max_clock_latency);
*/ */
struct dev_pm_opp *dev_pm_opp_get_suspend_opp(struct device *dev) struct dev_pm_opp *dev_pm_opp_get_suspend_opp(struct device *dev)
{ {
struct device_opp *dev_opp; struct opp_table *opp_table;
opp_rcu_lockdep_assert(); opp_rcu_lockdep_assert();
dev_opp = _find_device_opp(dev); opp_table = _find_opp_table(dev);
if (IS_ERR(dev_opp) || !dev_opp->suspend_opp || if (IS_ERR(opp_table) || !opp_table->suspend_opp ||
!dev_opp->suspend_opp->available) !opp_table->suspend_opp->available)
return NULL; return NULL;
return dev_opp->suspend_opp; return opp_table->suspend_opp;
} }
EXPORT_SYMBOL_GPL(dev_pm_opp_get_suspend_opp); EXPORT_SYMBOL_GPL(dev_pm_opp_get_suspend_opp);
/** /**
* dev_pm_opp_get_opp_count() - Get number of opps available in the opp list * dev_pm_opp_get_opp_count() - Get number of opps available in the opp table
* @dev: device for which we do this operation * @dev: device for which we do this operation
* *
* Return: This function returns the number of available opps if there are any, * Return: This function returns the number of available opps if there are any,
...@@ -268,21 +346,21 @@ EXPORT_SYMBOL_GPL(dev_pm_opp_get_suspend_opp); ...@@ -268,21 +346,21 @@ EXPORT_SYMBOL_GPL(dev_pm_opp_get_suspend_opp);
*/ */
int dev_pm_opp_get_opp_count(struct device *dev) int dev_pm_opp_get_opp_count(struct device *dev)
{ {
struct device_opp *dev_opp; struct opp_table *opp_table;
struct dev_pm_opp *temp_opp; struct dev_pm_opp *temp_opp;
int count = 0; int count = 0;
rcu_read_lock(); rcu_read_lock();
dev_opp = _find_device_opp(dev); opp_table = _find_opp_table(dev);
if (IS_ERR(dev_opp)) { if (IS_ERR(opp_table)) {
count = PTR_ERR(dev_opp); count = PTR_ERR(opp_table);
dev_err(dev, "%s: device OPP not found (%d)\n", dev_err(dev, "%s: OPP table not found (%d)\n",
__func__, count); __func__, count);
goto out_unlock; goto out_unlock;
} }
list_for_each_entry_rcu(temp_opp, &dev_opp->opp_list, node) { list_for_each_entry_rcu(temp_opp, &opp_table->opp_list, node) {
if (temp_opp->available) if (temp_opp->available)
count++; count++;
} }
...@@ -299,7 +377,7 @@ EXPORT_SYMBOL_GPL(dev_pm_opp_get_opp_count); ...@@ -299,7 +377,7 @@ EXPORT_SYMBOL_GPL(dev_pm_opp_get_opp_count);
* @freq: frequency to search for * @freq: frequency to search for
* @available: true/false - match for available opp * @available: true/false - match for available opp
* *
* Return: Searches for exact match in the opp list and returns pointer to the * Return: Searches for exact match in the opp table and returns pointer to the
* matching opp if found, else returns ERR_PTR in case of error and should * matching opp if found, else returns ERR_PTR in case of error and should
* be handled using IS_ERR. Error return values can be: * be handled using IS_ERR. Error return values can be:
* EINVAL: for bad pointer * EINVAL: for bad pointer
...@@ -323,19 +401,20 @@ struct dev_pm_opp *dev_pm_opp_find_freq_exact(struct device *dev, ...@@ -323,19 +401,20 @@ struct dev_pm_opp *dev_pm_opp_find_freq_exact(struct device *dev,
unsigned long freq, unsigned long freq,
bool available) bool available)
{ {
struct device_opp *dev_opp; struct opp_table *opp_table;
struct dev_pm_opp *temp_opp, *opp = ERR_PTR(-ERANGE); struct dev_pm_opp *temp_opp, *opp = ERR_PTR(-ERANGE);
opp_rcu_lockdep_assert(); opp_rcu_lockdep_assert();
dev_opp = _find_device_opp(dev); opp_table = _find_opp_table(dev);
if (IS_ERR(dev_opp)) { if (IS_ERR(opp_table)) {
int r = PTR_ERR(dev_opp); int r = PTR_ERR(opp_table);
dev_err(dev, "%s: device OPP not found (%d)\n", __func__, r);
dev_err(dev, "%s: OPP table not found (%d)\n", __func__, r);
return ERR_PTR(r); return ERR_PTR(r);
} }
list_for_each_entry_rcu(temp_opp, &dev_opp->opp_list, node) { list_for_each_entry_rcu(temp_opp, &opp_table->opp_list, node) {
if (temp_opp->available == available && if (temp_opp->available == available &&
temp_opp->rate == freq) { temp_opp->rate == freq) {
opp = temp_opp; opp = temp_opp;
...@@ -371,7 +450,7 @@ EXPORT_SYMBOL_GPL(dev_pm_opp_find_freq_exact); ...@@ -371,7 +450,7 @@ EXPORT_SYMBOL_GPL(dev_pm_opp_find_freq_exact);
struct dev_pm_opp *dev_pm_opp_find_freq_ceil(struct device *dev, struct dev_pm_opp *dev_pm_opp_find_freq_ceil(struct device *dev,
unsigned long *freq) unsigned long *freq)
{ {
struct device_opp *dev_opp; struct opp_table *opp_table;
struct dev_pm_opp *temp_opp, *opp = ERR_PTR(-ERANGE); struct dev_pm_opp *temp_opp, *opp = ERR_PTR(-ERANGE);
opp_rcu_lockdep_assert(); opp_rcu_lockdep_assert();
...@@ -381,11 +460,11 @@ struct dev_pm_opp *dev_pm_opp_find_freq_ceil(struct device *dev, ...@@ -381,11 +460,11 @@ struct dev_pm_opp *dev_pm_opp_find_freq_ceil(struct device *dev,
return ERR_PTR(-EINVAL); return ERR_PTR(-EINVAL);
} }
dev_opp = _find_device_opp(dev); opp_table = _find_opp_table(dev);
if (IS_ERR(dev_opp)) if (IS_ERR(opp_table))
return ERR_CAST(dev_opp); return ERR_CAST(opp_table);
list_for_each_entry_rcu(temp_opp, &dev_opp->opp_list, node) { list_for_each_entry_rcu(temp_opp, &opp_table->opp_list, node) {
if (temp_opp->available && temp_opp->rate >= *freq) { if (temp_opp->available && temp_opp->rate >= *freq) {
opp = temp_opp; opp = temp_opp;
*freq = opp->rate; *freq = opp->rate;
...@@ -421,7 +500,7 @@ EXPORT_SYMBOL_GPL(dev_pm_opp_find_freq_ceil); ...@@ -421,7 +500,7 @@ EXPORT_SYMBOL_GPL(dev_pm_opp_find_freq_ceil);
struct dev_pm_opp *dev_pm_opp_find_freq_floor(struct device *dev, struct dev_pm_opp *dev_pm_opp_find_freq_floor(struct device *dev,
unsigned long *freq) unsigned long *freq)
{ {
struct device_opp *dev_opp; struct opp_table *opp_table;
struct dev_pm_opp *temp_opp, *opp = ERR_PTR(-ERANGE); struct dev_pm_opp *temp_opp, *opp = ERR_PTR(-ERANGE);
opp_rcu_lockdep_assert(); opp_rcu_lockdep_assert();
...@@ -431,11 +510,11 @@ struct dev_pm_opp *dev_pm_opp_find_freq_floor(struct device *dev, ...@@ -431,11 +510,11 @@ struct dev_pm_opp *dev_pm_opp_find_freq_floor(struct device *dev,
return ERR_PTR(-EINVAL); return ERR_PTR(-EINVAL);
} }
dev_opp = _find_device_opp(dev); opp_table = _find_opp_table(dev);
if (IS_ERR(dev_opp)) if (IS_ERR(opp_table))
return ERR_CAST(dev_opp); return ERR_CAST(opp_table);
list_for_each_entry_rcu(temp_opp, &dev_opp->opp_list, node) { list_for_each_entry_rcu(temp_opp, &opp_table->opp_list, node) {
if (temp_opp->available) { if (temp_opp->available) {
/* go to the next node, before choosing prev */ /* go to the next node, before choosing prev */
if (temp_opp->rate > *freq) if (temp_opp->rate > *freq)
...@@ -451,130 +530,343 @@ struct dev_pm_opp *dev_pm_opp_find_freq_floor(struct device *dev, ...@@ -451,130 +530,343 @@ struct dev_pm_opp *dev_pm_opp_find_freq_floor(struct device *dev,
} }
EXPORT_SYMBOL_GPL(dev_pm_opp_find_freq_floor); EXPORT_SYMBOL_GPL(dev_pm_opp_find_freq_floor);
/* List-dev Helpers */ /*
static void _kfree_list_dev_rcu(struct rcu_head *head) * The caller needs to ensure that opp_table (and hence the clk) isn't freed,
* while clk returned here is used.
*/
static struct clk *_get_opp_clk(struct device *dev)
{
struct opp_table *opp_table;
struct clk *clk;
rcu_read_lock();
opp_table = _find_opp_table(dev);
if (IS_ERR(opp_table)) {
dev_err(dev, "%s: device opp doesn't exist\n", __func__);
clk = ERR_CAST(opp_table);
goto unlock;
}
clk = opp_table->clk;
if (IS_ERR(clk))
dev_err(dev, "%s: No clock available for the device\n",
__func__);
unlock:
rcu_read_unlock();
return clk;
}
static int _set_opp_voltage(struct device *dev, struct regulator *reg,
unsigned long u_volt, unsigned long u_volt_min,
unsigned long u_volt_max)
{
int ret;
/* Regulator not available for device */
if (IS_ERR(reg)) {
dev_dbg(dev, "%s: regulator not available: %ld\n", __func__,
PTR_ERR(reg));
return 0;
}
dev_dbg(dev, "%s: voltages (mV): %lu %lu %lu\n", __func__, u_volt_min,
u_volt, u_volt_max);
ret = regulator_set_voltage_triplet(reg, u_volt_min, u_volt,
u_volt_max);
if (ret)
dev_err(dev, "%s: failed to set voltage (%lu %lu %lu mV): %d\n",
__func__, u_volt_min, u_volt, u_volt_max, ret);
return ret;
}
/**
* dev_pm_opp_set_rate() - Configure new OPP based on frequency
* @dev: device for which we do this operation
* @target_freq: frequency to achieve
*
* This configures the power-supplies and clock source to the levels specified
* by the OPP corresponding to the target_freq.
*
* Locking: This function takes rcu_read_lock().
*/
int dev_pm_opp_set_rate(struct device *dev, unsigned long target_freq)
{
struct opp_table *opp_table;
struct dev_pm_opp *old_opp, *opp;
struct regulator *reg;
struct clk *clk;
unsigned long freq, old_freq;
unsigned long u_volt, u_volt_min, u_volt_max;
unsigned long ou_volt, ou_volt_min, ou_volt_max;
int ret;
if (unlikely(!target_freq)) {
dev_err(dev, "%s: Invalid target frequency %lu\n", __func__,
target_freq);
return -EINVAL;
}
clk = _get_opp_clk(dev);
if (IS_ERR(clk))
return PTR_ERR(clk);
freq = clk_round_rate(clk, target_freq);
if ((long)freq <= 0)
freq = target_freq;
old_freq = clk_get_rate(clk);
/* Return early if nothing to do */
if (old_freq == freq) {
dev_dbg(dev, "%s: old/new frequencies (%lu Hz) are same, nothing to do\n",
__func__, freq);
return 0;
}
rcu_read_lock();
opp_table = _find_opp_table(dev);
if (IS_ERR(opp_table)) {
dev_err(dev, "%s: device opp doesn't exist\n", __func__);
rcu_read_unlock();
return PTR_ERR(opp_table);
}
old_opp = dev_pm_opp_find_freq_ceil(dev, &old_freq);
if (!IS_ERR(old_opp)) {
ou_volt = old_opp->u_volt;
ou_volt_min = old_opp->u_volt_min;
ou_volt_max = old_opp->u_volt_max;
} else {
dev_err(dev, "%s: failed to find current OPP for freq %lu (%ld)\n",
__func__, old_freq, PTR_ERR(old_opp));
}
opp = dev_pm_opp_find_freq_ceil(dev, &freq);
if (IS_ERR(opp)) {
ret = PTR_ERR(opp);
dev_err(dev, "%s: failed to find OPP for freq %lu (%d)\n",
__func__, freq, ret);
rcu_read_unlock();
return ret;
}
u_volt = opp->u_volt;
u_volt_min = opp->u_volt_min;
u_volt_max = opp->u_volt_max;
reg = opp_table->regulator;
rcu_read_unlock();
/* Scaling up? Scale voltage before frequency */
if (freq > old_freq) {
ret = _set_opp_voltage(dev, reg, u_volt, u_volt_min,
u_volt_max);
if (ret)
goto restore_voltage;
}
/* Change frequency */
dev_dbg(dev, "%s: switching OPP: %lu Hz --> %lu Hz\n",
__func__, old_freq, freq);
ret = clk_set_rate(clk, freq);
if (ret) {
dev_err(dev, "%s: failed to set clock rate: %d\n", __func__,
ret);
goto restore_voltage;
}
/* Scaling down? Scale voltage after frequency */
if (freq < old_freq) {
ret = _set_opp_voltage(dev, reg, u_volt, u_volt_min,
u_volt_max);
if (ret)
goto restore_freq;
}
return 0;
restore_freq:
if (clk_set_rate(clk, old_freq))
dev_err(dev, "%s: failed to restore old-freq (%lu Hz)\n",
__func__, old_freq);
restore_voltage:
/* This shouldn't harm even if the voltages weren't updated earlier */
if (!IS_ERR(old_opp))
_set_opp_voltage(dev, reg, ou_volt, ou_volt_min, ou_volt_max);
return ret;
}
EXPORT_SYMBOL_GPL(dev_pm_opp_set_rate);
/* OPP-dev Helpers */
static void _kfree_opp_dev_rcu(struct rcu_head *head)
{ {
struct device_list_opp *list_dev; struct opp_device *opp_dev;
list_dev = container_of(head, struct device_list_opp, rcu_head); opp_dev = container_of(head, struct opp_device, rcu_head);
kfree_rcu(list_dev, rcu_head); kfree_rcu(opp_dev, rcu_head);
} }
static void _remove_list_dev(struct device_list_opp *list_dev, static void _remove_opp_dev(struct opp_device *opp_dev,
struct device_opp *dev_opp) struct opp_table *opp_table)
{ {
opp_debug_unregister(list_dev, dev_opp); opp_debug_unregister(opp_dev, opp_table);
list_del(&list_dev->node); list_del(&opp_dev->node);
call_srcu(&dev_opp->srcu_head.srcu, &list_dev->rcu_head, call_srcu(&opp_table->srcu_head.srcu, &opp_dev->rcu_head,
_kfree_list_dev_rcu); _kfree_opp_dev_rcu);
} }
struct device_list_opp *_add_list_dev(const struct device *dev, struct opp_device *_add_opp_dev(const struct device *dev,
struct device_opp *dev_opp) struct opp_table *opp_table)
{ {
struct device_list_opp *list_dev; struct opp_device *opp_dev;
int ret; int ret;
list_dev = kzalloc(sizeof(*list_dev), GFP_KERNEL); opp_dev = kzalloc(sizeof(*opp_dev), GFP_KERNEL);
if (!list_dev) if (!opp_dev)
return NULL; return NULL;
/* Initialize list-dev */ /* Initialize opp-dev */
list_dev->dev = dev; opp_dev->dev = dev;
list_add_rcu(&list_dev->node, &dev_opp->dev_list); list_add_rcu(&opp_dev->node, &opp_table->dev_list);
/* Create debugfs entries for the dev_opp */ /* Create debugfs entries for the opp_table */
ret = opp_debug_register(list_dev, dev_opp); ret = opp_debug_register(opp_dev, opp_table);
if (ret) if (ret)
dev_err(dev, "%s: Failed to register opp debugfs (%d)\n", dev_err(dev, "%s: Failed to register opp debugfs (%d)\n",
__func__, ret); __func__, ret);
return list_dev; return opp_dev;
} }
/** /**
* _add_device_opp() - Find device OPP table or allocate a new one * _add_opp_table() - Find OPP table or allocate a new one
* @dev: device for which we do this operation * @dev: device for which we do this operation
* *
* It tries to find an existing table first, if it couldn't find one, it * It tries to find an existing table first, if it couldn't find one, it
* allocates a new OPP table and returns that. * allocates a new OPP table and returns that.
* *
* Return: valid device_opp pointer if success, else NULL. * Return: valid opp_table pointer if success, else NULL.
*/ */
static struct device_opp *_add_device_opp(struct device *dev) static struct opp_table *_add_opp_table(struct device *dev)
{ {
struct device_opp *dev_opp; struct opp_table *opp_table;
struct device_list_opp *list_dev; struct opp_device *opp_dev;
struct device_node *np;
int ret;
/* Check for existing list for 'dev' first */ /* Check for existing table for 'dev' first */
dev_opp = _find_device_opp(dev); opp_table = _find_opp_table(dev);
if (!IS_ERR(dev_opp)) if (!IS_ERR(opp_table))
return dev_opp; return opp_table;
/* /*
* Allocate a new device OPP table. In the infrequent case where a new * Allocate a new OPP table. In the infrequent case where a new
* device is needed to be added, we pay this penalty. * device is needed to be added, we pay this penalty.
*/ */
dev_opp = kzalloc(sizeof(*dev_opp), GFP_KERNEL); opp_table = kzalloc(sizeof(*opp_table), GFP_KERNEL);
if (!dev_opp) if (!opp_table)
return NULL; return NULL;
INIT_LIST_HEAD(&dev_opp->dev_list); INIT_LIST_HEAD(&opp_table->dev_list);
list_dev = _add_list_dev(dev, dev_opp); opp_dev = _add_opp_dev(dev, opp_table);
if (!list_dev) { if (!opp_dev) {
kfree(dev_opp); kfree(opp_table);
return NULL; return NULL;
} }
srcu_init_notifier_head(&dev_opp->srcu_head); /*
INIT_LIST_HEAD(&dev_opp->opp_list); * Only required for backward compatibility with v1 bindings, but isn't
* harmful for other cases. And so we do it unconditionally.
*/
np = of_node_get(dev->of_node);
if (np) {
u32 val;
if (!of_property_read_u32(np, "clock-latency", &val))
opp_table->clock_latency_ns_max = val;
of_property_read_u32(np, "voltage-tolerance",
&opp_table->voltage_tolerance_v1);
of_node_put(np);
}
/* Set regulator to a non-NULL error value */
opp_table->regulator = ERR_PTR(-ENXIO);
/* Find clk for the device */
opp_table->clk = clk_get(dev, NULL);
if (IS_ERR(opp_table->clk)) {
ret = PTR_ERR(opp_table->clk);
if (ret != -EPROBE_DEFER)
dev_dbg(dev, "%s: Couldn't find clock: %d\n", __func__,
ret);
}
/* Secure the device list modification */ srcu_init_notifier_head(&opp_table->srcu_head);
list_add_rcu(&dev_opp->node, &dev_opp_list); INIT_LIST_HEAD(&opp_table->opp_list);
return dev_opp;
/* Secure the device table modification */
list_add_rcu(&opp_table->node, &opp_tables);
return opp_table;
} }
/** /**
* _kfree_device_rcu() - Free device_opp RCU handler * _kfree_device_rcu() - Free opp_table RCU handler
* @head: RCU head * @head: RCU head
*/ */
static void _kfree_device_rcu(struct rcu_head *head) static void _kfree_device_rcu(struct rcu_head *head)
{ {
struct device_opp *device_opp = container_of(head, struct device_opp, rcu_head); struct opp_table *opp_table = container_of(head, struct opp_table,
rcu_head);
kfree_rcu(device_opp, rcu_head); kfree_rcu(opp_table, rcu_head);
} }
/** /**
* _remove_device_opp() - Removes a device OPP table * _remove_opp_table() - Removes a OPP table
* @dev_opp: device OPP table to be removed. * @opp_table: OPP table to be removed.
* *
* Removes/frees device OPP table it it doesn't contain any OPPs. * Removes/frees OPP table if it doesn't contain any OPPs.
*/ */
static void _remove_device_opp(struct device_opp *dev_opp) static void _remove_opp_table(struct opp_table *opp_table)
{ {
struct device_list_opp *list_dev; struct opp_device *opp_dev;
if (!list_empty(&opp_table->opp_list))
return;
if (!list_empty(&dev_opp->opp_list)) if (opp_table->supported_hw)
return; return;
if (dev_opp->supported_hw) if (opp_table->prop_name)
return; return;
if (dev_opp->prop_name) if (!IS_ERR(opp_table->regulator))
return; return;
list_dev = list_first_entry(&dev_opp->dev_list, struct device_list_opp, /* Release clk */
node); if (!IS_ERR(opp_table->clk))
clk_put(opp_table->clk);
_remove_list_dev(list_dev, dev_opp); opp_dev = list_first_entry(&opp_table->dev_list, struct opp_device,
node);
_remove_opp_dev(opp_dev, opp_table);
/* dev_list must be empty now */ /* dev_list must be empty now */
WARN_ON(!list_empty(&dev_opp->dev_list)); WARN_ON(!list_empty(&opp_table->dev_list));
list_del_rcu(&dev_opp->node); list_del_rcu(&opp_table->node);
call_srcu(&dev_opp->srcu_head.srcu, &dev_opp->rcu_head, call_srcu(&opp_table->srcu_head.srcu, &opp_table->rcu_head,
_kfree_device_rcu); _kfree_device_rcu);
} }
...@@ -591,17 +883,17 @@ static void _kfree_opp_rcu(struct rcu_head *head) ...@@ -591,17 +883,17 @@ static void _kfree_opp_rcu(struct rcu_head *head)
/** /**
* _opp_remove() - Remove an OPP from a table definition * _opp_remove() - Remove an OPP from a table definition
* @dev_opp: points back to the device_opp struct this opp belongs to * @opp_table: points back to the opp_table struct this opp belongs to
* @opp: pointer to the OPP to remove * @opp: pointer to the OPP to remove
* @notify: OPP_EVENT_REMOVE notification should be sent or not * @notify: OPP_EVENT_REMOVE notification should be sent or not
* *
* This function removes an opp definition from the opp list. * This function removes an opp definition from the opp table.
* *
* Locking: The internal device_opp and opp structures are RCU protected. * Locking: The internal opp_table and opp structures are RCU protected.
* It is assumed that the caller holds required mutex for an RCU updater * It is assumed that the caller holds required mutex for an RCU updater
* strategy. * strategy.
*/ */
static void _opp_remove(struct device_opp *dev_opp, static void _opp_remove(struct opp_table *opp_table,
struct dev_pm_opp *opp, bool notify) struct dev_pm_opp *opp, bool notify)
{ {
/* /*
...@@ -609,22 +901,23 @@ static void _opp_remove(struct device_opp *dev_opp, ...@@ -609,22 +901,23 @@ static void _opp_remove(struct device_opp *dev_opp,
* frequency/voltage list. * frequency/voltage list.
*/ */
if (notify) if (notify)
srcu_notifier_call_chain(&dev_opp->srcu_head, OPP_EVENT_REMOVE, opp); srcu_notifier_call_chain(&opp_table->srcu_head,
OPP_EVENT_REMOVE, opp);
opp_debug_remove_one(opp); opp_debug_remove_one(opp);
list_del_rcu(&opp->node); list_del_rcu(&opp->node);
call_srcu(&dev_opp->srcu_head.srcu, &opp->rcu_head, _kfree_opp_rcu); call_srcu(&opp_table->srcu_head.srcu, &opp->rcu_head, _kfree_opp_rcu);
_remove_device_opp(dev_opp); _remove_opp_table(opp_table);
} }
/** /**
* dev_pm_opp_remove() - Remove an OPP from OPP list * dev_pm_opp_remove() - Remove an OPP from OPP table
* @dev: device for which we do this operation * @dev: device for which we do this operation
* @freq: OPP to remove with matching 'freq' * @freq: OPP to remove with matching 'freq'
* *
* This function removes an opp from the opp list. * This function removes an opp from the opp table.
* *
* Locking: The internal device_opp and opp structures are RCU protected. * Locking: The internal opp_table and opp structures are RCU protected.
* Hence this function internally uses RCU updater strategy with mutex locks * Hence this function internally uses RCU updater strategy with mutex locks
* to keep the integrity of the internal data structures. Callers should ensure * to keep the integrity of the internal data structures. Callers should ensure
* that this function is *NOT* called under RCU protection or in contexts where * that this function is *NOT* called under RCU protection or in contexts where
...@@ -633,17 +926,17 @@ static void _opp_remove(struct device_opp *dev_opp, ...@@ -633,17 +926,17 @@ static void _opp_remove(struct device_opp *dev_opp,
void dev_pm_opp_remove(struct device *dev, unsigned long freq) void dev_pm_opp_remove(struct device *dev, unsigned long freq)
{ {
struct dev_pm_opp *opp; struct dev_pm_opp *opp;
struct device_opp *dev_opp; struct opp_table *opp_table;
bool found = false; bool found = false;
/* Hold our list modification lock here */ /* Hold our table modification lock here */
mutex_lock(&dev_opp_list_lock); mutex_lock(&opp_table_lock);
dev_opp = _find_device_opp(dev); opp_table = _find_opp_table(dev);
if (IS_ERR(dev_opp)) if (IS_ERR(opp_table))
goto unlock; goto unlock;
list_for_each_entry(opp, &dev_opp->opp_list, node) { list_for_each_entry(opp, &opp_table->opp_list, node) {
if (opp->rate == freq) { if (opp->rate == freq) {
found = true; found = true;
break; break;
...@@ -656,14 +949,14 @@ void dev_pm_opp_remove(struct device *dev, unsigned long freq) ...@@ -656,14 +949,14 @@ void dev_pm_opp_remove(struct device *dev, unsigned long freq)
goto unlock; goto unlock;
} }
_opp_remove(dev_opp, opp, true); _opp_remove(opp_table, opp, true);
unlock: unlock:
mutex_unlock(&dev_opp_list_lock); mutex_unlock(&opp_table_lock);
} }
EXPORT_SYMBOL_GPL(dev_pm_opp_remove); EXPORT_SYMBOL_GPL(dev_pm_opp_remove);
static struct dev_pm_opp *_allocate_opp(struct device *dev, static struct dev_pm_opp *_allocate_opp(struct device *dev,
struct device_opp **dev_opp) struct opp_table **opp_table)
{ {
struct dev_pm_opp *opp; struct dev_pm_opp *opp;
...@@ -674,8 +967,8 @@ static struct dev_pm_opp *_allocate_opp(struct device *dev, ...@@ -674,8 +967,8 @@ static struct dev_pm_opp *_allocate_opp(struct device *dev,
INIT_LIST_HEAD(&opp->node); INIT_LIST_HEAD(&opp->node);
*dev_opp = _add_device_opp(dev); *opp_table = _add_opp_table(dev);
if (!*dev_opp) { if (!*opp_table) {
kfree(opp); kfree(opp);
return NULL; return NULL;
} }
...@@ -683,22 +976,38 @@ static struct dev_pm_opp *_allocate_opp(struct device *dev, ...@@ -683,22 +976,38 @@ static struct dev_pm_opp *_allocate_opp(struct device *dev,
return opp; return opp;
} }
static bool _opp_supported_by_regulators(struct dev_pm_opp *opp,
struct opp_table *opp_table)
{
struct regulator *reg = opp_table->regulator;
if (!IS_ERR(reg) &&
!regulator_is_supported_voltage(reg, opp->u_volt_min,
opp->u_volt_max)) {
pr_warn("%s: OPP minuV: %lu maxuV: %lu, not supported by regulator\n",
__func__, opp->u_volt_min, opp->u_volt_max);
return false;
}
return true;
}
static int _opp_add(struct device *dev, struct dev_pm_opp *new_opp, static int _opp_add(struct device *dev, struct dev_pm_opp *new_opp,
struct device_opp *dev_opp) struct opp_table *opp_table)
{ {
struct dev_pm_opp *opp; struct dev_pm_opp *opp;
struct list_head *head = &dev_opp->opp_list; struct list_head *head = &opp_table->opp_list;
int ret; int ret;
/* /*
* Insert new OPP in order of increasing frequency and discard if * Insert new OPP in order of increasing frequency and discard if
* already present. * already present.
* *
* Need to use &dev_opp->opp_list in the condition part of the 'for' * Need to use &opp_table->opp_list in the condition part of the 'for'
* loop, don't replace it with head otherwise it will become an infinite * loop, don't replace it with head otherwise it will become an infinite
* loop. * loop.
*/ */
list_for_each_entry_rcu(opp, &dev_opp->opp_list, node) { list_for_each_entry_rcu(opp, &opp_table->opp_list, node) {
if (new_opp->rate > opp->rate) { if (new_opp->rate > opp->rate) {
head = &opp->node; head = &opp->node;
continue; continue;
...@@ -716,14 +1025,20 @@ static int _opp_add(struct device *dev, struct dev_pm_opp *new_opp, ...@@ -716,14 +1025,20 @@ static int _opp_add(struct device *dev, struct dev_pm_opp *new_opp,
0 : -EEXIST; 0 : -EEXIST;
} }
new_opp->dev_opp = dev_opp; new_opp->opp_table = opp_table;
list_add_rcu(&new_opp->node, head); list_add_rcu(&new_opp->node, head);
ret = opp_debug_create_one(new_opp, dev_opp); ret = opp_debug_create_one(new_opp, opp_table);
if (ret) if (ret)
dev_err(dev, "%s: Failed to register opp to debugfs (%d)\n", dev_err(dev, "%s: Failed to register opp to debugfs (%d)\n",
__func__, ret); __func__, ret);
if (!_opp_supported_by_regulators(new_opp, opp_table)) {
new_opp->available = false;
dev_warn(dev, "%s: OPP not supported by regulators (%lu)\n",
__func__, new_opp->rate);
}
return 0; return 0;
} }
...@@ -734,14 +1049,14 @@ static int _opp_add(struct device *dev, struct dev_pm_opp *new_opp, ...@@ -734,14 +1049,14 @@ static int _opp_add(struct device *dev, struct dev_pm_opp *new_opp,
* @u_volt: Voltage in uVolts for this OPP * @u_volt: Voltage in uVolts for this OPP
* @dynamic: Dynamically added OPPs. * @dynamic: Dynamically added OPPs.
* *
* This function adds an opp definition to the opp list and returns status. * This function adds an opp definition to the opp table and returns status.
* The opp is made available by default and it can be controlled using * The opp is made available by default and it can be controlled using
* dev_pm_opp_enable/disable functions and may be removed by dev_pm_opp_remove. * dev_pm_opp_enable/disable functions and may be removed by dev_pm_opp_remove.
* *
* NOTE: "dynamic" parameter impacts OPPs added by the dev_pm_opp_of_add_table * NOTE: "dynamic" parameter impacts OPPs added by the dev_pm_opp_of_add_table
* and freed by dev_pm_opp_of_remove_table. * and freed by dev_pm_opp_of_remove_table.
* *
* Locking: The internal device_opp and opp structures are RCU protected. * Locking: The internal opp_table and opp structures are RCU protected.
* Hence this function internally uses RCU updater strategy with mutex locks * Hence this function internally uses RCU updater strategy with mutex locks
* to keep the integrity of the internal data structures. Callers should ensure * to keep the integrity of the internal data structures. Callers should ensure
* that this function is *NOT* called under RCU protection or in contexts where * that this function is *NOT* called under RCU protection or in contexts where
...@@ -757,14 +1072,15 @@ static int _opp_add(struct device *dev, struct dev_pm_opp *new_opp, ...@@ -757,14 +1072,15 @@ static int _opp_add(struct device *dev, struct dev_pm_opp *new_opp,
static int _opp_add_v1(struct device *dev, unsigned long freq, long u_volt, static int _opp_add_v1(struct device *dev, unsigned long freq, long u_volt,
bool dynamic) bool dynamic)
{ {
struct device_opp *dev_opp; struct opp_table *opp_table;
struct dev_pm_opp *new_opp; struct dev_pm_opp *new_opp;
unsigned long tol;
int ret; int ret;
/* Hold our list modification lock here */ /* Hold our table modification lock here */
mutex_lock(&dev_opp_list_lock); mutex_lock(&opp_table_lock);
new_opp = _allocate_opp(dev, &dev_opp); new_opp = _allocate_opp(dev, &opp_table);
if (!new_opp) { if (!new_opp) {
ret = -ENOMEM; ret = -ENOMEM;
goto unlock; goto unlock;
...@@ -772,33 +1088,36 @@ static int _opp_add_v1(struct device *dev, unsigned long freq, long u_volt, ...@@ -772,33 +1088,36 @@ static int _opp_add_v1(struct device *dev, unsigned long freq, long u_volt,
/* populate the opp table */ /* populate the opp table */
new_opp->rate = freq; new_opp->rate = freq;
tol = u_volt * opp_table->voltage_tolerance_v1 / 100;
new_opp->u_volt = u_volt; new_opp->u_volt = u_volt;
new_opp->u_volt_min = u_volt - tol;
new_opp->u_volt_max = u_volt + tol;
new_opp->available = true; new_opp->available = true;
new_opp->dynamic = dynamic; new_opp->dynamic = dynamic;
ret = _opp_add(dev, new_opp, dev_opp); ret = _opp_add(dev, new_opp, opp_table);
if (ret) if (ret)
goto free_opp; goto free_opp;
mutex_unlock(&dev_opp_list_lock); mutex_unlock(&opp_table_lock);
/* /*
* Notify the changes in the availability of the operable * Notify the changes in the availability of the operable
* frequency/voltage list. * frequency/voltage list.
*/ */
srcu_notifier_call_chain(&dev_opp->srcu_head, OPP_EVENT_ADD, new_opp); srcu_notifier_call_chain(&opp_table->srcu_head, OPP_EVENT_ADD, new_opp);
return 0; return 0;
free_opp: free_opp:
_opp_remove(dev_opp, new_opp, false); _opp_remove(opp_table, new_opp, false);
unlock: unlock:
mutex_unlock(&dev_opp_list_lock); mutex_unlock(&opp_table_lock);
return ret; return ret;
} }
/* TODO: Support multiple regulators */ /* TODO: Support multiple regulators */
static int opp_parse_supplies(struct dev_pm_opp *opp, struct device *dev, static int opp_parse_supplies(struct dev_pm_opp *opp, struct device *dev,
struct device_opp *dev_opp) struct opp_table *opp_table)
{ {
u32 microvolt[3] = {0}; u32 microvolt[3] = {0};
u32 val; u32 val;
...@@ -807,9 +1126,9 @@ static int opp_parse_supplies(struct dev_pm_opp *opp, struct device *dev, ...@@ -807,9 +1126,9 @@ static int opp_parse_supplies(struct dev_pm_opp *opp, struct device *dev,
char name[NAME_MAX]; char name[NAME_MAX];
/* Search for "opp-microvolt-<name>" */ /* Search for "opp-microvolt-<name>" */
if (dev_opp->prop_name) { if (opp_table->prop_name) {
snprintf(name, sizeof(name), "opp-microvolt-%s", snprintf(name, sizeof(name), "opp-microvolt-%s",
dev_opp->prop_name); opp_table->prop_name);
prop = of_find_property(opp->np, name, NULL); prop = of_find_property(opp->np, name, NULL);
} }
...@@ -844,14 +1163,20 @@ static int opp_parse_supplies(struct dev_pm_opp *opp, struct device *dev, ...@@ -844,14 +1163,20 @@ static int opp_parse_supplies(struct dev_pm_opp *opp, struct device *dev,
} }
opp->u_volt = microvolt[0]; opp->u_volt = microvolt[0];
opp->u_volt_min = microvolt[1];
opp->u_volt_max = microvolt[2]; if (count == 1) {
opp->u_volt_min = opp->u_volt;
opp->u_volt_max = opp->u_volt;
} else {
opp->u_volt_min = microvolt[1];
opp->u_volt_max = microvolt[2];
}
/* Search for "opp-microamp-<name>" */ /* Search for "opp-microamp-<name>" */
prop = NULL; prop = NULL;
if (dev_opp->prop_name) { if (opp_table->prop_name) {
snprintf(name, sizeof(name), "opp-microamp-%s", snprintf(name, sizeof(name), "opp-microamp-%s",
dev_opp->prop_name); opp_table->prop_name);
prop = of_find_property(opp->np, name, NULL); prop = of_find_property(opp->np, name, NULL);
} }
...@@ -878,7 +1203,7 @@ static int opp_parse_supplies(struct dev_pm_opp *opp, struct device *dev, ...@@ -878,7 +1203,7 @@ static int opp_parse_supplies(struct dev_pm_opp *opp, struct device *dev,
* OPPs, which are available for those versions, based on its 'opp-supported-hw' * OPPs, which are available for those versions, based on its 'opp-supported-hw'
* property. * property.
* *
* Locking: The internal device_opp and opp structures are RCU protected. * Locking: The internal opp_table and opp structures are RCU protected.
* Hence this function internally uses RCU updater strategy with mutex locks * Hence this function internally uses RCU updater strategy with mutex locks
* to keep the integrity of the internal data structures. Callers should ensure * to keep the integrity of the internal data structures. Callers should ensure
* that this function is *NOT* called under RCU protection or in contexts where * that this function is *NOT* called under RCU protection or in contexts where
...@@ -887,44 +1212,44 @@ static int opp_parse_supplies(struct dev_pm_opp *opp, struct device *dev, ...@@ -887,44 +1212,44 @@ static int opp_parse_supplies(struct dev_pm_opp *opp, struct device *dev,
int dev_pm_opp_set_supported_hw(struct device *dev, const u32 *versions, int dev_pm_opp_set_supported_hw(struct device *dev, const u32 *versions,
unsigned int count) unsigned int count)
{ {
struct device_opp *dev_opp; struct opp_table *opp_table;
int ret = 0; int ret = 0;
/* Hold our list modification lock here */ /* Hold our table modification lock here */
mutex_lock(&dev_opp_list_lock); mutex_lock(&opp_table_lock);
dev_opp = _add_device_opp(dev); opp_table = _add_opp_table(dev);
if (!dev_opp) { if (!opp_table) {
ret = -ENOMEM; ret = -ENOMEM;
goto unlock; goto unlock;
} }
/* Make sure there are no concurrent readers while updating dev_opp */ /* Make sure there are no concurrent readers while updating opp_table */
WARN_ON(!list_empty(&dev_opp->opp_list)); WARN_ON(!list_empty(&opp_table->opp_list));
/* Do we already have a version hierarchy associated with dev_opp? */ /* Do we already have a version hierarchy associated with opp_table? */
if (dev_opp->supported_hw) { if (opp_table->supported_hw) {
dev_err(dev, "%s: Already have supported hardware list\n", dev_err(dev, "%s: Already have supported hardware list\n",
__func__); __func__);
ret = -EBUSY; ret = -EBUSY;
goto err; goto err;
} }
dev_opp->supported_hw = kmemdup(versions, count * sizeof(*versions), opp_table->supported_hw = kmemdup(versions, count * sizeof(*versions),
GFP_KERNEL); GFP_KERNEL);
if (!dev_opp->supported_hw) { if (!opp_table->supported_hw) {
ret = -ENOMEM; ret = -ENOMEM;
goto err; goto err;
} }
dev_opp->supported_hw_count = count; opp_table->supported_hw_count = count;
mutex_unlock(&dev_opp_list_lock); mutex_unlock(&opp_table_lock);
return 0; return 0;
err: err:
_remove_device_opp(dev_opp); _remove_opp_table(opp_table);
unlock: unlock:
mutex_unlock(&dev_opp_list_lock); mutex_unlock(&opp_table_lock);
return ret; return ret;
} }
...@@ -932,13 +1257,13 @@ EXPORT_SYMBOL_GPL(dev_pm_opp_set_supported_hw); ...@@ -932,13 +1257,13 @@ EXPORT_SYMBOL_GPL(dev_pm_opp_set_supported_hw);
/** /**
* dev_pm_opp_put_supported_hw() - Releases resources blocked for supported hw * dev_pm_opp_put_supported_hw() - Releases resources blocked for supported hw
* @dev: Device for which supported-hw has to be set. * @dev: Device for which supported-hw has to be put.
* *
* This is required only for the V2 bindings, and is called for a matching * This is required only for the V2 bindings, and is called for a matching
* dev_pm_opp_set_supported_hw(). Until this is called, the device_opp structure * dev_pm_opp_set_supported_hw(). Until this is called, the opp_table structure
* will not be freed. * will not be freed.
* *
* Locking: The internal device_opp and opp structures are RCU protected. * Locking: The internal opp_table and opp structures are RCU protected.
* Hence this function internally uses RCU updater strategy with mutex locks * Hence this function internally uses RCU updater strategy with mutex locks
* to keep the integrity of the internal data structures. Callers should ensure * to keep the integrity of the internal data structures. Callers should ensure
* that this function is *NOT* called under RCU protection or in contexts where * that this function is *NOT* called under RCU protection or in contexts where
...@@ -946,42 +1271,43 @@ EXPORT_SYMBOL_GPL(dev_pm_opp_set_supported_hw); ...@@ -946,42 +1271,43 @@ EXPORT_SYMBOL_GPL(dev_pm_opp_set_supported_hw);
*/ */
void dev_pm_opp_put_supported_hw(struct device *dev) void dev_pm_opp_put_supported_hw(struct device *dev)
{ {
struct device_opp *dev_opp; struct opp_table *opp_table;
/* Hold our list modification lock here */ /* Hold our table modification lock here */
mutex_lock(&dev_opp_list_lock); mutex_lock(&opp_table_lock);
/* Check for existing list for 'dev' first */ /* Check for existing table for 'dev' first */
dev_opp = _find_device_opp(dev); opp_table = _find_opp_table(dev);
if (IS_ERR(dev_opp)) { if (IS_ERR(opp_table)) {
dev_err(dev, "Failed to find dev_opp: %ld\n", PTR_ERR(dev_opp)); dev_err(dev, "Failed to find opp_table: %ld\n",
PTR_ERR(opp_table));
goto unlock; goto unlock;
} }
/* Make sure there are no concurrent readers while updating dev_opp */ /* Make sure there are no concurrent readers while updating opp_table */
WARN_ON(!list_empty(&dev_opp->opp_list)); WARN_ON(!list_empty(&opp_table->opp_list));
if (!dev_opp->supported_hw) { if (!opp_table->supported_hw) {
dev_err(dev, "%s: Doesn't have supported hardware list\n", dev_err(dev, "%s: Doesn't have supported hardware list\n",
__func__); __func__);
goto unlock; goto unlock;
} }
kfree(dev_opp->supported_hw); kfree(opp_table->supported_hw);
dev_opp->supported_hw = NULL; opp_table->supported_hw = NULL;
dev_opp->supported_hw_count = 0; opp_table->supported_hw_count = 0;
/* Try freeing device_opp if this was the last blocking resource */ /* Try freeing opp_table if this was the last blocking resource */
_remove_device_opp(dev_opp); _remove_opp_table(opp_table);
unlock: unlock:
mutex_unlock(&dev_opp_list_lock); mutex_unlock(&opp_table_lock);
} }
EXPORT_SYMBOL_GPL(dev_pm_opp_put_supported_hw); EXPORT_SYMBOL_GPL(dev_pm_opp_put_supported_hw);
/** /**
* dev_pm_opp_set_prop_name() - Set prop-extn name * dev_pm_opp_set_prop_name() - Set prop-extn name
* @dev: Device for which the regulator has to be set. * @dev: Device for which the prop-name has to be set.
* @name: name to postfix to properties. * @name: name to postfix to properties.
* *
* This is required only for the V2 bindings, and it enables a platform to * This is required only for the V2 bindings, and it enables a platform to
...@@ -989,7 +1315,7 @@ EXPORT_SYMBOL_GPL(dev_pm_opp_put_supported_hw); ...@@ -989,7 +1315,7 @@ EXPORT_SYMBOL_GPL(dev_pm_opp_put_supported_hw);
* which the extension will apply are opp-microvolt and opp-microamp. OPP core * which the extension will apply are opp-microvolt and opp-microamp. OPP core
* should postfix the property name with -<name> while looking for them. * should postfix the property name with -<name> while looking for them.
* *
* Locking: The internal device_opp and opp structures are RCU protected. * Locking: The internal opp_table and opp structures are RCU protected.
* Hence this function internally uses RCU updater strategy with mutex locks * Hence this function internally uses RCU updater strategy with mutex locks
* to keep the integrity of the internal data structures. Callers should ensure * to keep the integrity of the internal data structures. Callers should ensure
* that this function is *NOT* called under RCU protection or in contexts where * that this function is *NOT* called under RCU protection or in contexts where
...@@ -997,42 +1323,42 @@ EXPORT_SYMBOL_GPL(dev_pm_opp_put_supported_hw); ...@@ -997,42 +1323,42 @@ EXPORT_SYMBOL_GPL(dev_pm_opp_put_supported_hw);
*/ */
int dev_pm_opp_set_prop_name(struct device *dev, const char *name) int dev_pm_opp_set_prop_name(struct device *dev, const char *name)
{ {
struct device_opp *dev_opp; struct opp_table *opp_table;
int ret = 0; int ret = 0;
/* Hold our list modification lock here */ /* Hold our table modification lock here */
mutex_lock(&dev_opp_list_lock); mutex_lock(&opp_table_lock);
dev_opp = _add_device_opp(dev); opp_table = _add_opp_table(dev);
if (!dev_opp) { if (!opp_table) {
ret = -ENOMEM; ret = -ENOMEM;
goto unlock; goto unlock;
} }
/* Make sure there are no concurrent readers while updating dev_opp */ /* Make sure there are no concurrent readers while updating opp_table */
WARN_ON(!list_empty(&dev_opp->opp_list)); WARN_ON(!list_empty(&opp_table->opp_list));
/* Do we already have a prop-name associated with dev_opp? */ /* Do we already have a prop-name associated with opp_table? */
if (dev_opp->prop_name) { if (opp_table->prop_name) {
dev_err(dev, "%s: Already have prop-name %s\n", __func__, dev_err(dev, "%s: Already have prop-name %s\n", __func__,
dev_opp->prop_name); opp_table->prop_name);
ret = -EBUSY; ret = -EBUSY;
goto err; goto err;
} }
dev_opp->prop_name = kstrdup(name, GFP_KERNEL); opp_table->prop_name = kstrdup(name, GFP_KERNEL);
if (!dev_opp->prop_name) { if (!opp_table->prop_name) {
ret = -ENOMEM; ret = -ENOMEM;
goto err; goto err;
} }
mutex_unlock(&dev_opp_list_lock); mutex_unlock(&opp_table_lock);
return 0; return 0;
err: err:
_remove_device_opp(dev_opp); _remove_opp_table(opp_table);
unlock: unlock:
mutex_unlock(&dev_opp_list_lock); mutex_unlock(&opp_table_lock);
return ret; return ret;
} }
...@@ -1040,13 +1366,13 @@ EXPORT_SYMBOL_GPL(dev_pm_opp_set_prop_name); ...@@ -1040,13 +1366,13 @@ EXPORT_SYMBOL_GPL(dev_pm_opp_set_prop_name);
/** /**
* dev_pm_opp_put_prop_name() - Releases resources blocked for prop-name * dev_pm_opp_put_prop_name() - Releases resources blocked for prop-name
* @dev: Device for which the regulator has to be set. * @dev: Device for which the prop-name has to be put.
* *
* This is required only for the V2 bindings, and is called for a matching * This is required only for the V2 bindings, and is called for a matching
* dev_pm_opp_set_prop_name(). Until this is called, the device_opp structure * dev_pm_opp_set_prop_name(). Until this is called, the opp_table structure
* will not be freed. * will not be freed.
* *
* Locking: The internal device_opp and opp structures are RCU protected. * Locking: The internal opp_table and opp structures are RCU protected.
* Hence this function internally uses RCU updater strategy with mutex locks * Hence this function internally uses RCU updater strategy with mutex locks
* to keep the integrity of the internal data structures. Callers should ensure * to keep the integrity of the internal data structures. Callers should ensure
* that this function is *NOT* called under RCU protection or in contexts where * that this function is *NOT* called under RCU protection or in contexts where
...@@ -1054,45 +1380,154 @@ EXPORT_SYMBOL_GPL(dev_pm_opp_set_prop_name); ...@@ -1054,45 +1380,154 @@ EXPORT_SYMBOL_GPL(dev_pm_opp_set_prop_name);
*/ */
void dev_pm_opp_put_prop_name(struct device *dev) void dev_pm_opp_put_prop_name(struct device *dev)
{ {
struct device_opp *dev_opp; struct opp_table *opp_table;
/* Hold our list modification lock here */ /* Hold our table modification lock here */
mutex_lock(&dev_opp_list_lock); mutex_lock(&opp_table_lock);
/* Check for existing list for 'dev' first */ /* Check for existing table for 'dev' first */
dev_opp = _find_device_opp(dev); opp_table = _find_opp_table(dev);
if (IS_ERR(dev_opp)) { if (IS_ERR(opp_table)) {
dev_err(dev, "Failed to find dev_opp: %ld\n", PTR_ERR(dev_opp)); dev_err(dev, "Failed to find opp_table: %ld\n",
PTR_ERR(opp_table));
goto unlock; goto unlock;
} }
/* Make sure there are no concurrent readers while updating dev_opp */ /* Make sure there are no concurrent readers while updating opp_table */
WARN_ON(!list_empty(&dev_opp->opp_list)); WARN_ON(!list_empty(&opp_table->opp_list));
if (!dev_opp->prop_name) { if (!opp_table->prop_name) {
dev_err(dev, "%s: Doesn't have a prop-name\n", __func__); dev_err(dev, "%s: Doesn't have a prop-name\n", __func__);
goto unlock; goto unlock;
} }
kfree(dev_opp->prop_name); kfree(opp_table->prop_name);
dev_opp->prop_name = NULL; opp_table->prop_name = NULL;
/* Try freeing device_opp if this was the last blocking resource */ /* Try freeing opp_table if this was the last blocking resource */
_remove_device_opp(dev_opp); _remove_opp_table(opp_table);
unlock: unlock:
mutex_unlock(&dev_opp_list_lock); mutex_unlock(&opp_table_lock);
} }
EXPORT_SYMBOL_GPL(dev_pm_opp_put_prop_name); EXPORT_SYMBOL_GPL(dev_pm_opp_put_prop_name);
static bool _opp_is_supported(struct device *dev, struct device_opp *dev_opp, /**
* dev_pm_opp_set_regulator() - Set regulator name for the device
* @dev: Device for which regulator name is being set.
* @name: Name of the regulator.
*
* In order to support OPP switching, OPP layer needs to know the name of the
* device's regulator, as the core would be required to switch voltages as well.
*
* This must be called before any OPPs are initialized for the device.
*
* Locking: The internal opp_table and opp structures are RCU protected.
* Hence this function internally uses RCU updater strategy with mutex locks
* to keep the integrity of the internal data structures. Callers should ensure
* that this function is *NOT* called under RCU protection or in contexts where
* mutex cannot be locked.
*/
int dev_pm_opp_set_regulator(struct device *dev, const char *name)
{
struct opp_table *opp_table;
struct regulator *reg;
int ret;
mutex_lock(&opp_table_lock);
opp_table = _add_opp_table(dev);
if (!opp_table) {
ret = -ENOMEM;
goto unlock;
}
/* This should be called before OPPs are initialized */
if (WARN_ON(!list_empty(&opp_table->opp_list))) {
ret = -EBUSY;
goto err;
}
/* Already have a regulator set */
if (WARN_ON(!IS_ERR(opp_table->regulator))) {
ret = -EBUSY;
goto err;
}
/* Allocate the regulator */
reg = regulator_get_optional(dev, name);
if (IS_ERR(reg)) {
ret = PTR_ERR(reg);
if (ret != -EPROBE_DEFER)
dev_err(dev, "%s: no regulator (%s) found: %d\n",
__func__, name, ret);
goto err;
}
opp_table->regulator = reg;
mutex_unlock(&opp_table_lock);
return 0;
err:
_remove_opp_table(opp_table);
unlock:
mutex_unlock(&opp_table_lock);
return ret;
}
EXPORT_SYMBOL_GPL(dev_pm_opp_set_regulator);
/**
* dev_pm_opp_put_regulator() - Releases resources blocked for regulator
* @dev: Device for which regulator was set.
*
* Locking: The internal opp_table and opp structures are RCU protected.
* Hence this function internally uses RCU updater strategy with mutex locks
* to keep the integrity of the internal data structures. Callers should ensure
* that this function is *NOT* called under RCU protection or in contexts where
* mutex cannot be locked.
*/
void dev_pm_opp_put_regulator(struct device *dev)
{
struct opp_table *opp_table;
mutex_lock(&opp_table_lock);
/* Check for existing table for 'dev' first */
opp_table = _find_opp_table(dev);
if (IS_ERR(opp_table)) {
dev_err(dev, "Failed to find opp_table: %ld\n",
PTR_ERR(opp_table));
goto unlock;
}
if (IS_ERR(opp_table->regulator)) {
dev_err(dev, "%s: Doesn't have regulator set\n", __func__);
goto unlock;
}
/* Make sure there are no concurrent readers while updating opp_table */
WARN_ON(!list_empty(&opp_table->opp_list));
regulator_put(opp_table->regulator);
opp_table->regulator = ERR_PTR(-ENXIO);
/* Try freeing opp_table if this was the last blocking resource */
_remove_opp_table(opp_table);
unlock:
mutex_unlock(&opp_table_lock);
}
EXPORT_SYMBOL_GPL(dev_pm_opp_put_regulator);
static bool _opp_is_supported(struct device *dev, struct opp_table *opp_table,
struct device_node *np) struct device_node *np)
{ {
unsigned int count = dev_opp->supported_hw_count; unsigned int count = opp_table->supported_hw_count;
u32 version; u32 version;
int ret; int ret;
if (!dev_opp->supported_hw) if (!opp_table->supported_hw)
return true; return true;
while (count--) { while (count--) {
...@@ -1105,7 +1540,7 @@ static bool _opp_is_supported(struct device *dev, struct device_opp *dev_opp, ...@@ -1105,7 +1540,7 @@ static bool _opp_is_supported(struct device *dev, struct device_opp *dev_opp,
} }
/* Both of these are bitwise masks of the versions */ /* Both of these are bitwise masks of the versions */
if (!(version & dev_opp->supported_hw[count])) if (!(version & opp_table->supported_hw[count]))
return false; return false;
} }
...@@ -1117,11 +1552,11 @@ static bool _opp_is_supported(struct device *dev, struct device_opp *dev_opp, ...@@ -1117,11 +1552,11 @@ static bool _opp_is_supported(struct device *dev, struct device_opp *dev_opp,
* @dev: device for which we do this operation * @dev: device for which we do this operation
* @np: device node * @np: device node
* *
* This function adds an opp definition to the opp list and returns status. The * This function adds an opp definition to the opp table and returns status. The
* opp can be controlled using dev_pm_opp_enable/disable functions and may be * opp can be controlled using dev_pm_opp_enable/disable functions and may be
* removed by dev_pm_opp_remove. * removed by dev_pm_opp_remove.
* *
* Locking: The internal device_opp and opp structures are RCU protected. * Locking: The internal opp_table and opp structures are RCU protected.
* Hence this function internally uses RCU updater strategy with mutex locks * Hence this function internally uses RCU updater strategy with mutex locks
* to keep the integrity of the internal data structures. Callers should ensure * to keep the integrity of the internal data structures. Callers should ensure
* that this function is *NOT* called under RCU protection or in contexts where * that this function is *NOT* called under RCU protection or in contexts where
...@@ -1137,16 +1572,16 @@ static bool _opp_is_supported(struct device *dev, struct device_opp *dev_opp, ...@@ -1137,16 +1572,16 @@ static bool _opp_is_supported(struct device *dev, struct device_opp *dev_opp,
*/ */
static int _opp_add_static_v2(struct device *dev, struct device_node *np) static int _opp_add_static_v2(struct device *dev, struct device_node *np)
{ {
struct device_opp *dev_opp; struct opp_table *opp_table;
struct dev_pm_opp *new_opp; struct dev_pm_opp *new_opp;
u64 rate; u64 rate;
u32 val; u32 val;
int ret; int ret;
/* Hold our list modification lock here */ /* Hold our table modification lock here */
mutex_lock(&dev_opp_list_lock); mutex_lock(&opp_table_lock);
new_opp = _allocate_opp(dev, &dev_opp); new_opp = _allocate_opp(dev, &opp_table);
if (!new_opp) { if (!new_opp) {
ret = -ENOMEM; ret = -ENOMEM;
goto unlock; goto unlock;
...@@ -1159,7 +1594,7 @@ static int _opp_add_static_v2(struct device *dev, struct device_node *np) ...@@ -1159,7 +1594,7 @@ static int _opp_add_static_v2(struct device *dev, struct device_node *np)
} }
/* 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, dev_opp, 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);
goto free_opp; goto free_opp;
} }
...@@ -1179,30 +1614,30 @@ static int _opp_add_static_v2(struct device *dev, struct device_node *np) ...@@ -1179,30 +1614,30 @@ static int _opp_add_static_v2(struct device *dev, struct device_node *np)
if (!of_property_read_u32(np, "clock-latency-ns", &val)) if (!of_property_read_u32(np, "clock-latency-ns", &val))
new_opp->clock_latency_ns = val; new_opp->clock_latency_ns = val;
ret = opp_parse_supplies(new_opp, dev, dev_opp); ret = opp_parse_supplies(new_opp, dev, opp_table);
if (ret) if (ret)
goto free_opp; goto free_opp;
ret = _opp_add(dev, new_opp, dev_opp); ret = _opp_add(dev, new_opp, opp_table);
if (ret) if (ret)
goto free_opp; goto free_opp;
/* OPP to select on device suspend */ /* OPP to select on device suspend */
if (of_property_read_bool(np, "opp-suspend")) { if (of_property_read_bool(np, "opp-suspend")) {
if (dev_opp->suspend_opp) { if (opp_table->suspend_opp) {
dev_warn(dev, "%s: Multiple suspend OPPs found (%lu %lu)\n", dev_warn(dev, "%s: Multiple suspend OPPs found (%lu %lu)\n",
__func__, dev_opp->suspend_opp->rate, __func__, opp_table->suspend_opp->rate,
new_opp->rate); new_opp->rate);
} else { } else {
new_opp->suspend = true; new_opp->suspend = true;
dev_opp->suspend_opp = new_opp; opp_table->suspend_opp = new_opp;
} }
} }
if (new_opp->clock_latency_ns > dev_opp->clock_latency_ns_max) if (new_opp->clock_latency_ns > opp_table->clock_latency_ns_max)
dev_opp->clock_latency_ns_max = new_opp->clock_latency_ns; opp_table->clock_latency_ns_max = new_opp->clock_latency_ns;
mutex_unlock(&dev_opp_list_lock); mutex_unlock(&opp_table_lock);
pr_debug("%s: turbo:%d rate:%lu uv:%lu uvmin:%lu uvmax:%lu latency:%lu\n", pr_debug("%s: turbo:%d rate:%lu uv:%lu uvmin:%lu uvmax:%lu latency:%lu\n",
__func__, new_opp->turbo, new_opp->rate, new_opp->u_volt, __func__, new_opp->turbo, new_opp->rate, new_opp->u_volt,
...@@ -1213,13 +1648,13 @@ static int _opp_add_static_v2(struct device *dev, struct device_node *np) ...@@ -1213,13 +1648,13 @@ static int _opp_add_static_v2(struct device *dev, struct device_node *np)
* Notify the changes in the availability of the operable * Notify the changes in the availability of the operable
* frequency/voltage list. * frequency/voltage list.
*/ */
srcu_notifier_call_chain(&dev_opp->srcu_head, OPP_EVENT_ADD, new_opp); srcu_notifier_call_chain(&opp_table->srcu_head, OPP_EVENT_ADD, new_opp);
return 0; return 0;
free_opp: free_opp:
_opp_remove(dev_opp, new_opp, false); _opp_remove(opp_table, new_opp, false);
unlock: unlock:
mutex_unlock(&dev_opp_list_lock); mutex_unlock(&opp_table_lock);
return ret; return ret;
} }
...@@ -1229,11 +1664,11 @@ static int _opp_add_static_v2(struct device *dev, struct device_node *np) ...@@ -1229,11 +1664,11 @@ static int _opp_add_static_v2(struct device *dev, struct device_node *np)
* @freq: Frequency in Hz for this OPP * @freq: Frequency in Hz for this OPP
* @u_volt: Voltage in uVolts for this OPP * @u_volt: Voltage in uVolts for this OPP
* *
* This function adds an opp definition to the opp list and returns status. * This function adds an opp definition to the opp table and returns status.
* The opp is made available by default and it can be controlled using * The opp is made available by default and it can be controlled using
* dev_pm_opp_enable/disable functions. * dev_pm_opp_enable/disable functions.
* *
* Locking: The internal device_opp and opp structures are RCU protected. * Locking: The internal opp_table and opp structures are RCU protected.
* Hence this function internally uses RCU updater strategy with mutex locks * Hence this function internally uses RCU updater strategy with mutex locks
* to keep the integrity of the internal data structures. Callers should ensure * to keep the integrity of the internal data structures. Callers should ensure
* that this function is *NOT* called under RCU protection or in contexts where * that this function is *NOT* called under RCU protection or in contexts where
...@@ -1265,7 +1700,7 @@ EXPORT_SYMBOL_GPL(dev_pm_opp_add); ...@@ -1265,7 +1700,7 @@ EXPORT_SYMBOL_GPL(dev_pm_opp_add);
* copy operation, returns 0 if no modification was done OR modification was * copy operation, returns 0 if no modification was done OR modification was
* successful. * successful.
* *
* Locking: The internal device_opp and opp structures are RCU protected. * Locking: The internal opp_table and opp structures are RCU protected.
* Hence this function internally uses RCU updater strategy with mutex locks to * Hence this function internally uses RCU updater strategy with mutex locks to
* keep the integrity of the internal data structures. Callers should ensure * keep the integrity of the internal data structures. Callers should ensure
* that this function is *NOT* called under RCU protection or in contexts where * that this function is *NOT* called under RCU protection or in contexts where
...@@ -1274,7 +1709,7 @@ EXPORT_SYMBOL_GPL(dev_pm_opp_add); ...@@ -1274,7 +1709,7 @@ EXPORT_SYMBOL_GPL(dev_pm_opp_add);
static int _opp_set_availability(struct device *dev, unsigned long freq, static int _opp_set_availability(struct device *dev, unsigned long freq,
bool availability_req) bool availability_req)
{ {
struct device_opp *dev_opp; struct opp_table *opp_table;
struct dev_pm_opp *new_opp, *tmp_opp, *opp = ERR_PTR(-ENODEV); struct dev_pm_opp *new_opp, *tmp_opp, *opp = ERR_PTR(-ENODEV);
int r = 0; int r = 0;
...@@ -1283,18 +1718,18 @@ static int _opp_set_availability(struct device *dev, unsigned long freq, ...@@ -1283,18 +1718,18 @@ static int _opp_set_availability(struct device *dev, unsigned long freq,
if (!new_opp) if (!new_opp)
return -ENOMEM; return -ENOMEM;
mutex_lock(&dev_opp_list_lock); mutex_lock(&opp_table_lock);
/* Find the device_opp */ /* Find the opp_table */
dev_opp = _find_device_opp(dev); opp_table = _find_opp_table(dev);
if (IS_ERR(dev_opp)) { if (IS_ERR(opp_table)) {
r = PTR_ERR(dev_opp); r = PTR_ERR(opp_table);
dev_warn(dev, "%s: Device OPP not found (%d)\n", __func__, r); dev_warn(dev, "%s: Device OPP not found (%d)\n", __func__, r);
goto unlock; goto unlock;
} }
/* Do we have the frequency? */ /* Do we have the frequency? */
list_for_each_entry(tmp_opp, &dev_opp->opp_list, node) { list_for_each_entry(tmp_opp, &opp_table->opp_list, node) {
if (tmp_opp->rate == freq) { if (tmp_opp->rate == freq) {
opp = tmp_opp; opp = tmp_opp;
break; break;
...@@ -1315,21 +1750,21 @@ static int _opp_set_availability(struct device *dev, unsigned long freq, ...@@ -1315,21 +1750,21 @@ static int _opp_set_availability(struct device *dev, unsigned long freq,
new_opp->available = availability_req; new_opp->available = availability_req;
list_replace_rcu(&opp->node, &new_opp->node); list_replace_rcu(&opp->node, &new_opp->node);
mutex_unlock(&dev_opp_list_lock); mutex_unlock(&opp_table_lock);
call_srcu(&dev_opp->srcu_head.srcu, &opp->rcu_head, _kfree_opp_rcu); call_srcu(&opp_table->srcu_head.srcu, &opp->rcu_head, _kfree_opp_rcu);
/* Notify the change of the OPP availability */ /* Notify the change of the OPP availability */
if (availability_req) if (availability_req)
srcu_notifier_call_chain(&dev_opp->srcu_head, OPP_EVENT_ENABLE, srcu_notifier_call_chain(&opp_table->srcu_head,
new_opp); OPP_EVENT_ENABLE, new_opp);
else else
srcu_notifier_call_chain(&dev_opp->srcu_head, OPP_EVENT_DISABLE, srcu_notifier_call_chain(&opp_table->srcu_head,
new_opp); OPP_EVENT_DISABLE, new_opp);
return 0; return 0;
unlock: unlock:
mutex_unlock(&dev_opp_list_lock); mutex_unlock(&opp_table_lock);
kfree(new_opp); kfree(new_opp);
return r; return r;
} }
...@@ -1343,7 +1778,7 @@ static int _opp_set_availability(struct device *dev, unsigned long freq, ...@@ -1343,7 +1778,7 @@ static int _opp_set_availability(struct device *dev, unsigned long freq,
* corresponding error value. It is meant to be used for users an OPP available * corresponding error value. It is meant to be used for users an OPP available
* after being temporarily made unavailable with dev_pm_opp_disable. * after being temporarily made unavailable with dev_pm_opp_disable.
* *
* Locking: The internal device_opp and opp structures are RCU protected. * Locking: The internal opp_table and opp structures are RCU protected.
* Hence this function indirectly uses RCU and mutex locks to keep the * Hence this function indirectly uses RCU and mutex locks to keep the
* integrity of the internal data structures. Callers should ensure that * integrity of the internal data structures. Callers should ensure that
* this function is *NOT* called under RCU protection or in contexts where * this function is *NOT* called under RCU protection or in contexts where
...@@ -1369,7 +1804,7 @@ EXPORT_SYMBOL_GPL(dev_pm_opp_enable); ...@@ -1369,7 +1804,7 @@ EXPORT_SYMBOL_GPL(dev_pm_opp_enable);
* control by users to make this OPP not available until the circumstances are * control by users to make this OPP not available until the circumstances are
* right to make it available again (with a call to dev_pm_opp_enable). * right to make it available again (with a call to dev_pm_opp_enable).
* *
* Locking: The internal device_opp and opp structures are RCU protected. * Locking: The internal opp_table and opp structures are RCU protected.
* Hence this function indirectly uses RCU and mutex locks to keep the * Hence this function indirectly uses RCU and mutex locks to keep the
* integrity of the internal data structures. Callers should ensure that * integrity of the internal data structures. Callers should ensure that
* this function is *NOT* called under RCU protection or in contexts where * this function is *NOT* called under RCU protection or in contexts where
...@@ -1387,26 +1822,26 @@ EXPORT_SYMBOL_GPL(dev_pm_opp_disable); ...@@ -1387,26 +1822,26 @@ EXPORT_SYMBOL_GPL(dev_pm_opp_disable);
/** /**
* dev_pm_opp_get_notifier() - find notifier_head of the device with opp * dev_pm_opp_get_notifier() - find notifier_head of the device with opp
* @dev: device pointer used to lookup device OPPs. * @dev: device pointer used to lookup OPP table.
* *
* Return: pointer to notifier head if found, otherwise -ENODEV or * Return: pointer to notifier head if found, otherwise -ENODEV or
* -EINVAL based on type of error casted as pointer. value must be checked * -EINVAL based on type of error casted as pointer. value must be checked
* with IS_ERR to determine valid pointer or error result. * with IS_ERR to determine valid pointer or error result.
* *
* Locking: This function must be called under rcu_read_lock(). dev_opp is a RCU * Locking: This function must be called under rcu_read_lock(). opp_table is a
* protected pointer. The reason for the same is that the opp pointer which is * RCU protected pointer. The reason for the same is that the opp pointer which
* returned will remain valid for use with opp_get_{voltage, freq} only while * is returned will remain valid for use with opp_get_{voltage, freq} only while
* under the locked area. The pointer returned must be used prior to unlocking * under the locked area. The pointer returned must be used prior to unlocking
* with rcu_read_unlock() to maintain the integrity of the pointer. * with rcu_read_unlock() to maintain the integrity of the pointer.
*/ */
struct srcu_notifier_head *dev_pm_opp_get_notifier(struct device *dev) struct srcu_notifier_head *dev_pm_opp_get_notifier(struct device *dev)
{ {
struct device_opp *dev_opp = _find_device_opp(dev); struct opp_table *opp_table = _find_opp_table(dev);
if (IS_ERR(dev_opp)) if (IS_ERR(opp_table))
return ERR_CAST(dev_opp); /* matching type */ return ERR_CAST(opp_table); /* matching type */
return &dev_opp->srcu_head; return &opp_table->srcu_head;
} }
EXPORT_SYMBOL_GPL(dev_pm_opp_get_notifier); EXPORT_SYMBOL_GPL(dev_pm_opp_get_notifier);
...@@ -1414,11 +1849,11 @@ EXPORT_SYMBOL_GPL(dev_pm_opp_get_notifier); ...@@ -1414,11 +1849,11 @@ EXPORT_SYMBOL_GPL(dev_pm_opp_get_notifier);
/** /**
* dev_pm_opp_of_remove_table() - Free OPP table entries created from static DT * dev_pm_opp_of_remove_table() - Free OPP table entries created from static DT
* entries * entries
* @dev: device pointer used to lookup device OPPs. * @dev: device pointer used to lookup OPP table.
* *
* Free OPPs created using static entries present in DT. * Free OPPs created using static entries present in DT.
* *
* Locking: The internal device_opp and opp structures are RCU protected. * Locking: The internal opp_table and opp structures are RCU protected.
* Hence this function indirectly uses RCU updater strategy with mutex locks * Hence this function indirectly uses RCU updater strategy with mutex locks
* to keep the integrity of the internal data structures. Callers should ensure * to keep the integrity of the internal data structures. Callers should ensure
* that this function is *NOT* called under RCU protection or in contexts where * that this function is *NOT* called under RCU protection or in contexts where
...@@ -1426,38 +1861,38 @@ EXPORT_SYMBOL_GPL(dev_pm_opp_get_notifier); ...@@ -1426,38 +1861,38 @@ EXPORT_SYMBOL_GPL(dev_pm_opp_get_notifier);
*/ */
void dev_pm_opp_of_remove_table(struct device *dev) void dev_pm_opp_of_remove_table(struct device *dev)
{ {
struct device_opp *dev_opp; struct opp_table *opp_table;
struct dev_pm_opp *opp, *tmp; struct dev_pm_opp *opp, *tmp;
/* Hold our list modification lock here */ /* Hold our table modification lock here */
mutex_lock(&dev_opp_list_lock); mutex_lock(&opp_table_lock);
/* Check for existing list for 'dev' */ /* Check for existing table for 'dev' */
dev_opp = _find_device_opp(dev); opp_table = _find_opp_table(dev);
if (IS_ERR(dev_opp)) { if (IS_ERR(opp_table)) {
int error = PTR_ERR(dev_opp); int error = PTR_ERR(opp_table);
if (error != -ENODEV) if (error != -ENODEV)
WARN(1, "%s: dev_opp: %d\n", WARN(1, "%s: opp_table: %d\n",
IS_ERR_OR_NULL(dev) ? IS_ERR_OR_NULL(dev) ?
"Invalid device" : dev_name(dev), "Invalid device" : dev_name(dev),
error); error);
goto unlock; goto unlock;
} }
/* Find if dev_opp manages a single device */ /* Find if opp_table manages a single device */
if (list_is_singular(&dev_opp->dev_list)) { if (list_is_singular(&opp_table->dev_list)) {
/* Free static OPPs */ /* Free static OPPs */
list_for_each_entry_safe(opp, tmp, &dev_opp->opp_list, node) { list_for_each_entry_safe(opp, tmp, &opp_table->opp_list, node) {
if (!opp->dynamic) if (!opp->dynamic)
_opp_remove(dev_opp, opp, true); _opp_remove(opp_table, opp, true);
} }
} else { } else {
_remove_list_dev(_find_list_dev(dev, dev_opp), dev_opp); _remove_opp_dev(_find_opp_dev(dev, opp_table), opp_table);
} }
unlock: unlock:
mutex_unlock(&dev_opp_list_lock); mutex_unlock(&opp_table_lock);
} }
EXPORT_SYMBOL_GPL(dev_pm_opp_of_remove_table); EXPORT_SYMBOL_GPL(dev_pm_opp_of_remove_table);
...@@ -1478,22 +1913,22 @@ struct device_node *_of_get_opp_desc_node(struct device *dev) ...@@ -1478,22 +1913,22 @@ struct device_node *_of_get_opp_desc_node(struct device *dev)
static int _of_add_opp_table_v2(struct device *dev, struct device_node *opp_np) static int _of_add_opp_table_v2(struct device *dev, struct device_node *opp_np)
{ {
struct device_node *np; struct device_node *np;
struct device_opp *dev_opp; struct opp_table *opp_table;
int ret = 0, count = 0; int ret = 0, count = 0;
mutex_lock(&dev_opp_list_lock); mutex_lock(&opp_table_lock);
dev_opp = _managed_opp(opp_np); opp_table = _managed_opp(opp_np);
if (dev_opp) { if (opp_table) {
/* OPPs are already managed */ /* OPPs are already managed */
if (!_add_list_dev(dev, dev_opp)) if (!_add_opp_dev(dev, opp_table))
ret = -ENOMEM; ret = -ENOMEM;
mutex_unlock(&dev_opp_list_lock); mutex_unlock(&opp_table_lock);
return ret; return ret;
} }
mutex_unlock(&dev_opp_list_lock); mutex_unlock(&opp_table_lock);
/* We have opp-list node now, iterate over it and add OPPs */ /* We have opp-table node now, iterate over it and add OPPs */
for_each_available_child_of_node(opp_np, np) { for_each_available_child_of_node(opp_np, np) {
count++; count++;
...@@ -1509,19 +1944,19 @@ static int _of_add_opp_table_v2(struct device *dev, struct device_node *opp_np) ...@@ -1509,19 +1944,19 @@ static int _of_add_opp_table_v2(struct device *dev, struct device_node *opp_np)
if (WARN_ON(!count)) if (WARN_ON(!count))
return -ENOENT; return -ENOENT;
mutex_lock(&dev_opp_list_lock); mutex_lock(&opp_table_lock);
dev_opp = _find_device_opp(dev); opp_table = _find_opp_table(dev);
if (WARN_ON(IS_ERR(dev_opp))) { if (WARN_ON(IS_ERR(opp_table))) {
ret = PTR_ERR(dev_opp); ret = PTR_ERR(opp_table);
mutex_unlock(&dev_opp_list_lock); mutex_unlock(&opp_table_lock);
goto free_table; goto free_table;
} }
dev_opp->np = opp_np; opp_table->np = opp_np;
dev_opp->shared_opp = of_property_read_bool(opp_np, "opp-shared"); opp_table->shared_opp = of_property_read_bool(opp_np, "opp-shared");
mutex_unlock(&dev_opp_list_lock); mutex_unlock(&opp_table_lock);
return 0; return 0;
...@@ -1550,7 +1985,7 @@ static int _of_add_opp_table_v1(struct device *dev) ...@@ -1550,7 +1985,7 @@ static int _of_add_opp_table_v1(struct device *dev)
*/ */
nr = prop->length / sizeof(u32); nr = prop->length / sizeof(u32);
if (nr % 2) { if (nr % 2) {
dev_err(dev, "%s: Invalid OPP list\n", __func__); dev_err(dev, "%s: Invalid OPP table\n", __func__);
return -EINVAL; return -EINVAL;
} }
...@@ -1570,11 +2005,11 @@ static int _of_add_opp_table_v1(struct device *dev) ...@@ -1570,11 +2005,11 @@ static int _of_add_opp_table_v1(struct device *dev)
/** /**
* dev_pm_opp_of_add_table() - Initialize opp table from device tree * dev_pm_opp_of_add_table() - Initialize opp table from device tree
* @dev: device pointer used to lookup device OPPs. * @dev: device pointer used to lookup OPP table.
* *
* Register the initial OPP table with the OPP library for given device. * Register the initial OPP table with the OPP library for given device.
* *
* Locking: The internal device_opp and opp structures are RCU protected. * Locking: The internal opp_table and opp structures are RCU protected.
* Hence this function indirectly uses RCU updater strategy with mutex locks * Hence this function indirectly uses RCU updater strategy with mutex locks
* to keep the integrity of the internal data structures. Callers should ensure * to keep the integrity of the internal data structures. Callers should ensure
* that this function is *NOT* called under RCU protection or in contexts where * that this function is *NOT* called under RCU protection or in contexts where
......
...@@ -31,7 +31,7 @@ ...@@ -31,7 +31,7 @@
* @table: Cpufreq table returned back to caller * @table: Cpufreq table returned back to caller
* *
* Generate a cpufreq table for a provided device- this assumes that the * Generate a cpufreq table for a provided device- this assumes that the
* opp list is already initialized and ready for usage. * opp table is already initialized and ready for usage.
* *
* This function allocates required memory for the cpufreq table. It is * This function allocates required memory for the cpufreq table. It is
* expected that the caller does the required maintenance such as freeing * expected that the caller does the required maintenance such as freeing
...@@ -44,7 +44,7 @@ ...@@ -44,7 +44,7 @@
* WARNING: It is important for the callers to ensure refreshing their copy of * WARNING: It is important for the callers to ensure refreshing their copy of
* the table if any of the mentioned functions have been invoked in the interim. * the table if any of the mentioned functions have been invoked in the interim.
* *
* Locking: The internal device_opp and opp structures are RCU protected. * Locking: The internal opp_table and opp structures are RCU protected.
* Since we just use the regular accessor functions to access the internal data * Since we just use the regular accessor functions to access the internal data
* structures, we use RCU read lock inside this function. As a result, users of * structures, we use RCU read lock inside this function. As a result, users of
* this function DONOT need to use explicit locks for invoking. * this function DONOT need to use explicit locks for invoking.
...@@ -122,15 +122,15 @@ EXPORT_SYMBOL_GPL(dev_pm_opp_free_cpufreq_table); ...@@ -122,15 +122,15 @@ EXPORT_SYMBOL_GPL(dev_pm_opp_free_cpufreq_table);
/* Required only for V1 bindings, as v2 can manage it from DT itself */ /* Required only for V1 bindings, as v2 can manage it from DT itself */
int dev_pm_opp_set_sharing_cpus(struct device *cpu_dev, cpumask_var_t cpumask) int dev_pm_opp_set_sharing_cpus(struct device *cpu_dev, cpumask_var_t cpumask)
{ {
struct device_list_opp *list_dev; struct opp_device *opp_dev;
struct device_opp *dev_opp; struct opp_table *opp_table;
struct device *dev; struct device *dev;
int cpu, ret = 0; int cpu, ret = 0;
mutex_lock(&dev_opp_list_lock); mutex_lock(&opp_table_lock);
dev_opp = _find_device_opp(cpu_dev); opp_table = _find_opp_table(cpu_dev);
if (IS_ERR(dev_opp)) { if (IS_ERR(opp_table)) {
ret = -EINVAL; ret = -EINVAL;
goto unlock; goto unlock;
} }
...@@ -146,15 +146,15 @@ int dev_pm_opp_set_sharing_cpus(struct device *cpu_dev, cpumask_var_t cpumask) ...@@ -146,15 +146,15 @@ int dev_pm_opp_set_sharing_cpus(struct device *cpu_dev, cpumask_var_t cpumask)
continue; continue;
} }
list_dev = _add_list_dev(dev, dev_opp); opp_dev = _add_opp_dev(dev, opp_table);
if (!list_dev) { if (!opp_dev) {
dev_err(dev, "%s: failed to add list-dev for cpu%d device\n", dev_err(dev, "%s: failed to add opp-dev for cpu%d device\n",
__func__, cpu); __func__, cpu);
continue; continue;
} }
} }
unlock: unlock:
mutex_unlock(&dev_opp_list_lock); mutex_unlock(&opp_table_lock);
return ret; return ret;
} }
......
...@@ -34,9 +34,9 @@ void opp_debug_remove_one(struct dev_pm_opp *opp) ...@@ -34,9 +34,9 @@ void opp_debug_remove_one(struct dev_pm_opp *opp)
debugfs_remove_recursive(opp->dentry); debugfs_remove_recursive(opp->dentry);
} }
int opp_debug_create_one(struct dev_pm_opp *opp, struct device_opp *dev_opp) int opp_debug_create_one(struct dev_pm_opp *opp, struct opp_table *opp_table)
{ {
struct dentry *pdentry = dev_opp->dentry; struct dentry *pdentry = opp_table->dentry;
struct dentry *d; struct dentry *d;
char name[25]; /* 20 chars for 64 bit value + 5 (opp:\0) */ char name[25]; /* 20 chars for 64 bit value + 5 (opp:\0) */
...@@ -83,52 +83,52 @@ int opp_debug_create_one(struct dev_pm_opp *opp, struct device_opp *dev_opp) ...@@ -83,52 +83,52 @@ int opp_debug_create_one(struct dev_pm_opp *opp, struct device_opp *dev_opp)
return 0; return 0;
} }
static int device_opp_debug_create_dir(struct device_list_opp *list_dev, static int opp_list_debug_create_dir(struct opp_device *opp_dev,
struct device_opp *dev_opp) struct opp_table *opp_table)
{ {
const struct device *dev = list_dev->dev; const struct device *dev = opp_dev->dev;
struct dentry *d; struct dentry *d;
opp_set_dev_name(dev, dev_opp->dentry_name); opp_set_dev_name(dev, opp_table->dentry_name);
/* Create device specific directory */ /* Create device specific directory */
d = debugfs_create_dir(dev_opp->dentry_name, rootdir); d = debugfs_create_dir(opp_table->dentry_name, rootdir);
if (!d) { if (!d) {
dev_err(dev, "%s: Failed to create debugfs dir\n", __func__); dev_err(dev, "%s: Failed to create debugfs dir\n", __func__);
return -ENOMEM; return -ENOMEM;
} }
list_dev->dentry = d; opp_dev->dentry = d;
dev_opp->dentry = d; opp_table->dentry = d;
return 0; return 0;
} }
static int device_opp_debug_create_link(struct device_list_opp *list_dev, static int opp_list_debug_create_link(struct opp_device *opp_dev,
struct device_opp *dev_opp) struct opp_table *opp_table)
{ {
const struct device *dev = list_dev->dev; const struct device *dev = opp_dev->dev;
char name[NAME_MAX]; char name[NAME_MAX];
struct dentry *d; struct dentry *d;
opp_set_dev_name(list_dev->dev, name); opp_set_dev_name(opp_dev->dev, name);
/* Create device specific directory link */ /* Create device specific directory link */
d = debugfs_create_symlink(name, rootdir, dev_opp->dentry_name); d = debugfs_create_symlink(name, rootdir, opp_table->dentry_name);
if (!d) { if (!d) {
dev_err(dev, "%s: Failed to create link\n", __func__); dev_err(dev, "%s: Failed to create link\n", __func__);
return -ENOMEM; return -ENOMEM;
} }
list_dev->dentry = d; opp_dev->dentry = d;
return 0; return 0;
} }
/** /**
* opp_debug_register - add a device opp node to the debugfs 'opp' directory * opp_debug_register - add a device opp node to the debugfs 'opp' directory
* @list_dev: list-dev pointer for device * @opp_dev: opp-dev pointer for device
* @dev_opp: the device-opp being added * @opp_table: the device-opp being added
* *
* Dynamically adds device specific directory in debugfs 'opp' directory. If the * Dynamically adds device specific directory in debugfs 'opp' directory. If the
* device-opp is shared with other devices, then links will be created for all * device-opp is shared with other devices, then links will be created for all
...@@ -136,73 +136,72 @@ static int device_opp_debug_create_link(struct device_list_opp *list_dev, ...@@ -136,73 +136,72 @@ static int device_opp_debug_create_link(struct device_list_opp *list_dev,
* *
* Return: 0 on success, otherwise negative error. * Return: 0 on success, otherwise negative error.
*/ */
int opp_debug_register(struct device_list_opp *list_dev, int opp_debug_register(struct opp_device *opp_dev, struct opp_table *opp_table)
struct device_opp *dev_opp)
{ {
if (!rootdir) { if (!rootdir) {
pr_debug("%s: Uninitialized rootdir\n", __func__); pr_debug("%s: Uninitialized rootdir\n", __func__);
return -EINVAL; return -EINVAL;
} }
if (dev_opp->dentry) if (opp_table->dentry)
return device_opp_debug_create_link(list_dev, dev_opp); return opp_list_debug_create_link(opp_dev, opp_table);
return device_opp_debug_create_dir(list_dev, dev_opp); return opp_list_debug_create_dir(opp_dev, opp_table);
} }
static void opp_migrate_dentry(struct device_list_opp *list_dev, static void opp_migrate_dentry(struct opp_device *opp_dev,
struct device_opp *dev_opp) struct opp_table *opp_table)
{ {
struct device_list_opp *new_dev; struct opp_device *new_dev;
const struct device *dev; const struct device *dev;
struct dentry *dentry; struct dentry *dentry;
/* Look for next list-dev */ /* Look for next opp-dev */
list_for_each_entry(new_dev, &dev_opp->dev_list, node) list_for_each_entry(new_dev, &opp_table->dev_list, node)
if (new_dev != list_dev) if (new_dev != opp_dev)
break; break;
/* new_dev is guaranteed to be valid here */ /* new_dev is guaranteed to be valid here */
dev = new_dev->dev; dev = new_dev->dev;
debugfs_remove_recursive(new_dev->dentry); debugfs_remove_recursive(new_dev->dentry);
opp_set_dev_name(dev, dev_opp->dentry_name); opp_set_dev_name(dev, opp_table->dentry_name);
dentry = debugfs_rename(rootdir, list_dev->dentry, rootdir, dentry = debugfs_rename(rootdir, opp_dev->dentry, rootdir,
dev_opp->dentry_name); opp_table->dentry_name);
if (!dentry) { if (!dentry) {
dev_err(dev, "%s: Failed to rename link from: %s to %s\n", dev_err(dev, "%s: Failed to rename link from: %s to %s\n",
__func__, dev_name(list_dev->dev), dev_name(dev)); __func__, dev_name(opp_dev->dev), dev_name(dev));
return; return;
} }
new_dev->dentry = dentry; new_dev->dentry = dentry;
dev_opp->dentry = dentry; opp_table->dentry = dentry;
} }
/** /**
* opp_debug_unregister - remove a device opp node from debugfs opp directory * opp_debug_unregister - remove a device opp node from debugfs opp directory
* @list_dev: list-dev pointer for device * @opp_dev: opp-dev pointer for device
* @dev_opp: the device-opp being removed * @opp_table: the device-opp being removed
* *
* Dynamically removes device specific directory from debugfs 'opp' directory. * Dynamically removes device specific directory from debugfs 'opp' directory.
*/ */
void opp_debug_unregister(struct device_list_opp *list_dev, void opp_debug_unregister(struct opp_device *opp_dev,
struct device_opp *dev_opp) struct opp_table *opp_table)
{ {
if (list_dev->dentry == dev_opp->dentry) { if (opp_dev->dentry == opp_table->dentry) {
/* Move the real dentry object under another device */ /* Move the real dentry object under another device */
if (!list_is_singular(&dev_opp->dev_list)) { if (!list_is_singular(&opp_table->dev_list)) {
opp_migrate_dentry(list_dev, dev_opp); opp_migrate_dentry(opp_dev, opp_table);
goto out; goto out;
} }
dev_opp->dentry = NULL; opp_table->dentry = NULL;
} }
debugfs_remove_recursive(list_dev->dentry); debugfs_remove_recursive(opp_dev->dentry);
out: out:
list_dev->dentry = NULL; opp_dev->dentry = NULL;
} }
static int __init opp_debug_init(void) static int __init opp_debug_init(void)
......
...@@ -22,13 +22,16 @@ ...@@ -22,13 +22,16 @@
#include <linux/rculist.h> #include <linux/rculist.h>
#include <linux/rcupdate.h> #include <linux/rcupdate.h>
struct clk;
struct regulator;
/* Lock to allow exclusive modification to the device and opp lists */ /* Lock to allow exclusive modification to the device and opp lists */
extern struct mutex dev_opp_list_lock; extern struct mutex opp_table_lock;
/* /*
* Internal data structure organization with the OPP layer library is as * Internal data structure organization with the OPP layer library is as
* follows: * follows:
* dev_opp_list (root) * opp_tables (root)
* |- device 1 (represents voltage domain 1) * |- device 1 (represents voltage domain 1)
* | |- opp 1 (availability, freq, voltage) * | |- opp 1 (availability, freq, voltage)
* | |- opp 2 .. * | |- opp 2 ..
...@@ -37,18 +40,18 @@ extern struct mutex dev_opp_list_lock; ...@@ -37,18 +40,18 @@ extern struct mutex dev_opp_list_lock;
* |- device 2 (represents the next voltage domain) * |- device 2 (represents the next voltage domain)
* ... * ...
* `- device m (represents mth voltage domain) * `- device m (represents mth voltage domain)
* device 1, 2.. are represented by dev_opp structure while each opp * device 1, 2.. are represented by opp_table structure while each opp
* is represented by the opp structure. * is represented by the opp structure.
*/ */
/** /**
* struct dev_pm_opp - Generic OPP description structure * struct dev_pm_opp - Generic OPP description structure
* @node: opp list node. The nodes are maintained throughout the lifetime * @node: opp table node. The nodes are maintained throughout the lifetime
* of boot. It is expected only an optimal set of OPPs are * of boot. It is expected only an optimal set of OPPs are
* added to the library by the SoC framework. * added to the library by the SoC framework.
* RCU usage: opp list is traversed with RCU locks. node * RCU usage: opp table is traversed with RCU locks. node
* modification is possible realtime, hence the modifications * modification is possible realtime, hence the modifications
* are protected by the dev_opp_list_lock for integrity. * are protected by the opp_table_lock for integrity.
* IMPORTANT: the opp nodes should be maintained in increasing * IMPORTANT: the opp nodes should be maintained in increasing
* order. * order.
* @available: true/false - marks if this OPP as available or not * @available: true/false - marks if this OPP as available or not
...@@ -62,7 +65,7 @@ extern struct mutex dev_opp_list_lock; ...@@ -62,7 +65,7 @@ extern struct mutex dev_opp_list_lock;
* @u_amp: Maximum current drawn by the device in microamperes * @u_amp: Maximum current drawn by the device in microamperes
* @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.
* @dev_opp: points back to the device_opp struct this opp belongs to * @opp_table: points back to the opp_table struct this opp belongs to
* @rcu_head: RCU callback head used for deferred freeing * @rcu_head: RCU callback head used for deferred freeing
* @np: OPP's device node. * @np: OPP's device node.
* @dentry: debugfs dentry pointer (per opp) * @dentry: debugfs dentry pointer (per opp)
...@@ -84,7 +87,7 @@ struct dev_pm_opp { ...@@ -84,7 +87,7 @@ struct dev_pm_opp {
unsigned long u_amp; unsigned long u_amp;
unsigned long clock_latency_ns; unsigned long clock_latency_ns;
struct device_opp *dev_opp; struct opp_table *opp_table;
struct rcu_head rcu_head; struct rcu_head rcu_head;
struct device_node *np; struct device_node *np;
...@@ -95,16 +98,16 @@ struct dev_pm_opp { ...@@ -95,16 +98,16 @@ struct dev_pm_opp {
}; };
/** /**
* struct device_list_opp - devices managed by 'struct device_opp' * struct opp_device - devices managed by 'struct opp_table'
* @node: list node * @node: list node
* @dev: device to which the struct object belongs * @dev: device to which the struct object belongs
* @rcu_head: RCU callback head used for deferred freeing * @rcu_head: RCU callback head used for deferred freeing
* @dentry: debugfs dentry pointer (per device) * @dentry: debugfs dentry pointer (per device)
* *
* This is an internal data structure maintaining the list of devices that are * This is an internal data structure maintaining the devices that are managed
* managed by 'struct device_opp'. * by 'struct opp_table'.
*/ */
struct device_list_opp { struct opp_device {
struct list_head node; struct list_head node;
const struct device *dev; const struct device *dev;
struct rcu_head rcu_head; struct rcu_head rcu_head;
...@@ -115,16 +118,16 @@ struct device_list_opp { ...@@ -115,16 +118,16 @@ struct device_list_opp {
}; };
/** /**
* struct device_opp - Device opp structure * struct opp_table - Device opp structure
* @node: list node - contains the devices with OPPs that * @node: table node - contains the devices with OPPs that
* have been registered. Nodes once added are not modified in this * have been registered. Nodes once added are not modified in this
* list. * table.
* RCU usage: nodes are not modified in the list of device_opp, * RCU usage: nodes are not modified in the table of opp_table,
* however addition is possible and is secured by dev_opp_list_lock * however addition is possible and is secured by opp_table_lock
* @srcu_head: notifier head to notify the OPP availability changes. * @srcu_head: notifier head to notify the OPP availability changes.
* @rcu_head: RCU callback head used for deferred freeing * @rcu_head: RCU callback head used for deferred freeing
* @dev_list: list of devices that share these OPPs * @dev_list: list of devices that share these OPPs
* @opp_list: list of opps * @opp_list: table of opps
* @np: struct device_node pointer for opp's DT node. * @np: struct device_node pointer for opp's DT node.
* @clock_latency_ns_max: Max clock latency in nanoseconds. * @clock_latency_ns_max: Max clock latency in nanoseconds.
* @shared_opp: OPP is shared between multiple devices. * @shared_opp: OPP is shared between multiple devices.
...@@ -132,9 +135,13 @@ struct device_list_opp { ...@@ -132,9 +135,13 @@ struct device_list_opp {
* @supported_hw: Array of version number to support. * @supported_hw: Array of version number to support.
* @supported_hw_count: Number of elements in supported_hw array. * @supported_hw_count: Number of elements in supported_hw array.
* @prop_name: A name to postfix to many DT properties, while parsing them. * @prop_name: A name to postfix to many DT properties, while parsing them.
* @clk: Device's clock handle
* @regulator: Supply regulator
* @dentry: debugfs dentry pointer of the real device directory (not links). * @dentry: debugfs dentry pointer of the real device directory (not links).
* @dentry_name: Name of the real dentry. * @dentry_name: Name of the real dentry.
* *
* @voltage_tolerance_v1: In percentage, for v1 bindings only.
*
* This is an internal data structure maintaining the link to opps attached to * This is an internal data structure maintaining the link to opps attached to
* a device. This structure is not meant to be shared to users as it is * a device. This structure is not meant to be shared to users as it is
* meant for book keeping and private to OPP library. * meant for book keeping and private to OPP library.
...@@ -143,7 +150,7 @@ struct device_list_opp { ...@@ -143,7 +150,7 @@ struct device_list_opp {
* need to wait for the grace period of both of them before freeing any * need to wait for the grace period of both of them before freeing any
* resources. And so we have used kfree_rcu() from within call_srcu() handlers. * resources. And so we have used kfree_rcu() from within call_srcu() handlers.
*/ */
struct device_opp { struct opp_table {
struct list_head node; struct list_head node;
struct srcu_notifier_head srcu_head; struct srcu_notifier_head srcu_head;
...@@ -153,12 +160,18 @@ struct device_opp { ...@@ -153,12 +160,18 @@ struct device_opp {
struct device_node *np; struct device_node *np;
unsigned long clock_latency_ns_max; unsigned long clock_latency_ns_max;
/* For backward compatibility with v1 bindings */
unsigned int voltage_tolerance_v1;
bool shared_opp; bool shared_opp;
struct dev_pm_opp *suspend_opp; struct dev_pm_opp *suspend_opp;
unsigned int *supported_hw; unsigned int *supported_hw;
unsigned int supported_hw_count; unsigned int supported_hw_count;
const char *prop_name; const char *prop_name;
struct clk *clk;
struct regulator *regulator;
#ifdef CONFIG_DEBUG_FS #ifdef CONFIG_DEBUG_FS
struct dentry *dentry; struct dentry *dentry;
...@@ -167,30 +180,27 @@ struct device_opp { ...@@ -167,30 +180,27 @@ struct device_opp {
}; };
/* Routines internal to opp core */ /* Routines internal to opp core */
struct device_opp *_find_device_opp(struct device *dev); struct opp_table *_find_opp_table(struct device *dev);
struct device_list_opp *_add_list_dev(const struct device *dev, struct opp_device *_add_opp_dev(const struct device *dev, struct opp_table *opp_table);
struct device_opp *dev_opp);
struct device_node *_of_get_opp_desc_node(struct device *dev); struct device_node *_of_get_opp_desc_node(struct device *dev);
#ifdef CONFIG_DEBUG_FS #ifdef CONFIG_DEBUG_FS
void opp_debug_remove_one(struct dev_pm_opp *opp); void opp_debug_remove_one(struct dev_pm_opp *opp);
int opp_debug_create_one(struct dev_pm_opp *opp, struct device_opp *dev_opp); int opp_debug_create_one(struct dev_pm_opp *opp, struct opp_table *opp_table);
int opp_debug_register(struct device_list_opp *list_dev, int opp_debug_register(struct opp_device *opp_dev, struct opp_table *opp_table);
struct device_opp *dev_opp); void opp_debug_unregister(struct opp_device *opp_dev, struct opp_table *opp_table);
void opp_debug_unregister(struct device_list_opp *list_dev,
struct device_opp *dev_opp);
#else #else
static inline void opp_debug_remove_one(struct dev_pm_opp *opp) {} static inline void opp_debug_remove_one(struct dev_pm_opp *opp) {}
static inline int opp_debug_create_one(struct dev_pm_opp *opp, static inline int opp_debug_create_one(struct dev_pm_opp *opp,
struct device_opp *dev_opp) struct opp_table *opp_table)
{ return 0; } { return 0; }
static inline int opp_debug_register(struct device_list_opp *list_dev, static inline int opp_debug_register(struct opp_device *opp_dev,
struct device_opp *dev_opp) struct opp_table *opp_table)
{ return 0; } { return 0; }
static inline void opp_debug_unregister(struct device_list_opp *list_dev, static inline void opp_debug_unregister(struct opp_device *opp_dev,
struct device_opp *dev_opp) struct opp_table *opp_table)
{ } { }
#endif /* DEBUG_FS */ #endif /* DEBUG_FS */
......
...@@ -34,6 +34,8 @@ bool dev_pm_opp_is_turbo(struct dev_pm_opp *opp); ...@@ -34,6 +34,8 @@ bool dev_pm_opp_is_turbo(struct dev_pm_opp *opp);
int dev_pm_opp_get_opp_count(struct device *dev); int dev_pm_opp_get_opp_count(struct device *dev);
unsigned long dev_pm_opp_get_max_clock_latency(struct device *dev); unsigned long dev_pm_opp_get_max_clock_latency(struct device *dev);
unsigned long dev_pm_opp_get_max_volt_latency(struct device *dev);
unsigned long dev_pm_opp_get_max_transition_latency(struct device *dev);
struct dev_pm_opp *dev_pm_opp_get_suspend_opp(struct device *dev); struct dev_pm_opp *dev_pm_opp_get_suspend_opp(struct device *dev);
struct dev_pm_opp *dev_pm_opp_find_freq_exact(struct device *dev, struct dev_pm_opp *dev_pm_opp_find_freq_exact(struct device *dev,
...@@ -60,6 +62,9 @@ int dev_pm_opp_set_supported_hw(struct device *dev, const u32 *versions, ...@@ -60,6 +62,9 @@ int dev_pm_opp_set_supported_hw(struct device *dev, const u32 *versions,
void dev_pm_opp_put_supported_hw(struct device *dev); void dev_pm_opp_put_supported_hw(struct device *dev);
int dev_pm_opp_set_prop_name(struct device *dev, const char *name); int dev_pm_opp_set_prop_name(struct device *dev, const char *name);
void dev_pm_opp_put_prop_name(struct device *dev); void dev_pm_opp_put_prop_name(struct device *dev);
int dev_pm_opp_set_regulator(struct device *dev, const char *name);
void dev_pm_opp_put_regulator(struct device *dev);
int dev_pm_opp_set_rate(struct device *dev, unsigned long target_freq);
#else #else
static inline unsigned long dev_pm_opp_get_voltage(struct dev_pm_opp *opp) static inline unsigned long dev_pm_opp_get_voltage(struct dev_pm_opp *opp)
{ {
...@@ -86,6 +91,16 @@ static inline unsigned long dev_pm_opp_get_max_clock_latency(struct device *dev) ...@@ -86,6 +91,16 @@ static inline unsigned long dev_pm_opp_get_max_clock_latency(struct device *dev)
return 0; return 0;
} }
static inline unsigned long dev_pm_opp_get_max_volt_latency(struct device *dev)
{
return 0;
}
static inline unsigned long dev_pm_opp_get_max_transition_latency(struct device *dev)
{
return 0;
}
static inline struct dev_pm_opp *dev_pm_opp_get_suspend_opp(struct device *dev) static inline struct dev_pm_opp *dev_pm_opp_get_suspend_opp(struct device *dev)
{ {
return NULL; return NULL;
...@@ -151,6 +166,18 @@ static inline int dev_pm_opp_set_prop_name(struct device *dev, const char *name) ...@@ -151,6 +166,18 @@ static inline int dev_pm_opp_set_prop_name(struct device *dev, const char *name)
static inline void dev_pm_opp_put_prop_name(struct device *dev) {} static inline void dev_pm_opp_put_prop_name(struct device *dev) {}
static inline int dev_pm_opp_set_regulator(struct device *dev, const char *name)
{
return -EINVAL;
}
static inline void dev_pm_opp_put_regulator(struct device *dev) {}
static inline int dev_pm_opp_set_rate(struct device *dev, unsigned long target_freq)
{
return -EINVAL;
}
#endif /* CONFIG_PM_OPP */ #endif /* CONFIG_PM_OPP */
#if defined(CONFIG_PM_OPP) && defined(CONFIG_OF) #if defined(CONFIG_PM_OPP) && defined(CONFIG_OF)
......
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