Skip to content

Commit 3275559

Browse files
[nrf noup] drivers: can: mcan: implement CAN_MODE_ONE_SHOT
Implement CAN_MODE_ONE_SHOT in MCAN impementation. The implementation contains a workaround for a bug in the MCAN IP which prevents an IRQ from triggering. This is a noup as the workaround is too complicated and the feature is too niche to be accepted it upstream. Signed-off-by: Bjarki Arge Andreasen <[email protected]>
1 parent 153e90f commit 3275559

File tree

3 files changed

+128
-2
lines changed

3 files changed

+128
-2
lines changed

drivers/can/Kconfig.mcan

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,18 @@ config CAN_MCAN
77
bool
88
help
99
Enable the Bosch M_CAN CAN IP module driver backend.
10+
11+
if CAN_MCAN
12+
13+
config CAN_MCAN_TXBCF_POLL_INTERVAL_MS
14+
int "Polling interval in milliseconds of TXBCF register if DAR is enabled"
15+
default 75
16+
help
17+
When DAR (Disable Automatic Retransmission), used for CAN_MODE_ONE_SHOT,
18+
is enabled, and a transmission fails, a bug in the MCAN IP prevents the
19+
TCF (Transmission Cancellation Finalized) interrupt from triggering,
20+
despite the correct bit being set in the TXBCF register. It is thus
21+
necessary to poll TXBCF register to detect when a transmission failed if
22+
DAR is enabled.
23+
24+
endif # CAN_MCAN

drivers/can/can_mcan.c

Lines changed: 112 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
LOG_MODULE_REGISTER(can_mcan, CONFIG_CAN_LOG_LEVEL);
1818

1919
#define CAN_INIT_TIMEOUT_MS 100
20+
#define TXBCF_TIMER_TIMEOUT K_MSEC(CONFIG_CAN_MCAN_TXBCF_POLL_INTERVAL_MS)
2021

