Skip to content

Commit a824dfb

Browse files
drivers: can: mcux: flexcan: add dynamic memory allocation for MB
This change replaces static memory allocation for FlexCAN message buffers with dynamic allocation using a per-instance heap. The motivation is to support FlexCAN instances with varying message buffer counts and CAN FD configurations. For CAN FD instances, message buffer count is adjusted based on 64-byte payload size (7 buffers per RAM block vs 32 for 8-byte payload). Key changes: - Add CAN_MCUX_FLEXCAN_HEAP_SIZE Kconfig option (default 2048 bytes) to configure heap size for message buffer allocation - Replace compile-time MCUX_FLEXCAN_MAX_MB/RX/TX macros with runtime calculation in mcux_flexcan_get_mb_config() - Add dynamic allocation of rx_allocs, tx_allocs, rx_cbs, and tx_cbs arrays using k_heap during driver initialization - Update all array bounds checks and loop limits to use runtime values stored in driver data structure - Remove Kconfig symbol `CAN_MAX_MB` beacuse such information is added to device tree. - Remove range limitation in Kconfig symbol `CAN_MAX_FILTER` because such validation is added in mcux_flexcan_get_mb_config() Fixes #92798 Signed-off-by: William Tang <william.tang@nxp.com>
1 parent 79a12ce commit a824dfb

File tree

2 files changed

+173
-54
lines changed

2 files changed

+173
-54
lines changed

drivers/can/Kconfig.mcux

Lines changed: 7 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -28,30 +28,19 @@ config CAN_MCUX_FLEXCAN_WAIT_TIMEOUT
2828
Maximum number of wait loop iterations for the MCUX FlexCAN HAL when entering/leaving
2929
freeze mode.
3030

31-
config CAN_MAX_MB
32-
int "Maximum number of message buffers for concurrent active instances"
33-
default 16
34-
depends on SOC_SERIES_S32K3 || SOC_SERIES_S32K1 || SOC_SERIES_S32ZE
35-
range 1 96 if SOC_SERIES_S32K3
36-
range 1 32 if SOC_SERIES_S32K1 && !SOC_S32K142W && !SOC_S32K144W
37-
range 1 64 if SOC_S32K142W || SOC_S32K144W
38-
range 1 128 if SOC_SERIES_S32ZE
39-
help
40-
Defines maximum number of message buffers for concurrent active instances.
41-
4231
config CAN_MAX_FILTER
4332
int "Maximum number of concurrent active RX filters"
4433
default 5
45-
range 1 15 if SOC_SERIES_KINETIS_KE1XF || SOC_SERIES_KINETIS_K6X
46-
range 1 13 if (SOC_SERIES_IMXRT10XX || SOC_SERIES_IMXRT11XX) && CAN_MCUX_FLEXCAN_FD
47-
range 1 63 if SOC_SERIES_IMXRT10XX || SOC_SERIES_IMXRT11XX
48-
range 1 96 if SOC_SERIES_S32K3
49-
range 1 32 if SOC_SERIES_S32K1 && !SOC_S32K142W && !SOC_S32K144W
50-
range 1 64 if SOC_S32K142W || SOC_S32K144W
51-
range 1 128 if SOC_SERIES_S32ZE
5234
help
5335
Defines maximum number of concurrent active RX filters
5436

37+
config CAN_MCUX_FLEXCAN_HEAP_SIZE
38+
int "The heap memory pool that FlexCAN message buffer needed"
39+
default 2048
40+
help
41+
Defines the heap memory pool size for FlexCAN message buffer allocation
42+
This can be adjusted based on specific platform
43+
5544
endif # CAN_MCUX_FLEXCAN
5645

5746
config CAN_MCUX_MCAN

drivers/can/can_mcux_flexcan.c

Lines changed: 166 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -33,20 +33,6 @@ LOG_MODULE_REGISTER(can_mcux_flexcan, CONFIG_CAN_LOG_LEVEL);
3333
#define RX_START_IDX 0
3434
#endif
3535

