[tip:timers/core] clocksource/drivers/stm32: Factor out more of the clockevent code
From: tip-bot for Daniel Lezcano
Date: Mon Jan 08 2018 - 12:37:34 EST
Commit-ID: 3c84e75b1e5d8406e12b533d44f54ad84d6e3bd6
Gitweb: https://git.kernel.org/tip/3c84e75b1e5d8406e12b533d44f54ad84d6e3bd6
Author: Daniel Lezcano <daniel.lezcano@xxxxxxxxxx>
AuthorDate: Mon, 8 Jan 2018 14:28:56 +0100
Committer: Ingo Molnar <mingo@xxxxxxxxxx>
CommitDate: Mon, 8 Jan 2018 17:57:26 +0100
clocksource/drivers/stm32: Factor out more of the clockevent code
In order to prepare the clocksource code, let's factor out the clockevent
code, split the prescaler and timer width code into separate functions.
Tested-by: Benjamin Gaignard <benjamin.gaignard@xxxxxx>
Signed-off-by: Daniel Lezcano <daniel.lezcano@xxxxxxxxxx>
Acked-by: Benjamin Gaignard <benjamin.gaignard@xxxxxx>
Cc: Alexandre Torgue <alexandre.torgue@xxxxxx>
Cc: Linus Torvalds <torvalds@xxxxxxxxxxxxxxxxxxxx>
Cc: Maxime Coquelin <mcoquelin.stm32@xxxxxxxxx>
Cc: Peter Zijlstra <peterz@xxxxxxxxxxxxx>
Cc: Thomas Gleixner <tglx@xxxxxxxxxxxxx>
Link: http://lkml.kernel.org/r/1515418139-23276-17-git-send-email-daniel.lezcano@xxxxxxxxxx
[ Small edits. ]
Signed-off-by: Ingo Molnar <mingo@xxxxxxxxxx>
---
drivers/clocksource/timer-stm32.c | 107 +++++++++++++++++++++++++++++---------
1 file changed, 82 insertions(+), 25 deletions(-)
diff --git a/drivers/clocksource/timer-stm32.c b/drivers/clocksource/timer-stm32.c
index 882037f..0d37f1a 100644
--- a/drivers/clocksource/timer-stm32.c
+++ b/drivers/clocksource/timer-stm32.c
@@ -44,6 +44,42 @@
#define TIM_PSC_MAX USHRT_MAX
#define TIM_PSC_CLKRATE 10000
+struct stm32_timer_private {
+ int bits;
+};
+
+/**
+ * stm32_timer_of_bits_set - set accessor helper
+ * @to: a timer_of structure pointer
+ * @bits: the number of bits (16 or 32)
+ *
+ * Accessor helper to set the number of bits in the timer-of private
+ * structure.
+ *
+ */
+static void stm32_timer_of_bits_set(struct timer_of *to, int bits)
+{
+ struct stm32_timer_private *pd = to->private_data;
+
+ pd->bits = bits;
+}
+
+/**
+ * stm32_timer_of_bits_get - get accessor helper
+ * @to: a timer_of structure pointer
+ *
+ * Accessor helper to get the number of bits in the timer-of private
+ * structure.
+ *
+ * Returns an integer corresponding to the number of bits.
+ */
+static int stm32_timer_of_bits_get(struct timer_of *to)
+{
+ struct stm32_timer_private *pd = to->private_data;
+
+ return pd->bits;
+}
+
static void stm32_clock_event_disable(struct timer_of *to)
{
writel_relaxed(0, timer_of_base(to) + TIM_DIER);
@@ -124,35 +160,31 @@ static irqreturn_t stm32_clock_event_handler(int irq, void *dev_id)
* is 32 bits wide, the result will be UINT_MAX, otherwise it will
* be truncated by the 16-bit register to USHRT_MAX.
*
- * Returns UINT_MAX if the timer is 32 bits wide, USHRT_MAX if it is a
- * 16 bits wide.
*/
-static u32 __init stm32_timer_width(struct timer_of *to)
+static void __init stm32_timer_set_width(struct timer_of *to)
{
+ u32 width;
+
writel_relaxed(UINT_MAX, timer_of_base(to) + TIM_ARR);
- return readl_relaxed(timer_of_base(to) + TIM_ARR);
+ width = readl_relaxed(timer_of_base(to) + TIM_ARR);
+
+ stm32_timer_of_bits_set(to, width == UINT_MAX ? 32 : 16);
}
-static void __init stm32_clockevent_init(struct timer_of *to)
+/**
+ * stm32_timer_set_prescaler - Compute and set the prescaler register
+ * @to: a pointer to a timer-of structure
+ *
+ * Depending on the timer width, compute the prescaler to always
+ * target a 10MHz timer rate for 16 bits. 32-bit timers are
+ * considered precise and long enough to not use the prescaler.
+ */
+static void __init stm32_timer_set_prescaler(struct timer_of *to)
{
- u32 width = 0;
- int prescaler;
+ int prescaler = 1;
- to->clkevt.name = to->np->full_name;
- to->clkevt.features = CLOCK_EVT_FEAT_PERIODIC;
- to->clkevt.features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT;
- to->clkevt.set_state_shutdown = stm32_clock_event_shutdown;
- to->clkevt.set_state_periodic = stm32_clock_event_set_periodic;
- to->clkevt.set_state_oneshot = stm32_clock_event_set_oneshot;
- to->clkevt.tick_resume = stm32_clock_event_shutdown;
- to->clkevt.set_next_event = stm32_clock_event_set_next_event;
-
- width = stm32_timer_width(to);
- if (width == UINT_MAX) {
- prescaler = 1;
- to->clkevt.rating = 250;
- } else {
+ if (stm32_timer_of_bits_get(to) != 32) {
prescaler = DIV_ROUND_CLOSEST(timer_of_rate(to),
TIM_PSC_CLKRATE);
/*
@@ -161,7 +193,6 @@ static void __init stm32_clockevent_init(struct timer_of *to)
* this case.
*/
prescaler = prescaler < TIM_PSC_MAX ? prescaler : TIM_PSC_MAX;
- to->clkevt.rating = 100;
}
writel_relaxed(prescaler - 1, timer_of_base(to) + TIM_PSC);
@@ -171,12 +202,26 @@ static void __init stm32_clockevent_init(struct timer_of *to)
/* Adjust rate and period given the prescaler value */
to->of_clk.rate = DIV_ROUND_CLOSEST(to->of_clk.rate, prescaler);
to->of_clk.period = DIV_ROUND_UP(to->of_clk.rate, HZ);
+}
+
+static void __init stm32_clockevent_init(struct timer_of *to)
+{
+ u32 bits = stm32_timer_of_bits_get(to);
- clockevents_config_and_register(&to->clkevt,
- timer_of_rate(to), 0x1, width);
+ to->clkevt.name = to->np->full_name;
+ to->clkevt.features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT;
+ to->clkevt.set_state_shutdown = stm32_clock_event_shutdown;
+ to->clkevt.set_state_periodic = stm32_clock_event_set_periodic;
+ to->clkevt.set_state_oneshot = stm32_clock_event_set_oneshot;
+ to->clkevt.tick_resume = stm32_clock_event_shutdown;
+ to->clkevt.set_next_event = stm32_clock_event_set_next_event;
+ to->clkevt.rating = bits == 32 ? 250 : 100;
+
+ clockevents_config_and_register(&to->clkevt, timer_of_rate(to), 0x1,
+ (1 << bits) - 1);
pr_info("%pOF: STM32 clockevent driver initialized (%d bits)\n",
- to->np, width == UINT_MAX ? 32 : 16);
+ to->np, bits);
}
static int __init stm32_timer_init(struct device_node *node)
@@ -196,14 +241,26 @@ static int __init stm32_timer_init(struct device_node *node)
if (ret)
goto err;
+ to->private_data = kzalloc(sizeof(struct stm32_timer_private),
+ GFP_KERNEL);
+ if (!to->private_data)
+ goto deinit;
+
rstc = of_reset_control_get(node, NULL);
if (!IS_ERR(rstc)) {
reset_control_assert(rstc);
reset_control_deassert(rstc);
}
+ stm32_timer_set_width(to);
+
+ stm32_timer_set_prescaler(to);
+
stm32_clockevent_init(to);
return 0;
+
+deinit:
+ timer_of_cleanup(to);
err:
kfree(to);
return ret;