1 // SPDX-License-Identifier: GPL-2.0-only
3 * Copyright (C) Maxime Coquelin 2015
4 * Author: Maxime Coquelin <mcoquelin.stm32@gmail.com>
6 * Inspired by time-efm32.c from Uwe Kleine-Koenig
9 #include <linux/kernel.h>
10 #include <linux/clocksource.h>
11 #include <linux/clockchips.h>
12 #include <linux/delay.h>
13 #include <linux/irq.h>
14 #include <linux/interrupt.h>
16 #include <linux/of_address.h>
17 #include <linux/of_irq.h>
18 #include <linux/clk.h>
19 #include <linux/reset.h>
20 #include <linux/sched_clock.h>
21 #include <linux/slab.h>
34 #define TIM_CR1_CEN BIT(0)
35 #define TIM_CR1_UDIS BIT(1)
36 #define TIM_CR1_OPM BIT(3)
37 #define TIM_CR1_ARPE BIT(7)
39 #define TIM_DIER_UIE BIT(0)
40 #define TIM_DIER_CC1IE BIT(1)
42 #define TIM_SR_UIF BIT(0)
44 #define TIM_EGR_UG BIT(0)
46 #define TIM_PSC_MAX USHRT_MAX
47 #define TIM_PSC_CLKRATE 10000
49 struct stm32_timer_private
{
54 * stm32_timer_of_bits_set - set accessor helper
55 * @to: a timer_of structure pointer
56 * @bits: the number of bits (16 or 32)
58 * Accessor helper to set the number of bits in the timer-of private
62 static void stm32_timer_of_bits_set(struct timer_of
*to
, int bits
)
64 struct stm32_timer_private
*pd
= to
->private_data
;
70 * stm32_timer_of_bits_get - get accessor helper
71 * @to: a timer_of structure pointer
73 * Accessor helper to get the number of bits in the timer-of private
76 * Returns an integer corresponding to the number of bits.
78 static int stm32_timer_of_bits_get(struct timer_of
*to
)
80 struct stm32_timer_private
*pd
= to
->private_data
;
85 static void __iomem
*stm32_timer_cnt __read_mostly
;
87 static u64 notrace
stm32_read_sched_clock(void)
89 return readl_relaxed(stm32_timer_cnt
);
92 static struct delay_timer stm32_timer_delay
;
94 static unsigned long stm32_read_delay(void)
96 return readl_relaxed(stm32_timer_cnt
);
99 static void stm32_clock_event_disable(struct timer_of
*to
)
101 writel_relaxed(0, timer_of_base(to
) + TIM_DIER
);
105 * stm32_timer_start - Start the counter without event
106 * @to: a timer_of structure pointer
108 * Start the timer in order to have the counter reset and start
109 * incrementing but disable interrupt event when there is a counter
110 * overflow. By default, the counter direction is used as upcounter.
112 static void stm32_timer_start(struct timer_of
*to
)
114 writel_relaxed(TIM_CR1_UDIS
| TIM_CR1_CEN
, timer_of_base(to
) + TIM_CR1
);
117 static int stm32_clock_event_shutdown(struct clock_event_device
*clkevt
)
119 struct timer_of
*to
= to_timer_of(clkevt
);
121 stm32_clock_event_disable(to
);
126 static int stm32_clock_event_set_next_event(unsigned long evt
,
127 struct clock_event_device
*clkevt
)
129 struct timer_of
*to
= to_timer_of(clkevt
);
130 unsigned long now
, next
;
132 next
= readl_relaxed(timer_of_base(to
) + TIM_CNT
) + evt
;
133 writel_relaxed(next
, timer_of_base(to
) + TIM_CCR1
);
134 now
= readl_relaxed(timer_of_base(to
) + TIM_CNT
);
136 if ((next
- now
) > evt
)
139 writel_relaxed(TIM_DIER_CC1IE
, timer_of_base(to
) + TIM_DIER
);
144 static int stm32_clock_event_set_periodic(struct clock_event_device
*clkevt
)
146 struct timer_of
*to
= to_timer_of(clkevt
);
148 stm32_timer_start(to
);
150 return stm32_clock_event_set_next_event(timer_of_period(to
), clkevt
);
153 static int stm32_clock_event_set_oneshot(struct clock_event_device
*clkevt
)
155 struct timer_of
*to
= to_timer_of(clkevt
);
157 stm32_timer_start(to
);
162 static irqreturn_t
stm32_clock_event_handler(int irq
, void *dev_id
)
164 struct clock_event_device
*clkevt
= (struct clock_event_device
*)dev_id
;
165 struct timer_of
*to
= to_timer_of(clkevt
);
167 writel_relaxed(0, timer_of_base(to
) + TIM_SR
);
169 if (clockevent_state_periodic(clkevt
))
170 stm32_clock_event_set_periodic(clkevt
);
172 stm32_clock_event_shutdown(clkevt
);
174 clkevt
->event_handler(clkevt
);
180 * stm32_timer_width - Sort out the timer width (32/16)
181 * @to: a pointer to a timer-of structure
183 * Write the 32-bit max value and read/return the result. If the timer
184 * is 32 bits wide, the result will be UINT_MAX, otherwise it will
185 * be truncated by the 16-bit register to USHRT_MAX.
188 static void __init
stm32_timer_set_width(struct timer_of
*to
)
192 writel_relaxed(UINT_MAX
, timer_of_base(to
) + TIM_ARR
);
194 width
= readl_relaxed(timer_of_base(to
) + TIM_ARR
);
196 stm32_timer_of_bits_set(to
, width
== UINT_MAX
? 32 : 16);
200 * stm32_timer_set_prescaler - Compute and set the prescaler register
201 * @to: a pointer to a timer-of structure
203 * Depending on the timer width, compute the prescaler to always
204 * target a 10MHz timer rate for 16 bits. 32-bit timers are
205 * considered precise and long enough to not use the prescaler.
207 static void __init
stm32_timer_set_prescaler(struct timer_of
*to
)
211 if (stm32_timer_of_bits_get(to
) != 32) {
212 prescaler
= DIV_ROUND_CLOSEST(timer_of_rate(to
),
215 * The prescaler register is an u16, the variable
216 * can't be greater than TIM_PSC_MAX, let's cap it in
219 prescaler
= prescaler
< TIM_PSC_MAX
? prescaler
: TIM_PSC_MAX
;
222 writel_relaxed(prescaler
- 1, timer_of_base(to
) + TIM_PSC
);
223 writel_relaxed(TIM_EGR_UG
, timer_of_base(to
) + TIM_EGR
);
224 writel_relaxed(0, timer_of_base(to
) + TIM_SR
);
226 /* Adjust rate and period given the prescaler value */
227 to
->of_clk
.rate
= DIV_ROUND_CLOSEST(to
->of_clk
.rate
, prescaler
);
228 to
->of_clk
.period
= DIV_ROUND_UP(to
->of_clk
.rate
, HZ
);
231 static int __init
stm32_clocksource_init(struct timer_of
*to
)
233 u32 bits
= stm32_timer_of_bits_get(to
);
234 const char *name
= to
->np
->full_name
;
237 * This driver allows to register several timers and relies on
238 * the generic time framework to select the right one.
239 * However, nothing allows to do the same for the
240 * sched_clock. We are not interested in a sched_clock for the
241 * 16-bit timers but only for the 32-bit one, so if no 32-bit
242 * timer is registered yet, we select this 32-bit timer as a
245 if (bits
== 32 && !stm32_timer_cnt
) {
248 * Start immediately the counter as we will be using
251 stm32_timer_start(to
);
253 stm32_timer_cnt
= timer_of_base(to
) + TIM_CNT
;
254 sched_clock_register(stm32_read_sched_clock
, bits
, timer_of_rate(to
));
255 pr_info("%s: STM32 sched_clock registered\n", name
);
257 stm32_timer_delay
.read_current_timer
= stm32_read_delay
;
258 stm32_timer_delay
.freq
= timer_of_rate(to
);
259 register_current_timer_delay(&stm32_timer_delay
);
260 pr_info("%s: STM32 delay timer registered\n", name
);
263 return clocksource_mmio_init(timer_of_base(to
) + TIM_CNT
, name
,
264 timer_of_rate(to
), bits
== 32 ? 250 : 100,
265 bits
, clocksource_mmio_readl_up
);
268 static void __init
stm32_clockevent_init(struct timer_of
*to
)
270 u32 bits
= stm32_timer_of_bits_get(to
);
272 to
->clkevt
.name
= to
->np
->full_name
;
273 to
->clkevt
.features
= CLOCK_EVT_FEAT_PERIODIC
| CLOCK_EVT_FEAT_ONESHOT
;
274 to
->clkevt
.set_state_shutdown
= stm32_clock_event_shutdown
;
275 to
->clkevt
.set_state_periodic
= stm32_clock_event_set_periodic
;
276 to
->clkevt
.set_state_oneshot
= stm32_clock_event_set_oneshot
;
277 to
->clkevt
.tick_resume
= stm32_clock_event_shutdown
;
278 to
->clkevt
.set_next_event
= stm32_clock_event_set_next_event
;
279 to
->clkevt
.rating
= bits
== 32 ? 250 : 100;
281 clockevents_config_and_register(&to
->clkevt
, timer_of_rate(to
), 0x1,
284 pr_info("%pOF: STM32 clockevent driver initialized (%d bits)\n",
288 static int __init
stm32_timer_init(struct device_node
*node
)
290 struct reset_control
*rstc
;
294 to
= kzalloc(sizeof(*to
), GFP_KERNEL
);
298 to
->flags
= TIMER_OF_IRQ
| TIMER_OF_CLOCK
| TIMER_OF_BASE
;
299 to
->of_irq
.handler
= stm32_clock_event_handler
;
301 ret
= timer_of_init(node
, to
);
305 to
->private_data
= kzalloc(sizeof(struct stm32_timer_private
),
307 if (!to
->private_data
) {
312 rstc
= of_reset_control_get(node
, NULL
);
314 reset_control_assert(rstc
);
315 reset_control_deassert(rstc
);
318 stm32_timer_set_width(to
);
320 stm32_timer_set_prescaler(to
);
322 ret
= stm32_clocksource_init(to
);
326 stm32_clockevent_init(to
);
330 timer_of_cleanup(to
);
336 TIMER_OF_DECLARE(stm32
, "st,stm32-timer", stm32_timer_init
);