Skip to content

Commit 9b2cd75

Browse files
committed
drivers: can: Add driver support for TI MSPM0 G-Series MCAN module
Add driver support for MCAN module on TI's MSPM0 G-Series MCUs. The MCAN module supports both classic CAN and CAN FD protocols. Signed-off-by: Santhosh Charles <[email protected]> Signed-off-by: Jackson Farley <[email protected]> Signed-off-by: Tomasz Bursztyka <[email protected]>
1 parent f7ef082 commit 9b2cd75

File tree

5 files changed

+312
-0
lines changed

5 files changed

+312
-0
lines changed

drivers/can/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ zephyr_library_sources_ifdef(CONFIG_CAN_MCP2515 can_mcp2515.c)
2323
zephyr_library_sources_ifdef(CONFIG_CAN_MCP251XFD can_mcp251xfd.c)
2424
zephyr_library_sources_ifdef(CONFIG_CAN_MCUX_FLEXCAN can_mcux_flexcan.c)
2525
zephyr_library_sources_ifdef(CONFIG_CAN_MCUX_MCAN can_mcux_mcan.c)
26+
zephyr_library_sources_ifdef(CONFIG_CAN_MSPM0_CANFD can_mspm0_canfd.c)
2627
zephyr_library_sources_ifdef(CONFIG_CAN_NRF can_nrf.c)
2728
zephyr_library_sources_ifdef(CONFIG_CAN_NUMAKER can_numaker.c)
2829
zephyr_library_sources_ifdef(CONFIG_CAN_NXP_S32_CANXL can_nxp_s32_canxl.c)

drivers/can/Kconfig

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,7 @@ source "drivers/can/Kconfig.nrf"
140140
source "drivers/can/Kconfig.renesas_ra"
141141
source "drivers/can/Kconfig.renesas_rz"
142142
source "drivers/can/Kconfig.max32"
143+
source "drivers/can/Kconfig.mspm0"
143144

144145
source "drivers/can/transceiver/Kconfig"
145146

drivers/can/Kconfig.mspm0

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
# Copyright (c) 2024 Texas Instruments
2+
# SPDX-License-Identifier: Apache-2.0
3+
4+
config CAN_MSPM0_CANFD
5+
bool "MSPM0 CANFD driver"
6+
default y
7+
depends on DT_HAS_TI_MSPM0_CANFD_ENABLED
8+
select CAN_MCAN
9+
select USE_MSPM0_DL_MCAN
10+
help
11+
Enable support for CANFD on the TI MSPM0 series.
12+
13+
if CAN_MSPM0_CANFD
14+
15+
config CAN_MAX_STD_ID_FILTER
16+
int "Maximum number of standard (11-bit) ID filters"
17+
default 128
18+
range 0 128
19+
help
20+
Defines the maximum number of filters with standard ID (11-bit)
21+
that can be added by the application.
22+
23+
config CAN_MAX_EXT_ID_FILTER
24+
int "Maximum number of extended (29-bit) ID filters"
25+
default 64
26+
range 0 64
27+
help
28+
Defines the maximum number of filters with extended ID (29-bit)
29+
that can be added by the application.
30+
31+
endif # CAN_MSPM0G3XXX_CANFD

drivers/can/can_mspm0_canfd.c

