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:
parent
1f046d86a0
commit
c0da8a7901
10 changed files with 598 additions and 56 deletions
|
@ -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 |
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue