Skip to content

Commit 63737d4

Browse files
committed
drivers: mfd: add support for stm32 mfd timers
Added multi functional device driver. Added support for new timer base (mfd stm32 timers), which aims to be more versatile. Signed-off-by: Max van Kessel <[email protected]>
1 parent 6a12a2d commit 63737d4

File tree

9 files changed

+447
-5
lines changed

9 files changed

+447
-5
lines changed

drivers/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ add_definitions(-D__ZEPHYR_SUPERVISOR__)
44

55
add_subdirectory(console)
66
add_subdirectory(interrupt_controller)
7+
add_subdirectory(mfd)
78

89
add_subdirectory_if_kconfig(adc)
910
add_subdirectory_if_kconfig(clock_control)

drivers/Kconfig

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,4 +95,6 @@ source "drivers/eeprom/Kconfig"
9595

9696
source "drivers/peci/Kconfig"
9797

98+
source "drivers/mfd/Kconfig"
99+
98100
endmenu

drivers/mfd/CMakeLists.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# SPDX-License-Identifier: Apache-2.0
2+
3+
zephyr_sources_ifdef(CONFIG_MFD_TIMER_STM32 mfd_timer_stm32.c)

drivers/mfd/Kconfig

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
# MFD configuration options
2+
3+
# Copyright (c) 2019 Max van Kessel
4+
# SPDX-License-Identifier: Apache-2.0
5+
6+
menu "Multifunction Device Drivers"
7+
8+
config MFD_TIMER_STM32
9+
bool "STM32 MCU timer driver"
10+
depends on SOC_FAMILY_STM32
11+
help
12+
Enable This option enables the base timer driver for STM32 family of
13+
processors. Say y if you wish to use timer interface on STM32
14+
MCU.
15+
16+
endmenu

drivers/mfd/mfd_timer_stm32.c

