Skip to content

Commit 906e5f6

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 6dbdd28 commit 906e5f6

File tree

3 files changed

+104
-2
lines changed

3 files changed

+104
-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+
neccesary 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: 88 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;
@@ -294,6 +295,7 @@ int can_mcan_start(const struct device *dev)
294295
const struct can_mcan_config *config = dev->config;
295296
struct can_mcan_data *data = dev->data;
296297
int err = 0;
298+
uint32_t cccr;
297299

298300
if (data->common.started) {
299301
return -EALREADY;
@@ -322,12 +324,76 @@ int can_mcan_start(const struct device *dev)
322324
return err;
323325
}
324326

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

328347
return err;
329348
}
330349

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

431+
k_timer_stop(&data->txbcf_timer);
432+
365433
/* CAN transmissions are automatically stopped when entering init mode */
366434
err = can_mcan_enter_init_mode(dev, K_MSEC(CAN_INIT_TIMEOUT_MS));
367435
if (err != 0) {
@@ -402,7 +470,7 @@ int can_mcan_stop(const struct device *dev)
402470

403471
int can_mcan_set_mode(const struct device *dev, can_mode_t mode)
404472
{
405-
can_mode_t supported = CAN_MODE_LOOPBACK | CAN_MODE_LISTENONLY;
473+
can_mode_t supported = CAN_MODE_LOOPBACK | CAN_MODE_LISTENONLY | CAN_MODE_ONE_SHOT;
406474
struct can_mcan_data *data = dev->data;
407475
uint32_t cccr;
408476
uint32_t test;
@@ -460,6 +528,13 @@ int can_mcan_set_mode(const struct device *dev, can_mode_t mode)
460528
}
461529
#endif /* CONFIG_CAN_FD_MODE */
462530

531+
if ((mode & CAN_MODE_ONE_SHOT) != 0) {
532+
/* Disable Automatic Retransmission */
533+
cccr |= CAN_MCAN_CCCR_DAR;
534+
} else {
535+
cccr &= ~CAN_MCAN_CCCR_DAR;
536+
}
537+
463538
err = can_mcan_write_reg(dev, CAN_MCAN_CCCR, cccr);
464539
if (err != 0) {
465540
goto unlock;
@@ -1424,6 +1499,8 @@ int can_mcan_init(const struct device *dev)
14241499
k_mutex_init(&data->lock);
14251500
k_mutex_init(&data->tx_mtx);
14261501
k_sem_init(&data->tx_sem, cbs->num_tx, cbs->num_tx);
1502+
k_timer_init(&data->txbcf_timer, can_mcan_txbcf_timer_handler, NULL);
1503+
k_timer_user_data_set(&data->txbcf_timer, (void *)dev);
14271504

14281505
if (config->common.phy != NULL && !device_is_ready(config->common.phy)) {
14291506
LOG_ERR("CAN transceiver not ready");
@@ -1565,5 +1642,14 @@ int can_mcan_init(const struct device *dev)
15651642
return err;
15661643
}
15671644

1645+
/*
1646+
* Interrupt on every TX buffer cancellation finished event.
1647+
*/
1648+
reg = CAN_MCAN_TXBCIE_CFIE;
1649+
err = can_mcan_write_reg(dev, CAN_MCAN_TXBCIE, reg);
1650+
if (err != 0) {
1651+
return err;
1652+
}
1653+
15681654
return can_mcan_clear_mram(dev, 0, config->mram_size);
15691655
}

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)