2122
int can_mcan_read_reg(const struct device *dev, uint16_t reg, uint32_t *val)
2223
{
@@ -276,7 +277,7 @@ int can_mcan_get_capabilities(const struct device *dev, can_mode_t *cap)
276277
{
277278
ARG_UNUSED(dev);
278279

279-
*cap = CAN_MODE_NORMAL | CAN_MODE_LOOPBACK | CAN_MODE_LISTENONLY;
280+
*cap = CAN_MODE_NORMAL | CAN_MODE_LOOPBACK | CAN_MODE_LISTENONLY | CAN_MODE_ONE_SHOT;
280281

281282
if (IS_ENABLED(CONFIG_CAN_MANUAL_RECOVERY_MODE)) {
282283
*cap |= CAN_MODE_MANUAL_RECOVERY;
@@ -322,12 +323,78 @@ int can_mcan_start(const struct device *dev)
322323
return err;
323324
}
324325

326+
uint32_t cccr;
327+
328+
err = can_mcan_read_reg(dev, CAN_MCAN_CCCR, &cccr);
329+
if (err != 0) {
330+
return err;
331+
}
332+
333+
if (cccr & CAN_MCAN_CCCR_DAR) {
334+
/*
335+
* When DAR (Disable Automatic Retransmission), used for CAN_MODE_ONE_SHOT,
336+
* is enabled, and a transmission fails, a bug in the MCAN IP prevents the
337+
* TCF (Transmission Cancellation Finalized) interrupt from triggering,
338+
* despite the correct bit being set in the TXBCF register. It is thus
339+
* necessary to poll TXBCF register to detect when a transmission failed if
340+
* DAR is enabled.
341+
*/
342+
k_timer_start(&data->txbcf_timer, TXBCF_TIMER_TIMEOUT, TXBCF_TIMER_TIMEOUT);
343+
}
344+
325345
data->common.started = true;
326346
pm_device_busy_set(dev);
327347

328348
return err;
329349
}
330350

351+
static int can_mcan_read_txbcf(const struct device *dev)
352+
{
353+
const struct can_mcan_config *config = dev->config;
354+
const struct can_mcan_callbacks *cbs = config->callbacks;
355+
struct can_mcan_data *data = dev->data;
356+
uint32_t txbcfs;
357+
int err;
358+
can_tx_callback_t tx_cb;
359+
void *user_data;
360+
361+
err = can_mcan_read_reg(dev, CAN_MCAN_TXBCF, &txbcfs);
362+
if (err != 0) {
363+
LOG_ERR("failed to read tx cancellation finished (err %d)", err);
364+
return err;
365+
}
366+
367+
if (txbcfs == 0) {
368+
return 0;
369+
}
370+
371+
for (size_t tx_idx = 0; tx_idx < cbs->num_tx; tx_idx++) {
372+
if ((txbcfs & BIT(tx_idx)) == 0) {
373+
continue;
374+
}
375+
376+
if (cbs->tx[tx_idx].function == NULL) {
377+
continue;
378+
}
379+
380+
tx_cb = cbs->tx[tx_idx].function;
381+
user_data = cbs->tx[tx_idx].user_data;
382+
cbs->tx[tx_idx].function = NULL;
383+
LOG_DBG("tx buffer cancellation finished (idx %u)", tx_idx);
384+
k_sem_give(&data->tx_sem);
385+
tx_cb(dev, -EIO, user_data);
386+
}
387+
388+
return 0;
389+
}
390+
391+
static void can_mcan_txbcf_timer_handler(struct k_timer *timer_id)
392+
{
393+
const struct device *dev = k_timer_user_data_get(timer_id);
394+
395+
can_mcan_read_txbcf(dev);
396+
}
397+
331398
static bool can_mcan_rx_filters_exist(const struct device *dev)
332399
{
333400
const struct can_mcan_config *config = dev->config;
@@ -362,6 +429,8 @@ int can_mcan_stop(const struct device *dev)
362429
return -EALREADY;
363430
}
364431

432+
k_timer_stop(&data->txbcf_timer);
433+
365434
/* CAN transmissions are automatically stopped when entering init mode */
366435
err = can_mcan_enter_init_mode(dev, K_MSEC(CAN_INIT_TIMEOUT_MS));
367436
if (err != 0) {
@@ -402,7 +471,7 @@ int can_mcan_stop(const struct device *dev)
402471

403472
int can_mcan_set_mode(const struct device *dev, can_mode_t mode)
404473
{
405-
can_mode_t supported = CAN_MODE_LOOPBACK | CAN_MODE_LISTENONLY;
474+
can_mode_t supported = CAN_MODE_LOOPBACK | CAN_MODE_LISTENONLY | CAN_MODE_ONE_SHOT;
406475
struct can_mcan_data *data = dev->data;
407476
uint32_t cccr;
408477
uint32_t test;
@@ -460,6 +529,13 @@ int can_mcan_set_mode(const struct device *dev, can_mode_t mode)
460529
}
461530
#endif /* CONFIG_CAN_FD_MODE */
462531

532+
if ((mode & CAN_MODE_ONE_SHOT) != 0) {
533+
/* Disable Automatic Retransmission */
534+
cccr |= CAN_MCAN_CCCR_DAR;
535+
} else {
536+
cccr &= ~CAN_MCAN_CCCR_DAR;
537+
}
538+
463539
err = can_mcan_write_reg(dev, CAN_MCAN_CCCR, cccr);
464540
if (err != 0) {
465541
goto unlock;
@@ -1053,6 +1129,21 @@ int can_mcan_send(const struct device *dev, const struct can_frame *frame, k_tim
10531129
}
10541130
}
10551131

1132+
uint32_t cccr;
1133+
1134+
err = can_mcan_read_reg(dev, CAN_MCAN_CCCR, &cccr);
1135+
if (err != 0) {
1136+
return err;
1137+
}
1138+
1139+
if (cccr & CAN_MCAN_CCCR_DAR) {
1140+
/*
1141+
* TXBCR is cleared after TXBAR is set. Stop timer to ensure
1142+
* TXBCR is not read before TXBAR has been set.
1143+
*/
1144+
k_timer_stop(&data->txbcf_timer);
1145+
}
1146+
10561147
__ASSERT_NO_MSG(put_idx < cbs->num_tx);
10571148
cbs->tx[put_idx].function = callback;
10581149
cbs->tx[put_idx].user_data = user_data;
@@ -1063,10 +1154,18 @@ int can_mcan_send(const struct device *dev, const struct can_frame *frame, k_tim
10631154
goto err_unlock;
10641155
}
10651156

1157+
if (cccr & CAN_MCAN_CCCR_DAR) {
1158+
k_timer_start(&data->txbcf_timer, TXBCF_TIMER_TIMEOUT, TXBCF_TIMER_TIMEOUT);
1159+
}
1160+
10661161
k_mutex_unlock(&data->tx_mtx);
10671162
return 0;
10681163

10691164
err_unlock:
1165+
if (cccr & CAN_MCAN_CCCR_DAR) {
1166+
k_timer_start(&data->txbcf_timer, TXBCF_TIMER_TIMEOUT, TXBCF_TIMER_TIMEOUT);
1167+
}
1168+
10701169
k_mutex_unlock(&data->tx_mtx);
10711170
k_sem_give(&data->tx_sem);
10721171

@@ -1424,6 +1523,8 @@ int can_mcan_init(const struct device *dev)
14241523
k_mutex_init(&data->lock);
14251524
k_mutex_init(&data->tx_mtx);
14261525
k_sem_init(&data->tx_sem, cbs->num_tx, cbs->num_tx);
1526+
k_timer_init(&data->txbcf_timer, can_mcan_txbcf_timer_handler, NULL);
1527+
k_timer_user_data_set(&data->txbcf_timer, (void *)dev);
14271528

14281529
if (config->common.phy != NULL && !device_is_ready(config->common.phy)) {
14291530
LOG_ERR("CAN transceiver not ready");
@@ -1565,5 +1666,14 @@ int can_mcan_init(const struct device *dev)
15651666
return err;
15661667
}
15671668

1669+
/*
1670+
* Interrupt on every TX buffer cancellation finished event.
1671+
*/
1672+
reg = CAN_MCAN_TXBCIE_CFIE;
1673+
err = can_mcan_write_reg(dev, CAN_MCAN_TXBCIE, reg);
1674+
if (err != 0) {
1675+
return err;
1676+
}
1677+
15681678
return can_mcan_clear_mram(dev, 0, config->mram_size);
15691679
}

include/zephyr/drivers/can/can_mcan.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1065,6 +1065,7 @@ struct can_mcan_data {
10651065
struct k_mutex lock;
10661066
struct k_sem tx_sem;
10671067
struct k_mutex tx_mtx;
1068+
struct k_timer txbcf_timer;
10681069
void *custom;
10691070
} __aligned(4);
10701071

0 commit comments

Comments
 (0)