Skip to content

Commit 1d9a542

Browse files
Tero Kristotmlind
authored andcommitted
ARM: OMAP2+: clockdomain: add usecounting support to autoidle APIs
The previous implementation was racy in many locations, where the current status of the clockdomain was read out, some operations were executed, and the previous status info was used afterwards to decide next state for the clockdomain. Instead, fix the implementation of the allow_idle / deny_idle APIs to properly have usecounting support. This allows clean handling internally within the clockdomain core, and simplifies the usage also within hwmod. Signed-off-by: Tero Kristo <t-kristo@ti.com> Signed-off-by: Tony Lindgren <tony@atomide.com>
1 parent e98580e commit 1d9a542

File tree

7 files changed

+47
-50
lines changed

7 files changed

+47
-50
lines changed

arch/arm/mach-omap2/clockdomain.c

Lines changed: 24 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -465,10 +465,7 @@ int clkdm_complete_init(void)
465465
return -EACCES;
466466

467467
list_for_each_entry(clkdm, &clkdm_list, node) {
468-
if (clkdm->flags & CLKDM_CAN_FORCE_WAKEUP)
469-
clkdm_wakeup(clkdm);
470-
else if (clkdm->flags & CLKDM_CAN_DISABLE_AUTO)
471-
clkdm_deny_idle(clkdm);
468+
clkdm_deny_idle(clkdm);
472469

473470
_resolve_clkdm_deps(clkdm, clkdm->wkdep_srcs);
474471
clkdm_clear_all_wkdeps(clkdm);
@@ -925,11 +922,20 @@ void clkdm_allow_idle_nolock(struct clockdomain *clkdm)
925922
if (!clkdm)
926923
return;
927924

928-
if (!(clkdm->flags & CLKDM_CAN_ENABLE_AUTO)) {
929-
pr_debug("clock: %s: automatic idle transitions cannot be enabled\n",
930-
clkdm->name);
925+
if (!WARN_ON(!clkdm->forcewake_count))
926+
clkdm->forcewake_count--;
927+
928+
if (clkdm->forcewake_count)
929+
return;
930+
931+
if (!clkdm->usecount && (clkdm->flags & CLKDM_CAN_FORCE_SLEEP))
932+
clkdm_sleep_nolock(clkdm);
933+
934+
if (!(clkdm->flags & CLKDM_CAN_ENABLE_AUTO))
935+
return;
936+
937+
if (clkdm->flags & CLKDM_MISSING_IDLE_REPORTING)
931938
return;
932-
}
933939

934940
if (!arch_clkdm || !arch_clkdm->clkdm_allow_idle)
935941
return;
@@ -974,11 +980,17 @@ void clkdm_deny_idle_nolock(struct clockdomain *clkdm)
974980
if (!clkdm)
975981
return;
976982

977-
if (!(clkdm->flags & CLKDM_CAN_DISABLE_AUTO)) {
978-
pr_debug("clockdomain: %s: automatic idle transitions cannot be disabled\n",
979-
clkdm->name);
983+
if (clkdm->forcewake_count++)
984+
return;
985+
986+
if (clkdm->flags & CLKDM_CAN_FORCE_WAKEUP)
987+
clkdm_wakeup_nolock(clkdm);
988+
989+
if (!(clkdm->flags & CLKDM_CAN_DISABLE_AUTO))
990+
return;
991+
992+
if (clkdm->flags & CLKDM_MISSING_IDLE_REPORTING)
980993
return;
981-
}
982994

983995
if (!arch_clkdm || !arch_clkdm->clkdm_deny_idle)
984996
return;

arch/arm/mach-omap2/clockdomain.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,7 @@ struct omap_hwmod;
114114
* @wkdep_srcs: Clockdomains that can be told to wake this powerdomain up
115115
* @sleepdep_srcs: Clockdomains that can be told to keep this clkdm from inact
116116
* @usecount: Usecount tracking
117+
* @forcewake_count: Usecount for forcing the domain active
117118
* @node: list_head to link all clockdomains together
118119
*
119120
* @prcm_partition should be a macro from mach-omap2/prcm44xx.h (OMAP4 only)
@@ -138,6 +139,7 @@ struct clockdomain {
138139
struct clkdm_dep *wkdep_srcs;
139140
struct clkdm_dep *sleepdep_srcs;
140141
int usecount;
142+
int forcewake_count;
141143
struct list_head node;
142144
};
143145

arch/arm/mach-omap2/cpuidle44xx.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,7 @@ static int omap_enter_idle_coupled(struct cpuidle_device *dev,
140140
mpuss_can_lose_context)
141141
gic_dist_disable();
142142

143-
clkdm_wakeup(cpu_clkdm[1]);
143+
clkdm_deny_idle(cpu_clkdm[1]);
144144
omap_set_pwrdm_state(cpu_pd[1], PWRDM_POWER_ON);
145145
clkdm_allow_idle(cpu_clkdm[1]);
146146

arch/arm/mach-omap2/omap-smp.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -200,7 +200,7 @@ static int omap4_boot_secondary(unsigned int cpu, struct task_struct *idle)
200200
* Ensure that CPU power state is set to ON to avoid CPU
201201
* powerdomain transition on wfi
202202
*/
203-
clkdm_wakeup_nolock(cpu1_clkdm);
203+
clkdm_deny_idle_nolock(cpu1_clkdm);
204204
pwrdm_set_next_pwrst(cpu1_pwrdm, PWRDM_POWER_ON);
205205
clkdm_allow_idle_nolock(cpu1_clkdm);
206206

arch/arm/mach-omap2/omap_hwmod.c

Lines changed: 12 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1702,7 +1702,6 @@ static int _deassert_hardreset(struct omap_hwmod *oh, const char *name)
17021702
{
17031703
struct omap_hwmod_rst_info ohri;
17041704
int ret = -EINVAL;
1705-
int hwsup = 0;
17061705

17071706
if (!oh)
17081707
return -EINVAL;
@@ -1720,7 +1719,7 @@ static int _deassert_hardreset(struct omap_hwmod *oh, const char *name)
17201719
* might not be completed. The clockdomain can be set
17211720
* in HW_AUTO only when the module become ready.
17221721
*/
1723-
hwsup = clkdm_in_hwsup(oh->clkdm);
1722+
clkdm_deny_idle(oh->clkdm);
17241723
ret = clkdm_hwmod_enable(oh->clkdm, oh);
17251724
if (ret) {
17261725
WARN(1, "omap_hwmod: %s: could not enable clockdomain %s: %d\n",
@@ -1747,8 +1746,7 @@ static int _deassert_hardreset(struct omap_hwmod *oh, const char *name)
17471746
* Set the clockdomain to HW_AUTO, assuming that the
17481747
* previous state was HW_AUTO.
17491748
*/
1750-
if (hwsup)
1751-
clkdm_allow_idle(oh->clkdm);
1749+
clkdm_allow_idle(oh->clkdm);
17521750

17531751
clkdm_hwmod_disable(oh->clkdm, oh);
17541752
}
@@ -2102,7 +2100,6 @@ static int _enable_preprogram(struct omap_hwmod *oh)
21022100
static int _enable(struct omap_hwmod *oh)
21032101
{
21042102
int r;
2105-
int hwsup = 0;
21062103

21072104
pr_debug("omap_hwmod: %s: enabling\n", oh->name);
21082105

@@ -2162,8 +2159,7 @@ static int _enable(struct omap_hwmod *oh)
21622159
* completely the module. The clockdomain can be set
21632160
* in HW_AUTO only when the module become ready.
21642161
*/
2165-
hwsup = clkdm_in_hwsup(oh->clkdm) &&
2166-
!clkdm_missing_idle_reporting(oh->clkdm);
2162+
clkdm_deny_idle(oh->clkdm);
21672163
r = clkdm_hwmod_enable(oh->clkdm, oh);
21682164
if (r) {
21692165
WARN(1, "omap_hwmod: %s: could not enable clockdomain %s: %d\n",
@@ -2183,14 +2179,10 @@ static int _enable(struct omap_hwmod *oh)
21832179

21842180
r = (soc_ops.wait_target_ready) ? soc_ops.wait_target_ready(oh) :
21852181
-EINVAL;
2186-
if (!r) {
2187-
/*
2188-
* Set the clockdomain to HW_AUTO only if the target is ready,
2189-
* assuming that the previous state was HW_AUTO
2190-
*/
2191-
if (oh->clkdm && hwsup)
2192-
clkdm_allow_idle(oh->clkdm);
2182+
if (oh->clkdm)
2183+
clkdm_allow_idle(oh->clkdm);
21932184

2185+
if (!r) {
21942186
oh->_state = _HWMOD_STATE_ENABLED;
21952187

21962188
/* Access the sysconfig only if the target is ready */
@@ -2244,6 +2236,9 @@ static int _idle(struct omap_hwmod *oh)
22442236
_idle_sysc(oh);
22452237
_del_initiator_dep(oh, mpu_oh);
22462238

2239+
if (oh->clkdm)
2240+
clkdm_deny_idle(oh->clkdm);
2241+
22472242
if (oh->flags & HWMOD_BLOCK_WFI)
22482243
cpu_idle_poll_ctrl(false);
22492244
if (soc_ops.disable_module)
@@ -2256,8 +2251,10 @@ static int _idle(struct omap_hwmod *oh)
22562251
* transition to complete properly.
22572252
*/
22582253
_disable_clocks(oh);
2259-
if (oh->clkdm)
2254+
if (oh->clkdm) {
2255+
clkdm_allow_idle(oh->clkdm);
22602256
clkdm_hwmod_disable(oh->clkdm, oh);
2257+
}
22612258

22622259
/* Mux pins for device idle if populated */
22632260
if (oh->mux && oh->mux->pads_dynamic) {

arch/arm/mach-omap2/pm.c

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -110,13 +110,7 @@ static void __init omap2_init_processor_devices(void)
110110

111111
int __init omap_pm_clkdms_setup(struct clockdomain *clkdm, void *unused)
112112
{
113-
/* XXX The usecount test is racy */
114-
if ((clkdm->flags & CLKDM_CAN_ENABLE_AUTO) &&
115-
!(clkdm->flags & CLKDM_MISSING_IDLE_REPORTING))
116-
clkdm_allow_idle(clkdm);
117-
else if (clkdm->flags & CLKDM_CAN_FORCE_SLEEP &&
118-
clkdm->usecount == 0)
119-
clkdm_sleep(clkdm);
113+
clkdm_allow_idle(clkdm);
120114
return 0;
121115
}
122116

arch/arm/mach-omap2/powerdomain.c

Lines changed: 6 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -222,7 +222,6 @@ static int _pwrdm_post_transition_cb(struct powerdomain *pwrdm, void *unused)
222222
* @pwrdm: struct powerdomain * to operate on
223223
* @curr_pwrst: current power state of @pwrdm
224224
* @pwrst: power state to switch to
225-
* @hwsup: ptr to a bool to return whether the clkdm is hardware-supervised
226225
*
227226
* Determine whether the powerdomain needs to be turned on before
228227
* attempting to switch power states. Called by
@@ -233,8 +232,7 @@ static int _pwrdm_post_transition_cb(struct powerdomain *pwrdm, void *unused)
233232
* "Types of sleep_switch" comment above).
234233
*/
235234
static u8 _pwrdm_save_clkdm_state_and_activate(struct powerdomain *pwrdm,
236-
u8 curr_pwrst, u8 pwrst,
237-
bool *hwsup)
235+
u8 curr_pwrst, u8 pwrst)
238236
{
239237
u8 sleep_switch;
240238

@@ -244,8 +242,7 @@ static u8 _pwrdm_save_clkdm_state_and_activate(struct powerdomain *pwrdm,
244242
arch_pwrdm->pwrdm_set_lowpwrstchange) {
245243
sleep_switch = LOWPOWERSTATE_SWITCH;
246244
} else {
247-
*hwsup = clkdm_in_hwsup(pwrdm->pwrdm_clkdms[0]);
248-
clkdm_wakeup_nolock(pwrdm->pwrdm_clkdms[0]);
245+
clkdm_deny_idle_nolock(pwrdm->pwrdm_clkdms[0]);
249246
sleep_switch = FORCEWAKEUP_SWITCH;
250247
}
251248
} else {
@@ -259,7 +256,6 @@ static u8 _pwrdm_save_clkdm_state_and_activate(struct powerdomain *pwrdm,
259256
* _pwrdm_restore_clkdm_state - restore the clkdm hwsup state after pwrst change
260257
* @pwrdm: struct powerdomain * to operate on
261258
* @sleep_switch: return value from _pwrdm_save_clkdm_state_and_activate()
262-
* @hwsup: should @pwrdm's first clockdomain be set to hardware-supervised mode?
263259
*
264260
* Restore the clockdomain state perturbed by
265261
* _pwrdm_save_clkdm_state_and_activate(), and call the power state
@@ -270,14 +266,11 @@ static u8 _pwrdm_save_clkdm_state_and_activate(struct powerdomain *pwrdm,
270266
* software-supervised sleep. No return value.
271267
*/
272268
static void _pwrdm_restore_clkdm_state(struct powerdomain *pwrdm,
273-
u8 sleep_switch, bool hwsup)
269+
u8 sleep_switch)
274270
{
275271
switch (sleep_switch) {
276272
case FORCEWAKEUP_SWITCH:
277-
if (hwsup)
278-
clkdm_allow_idle_nolock(pwrdm->pwrdm_clkdms[0]);
279-
else
280-
clkdm_sleep_nolock(pwrdm->pwrdm_clkdms[0]);
273+
clkdm_allow_idle_nolock(pwrdm->pwrdm_clkdms[0]);
281274
break;
282275
case LOWPOWERSTATE_SWITCH:
283276
if (pwrdm->flags & PWRDM_HAS_LOWPOWERSTATECHANGE &&
@@ -1092,7 +1085,6 @@ int omap_set_pwrdm_state(struct powerdomain *pwrdm, u8 pwrst)
10921085
u8 next_pwrst, sleep_switch;
10931086
int curr_pwrst;
10941087
int ret = 0;
1095-
bool hwsup = false;
10961088

10971089
if (!pwrdm || IS_ERR(pwrdm))
10981090
return -EINVAL;
@@ -1116,14 +1108,14 @@ int omap_set_pwrdm_state(struct powerdomain *pwrdm, u8 pwrst)
11161108
goto osps_out;
11171109

11181110
sleep_switch = _pwrdm_save_clkdm_state_and_activate(pwrdm, curr_pwrst,
1119-
pwrst, &hwsup);
1111+
pwrst);
11201112

11211113
ret = pwrdm_set_next_pwrst(pwrdm, pwrst);
11221114
if (ret)
11231115
pr_err("%s: unable to set power state of powerdomain: %s\n",
11241116
__func__, pwrdm->name);
11251117

1126-
_pwrdm_restore_clkdm_state(pwrdm, sleep_switch, hwsup);
1118+
_pwrdm_restore_clkdm_state(pwrdm, sleep_switch);
11271119

11281120
osps_out:
11291121
pwrdm_unlock(pwrdm);

0 commit comments

Comments
 (0)