rt-thread/bsp/phytium/libraries/standalone/drivers/can/fcan/fcan.c

1154 lines
35 KiB
C
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
* Copyright : (C) 2022 Phytium Information Technology, Inc.
* All Rights Reserved.
*
* This program is OPEN SOURCE software: you can redistribute it and/or modify it
* under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd,
* either version 1.0 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the Phytium Public License for more details.
*
*
* FilePath: fcan.c
* Date: 2021-04-29 10:21:53
* LastEditTime: 2022-02-18 08:29:20
* Description:  This files is for the can functions
*
* Modify History:
* Ver   Who        Date         Changes
* ----- ------     --------    --------------------------------------
* 1.0 wangxiaodong 2022/5/26 first release
* 1.1 wangxiaodong 2022/9/23 improve functions
* 1.2 zhangyan 2022/12/7 improve functions
*/
#include "string.h"
#include <limits.h>
#include <stdlib.h>
#include "fkernel.h"
#include "fcan.h"
#include "fcan_hw.h"
#include "fassert.h"
#include "fdebug.h"
#include "fswap.h"
#include "fparameters.h"
#include "fsleep.h"
#define FT_CAN_DEBUG_TAG "FT_CAN"
#define FCAN_DEBUG(format, ...) FT_DEBUG_PRINT_D(FT_CAN_DEBUG_TAG, format, ##__VA_ARGS__)
#define FCAN_INFO(format, ...) FT_DEBUG_PRINT_I(FT_CAN_DEBUG_TAG, format, ##__VA_ARGS__)
#define FCAN_WARN(format, ...) FT_DEBUG_PRINT_W(FT_CAN_DEBUG_TAG, format, ##__VA_ARGS__)
#define FCAN_ERROR(format, ...) FT_DEBUG_PRINT_E(FT_CAN_DEBUG_TAG, format, ##__VA_ARGS__)
typedef struct
{
u32 tseg1_min; /* Time segement 1 = prop_seg + phase_seg1 */
u32 tseg1_max;
u32 tseg2_min; /* Time segement 2 = phase_seg2 */
u32 tseg2_max;
u32 sjw_max; /* Synchronisation jump width */
u32 brp_min; /* Bit-rate prescaler */
u32 brp_max;
u32 brp_inc;
} FCanBittimingConst;
/* 仲裁段速率默认值 */
static const FCanBittimingConst FCanArbBitConst =
{
.tseg1_min = FCAN_ARB_TSEG1_MIN, /* Time segement 1 = prop_seg + phase_seg1 */
.tseg1_max = FCAN_ARB_TSEG1_MAX,
.tseg2_min = FCAN_ARB_TSEG2_MIN, /* Time segement 2 = phase_seg2 */
.tseg2_max = FCAN_ARB_TSEG2_MAX,
.sjw_max = FCAN_ARB_SJW_MAX, /* Synchronisation jump width */
.brp_min = FCAN_ARB_BRP_MIN, /* Bit-rate prescaler */
.brp_max = FCAN_ARB_BRP_MAX,
.brp_inc = FCAN_ARB_BRP_INC,
};
/* 数据段速率默认值 */
static const FCanBittimingConst FCanDataBitConst =
{
.tseg1_min = FCAN_DATA_TSEG1_MIN, /* Time segement 1 = prop_seg + phase_seg1 */
.tseg1_max = FCAN_DATA_TSEG1_MAX,
.tseg2_min = FCAN_DATA_TSEG2_MIN, /* Time segement 2 = phase_seg2 */
.tseg2_max = FCAN_DATA_TSEG2_MAX,
.sjw_max = FCAN_DATA_SJW_MAX, /* Synchronisation jump width */
.brp_min = FCAN_DATA_BRP_MIN, /* Bit-rate prescaler */
.brp_max = FCAN_DATA_BRP_MAX,
.brp_inc = FCAN_DATA_BRP_INC,
};
/* calculate the can sample point */
static s32 FCanUpdateSamplePoint(const FCanBittimingConst *btc,
u32 sample_point_nominal, u32 tseg,
u32 *tseg1_ptr, u32 *tseg2_ptr,
u32 *sample_point_error_ptr)
{
u32 sample_point_error, best_sample_point_error = UINT_MAX;
u32 sample_point, best_sample_point = 0;
u32 tseg1, tseg2;
s32 i;
for (i = 0; i <= 1; i++)
{
tseg2 = tseg + CAN_CALC_SYNC_SEG - (sample_point_nominal * (tseg + CAN_CALC_SYNC_SEG)) / 1000 - i;
tseg2 = clamp(tseg2, btc->tseg2_min, btc->tseg2_max);
tseg1 = tseg - tseg2;
if (tseg1 > btc->tseg1_max)
{
tseg1 = btc->tseg1_max;
tseg2 = tseg - tseg1;
}
sample_point = 1000 * (tseg + CAN_CALC_SYNC_SEG - tseg2) / (tseg + CAN_CALC_SYNC_SEG);
sample_point_error = abs(sample_point_nominal - sample_point);
if ((sample_point <= sample_point_nominal) && (sample_point_error < best_sample_point_error))
{
best_sample_point = sample_point;
best_sample_point_error = sample_point_error;
*tseg1_ptr = tseg1;
*tseg2_ptr = tseg2;
}
}
if (sample_point_error_ptr)
{
*sample_point_error_ptr = best_sample_point_error;
}
return best_sample_point;
}
/**
* @name: FCanCalcBittiming
* @msg: This routine calculate Bit timing
* @param {structFCanBittiming} *bt_p is is a structure that contains the CAN baud rate configuration parameter , The user needs to fill in the baudrate first
* @param {u32} target_baudrate, parameters of target baudrate
* @param {u32} target_sample_point, parameters of target sample point, 0 means the general configuration is used
* @param {FCanSegmentType} target_segment, specifies which target is to be selected. followed by FCAN_ARB_SEGMENT or FCAN_DATA_SEGMENT
* @return err code information, FCAN_SUCCESS indicates successothers indicates failed
*/
static FError FCanCalcBittiming(FCanBaudrateConfig *bt_p, u32 target_baudrate, u32 target_sample_point, FCanSegmentType target_segment)
{
u32 baudrate; /* current baudrate */
u32 baudrate_error; /* difference between current and nominal value */
u32 best_baudrate_error = UINT_MAX;
u32 sample_point_error; /* difference between current and nominal value */
u32 best_sample_point_error = UINT_MAX;
u32 sample_point_nominal; /* nominal sample point */
u32 best_tseg = 0; /* current best value for tseg */
u32 best_brp = 0; /* current best value for brp */
u32 brp, tsegall, tseg, tseg1 = 0, tseg2 = 0;
u64 v64;
u32 reg_val;
const FCanBittimingConst *btc;
FCanBaudrateConfig *bt = bt_p;
FASSERT(bt_p != NULL);
FASSERT(target_segment < FCAN_SEGMENT_TYPE_NUM);
if (target_segment == FCAN_DATA_SEGMENT)
{
btc = &FCanDataBitConst;
}
else
{
btc = &FCanArbBitConst;
}
if (target_sample_point)
{
sample_point_nominal = target_sample_point;
}
else
{
if (target_baudrate > 4000000)
{
sample_point_nominal = 650;
}
else if (target_baudrate > 2000000)
{
sample_point_nominal = 680;
}
else if (target_baudrate > 1000000)
{
sample_point_nominal = 725;
}
else if (target_baudrate > 800000)
{
sample_point_nominal = 750;
}
else if (target_baudrate > 500000)
{
sample_point_nominal = 800;
}
else
{
sample_point_nominal = 875;
}
}
for (tseg = (btc->tseg1_max + btc->tseg2_max) * 2 + 1;
tseg >= (btc->tseg1_min + btc->tseg2_min) * 2; tseg--)
{
tsegall = CAN_CALC_SYNC_SEG + tseg / 2;
/* Compute all possible tseg choices (tseg=tseg1+tseg2) */
brp = FCAN_CLK_FREQ_HZ / (tsegall * target_baudrate) + tseg % 2;
/* choose brp step which is possible in system */
brp = (brp / btc->brp_inc) * btc->brp_inc;
if ((brp < btc->brp_min) || (brp > btc->brp_max))
{
continue;
}
baudrate = FCAN_CLK_FREQ_HZ / (brp * tsegall);
baudrate_error = abs(target_baudrate - baudrate);
/* tseg brp biterror */
if (baudrate_error > best_baudrate_error)
{
continue;
}
/* reset sample point error if we have a better baudrate */
if (baudrate_error < best_baudrate_error)
{
best_sample_point_error = UINT_MAX;
}
FCanUpdateSamplePoint(btc, sample_point_nominal, tseg / 2, &tseg1, &tseg2, &sample_point_error);
FCAN_DEBUG("target_segment=%d, brp=%d, tseg=%d, tseg1=%d, tseg2=%d, sample_point_nominal=%d",
target_segment, brp, tseg, tseg1, tseg2, sample_point_nominal);
u32 prop_seg = tseg1 / 2;
u32 phase_seg1 = tseg1 - prop_seg;
u32 phase_seg2 = tseg2;
u32 sjw = 1;
/* Setting Baud Rate prescalar value in BRPR Register */
reg_val = (brp - 1) << 16;
reg_val |= (prop_seg - 1) << 2;
reg_val |= (phase_seg1 - 1) << 5;
reg_val |= (phase_seg2 - 1) << 8;
reg_val |= (sjw - 1);
FCAN_DEBUG("reg_val=%#x\n", reg_val);
if (sample_point_error > best_sample_point_error)
{
continue;
}
best_sample_point_error = sample_point_error;
best_baudrate_error = baudrate_error;
best_tseg = tseg / 2;
best_brp = brp;
if (baudrate_error == 0 && sample_point_error == 0)
{
break;
}
}
if (best_baudrate_error)
{
/* Error in one-tenth of a percent */
v64 = (u64)best_baudrate_error * 1000;
do_div(v64, target_baudrate);
baudrate_error = (u32)v64;
if (baudrate_error > CAN_CALC_MAX_ERROR)
{
FCAN_ERROR("Baudrate error.");
return FCAN_FAILURE;
}
}
/* real sample point */
FCanUpdateSamplePoint(btc, sample_point_nominal, best_tseg,
&tseg1, &tseg2, NULL);
FCAN_DEBUG("tseg1=%d, tseg2=%d, sample_point_nominal=%d", tseg1, tseg2, sample_point_nominal);
bt->prop_seg = tseg1 / 2;
bt->phase_seg1 = tseg1 - bt->prop_seg;
bt->phase_seg2 = tseg2;
/* check for sjw user settings */
if (!bt->sjw || !btc->sjw_max)
{
bt->sjw = 1;
}
else
{
/* bt->sjw is at least 1 -> sanitize upper bound to sjw_max */
if (bt->sjw > btc->sjw_max)
{
bt->sjw = btc->sjw_max;
}
/* bt->sjw must not be higher than tseg2 */
if (tseg2 < bt->sjw)
{
bt->sjw = tseg2;
}
}
bt->brp = best_brp;
/* real baudrate */
if (target_baudrate != FCAN_CLK_FREQ_HZ / (bt->brp * (CAN_CALC_SYNC_SEG + tseg1 + tseg2)))
{
FCAN_ERROR("Target baudrate calculate timing failed.");
return FCAN_FAILURE;
}
FCAN_DEBUG("bt->prop_seg=%d, bt->phase_seg1=%d, bt->phase_seg2=%d, bt->sjw=%d, bt->brp=%d",
bt->prop_seg, bt->phase_seg1, bt->phase_seg2, bt->sjw, bt->brp);
return FCAN_SUCCESS;
}
static u32 FCanGetDlcLen(u32 dlc)
{
u32 dlc_len = 0;
if (dlc == 0)
{
dlc_len = 8;
}
switch (dlc)
{
case 1:
dlc_len = 1;
break;
case 2:
dlc_len = 2;
break;
case 3:
dlc_len = 3;
break;
case 4:
dlc_len = 4;
break;
case 5:
dlc_len = 5;
break;
case 6:
dlc_len = 6;
break;
case 7:
dlc_len = 7;
break;
case 8:
dlc_len = 8;
break;
case 9:
dlc_len = 12;
break;
case 10:
dlc_len = 16;
break;
case 11:
dlc_len = 20;
break;
case 12:
dlc_len = 24;
break;
case 13:
dlc_len = 32;
break;
case 14:
dlc_len = 48;
break;
case 15:
dlc_len = 64;
break;
default :
dlc_len = 0;
break;
}
return dlc_len;
}
static u32 FCanSetDlcLen(u32 len)
{
if (len <= 8)
{
return len;
}
else if (len <= 12)
{
return 9;
}
else if (len <= 16)
{
return 10;
}
else if (len <= 20)
{
return 11;
}
else if (len <= 24)
{
return 12;
}
else if (len <= 32)
{
return 13;
}
else if (len <= 48)
{
return 14;
}
else if (len <= 64)
{
return 15;
}
else
{
return 0;
}
}
/**
* @name: FCanReset
* @msg: reset a specific can instance
* @param {FCanCtrl} *pctrl, instance of FCan controller
* @return {*}
*/
void FCanReset(FCanCtrl *instance_p)
{
u32 reg_value;
FCanConfig *config_p;
FASSERT(instance_p != NULL);
FASSERT(instance_p->is_ready == FT_COMPONENT_IS_READY);
config_p = &instance_p->config;
FCAN_WRITE_REG32(config_p->base_address, FCAN_CTRL_OFFSET, 0);
reg_value = FCAN_READ_REG32(config_p->base_address, FCAN_CTRL_OFFSET);
if (reg_value & FCAN_CTRL_XFER_MASK)
{
FCAN_ERROR("Can is not in configration mode.");
return;
}
/* reset can */
FCAN_WRITE_REG32(config_p->base_address, FCAN_CTRL_OFFSET, FCAN_CTRL_RST_MASK);
}
/**
* @name: FCanDeInitialize
* @msg: Deinitializes a specific can instance
* @param {FCanCtrl} *pctrl, instance of FCan controller
* @return {*}
*/
void FCanDeInitialize(FCanCtrl *instance_p)
{
FASSERT(instance_p);
instance_p->is_ready = 0;
memset(instance_p, 0, sizeof(*instance_p));
return;
}
/**
* @name: FCanCfgInitialize
* @msg: Initializes a specific instance such that it is ready to be used.
* @param {FCanCtrl} *instance_p, instance of FCanCtrl controller
* @param {FCanConfig} *input_config_p, configuration parameters of FCanCtrl
* @return err code information, FCAN_SUCCESS indicates successothers indicates failed
*/
FError FCanCfgInitialize(FCanCtrl *instance_p, const FCanConfig *input_config_p)
{
FASSERT(instance_p != NULL);
FASSERT(input_config_p != NULL);
FError ret = FCAN_SUCCESS;
/*
* If the device is started, disallow the initialize and return a Status
* indicating it is started. This allows the user to de-initialize the device
* and reinitialize, but prevents a user from inadvertently
* initializing.
*/
if (FT_COMPONENT_IS_READY == instance_p->is_ready)
{
FCAN_WARN("Device is already initialized.");
}
/*Set default values and configuration data */
FCanDeInitialize(instance_p);
instance_p->config = *input_config_p;
instance_p->is_ready = FT_COMPONENT_IS_READY;
FCanReset(instance_p);
return ret;
}
/**
* @name: FCanStatusGet
* @msg: read can status, include transfer status, error and fifo count.
* @param {FCanCtrl} *instance_p, pointer to a FCanCtrl structure that contains
* the configuration information for the specified can module.
* @param {FCanStatus} *status_p, pointer to can status, include send and receive, error and fifo count .
* @return err code information, FCAN_SUCCESS indicates successothers indicates failed.
*/
FError FCanStatusGet(FCanCtrl *instance_p, FCanStatus *status_p)
{
FASSERT(instance_p != NULL);
FASSERT(instance_p->is_ready == FT_COMPONENT_IS_READY);
FASSERT(status_p != NULL);
uintptr base_address = instance_p->config.base_address;
u32 reg_val = 0;
reg_val = FCAN_READ_REG32(base_address, FCAN_XFER_STS_OFFSET);
status_p->xfer_status.xfers = FCAN_XFER_STS_XFERS_GET(reg_val);
status_p->xfer_status.rs = FCAN_XFER_STS_RS_GET(reg_val);
status_p->xfer_status.ts = FCAN_XFER_STS_TS_GET(reg_val);
status_p->xfer_status.fies = FCAN_XFER_STS_FIES_GET(reg_val);
status_p->xfer_status.fras = FCAN_XFER_STS_FRAS_GET(reg_val);
reg_val = FCAN_READ_REG32(base_address, FCAN_ERR_CNT_OFFSET);
status_p->rx_err_cnt = FCAN_ERR_CNT_RFN_GET(reg_val);
status_p->tx_err_cnt = FCAN_ERR_CNT_TFN_GET(reg_val);
reg_val = FCAN_READ_REG32(base_address, FCAN_FIFO_CNT_OFFSET);
status_p->tx_fifo_cnt = FCAN_FIFO_CNT_TFN_GET(reg_val);
status_p->rx_fifo_cnt = FCAN_FIFO_CNT_RFN_GET(reg_val);
return FCAN_SUCCESS;
}
/**
* @name: FCanRecv
* @msg: receive can message by specific can instance.
* @param {FCanCtrl} *instance_p, pointer to a FCanCtrl structure that contains
* the configuration information for the specific can module.
* @param {FCanFrame} *frame_p, can message receive struct.
* @return err code information, FCAN_SUCCESS indicates successothers indicates failed.
*/
FError FCanRecv(FCanCtrl *instance_p, FCanFrame *frame_p)
{
u32 canid;
u32 dlc;
int i = 0, j = 0;
FASSERT(instance_p != NULL);
FASSERT(instance_p->is_ready == FT_COMPONENT_IS_READY);
FASSERT(frame_p != NULL);
uintptr base_address = instance_p->config.base_address;
memset(frame_p, 0, sizeof(FCanFrame));
/* Read a frame from Phytium CAN */
canid = FCAN_READ_REG32(base_address, FCAN_RX_FIFO_OFFSET);
/* if canid is big-endian ,use swap change to little-endian */
canid = be32_to_cpu(canid);
FCAN_DEBUG("FCanRecv canid = %#x\n", canid);
/* identifier extension */
if (canid & FCAN_IDR_IDE_MASK)
{
dlc = FCAN_READ_REG32(base_address, FCAN_RX_FIFO_OFFSET);
dlc = be32_to_cpu(dlc);
FCAN_DEBUG("FCanRecv dlc = %#x\n", dlc);
if (dlc & FTCANFD_ID2_FDL_MASK)
{
if (dlc & FTCANFD_ID2_BRS_MASK)
{
frame_p->flags |= CANFD_BRS;
}
if (dlc & FTCANFD_ID2_ESI_MASK)
{
frame_p->flags |= CANFD_ESI;
}
dlc = FTCANFD_ID2_EDLC_GET(dlc);
}
else
{
dlc = FCAN_IDR_EDLC_GET(dlc);
}
frame_p->canid = FCAN_IDR_ID1_GET(canid) << FCAN_ACC_IDN_SHIFT;
frame_p->canid |= FCAN_IDR_ID2_GET(canid);
frame_p->canid |= CAN_EFF_FLAG;
if (canid & FCAN_IDR_RTR_MASK)
{
frame_p->canid |= CAN_RTR_FLAG;
}
}
else
{
if (canid & FTCANFD_ID1_FDL_MASK)
{
if (canid & FTCANFD_ID1_BRS_MASK)
{
frame_p->flags |= CANFD_BRS;
}
if (canid & FTCANFD_ID1_ESI_MASK)
{
frame_p->flags |= CANFD_ESI;
}
dlc = FTCANFD_ID1_SDLC_GET(canid);
}
else
{
dlc = FCAN_IDR_DLC_GET(canid);
}
/* The received frame is a standard format frame */
frame_p->canid = FCAN_IDR_ID1_GET(canid);
if (canid & FCAN_IDR_SRR_MASK)
{
frame_p->canid |= CAN_RTR_FLAG;
}
}
frame_p->candlc = FCanGetDlcLen(dlc);
FCAN_DEBUG("FCanRecv frame_p->candlc = %d\n", frame_p->candlc);
if (!(frame_p->canid & CAN_RTR_FLAG))
{
j = 0;
for (i = frame_p->candlc; i > 0; i -= 4)
{
*(u32 *)(frame_p->data + j) = FCAN_READ_REG32(base_address, FCAN_RX_FIFO_OFFSET);
j += 4;
}
if (i > 0)
{
*(u32 *)(frame_p->data + j) = FCAN_READ_REG32(base_address, FCAN_RX_FIFO_OFFSET);
}
}
return FCAN_SUCCESS;
}
/**
* @name: FCanSend
* @msg: send can message by specific can instance.
* @param {FCanCtrl} *instance_p, pointer to a FCanCtrl structure that contains
* the configuration information for the specific can module.
* @param {FCanFrame} *frame_p, can message send struct.
* @return err code information, FCAN_SUCCESS indicates successothers indicates failed.
*/
FError FCanSend(FCanCtrl *instance_p, FCanFrame *frame_p)
{
u32 id, dlc;
int i = 0, j = 0;
FASSERT(instance_p != NULL);
FASSERT(instance_p->is_ready == FT_COMPONENT_IS_READY);
uintptr base_address = instance_p->config.base_address;
while (FCAN_TX_FIFO_FULL(instance_p));
u32 can_send_dlc = FCanSetDlcLen(frame_p->candlc);
if (frame_p->canid & CAN_EFF_FLAG)
{
/* Extended CAN id format */
id = FCAN_IDR_ID2_SET(frame_p->canid & CAN_EFF_MASK);
id |= FCAN_IDR_ID1_SET((frame_p->canid & CAN_EFF_MASK) >>
(CAN_EFF_ID_BITS - CAN_SFF_ID_BITS));
/* The substibute remote TX request bit should be "1"
* for extended frames as in the Phytium CAN datasheet
*/
id |= FCAN_IDR_IDE_MASK | FCAN_IDR_SRR_MASK;
if (frame_p->canid & CAN_RTR_FLAG)
{
id |= FCAN_IDR_RTR_MASK;
}
if (instance_p->use_canfd == TRUE)
{
dlc = can_send_dlc << FCANFD_IDR_EDLC_SHIFT;
dlc |= FTCANFD_ID2_FDL_MASK;
/* enable brs-Bit Rate Switch */
frame_p->flags |= CANFD_BRS;
if (frame_p->flags & CANFD_BRS)
{
dlc |= FTCANFD_ID2_BRS_MASK;
}
if (frame_p->flags & CANFD_ESI)
{
dlc |= FTCANFD_ID2_ESI_MASK;
}
}
else
{
dlc = can_send_dlc << FCAN_IDR_EDLC_SHIFT;
}
FCAN_DEBUG("FCanSend id = %#x\n", id);
FCAN_DEBUG("FCanSend dlc = %#x\n", dlc);
FCAN_WRITE_REG32(base_address, FCAN_TX_FIFO_OFFSET, be32_to_cpu(id));
FCAN_WRITE_REG32(base_address, FCAN_TX_FIFO_OFFSET, be32_to_cpu(dlc));
}
else
{
/* Standard CAN id format */
id = FCAN_IDR_ID1_SET(frame_p->canid & CAN_SFF_MASK);
if (frame_p->canid & CAN_RTR_FLAG)
{
id |= FCAN_IDR_SRR_MASK;
}
FCAN_DEBUG("instance_p->use_canfd = %d, can_send_dlc = %d \n",
instance_p->use_canfd, can_send_dlc);
if (instance_p->use_canfd == TRUE)
{
dlc = ((can_send_dlc << FCANFD_IDR1_SDLC_SHIFT) | FTCANFD_IDR_PAD_MASK);
dlc |= FTCANFD_ID1_FDL_MASK;
/* enable brs-Bit Rate Switch */
frame_p->flags |= CANFD_BRS;
if (frame_p->flags & CANFD_BRS)
{
dlc |= FTCANFD_ID1_BRS_MASK;
}
if (frame_p->flags & CANFD_ESI)
{
dlc |= FTCANFD_ID1_ESI_MASK;
}
}
else
{
dlc = ((can_send_dlc << FCAN_IDR_SDLC_SHIFT) | FCAN_IDR_PAD_MASK);
}
id |= dlc;
FCAN_DEBUG("FCanSend id = %#x\n", id);
FCAN_WRITE_REG32(base_address, FCAN_TX_FIFO_OFFSET, be32_to_cpu(id));
}
if (!(frame_p->canid & CAN_RTR_FLAG))
{
j = 0;
FCAN_DEBUG("frame_p->canid=%#x, frame_p->candlc = %d\n", frame_p->canid, frame_p->candlc);
for (i = frame_p->candlc; i > 0; i -= 4)
{
FCAN_WRITE_REG32(base_address, FCAN_TX_FIFO_OFFSET, *(u32 *)(frame_p->data + j));
j += 4;
}
if (i > 0)
{
FCAN_WRITE_REG32(base_address, FCAN_TX_FIFO_OFFSET, *(u32 *)(frame_p->data + j));
}
}
/* triggers tranmission */
if (FCAN_READ_REG32(base_address, FCAN_CTRL_OFFSET) & FCAN_CTRL_TXREQ_MASK)
{
FCAN_CLEARBIT(base_address, FCAN_CTRL_OFFSET, FCAN_CTRL_XFER_MASK);
FCAN_SETBIT(base_address, FCAN_CTRL_OFFSET, FCAN_CTRL_TXREQ_MASK | FCAN_CTRL_XFER_MASK);
return FCAN_SUCCESS;
}
else
{
FCAN_WARN("Monitoring mode cannot send message!!!");
return FCAN_FAILURE;
}
}
/**
* @name: FCan_SetTiming
* @msg: This routine sets Bit time
* @param {FCanCtrl} *instance_p is a pointer to the FCanCtrl instance.
* @param {FCanBaudrateConfig} *bittiming_p, parameters of arbitration or data segment baudrate
* @param {FCanSegmentType} target_segment, specifies which target is to be selected. followed by FCAN_ARB_SEGMENT or FCAN_DATA_SEGMENT
* @out param:
* @return {*}
*/
static FError FCanSetTiming(FCanCtrl *instance_p, FCanBaudrateConfig *bittiming_p, FCanSegmentType target_segment)
{
u32 reg_val = 0;
u32 transfer_enable;
FASSERT(instance_p != NULL);
FASSERT(instance_p->is_ready == FT_COMPONENT_IS_READY);
FASSERT(target_segment < FCAN_SEGMENT_TYPE_NUM);
uintptr base_address = instance_p->config.base_address;
FASSERT(bittiming_p->brp != 0);
FASSERT(bittiming_p->prop_seg != 0);
FASSERT(bittiming_p->phase_seg1 != 0);
FASSERT(bittiming_p->phase_seg2 != 0);
FCAN_DEBUG("brp=%d, prop_seg=%d, phase_seg1=%d, phase_seg2=%d, sjw=%d",
bittiming_p->brp, bittiming_p->prop_seg, bittiming_p->phase_seg1,
bittiming_p->phase_seg2, bittiming_p->sjw);
/* Setting Baud Rate prescalar value in BRPR Register */
reg_val = (bittiming_p->brp - 1) << 16;
reg_val |= (bittiming_p->prop_seg - 1) << 2;
reg_val |= (bittiming_p->phase_seg1 - 1) << 5;
reg_val |= (bittiming_p->phase_seg2 - 1) << 8;
reg_val |= (bittiming_p->sjw - 1);
transfer_enable = (FCAN_READ_REG32(base_address, FCAN_CTRL_OFFSET) & FCAN_CTRL_XFER_MASK);
if (transfer_enable)
{
FCAN_CLEARBIT(base_address, FCAN_CTRL_OFFSET, FCAN_CTRL_XFER_MASK);
}
if (reg_val)
{
if (target_segment == FCAN_DATA_SEGMENT)
{
FCAN_WRITE_REG32(base_address, FCAN_DAT_RATE_CTRL_OFFSET, reg_val);
}
else
{
FCAN_WRITE_REG32(base_address, FCAN_ARB_RATE_CTRL_OFFSET, reg_val);
}
}
else
{
FCAN_ERROR("FCanSetTiming reg_val failed");
return FCAN_FAILURE;
}
return FCAN_SUCCESS;
}
/**
* @name: FCanBaudrateSet
* @msg: Set the fcan baudrate by FCanBaudrateConfig parameters.
* @param {FCanCtrl} *instance_p, instance of FCanCtrl controller
* @param {FCanBaudrateConfig} *baudrate_p, parameters of arbitration or data segment baudrate
* include baudrate, parameters of target baudrate
* include sample_point, parameters of target sample point, 0 means the general configuration is used.
* The value is the percentage of sampling points multiplied by 1000.
* For example, if sample point is 0.75, set target_sample_point = 750.
* Or manual config baudrate parameters.
* @param {FCanSegmentType} segment, specifies data segment or arbitration segment is selected. followed by FCAN_ARB_SEGMENT or FCAN_DATA_SEGMENT
* @return err code information, FQSPI_SUCCESS indicates successothers indicates failed
* @note this function is to set arb and data segment baudrate, according to the prop_seg,
* phase_seg1, phase_seg2 ,brp and sjw parameters, users can use this function to set can baudrate.
* A formula to calculate baudrate is:
* baudrate = FCAN_CLK_FREQ_HZ/(brp*(sjw+prop_seg+phase_seg1++phase_seg2))
* sample point = (sjw+prop_seg+phase_seg1)/(sjw+prop_seg+phase_seg1++phase_seg2)
* Recommended sample point :
* 75.0% : baudrate > 800000
* 80.0% : baudrate > 500000
* 87.5% : baudrate <= 500000
*/
FError FCanBaudrateSet(FCanCtrl *instance_p, FCanBaudrateConfig *baudrate_p)
{
FASSERT(instance_p != NULL);
FASSERT(instance_p->is_ready == FT_COMPONENT_IS_READY);
FCanSegmentType segment = baudrate_p->segment;
FASSERT(segment < FCAN_SEGMENT_TYPE_NUM);
u32 baudrate = baudrate_p->baudrate;
u32 sample_point = baudrate_p->sample_point;
if (baudrate_p->auto_calc == TRUE)
{
#if defined(CONFIG_TARGET_F2000_4) || defined(CONFIG_TARGET_D2000)
if ((segment == FCAN_ARB_SEGMENT) && ((baudrate > FCAN_BAUDRATE_1000K) || (baudrate < FCAN_BAUDRATE_50K)))
{
FCAN_ERROR("FCanBaudrateSet FCAN_ARB_SEGMENT baudrate = %d invalid", baudrate);
return FCAN_INVAL_PARAM;
}
if ((segment == FCAN_DATA_SEGMENT) && ((baudrate > FCAN_BAUDRATE_1000K) || (baudrate < FCAN_BAUDRATE_50K)))
{
FCAN_ERROR("FCanBaudrateSet FCAN_DATA_SEGMENT baudrate = %d invalid", baudrate);
return FCAN_INVAL_PARAM;
}
#elif defined(CONFIG_TARGET_E2000) || defined(TARDIGRADE)
if ((segment == FCAN_ARB_SEGMENT) && ((baudrate > FCAN_BAUDRATE_1000K) || (baudrate < FCAN_BAUDRATE_10K)))
{
FCAN_ERROR("FCanBaudrateSet FCAN_ARB_SEGMENT baudrate = %d invalid", baudrate);
return FCAN_INVAL_PARAM;
}
if ((segment == FCAN_DATA_SEGMENT) && ((baudrate > FCAN_BAUDRATE_5000K) || (baudrate < FCAN_BAUDRATE_10K)))
{
FCAN_ERROR("FCanBaudrateSet FCAN_DATA_SEGMENT baudrate = %d invalid", baudrate);
return FCAN_INVAL_PARAM;
}
#endif
}
FError ret = FCAN_SUCCESS;
FCanBaudrateConfig timing_config;
memset(&timing_config, 0, sizeof(timing_config));
/* Automatically calculate parameters based on baudrate and sample point */
if (baudrate_p->auto_calc == TRUE)
{
ret = FCanCalcBittiming(&timing_config, baudrate, sample_point, segment);
if (ret != FCAN_SUCCESS)
{
FCAN_ERROR("FCanCalcBittiming %d failed", segment);
return FCAN_FAILURE;
}
ret = FCanSetTiming(instance_p, &timing_config, segment);
if (ret != FCAN_SUCCESS)
{
FCAN_ERROR("FCanSetTiming %d failed", segment);
return FCAN_FAILURE;
}
}
else
{
ret = FCanSetTiming(instance_p, baudrate_p, segment);
if (ret != FCAN_SUCCESS)
{
FCAN_ERROR("FCanSetTiming failed");
return FCAN_FAILURE;
}
}
return ret;
}
/**
* @name: FCanFdEnable
* @msg: Enable or disable can.
* @param {FCanCtrl} *instance_p, instance of FCanCtrl controller
* @param {boolean} enable, TRUE-enable canfd, FALSE-disable canfd.
* @return err code information, FCAN_SUCCESS indicates successothers indicates failed
*/
FError FCanEnable(FCanCtrl *instance_p, boolean enable)
{
FASSERT(instance_p);
FASSERT(instance_p->is_ready == FT_COMPONENT_IS_READY);
uintptr base_addr = instance_p->config.base_address;
if (enable == TRUE)
{
FCAN_SETBIT(base_addr, FCAN_CTRL_OFFSET, FCAN_CTRL_XFER_MASK);
}
else
{
FCAN_CLEARBIT(base_addr, FCAN_CTRL_OFFSET, FCAN_CTRL_XFER_MASK);
}
return FCAN_SUCCESS;
}
/**
* @name: FCanIdMaskFilterSet
* @msg: Set the can mask and umask id.
* @param {FCanCtrl} *instance_p, instance of FCanCtrl controller
* @param {FCanIdMaskConfig} *id_mask_p, include filter register index, umask id and mask id
* id indicates a specific ID can receive
* mask indicates mask the corresponding bit of the id
* @return err code information, FCAN_SUCCESS indicates successothers indicates failed
*/
FError FCanIdMaskFilterSet(FCanCtrl *instance_p, FCanIdMaskConfig *id_mask_p)
{
FASSERT(instance_p);
FASSERT(instance_p->is_ready == FT_COMPONENT_IS_READY);
FASSERT(id_mask_p);
u32 filter_index = id_mask_p->filter_index;
FASSERT(filter_index < FCAN_ACC_ID_REG_NUM);
uintptr base_address = instance_p->config.base_address;
u32 id_reg_offset = 0;
u32 mask_reg_offset = 0;
u32 id = 0;
u32 mask = 0;
switch (filter_index)
{
case 0:
id_reg_offset = FCAN_ACC_ID0_OFFSET;
mask_reg_offset = FCAN_ACC_ID0_MASK_OFFSET;
break;
case 1:
id_reg_offset = FCAN_ACC_ID1_OFFSET;
mask_reg_offset = FCAN_ACC_ID1_MASK_OFFSET;
break;
case 2:
id_reg_offset = FCAN_ACC_ID2_OFFSET;
mask_reg_offset = FCAN_ACC_ID2_MASK_OFFSET;
break;
case 3:
id_reg_offset = FCAN_ACC_ID3_OFFSET;
mask_reg_offset = FCAN_ACC_ID3_MASK_OFFSET;
break;
default:
return FCAN_FAILURE;
}
if (id_mask_p->type == FCAN_STANDARD_FRAME)
{
id = id_mask_p->id << FCAN_ACC_IDN_SHIFT;
mask = id_mask_p->mask << FCAN_ACC_IDN_SHIFT;
}
FCAN_WRITE_REG32(base_address, id_reg_offset, id);
FCAN_WRITE_REG32(base_address, mask_reg_offset, mask);
return FCAN_SUCCESS;
}
/**
* @name: FCanIdMaskFilterEnable
* @msg: Set the can id mask filter enable.
* @param {FCanCtrl} *instance_p, instance of FCanCtrl controller
* @return {*}
*/
void FCanIdMaskFilterEnable(FCanCtrl *instance_p)
{
FCanConfig *config_p;
FASSERT(instance_p != NULL);
FASSERT(instance_p->is_ready == FT_COMPONENT_IS_READY);
config_p = &instance_p->config;
FCAN_SETBIT(config_p->base_address, FCAN_CTRL_OFFSET, FCAN_CTRL_AIME_MASK);
}
/**
* @name: FCanIdMaskFilterDisable
* @msg: Set the can id mask filter disable.
* @param {FCanCtrl} *instance_p, instance of FCanCtrl controller
* @return {*}
*/
void FCanIdMaskFilterDisable(FCanCtrl *instance_p)
{
FCanConfig *config_p;
FASSERT(instance_p != NULL);
FASSERT(instance_p->is_ready == FT_COMPONENT_IS_READY);
config_p = &instance_p->config;
FCAN_CLEARBIT(config_p->base_address, FCAN_CTRL_OFFSET, FCAN_CTRL_AIME_MASK);
}
/**
* @name: FCanInterruptEnable
* @msg: Enable can interrupt.
* @param {FCanCtrl} *instance_p, instance of FCanCtrl controller
* @param {FCanIntrEventType} event_type, interrupt event type
* @return err code information, FCAN_SUCCESS indicates successothers indicates failed
*/
FError FCanInterruptEnable(FCanCtrl *instance_p, FCanIntrEventType event_type)
{
FASSERT(instance_p != NULL);
FASSERT(FT_COMPONENT_IS_READY == instance_p->is_ready);
uintptr base_addr = instance_p->config.base_address;
u32 reg_val = 0;
reg_val = FCAN_READ_REG32(base_addr, FCAN_INTR_OFFSET);
switch (event_type)
{
case FCAN_INTR_EVENT_SEND:
reg_val |= FCAN_INTR_TEIE_MASK;
break;
case FCAN_INTR_EVENT_RECV:
reg_val |= FCAN_INTR_REIE_MASK;
break;
case FCAN_INTR_EVENT_ERROR:
reg_val |= FCAN_INTR_EIE_MASK;
break;
case FCAN_INTR_EVENT_BUSOFF:
reg_val |= FCAN_INTR_BOIE_MASK;
break;
case FCAN_INTR_EVENT_PERROE:
reg_val |= FCAN_INTR_PEIE_MASK;
break;
case FCAN_INTR_EVENT_PWARN:
reg_val |= FCAN_INTR_PWIE_MASK;
break;
case FCAN_INTR_EVENT_FIFOFULL:
reg_val |= FCAN_INTR_RFIE_MASK;
break;
case FCAN_INTR_EVENT_FIFOEMPTY:
reg_val |= FCAN_INTR_TFIE_MASK;
break;
default:
break;
}
FCAN_WRITE_REG32(base_addr, FCAN_INTR_OFFSET, reg_val);
return FCAN_SUCCESS;
}
/**
* @name: FCanFdEnable
* @msg: Enable or disable can fd.
* @param {FCanCtrl} *instance_p, instance of FCanCtrl controller
* @param {boolean} enable, TRUE-enable canfd, FALSE-disable canfd.
* @return err code information, FCAN_SUCCESS indicates successothers indicates failed
*/
FError FCanFdEnable(FCanCtrl *instance_p, boolean enable)
{
FASSERT(instance_p != NULL);
FASSERT(FT_COMPONENT_IS_READY == instance_p->is_ready);
uintptr base_addr = instance_p->config.base_address;
if (enable == TRUE)
{
instance_p->use_canfd = TRUE;
FCAN_SETBIT(base_addr, FCAN_CTRL_OFFSET, FCAN_CTRL_FDCRC_MASK | FCAN_CTRL_IOF_MASK);
}
else
{
instance_p->use_canfd = FALSE;
FCAN_SETBIT(base_addr, FCAN_CTRL_OFFSET, FCAN_CTRL_IOF_MASK);
FCAN_CLEARBIT(base_addr, FCAN_CTRL_OFFSET, FCAN_CTRL_FDCRC_MASK);
}
return FCAN_SUCCESS;
}
/**
* @name: FCanSetMode
* @msg: Set the transmit mode, Monitor or Normal
* @param {FCanCtrl} *instance_p, instance of FCanCtrl controller
* @param {u32} tran_mode,parameters of target mode,0 Monitor mode,1 Normal mode
* @return err code information, FCAN_SUCCESS indicates successothers indicates failed
*/
FError FCanSetMode(FCanCtrl *instance_p, u32 tran_mode)
{
FASSERT(instance_p != NULL);
FASSERT(FT_COMPONENT_IS_READY == instance_p->is_ready);
uintptr base_addr = instance_p->config.base_address;
if (tran_mode == FCAN_PROBE_MONITOR_MODE)
{
FCAN_CLEARBIT(base_addr, FCAN_CTRL_OFFSET, FCAN_CTRL_TXREQ_MASK);
}
if (tran_mode == FCAN_PROBE_NORMAL_MODE)
{
FCAN_SETBIT(base_addr, FCAN_CTRL_OFFSET, FCAN_CTRL_TXREQ_MASK);
}
return FCAN_SUCCESS;
}