Lines changed: 276 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,276 @@
1+
/*
2+
* Copyright (c) 2024 Texas Instruments
3+
*
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
#include <zephyr/kernel.h>
8+
#include <zephyr/irq.h>
9+
10+
#include <zephyr/drivers/can.h>
11+
#include <zephyr/drivers/can/can_mcan.h>
12+
#include <zephyr/drivers/pinctrl.h>
13+
#include <zephyr/drivers/clock_control.h>
14+
#include <zephyr/drivers/clock_control/mspm0_clock_control.h>
15+
#include <zephyr/logging/log.h>
16+
17+
/* Driverlib includes */
18+
#include <ti/driverlib/dl_mcan.h>
19+
20+
LOG_MODULE_REGISTER(can_mspm0_canfd, CONFIG_CAN_LOG_LEVEL);
21+
22+
#define DT_DRV_COMPAT ti_mspm0_canfd
23+
24+
#define CAN_MSPM0_CLK_SRC_PLLCLK1 1
25+
26+
struct can_mspm0_canfd_config {
27+
uint32_t ti_canfd_base;
28+
const struct mspm0_sys_clock *clock_subsys;
29+
30+
const DL_MCAN_ClockConfig clock_cfg;
31+
mm_reg_t mcan_base;
32+
mem_addr_t mram;
33+
34+
void (*irq_cfg_func)(void);
35+
const struct pinctrl_dev_config *pinctrl;
36+
};
37+
38+
static int can_mspm0_canfd_read_reg(const struct device *dev, uint16_t reg, uint32_t *val)
39+
{
40+
const struct can_mcan_config *mcan_config = dev->config;
41+
const struct can_mspm0_canfd_config *config = mcan_config->custom;
42+
43+
return can_mcan_sys_read_reg(config->mcan_base, reg, val);
44+
}
45+
46+
static int can_mspm0_canfd_write_reg(const struct device *dev, uint16_t reg, uint32_t val)
47+
{
48+
const struct can_mcan_config *mcan_config = dev->config;
49+
const struct can_mspm0_canfd_config *config = mcan_config->custom;
50+
51+
return can_mcan_sys_write_reg(config->mcan_base, reg, val);
52+
}
53+
54+
static int can_mspm0_canfd_read_mram(const struct device *dev, uint16_t offset, void *dst,
55+
size_t len)
56+
{
57+
const struct can_mcan_config *mcan_config = dev->config;
58+
const struct can_mspm0_canfd_config *config = mcan_config->custom;
59+
60+
return can_mcan_sys_read_mram(config->mram, offset, dst, len);
61+
}
62+
63+
static int can_mspm0_canfd_write_mram(const struct device *dev, uint16_t offset, const void *src,
64+
size_t len)
65+
{
66+
const struct can_mcan_config *mcan_config = dev->config;
67+
const struct can_mspm0_canfd_config *config = mcan_config->custom;
68+
69+
return can_mcan_sys_write_mram(config->mram, offset, src, len);
70+
}
71+
72+
static int can_mspm0_canfd_clear_mram(const struct device *dev, uint16_t offset, size_t len)
73+
{
74+
const struct can_mcan_config *mcan_config = dev->config;
75+
const struct can_mspm0_canfd_config *config = mcan_config->custom;
76+
77+
return can_mcan_sys_clear_mram(config->mram, offset, len);
78+
}
79+
80+
static int can_mspm0_canfd_get_core_clock(const struct device *dev, uint32_t *rate)
81+
{
82+
const struct can_mcan_config *mcan_config = dev->config;
83+
const struct can_mspm0_canfd_config *config = mcan_config->custom;
84+
const struct device *clk_dev = DEVICE_DT_GET(DT_NODELABEL(ckm));
85+
uint32_t clock_rate;
86+
int ret;
87+
88+
ret = clock_control_get_rate(clk_dev, (struct mspm0_sys_clock *)config->clock_subsys,
89+
&clock_rate);
90+
if (ret < 0) {
91+
return ret;
92+
}
93+
94+
if (config->clock_cfg.divider != DL_MCAN_FCLK_DIV_1) {
95+
*rate = clock_rate / (config->clock_cfg.divider >> MCAN_CLKDIV_RATIO_OFS);
96+
} else {
97+
*rate = clock_rate;
98+
}
99+
100+
return ret;
101+
}
102+
103+
static int can_mspm0_canfd_clock_enable(const struct device *dev)
104+
{
105+
const struct can_mcan_config *mcan_cfg = dev->config;
106+
const struct can_mspm0_canfd_config *config = mcan_cfg->custom;
107+
DL_MCAN_RevisionId revid_MCAN0;
108+
109+
DL_MCAN_setClockConfig((MCAN_Regs *)config->ti_canfd_base,
110+
(DL_MCAN_ClockConfig *)&config->clock_cfg);
111+
112+
/* Get MCANSS Revision ID. */
113+
do {
114+
DL_MCAN_enableModuleClock((MCAN_Regs *)config->ti_canfd_base);
115+
DL_MCAN_getRevisionId((MCAN_Regs *)config->ti_canfd_base, &revid_MCAN0);
116+
} while ((uint32_t)revid_MCAN0.scheme == 0x00);
117+
118+
return 0;
119+
}
120+
121+
static int can_mspm0_canfd_init(const struct device *dev)
122+
{
123+
const struct can_mcan_config *mcan_cfg = dev->config;
124+
const struct can_mspm0_canfd_config *config = mcan_cfg->custom;
125+
int ret = 0;
126+
127+
LOG_DBG("Initializing %s", dev->name);
128+
129+
/* Init GPIO */
130+
ret = pinctrl_apply_state(config->pinctrl, PINCTRL_STATE_DEFAULT);
131+
if (ret < 0) {
132+
LOG_ERR("MSPM0 CAN pinctrl error (%d)", ret);
133+
return ret;
134+
}
135+
136+
/* Init power */
137+
DL_MCAN_reset((MCAN_Regs *)config->ti_canfd_base);
138+
DL_MCAN_enablePower((MCAN_Regs *)config->ti_canfd_base);
139+
delay_cycles(CONFIG_MSPM0_PERIPH_STARTUP_DELAY);
140+
141+
can_mspm0_canfd_clock_enable(dev);
142+
143+
while (false == DL_MCAN_isMemInitDone((MCAN_Regs *)config->ti_canfd_base)) {
144+
/* Wait for Memory initialization to be completed. */
145+
}
146+
147+
ret = can_mcan_configure_mram(dev, 0x8000, config->mram);
148+
if (ret != 0) {
149+
return ret;
150+
}
151+
152+
ret = can_mcan_init(dev);
153+
if (ret != 0) {
154+
return ret;
155+
}
156+
157+
DL_MCAN_clearInterruptStatus((MCAN_Regs *)config->ti_canfd_base,
158+
(DL_MCAN_MSP_INTERRUPT_LINE0 | DL_MCAN_MSP_INTERRUPT_LINE1));
159+
DL_MCAN_enableInterrupt((MCAN_Regs *)config->ti_canfd_base,
160+
(DL_MCAN_MSP_INTERRUPT_LINE0 | DL_MCAN_MSP_INTERRUPT_LINE1));
161+
config->irq_cfg_func();
162+
163+
return ret;
164+
}
165+
166+
static void can_mspm0_canfd_isr(const struct device *dev)
167+
{
168+
const struct can_mcan_config *mcan_cfg = dev->config;
169+
const struct can_mspm0_canfd_config *config = mcan_cfg->custom;
170+
171+
switch (DL_MCAN_getPendingInterrupt((MCAN_Regs *)config->ti_canfd_base)) {
172+
case DL_MCAN_IIDX_LINE0:
173+
can_mcan_line_0_isr(dev);
174+
((MCAN_Regs *)config->ti_canfd_base)
175+
->MCANSS.TI_WRAPPER.PROCESSORS.MCANSS_REGS.MCANSS_EOI =
176+
DL_MCAN_INTR_SRC_MCAN_LINE_0;
177+
break;
178+
case DL_MCAN_IIDX_LINE1:
179+
can_mcan_line_1_isr(dev);
180+
((MCAN_Regs *)config->ti_canfd_base)
181+
->MCANSS.TI_WRAPPER.PROCESSORS.MCANSS_REGS.MCANSS_EOI =
182+
DL_MCAN_INTR_SRC_MCAN_LINE_1;
183+
break;
184+
default:
185+
break;
186+
}
187+
}
188+
189+
static const struct can_driver_api can_mspm0_canfd_driver_api = {
190+
.get_capabilities = can_mcan_get_capabilities,
191+
.start = can_mcan_start,
192+
.stop = can_mcan_stop,
193+
.set_mode = can_mcan_set_mode,
194+
.set_timing = can_mcan_set_timing,
195+
.send = can_mcan_send,
196+
.add_rx_filter = can_mcan_add_rx_filter,
197+
.remove_rx_filter = can_mcan_remove_rx_filter,
198+
.get_state = can_mcan_get_state,
199+
#ifdef CONFIG_CAN_MANUAL_RECOVERY_MODE
200+
.recover = can_mcan_recover,
201+
#endif /* CONFIG_CAN_MANUAL_RECOVERY_MODE */
202+
.get_core_clock = can_mspm0_canfd_get_core_clock,
203+
.get_max_filters = can_mcan_get_max_filters,
204+
.set_state_change_callback = can_mcan_set_state_change_callback,
205+
.timing_min = CAN_MCAN_TIMING_MIN_INITIALIZER,
206+
.timing_max = CAN_MCAN_TIMING_MAX_INITIALIZER,
207+
#ifdef CONFIG_CAN_FD_MODE
208+
.set_timing_data = can_mcan_set_timing_data,
209+
.timing_data_min = CAN_MCAN_TIMING_DATA_MIN_INITIALIZER,
210+
.timing_data_max = CAN_MCAN_TIMING_DATA_MAX_INITIALIZER,
211+
#endif /* CONFIG_CAN_FD_MODE */
212+
};
213+
214+
static const struct can_mcan_ops can_mspm0_canfd_ops = {
215+
.read_reg = can_mspm0_canfd_read_reg,
216+
.write_reg = can_mspm0_canfd_write_reg,
217+
.read_mram = can_mspm0_canfd_read_mram,
218+
.write_mram = can_mspm0_canfd_write_mram,
219+
.clear_mram = can_mspm0_canfd_clear_mram,
220+
};
221+
222+
#define can_mspm0_canfd_IRQ_CFG_FUNCTION(index) \
223+
static void config_can_##index(void) \
224+
{ \
225+
IRQ_CONNECT(DT_INST_IRQN(index), DT_INST_IRQ(index, priority), \
226+
can_mspm0_canfd_isr, DEVICE_DT_INST_GET(index), 0); \
227+
irq_enable(DT_INST_IRQN(index)); \
228+
}
229+
230+
#define can_mspm0_canfd_CFG_INST(index) \
231+
BUILD_ASSERT(CAN_MCAN_DT_INST_MRAM_ELEMENTS_SIZE(index) <= \
232+
CAN_MCAN_DT_INST_MRAM_SIZE(index), \
233+
"Insufficient Message RAM size"); \
234+
\
235+
PINCTRL_DT_INST_DEFINE(index); \
236+
CAN_MCAN_CALLBACKS_DEFINE(can_mspm0_canfd_cbs_##index, \
237+
CAN_MCAN_DT_INST_MRAM_TX_BUFFER_ELEMENTS(index), \
238+
CONFIG_CAN_MAX_STD_ID_FILTER, CONFIG_CAN_MAX_EXT_ID_FILTER); \
239+
\
240+
static const struct mspm0_sys_clock mspm0_can_sys_clock##index = \
241+
MSPM0_CLOCK_SUBSYS_FN(index); \
242+
\
243+
static const struct can_mspm0_canfd_config can_mspm0_canfd_cfg_##index = { \
244+
.ti_canfd_base = DT_REG_ADDR_BY_NAME(DT_DRV_INST(index), ti_canfd), \
245+
.clock_subsys = &mspm0_can_sys_clock##index, \
246+
.clock_cfg = \
247+
{ \
248+
.clockSel = DT_PROP_OR(DT_DRV_INST(index), \
249+
ti_canclk_source, CAN_MSPM0_CLK_SRC_PLLCLK1) == \
250+
CAN_MSPM0_CLK_SRC_PLLCLK1 ? DL_MCAN_FCLK_SYSPLLCLK1 \
251+
: DL_MCAN_FCLK_HFCLK, \
252+
.divider = DT_PROP(DT_DRV_INST(index), ti_divider), \
253+
}, \
254+
.mcan_base = CAN_MCAN_DT_INST_MCAN_ADDR(index), \
255+
.mram = CAN_MCAN_DT_INST_MRAM_ADDR(index), \
256+
.irq_cfg_func = config_can_##index, \
257+
.pinctrl = PINCTRL_DT_INST_DEV_CONFIG_GET(index), \
258+
}; \
259+
\
260+
static const struct can_mcan_config can_mcan_cfg_##index = CAN_MCAN_DT_CONFIG_INST_GET( \
261+
index, &can_mspm0_canfd_cfg_##index, &can_mspm0_canfd_ops, \
262+
&can_mspm0_canfd_cbs_##index);
263+
264+
#define can_mspm0_canfd_DATA_INST(index) \
265+
static struct can_mcan_data can_mcan_data_##index = CAN_MCAN_DATA_INITIALIZER(NULL);
266+
267+
#define can_mspm0_canfd_DEVICE_INST(index) \
268+
CAN_DEVICE_DT_INST_DEFINE(index, can_mspm0_canfd_init, NULL, &can_mcan_data_##index, \
269+
&can_mcan_cfg_##index, POST_KERNEL, CONFIG_CAN_INIT_PRIORITY, \
270+
&can_mspm0_canfd_driver_api);
271+
272+
#define can_mspm0_canfd_INST(index) \
273+
can_mspm0_canfd_IRQ_CFG_FUNCTION(index) can_mspm0_canfd_CFG_INST(index) \
274+
can_mspm0_canfd_DATA_INST(index) can_mspm0_canfd_DEVICE_INST(index)
275+
276+
DT_INST_FOREACH_STATUS_OKAY(can_mspm0_canfd_INST)

modules/Kconfig.mspm0

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,3 +14,6 @@ config USE_MSPM0_DL_UART
1414

1515
config USE_MSPM0_DL_TIMER
1616
bool
17+
18+
config USE_MSPM0_DL_MCAN
19+
bool

0 commit comments

Comments
 (0)