Skip to content

Commit 2b863dc

Browse files
drivers: can: Implement stm32fd driver
This driver is the SoC specific implementation of the Bosch M_CAN IP. Signed-off-by: Alexander Wachter <[email protected]>
1 parent 102b204 commit 2b863dc

File tree

4 files changed

+338
-21
lines changed

4 files changed

+338
-21
lines changed

drivers/can/Kconfig.stm32fd

Lines changed: 20 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,17 @@
11
# STM32 CAN configuration options
2-
if CAN_MCAN && SOC_SERIES_STM32G4X
2+
3+
# Copyright (c) 2020 Alexander Wachter
4+
# SPDX-License-Identifier: Apache-2.0
5+
6+
DT_COMPAT_STM32_FDCAN := st,stm32-fdcan
37

48
config CAN_STM32FD
5-
bool
6-
default y
9+
bool "STM32 FDCAN driver"
10+
default $(dt_compat_enabled,$(DT_COMPAT_STM32_FDCAN))
11+
select CAN_MCAN
12+
select USE_STM32_LL_RCC
13+
14+
if CAN_STM32FD
715

816
config CAN_MAX_STD_ID_FILTER
917
int "Maximum number of std ID filters"
@@ -21,15 +29,14 @@ config CAN_MAX_EXT_ID_FILTER
2129
Defines the maximum number of filters with extended ID (29-bit)
2230
that can be attached.
2331

24-
config CAN_CKDIV
25-
int "CKDIV register value"
26-
range 0 15
27-
default 0
32+
config CAN_STM32_CLOCK_DIVISOR
33+
int "CAN clock divisor"
34+
range 1 30
35+
default 1
2836
help
29-
This value is written to the CKDIV register.
30-
The APB clock is divided according to this value before it is feed to
31-
CAN core. Note that the the divider affects all CAN controllers.
32-
The values of the register are multiplied by two and zero corresponds
33-
to one. The value six, for example results in a clock divided by 12.
37+
The APB clock is divided by this value (stored in CKDIV register)
38+
before it is fed to the CAN core.
39+
Note that the the divisor affects all CAN controllers.
40+
Allowed values: 1 or 2 * n, where n <= 15.
3441

35-
endif #CAN_MCAN
42+
endif #CAN_STM32FD

drivers/can/can_stm32fd.c

Lines changed: 273 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,20 +5,285 @@
55
*/
66

77
#include <drivers/can.h>
8+
#include <kernel.h>
9+
#include <soc.h>
10+
#include <stm32_ll_rcc.h>
11+
#include "can_stm32fd.h"
12+
#include <pinmux/stm32/pinmux_stm32.h>
813

