Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
L
linux
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
Kirill Smelkov
linux
Commits
ade0c949
Commit
ade0c949
authored
Dec 14, 2018
by
Viresh Kumar
Browse files
Options
Browse Files
Download
Plain Diff
Merge branch 'opp/genpd/propagation' into opp/linux-next
parents
46f48aca
18edf49c
Changes
6
Show whitespace changes
Inline
Side-by-side
Showing
6 changed files
with
756 additions
and
216 deletions
+756
-216
drivers/base/power/domain.c
drivers/base/power/domain.c
+175
-97
drivers/opp/core.c
drivers/opp/core.c
+254
-70
drivers/opp/of.c
drivers/opp/of.c
+278
-41
drivers/opp/opp.h
drivers/opp/opp.h
+20
-0
include/linux/pm_domain.h
include/linux/pm_domain.h
+10
-4
include/linux/pm_opp.h
include/linux/pm_opp.h
+19
-4
No files found.
drivers/base/power/domain.c
View file @
ade0c949
...
...
@@ -239,6 +239,127 @@ static void genpd_update_accounting(struct generic_pm_domain *genpd)
static
inline
void
genpd_update_accounting
(
struct
generic_pm_domain
*
genpd
)
{}
#endif
static
int
_genpd_reeval_performance_state
(
struct
generic_pm_domain
*
genpd
,
unsigned
int
state
)
{
struct
generic_pm_domain_data
*
pd_data
;
struct
pm_domain_data
*
pdd
;
struct
gpd_link
*
link
;
/* New requested state is same as Max requested state */
if
(
state
==
genpd
->
performance_state
)
return
state
;
/* New requested state is higher than Max requested state */
if
(
state
>
genpd
->
performance_state
)
return
state
;
/* Traverse all devices within the domain */
list_for_each_entry
(
pdd
,
&
genpd
->
dev_list
,
list_node
)
{
pd_data
=
to_gpd_data
(
pdd
);
if
(
pd_data
->
performance_state
>
state
)
state
=
pd_data
->
performance_state
;
}
/*
* Traverse all sub-domains within the domain. This can be
* done without any additional locking as the link->performance_state
* field is protected by the master genpd->lock, which is already taken.
*
* Also note that link->performance_state (subdomain's performance state
* requirement to master domain) is different from
* link->slave->performance_state (current performance state requirement
* of the devices/sub-domains of the subdomain) and so can have a
* different value.
*
* Note that we also take vote from powered-off sub-domains into account
* as the same is done for devices right now.
*/
list_for_each_entry
(
link
,
&
genpd
->
master_links
,
master_node
)
{
if
(
link
->
performance_state
>
state
)
state
=
link
->
performance_state
;
}
return
state
;
}
static
int
_genpd_set_performance_state
(
struct
generic_pm_domain
*
genpd
,
unsigned
int
state
,
int
depth
)
{
struct
generic_pm_domain
*
master
;
struct
gpd_link
*
link
;
int
master_state
,
ret
;
if
(
state
==
genpd
->
performance_state
)
return
0
;
/* Propagate to masters of genpd */
list_for_each_entry
(
link
,
&
genpd
->
slave_links
,
slave_node
)
{
master
=
link
->
master
;
if
(
!
master
->
set_performance_state
)
continue
;
/* Find master's performance state */
ret
=
dev_pm_opp_xlate_performance_state
(
genpd
->
opp_table
,
master
->
opp_table
,
state
);
if
(
unlikely
(
ret
<
0
))
goto
err
;
master_state
=
ret
;
genpd_lock_nested
(
master
,
depth
+
1
);
link
->
prev_performance_state
=
link
->
performance_state
;
link
->
performance_state
=
master_state
;
master_state
=
_genpd_reeval_performance_state
(
master
,
master_state
);
ret
=
_genpd_set_performance_state
(
master
,
master_state
,
depth
+
1
);
if
(
ret
)
link
->
performance_state
=
link
->
prev_performance_state
;
genpd_unlock
(
master
);
if
(
ret
)
goto
err
;
}
ret
=
genpd
->
set_performance_state
(
genpd
,
state
);
if
(
ret
)
goto
err
;
genpd
->
performance_state
=
state
;
return
0
;
err:
/* Encountered an error, lets rollback */
list_for_each_entry_continue_reverse
(
link
,
&
genpd
->
slave_links
,
slave_node
)
{
master
=
link
->
master
;
if
(
!
master
->
set_performance_state
)
continue
;
genpd_lock_nested
(
master
,
depth
+
1
);
master_state
=
link
->
prev_performance_state
;
link
->
performance_state
=
master_state
;
master_state
=
_genpd_reeval_performance_state
(
master
,
master_state
);
if
(
_genpd_set_performance_state
(
master
,
master_state
,
depth
+
1
))
{
pr_err
(
"%s: Failed to roll back to %d performance state
\n
"
,
master
->
name
,
master_state
);
}
genpd_unlock
(
master
);
}
return
ret
;
}
/**
* dev_pm_genpd_set_performance_state- Set performance state of device's power
* domain.
...
...
@@ -257,10 +378,9 @@ static inline void genpd_update_accounting(struct generic_pm_domain *genpd) {}
int
dev_pm_genpd_set_performance_state
(
struct
device
*
dev
,
unsigned
int
state
)
{
struct
generic_pm_domain
*
genpd
;
struct
generic_pm_domain_data
*
gpd_data
,
*
pd_data
;
struct
pm_domain_data
*
pdd
;
struct
generic_pm_domain_data
*
gpd_data
;
unsigned
int
prev
;
int
ret
=
0
;
int
ret
;
genpd
=
dev_to_genpd
(
dev
);
if
(
IS_ERR
(
genpd
))
...
...
@@ -281,47 +401,11 @@ int dev_pm_genpd_set_performance_state(struct device *dev, unsigned int state)
prev
=
gpd_data
->
performance_state
;
gpd_data
->
performance_state
=
state
;
/* New requested state is same as Max requested state */
if
(
state
==
genpd
->
performance_state
)
goto
unlock
;
/* New requested state is higher than Max requested state */
if
(
state
>
genpd
->
performance_state
)
goto
update_state
;
/* Traverse all devices within the domain */
list_for_each_entry
(
pdd
,
&
genpd
->
dev_list
,
list_node
)
{
pd_data
=
to_gpd_data
(
pdd
);
if
(
pd_data
->
performance_state
>
state
)
state
=
pd_data
->
performance_state
;
}
if
(
state
==
genpd
->
performance_state
)
goto
unlock
;
/*
* We aren't propagating performance state changes of a subdomain to its
* masters as we don't have hardware that needs it. Over that, the
* performance states of subdomain and its masters may not have
* one-to-one mapping and would require additional information. We can
* get back to this once we have hardware that needs it. For that
* reason, we don't have to consider performance state of the subdomains
* of genpd here.
*/
update_state:
if
(
genpd_status_on
(
genpd
))
{
ret
=
genpd
->
set_performance_state
(
genpd
,
state
);
if
(
ret
)
{
state
=
_genpd_reeval_performance_state
(
genpd
,
state
);
ret
=
_genpd_set_performance_state
(
genpd
,
state
,
0
);
if
(
ret
)
gpd_data
->
performance_state
=
prev
;
goto
unlock
;
}
}
genpd
->
performance_state
=
state
;
unlock:
genpd_unlock
(
genpd
);
return
ret
;
...
...
@@ -347,15 +431,6 @@ static int _genpd_power_on(struct generic_pm_domain *genpd, bool timed)
return
ret
;
elapsed_ns
=
ktime_to_ns
(
ktime_sub
(
ktime_get
(),
time_start
));
if
(
unlikely
(
genpd
->
set_performance_state
))
{
ret
=
genpd
->
set_performance_state
(
genpd
,
genpd
->
performance_state
);
if
(
ret
)
{
pr_warn
(
"%s: Failed to set performance state %d (%d)
\n
"
,
genpd
->
name
,
genpd
->
performance_state
,
ret
);
}
}
if
(
elapsed_ns
<=
genpd
->
states
[
state_idx
].
power_on_latency_ns
)
return
ret
;
...
...
@@ -1907,12 +1982,21 @@ int of_genpd_add_provider_simple(struct device_node *np,
ret
);
goto
unlock
;
}
/*
* Save table for faster processing while setting performance
* state.
*/
genpd
->
opp_table
=
dev_pm_opp_get_opp_table
(
&
genpd
->
dev
);
WARN_ON
(
!
genpd
->
opp_table
);
}
ret
=
genpd_add_provider
(
np
,
genpd_xlate_simple
,
genpd
);
if
(
ret
)
{
if
(
genpd
->
set_performance_state
)
if
(
genpd
->
set_performance_state
)
{
dev_pm_opp_put_opp_table
(
genpd
->
opp_table
);
dev_pm_opp_of_remove_table
(
&
genpd
->
dev
);
}
goto
unlock
;
}
...
...
@@ -1965,6 +2049,13 @@ int of_genpd_add_provider_onecell(struct device_node *np,
i
,
ret
);
goto
error
;
}
/*
* Save table for faster processing while setting
* performance state.
*/
genpd
->
opp_table
=
dev_pm_opp_get_opp_table_indexed
(
&
genpd
->
dev
,
i
);
WARN_ON
(
!
genpd
->
opp_table
);
}
genpd
->
provider
=
&
np
->
fwnode
;
...
...
@@ -1989,9 +2080,11 @@ int of_genpd_add_provider_onecell(struct device_node *np,
genpd
->
provider
=
NULL
;
genpd
->
has_provider
=
false
;
if
(
genpd
->
set_performance_state
)
if
(
genpd
->
set_performance_state
)
{
dev_pm_opp_put_opp_table
(
genpd
->
opp_table
);
dev_pm_opp_of_remove_table
(
&
genpd
->
dev
);
}
}
mutex_unlock
(
&
gpd_list_lock
);
...
...
@@ -2024,6 +2117,7 @@ void of_genpd_del_provider(struct device_node *np)
if
(
!
gpd
->
set_performance_state
)
continue
;
dev_pm_opp_put_opp_table
(
gpd
->
opp_table
);
dev_pm_opp_of_remove_table
(
&
gpd
->
dev
);
}
}
...
...
@@ -2338,7 +2432,7 @@ EXPORT_SYMBOL_GPL(genpd_dev_pm_attach);
struct
device
*
genpd_dev_pm_attach_by_id
(
struct
device
*
dev
,
unsigned
int
index
)
{
struct
device
*
genpd
_dev
;
struct
device
*
virt
_dev
;
int
num_domains
;
int
ret
;
...
...
@@ -2352,31 +2446,31 @@ struct device *genpd_dev_pm_attach_by_id(struct device *dev,
return
NULL
;
/* Allocate and register device on the genpd bus. */
genpd_dev
=
kzalloc
(
sizeof
(
*
genpd
_dev
),
GFP_KERNEL
);
if
(
!
genpd
_dev
)
virt_dev
=
kzalloc
(
sizeof
(
*
virt
_dev
),
GFP_KERNEL
);
if
(
!
virt
_dev
)
return
ERR_PTR
(
-
ENOMEM
);
dev_set_name
(
genpd
_dev
,
"genpd:%u:%s"
,
index
,
dev_name
(
dev
));
genpd
_dev
->
bus
=
&
genpd_bus_type
;
genpd
_dev
->
release
=
genpd_release_dev
;
dev_set_name
(
virt
_dev
,
"genpd:%u:%s"
,
index
,
dev_name
(
dev
));
virt
_dev
->
bus
=
&
genpd_bus_type
;
virt
_dev
->
release
=
genpd_release_dev
;
ret
=
device_register
(
genpd
_dev
);
ret
=
device_register
(
virt
_dev
);
if
(
ret
)
{
kfree
(
genpd
_dev
);
kfree
(
virt
_dev
);
return
ERR_PTR
(
ret
);
}
/* Try to attach the device to the PM domain at the specified index. */
ret
=
__genpd_dev_pm_attach
(
genpd
_dev
,
dev
->
of_node
,
index
,
false
);
ret
=
__genpd_dev_pm_attach
(
virt
_dev
,
dev
->
of_node
,
index
,
false
);
if
(
ret
<
1
)
{
device_unregister
(
genpd
_dev
);
device_unregister
(
virt
_dev
);
return
ret
?
ERR_PTR
(
ret
)
:
NULL
;
}
pm_runtime_enable
(
genpd
_dev
);
genpd_queue_power_off_work
(
dev_to_genpd
(
genpd
_dev
));
pm_runtime_enable
(
virt
_dev
);
genpd_queue_power_off_work
(
dev_to_genpd
(
virt
_dev
));
return
genpd
_dev
;
return
virt
_dev
;
}
EXPORT_SYMBOL_GPL
(
genpd_dev_pm_attach_by_id
);
...
...
@@ -2521,52 +2615,36 @@ int of_genpd_parse_idle_states(struct device_node *dn,
EXPORT_SYMBOL_GPL
(
of_genpd_parse_idle_states
);
/**
* of_genpd_opp_to_performance_state- Gets performance state of device's
* power domain corresponding to a DT node's "required-opps" property.
* pm_genpd_opp_to_performance_state - Gets performance state of the genpd from its OPP node.
*
* @dev: Device for which the performance-state needs to be found.
* @np: DT node where the "required-opps" property is present. This can be
* the device node itself (if it doesn't have an OPP table) or a node
* within the OPP table of a device (if device has an OPP table).
* @genpd_dev: Genpd's device for which the performance-state needs to be found.
* @opp: struct dev_pm_opp of the OPP for which we need to find performance
* state.
*
* Returns performance state
corresponding to the "required-opps" property of
*
a DT node. This calls platform specific genpd->opp_to_performance_state()
*
callback to translate
power domain OPP to performance state.
* Returns performance state
encoded in the OPP of the genpd. This calls
*
platform specific genpd->opp_to_performance_state() callback to translate
* power domain OPP to performance state.
*
* Returns performance state on success and 0 on failure.
*/
unsigned
int
of_genpd_opp_to_performance_state
(
struct
device
*
dev
,
struct
dev
ice_node
*
n
p
)
unsigned
int
pm_genpd_opp_to_performance_state
(
struct
device
*
genpd_
dev
,
struct
dev
_pm_opp
*
op
p
)
{
struct
generic_pm_domain
*
genpd
;
struct
dev_pm_opp
*
opp
;
int
state
=
0
;
struct
generic_pm_domain
*
genpd
=
NULL
;
int
state
;
genpd
=
dev_to_genpd
(
dev
);
if
(
IS_ERR
(
genpd
))
return
0
;
genpd
=
container_of
(
genpd_dev
,
struct
generic_pm_domain
,
dev
);
if
(
unlikely
(
!
genpd
->
set
_performance_state
))
if
(
unlikely
(
!
genpd
->
opp_to
_performance_state
))
return
0
;
genpd_lock
(
genpd
);
opp
=
of_dev_pm_opp_find_required_opp
(
&
genpd
->
dev
,
np
);
if
(
IS_ERR
(
opp
))
{
dev_err
(
dev
,
"Failed to find required OPP: %ld
\n
"
,
PTR_ERR
(
opp
));
goto
unlock
;
}
state
=
genpd
->
opp_to_performance_state
(
genpd
,
opp
);
dev_pm_opp_put
(
opp
);
unlock:
genpd_unlock
(
genpd
);
return
state
;
}
EXPORT_SYMBOL_GPL
(
of
_genpd_opp_to_performance_state
);
EXPORT_SYMBOL_GPL
(
pm
_genpd_opp_to_performance_state
);
static
int
__init
genpd_bus_init
(
void
)
{
...
...
drivers/opp/core.c
View file @
ade0c949
...
...
@@ -548,44 +548,6 @@ _generic_set_opp_clk_only(struct device *dev, struct clk *clk,
return
ret
;
}
static
inline
int
_generic_set_opp_domain
(
struct
device
*
dev
,
struct
clk
*
clk
,
unsigned
long
old_freq
,
unsigned
long
freq
,
unsigned
int
old_pstate
,
unsigned
int
new_pstate
)
{
int
ret
;
/* Scaling up? Scale domain performance state before frequency */
if
(
freq
>
old_freq
)
{
ret
=
dev_pm_genpd_set_performance_state
(
dev
,
new_pstate
);
if
(
ret
)
return
ret
;
}
ret
=
_generic_set_opp_clk_only
(
dev
,
clk
,
old_freq
,
freq
);
if
(
ret
)
goto
restore_domain_state
;
/* Scaling down? Scale domain performance state after frequency */
if
(
freq
<
old_freq
)
{
ret
=
dev_pm_genpd_set_performance_state
(
dev
,
new_pstate
);
if
(
ret
)
goto
restore_freq
;
}
return
0
;
restore_freq:
if
(
_generic_set_opp_clk_only
(
dev
,
clk
,
freq
,
old_freq
))
dev_err
(
dev
,
"%s: failed to restore old-freq (%lu Hz)
\n
"
,
__func__
,
old_freq
);
restore_domain_state:
if
(
freq
>
old_freq
)
dev_pm_genpd_set_performance_state
(
dev
,
old_pstate
);
return
ret
;
}
static
int
_generic_set_opp_regulator
(
const
struct
opp_table
*
opp_table
,
struct
device
*
dev
,
unsigned
long
old_freq
,
...
...
@@ -635,6 +597,84 @@ static int _generic_set_opp_regulator(const struct opp_table *opp_table,
return
ret
;
}
static
int
_set_opp_custom
(
const
struct
opp_table
*
opp_table
,
struct
device
*
dev
,
unsigned
long
old_freq
,
unsigned
long
freq
,
struct
dev_pm_opp_supply
*
old_supply
,
struct
dev_pm_opp_supply
*
new_supply
)
{
struct
dev_pm_set_opp_data
*
data
;
int
size
;
data
=
opp_table
->
set_opp_data
;
data
->
regulators
=
opp_table
->
regulators
;
data
->
regulator_count
=
opp_table
->
regulator_count
;
data
->
clk
=
opp_table
->
clk
;
data
->
dev
=
dev
;
data
->
old_opp
.
rate
=
old_freq
;
size
=
sizeof
(
*
old_supply
)
*
opp_table
->
regulator_count
;
if
(
IS_ERR
(
old_supply
))
memset
(
data
->
old_opp
.
supplies
,
0
,
size
);
else
memcpy
(
data
->
old_opp
.
supplies
,
old_supply
,
size
);
data
->
new_opp
.
rate
=
freq
;
memcpy
(
data
->
new_opp
.
supplies
,
new_supply
,
size
);
return
opp_table
->
set_opp
(
data
);
}
/* This is only called for PM domain for now */
static
int
_set_required_opps
(
struct
device
*
dev
,
struct
opp_table
*
opp_table
,
struct
dev_pm_opp
*
opp
)
{
struct
opp_table
**
required_opp_tables
=
opp_table
->
required_opp_tables
;
struct
device
**
genpd_virt_devs
=
opp_table
->
genpd_virt_devs
;
unsigned
int
pstate
;
int
i
,
ret
=
0
;
if
(
!
required_opp_tables
)
return
0
;
/* Single genpd case */
if
(
!
genpd_virt_devs
)
{
pstate
=
opp
->
required_opps
[
0
]
->
pstate
;
ret
=
dev_pm_genpd_set_performance_state
(
dev
,
pstate
);
if
(
ret
)
{
dev_err
(
dev
,
"Failed to set performance state of %s: %d (%d)
\n
"
,
dev_name
(
dev
),
pstate
,
ret
);
}
return
ret
;
}
/* Multiple genpd case */
/*
* Acquire genpd_virt_dev_lock to make sure we don't use a genpd_dev
* after it is freed from another thread.
*/
mutex_lock
(
&
opp_table
->
genpd_virt_dev_lock
);
for
(
i
=
0
;
i
<
opp_table
->
required_opp_count
;
i
++
)
{
pstate
=
opp
->
required_opps
[
i
]
->
pstate
;
if
(
!
genpd_virt_devs
[
i
])
continue
;
ret
=
dev_pm_genpd_set_performance_state
(
genpd_virt_devs
[
i
],
pstate
);
if
(
ret
)
{
dev_err
(
dev
,
"Failed to set performance rate of %s: %d (%d)
\n
"
,
dev_name
(
genpd_virt_devs
[
i
]),
pstate
,
ret
);
break
;
}
}
mutex_unlock
(
&
opp_table
->
genpd_virt_dev_lock
);
return
ret
;
}
/**
* dev_pm_opp_set_rate() - Configure new OPP based on frequency
* @dev: device for which we do this operation
...
...
@@ -649,7 +689,7 @@ int dev_pm_opp_set_rate(struct device *dev, unsigned long target_freq)
unsigned
long
freq
,
old_freq
;
struct
dev_pm_opp
*
old_opp
,
*
opp
;
struct
clk
*
clk
;
int
ret
,
size
;
int
ret
;
if
(
unlikely
(
!
target_freq
))
{
dev_err
(
dev
,
"%s: Invalid target frequency %lu
\n
"
,
__func__
,
...
...
@@ -702,44 +742,34 @@ int dev_pm_opp_set_rate(struct device *dev, unsigned long target_freq)
dev_dbg
(
dev
,
"%s: switching OPP: %lu Hz --> %lu Hz
\n
"
,
__func__
,
old_freq
,
freq
);
/* Only frequency scaling */
if
(
!
opp_table
->
regulators
)
{
/*
* We don't support devices with both regulator and
* domain performance-state for now.
*/
if
(
opp_table
->
genpd_performance_state
)
ret
=
_generic_set_opp_domain
(
dev
,
clk
,
old_freq
,
freq
,
IS_ERR
(
old_opp
)
?
0
:
old_opp
->
pstate
,
opp
->
pstate
);
else
ret
=
_generic_set_opp_clk_only
(
dev
,
clk
,
old_freq
,
freq
);
}
else
if
(
!
opp_table
->
set_opp
)
{
/* Scaling up? Configure required OPPs before frequency */
if
(
freq
>
old_freq
)
{
ret
=
_set_required_opps
(
dev
,
opp_table
,
opp
);
if
(
ret
)
goto
put_opp
;
}
if
(
opp_table
->
set_opp
)
{
ret
=
_set_opp_custom
(
opp_table
,
dev
,
old_freq
,
freq
,
IS_ERR
(
old_opp
)
?
NULL
:
old_opp
->
supplies
,
opp
->
supplies
);
}
else
if
(
opp_table
->
regulators
)
{
ret
=
_generic_set_opp_regulator
(
opp_table
,
dev
,
old_freq
,
freq
,
IS_ERR
(
old_opp
)
?
NULL
:
old_opp
->
supplies
,
opp
->
supplies
);
}
else
{
struct
dev_pm_set_opp_data
*
data
;
data
=
opp_table
->
set_opp_data
;
data
->
regulators
=
opp_table
->
regulators
;
data
->
regulator_count
=
opp_table
->
regulator_count
;
data
->
clk
=
clk
;
data
->
dev
=
dev
;
data
->
old_opp
.
rate
=
old_freq
;
size
=
sizeof
(
*
opp
->
supplies
)
*
opp_table
->
regulator_count
;
if
(
IS_ERR
(
old_opp
))
memset
(
data
->
old_opp
.
supplies
,
0
,
size
);
else
memcpy
(
data
->
old_opp
.
supplies
,
old_opp
->
supplies
,
size
);
data
->
new_opp
.
rate
=
freq
;
memcpy
(
data
->
new_opp
.
supplies
,
opp
->
supplies
,
size
);
/* Only frequency scaling */
ret
=
_generic_set_opp_clk_only
(
dev
,
clk
,
old_freq
,
freq
);
}
ret
=
opp_table
->
set_opp
(
data
);
/* Scaling down? Configure required OPPs after frequency */
if
(
!
ret
&&
freq
<
old_freq
)
{
ret
=
_set_required_opps
(
dev
,
opp_table
,
opp
);
if
(
ret
)
dev_err
(
dev
,
"Failed to set required opps: %d
\n
"
,
ret
);
}
put_opp:
dev_pm_opp_put
(
opp
);
put_old_opp:
if
(
!
IS_ERR
(
old_opp
))
...
...
@@ -810,6 +840,7 @@ static struct opp_table *_allocate_opp_table(struct device *dev, int index)
return
NULL
;
mutex_init
(
&
opp_table
->
lock
);
mutex_init
(
&
opp_table
->
genpd_virt_dev_lock
);
INIT_LIST_HEAD
(
&
opp_table
->
dev_list
);
/* Mark regulator count uninitialized */
...
...
@@ -891,6 +922,8 @@ static void _opp_table_kref_release(struct kref *kref)
struct
opp_table
*
opp_table
=
container_of
(
kref
,
struct
opp_table
,
kref
);
struct
opp_device
*
opp_dev
,
*
temp
;
_of_clear_opp_table
(
opp_table
);
/* Release clk */
if
(
!
IS_ERR
(
opp_table
->
clk
))
clk_put
(
opp_table
->
clk
);
...
...
@@ -908,6 +941,7 @@ static void _opp_table_kref_release(struct kref *kref)
_remove_opp_dev
(
opp_dev
,
opp_table
);
}
mutex_destroy
(
&
opp_table
->
genpd_virt_dev_lock
);
mutex_destroy
(
&
opp_table
->
lock
);
list_del
(
&
opp_table
->
node
);
kfree
(
opp_table
);
...
...
@@ -964,6 +998,7 @@ static void _opp_kref_release(struct kref *kref)
* frequency/voltage list.
*/
blocking_notifier_call_chain
(
&
opp_table
->
head
,
OPP_EVENT_REMOVE
,
opp
);
_of_opp_free_required_opps
(
opp_table
,
opp
);
opp_debug_remove_one
(
opp
);
list_del
(
&
opp
->
node
);
kfree
(
opp
);
...
...
@@ -1592,6 +1627,155 @@ void dev_pm_opp_unregister_set_opp_helper(struct opp_table *opp_table)
}
EXPORT_SYMBOL_GPL
(
dev_pm_opp_unregister_set_opp_helper
);
/**
* dev_pm_opp_set_genpd_virt_dev - Set virtual genpd device for an index
* @dev: Consumer device for which the genpd device is getting set.
* @virt_dev: virtual genpd device.
* @index: index.
*
* Multiple generic power domains for a device are supported with the help of
* virtual genpd devices, which are created for each consumer device - genpd
* pair. These are the device structures which are attached to the power domain
* and are required by the OPP core to set the performance state of the genpd.
*
* This helper will normally be called by the consumer driver of the device
* "dev", as only that has details of the genpd devices.
*
* This helper needs to be called once for each of those virtual devices, but
* only if multiple domains are available for a device. Otherwise the original
* device structure will be used instead by the OPP core.
*/
struct
opp_table
*
dev_pm_opp_set_genpd_virt_dev
(
struct
device
*
dev
,
struct
device
*
virt_dev
,
int
index
)
{
struct
opp_table
*
opp_table
;
opp_table
=
dev_pm_opp_get_opp_table
(
dev
);
if
(
!
opp_table
)
return
ERR_PTR
(
-
ENOMEM
);
mutex_lock
(
&
opp_table
->
genpd_virt_dev_lock
);
if
(
unlikely
(
!
opp_table
->
genpd_virt_devs
||
index
>=
opp_table
->
required_opp_count
||
opp_table
->
genpd_virt_devs
[
index
]))
{
dev_err
(
dev
,
"Invalid request to set required device
\n
"
);
dev_pm_opp_put_opp_table
(
opp_table
);
mutex_unlock
(
&
opp_table
->
genpd_virt_dev_lock
);
return
ERR_PTR
(
-
EINVAL
);
}
opp_table
->
genpd_virt_devs
[
index
]
=
virt_dev
;
mutex_unlock
(
&
opp_table
->
genpd_virt_dev_lock
);
return
opp_table
;
}
/**
* dev_pm_opp_put_genpd_virt_dev() - Releases resources blocked for genpd device.
* @opp_table: OPP table returned by dev_pm_opp_set_genpd_virt_dev().
* @virt_dev: virtual genpd device.
*
* This releases the resource previously acquired with a call to
* dev_pm_opp_set_genpd_virt_dev(). The consumer driver shall call this helper
* if it doesn't want OPP core to update performance state of a power domain
* anymore.
*/
void
dev_pm_opp_put_genpd_virt_dev
(
struct
opp_table
*
opp_table
,
struct
device
*
virt_dev
)
{
int
i
;
/*
* Acquire genpd_virt_dev_lock to make sure virt_dev isn't getting
* used in parallel.
*/
mutex_lock
(
&
opp_table
->
genpd_virt_dev_lock
);
for
(
i
=
0
;
i
<
opp_table
->
required_opp_count
;
i
++
)
{
if
(
opp_table
->
genpd_virt_devs
[
i
]
!=
virt_dev
)
continue
;
opp_table
->
genpd_virt_devs
[
i
]
=
NULL
;
dev_pm_opp_put_opp_table
(
opp_table
);
/* Drop the vote */
dev_pm_genpd_set_performance_state
(
virt_dev
,
0
);
break
;
}
mutex_unlock
(
&
opp_table
->
genpd_virt_dev_lock
);
if
(
unlikely
(
i
==
opp_table
->
required_opp_count
))
dev_err
(
virt_dev
,
"Failed to find required device entry
\n
"
);
}
/**
* dev_pm_opp_xlate_performance_state() - Find required OPP's pstate for src_table.
* @src_table: OPP table which has dst_table as one of its required OPP table.
* @dst_table: Required OPP table of the src_table.
* @pstate: Current performance state of the src_table.
*
* This Returns pstate of the OPP (present in @dst_table) pointed out by the
* "required-opps" property of the OPP (present in @src_table) which has
* performance state set to @pstate.
*
* Return: Zero or positive performance state on success, otherwise negative
* value on errors.
*/
int
dev_pm_opp_xlate_performance_state
(
struct
opp_table
*
src_table
,
struct
opp_table
*
dst_table
,
unsigned
int
pstate
)
{
struct
dev_pm_opp
*
opp
;
int
dest_pstate
=
-
EINVAL
;
int
i
;
if
(
!
pstate
)
return
0
;
/*
* Normally the src_table will have the "required_opps" property set to
* point to one of the OPPs in the dst_table, but in some cases the
* genpd and its master have one to one mapping of performance states
* and so none of them have the "required-opps" property set. Return the
* pstate of the src_table as it is in such cases.
*/
if
(
!
src_table
->
required_opp_count
)
return
pstate
;
for
(
i
=
0
;
i
<
src_table
->
required_opp_count
;
i
++
)
{
if
(
src_table
->
required_opp_tables
[
i
]
->
np
==
dst_table
->
np
)
break
;
}
if
(
unlikely
(
i
==
src_table
->
required_opp_count
))
{
pr_err
(
"%s: Couldn't find matching OPP table (%p: %p)
\n
"
,
__func__
,
src_table
,
dst_table
);
return
-
EINVAL
;
}
mutex_lock
(
&
src_table
->
lock
);
list_for_each_entry
(
opp
,
&
src_table
->
opp_list
,
node
)
{
if
(
opp
->
pstate
==
pstate
)
{
dest_pstate
=
opp
->
required_opps
[
i
]
->
pstate
;
goto
unlock
;
}
}
pr_err
(
"%s: Couldn't find matching OPP (%p: %p)
\n
"
,
__func__
,
src_table
,
dst_table
);
unlock:
mutex_unlock
(
&
src_table
->
lock
);
return
dest_pstate
;
}
/**
* dev_pm_opp_add() - Add an OPP table from a table definitions
* @dev: device for which we do this operation
...
...
drivers/opp/of.c
View file @
ade0c949
...
...
@@ -73,6 +73,167 @@ struct opp_table *_managed_opp(struct device *dev, int index)
return
managed_table
;
}
/* The caller must call dev_pm_opp_put() after the OPP is used */
static
struct
dev_pm_opp
*
_find_opp_of_np
(
struct
opp_table
*
opp_table
,
struct
device_node
*
opp_np
)
{
struct
dev_pm_opp
*
opp
;
lockdep_assert_held
(
&
opp_table_lock
);
mutex_lock
(
&
opp_table
->
lock
);
list_for_each_entry
(
opp
,
&
opp_table
->
opp_list
,
node
)
{
if
(
opp
->
np
==
opp_np
)
{
dev_pm_opp_get
(
opp
);
mutex_unlock
(
&
opp_table
->
lock
);
return
opp
;
}
}
mutex_unlock
(
&
opp_table
->
lock
);
return
NULL
;
}
static
struct
device_node
*
of_parse_required_opp
(
struct
device_node
*
np
,
int
index
)
{
struct
device_node
*
required_np
;
required_np
=
of_parse_phandle
(
np
,
"required-opps"
,
index
);
if
(
unlikely
(
!
required_np
))
{
pr_err
(
"%s: Unable to parse required-opps: %pOF, index: %d
\n
"
,
__func__
,
np
,
index
);
}
return
required_np
;
}
/* The caller must call dev_pm_opp_put_opp_table() after the table is used */
static
struct
opp_table
*
_find_table_of_opp_np
(
struct
device_node
*
opp_np
)
{
struct
opp_table
*
opp_table
;
struct
device_node
*
opp_table_np
;
lockdep_assert_held
(
&
opp_table_lock
);
opp_table_np
=
of_get_parent
(
opp_np
);
if
(
!
opp_table_np
)
goto
err
;
/* It is safe to put the node now as all we need now is its address */
of_node_put
(
opp_table_np
);
list_for_each_entry
(
opp_table
,
&
opp_tables
,
node
)
{
if
(
opp_table_np
==
opp_table
->
np
)
{
_get_opp_table_kref
(
opp_table
);
return
opp_table
;
}
}
err:
return
ERR_PTR
(
-
ENODEV
);
}
/* Free resources previously acquired by _opp_table_alloc_required_tables() */
static
void
_opp_table_free_required_tables
(
struct
opp_table
*
opp_table
)
{
struct
opp_table
**
required_opp_tables
=
opp_table
->
required_opp_tables
;
struct
device
**
genpd_virt_devs
=
opp_table
->
genpd_virt_devs
;
int
i
;
if
(
!
required_opp_tables
)
return
;
for
(
i
=
0
;
i
<
opp_table
->
required_opp_count
;
i
++
)
{
if
(
IS_ERR_OR_NULL
(
required_opp_tables
[
i
]))
break
;
dev_pm_opp_put_opp_table
(
required_opp_tables
[
i
]);
}
kfree
(
required_opp_tables
);
kfree
(
genpd_virt_devs
);
opp_table
->
required_opp_count
=
0
;
opp_table
->
genpd_virt_devs
=
NULL
;
opp_table
->
required_opp_tables
=
NULL
;
}
/*
* Populate all devices and opp tables which are part of "required-opps" list.
* Checking only the first OPP node should be enough.
*/
static
void
_opp_table_alloc_required_tables
(
struct
opp_table
*
opp_table
,
struct
device
*
dev
,
struct
device_node
*
opp_np
)
{
struct
opp_table
**
required_opp_tables
;
struct
device
**
genpd_virt_devs
=
NULL
;
struct
device_node
*
required_np
,
*
np
;
int
count
,
i
;
/* Traversing the first OPP node is all we need */
np
=
of_get_next_available_child
(
opp_np
,
NULL
);
if
(
!
np
)
{
dev_err
(
dev
,
"Empty OPP table
\n
"
);
return
;
}
count
=
of_count_phandle_with_args
(
np
,
"required-opps"
,
NULL
);
if
(
!
count
)
goto
put_np
;
if
(
count
>
1
)
{
genpd_virt_devs
=
kcalloc
(
count
,
sizeof
(
*
genpd_virt_devs
),
GFP_KERNEL
);
if
(
!
genpd_virt_devs
)
goto
put_np
;
}
required_opp_tables
=
kcalloc
(
count
,
sizeof
(
*
required_opp_tables
),
GFP_KERNEL
);
if
(
!
required_opp_tables
)
{
kfree
(
genpd_virt_devs
);
goto
put_np
;
}
opp_table
->
genpd_virt_devs
=
genpd_virt_devs
;
opp_table
->
required_opp_tables
=
required_opp_tables
;
opp_table
->
required_opp_count
=
count
;
for
(
i
=
0
;
i
<
count
;
i
++
)
{
required_np
=
of_parse_required_opp
(
np
,
i
);
if
(
!
required_np
)
goto
free_required_tables
;
required_opp_tables
[
i
]
=
_find_table_of_opp_np
(
required_np
);
of_node_put
(
required_np
);
if
(
IS_ERR
(
required_opp_tables
[
i
]))
goto
free_required_tables
;
/*
* We only support genpd's OPPs in the "required-opps" for now,
* as we don't know how much about other cases. Error out if the
* required OPP doesn't belong to a genpd.
*/
if
(
!
required_opp_tables
[
i
]
->
is_genpd
)
{
dev_err
(
dev
,
"required-opp doesn't belong to genpd: %pOF
\n
"
,
required_np
);
goto
free_required_tables
;
}
}
goto
put_np
;
free_required_tables:
_opp_table_free_required_tables
(
opp_table
);
put_np:
of_node_put
(
np
);
}
void
_of_init_opp_table
(
struct
opp_table
*
opp_table
,
struct
device
*
dev
,
int
index
)
{
...
...
@@ -92,6 +253,9 @@ void _of_init_opp_table(struct opp_table *opp_table, struct device *dev,
of_property_read_u32
(
np
,
"voltage-tolerance"
,
&
opp_table
->
voltage_tolerance_v1
);
if
(
of_find_property
(
np
,
"#power-domain-cells"
,
NULL
))
opp_table
->
is_genpd
=
true
;
/* Get OPP table node */
opp_np
=
_opp_of_get_opp_desc_node
(
np
,
index
);
of_node_put
(
np
);
...
...
@@ -106,9 +270,86 @@ void _of_init_opp_table(struct opp_table *opp_table, struct device *dev,
opp_table
->
np
=
opp_np
;
_opp_table_alloc_required_tables
(
opp_table
,
dev
,
opp_np
);
of_node_put
(
opp_np
);
}
void
_of_clear_opp_table
(
struct
opp_table
*
opp_table
)
{
_opp_table_free_required_tables
(
opp_table
);
}
/*
* Release all resources previously acquired with a call to
* _of_opp_alloc_required_opps().
*/
void
_of_opp_free_required_opps
(
struct
opp_table
*
opp_table
,
struct
dev_pm_opp
*
opp
)
{
struct
dev_pm_opp
**
required_opps
=
opp
->
required_opps
;
int
i
;
if
(
!
required_opps
)
return
;
for
(
i
=
0
;
i
<
opp_table
->
required_opp_count
;
i
++
)
{
if
(
!
required_opps
[
i
])
break
;
/* Put the reference back */
dev_pm_opp_put
(
required_opps
[
i
]);
}
kfree
(
required_opps
);
opp
->
required_opps
=
NULL
;
}
/* Populate all required OPPs which are part of "required-opps" list */
static
int
_of_opp_alloc_required_opps
(
struct
opp_table
*
opp_table
,
struct
dev_pm_opp
*
opp
)
{
struct
dev_pm_opp
**
required_opps
;
struct
opp_table
*
required_table
;
struct
device_node
*
np
;
int
i
,
ret
,
count
=
opp_table
->
required_opp_count
;
if
(
!
count
)
return
0
;
required_opps
=
kcalloc
(
count
,
sizeof
(
*
required_opps
),
GFP_KERNEL
);
if
(
!
required_opps
)
return
-
ENOMEM
;
opp
->
required_opps
=
required_opps
;
for
(
i
=
0
;
i
<
count
;
i
++
)
{
required_table
=
opp_table
->
required_opp_tables
[
i
];
np
=
of_parse_required_opp
(
opp
->
np
,
i
);
if
(
unlikely
(
!
np
))
{
ret
=
-
ENODEV
;
goto
free_required_opps
;
}
required_opps
[
i
]
=
_find_opp_of_np
(
required_table
,
np
);
of_node_put
(
np
);
if
(
!
required_opps
[
i
])
{
pr_err
(
"%s: Unable to find required OPP node: %pOF (%d)
\n
"
,
__func__
,
opp
->
np
,
i
);
ret
=
-
ENODEV
;
goto
free_required_opps
;
}
}
return
0
;
free_required_opps:
_of_opp_free_required_opps
(
opp_table
,
opp
);
return
ret
;
}
static
bool
_opp_is_supported
(
struct
device
*
dev
,
struct
opp_table
*
opp_table
,
struct
device_node
*
np
)
{
...
...
@@ -338,8 +579,7 @@ static struct dev_pm_opp *_opp_add_static_v2(struct opp_table *opp_table,
ret
=
of_property_read_u64
(
np
,
"opp-hz"
,
&
rate
);
if
(
ret
<
0
)
{
/* "opp-hz" is optional for devices like power domains. */
if
(
!
of_find_property
(
dev
->
of_node
,
"#power-domain-cells"
,
NULL
))
{
if
(
!
opp_table
->
is_genpd
)
{
dev_err
(
dev
,
"%s: opp-hz not found
\n
"
,
__func__
);
goto
free_opp
;
}
...
...
@@ -366,21 +606,26 @@ static struct dev_pm_opp *_opp_add_static_v2(struct opp_table *opp_table,
new_opp
->
dynamic
=
false
;
new_opp
->
available
=
true
;
ret
=
_of_opp_alloc_required_opps
(
opp_table
,
new_opp
);
if
(
ret
)
goto
free_opp
;
if
(
!
of_property_read_u32
(
np
,
"clock-latency-ns"
,
&
val
))
new_opp
->
clock_latency_ns
=
val
;
new_opp
->
pstate
=
of_genpd_opp_to_performance_state
(
dev
,
np
);
ret
=
opp_parse_supplies
(
new_opp
,
dev
,
opp_table
);
if
(
ret
)
goto
free_opp
;
goto
free_required_opps
;
if
(
opp_table
->
is_genpd
)
new_opp
->
pstate
=
pm_genpd_opp_to_performance_state
(
dev
,
new_opp
);
ret
=
_opp_add
(
dev
,
new_opp
,
opp_table
,
rate_not_available
);
if
(
ret
)
{
/* Don't return error for duplicate OPPs */
if
(
ret
==
-
EBUSY
)
ret
=
0
;
goto
free_
opp
;
goto
free_
required_opps
;
}
/* OPP to select on device suspend */
...
...
@@ -410,6 +655,8 @@ static struct dev_pm_opp *_opp_add_static_v2(struct opp_table *opp_table,
blocking_notifier_call_chain
(
&
opp_table
->
head
,
OPP_EVENT_ADD
,
new_opp
);
return
new_opp
;
free_required_opps:
_of_opp_free_required_opps
(
opp_table
,
new_opp
);
free_opp:
_opp_free
(
new_opp
);
...
...
@@ -739,58 +986,48 @@ int dev_pm_opp_of_get_sharing_cpus(struct device *cpu_dev,
EXPORT_SYMBOL_GPL
(
dev_pm_opp_of_get_sharing_cpus
);
/**
* of_dev_pm_opp_find_required_opp() - Search for required OPP.
* @dev: The device whose OPP node is referenced by the 'np' DT node.
* of_get_required_opp_performance_state() - Search for required OPP and return its performance state.
* @np: Node that contains the "required-opps" property.
* @index: Index of the phandle to parse.
*
* Returns the OPP of the device 'dev', whose phandle is present in the "np"
* node. Although the "required-opps" property supports having multiple
* phandles, this helper routine only parses the very first phandle in the list.
*
* Return: Matching opp, else returns ERR_PTR in case of error and should be
* handled using IS_ERR.
* Returns the performance state of the OPP pointed out by the "required-opps"
* property at @index in @np.
*
*
The callers are required to call dev_pm_opp_put() for the returned OPP after
*
use
.
*
Return: Zero or positive performance state on success, otherwise negative
*
value on errors
.
*/
struct
dev_pm_opp
*
of_dev_pm_opp_find_required_opp
(
struct
device
*
dev
,
struct
device_node
*
np
)
int
of_get_required_opp_performance_state
(
struct
device_node
*
np
,
int
index
)
{
struct
dev_pm_opp
*
temp_opp
,
*
opp
=
ERR_PTR
(
-
ENODEV
)
;
struct
dev_pm_opp
*
opp
;
struct
device_node
*
required_np
;
struct
opp_table
*
opp_table
;
int
pstate
=
-
EINVAL
;
opp_table
=
_find_opp_table
(
dev
);
if
(
IS_ERR
(
opp_table
)
)
return
ERR_CAST
(
opp_table
)
;
required_np
=
of_parse_required_opp
(
np
,
index
);
if
(
!
required_np
)
return
-
EINVAL
;
required_np
=
of_parse_phandle
(
np
,
"required-opps"
,
0
);
if
(
unlikely
(
!
required_np
))
{
dev_err
(
dev
,
"Unable to parse required-opps
\n
"
);
goto
put_opp_table
;
opp_table
=
_find_table_of_opp_np
(
required_np
);
if
(
IS_ERR
(
opp_table
))
{
pr_err
(
"%s: Failed to find required OPP table %pOF: %ld
\n
"
,
__func__
,
np
,
PTR_ERR
(
opp_table
));
goto
put_required_np
;
}
mutex_lock
(
&
opp_table
->
lock
);
list_for_each_entry
(
temp_opp
,
&
opp_table
->
opp_list
,
node
)
{
if
(
temp_opp
->
available
&&
temp_opp
->
np
==
required_np
)
{
opp
=
temp_opp
;
/* Increment the reference count of OPP */
dev_pm_opp_get
(
opp
);
break
;
}
opp
=
_find_opp_of_np
(
opp_table
,
required_np
);
if
(
opp
)
{
pstate
=
opp
->
pstate
;
dev_pm_opp_put
(
opp
);
}
mutex_unlock
(
&
opp_table
->
lock
);
dev_pm_opp_put_opp_table
(
opp_table
);
put_required_np:
of_node_put
(
required_np
);
put_opp_table:
dev_pm_opp_put_opp_table
(
opp_table
);
return
opp
;
return
pstate
;
}
EXPORT_SYMBOL_GPL
(
of_
dev_pm_opp_find_required_opp
);
EXPORT_SYMBOL_GPL
(
of_
get_required_opp_performance_state
);
/**
* dev_pm_opp_get_of_node() - Gets the DT node corresponding to an opp
...
...
drivers/opp/opp.h
View file @
ade0c949
...
...
@@ -63,6 +63,7 @@ extern struct list_head opp_tables;
* @supplies: Power supplies voltage/current values
* @clock_latency_ns: Latency (in nanoseconds) of switching to this OPP's
* frequency from any other OPP's frequency.
* @required_opps: List of OPPs that are required by this OPP.
* @opp_table: points back to the opp_table struct this opp belongs to
* @np: OPP's device node.
* @dentry: debugfs dentry pointer (per opp)
...
...
@@ -84,6 +85,7 @@ struct dev_pm_opp {
unsigned
long
clock_latency_ns
;
struct
dev_pm_opp
**
required_opps
;
struct
opp_table
*
opp_table
;
struct
device_node
*
np
;
...
...
@@ -133,6 +135,11 @@ enum opp_table_access {
* @parsed_static_opps: True if OPPs are initialized from DT.
* @shared_opp: OPP is shared between multiple devices.
* @suspend_opp: Pointer to OPP to be used during device suspend.
* @genpd_virt_dev_lock: Mutex protecting the genpd virtual device pointers.
* @genpd_virt_devs: List of virtual devices for multiple genpd support.
* @required_opp_tables: List of device OPP tables that are required by OPPs in
* this table.
* @required_opp_count: Number of required devices.
* @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.
...
...
@@ -142,6 +149,7 @@ enum opp_table_access {
* (uninitialized), 0 (no opp-microvolt property) or > 0 (has opp-microvolt
* property).
* @genpd_performance_state: Device's power domain support performance state.
* @is_genpd: Marks if the OPP table belongs to a genpd.
* @set_opp: Platform specific set_opp callback
* @set_opp_data: Data to be passed to set_opp callback
* @dentry: debugfs dentry pointer of the real device directory (not links).
...
...
@@ -173,6 +181,11 @@ struct opp_table {
enum
opp_table_access
shared_opp
;
struct
dev_pm_opp
*
suspend_opp
;
struct
mutex
genpd_virt_dev_lock
;
struct
device
**
genpd_virt_devs
;
struct
opp_table
**
required_opp_tables
;
unsigned
int
required_opp_count
;
unsigned
int
*
supported_hw
;
unsigned
int
supported_hw_count
;
const
char
*
prop_name
;
...
...
@@ -180,6 +193,7 @@ struct opp_table {
struct
regulator
**
regulators
;
int
regulator_count
;
bool
genpd_performance_state
;
bool
is_genpd
;
int
(
*
set_opp
)(
struct
dev_pm_set_opp_data
*
data
);
struct
dev_pm_set_opp_data
*
set_opp_data
;
...
...
@@ -208,10 +222,16 @@ void _put_opp_list_kref(struct opp_table *opp_table);
#ifdef CONFIG_OF
void
_of_init_opp_table
(
struct
opp_table
*
opp_table
,
struct
device
*
dev
,
int
index
);
void
_of_clear_opp_table
(
struct
opp_table
*
opp_table
);
struct
opp_table
*
_managed_opp
(
struct
device
*
dev
,
int
index
);
void
_of_opp_free_required_opps
(
struct
opp_table
*
opp_table
,
struct
dev_pm_opp
*
opp
);
#else
static
inline
void
_of_init_opp_table
(
struct
opp_table
*
opp_table
,
struct
device
*
dev
,
int
index
)
{}
static
inline
void
_of_clear_opp_table
(
struct
opp_table
*
opp_table
)
{}
static
inline
struct
opp_table
*
_managed_opp
(
struct
device
*
dev
,
int
index
)
{
return
NULL
;
}
static
inline
void
_of_opp_free_required_opps
(
struct
opp_table
*
opp_table
,
struct
dev_pm_opp
*
opp
)
{}
#endif
#ifdef CONFIG_DEBUG_FS
...
...
include/linux/pm_domain.h
View file @
ade0c949
...
...
@@ -73,6 +73,7 @@ struct genpd_power_state {
struct
genpd_lock_ops
;
struct
dev_pm_opp
;
struct
opp_table
;
struct
generic_pm_domain
{
struct
device
dev
;
...
...
@@ -94,6 +95,7 @@ struct generic_pm_domain {
unsigned
int
performance_state
;
/* Aggregated max performance state */
int
(
*
power_off
)(
struct
generic_pm_domain
*
domain
);
int
(
*
power_on
)(
struct
generic_pm_domain
*
domain
);
struct
opp_table
*
opp_table
;
/* OPP table of the genpd */
unsigned
int
(
*
opp_to_performance_state
)(
struct
generic_pm_domain
*
genpd
,
struct
dev_pm_opp
*
opp
);
int
(
*
set_performance_state
)(
struct
generic_pm_domain
*
genpd
,
...
...
@@ -134,6 +136,10 @@ struct gpd_link {
struct
list_head
master_node
;
struct
generic_pm_domain
*
slave
;
struct
list_head
slave_node
;
/* Sub-domain's per-master domain performance state */
unsigned
int
performance_state
;
unsigned
int
prev_performance_state
;
};
struct
gpd_timing_data
{
...
...
@@ -258,8 +264,8 @@ int of_genpd_add_subdomain(struct of_phandle_args *parent,
struct
generic_pm_domain
*
of_genpd_remove_last
(
struct
device_node
*
np
);
int
of_genpd_parse_idle_states
(
struct
device_node
*
dn
,
struct
genpd_power_state
**
states
,
int
*
n
);
unsigned
int
of_genpd_opp_to_performance_state
(
struct
device
*
dev
,
struct
device_node
*
n
p
);
unsigned
int
pm_genpd_opp_to_performance_state
(
struct
device
*
genpd_
dev
,
struct
dev_pm_opp
*
op
p
);
int
genpd_dev_pm_attach
(
struct
device
*
dev
);
struct
device
*
genpd_dev_pm_attach_by_id
(
struct
device
*
dev
,
...
...
@@ -300,8 +306,8 @@ static inline int of_genpd_parse_idle_states(struct device_node *dn,
}
static
inline
unsigned
int
of_genpd_opp_to_performance_state
(
struct
device
*
dev
,
struct
dev
ice_node
*
n
p
)
pm_genpd_opp_to_performance_state
(
struct
device
*
genpd_
dev
,
struct
dev
_pm_opp
*
op
p
)
{
return
0
;
}
...
...
include/linux/pm_opp.h
View file @
ade0c949
...
...
@@ -126,6 +126,9 @@ struct opp_table *dev_pm_opp_set_clkname(struct device *dev, const char * name);
void
dev_pm_opp_put_clkname
(
struct
opp_table
*
opp_table
);
struct
opp_table
*
dev_pm_opp_register_set_opp_helper
(
struct
device
*
dev
,
int
(
*
set_opp
)(
struct
dev_pm_set_opp_data
*
data
));
void
dev_pm_opp_unregister_set_opp_helper
(
struct
opp_table
*
opp_table
);
struct
opp_table
*
dev_pm_opp_set_genpd_virt_dev
(
struct
device
*
dev
,
struct
device
*
virt_dev
,
int
index
);
void
dev_pm_opp_put_genpd_virt_dev
(
struct
opp_table
*
opp_table
,
struct
device
*
virt_dev
);
int
dev_pm_opp_xlate_performance_state
(
struct
opp_table
*
src_table
,
struct
opp_table
*
dst_table
,
unsigned
int
pstate
);
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
);
...
...
@@ -272,6 +275,18 @@ static inline struct opp_table *dev_pm_opp_set_clkname(struct device *dev, const
static
inline
void
dev_pm_opp_put_clkname
(
struct
opp_table
*
opp_table
)
{}
static
inline
struct
opp_table
*
dev_pm_opp_set_genpd_virt_dev
(
struct
device
*
dev
,
struct
device
*
virt_dev
,
int
index
)
{
return
ERR_PTR
(
-
ENOTSUPP
);
}
static
inline
void
dev_pm_opp_put_genpd_virt_dev
(
struct
opp_table
*
opp_table
,
struct
device
*
virt_dev
)
{}
static
inline
int
dev_pm_opp_xlate_performance_state
(
struct
opp_table
*
src_table
,
struct
opp_table
*
dst_table
,
unsigned
int
pstate
)
{
return
-
ENOTSUPP
;
}
static
inline
int
dev_pm_opp_set_rate
(
struct
device
*
dev
,
unsigned
long
target_freq
)
{
return
-
ENOTSUPP
;
...
...
@@ -305,8 +320,8 @@ 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
);
struct
device_node
*
dev_pm_opp_of_get_opp_desc_node
(
struct
device
*
dev
);
struct
dev_pm_opp
*
of_dev_pm_opp_find_required_opp
(
struct
device
*
dev
,
struct
device_node
*
np
);
struct
device_node
*
dev_pm_opp_get_of_node
(
struct
dev_pm_opp
*
opp
);
int
of_get_required_opp_performance_state
(
struct
device_node
*
np
,
int
index
);
#else
static
inline
int
dev_pm_opp_of_add_table
(
struct
device
*
dev
)
{
...
...
@@ -341,13 +356,13 @@ static inline struct device_node *dev_pm_opp_of_get_opp_desc_node(struct device
return
NULL
;
}
static
inline
struct
dev
_pm_opp
*
of_dev_pm_opp_find_required_opp
(
struct
device
*
dev
,
struct
device_node
*
n
p
)
static
inline
struct
dev
ice_node
*
dev_pm_opp_get_of_node
(
struct
dev_pm_opp
*
op
p
)
{
return
NULL
;
}
static
inline
struct
device_node
*
dev_pm_opp_get_of_node
(
struct
dev_pm_opp
*
opp
)
static
inline
int
of_get_required_opp_performance_state
(
struct
device_node
*
np
,
int
index
)
{
return
NULL
;
return
-
ENOTSUPP
;
}
#endif
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment