| /* |
| * 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/can/transceiver.h> |
| #include <drivers/clock_control.h> |
| #include <drivers/clock_control/rcar_clock_control.h> |
| #include <drivers/pinctrl.h> |
| #include <logging/log.h> |
| |
| LOG_MODULE_REGISTER(can_rcar, 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; |
| const struct pinctrl_dev_config *pcfg; |
| const struct device *phy; |
| uint32_t max_bitrate; |
| }; |
| |
| 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_callback_t state_change_cb; |
| void *state_change_cb_data; |
| enum can_state state; |
| }; |
| |
| 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->data; |
| 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(dev, 0, 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->config; |
| struct can_rcar_data *data = dev->data; |
| const can_state_change_callback_t cb = data->state_change_cb; |
| void *state_change_cb_data = data->state_change_cb_data; |
| 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 (cb == NULL) { |
| return; |
| } |
| can_rcar_get_error_count(config, &err_cnt); |
| cb(dev, newstate, err_cnt, state_change_cb_data); |
| } |
| |
| static void can_rcar_error(const struct device *dev) |
| { |
| const struct can_rcar_cfg *config = dev->config; |
| uint8_t eifr, ecsr; |
| |
| eifr = sys_read8(config->reg_addr + RCAR_CAN_EIFR); |
| |
| if (eifr & RCAR_CAN_EIFR_BEIF) { |
| |
| ecsr = sys_read8(config->reg_addr + RCAR_CAN_ECSR); |
| if (ecsr & RCAR_CAN_ECSR_ADEF) { |
| CAN_STATS_ACK_ERROR_INC(dev); |
| sys_write8((uint8_t)~RCAR_CAN_ECSR_ADEF, |
| config->reg_addr + RCAR_CAN_ECSR); |
| } |
| if (ecsr & RCAR_CAN_ECSR_BE0F) { |
| CAN_STATS_BIT0_ERROR_INC(dev); |
| sys_write8((uint8_t)~RCAR_CAN_ECSR_BE0F, |
| config->reg_addr + RCAR_CAN_ECSR); |
| } |
| if (ecsr & RCAR_CAN_ECSR_BE1F) { |
| CAN_STATS_BIT1_ERROR_INC(dev); |
| sys_write8((uint8_t)~RCAR_CAN_ECSR_BE1F, |
| config->reg_addr + RCAR_CAN_ECSR); |
| } |
| if (ecsr & RCAR_CAN_ECSR_CEF) { |
| CAN_STATS_CRC_ERROR_INC(dev); |
| sys_write8((uint8_t)~RCAR_CAN_ECSR_CEF, |
| config->reg_addr + RCAR_CAN_ECSR); |
| } |
| if (ecsr & RCAR_CAN_ECSR_AEF) { |
| CAN_STATS_ACK_ERROR_INC(dev); |
| sys_write8((uint8_t)~RCAR_CAN_ECSR_AEF, |
| config->reg_addr + RCAR_CAN_ECSR); |
| } |
| if (ecsr & RCAR_CAN_ECSR_FEF) { |
| CAN_STATS_FORM_ERROR_INC(dev); |
| sys_write8((uint8_t)~RCAR_CAN_ECSR_FEF, |
| config->reg_addr + RCAR_CAN_ECSR); |
| } |
| if (ecsr & RCAR_CAN_ECSR_SEF) { |
| CAN_STATS_STUFF_ERROR_INC(dev); |
| 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); |
| can_rcar_state_change(dev, CAN_ERROR_WARNING); |
| } |
| 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(const struct device *dev, |
| struct can_rcar_data *data, |
| const struct zcan_frame *frame) |
| { |
| struct zcan_frame tmp_frame; |
| 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(frame, |
| &data->filter[i])) { |
| continue; /* filter did not match */ |
| } |
| /* Make a temporary copy in case the user |
| * modifies the message. |
| */ |
| tmp_frame = *frame; |
| data->rx_callback[i](dev, &tmp_frame, data->rx_callback_arg[i]); |
| } |
| } |
| |
| static void can_rcar_rx_isr(const struct device *dev) |
| { |
| const struct can_rcar_cfg *config = dev->config; |
| struct can_rcar_data *data = dev->data; |
| struct zcan_frame frame; |
| uint32_t val; |
| int i; |
| |
| val = sys_read32(config->reg_addr + RCAR_CAN_MB_60); |
| if (val & RCAR_CAN_MB_IDE) { |
| frame.id_type = CAN_EXTENDED_IDENTIFIER; |
| frame.id = val & RCAR_CAN_MB_EID_MASK; |
| } else { |
| frame.id_type = CAN_STANDARD_IDENTIFIER; |
| frame.id = (val & RCAR_CAN_MB_SID_MASK) >> RCAR_CAN_MB_SID_SHIFT; |
| } |
| |
| if (val & RCAR_CAN_MB_RTR) { |
| frame.rtr = CAN_REMOTEREQUEST; |
| } else { |
| frame.rtr = CAN_DATAFRAME; |
| } |
| |
| frame.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 (frame.dlc > CAN_MAX_DLC) { |
| frame.dlc = CAN_MAX_DLC; |
| } |
| |
| for (i = 0; i < frame.dlc; i++) { |
| frame.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 */ |
| frame.timestamp = sys_read8(config->reg_addr + |
| RCAR_CAN_MB_60 + RCAR_CAN_MB_TSH_OFFSET) << 8; |
| /* and then read lower byte */ |
| frame.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(dev, data, &frame); |
| } |
| |
| static void can_rcar_isr(const struct device *dev) |
| { |
| const struct can_rcar_cfg *config = dev->config; |
| struct can_rcar_data *data = dev->data; |
| 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 -EAGAIN; |
| } |
| |
| 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 -EAGAIN; |
| } |
| |
| 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 -EAGAIN; |
| } |
| |
| 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 -EAGAIN; |
| } |
| |
| /* 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; |
| } |
| |
| static int can_rcar_set_mode(const struct device *dev, enum can_mode mode) |
| { |
| const struct can_rcar_cfg *config = dev->config; |
| struct can_rcar_data *data = dev->data; |
| 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; |
| } |
| |
| /* Enable CAN transceiver */ |
| if (config->phy != NULL) { |
| ret = can_transceiver_enable(config->phy); |
| if (ret != 0) { |
| LOG_ERR("failed to enable CAN transceiver (err %d)", ret); |
| 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: |
| if (ret != 0) { |
| if (config->phy != NULL) { |
| /* Attempt to disable the CAN transceiver in case of error */ |
| (void)can_transceiver_disable(config->phy); |
| } |
| } |
| |
| 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); |
| } |
| |
| static 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->config; |
| struct can_rcar_data *data = dev->data; |
| 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_set_state_change_callback(const struct device *dev, |
| can_state_change_callback_t cb, |
| void *user_data) |
| { |
| struct can_rcar_data *data = dev->data; |
| |
| data->state_change_cb = cb; |
| data->state_change_cb_data = user_data; |
| } |
| |
| static int can_rcar_get_state(const struct device *dev, enum can_state *state, |
| struct can_bus_err_cnt *err_cnt) |
| { |
| const struct can_rcar_cfg *config = dev->config; |
| struct can_rcar_data *data = dev->data; |
| |
| if (state != NULL) { |
| *state = data->state; |
| } |
| |
| if (err_cnt != NULL) { |
| can_rcar_get_error_count(config, err_cnt); |
| } |
| |
| return 0; |
| } |
| |
| #ifndef CONFIG_CAN_AUTO_BUS_OFF_RECOVERY |
| static int can_rcar_recover(const struct device *dev, k_timeout_t timeout) |
| { |
| const struct can_rcar_cfg *config = dev->config; |
| struct can_rcar_data *data = dev->data; |
| int64_t start_time; |
| int ret; |
| |
| if (data->state != CAN_BUS_OFF) { |
| return 0; |
| } |
| |
| if (k_mutex_lock(&data->inst_mutex, K_FOREVER)) { |
| return -EAGAIN; |
| } |
| |
| 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 = -EAGAIN; |
| goto done; |
| } |
| } |
| |
| done: |
| k_mutex_unlock(&data->inst_mutex); |
| return ret; |
| } |
| #endif /* CONFIG_CAN_AUTO_BUS_OFF_RECOVERY */ |
| |
| static int can_rcar_send(const struct device *dev, const struct zcan_frame *frame, |
| k_timeout_t timeout, can_tx_callback_t callback, |
| void *user_data) |
| { |
| const struct can_rcar_cfg *config = dev->config; |
| struct can_rcar_data *data = dev->data; |
| 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" |
| , frame->dlc, dev->name |
| , frame->id |
| , frame->id_type == CAN_STANDARD_IDENTIFIER ? |
| "standard" : "extended" |
| , frame->rtr == CAN_DATAFRAME ? "no" : "yes"); |
| |
| __ASSERT(frame->dlc == 0U || frame->data != NULL, "Dataptr is null"); |
| |
| if (frame->dlc > CAN_MAX_DLC) { |
| LOG_ERR("DLC of %d exceeds maximum (%d)", |
| frame->dlc, CAN_MAX_DLC); |
| return -EINVAL; |
| } |
| |
| /* Wait for a slot into the tx FIFO */ |
| if (k_sem_take(&data->tx_sem, timeout) != 0) { |
| return -EAGAIN; |
| } |
| |
| k_mutex_lock(&data->inst_mutex, K_FOREVER); |
| tx_cb = &data->tx_cb[data->tx_head]; |
| tx_cb->cb = callback; |
| tx_cb->cb_arg = user_data; |
| |
| k_sem_reset(&tx_cb->sem); |
| |
| data->tx_head++; |
| if (data->tx_head >= RCAR_CAN_FIFO_DEPTH) { |
| data->tx_head = 0; |
| } |
| |
| if (frame->id_type == CAN_STANDARD_IDENTIFIER) { |
| identifier = frame->id << RCAR_CAN_MB_SID_SHIFT; |
| } else { |
| identifier = frame->id | RCAR_CAN_MB_IDE; |
| } |
| |
| if (frame->rtr == CAN_REMOTEREQUEST) { |
| identifier |= RCAR_CAN_MB_RTR; |
| } |
| |
| sys_write32(identifier, config->reg_addr + RCAR_CAN_MB_56); |
| |
| sys_write16(frame->dlc, config->reg_addr |
| + RCAR_CAN_MB_56 + RCAR_CAN_MB_DLC_OFFSET); |
| |
| for (i = 0; i < frame->dlc; i++) { |
| sys_write8(frame->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 0; |
| } |
| |
| static inline int can_rcar_add_rx_filter_unlocked(const struct device *dev, |
| can_rx_callback_t cb, |
| void *cb_arg, |
| const struct zcan_filter *filter) |
| { |
| struct can_rcar_data *data = dev->data; |
| 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 -ENOSPC; |
| } |
| |
| static int can_rcar_add_rx_filter(const struct device *dev, can_rx_callback_t cb, |
| void *cb_arg, const struct zcan_filter *filter) |
| { |
| struct can_rcar_data *data = dev->data; |
| int filter_id; |
| |
| k_mutex_lock(&data->rx_mutex, K_FOREVER); |
| filter_id = can_rcar_add_rx_filter_unlocked(dev, cb, cb_arg, filter); |
| k_mutex_unlock(&data->rx_mutex); |
| return filter_id; |
| } |
| |
| static void can_rcar_remove_rx_filter(const struct device *dev, int filter_id) |
| { |
| struct can_rcar_data *data = dev->data; |
| |
| if (filter_id >= CONFIG_CAN_RCAR_MAX_FILTER) { |
| return; |
| } |
| |
| k_mutex_lock(&data->rx_mutex, K_FOREVER); |
| compiler_barrier(); |
| data->rx_callback[filter_id] = NULL; |
| k_mutex_unlock(&data->rx_mutex); |
| } |
| |
| static int can_rcar_init(const struct device *dev) |
| { |
| const struct can_rcar_cfg *config = dev->config; |
| struct can_rcar_data *data = dev->data; |
| 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_cb = NULL; |
| data->state_change_cb_data = NULL; |
| |
| if (config->phy != NULL) { |
| if (!device_is_ready(config->phy)) { |
| LOG_ERR("CAN transceiver not ready"); |
| return -ENODEV; |
| } |
| } |
| |
| /* Configure dt provided device signals when available */ |
| ret = pinctrl_apply_state(config->pcfg, PINCTRL_STATE_DEFAULT); |
| if (ret < 0) { |
| return ret; |
| } |
| |
| /* 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->config; |
| |
| *rate = config->bus_clk.rate; |
| return 0; |
| } |
| |
| static int can_rcar_get_max_filters(const struct device *dev, enum can_ide id_type) |
| { |
| ARG_UNUSED(id_type); |
| |
| return CONFIG_CAN_RCAR_MAX_FILTER; |
| } |
| |
| static int can_rcar_get_max_bitrate(const struct device *dev, uint32_t *max_bitrate) |
| { |
| const struct can_rcar_cfg *config = dev->config; |
| |
| *max_bitrate = config->max_bitrate; |
| |
| 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, |
| .add_rx_filter = can_rcar_add_rx_filter, |
| .remove_rx_filter = can_rcar_remove_rx_filter, |
| .get_state = can_rcar_get_state, |
| #ifndef CONFIG_CAN_AUTO_BUS_OFF_RECOVERY |
| .recover = can_rcar_recover, |
| #endif |
| .set_state_change_callback = can_rcar_set_state_change_callback, |
| .get_core_clock = can_rcar_get_core_clock, |
| .get_max_filters = can_rcar_get_max_filters, |
| .get_max_bitrate = can_rcar_get_max_bitrate, |
| .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) \ |
| PINCTRL_DT_INST_DEFINE(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), \ |
| .pcfg = PINCTRL_DT_INST_DEV_CONFIG_GET(n), \ |
| .phy = DEVICE_DT_GET_OR_NULL(DT_INST_PHANDLE(n, phys)), \ |
| .max_bitrate = DT_INST_CAN_TRANSCEIVER_MAX_BITRATE(n, 1000000), \ |
| }; \ |
| static struct can_rcar_data can_rcar_data_##n; \ |
| \ |
| CAN_DEVICE_DT_INST_DEFINE(n, can_rcar_init, \ |
| NULL, \ |
| &can_rcar_data_##n, \ |
| &can_rcar_cfg_##n, \ |
| POST_KERNEL, \ |
| CONFIG_CAN_INIT_PRIORITY, \ |
| &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) |