Skip to content

Commit cef393a

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 9f0ed10 commit cef393a

File tree

5 files changed

+295
-0
lines changed

5 files changed

+295
-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: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
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+
endif # CAN_MSPM0G3XXX_CANFD

drivers/can/can_mspm0_canfd.c

Lines changed: 275 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,275 @@
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+
MCAN_Regs *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 0;
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(config->ti_canfd_base,
110+
(DL_MCAN_ClockConfig *)&config->clock_cfg);
111+
112+
/* Retrieve a valid MCANSS Revision ID after enabling the clock module.
113+
* The revision ID registers may initially return an invalid value until
114+
* the clock domain is fully stabilized.
115+
*/
116+
do {
117+
DL_MCAN_enableModuleClock(config->ti_canfd_base);
118+
DL_MCAN_getRevisionId(config->ti_canfd_base, &revid_MCAN0);
119+
} while ((uint32_t)revid_MCAN0.scheme == 0x00);
120+
121+
return 0;
122+
}
123+
124+
static int can_mspm0_canfd_init(const struct device *dev)
125+
{
126+
const struct can_mcan_config *mcan_cfg = dev->config;
127+
const struct can_mspm0_canfd_config *config = mcan_cfg->custom;
128+
int ret = 0;
129+
130+
LOG_DBG("Initializing %s", dev->name);
131+
132+
ret = pinctrl_apply_state(config->pinctrl, PINCTRL_STATE_DEFAULT);
133+
if (ret < 0) {
134+
LOG_ERR("MSPM0 CAN pinctrl error (%d)", ret);
135+
return ret;
136+
}
137+
138+
DL_MCAN_reset(config->ti_canfd_base);
139+
DL_MCAN_enablePower(config->ti_canfd_base);
140+
delay_cycles(CONFIG_MSPM0_PERIPH_STARTUP_DELAY);
141+
142+
can_mspm0_canfd_clock_enable(dev);
143+
144+
while (false == DL_MCAN_isMemInitDone(config->ti_canfd_base)) {
145+
/* Wait for Memory initialization to be completed. */
146+
}
147+
148+
ret = can_mcan_configure_mram(dev, 0x8000, config->mram);
149+
if (ret != 0) {
150+
return ret;
151+
}
152+
153+
ret = can_mcan_init(dev);
154+
if (ret != 0) {
155+
return ret;
156+
}
157+
158+
DL_MCAN_clearInterruptStatus(config->ti_canfd_base,
159+
(DL_MCAN_MSP_INTERRUPT_LINE0 | DL_MCAN_MSP_INTERRUPT_LINE1));
160+
DL_MCAN_enableInterrupt(config->ti_canfd_base,
161+
(DL_MCAN_MSP_INTERRUPT_LINE0 | DL_MCAN_MSP_INTERRUPT_LINE1));
162+
config->irq_cfg_func();
163+
164+
return ret;
165+
}
166+
167+
static void can_mspm0_canfd_isr(const struct device *dev)
168+
{
169+
const struct can_mcan_config *mcan_cfg = dev->config;
170+
const struct can_mspm0_canfd_config *config = mcan_cfg->custom;
171+
172+
switch (DL_MCAN_getPendingInterrupt(config->ti_canfd_base)) {
173+
case DL_MCAN_IIDX_LINE0:
174+
can_mcan_line_0_isr(dev);
175+
(config->ti_canfd_base)
176+
->MCANSS.TI_WRAPPER.PROCESSORS.MCANSS_REGS.MCANSS_EOI =
177+
DL_MCAN_INTR_SRC_MCAN_LINE_0;
178+
break;
179+
case DL_MCAN_IIDX_LINE1:
180+
can_mcan_line_1_isr(dev);
181+
(config->ti_canfd_base)
182+
->MCANSS.TI_WRAPPER.PROCESSORS.MCANSS_REGS.MCANSS_EOI =
183+
DL_MCAN_INTR_SRC_MCAN_LINE_1;
184+
break;
185+
default:
186+
break;
187+
}
188+
}
189+
190+
static const struct can_driver_api can_mspm0_canfd_driver_api = {
191+
.get_capabilities = can_mcan_get_capabilities,
192+
.start = can_mcan_start,
193+
.stop = can_mcan_stop,
194+
.set_mode = can_mcan_set_mode,
195+
.set_timing = can_mcan_set_timing,
196+
.send = can_mcan_send,
197+
.add_rx_filter = can_mcan_add_rx_filter,
198+
.remove_rx_filter = can_mcan_remove_rx_filter,
199+
.get_state = can_mcan_get_state,
200+
#ifdef CONFIG_CAN_MANUAL_RECOVERY_MODE
201+
.recover = can_mcan_recover,
202+
#endif /* CONFIG_CAN_MANUAL_RECOVERY_MODE */
203+
.get_core_clock = can_mspm0_canfd_get_core_clock,
204+
.get_max_filters = can_mcan_get_max_filters,
205+
.set_state_change_callback = can_mcan_set_state_change_callback,
206+
.timing_min = CAN_MCAN_TIMING_MIN_INITIALIZER,
207+
.timing_max = CAN_MCAN_TIMING_MAX_INITIALIZER,
208+
#ifdef CONFIG_CAN_FD_MODE
209+
.set_timing_data = can_mcan_set_timing_data,
210+
.timing_data_min = CAN_MCAN_TIMING_DATA_MIN_INITIALIZER,
211+
.timing_data_max = CAN_MCAN_TIMING_DATA_MAX_INITIALIZER,
212+
#endif /* CONFIG_CAN_FD_MODE */
213+
};
214+
215+
static const struct can_mcan_ops can_mspm0_canfd_ops = {
216+
.read_reg = can_mspm0_canfd_read_reg,
217+
.write_reg = can_mspm0_canfd_write_reg,
218+
.read_mram = can_mspm0_canfd_read_mram,
219+
.write_mram = can_mspm0_canfd_write_mram,
220+
.clear_mram = can_mspm0_canfd_clear_mram,
221+
};
222+
223+
#define CAN_MSPM0_CANFD_INIT(inst) \
224+
static void can_mspm0_canfd_irq_cfg_##inst(void) { \
225+
IRQ_CONNECT(DT_INST_IRQN(inst), \
226+
DT_INST_IRQ(inst, priority), \
227+
can_mspm0_canfd_isr, \
228+
DEVICE_DT_INST_GET(inst), 0); \
229+
irq_enable(DT_INST_IRQN(inst)); \
230+
} \
231+
\
232+
static const struct mspm0_sys_clock can_mspm0_canfd_sys_clock_##inst = \
233+
MSPM0_CLOCK_SUBSYS_FN(inst); \
234+
\
235+
PINCTRL_DT_INST_DEFINE(inst); \
236+
\
237+
CAN_MCAN_DT_INST_CALLBACKS_DEFINE(inst, can_mspm0_canfd_cbs_##inst); \
238+
\
239+
BUILD_ASSERT(CAN_MCAN_DT_INST_MRAM_ELEMENTS_SIZE(inst) <= \
240+
CAN_MCAN_DT_INST_MRAM_SIZE(inst), \
241+
"Insufficient Message RAM size"); \
242+
\
243+
static const struct can_mspm0_canfd_config can_mspm0_canfd_cfg_##inst = { \
244+
.ti_canfd_base = (MCAN_Regs *)DT_REG_ADDR_BY_NAME(DT_DRV_INST(inst), ti_canfd), \
245+
.clock_subsys = &can_mspm0_canfd_sys_clock_##inst, \
246+
.clock_cfg = { \
247+
.clockSel = DT_PROP_OR(DT_DRV_INST(inst), ti_canclk_source, \
248+
CAN_MSPM0_CLK_SRC_PLLCLK1) == \
249+
CAN_MSPM0_CLK_SRC_PLLCLK1 \
250+
? DL_MCAN_FCLK_SYSPLLCLK1 \
251+
: DL_MCAN_FCLK_HFCLK, \
252+
.divider = DT_PROP(DT_DRV_INST(inst), ti_divider), \
253+
}, \
254+
.mcan_base = CAN_MCAN_DT_INST_MCAN_ADDR(inst), \
255+
.mram = CAN_MCAN_DT_INST_MRAM_ADDR(inst), \
256+
.irq_cfg_func = can_mspm0_canfd_irq_cfg_##inst, \
257+
.pinctrl = PINCTRL_DT_INST_DEV_CONFIG_GET(inst), \
258+
}; \
259+
\
260+
static struct can_mcan_data can_mcan_data_##inst = \
261+
CAN_MCAN_DATA_INITIALIZER(NULL); \
262+
\
263+
static const struct can_mcan_config can_mcan_cfg_##inst = \
264+
CAN_MCAN_DT_CONFIG_INST_GET(inst, \
265+
&can_mspm0_canfd_cfg_##inst, \
266+
&can_mspm0_canfd_ops, \
267+
&can_mspm0_canfd_cbs_##inst); \
268+
\
269+
CAN_DEVICE_DT_INST_DEFINE(inst, \
270+
can_mspm0_canfd_init, NULL, \
271+
&can_mcan_data_##inst, &can_mcan_cfg_##inst, \
272+
POST_KERNEL, CONFIG_CAN_INIT_PRIORITY, \
273+
&can_mspm0_canfd_driver_api);
274+
275+
DT_INST_FOREACH_STATUS_OKAY(CAN_MSPM0_CANFD_INIT)

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)