36-
/* The maximum number of message buffers for concurrent active instances */
37-
#ifdef CONFIG_CAN_MAX_MB
38-
#define MCUX_FLEXCAN_MAX_MB CONFIG_CAN_MAX_MB
39-
#else
40-
#define MCUX_FLEXCAN_MAX_MB FSL_FEATURE_FLEXCAN_HAS_MESSAGE_BUFFER_MAX_NUMBERn(0)
41-
#endif
42-
43-
/*
44-
* RX message buffers (filters) will take up the first N message
45-
* buffers. The rest are available for TX use.
46-
*/
47-
#define MCUX_FLEXCAN_MAX_RX (CONFIG_CAN_MAX_FILTER + RX_START_IDX)
48-
#define MCUX_FLEXCAN_MAX_TX (MCUX_FLEXCAN_MAX_MB - MCUX_FLEXCAN_MAX_RX)
49-
5036
/*
5137
* Convert from RX message buffer index to allocated filter ID and
5238
* vice versa.
@@ -58,8 +44,8 @@ LOG_MODULE_REGISTER(can_mcux_flexcan, CONFIG_CAN_LOG_LEVEL);
5844
* Convert from TX message buffer index to allocated TX ID and vice
5945
* versa.
6046
*/
61-
#define TX_MBIDX_TO_ALLOC_IDX(x) (x - MCUX_FLEXCAN_MAX_RX)
62-
#define ALLOC_IDX_TO_TXMB_IDX(x) (x + MCUX_FLEXCAN_MAX_RX)
47+
#define TX_MBIDX_TO_ALLOC_IDX(x) (x - data->max_rx_mb)
48+
#define ALLOC_IDX_TO_TXMB_IDX(x) (x + data->max_rx_mb)
6349

