Skip to content

Commit d43ce71

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 d43ce71

File tree

3 files changed

+97
-2
lines changed

3 files changed

+97
-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: 81 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,68 @@ 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+
k_timer_start(&data->txbcf_timer, TXBCF_TIMER_TIMEOUT, TXBCF_TIMER_TIMEOUT);
334+
}
335+
325336
data->common.started = true;
326337
pm_device_busy_set(dev);
327338

328339
return err;
329340
}
330341

342+
static int can_mcan_read_txbcf(const struct device *dev)
343+
{
344+
const struct can_mcan_config *config = dev->config;
345+
const struct can_mcan_callbacks *cbs = config->callbacks;
346+
struct can_mcan_data *data = dev->data;
347+
uint32_t txbcfs;
348+
int err;
349+
can_tx_callback_t tx_cb;
350+
void *user_data;
351+
352+
err = can_mcan_read_reg(dev, CAN_MCAN_TXBCF, &txbcfs);
353+
if (err != 0) {
354+
LOG_ERR("failed to read tx cancellation finished (err %d)", err);
355+
return err;
356+
}
357+
358+
if (txbcfs == 0) {
359+
return 0;
360+
}
361+
362+
for (size_t tx_idx = 0; tx_idx < cbs->num_tx; tx_idx++) {
363+
if ((txbcfs & BIT(tx_idx)) == 0) {
364+
continue;
365+
}
366+
367+
if (cbs->tx[tx_idx].function == NULL) {
368+
continue;
369+
}
370+
371+
tx_cb = cbs->tx[tx_idx].function;
372+
user_data = cbs->tx[tx_idx].user_data;
373+
cbs->tx[tx_idx].function = NULL;
374+
LOG_DBG("tx buffer cancellation finished (idx %u)", tx_idx);
375+
k_sem_give(&data->tx_sem);
376+
tx_cb(dev, -EIO, user_data);
377+
}
378+
379+
return 0;
380+
}
381+
382+
static void can_mcan_txbcf_timer_handler(struct k_timer *timer_id)
383+
{
384+
const struct device *dev = k_timer_user_data_get(timer_id);
385+
386+
can_mcan_read_txbcf(dev);
387+
}
388+
331389
static bool can_mcan_rx_filters_exist(const struct device *dev)
332390
{
333391
const struct can_mcan_config *config = dev->config;
@@ -362,6 +420,8 @@ int can_mcan_stop(const struct device *dev)
362420
return -EALREADY;
363421
}
364422

423+
k_timer_stop(&data->txbcf_timer);
424+
365425
/* CAN transmissions are automatically stopped when entering init mode */
366426
err = can_mcan_enter_init_mode(dev, K_MSEC(CAN_INIT_TIMEOUT_MS));
367427
if (err != 0) {
@@ -402,7 +462,7 @@ int can_mcan_stop(const struct device *dev)
402462

403463
int can_mcan_set_mode(const struct device *dev, can_mode_t mode)
404464
{
405-
can_mode_t supported = CAN_MODE_LOOPBACK | CAN_MODE_LISTENONLY;
465+
can_mode_t supported = CAN_MODE_LOOPBACK | CAN_MODE_LISTENONLY | CAN_MODE_ONE_SHOT;
406466
struct can_mcan_data *data = dev->data;
407467
uint32_t cccr;
408468
uint32_t test;
@@ -460,6 +520,13 @@ int can_mcan_set_mode(const struct device *dev, can_mode_t mode)
460520
}
461521
#endif /* CONFIG_CAN_FD_MODE */
462522

523+
if ((mode & CAN_MODE_ONE_SHOT) != 0) {
524+
/* Disable Automatic Retransmission */
525+
cccr |= CAN_MCAN_CCCR_DAR;
526+
} else {
527+
cccr &= ~CAN_MCAN_CCCR_DAR;
528+
}
529+
463530
err = can_mcan_write_reg(dev, CAN_MCAN_CCCR, cccr);
464531
if (err != 0) {
465532
goto unlock;
@@ -694,6 +761,7 @@ void can_mcan_line_0_isr(const struct device *dev)
694761
#ifdef CONFIG_CAN_STATS
695762
if ((ir & (CAN_MCAN_IR_PEA | CAN_MCAN_IR_PED)) != 0U) {
696763
uint32_t reg;
764+
697765
/* This function automatically updates protocol error stats */
698766
can_mcan_read_psr(dev, &reg);
699767
}
@@ -1424,6 +1492,8 @@ int can_mcan_init(const struct device *dev)
14241492
k_mutex_init(&data->lock);
14251493
k_mutex_init(&data->tx_mtx);
14261494
k_sem_init(&data->tx_sem, cbs->num_tx, cbs->num_tx);
1495+
k_timer_init(&data->txbcf_timer, can_mcan_txbcf_timer_handler, NULL);
1496+
k_timer_user_data_set(&data->txbcf_timer, (void *)dev);
14271497

14281498
if (config->common.phy != NULL && !device_is_ready(config->common.phy)) {
14291499
LOG_ERR("CAN transceiver not ready");
@@ -1565,5 +1635,14 @@ int can_mcan_init(const struct device *dev)
15651635
return err;
15661636
}
15671637

1638+
/*
1639+
* Interrupt on every TX buffer cancellation finished event.
1640+
*/
1641+
reg = CAN_MCAN_TXBCIE_CFIE;
1642+
err = can_mcan_write_reg(dev, CAN_MCAN_TXBCIE, reg);
1643+
if (err != 0) {
1644+
return err;
1645+
}
1646+
15681647
return can_mcan_clear_mram(dev, 0, config->mram_size);
15691648
}

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)