It's useful to have tracepoints around operations that change the
hardware state so that we can debug clock hardware performance
and operations. Four basic types of events are supported: on/off
events for enable, disable, prepare, unprepare that only record
an event and a clock name, rate changing events for
clk_set_{min_,max_}rate{_range}(), phase changing events for
clk_set_phase() and parent changing events for clk_set_parent().
Cc: Steven Rostedt <[email protected]>
Signed-off-by: Stephen Boyd <[email protected]>
---
Changes since v1:
* Fixed missing trace_*_complete() tracepoints
Note this is based on another patch titled "clk: Missing
set_phase op is an error"
drivers/clk/clk.c | 56 ++++++++++---
include/trace/events/clk.h | 198 +++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 244 insertions(+), 10 deletions(-)
create mode 100644 include/trace/events/clk.h
diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c
index 670c8c86ce9f..3ffadce9a0c2 100644
--- a/drivers/clk/clk.c
+++ b/drivers/clk/clk.c
@@ -22,6 +22,9 @@
#include <linux/init.h>
#include <linux/sched.h>
+#define CREATE_TRACE_POINTS
+#include <trace/events/clk.h>
+
#include "clk.h"
static DEFINE_SPINLOCK(enable_lock);
@@ -448,10 +451,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);
}
}
@@ -478,10 +483,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:
@@ -861,9 +868,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);
}
@@ -901,12 +911,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;
}
}
@@ -953,9 +967,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);
}
@@ -1008,12 +1026,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;
}
}
@@ -1438,10 +1460,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);
@@ -1665,6 +1691,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;
@@ -1675,12 +1702,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)
@@ -2083,9 +2115,13 @@ int clk_set_phase(struct clk *clk, int degrees)
clk_prepare_lock();
+ trace_clk_set_phase(clk->core, 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;
diff --git a/include/trace/events/clk.h b/include/trace/events/clk.h
new file mode 100644
index 000000000000..758607226bfd
--- /dev/null
+++ b/include/trace/events/clk.h
@@ -0,0 +1,198 @@
+/*
+ * Copyright (c) 2014-2015, The Linux Foundation. All rights reserved.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#undef TRACE_SYSTEM
+#define TRACE_SYSTEM clk
+
+#if !defined(_TRACE_CLK_H) || defined(TRACE_HEADER_MULTI_READ)
+#define _TRACE_CLK_H
+
+#include <linux/tracepoint.h>
+
+struct clk_core;
+
+DECLARE_EVENT_CLASS(clk,
+
+ TP_PROTO(struct clk_core *core),
+
+ TP_ARGS(core),
+
+ TP_STRUCT__entry(
+ __string( name, core->name )
+ ),
+
+ TP_fast_assign(
+ __assign_str(name, core->name);
+ ),
+
+ TP_printk("%s", __get_str(name))
+);
+
+DEFINE_EVENT(clk, clk_enable,
+
+ TP_PROTO(struct clk_core *core),
+
+ TP_ARGS(core)
+);
+
+DEFINE_EVENT(clk, clk_enable_complete,
+
+ TP_PROTO(struct clk_core *core),
+
+ TP_ARGS(core)
+);
+
+DEFINE_EVENT(clk, clk_disable,
+
+ TP_PROTO(struct clk_core *core),
+
+ TP_ARGS(core)
+);
+
+DEFINE_EVENT(clk, clk_disable_complete,
+
+ TP_PROTO(struct clk_core *core),
+
+ TP_ARGS(core)
+);
+
+DEFINE_EVENT(clk, clk_prepare,
+
+ TP_PROTO(struct clk_core *core),
+
+ TP_ARGS(core)
+);
+
+DEFINE_EVENT(clk, clk_prepare_complete,
+
+ TP_PROTO(struct clk_core *core),
+
+ TP_ARGS(core)
+);
+
+DEFINE_EVENT(clk, clk_unprepare,
+
+ TP_PROTO(struct clk_core *core),
+
+ TP_ARGS(core)
+);
+
+DEFINE_EVENT(clk, clk_unprepare_complete,
+
+ TP_PROTO(struct clk_core *core),
+
+ TP_ARGS(core)
+);
+
+DECLARE_EVENT_CLASS(clk_rate,
+
+ TP_PROTO(struct clk_core *core, unsigned long rate),
+
+ TP_ARGS(core, rate),
+
+ TP_STRUCT__entry(
+ __string( name, core->name )
+ __field(unsigned long, rate )
+ ),
+
+ TP_fast_assign(
+ __assign_str(name, core->name);
+ __entry->rate = rate;
+ ),
+
+ TP_printk("%s %lu", __get_str(name), (unsigned long)__entry->rate)
+);
+
+DEFINE_EVENT(clk_rate, clk_set_rate,
+
+ TP_PROTO(struct clk_core *core, unsigned long rate),
+
+ TP_ARGS(core, rate)
+);
+
+DEFINE_EVENT(clk_rate, clk_set_rate_complete,
+
+ TP_PROTO(struct clk_core *core, unsigned long rate),
+
+ TP_ARGS(core, rate)
+);
+
+DECLARE_EVENT_CLASS(clk_parent,
+
+ TP_PROTO(struct clk_core *core, struct clk_core *parent),
+
+ TP_ARGS(core, parent),
+
+ TP_STRUCT__entry(
+ __string( name, core->name )
+ __string( pname, parent->name )
+ ),
+
+ TP_fast_assign(
+ __assign_str(name, core->name);
+ __assign_str(pname, parent->name);
+ ),
+
+ TP_printk("%s %s", __get_str(name), __get_str(pname))
+);
+
+DEFINE_EVENT(clk_parent, clk_set_parent,
+
+ TP_PROTO(struct clk_core *core, struct clk_core *parent),
+
+ TP_ARGS(core, parent)
+);
+
+DEFINE_EVENT(clk_parent, clk_set_parent_complete,
+
+ TP_PROTO(struct clk_core *core, struct clk_core *parent),
+
+ TP_ARGS(core, parent)
+);
+
+DECLARE_EVENT_CLASS(clk_phase,
+
+ TP_PROTO(struct clk_core *core, int phase),
+
+ TP_ARGS(core, phase),
+
+ TP_STRUCT__entry(
+ __string( name, core->name )
+ __field( int, phase )
+ ),
+
+ TP_fast_assign(
+ __assign_str(name, core->name);
+ __entry->phase = phase;
+ ),
+
+ TP_printk("%s %d", __get_str(name), (int)__entry->phase)
+);
+
+DEFINE_EVENT(clk_phase, clk_set_phase,
+
+ TP_PROTO(struct clk_core *core, int phase),
+
+ TP_ARGS(core, phase)
+);
+
+DEFINE_EVENT(clk_phase, clk_set_phase_complete,
+
+ TP_PROTO(struct clk_core *core, int phase),
+
+ TP_ARGS(core, phase)
+);
+
+#endif /* _TRACE_CLK_H */
+
+/* This part must be outside protection */
+#include <trace/define_trace.h>
--
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project
Quoting Stephen Boyd (2015-02-02 14:37:41)
> It's useful to have tracepoints around operations that change the
> hardware state so that we can debug clock hardware performance
> and operations. Four basic types of events are supported: on/off
> events for enable, disable, prepare, unprepare that only record
> an event and a clock name, rate changing events for
> clk_set_{min_,max_}rate{_range}(), phase changing events for
> clk_set_phase() and parent changing events for clk_set_parent().
>
> Cc: Steven Rostedt <[email protected]>
> Signed-off-by: Stephen Boyd <[email protected]>
Applied to clk-next.
Thanks,
Mike
> ---
>
> Changes since v1:
> * Fixed missing trace_*_complete() tracepoints
>
> Note this is based on another patch titled "clk: Missing
> set_phase op is an error"
>
> drivers/clk/clk.c | 56 ++++++++++---
> include/trace/events/clk.h | 198 +++++++++++++++++++++++++++++++++++++++++++++
> 2 files changed, 244 insertions(+), 10 deletions(-)
> create mode 100644 include/trace/events/clk.h
>
> diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c
> index 670c8c86ce9f..3ffadce9a0c2 100644
> --- a/drivers/clk/clk.c
> +++ b/drivers/clk/clk.c
> @@ -22,6 +22,9 @@
> #include <linux/init.h>
> #include <linux/sched.h>
>
> +#define CREATE_TRACE_POINTS
> +#include <trace/events/clk.h>
> +
> #include "clk.h"
>
> static DEFINE_SPINLOCK(enable_lock);
> @@ -448,10 +451,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);
> }
> }
>
> @@ -478,10 +483,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:
> @@ -861,9 +868,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);
> }
>
> @@ -901,12 +911,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;
> }
> }
>
> @@ -953,9 +967,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);
> }
>
> @@ -1008,12 +1026,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;
> }
> }
>
> @@ -1438,10 +1460,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);
> @@ -1665,6 +1691,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;
> @@ -1675,12 +1702,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)
> @@ -2083,9 +2115,13 @@ int clk_set_phase(struct clk *clk, int degrees)
>
> clk_prepare_lock();
>
> + trace_clk_set_phase(clk->core, 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;
>
> diff --git a/include/trace/events/clk.h b/include/trace/events/clk.h
> new file mode 100644
> index 000000000000..758607226bfd
> --- /dev/null
> +++ b/include/trace/events/clk.h
> @@ -0,0 +1,198 @@
> +/*
> + * Copyright (c) 2014-2015, The Linux Foundation. All rights reserved.
> + *
> + * This software is licensed under the terms of the GNU General Public
> + * License version 2, as published by the Free Software Foundation, and
> + * may be copied, distributed, and modified under those terms.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License for more details.
> + */
> +#undef TRACE_SYSTEM
> +#define TRACE_SYSTEM clk
> +
> +#if !defined(_TRACE_CLK_H) || defined(TRACE_HEADER_MULTI_READ)
> +#define _TRACE_CLK_H
> +
> +#include <linux/tracepoint.h>
> +
> +struct clk_core;
> +
> +DECLARE_EVENT_CLASS(clk,
> +
> + TP_PROTO(struct clk_core *core),
> +
> + TP_ARGS(core),
> +
> + TP_STRUCT__entry(
> + __string( name, core->name )
> + ),
> +
> + TP_fast_assign(
> + __assign_str(name, core->name);
> + ),
> +
> + TP_printk("%s", __get_str(name))
> +);
> +
> +DEFINE_EVENT(clk, clk_enable,
> +
> + TP_PROTO(struct clk_core *core),
> +
> + TP_ARGS(core)
> +);
> +
> +DEFINE_EVENT(clk, clk_enable_complete,
> +
> + TP_PROTO(struct clk_core *core),
> +
> + TP_ARGS(core)
> +);
> +
> +DEFINE_EVENT(clk, clk_disable,
> +
> + TP_PROTO(struct clk_core *core),
> +
> + TP_ARGS(core)
> +);
> +
> +DEFINE_EVENT(clk, clk_disable_complete,
> +
> + TP_PROTO(struct clk_core *core),
> +
> + TP_ARGS(core)
> +);
> +
> +DEFINE_EVENT(clk, clk_prepare,
> +
> + TP_PROTO(struct clk_core *core),
> +
> + TP_ARGS(core)
> +);
> +
> +DEFINE_EVENT(clk, clk_prepare_complete,
> +
> + TP_PROTO(struct clk_core *core),
> +
> + TP_ARGS(core)
> +);
> +
> +DEFINE_EVENT(clk, clk_unprepare,
> +
> + TP_PROTO(struct clk_core *core),
> +
> + TP_ARGS(core)
> +);
> +
> +DEFINE_EVENT(clk, clk_unprepare_complete,
> +
> + TP_PROTO(struct clk_core *core),
> +
> + TP_ARGS(core)
> +);
> +
> +DECLARE_EVENT_CLASS(clk_rate,
> +
> + TP_PROTO(struct clk_core *core, unsigned long rate),
> +
> + TP_ARGS(core, rate),
> +
> + TP_STRUCT__entry(
> + __string( name, core->name )
> + __field(unsigned long, rate )
> + ),
> +
> + TP_fast_assign(
> + __assign_str(name, core->name);
> + __entry->rate = rate;
> + ),
> +
> + TP_printk("%s %lu", __get_str(name), (unsigned long)__entry->rate)
> +);
> +
> +DEFINE_EVENT(clk_rate, clk_set_rate,
> +
> + TP_PROTO(struct clk_core *core, unsigned long rate),
> +
> + TP_ARGS(core, rate)
> +);
> +
> +DEFINE_EVENT(clk_rate, clk_set_rate_complete,
> +
> + TP_PROTO(struct clk_core *core, unsigned long rate),
> +
> + TP_ARGS(core, rate)
> +);
> +
> +DECLARE_EVENT_CLASS(clk_parent,
> +
> + TP_PROTO(struct clk_core *core, struct clk_core *parent),
> +
> + TP_ARGS(core, parent),
> +
> + TP_STRUCT__entry(
> + __string( name, core->name )
> + __string( pname, parent->name )
> + ),
> +
> + TP_fast_assign(
> + __assign_str(name, core->name);
> + __assign_str(pname, parent->name);
> + ),
> +
> + TP_printk("%s %s", __get_str(name), __get_str(pname))
> +);
> +
> +DEFINE_EVENT(clk_parent, clk_set_parent,
> +
> + TP_PROTO(struct clk_core *core, struct clk_core *parent),
> +
> + TP_ARGS(core, parent)
> +);
> +
> +DEFINE_EVENT(clk_parent, clk_set_parent_complete,
> +
> + TP_PROTO(struct clk_core *core, struct clk_core *parent),
> +
> + TP_ARGS(core, parent)
> +);
> +
> +DECLARE_EVENT_CLASS(clk_phase,
> +
> + TP_PROTO(struct clk_core *core, int phase),
> +
> + TP_ARGS(core, phase),
> +
> + TP_STRUCT__entry(
> + __string( name, core->name )
> + __field( int, phase )
> + ),
> +
> + TP_fast_assign(
> + __assign_str(name, core->name);
> + __entry->phase = phase;
> + ),
> +
> + TP_printk("%s %d", __get_str(name), (int)__entry->phase)
> +);
> +
> +DEFINE_EVENT(clk_phase, clk_set_phase,
> +
> + TP_PROTO(struct clk_core *core, int phase),
> +
> + TP_ARGS(core, phase)
> +);
> +
> +DEFINE_EVENT(clk_phase, clk_set_phase_complete,
> +
> + TP_PROTO(struct clk_core *core, int phase),
> +
> + TP_ARGS(core, phase)
> +);
> +
> +#endif /* _TRACE_CLK_H */
> +
> +/* This part must be outside protection */
> +#include <trace/define_trace.h>
> --
> The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
> a Linux Foundation Collaborative Project
>