6450
/* Convert from back from FLEXCAN IDs to Zephyr CAN IDs. */
6551
#define FLEXCAN_ID_TO_CAN_ID_STD(id) \
@@ -79,6 +65,7 @@ struct mcux_flexcan_config {
7965
const struct device *clock_dev;
8066
clock_control_subsys_t clock_subsys;
8167
int clk_source;
68+
uint16_t max_mb;
8269
#ifdef CONFIG_CAN_MCUX_FLEXCAN_FD
8370
bool flexcan_fd;
8471
#endif /* CONFIG_CAN_MCUX_FLEXCAN_FD */
@@ -113,16 +100,24 @@ struct mcux_flexcan_data {
113100
const struct device *dev;
114101
flexcan_handle_t handle;
115102

116-
ATOMIC_DEFINE(rx_allocs, MCUX_FLEXCAN_MAX_RX);
117-
struct k_mutex rx_mutex;
118-
struct mcux_flexcan_rx_callback rx_cbs[MCUX_FLEXCAN_MAX_RX];
119-
120-
ATOMIC_DEFINE(tx_allocs, MCUX_FLEXCAN_MAX_TX);
121-
struct k_sem tx_allocs_sem;
122-
struct k_mutex tx_mutex;
123-
struct mcux_flexcan_tx_callback tx_cbs[MCUX_FLEXCAN_MAX_TX];
124103
enum can_state state;
125104
struct can_timing timing;
105+
106+
uint32_t max_tx_mb;
107+
uint32_t max_rx_mb;
108+
109+
struct k_heap instance_heap;
110+
uint8_t heap_buffer[CONFIG_CAN_MCUX_FLEXCAN_HEAP_SIZE];
111+
112+
atomic_t *rx_allocs;
113+
atomic_t *tx_allocs;
114+
struct mcux_flexcan_rx_callback *rx_cbs;
115+
struct mcux_flexcan_tx_callback *tx_cbs;
116+
117+
struct k_mutex rx_mutex;
118+
struct k_mutex tx_mutex;
119+
struct k_sem tx_allocs_sem;
120+
126121
#ifdef CONFIG_CAN_MCUX_FLEXCAN_FD
127122
struct can_timing timing_data;
128123
#endif /* CONFIG_CAN_MCUX_FLEXCAN_FD */
@@ -133,6 +128,118 @@ static inline CAN_Type *get_base(const struct device *dev)
133128
return (CAN_Type *)DEVICE_MMIO_NAMED_GET(dev, flexcan_mmio);
134129
}
135130

131+
static int mcux_flexcan_get_mb_config(const struct device *dev, uint32_t *max_mb,
132+
uint32_t *max_rx, uint32_t *max_tx)
133+
{
134+
const struct mcux_flexcan_config *config = dev->config;
135+
136+
#ifdef CONFIG_CAN_MCUX_FLEXCAN_FD
137+
/*
138+
* When CAN FD is enabled, FlexCAN RAM can be partitioned into several blocks.
139+
* Each block can accommodate 7 message buffers if payload size is 64-byte.
140+
* Each block can accommodate 32 message buffers if payload size is 8-byte.
141+
* First calculate how many RAM blocks each FlexCAN instance support, then get
142+
* maximum accommodated message buffers under 64-byte payload size.
143+
*/
144+
if (config->flexcan_fd) {
145+
/* For FlexCAN FD instances with 64-byte payload */
146+
*max_mb = config->max_mb / 32U * 7U;
147+
}
148+
else {
149+
/*
150+
* For non-FD FlexCAN instances.
151+
* Some platforms have both classical CAN and CAN FD instances.
152+
* In this case, use the maximum message buffers.
153+
*/
154+
*max_mb = config->max_mb;
155+
}
156+
#else
157+
*max_mb = config->max_mb;
158+
#endif /* CONFIG_CAN_MCUX_FLEXCAN_FD */
159+
160+
/*
161+
* RX message buffers (filters) will take up the first N message
162+
* buffers. The rest are available for TX use.
163+
*/
164+
*max_rx = (CONFIG_CAN_MAX_FILTER + RX_START_IDX);
165+
166+
if (*max_mb < *max_rx) {
167+
LOG_ERR("Insufficient message buffers for RX filters");
168+
return -EINVAL;
169+
}
170+
171+
*max_tx = *max_mb - *max_rx;
172+
173+
return 0;
174+
}
175+
176+
static int mcux_flexcan_allocate_memory(const struct device *dev)
177+
{
178+
struct mcux_flexcan_data *data = dev->data;
179+
size_t rx_bitmap_size, tx_bitmap_size;
180+
size_t rx_cb_array_size, tx_cb_array_size;
181+
size_t total_size;
182+
183+
rx_bitmap_size = ATOMIC_BITMAP_SIZE(data->max_rx_mb) * sizeof(atomic_t);
184+
tx_bitmap_size = ATOMIC_BITMAP_SIZE(data->max_tx_mb) * sizeof(atomic_t);
185+
rx_cb_array_size = data->max_rx_mb * sizeof(struct mcux_flexcan_rx_callback);
186+
tx_cb_array_size = data->max_tx_mb * sizeof(struct mcux_flexcan_tx_callback);
187+
total_size = rx_bitmap_size + tx_bitmap_size + rx_cb_array_size + tx_cb_array_size;
188+
189+
if (total_size > sizeof(data->heap_buffer)) {
190+
LOG_ERR("Insufficient heap memory for CAN buffers. Required: %zu, Available: %zu",
191+
total_size, sizeof(data->heap_buffer));
192+
return -ENOMEM;
193+
}
194+
else {
195+
LOG_DBG("Total memory size to allocate: %zu bytes", total_size);
196+
}
197+
198+
data->rx_allocs = NULL;
199+
data->tx_allocs = NULL;
200+
data->rx_cbs = NULL;
201+
data->tx_cbs = NULL;
202+
203+
/* Dynamic allocate memory for TX RX data structures using heap */
204+
k_heap_init(&data->instance_heap, data->heap_buffer, sizeof(data->heap_buffer));
205+
206+
data->rx_allocs = k_heap_alloc(&data->instance_heap, rx_bitmap_size, K_NO_WAIT);
207+
data->tx_allocs = k_heap_alloc(&data->instance_heap, tx_bitmap_size, K_NO_WAIT);
208+
data->rx_cbs = k_heap_alloc(&data->instance_heap, rx_cb_array_size, K_NO_WAIT);
209+
data->tx_cbs = k_heap_alloc(&data->instance_heap, tx_cb_array_size, K_NO_WAIT);
210+
211+
if (!data->rx_allocs || !data->tx_allocs || !data->rx_cbs || !data->tx_cbs) {
212+
LOG_ERR("Failed to allocate memory for CAN buffers");
213+
goto cleanup;
214+
}
215+
216+
memset(data->rx_allocs, 0, rx_bitmap_size);
217+
memset(data->tx_allocs, 0, tx_bitmap_size);
218+
memset(data->rx_cbs, 0, rx_cb_array_size);
219+
memset(data->tx_cbs, 0, tx_cb_array_size);
220+
221+
return 0;
222+
223+
cleanup:
224+
if (data->tx_cbs) {
225+
k_heap_free(&data->instance_heap, data->tx_cbs);
226+
data->tx_cbs = NULL;
227+
}
228+
if (data->rx_cbs) {
229+
k_heap_free(&data->instance_heap, data->rx_cbs);
230+
data->rx_cbs = NULL;
231+
}
232+
if (data->tx_allocs) {
233+
k_heap_free(&data->instance_heap, data->tx_allocs);
234+
data->tx_allocs = NULL;
235+
}
236+
if (data->rx_allocs) {
237+
k_heap_free(&data->instance_heap, data->rx_allocs);
238+
data->rx_allocs = NULL;
239+
}
240+
return -ENOMEM;
241+
}
242+
136243
static int mcux_flexcan_get_core_clock(const struct device *dev, uint32_t *rate)
137244
{
138245
const struct mcux_flexcan_config *config = dev->config;
@@ -209,7 +316,7 @@ static status_t mcux_flexcan_mb_start(const struct device *dev, int alloc)
209316
flexcan_mb_transfer_t xfer;
210317
status_t status;
211318

212-
__ASSERT_NO_MSG(alloc >= 0 && alloc < ARRAY_SIZE(data->rx_cbs));
319+
__ASSERT_NO_MSG(alloc >= 0 && alloc < data->max_rx_mb);
213320

214321
xfer.mbIdx = ALLOC_IDX_TO_RXMB_IDX(alloc);
215322

@@ -237,7 +344,7 @@ static void mcux_flexcan_mb_stop(const struct device *dev, int alloc)
237344
struct mcux_flexcan_data *data = dev->data;
238345
CAN_Type *base = get_base(dev);
239346

240-
__ASSERT_NO_MSG(alloc >= 0 && alloc < ARRAY_SIZE(data->rx_cbs));
347+
__ASSERT_NO_MSG(alloc >= 0 && alloc < data->max_rx_mb);
241348

242349
#ifdef CONFIG_CAN_MCUX_FLEXCAN_FD
243350
if ((data->common.mode & CAN_MODE_FD) != 0U) {
@@ -288,7 +395,7 @@ static int mcux_flexcan_start(const struct device *dev)
288395
/* Re-add all RX filters using current mode */
289396
k_mutex_lock(&data->rx_mutex, K_FOREVER);
290397

291-
for (alloc = RX_START_IDX; alloc < MCUX_FLEXCAN_MAX_RX; alloc++) {
398+
for (alloc = RX_START_IDX; alloc < data->max_rx_mb; alloc++) {
292399
if (atomic_test_bit(data->rx_allocs, alloc)) {
293400
status = mcux_flexcan_mb_start(dev, alloc);
294401
if (status != kStatus_Success) {
@@ -351,7 +458,7 @@ static int mcux_flexcan_stop(const struct device *dev)
351458
data->common.started = false;
352459

353460
/* Abort any pending TX frames before entering freeze mode */
354-
for (alloc = 0; alloc < MCUX_FLEXCAN_MAX_TX; alloc++) {
461+
for (alloc = 0; alloc < data->max_tx_mb; alloc++) {
355462
function = data->tx_cbs[alloc].function;
356463
arg = data->tx_cbs[alloc].arg;
357464

@@ -382,7 +489,7 @@ static int mcux_flexcan_stop(const struct device *dev)
382489
*/
383490
k_mutex_lock(&data->rx_mutex, K_FOREVER);
384491

385-
for (alloc = RX_START_IDX; alloc < MCUX_FLEXCAN_MAX_RX; alloc++) {
492+
for (alloc = RX_START_IDX; alloc < data->max_rx_mb; alloc++) {
386493
if (atomic_test_bit(data->rx_allocs, alloc)) {
387494
mcux_flexcan_mb_stop(dev, alloc);
388495
}
@@ -744,7 +851,7 @@ static int mcux_flexcan_send(const struct device *dev,
744851
return -EAGAIN;
745852
}
746853

747-
for (alloc = 0; alloc < MCUX_FLEXCAN_MAX_TX; alloc++) {
854+
for (alloc = 0; alloc < data->max_tx_mb; alloc++) {
748855
if (!atomic_test_and_set_bit(data->tx_allocs, alloc)) {
749856
break;
750857
}
@@ -817,7 +924,7 @@ static int mcux_flexcan_add_rx_filter(const struct device *dev,
817924
k_mutex_lock(&data->rx_mutex, K_FOREVER);
818925

819926
/* Find and allocate RX message buffer */
820-
for (i = RX_START_IDX; i < MCUX_FLEXCAN_MAX_RX; i++) {
927+
for (i = RX_START_IDX; i < data->max_rx_mb; i++) {
821928
if (!atomic_test_and_set_bit(data->rx_allocs, i)) {
822929
alloc = i;
823930
break;
@@ -920,7 +1027,7 @@ static void mcux_flexcan_remove_rx_filter(const struct device *dev, int filter_i
9201027
{
9211028
struct mcux_flexcan_data *data = dev->data;
9221029

923-
if (filter_id < 0 || filter_id >= MCUX_FLEXCAN_MAX_RX) {
1030+
if (filter_id < 0 || filter_id >= data->max_rx_mb) {
9241031
LOG_ERR("filter ID %d out of bounds", filter_id);
9251032
return;
9261033
}
@@ -996,7 +1103,7 @@ static inline void mcux_flexcan_transfer_error_status(const struct device *dev,
9961103

9971104
if (state == CAN_STATE_BUS_OFF) {
9981105
/* Abort any pending TX frames in case of bus-off */
999-
for (alloc = 0; alloc < MCUX_FLEXCAN_MAX_TX; alloc++) {
1106+
for (alloc = 0; alloc < data->max_tx_mb; alloc++) {
10001107
/* Copy callback function and argument before clearing bit */
10011108
function = data->tx_cbs[alloc].function;
10021109
arg = data->tx_cbs[alloc].arg;
@@ -1159,6 +1266,7 @@ static int mcux_flexcan_init(const struct device *dev)
11591266
struct mcux_flexcan_data *data = dev->data;
11601267
CAN_Type *base;
11611268
flexcan_config_t flexcan_config;
1269+
uint32_t max_mb, max_rx, max_tx;
11621270
uint32_t clock_freq;
11631271
int err;
11641272

@@ -1176,10 +1284,31 @@ static int mcux_flexcan_init(const struct device *dev)
11761284

11771285
DEVICE_MMIO_NAMED_MAP(dev, flexcan_mmio, K_MEM_CACHE_NONE | K_MEM_DIRECT_MAP);
11781286

1287+
err = mcux_flexcan_get_mb_config(dev, &max_mb, &max_rx, &max_tx);
1288+
if (err != 0) {
1289+
return err;
1290+
}
1291+
1292+
if (max_mb == 0 || max_rx == 0 || max_tx == 0) {
1293+
LOG_ERR("Invalid message buffer configuration. max_mb: %d, rx_mb: %d, tx_mb: %d",
1294+
max_mb, max_rx, max_tx);
1295+
return -EINVAL;
1296+
}
1297+
else {
1298+
LOG_DBG("max_mb: %d, rx_mb: %d, tx_mb: %d", max_mb, max_rx, max_tx);
1299+
}
1300+
1301+
data->max_rx_mb = max_rx;
1302+
data->max_tx_mb = max_tx;
1303+
1304+
err = mcux_flexcan_allocate_memory(dev);
1305+
if (err != 0) {
1306+
return err;
1307+
}
1308+
11791309
k_mutex_init(&data->rx_mutex);
11801310
k_mutex_init(&data->tx_mutex);
1181-
k_sem_init(&data->tx_allocs_sem, MCUX_FLEXCAN_MAX_TX,
1182-
MCUX_FLEXCAN_MAX_TX);
1311+
k_sem_init(&data->tx_allocs_sem, max_tx, max_tx);
11831312

11841313
err = can_calc_timing(dev, &data->timing, config->common.bitrate,
11851314
config->common.sample_point);
@@ -1235,7 +1364,7 @@ static int mcux_flexcan_init(const struct device *dev)
12351364
data->dev = dev;
12361365

12371366
FLEXCAN_GetDefaultConfig(&flexcan_config);
1238-
flexcan_config.maxMbNum = MCUX_FLEXCAN_MAX_MB;
1367+
flexcan_config.maxMbNum = max_mb;
12391368
flexcan_config.clkSrc = config->clk_source;
12401369
flexcan_config.baudRate = clock_freq /
12411370
(1U + data->timing.prop_seg + data->timing.phase_seg1 +
@@ -1446,6 +1575,7 @@ static DEVICE_API(can, mcux_flexcan_fd_driver_api) = {
14461575
.clock_subsys = (clock_control_subsys_t) \
14471576
DT_INST_CLOCKS_CELL(id, name), \
14481577
.clk_source = DT_INST_PROP(id, clk_source), \
1578+
.max_mb = DT_INST_PROP(id, nxp_max_mb), \
14491579
IF_ENABLED(CONFIG_CAN_MCUX_FLEXCAN_FD, ( \
14501580
.flexcan_fd = DT_INST_NODE_HAS_COMPAT(id, FLEXCAN_FD_DRV_COMPAT), \
14511581
)) \

0 commit comments

Comments
 (0)