From e6f6eccaef9dc0778d535c3029ff84814322f7dd Mon Sep 17 00:00:00 2001 From: Michal Lenc Date: Sun, 26 May 2024 13:28:31 +0200 Subject: [PATCH] can: add support for CTU CAN FD controller This commit adds support for CTU CAN FD controller. The controller can be initialized from BSP by ctucanfd_initialize() function call. It also implements the dynamic redistribution of hardware transmission buffers to CAN stack priority classes in order to avoid possible priority inversion. Signed-off-by: Michal Lenc Co-authored-by: Pavel Pisa --- cpukit/dev/can/ctucanfd/ctucanfd.c | 1864 +++++++++++++++++++ cpukit/dev/can/ctucanfd/ctucanfd_internal.h | 148 ++ cpukit/dev/can/ctucanfd/ctucanfd_kframe.h | 77 + cpukit/dev/can/ctucanfd/ctucanfd_kregs.h | 349 ++++ cpukit/dev/can/ctucanfd/ctucanfd_txb.h | 122 ++ cpukit/include/dev/can/ctucanfd.h | 86 + spec/build/cpukit/librtemscpu.yml | 2 + 7 files changed, 2648 insertions(+) create mode 100644 cpukit/dev/can/ctucanfd/ctucanfd.c create mode 100644 cpukit/dev/can/ctucanfd/ctucanfd_internal.h create mode 100644 cpukit/dev/can/ctucanfd/ctucanfd_kframe.h create mode 100644 cpukit/dev/can/ctucanfd/ctucanfd_kregs.h create mode 100644 cpukit/dev/can/ctucanfd/ctucanfd_txb.h create mode 100644 cpukit/include/dev/can/ctucanfd.h diff --git a/cpukit/dev/can/ctucanfd/ctucanfd.c b/cpukit/dev/can/ctucanfd/ctucanfd.c new file mode 100644 index 0000000000..0a573c86d6 --- /dev/null +++ b/cpukit/dev/can/ctucanfd/ctucanfd.c @@ -0,0 +1,1864 @@ +/* SPDX-License-Identifier: BSD-2-Clause OR Apache-2.0 OR GPL-2.0-or-later */ + +/** + * @file + * + * @ingroup CTUCANFD + * + * @brief This source file contains the implementation of CTU CAN FD + * controller. + */ + +/* + * Copyright (C) 2023-2024 Michal Lenc + * Implementation is based on Linux CTU CAN FD dlriver + * Copyright (C) 2015-2018 Ondrej Ille FEE CTU + * Copyright (C) 2018-2021 Ondrej Ille self-funded + * Copyright (C) 2018-2019 Martin Jerabek FEE CTU + * Copyright (C) 2018-2022 Pavel Pisa FEE CTU/self-funded + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the folowing disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include + +#include +#include "ctucanfd_kframe.h" +#include "ctucanfd_kregs.h" +#include "ctucanfd_txb.h" +#include "ctucanfd_internal.h" + +#ifdef RTEMS_DEBUG +# define CTUCANFD_DEBUG 1 +#else +# undef CTUCANFD_DEBUG +#endif + +/** + * @brief The magic number synthesized to the core. See 3.1.1 DEVICE_ID + * register description in CTU CAN FD Core Datasheet Documentation. + */ +#define CTUCANFD_ID 0xCAFD +#define CTUCANFD_ID_MASK 0xFFFF + +/** + * @brief This defines initial transmit buffer order. Every 4 bits correspond + * to one buffer number. The buffer located on the least significant 4 bits + * has the highest priority. + */ +#define CTUCANFD_INIT_TXB_ORDER 0x76543210 +#define CAN_CTUCANFD_TYPE "ctucanfd" + +enum ctucanfd_txtb_status { + /* Buffer does not exists in the core */ + TXT_NOT_EXIST = 0x0, + /* Buffer is in ready state */ + TXT_RDY = 0x1, + /* Buffer is in TX in progress state */ + TXT_TRAN = 0x2, + /* Buffer is in abort in progress state */ + TXT_ABTP = 0x3, + /* Buffer is in TX OK state */ + TXT_TOK = 0x4, + /* Buffer is in failed state */ + TXT_ERR = 0x6, + /* Buffer is in aborted state */ + TXT_ABT = 0x7, + /* Buffer is in empty state */ + TXT_ETY = 0x8, +}; + +enum ctucanfd_txtb_command { + /* Requests TXT buffer to move to "Empty" state */ + TXT_CMD_SET_EMPTY = 0x01, + /* Requests TXT buffer to move to “Aborted” or “Abort in progress” state */ + TXT_CMD_SET_READY = 0x02, + /* Requests TXT buffer to move to “Ready” state*/ + TXT_CMD_SET_ABORT = 0x04 +}; + +static const struct rtems_can_bittiming_const ctucanfd_nominal_bittiming_const = { + .name = "ctucanfd_nominal", + .tseg1_min = 2, + .tseg1_max = 190, + .tseg2_min = 1, + .tseg2_max = 63, + .sjw_max = 31, + .brp_min = 1, + .brp_max = 8, + .brp_inc = 1, +}; + +static const struct rtems_can_bittiming_const ctucanfd_data_bittiming_const = { + .name = "ctucanfd_data", + .tseg1_min = 2, + .tseg1_max = 94, + .tseg2_min = 1, + .tseg2_max = 31, + .sjw_max = 31, + .brp_min = 1, + .brp_max = 2, + .brp_inc = 1, +}; + +#ifdef CTUCANFD_DEBUG + +static int ctucanfd_check_state( + struct ctucanfd_internal *internal, + const char *where +) +{ + int i; + int ret = 0; + int prio = 0; + unsigned int idx_limit = 0; + uint32_t txb_alloc = 0; + uint32_t txb_found = 0; + uint32_t tx_status = ctucanfd_read32( internal, CTUCANFD_TX_STATUS ); + + for ( + prio = 0, + idx_limit = internal->ntxbufs; + prio < RTEMS_CAN_QUEUE_PRIO_NR; prio++ + ) { + if ( internal->txb_prio_tail[prio] > internal->ntxbufs) { + printk( "CTUCANFD CHECK at %s: prio_tail[%d] %d > ntxbufs %d\n", + where, prio, internal->txb_prio_tail[prio], internal->ntxbufs ); + ret = -1; + } else if ( internal->txb_prio_tail[prio] > idx_limit ) { + printk( "CTUCANFD CHECK at %s: prio_tail[%d] %d > lower prio tail %d\n", + where, prio, internal->txb_prio_tail[prio], idx_limit ); + ret = -1; + } + idx_limit = internal->txb_prio_tail[prio]; + } + + for ( i = 0; i < internal->ntxbufs; i++ ) { + uint32_t mask; + unsigned int txtb_st; + unsigned int txtb_id; + + txtb_id = ctucanfd_txb_from_order( internal->txb_order, i ); + if ( txtb_id >= internal->ntxbufs ) { + printk( + "CTUCANFD CHECK at %s: txb_order[%d] %d >= ntxbufs %d, " + "txb_order = 0x%08x\n", + where, + i, + txtb_id, + internal->ntxbufs, + internal->txb_order + ); + ret = -1; + continue; + } + mask = 1 << txtb_id; + if ( txb_found & mask ) { + printk( + "CTUCANFD CHECK at %s: txtb_id %d doubled in txb_order[%d], " + "txb_order = 0x%08x\n", + where, + txtb_id, + i, + internal->txb_order + ); + ret = -1; + } + txb_found |= mask; + txtb_st = TXTB_GET_STATUS( tx_status, txtb_id ); + if ( i < internal->txb_prio_tail[0]) { + txb_alloc |= mask; + if ( txtb_st == TXT_ETY ) { + printk( + "CTUCANFD CHECK at %s: txtb_id %d @ txb_order[%d] is EMPTY but " + "before tail %d, txb_order = 0x%08x\n", + where, + txtb_id, + i, + internal->txb_prio_tail[0], + internal->txb_order + ); + ret = -1; + } + } else { + if ( txtb_st != TXT_ETY ) { + printk( + "CTUCANFD CHECK at %s: txtb_id %d @ txb_order[%d] is not EMPTY " + "but after tail %d, txb_order = 0x%08x\n", + where, + txtb_id, + i, + internal->txb_prio_tail[0], + internal->txb_order + ); + ret = -1; + } + } + } + + if ( txb_found != ( 1 << internal->ntxbufs ) - 1 ) { + printk( + "CTUCANFD CHECK at %s: missing buffers in txb_order = 0x%08x, " + "txb_found = 0x%02x ntxbufs %d, tail %d\n", + where, + internal->txb_order, + txb_found, + internal->ntxbufs, + internal->txb_prio_tail[0] + ); + ret = -1; + } + for ( i = 0; i < internal->ntxbufs; i++ ) { + if ( txb_alloc & ( 1 << i ) ) { + if ( + ( internal->txb_info[i].slot == NULL ) || + ( internal->txb_info[i].edge == NULL ) + ) { + printk( + "CTUCANFD CHECK at %s: txb_info[%d] not filled, but active in " + "txb_order = 0x%08x, ntxbufs %d, tail %d\n", + where, + i, + internal->txb_order, + internal->ntxbufs, + internal->txb_prio_tail[0] + ); + ret = -1; + } + } else { + if ( + ( internal->txb_info[i].slot != NULL ) || + ( internal->txb_info[i].edge != NULL ) + ) { + printk( + "CTUCANFD CHECK at %s: txb_info[%d] filled, but shoul be inactive " + "active in txb_order = 0x%08x, ntxbufs %d, tail %d\n", + where, + i, + internal->txb_order, + internal->ntxbufs, + internal->txb_prio_tail[0] + ); + ret = -1; + } + } + } + return ret; +} + +#else /*CTUCANFD_DEBUG*/ + +static int ctucanfd_check_state( + struct ctucanfd_internal *internal, + const char *where +) { + return 0; +} + +#endif /*CTUCANFD_DEBUG*/ + +static int ctucanfd_get_timestamp( struct rtems_can_chip *chip, uint64_t *timestamp ) +{ + struct ctucanfd_internal *internal = chip->internal; + uint32_t lower; + uint32_t upper; + uint32_t upper_check; + + upper = ctucanfd_read32( internal, CTUCANFD_TIMESTAMP_HIGH ); + lower = ctucanfd_read32( internal, CTUCANFD_TIMESTAMP_LOW ); + upper_check = ctucanfd_read32( internal, CTUCANFD_TIMESTAMP_HIGH ); + if ( upper != upper_check ) { + upper = ctucanfd_read32( internal, CTUCANFD_TIMESTAMP_HIGH ); + lower = ctucanfd_read32( internal, CTUCANFD_TIMESTAMP_LOW ); + } + + *timestamp = ( ( uint64_t )upper << 32) | lower; + + return 0; +} + +static int ctucanfd_get_chip_info( struct rtems_can_chip *chip, int what ) +{ + int ret; + + switch( what ) { + case RTEMS_CAN_CHIP_BITRATE: + ret = chip->bittiming.bitrate; + break; + case RTEMS_CAN_CHIP_DBITRATE: + ret = chip->data_bittiming.bitrate; + break; + case RTEMS_CAN_CHIP_NUSERS: + ret = atomic_load( &chip->used ); + break; + case RTEMS_CAN_CHIP_FLAGS: + ret = atomic_load( &chip->flags ); + break; + case RTEMS_CAN_CHIP_MODE: + ret = chip->ctrlmode; + break; + case RTEMS_CAN_CHIP_MODE_SUPPORTED: + ret = chip->ctrlmode_supported; + break; + default: + ret = -EINVAL; + break; + } + + return ret; +} + +/** + * @brief This function issues software reset request to CTU CAN FD. + * + * @param internal Pointer to internal CTU CAN FD structure. + * + * @return 0 for success, -ETIMEDOUT if CAN controller does not leave reset + */ +static int ctucanfd_reset( struct ctucanfd_internal *internal ) +{ + uint16_t dev_id; + int i = 100; + + ctucanfd_write32( internal, CTUCANFD_MODE, REG_MODE_RST ); + + while ( i ) { + /* According to CTU CAN FD documentation, if HW reset was issued to + * the driver shall not be accessed for two clock periods of System clock. + * For example if CTU CAN FD System clock is 100 MHz, SW shall wait 20 ns + * after HW reset was released. + * + * This while loop check is to avoid stuck when there is problem with + * FPGA synthesis or some clocks blocked, not configured. + */ + dev_id = FIELD_GET( REG_DEVICE_ID_DEVICE_ID, + ctucanfd_read32( internal, CTUCANFD_DEVICE_ID ) ); + + if ( dev_id == CTUCANFD_ID ) { + return 0; + } + + i--; + } + + return -ETIMEDOUT; +} + +/** + * @brief This function sets CAN bus bit timing in CTU CAN FD + * + * @param chip Pointer to chip structure. + * @param bt Pointer to bit timing structure. + * @param nominal True - Nominal bit timing, False - Data bit timing + * + * @return 0 for success, -EPERM if CAN controller is enabled + */ +static int ctucanfd_set_btr( + struct rtems_can_chip *chip, + struct rtems_can_bittiming *bt, + bool nominal +) +{ + struct ctucanfd_internal *internal = chip->internal; + int max_ph1_len = 31; + uint32_t btr = 0; + uint32_t prop_seg = bt->prop_seg; + uint32_t phase_seg1 = bt->phase_seg1; + + if ( CTU_CAN_FD_ENABLED( internal ) ) { + return -EPERM; + } + + if ( nominal ) { + max_ph1_len = 63; + } + + /* The timing calculation functions have only constraints on tseg1, which is + * prop_seg + phase1_seg combined. tseg1 is then split in half and stored + * into prog_seg and phase_seg1. In CTU CAN FD, PROP is 6/7 bits wide + * but PH1 only 6/5, so we must re-distribute the values here. + */ + if ( phase_seg1 > max_ph1_len ) { + prop_seg += phase_seg1 - max_ph1_len; + phase_seg1 = max_ph1_len; + bt->prop_seg = prop_seg; + bt->phase_seg1 = phase_seg1; + } + + if ( nominal ) { + btr = FIELD_PREP( REG_BTR_PROP, prop_seg ); + btr |= FIELD_PREP( REG_BTR_PH1, phase_seg1 ); + btr |= FIELD_PREP( REG_BTR_PH2, bt->phase_seg2 ); + btr |= FIELD_PREP( REG_BTR_BRP, bt->brp ); + btr |= FIELD_PREP( REG_BTR_SJW, bt->sjw ); + + ctucanfd_write32( internal, CTUCANFD_BTR, btr ); + } else { + btr = FIELD_PREP( REG_BTR_FD_PROP_FD, prop_seg ); + btr |= FIELD_PREP( REG_BTR_FD_PH1_FD, phase_seg1 ); + btr |= FIELD_PREP( REG_BTR_FD_PH2_FD, bt->phase_seg2 ); + btr |= FIELD_PREP( REG_BTR_FD_BRP_FD, bt->brp ); + btr |= FIELD_PREP( REG_BTR_FD_SJW_FD, bt->sjw ); + + ctucanfd_write32( internal, CTUCANFD_BTR_FD, btr ); + } + + return 0; +} + +/** + * @brief This function sets nominal bit timing routine + * + * @param chip Pointer to chip structure. + * + * @return 0 for success, -EPERM on error + */ +static int ctucanfd_set_bittiming( struct rtems_can_chip *chip ) +{ + struct rtems_can_bittiming *bt = &chip->bittiming; + + /* Note that bt may be modified here */ + return ctucanfd_set_btr( chip, bt, true ); +} + +/** + * @brief This function sets data bit timing routine + * + * @param chip Pointer to chip structure. + * + * @return 0 for success, -EPERM on error + */ +static int ctucanfd_set_data_bittiming( struct rtems_can_chip *chip ) +{ + struct rtems_can_bittiming *dbt = &chip->data_bittiming; + + /* Note that dbt may be modified here */ + return ctucanfd_set_btr( chip, dbt, false ); +} + +/** + * @brief This function sets secondary sample point in CTU CAN FD + * + * @param chip Pointer to chip structure. + * + * @return 0 for success, -EPERM if controller is enabled + */ +static int ctucanfd_set_secondary_sample_point( struct rtems_can_chip *chip ) +{ + struct ctucanfd_internal *internal = chip->internal; + struct rtems_can_bittiming *dbt = &chip->data_bittiming; + int ssp_offset = 0; + uint32_t ssp_cfg = 0; /* No SSP by default */ + + if ( CTU_CAN_FD_ENABLED( internal ) ) { + return -EPERM; + } + + /* Use SSP for bit-rates above 1 Mbits/s */ + if ( dbt->bitrate > 1000000 ) { + /* Calculate SSP in minimal time quanta */ + ssp_offset = ( chip->freq / 1000 ) * dbt->sample_point / dbt->bitrate; + + if ( ssp_offset > 127 ) { + ssp_offset = 127; + } + + ssp_cfg = FIELD_PREP( REG_TRV_DELAY_SSP_OFFSET, ssp_offset ); + ssp_cfg |= FIELD_PREP( REG_TRV_DELAY_SSP_SRC, 0x1 ); + } + + ctucanfd_write32( internal, CTUCANFD_TRV_DELAY, ssp_cfg ); + + return 0; +} + +/** + * @brief This function sets CTU CAN FDs mode + * + * @param internal Pointer to internal CTU CAN FD structure + * @param ctrlmode Mode set bitfield + * + * @return None + */ +static void ctucanfd_set_mode( + struct ctucanfd_internal *internal, + uint32_t ctrlmode +) +{ + uint32_t mode_reg = ctucanfd_read32( internal, CTUCANFD_MODE ); + + mode_reg = ( ctrlmode & CAN_CTRLMODE_LOOPBACK ) ? + ( mode_reg | REG_MODE_ILBP ) : + ( mode_reg & ~REG_MODE_ILBP ); + + mode_reg = ( ctrlmode & CAN_CTRLMODE_LISTENONLY ) ? + ( mode_reg | REG_MODE_BMM ) : + ( mode_reg & ~REG_MODE_BMM ); + + mode_reg = ( ctrlmode & CAN_CTRLMODE_FD ) ? + ( mode_reg | REG_MODE_FDE ) : + ( mode_reg & ~REG_MODE_FDE ); + + mode_reg = ( ctrlmode & CAN_CTRLMODE_PRESUME_ACK ) ? + ( mode_reg | REG_MODE_SAM ) : + ( mode_reg & ~REG_MODE_SAM ); + + mode_reg = ( ctrlmode & CAN_CTRLMODE_FD_NON_ISO ) ? + ( mode_reg | REG_MODE_NISOFD ) : + ( mode_reg & ~REG_MODE_NISOFD ); + + /* One shot mode supported indirectly via Retransmit limit */ + mode_reg &= ~FIELD_PREP( REG_MODE_RTRTH, 0xF ); + mode_reg = ( ctrlmode & CAN_CTRLMODE_ONE_SHOT ) ? + ( mode_reg | REG_MODE_RTRLE ) : + ( mode_reg & ~REG_MODE_RTRLE ); + + /* Some bits fixed: + * TSTM - Off, User shall not be able to change REC/TEC by hand during + * operation + */ + mode_reg &= ~REG_MODE_TSTM; + + ctucanfd_write32( internal, CTUCANFD_MODE, mode_reg ); +} + +/** + * @brief This function gets status of TXT buffer + * + * @param internal Pointer to internal CTU CAN FD structure + * @param buf Buffer index (0-based) + * + * @return Status of TXT buffer + */ +static enum ctucanfd_txtb_status ctucanfd_get_tx_status( + struct ctucanfd_internal *internal, + uint8_t buf +) +{ + uint32_t tx_status = ctucanfd_read32( internal, CTUCANFD_TX_STATUS ); + enum ctucanfd_txtb_status status = ( tx_status >> ( buf * 4 ) ) & 0x7; + + return status; +} + +/** + * @brief This function checks if frame can be inserted to TXT Buffer + * + * @param internal Pointer to internal CTU CAN FD structure + * @param buf Buffer index (0-based) + * + * @return True - Frame can be inserted to TXT Buffer, False - If attempted, + * frame will not be inserted to TXT Buffer + */ +static bool ctucanfd_is_txt_buf_writable( + struct ctucanfd_internal *internal, + uint8_t buf +) +{ + enum ctucanfd_txtb_status buf_status; + + buf_status = ctucanfd_get_tx_status( internal, buf ); + if ( + buf_status == TXT_RDY || + buf_status == TXT_TRAN || + buf_status == TXT_ABTP + ) { + return false; + } + + return true; +} + +/** + * @brief This function inserts frame to TXT buffer + * + * @param internal Pointer to internal CTU CAN FD structure + * @param cf Pointer to CAN frame. + * @param buf TX buffer index (0-based) + * + * @return True - Frame inserted successfully + * False - Frame was not inserted due to one of: + * 1. TXT Buffer is not writable (it is in wrong state) + * 2. Invalid TXT buffer index + * 3. Invalid frame length + */ +static bool ctucanfd_insert_frame( + struct ctucanfd_internal *internal, + const struct can_frame *cf, + uint8_t buf +) +{ + uint32_t buf_base; + uint32_t ffw = 0; + uint32_t idw = 0; + unsigned int i; + + if ( buf >= internal->ntxbufs ) { + return false; + } + + if ( !ctucanfd_is_txt_buf_writable( internal, buf ) ) { + return false; + } + + if ( cf->header.dlen > CAN_FRAME_MAX_DLEN ) { + return false; + } + + /* Prepare Frame format */ + if ( cf->header.flags & CAN_FRAME_RTR ) { + ffw |= REG_FRAME_FORMAT_W_RTR; + } + + if ( cf->header.flags & CAN_FRAME_IDE ) { + ffw |= REG_FRAME_FORMAT_W_IDE; + } + + if ( cf->header.flags & CAN_FRAME_FDF ) { + ffw |= REG_FRAME_FORMAT_W_FDF; + if ( cf->header.flags & CAN_FRAME_BRS ) { + ffw |= REG_FRAME_FORMAT_W_BRS; + } + } + + ffw |= FIELD_PREP( + REG_FRAME_FORMAT_W_DLC, + rtems_canfd_len2dlc( cf->header.dlen ) + ); + + /* Prepare identifier */ + if ( cf->header.flags & CAN_FRAME_IDE ) { + idw = cf->header.can_id & CAN_FRAME_EFF_ID_MASK; + } else { + idw = FIELD_PREP( + REG_IDENTIFIER_W_IDENTIFIER_BASE, + cf->header.can_id & CAN_FRAME_BFF_ID_MASK + ); + } + + /* Write ID, Frame format, Don't write timestamp -> Time triggered + * transmission disabled + */ + buf_base = ( buf + 1 ) * 0x100; + ctucanfd_write_txt_buf( internal, buf_base, CTUCANFD_FRAME_FORMAT_W, ffw ); + ctucanfd_write_txt_buf( internal, buf_base, CTUCANFD_IDENTIFIER_W, idw ); + + /* Write Data payload */ + if ( !( cf->header.flags & CAN_FRAME_RTR ) ) { + for ( i = 0; i < cf->header.dlen; i += 4 ) { + uint32_t data = *( uint32_t * )( cf->data + i ); + + ctucanfd_write_txt_buf( + internal, + buf_base, + CTUCANFD_DATA_1_4_W + i, + data + ); + } + } + + return true; +} + +/** + * @brief This function applies command on TXT buffer + * + * @param internal Pointer to internal CTU CAN FD structure + * @param cmd Command to give. + * @param buf TX buffer index (0-based) + * + * @return None + */ +static void ctucanfd_give_txtb_cmd( + struct ctucanfd_internal *internal, + enum ctucanfd_txtb_command cmd, + uint8_t buf +) +{ + uint32_t tx_cmd = cmd; + + tx_cmd |= 1 << ( buf + 8 ); + ctucanfd_write32( internal, CTUCANFD_TX_COMMAND, tx_cmd ); +} + +/** + * @brief This function reads frame from RX FIFO + * + * @param chip Pointer to CAN chip structure + * @param cf Pointer to CAN frame + * @param ffw Previously read frame format word + * + * @return TX buffer number on success, negative value on error. + */ +static void ctucanfd_read_rx_frame( + struct rtems_can_chip *chip, + struct can_frame *frame, + uint32_t ffw +) +{ + struct ctucanfd_internal *internal = chip->internal; + struct rtems_can_queue_ends_dev *qends_dev = chip->qends_dev; + struct rtems_can_queue_ends *qends = &qends_dev->base; + uint32_t idw; + unsigned int i; + unsigned int wc; + unsigned int len; + + frame->header.flags = 0; + + idw = ctucanfd_read32( internal, CTUCANFD_RX_DATA ); + if ( FIELD_GET( REG_FRAME_FORMAT_W_IDE, ffw ) ) { + frame->header.can_id = idw & CAN_FRAME_EFF_ID_MASK; + frame->header.flags |= CAN_FRAME_IDE; + } else { + frame->header.can_id = ( idw >> 18 ) & CAN_FRAME_BFF_ID_MASK; + } + + /* BRS, ESI, RTR Flags */ + if ( FIELD_GET( REG_FRAME_FORMAT_W_FDF, ffw ) ) { + frame->header.flags |= CAN_FRAME_FDF; + if ( FIELD_GET( REG_FRAME_FORMAT_W_BRS, ffw ) ) { + frame->header.flags |= CAN_FRAME_BRS; + } + if ( FIELD_GET( REG_FRAME_FORMAT_W_ESI_RSV, ffw ) ) { + frame->header.flags |= CAN_FRAME_ESI; + } + } else if ( FIELD_GET( REG_FRAME_FORMAT_W_RTR, ffw ) ) { + frame->header.flags |= CAN_FRAME_RTR; + } + + wc = FIELD_GET( REG_FRAME_FORMAT_W_RWCNT, ffw ) - 3; + + /* DLC */ + if ( FIELD_GET( REG_FRAME_FORMAT_W_DLC, ffw ) <= 8 ) { + len = FIELD_GET( REG_FRAME_FORMAT_W_DLC, ffw ); + } else { + if ( FIELD_GET( REG_FRAME_FORMAT_W_FDF, ffw ) ) { + len = wc << 2; + } else { + len = 8; + } + } + frame->header.dlen = len; + if ( len > wc * 4 ) { + len = wc * 4; + } + + /* Timestamp */ + frame->header.timestamp = ctucanfd_read32( internal, CTUCANFD_RX_DATA ); + frame->header.timestamp |= ( uint64_t )ctucanfd_read32( + internal, + CTUCANFD_RX_DATA + ) << 32; + + /* Data */ + for ( i = 0; i < len; i += 4 ) { + uint32_t data = ctucanfd_read32( internal, CTUCANFD_RX_DATA ); + *(uint32_t *)( frame->data + i ) = data; + } + while ( i < wc * 4 ) { + ctucanfd_read32( internal, CTUCANFD_RX_DATA ); + i += 4; + } + + /* Pass frame to edges */ + rtems_can_queue_filter_frame_to_edges( qends, NULL, frame, 0 ); +} + +/** + * @brief Called from CAN interrupt worker to complete the received + * frame processing + * + * @param chip Pointer to CAN chip structure + * + * @return 1 when frame is received, -EAGAIN otherwise. + */ +static int ctucanfd_receive( struct rtems_can_chip *chip ) +{ + struct ctucanfd_internal *internal = chip->internal; + struct can_frame frame; + uint32_t ffw; + + ffw = ctucanfd_read32( internal, CTUCANFD_RX_DATA ); + + if ( !FIELD_GET( REG_FRAME_FORMAT_W_RWCNT, ffw ) ) { + return -EAGAIN; + } + + ctucanfd_read_rx_frame( chip, &frame, ffw ); + + rtems_can_stats_add_rx_done( &chip->chip_stats ); + rtems_can_stats_add_rx_bytes( &chip->chip_stats, frame.header.dlen ); + + return 1; +} + +/** + * @brief This function reads CTU CAN FDs fault confinement state. + * + * @param internal Pointer to internal CTU CAN FD structure + * + * @return Fault confinement state of controller + */ +static enum can_state ctucanfd_read_fault_state( + struct ctucanfd_internal *internal +) +{ + uint32_t fs; + uint32_t rec_tec; + uint32_t ewl; + + fs = ctucanfd_read32( internal, CTUCANFD_EWL ); + rec_tec = ctucanfd_read32( internal, CTUCANFD_REC ); + ewl = FIELD_GET( REG_EWL_EW_LIMIT, fs ); + + if ( FIELD_GET( REG_EWL_ERA, fs ) ) { + if ( ewl > FIELD_GET( REG_REC_REC_VAL, rec_tec ) && + ewl > FIELD_GET( REG_REC_TEC_VAL, rec_tec ) ) { + return CAN_STATE_ERROR_ACTIVE; + } else { + return CAN_STATE_ERROR_WARNING; + } + } else if ( FIELD_GET( REG_EWL_ERP, fs ) ) { + return CAN_STATE_ERROR_PASSIVE; + } else if ( FIELD_GET( REG_EWL_BOF, fs ) ) { + return CAN_STATE_BUS_OFF; + } + + return CAN_STATE_ERROR_PASSIVE; +} + +/** + * @brief This function handles error frame interrupts. + * + * @param chip Pointer to CAN chip structure + * @param isr Interrupt status register value + * + * @return None + */ +static void ctucanfd_err_interrupt( struct rtems_can_chip *chip, uint32_t isr ) +{ + struct ctucanfd_internal *internal = chip->internal; + struct can_frame err_frame = {}; + enum can_state state; + uint32_t regval; + uint16_t rxerr; + uint16_t txerr; + + regval = ctucanfd_read32( internal, CTUCANFD_REC ); + + rxerr = FIELD_GET( REG_REC_REC_VAL, regval ); + txerr = FIELD_GET( REG_REC_TEC_VAL, regval ); + + state = ctucanfd_read_fault_state( internal ); + rtems_can_stats_set_state(&chip->chip_stats, state); + + if ( + FIELD_GET( REG_INT_STAT_FCSI, isr ) || + FIELD_GET( REG_INT_STAT_EWLI, isr ) + ) { + switch ( state ) { + case CAN_STATE_BUS_OFF: + err_frame.header.can_id = CAN_ERR_ID_BUSOFF; + err_frame.header.flags = CAN_FRAME_ERR; + break; + case CAN_STATE_ERROR_PASSIVE: + err_frame.header.can_id = CAN_ERR_ID_CRTL | CAN_ERR_ID_CNT; + err_frame.header.flags = CAN_FRAME_ERR; + err_frame.data[CAN_ERR_DATA_BYTE_TRX_CTRL] = ( rxerr > 127 ) ? + CAN_ERR_CRTL_RX_PASSIVE : + CAN_ERR_CRTL_TX_PASSIVE; + err_frame.data[CAN_ERR_DATA_BYTE_CNT_TX] = txerr; + err_frame.data[CAN_ERR_DATA_BYTE_CNT_RX] = rxerr; + break; + case CAN_STATE_ERROR_WARNING: + err_frame.header.can_id = CAN_ERR_ID_CRTL | CAN_ERR_ID_CNT; + err_frame.header.flags = CAN_FRAME_ERR; + err_frame.data[CAN_ERR_DATA_BYTE_TRX_CTRL] = ( txerr > rxerr ) ? + CAN_ERR_CRTL_TX_WARNING : + CAN_ERR_CRTL_RX_WARNING; + err_frame.data[CAN_ERR_DATA_BYTE_CNT_TX] = txerr; + err_frame.data[CAN_ERR_DATA_BYTE_CNT_RX] = rxerr; + break; + case CAN_STATE_ERROR_ACTIVE: + err_frame.header.can_id = CAN_ERR_ID_CRTL; + err_frame.header.flags = CAN_FRAME_ERR; + err_frame.data[CAN_ERR_DATA_BYTE_TRX_CTRL] = CAN_ERR_CRTL_ACTIVE; + err_frame.data[CAN_ERR_DATA_BYTE_CNT_TX] = txerr; + err_frame.data[CAN_ERR_DATA_BYTE_CNT_RX] = rxerr; + break; + default: + break; + } + } + + /* Check for Arbitration Lost interrupt */ + if ( FIELD_GET( REG_INT_STAT_ALI, isr ) ) { + err_frame.header.can_id |= CAN_ERR_ID_CRTL | CAN_ERR_ID_LOSTARB; + err_frame.header.flags |= CAN_FRAME_ERR; + err_frame.data[CAN_ERR_DATA_BYTE_TRX_LOSTARB] = CAN_ERR_LOSTARB_UNSPEC; + } + + /* Check for Bus Error interrupt */ + if ( FIELD_GET( REG_INT_STAT_BEI, isr ) ) { + rtems_can_stats_add_rx_error( &chip->chip_stats ); + err_frame.header.can_id |= CAN_ERR_ID_CRTL | CAN_ERR_ID_PROT | \ + CAN_ERR_ID_BUSERROR; + err_frame.header.flags |= CAN_FRAME_ERR; + err_frame.data[CAN_ERR_DATA_BYTE_TRX_PROT] = CAN_ERR_PROT_UNSPEC; + err_frame.data[CAN_ERR_DATA_BYTE_TRX_PROT_LOC] = CAN_ERR_PROT_LOC_UNSPEC; + } + + /* Check for RX overflow interrupt */ + if ( FIELD_GET( REG_INT_STAT_DOI, isr ) ) { + rtems_can_stats_add_rx_overflows( &chip->chip_stats ); + ctucanfd_write32( internal, CTUCANFD_TX_COMMAND, REG_COMMAND_CDO ); + err_frame.header.can_id |= CAN_ERR_ID_CRTL; + err_frame.header.flags |= CAN_FRAME_ERR; + err_frame.data[CAN_ERR_ID_LOSTARB] |= CAN_ERR_CRTL_RX_OVERFLOW; + } + + if ( err_frame.header.flags != 0 ) { + rtems_can_queue_filter_frame_to_edges( + &chip->qends_dev->base, + NULL, + &err_frame, + 0 + ); + } +} + +/** + * @brief This function adds message to TX buffer priority slot. + * + * @param internal Pointer to CTU CAN FD structure + * @param txtb_id ID to be moved + * @param prio Message buffer priority + * + * @return None + */ +static void ctucanfd_txb_add( + struct ctucanfd_internal *internal, + unsigned int txb_order_idx, + unsigned int prio +) { + int i; + internal->txb_order = ctucanfd_txb_slot_promote( + internal->txb_order, + txb_order_idx, + internal->txb_prio_tail[prio] + ); + for ( i = 0; i <= prio; i++ ) { + internal->txb_prio_tail[i]++; + } +} + +/** + * @brief This function removes send message from TX buffer priority slot. + * + * @param internal Pointer to CTU CAN FD structure + * @param txtb_id ID to be removed + * + * @return None + */ +static void ctucanfd_txb_free( + struct ctucanfd_internal *internal, + unsigned int txb_order_idx +) { + int i = RTEMS_CAN_QUEUE_PRIO_NR - 1; + internal->txb_order = ctucanfd_txb_slot_demote( + internal->txb_order, + txb_order_idx, + internal->ntxbufs - 1 + ); + do { + if ( internal->txb_prio_tail[i] > txb_order_idx ) { + internal->txb_prio_tail[i]--; + } + } while( i-- ); +} + +/** + * @brief This function handles TX buffer done interrupt. + * + * @param internal Pointer to CTU CAN FD structure + * @param txtb_id ID of the buffer + * @param txb_order_idx Index of the buffer in transmission order + * @param txtb_st Status of the buffer + * + * @return None + */ +static void ctucanfd_handle_txtb_done( + struct rtems_can_chip *chip, + unsigned int txtb_id, + unsigned int txb_order_idx, + unsigned int txtb_st +) +{ + struct ctucanfd_internal *internal; + struct ctucanfd_txb_info *txb_info; + + internal = (struct ctucanfd_internal *) chip->internal; + txb_info = &internal->txb_info[txtb_id]; + ctucanfd_give_txtb_cmd( internal, TXT_CMD_SET_EMPTY, txtb_id ); + switch ( txtb_st ) { + case TXT_TOK: + rtems_can_stats_add_tx_done( &chip->chip_stats ); + rtems_can_stats_add_tx_bytes( + &chip->chip_stats, + txb_info->slot->frame.header.dlen + ); + rtems_can_queue_filter_frame_to_edges( + &chip->qends_dev->base, + txb_info->edge, + &txb_info->slot->frame, + CAN_FRAME_LOCAL + ); + rtems_can_queue_free_outslot( + &chip->qends_dev->base, + txb_info->edge, + txb_info->slot + ); + txb_info->slot = NULL; + txb_info->edge = NULL; + ctucanfd_txb_free( internal, txb_order_idx ); + break; + case TXT_ABT: + /* Reschedule aborted frames only if the chip is running, otherwise + * return them as TX error frames (the chip was stopped with some + * frames still in the FIFOs/HW buffers). + */ + if ( rtems_can_test_bit( RTEMS_CAN_CHIP_RUNNING, &chip->flags ) == 1 ) { + /* Reschedule aborted frames for future processing only if the chip + * is running. Otherwise handle them as errors. + */ + rtems_can_queue_push_back_outslot( + &chip->qends_dev->base, + txb_info->edge, + txb_info->slot + ); + txb_info->slot = NULL; + txb_info->edge = NULL; + ctucanfd_txb_free( internal, txb_order_idx ); + break; + } + case TXT_ERR: + #ifdef CTUCANFD_DEBUG + printk("CTUCANFD TXT_ERR\n"); + #endif /*CTUCANFD_DEBUG*/ + rtems_can_queue_filter_frame_to_edges( + &chip->qends_dev->base, + txb_info->edge, + &txb_info->slot->frame, + CAN_FRAME_TXERR + ); + rtems_can_queue_free_outslot( + &chip->qends_dev->base, + txb_info->edge, + txb_info->slot + ); + txb_info->slot = NULL; + txb_info->edge = NULL; + ctucanfd_txb_free( internal, txb_order_idx ); + rtems_can_stats_add_tx_error( &chip->chip_stats ); + break; + default: + #ifdef CTUCANFD_DEBUG + printk("CTUCANFD TXT state unknown\n"); + #endif /*CTUCANFD_DEBUG*/ + rtems_can_queue_free_outslot( + &chip->qends_dev->base, + txb_info->edge, + txb_info->slot + ); + txb_info->slot = NULL; + txb_info->edge = NULL; + ctucanfd_txb_free( internal, txb_order_idx ); + break; + } +} + +/** + * @brief This function handles TX interrupts + * + * TX done, TX abort and TX error interrupts are handled in this function. + * It takes care of freeing HW buffers on succesfull transfer or of + * rescheduling messages in case of abort/error. + * + * @param chip Pointer to CAN chip structure + * + * @return None + */ +static void ctucanfd_tx_interrupt( struct rtems_can_chip *chip ) +{ + struct ctucanfd_internal *internal = + ( struct ctucanfd_internal * )chip->internal; + + while ( 1 ) { + uint32_t tx_status; + unsigned int txb_order_idx = 0; + unsigned int txtb_id; + unsigned int txtb_st; + ctucanfd_write32( internal, CTUCANFD_INT_STAT, REG_INT_STAT_TXBHCI ); + tx_status = ctucanfd_read32( internal, CTUCANFD_TX_STATUS ); + if ( ( tx_status & TXT_ANY_DONE ) == 0 ) + break; + + txtb_id = ctucanfd_txb_from_order( internal->txb_order, txb_order_idx ); + txtb_st = TXTB_GET_STATUS( tx_status, txtb_id ); + if ( ( txtb_st & TXT_DONE ) == 0 ) { + for ( + txb_order_idx = 1; + txb_order_idx < RTEMS_CTUCANFD_NTXBUFS_MAX; + txb_order_idx++ + ) { + txtb_id = ctucanfd_txb_from_order( + internal->txb_order, + txb_order_idx + ); + txtb_st = TXTB_GET_STATUS( tx_status, txtb_id ); + if ( ( txtb_st & TXT_DONE ) != 0 ) + break; + } + + if ( txb_order_idx >= internal->ntxbufs ) { + ctucanfd_give_txtb_cmd( internal, TXT_CMD_SET_EMPTY, txtb_id ); + continue; + } + } + + ctucanfd_check_state( internal, "before Tx done processed" ); + ctucanfd_handle_txtb_done( chip, txtb_id, txb_order_idx, txtb_st ); + ctucanfd_check_state( internal, "after Tx done processed" ); + } +} + +/** + * @brief This function handles CAN interrupts. + * + * @param arg Argument passed throught interrupt routine. + * + * @return None + */ +static void ctucanfd_interrupt( void *arg ) +{ + struct rtems_can_chip *chip = (struct rtems_can_chip *)arg; + struct ctucanfd_internal *internal = chip->internal; + + /* Just disable interrupts and start worker thread. */ + + ctucanfd_write32( internal, CTUCANFD_INT_ENA_CLR, 0xffff ); + + rtems_binary_semaphore_post( &chip->qends_dev->worker_sem ); + + return; +} + +/** + * @brief This is the part of the worker function that processes interrupts. + * + * This is implemented as a while loop that goes on until there are no + * interrupts to be read. + * + * @param rtems_can_chip Pointer to chip structure. + * @param abort_recheck This indicates TX part of the worker requested abort. + * + * @return None + */ +static inline void ctucanfd_process_interrupts( + struct rtems_can_chip *chip, + int *abort_recheck +) +{ + struct ctucanfd_internal *internal = + (struct ctucanfd_internal *)chip->internal; +#ifdef CTUCANFD_DEBUG + int loop_counter = 0; +#endif /* CTUCANFD_DEBUG*/ + uint32_t isr; + uint32_t icr; + + while ( 1 ) { + ctucanfd_check_state( internal, "in worker loop start" ); +#ifdef CTUCANFD_DEBUG + if ( ++loop_counter > 100 ) { + printk("CTUCANFD CHECK detected too much active loops\n"); + } +#endif /*CTUCANFD_DEBUG*/ + + /* Get interrupt status register */ + isr = ctucanfd_read32( internal, CTUCANFD_INT_STAT ); + if ( ( isr == 0 ) && ( *abort_recheck == 0 ) ) { + /* Leave the loop if no interrupt is set */ + break; + } + + if ( FIELD_GET( REG_INT_STAT_RBNEI, isr ) ) { + /* RX available interrupt, receive new message */ + ctucanfd_write32( internal, CTUCANFD_INT_MASK_SET, REG_INT_STAT_RBNEI ); + ctucanfd_write32( internal, CTUCANFD_INT_STAT, REG_INT_STAT_RBNEI ); + ctucanfd_receive( chip ); + ctucanfd_write32( internal, CTUCANFD_INT_STAT, REG_INT_STAT_RBNEI ); + } + + if ( FIELD_GET( REG_INT_STAT_TXBHCI, isr ) || ( *abort_recheck == 1 ) ) { + /* TX interrupt */ + *abort_recheck = 0; + ctucanfd_tx_interrupt( chip ); + } + + if ( + FIELD_GET( REG_INT_STAT_BEI, isr ) || + FIELD_GET( REG_INT_STAT_EWLI, isr ) || + FIELD_GET( REG_INT_STAT_FCSI, isr ) || + FIELD_GET( REG_INT_STAT_ALI, isr ) || + FIELD_GET( REG_INT_STAT_DOI, isr ) + ) { + /* Error interrupts */ + icr = isr & ( + REG_INT_STAT_EWLI | + REG_INT_STAT_FCSI | + REG_INT_STAT_ALI | + REG_INT_STAT_BEI | + REG_INT_STAT_DOI + ); + + ctucanfd_write32( internal, CTUCANFD_INT_STAT, icr ); + ctucanfd_err_interrupt( chip, isr ); + } + } +} + +/** + * @brief This is the main worker for processing of TX and RX messages. + * + * The worker is released either from CAN interrupt or from upper layer + * when there is new frame to be sent. The function handles all interrupts + * and process new messages to be transfer. + * + * @param arg RTEMS task argument, this is the pointer to CAN chip structure. + * + * @return None + */ +static rtems_task ctucanfd_worker( rtems_task_argument arg ) +{ + struct rtems_can_chip *chip = (struct rtems_can_chip *)arg; + struct ctucanfd_internal *internal = + (struct ctucanfd_internal *)chip->internal; + struct ctucanfd_txb_info *txb_info; + struct rtems_can_queue_ends_dev *qends_dev = chip->qends_dev; + struct rtems_can_queue_ends *qends = &qends_dev->base; + struct rtems_can_queue_edge *qedge; + struct rtems_can_queue_slot *slot; + unsigned int txtb_id; + int abort_recheck = 0; + int ret; + + while ( 1 ) { + /* First process all received interrupts. This is implemented as a + * while loop that goes on until there are no interrupts to be read. + */ + ctucanfd_process_interrupts( chip, &abort_recheck ); + + if ( rtems_can_test_bit( RTEMS_CAN_CHIP_RUNNING, &chip->flags ) == 0 ) { + /* Abort all filled HW buffers. */ + for ( int i = 0; i < internal->txb_prio_tail[0]; i++ ) { + txtb_id = ctucanfd_txb_from_order( internal->txb_order, i ); + + if ( ctucanfd_get_tx_status( internal, txtb_id ) == TXT_ETY ) { + txb_info = &internal->txb_info[txtb_id]; + if ( txb_info != NULL ) { + rtems_can_queue_filter_frame_to_edges( + &chip->qends_dev->base, + txb_info->edge, + &txb_info->slot->frame, + CAN_FRAME_TXERR + ); + rtems_can_queue_free_outslot( + &chip->qends_dev->base, + txb_info->edge, + txb_info->slot + ); + txb_info->slot = NULL; + txb_info->edge = NULL; + ctucanfd_txb_free( internal, i ); + } + } else { + ctucanfd_give_txtb_cmd( internal, TXT_CMD_SET_ABORT, txtb_id ); + abort_recheck = 1; + } + } + + /* Clear the FIFOs filled with frames to be sent. */ + while ( rtems_can_queue_test_outslot( qends, &qedge, &slot ) >= 0 ) { + /* Filter these frames back to the application as TX error frames. */ + rtems_can_queue_filter_frame_to_edges( + &chip->qends_dev->base, + qedge, + &slot->frame, + CAN_FRAME_TXERR + ); + rtems_can_queue_free_outslot( + &chip->qends_dev->base, + qedge, + slot + ); + rtems_can_stats_add_tx_error( &chip->chip_stats ); + } + + if ( internal->txb_prio_tail[0] == 0 ) { + /* Notify the stop function all frames were aborted/sent back */ + rtems_binary_semaphore_post( &chip->stop_sem ); + } + } else if ( internal->txb_prio_tail[0] < internal->ntxbufs ) { + /* We have some space in HW buffers for outgoing messages, + * chek whether there is something to send. + */ + ret = rtems_can_queue_test_outslot( qends, &qedge, &slot ); + if ( ret >= 0 ) { + unsigned int txb_order_idx = internal->txb_prio_tail[0]; + unsigned int txtb_id = ctucanfd_txb_from_order ( + internal->txb_order, + txb_order_idx + ); + ctucanfd_check_state( internal, "before insert_frame" ); + /* Insert frame to HW buffer */ + bool ok = ctucanfd_insert_frame( internal, &slot->frame, txtb_id ); + if ( ok == true ) { + /* Frame inserted succesfully, update TX buffer representation, + * buffer priorities and set buffer as ready. + */ + txb_info = &internal->txb_info[txtb_id]; + txb_info->edge = qedge; + txb_info->slot = slot; + ctucanfd_txb_add( internal, txb_order_idx, qedge->edge_prio ); + ctucanfd_write32( + internal, + CTUCANFD_TX_PRIORITY, + ctucanfd_txb_order2prio( internal->txb_order ) + ); + ctucanfd_give_txtb_cmd( internal, TXT_CMD_SET_READY, txtb_id ); + ctucanfd_give_txtb_cmd( internal, TXT_CMD_SET_READY, txtb_id ); + ctucanfd_check_state( internal, "after insert_frame succeed" ); + continue; + } else { + /* Insert failed, schedule frame for later processing */ + ctucanfd_check_state( internal, "before insert_frame failed" ); + rtems_can_queue_push_back_outslot( qends, qedge, slot ); + } + } + } else { + /* There is no free space in HW buffers. Check whether pending + * message has higher priority class then some message in HW buffers. + */ + int pending_prio = -1; + int avail_prio; + for ( + avail_prio = 1; + avail_prio < RTEMS_CAN_QUEUE_PRIO_NR; + avail_prio++ + ) { + if ( internal->txb_prio_tail[avail_prio] < internal->ntxbufs ) { + pending_prio = rtems_can_queue_pending_outslot_prio( + qends, + avail_prio + ); + break; + } + } + if ( pending_prio >= avail_prio ) { + /* There is a pending message with higher priority class. Abort + * the lowest priority message and discard it from HW buffer. High + * priority message will be inserted into this buffer in next loop. + */ + unsigned int txtb_id; + txtb_id = ctucanfd_txb_from_order( + internal->txb_order, + internal->ntxbufs - 1 + ); + ctucanfd_give_txtb_cmd( internal, TXT_CMD_SET_ABORT, txtb_id ); + abort_recheck = 1; + continue; + } + } + + /* Re-enable the interrupts */ + ctucanfd_write32( internal, CTUCANFD_INT_ENA_SET, internal->int_ena ); + ctucanfd_write32( internal, CTUCANFD_INT_MASK_CLR, internal->int_ena ); + + ctucanfd_check_state( internal, "in worker before wait" ); + + rtems_binary_semaphore_wait( &qends_dev->worker_sem ); + } +} + +/** + * @brief This function implements chip specific ioctl calls. + * + * @param chip Pointer to chip structure. + * @param command IOCTL command + * @param arg Void pointer to IOCL argument + * + * @return Negative value on error. + */ +static int ctucanfd_chip_ioctl( + struct rtems_can_chip *chip, + ioctl_command_t command, + void *arg +) +{ + int ret; + + switch( command ) { + default: + ret = -EINVAL; + break; + } + + return ret; +} + +/** + * @brief This function calculates and sets bittiming from giver bitrate + * + * @param chip Pointer to chip structure. + * @param type Bittiming type (nominal, FD) + * @param bt Pointer to rtems_can_bittiming structure + * + * @return 0 on success, negated errno on error. + * @retval -EPERM Chip is already started, cannot change bittiming. + * @retval -EINVAL Incorrect bit time type. + */ +static int ctucanfd_calc_and_set_bittiming( + struct rtems_can_chip *chip, + int type, + struct rtems_can_bittiming *bt +) +{ + const struct rtems_can_bittiming_const *btc; + struct rtems_can_bittiming *chip_bt; + int ret; + + if ( rtems_can_test_bit( RTEMS_CAN_CHIP_RUNNING, &chip->flags ) ) { + /* Chip was already started, cannot change bitrate */ + return -EPERM; + } + + if ( type == RTEMS_CAN_BITTIME_NOMINAL ) { + btc = &ctucanfd_nominal_bittiming_const; + chip_bt = &chip->bittiming; + } else if ( type == RTEMS_CAN_BITTIME_DATA ) { + btc = &ctucanfd_data_bittiming_const; + chip_bt = &chip->data_bittiming; + } else { + return -EINVAL; + } + + ret = rtems_can_bitrate2bittiming( chip, bt, btc ); + if ( ret < 0 ) { + return ret; + } + + *chip_bt = *bt; + + return 0; +} + +/** + * @brief This function checks and sets given bittiming + * + * @param chip Pointer to chip structure. + * @param type Bittiming type (nominal, FD) + * @param bt Pointer to rtems_can_bittiming structure + * + * @return 0 on success, negated errno on error. + * @retval -EPERM Chip is already started, cannot change bittiming. + * @retval -EINVAL Incorrect bit time type. + */ +static int ctucanfd_check_and_set_bittiming( + struct rtems_can_chip *chip, + int type, + struct rtems_can_bittiming *bt +) +{ + if ( rtems_can_test_bit( RTEMS_CAN_CHIP_RUNNING, &chip->flags ) ) { + /* Chip was already started, cannot change bitrate */ + return -EPERM; + } + + // TODO: check whether inserted rtems_can_bittiming is within limits + + if ( type == RTEMS_CAN_BITTIME_NOMINAL ) { + chip->bittiming = *bt; + ctucanfd_set_bittiming( chip ); + } else if ( type == RTEMS_CAN_BITTIME_DATA ) { + chip->data_bittiming = *bt; + ctucanfd_set_data_bittiming( chip ); + } else { + return -EINVAL; + } + + return 0; +} + +/** + * @brief This function stops the chip. + * + * @param chip Pointer to chip structure. + * + * @return 0 on success. -ETIME in case of timeout or -EAGAIN if no timeout + * is set. + */ +static int ctucanfd_stop_chip( + struct rtems_can_chip *chip, + struct timespec *ts +) +{ + struct ctucanfd_internal *internal = chip->internal; + rtems_interval timeout; + struct timespec curr; + struct timespec final; + struct timespec towait; + uint32_t mode; + uint32_t mask = 0xffff; + int ret = -1; + + rtems_mutex_lock( &chip->lock ); + if ( rtems_can_test_bit( RTEMS_CAN_CHIP_RUNNING, &chip->flags ) == 0 ) { + /* Already stopped, return. */ + rtems_mutex_unlock( &chip->lock ); + return 0; + } + + rtems_can_clear_bit( RTEMS_CAN_CHIP_RUNNING, &chip->flags ); + internal->int_ena &= REG_INT_STAT_TXBHCI; + rtems_binary_semaphore_post( &chip->qends_dev->worker_sem ); + + if ( ts != NULL ) { + /* Get absolute monotonic final time */ + clock_gettime( CLOCK_MONOTONIC, &final ); + rtems_timespec_add_to( &final, ts ); + + clock_gettime( CLOCK_MONOTONIC, &curr ); + rtems_timespec_subtract( &curr, &final, &towait ); + if ( towait.tv_sec < 0 ) { + ret = rtems_binary_semaphore_try_wait( &chip->stop_sem ); + } else { + timeout = rtems_timespec_to_ticks( &towait ); + ret = rtems_binary_semaphore_wait_timed_ticks( + &chip->stop_sem, + timeout + ); + } + } else { + ret = rtems_binary_semaphore_try_wait( &chip->stop_sem ); + } + + if (ret < 0) { + /* The controller didn't managed to clear all frames before timeout, + * flush the buffers. + */ + rtems_can_queue_ends_flush_outlist( &chip->qends_dev->base ); + } + + /* Disable interrupts and disable the controller */ + internal->int_ena = 0; + ctucanfd_write32( internal, CTUCANFD_INT_ENA_CLR, mask ); + ctucanfd_write32( internal, CTUCANFD_INT_MASK_SET, mask ); + mode = ctucanfd_read32( internal, CTUCANFD_MODE ); + mode &= ~REG_MODE_ENA; + rtems_binary_semaphore_post( &chip->qends_dev->worker_sem ); + ctucanfd_write32( internal, CTUCANFD_MODE, mode ); + rtems_can_stats_set_state( &chip->chip_stats, CAN_STATE_STOPPED ); + rtems_can_bus_notify_chip_stop( chip ); + rtems_mutex_unlock( &chip->lock ); + + return ret; +} + +/** + * @brief This function starts the chip. + * + * It sets bit timing values, correct mode, enables interrupts and HW + * controller. + * + * @param chip Pointer to chip structure. + * + * @return 0 on success, negative value otherwise. + */ +static int ctucanfd_start_chip( struct rtems_can_chip *chip ) +{ + struct ctucanfd_internal *internal = chip->internal; + uint32_t regval; + int ret = 0; + + rtems_mutex_lock( &chip->lock ); + if ( rtems_can_test_bit( RTEMS_CAN_CHIP_RUNNING, &chip->flags ) ) { + /* Chip was already configured, skip */ + rtems_mutex_unlock( &chip->lock ); + return 0; + } + + internal->int_ena = 0; + internal->int_mask = 0; + + ctucanfd_write32( internal, CTUCANFD_TX_PRIORITY, 0x01234567 ); + + /* Set timestamp at the Start of Frame bit */ + ctucanfd_write32( internal, CTUCANFD_RX_STATUS, REG_RX_STATUS_RTSOP); + + /* Configure bit-rates and ssp */ + ret = ctucanfd_set_bittiming( chip ); + if ( ret < 0 ) + goto start_chip_unlock; + + ret = ctucanfd_set_data_bittiming( chip ); + if ( ret < 0 ) + goto start_chip_unlock; + + ret = ctucanfd_set_secondary_sample_point( chip ); + if ( ret < 0 ) + goto start_chip_unlock; + + /* Configure modes */ + ctucanfd_set_mode( internal, chip->ctrlmode ); + + /* Configure interrupts */ + internal->int_ena = REG_INT_STAT_RBNEI | + REG_INT_STAT_TXBHCI | + REG_INT_STAT_EWLI | + REG_INT_STAT_FCSI | + REG_INT_STAT_DOI; + + /* Bus error reporting -> Allow Error/Arb.lost interrupts */ + if ( chip->ctrlmode & CAN_CTRLMODE_BERR_REPORTING ) + internal->int_ena |= REG_INT_STAT_ALI | REG_INT_STAT_BEI; + + internal->int_mask = ~internal->int_ena; + + ctucanfd_write32( internal, CTUCANFD_INT_MASK_CLR, internal->int_ena ); + ctucanfd_write32( internal, CTUCANFD_INT_MASK_SET, internal->int_mask ); + ctucanfd_write32( internal, CTUCANFD_INT_ENA_SET, internal->int_ena ); + + /* Enable the controller */ + regval = ctucanfd_read32( internal, CTUCANFD_MODE ); + regval |= REG_MODE_ENA; + ctucanfd_write32( internal, CTUCANFD_MODE, regval ); + + /* Mark HW as configured */ + rtems_binary_semaphore_try_wait( &chip->stop_sem ); + rtems_can_set_bit( RTEMS_CAN_CHIP_RUNNING, &chip->flags ); + rtems_can_stats_reset( &chip->chip_stats ); + rtems_can_stats_set_state( &chip->chip_stats, CAN_STATE_ERROR_ACTIVE ); + +start_chip_unlock: + rtems_mutex_unlock( &chip->lock ); + return ret; +} + +/** + * @brief This function registers the chip operations. + * + * @param chip_ops Pointer to rtems_can_chip_ops structure. + * + * @return None + */ +static void ctucanfd_register( struct rtems_can_chip_ops *chip_ops ) +{ + chip_ops->start_chip = ctucanfd_start_chip; + chip_ops->stop_chip = ctucanfd_stop_chip; + chip_ops->chip_ioctl = ctucanfd_chip_ioctl; + chip_ops->get_chip_timestamp = ctucanfd_get_timestamp; + chip_ops->get_chip_info = ctucanfd_get_chip_info; + chip_ops->calc_bittiming = ctucanfd_calc_and_set_bittiming; + chip_ops->check_and_set_bittiming = ctucanfd_check_and_set_bittiming; +} + +struct rtems_can_chip *rtems_ctucanfd_initialize( + uintptr_t addr, + rtems_vector_number irq, + rtems_task_priority worker_priority, + int ntxbufs, + rtems_option irq_option, + unsigned long can_clk_rate +) +{ + rtems_id worker_task_id; + rtems_status_code sc; + struct rtems_can_chip *chip; + struct ctucanfd_internal *internal; + uint32_t txb_count; + int ret; + + if ( ntxbufs < 0 ) { + return NULL; + } + + internal = calloc( 1, sizeof( struct ctucanfd_internal ) ); + if ( internal == NULL ) { + return NULL; + } + + chip = calloc( 1, sizeof( struct rtems_can_chip ) ); + if ( chip == NULL ) { + free( internal ); + return NULL; + } + + chip->qends_dev = calloc( 1, sizeof( struct rtems_can_queue_ends_dev ) ); + if ( chip->qends_dev == NULL ) { + free( internal ); + free( chip ); + return NULL; + } + + chip->type = CAN_CTUCANFD_TYPE; + chip->irq = irq; + chip->freq = can_clk_rate; + chip->ctrlmode = CAN_CTRLMODE_FD; + chip->ctrlmode_supported = CAN_CTRLMODE_LOOPBACK | + CAN_CTRLMODE_LISTENONLY | + CAN_CTRLMODE_FD | + CAN_CTRLMODE_PRESUME_ACK | + CAN_CTRLMODE_BERR_REPORTING | + CAN_CTRLMODE_FD_NON_ISO | + CAN_CTRLMODE_ONE_SHOT; + chip->bittiming_const = &ctucanfd_nominal_bittiming_const; + chip->data_bittiming_const = &ctucanfd_data_bittiming_const; + chip->capabilities = RTEMS_CAN_CHIP_CAPABILITIES_FD; + rtems_mutex_init( &chip->lock, "ctucanfd_lock" ); + + ctucanfd_register( &chip->chip_ops ); + + internal->txb_order = CTUCANFD_INIT_TXB_ORDER; + internal->base = addr; + + if ( + ( ctucanfd_read32( internal, CTUCANFD_DEVICE_ID ) & CTUCANFD_ID_MASK ) != + CTUCANFD_ID + ) { + free( internal ); + free( chip->qends_dev ); + free( chip ); + return NULL; + } + + txb_count = FIELD_GET( + REG_TX_COMMAND_TXT_BUFFER_COUNT, + ctucanfd_read32( internal, CTUCANFD_TX_COMMAND ) + ); + + if ( txb_count == 0 ) { + /* Ensure the compatibility with older IP core versions before synthesis + * parameter for TX buffer count was been added. + */ + txb_count = 4; + } + + if ( ntxbufs > txb_count ) { + /* Limit the number of HW buffers if bigger than supported in HW. */ + ntxbufs = txb_count; + } + + if ( ntxbufs > RTEMS_CTUCANFD_NTXBUFS_MAX ) { + /* Limit the number of HW buffers if bigger than driver is compiled to. */ + ntxbufs = RTEMS_CTUCANFD_NTXBUFS_MAX; + } + + internal->ntxbufs = ntxbufs; + chip->internal = internal; + + ret = ctucanfd_reset( internal ); + if ( ret < 0 ) { + free( internal ); + free( chip->qends_dev ); + free( chip ); + return NULL; + } + + rtems_binary_semaphore_init( + &chip->stop_sem, + "can_ctucanfd_stop_sem" + ); + + rtems_can_queue_ends_init_chip( + chip, + "can_ctucanfd_worker" + ); + + sc = rtems_interrupt_handler_install( + chip->irq, + "ctucanfd", + irq_option, + ctucanfd_interrupt, + chip + ); + if ( sc != RTEMS_SUCCESSFUL ) { + free( internal ); + free( chip->qends_dev ); + free( chip ); + return NULL; + } + + sc = rtems_task_create( + rtems_build_name( 'C', 'A', 'N', 'C' ), + worker_priority, + RTEMS_MINIMUM_STACK_SIZE + 0x1000, + RTEMS_DEFAULT_MODES, + RTEMS_DEFAULT_ATTRIBUTES, + &worker_task_id + ); + if ( sc != RTEMS_SUCCESSFUL ) { + free( internal ); + rtems_interrupt_handler_remove( + chip->irq, + ctucanfd_interrupt, + chip + ); + free( chip->qends_dev ); + free( chip ); + return NULL; + } + + rtems_task_start( worker_task_id, ctucanfd_worker, + (rtems_task_argument)chip ); + + rtems_can_set_bit( RTEMS_CAN_CHIP_CONFIGURED, &chip->flags ); + rtems_can_stats_set_state( &chip->chip_stats, CAN_STATE_STOPPED ); + + return chip; +} diff --git a/cpukit/dev/can/ctucanfd/ctucanfd_internal.h b/cpukit/dev/can/ctucanfd/ctucanfd_internal.h new file mode 100644 index 0000000000..2fd66308e3 --- /dev/null +++ b/cpukit/dev/can/ctucanfd/ctucanfd_internal.h @@ -0,0 +1,148 @@ +/* SPDX-License-Identifier: BSD-2-Clause OR Apache-2.0 OR GPL-2.0-or-later */ + +/** + * @file + * + * @ingroup CTUCANFD + * + * @brief This file is part of CTU CAN FD core driver implementation. + */ + +/* +* Copyright (C) 2023-2024 Michal Lenc +* Implementation is based on Linux CTU CAN FD driver +* Copyright (C) 2015-2018 Ondrej Ille FEE CTU +* Copyright (C) 2018-2021 Ondrej Ille self-funded +* Copyright (C) 2018-2019 Martin Jerabek FEE CTU +* Copyright (C) 2018-2022 Pavel Pisa FEE CTU/self-funded +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions +* are met: +* 1. Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* 2. Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in the +* documentation and/or other materials provided with the distribution. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +* POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef __CTUCANFD_INTERNAL_H +#define __CTUCANFD_INTERNAL_H + +#include +#include + +#include + +#include "ctucanfd_kregs.h" + +/** + * @brief This represents one HW transmission buffer. + */ +struct ctucanfd_txb_info { + /** + * @brief This holds the pointer to the slot the buffer acquired. + * + * The stack does not free the slot immediately after it is assigned to + * the buffer, but waits for succesful transmission (or error/abort). Hence + * the buffer has to keep the information about the slot it acquired to + * provide correct error reporting or to schedule it for later processing. + */ + struct rtems_can_queue_slot *slot; + /** + * @brief This holds the pointer to the edge from which the slot was + * acquired. + * + * This information is useful for future filtering of echo frames back to + * sending instances. + */ + struct rtems_can_queue_edge *edge; +}; + +/** + * @brief This represents internal CTU CAN FD structure. + * + * This structure is CTU CAN FD controller dependent and provides + * access to non generic parts that do not belong to generic + * @ref rtems_can_chip structure. + */ +struct ctucanfd_internal { + /** + * @brief Base memory address. + */ + uintptr_t base; + /** + * @brief This member holds the enabled interrupts. + */ + uint32_t int_ena; + /** + * @brief This member holds the masked out interrupts. + */ + uint32_t int_mask; + /** + * @brief This member holds the array of @ref ctucanfd_txb_info structures. + */ + struct ctucanfd_txb_info txb_info[RTEMS_CTUCANFD_NTXBUFS_MAX]; + /** + * @brief This member holds tails for mapping of priority classes to bufders. + * + * These are the positions where buffers with frames from given priority + * class should be inserted. + */ + uint8_t txb_prio_tail[RTEMS_CAN_QUEUE_PRIO_NR]; + /** + * @brief This member holds the order in which the buffers should be + * scheduled for transmisison. + */ + uint32_t txb_order; + /** + * @brief This member holds the number of TX buffers for given core. + */ + unsigned int ntxbufs; +}; + +static inline void ctucanfd_write32( + struct ctucanfd_internal *internal, + enum ctu_can_fd_can_registers reg, + uint32_t val +) +{ + *( volatile uint32_t * )( internal->base + reg ) = val; +} + +static inline uint32_t ctucanfd_read32( + struct ctucanfd_internal *internal, + enum ctu_can_fd_can_registers reg +) +{ + return *( volatile uint32_t * )( internal->base + reg ); +} + +static inline void ctucanfd_write_txt_buf( + struct ctucanfd_internal *internal, + enum ctu_can_fd_can_registers buf_base, + uint32_t offset, + uint32_t val +) +{ + *( volatile uint32_t * )( internal->base + buf_base + offset ) = val; +} + +#define CTU_CAN_FD_ENABLED( internal ) (!!FIELD_GET( \ + REG_MODE_ENA, \ + ctucanfd_read32( internal, CTUCANFD_MODE ) \ +) ) + +#endif /* __CTUCANFD_PRIV_H */ diff --git a/cpukit/dev/can/ctucanfd/ctucanfd_kframe.h b/cpukit/dev/can/ctucanfd/ctucanfd_kframe.h new file mode 100644 index 0000000000..c333035c4e --- /dev/null +++ b/cpukit/dev/can/ctucanfd/ctucanfd_kframe.h @@ -0,0 +1,77 @@ +/* SPDX-License-Identifier: BSD-2-Clause OR Apache-2.0 OR GPL-2.0-or-later */ +/******************************************************************************* + * + * CTU CAN FD IP Core + * + * Copyright (C) 2015-2018 Ondrej Ille FEE CTU + * Copyright (C) 2018-2021 Ondrej Ille self-funded + * Copyright (C) 2018-2019 Martin Jerabek FEE CTU + * Copyright (C) 2018-2021 Pavel Pisa FEE CTU/self-funded + * + * Project advisors: + * Jiri Novak + * Pavel Pisa + * + * Department of Measurement (http://meas.fel.cvut.cz/) + * Faculty of Electrical Engineering (http://www.fel.cvut.cz) + * Czech Technical University (http://www.cvut.cz/) + ******************************************************************************/ + +/* This file is autogenerated, DO NOT EDIT! */ + +#ifndef __CTU_CAN_FD_CAN_FD_FRAME_FORMAT__ +#define __CTU_CAN_FD_CAN_FD_FRAME_FORMAT__ + +#include + +/* CAN_Frame_format memory map */ +enum ctu_can_fd_can_frame_format { + CTUCANFD_FRAME_FORMAT_W = 0x0, + CTUCANFD_IDENTIFIER_W = 0x4, + CTUCANFD_TIMESTAMP_L_W = 0x8, + CTUCANFD_TIMESTAMP_U_W = 0xc, + CTUCANFD_DATA_1_4_W = 0x10, + CTUCANFD_DATA_5_8_W = 0x14, + CTUCANFD_DATA_61_64_W = 0x4c, +}; + +/* CAN_FD_Frame_format memory region */ + +/* FRAME_FORMAT_W registers */ +#define REG_FRAME_FORMAT_W_DLC GENMASK(3, 0) +#define REG_FRAME_FORMAT_W_RTR BIT(5) +#define REG_FRAME_FORMAT_W_IDE BIT(6) +#define REG_FRAME_FORMAT_W_FDF BIT(7) +#define REG_FRAME_FORMAT_W_BRS BIT(9) +#define REG_FRAME_FORMAT_W_ESI_RSV BIT(10) +#define REG_FRAME_FORMAT_W_RWCNT GENMASK(15, 11) + +/* IDENTIFIER_W registers */ +#define REG_IDENTIFIER_W_IDENTIFIER_EXT GENMASK(17, 0) +#define REG_IDENTIFIER_W_IDENTIFIER_BASE GENMASK(28, 18) + +/* TIMESTAMP_L_W registers */ +#define REG_TIMESTAMP_L_W_TIME_STAMP_L_W GENMASK(31, 0) + +/* TIMESTAMP_U_W registers */ +#define REG_TIMESTAMP_U_W_TIMESTAMP_U_W GENMASK(31, 0) + +/* DATA_1_4_W registers */ +#define REG_DATA_1_4_W_DATA_1 GENMASK(7, 0) +#define REG_DATA_1_4_W_DATA_2 GENMASK(15, 8) +#define REG_DATA_1_4_W_DATA_3 GENMASK(23, 16) +#define REG_DATA_1_4_W_DATA_4 GENMASK(31, 24) + +/* DATA_5_8_W registers */ +#define REG_DATA_5_8_W_DATA_5 GENMASK(7, 0) +#define REG_DATA_5_8_W_DATA_6 GENMASK(15, 8) +#define REG_DATA_5_8_W_DATA_7 GENMASK(23, 16) +#define REG_DATA_5_8_W_DATA_8 GENMASK(31, 24) + +/* DATA_61_64_W registers */ +#define REG_DATA_61_64_W_DATA_61 GENMASK(7, 0) +#define REG_DATA_61_64_W_DATA_62 GENMASK(15, 8) +#define REG_DATA_61_64_W_DATA_63 GENMASK(23, 16) +#define REG_DATA_61_64_W_DATA_64 GENMASK(31, 24) + +#endif diff --git a/cpukit/dev/can/ctucanfd/ctucanfd_kregs.h b/cpukit/dev/can/ctucanfd/ctucanfd_kregs.h new file mode 100644 index 0000000000..e18bb95d26 --- /dev/null +++ b/cpukit/dev/can/ctucanfd/ctucanfd_kregs.h @@ -0,0 +1,349 @@ +/* SPDX-License-Identifier: BSD-2-Clause OR Apache-2.0 OR GPL-2.0-or-later */ +/******************************************************************************* + * + * CTU CAN FD IP Core + * + * Copyright (C) 2015-2018 Ondrej Ille FEE CTU + * Copyright (C) 2018-2022 Ondrej Ille self-funded + * Copyright (C) 2018-2019 Martin Jerabek FEE CTU + * Copyright (C) 2018-2022 Pavel Pisa FEE CTU/self-funded + * + * Project advisors: + * Jiri Novak + * Pavel Pisa + * + * Department of Measurement (http://meas.fel.cvut.cz/) + * Faculty of Electrical Engineering (http://www.fel.cvut.cz) + * Czech Technical University (http://www.cvut.cz/) + ******************************************************************************/ + +/* This file is autogenerated, DO NOT EDIT! */ + +#ifndef __CTU_CAN_FD_CAN_FD_REGISTER_MAP__ +#define __CTU_CAN_FD_CAN_FD_REGISTER_MAP__ + +#include + +/* CAN_Registers memory map */ +enum ctu_can_fd_can_registers { + CTUCANFD_DEVICE_ID = 0x0, + CTUCANFD_VERSION = 0x2, + CTUCANFD_MODE = 0x4, + CTUCANFD_SETTINGS = 0x6, + CTUCANFD_STATUS = 0x8, + CTUCANFD_COMMAND = 0xc, + CTUCANFD_INT_STAT = 0x10, + CTUCANFD_INT_ENA_SET = 0x14, + CTUCANFD_INT_ENA_CLR = 0x18, + CTUCANFD_INT_MASK_SET = 0x1c, + CTUCANFD_INT_MASK_CLR = 0x20, + CTUCANFD_BTR = 0x24, + CTUCANFD_BTR_FD = 0x28, + CTUCANFD_EWL = 0x2c, + CTUCANFD_ERP = 0x2d, + CTUCANFD_FAULT_STATE = 0x2e, + CTUCANFD_REC = 0x30, + CTUCANFD_TEC = 0x32, + CTUCANFD_ERR_NORM = 0x34, + CTUCANFD_ERR_FD = 0x36, + CTUCANFD_CTR_PRES = 0x38, + CTUCANFD_FILTER_A_MASK = 0x3c, + CTUCANFD_FILTER_A_VAL = 0x40, + CTUCANFD_FILTER_B_MASK = 0x44, + CTUCANFD_FILTER_B_VAL = 0x48, + CTUCANFD_FILTER_C_MASK = 0x4c, + CTUCANFD_FILTER_C_VAL = 0x50, + CTUCANFD_FILTER_RAN_LOW = 0x54, + CTUCANFD_FILTER_RAN_HIGH = 0x58, + CTUCANFD_FILTER_CONTROL = 0x5c, + CTUCANFD_FILTER_STATUS = 0x5e, + CTUCANFD_RX_MEM_INFO = 0x60, + CTUCANFD_RX_POINTERS = 0x64, + CTUCANFD_RX_STATUS = 0x68, + CTUCANFD_RX_SETTINGS = 0x6a, + CTUCANFD_RX_DATA = 0x6c, + CTUCANFD_TX_STATUS = 0x70, + CTUCANFD_TX_COMMAND = 0x74, + CTUCANFD_TXTB_INFO = 0x76, + CTUCANFD_TX_PRIORITY = 0x78, + CTUCANFD_ERR_CAPT = 0x7c, + CTUCANFD_RETR_CTR = 0x7d, + CTUCANFD_ALC = 0x7e, + CTUCANFD_TS_INFO = 0x7f, + CTUCANFD_TRV_DELAY = 0x80, + CTUCANFD_SSP_CFG = 0x82, + CTUCANFD_RX_FR_CTR = 0x84, + CTUCANFD_TX_FR_CTR = 0x88, + CTUCANFD_DEBUG_REGISTER = 0x8c, + CTUCANFD_YOLO_REG = 0x90, + CTUCANFD_TIMESTAMP_LOW = 0x94, + CTUCANFD_TIMESTAMP_HIGH = 0x98, + CTUCANFD_TXTB1_DATA_1 = 0x100, + CTUCANFD_TXTB1_DATA_2 = 0x104, + CTUCANFD_TXTB1_DATA_20 = 0x14c, + CTUCANFD_TXTB2_DATA_1 = 0x200, + CTUCANFD_TXTB2_DATA_2 = 0x204, + CTUCANFD_TXTB2_DATA_20 = 0x24c, + CTUCANFD_TXTB3_DATA_1 = 0x300, + CTUCANFD_TXTB3_DATA_2 = 0x304, + CTUCANFD_TXTB3_DATA_20 = 0x34c, + CTUCANFD_TXTB4_DATA_1 = 0x400, + CTUCANFD_TXTB4_DATA_2 = 0x404, + CTUCANFD_TXTB4_DATA_20 = 0x44c, +}; + +/* Control_registers memory region */ + +/* DEVICE_ID VERSION registers */ +#define REG_DEVICE_ID_DEVICE_ID GENMASK(15, 0) +#define REG_DEVICE_ID_VER_MINOR GENMASK(23, 16) +#define REG_DEVICE_ID_VER_MAJOR GENMASK(31, 24) + +/* MODE SETTINGS registers */ +#define REG_MODE_RST BIT(0) +#define REG_MODE_BMM BIT(1) +#define REG_MODE_STM BIT(2) +#define REG_MODE_AFM BIT(3) +#define REG_MODE_FDE BIT(4) +#define REG_MODE_TTTM BIT(5) +#define REG_MODE_ROM BIT(6) +#define REG_MODE_ACF BIT(7) +#define REG_MODE_TSTM BIT(8) +#define REG_MODE_RXBAM BIT(9) +#define REG_MODE_SAM BIT(11) +#define REG_MODE_RTRLE BIT(16) +#define REG_MODE_RTRTH GENMASK(20, 17) +#define REG_MODE_ILBP BIT(21) +#define REG_MODE_ENA BIT(22) +#define REG_MODE_NISOFD BIT(23) +#define REG_MODE_PEX BIT(24) +#define REG_MODE_TBFBO BIT(25) +#define REG_MODE_FDRF BIT(26) + +/* STATUS registers */ +#define REG_STATUS_RXNE BIT(0) +#define REG_STATUS_DOR BIT(1) +#define REG_STATUS_TXNF BIT(2) +#define REG_STATUS_EFT BIT(3) +#define REG_STATUS_RXS BIT(4) +#define REG_STATUS_TXS BIT(5) +#define REG_STATUS_EWL BIT(6) +#define REG_STATUS_IDLE BIT(7) +#define REG_STATUS_PEXS BIT(8) +#define REG_STATUS_STCNT BIT(16) + +/* COMMAND registers */ +#define REG_COMMAND_RXRPMV BIT(1) +#define REG_COMMAND_RRB BIT(2) +#define REG_COMMAND_CDO BIT(3) +#define REG_COMMAND_ERCRST BIT(4) +#define REG_COMMAND_RXFCRST BIT(5) +#define REG_COMMAND_TXFCRST BIT(6) +#define REG_COMMAND_CPEXS BIT(7) + +/* INT_STAT registers */ +#define REG_INT_STAT_RXI BIT(0) +#define REG_INT_STAT_TXI BIT(1) +#define REG_INT_STAT_EWLI BIT(2) +#define REG_INT_STAT_DOI BIT(3) +#define REG_INT_STAT_FCSI BIT(4) +#define REG_INT_STAT_ALI BIT(5) +#define REG_INT_STAT_BEI BIT(6) +#define REG_INT_STAT_OFI BIT(7) +#define REG_INT_STAT_RXFI BIT(8) +#define REG_INT_STAT_BSI BIT(9) +#define REG_INT_STAT_RBNEI BIT(10) +#define REG_INT_STAT_TXBHCI BIT(11) + +/* INT_ENA_SET registers */ +#define REG_INT_ENA_SET_INT_ENA_SET GENMASK(11, 0) + +/* INT_ENA_CLR registers */ +#define REG_INT_ENA_CLR_INT_ENA_CLR GENMASK(11, 0) + +/* INT_MASK_SET registers */ +#define REG_INT_MASK_SET_INT_MASK_SET GENMASK(11, 0) + +/* INT_MASK_CLR registers */ +#define REG_INT_MASK_CLR_INT_MASK_CLR GENMASK(11, 0) + +/* BTR registers */ +#define REG_BTR_PROP GENMASK(6, 0) +#define REG_BTR_PH1 GENMASK(12, 7) +#define REG_BTR_PH2 GENMASK(18, 13) +#define REG_BTR_BRP GENMASK(26, 19) +#define REG_BTR_SJW GENMASK(31, 27) + +/* BTR_FD registers */ +#define REG_BTR_FD_PROP_FD GENMASK(5, 0) +#define REG_BTR_FD_PH1_FD GENMASK(11, 7) +#define REG_BTR_FD_PH2_FD GENMASK(17, 13) +#define REG_BTR_FD_BRP_FD GENMASK(26, 19) +#define REG_BTR_FD_SJW_FD GENMASK(31, 27) + +/* EWL ERP FAULT_STATE registers */ +#define REG_EWL_EW_LIMIT GENMASK(7, 0) +#define REG_EWL_ERP_LIMIT GENMASK(15, 8) +#define REG_EWL_ERA BIT(16) +#define REG_EWL_ERP BIT(17) +#define REG_EWL_BOF BIT(18) + +/* REC TEC registers */ +#define REG_REC_REC_VAL GENMASK(8, 0) +#define REG_REC_TEC_VAL GENMASK(24, 16) + +/* ERR_NORM ERR_FD registers */ +#define REG_ERR_NORM_ERR_NORM_VAL GENMASK(15, 0) +#define REG_ERR_NORM_ERR_FD_VAL GENMASK(31, 16) + +/* CTR_PRES registers */ +#define REG_CTR_PRES_CTPV GENMASK(8, 0) +#define REG_CTR_PRES_PTX BIT(9) +#define REG_CTR_PRES_PRX BIT(10) +#define REG_CTR_PRES_ENORM BIT(11) +#define REG_CTR_PRES_EFD BIT(12) + +/* FILTER_A_MASK registers */ +#define REG_FILTER_A_MASK_BIT_MASK_A_VAL GENMASK(28, 0) + +/* FILTER_A_VAL registers */ +#define REG_FILTER_A_VAL_BIT_VAL_A_VAL GENMASK(28, 0) + +/* FILTER_B_MASK registers */ +#define REG_FILTER_B_MASK_BIT_MASK_B_VAL GENMASK(28, 0) + +/* FILTER_B_VAL registers */ +#define REG_FILTER_B_VAL_BIT_VAL_B_VAL GENMASK(28, 0) + +/* FILTER_C_MASK registers */ +#define REG_FILTER_C_MASK_BIT_MASK_C_VAL GENMASK(28, 0) + +/* FILTER_C_VAL registers */ +#define REG_FILTER_C_VAL_BIT_VAL_C_VAL GENMASK(28, 0) + +/* FILTER_RAN_LOW registers */ +#define REG_FILTER_RAN_LOW_BIT_RAN_LOW_VAL GENMASK(28, 0) + +/* FILTER_RAN_HIGH registers */ +#define REG_FILTER_RAN_HIGH_BIT_RAN_HIGH_VAL GENMASK(28, 0) + +/* FILTER_CONTROL FILTER_STATUS registers */ +#define REG_FILTER_CONTROL_FANB BIT(0) +#define REG_FILTER_CONTROL_FANE BIT(1) +#define REG_FILTER_CONTROL_FAFB BIT(2) +#define REG_FILTER_CONTROL_FAFE BIT(3) +#define REG_FILTER_CONTROL_FBNB BIT(4) +#define REG_FILTER_CONTROL_FBNE BIT(5) +#define REG_FILTER_CONTROL_FBFB BIT(6) +#define REG_FILTER_CONTROL_FBFE BIT(7) +#define REG_FILTER_CONTROL_FCNB BIT(8) +#define REG_FILTER_CONTROL_FCNE BIT(9) +#define REG_FILTER_CONTROL_FCFB BIT(10) +#define REG_FILTER_CONTROL_FCFE BIT(11) +#define REG_FILTER_CONTROL_FRNB BIT(12) +#define REG_FILTER_CONTROL_FRNE BIT(13) +#define REG_FILTER_CONTROL_FRFB BIT(14) +#define REG_FILTER_CONTROL_FRFE BIT(15) +#define REG_FILTER_CONTROL_SFA BIT(16) +#define REG_FILTER_CONTROL_SFB BIT(17) +#define REG_FILTER_CONTROL_SFC BIT(18) +#define REG_FILTER_CONTROL_SFR BIT(19) + +/* RX_MEM_INFO registers */ +#define REG_RX_MEM_INFO_RX_BUFF_SIZE GENMASK(12, 0) +#define REG_RX_MEM_INFO_RX_MEM_FREE GENMASK(28, 16) + +/* RX_POINTERS registers */ +#define REG_RX_POINTERS_RX_WPP GENMASK(11, 0) +#define REG_RX_POINTERS_RX_RPP GENMASK(27, 16) + +/* RX_STATUS RX_SETTINGS registers */ +#define REG_RX_STATUS_RXE BIT(0) +#define REG_RX_STATUS_RXF BIT(1) +#define REG_RX_STATUS_RXMOF BIT(2) +#define REG_RX_STATUS_RXFRC GENMASK(14, 4) +#define REG_RX_STATUS_RTSOP BIT(16) + +/* RX_DATA registers */ +#define REG_RX_DATA_RX_DATA GENMASK(31, 0) + +/* TX_STATUS registers */ +#define REG_TX_STATUS_TX1S GENMASK(3, 0) +#define REG_TX_STATUS_TX2S GENMASK(7, 4) +#define REG_TX_STATUS_TX3S GENMASK(11, 8) +#define REG_TX_STATUS_TX4S GENMASK(15, 12) +#define REG_TX_STATUS_TX5S GENMASK(19, 16) +#define REG_TX_STATUS_TX6S GENMASK(23, 20) +#define REG_TX_STATUS_TX7S GENMASK(27, 24) +#define REG_TX_STATUS_TX8S GENMASK(31, 28) + +/* TX_COMMAND TXTB_INFO registers */ +#define REG_TX_COMMAND_TXCE BIT(0) +#define REG_TX_COMMAND_TXCR BIT(1) +#define REG_TX_COMMAND_TXCA BIT(2) +#define REG_TX_COMMAND_TXB1 BIT(8) +#define REG_TX_COMMAND_TXB2 BIT(9) +#define REG_TX_COMMAND_TXB3 BIT(10) +#define REG_TX_COMMAND_TXB4 BIT(11) +#define REG_TX_COMMAND_TXB5 BIT(12) +#define REG_TX_COMMAND_TXB6 BIT(13) +#define REG_TX_COMMAND_TXB7 BIT(14) +#define REG_TX_COMMAND_TXB8 BIT(15) +#define REG_TX_COMMAND_TXT_BUFFER_COUNT GENMASK(19, 16) + +/* TX_PRIORITY registers */ +#define REG_TX_PRIORITY_TXT1P GENMASK(2, 0) +#define REG_TX_PRIORITY_TXT2P GENMASK(6, 4) +#define REG_TX_PRIORITY_TXT3P GENMASK(10, 8) +#define REG_TX_PRIORITY_TXT4P GENMASK(14, 12) +#define REG_TX_PRIORITY_TXT5P GENMASK(18, 16) +#define REG_TX_PRIORITY_TXT6P GENMASK(22, 20) +#define REG_TX_PRIORITY_TXT7P GENMASK(26, 24) +#define REG_TX_PRIORITY_TXT8P GENMASK(30, 28) + +/* ERR_CAPT RETR_CTR ALC TS_INFO registers */ +#define REG_ERR_CAPT_ERR_POS GENMASK(4, 0) +#define REG_ERR_CAPT_ERR_TYPE GENMASK(7, 5) +#define REG_ERR_CAPT_RETR_CTR_VAL GENMASK(11, 8) +#define REG_ERR_CAPT_ALC_BIT GENMASK(20, 16) +#define REG_ERR_CAPT_ALC_ID_FIELD GENMASK(23, 21) +#define REG_ERR_CAPT_TS_BITS GENMASK(29, 24) + +/* TRV_DELAY SSP_CFG registers */ +#define REG_TRV_DELAY_TRV_DELAY_VALUE GENMASK(6, 0) +#define REG_TRV_DELAY_SSP_OFFSET GENMASK(23, 16) +#define REG_TRV_DELAY_SSP_SRC GENMASK(25, 24) + +/* RX_FR_CTR registers */ +#define REG_RX_FR_CTR_RX_FR_CTR_VAL GENMASK(31, 0) + +/* TX_FR_CTR registers */ +#define REG_TX_FR_CTR_TX_FR_CTR_VAL GENMASK(31, 0) + +/* DEBUG_REGISTER registers */ +#define REG_DEBUG_REGISTER_STUFF_COUNT GENMASK(2, 0) +#define REG_DEBUG_REGISTER_DESTUFF_COUNT GENMASK(5, 3) +#define REG_DEBUG_REGISTER_PC_ARB BIT(6) +#define REG_DEBUG_REGISTER_PC_CON BIT(7) +#define REG_DEBUG_REGISTER_PC_DAT BIT(8) +#define REG_DEBUG_REGISTER_PC_STC BIT(9) +#define REG_DEBUG_REGISTER_PC_CRC BIT(10) +#define REG_DEBUG_REGISTER_PC_CRCD BIT(11) +#define REG_DEBUG_REGISTER_PC_ACK BIT(12) +#define REG_DEBUG_REGISTER_PC_ACKD BIT(13) +#define REG_DEBUG_REGISTER_PC_EOF BIT(14) +#define REG_DEBUG_REGISTER_PC_INT BIT(15) +#define REG_DEBUG_REGISTER_PC_SUSP BIT(16) +#define REG_DEBUG_REGISTER_PC_OVR BIT(17) +#define REG_DEBUG_REGISTER_PC_SOF BIT(18) + +/* YOLO_REG registers */ +#define REG_YOLO_REG_YOLO_VAL GENMASK(31, 0) + +/* TIMESTAMP_LOW registers */ +#define REG_TIMESTAMP_LOW_TIMESTAMP_LOW GENMASK(31, 0) + +/* TIMESTAMP_HIGH registers */ +#define REG_TIMESTAMP_HIGH_TIMESTAMP_HIGH GENMASK(31, 0) + +#endif diff --git a/cpukit/dev/can/ctucanfd/ctucanfd_txb.h b/cpukit/dev/can/ctucanfd/ctucanfd_txb.h new file mode 100644 index 0000000000..55738385ed --- /dev/null +++ b/cpukit/dev/can/ctucanfd/ctucanfd_txb.h @@ -0,0 +1,122 @@ +/* SPDX-License-Identifier: BSD-2-Clause OR Apache-2.0 OR GPL-2.0-or-later */ + +/** + * @file ctucanfd_txb.h + * + * @ingroup CTUCANFD + * + * @brief This file is part of CTU CAN FD core driver implementation. + */ + +/* + * Copyright (C) 2024 Michal Lenc FEE CTU + * Copyright (C) 2024 Pavel Pisa FEE CTU + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _DEV_CTUCANFD_CTUCANFD_TXB_H +#define _DEV_CTUCANFD_CTUCANFD_TXB_H + +#include "ctucanfd_internal.h" + +#undef TXT_DONE +#undef TXT_BF +#undef TXT_MASK +#undef TXT_ANY_DONE +#undef TXB_BF +#undef TXB_M +#undef TXB_ALL +#undef TXB_SH + +#define TXT_DONE 0x4 +#define TXT_BF 4 +#define TXT_MASK 0xf +#define TXT_ANY_DONE ( ( TXT_DONE << ( 0 * TXT_BF ) ) | \ + ( TXT_DONE << ( 1 * TXT_BF ) ) | \ + ( TXT_DONE << ( 2 * TXT_BF ) ) | \ + ( TXT_DONE << ( 3 * TXT_BF ) ) | \ + ( TXT_DONE << ( 4 * TXT_BF ) ) | \ + ( TXT_DONE << ( 5 * TXT_BF ) ) | \ + ( TXT_DONE << ( 6 * TXT_BF ) ) | \ + ( TXT_DONE << ( 7 * TXT_BF ) ) ) +#define TXTB_GET_STATUS( status, id ) ( ( status >> ( id * TXT_BF ) ) & \ + TXT_MASK) + +#define TXB_BF 4 +#define TXB_MASK 0xf +#define TXB_ALL 0xffffffff +#define TXB_SH(idx) (idx * TXB_BF) + +static inline uint32_t ctucanfd_txb_slot_demote( + uint32_t txb_order, + int from, + int to +) +{ + uint32_t txb_order_new; + uint32_t txb_move = ( txb_order >> TXB_SH( from ) ) & TXB_MASK; + uint32_t mask_from = TXB_ALL << TXB_SH( from ); + uint32_t mask_to = TXB_ALL << TXB_SH( to ); + txb_order_new = txb_move << TXB_SH( to ); + txb_order_new |= txb_order & ( ~mask_from | ( mask_to << TXB_BF ) ); + txb_order_new |= ( ( txb_order >> TXB_BF) & ~mask_to ) & mask_from; + return txb_order_new; +} + +static inline uint32_t ctucanfd_txb_slot_promote( + uint32_t txb_order, + int from, + int to +) +{ + uint32_t txb_order_new; + uint32_t txb_move = ( txb_order >> TXB_SH( from ) ) & TXB_MASK; + uint32_t mask_from = TXB_ALL << TXB_SH( from ); + uint32_t mask_to = TXB_ALL << TXB_SH( to ); + txb_order_new = txb_move << TXB_SH( to ); + txb_order_new |= ( txb_order ) & ( ~mask_to | ( mask_from << TXB_BF ) ); + txb_order_new |= ( ( (txb_order & mask_to ) & ~mask_from ) << TXB_BF ); + return txb_order_new; +} + +static inline uint32_t ctucanfd_txb_order2prio( uint32_t txb_order ) +{ + uint32_t prio = 0; + int i = RTEMS_CTUCANFD_NTXBUFS_MAX - 1; + do { + prio |= i << ( 4 * ( txb_order & TXB_MASK ) ); + txb_order >>= TXB_BF; + } while ( i-- ); + + return prio; +} + +static inline unsigned int ctucanfd_txb_from_order( + uint32_t txb_order, + unsigned int at +) +{ + return ( txb_order >> TXB_SH ( at ) ) & TXB_MASK; +} + +#endif /* _DEV_CTUCANFD_CTUCANFD_TXB_H */ \ No newline at end of file diff --git a/cpukit/include/dev/can/ctucanfd.h b/cpukit/include/dev/can/ctucanfd.h new file mode 100644 index 0000000000..2e1a2f3023 --- /dev/null +++ b/cpukit/include/dev/can/ctucanfd.h @@ -0,0 +1,86 @@ +/* SPDX-License-Identifier: BSD-2-Clause OR Apache-2.0 OR GPL-2.0-or-later */ + +/** +* @file +* +* @ingroup CTUCANFD +* +* @brief This header file contains the implementation of CTU CAN FD + * controller. +*/ + +/* +* Copyright (C) 2023-2024 Michal Lenc +* Implementation is based on Linux CTU CAN FD driver +* Copyright (C) 2015-2018 Ondrej Ille FEE CTU +* Copyright (C) 2018-2021 Ondrej Ille self-funded +* Copyright (C) 2018-2019 Martin Jerabek FEE CTU +* Copyright (C) 2018-2022 Pavel Pisa FEE CTU/self-funded +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions +* are met: +* 1. Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* 2. Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in the +* documentation and/or other materials provided with the distribution. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +* POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef _DEV_CAN_CTUCANFD_H +#define _DEV_CAN_CTUCANFD_H + +/** + * @brief This defines the maximum number of TX buffers. + * + * This is the maximum number of HW buffers that can be implemented to any + * CTU CAN FD core. The user can use this as an argument to initialization + * function @ref rtems_ctucanfd_initialize and the function automatically + * limits the used buffers based on the used core. + */ +#define RTEMS_CTUCANFD_NTXBUFS_MAX ( 8 ) + +struct rtems_can_chip; + +/** + * @brief This function initializes the CTU CAN FD controller. + * + * This is an entry point for CTU CAN FD controller initialization. This + * function allocates generic CAN and CTU CAN FD related structures, sets + * default values and initializes the resources (interrupt handler, semaphore. + * worker thread). + * + * @param addr CTU CAN FD code base address. + * @param irq Interrupt number + * @param worker_priotiry The priority of TX/RX worker thread + * @param ntxbufs Number of TX hardware buffers to be used. + * The function automatically limits the maximum HW buffers based on the + * used core. You can use @ref RTEMS_CTUCANFD_NTXBUFS_MAX to setup maximum + * available buffers. + * @param irq_option RTEMS_INTERRUPT_SHARED or RTEMS_INTERRUPT_UNIQUE + * @param can_clk_rate CAN clock rate. + * + * @return Pointer to CAN chip structure on success, NULL otherwise. + */ +struct rtems_can_chip *rtems_ctucanfd_initialize( + uintptr_t addr, + rtems_vector_number irq, + rtems_task_priority worker_priority, + int ntxbufs, + rtems_option irq_option, + unsigned long can_clk_rate +); + +#endif /* _DEV_CAN_CTUCANFD_H */ diff --git a/spec/build/cpukit/librtemscpu.yml b/spec/build/cpukit/librtemscpu.yml index 3a1517b1ab..4825d9326c 100644 --- a/spec/build/cpukit/librtemscpu.yml +++ b/spec/build/cpukit/librtemscpu.yml @@ -48,6 +48,7 @@ install: - cpukit/include/dev/can/can-stats.h - cpukit/include/dev/can/can-virtual.h - cpukit/include/dev/can/can.h + - cpukit/include/dev/can/ctucanfd.h - destination: ${BSP_INCLUDEDIR}/dev/i2c source: - cpukit/include/dev/i2c/eeprom.h @@ -550,6 +551,7 @@ source: - cpukit/dev/can/can-quekern.c - cpukit/dev/can/can-queue.c - cpukit/dev/can/can-virtual.c +- cpukit/dev/can/ctucanfd/ctucanfd.c - cpukit/dev/flash/flashdev.c - cpukit/dev/i2c/eeprom.c - cpukit/dev/i2c/fpga-i2c-slave.c