Lines changed: 273 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,273 @@
1+
/*
2+
* Copyright (c) 2019 Max van Kessel
3+
*
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
#include <errno.h>
8+
#include <soc.h>
9+
#include <device.h>
10+
#include <clock_control/stm32_clock_control.h>
11+
#include <mfd/mfd_timer_stm32.h>
12+
13+
#define DEV_CFG(dev) \
14+
((const struct mfd_timer_stm32_config * const) \
15+
(dev)->config->config_info)
16+
#define DEV_DATA(dev) \
17+
((struct mfd_timer_stm32_data * const)(dev)->driver_data)
18+
19+
struct mfd_timer_stm32_config {
20+
u32_t tim_base; /**< Timer base */
21+
struct stm32_pclken pclken; /**< subsystem driving this peripheral */
22+
23+
u8_t align_mode;
24+
u8_t dir;
25+
u8_t msm;
26+
u8_t slave_mode;
27+
u8_t slave_trig;
28+
u8_t master_trig;
29+
u32_t prescaler;
30+
};
31+
32+
enum {
33+
ALIGN_EDGE = 0,
34+
ALIGN_CENTER_1,
35+
ALIGN_CENTER_2,
36+
ALIGN_CENTER_3
37+
};
38+
39+
static inline void tim_stm32_get_clock(struct device *dev)
40+
{
41+
struct mfd_timer_stm32_data *data = DEV_DATA(dev);
42+
struct device *clk = device_get_binding(STM32_CLOCK_CONTROL_NAME);
43+
44+
__ASSERT_NO_MSG(clk);
45+
46+
data->clock = clk;
47+
}
48+
49+
static u32_t tim_stm32_get_rate(u32_t bus_clk,
50+
clock_control_subsys_t *sub_system)
51+
{
52+
struct stm32_pclken *pclken = (struct stm32_pclken *)(sub_system);
53+
u32_t tim_clk, apb_psc;
54+
55+
if (pclken->bus == STM32_CLOCK_BUS_APB1) {
56+
apb_psc = CONFIG_CLOCK_STM32_APB1_PRESCALER;
57+
}
58+
#if !defined(CONFIG_SOC_SERIES_STM32F0X) && !defined(CONFIG_SOC_SERIES_STM32G0X)
59+
else {
60+
apb_psc = CONFIG_CLOCK_STM32_APB2_PRESCALER;
61+
}
62+
#endif
63+
64+
/*
65+
* If the APB prescaler equals 1, the timer clock frequencies
66+
* are set to the same frequency as that of the APB domain.
67+
* Otherwise, they are set to twice (×2) the frequency of the
68+
* APB domain.
69+
*/
70+
if (apb_psc == 1U) {
71+
tim_clk = bus_clk;
72+
} else {
73+
tim_clk = bus_clk * 2U;
74+
}
75+
76+
return tim_clk;
77+
}
78+
79+
static int init(struct device *dev)
80+
{
81+
int err = -EIO;
82+
const struct mfd_timer_stm32_config *cfg = DEV_CFG(dev);
83+
struct mfd_timer_stm32_data *data = DEV_DATA(dev);
84+
85+
tim_stm32_get_clock(dev);
86+
87+
/* enable clock */
88+
if (clock_control_on(data->clock,
89+
(clock_control_subsys_t *)&cfg->pclken) == 0) {
90+
err = 0;
91+
}
92+
93+
if (err == 0) {
94+
TIM_TypeDef *tim = (TIM_TypeDef *)cfg->tim_base;
95+
u32_t mode = 0;
96+
97+
data->tim = tim;
98+
99+
/* TODO: create binding for me */
100+
LL_TIM_SetClockSource(tim, LL_TIM_CLOCKSOURCE_INTERNAL);
101+
102+
if (cfg->prescaler > 0) {
103+
LL_TIM_SetPrescaler(tim, cfg->prescaler - 1);
104+
} else {
105+
LL_TIM_SetPrescaler(tim, 0);
106+
}
107+
108+
/* TODO find a more consistent solution for all soc's */
109+
if (cfg->align_mode == ALIGN_EDGE) {
110+
mode = cfg->dir << TIM_CR1_DIR_Pos;
111+
} else {
112+
mode = cfg->align_mode << TIM_CR1_CMS_Pos;
113+
}
114+
115+
LL_TIM_SetCounterMode(tim, mode);
116+
117+
if (cfg->msm > 0) {
118+
/* Trigger input delayed to allow synchronization */
119+
LL_TIM_EnableMasterSlaveMode(tim);
120+
}
121+
122+
mode = cfg->slave_mode << TIM_SMCR_SMS_Pos;
123+
LL_TIM_SetSlaveMode(tim, mode);
124+
125+
if (cfg->slave_mode != 0) {
126+
mode = cfg->slave_trig << TIM_SMCR_TS_Pos;
127+
LL_TIM_SetTriggerInput(tim, mode);
128+
}
129+
130+
mode = cfg->master_trig << TIM_CR2_MMS_Pos;
131+
LL_TIM_SetTriggerOutput(tim, mode);
132+
133+
/* TODO: create binding for me ?*/
134+
LL_TIM_DisableARRPreload(tim);
135+
}
136+
137+
return err;
138+
}
139+
140+
static void enable(struct device *dev)
141+
{
142+
const struct mfd_timer_stm32_config *cfg = DEV_CFG(dev);
143+
144+
/* Timer is enabled by master timer if slave mode is set */
145+
if ((cfg->slave_mode << TIM_SMCR_SMS_Pos) != LL_TIM_SLAVEMODE_TRIGGER) {
146+
struct mfd_timer_stm32_data *data = DEV_DATA(dev);
147+
148+
LL_TIM_EnableCounter(data->tim);
149+
}
150+
}
151+
152+
static void disable(struct device *dev)
153+
{
154+
struct mfd_timer_stm32_data *data = DEV_DATA(dev);
155+
156+
LL_TIM_DisableCounter(data->tim);
157+
}
158+
159+
static int get_cycles_per_sec(struct device *dev, u64_t *cycles)
160+
{
161+
int err = -EINVAL;
162+
const struct mfd_timer_stm32_config *cfg = DEV_CFG(dev);
163+
struct mfd_timer_stm32_data *data = DEV_DATA(dev);
164+
u32_t bus_clk, tim_clk;
165+
166+
if (cycles != NULL) {
167+
/* Timer clock depends on APB prescaler */
168+
err = clock_control_get_rate(data->clock,
169+
(clock_control_subsys_t *)&cfg->pclken,
170+
&bus_clk);
171+
172+
if (err >= 0) {
173+
tim_clk = tim_stm32_get_rate(bus_clk,
174+
(clock_control_subsys_t *)&cfg->pclken);
175+
176+
*cycles = (u64_t) (tim_clk / (cfg->prescaler));
177+
178+
err = 0;
179+
}
180+
}
181+
return err;
182+
}
183+
184+
static const struct mfd_timer_stm32 api = {
185+
.enable = enable,
186+
.disable = disable,
187+
.get_cycles_per_sec = get_cycles_per_sec,
188+
};
189+
190+
#define TIMER_DEVICE_INIT(n) \
191+
static struct mfd_timer_stm32_data mfd_timer_stm32_dev_data_ ## n; \
192+
static const struct mfd_timer_stm32_config mfd_timer_stm32_dev_cfg_ ## n = { \
193+
.tim_base = DT_INST_## n ##_ST_STM32_TIMERS_BASE_ADDRESS, \
194+
.align_mode = DT_INST_## n ##_ST_STM32_TIMERS_ST_ALIGN_MODE_ENUM,\
195+
.dir = DT_INST_## n ##_ST_STM32_TIMERS_ST_COUNTER_DIR_ENUM, \
196+
.msm = DT_INST_## n ##_ST_STM32_TIMERS_ST_MASTER_SLAVE_MODE, \
197+
.slave_mode = DT_INST_## n ##_ST_STM32_TIMERS_ST_SLAVE_MODE, \
198+
.slave_trig = DT_INST_## n ##_ST_STM32_TIMERS_ST_SLAVE_TRIGGER_IN, \
199+
.master_trig = DT_INST_## n ##_ST_STM32_TIMERS_ST_MASTER_TRIGGER_OUT,\
200+
.prescaler = DT_INST_## n ##_ST_STM32_TIMERS_ST_PRESCALER, \
201+
.pclken = { \
202+
.bus = DT_INST_## n ##_ST_STM32_TIMERS_CLOCK_BUS, \
203+
.enr = DT_INST_## n ##_ST_STM32_TIMERS_CLOCK_BITS }, \
204+
}; \
205+
\
206+
DEVICE_AND_API_INIT(timer_stm32_ ## n, \
207+
DT_INST_## n ##_ST_STM32_TIMERS_LABEL, \
208+
&init, \
209+
&mfd_timer_stm32_dev_data_ ## n, \
210+
&mfd_timer_stm32_dev_cfg_ ## n, \
211+
POST_KERNEL, CONFIG_KERNEL_INIT_PRIORITY_DEVICE, \
212+
&api)
213+
214+
#ifdef DT_INST_0_ST_STM32_TIMERS
215+
TIMER_DEVICE_INIT(0);
216+
#endif
217+
218+
#ifdef DT_INST_1_ST_STM32_TIMERS
219+
TIMER_DEVICE_INIT(1);
220+
#endif
221+
222+
#ifdef DT_INST_2_ST_STM32_TIMERS
223+
TIMER_DEVICE_INIT(2);
224+
#endif
225+
226+
#ifdef DT_INST_3_ST_STM32_TIMERS
227+
TIMER_DEVICE_INIT(3);
228+
#endif
229+
230+
#ifdef DT_INST_4_ST_STM32_TIMERS
231+
TIMER_DEVICE_INIT(4);
232+
#endif
233+
234+
#ifdef DT_INST_6_ST_STM32_TIMERS
235+
TIMER_DEVICE_INIT(6);
236+
#endif
237+
238+
#ifdef DT_INST_7_ST_STM32_TIMERS
239+
TIMER_DEVICE_INIT(7);
240+
#endif
241+
242+
#ifdef DT_INST_8_ST_STM32_TIMERS
243+
TIMER_DEVICE_INIT(8);
244+
#endif
245+
246+
#ifdef DT_INST_9_ST_STM32_TIMERS
247+
TIMER_DEVICE_INIT(9);
248+
#endif
249+
250+
#ifdef DT_INST_10_ST_STM32_TIMERS
251+
TIMER_DEVICE_INIT(10);
252+
#endif
253+
254+
#ifdef DT_INST_11_ST_STM32_TIMERS
255+
TIMER_DEVICE_INIT(11);
256+
#endif
257+
258+
#ifdef DT_INST_12_ST_STM32_TIMERS
259+
TIMER_DEVICE_INIT(12);
260+
#endif
261+
262+
#ifdef DT_INST_13_ST_STM32_TIMERS
263+
TIMER_DEVICE_INIT(13);
264+
#endif
265+
266+
#ifdef DT_INST_14_ST_STM32_TIMERS
267+
TIMER_DEVICE_INIT(14);
268+
#endif
269+
270+
#ifdef DT_INST_15_ST_STM32_TIMERS
271+
TIMER_DEVICE_INIT(15);
272+
#endif
273+

