Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
openeuler
Kernel
提交
c8541203
K
Kernel
项目概览
openeuler
/
Kernel
大约 1 年 前同步成功
通知
5
Star
0
Fork
0
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
0
列表
看板
标记
里程碑
合并请求
0
DevOps
流水线
流水线任务
计划
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
K
Kernel
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
0
Issue
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
Pages
DevOps
DevOps
流水线
流水线任务
计划
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
流水线任务
提交
Issue看板
体验新版 GitCode,发现更多精彩内容 >>
提交
c8541203
编写于
5月 06, 2016
作者:
R
Rafael J. Wysocki
浏览文件
操作
浏览文件
下载
差异文件
Merge back new material for v4.7.
上级
21f8a99c
f47b72a1
变更
6
隐藏空白更改
内联
并排
Showing
6 changed file
with
763 addition
and
544 deletion
+763
-544
drivers/base/power/opp/Makefile
drivers/base/power/opp/Makefile
+1
-0
drivers/base/power/opp/core.c
drivers/base/power/opp/core.c
+21
-419
drivers/base/power/opp/cpu.c
drivers/base/power/opp/cpu.c
+97
-102
drivers/base/power/opp/of.c
drivers/base/power/opp/of.c
+591
-0
drivers/base/power/opp/opp.h
drivers/base/power/opp/opp.h
+14
-0
include/linux/pm_opp.h
include/linux/pm_opp.h
+39
-23
未找到文件。
drivers/base/power/opp/Makefile
浏览文件 @
c8541203
ccflags-$(CONFIG_DEBUG_DRIVER)
:=
-DDEBUG
obj-y
+=
core.o cpu.o
obj-$(CONFIG_OF)
+=
of.o
obj-$(CONFIG_DEBUG_FS)
+=
debugfs.o
drivers/base/power/opp/core.c
浏览文件 @
c8541203
...
...
@@ -18,7 +18,6 @@
#include <linux/err.h>
#include <linux/slab.h>
#include <linux/device.h>
#include <linux/of.h>
#include <linux/export.h>
#include <linux/regulator/consumer.h>
...
...
@@ -29,7 +28,7 @@
* from here, with each opp_table containing the list of opps it supports in
* various states of availability.
*/
static
LIST_HEAD
(
opp_tables
);
LIST_HEAD
(
opp_tables
);
/* Lock to allow exclusive modification to the device and opp lists */
DEFINE_MUTEX
(
opp_table_lock
);
...
...
@@ -53,26 +52,6 @@ static struct opp_device *_find_opp_dev(const struct device *dev,
return
NULL
;
}
static
struct
opp_table
*
_managed_opp
(
const
struct
device_node
*
np
)
{
struct
opp_table
*
opp_table
;
list_for_each_entry_rcu
(
opp_table
,
&
opp_tables
,
node
)
{
if
(
opp_table
->
np
==
np
)
{
/*
* Multiple devices can point to the same OPP table and
* so will have same node-pointer, np.
*
* But the OPPs will be considered as shared only if the
* OPP table contains a "opp-shared" property.
*/
return
opp_table
->
shared_opp
?
opp_table
:
NULL
;
}
}
return
NULL
;
}
/**
* _find_opp_table() - find opp_table struct using device pointer
* @dev: device pointer used to lookup OPP table
...
...
@@ -757,7 +736,6 @@ static struct opp_table *_add_opp_table(struct device *dev)
{
struct
opp_table
*
opp_table
;
struct
opp_device
*
opp_dev
;
struct
device_node
*
np
;
int
ret
;
/* Check for existing table for 'dev' first */
...
...
@@ -781,20 +759,7 @@ static struct opp_table *_add_opp_table(struct device *dev)
return
NULL
;
}
/*
* 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
);
}
_of_init_opp_table
(
opp_table
,
dev
);
/* Set regulator to a non-NULL error value */
opp_table
->
regulator
=
ERR_PTR
(
-
ENXIO
);
...
...
@@ -890,8 +855,8 @@ static void _kfree_opp_rcu(struct rcu_head *head)
* It is assumed that the caller holds required mutex for an RCU updater
* strategy.
*/
static
void
_opp_remove
(
struct
opp_table
*
opp_table
,
struct
dev_pm_opp
*
opp
,
bool
notify
)
void
_opp_remove
(
struct
opp_table
*
opp_table
,
struct
dev_pm_opp
*
opp
,
bool
notify
)
{
/*
* Notify the changes in the availability of the operable
...
...
@@ -952,8 +917,8 @@ void dev_pm_opp_remove(struct device *dev, unsigned long freq)
}
EXPORT_SYMBOL_GPL
(
dev_pm_opp_remove
);
st
atic
st
ruct
dev_pm_opp
*
_allocate_opp
(
struct
device
*
dev
,
struct
opp_table
**
opp_table
)
struct
dev_pm_opp
*
_allocate_opp
(
struct
device
*
dev
,
struct
opp_table
**
opp_table
)
{
struct
dev_pm_opp
*
opp
;
...
...
@@ -989,8 +954,8 @@ static bool _opp_supported_by_regulators(struct dev_pm_opp *opp,
return
true
;
}
static
int
_opp_add
(
struct
device
*
dev
,
struct
dev_pm_opp
*
new_opp
,
struct
opp_table
*
opp_table
)
int
_opp_add
(
struct
device
*
dev
,
struct
dev_pm_opp
*
new_opp
,
struct
opp_table
*
opp_table
)
{
struct
dev_pm_opp
*
opp
;
struct
list_head
*
head
=
&
opp_table
->
opp_list
;
...
...
@@ -1066,8 +1031,8 @@ static int _opp_add(struct device *dev, struct dev_pm_opp *new_opp,
* Duplicate OPPs (both freq and volt are same) and !opp->available
* -ENOMEM Memory allocation failure
*/
static
int
_opp_add_v1
(
struct
device
*
dev
,
unsigned
long
freq
,
long
u_volt
,
bool
dynamic
)
int
_opp_add_v1
(
struct
device
*
dev
,
unsigned
long
freq
,
long
u_volt
,
bool
dynamic
)
{
struct
opp_table
*
opp_table
;
struct
dev_pm_opp
*
new_opp
;
...
...
@@ -1112,83 +1077,6 @@ static int _opp_add_v1(struct device *dev, unsigned long freq, long u_volt,
return
ret
;
}
/* TODO: Support multiple regulators */
static
int
opp_parse_supplies
(
struct
dev_pm_opp
*
opp
,
struct
device
*
dev
,
struct
opp_table
*
opp_table
)
{
u32
microvolt
[
3
]
=
{
0
};
u32
val
;
int
count
,
ret
;
struct
property
*
prop
=
NULL
;
char
name
[
NAME_MAX
];
/* Search for "opp-microvolt-<name>" */
if
(
opp_table
->
prop_name
)
{
snprintf
(
name
,
sizeof
(
name
),
"opp-microvolt-%s"
,
opp_table
->
prop_name
);
prop
=
of_find_property
(
opp
->
np
,
name
,
NULL
);
}
if
(
!
prop
)
{
/* Search for "opp-microvolt" */
sprintf
(
name
,
"opp-microvolt"
);
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 %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 %s property (%d)
\n
"
,
__func__
,
name
,
count
);
return
-
EINVAL
;
}
ret
=
of_property_read_u32_array
(
opp
->
np
,
name
,
microvolt
,
count
);
if
(
ret
)
{
dev_err
(
dev
,
"%s: error parsing %s: %d
\n
"
,
__func__
,
name
,
ret
);
return
-
EINVAL
;
}
opp
->
u_volt
=
microvolt
[
0
];
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>" */
prop
=
NULL
;
if
(
opp_table
->
prop_name
)
{
snprintf
(
name
,
sizeof
(
name
),
"opp-microamp-%s"
,
opp_table
->
prop_name
);
prop
=
of_find_property
(
opp
->
np
,
name
,
NULL
);
}
if
(
!
prop
)
{
/* Search for "opp-microamp" */
sprintf
(
name
,
"opp-microamp"
);
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.
...
...
@@ -1517,144 +1405,6 @@ void dev_pm_opp_put_regulator(struct device *dev)
}
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
)
{
unsigned
int
count
=
opp_table
->
supported_hw_count
;
u32
version
;
int
ret
;
if
(
!
opp_table
->
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
&
opp_table
->
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
* @np: device node
*
* 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
* removed by dev_pm_opp_remove.
*
* 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.
*
* 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
* -EINVAL Failed parsing the OPP node
*/
static
int
_opp_add_static_v2
(
struct
device
*
dev
,
struct
device_node
*
np
)
{
struct
opp_table
*
opp_table
;
struct
dev_pm_opp
*
new_opp
;
u64
rate
;
u32
val
;
int
ret
;
/* Hold our table modification lock here */
mutex_lock
(
&
opp_table_lock
);
new_opp
=
_allocate_opp
(
dev
,
&
opp_table
);
if
(
!
new_opp
)
{
ret
=
-
ENOMEM
;
goto
unlock
;
}
ret
=
of_property_read_u64
(
np
,
"opp-hz"
,
&
rate
);
if
(
ret
<
0
)
{
dev_err
(
dev
,
"%s: opp-hz not found
\n
"
,
__func__
);
goto
free_opp
;
}
/* Check if the OPP supports hardware's hierarchy of versions or not */
if
(
!
_opp_is_supported
(
dev
,
opp_table
,
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
* guaranteed in clk API.
*/
new_opp
->
rate
=
(
unsigned
long
)
rate
;
new_opp
->
turbo
=
of_property_read_bool
(
np
,
"turbo-mode"
);
new_opp
->
np
=
np
;
new_opp
->
dynamic
=
false
;
new_opp
->
available
=
true
;
if
(
!
of_property_read_u32
(
np
,
"clock-latency-ns"
,
&
val
))
new_opp
->
clock_latency_ns
=
val
;
ret
=
opp_parse_supplies
(
new_opp
,
dev
,
opp_table
);
if
(
ret
)
goto
free_opp
;
ret
=
_opp_add
(
dev
,
new_opp
,
opp_table
);
if
(
ret
)
goto
free_opp
;
/* OPP to select on device suspend */
if
(
of_property_read_bool
(
np
,
"opp-suspend"
))
{
if
(
opp_table
->
suspend_opp
)
{
dev_warn
(
dev
,
"%s: Multiple suspend OPPs found (%lu %lu)
\n
"
,
__func__
,
opp_table
->
suspend_opp
->
rate
,
new_opp
->
rate
);
}
else
{
new_opp
->
suspend
=
true
;
opp_table
->
suspend_opp
=
new_opp
;
}
}
if
(
new_opp
->
clock_latency_ns
>
opp_table
->
clock_latency_ns_max
)
opp_table
->
clock_latency_ns_max
=
new_opp
->
clock_latency_ns
;
mutex_unlock
(
&
opp_table_lock
);
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
,
new_opp
->
u_volt_min
,
new_opp
->
u_volt_max
,
new_opp
->
clock_latency_ns
);
/*
* Notify the changes in the availability of the operable
* frequency/voltage list.
*/
srcu_notifier_call_chain
(
&
opp_table
->
srcu_head
,
OPP_EVENT_ADD
,
new_opp
);
return
0
;
free_opp:
_opp_remove
(
opp_table
,
new_opp
,
false
);
unlock:
mutex_unlock
(
&
opp_table_lock
);
return
ret
;
}
/**
* dev_pm_opp_add() - Add an OPP table from a table definitions
* @dev: device for which we do this operation
...
...
@@ -1842,21 +1592,11 @@ struct srcu_notifier_head *dev_pm_opp_get_notifier(struct device *dev)
}
EXPORT_SYMBOL_GPL
(
dev_pm_opp_get_notifier
);
#ifdef CONFIG_OF
/**
* dev_pm_opp_of_remove_table() - Free OPP table entries created from static DT
* entries
* @dev: device pointer used to lookup OPP table.
*
* Free OPPs created using static entries present in DT.
*
* Locking: The internal opp_table and opp structures are RCU protected.
* Hence this function indirectly 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.
/*
* Free OPPs either created using static entries present in DT or even the
* dynamically added entries based on remove_all param.
*/
void
dev_pm_opp_of_remove_table
(
struct
device
*
dev
)
void
_dev_pm_opp_remove_table
(
struct
device
*
dev
,
bool
remove_all
)
{
struct
opp_table
*
opp_table
;
struct
dev_pm_opp
*
opp
,
*
tmp
;
...
...
@@ -1881,7 +1621,7 @@ void dev_pm_opp_of_remove_table(struct device *dev)
if
(
list_is_singular
(
&
opp_table
->
dev_list
))
{
/* Free static OPPs */
list_for_each_entry_safe
(
opp
,
tmp
,
&
opp_table
->
opp_list
,
node
)
{
if
(
!
opp
->
dynamic
)
if
(
remove_all
||
!
opp
->
dynamic
)
_opp_remove
(
opp_table
,
opp
,
true
);
}
}
else
{
...
...
@@ -1891,160 +1631,22 @@ void dev_pm_opp_of_remove_table(struct device *dev)
unlock:
mutex_unlock
(
&
opp_table_lock
);
}
EXPORT_SYMBOL_GPL
(
dev_pm_opp_of_remove_table
);
/* Returns opp descriptor node for a device, caller must do of_node_put() */
struct
device_node
*
_of_get_opp_desc_node
(
struct
device
*
dev
)
{
/*
* TODO: Support for multiple OPP tables.
*
* There should be only ONE phandle present in "operating-points-v2"
* property.
*/
return
of_parse_phandle
(
dev
->
of_node
,
"operating-points-v2"
,
0
);
}
/* Initializes OPP tables based on new bindings */
static
int
_of_add_opp_table_v2
(
struct
device
*
dev
,
struct
device_node
*
opp_np
)
{
struct
device_node
*
np
;
struct
opp_table
*
opp_table
;
int
ret
=
0
,
count
=
0
;
mutex_lock
(
&
opp_table_lock
);
opp_table
=
_managed_opp
(
opp_np
);
if
(
opp_table
)
{
/* OPPs are already managed */
if
(
!
_add_opp_dev
(
dev
,
opp_table
))
ret
=
-
ENOMEM
;
mutex_unlock
(
&
opp_table_lock
);
return
ret
;
}
mutex_unlock
(
&
opp_table_lock
);
/* We have opp-table node now, iterate over it and add OPPs */
for_each_available_child_of_node
(
opp_np
,
np
)
{
count
++
;
ret
=
_opp_add_static_v2
(
dev
,
np
);
if
(
ret
)
{
dev_err
(
dev
,
"%s: Failed to add OPP, %d
\n
"
,
__func__
,
ret
);
goto
free_table
;
}
}
/* There should be one of more OPP defined */
if
(
WARN_ON
(
!
count
))
return
-
ENOENT
;
mutex_lock
(
&
opp_table_lock
);
opp_table
=
_find_opp_table
(
dev
);
if
(
WARN_ON
(
IS_ERR
(
opp_table
)))
{
ret
=
PTR_ERR
(
opp_table
);
mutex_unlock
(
&
opp_table_lock
);
goto
free_table
;
}
opp_table
->
np
=
opp_np
;
opp_table
->
shared_opp
=
of_property_read_bool
(
opp_np
,
"opp-shared"
);
mutex_unlock
(
&
opp_table_lock
);
return
0
;
free_table:
dev_pm_opp_of_remove_table
(
dev
);
return
ret
;
}
/* Initializes OPP tables based on old-deprecated bindings */
static
int
_of_add_opp_table_v1
(
struct
device
*
dev
)
{
const
struct
property
*
prop
;
const
__be32
*
val
;
int
nr
;
prop
=
of_find_property
(
dev
->
of_node
,
"operating-points"
,
NULL
);
if
(
!
prop
)
return
-
ENODEV
;
if
(
!
prop
->
value
)
return
-
ENODATA
;
/*
* Each OPP is a set of tuples consisting of frequency and
* voltage like <freq-kHz vol-uV>.
*/
nr
=
prop
->
length
/
sizeof
(
u32
);
if
(
nr
%
2
)
{
dev_err
(
dev
,
"%s: Invalid OPP table
\n
"
,
__func__
);
return
-
EINVAL
;
}
val
=
prop
->
value
;
while
(
nr
)
{
unsigned
long
freq
=
be32_to_cpup
(
val
++
)
*
1000
;
unsigned
long
volt
=
be32_to_cpup
(
val
++
);
if
(
_opp_add_v1
(
dev
,
freq
,
volt
,
false
))
dev_warn
(
dev
,
"%s: Failed to add OPP %ld
\n
"
,
__func__
,
freq
);
nr
-=
2
;
}
return
0
;
}
/**
* dev_pm_opp_
of_add_table() - Initialize opp table from device tre
e
* dev_pm_opp_
remove_table() - Free all OPPs associated with the devic
e
* @dev: device pointer used to lookup OPP table.
*
* Register the initial OPP table with the OPP library for given device.
* Free both OPPs created using static entries present in DT and the
* dynamically added entries.
*
* Locking: The internal opp_table and opp structures are RCU protected.
* Hence this function indirectly 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
* -ENODEV when 'operating-points' property is not found or is invalid data
* in device node.
* -ENODATA when empty 'operating-points' property is found
* -EINVAL when invalid entries are found in opp-v2 table
*/
int
dev_pm_opp_of_add
_table
(
struct
device
*
dev
)
void
dev_pm_opp_remove
_table
(
struct
device
*
dev
)
{
struct
device_node
*
opp_np
;
int
ret
;
/*
* OPPs have two version of bindings now. The older one is deprecated,
* try for the new binding first.
*/
opp_np
=
_of_get_opp_desc_node
(
dev
);
if
(
!
opp_np
)
{
/*
* Try old-deprecated bindings for backward compatibility with
* older dtbs.
*/
return
_of_add_opp_table_v1
(
dev
);
}
ret
=
_of_add_opp_table_v2
(
dev
,
opp_np
);
of_node_put
(
opp_np
);
return
ret
;
_dev_pm_opp_remove_table
(
dev
,
true
);
}
EXPORT_SYMBOL_GPL
(
dev_pm_opp_of_add_table
);
#endif
EXPORT_SYMBOL_GPL
(
dev_pm_opp_remove_table
);
drivers/base/power/opp/cpu.c
浏览文件 @
c8541203
...
...
@@ -18,7 +18,6 @@
#include <linux/err.h>
#include <linux/errno.h>
#include <linux/export.h>
#include <linux/of.h>
#include <linux/slab.h>
#include "opp.h"
...
...
@@ -119,8 +118,66 @@ void dev_pm_opp_free_cpufreq_table(struct device *dev,
EXPORT_SYMBOL_GPL
(
dev_pm_opp_free_cpufreq_table
);
#endif
/* CONFIG_CPU_FREQ */
/* 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
)
void
_dev_pm_opp_cpumask_remove_table
(
const
struct
cpumask
*
cpumask
,
bool
of
)
{
struct
device
*
cpu_dev
;
int
cpu
;
WARN_ON
(
cpumask_empty
(
cpumask
));
for_each_cpu
(
cpu
,
cpumask
)
{
cpu_dev
=
get_cpu_device
(
cpu
);
if
(
!
cpu_dev
)
{
pr_err
(
"%s: failed to get cpu%d device
\n
"
,
__func__
,
cpu
);
continue
;
}
if
(
of
)
dev_pm_opp_of_remove_table
(
cpu_dev
);
else
dev_pm_opp_remove_table
(
cpu_dev
);
}
}
/**
* dev_pm_opp_cpumask_remove_table() - Removes OPP table for @cpumask
* @cpumask: cpumask for which OPP table needs to be removed
*
* This removes the OPP tables for CPUs present in the @cpumask.
* This should be used to remove all the OPPs entries associated with
* the cpus in @cpumask.
*
* 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_cpumask_remove_table
(
const
struct
cpumask
*
cpumask
)
{
_dev_pm_opp_cpumask_remove_table
(
cpumask
,
false
);
}
EXPORT_SYMBOL_GPL
(
dev_pm_opp_cpumask_remove_table
);
/**
* dev_pm_opp_set_sharing_cpus() - Mark OPP table as shared by few CPUs
* @cpu_dev: CPU device for which we do this operation
* @cpumask: cpumask of the CPUs which share the OPP table with @cpu_dev
*
* This marks OPP table of the @cpu_dev as shared by the CPUs present in
* @cpumask.
*
* Returns -ENODEV if OPP table isn't already present.
*
* 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_sharing_cpus
(
struct
device
*
cpu_dev
,
const
struct
cpumask
*
cpumask
)
{
struct
opp_device
*
opp_dev
;
struct
opp_table
*
opp_table
;
...
...
@@ -131,7 +188,7 @@ int dev_pm_opp_set_sharing_cpus(struct device *cpu_dev, cpumask_var_t cpumask)
opp_table
=
_find_opp_table
(
cpu_dev
);
if
(
IS_ERR
(
opp_table
))
{
ret
=
-
EINVAL
;
ret
=
PTR_ERR
(
opp_table
)
;
goto
unlock
;
}
...
...
@@ -152,6 +209,9 @@ int dev_pm_opp_set_sharing_cpus(struct device *cpu_dev, cpumask_var_t cpumask)
__func__
,
cpu
);
continue
;
}
/* Mark opp-table as multiple CPUs are sharing it now */
opp_table
->
shared_opp
=
true
;
}
unlock:
mutex_unlock
(
&
opp_table_lock
);
...
...
@@ -160,112 +220,47 @@ int dev_pm_opp_set_sharing_cpus(struct device *cpu_dev, cpumask_var_t cpumask)
}
EXPORT_SYMBOL_GPL
(
dev_pm_opp_set_sharing_cpus
);
#ifdef CONFIG_OF
void
dev_pm_opp_of_cpumask_remove_table
(
cpumask_var_t
cpumask
)
{
struct
device
*
cpu_dev
;
int
cpu
;
WARN_ON
(
cpumask_empty
(
cpumask
));
for_each_cpu
(
cpu
,
cpumask
)
{
cpu_dev
=
get_cpu_device
(
cpu
);
if
(
!
cpu_dev
)
{
pr_err
(
"%s: failed to get cpu%d device
\n
"
,
__func__
,
cpu
);
continue
;
}
dev_pm_opp_of_remove_table
(
cpu_dev
);
}
}
EXPORT_SYMBOL_GPL
(
dev_pm_opp_of_cpumask_remove_table
);
int
dev_pm_opp_of_cpumask_add_table
(
cpumask_var_t
cpumask
)
{
struct
device
*
cpu_dev
;
int
cpu
,
ret
=
0
;
WARN_ON
(
cpumask_empty
(
cpumask
));
for_each_cpu
(
cpu
,
cpumask
)
{
cpu_dev
=
get_cpu_device
(
cpu
);
if
(
!
cpu_dev
)
{
pr_err
(
"%s: failed to get cpu%d device
\n
"
,
__func__
,
cpu
);
continue
;
}
ret
=
dev_pm_opp_of_add_table
(
cpu_dev
);
if
(
ret
)
{
pr_err
(
"%s: couldn't find opp table for cpu:%d, %d
\n
"
,
__func__
,
cpu
,
ret
);
/* Free all other OPPs */
dev_pm_opp_of_cpumask_remove_table
(
cpumask
);
break
;
}
}
return
ret
;
}
EXPORT_SYMBOL_GPL
(
dev_pm_opp_of_cpumask_add_table
);
/*
* Works only for OPP v2 bindings.
/**
* dev_pm_opp_get_sharing_cpus() - Get cpumask of CPUs sharing OPPs with @cpu_dev
* @cpu_dev: CPU device for which we do this operation
* @cpumask: cpumask to update with information of sharing CPUs
*
* This updates the @cpumask with CPUs that are sharing OPPs with @cpu_dev.
*
* Returns -ENOENT if operating-points-v2 bindings aren't supported.
* Returns -ENODEV if OPP table isn't already present.
*
* 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_
of_get_sharing_cpus
(
struct
device
*
cpu_dev
,
cpumask_var_t
cpumask
)
int
dev_pm_opp_
get_sharing_cpus
(
struct
device
*
cpu_dev
,
struct
cpumask
*
cpumask
)
{
struct
device_node
*
np
,
*
tmp_np
;
struct
device
*
tcpu_dev
;
int
cpu
,
ret
=
0
;
/* Get OPP descriptor node */
np
=
_of_get_opp_desc_node
(
cpu_dev
);
if
(
!
np
)
{
dev_dbg
(
cpu_dev
,
"%s: Couldn't find cpu_dev node.
\n
"
,
__func__
);
return
-
ENOENT
;
}
cpumask_set_cpu
(
cpu_dev
->
id
,
cpumask
);
/* OPPs are shared ? */
if
(
!
of_property_read_bool
(
np
,
"opp-shared"
))
goto
put_cpu_node
;
for_each_possible_cpu
(
cpu
)
{
if
(
cpu
==
cpu_dev
->
id
)
continue
;
struct
opp_device
*
opp_dev
;
struct
opp_table
*
opp_table
;
int
ret
=
0
;
tcpu_dev
=
get_cpu_device
(
cpu
);
if
(
!
tcpu_dev
)
{
dev_err
(
cpu_dev
,
"%s: failed to get cpu%d device
\n
"
,
__func__
,
cpu
);
ret
=
-
ENODEV
;
goto
put_cpu_node
;
}
mutex_lock
(
&
opp_table_lock
);
/* Get OPP descriptor node */
tmp_np
=
_of_get_opp_desc_node
(
tcpu_dev
);
if
(
!
tmp_np
)
{
dev_err
(
tcpu_dev
,
"%s: Couldn't find tcpu_dev node.
\n
"
,
__func__
);
ret
=
-
ENOENT
;
goto
put_cpu_node
;
}
opp_table
=
_find_opp_table
(
cpu_dev
);
if
(
IS_ERR
(
opp_table
))
{
ret
=
PTR_ERR
(
opp_table
);
goto
unlock
;
}
/* CPUs are sharing opp node */
if
(
np
==
tmp_np
)
cpumask_set_cpu
(
cpu
,
cpumask
);
cpumask_clear
(
cpumask
);
of_node_put
(
tmp_np
);
if
(
opp_table
->
shared_opp
)
{
list_for_each_entry
(
opp_dev
,
&
opp_table
->
dev_list
,
node
)
cpumask_set_cpu
(
opp_dev
->
dev
->
id
,
cpumask
);
}
else
{
cpumask_set_cpu
(
cpu_dev
->
id
,
cpumask
);
}
put_cpu_node:
of_node_put
(
np
);
unlock:
mutex_unlock
(
&
opp_table_lock
);
return
ret
;
}
EXPORT_SYMBOL_GPL
(
dev_pm_opp_of_get_sharing_cpus
);
#endif
EXPORT_SYMBOL_GPL
(
dev_pm_opp_get_sharing_cpus
);
drivers/base/power/opp/of.c
0 → 100644
浏览文件 @
c8541203
/*
* Generic OPP OF helpers
*
* Copyright (C) 2009-2010 Texas Instruments Incorporated.
* Nishanth Menon
* Romit Dasgupta
* Kevin Hilman
*
* 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/cpu.h>
#include <linux/errno.h>
#include <linux/device.h>
#include <linux/of.h>
#include <linux/export.h>
#include "opp.h"
static
struct
opp_table
*
_managed_opp
(
const
struct
device_node
*
np
)
{
struct
opp_table
*
opp_table
;
list_for_each_entry_rcu
(
opp_table
,
&
opp_tables
,
node
)
{
if
(
opp_table
->
np
==
np
)
{
/*
* Multiple devices can point to the same OPP table and
* so will have same node-pointer, np.
*
* But the OPPs will be considered as shared only if the
* OPP table contains a "opp-shared" property.
*/
return
opp_table
->
shared_opp
?
opp_table
:
NULL
;
}
}
return
NULL
;
}
void
_of_init_opp_table
(
struct
opp_table
*
opp_table
,
struct
device
*
dev
)
{
struct
device_node
*
np
;
/*
* 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
);
}
}
static
bool
_opp_is_supported
(
struct
device
*
dev
,
struct
opp_table
*
opp_table
,
struct
device_node
*
np
)
{
unsigned
int
count
=
opp_table
->
supported_hw_count
;
u32
version
;
int
ret
;
if
(
!
opp_table
->
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
&
opp_table
->
supported_hw
[
count
]))
return
false
;
}
return
true
;
}
/* TODO: Support multiple regulators */
static
int
opp_parse_supplies
(
struct
dev_pm_opp
*
opp
,
struct
device
*
dev
,
struct
opp_table
*
opp_table
)
{
u32
microvolt
[
3
]
=
{
0
};
u32
val
;
int
count
,
ret
;
struct
property
*
prop
=
NULL
;
char
name
[
NAME_MAX
];
/* Search for "opp-microvolt-<name>" */
if
(
opp_table
->
prop_name
)
{
snprintf
(
name
,
sizeof
(
name
),
"opp-microvolt-%s"
,
opp_table
->
prop_name
);
prop
=
of_find_property
(
opp
->
np
,
name
,
NULL
);
}
if
(
!
prop
)
{
/* Search for "opp-microvolt" */
sprintf
(
name
,
"opp-microvolt"
);
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 %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 %s property (%d)
\n
"
,
__func__
,
name
,
count
);
return
-
EINVAL
;
}
ret
=
of_property_read_u32_array
(
opp
->
np
,
name
,
microvolt
,
count
);
if
(
ret
)
{
dev_err
(
dev
,
"%s: error parsing %s: %d
\n
"
,
__func__
,
name
,
ret
);
return
-
EINVAL
;
}
opp
->
u_volt
=
microvolt
[
0
];
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>" */
prop
=
NULL
;
if
(
opp_table
->
prop_name
)
{
snprintf
(
name
,
sizeof
(
name
),
"opp-microamp-%s"
,
opp_table
->
prop_name
);
prop
=
of_find_property
(
opp
->
np
,
name
,
NULL
);
}
if
(
!
prop
)
{
/* Search for "opp-microamp" */
sprintf
(
name
,
"opp-microamp"
);
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_of_remove_table() - Free OPP table entries created from static DT
* entries
* @dev: device pointer used to lookup OPP table.
*
* Free OPPs created using static entries present in DT.
*
* Locking: The internal opp_table and opp structures are RCU protected.
* Hence this function indirectly 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_of_remove_table
(
struct
device
*
dev
)
{
_dev_pm_opp_remove_table
(
dev
,
false
);
}
EXPORT_SYMBOL_GPL
(
dev_pm_opp_of_remove_table
);
/* Returns opp descriptor node for a device, caller must do of_node_put() */
struct
device_node
*
_of_get_opp_desc_node
(
struct
device
*
dev
)
{
/*
* TODO: Support for multiple OPP tables.
*
* There should be only ONE phandle present in "operating-points-v2"
* property.
*/
return
of_parse_phandle
(
dev
->
of_node
,
"operating-points-v2"
,
0
);
}
/**
* _opp_add_static_v2() - Allocate static OPPs (As per 'v2' DT bindings)
* @dev: device for which we do this operation
* @np: device node
*
* 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
* removed by dev_pm_opp_remove.
*
* 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.
*
* 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
* -EINVAL Failed parsing the OPP node
*/
static
int
_opp_add_static_v2
(
struct
device
*
dev
,
struct
device_node
*
np
)
{
struct
opp_table
*
opp_table
;
struct
dev_pm_opp
*
new_opp
;
u64
rate
;
u32
val
;
int
ret
;
/* Hold our table modification lock here */
mutex_lock
(
&
opp_table_lock
);
new_opp
=
_allocate_opp
(
dev
,
&
opp_table
);
if
(
!
new_opp
)
{
ret
=
-
ENOMEM
;
goto
unlock
;
}
ret
=
of_property_read_u64
(
np
,
"opp-hz"
,
&
rate
);
if
(
ret
<
0
)
{
dev_err
(
dev
,
"%s: opp-hz not found
\n
"
,
__func__
);
goto
free_opp
;
}
/* Check if the OPP supports hardware's hierarchy of versions or not */
if
(
!
_opp_is_supported
(
dev
,
opp_table
,
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
* guaranteed in clk API.
*/
new_opp
->
rate
=
(
unsigned
long
)
rate
;
new_opp
->
turbo
=
of_property_read_bool
(
np
,
"turbo-mode"
);
new_opp
->
np
=
np
;
new_opp
->
dynamic
=
false
;
new_opp
->
available
=
true
;
if
(
!
of_property_read_u32
(
np
,
"clock-latency-ns"
,
&
val
))
new_opp
->
clock_latency_ns
=
val
;
ret
=
opp_parse_supplies
(
new_opp
,
dev
,
opp_table
);
if
(
ret
)
goto
free_opp
;
ret
=
_opp_add
(
dev
,
new_opp
,
opp_table
);
if
(
ret
)
goto
free_opp
;
/* OPP to select on device suspend */
if
(
of_property_read_bool
(
np
,
"opp-suspend"
))
{
if
(
opp_table
->
suspend_opp
)
{
dev_warn
(
dev
,
"%s: Multiple suspend OPPs found (%lu %lu)
\n
"
,
__func__
,
opp_table
->
suspend_opp
->
rate
,
new_opp
->
rate
);
}
else
{
new_opp
->
suspend
=
true
;
opp_table
->
suspend_opp
=
new_opp
;
}
}
if
(
new_opp
->
clock_latency_ns
>
opp_table
->
clock_latency_ns_max
)
opp_table
->
clock_latency_ns_max
=
new_opp
->
clock_latency_ns
;
mutex_unlock
(
&
opp_table_lock
);
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
,
new_opp
->
u_volt_min
,
new_opp
->
u_volt_max
,
new_opp
->
clock_latency_ns
);
/*
* Notify the changes in the availability of the operable
* frequency/voltage list.
*/
srcu_notifier_call_chain
(
&
opp_table
->
srcu_head
,
OPP_EVENT_ADD
,
new_opp
);
return
0
;
free_opp:
_opp_remove
(
opp_table
,
new_opp
,
false
);
unlock:
mutex_unlock
(
&
opp_table_lock
);
return
ret
;
}
/* Initializes OPP tables based on new bindings */
static
int
_of_add_opp_table_v2
(
struct
device
*
dev
,
struct
device_node
*
opp_np
)
{
struct
device_node
*
np
;
struct
opp_table
*
opp_table
;
int
ret
=
0
,
count
=
0
;
mutex_lock
(
&
opp_table_lock
);
opp_table
=
_managed_opp
(
opp_np
);
if
(
opp_table
)
{
/* OPPs are already managed */
if
(
!
_add_opp_dev
(
dev
,
opp_table
))
ret
=
-
ENOMEM
;
mutex_unlock
(
&
opp_table_lock
);
return
ret
;
}
mutex_unlock
(
&
opp_table_lock
);
/* We have opp-table node now, iterate over it and add OPPs */
for_each_available_child_of_node
(
opp_np
,
np
)
{
count
++
;
ret
=
_opp_add_static_v2
(
dev
,
np
);
if
(
ret
)
{
dev_err
(
dev
,
"%s: Failed to add OPP, %d
\n
"
,
__func__
,
ret
);
goto
free_table
;
}
}
/* There should be one of more OPP defined */
if
(
WARN_ON
(
!
count
))
return
-
ENOENT
;
mutex_lock
(
&
opp_table_lock
);
opp_table
=
_find_opp_table
(
dev
);
if
(
WARN_ON
(
IS_ERR
(
opp_table
)))
{
ret
=
PTR_ERR
(
opp_table
);
mutex_unlock
(
&
opp_table_lock
);
goto
free_table
;
}
opp_table
->
np
=
opp_np
;
opp_table
->
shared_opp
=
of_property_read_bool
(
opp_np
,
"opp-shared"
);
mutex_unlock
(
&
opp_table_lock
);
return
0
;
free_table:
dev_pm_opp_of_remove_table
(
dev
);
return
ret
;
}
/* Initializes OPP tables based on old-deprecated bindings */
static
int
_of_add_opp_table_v1
(
struct
device
*
dev
)
{
const
struct
property
*
prop
;
const
__be32
*
val
;
int
nr
;
prop
=
of_find_property
(
dev
->
of_node
,
"operating-points"
,
NULL
);
if
(
!
prop
)
return
-
ENODEV
;
if
(
!
prop
->
value
)
return
-
ENODATA
;
/*
* Each OPP is a set of tuples consisting of frequency and
* voltage like <freq-kHz vol-uV>.
*/
nr
=
prop
->
length
/
sizeof
(
u32
);
if
(
nr
%
2
)
{
dev_err
(
dev
,
"%s: Invalid OPP table
\n
"
,
__func__
);
return
-
EINVAL
;
}
val
=
prop
->
value
;
while
(
nr
)
{
unsigned
long
freq
=
be32_to_cpup
(
val
++
)
*
1000
;
unsigned
long
volt
=
be32_to_cpup
(
val
++
);
if
(
_opp_add_v1
(
dev
,
freq
,
volt
,
false
))
dev_warn
(
dev
,
"%s: Failed to add OPP %ld
\n
"
,
__func__
,
freq
);
nr
-=
2
;
}
return
0
;
}
/**
* dev_pm_opp_of_add_table() - Initialize opp table from device tree
* @dev: device pointer used to lookup OPP table.
*
* Register the initial OPP table with the OPP library for given device.
*
* Locking: The internal opp_table and opp structures are RCU protected.
* Hence this function indirectly 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
* -ENODEV when 'operating-points' property is not found or is invalid data
* in device node.
* -ENODATA when empty 'operating-points' property is found
* -EINVAL when invalid entries are found in opp-v2 table
*/
int
dev_pm_opp_of_add_table
(
struct
device
*
dev
)
{
struct
device_node
*
opp_np
;
int
ret
;
/*
* OPPs have two version of bindings now. The older one is deprecated,
* try for the new binding first.
*/
opp_np
=
_of_get_opp_desc_node
(
dev
);
if
(
!
opp_np
)
{
/*
* Try old-deprecated bindings for backward compatibility with
* older dtbs.
*/
return
_of_add_opp_table_v1
(
dev
);
}
ret
=
_of_add_opp_table_v2
(
dev
,
opp_np
);
of_node_put
(
opp_np
);
return
ret
;
}
EXPORT_SYMBOL_GPL
(
dev_pm_opp_of_add_table
);
/* CPU device specific helpers */
/**
* dev_pm_opp_of_cpumask_remove_table() - Removes OPP table for @cpumask
* @cpumask: cpumask for which OPP table needs to be removed
*
* This removes the OPP tables for CPUs present in the @cpumask.
* This should be used only to remove static entries created from DT.
*
* 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_of_cpumask_remove_table
(
const
struct
cpumask
*
cpumask
)
{
_dev_pm_opp_cpumask_remove_table
(
cpumask
,
true
);
}
EXPORT_SYMBOL_GPL
(
dev_pm_opp_of_cpumask_remove_table
);
/**
* dev_pm_opp_of_cpumask_add_table() - Adds OPP table for @cpumask
* @cpumask: cpumask for which OPP table needs to be added.
*
* This adds the OPP tables for CPUs present in the @cpumask.
*
* 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_of_cpumask_add_table
(
const
struct
cpumask
*
cpumask
)
{
struct
device
*
cpu_dev
;
int
cpu
,
ret
=
0
;
WARN_ON
(
cpumask_empty
(
cpumask
));
for_each_cpu
(
cpu
,
cpumask
)
{
cpu_dev
=
get_cpu_device
(
cpu
);
if
(
!
cpu_dev
)
{
pr_err
(
"%s: failed to get cpu%d device
\n
"
,
__func__
,
cpu
);
continue
;
}
ret
=
dev_pm_opp_of_add_table
(
cpu_dev
);
if
(
ret
)
{
pr_err
(
"%s: couldn't find opp table for cpu:%d, %d
\n
"
,
__func__
,
cpu
,
ret
);
/* Free all other OPPs */
dev_pm_opp_of_cpumask_remove_table
(
cpumask
);
break
;
}
}
return
ret
;
}
EXPORT_SYMBOL_GPL
(
dev_pm_opp_of_cpumask_add_table
);
/*
* Works only for OPP v2 bindings.
*
* Returns -ENOENT if operating-points-v2 bindings aren't supported.
*/
/**
* dev_pm_opp_of_get_sharing_cpus() - Get cpumask of CPUs sharing OPPs with
* @cpu_dev using operating-points-v2
* bindings.
*
* @cpu_dev: CPU device for which we do this operation
* @cpumask: cpumask to update with information of sharing CPUs
*
* This updates the @cpumask with CPUs that are sharing OPPs with @cpu_dev.
*
* Returns -ENOENT if operating-points-v2 isn't present for @cpu_dev.
*
* 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_of_get_sharing_cpus
(
struct
device
*
cpu_dev
,
struct
cpumask
*
cpumask
)
{
struct
device_node
*
np
,
*
tmp_np
;
struct
device
*
tcpu_dev
;
int
cpu
,
ret
=
0
;
/* Get OPP descriptor node */
np
=
_of_get_opp_desc_node
(
cpu_dev
);
if
(
!
np
)
{
dev_dbg
(
cpu_dev
,
"%s: Couldn't find cpu_dev node.
\n
"
,
__func__
);
return
-
ENOENT
;
}
cpumask_set_cpu
(
cpu_dev
->
id
,
cpumask
);
/* OPPs are shared ? */
if
(
!
of_property_read_bool
(
np
,
"opp-shared"
))
goto
put_cpu_node
;
for_each_possible_cpu
(
cpu
)
{
if
(
cpu
==
cpu_dev
->
id
)
continue
;
tcpu_dev
=
get_cpu_device
(
cpu
);
if
(
!
tcpu_dev
)
{
dev_err
(
cpu_dev
,
"%s: failed to get cpu%d device
\n
"
,
__func__
,
cpu
);
ret
=
-
ENODEV
;
goto
put_cpu_node
;
}
/* Get OPP descriptor node */
tmp_np
=
_of_get_opp_desc_node
(
tcpu_dev
);
if
(
!
tmp_np
)
{
dev_err
(
tcpu_dev
,
"%s: Couldn't find tcpu_dev node.
\n
"
,
__func__
);
ret
=
-
ENOENT
;
goto
put_cpu_node
;
}
/* CPUs are sharing opp node */
if
(
np
==
tmp_np
)
cpumask_set_cpu
(
cpu
,
cpumask
);
of_node_put
(
tmp_np
);
}
put_cpu_node:
of_node_put
(
np
);
return
ret
;
}
EXPORT_SYMBOL_GPL
(
dev_pm_opp_of_get_sharing_cpus
);
drivers/base/power/opp/opp.h
浏览文件 @
c8541203
...
...
@@ -28,6 +28,8 @@ struct regulator;
/* Lock to allow exclusive modification to the device and opp lists */
extern
struct
mutex
opp_table_lock
;
extern
struct
list_head
opp_tables
;
/*
* Internal data structure organization with the OPP layer library is as
* follows:
...
...
@@ -183,6 +185,18 @@ struct opp_table {
struct
opp_table
*
_find_opp_table
(
struct
device
*
dev
);
struct
opp_device
*
_add_opp_dev
(
const
struct
device
*
dev
,
struct
opp_table
*
opp_table
);
struct
device_node
*
_of_get_opp_desc_node
(
struct
device
*
dev
);
void
_dev_pm_opp_remove_table
(
struct
device
*
dev
,
bool
remove_all
);
struct
dev_pm_opp
*
_allocate_opp
(
struct
device
*
dev
,
struct
opp_table
**
opp_table
);
int
_opp_add
(
struct
device
*
dev
,
struct
dev_pm_opp
*
new_opp
,
struct
opp_table
*
opp_table
);
void
_opp_remove
(
struct
opp_table
*
opp_table
,
struct
dev_pm_opp
*
opp
,
bool
notify
);
int
_opp_add_v1
(
struct
device
*
dev
,
unsigned
long
freq
,
long
u_volt
,
bool
dynamic
);
void
_dev_pm_opp_cpumask_remove_table
(
const
struct
cpumask
*
cpumask
,
bool
of
);
#ifdef CONFIG_OF
void
_of_init_opp_table
(
struct
opp_table
*
opp_table
,
struct
device
*
dev
);
#else
static
inline
void
_of_init_opp_table
(
struct
opp_table
*
opp_table
,
struct
device
*
dev
)
{}
#endif
#ifdef CONFIG_DEBUG_FS
void
opp_debug_remove_one
(
struct
dev_pm_opp
*
opp
);
...
...
include/linux/pm_opp.h
浏览文件 @
c8541203
...
...
@@ -65,6 +65,10 @@ 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
);
int
dev_pm_opp_set_sharing_cpus
(
struct
device
*
cpu_dev
,
const
struct
cpumask
*
cpumask
);
int
dev_pm_opp_get_sharing_cpus
(
struct
device
*
cpu_dev
,
struct
cpumask
*
cpumask
);
void
dev_pm_opp_remove_table
(
struct
device
*
dev
);
void
dev_pm_opp_cpumask_remove_table
(
const
struct
cpumask
*
cpumask
);
#else
static
inline
unsigned
long
dev_pm_opp_get_voltage
(
struct
dev_pm_opp
*
opp
)
{
...
...
@@ -109,25 +113,25 @@ static inline struct dev_pm_opp *dev_pm_opp_get_suspend_opp(struct device *dev)
static
inline
struct
dev_pm_opp
*
dev_pm_opp_find_freq_exact
(
struct
device
*
dev
,
unsigned
long
freq
,
bool
available
)
{
return
ERR_PTR
(
-
E
INVAL
);
return
ERR_PTR
(
-
E
NOTSUPP
);
}
static
inline
struct
dev_pm_opp
*
dev_pm_opp_find_freq_floor
(
struct
device
*
dev
,
unsigned
long
*
freq
)
{
return
ERR_PTR
(
-
E
INVAL
);
return
ERR_PTR
(
-
E
NOTSUPP
);
}
static
inline
struct
dev_pm_opp
*
dev_pm_opp_find_freq_ceil
(
struct
device
*
dev
,
unsigned
long
*
freq
)
{
return
ERR_PTR
(
-
E
INVAL
);
return
ERR_PTR
(
-
E
NOTSUPP
);
}
static
inline
int
dev_pm_opp_add
(
struct
device
*
dev
,
unsigned
long
freq
,
unsigned
long
u_volt
)
{
return
-
E
INVAL
;
return
-
E
NOTSUPP
;
}
static
inline
void
dev_pm_opp_remove
(
struct
device
*
dev
,
unsigned
long
freq
)
...
...
@@ -147,73 +151,85 @@ static inline int dev_pm_opp_disable(struct device *dev, unsigned long freq)
static
inline
struct
srcu_notifier_head
*
dev_pm_opp_get_notifier
(
struct
device
*
dev
)
{
return
ERR_PTR
(
-
E
INVAL
);
return
ERR_PTR
(
-
E
NOTSUPP
);
}
static
inline
int
dev_pm_opp_set_supported_hw
(
struct
device
*
dev
,
const
u32
*
versions
,
unsigned
int
count
)
{
return
-
E
INVAL
;
return
-
E
NOTSUPP
;
}
static
inline
void
dev_pm_opp_put_supported_hw
(
struct
device
*
dev
)
{}
static
inline
int
dev_pm_opp_set_prop_name
(
struct
device
*
dev
,
const
char
*
name
)
{
return
-
E
INVAL
;
return
-
E
NOTSUPP
;
}
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
-
E
INVAL
;
return
-
E
NOTSUPP
;
}
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
-
ENOTSUPP
;
}
static
inline
int
dev_pm_opp_set_sharing_cpus
(
struct
device
*
cpu_dev
,
const
struct
cpumask
*
cpumask
)
{
return
-
ENOTSUPP
;
}
static
inline
int
dev_pm_opp_get_sharing_cpus
(
struct
device
*
cpu_dev
,
struct
cpumask
*
cpumask
)
{
return
-
EINVAL
;
}
static
inline
void
dev_pm_opp_remove_table
(
struct
device
*
dev
)
{
}
static
inline
void
dev_pm_opp_cpumask_remove_table
(
const
struct
cpumask
*
cpumask
)
{
}
#endif
/* CONFIG_PM_OPP */
#if defined(CONFIG_PM_OPP) && defined(CONFIG_OF)
int
dev_pm_opp_of_add_table
(
struct
device
*
dev
);
void
dev_pm_opp_of_remove_table
(
struct
device
*
dev
);
int
dev_pm_opp_of_cpumask_add_table
(
cpumask_var_t
cpumask
);
void
dev_pm_opp_of_cpumask_remove_table
(
cpumask_var_t
cpumask
);
int
dev_pm_opp_of_get_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
);
int
dev_pm_opp_of_cpumask_add_table
(
const
struct
cpumask
*
cpumask
);
void
dev_pm_opp_of_cpumask_remove_table
(
const
struct
cpumask
*
cpumask
);
int
dev_pm_opp_of_get_sharing_cpus
(
struct
device
*
cpu_dev
,
struct
cpumask
*
cpumask
);
#else
static
inline
int
dev_pm_opp_of_add_table
(
struct
device
*
dev
)
{
return
-
E
INVAL
;
return
-
E
NOTSUPP
;
}
static
inline
void
dev_pm_opp_of_remove_table
(
struct
device
*
dev
)
{
}
static
inline
int
dev_pm_opp_of_cpumask_add_table
(
cpumask_var_t
cpumask
)
{
return
-
ENOSYS
;
}
static
inline
void
dev_pm_opp_of_cpumask_remove_table
(
cpumask_var_t
cpumask
)
static
inline
int
dev_pm_opp_of_cpumask_add_table
(
const
struct
cpumask
*
cpumask
)
{
return
-
ENOTSUPP
;
}
static
inline
int
dev_pm_opp_of_get_sharing_cpus
(
struct
device
*
cpu_dev
,
cpumask_var_t
cpumask
)
static
inline
void
dev_pm_opp_of_cpumask_remove_table
(
const
struct
cpumask
*
cpumask
)
{
return
-
ENOSYS
;
}
static
inline
int
dev_pm_opp_
set_sharing_cpus
(
struct
device
*
cpu_dev
,
cpumask_var_t
cpumask
)
static
inline
int
dev_pm_opp_
of_get_sharing_cpus
(
struct
device
*
cpu_dev
,
struct
cpumask
*
cpumask
)
{
return
-
ENO
SYS
;
return
-
ENO
TSUPP
;
}
#endif
...
...
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录