aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/clocksource/timer-stm32.c
diff options
context:
space:
mode:
authorBenjamin Gaignard <benjamin.gaignard@st.com>2018-01-08 08:28:55 -0500
committerIngo Molnar <mingo@kernel.org>2018-01-08 11:57:26 -0500
commit8e82df381b676ae5f6c93ab4a75f56d8f61babc4 (patch)
tree371c81f9c8cbde911b4aa678d78d513308054454 /drivers/clocksource/timer-stm32.c
parent4744daa10dcd3a1470fbeba4945fbf44dcb1b0d1 (diff)
clocksource/drivers/stm32: Add oneshot mode
The stm32 timer block is able to have a counter and a comparator. Instead of using the auto-reload register for periodic events, we switch to oneshot mode by using the comparator register. The timer is able to generate an interrupt when the counter overflows but we don't want that as this counter will be use as a clocksource in the next patches. So it is disabled by the UDIS bit of the control register. Tested-by: Benjamin Gaignard <benjamin.gaignard@st.com> Signed-off-by: Benjamin Gaignard <benjamin.gaignard@st.com> Signed-off-by: Daniel Lezcano <daniel.lezcano@linaro.org> Acked-by: Benjamin Gaignard <benjamin.gaignard@st.com> Cc: Alexandre Torgue <alexandre.torgue@st.com> Cc: Linus Torvalds <torvalds@linux-foundation.org> Cc: Maxime Coquelin <mcoquelin.stm32@gmail.com> Cc: Peter Zijlstra <peterz@infradead.org> Cc: Thomas Gleixner <tglx@linutronix.de> Link: http://lkml.kernel.org/r/1515418139-23276-16-git-send-email-daniel.lezcano@linaro.org Signed-off-by: Ingo Molnar <mingo@kernel.org>
Diffstat (limited to 'drivers/clocksource/timer-stm32.c')
-rw-r--r--drivers/clocksource/timer-stm32.c56
1 files changed, 44 insertions, 12 deletions
diff --git a/drivers/clocksource/timer-stm32.c b/drivers/clocksource/timer-stm32.c
index 928ac281f937..882037f1d8d6 100644
--- a/drivers/clocksource/timer-stm32.c
+++ b/drivers/clocksource/timer-stm32.c
@@ -24,14 +24,18 @@
24#define TIM_DIER 0x0c 24#define TIM_DIER 0x0c
25#define TIM_SR 0x10 25#define TIM_SR 0x10
26#define TIM_EGR 0x14 26#define TIM_EGR 0x14
27#define TIM_CNT 0x24
27#define TIM_PSC 0x28 28#define TIM_PSC 0x28
28#define TIM_ARR 0x2c 29#define TIM_ARR 0x2c
30#define TIM_CCR1 0x34
29 31
30#define TIM_CR1_CEN BIT(0) 32#define TIM_CR1_CEN BIT(0)
33#define TIM_CR1_UDIS BIT(1)
31#define TIM_CR1_OPM BIT(3) 34#define TIM_CR1_OPM BIT(3)
32#define TIM_CR1_ARPE BIT(7) 35#define TIM_CR1_ARPE BIT(7)
33 36
34#define TIM_DIER_UIE BIT(0) 37#define TIM_DIER_UIE BIT(0)
38#define TIM_DIER_CC1IE BIT(1)
35 39
36#define TIM_SR_UIF BIT(0) 40#define TIM_SR_UIF BIT(0)
37 41
@@ -40,33 +44,57 @@
40#define TIM_PSC_MAX USHRT_MAX 44#define TIM_PSC_MAX USHRT_MAX
41#define TIM_PSC_CLKRATE 10000 45#define TIM_PSC_CLKRATE 10000
42 46
47static void stm32_clock_event_disable(struct timer_of *to)
48{
49 writel_relaxed(0, timer_of_base(to) + TIM_DIER);
50}
51
52static void stm32_clock_event_enable(struct timer_of *to)
53{
54 writel_relaxed(TIM_CR1_UDIS | TIM_CR1_CEN, timer_of_base(to) + TIM_CR1);
55}
56
43static int stm32_clock_event_shutdown(struct clock_event_device *clkevt) 57static int stm32_clock_event_shutdown(struct clock_event_device *clkevt)
44{ 58{
45 struct timer_of *to = to_timer_of(clkevt); 59 struct timer_of *to = to_timer_of(clkevt);
46 60
47 writel_relaxed(0, timer_of_base(to) + TIM_CR1); 61 stm32_clock_event_disable(to);
48 62
49 return 0; 63 return 0;
50} 64}
51 65
52static int stm32_clock_event_set_periodic(struct clock_event_device *clkevt) 66static int stm32_clock_event_set_next_event(unsigned long evt,
67 struct clock_event_device *clkevt)
53{ 68{
54 struct timer_of *to = to_timer_of(clkevt); 69 struct timer_of *to = to_timer_of(clkevt);
70 unsigned long now, next;
71
72 next = readl_relaxed(timer_of_base(to) + TIM_CNT) + evt;
73 writel_relaxed(next, timer_of_base(to) + TIM_CCR1);
74 now = readl_relaxed(timer_of_base(to) + TIM_CNT);
75
76 if ((next - now) > evt)
77 return -ETIME;
55 78
56 writel_relaxed(timer_of_period(to), timer_of_base(to) + TIM_ARR); 79 writel_relaxed(TIM_DIER_CC1IE, timer_of_base(to) + TIM_DIER);
57 writel_relaxed(TIM_CR1_ARPE | TIM_CR1_CEN, timer_of_base(to) + TIM_CR1);
58 80
59 return 0; 81 return 0;
60} 82}
61 83
62static int stm32_clock_event_set_next_event(unsigned long evt, 84static int stm32_clock_event_set_periodic(struct clock_event_device *clkevt)
63 struct clock_event_device *clkevt) 85{
86 struct timer_of *to = to_timer_of(clkevt);
87
88 stm32_clock_event_enable(to);
89
90 return stm32_clock_event_set_next_event(timer_of_period(to), clkevt);
91}
92
93static int stm32_clock_event_set_oneshot(struct clock_event_device *clkevt)
64{ 94{
65 struct timer_of *to = to_timer_of(clkevt); 95 struct timer_of *to = to_timer_of(clkevt);
66 96
67 writel_relaxed(evt, timer_of_base(to) + TIM_ARR); 97 stm32_clock_event_enable(to);
68 writel_relaxed(TIM_CR1_ARPE | TIM_CR1_OPM | TIM_CR1_CEN,
69 timer_of_base(to) + TIM_CR1);
70 98
71 return 0; 99 return 0;
72} 100}
@@ -78,6 +106,11 @@ static irqreturn_t stm32_clock_event_handler(int irq, void *dev_id)
78 106
79 writel_relaxed(0, timer_of_base(to) + TIM_SR); 107 writel_relaxed(0, timer_of_base(to) + TIM_SR);
80 108
109 if (clockevent_state_periodic(clkevt))
110 stm32_clock_event_set_periodic(clkevt);
111 else
112 stm32_clock_event_shutdown(clkevt);
113
81 clkevt->event_handler(clkevt); 114 clkevt->event_handler(clkevt);
82 115
83 return IRQ_HANDLED; 116 return IRQ_HANDLED;
@@ -108,9 +141,10 @@ static void __init stm32_clockevent_init(struct timer_of *to)
108 141
109 to->clkevt.name = to->np->full_name; 142 to->clkevt.name = to->np->full_name;
110 to->clkevt.features = CLOCK_EVT_FEAT_PERIODIC; 143 to->clkevt.features = CLOCK_EVT_FEAT_PERIODIC;
144 to->clkevt.features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT;
111 to->clkevt.set_state_shutdown = stm32_clock_event_shutdown; 145 to->clkevt.set_state_shutdown = stm32_clock_event_shutdown;
112 to->clkevt.set_state_periodic = stm32_clock_event_set_periodic; 146 to->clkevt.set_state_periodic = stm32_clock_event_set_periodic;
113 to->clkevt.set_state_oneshot = stm32_clock_event_shutdown; 147 to->clkevt.set_state_oneshot = stm32_clock_event_set_oneshot;
114 to->clkevt.tick_resume = stm32_clock_event_shutdown; 148 to->clkevt.tick_resume = stm32_clock_event_shutdown;
115 to->clkevt.set_next_event = stm32_clock_event_set_next_event; 149 to->clkevt.set_next_event = stm32_clock_event_set_next_event;
116 150
@@ -129,12 +163,10 @@ static void __init stm32_clockevent_init(struct timer_of *to)
129 prescaler = prescaler < TIM_PSC_MAX ? prescaler : TIM_PSC_MAX; 163 prescaler = prescaler < TIM_PSC_MAX ? prescaler : TIM_PSC_MAX;
130 to->clkevt.rating = 100; 164 to->clkevt.rating = 100;
131 } 165 }
132 writel_relaxed(0, timer_of_base(to) + TIM_ARR);
133 166
134 writel_relaxed(prescaler - 1, timer_of_base(to) + TIM_PSC); 167 writel_relaxed(prescaler - 1, timer_of_base(to) + TIM_PSC);
135 writel_relaxed(TIM_EGR_UG, timer_of_base(to) + TIM_EGR); 168 writel_relaxed(TIM_EGR_UG, timer_of_base(to) + TIM_EGR);
136 writel_relaxed(0, timer_of_base(to) + TIM_SR); 169 writel_relaxed(0, timer_of_base(to) + TIM_SR);
137 writel_relaxed(TIM_DIER_UIE, timer_of_base(to) + TIM_DIER);
138 170
139 /* Adjust rate and period given the prescaler value */ 171 /* Adjust rate and period given the prescaler value */
140 to->of_clk.rate = DIV_ROUND_CLOSEST(to->of_clk.rate, prescaler); 172 to->of_clk.rate = DIV_ROUND_CLOSEST(to->of_clk.rate, prescaler);