drivers/mfd/mfd_timer_stm32.h

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
/*
2+
* Copyright (c) 2019 Max van Kessel
3+
*
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
#ifndef ZEPHYR_DRIVERS_MFD_MFD_TIMER_STM32_H_
8+
#define ZEPHYR_DRIVERS_MFD_MFD_TIMER_STM32_H_
9+
10+
#include <errno.h>
11+
#include <zephyr/types.h>
12+
#include <device.h>
13+
14+
#ifdef __cplusplus
15+
extern "C" {
16+
#endif
17+
18+
/**
19+
* @brief MFD timer data structure
20+
*/
21+
struct mfd_timer_stm32_data {
22+
TIM_TypeDef *tim; /**< Base address */
23+
struct device *clock; /**< Clock device */
24+
};
25+
26+
typedef void (*mfd_timer_stm32_enable_t)(struct device *dev);
27+
28+
typedef void (*mfd_timer_stm32_disable_t)(struct device *dev);
29+
30+
typedef int (*mfd_timer_stm32_get_cycles_per_sec_t)(struct device *dev,
31+
u64_t *cycles);
32+
struct mfd_timer_stm32 {
33+
mfd_timer_stm32_enable_t enable;
34+
mfd_timer_stm32_disable_t disable;
35+
mfd_timer_stm32_get_cycles_per_sec_t get_cycles_per_sec;
36+
};
37+
38+
/**
39+
* @brief Enable stm32 timer device
40+
* @param dev The device to enable
41+
*/
42+
static inline void mfd_timer_stm32_enable(struct device *dev)
43+
{
44+
const struct mfd_timer_stm32 *api = (const struct mfd_timer_stm32 *)
45+
dev->driver_api;
46+
return api->enable(dev);
47+
}
48+
49+
/**
50+
* @brief Disable stm32 timer device
51+
* @param dev The device to disable
52+
*/
53+
static inline void mfd_timer_stm32_disable(struct device *dev)
54+
{
55+
const struct mfd_timer_stm32 *api = (const struct mfd_timer_stm32 *)
56+
dev->driver_api;
57+
return api->disable(dev);
58+
}
59+
60+
/**
61+
* @brief Get the clock rate (cycles per second)
62+
* @param dev Pointer to timer device structure
63+
* @param cycles Pointer to the memory to store clock rate
64+
* (cycles per second)
65+
* @retval 0 for success
66+
* @retval negative errno code
67+
*/
68+
static inline int mfd_timer_stm32_get_cycles_per_sec(struct device *dev,
69+
u64_t *cycles)
70+
{
71+
const struct mfd_timer_stm32 *api = (const struct mfd_timer_stm32 *)
72+
dev->driver_api;
73+
return api->get_cycles_per_sec(dev, cycles);
74+
}
75+
76+
#ifdef __cplusplus
77+
}
78+
#endif
79+
80+
#endif /* ZEPHYR_DRIVERS_MFD_MFD_TIMER_STM32_H_ */

0 commit comments

Comments
 (0)