mirror of
https://github.com/AuxXxilium/linux_dsm_epyc7002.git
synced 2024-12-26 04:55:16 +07:00
Merge branch 'pm-opp' into pm-cpufreq
This commit is contained in:
commit
2af3411f0e
@ -49,11 +49,12 @@
|
|||||||
* are protected by the dev_opp_list_lock for integrity.
|
* are protected by the dev_opp_list_lock for integrity.
|
||||||
* IMPORTANT: the opp nodes should be maintained in increasing
|
* IMPORTANT: the opp nodes should be maintained in increasing
|
||||||
* order.
|
* order.
|
||||||
|
* @dynamic: not-created from static DT entries.
|
||||||
* @available: true/false - marks if this OPP as available or not
|
* @available: true/false - marks if this OPP as available or not
|
||||||
* @rate: Frequency in hertz
|
* @rate: Frequency in hertz
|
||||||
* @u_volt: Nominal voltage in microvolts corresponding to this OPP
|
* @u_volt: Nominal voltage in microvolts corresponding to this OPP
|
||||||
* @dev_opp: points back to the device_opp struct this opp belongs to
|
* @dev_opp: points back to the device_opp struct this opp belongs to
|
||||||
* @head: RCU callback head used for deferred freeing
|
* @rcu_head: RCU callback head used for deferred freeing
|
||||||
*
|
*
|
||||||
* This structure stores the OPP information for a given device.
|
* This structure stores the OPP information for a given device.
|
||||||
*/
|
*/
|
||||||
@ -61,11 +62,12 @@ struct dev_pm_opp {
|
|||||||
struct list_head node;
|
struct list_head node;
|
||||||
|
|
||||||
bool available;
|
bool available;
|
||||||
|
bool dynamic;
|
||||||
unsigned long rate;
|
unsigned long rate;
|
||||||
unsigned long u_volt;
|
unsigned long u_volt;
|
||||||
|
|
||||||
struct device_opp *dev_opp;
|
struct device_opp *dev_opp;
|
||||||
struct rcu_head head;
|
struct rcu_head rcu_head;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -76,7 +78,8 @@ struct dev_pm_opp {
|
|||||||
* RCU usage: nodes are not modified in the list of device_opp,
|
* RCU usage: nodes are not modified in the list of device_opp,
|
||||||
* however addition is possible and is secured by dev_opp_list_lock
|
* however addition is possible and is secured by dev_opp_list_lock
|
||||||
* @dev: device pointer
|
* @dev: device pointer
|
||||||
* @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
|
||||||
* @opp_list: list of opps
|
* @opp_list: list of opps
|
||||||
*
|
*
|
||||||
* 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
|
||||||
@ -87,7 +90,8 @@ struct device_opp {
|
|||||||
struct list_head node;
|
struct list_head node;
|
||||||
|
|
||||||
struct device *dev;
|
struct device *dev;
|
||||||
struct srcu_notifier_head head;
|
struct srcu_notifier_head srcu_head;
|
||||||
|
struct rcu_head rcu_head;
|
||||||
struct list_head opp_list;
|
struct list_head opp_list;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -378,30 +382,8 @@ 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);
|
||||||
|
|
||||||
/**
|
static int dev_pm_opp_add_dynamic(struct device *dev, unsigned long freq,
|
||||||
* dev_pm_opp_add() - Add an OPP table from a table definitions
|
unsigned long u_volt, bool dynamic)
|
||||||
* @dev: device for which we do this operation
|
|
||||||
* @freq: Frequency in Hz for this OPP
|
|
||||||
* @u_volt: Voltage in uVolts for this OPP
|
|
||||||
*
|
|
||||||
* This function adds an opp definition to the opp list and returns status.
|
|
||||||
* The opp is made available by default and it can be controlled using
|
|
||||||
* dev_pm_opp_enable/disable functions.
|
|
||||||
*
|
|
||||||
* Locking: The internal device_opp 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.
|
|
||||||
*
|
|
||||||
* Return:
|
|
||||||
* 0: On success OR
|
|
||||||
* Duplicate OPPs (both freq and volt are same) and opp->available
|
|
||||||
* -EEXIST: Freq are same and volt are different OR
|
|
||||||
* Duplicate OPPs (both freq and volt are same) and !opp->available
|
|
||||||
* -ENOMEM: Memory allocation failure
|
|
||||||
*/
|
|
||||||
int dev_pm_opp_add(struct device *dev, unsigned long freq, unsigned long u_volt)
|
|
||||||
{
|
{
|
||||||
struct device_opp *dev_opp = NULL;
|
struct device_opp *dev_opp = NULL;
|
||||||
struct dev_pm_opp *opp, *new_opp;
|
struct dev_pm_opp *opp, *new_opp;
|
||||||
@ -417,6 +399,13 @@ int dev_pm_opp_add(struct device *dev, unsigned long freq, unsigned long u_volt)
|
|||||||
/* Hold our list modification lock here */
|
/* Hold our list modification lock here */
|
||||||
mutex_lock(&dev_opp_list_lock);
|
mutex_lock(&dev_opp_list_lock);
|
||||||
|
|
||||||
|
/* populate the opp table */
|
||||||
|
new_opp->dev_opp = dev_opp;
|
||||||
|
new_opp->rate = freq;
|
||||||
|
new_opp->u_volt = u_volt;
|
||||||
|
new_opp->available = true;
|
||||||
|
new_opp->dynamic = dynamic;
|
||||||
|
|
||||||
/* Check for existing list for 'dev' */
|
/* Check for existing list for 'dev' */
|
||||||
dev_opp = find_device_opp(dev);
|
dev_opp = find_device_opp(dev);
|
||||||
if (IS_ERR(dev_opp)) {
|
if (IS_ERR(dev_opp)) {
|
||||||
@ -436,19 +425,15 @@ int dev_pm_opp_add(struct device *dev, unsigned long freq, unsigned long u_volt)
|
|||||||
}
|
}
|
||||||
|
|
||||||
dev_opp->dev = dev;
|
dev_opp->dev = dev;
|
||||||
srcu_init_notifier_head(&dev_opp->head);
|
srcu_init_notifier_head(&dev_opp->srcu_head);
|
||||||
INIT_LIST_HEAD(&dev_opp->opp_list);
|
INIT_LIST_HEAD(&dev_opp->opp_list);
|
||||||
|
|
||||||
/* Secure the device list modification */
|
/* Secure the device list modification */
|
||||||
list_add_rcu(&dev_opp->node, &dev_opp_list);
|
list_add_rcu(&dev_opp->node, &dev_opp_list);
|
||||||
|
head = &dev_opp->opp_list;
|
||||||
|
goto list_add;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* populate the opp table */
|
|
||||||
new_opp->dev_opp = dev_opp;
|
|
||||||
new_opp->rate = freq;
|
|
||||||
new_opp->u_volt = u_volt;
|
|
||||||
new_opp->available = true;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Insert new OPP in order of increasing frequency
|
* Insert new OPP in order of increasing frequency
|
||||||
* and discard if already present
|
* and discard if already present
|
||||||
@ -474,6 +459,7 @@ int dev_pm_opp_add(struct device *dev, unsigned long freq, unsigned long u_volt)
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
list_add:
|
||||||
list_add_rcu(&new_opp->node, head);
|
list_add_rcu(&new_opp->node, head);
|
||||||
mutex_unlock(&dev_opp_list_lock);
|
mutex_unlock(&dev_opp_list_lock);
|
||||||
|
|
||||||
@ -481,11 +467,109 @@ int dev_pm_opp_add(struct device *dev, unsigned long freq, unsigned long u_volt)
|
|||||||
* 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->head, OPP_EVENT_ADD, new_opp);
|
srcu_notifier_call_chain(&dev_opp->srcu_head, OPP_EVENT_ADD, new_opp);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* dev_pm_opp_add() - Add an OPP table from a table definitions
|
||||||
|
* @dev: device for which we do this operation
|
||||||
|
* @freq: Frequency in Hz for this OPP
|
||||||
|
* @u_volt: Voltage in uVolts for this OPP
|
||||||
|
*
|
||||||
|
* This function adds an opp definition to the opp list and returns status.
|
||||||
|
* The opp is made available by default and it can be controlled using
|
||||||
|
* dev_pm_opp_enable/disable functions.
|
||||||
|
*
|
||||||
|
* Locking: The internal device_opp 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.
|
||||||
|
*
|
||||||
|
* Return:
|
||||||
|
* 0: On success OR
|
||||||
|
* Duplicate OPPs (both freq and volt are same) and opp->available
|
||||||
|
* -EEXIST: Freq are same and volt are different OR
|
||||||
|
* Duplicate OPPs (both freq and volt are same) and !opp->available
|
||||||
|
* -ENOMEM: Memory allocation failure
|
||||||
|
*/
|
||||||
|
int dev_pm_opp_add(struct device *dev, unsigned long freq, unsigned long u_volt)
|
||||||
|
{
|
||||||
|
return dev_pm_opp_add_dynamic(dev, freq, u_volt, true);
|
||||||
|
}
|
||||||
EXPORT_SYMBOL_GPL(dev_pm_opp_add);
|
EXPORT_SYMBOL_GPL(dev_pm_opp_add);
|
||||||
|
|
||||||
|
static void kfree_opp_rcu(struct rcu_head *head)
|
||||||
|
{
|
||||||
|
struct dev_pm_opp *opp = container_of(head, struct dev_pm_opp, rcu_head);
|
||||||
|
|
||||||
|
kfree_rcu(opp, rcu_head);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void kfree_device_rcu(struct rcu_head *head)
|
||||||
|
{
|
||||||
|
struct device_opp *device_opp = container_of(head, struct device_opp, rcu_head);
|
||||||
|
|
||||||
|
kfree(device_opp);
|
||||||
|
}
|
||||||
|
|
||||||
|
void __dev_pm_opp_remove(struct device_opp *dev_opp, struct dev_pm_opp *opp)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* Notify the changes in the availability of the operable
|
||||||
|
* frequency/voltage list.
|
||||||
|
*/
|
||||||
|
srcu_notifier_call_chain(&dev_opp->srcu_head, OPP_EVENT_REMOVE, opp);
|
||||||
|
list_del_rcu(&opp->node);
|
||||||
|
call_srcu(&dev_opp->srcu_head.srcu, &opp->rcu_head, kfree_opp_rcu);
|
||||||
|
|
||||||
|
if (list_empty(&dev_opp->opp_list)) {
|
||||||
|
list_del_rcu(&dev_opp->node);
|
||||||
|
call_srcu(&dev_opp->srcu_head.srcu, &dev_opp->rcu_head,
|
||||||
|
kfree_device_rcu);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* dev_pm_opp_remove() - Remove an OPP from OPP list
|
||||||
|
* @dev: device for which we do this operation
|
||||||
|
* @freq: OPP to remove with matching 'freq'
|
||||||
|
*
|
||||||
|
* This function removes an opp from the opp list.
|
||||||
|
*/
|
||||||
|
void dev_pm_opp_remove(struct device *dev, unsigned long freq)
|
||||||
|
{
|
||||||
|
struct dev_pm_opp *opp;
|
||||||
|
struct device_opp *dev_opp;
|
||||||
|
bool found = false;
|
||||||
|
|
||||||
|
/* Hold our list modification lock here */
|
||||||
|
mutex_lock(&dev_opp_list_lock);
|
||||||
|
|
||||||
|
dev_opp = find_device_opp(dev);
|
||||||
|
if (IS_ERR(dev_opp))
|
||||||
|
goto unlock;
|
||||||
|
|
||||||
|
list_for_each_entry(opp, &dev_opp->opp_list, node) {
|
||||||
|
if (opp->rate == freq) {
|
||||||
|
found = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!found) {
|
||||||
|
dev_warn(dev, "%s: Couldn't find OPP with freq: %lu\n",
|
||||||
|
__func__, freq);
|
||||||
|
goto unlock;
|
||||||
|
}
|
||||||
|
|
||||||
|
__dev_pm_opp_remove(dev_opp, opp);
|
||||||
|
unlock:
|
||||||
|
mutex_unlock(&dev_opp_list_lock);
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(dev_pm_opp_remove);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* opp_set_availability() - helper to set the availability of an opp
|
* opp_set_availability() - helper to set the availability of an opp
|
||||||
* @dev: device for which we do this operation
|
* @dev: device for which we do this operation
|
||||||
@ -557,14 +641,14 @@ static int opp_set_availability(struct device *dev, unsigned long freq,
|
|||||||
|
|
||||||
list_replace_rcu(&opp->node, &new_opp->node);
|
list_replace_rcu(&opp->node, &new_opp->node);
|
||||||
mutex_unlock(&dev_opp_list_lock);
|
mutex_unlock(&dev_opp_list_lock);
|
||||||
kfree_rcu(opp, head);
|
call_srcu(&dev_opp->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->head, OPP_EVENT_ENABLE,
|
srcu_notifier_call_chain(&dev_opp->srcu_head, OPP_EVENT_ENABLE,
|
||||||
new_opp);
|
new_opp);
|
||||||
else
|
else
|
||||||
srcu_notifier_call_chain(&dev_opp->head, OPP_EVENT_DISABLE,
|
srcu_notifier_call_chain(&dev_opp->srcu_head, OPP_EVENT_DISABLE,
|
||||||
new_opp);
|
new_opp);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
@ -629,7 +713,7 @@ struct srcu_notifier_head *dev_pm_opp_get_notifier(struct device *dev)
|
|||||||
if (IS_ERR(dev_opp))
|
if (IS_ERR(dev_opp))
|
||||||
return ERR_CAST(dev_opp); /* matching type */
|
return ERR_CAST(dev_opp); /* matching type */
|
||||||
|
|
||||||
return &dev_opp->head;
|
return &dev_opp->srcu_head;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef CONFIG_OF
|
#ifdef CONFIG_OF
|
||||||
@ -666,7 +750,7 @@ int of_init_opp_table(struct device *dev)
|
|||||||
unsigned long freq = be32_to_cpup(val++) * 1000;
|
unsigned long freq = be32_to_cpup(val++) * 1000;
|
||||||
unsigned long volt = be32_to_cpup(val++);
|
unsigned long volt = be32_to_cpup(val++);
|
||||||
|
|
||||||
if (dev_pm_opp_add(dev, freq, volt))
|
if (dev_pm_opp_add_dynamic(dev, freq, volt, false))
|
||||||
dev_warn(dev, "%s: Failed to add OPP %ld\n",
|
dev_warn(dev, "%s: Failed to add OPP %ld\n",
|
||||||
__func__, freq);
|
__func__, freq);
|
||||||
nr -= 2;
|
nr -= 2;
|
||||||
@ -675,4 +759,34 @@ int of_init_opp_table(struct device *dev)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(of_init_opp_table);
|
EXPORT_SYMBOL_GPL(of_init_opp_table);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* of_free_opp_table() - Free OPP table entries created from static DT entries
|
||||||
|
* @dev: device pointer used to lookup device OPPs.
|
||||||
|
*
|
||||||
|
* Free OPPs created using static entries present in DT.
|
||||||
|
*/
|
||||||
|
void of_free_opp_table(struct device *dev)
|
||||||
|
{
|
||||||
|
struct device_opp *dev_opp = find_device_opp(dev);
|
||||||
|
struct dev_pm_opp *opp, *tmp;
|
||||||
|
|
||||||
|
/* Check for existing list for 'dev' */
|
||||||
|
dev_opp = find_device_opp(dev);
|
||||||
|
if (WARN(IS_ERR(dev_opp), "%s: dev_opp: %ld\n", dev_name(dev),
|
||||||
|
PTR_ERR(dev_opp)))
|
||||||
|
return;
|
||||||
|
|
||||||
|
/* Hold our list modification lock here */
|
||||||
|
mutex_lock(&dev_opp_list_lock);
|
||||||
|
|
||||||
|
/* Free static OPPs */
|
||||||
|
list_for_each_entry_safe(opp, tmp, &dev_opp->opp_list, node) {
|
||||||
|
if (!opp->dynamic)
|
||||||
|
__dev_pm_opp_remove(dev_opp, opp);
|
||||||
|
}
|
||||||
|
|
||||||
|
mutex_unlock(&dev_opp_list_lock);
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(of_free_opp_table);
|
||||||
#endif
|
#endif
|
||||||
|
@ -21,7 +21,7 @@ struct dev_pm_opp;
|
|||||||
struct device;
|
struct device;
|
||||||
|
|
||||||
enum dev_pm_opp_event {
|
enum dev_pm_opp_event {
|
||||||
OPP_EVENT_ADD, OPP_EVENT_ENABLE, OPP_EVENT_DISABLE,
|
OPP_EVENT_ADD, OPP_EVENT_REMOVE, OPP_EVENT_ENABLE, OPP_EVENT_DISABLE,
|
||||||
};
|
};
|
||||||
|
|
||||||
#if defined(CONFIG_PM_OPP)
|
#if defined(CONFIG_PM_OPP)
|
||||||
@ -44,6 +44,7 @@ struct dev_pm_opp *dev_pm_opp_find_freq_ceil(struct device *dev,
|
|||||||
|
|
||||||
int dev_pm_opp_add(struct device *dev, unsigned long freq,
|
int dev_pm_opp_add(struct device *dev, unsigned long freq,
|
||||||
unsigned long u_volt);
|
unsigned long u_volt);
|
||||||
|
void dev_pm_opp_remove(struct device *dev, unsigned long freq);
|
||||||
|
|
||||||
int dev_pm_opp_enable(struct device *dev, unsigned long freq);
|
int dev_pm_opp_enable(struct device *dev, unsigned long freq);
|
||||||
|
|
||||||
@ -90,6 +91,10 @@ static inline int dev_pm_opp_add(struct device *dev, unsigned long freq,
|
|||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline void dev_pm_opp_remove(struct device *dev, unsigned long freq)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
static inline int dev_pm_opp_enable(struct device *dev, unsigned long freq)
|
static inline int dev_pm_opp_enable(struct device *dev, unsigned long freq)
|
||||||
{
|
{
|
||||||
return 0;
|
return 0;
|
||||||
@ -109,11 +114,16 @@ static inline struct srcu_notifier_head *dev_pm_opp_get_notifier(
|
|||||||
|
|
||||||
#if defined(CONFIG_PM_OPP) && defined(CONFIG_OF)
|
#if defined(CONFIG_PM_OPP) && defined(CONFIG_OF)
|
||||||
int of_init_opp_table(struct device *dev);
|
int of_init_opp_table(struct device *dev);
|
||||||
|
void of_free_opp_table(struct device *dev);
|
||||||
#else
|
#else
|
||||||
static inline int of_init_opp_table(struct device *dev)
|
static inline int of_init_opp_table(struct device *dev)
|
||||||
{
|
{
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline void of_free_opp_table(struct device *dev)
|
||||||
|
{
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#endif /* __LINUX_OPP_H__ */
|
#endif /* __LINUX_OPP_H__ */
|
||||||
|
Loading…
Reference in New Issue
Block a user