From ae95137f9523fcea43481e0132151c31eef87a70 Mon Sep 17 00:00:00 2001 From: Vincent Dupont Date: Thu, 24 Nov 2016 16:29:33 +0100 Subject: [PATCH] can stm32: add a driver for STM32 bxCAN peripheral This driver is compliant with the candev interface. It has been tested with STM32F0 and STM32F2 and STM32F413 ONLY at this time but should be compliant with other STM32Fx devices --- cpu/stm32_common/include/can_params.h | 138 ++ cpu/stm32_common/include/candev_stm32.h | 193 +++ cpu/stm32_common/include/periph_cpu_common.h | 4 + cpu/stm32_common/periph/can.c | 1263 ++++++++++++++++++ drivers/include/periph/can.h | 57 + sys/auto_init/can/auto_init_can.c | 10 + sys/auto_init/can/auto_init_periph_can.c | 57 + 7 files changed, 1722 insertions(+) create mode 100644 cpu/stm32_common/include/can_params.h create mode 100644 cpu/stm32_common/include/candev_stm32.h create mode 100644 cpu/stm32_common/periph/can.c create mode 100644 drivers/include/periph/can.h create mode 100644 sys/auto_init/can/auto_init_periph_can.c diff --git a/cpu/stm32_common/include/can_params.h b/cpu/stm32_common/include/can_params.h new file mode 100644 index 0000000000..ff4f80fd88 --- /dev/null +++ b/cpu/stm32_common/include/can_params.h @@ -0,0 +1,138 @@ +/* + * Copyright (C) 2016 OTA keys S.A. + * + * This file is subject to the terms and conditions of the GNU Lesser General + * Public License v2.1. See the file LICENSE in the top level directory for more + * details. + */ + +/** + * @ingroup candev_stm32 + * @{ + * + * @file + * @brief STM32 CAN controller driver (bxCAN) default parameters + * + * @author Vincent Dupont + * @} + */ + +#ifndef CANDEV_STM32_PARAMS_H +#define CANDEV_STM32_PARAMS_H + +#include "can/device.h" +#include "periph/can.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** Default STM32 CAN devices config */ +static const can_conf_t candev_conf[] = { + { +#if defined(CPU_FAM_STM32F0) + .can = CAN, + .rcc_mask = RCC_APB1ENR_CANEN, + .rx_pin = GPIO_PIN(PORT_A, 11), + .tx_pin = GPIO_PIN(PORT_A, 12), + .af = GPIO_AF4, + .irqn = CEC_CAN_IRQn, +#else + .can = CAN1, + .rcc_mask = RCC_APB1ENR_CAN1EN, + .can_master = CAN1, + .master_rcc_mask = RCC_APB1ENR_CAN1EN, + .first_filter = 0, + .nb_filters = 14, +#ifndef CPU_FAM_STM32F1 + .rx_pin = GPIO_PIN(PORT_D, 0), + .tx_pin = GPIO_PIN(PORT_D, 1), + .af = GPIO_AF9, +#else + .rx_pin = GPIO_PIN(PORT_A, 11), + .tx_pin = GPIO_PIN(PORT_A, 12), +#endif + .tx_irqn = CAN1_TX_IRQn, + .rx0_irqn = CAN1_RX0_IRQn, + .rx1_irqn = CAN1_RX1_IRQn, + .sce_irqn = CAN1_SCE_IRQn, +#endif + .ttcm = 0, + .abom = 1, + .awum = 1, + .nart = 0, + .rflm = 0, + .txfp = 0, + }, +#if (CANDEV_STM32_CHAN_NUMOF >= 2) && (CAN_DLL_NUMOF >= 2) + { + .can = CAN2, + .rcc_mask = RCC_APB1ENR_CAN2EN, + .can_master = CAN1, + .master_rcc_mask = RCC_APB1ENR_CAN1EN, + .first_filter = 14, + .nb_filters = 14, + .rx_pin = GPIO_PIN(PORT_B, 5), + .tx_pin = GPIO_PIN(PORT_B, 6), +#ifndef CPU_FAM_STM32F1 + .af = GPIO_AF9, +#endif + .tx_irqn = CAN2_TX_IRQn, + .rx0_irqn = CAN2_RX0_IRQn, + .rx1_irqn = CAN2_RX1_IRQn, + .sce_irqn = CAN2_SCE_IRQn, + .ttcm = 0, + .abom = 1, + .awum = 1, + .nart = 0, + .rflm = 0, + .txfp = 0, + }, +#endif +#if (CANDEV_STM32_CHAN_NUMOF >= 3) && (CAN_DLL_NUMOF >= 3) + { + .can = CAN3, + .rcc_mask = RCC_APB1ENR_CAN3EN, + .can_master = CAN3, + .master_rcc_mask = RCC_APB1ENR_CAN3EN, + .first_filter = 0, + .nb_filters = 14, + .rx_pin = GPIO_PIN(PORT_B, 3), + .tx_pin = GPIO_PIN(PORT_B, 4), + .af = GPIO_AF11, + .tx_irqn = CAN3_TX_IRQn, + .rx0_irqn = CAN3_RX0_IRQn, + .rx1_irqn = CAN3_RX1_IRQn, + .sce_irqn = CAN3_SCE_IRQn, + .ttcm = 0, + .abom = 1, + .awum = 1, + .nart = 0, + .rflm = 0, + .txfp = 0, + }, +#endif +}; + +/** Default STM32 CAN devices parameters */ +static const candev_params_t candev_params[] = { + { + .name = "can_stm32_0", + }, +#if (CANDEV_STM32_CHAN_NUMOF >= 2) && (CAN_DLL_NUMOF >= 2) + { + .name = "can_stm32_1", + }, +#endif +#if (CANDEV_STM32_CHAN_NUMOF >= 3) && (CAN_DLL_NUMOF >= 3) + { + .name = "can_stm32_2", + }, +#endif +}; + +#ifdef __cplusplus +} +#endif + +#endif /* CANDEV_STM32_PARAMS_H */ diff --git a/cpu/stm32_common/include/candev_stm32.h b/cpu/stm32_common/include/candev_stm32.h new file mode 100644 index 0000000000..c4ec14860f --- /dev/null +++ b/cpu/stm32_common/include/candev_stm32.h @@ -0,0 +1,193 @@ +/* + * Copyright (C) 2016 OTA keys S.A. + * + * This file is subject to the terms and conditions of the GNU Lesser General + * Public License v2.1. See the file LICENSE in the top level directory for more + * details. + */ + +/** + * @ingroup cpu_stm32_common + * @ingroup drivers_can + * @defgroup candev_stm32 STM32 CAN controller + * @brief STM32 CAN controller driver (bxCAN) + * + * The STM32Fx microcontroller can have an integrated CAN controller (bxCAN) + * + * This driver has been tested with a STM32F0,STM32F2 and STM32F4 MCU + * but should work on others + * @{ + * + * @file + * @brief bxCAN specific definitions + * + * @author Vincent Dupont + * @} + */ + +#ifndef CANDEV_STM32_H +#define CANDEV_STM32_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "can/candev.h" + +#if defined(CPU_LINE_STM32F413xx) || defined(CPU_LINE_STM32F423xx) +#define CANDEV_STM32_CHAN_NUMOF 3 +#elif defined(CPU_FAM_STM32F1) || defined(CPU_FAM_STM32F2) || defined(CPU_FAM_STM32F4) +#define CANDEV_STM32_CHAN_NUMOF 2 +#elif defined(CPU_FAM_STM32F0) || defined(CPU_FAM_STM32F3) || DOXYGEN +/** Number of channels in the device (up to 3) */ +#define CANDEV_STM32_CHAN_NUMOF 1 +#else +#error "CAN STM32: CPU not supported" +#endif + +#if defined(CPU_FAM_STM32F1) +#define ISR_CAN1_TX isr_usb_hp_can1_tx +#define ISR_CAN1_RX0 isr_usb_lp_can1_rx0 +#define ISR_CAN1_RX1 isr_can1_rx1 +#define ISR_CAN1_SCE isr_can1_sce +#else +#define ISR_CAN1_TX isr_can1_tx +#define ISR_CAN1_RX0 isr_can1_rx0 +#define ISR_CAN1_RX1 isr_can1_rx1 +#define ISR_CAN1_SCE isr_can1_sce +#define ISR_CAN2_TX isr_can2_tx +#define ISR_CAN2_RX0 isr_can2_rx0 +#define ISR_CAN2_RX1 isr_can2_rx1 +#define ISR_CAN2_SCE isr_can2_sce +#define ISR_CAN3_TX isr_can3_tx +#define ISR_CAN3_RX0 isr_can3_rx0 +#define ISR_CAN3_RX1 isr_can3_rx1 +#define ISR_CAN3_SCE isr_can3_sce +#endif + +#if CANDEV_STM32_CHAN_NUMOF > 1 || DOXYGEN +/** The maximum number of filters: 28 for dual channel, 14 for single channel */ +#define CAN_STM32_NB_FILTER 28 +#else +#define CAN_STM32_NB_FILTER 14 +#endif + +#ifndef CANDEV_STM32_DEFAULT_BITRATE +/** Default bitrate */ +#define CANDEV_STM32_DEFAULT_BITRATE 500000U +#endif + +#ifndef CANDEV_STM32_DEFAULT_SPT +/** Default sampling-point */ +#define CANDEV_STM32_DEFAULT_SPT 875 +#endif + +/** bxCAN device configuration */ +typedef struct { + CAN_TypeDef *can; /**< CAN device */ + uint32_t rcc_mask; /**< RCC mask to enable clock */ + gpio_t rx_pin; /**< RX pin */ + gpio_t tx_pin; /**< TX pin */ +#ifndef CPU_FAM_STM32F1 + gpio_af_t af; /**< Alternate pin function to use */ +#endif +#if CANDEV_STM32_CHAN_NUMOF > 1 || defined(DOXYGEN) + CAN_TypeDef *can_master; /**< Master CAN device */ + uint32_t master_rcc_mask; /**< Master device RCC mask */ + /** First filter in the bank. For a master channel it must be 0. + * For a slave channel, it is used without checking with the master channel, + * beware bot to overwrite the master config. */ + uint8_t first_filter; + /** Number of filters to use. Must be less or equal + * to CAN_STM32_NB_FILTER - first_filter */ + uint8_t nb_filters; +#endif +#if defined(CPU_FAM_STM32F0) + uint8_t irqn; /**< CAN common IRQ channel */ +#else + uint8_t tx_irqn; /**< TX IRQ channel */ + uint8_t rx0_irqn; /**< RX0 IRQ channel */ + uint8_t rx1_irqn; /**< RX1 IRQ channel */ + uint8_t sce_irqn; /**< SCE IRQ channel */ +#endif + uint8_t ttcm : 1; /**< Time triggered communication mode */ + uint8_t abom : 1; /**< Automatic bus-off management */ + uint8_t awum : 1; /**< Automatic wakeup mode */ + uint8_t nart : 1; /**< No automatic retransmission */ + uint8_t rflm : 1; /**< Receive FIFO locked mode */ + uint8_t txfp : 1; /**< Transmit FIFO priority */ + uint8_t lbkm : 1; /**< Loopback mode */ + uint8_t silm : 1; /**< Silent mode */ +} can_conf_t; +#define HAVE_CAN_CONF_T + +/** The number of transmit mailboxes */ +#define CAN_STM32_TX_MAILBOXES 3 +/** The number of receive FIFO */ +#define CAN_STM32_RX_MAILBOXES 2 + + +#ifndef CAN_STM32_RX_MAIL_FIFO +/** This is the maximum number of frame the driver can receive simultaneously */ +#define CAN_STM32_RX_MAIL_FIFO 12 +#endif + +/** bxCAN candev descriptor */ +typedef struct can can_t; +#define HAVE_CAN_T + +/** This structure holds anything related to the receive part */ +typedef struct candev_stm32_rx_fifo { + struct can_frame frame[CAN_STM32_RX_MAIL_FIFO]; /**< Receive FIFO */ + int write_idx; /**< Write index in the receive FIFO */ + int read_idx; /**< Read index in the receive FIFO*/ + int is_full; /**< Flag set when the FIFO is full */ +} candev_stm32_rx_fifo_t; + +/** Internal interrupt flags */ +typedef struct candev_stm32_isr { + int isr_tx : 3; /**< Tx mailboxes interrupt */ + int isr_rx : 2; /**< Rx FIFO interrupt */ + int isr_wkup : 1; /**< Wake up interrupt */ +} candev_stm32_isr_t; + +/** STM32 CAN device descriptor */ +struct can { + candev_t candev; /**< Common candev struct */ + const can_conf_t *conf; /**< Configuration */ + gpio_t rx_pin; /**< RX pin */ + gpio_t tx_pin; /**< TX pin */ + gpio_af_t af; /**< Alternate pin function to use */ + /** Tx mailboxes */ + const struct can_frame *tx_mailbox[CAN_STM32_TX_MAILBOXES]; + candev_stm32_rx_fifo_t rx_fifo; /**< Rx FIFOs */ + candev_stm32_isr_t isr_flags; /**< ISR flags */ +}; + +#ifndef CPU_FAM_STM32F1 +/** + * @brief Set the pins of an stm32 CAN device + * + * @param[in,out] dev the device to set pins + * @param[in] tx_pin tx pin + * @param[in] rx_pin rx pin + * @param[in] af alternate function + */ +void candev_stm32_set_pins(can_t *dev, gpio_t tx_pin, gpio_t rx_pin, + gpio_af_t af); +#else +/** + * @brief Set the pins of an stm32 CAN device + * + * @param[in,out] dev the device to set pins + * @param[in] tx_pin tx pin + * @param[in] rx_pin rx pin + */ +void candev_stm32_set_pins(can_t *dev, gpio_t tx_pin, gpio_t rx_pin); +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* CANDEV_STM32_H */ diff --git a/cpu/stm32_common/include/periph_cpu_common.h b/cpu/stm32_common/include/periph_cpu_common.h index 2debe87737..19e1bc060a 100644 --- a/cpu/stm32_common/include/periph_cpu_common.h +++ b/cpu/stm32_common/include/periph_cpu_common.h @@ -704,6 +704,10 @@ int dma_configure(dma_t dma, int chan, const volatile void *src, volatile void * #endif /* MODULE_PERIPH_DMA */ +#ifdef MODULE_PERIPH_CAN +#include "candev_stm32.h" +#endif + #ifdef __cplusplus } #endif diff --git a/cpu/stm32_common/periph/can.c b/cpu/stm32_common/periph/can.c new file mode 100644 index 0000000000..19717cd2d9 --- /dev/null +++ b/cpu/stm32_common/periph/can.c @@ -0,0 +1,1263 @@ +/* + * Copyright (C) 2016-2018 OTA keys S.A. + * + * This file is subject to the terms and conditions of the GNU Lesser General + * Public License v2.1. See the file LICENSE in the top level directory for more + * details. + */ + +/** + * @ingroup cpu_stm32_common + * @{ + * + * @file + * @brief Implementation of the CAN controller driver + * + * @author Vincent Dupont + * @} + */ + +#include +#include +#include +#include + +#include "periph/can.h" +#include "periph/gpio.h" +#include "can/device.h" +#include "can/common.h" +#include "periph_conf.h" +#include "pm_layered.h" +#include "thread.h" +#include "sched.h" +#include "mutex.h" + +#define ENABLE_DEBUG (0) +#include "debug.h" + +#define CAN_MAX_WAIT_CHANGE (10000U) +#define CAN_TIxR_SFF_SHIFT (21U) +#define CAN_TIxR_EFF_SHIFT (3U) + +#define CAN_MCR_BIT_DONT_TOUCH (0xFFFFFF03) + +typedef enum { + MODE_NORMAL, + MODE_SLEEP, + MODE_INIT, +} can_mode_t; + +static int _init(candev_t *candev); +static void _isr(candev_t *candev); +static int _send(candev_t *candev, const struct can_frame *frame); +static int _abort(candev_t *candev, const struct can_frame *frame); +static int _set(candev_t *candev, canopt_t opt, void *value, size_t value_len); +static int _get(candev_t *candev, canopt_t opt, void *value, size_t max_len); +static int _set_filter(candev_t *candev, const struct can_filter *filter); +static int _remove_filter(candev_t *candev, const struct can_filter *filter); + +static void tx_irq_handler(can_t *dev); +static void tx_isr(can_t *dev); +static void tx_conf(can_t *dev, int mailbox); +static void rx_irq_handler(can_t *dev, int mailbox); +static void rx_isr(can_t *dev); +static void sce_irq_handler(can_t *dev); + +static inline void set_bit_timing(can_t *dev); + +static inline can_mode_t get_mode(CAN_TypeDef *can); +static int set_mode(CAN_TypeDef *can, can_mode_t mode); + +static const candev_driver_t candev_stm32_driver = { + .send = _send, + .init = _init, + .isr = _isr, + .get = _get, + .set = _set, + .abort = _abort, + .set_filter = _set_filter, + .remove_filter = _remove_filter, +}; + +static const struct can_bittiming_const bittiming_const = { + .tseg1_min = 1, + .tseg1_max = 16, + .tseg2_min = 1, + .tseg2_max = 8, + .sjw_max = 1, + .brp_min = 1, + .brp_max = 1024, + .brp_inc = 1, +}; + +enum { + STATUS_NOT_USED, + STATUS_ON, + STATUS_READY_FOR_SLEEP, + STATUS_SLEEP, +}; + +static uint8_t _status[CANDEV_STM32_CHAN_NUMOF]; + +static can_t *_can[CANDEV_STM32_CHAN_NUMOF]; + +static inline int get_channel(CAN_TypeDef *can) +{ +#if CANDEV_STM32_CHAN_NUMOF == 1 + (void)can; + return 0; +#else + return (int) (((uint32_t)can - (uint32_t)CAN1) >> 10); +#endif +} + +static inline can_mode_t get_mode(CAN_TypeDef *can) +{ + if ((can->MCR & CAN_MCR_SLEEP) == CAN_MCR_SLEEP) { + return MODE_SLEEP; + } + else if ((can->MSR & CAN_MSR_INAK) == CAN_MSR_INAK) { + return MODE_INIT; + } + else { + return MODE_NORMAL; + } +} + +static int set_mode(CAN_TypeDef *can, can_mode_t mode) +{ + int max_loop = CAN_MAX_WAIT_CHANGE; + int res = 0; + + switch (mode) { + case MODE_NORMAL: + can->MCR &= ~(CAN_MCR_SLEEP | CAN_MCR_INRQ); + /* wait for hardare confirmation */ + while (((can->MSR & CAN_MSR_INAK) || (can->MSR & CAN_MSR_SLAK)) && max_loop != 0) { + max_loop--; + } + break; + case MODE_SLEEP: + /* set sleep mode */ + can->MCR &= ~CAN_MCR_INRQ; + can->MCR |= CAN_MCR_SLEEP; + while (((can->MSR & CAN_MSR_INAK) || !(can->MSR & CAN_MSR_SLAK)) && max_loop != 0) { + max_loop--; + } + break; + case MODE_INIT: + can->MCR &= ~CAN_MCR_SLEEP; + can->MCR |= CAN_MCR_INRQ; + /* wait for hardare confirmation */ + while ((!(can->MSR & CAN_MSR_INAK) || (can->MSR & CAN_MSR_SLAK)) && max_loop != 0) { + max_loop--; + } + break; + default: + DEBUG("unsupported mode\n"); + res = -1; + break; + } + + if (max_loop == 0) { + DEBUG("candev_stm32: didn't switch mode %d\n", mode); + res = -1; + } + return res; +} + +static inline int filter_is_set(CAN_TypeDef *master, uint8_t filter) +{ + return (master->FA1R & (1 << filter)) >> filter; +} + +void can_init(can_t *dev, const can_conf_t *conf) +{ + dev->candev.driver = &candev_stm32_driver; + + struct can_bittiming timing = { .bitrate = CANDEV_STM32_DEFAULT_BITRATE, + .sample_point = CANDEV_STM32_DEFAULT_SPT }; + can_device_calc_bittiming(CLOCK_APB1, &bittiming_const, &timing); + + memcpy(&dev->candev.bittiming, &timing, sizeof(timing)); + dev->conf = conf; + + dev->rx_pin = GPIO_UNDEF; + dev->tx_pin = GPIO_UNDEF; +} + +static void set_filter(CAN_TypeDef *can, uint32_t fr1, uint32_t fr2, uint8_t filter, uint8_t fifo) +{ + /* Set filter/mask */ + if ((fr1 & CAN_EFF_FLAG) == CAN_EFF_FLAG) { + can->sFilterRegister[filter].FR1 = ((fr1 & CAN_EFF_MASK) << CAN_TIxR_EFF_SHIFT) + | CAN_TI0R_IDE; + can->sFilterRegister[filter].FR2 = ((fr2 & CAN_EFF_MASK) << CAN_TIxR_EFF_SHIFT) + | CAN_TI0R_IDE; + } + else { + can->sFilterRegister[filter].FR1 = (fr1 & CAN_SFF_MASK) << CAN_TIxR_SFF_SHIFT; + can->sFilterRegister[filter].FR2 = (fr2 & CAN_SFF_MASK) << CAN_TIxR_SFF_SHIFT; + } + if (fifo == 0) { + can->FFA1R &= ~(1 << filter); /* To FIFO 0 */ + } + else { + can->FFA1R |= (1 << filter); /* To FIFO 1 */ + } + can->FM1R &= ~(1 << filter); /* Identifier Mask mode */ + can->FS1R |= (1 << filter); /* Single 32-bit scale config */ + can->FA1R |= (1 << filter); /* Activate filer */ +} + +static void get_can_filter(CAN_TypeDef *can, uint8_t filter_id, uint32_t *filter, uint32_t *mask) +{ + uint32_t fr1 = can->sFilterRegister[filter_id].FR1; + uint32_t fr2 = can->sFilterRegister[filter_id].FR2; + + if ((fr1 & CAN_TI0R_IDE) == CAN_TI0R_IDE) { + *filter = ((fr1 >> CAN_TIxR_EFF_SHIFT) & CAN_EFF_MASK) | CAN_EFF_FLAG; + *mask = ((fr2 >> CAN_TIxR_EFF_SHIFT) & CAN_EFF_MASK) | CAN_EFF_FLAG; + } + else { + *filter = (fr1 >> CAN_TIxR_SFF_SHIFT) & CAN_SFF_MASK; + *mask = (fr2 >> CAN_TIxR_SFF_SHIFT) & CAN_SFF_MASK; + } + if (fr1 & CAN_TI0R_RTR) { + *filter |= CAN_RTR_FLAG; + } + if (fr2 & CAN_TI0R_RTR) { + *mask |= CAN_RTR_FLAG; + } +} + +static inline void unset_filter(CAN_TypeDef *can, uint8_t filter) +{ + can->FA1R &= ~(1 << filter); +} + +#ifndef CPU_FAM_STM32F1 +void candev_stm32_set_pins(can_t *dev, gpio_t tx_pin, gpio_t rx_pin, + gpio_af_t af) +#else +void candev_stm32_set_pins(can_t *dev, gpio_t tx_pin, gpio_t rx_pin) +#endif +{ + if (dev->tx_pin != GPIO_UNDEF) { + gpio_init(dev->tx_pin, GPIO_IN); + gpio_init_analog(dev->tx_pin); + } + if (dev->rx_pin != GPIO_UNDEF) { + gpio_init(dev->rx_pin, GPIO_IN); + gpio_init_analog(dev->rx_pin); + } + dev->tx_pin = tx_pin; + dev->rx_pin = rx_pin; + /* configure pins */ + gpio_init(rx_pin, GPIO_IN); + gpio_init(tx_pin, GPIO_OUT); +#ifndef CPU_FAM_STM32F1 + dev->af = af; + gpio_init_af(rx_pin, af); + gpio_init_af(tx_pin, af); +#else + gpio_init_af(tx_pin, GPIO_AF_OUT_PP); +#endif +} + +static int _init(candev_t *candev) +{ + can_t *dev = (can_t *)candev; + int res = 0; + + _can[get_channel(dev->conf->can)] = dev; + + memset(dev->tx_mailbox, 0, sizeof(dev->tx_mailbox)); + memset(&dev->rx_fifo, 0, sizeof(dev->rx_fifo)); + + dev->isr_flags.isr_tx = 0; + dev->isr_flags.isr_rx = 0; + +#if CANDEV_STM32_CHAN_NUMOF > 1 + /* Enable master clock */ + periph_clk_en(APB1, dev->conf->master_rcc_mask); +#endif + /* Enable device clock */ + periph_clk_en(APB1, dev->conf->rcc_mask); + + _status[get_channel(dev->conf->can)] = STATUS_ON; + + /* configure pins */ +#ifndef CPU_FAM_STM32F1 + candev_stm32_set_pins(dev, dev->conf->tx_pin, dev->conf->rx_pin, dev->conf->af); +#else + candev_stm32_set_pins(dev, dev->conf->tx_pin, dev->conf->rx_pin); +#endif + + set_mode(dev->conf->can, MODE_INIT); + + /* Set configuration */ + uint32_t mask_config = (CAN_MCR_TTCM * dev->conf->ttcm) | (CAN_MCR_ABOM * dev->conf->abom) | + (CAN_MCR_AWUM * dev->conf->awum) | (CAN_MCR_NART * dev->conf->nart) | + (CAN_MCR_RFLM * dev->conf->rflm) | (CAN_MCR_TXFP * dev->conf->txfp); + dev->conf->can->MCR |= mask_config; + dev->conf->can->MCR &= (mask_config | CAN_MCR_BIT_DONT_TOUCH); + + set_bit_timing(dev); + + /* Loopback and/or silent mode */ + dev->conf->can->BTR |= (CAN_BTR_LBKM * dev->conf->lbkm) | (CAN_BTR_SILM * dev->conf->silm); + + /* Default filter config: No rx frame */ +#if CANDEV_STM32_CHAN_NUMOF > 1 + if (dev->conf->can == dev->conf->can_master) { + + assert(dev->conf->first_filter == 0); + assert(dev->conf->nb_filters <= CAN_STM32_NB_FILTER); +#endif + + dev->conf->can->FMR = CAN_FMR_FINIT; +#if CANDEV_STM32_CHAN_NUMOF > 1 + /* Clear start bank */ + dev->conf->can->FMR &= ~CAN_FMR_CAN2SB; + /* Set start filter */ + dev->conf->can->FMR |= dev->conf->nb_filters << 8; + + for (int i = 0; i < dev->conf->nb_filters; i++) { +#else + for (int i = 0; i < CAN_STM32_NB_FILTER; i++) { +#endif + dev->conf->can->FA1R &= ~(1 << i); + } + dev->conf->can->FMR &= ~CAN_FMR_FINIT; +#if CANDEV_STM32_CHAN_NUMOF > 1 +} +else { + can_mode_t mode = get_mode(dev->conf->can_master); + set_mode(dev->conf->can_master, MODE_INIT); + dev->conf->can_master->FMR = CAN_FMR_FINIT; + dev->conf->can_master->FMR &= ~CAN_FMR_CAN2SB; + dev->conf->can_master->FMR |= dev->conf->first_filter << 8; + for (int i = 0; i < dev->conf->nb_filters; i++) { + dev->conf->can_master->FA1R &= ~(1 << (i + dev->conf->first_filter)); + } + dev->conf->can_master->FMR &= ~CAN_FMR_FINIT; + set_mode(dev->conf->can_master, mode); +} +#endif + + /* Clear interrupt flags */ + dev->conf->can->TSR |= CAN_TSR_RQCP0 | CAN_TSR_RQCP1 | CAN_TSR_RQCP2; + dev->conf->can->RF0R |= CAN_RF0R_FMP0 | CAN_RF0R_FULL0 | CAN_RF0R_FOVR0; + dev->conf->can->RF1R |= CAN_RF1R_FMP1 | CAN_RF1R_FULL1 | CAN_RF1R_FOVR1; + dev->conf->can->ESR |= CAN_ESR_EWGF | CAN_ESR_EPVF | CAN_ESR_BOFF; + dev->conf->can->MSR |= CAN_MSR_WKUI; + + dev->conf->can->IER = CAN_IER_WKUIE | + CAN_IER_EPVIE | CAN_IER_EWGIE | CAN_IER_ERRIE | CAN_IER_BOFIE | + CAN_IER_FOVIE1 | CAN_IER_FMPIE1 | CAN_IER_FOVIE0 | CAN_IER_FMPIE0 | + CAN_IER_TMEIE; + /* Enable interrupts */ +#if defined(CPU_FAM_STM32F0) + NVIC_EnableIRQ(dev->conf->irqn); +#else + NVIC_EnableIRQ(dev->conf->tx_irqn); + NVIC_EnableIRQ(dev->conf->rx0_irqn); + NVIC_EnableIRQ(dev->conf->rx1_irqn); + NVIC_EnableIRQ(dev->conf->sce_irqn); +#endif + + res = set_mode(dev->conf->can, MODE_NORMAL); +#ifdef STM32_PM_STOP + pm_block(STM32_PM_STOP); +#endif + + return res; +} + +static inline void set_bit_timing(can_t *dev) +{ + /* Set bit timing */ + dev->conf->can->BTR = (((uint32_t)(dev->candev.bittiming.sjw - 1) << 24) & CAN_BTR_SJW) | + (((uint32_t)(dev->candev.bittiming.phase_seg2 - 1) << 20) & CAN_BTR_TS2) | + (((uint32_t)((dev->candev.bittiming.phase_seg1 + + dev->candev.bittiming.prop_seg) + - 1) << 16) & CAN_BTR_TS1) | + ((uint32_t)(dev->candev.bittiming.brp - 1) & CAN_BTR_BRP); +} + +static int _send(candev_t *candev, const struct can_frame *frame) +{ + can_t *dev = (can_t *)candev; + CAN_TypeDef *can = dev->conf->can; + int mailbox = 0; + + DEBUG("_send: candev=%p, frame=%p\n", (void *) candev, (void *) frame); + + for (mailbox = 0; mailbox < CAN_STM32_TX_MAILBOXES; mailbox++) { + if (dev->tx_mailbox[mailbox] == NULL) { + break; + } + } + if (mailbox == CAN_STM32_TX_MAILBOXES) { + return -EBUSY; + } + dev->tx_mailbox[mailbox] = frame; + + if ((frame->can_id & CAN_EFF_FLAG) == CAN_EFF_FLAG) { + can->sTxMailBox[mailbox].TIR = (frame->can_id & CAN_EFF_MASK) << CAN_TIxR_EFF_SHIFT + | CAN_TI0R_IDE; + } + else { + can->sTxMailBox[mailbox].TIR = (frame->can_id & CAN_SFF_MASK) << CAN_TIxR_SFF_SHIFT; + } + can->sTxMailBox[mailbox].TDTR = frame->can_dlc & CAN_TDT0R_DLC; + + can->sTxMailBox[mailbox].TDLR = 0; + can->sTxMailBox[mailbox].TDHR = 0; + for (int j = 0; j < 4 && frame->can_dlc > j; j++) { + can->sTxMailBox[mailbox].TDLR |= (uint32_t)(frame->data[j] << (8 * j)); + } + for (int j = 4; j < 8 && frame->can_dlc > j; j++) { + can->sTxMailBox[mailbox].TDHR |= (uint32_t)(frame->data[j] << (8 * (j - 4))); + } + + can->sTxMailBox[mailbox].TIR |= CAN_TI0R_TXRQ; + + return mailbox; +} + +static int _abort(candev_t *candev, const struct can_frame *frame) +{ + can_t *dev = (can_t *)candev; + CAN_TypeDef *can = dev->conf->can; + int mailbox = 0; + + for (mailbox = 0; mailbox < CAN_STM32_TX_MAILBOXES; mailbox++) { + if (dev->tx_mailbox[mailbox] == frame) { + break; + } + } + if (mailbox == CAN_STM32_TX_MAILBOXES) { + return -EOVERFLOW; + } + + can->TSR |= CAN_TSR_ABRQ0 << (8 * mailbox); + dev->tx_mailbox[mailbox] = NULL; + + return 0; +} + +#define CAN_RIxR_SFF_SHIFT 21 +#define CAN_RIxR_EFF_SHIFT 3 +#define CAN_RDTxR_FMI_SHIFT 8 + +static int read_frame(can_t *dev, struct can_frame *frame, int mailbox) +{ + CAN_TypeDef *can = dev->conf->can; + + /* Get frame ID */ + if ((can->sFIFOMailBox[mailbox].RIR & CAN_RI0R_IDE) == CAN_RI0R_IDE) { + frame->can_id = can->sFIFOMailBox[mailbox].RIR >> CAN_RIxR_EFF_SHIFT; + frame->can_id |= CAN_EFF_FLAG; + } + else { + frame->can_id = can->sFIFOMailBox[mailbox].RIR >> CAN_RIxR_SFF_SHIFT; + } + if ((can->sFIFOMailBox[mailbox].RIR & CAN_RI0R_RTR) == CAN_RI0R_RTR) { + frame->can_id |= CAN_RTR_FLAG; + } + + /* Get DLC */ + frame->can_dlc = can->sFIFOMailBox[mailbox].RDTR & CAN_RDT0R_DLC; + + /* Get Data */ + for (int j = 0; j < 4; j++) { + frame->data[j] = (can->sFIFOMailBox[mailbox].RDLR >> (j * 8)) & 0xFF; + } + for (int j = 4; j < 8; j++) { + frame->data[j] = (can->sFIFOMailBox[mailbox].RDHR >> ((j - 4) * 8)) & 0xFF; + } + + /* filter number matching the reveived frame */ + /* filter = (can->sFIFOMailBox[mailbox].RDTR & CAN_RDT0R_FMI) >> CAN_RDTxR_FMI_SHIFT; */ + + /* Release input mailbox */ + if (mailbox == 0) { + can->RF0R |= CAN_RF0R_RFOM0; + } + else { + can->RF1R |= CAN_RF1R_RFOM1; + } + + return 0; +} + +static void _isr(candev_t *candev) +{ + can_t *dev = (can_t *)candev; + + if (dev->isr_flags.isr_tx) { + tx_isr(dev); + } + + if (dev->isr_flags.isr_wkup) { + if (dev->candev.event_callback) { + dev->candev.event_callback(&dev->candev, CANDEV_EVENT_WAKE_UP, NULL); + } + } + + unsigned int irq; + + irq = irq_disable(); + if (dev->isr_flags.isr_rx & 1) { + dev->isr_flags.isr_rx &= ~1; + irq_restore(irq); + rx_isr(dev); + } + else { + irq_restore(irq); + } + + irq = irq_disable(); + if (dev->isr_flags.isr_rx & 2) { + dev->isr_flags.isr_rx &= ~2; + irq_restore(irq); + rx_isr(dev); + } + else { + irq_restore(irq); + } +} + +static inline int get_first_filter(can_t *dev) +{ +#if CANDEV_STM32_CHAN_NUMOF == 1 + (void)dev; + return 0; +#else + return dev->conf->first_filter; +#endif +} + +static inline int get_nb_filter(can_t *dev) +{ +#if CANDEV_STM32_CHAN_NUMOF == 1 + (void)dev; + return CAN_STM32_NB_FILTER; +#else + return dev->conf->nb_filters; +#endif +} + +static inline CAN_TypeDef *get_master(can_t *dev) +{ +#if CANDEV_STM32_CHAN_NUMOF == 1 + return dev->conf->can; +#else + return dev->conf->can_master; +#endif +} + +static inline int is_master(can_t *dev) +{ +#if CANDEV_STM32_CHAN_NUMOF == 1 + (void)dev; + return 1; +#else + return dev->conf->can_master == dev->conf->can; +#endif +} + +static void _wkup_cb(void *arg) +{ + can_t *dev = arg; + gpio_irq_disable(dev->rx_pin); + + DEBUG("int wkup: %p\n", arg); + + dev->isr_flags.isr_wkup = 1; + + if (dev->candev.event_callback) { + dev->candev.event_callback(&dev->candev, CANDEV_EVENT_ISR, NULL); + } +} + +#if CANDEV_STM32_CHAN_NUMOF > 1 +static void enable_int(can_t *dev, int master_from_slave) +{ + DEBUG("EN int (%d) (%p)\n", master_from_slave, (void *)dev); + + if (master_from_slave) { + can_t *master = _can[get_channel(get_master(dev))]; + gpio_init_int(master->rx_pin, GPIO_IN, GPIO_FALLING, _wkup_cb, master); + } + else { + gpio_init_int(dev->rx_pin, GPIO_IN, GPIO_FALLING, _wkup_cb, dev); + } +} +#endif + +static void disable_int(can_t *dev, int master_from_slave) +{ + DEBUG("DIS int (%d) (%p)\n", master_from_slave, (void *)dev); + + if (master_from_slave) { +#if CANDEV_STM32_CHAN_NUMOF > 1 + can_t *master = _can[get_channel(get_master(dev))]; + gpio_irq_disable(master->rx_pin); +#ifndef CPU_FAM_STM32F1 + candev_stm32_set_pins(master, master->tx_pin, master->rx_pin, master->af); +#else + candev_stm32_set_pins(master, master->tx_pin, master->rx_pin); +#endif +#endif + } + else { + gpio_irq_disable(dev->rx_pin); +#ifndef CPU_FAM_STM32F1 + candev_stm32_set_pins(dev, dev->tx_pin, dev->rx_pin, dev->af); +#else + candev_stm32_set_pins(dev, dev->tx_pin, dev->rx_pin); +#endif + } +} + +static void turn_off(can_t *dev) +{ + DEBUG("turn off (%p)\n", (void *)dev); + + unsigned irq = irq_disable(); +#if CANDEV_STM32_CHAN_NUMOF > 1 + if (is_master(dev)) { + int chan = get_channel(dev->conf->can); + if (chan < CANDEV_STM32_CHAN_NUMOF - 1 && _status[chan + 1] != STATUS_SLEEP) { + /* a slave exists and is not sleeping */ + _status[chan] = STATUS_READY_FOR_SLEEP; + } + else { + /* no slave or slave already sleeping */ + if (_status[get_channel(dev->conf->can)] != STATUS_SLEEP) { +#ifdef STM32_PM_STOP + pm_unblock(STM32_PM_STOP); +#endif + } + _status[chan] = STATUS_SLEEP; + periph_clk_dis(APB1, dev->conf->rcc_mask); + enable_int(dev, 0); + } + } + else { + int master_chan = get_channel(get_master(dev)); + switch (_status[master_chan]) { + case STATUS_READY_FOR_SLEEP: + _status[master_chan] = STATUS_SLEEP; +#ifdef STM32_PM_STOP + pm_unblock(STM32_PM_STOP); +#endif + /* Fall through */ + case STATUS_NOT_USED: + periph_clk_dis(APB1, dev->conf->master_rcc_mask); + break; + } + periph_clk_dis(APB1, dev->conf->rcc_mask); + if (_status[get_channel(dev->conf->can)] != STATUS_SLEEP) { +#ifdef STM32_PM_STOP + pm_unblock(STM32_PM_STOP); +#endif + } + _status[get_channel(dev->conf->can)] = STATUS_SLEEP; + if (_status[master_chan] == STATUS_SLEEP) { + enable_int(dev, 1); + } + enable_int(dev, 0); + } +#else + if (_status[get_channel(dev->conf->can)] != STATUS_SLEEP) { +#ifdef STM32_PM_STOP + pm_unblock(STM32_PM_STOP); +#endif + } + _status[get_channel(dev->conf->can)] = STATUS_SLEEP; + periph_clk_dis(APB1, dev->conf->rcc_mask); + gpio_init_int(dev->rx_pin, GPIO_IN, GPIO_FALLING, _wkup_cb, dev); +#endif + irq_restore(irq); +} + +static void turn_on(can_t *dev) +{ + DEBUG("turn on (%p)\n", (void *)dev); + + unsigned irq = irq_disable(); +#if CANDEV_STM32_CHAN_NUMOF > 1 + if (!is_master(dev)) { + int master_chan = get_channel(get_master(dev)); + switch (_status[master_chan]) { + case STATUS_SLEEP: + _status[master_chan] = STATUS_READY_FOR_SLEEP; + disable_int(dev, 1); +#ifdef STM32_PM_STOP + pm_block(STM32_PM_STOP); +#endif + /* Fall through */ + case STATUS_NOT_USED: + periph_clk_en(APB1, dev->conf->master_rcc_mask); + break; + } + } +#endif + if (_status[get_channel(dev->conf->can)] == STATUS_SLEEP) { +#ifdef STM32_PM_STOP + pm_block(STM32_PM_STOP); +#endif + disable_int(dev, 0); + periph_clk_en(APB1, dev->conf->rcc_mask); + } + _status[get_channel(dev->conf->can)] = STATUS_ON; + + irq_restore(irq); +} + +static int _wake_up(can_t *dev) +{ + turn_on(dev); + return set_mode(dev->conf->can, MODE_NORMAL); +} + +static int _sleep(can_t *dev) +{ + int res = set_mode(dev->conf->can, MODE_SLEEP); + turn_off(dev); + return res; +} + +static int _set(candev_t *candev, canopt_t opt, void *value, size_t value_len) +{ + can_t *dev = (can_t *)candev; + CAN_TypeDef *can = dev->conf->can; + int res = 0; + can_mode_t mode; + + switch (opt) { + case CANOPT_BITTIMING: + if (value_len < sizeof(dev->candev.bittiming)) { + res = -EOVERFLOW; + } + else { + memcpy(&dev->candev.bittiming, value, sizeof(dev->candev.bittiming)); + mode = get_mode(can); + if (mode == MODE_SLEEP) { + res = _wake_up(dev); + if (res != 0) { + res = -EBUSY; + break; + } + } + res = set_mode(can, MODE_INIT); + if (res == 0) { + set_bit_timing(dev); + res = sizeof(dev->candev.bittiming); + } + if (mode == MODE_SLEEP) { + if (_sleep(dev) < 0) { + res = -EBUSY; + } + } + else if (set_mode(can, mode) < 0) { + res = -EBUSY; + } + } + break; + case CANOPT_STATE: + if (value_len < sizeof(canopt_state_t)) { + res = -EOVERFLOW; + } + else { + switch (*((canopt_state_t *)value)) { + case CANOPT_STATE_OFF: + case CANOPT_STATE_SLEEP: + DEBUG("candev_stm32 %p: power down\n", (void *)dev); + res = _sleep(dev); + break; + case CANOPT_STATE_ON: + DEBUG("candev_stm32 %p: power up\n", (void *)dev); + res = _wake_up(dev); + break; + case CANOPT_STATE_LISTEN_ONLY: + mode = get_mode(can); + res = set_mode(can, MODE_INIT); + can->BTR |= CAN_BTR_SILM; + res += set_mode(can, mode); + break; + } + } + break; + default: + res = -ENOTSUP; + break; + } + + return res; +} + +#define CAN_ESR_REC_SHIFT 24 +#define CAN_ESR_TEC_SHIFT 16 + +static int _get(candev_t *candev, canopt_t opt, void *value, size_t max_len) +{ + can_t *dev = (can_t *)candev; + CAN_TypeDef *can = dev->conf->can; + int res = 0; + + switch (opt) { + case CANOPT_BITTIMING: + if (max_len < sizeof(dev->candev.bittiming)) { + res = -EOVERFLOW; + } + else { + memcpy(value, &dev->candev.bittiming, sizeof(dev->candev.bittiming)); + res = sizeof(dev->candev.bittiming); + } + break; + case CANOPT_BITTIMING_CONST: + if (max_len < sizeof(bittiming_const)) { + res = -EOVERFLOW; + } + else { + memcpy(value, &bittiming_const, sizeof(bittiming_const)); + res = sizeof(bittiming_const); + } + break; + case CANOPT_CLOCK: + if (max_len < sizeof(uint32_t)) { + res = -EOVERFLOW; + } + else { + *((uint32_t *)value) = CLOCK_APB1; + res = sizeof(uint32_t); + } + break; + case CANOPT_RX_FILTERS: + if (max_len % sizeof(struct can_filter) != 0) { + res = -EOVERFLOW; + } + else { + unsigned en_filt = 0; + unsigned base_filter = get_first_filter(dev); + unsigned last_filter = base_filter + get_nb_filter(dev); + for (unsigned i = base_filter; i < last_filter; i++) { + if ((get_master(dev)->FA1R & (1u << i)) == (1u << i)) { + en_filt++; + } + } + if (max_len / sizeof(struct can_filter) < en_filt) { + res = -EOVERFLOW; + } + else { + for (unsigned i = 0; i < en_filt; i++) { + struct can_filter *filter = (struct can_filter *)value + i; + get_can_filter(get_master( + dev), i + base_filter, &filter->can_id, + &filter->can_mask); + } + res = en_filt * sizeof(struct can_filter); + } + } + break; + case CANOPT_TEC: + if (max_len != sizeof(uint16_t)) { + res = -EOVERFLOW; + } + else { + uint16_t *tec = (uint16_t *)value; + *tec = (can->ESR & CAN_ESR_TEC) >> CAN_ESR_TEC_SHIFT; + res = sizeof(uint16_t); + } + break; + case CANOPT_REC: + if (max_len != sizeof(uint16_t)) { + res = -EOVERFLOW; + } + else { + uint16_t *rec = (uint16_t *)value; + *rec = (can->ESR & CAN_ESR_REC) >> CAN_ESR_REC_SHIFT; + res = sizeof(uint16_t); + } + break; + default: + res = -ENOTSUP; + break; + } + + return res; +} + +static int _set_filter(candev_t *candev, const struct can_filter *filter) +{ + can_t *dev = (can_t *)candev; + + DEBUG("_set_filter: dev=%p, filter=0x%" PRIx32 "\n", (void *)candev, filter->can_id); + + int first_filter = get_first_filter(dev); + int last_filter = first_filter + get_nb_filter(dev); + + uint8_t i; + for (i = first_filter; i < last_filter; i++) { + if (!filter_is_set(get_master(dev), i)) { + get_master(dev)->FMR = CAN_FMR_FINIT; + set_filter(get_master( + dev), filter->can_id, filter->can_mask, i, i % CAN_STM32_RX_MAILBOXES); + get_master(dev)->FMR &= ~CAN_FMR_FINIT; + break; + } + } + if (i == last_filter) { + return -EOVERFLOW; + } + + return i - first_filter; +} + +static int _remove_filter(candev_t *candev, const struct can_filter *filter) +{ + can_t *dev = (can_t *)candev; + + int first_filter = get_first_filter(dev); + int last_filter = first_filter + get_nb_filter(dev); + + uint8_t i; + + for (i = first_filter; i < last_filter; i++) { + if (filter_is_set(get_master(dev), i)) { + uint32_t filt, mask; + /* Clear RTR and ERR flags as they are ignored by the set/get filter */ + get_can_filter(get_master(dev), i, &filt, &mask); + DEBUG("_remove_filter: filter=0x%" PRIx32 ",0x%" PRIx32 ", nb=%d, " + "dev_filter=0x%" PRIx32 ",0x%" PRIx32 "\n", + filter->can_id, filter->can_mask, (int)i, filt, mask); + if ((filt == filter->can_id) && /* ID match */ + /* Filter match (extended case */ + (((filt & CAN_EFF_FLAG) && + ((mask & CAN_EFF_MASK) == (filter->can_mask & CAN_EFF_MASK))) + /* Filter match (standard case */ + || (!(filt & CAN_EFF_FLAG) && + ((mask & CAN_SFF_MASK) == (filter->can_mask & CAN_SFF_MASK))))) { + get_master(dev)->FMR = CAN_FMR_FINIT; + unset_filter(get_master(dev), i); + get_master(dev)->FMR &= ~CAN_FMR_FINIT; + break; + } + } + } + if (i == last_filter) { + return -EOVERFLOW; + } + + return 0; +} + +static void tx_conf(can_t *dev, int mailbox) +{ + candev_t *candev = (candev_t *) dev; + const struct can_frame *frame = dev->tx_mailbox[mailbox]; + + dev->tx_mailbox[mailbox] = NULL; + + DEBUG("_tx_conf: device=%p, mb=%d\n", (void *)dev, mailbox); + + if (frame && dev->candev.event_callback) { + dev->candev.event_callback(candev, CANDEV_EVENT_TX_CONFIRMATION, + (void *) frame); + } +} + +static void tx_irq_handler(can_t *dev) +{ + CAN_TypeDef *can = dev->conf->can; + int flags = dev->isr_flags.isr_tx; + + DEBUG("tx irq\n"); + + if (can->TSR & CAN_TSR_RQCP0) { + if (can->TSR & CAN_TSR_TXOK0) { + dev->isr_flags.isr_tx |= 1; + } + can->TSR |= CAN_TSR_RQCP0; + } + if (can->TSR & CAN_TSR_RQCP1) { + if (can->TSR & CAN_TSR_TXOK1) { + dev->isr_flags.isr_tx |= 2; + } + can->TSR |= CAN_TSR_RQCP1; + } + if (can->TSR & CAN_TSR_RQCP2) { + if (can->TSR & CAN_TSR_TXOK2) { + dev->isr_flags.isr_tx |= 4; + } + can->TSR |= CAN_TSR_RQCP2; + } + + if (dev->candev.event_callback && flags != dev->isr_flags.isr_tx) { + dev->candev.event_callback(&(dev->candev), CANDEV_EVENT_ISR, NULL); + } +} + +static void tx_isr(can_t *dev) +{ + unsigned int irq; + + irq = irq_disable(); + if (dev->isr_flags.isr_tx & 1) { + dev->isr_flags.isr_tx &= ~1; + irq_restore(irq); + tx_conf(dev, 0); + } + else { + irq_restore(irq); + } + + irq = irq_disable(); + if (dev->isr_flags.isr_tx & 2) { + dev->isr_flags.isr_tx &= ~2; + irq_restore(irq); + tx_conf(dev, 1); + } + else { + irq_restore(irq); + } + + irq = irq_disable(); + if (dev->isr_flags.isr_tx & 4) { + dev->isr_flags.isr_tx &= ~4; + irq_restore(irq); + tx_conf(dev, 2); + } + else { + irq_restore(irq); + } +} + +static void rx_irq_handler(can_t *dev, int mailbox) +{ + CAN_TypeDef *can = dev->conf->can; + candev_t *candev = (candev_t *) dev; + + DEBUG("rx irq\n"); + if ((can->RF0R & CAN_RF0R_FOVR0) == CAN_RF0R_FOVR0) { + can->RF0R |= CAN_RF0R_FOVR0; + if (dev->candev.event_callback) { + dev->candev.event_callback(candev, CANDEV_EVENT_RX_ERROR, NULL); + } + return; + } + if ((can->RF1R & CAN_RF1R_FOVR1) == CAN_RF1R_FOVR1) { + can->RF1R |= CAN_RF1R_FOVR1; + if (dev->candev.event_callback) { + dev->candev.event_callback(candev, CANDEV_EVENT_RX_ERROR, NULL); + } + return; + } + + if (!dev->rx_fifo.is_full) { + int i = dev->rx_fifo.write_idx; + read_frame(dev, &(dev->rx_fifo.frame[i]), mailbox); + + if (!dev->isr_flags.isr_rx) { + dev->isr_flags.isr_rx |= mailbox + 1; + if (dev->candev.event_callback) { + dev->candev.event_callback(candev, CANDEV_EVENT_ISR, NULL); + } + } + + dev->rx_fifo.write_idx++; + if (dev->rx_fifo.write_idx == CAN_STM32_RX_MAIL_FIFO) { + dev->rx_fifo.write_idx = 0; + } + if (dev->rx_fifo.write_idx == dev->rx_fifo.read_idx) { + dev->rx_fifo.is_full = 1; + } + } + else { + if (dev->candev.event_callback) { + dev->candev.event_callback(candev, CANDEV_EVENT_RX_ERROR, NULL); + } + } +} + +static void rx_isr(can_t *dev) +{ + DEBUG("_rx_isr: device=%p\n", (void *)dev); + + while (dev->rx_fifo.is_full || dev->rx_fifo.read_idx != dev->rx_fifo.write_idx) { + int i = dev->rx_fifo.read_idx; + + if (dev->candev.event_callback) { + dev->candev.event_callback(&dev->candev, + CANDEV_EVENT_RX_INDICATION, + &dev->rx_fifo.frame[i]); + } + + dev->rx_fifo.read_idx++; + if (dev->rx_fifo.read_idx == CAN_STM32_RX_MAIL_FIFO) { + dev->rx_fifo.read_idx = 0; + } + + dev->rx_fifo.is_full = 0; + } +} + +static void sce_irq_handler(can_t *dev) +{ + CAN_TypeDef *can = dev->conf->can; + candev_t *candev = (candev_t *) dev; + + DEBUG("sce irq: "); + + if ((can->MSR & CAN_MSR_ERRI) == CAN_MSR_ERRI) { + can->MSR = CAN_MSR_ERRI; + if ((can->ESR & CAN_ESR_BOFF) == CAN_ESR_BOFF) { + DEBUG("bus-off\n"); + if (!dev->conf->abom) { + set_mode(can, MODE_INIT); + } + if (dev->candev.event_callback) { + dev->candev.event_callback(candev, CANDEV_EVENT_BUS_OFF, NULL); + } + } + else if ((can->ESR & CAN_ESR_EPVF) == CAN_ESR_EPVF) { + DEBUG("error passive\n"); + if (dev->candev.event_callback) { + dev->candev.event_callback(candev, CANDEV_EVENT_ERROR_PASSIVE, NULL); + } + } + else if ((can->ESR & CAN_ESR_EWGF) == CAN_ESR_EWGF) { + DEBUG("error warning\n"); + if (dev->candev.event_callback) { + dev->candev.event_callback(candev, CANDEV_EVENT_ERROR_WARNING, NULL); + } + } + } + else if ((can->MSR & CAN_MSR_WKUI) == CAN_MSR_WKUI) { + can->MSR = CAN_MSR_WKUI; + DEBUG("wakeup\n"); + if (dev->candev.event_callback) { + dev->candev.event_callback(candev, CANDEV_EVENT_WAKE_UP, NULL); + } + } +} + +#if defined(CPU_FAM_STM32F0) +#define CAN_MSR_INT_MASK 0x0000001C +#define CAN_TSR_INT_MASK 0x000F0F0F +#define CAN_RFxR_INT_MASK 0x0000001B +#define CAN_ESR_INT_MASK 0x00000077 + +void isr_cec_can(void) +{ + DEBUG("bxCAN irq\n"); + + if ((CAN->ESR & CAN_ESR_INT_MASK) || (CAN->MSR & CAN_MSR_INT_MASK)) { + sce_irq_handler(_can[0]); + } + if (CAN->TSR & CAN_TSR_INT_MASK) { + tx_irq_handler(_can[0]); + } + if (CAN->RF0R & CAN_RFxR_INT_MASK) { + rx_irq_handler(_can[0], 0); + } + if (CAN->RF1R & CAN_RFxR_INT_MASK) { + rx_irq_handler(_can[0], 1); + } + + cortexm_isr_end(); +} +#else +void ISR_CAN1_TX(void) +{ + tx_irq_handler(_can[0]); + + cortexm_isr_end(); +} + +void ISR_CAN1_RX0(void) +{ + rx_irq_handler(_can[0], 0); + + cortexm_isr_end(); +} + +void ISR_CAN1_RX1(void) +{ + rx_irq_handler(_can[0], 1); + + cortexm_isr_end(); +} + +void ISR_CAN1_SCE(void) +{ + sce_irq_handler(_can[0]); + + cortexm_isr_end(); +} + +#if CANDEV_STM32_CHAN_NUMOF > 1 +void ISR_CAN2_TX(void) +{ + tx_irq_handler(_can[1]); + + cortexm_isr_end(); +} + +void ISR_CAN2_RX0(void) +{ + rx_irq_handler(_can[1], 0); + + cortexm_isr_end(); +} + +void ISR_CAN2_RX1(void) +{ + rx_irq_handler(_can[1], 1); + + cortexm_isr_end(); +} + +void ISR_CAN2_SCE(void) +{ + sce_irq_handler(_can[1]); + + cortexm_isr_end(); +} +#endif + +#if CANDEV_STM32_CHAN_NUMOF > 2 +void ISR_CAN3_TX(void) +{ + tx_irq_handler(_can[2]); + + cortexm_isr_end(); +} + +void ISR_CAN3_RX0(void) +{ + rx_irq_handler(_can[2], 0); + + cortexm_isr_end(); +} + +void ISR_CAN3_RX1(void) +{ + rx_irq_handler(_can[2], 1); + + cortexm_isr_end(); +} + +void ISR_CAN3_SCE(void) +{ + sce_irq_handler(_can[2]); + + cortexm_isr_end(); +} +#endif +#endif diff --git a/drivers/include/periph/can.h b/drivers/include/periph/can.h new file mode 100644 index 0000000000..08abbf5b16 --- /dev/null +++ b/drivers/include/periph/can.h @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2018 OTA keys S.A. + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @defgroup drivers_periph_can CAN + * @ingroup drivers_periph + * @brief Low-level CAN peripheral driver interface + * + * This is a basic abstract peripheral to be used with candev. + * + * The @p can_t type should be compatible with @p candev_t to be used by the + * candev interface. + * + * See auto init for more details how the can_t is used. + * + * @{ + * + * @file + * @brief Low-level CAN peripheral driver interface definitions + * + * @author Vincent Dupont + */ + +#ifndef PERIPH_CAN_H +#define PERIPH_CAN_H + +#include "periph_cpu.h" +#include "can/candev.h" + +#ifndef HAVE_CAN_T +/** + * @brief CAN device descriptor identifier + */ +typedef candev_t can_t; +#endif + +#ifndef HAVE_CAN_CONF_T +/** + * @brief CAN configuration identifier + */ +typedef void can_conf_t; +#endif + +/** + * @brief Initialize a periph CAN device with the given configuration + * + * @param[in,out] dev device descriptor + * @param[in] conf device vonfiguration + */ +void can_init(can_t *dev, const can_conf_t *conf); + +#endif /* PERIPH_CAN_H */ diff --git a/sys/auto_init/can/auto_init_can.c b/sys/auto_init/can/auto_init_can.c index c340dfcc67..6496413731 100644 --- a/sys/auto_init/can/auto_init_can.c +++ b/sys/auto_init/can/auto_init_can.c @@ -54,8 +54,18 @@ void auto_init_candev(void) auto_init_can_native(); #endif +#ifdef MODULE_PERIPH_CAN + extern void auto_init_periph_can(void); + auto_init_periph_can(); +#endif + #ifdef MODULE_ESP_CAN extern void auto_init_esp_can(void); auto_init_esp_can(); #endif + +#ifdef MODULE_CAN_STM32 + extern void auto_init_can_stm32(void); + auto_init_can_stm32(); +#endif } diff --git a/sys/auto_init/can/auto_init_periph_can.c b/sys/auto_init/can/auto_init_periph_can.c new file mode 100644 index 0000000000..69d941649a --- /dev/null +++ b/sys/auto_init/can/auto_init_periph_can.c @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2016-2018 OTA keys S.A. + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @ingroup auto_init + * @{ + * @file + * @brief initializes periph_can devices + * + * @author Vincent Dupont + * @} + */ + +#ifdef MODULE_PERIPH_CAN +#include "can/device.h" +#include "can_params.h" + +#define CANDEV_NUMOF ((sizeof(candev_params) / sizeof(candev_params[0]))) + +#ifndef CANDEV_STACKSIZE +#define CANDEV_STACKSIZE (THREAD_STACKSIZE_DEFAULT + THREAD_EXTRA_STACKSIZE_PRINTF) +#endif + +#ifndef CANDEV_BASE_PRIORITY +#define CANDEV_BASE_PRIORITY (THREAD_PRIORITY_MAIN - CANDEV_NUMOF - 2) +#endif + +static candev_dev_t candev_dev[CANDEV_NUMOF]; +static char _can_stacks[CANDEV_NUMOF][CANDEV_STACKSIZE]; +static can_t candev[CANDEV_NUMOF]; + +void auto_init_periph_can(void) { + + for (size_t i = 0; i < CANDEV_NUMOF; i++) { + can_init(&candev[i], &candev_conf[i]); + candev_dev[i].dev = (candev_t *)&candev[i]; + candev_dev[i].name = candev_params[i].name; +#ifdef MODULE_CAN_TRX + candev_dev[i].trx = candev_params[i].trx; +#endif +#ifdef MODULE_CAN_PM + candev_dev[i].rx_inactivity_timeout = candev_params[i].rx_inactivity_timeout; + candev_dev[i].tx_wakeup_timeout = candev_params[i].tx_wakeup_timeout; +#endif + + can_device_init(_can_stacks[i], CANDEV_STACKSIZE, CANDEV_BASE_PRIORITY + i, + candev_params[i].name, &candev_dev[i]); + } +} +#else +typedef int dont_be_pedantic; +#endif