drivers: can: Extend CAN API for bus-state management.

This commit extends the CAN API with the following functions:
- can_get_state
- can_recover
- can_register_state_change_isr

This functions can be used to get the error-counters and the state
of the CAN controller. The recover function can be used to recover
from bus-off state when automatic recovery is disabled.

Signed-off-by: Alexander Wachter <alexander.wachter@student.tugraz.at>
This commit is contained in:
Alexander Wachter 2018-06-19 18:26:31 +02:00 committed by Jukka Rissanen
commit c0da8a7901
10 changed files with 598 additions and 56 deletions

View file

@ -18,7 +18,8 @@
#include <logging/log.h>
LOG_MODULE_DECLARE(can_driver, CONFIG_CAN_LOG_LEVEL);
#define INIT_TIMEOUT (10 * sys_clock_hw_cycles_per_sec() / MSEC_PER_SEC)
#define CAN_INIT_TIMEOUT (10 * sys_clock_hw_cycles_per_sec() / MSEC_PER_SEC)
#define CAN_BOFF_RECOVER_TIMEOUT (10 * sys_clock_hw_cycles_per_sec() / MSEC_PER_SEC)
/*
* Translation tables
@ -92,6 +93,32 @@ void can_stm32_rx_isr_handler(CAN_TypeDef *can, struct can_stm32_data *data)
}
}
static inline void can_stm32_bus_state_change_isr(CAN_TypeDef *can,
struct can_stm32_data *data)
{
struct can_bus_err_cnt err_cnt;
enum can_state state;
if (!(can->ESR & CAN_ESR_EPVF) && !(can->ESR & CAN_ESR_BOFF)) {
return;
}
err_cnt.tx_err_cnt = ((can->ESR & CAN_ESR_TEC) >> CAN_ESR_TEC_Pos);
err_cnt.rx_err_cnt = ((can->ESR & CAN_ESR_REC) >> CAN_ESR_REC_Pos);
if (can->ESR & CAN_ESR_BOFF) {
state = CAN_BUS_OFF;
} else if (can->ESR & CAN_ESR_EPVF) {
state = CAN_ERROR_PASSIVE;
} else {
state = CAN_ERROR_ACTIVE;
}
if (data->state_change_isr) {
data->state_change_isr(state, err_cnt);
}
}
static inline
void can_stm32_tx_isr_handler(CAN_TypeDef *can, struct can_stm32_data *data)
{
@ -156,7 +183,10 @@ static void can_stm32_isr(void *arg)
can_stm32_tx_isr_handler(can, data);
can_stm32_rx_isr_handler(can, data);
if (can->MSR & CAN_MSR_ERRI) {
can_stm32_bus_state_change_isr(can, data);
can->MSR |= CAN_MSR_ERRI;
}
}
#else
@ -191,6 +221,27 @@ static void can_stm32_tx_isr(void *arg)
can_stm32_tx_isr_handler(can, data);
}
static void can_stm32_state_change_isr(void *arg)
{
struct device *dev;
struct can_stm32_data *data;
const struct can_stm32_config *cfg;
CAN_TypeDef *can;
dev = (struct device *)arg;
data = DEV_DATA(dev);
cfg = DEV_CFG(dev);
can = cfg->can;
/*Signal bus-off to waiting tx*/
if (can->MSR & CAN_MSR_ERRI) {
can_stm32_tx_isr_handler(can, data);
can_stm32_bus_state_change_isr(can, data);
can->MSR |= CAN_MSR_ERRI;
}
}
#endif
static int can_enter_init_mode(CAN_TypeDef *can)
@ -201,7 +252,8 @@ static int can_enter_init_mode(CAN_TypeDef *can)
start_time = k_cycle_get_32();
while ((can->MSR & CAN_MSR_INAK) == 0U) {
if (k_cycle_get_32() - start_time > INIT_TIMEOUT) {
if (k_cycle_get_32() - start_time > CAN_INIT_TIMEOUT) {
can->MCR &= ~CAN_MCR_INRQ;
return CAN_TIMEOUT;
}
}
@ -217,7 +269,7 @@ static int can_leave_init_mode(CAN_TypeDef *can)
start_time = k_cycle_get_32();
while ((can->MSR & CAN_MSR_INAK) != 0U) {
if (k_cycle_get_32() - start_time > INIT_TIMEOUT) {
if (k_cycle_get_32() - start_time > CAN_INIT_TIMEOUT) {
return CAN_TIMEOUT;
}
}
@ -233,7 +285,7 @@ static int can_leave_sleep_mode(CAN_TypeDef *can)
start_time = k_cycle_get_32();
while ((can->MSR & CAN_MSR_SLAK) != 0) {
if (k_cycle_get_32() - start_time > INIT_TIMEOUT) {
if (k_cycle_get_32() - start_time > CAN_INIT_TIMEOUT) {
return CAN_TIMEOUT;
}
}
@ -247,6 +299,7 @@ int can_stm32_runtime_configure(struct device *dev, enum can_mode mode,
CAN_HandleTypeDef hcan;
const struct can_stm32_config *cfg = DEV_CFG(dev);
CAN_TypeDef *can = cfg->can;
struct can_stm32_data *data = DEV_DATA(dev);
struct device *clock;
u32_t clock_rate;
u32_t prescaler;
@ -301,10 +354,11 @@ int can_stm32_runtime_configure(struct device *dev, enum can_mode mode,
(mode == CAN_SILENT_MODE) ? CAN_BTR_SILM :
CAN_BTR_LBKM | CAN_BTR_SILM;
k_mutex_lock(&data->inst_mutex, K_FOREVER);
ret = can_enter_init_mode(can);
if (ret) {
LOG_ERR("Failed to enter init mode");
return ret;
goto done;
}
can->BTR = reg_mode | sjw | ts1 | ts2 | (prescaler - 1U);
@ -312,11 +366,14 @@ int can_stm32_runtime_configure(struct device *dev, enum can_mode mode,
ret = can_leave_init_mode(can);
if (ret) {
LOG_ERR("Failed to leave init mode");
return ret;
goto done;
}
LOG_DBG("Runtime configure of %s done", dev->config->name);
return 0;
ret = 0;
done:
k_mutex_unlock(&data->inst_mutex);
return ret;
}
static int can_stm32_init(struct device *dev)
@ -327,8 +384,7 @@ static int can_stm32_init(struct device *dev)
struct device *clock;
int ret;
k_mutex_init(&data->tx_mutex);
k_mutex_init(&data->set_filter_mutex);
k_mutex_init(&data->inst_mutex);
k_sem_init(&data->tx_int_sem, 0, 1);
k_sem_init(&data->mb0.tx_int_sem, 0, 1);
k_sem_init(&data->mb1.tx_int_sem, 0, 1);
@ -336,6 +392,7 @@ static int can_stm32_init(struct device *dev)
data->mb0.tx_callback = NULL;
data->mb1.tx_callback = NULL;
data->mb2.tx_callback = NULL;
data->state_change_isr = NULL;
data->filter_usage = (1ULL << CAN_MAX_NUMBER_OF_FILTERS) - 1ULL;
(void)memset(data->rx_cb, 0, sizeof(data->rx_cb));
@ -369,6 +426,9 @@ static int can_stm32_init(struct device *dev)
#ifdef CONFIG_CAN_RX_TIMESTAMP
can->MCR |= CAN_MCR_TTCM;
#endif
#ifdef CONFIG_CAN_AUTO_BUS_OFF_RECOVERY
can->MCR |= CAN_MCR_ABOM;
#endif
ret = can_stm32_runtime_configure(dev, CAN_NORMAL_MODE, 0);
if (ret) {
@ -384,6 +444,88 @@ static int can_stm32_init(struct device *dev)
return 0;
}
static void can_stm32_register_state_change_isr(struct device *dev,
can_state_change_isr_t isr)
{
struct can_stm32_data *data = DEV_DATA(dev);
const struct can_stm32_config *cfg = DEV_CFG(dev);
CAN_TypeDef *can = cfg->can;
data->state_change_isr = isr;
if (isr == NULL) {
can->IER &= ~CAN_IER_EPVIE;
} else {
can->IER |= CAN_IER_EPVIE;
}
}
static enum can_state can_stm32_get_state(struct device *dev,
struct can_bus_err_cnt *err_cnt)
{
const struct can_stm32_config *cfg = DEV_CFG(dev);
CAN_TypeDef *can = cfg->can;
if (err_cnt) {
err_cnt->tx_err_cnt =
((can->ESR & CAN_ESR_TEC) >> CAN_ESR_TEC_Pos);
err_cnt->rx_err_cnt =
((can->ESR & CAN_ESR_REC) >> CAN_ESR_REC_Pos);
}
if (can->ESR & CAN_ESR_BOFF) {
return CAN_BUS_OFF;
}
if (can->ESR & CAN_ESR_EPVF) {
return CAN_ERROR_PASSIVE;
}
return CAN_ERROR_ACTIVE;
}
#ifndef CONFIG_CAN_AUTO_BUS_OFF_RECOVERY
int can_stm32_recover(struct device *dev, s32_t timeout)
{
const struct can_stm32_config *cfg = DEV_CFG(dev);
struct can_stm32_data *data = DEV_DATA(dev);
CAN_TypeDef *can = cfg->can;
int ret = CAN_TIMEOUT;
u32_t start_time;
if (!(can->ESR & CAN_ESR_BOFF)) {
return 0;
}
if (k_mutex_lock(&data->inst_mutex, K_FOREVER)) {
return CAN_TIMEOUT;
}
ret = can_enter_init_mode(can);
if (ret) {
goto done;
}
can_leave_init_mode(can);
start_time = k_cycle_get_32();
while (can->ESR & CAN_ESR_BOFF) {
if (k_cycle_get_32() - start_time >= CAN_BOFF_RECOVER_TIMEOUT) {
goto done;
}
}
ret = 0;
done:
k_mutex_unlock(&data->inst_mutex);
return ret;
}
#endif /* CONFIG_CAN_AUTO_BUS_OFF_RECOVERY */
int can_stm32_send(struct device *dev, const struct zcan_frame *msg,
s32_t timeout, can_tx_callback_t callback, void *callback_arg)
{
@ -392,7 +534,6 @@ int can_stm32_send(struct device *dev, const struct zcan_frame *msg,
CAN_TypeDef *can = cfg->can;
u32_t transmit_status_register = can->TSR;
CAN_TxMailBox_TypeDef *mailbox = NULL;
struct k_mutex *tx_mutex = &data->tx_mutex;
struct can_mailbox *mb = NULL;
LOG_DBG("Sending %d bytes on %s. "
@ -413,16 +554,16 @@ int can_stm32_send(struct device *dev, const struct zcan_frame *msg,
return CAN_TX_BUS_OFF;
}
k_mutex_lock(tx_mutex, K_FOREVER);
k_mutex_lock(&data->inst_mutex, K_FOREVER);
while (!(transmit_status_register & CAN_TSR_TME)) {
k_mutex_unlock(tx_mutex);
k_mutex_unlock(&data->inst_mutex);
LOG_DBG("Transmit buffer full. Wait with timeout (%dms)",
timeout);
if (k_sem_take(&data->tx_int_sem, timeout)) {
return CAN_TIMEOUT;
}
k_mutex_lock(tx_mutex, K_FOREVER);
k_mutex_lock(&data->inst_mutex, K_FOREVER);
transmit_status_register = can->TSR;
}
@ -465,7 +606,7 @@ int can_stm32_send(struct device *dev, const struct zcan_frame *msg,
mailbox->TDHR = msg->data_32[1];
mailbox->TIR |= CAN_TI0R_TXRQ;
k_mutex_unlock(tx_mutex);
k_mutex_unlock(&data->inst_mutex);
if (callback == NULL) {
k_sem_take(&mb->tx_int_sem, K_FOREVER);
@ -817,9 +958,9 @@ int can_stm32_attach_isr(struct device *dev, can_rx_callback_t isr,
struct can_stm32_data *data = DEV_DATA(dev);
int filter_nr;
k_mutex_lock(&data->set_filter_mutex, K_FOREVER);
k_mutex_lock(&data->inst_mutex, K_FOREVER);
filter_nr = can_stm32_attach(dev, isr, cb_arg, filter);
k_mutex_unlock(&data->set_filter_mutex);
k_mutex_unlock(&data->inst_mutex);
return filter_nr;
}
@ -838,7 +979,7 @@ void can_stm32_detach(struct device *dev, int filter_nr)
__ASSERT_NO_MSG(filter_nr >= 0 && filter_nr < CAN_MAX_NUMBER_OF_FILTERS);
k_mutex_lock(&data->set_filter_mutex, K_FOREVER);
k_mutex_lock(&data->inst_mutex, K_FOREVER);
bank_nr = filter_nr / 4;
bank_bit = (1U << bank_nr);
@ -870,14 +1011,19 @@ void can_stm32_detach(struct device *dev, int filter_nr)
data->rx_cb[filter_index] = NULL;
data->cb_arg[filter_index] = NULL;
k_mutex_unlock(&data->set_filter_mutex);
k_mutex_unlock(&data->inst_mutex);
}
static const struct can_driver_api can_api_funcs = {
.configure = can_stm32_runtime_configure,
.send = can_stm32_send,
.attach_isr = can_stm32_attach_isr,
.detach = can_stm32_detach
.detach = can_stm32_detach,
.get_state = can_stm32_get_state,
#ifndef CONFIG_CAN_AUTO_BUS_OFF_RECOVERY
.recover = can_stm32_recover,
#endif
.register_state_change_isr = can_stm32_register_state_change_isr
};
#ifdef CONFIG_CAN_1
@ -921,7 +1067,7 @@ static void config_can_1_irq(CAN_TypeDef *can)
irq_enable(DT_CAN_1_IRQ_TX);
IRQ_CONNECT(DT_CAN_1_IRQ_SCE, DT_CAN_1_IRQ_PRIORITY,
can_stm32_tx_isr, DEVICE_GET(can_stm32_1), 0);
can_stm32_state_change_isr, DEVICE_GET(can_stm32_1), 0);
irq_enable(DT_CAN_1_IRQ_SCE);
#endif
can->IER |= CAN_IER_TMEIE | CAN_IER_ERRIE | CAN_IER_FMPIE0 |