1
0
mirror of https://github.com/RIOT-OS/RIOT.git synced 2024-12-29 04:50:03 +01:00
RIOT/cpu/stm32/periph/can.c
Vincent Dupont eb0f6582c7 stm32/can: add option to enable deep-sleep per device
Deep-sleep was based on using rx pin as external interrupt to be able to
wake up from stop mode. If rx pin cannot be used as interrupt or user
does not need to wake up from stop from the CAN, an option is now
present. If en_deep_sleep_wake_up is set to false, setting the device to
sleep simply unblock stop mode. Otherwise the behavior is unchanged.
2021-02-02 15:32:25 +01:00

1279 lines
35 KiB
C

/*
* 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
* @{
*
* @file
* @brief Implementation of the CAN controller driver
*
* @author Vincent Dupont <vincent@otakeys.com>
* @}
*/
#include <assert.h>
#include <string.h>
#include <errno.h>
#include <limits.h>
#include <cpu_conf.h>
#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 hardware 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 hardware 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;
if (dev->conf->en_deep_sleep_wake_up) {
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:
if (dev->conf->en_deep_sleep_wake_up) {
periph_clk_dis(APB1, dev->conf->master_rcc_mask);
}
break;
}
if (dev->conf->en_deep_sleep_wake_up) {
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 (dev->conf->en_deep_sleep_wake_up) {
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;
if (dev->conf->en_deep_sleep_wake_up) {
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;
if (dev->conf->en_deep_sleep_wake_up) {
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
if (dev->conf->en_deep_sleep_wake_up) {
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