9-
u32_t can_mcan_get_core_clock(struct device *dev)
10-
{
11-
u32_t core_clock = LL_RCC_GetFDCANClockFreq(LL_RCC_FDCAN_CLKSOURCE);
12-
u32_t dbrp, nbrp;
14+
#include <logging/log.h>
15+
LOG_MODULE_DECLARE(can_driver, CONFIG_CAN_LOG_LEVEL);
1316

14-
#if CONFIG_CAN_CKDIV != 0
15-
core_clock /= CONFIG_CAN_CKDIV * 2;
17+
#if CONFIG_CAN_STM32_CLOCK_DIVISOR != 1 && CONFIG_CAN_STM32_CLOCK_DIVISOR & 0x01
18+
#error CAN_STM32_CLOCK_DIVISOR invalid.\
19+
Allowed values are 1 or 2 * n, where n <= 15
1620
#endif
1721

18-
__weak void can_mcan_clock_enable()
22+
#define DT_DRV_COMPAT st_stm32_fdcan
23+
24+
int can_stm32fd_get_core_clock(const struct device *dev, uint32_t *rate)
25+
{
26+
ARG_UNUSED(dev);
27+
int rate_tmp;
28+
29+
rate_tmp = LL_RCC_GetFDCANClockFreq(LL_RCC_FDCAN_CLKSOURCE);
30+
31+
if (rate_tmp == LL_RCC_PERIPH_FREQUENCY_NO) {
32+
LOG_ERR("Can't read core clock");
33+
return -EIO;
34+
}
35+
36+
*rate = rate_tmp / CONFIG_CAN_STM32_CLOCK_DIVISOR;
37+
38+
return 0;
39+
}
40+
41+
void can_stm32fd_clock_enable(void)
1942
{
2043
LL_RCC_SetFDCANClockSource(LL_RCC_FDCAN_CLKSOURCE_PCLK1);
2144
__HAL_RCC_FDCAN_CLK_ENABLE();
2245

23-
FDCAN_CONFIG->CKDIV = CONFIG_CAN_CKDIV;
46+
FDCAN_CONFIG->CKDIV = CONFIG_CAN_STM32_CLOCK_DIVISOR >> 1;
47+
}
48+
49+
void can_stm32fd_register_state_change_isr(const struct device *dev,
50+
can_state_change_isr_t isr)
51+
{
52+
struct can_stm32fd_data *data = DEV_DATA(dev);
53+
54+
data->mcan_data.state_change_isr = isr;
55+
}
56+
57+
static int can_stm32fd_init(const struct device *dev)
58+
{
59+
const struct can_stm32fd_config *cfg = DEV_CFG(dev);
60+
const struct can_mcan_config *mcan_cfg = &cfg->mcan_cfg;
61+
struct can_mcan_data *mcan_data = &DEV_DATA(dev)->mcan_data;
62+
struct can_mcan_msg_sram *msg_ram = cfg->msg_sram;
63+
int ret;
64+
65+
/* Configure dt provided device signals when available */
66+
ret = stm32_dt_pinctrl_configure(cfg->pinctrl,
67+
ARRAY_SIZE(cfg->pinctrl),
68+
(uint32_t)mcan_cfg->can);
69+
if (ret < 0) {
70+
LOG_ERR("CAN pinctrl setup failed (%d)", ret);
71+
return ret;
72+
}
73+
74+
can_stm32fd_clock_enable();
75+
ret = can_mcan_init(dev, mcan_cfg, msg_ram, mcan_data);
76+
if (ret) {
77+
return ret;
78+
}
79+
80+
cfg->config_irq();
81+
82+
return ret;
83+
}
84+
85+
enum can_state can_stm32fd_get_state(const struct device *dev,
86+
struct can_bus_err_cnt *err_cnt)
87+
{
88+
const struct can_stm32fd_config *cfg = DEV_CFG(dev);
89+
const struct can_mcan_config *mcan_cfg = &cfg->mcan_cfg;
90+
91+
return can_mcan_get_state(mcan_cfg, err_cnt);
92+
}
93+
94+
int can_stm32fd_send(const struct device *dev, const struct zcan_frame *frame,
95+
k_timeout_t timeout, can_tx_callback_t callback,
96+
void *callback_arg)
97+
{
98+
const struct can_stm32fd_config *cfg = DEV_CFG(dev);
99+
const struct can_mcan_config *mcan_cfg = &cfg->mcan_cfg;
100+
struct can_mcan_data *mcan_data = &DEV_DATA(dev)->mcan_data;
101+
struct can_mcan_msg_sram *msg_ram = cfg->msg_sram;
102+
103+
return can_mcan_send(mcan_cfg, mcan_data, msg_ram, frame, timeout,
104+
callback, callback_arg);
105+
}
106+
107+
int can_stm32fd_attach_isr(const struct device *dev, can_rx_callback_t isr,
108+
void *cb_arg, const struct zcan_filter *filter)
109+
{
110+
const struct can_stm32fd_config *cfg = DEV_CFG(dev);
111+
struct can_mcan_data *mcan_data = &DEV_DATA(dev)->mcan_data;
112+
struct can_mcan_msg_sram *msg_ram = cfg->msg_sram;
113+
114+
return can_mcan_attach_isr(mcan_data, msg_ram, isr, cb_arg, filter);
115+
}
116+
117+
void can_stm32fd_detach(const struct device *dev, int filter_nr)
118+
{
119+
const struct can_stm32fd_config *cfg = DEV_CFG(dev);
120+
struct can_mcan_data *mcan_data = &DEV_DATA(dev)->mcan_data;
121+
struct can_mcan_msg_sram *msg_ram = cfg->msg_sram;
122+
123+
can_mcan_detach(mcan_data, msg_ram, filter_nr);
24124
}
125+
126+
int can_stm32fd_set_mode(const struct device *dev, enum can_mode mode)
127+
{
128+
const struct can_stm32fd_config *cfg = DEV_CFG(dev);
129+
const struct can_mcan_config *mcan_cfg = &cfg->mcan_cfg;
130+
131+
return can_mcan_set_mode(mcan_cfg, mode);
132+
}
133+
134+
int can_stm32fd_set_timing(const struct device *dev,
135+
const struct can_timing *timing,
136+
const struct can_timing *timing_data)
137+
{
138+
const struct can_stm32fd_config *cfg = DEV_CFG(dev);
139+
const struct can_mcan_config *mcan_cfg = &cfg->mcan_cfg;
140+
141+
return can_mcan_set_timing(mcan_cfg, timing, timing_data);
142+
}
143+
144+
void can_stm32fd_line_0_isr(void *arg)
145+
{
146+
struct device *dev = (struct device *)arg;
147+
const struct can_stm32fd_config *cfg = DEV_CFG(dev);
148+
const struct can_mcan_config *mcan_cfg = &cfg->mcan_cfg;
149+
struct can_stm32fd_data *data = DEV_DATA(dev);
150+
struct can_mcan_data *mcan_data = &data->mcan_data;
151+
struct can_mcan_msg_sram *msg_ram = cfg->msg_sram;
152+
153+
can_mcan_line_0_isr(mcan_cfg, msg_ram, mcan_data);
154+
}
155+
156+
void can_stm32fd_line_1_isr(void *arg)
157+
{
158+
struct device *dev = (struct device *)arg;
159+
const struct can_stm32fd_config *cfg = DEV_CFG(dev);
160+
const struct can_mcan_config *mcan_cfg = &cfg->mcan_cfg;
161+
struct can_mcan_data *mcan_data = &DEV_DATA(dev)->mcan_data;
162+
struct can_mcan_msg_sram *msg_ram = cfg->msg_sram;
163+
164+
can_mcan_line_1_isr(mcan_cfg, msg_ram, mcan_data);
165+
}
166+
167+
static const struct can_driver_api can_api_funcs = {
168+
.set_mode = can_stm32fd_set_mode,
169+
.set_timing = can_stm32fd_set_timing,
170+
.send = can_stm32fd_send,
171+
.attach_isr = can_stm32fd_attach_isr,
172+
.detach = can_stm32fd_detach,
173+
.get_state = can_stm32fd_get_state,
174+
#ifndef CONFIG_CAN_AUTO_BUS_OFF_RECOVERY
175+
.recover = can_mcan_recover,
176+
#endif
177+
.get_core_clock = can_stm32fd_get_core_clock,
178+
.register_state_change_isr = can_stm32fd_register_state_change_isr,
179+
.timing_min = {
180+
.sjw = 0x7f,
181+
.prop_seg = 0x00,
182+
.phase_seg1 = 0x01,
183+
.phase_seg2 = 0x01,
184+
.prescaler = 0x01
185+
},
186+
.timing_max = {
187+
.sjw = 0x7f,
188+
.prop_seg = 0x00,
189+
.phase_seg1 = 0x100,
190+
.phase_seg2 = 0x80,
191+
.prescaler = 0x200
192+
},
193+
#ifdef CONFIG_CAN_FD_MODE
194+
.timing_min_data = {
195+
.sjw = 0x01,
196+
.prop_seg = 0x01,
197+
.phase_seg1 = 0x01,
198+
.phase_seg2 = 0x01,
199+
.prescaler = 0x01
200+
},
201+
.timing_max_data = {
202+
.sjw = 0x10,
203+
.prop_seg = 0x00,
204+
.phase_seg1 = 0x20,
205+
.phase_seg2 = 0x10,
206+
.prescaler = 0x20
207+
}
208+
#endif
209+
};
210+
211+
#define CAN_STM32FD_IRQ_CFG_FUNCTION(inst) \
212+
static void config_can_##inst##_irq(void) \
213+
{ \
214+
LOG_DBG("Enable CAN" #inst " IRQ"); \
215+
IRQ_CONNECT(DT_INST_IRQ_BY_NAME(inst, line_0, irq), \
216+
DT_INST_IRQ_BY_NAME(inst, line_0, priority), \
217+
can_stm32fd_line_0_isr, DEVICE_DT_INST_GET(inst), 0); \
218+
irq_enable(DT_INST_IRQ_BY_NAME(inst, line_0, irq)); \
219+
IRQ_CONNECT(DT_INST_IRQ_BY_NAME(inst, line_1, irq), \
220+
DT_INST_IRQ_BY_NAME(inst, line_1, priority), \
221+
can_stm32fd_line_1_isr, DEVICE_DT_INST_GET(inst), 0); \
222+
irq_enable(DT_INST_IRQ_BY_NAME(inst, line_1, irq)); \
223+
}
224+
225+
#ifdef CONFIG_CAN_FD_MODE
226+
227+
#define CAN_STM32FD_CFG_INST(inst) \
228+
static const struct can_stm32fd_config can_stm32fd_cfg_##inst = { \
229+
.msg_sram = (struct can_mcan_msg_sram *) \
230+
DT_INST_REG_ADDR_BY_NAME(inst, message_ram), \
231+
.config_irq = config_can_##inst##_irq, \
232+
.mcan_cfg = { \
233+
.can = (struct can_mcan_reg *) \
234+
DT_INST_REG_ADDR_BY_NAME(inst, m_can), \
235+
.bus_speed = DT_INST_PROP(inst, bus_speed), \
236+
.sjw = DT_INST_PROP(inst, sjw), \
237+
.sample_point = DT_INST_PROP_OR(inst, sample_point, 0), \
238+
.prop_ts1 = DT_INST_PROP_OR(inst, prop_seg, 0) + \
239+
DT_INST_PROP_OR(inst, phase_seg1, 0), \
240+
.ts2 = DT_INST_PROP_OR(inst, phase_seg2, 0), \
241+
.bus_speed_data = DT_INST_PROP(inst, bus_speed_data), \
242+
.sjw_data = DT_INST_PROP(inst, sjw_data), \
243+
.sample_point_data = \
244+
DT_INST_PROP_OR(inst, sample_point_data, 0), \
245+
.prop_ts1_data = DT_INST_PROP_OR(inst, prop_seg_data, 0) + \
246+
DT_INST_PROP_OR(inst, phase_seg1_data, 0), \
247+
.ts2_data = DT_INST_PROP_OR(inst, phase_seg2_data, 0), \
248+
}, \
249+
.pinctrl = ST_STM32_DT_INST_PINCTRL(inst, 0), \
250+
};
251+
252+
#else /* CONFIG_CAN_FD_MODE */
253+
254+
#define CAN_STM32FD_CFG_INST(inst) \
255+
static const struct can_stm32fd_config can_stm32fd_cfg_##inst = { \
256+
.msg_sram = (struct can_mcan_msg_sram *) \
257+
DT_INST_REG_ADDR_BY_NAME(inst, message_ram), \
258+
.config_irq = config_can_##inst##_irq, \
259+
.mcan_cfg = { \
260+
.can = (struct can_mcan_reg *) \
261+
DT_INST_REG_ADDR_BY_NAME(inst, m_can), \
262+
.bus_speed = DT_INST_PROP(inst, bus_speed), \
263+
.sjw = DT_INST_PROP(inst, sjw), \
264+
.sample_point = DT_INST_PROP_OR(inst, sample_point, 0), \
265+
.prop_ts1 = DT_INST_PROP_OR(inst, prop_seg, 0) + \
266+
DT_INST_PROP_OR(inst, phase_seg1, 0), \
267+
.ts2 = DT_INST_PROP_OR(inst, phase_seg2, 0), \
268+
}, \
269+
.pinctrl = ST_STM32_DT_INST_PINCTRL(inst, 0), \
270+
};
271+
272+
#endif /* CONFIG_CAN_FD_MODE */
273+
274+
#define CAN_STM32FD_DATA_INST(inst) \
275+
static struct can_stm32fd_data can_stm32fd_dev_data_##inst;
276+
277+
#define CAN_STM32FD_DEVICE_INST(inst) \
278+
DEVICE_DT_INST_DEFINE(inst, &can_stm32fd_init, device_pm_control_nop, \
279+
&can_stm32fd_dev_data_##inst, &can_stm32fd_cfg_##inst, \
280+
POST_KERNEL, CONFIG_KERNEL_INIT_PRIORITY_DEVICE, \
281+
&can_api_funcs);
282+
283+
#define CAN_STM32FD_INST(inst) \
284+
CAN_STM32FD_IRQ_CFG_FUNCTION(inst) \
285+
CAN_STM32FD_CFG_INST(inst) \
286+
CAN_STM32FD_DATA_INST(inst) \
287+
CAN_STM32FD_DEVICE_INST(inst)
288+
289+
DT_INST_FOREACH_STATUS_OKAY(CAN_STM32FD_INST)

