@@ -265,6 +265,16 @@ static inline void adjust_jiffies(unsigned long val, struct cpufreq_freqs *ci)
}
#endif
+void cpufreq_transition_complete(struct cpufreq_policy *policy)
+{
+ unsigned long flags;
+
+ write_lock_irqsave(&cpufreq_driver_lock, flags);
+ policy->transition_ongoing--;
+ write_unlock_irqrestore(&cpufreq_driver_lock, flags);
+}
+EXPORT_SYMBOL_GPL(cpufreq_transition_complete);
+
static void __cpufreq_notify_transition(struct cpufreq_policy *policy,
struct cpufreq_freqs *freqs, unsigned int state)
{
@@ -350,16 +360,12 @@ void cpufreq_notify_transition(struct cpufreq_policy *policy,
if ((cpufreq_driver->flags & CPUFREQ_ASYNC_NOTIFICATION)
&& (state == CPUFREQ_POSTCHANGE)) {
- unsigned long flags;
-
/*
* Some drivers don't send POSTCHANGE notification from their
* ->target() but from some kind of bottom half and so we are
* ending transaction here..
*/
- write_lock_irqsave(&cpufreq_driver_lock, flags);
- policy->transition_ongoing--;
- write_unlock_irqrestore(&cpufreq_driver_lock, flags);
+ cpufreq_transition_complete(policy);
}
}
EXPORT_SYMBOL_GPL(cpufreq_notify_transition);
@@ -1430,9 +1436,7 @@ static void cpufreq_out_of_sync(unsigned int cpu, unsigned int old_freq,
if (cpufreq_driver->flags & CPUFREQ_ASYNC_NOTIFICATION)
return;
- write_lock_irqsave(&cpufreq_driver_lock, flags);
- policy->transition_ongoing--;
- write_unlock_irqrestore(&cpufreq_driver_lock, flags);
+ cpufreq_transition_complete(policy);
}
/**
@@ -1754,10 +1758,7 @@ int __cpufreq_driver_target(struct cpufreq_policy *policy,
&& (retval == -EINPROGRESS))
return retval;
- write_lock_irqsave(&cpufreq_driver_lock, flags);
- policy->transition_ongoing--;
- write_unlock_irqrestore(&cpufreq_driver_lock, flags);
-
+ cpufreq_transition_complete(policy);
return retval;
}
EXPORT_SYMBOL_GPL(__cpufreq_driver_target);
@@ -231,6 +231,13 @@ int cpufreq_unregister_driver(struct cpufreq_driver *driver_data);
const char *cpufreq_get_current_driver(void);
+/*
+ * Only for drivers which have CPUFREQ_ASYNC_NOTIFICATION flag set and they need
+ * to mark transaction over before starting any notifications, otherwise the
+ * POSTCHANGE notification already does this.
+ */
+void cpufreq_transition_complete(struct cpufreq_policy *policy);
+
static inline void cpufreq_verify_within_limits(struct cpufreq_policy *policy,
unsigned int min, unsigned int max)
{
Following patch "cpufreq: make sure frequency transitions are serialized" guarantees that we don't have any races while changing cpu frequency or sending notifications. It handled a special case with CPUFREQ_ASYNC_NOTIFICATION flag for drivers that don't complete their freq change from ->target() and exynos5440 driver is well adopted to it as well.. There is one more driver powernow-k8 that has similar implementation, schedules a work for doing transitions. All is good if that work function does notifications on every call to it and so the transition_ongoing count stays stable. But there are chances that the work function may return without actually doing the notifications, in which case transition_ongoing count will not be set to zero and so no transitions would be possible after that. This patch adds another routine cpufreq_transition_complete() which would be used by powernow-k8 (or even exynos5440 if required), that will be used to mark end of transition in such cases. Later patch will change powernow-k8 to use this routine. Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org> --- drivers/cpufreq/cpufreq.c | 25 +++++++++++++------------ include/linux/cpufreq.h | 7 +++++++ 2 files changed, 20 insertions(+), 12 deletions(-)