summaryrefslogtreecommitdiff
path: root/drivers/clk/clk.c
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2015-04-21 09:24:09 -0700
committerLinus Torvalds <torvalds@linux-foundation.org>2015-04-21 09:24:09 -0700
commite98bf5cedf25eb32f8f224e8dff9845f0856d18f (patch)
treef8a33eb26cb9d47bfbb36e50de7ad85081cc349e /drivers/clk/clk.c
parent8f443e2372ba23d51ee365974f54507acd6f69d1 (diff)
parent03bc10ab5b0f9b8f81bffbe6e40c944f9d3dbcc5 (diff)
Merge tag 'clk-for-linus-4.1' of git://git.kernel.org/pub/scm/linux/kernel/git/clk/linux
Pull clock framework updates from Michael Turquette: "The changes to the common clock framework for 4.0 are mostly new clock drivers and updates to existing ones for feature enhancements and bug fixes. There is more churn than usual in the framework core due to the change to introduce per-user unique struct clk pointers in 4.0. This caused several regressions to surface, some of which were sent as fixes to 4.0. New generic clock drivers were added for GPIO- and PWM-based clock controllers. Additionally the common clk-divider code recieved several fixes to the way it rounds rates" * tag 'clk-for-linus-4.1' of git://git.kernel.org/pub/scm/linux/kernel/git/clk/linux: (91 commits) clk: check ->determine/round_rate() return value in clk_calc_new_rates clk: at91: usb: propagate rate modification to the parent clk clk: samsung: exynos4: Disable ARMCLK down feature on Exynos4210 SoC clk: don't use __initconst for non-const arrays clk: at91: change to using endian agnositc IO clk: clk-gpio-gate: Fix active low clk: Add PWM clock driver clk: Add clock driver for mb86s7x clk: pxa: pxa3xx: add missing os timer clock clk: tegra: Use the proper parent for plld_dsi clk: tegra: Use generic tegra_osc_clk_init() on Tegra114 clk: tegra: Model oscillator as clock clk: tegra: Add peripheral registers for bank Y clk: tegra: Register the proper number of resets clk: tegra: Remove needless initializations clk: tegra: Use consistent indentation clk: tegra: Various whitespace cleanups clk: tegra: Enable HDA to HDMI clocks on Tegra124 clk: tegra: Fix a bunch of sparse warnings clk: tegra: Fix typo tabel -> table ...
Diffstat (limited to 'drivers/clk/clk.c')
-rw-r--r--drivers/clk/clk.c157
1 files changed, 105 insertions, 52 deletions
diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c
index 237f23f68bfc..459ce9da13e0 100644
--- a/drivers/clk/clk.c
+++ b/drivers/clk/clk.c
@@ -77,13 +77,16 @@ struct clk_core {
struct kref ref;
};
+#define CREATE_TRACE_POINTS
+#include <trace/events/clk.h>
+
struct clk {
struct clk_core *core;
const char *dev_id;
const char *con_id;
unsigned long min_rate;
unsigned long max_rate;
- struct hlist_node child_node;
+ struct hlist_node clks_node;
};
/*** locking ***/
@@ -480,6 +483,8 @@ static void clk_unprepare_unused_subtree(struct clk_core *clk)
{
struct clk_core *child;
+ lockdep_assert_held(&prepare_lock);
+
hlist_for_each_entry(child, &clk->children, child_node)
clk_unprepare_unused_subtree(child);
@@ -490,10 +495,12 @@ static void clk_unprepare_unused_subtree(struct clk_core *clk)
return;
if (clk_core_is_prepared(clk)) {
+ trace_clk_unprepare(clk);
if (clk->ops->unprepare_unused)
clk->ops->unprepare_unused(clk->hw);
else if (clk->ops->unprepare)
clk->ops->unprepare(clk->hw);
+ trace_clk_unprepare_complete(clk);
}
}
@@ -503,6 +510,8 @@ static void clk_disable_unused_subtree(struct clk_core *clk)
struct clk_core *child;
unsigned long flags;
+ lockdep_assert_held(&prepare_lock);
+
hlist_for_each_entry(child, &clk->children, child_node)
clk_disable_unused_subtree(child);
@@ -520,10 +529,12 @@ static void clk_disable_unused_subtree(struct clk_core *clk)
* back to .disable
*/
if (clk_core_is_enabled(clk)) {
+ trace_clk_disable(clk);
if (clk->ops->disable_unused)
clk->ops->disable_unused(clk->hw);
else if (clk->ops->disable)
clk->ops->disable(clk->hw);
+ trace_clk_disable_complete(clk);
}
unlock_out:
@@ -851,10 +862,10 @@ static void clk_core_get_boundaries(struct clk_core *clk,
*min_rate = 0;
*max_rate = ULONG_MAX;
- hlist_for_each_entry(clk_user, &clk->clks, child_node)
+ hlist_for_each_entry(clk_user, &clk->clks, clks_node)
*min_rate = max(*min_rate, clk_user->min_rate);
- hlist_for_each_entry(clk_user, &clk->clks, child_node)
+ hlist_for_each_entry(clk_user, &clk->clks, clks_node)
*max_rate = min(*max_rate, clk_user->max_rate);
}
@@ -903,9 +914,12 @@ static void clk_core_unprepare(struct clk_core *clk)
WARN_ON(clk->enable_count > 0);
+ trace_clk_unprepare(clk);
+
if (clk->ops->unprepare)
clk->ops->unprepare(clk->hw);
+ trace_clk_unprepare_complete(clk);
clk_core_unprepare(clk->parent);
}
@@ -943,12 +957,16 @@ static int clk_core_prepare(struct clk_core *clk)
if (ret)
return ret;
- if (clk->ops->prepare) {
+ trace_clk_prepare(clk);
+
+ if (clk->ops->prepare)
ret = clk->ops->prepare(clk->hw);
- if (ret) {
- clk_core_unprepare(clk->parent);
- return ret;
- }
+
+ trace_clk_prepare_complete(clk);
+
+ if (ret) {
+ clk_core_unprepare(clk->parent);
+ return ret;
}
}
@@ -995,9 +1013,13 @@ static void clk_core_disable(struct clk_core *clk)
if (--clk->enable_count > 0)
return;
+ trace_clk_disable(clk);
+
if (clk->ops->disable)
clk->ops->disable(clk->hw);
+ trace_clk_disable_complete(clk);
+
clk_core_disable(clk->parent);
}
@@ -1050,12 +1072,16 @@ static int clk_core_enable(struct clk_core *clk)
if (ret)
return ret;
- if (clk->ops->enable) {
+ trace_clk_enable(clk);
+
+ if (clk->ops->enable)
ret = clk->ops->enable(clk->hw);
- if (ret) {
- clk_core_disable(clk->parent);
- return ret;
- }
+
+ trace_clk_enable_complete(clk);
+
+ if (ret) {
+ clk_core_disable(clk->parent);
+ return ret;
}
}
@@ -1106,6 +1132,8 @@ static unsigned long clk_core_round_rate_nolock(struct clk_core *clk,
struct clk_core *parent;
struct clk_hw *parent_hw;
+ lockdep_assert_held(&prepare_lock);
+
if (!clk)
return 0;
@@ -1245,6 +1273,8 @@ static void __clk_recalc_accuracies(struct clk_core *clk)
unsigned long parent_accuracy = 0;
struct clk_core *child;
+ lockdep_assert_held(&prepare_lock);
+
if (clk->parent)
parent_accuracy = clk->parent->accuracy;
@@ -1318,6 +1348,8 @@ static void __clk_recalc_rates(struct clk_core *clk, unsigned long msg)
unsigned long parent_rate = 0;
struct clk_core *child;
+ lockdep_assert_held(&prepare_lock);
+
old_rate = clk->rate;
if (clk->parent)
@@ -1479,10 +1511,14 @@ static int __clk_set_parent(struct clk_core *clk, struct clk_core *parent,
old_parent = __clk_set_parent_before(clk, parent);
+ trace_clk_set_parent(clk, parent);
+
/* change clock input source */
if (parent && clk->ops->set_parent)
ret = clk->ops->set_parent(clk->hw, p_index);
+ trace_clk_set_parent_complete(clk, parent);
+
if (ret) {
flags = clk_enable_lock();
clk_reparent(clk, old_parent);
@@ -1524,6 +1560,8 @@ static int __clk_speculate_rates(struct clk_core *clk,
unsigned long new_rate;
int ret = NOTIFY_DONE;
+ lockdep_assert_held(&prepare_lock);
+
new_rate = clk_recalc(clk, parent_rate);
/* abort rate change if a driver returns NOTIFY_BAD or NOTIFY_STOP */
@@ -1580,6 +1618,7 @@ static struct clk_core *clk_calc_new_rates(struct clk_core *clk,
unsigned long min_rate;
unsigned long max_rate;
int p_index = 0;
+ long ret;
/* sanity */
if (IS_ERR_OR_NULL(clk))
@@ -1595,15 +1634,23 @@ static struct clk_core *clk_calc_new_rates(struct clk_core *clk,
/* find the closest rate and parent clk/rate */
if (clk->ops->determine_rate) {
parent_hw = parent ? parent->hw : NULL;
- new_rate = clk->ops->determine_rate(clk->hw, rate,
- min_rate,
- max_rate,
- &best_parent_rate,
- &parent_hw);
+ ret = clk->ops->determine_rate(clk->hw, rate,
+ min_rate,
+ max_rate,
+ &best_parent_rate,
+ &parent_hw);
+ if (ret < 0)
+ return NULL;
+
+ new_rate = ret;
parent = parent_hw ? parent_hw->core : NULL;
} else if (clk->ops->round_rate) {
- new_rate = clk->ops->round_rate(clk->hw, rate,
- &best_parent_rate);
+ ret = clk->ops->round_rate(clk->hw, rate,
+ &best_parent_rate);
+ if (ret < 0)
+ return NULL;
+
+ new_rate = ret;
if (new_rate < min_rate || new_rate > max_rate)
return NULL;
} else if (!parent || !(clk->flags & CLK_SET_RATE_PARENT)) {
@@ -1706,6 +1753,7 @@ static void clk_change_rate(struct clk_core *clk)
if (clk->new_parent && clk->new_parent != clk->parent) {
old_parent = __clk_set_parent_before(clk, clk->new_parent);
+ trace_clk_set_parent(clk, clk->new_parent);
if (clk->ops->set_rate_and_parent) {
skip_set_rate = true;
@@ -1716,12 +1764,17 @@ static void clk_change_rate(struct clk_core *clk)
clk->ops->set_parent(clk->hw, clk->new_parent_index);
}
+ trace_clk_set_parent_complete(clk, clk->new_parent);
__clk_set_parent_after(clk, clk->new_parent, old_parent);
}
+ trace_clk_set_rate(clk, clk->new_rate);
+
if (!skip_set_rate && clk->ops->set_rate)
clk->ops->set_rate(clk->hw, clk->new_rate, best_parent_rate);
+ trace_clk_set_rate_complete(clk, clk->new_rate);
+
clk->rate = clk_recalc(clk, best_parent_rate);
if (clk->notifier_count && old_rate != clk->rate)
@@ -2010,16 +2063,18 @@ static int clk_core_set_parent(struct clk_core *clk, struct clk_core *parent)
if (!clk)
return 0;
- /* verify ops for for multi-parent clks */
- if ((clk->num_parents > 1) && (!clk->ops->set_parent))
- return -ENOSYS;
-
/* prevent racing with updates to the clock topology */
clk_prepare_lock();
if (clk->parent == parent)
goto out;
+ /* verify ops for for multi-parent clks */
+ if ((clk->num_parents > 1) && (!clk->ops->set_parent)) {
+ ret = -ENOSYS;
+ goto out;
+ }
+
/* check that we are allowed to re-parent if the clock is in use */
if ((clk->flags & CLK_SET_PARENT_GATE) && clk->prepare_count) {
ret = -EBUSY;
@@ -2110,10 +2165,10 @@ EXPORT_SYMBOL_GPL(clk_set_parent);
*/
int clk_set_phase(struct clk *clk, int degrees)
{
- int ret = 0;
+ int ret = -EINVAL;
if (!clk)
- goto out;
+ return 0;
/* sanity check degrees */
degrees %= 360;
@@ -2122,18 +2177,18 @@ int clk_set_phase(struct clk *clk, int degrees)
clk_prepare_lock();
- if (!clk->core->ops->set_phase)
- goto out_unlock;
+ trace_clk_set_phase(clk->core, degrees);
- ret = clk->core->ops->set_phase(clk->core->hw, degrees);
+ if (clk->core->ops->set_phase)
+ ret = clk->core->ops->set_phase(clk->core->hw, degrees);
+
+ trace_clk_set_phase_complete(clk->core, degrees);
if (!ret)
clk->core->phase = degrees;
-out_unlock:
clk_prepare_unlock();
-out:
return ret;
}
EXPORT_SYMBOL_GPL(clk_set_phase);
@@ -2401,7 +2456,7 @@ struct clk *__clk_create_clk(struct clk_hw *hw, const char *dev_id,
clk->max_rate = ULONG_MAX;
clk_prepare_lock();
- hlist_add_head(&clk->child_node, &hw->core->clks);
+ hlist_add_head(&clk->clks_node, &hw->core->clks);
clk_prepare_unlock();
return clk;
@@ -2410,7 +2465,7 @@ struct clk *__clk_create_clk(struct clk_hw *hw, const char *dev_id,
void __clk_free_clk(struct clk *clk)
{
clk_prepare_lock();
- hlist_del(&clk->child_node);
+ hlist_del(&clk->clks_node);
clk_prepare_unlock();
kfree(clk);
@@ -2513,6 +2568,8 @@ static void __clk_release(struct kref *ref)
struct clk_core *clk = container_of(ref, struct clk_core, ref);
int i = clk->num_parents;
+ lockdep_assert_held(&prepare_lock);
+
kfree(clk->parents);
while (--i >= 0)
kfree_const(clk->parent_names[i]);
@@ -2688,7 +2745,7 @@ void __clk_put(struct clk *clk)
clk_prepare_lock();
- hlist_del(&clk->child_node);
+ hlist_del(&clk->clks_node);
if (clk->min_rate > clk->core->req_rate ||
clk->max_rate < clk->core->req_rate)
clk_core_set_rate_nolock(clk->core, clk->core->req_rate);
@@ -2834,17 +2891,6 @@ static const struct of_device_id __clk_of_table_sentinel
static LIST_HEAD(of_clk_providers);
static DEFINE_MUTEX(of_clk_mutex);
-/* of_clk_provider list locking helpers */
-void of_clk_lock(void)
-{
- mutex_lock(&of_clk_mutex);
-}
-
-void of_clk_unlock(void)
-{
- mutex_unlock(&of_clk_mutex);
-}
-
struct clk *of_clk_src_simple_get(struct of_phandle_args *clkspec,
void *data)
{
@@ -2928,7 +2974,11 @@ struct clk *__of_clk_get_from_provider(struct of_phandle_args *clkspec,
struct of_clk_provider *provider;
struct clk *clk = ERR_PTR(-EPROBE_DEFER);
+ if (!clkspec)
+ return ERR_PTR(-EINVAL);
+
/* Check if we have such a provider in our array */
+ mutex_lock(&of_clk_mutex);
list_for_each_entry(provider, &of_clk_providers, link) {
if (provider->node == clkspec->np)
clk = provider->get(clkspec, provider->data);
@@ -2944,19 +2994,22 @@ struct clk *__of_clk_get_from_provider(struct of_phandle_args *clkspec,
break;
}
}
+ mutex_unlock(&of_clk_mutex);
return clk;
}
+/**
+ * of_clk_get_from_provider() - Lookup a clock from a clock provider
+ * @clkspec: pointer to a clock specifier data structure
+ *
+ * This function looks up a struct clk from the registered list of clock
+ * providers, an input is a clock specifier data structure as returned
+ * from the of_parse_phandle_with_args() function call.
+ */
struct clk *of_clk_get_from_provider(struct of_phandle_args *clkspec)
{
- struct clk *clk;
-
- mutex_lock(&of_clk_mutex);
- clk = __of_clk_get_from_provider(clkspec, NULL, __func__);
- mutex_unlock(&of_clk_mutex);
-
- return clk;
+ return __of_clk_get_from_provider(clkspec, NULL, __func__);
}
int of_clk_get_parent_count(struct device_node *np)