blob: 66fcc4bac9297706a6fa2716f53283cecd737db0 [file] [log] [blame]
/*
* Copyright (c) 2021 IoT.bzh
*
* SPDX-License-Identifier: Apache-2.0
*/
#define DT_DRV_COMPAT renesas_rcar_can
#include <kernel.h>
#include <errno.h>
#include <drivers/can.h>
#include <drivers/clock_control.h>
#include <drivers/clock_control/rcar_clock_control.h>
#include <logging/log.h>
LOG_MODULE_DECLARE(can_driver, CONFIG_CAN_LOG_LEVEL);
#include "can_utils.h"
/* Control Register */
#define RCAR_CAN_CTLR 0x0840
/* Control Register bits */
#define RCAR_CAN_CTLR_BOM (3 << 11) /* Bus-Off Recovery Mode Bits */
#define RCAR_CAN_CTLR_BOM_ENT BIT(11) /* Entry to halt mode */
/* at bus-off entry */
#define RCAR_CAN_CTLR_SLPM BIT(10)
#define RCAR_CAN_CTLR_CANM_HALT BIT(9)
#define RCAR_CAN_CTLR_CANM_RESET BIT(8)
#define RCAR_CAN_CTLR_CANM_MASK (3 << 8)
#define RCAR_CAN_CTLR_MLM BIT(3) /* Message Lost Mode Select */
#define RCAR_CAN_CTLR_IDFM (3 << 1) /* ID Format Mode Select Bits */
#define RCAR_CAN_CTLR_IDFM_MIXED BIT(2) /* Mixed ID mode */
#define RCAR_CAN_CTLR_MBM BIT(0) /* Mailbox Mode select */
/* Mask Register */
#define RCAR_CAN_MKR0 0x0430
#define RCAR_CAN_MKR1 0x0434
#define RCAR_CAN_MKR2 0x0400
#define RCAR_CAN_MKR3 0x0404
#define RCAR_CAN_MKR4 0x0408
#define RCAR_CAN_MKR5 0x040C
#define RCAR_CAN_MKR6 0x0410
#define RCAR_CAN_MKR7 0x0414
#define RCAR_CAN_MKR8 0x0418
#define RCAR_CAN_MKR9 0x041C
/* FIFO Received ID Compare Register 0 */
#define RCAR_CAN_FIDCR0 0x0420
/* FIFO Received ID Compare Register 1 */
#define RCAR_CAN_FIDCR1 0x0424
/* FIFO Received ID Compare Registers 0 and 1 bits */
#define RCAR_CAN_FIDCR_IDE BIT(31) /* ID Extension Bit */
#define RCAR_CAN_FIDCR_RTR BIT(30) /* RTR Bit */
/* Mask Invalid Register 0 */
#define RCAR_CAN_MKIVLR0 0x0438
/* Mask Invalid Register 1 */
#define RCAR_CAN_MKIVLR1 0x0428
/* Mailbox Interrupt Enable Registers*/
#define RCAR_CAN_MIER0 0x043C
#define RCAR_CAN_MIER1 0x042C
#define RCAR_CAN_MIER1_RXFIE BIT(28) /* Rx FIFO Interrupt Enable */
#define RCAR_CAN_MIER1_TXFIE BIT(24) /* Tx FIFO Interrupt Enable */
#define RCAR_CAN_STR 0x0842 /* Status Register */
#define RCAR_CAN_STR_RSTST BIT(8) /* Reset Status Bit */
#define RCAR_CAN_STR_HLTST BIT(9) /* Halt Status Bit */
#define RCAR_CAN_STR_SLPST BIT(10) /* Sleep Status Bit */
#define MAX_STR_READS 0x100
/* Bit Configuration Register */
#define RCAR_CAN_BCR 0x0844
/* Clock Select Register */
#define RCAR_CAN_CLKR 0x0847
#define RCAR_CAN_CLKR_EXT_CLOCK 0x3 /* External input clock */
#define RCAR_CAN_CLKR_CLKP2 0x1
#define RCAR_CAN_CLKR_CLKP1 0x0
/* Error Interrupt Enable Register */
#define RCAR_CAN_EIER 0x084C
/* Interrupt Enable Register */
#define RCAR_CAN_IER 0x0860
#define RCAR_CAN_IER_ERSIE BIT(5) /* Error Interrupt Enable Bit */
#define RCAR_CAN_IER_RXFIE BIT(4) /* Rx FIFO Interrupt Enable Bit */
#define RCAR_CAN_IER_TXFIE BIT(3) /* Tx FIFO Interrupt Enable Bit */
/* Interrupt Status Register */
#define RCAR_CAN_ISR 0x0861
#define RCAR_CAN_ISR_ERSF BIT(5) /* Error (ERS) Interrupt */
#define RCAR_CAN_ISR_RXFF BIT(4) /* Reception FIFO Interrupt */
#define RCAR_CAN_ISR_TXFF BIT(3) /* Transmission FIFO Interrupt */
/* Receive FIFO Control Register */
#define RCAR_CAN_RFCR 0x0848
#define RCAR_CAN_RFCR_RFE BIT(0) /* Receive FIFO Enable */
#define RCAR_CAN_RFCR_RFEST BIT(7) /* Receive FIFO Empty Flag */
/* Receive FIFO Pointer Control Register */
#define RCAR_CAN_RFPCR 0x0849
/* Transmit FIFO Control Register */
#define RCAR_CAN_TFCR 0x084A
#define RCAR_CAN_TFCR_TFE BIT(0) /* Transmit FIFO Enable */
#define RCAR_CAN_TFCR_TFUST (7 << 1) /* Transmit FIFO Unsent Message */
/* Number Status Bits */
#define RCAR_CAN_TFCR_TFUST_SHIFT 1 /* Offset of Tx FIFO Unsent */
/* Transmit FIFO Pointer Control Register */
#define RCAR_CAN_TFPCR 0x084B
/* Error Code Store Register*/
#define RCAR_CAN_ECSR 0x0850 /* Error Code Store Register */
#define RCAR_CAN_ECSR_EDPM BIT(7) /* Error Display Mode Select */
#define RCAR_CAN_ECSR_ADEF BIT(6) /* ACK Delimiter Error Flag */
#define RCAR_CAN_ECSR_BE0F BIT(5) /* Bit Error (dominant) Flag */
#define RCAR_CAN_ECSR_BE1F BIT(4) /* Bit Error (recessive) Flag */
#define RCAR_CAN_ECSR_CEF BIT(3) /* CRC Error Flag */
#define RCAR_CAN_ECSR_AEF BIT(2) /* ACK Error Flag */
#define RCAR_CAN_ECSR_FEF BIT(1) /* Form Error Flag */
#define RCAR_CAN_ECSR_SEF BIT(0) /* Stuff Error Flag */
/* Test Control Register */
#define RCAR_CAN_TCR 0x0858
#define RCAR_CAN_TCR_TSTE BIT(0) /* Test Mode Enable Bit*/
#define RCAR_CAN_TCR_LISTEN_ONLY BIT(1)
#define RCAR_CAN_TCR_INT_LOOP (3 << 1) /* Internal loopback*/
/* Error Interrupt Factor Judge Register bits */
#define RCAR_CAN_EIFR 0x084D
#define RCAR_CAN_EIFR_BLIF BIT(7) /* Bus Lock Detect Flag */
#define RCAR_CAN_EIFR_OLIF BIT(6) /* Overload Frame Transmission */
#define RCAR_CAN_EIFR_ORIF BIT(5) /* Receive Overrun Detect Flag */
#define RCAR_CAN_EIFR_BORIF BIT(4) /* Bus-Off Recovery Detect Flag */
#define RCAR_CAN_EIFR_BOEIF BIT(3) /* Bus-Off Entry Detect Flag */
#define RCAR_CAN_EIFR_EPIF BIT(2) /* Error Passive Detect Flag */
#define RCAR_CAN_EIFR_EWIF BIT(1) /* Error Warning Detect Flag */
#define RCAR_CAN_EIFR_BEIF BIT(0) /* Bus Error Detect Flag */
/* Receive Error Count Register */
#define RCAR_CAN_RECR 0x084D
/* Transmit Error Count Register */
#define RCAR_CAN_TECR 0x084F
/* Mailbox configuration:
* mailbox 60 - 63 - Rx FIFO mailboxes
* mailbox 56 - 59 - Tx FIFO mailboxes
* non-FIFO mailboxes are not used
*/
#define RCAR_CAN_MB_56 0x0380
#define RCAR_CAN_MB_60 0x03C0
/* DLC must be accessed as a 16 bit register */
#define RCAR_CAN_MB_DLC_OFFSET 0x4 /* Data length code */
#define RCAR_CAN_MB_DATA_OFFSET 0x6 /* Data section */
#define RCAR_CAN_MB_TSH_OFFSET 0x14 /* Timestamp upper byte */
#define RCAR_CAN_MB_TSL_OFFSET 0x15 /* Timestamp lower byte */
#define RCAR_CAN_FIFO_DEPTH 4
#define RCAR_CAN_MB_SID_SHIFT 18
#define RCAR_CAN_MB_RTR BIT(30)
#define RCAR_CAN_MB_IDE BIT(31)
#define RCAR_CAN_MB_SID_MASK 0x1FFC0000
#define RCAR_CAN_MB_EID_MASK 0x1FFFFFFF
typedef void (*init_func_t)(const struct device *dev);
struct can_rcar_cfg {
uint32_t reg_addr;
int reg_size;
init_func_t init_func;
const struct device *clock_dev;
struct rcar_cpg_clk mod_clk;
struct rcar_cpg_clk bus_clk;
uint32_t bus_speed;
uint8_t sjw;
uint8_t prop_seg;
uint8_t phase_seg1;
uint8_t phase_seg2;
uint16_t sample_point;
};
struct can_rcar_tx_cb {
struct k_sem sem;
can_tx_callback_t cb;
void *cb_arg;
};
struct can_rcar_data {
struct k_mutex inst_mutex;
struct k_sem tx_sem;
struct can_rcar_tx_cb tx_cb[RCAR_CAN_FIFO_DEPTH];
uint8_t tx_head;
uint8_t tx_tail;
uint8_t tx_unsent;
struct k_mutex rx_mutex;
can_rx_callback_t rx_callback[CONFIG_CAN_RCAR_MAX_FILTER];
void *rx_callback_arg[CONFIG_CAN_RCAR_MAX_FILTER];
struct zcan_filter filter[CONFIG_CAN_RCAR_MAX_FILTER];
can_state_change_isr_t state_change_isr;
enum can_state state;
};
#define DEV_CAN_CFG(dev) \
((const struct can_rcar_cfg *)(dev)->config)
#define DEV_CAN_DATA(dev) ((struct can_rcar_data *const)(dev)->data)
static inline uint16_t can_rcar_read16(const struct can_rcar_cfg *config,
uint32_t offs)
{
return sys_read16(config->reg_addr + offs);
}
static inline void can_rcar_write16(const struct can_rcar_cfg *config,
uint32_t offs, uint16_t value)
{
sys_write16(value, config->reg_addr + offs);
}
static void can_rcar_tx_done(const struct device *dev)
{
struct can_rcar_data *data = DEV_CAN_DATA(dev);
struct can_rcar_tx_cb *tx_cb;
tx_cb = &data->tx_cb[data->tx_tail];
data->tx_tail++;
if (data->tx_tail >= RCAR_CAN_FIFO_DEPTH) {
data->tx_tail = 0;
}
data->tx_unsent--;
if (tx_cb->cb != NULL) {
tx_cb->cb(CAN_TX_OK, tx_cb->cb_arg);
} else {
k_sem_give(&tx_cb->sem);
}
k_sem_give(&data->tx_sem);
}
static void can_rcar_get_error_count(const struct can_rcar_cfg *config,
struct can_bus_err_cnt *err_cnt)
{
err_cnt->tx_err_cnt = sys_read8(config->reg_addr + RCAR_CAN_TECR);
err_cnt->rx_err_cnt = sys_read8(config->reg_addr + RCAR_CAN_RECR);
}
static void can_rcar_state_change(const struct device *dev, uint32_t newstate)
{
const struct can_rcar_cfg *config = DEV_CAN_CFG(dev);
struct can_rcar_data *data = DEV_CAN_DATA(dev);
struct can_bus_err_cnt err_cnt;
if (data->state == newstate) {
return;
}
LOG_DBG("Can state change new: %u old:%u\n", newstate, data->state);
data->state = newstate;
if (data->state_change_isr == NULL) {
return;
}
can_rcar_get_error_count(config, &err_cnt);
data->state_change_isr(newstate, err_cnt);
}
static void can_rcar_error(const struct device *dev)
{
const struct can_rcar_cfg *config = DEV_CAN_CFG(dev);
uint8_t eifr, ecsr;
eifr = sys_read8(config->reg_addr + RCAR_CAN_EIFR);
if (eifr & RCAR_CAN_EIFR_BEIF) {
LOG_DBG("Bus error interrupt:\n");
ecsr = sys_read8(config->reg_addr + RCAR_CAN_ECSR);
if (ecsr & RCAR_CAN_ECSR_ADEF) {
LOG_DBG("ACK Delimiter Error\n");
sys_write8((uint8_t)~RCAR_CAN_ECSR_ADEF,
config->reg_addr + RCAR_CAN_ECSR);
}
if (ecsr & RCAR_CAN_ECSR_BE0F) {
LOG_DBG("Bit Error (dominant)\n");
sys_write8((uint8_t)~RCAR_CAN_ECSR_BE0F,
config->reg_addr + RCAR_CAN_ECSR);
}
if (ecsr & RCAR_CAN_ECSR_BE1F) {
LOG_DBG("Bit Error (recessive)\n");
sys_write8((uint8_t)~RCAR_CAN_ECSR_BE1F,
config->reg_addr + RCAR_CAN_ECSR);
}
if (ecsr & RCAR_CAN_ECSR_CEF) {
LOG_DBG("CRC Error\n");
sys_write8((uint8_t)~RCAR_CAN_ECSR_CEF,
config->reg_addr + RCAR_CAN_ECSR);
}
if (ecsr & RCAR_CAN_ECSR_AEF) {
LOG_DBG("ACK Error\n");
sys_write8((uint8_t)~RCAR_CAN_ECSR_AEF,
config->reg_addr + RCAR_CAN_ECSR);
}
if (ecsr & RCAR_CAN_ECSR_FEF) {
LOG_DBG("Form Error\n");
sys_write8((uint8_t)~RCAR_CAN_ECSR_FEF,
config->reg_addr + RCAR_CAN_ECSR);
}
if (ecsr & RCAR_CAN_ECSR_SEF) {
LOG_DBG("Stuff Error\n");
sys_write8((uint8_t)~RCAR_CAN_ECSR_SEF,
config->reg_addr + RCAR_CAN_ECSR);
}
sys_write8((uint8_t)~RCAR_CAN_EIFR_BEIF,
config->reg_addr + RCAR_CAN_EIFR);
}
if (eifr & RCAR_CAN_EIFR_EWIF) {
LOG_DBG("Error warning interrupt\n");
/* Clear interrupt condition */
sys_write8((uint8_t)~RCAR_CAN_EIFR_EWIF,
config->reg_addr + RCAR_CAN_EIFR);
}
if (eifr & RCAR_CAN_EIFR_EPIF) {
LOG_DBG("Error passive interrupt\n");
/* Clear interrupt condition */
sys_write8((uint8_t)~RCAR_CAN_EIFR_EPIF,
config->reg_addr + RCAR_CAN_EIFR);
can_rcar_state_change(dev, CAN_ERROR_PASSIVE);
}
if (eifr & RCAR_CAN_EIFR_BORIF) {
LOG_DBG("Bus-off recovery interrupt\n");
sys_write8(RCAR_CAN_IER_ERSIE, config->reg_addr + RCAR_CAN_IER);
/* Clear interrupt condition */
sys_write8((uint8_t)~RCAR_CAN_EIFR_BORIF,
config->reg_addr + RCAR_CAN_EIFR);
can_rcar_state_change(dev, CAN_BUS_OFF);
}
if (eifr & RCAR_CAN_EIFR_BOEIF) {
LOG_DBG("Bus-off entry interrupt\n");
sys_write8(RCAR_CAN_IER_ERSIE, config->reg_addr + RCAR_CAN_IER);
/* Clear interrupt condition */
sys_write8((uint8_t)~RCAR_CAN_EIFR_BOEIF,
config->reg_addr + RCAR_CAN_EIFR);
can_rcar_state_change(dev, CAN_BUS_OFF);
}
if (eifr & RCAR_CAN_EIFR_ORIF) {
LOG_DBG("Receive overrun error interrupt\n");
sys_write8((uint8_t)~RCAR_CAN_EIFR_ORIF,
config->reg_addr + RCAR_CAN_EIFR);
}
if (eifr & RCAR_CAN_EIFR_OLIF) {
LOG_DBG("Overload Frame Transmission error interrupt\n");
sys_write8((uint8_t)~RCAR_CAN_EIFR_OLIF,
config->reg_addr + RCAR_CAN_EIFR);
}
if (eifr & RCAR_CAN_EIFR_BLIF) {
LOG_DBG("Bus lock detected interrupt\n");
sys_write8((uint8_t)~RCAR_CAN_EIFR_BLIF,
config->reg_addr + RCAR_CAN_EIFR);
}
}
static void can_rcar_rx_filter_isr(struct can_rcar_data *data,
const struct zcan_frame *msg)
{
struct zcan_frame tmp_msg;
uint8_t i;
for (i = 0; i < CONFIG_CAN_RCAR_MAX_FILTER; i++) {
if (data->rx_callback[i] == NULL) {
continue;
}
if (!can_utils_filter_match(msg,
&data->filter[i])) {
continue; /* filter did not match */
}
/* Make a temporary copy in case the user
* modifies the message.
*/
tmp_msg = *msg;
data->rx_callback[i](&tmp_msg, data->rx_callback_arg[i]);
}
}
static void can_rcar_rx_isr(const struct device *dev)
{
struct can_rcar_data *data = DEV_CAN_DATA(dev);
const struct can_rcar_cfg *config = DEV_CAN_CFG(dev);
struct zcan_frame msg;
uint32_t val;
int i;
val = sys_read32(config->reg_addr + RCAR_CAN_MB_60);
if (val & RCAR_CAN_MB_IDE) {
msg.id_type = CAN_EXTENDED_IDENTIFIER;
msg.id = val & RCAR_CAN_MB_EID_MASK;
} else {
msg.id_type = CAN_STANDARD_IDENTIFIER;
msg.id = (val & RCAR_CAN_MB_SID_MASK) >> RCAR_CAN_MB_SID_SHIFT;
}
if (val & RCAR_CAN_MB_RTR) {
msg.rtr = CAN_REMOTEREQUEST;
} else {
msg.rtr = CAN_DATAFRAME;
}
msg.dlc = sys_read16(config->reg_addr
+ RCAR_CAN_MB_60 + RCAR_CAN_MB_DLC_OFFSET) & 0xF;
/* Be paranoid doc states that any value greater than 8
* should be considered as 8 bytes.
*/
if (msg.dlc > CAN_MAX_DLC) {
msg.dlc = CAN_MAX_DLC;
}
for (i = 0; i < msg.dlc; i++) {
msg.data[i] = sys_read8(config->reg_addr
+ RCAR_CAN_MB_60 + RCAR_CAN_MB_DATA_OFFSET + i);
}
#if defined(CONFIG_CAN_RX_TIMESTAMP)
/* read upper byte */
msg.timestamp = sys_read8(config->reg_addr +
RCAR_CAN_MB_60 + RCAR_CAN_MB_TSH_OFFSET) << 8;
/* and then read lower byte */
msg.timestamp |= sys_read8(config->reg_addr +
RCAR_CAN_MB_60 + RCAR_CAN_MB_TSL_OFFSET);
#endif
/* Increment CPU side pointer */
sys_write8(0xff, config->reg_addr + RCAR_CAN_RFPCR);
can_rcar_rx_filter_isr(data, &msg);
}
static void can_rcar_isr(const struct device *dev)
{
const struct can_rcar_cfg *config = DEV_CAN_CFG(dev);
struct can_rcar_data *data = DEV_CAN_DATA(dev);
uint8_t isr, unsent;
isr = sys_read8(config->reg_addr + RCAR_CAN_ISR);
if (isr & RCAR_CAN_ISR_ERSF) {
/* Clear the Error interrupt */
isr &= ~RCAR_CAN_ISR_ERSF;
sys_write8(isr, config->reg_addr + RCAR_CAN_ISR);
can_rcar_error(dev);
}
if (isr & RCAR_CAN_ISR_TXFF) {
/* Check for sent messages */
while (1) {
unsent = sys_read8(config->reg_addr + RCAR_CAN_TFCR);
unsent = (unsent & RCAR_CAN_TFCR_TFUST) >>
RCAR_CAN_TFCR_TFUST_SHIFT;
if (data->tx_unsent <= unsent) {
break;
}
can_rcar_tx_done(dev);
}
/* Clear the Tx interrupt */
isr = sys_read8(config->reg_addr + RCAR_CAN_ISR);
isr &= ~RCAR_CAN_ISR_TXFF;
sys_write8(isr, config->reg_addr + RCAR_CAN_ISR);
}
if (isr & RCAR_CAN_ISR_RXFF) {
/* while there is unread messages */
while (!(sys_read8(config->reg_addr + RCAR_CAN_RFCR)
& RCAR_CAN_RFCR_RFEST)) {
can_rcar_rx_isr(dev);
}
/* Clear the Rx interrupt */
isr = sys_read8(config->reg_addr + RCAR_CAN_ISR);
isr &= ~RCAR_CAN_ISR_RXFF;
sys_write8(isr, config->reg_addr + RCAR_CAN_ISR);
}
}
static int can_rcar_leave_sleep_mode(const struct can_rcar_cfg *config)
{
uint16_t ctlr, str;
int i;
ctlr = can_rcar_read16(config, RCAR_CAN_CTLR);
ctlr &= ~RCAR_CAN_CTLR_SLPM;
can_rcar_write16(config, RCAR_CAN_CTLR, ctlr);
for (i = 0; i < MAX_STR_READS; i++) {
str = can_rcar_read16(config, RCAR_CAN_STR);
if (!(str & RCAR_CAN_STR_SLPST)) {
return 0;
}
}
return CAN_TIMEOUT;
}
static int can_rcar_enter_reset_mode(const struct can_rcar_cfg *config, bool force)
{
uint16_t ctlr;
int i;
ctlr = can_rcar_read16(config, RCAR_CAN_CTLR);
ctlr &= ~RCAR_CAN_CTLR_CANM_MASK;
ctlr |= RCAR_CAN_CTLR_CANM_RESET;
if (force) {
ctlr |= RCAR_CAN_CTLR_CANM_HALT;
}
can_rcar_write16(config, RCAR_CAN_CTLR, ctlr);
for (i = 0; i < MAX_STR_READS; i++) {
if (can_rcar_read16(config, RCAR_CAN_STR) & RCAR_CAN_STR_RSTST) {
return 0;
}
}
return CAN_TIMEOUT;
}
static int can_rcar_enter_halt_mode(const struct can_rcar_cfg *config)
{
uint16_t ctlr;
int i;
ctlr = can_rcar_read16(config, RCAR_CAN_CTLR);
ctlr &= ~RCAR_CAN_CTLR_CANM_MASK;
ctlr |= RCAR_CAN_CTLR_CANM_HALT;
can_rcar_write16(config, RCAR_CAN_CTLR, ctlr);
for (i = 0; i < MAX_STR_READS; i++) {
if (can_rcar_read16(config, RCAR_CAN_STR) & RCAR_CAN_STR_HLTST) {
return 0;
}
}
return CAN_TIMEOUT;
}
static int can_rcar_enter_operation_mode(const struct can_rcar_cfg *config)
{
uint16_t ctlr, str;
int i;
ctlr = can_rcar_read16(config, RCAR_CAN_CTLR);
ctlr &= ~RCAR_CAN_CTLR_CANM_MASK;
can_rcar_write16(config, RCAR_CAN_CTLR, ctlr);
for (i = 0; i < MAX_STR_READS; i++) {
str = can_rcar_read16(config, RCAR_CAN_STR);
if (!(str & RCAR_CAN_CTLR_CANM_MASK)) {
break;
}
}
if (i == MAX_STR_READS) {
return CAN_TIMEOUT;
}
/* Enable Rx and Tx FIFO */
sys_write8(RCAR_CAN_RFCR_RFE, config->reg_addr + RCAR_CAN_RFCR);
sys_write8(RCAR_CAN_TFCR_TFE, config->reg_addr + RCAR_CAN_TFCR);
return 0;
}
int can_rcar_set_mode(const struct device *dev, enum can_mode mode)
{
const struct can_rcar_cfg *config = DEV_CAN_CFG(dev);
struct can_rcar_data *data = DEV_CAN_DATA(dev);
uint8_t tcr = 0;
int ret = 0;
k_mutex_lock(&data->inst_mutex, K_FOREVER);
switch (mode) {
case CAN_NORMAL_MODE:
tcr = 0;
break;
/*Controller is not allowed to send dominant bits*/
case CAN_SILENT_MODE:
tcr = RCAR_CAN_TCR_LISTEN_ONLY | RCAR_CAN_TCR_TSTE;
break;
/*Controller is in loopback mode (receive own messages)*/
case CAN_LOOPBACK_MODE:
tcr = RCAR_CAN_TCR_INT_LOOP | RCAR_CAN_TCR_TSTE;
break;
/*Combination of loopback and silent*/
case CAN_SILENT_LOOPBACK_MODE:
ret = -ENOTSUP;
goto unlock;
}
/* Writing to TCR registers must be done in halt mode */
ret = can_rcar_enter_halt_mode(config);
if (ret) {
goto unlock;
}
sys_write8(tcr, config->reg_addr + RCAR_CAN_TCR);
/* Go back to operation mode */
ret = can_rcar_enter_operation_mode(config);
unlock:
k_mutex_unlock(&data->inst_mutex);
return ret;
}
/* Bit Configuration Register settings */
#define RCAR_CAN_BCR_TSEG1(x) (((x) & 0x0f) << 20)
#define RCAR_CAN_BCR_BPR(x) (((x) & 0x3ff) << 8)
#define RCAR_CAN_BCR_SJW(x) (((x) & 0x3) << 4)
#define RCAR_CAN_BCR_TSEG2(x) ((x) & 0x07)
static void can_rcar_set_bittiming(const struct can_rcar_cfg *config,
const struct can_timing *timing)
{
uint32_t bcr;
bcr = RCAR_CAN_BCR_TSEG1(timing->phase_seg1 + timing->prop_seg - 1) |
RCAR_CAN_BCR_BPR(timing->prescaler - 1) |
RCAR_CAN_BCR_SJW(timing->sjw - 1) |
RCAR_CAN_BCR_TSEG2(timing->phase_seg2 - 1);
/* Don't overwrite CLKR with 32-bit BCR access; CLKR has 8-bit access.
* All the registers are big-endian but they get byte-swapped on 32-bit
* read/write (but not on 8-bit, contrary to the manuals)...
*/
sys_write32((bcr << 8) | RCAR_CAN_CLKR_CLKP2,
config->reg_addr + RCAR_CAN_BCR);
}
int can_rcar_set_timing(const struct device *dev,
const struct can_timing *timing,
const struct can_timing *timing_data)
{
const struct can_rcar_cfg *config = DEV_CAN_CFG(dev);
struct can_rcar_data *data = DEV_CAN_DATA(dev);
int ret = 0;
ARG_UNUSED(timing_data);
k_mutex_lock(&data->inst_mutex, K_FOREVER);
/* Changing bittiming should be done in reset mode */
ret = can_rcar_enter_reset_mode(config, true);
if (ret != 0) {
goto unlock;
}
can_rcar_set_bittiming(config, timing);
/* Go back to operation mode */
ret = can_rcar_enter_operation_mode(config);
unlock:
k_mutex_unlock(&data->inst_mutex);
return ret;
}
static void can_rcar_register_state_change_isr(const struct device *dev,
can_state_change_isr_t isr)
{
struct can_rcar_data *data = DEV_CAN_DATA(dev);
data->state_change_isr = isr;
}
static enum can_state can_rcar_get_state(const struct device *dev,
struct can_bus_err_cnt *err_cnt)
{
const struct can_rcar_cfg *config = DEV_CAN_CFG(dev);
struct can_rcar_data *data = DEV_CAN_DATA(dev);
if (err_cnt != NULL) {
can_rcar_get_error_count(config, err_cnt);
}
return data->state;
}
#ifndef CONFIG_CAN_AUTO_BUS_OFF_RECOVERY
int can_rcar_recover(const struct device *dev, k_timeout_t timeout)
{
const struct can_rcar_cfg *config = DEV_CAN_CFG(dev);
struct can_rcar_data *data = DEV_CAN_DATA(dev);
int64_t start_time;
int ret;
if (data->state != CAN_BUS_OFF) {
return 0;
}
if (k_mutex_lock(&data->inst_mutex, K_FOREVER)) {
return CAN_TIMEOUT;
}
start_time = k_uptime_ticks();
while (data->state == CAN_BUS_OFF) {
ret = can_rcar_enter_operation_mode(config);
if (ret != 0) {
goto done;
}
if (!K_TIMEOUT_EQ(timeout, K_FOREVER) &&
k_uptime_ticks() - start_time >= timeout.ticks) {
ret = CAN_TIMEOUT;
goto done;
}
}
done:
k_mutex_unlock(&data->inst_mutex);
return ret;
}
#endif /* CONFIG_CAN_AUTO_BUS_OFF_RECOVERY */
int can_rcar_send(const struct device *dev, const struct zcan_frame *msg,
k_timeout_t timeout, can_tx_callback_t callback,
void *callback_arg)
{
const struct can_rcar_cfg *config = DEV_CAN_CFG(dev);
struct can_rcar_data *data = DEV_CAN_DATA(dev);
struct can_rcar_tx_cb *tx_cb;
uint32_t identifier;
int i;
LOG_DBG("Sending %d bytes on %s. "
"Id: 0x%x, "
"ID type: %s, "
"Remote Frame: %s"
, msg->dlc, dev->name
, msg->id
, msg->id_type == CAN_STANDARD_IDENTIFIER ?
"standard" : "extended"
, msg->rtr == CAN_DATAFRAME ? "no" : "yes");
__ASSERT(msg->dlc == 0U || msg->data != NULL, "Dataptr is null");
if (msg->dlc > CAN_MAX_DLC) {
LOG_ERR("DLC of %d exceeds maximum (%d)",
msg->dlc, CAN_MAX_DLC);
return CAN_TX_EINVAL;
}
/* Wait for a slot into the tx FIFO */
if (k_sem_take(&data->tx_sem, timeout) != 0) {
return CAN_TIMEOUT;
}
k_mutex_lock(&data->inst_mutex, K_FOREVER);
tx_cb = &data->tx_cb[data->tx_head];
tx_cb->cb = callback;
tx_cb->cb_arg = callback_arg;
k_sem_reset(&tx_cb->sem);
data->tx_head++;
if (data->tx_head >= RCAR_CAN_FIFO_DEPTH) {
data->tx_head = 0;
}
if (msg->id_type == CAN_STANDARD_IDENTIFIER) {
identifier = msg->id << RCAR_CAN_MB_SID_SHIFT;
} else {
identifier = msg->id | RCAR_CAN_MB_IDE;
}
if (msg->rtr == CAN_REMOTEREQUEST) {
identifier |= RCAR_CAN_MB_RTR;
}
sys_write32(identifier, config->reg_addr + RCAR_CAN_MB_56);
sys_write16(msg->dlc, config->reg_addr
+ RCAR_CAN_MB_56 + RCAR_CAN_MB_DLC_OFFSET);
for (i = 0; i < msg->dlc; i++) {
sys_write8(msg->data[i], config->reg_addr
+ RCAR_CAN_MB_56 + RCAR_CAN_MB_DATA_OFFSET + i);
}
compiler_barrier();
data->tx_unsent++;
/* Start Tx: increment the CPU-side pointer for the transmit FIFO
* to the next mailbox location
*/
sys_write8(0xff, config->reg_addr + RCAR_CAN_TFPCR);
k_mutex_unlock(&data->inst_mutex);
if (callback == NULL) {
k_sem_take(&tx_cb->sem, K_FOREVER);
}
return CAN_TX_OK;
}
static inline int can_rcar_attach(const struct device *dev,
can_rx_callback_t cb,
void *cb_arg,
const struct zcan_filter *filter)
{
struct can_rcar_data *data = DEV_CAN_DATA(dev);
int i;
for (i = 0; i < CONFIG_CAN_RCAR_MAX_FILTER; i++) {
if (data->rx_callback[i] == NULL) {
data->rx_callback_arg[i] = cb_arg;
data->filter[i] = *filter;
compiler_barrier();
data->rx_callback[i] = cb;
return i;
}
}
return CAN_NO_FREE_FILTER;
}
int can_rcar_attach_isr(const struct device *dev, can_rx_callback_t isr,
void *cb_arg,
const struct zcan_filter *filter)
{
struct can_rcar_data *data = DEV_CAN_DATA(dev);
int filter_nr;
k_mutex_lock(&data->rx_mutex, K_FOREVER);
filter_nr = can_rcar_attach(dev, isr, cb_arg, filter);
k_mutex_unlock(&data->rx_mutex);
return filter_nr;
}
void can_rcar_detach(const struct device *dev, int filter_nr)
{
struct can_rcar_data *data = DEV_CAN_DATA(dev);
if (filter_nr < 0 || filter_nr >= CONFIG_CAN_RCAR_MAX_FILTER) {
LOG_ERR("filter ID %d out of bounds", filter_nr);
return;
}
k_mutex_lock(&data->rx_mutex, K_FOREVER);
compiler_barrier();
data->rx_callback[filter_nr] = NULL;
k_mutex_unlock(&data->rx_mutex);
}
static int can_rcar_init(const struct device *dev)
{
const struct can_rcar_cfg *config = DEV_CAN_CFG(dev);
struct can_rcar_data *data = DEV_CAN_DATA(dev);
struct can_timing timing;
int ret;
uint16_t ctlr;
uint8_t idx;
k_mutex_init(&data->inst_mutex);
k_mutex_init(&data->rx_mutex);
k_sem_init(&data->tx_sem, RCAR_CAN_FIFO_DEPTH, RCAR_CAN_FIFO_DEPTH);
for (idx = 0; idx < RCAR_CAN_FIFO_DEPTH; idx++) {
k_sem_init(&data->tx_cb[idx].sem, 0, 1);
}
data->tx_head = 0;
data->tx_tail = 0;
data->tx_unsent = 0;
memset(data->rx_callback, 0, sizeof(data->rx_callback));
data->state = CAN_ERROR_ACTIVE;
data->state_change_isr = NULL;
/* reset the registers */
ret = clock_control_off(config->clock_dev,
(clock_control_subsys_t *)&config->mod_clk);
if (ret < 0) {
return ret;
}
ret = clock_control_on(config->clock_dev,
(clock_control_subsys_t *)&config->mod_clk);
if (ret < 0) {
return ret;
}
ret = clock_control_on(config->clock_dev,
(clock_control_subsys_t *)&config->bus_clk);
if (ret < 0) {
return ret;
}
ret = can_rcar_enter_reset_mode(config, false);
__ASSERT(!ret, "Fail to set CAN controller to reset mode");
if (ret) {
return ret;
}
ret = can_rcar_leave_sleep_mode(config);
__ASSERT(!ret, "Fail to leave CAN controller from sleep mode");
if (ret) {
return ret;
}
timing.sjw = config->sjw;
if (config->sample_point) {
ret = can_calc_timing(dev, &timing, config->bus_speed,
config->sample_point);
if (ret == -EINVAL) {
LOG_ERR("Can't find timing for given param");
return -EIO;
}
LOG_DBG("Presc: %d, TS1: %d, TS2: %d",
timing.prescaler, timing.phase_seg1, timing.phase_seg2);
LOG_DBG("Sample-point err : %d", ret);
} else {
timing.prop_seg = config->prop_seg;
timing.phase_seg1 = config->phase_seg1;
timing.phase_seg2 = config->phase_seg2;
ret = can_calc_prescaler(dev, &timing, config->bus_speed);
if (ret) {
LOG_WRN("Bitrate error: %d", ret);
}
}
ret = can_rcar_set_timing(dev, &timing, NULL);
if (ret) {
return ret;
}
ret = can_rcar_set_mode(dev, CAN_NORMAL_MODE);
if (ret) {
return ret;
}
ctlr = can_rcar_read16(config, RCAR_CAN_CTLR);
ctlr |= RCAR_CAN_CTLR_IDFM_MIXED; /* Select mixed ID mode */
#ifndef CONFIG_CAN_AUTO_BUS_OFF_RECOVERY
ctlr |= RCAR_CAN_CTLR_BOM_ENT; /* Entry to halt mode automatically */
/* at bus-off */
#endif
ctlr |= RCAR_CAN_CTLR_MBM; /* Select FIFO mailbox mode */
ctlr |= RCAR_CAN_CTLR_MLM; /* Overrun mode */
ctlr &= ~RCAR_CAN_CTLR_SLPM; /* Clear CAN Sleep mode */
can_rcar_write16(config, RCAR_CAN_CTLR, ctlr);
/* Accept all SID and EID */
sys_write32(0, config->reg_addr + RCAR_CAN_MKR8);
sys_write32(0, config->reg_addr + RCAR_CAN_MKR9);
/* In FIFO mailbox mode, write "0" to bits 24 to 31 */
sys_write32(0, config->reg_addr + RCAR_CAN_MKIVLR0);
sys_write32(0, config->reg_addr + RCAR_CAN_MKIVLR1);
/* Accept standard and extended ID frames, but not
* remote frame.
*/
sys_write32(0, config->reg_addr + RCAR_CAN_FIDCR0);
sys_write32(RCAR_CAN_FIDCR_IDE,
config->reg_addr + RCAR_CAN_FIDCR1);
/* Enable and configure FIFO mailbox interrupts Rx and Tx */
sys_write32(RCAR_CAN_MIER1_RXFIE | RCAR_CAN_MIER1_TXFIE,
config->reg_addr + RCAR_CAN_MIER1);
sys_write8(RCAR_CAN_IER_ERSIE | RCAR_CAN_IER_RXFIE | RCAR_CAN_IER_TXFIE,
config->reg_addr + RCAR_CAN_IER);
/* Accumulate error codes */
sys_write8(RCAR_CAN_ECSR_EDPM, config->reg_addr + RCAR_CAN_ECSR);
/* Enable interrupts for all type of errors */
sys_write8(0xFF, config->reg_addr + RCAR_CAN_EIER);
/* Go to operation mode */
ret = can_rcar_enter_operation_mode(config);
__ASSERT(!ret, "Fail to set CAN controller to operation mode");
if (ret) {
return ret;
}
config->init_func(dev);
return 0;
}
static int can_rcar_get_core_clock(const struct device *dev, uint32_t *rate)
{
const struct can_rcar_cfg *config = DEV_CAN_CFG(dev);
*rate = config->bus_clk.rate;
return 0;
}
static const struct can_driver_api can_rcar_driver_api = {
.set_mode = can_rcar_set_mode,
.set_timing = can_rcar_set_timing,
.send = can_rcar_send,
.attach_isr = can_rcar_attach_isr,
.detach = can_rcar_detach,
.get_state = can_rcar_get_state,
#ifndef CONFIG_CAN_AUTO_BUS_OFF_RECOVERY
.recover = can_rcar_recover,
#endif
.register_state_change_isr = can_rcar_register_state_change_isr,
.get_core_clock = can_rcar_get_core_clock,
.timing_min = {
.sjw = 0x1,
.prop_seg = 0x00,
.phase_seg1 = 0x04,
.phase_seg2 = 0x02,
.prescaler = 0x01
},
.timing_max = {
.sjw = 0x4,
.prop_seg = 0x00,
.phase_seg1 = 0x10,
.phase_seg2 = 0x08,
.prescaler = 0x400
}
};
/* Device Instantiation */
#define CAN_RCAR_INIT(n) \
static void can_rcar_##n##_init(const struct device *dev); \
static const struct can_rcar_cfg can_rcar_cfg_##n = { \
.reg_addr = DT_INST_REG_ADDR(n), \
.reg_size = DT_INST_REG_SIZE(n), \
.init_func = can_rcar_##n##_init, \
.clock_dev = DEVICE_DT_GET(DT_INST_CLOCKS_CTLR(n)), \
.mod_clk.module = \
DT_INST_CLOCKS_CELL_BY_IDX(n, 0, module), \
.mod_clk.domain = \
DT_INST_CLOCKS_CELL_BY_IDX(n, 0, domain), \
.bus_clk.module = \
DT_INST_CLOCKS_CELL_BY_IDX(n, 1, module), \
.bus_clk.domain = \
DT_INST_CLOCKS_CELL_BY_IDX(n, 1, domain), \
.bus_clk.rate = 40000000, \
.bus_speed = DT_INST_PROP(n, bus_speed), \
.sjw = DT_INST_PROP(n, sjw), \
.prop_seg = DT_INST_PROP_OR(n, prop_seg, 0), \
.phase_seg1 = DT_INST_PROP_OR(n, phase_seg1, 0), \
.phase_seg2 = DT_INST_PROP_OR(n, phase_seg2, 0), \
.sample_point = DT_INST_PROP_OR(n, sample_point, 0), \
}; \
static struct can_rcar_data can_rcar_data_##n; \
\
DEVICE_DT_INST_DEFINE(n, can_rcar_init, \
NULL, \
&can_rcar_data_##n, \
&can_rcar_cfg_##n, \
POST_KERNEL, \
CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, \
&can_rcar_driver_api \
); \
static void can_rcar_##n##_init(const struct device *dev) \
{ \
IRQ_CONNECT(DT_INST_IRQN(n), \
0, \
can_rcar_isr, \
DEVICE_DT_INST_GET(n), 0); \
\
irq_enable(DT_INST_IRQN(n)); \
}
DT_INST_FOREACH_STATUS_OKAY(CAN_RCAR_INIT)