Merge back earlier cpufreq material for v4.5.

This commit is contained in:
Rafael J. Wysocki
2015-12-28 01:34:35 +01:00
23 changed files with 1510 additions and 217 deletions

View File

@@ -1,2 +1,3 @@
ccflags-$(CONFIG_DEBUG_DRIVER) := -DDEBUG
obj-y += core.o cpu.o
obj-$(CONFIG_DEBUG_FS) += debugfs.o

View File

@@ -463,6 +463,7 @@ static void _kfree_list_dev_rcu(struct rcu_head *head)
static void _remove_list_dev(struct device_list_opp *list_dev,
struct device_opp *dev_opp)
{
opp_debug_unregister(list_dev, dev_opp);
list_del(&list_dev->node);
call_srcu(&dev_opp->srcu_head.srcu, &list_dev->rcu_head,
_kfree_list_dev_rcu);
@@ -472,6 +473,7 @@ struct device_list_opp *_add_list_dev(const struct device *dev,
struct device_opp *dev_opp)
{
struct device_list_opp *list_dev;
int ret;
list_dev = kzalloc(sizeof(*list_dev), GFP_KERNEL);
if (!list_dev)
@@ -481,6 +483,12 @@ struct device_list_opp *_add_list_dev(const struct device *dev,
list_dev->dev = dev;
list_add_rcu(&list_dev->node, &dev_opp->dev_list);
/* Create debugfs entries for the dev_opp */
ret = opp_debug_register(list_dev, dev_opp);
if (ret)
dev_err(dev, "%s: Failed to register opp debugfs (%d)\n",
__func__, ret);
return list_dev;
}
@@ -551,6 +559,12 @@ static void _remove_device_opp(struct device_opp *dev_opp)
if (!list_empty(&dev_opp->opp_list))
return;
if (dev_opp->supported_hw)
return;
if (dev_opp->prop_name)
return;
list_dev = list_first_entry(&dev_opp->dev_list, struct device_list_opp,
node);
@@ -596,6 +610,7 @@ static void _opp_remove(struct device_opp *dev_opp,
*/
if (notify)
srcu_notifier_call_chain(&dev_opp->srcu_head, OPP_EVENT_REMOVE, opp);
opp_debug_remove_one(opp);
list_del_rcu(&opp->node);
call_srcu(&dev_opp->srcu_head.srcu, &opp->rcu_head, _kfree_opp_rcu);
@@ -673,6 +688,7 @@ static int _opp_add(struct device *dev, struct dev_pm_opp *new_opp,
{
struct dev_pm_opp *opp;
struct list_head *head = &dev_opp->opp_list;
int ret;
/*
* Insert new OPP in order of increasing frequency and discard if
@@ -703,6 +719,11 @@ static int _opp_add(struct device *dev, struct dev_pm_opp *new_opp,
new_opp->dev_opp = dev_opp;
list_add_rcu(&new_opp->node, head);
ret = opp_debug_create_one(new_opp, dev_opp);
if (ret)
dev_err(dev, "%s: Failed to register opp to debugfs (%d)\n",
__func__, ret);
return 0;
}
@@ -776,35 +797,48 @@ unlock:
}
/* 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)
{
u32 microvolt[3] = {0};
u32 val;
int count, ret;
struct property *prop = NULL;
char name[NAME_MAX];
/* Missing property isn't a problem, but an invalid entry is */
if (!of_find_property(opp->np, "opp-microvolt", NULL))
return 0;
/* Search for "opp-microvolt-<name>" */
if (dev_opp->prop_name) {
sprintf(name, "opp-microvolt-%s", dev_opp->prop_name);
prop = of_find_property(opp->np, name, NULL);
}
count = of_property_count_u32_elems(opp->np, "opp-microvolt");
if (!prop) {
/* Search for "opp-microvolt" */
name[13] = '\0';
prop = of_find_property(opp->np, name, NULL);
/* Missing property isn't a problem, but an invalid entry is */
if (!prop)
return 0;
}
count = of_property_count_u32_elems(opp->np, name);
if (count < 0) {
dev_err(dev, "%s: Invalid opp-microvolt property (%d)\n",
__func__, count);
dev_err(dev, "%s: Invalid %s property (%d)\n",
__func__, name, count);
return count;
}
/* There can be one or three elements here */
if (count != 1 && count != 3) {
dev_err(dev, "%s: Invalid number of elements in opp-microvolt property (%d)\n",
__func__, count);
dev_err(dev, "%s: Invalid number of elements in %s property (%d)\n",
__func__, name, count);
return -EINVAL;
}
ret = of_property_read_u32_array(opp->np, "opp-microvolt", microvolt,
count);
ret = of_property_read_u32_array(opp->np, name, microvolt, count);
if (ret) {
dev_err(dev, "%s: error parsing opp-microvolt: %d\n", __func__,
ret);
dev_err(dev, "%s: error parsing %s: %d\n", __func__, name, ret);
return -EINVAL;
}
@@ -812,12 +846,270 @@ static int opp_parse_supplies(struct dev_pm_opp *opp, struct device *dev)
opp->u_volt_min = microvolt[1];
opp->u_volt_max = microvolt[2];
if (!of_property_read_u32(opp->np, "opp-microamp", &val))
/* Search for "opp-microamp-<name>" */
prop = NULL;
if (dev_opp->prop_name) {
sprintf(name, "opp-microamp-%s", dev_opp->prop_name);
prop = of_find_property(opp->np, name, NULL);
}
if (!prop) {
/* Search for "opp-microamp" */
name[12] = '\0';
prop = of_find_property(opp->np, name, NULL);
}
if (prop && !of_property_read_u32(opp->np, name, &val))
opp->u_amp = val;
return 0;
}
/**
* dev_pm_opp_set_supported_hw() - Set supported platforms
* @dev: Device for which supported-hw has to be set.
* @versions: Array of hierarchy of versions to match.
* @count: Number of elements in the array.
*
* This is required only for the V2 bindings, and it enables a platform to
* specify the hierarchy of versions it supports. OPP layer will then enable
* OPPs, which are available for those versions, based on its 'opp-supported-hw'
* property.
*
* 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.
*/
int dev_pm_opp_set_supported_hw(struct device *dev, const u32 *versions,
unsigned int count)
{
struct device_opp *dev_opp;
int ret = 0;
/* Hold our list modification lock here */
mutex_lock(&dev_opp_list_lock);
dev_opp = _add_device_opp(dev);
if (!dev_opp) {
ret = -ENOMEM;
goto unlock;
}
/* Make sure there are no concurrent readers while updating dev_opp */
WARN_ON(!list_empty(&dev_opp->opp_list));
/* Do we already have a version hierarchy associated with dev_opp? */
if (dev_opp->supported_hw) {
dev_err(dev, "%s: Already have supported hardware list\n",
__func__);
ret = -EBUSY;
goto err;
}
dev_opp->supported_hw = kmemdup(versions, count * sizeof(*versions),
GFP_KERNEL);
if (!dev_opp->supported_hw) {
ret = -ENOMEM;
goto err;
}
dev_opp->supported_hw_count = count;
mutex_unlock(&dev_opp_list_lock);
return 0;
err:
_remove_device_opp(dev_opp);
unlock:
mutex_unlock(&dev_opp_list_lock);
return ret;
}
EXPORT_SYMBOL_GPL(dev_pm_opp_set_supported_hw);
/**
* dev_pm_opp_put_supported_hw() - Releases resources blocked for supported hw
* @dev: Device for which supported-hw has to be set.
*
* 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
* will not be freed.
*
* 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.
*/
void dev_pm_opp_put_supported_hw(struct device *dev)
{
struct device_opp *dev_opp;
/* Hold our list modification lock here */
mutex_lock(&dev_opp_list_lock);
/* Check for existing list for 'dev' first */
dev_opp = _find_device_opp(dev);
if (IS_ERR(dev_opp)) {
dev_err(dev, "Failed to find dev_opp: %ld\n", PTR_ERR(dev_opp));
goto unlock;
}
/* Make sure there are no concurrent readers while updating dev_opp */
WARN_ON(!list_empty(&dev_opp->opp_list));
if (!dev_opp->supported_hw) {
dev_err(dev, "%s: Doesn't have supported hardware list\n",
__func__);
goto unlock;
}
kfree(dev_opp->supported_hw);
dev_opp->supported_hw = NULL;
dev_opp->supported_hw_count = 0;
/* Try freeing device_opp if this was the last blocking resource */
_remove_device_opp(dev_opp);
unlock:
mutex_unlock(&dev_opp_list_lock);
}
EXPORT_SYMBOL_GPL(dev_pm_opp_put_supported_hw);
/**
* dev_pm_opp_set_prop_name() - Set prop-extn name
* @dev: Device for which the regulator has to be set.
* @name: name to postfix to properties.
*
* This is required only for the V2 bindings, and it enables a platform to
* specify the extn to be used for certain property names. The properties to
* which the extension will apply are opp-microvolt and opp-microamp. OPP core
* should postfix the property name with -<name> while looking for them.
*
* 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.
*/
int dev_pm_opp_set_prop_name(struct device *dev, const char *name)
{
struct device_opp *dev_opp;
int ret = 0;
/* Hold our list modification lock here */
mutex_lock(&dev_opp_list_lock);
dev_opp = _add_device_opp(dev);
if (!dev_opp) {
ret = -ENOMEM;
goto unlock;
}
/* Make sure there are no concurrent readers while updating dev_opp */
WARN_ON(!list_empty(&dev_opp->opp_list));
/* Do we already have a prop-name associated with dev_opp? */
if (dev_opp->prop_name) {
dev_err(dev, "%s: Already have prop-name %s\n", __func__,
dev_opp->prop_name);
ret = -EBUSY;
goto err;
}
dev_opp->prop_name = kstrdup(name, GFP_KERNEL);
if (!dev_opp->prop_name) {
ret = -ENOMEM;
goto err;
}
mutex_unlock(&dev_opp_list_lock);
return 0;
err:
_remove_device_opp(dev_opp);
unlock:
mutex_unlock(&dev_opp_list_lock);
return ret;
}
EXPORT_SYMBOL_GPL(dev_pm_opp_set_prop_name);
/**
* dev_pm_opp_put_prop_name() - Releases resources blocked for prop-name
* @dev: Device for which the regulator has to be set.
*
* 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
* will not be freed.
*
* 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.
*/
void dev_pm_opp_put_prop_name(struct device *dev)
{
struct device_opp *dev_opp;
/* Hold our list modification lock here */
mutex_lock(&dev_opp_list_lock);
/* Check for existing list for 'dev' first */
dev_opp = _find_device_opp(dev);
if (IS_ERR(dev_opp)) {
dev_err(dev, "Failed to find dev_opp: %ld\n", PTR_ERR(dev_opp));
goto unlock;
}
/* Make sure there are no concurrent readers while updating dev_opp */
WARN_ON(!list_empty(&dev_opp->opp_list));
if (!dev_opp->prop_name) {
dev_err(dev, "%s: Doesn't have a prop-name\n", __func__);
goto unlock;
}
kfree(dev_opp->prop_name);
dev_opp->prop_name = NULL;
/* Try freeing device_opp if this was the last blocking resource */
_remove_device_opp(dev_opp);
unlock:
mutex_unlock(&dev_opp_list_lock);
}
EXPORT_SYMBOL_GPL(dev_pm_opp_put_prop_name);
static bool _opp_is_supported(struct device *dev, struct device_opp *dev_opp,
struct device_node *np)
{
unsigned int count = dev_opp->supported_hw_count;
u32 version;
int ret;
if (!dev_opp->supported_hw)
return true;
while (count--) {
ret = of_property_read_u32_index(np, "opp-supported-hw", count,
&version);
if (ret) {
dev_warn(dev, "%s: failed to read opp-supported-hw property at index %d: %d\n",
__func__, count, ret);
return false;
}
/* Both of these are bitwise masks of the versions */
if (!(version & dev_opp->supported_hw[count]))
return false;
}
return true;
}
/**
* _opp_add_static_v2() - Allocate static OPPs (As per 'v2' DT bindings)
* @dev: device for which we do this operation
@@ -864,6 +1156,12 @@ static int _opp_add_static_v2(struct device *dev, struct device_node *np)
goto free_opp;
}
/* Check if the OPP supports hardware's hierarchy of versions or not */
if (!_opp_is_supported(dev, dev_opp, np)) {
dev_dbg(dev, "OPP not supported by hardware: %llu\n", rate);
goto free_opp;
}
/*
* Rate is defined as an unsigned long in clk API, and so casting
* explicitly to its type. Must be fixed once rate is 64 bit
@@ -879,7 +1177,7 @@ static int _opp_add_static_v2(struct device *dev, struct device_node *np)
if (!of_property_read_u32(np, "clock-latency-ns", &val))
new_opp->clock_latency_ns = val;
ret = opp_parse_supplies(new_opp, dev);
ret = opp_parse_supplies(new_opp, dev, dev_opp);
if (ret)
goto free_opp;
@@ -889,12 +1187,14 @@ static int _opp_add_static_v2(struct device *dev, struct device_node *np)
/* OPP to select on device suspend */
if (of_property_read_bool(np, "opp-suspend")) {
if (dev_opp->suspend_opp)
if (dev_opp->suspend_opp) {
dev_warn(dev, "%s: Multiple suspend OPPs found (%lu %lu)\n",
__func__, dev_opp->suspend_opp->rate,
new_opp->rate);
else
} else {
new_opp->suspend = true;
dev_opp->suspend_opp = new_opp;
}
}
if (new_opp->clock_latency_ns > dev_opp->clock_latency_ns_max)

View File

@@ -0,0 +1,219 @@
/*
* Generic OPP debugfs interface
*
* Copyright (C) 2015-2016 Viresh Kumar <viresh.kumar@linaro.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/debugfs.h>
#include <linux/device.h>
#include <linux/err.h>
#include <linux/init.h>
#include <linux/limits.h>
#include "opp.h"
static struct dentry *rootdir;
static void opp_set_dev_name(const struct device *dev, char *name)
{
if (dev->parent)
snprintf(name, NAME_MAX, "%s-%s", dev_name(dev->parent),
dev_name(dev));
else
snprintf(name, NAME_MAX, "%s", dev_name(dev));
}
void opp_debug_remove_one(struct dev_pm_opp *opp)
{
debugfs_remove_recursive(opp->dentry);
}
int opp_debug_create_one(struct dev_pm_opp *opp, struct device_opp *dev_opp)
{
struct dentry *pdentry = dev_opp->dentry;
struct dentry *d;
char name[25]; /* 20 chars for 64 bit value + 5 (opp:\0) */
/* Rate is unique to each OPP, use it to give opp-name */
snprintf(name, sizeof(name), "opp:%lu", opp->rate);
/* Create per-opp directory */
d = debugfs_create_dir(name, pdentry);
if (!d)
return -ENOMEM;
if (!debugfs_create_bool("available", S_IRUGO, d, &opp->available))
return -ENOMEM;
if (!debugfs_create_bool("dynamic", S_IRUGO, d, &opp->dynamic))
return -ENOMEM;
if (!debugfs_create_bool("turbo", S_IRUGO, d, &opp->turbo))
return -ENOMEM;
if (!debugfs_create_bool("suspend", S_IRUGO, d, &opp->suspend))
return -ENOMEM;
if (!debugfs_create_ulong("rate_hz", S_IRUGO, d, &opp->rate))
return -ENOMEM;
if (!debugfs_create_ulong("u_volt_target", S_IRUGO, d, &opp->u_volt))
return -ENOMEM;
if (!debugfs_create_ulong("u_volt_min", S_IRUGO, d, &opp->u_volt_min))
return -ENOMEM;
if (!debugfs_create_ulong("u_volt_max", S_IRUGO, d, &opp->u_volt_max))
return -ENOMEM;
if (!debugfs_create_ulong("u_amp", S_IRUGO, d, &opp->u_amp))
return -ENOMEM;
if (!debugfs_create_ulong("clock_latency_ns", S_IRUGO, d,
&opp->clock_latency_ns))
return -ENOMEM;
opp->dentry = d;
return 0;
}
static int device_opp_debug_create_dir(struct device_list_opp *list_dev,
struct device_opp *dev_opp)
{
const struct device *dev = list_dev->dev;
struct dentry *d;
opp_set_dev_name(dev, dev_opp->dentry_name);
/* Create device specific directory */
d = debugfs_create_dir(dev_opp->dentry_name, rootdir);
if (!d) {
dev_err(dev, "%s: Failed to create debugfs dir\n", __func__);
return -ENOMEM;
}
list_dev->dentry = d;
dev_opp->dentry = d;
return 0;
}
static int device_opp_debug_create_link(struct device_list_opp *list_dev,
struct device_opp *dev_opp)
{
const struct device *dev = list_dev->dev;
char name[NAME_MAX];
struct dentry *d;
opp_set_dev_name(list_dev->dev, name);
/* Create device specific directory link */
d = debugfs_create_symlink(name, rootdir, dev_opp->dentry_name);
if (!d) {
dev_err(dev, "%s: Failed to create link\n", __func__);
return -ENOMEM;
}
list_dev->dentry = d;
return 0;
}
/**
* opp_debug_register - add a device opp node to the debugfs 'opp' directory
* @list_dev: list-dev pointer for device
* @dev_opp: the device-opp being added
*
* 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
* devices except the first.
*
* Return: 0 on success, otherwise negative error.
*/
int opp_debug_register(struct device_list_opp *list_dev,
struct device_opp *dev_opp)
{
if (!rootdir) {
pr_debug("%s: Uninitialized rootdir\n", __func__);
return -EINVAL;
}
if (dev_opp->dentry)
return device_opp_debug_create_link(list_dev, dev_opp);
return device_opp_debug_create_dir(list_dev, dev_opp);
}
static void opp_migrate_dentry(struct device_list_opp *list_dev,
struct device_opp *dev_opp)
{
struct device_list_opp *new_dev;
const struct device *dev;
struct dentry *dentry;
/* Look for next list-dev */
list_for_each_entry(new_dev, &dev_opp->dev_list, node)
if (new_dev != list_dev)
break;
/* new_dev is guaranteed to be valid here */
dev = new_dev->dev;
debugfs_remove_recursive(new_dev->dentry);
opp_set_dev_name(dev, dev_opp->dentry_name);
dentry = debugfs_rename(rootdir, list_dev->dentry, rootdir,
dev_opp->dentry_name);
if (!dentry) {
dev_err(dev, "%s: Failed to rename link from: %s to %s\n",
__func__, dev_name(list_dev->dev), dev_name(dev));
return;
}
new_dev->dentry = dentry;
dev_opp->dentry = dentry;
}
/**
* opp_debug_unregister - remove a device opp node from debugfs opp directory
* @list_dev: list-dev pointer for device
* @dev_opp: the device-opp being removed
*
* Dynamically removes device specific directory from debugfs 'opp' directory.
*/
void opp_debug_unregister(struct device_list_opp *list_dev,
struct device_opp *dev_opp)
{
if (list_dev->dentry == dev_opp->dentry) {
/* Move the real dentry object under another device */
if (!list_is_singular(&dev_opp->dev_list)) {
opp_migrate_dentry(list_dev, dev_opp);
goto out;
}
dev_opp->dentry = NULL;
}
debugfs_remove_recursive(list_dev->dentry);
out:
list_dev->dentry = NULL;
}
static int __init opp_debug_init(void)
{
/* Create /sys/kernel/debug/opp directory */
rootdir = debugfs_create_dir("opp", NULL);
if (!rootdir) {
pr_err("%s: Failed to create root directory\n", __func__);
return -ENOMEM;
}
return 0;
}
core_initcall(opp_debug_init);

View File

@@ -17,6 +17,7 @@
#include <linux/device.h>
#include <linux/kernel.h>
#include <linux/list.h>
#include <linux/limits.h>
#include <linux/pm_opp.h>
#include <linux/rculist.h>
#include <linux/rcupdate.h>
@@ -50,9 +51,10 @@ extern struct mutex dev_opp_list_lock;
* are protected by the dev_opp_list_lock for integrity.
* IMPORTANT: the opp nodes should be maintained in increasing
* order.
* @dynamic: not-created from static DT entries.
* @available: true/false - marks if this OPP as available or not
* @dynamic: not-created from static DT entries.
* @turbo: true if turbo (boost) OPP
* @suspend: true if suspend OPP
* @rate: Frequency in hertz
* @u_volt: Target voltage in microvolts corresponding to this OPP
* @u_volt_min: Minimum voltage in microvolts corresponding to this OPP
@@ -63,6 +65,7 @@ extern struct mutex dev_opp_list_lock;
* @dev_opp: points back to the device_opp struct this opp belongs to
* @rcu_head: RCU callback head used for deferred freeing
* @np: OPP's device node.
* @dentry: debugfs dentry pointer (per opp)
*
* This structure stores the OPP information for a given device.
*/
@@ -72,6 +75,7 @@ struct dev_pm_opp {
bool available;
bool dynamic;
bool turbo;
bool suspend;
unsigned long rate;
unsigned long u_volt;
@@ -84,6 +88,10 @@ struct dev_pm_opp {
struct rcu_head rcu_head;
struct device_node *np;
#ifdef CONFIG_DEBUG_FS
struct dentry *dentry;
#endif
};
/**
@@ -91,6 +99,7 @@ struct dev_pm_opp {
* @node: list node
* @dev: device to which the struct object belongs
* @rcu_head: RCU callback head used for deferred freeing
* @dentry: debugfs dentry pointer (per device)
*
* This is an internal data structure maintaining the list of devices that are
* managed by 'struct device_opp'.
@@ -99,6 +108,10 @@ struct device_list_opp {
struct list_head node;
const struct device *dev;
struct rcu_head rcu_head;
#ifdef CONFIG_DEBUG_FS
struct dentry *dentry;
#endif
};
/**
@@ -113,7 +126,14 @@ struct device_list_opp {
* @dev_list: list of devices that share these OPPs
* @opp_list: list of opps
* @np: struct device_node pointer for opp's DT node.
* @clock_latency_ns_max: Max clock latency in nanoseconds.
* @shared_opp: OPP is shared between multiple devices.
* @suspend_opp: Pointer to OPP to be used during device suspend.
* @supported_hw: Array of version number to support.
* @supported_hw_count: Number of elements in supported_hw array.
* @prop_name: A name to postfix to many DT properties, while parsing them.
* @dentry: debugfs dentry pointer of the real device directory (not links).
* @dentry_name: Name of the real dentry.
*
* 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
@@ -135,6 +155,15 @@ struct device_opp {
unsigned long clock_latency_ns_max;
bool shared_opp;
struct dev_pm_opp *suspend_opp;
unsigned int *supported_hw;
unsigned int supported_hw_count;
const char *prop_name;
#ifdef CONFIG_DEBUG_FS
struct dentry *dentry;
char dentry_name[NAME_MAX];
#endif
};
/* Routines internal to opp core */
@@ -143,4 +172,26 @@ struct device_list_opp *_add_list_dev(const struct device *dev,
struct device_opp *dev_opp);
struct device_node *_of_get_opp_desc_node(struct device *dev);
#ifdef CONFIG_DEBUG_FS
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_register(struct device_list_opp *list_dev,
struct device_opp *dev_opp);
void opp_debug_unregister(struct device_list_opp *list_dev,
struct device_opp *dev_opp);
#else
static inline void opp_debug_remove_one(struct dev_pm_opp *opp) {}
static inline int opp_debug_create_one(struct dev_pm_opp *opp,
struct device_opp *dev_opp)
{ return 0; }
static inline int opp_debug_register(struct device_list_opp *list_dev,
struct device_opp *dev_opp)
{ return 0; }
static inline void opp_debug_unregister(struct device_list_opp *list_dev,
struct device_opp *dev_opp)
{ }
#endif /* DEBUG_FS */
#endif /* __DRIVER_OPP_H__ */