drivers/can/can_stm32fd.h

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
/*
2+
* Copyright (c) 2020 Alexander Wachter
3+
*
4+
* SPDX-License-Identifier: Apache-2.0
5+
*
6+
*/
7+
8+
#ifndef ZEPHYR_DRIVERS_CAN_STM32FD_H_
9+
#define ZEPHYR_DRIVERS_CAN_STM32FD_H_
10+
11+
#include "can_mcan.h"
12+
#include <pinmux/stm32/pinmux_stm32.h>
13+
14+
#define DEV_DATA(dev) ((struct can_stm32fd_data *)(dev)->data)
15+
#define DEV_CFG(dev) ((const struct can_stm32fd_config *)(dev)->config)
16+
17+
struct can_stm32fd_config {
18+
struct can_mcan_msg_sram *msg_sram;
19+
void (*config_irq)(void);
20+
struct can_mcan_config mcan_cfg;
21+
/* CAN always has an RX and TX pin. Hence, hardcode it to two*/
22+
const struct soc_gpio_pinctrl pinctrl[2];
23+
};
24+
25+
struct can_stm32fd_data {
26+
struct can_mcan_data mcan_data;
27+
};
28+
29+
#endif /*ZEPHYR_DRIVERS_CAN_STM32FD_H_*/
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
description: Bosch m_can CAN-FD controller
2+
3+
compatible: "st,stm32-fdcan"
4+
5+
include: bosch-mcan.yaml
6+
7+
properties:
8+
pinctrl-0:
9+
type: phandles
10+
required: false
11+
description: |
12+
GPIO pin configuration for CAN signals (RX, TX). We expect
13+
that the phandles will reference pinctrl nodes.
14+
15+
For example the can1 would be
16+
pinctrl-0 = <&fdcan1_rx_pa11 &fdcan1_tx_pa12>;

0 commit comments

Comments
 (0)