You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1209 lines
35 KiB

/* vxbFtcan.c - CAN driver */
/*
*
* This program is OPEN SOURCE software: you can redistribute it and/or modify it;
* 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.
*
*/
#include <vxWorks.h>
#include <stdio.h>
#include <taskLib.h>
#include <msgQLib.h>
#include <intLib.h>
#include <cacheLib.h>
#include <drv/timer/timerDev.h>
#include <stdlib.h>
#include <time.h>
#include <string.h>
#include <sys/times.h>
#include <vxBusLib.h>
#include <hwif/vxbus/vxBus.h>
#include <hwif/util/hwMemLib.h>
#include <drv/pci/pciConfigLib.h>
#include <vxBusLib.h>
#include <hwif/vxbus/vxBus.h>
#include <hwif/vxbus/hwConf.h>
#include <hwif/vxbus/vxbPciLib.h>
#include <hwif/util/vxbParamSys.h>
#include <../src/hwif/h/vxbus/vxbAccess.h>
#include "vxbFtcan.h"
#undef FTCAN_DEBUG
#ifdef FTCAN_DEBUG
#define FTCAN_LOGMSG(fmt,p1,p2,p3,p4,p5,p6) logMsg(fmt,p1,p2,p3,p4,p5,p6)
#else
#define FTCAN_LOGMSG(fmt,p1,p2,p3,p4,p5,p6)
#endif
/* VxBus methods */
LOCAL void ftCanInstInit (VXB_DEVICE_ID);
LOCAL void ftCanInstInit2 (VXB_DEVICE_ID);
LOCAL void ftCanInstConnect (VXB_DEVICE_ID);
LOCAL void ftCanInt (FTCAN_DRV_CTRL *pDrvCtrl);
LOCAL void ftCanRecvTask(FTCAN_DRV_CTRL * pDrvCtrl);
LOCAL int ftcan_set_bittiming(VXB_DEVICE_ID pDev,struct can_bittiming *bt);
LOCAL int can_calc_bittiming( struct can_bittiming *bt,const struct can_bittiming_const *btc);
/* END functions */
LOCAL struct drvBusFuncs ftCanFuncs =
{
ftCanInstInit, /* devInstanceInit */
ftCanInstInit2, /* devInstanceInit2 */
ftCanInstConnect /* devConnect */
};
LOCAL device_method_t ftCan_methods[] =
{
{ 0, 0}
};
LOCAL struct vxbDevRegInfo ftCanDevPlbRegistration =
{
NULL, /* pNext */
VXB_DEVID_DEVICE, /* devID */
VXB_BUSID_PLB, /* busID = PLB */
VXB_VER_4_0_0, /* vxbVersion */
"ftCan", /* drvName */
&ftCanFuncs, /* pDrvBusFuncs */
NULL, /* pMethods */
NULL, /* devProbe */
NULL /* pParamDefaults */
};
LOCAL VXB_DEVICE_ID glftCanDev[CAN_MAX_CTL];
#ifdef __DCC__
LOCAL const struct can_bittiming_const ftcan_bittiming_const = {
"vxbftCan",
1,
16,
1,
8,
4,
1,
512,
2
};
#else
LOCAL const struct can_bittiming_const ftcan_bittiming_const = {
.name = "vxbftCan",
.tseg1_min = 1,
.tseg1_max = 16,
.tseg2_min = 1,
.tseg2_max = 8,
.sjw_max = 4,
.brp_min = 1,
.brp_max = 512,
.brp_inc = 2,
};
#endif
/*****************************************************************************
*
*vxbFtCanRegister - register with the VxBus subsystem
*
* This routine registers the Template driver with VxBus as a
* child of the PCI bus type.
*
* RETURNS: N/A
*
* ERRNO: N/A
*/
void vxbFtCanRegister(void)
{
vxbDevRegister((struct vxbDevRegInfo *)&ftCanDevPlbRegistration);
return;
}
/*****************************************************************************
*
* ftCanInstInit - VxBus instInit handler
*
* This function implements the VxBus instInit handler for an template
* device instance. The only thing done here is to select a unit
* number for the device.
*
* RETURNS: N/A
*
* ERRNO: N/A
*/
LOCAL void ftCanInstInit
(
VXB_DEVICE_ID pDev
)
{
FTCAN_DRV_CTRL * pDrvCtrl;
HCF_DEVICE * pHcf;
/* get the HCF device from the instance ID */
pHcf = hcfDeviceGet (pDev);
/* if pHcf is NULL, no device is present in hwconf.c */
if (pHcf == NULL)
return;
/* allocate memory for the data */
pDrvCtrl = (FTCAN_DRV_CTRL *)hwMemAlloc (sizeof(FTCAN_DRV_CTRL));
if (pDrvCtrl == NULL)
return;
bzero((char *)pDrvCtrl, sizeof(FTCAN_DRV_CTRL));
pDrvCtrl->ftCanDev = pDev;
if (devResourceGet (pHcf, "regBase", HCF_RES_INT,
(void *)&pDrvCtrl->ftCanRegbase) != OK)
{
#ifndef _VXBUS_BASIC_HWMEMLIB
hwMemFree((char *)pDrvCtrl);
#endif
return;
}
if (devResourceGet (pHcf, "irq", HCF_RES_INT,
(void *)&pDrvCtrl->irq) != OK)
{
#ifndef _VXBUS_BASIC_HWMEMLIB
hwMemFree((char *)pDrvCtrl);
#endif
return;
}
if (devResourceGet (pHcf, "bitrate", HCF_RES_INT,
(void *)&pDrvCtrl->ftcan_bittiming.bitrate) != OK)
{
#ifndef _VXBUS_BASIC_HWMEMLIB
hwMemFree((char *)pDrvCtrl);
#endif
return;
}
pDev->pDrvCtrl = pDrvCtrl;
vxbRegMap(pDev, 0, &pDrvCtrl->ftCanHandle);
glftCanDev[pDev->unitNumber]= pDev;
return;
}
/*****************************************************************************
*
* ftCanInstInit2 - VxBus instInit2 handler
*
* This function implements the VxBus instInit2 handler for an template
* device instance. Once we reach this stage of initialization, it's
* safe for us to allocate memory, so we can create our pDrvCtrl
* structure and do some initial hardware setup. The important
* steps we do here are to create a child miiBus instance, connect
* our ISR to our assigned interrupt vector, read the station
* address from the EEPROM, and set up our vxbDma tags and memory
* regions. We need to allocate a 64K region for the RX DMA window
* here.
*
* RETURNS: N/A
*
* ERRNO: N/A
*/
LOCAL void ftCanInstInit2
(
VXB_DEVICE_ID pDev
)
{
FTCAN_DRV_CTRL *pDrvCtrl;
UINT is_config_mode=0;
struct can_bittiming bt;
int ret;
pDrvCtrl = (FTCAN_DRV_CTRL *)pDev->pDrvCtrl;;
if ((pDrvCtrl->ftcanQueue = msgQCreate(FTCAN_QUEUE_SIZE, VXB_FTCAN_MSG_SIZE,
MSG_Q_FIFO)) == NULL)
{
FTCAN_LOGMSG("<ftCanInstInit2> Unable to create FT CAN message queue\n",
0,0,0,0,0,0);
return;
}
if ((pDrvCtrl->canTxSem = semBCreate (SEM_Q_PRIORITY, SEM_EMPTY)) == NULL)
{
FTCAN_LOGMSG("<ftCanInstInit2> Unable to create FT CAN Tx Sem\n",
0,0,0,0,0,0);
return;
}
is_config_mode = CSR_READ_4(pDev,FTCAN_CTRL_OFFSET);
if (is_config_mode&FTCAN_CTRL_XFER_MASK) {
FTCAN_LOGMSG("FT can is not in configration mode\n",1,2,3,4,5,6);
return;
}
CSR_WRITE_4(pDev, FTCAN_CTRL_OFFSET, (0x1<<6));/*bit 6 is internal reset**/
CSR_SETBIT_4(pDev, FTCAN_CTRL_OFFSET, FTCAN_CTRL_AIME_MASK);
CSR_WRITE_4(pDev, FTCAN_ACC_ID0_MASK_OFFSET, FTCAN_ACC_IDN_MASK);
CSR_WRITE_4(pDev, FTCAN_ACC_ID1_MASK_OFFSET, FTCAN_ACC_IDN_MASK);
CSR_WRITE_4(pDev, FTCAN_ACC_ID2_MASK_OFFSET, FTCAN_ACC_IDN_MASK);
CSR_WRITE_4(pDev, FTCAN_ACC_ID3_MASK_OFFSET, FTCAN_ACC_IDN_MASK);
CSR_CLRBIT_4(pDev, FTCAN_CTRL_OFFSET, (0x1<<6));
bt.bitrate = pDrvCtrl->ftcan_bittiming.bitrate;
bt.sjw = 0;
bt.sample_point = 0;
ret = can_calc_bittiming(&bt,&ftcan_bittiming_const);
if(ret<0){
FTCAN_LOGMSG("can_calc_bittiming error\n",1,2,3,4,5,6);
return ;
}
if (ftcan_set_bittiming(pDev,&bt)) {
FTCAN_LOGMSG("ftcan_set_bittiming error\n",1,2,3,4,5,6);
return ;
}
pDrvCtrl->ftcan_bittiming.brp=bt.brp;
pDrvCtrl->ftcan_bittiming.sjw=bt.sjw;
pDrvCtrl->ftcan_bittiming.tq =bt.tq;
pDrvCtrl->ftcan_bittiming.phase_seg1= bt.phase_seg1;
pDrvCtrl->ftcan_bittiming.phase_seg2= bt.phase_seg2;
pDrvCtrl->ftcan_bittiming.sample_point=bt.sample_point;
pDrvCtrl->txCfMax = FTCAN_TX_FIFO_MAX/FTCAN_FIFOFRAME_SIZE;
pDrvCtrl->txCfCnt = 0;
}
LOCAL unsigned int div64_32(unsigned long long *n, unsigned int base)
{
unsigned long long rem = *n;
unsigned long long b = base;
unsigned long long res, d = 1;
unsigned int high = rem >> 32;
/* Reduce the thing a bit first */
res = 0;
if (high >= base) {
high /= base;
res = (unsigned long long) high << 32;
rem -= (unsigned long long) (high*base) << 32;
}
while ((unsigned long long)b > 0 && b < rem) {
b = b+b;
d = d+d;
}
do {
if (rem >= b) {
rem -= b;
res += d;
}
b >>= 1;
d >>= 1;
} while (d);
*n = res;
return rem;
}
/*****************************************************************************
*
* can_update_sample_point - Bit-timing calculation
*
* Calculates proper bit-timing parameters for a specified bit-rate
* and sample-point, which can then be used to set the bit-timing
* registers of the CAN controller.
*
* RETURNS: sample point
*
* ERRNO: N/A
*/
LOCAL int can_update_sample_point( const struct can_bittiming_const *btc,
unsigned int sample_point_nominal, unsigned int tseg,
unsigned int *tseg1_ptr, unsigned int *tseg2_ptr,
unsigned int *sample_point_error_ptr)
{
unsigned int sample_point_error, best_sample_point_error = UINT_MAX;
unsigned int sample_point, best_sample_point = 0;
unsigned int tseg1, tseg2;
int 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;
}
/*****************************************************************************
*
* can_calc_bittiming - Bit-timing calculation
*
* Calculates proper bit-timing parameters for a specified bit-rate
* and sample-point, which can then be used to set the bit-timing
* registers of the CAN controller.
*
* RETURNS: 0 on success and failure value on error
*
* ERRNO: N/A
*/
LOCAL int can_calc_bittiming( struct can_bittiming *bt,
const struct can_bittiming_const *btc)
{
unsigned int bitrate; /* current bitrate */
unsigned int bitrate_error; /* difference between current and nominal value */
unsigned int best_bitrate_error = UINT_MAX;
unsigned int sample_point_error; /* difference between current and nominal value */
unsigned int best_sample_point_error = UINT_MAX;
unsigned int sample_point_nominal; /* nominal sample point */
unsigned int best_tseg = 0; /* current best value for tseg */
unsigned int best_brp = 0; /* current best value for brp */
unsigned int brp, tsegall, tseg, tseg1 = 0, tseg2 = 0;
unsigned long long v64;
/* Use CiA recommended sample points */
if (bt->sample_point) {
sample_point_nominal = bt->sample_point;
} else {
if (bt->bitrate > 800000)
sample_point_nominal = 750;
else if (bt->bitrate > 500000)
sample_point_nominal = 800;
else
sample_point_nominal = 875;
}
/* tseg even = round down, odd = round up */
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 = CAN_CLK_FREQ / (tsegall * bt->bitrate) + 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;
bitrate = CAN_CLK_FREQ / (brp * tsegall);
bitrate_error = abs(bt->bitrate - bitrate);
/* tseg brp biterror */
if (bitrate_error > best_bitrate_error)
continue;
/* reset sample point error if we have a better bitrate */
if (bitrate_error < best_bitrate_error)
best_sample_point_error = UINT_MAX;
can_update_sample_point(btc, sample_point_nominal, tseg / 2, &tseg1, &tseg2, &sample_point_error);
if (sample_point_error > best_sample_point_error)
continue;
best_sample_point_error = sample_point_error;
best_bitrate_error = bitrate_error;
best_tseg = tseg / 2;
best_brp = brp;
if (bitrate_error == 0 && sample_point_error == 0)
break;
}
if (best_bitrate_error) {
/* Error in one-tenth of a percent */
v64 = (unsigned long long)best_bitrate_error * 1000;
div64_32(&v64, bt->bitrate);
bitrate_error = (unsigned int)v64;
if (bitrate_error > CAN_CALC_MAX_ERROR) {
FTCAN_LOGMSG("bitrate error %d.%d%% too high\n",
bitrate_error / 10, bitrate_error % 10,3,4,5,6);
return -EDOM;
}
FTCAN_LOGMSG( "bitrate error %d.%d%%\n",
bitrate_error / 10, bitrate_error % 10,3,4,5,6);
}
/* real sample point */
bt->sample_point = can_update_sample_point(btc, sample_point_nominal, best_tseg,
&tseg1, &tseg2, NULL);
v64 = (unsigned long long)best_brp * 1000 * 1000 * 1000;
div64_32(&v64, CAN_CLK_FREQ);
bt->tq = (unsigned long long)v64;
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 bitrate */
bt->bitrate = CAN_CLK_FREQ / (bt->brp * (CAN_CALC_SYNC_SEG + tseg1 + tseg2));
return 0;
}
/*****************************************************************************
*
* ftcan_set_bittiming - CAN set bit timing routine
*
* This is the driver set bittiming routine.
*
* RETURNS: 0 on success and failure value on error
*
* ERRNO: N/A
*/
LOCAL int ftcan_set_bittiming(VXB_DEVICE_ID pDev,struct can_bittiming *bt)
{
UINT btr;
UINT is_config_mode;
/* Setting Baud Rate prescalar value in BRPR Register */
btr = (bt->brp - 1) << 16;
/* Setting Time Segment 1 in BTR Register */
btr |= (bt->prop_seg - 1) << 2;
btr |= (bt->phase_seg1 - 1) << 5;
/* Setting Time Segment 2 in BTR Register */
btr |= (bt->phase_seg2 - 1) << 8;
/* Setting Synchronous jump width in BTR Register */
btr |= (bt->sjw - 1);
FTCAN_LOGMSG("%s:Update Can btr:0x%x \n", __func__, btr,3,4,5,6);
/* Check whether FT2000/4 CAN is in configuration mode.
* It cannot set bit timing if FT2000/4 CAN is not in configuration mode.
*/
is_config_mode = (CSR_READ_4(pDev, FTCAN_CTRL_OFFSET) &
FTCAN_CTRL_XFER_MASK);
if (is_config_mode) {
/*Disable Transfer*/
CSR_CLRBIT_4(pDev, FTCAN_CTRL_OFFSET, FTCAN_CTRL_XFER_MASK);
}
CSR_WRITE_4(pDev, FTCAN_DAT_RATE_CTRL_OFFSET, btr);
CSR_WRITE_4(pDev, FTCAN_ARB_RATE_CTRL_OFFSET, btr);
/*Enable Transfer*/
CSR_SETBIT_4(pDev, FTCAN_CTRL_OFFSET, FTCAN_CTRL_XFER_MASK);
FTCAN_LOGMSG( "DAT=0x%08x, ARB=0x%08x\n",
CSR_READ_4(pDev, FTCAN_DAT_RATE_CTRL_OFFSET),
CSR_READ_4(pDev, FTCAN_ARB_RATE_CTRL_OFFSET),3,4,5,6);
return 0;
}
/*****************************************************************************
*
* ftcan_err_interrupt - error frame Isr
*
* This is the CAN error interrupt and it will
* check the the type of error
*
* RETURNS: N/A
*
* ERRNO: N/A
*/
LOCAL void ftcan_err_interrupt(VXB_DEVICE_ID pDev, UINT isr)
{
UINT txerr = 0, rxerr = 0;
rxerr = CSR_READ_4(pDev, FTCAN_ERR_CNT_OFFSET) & FTCAN_ERR_CNT_RFN_MASK;
txerr = ((CSR_READ_4(pDev, FTCAN_ERR_CNT_OFFSET) &
FTCAN_ERR_CNT_TFN_MASK) >> FTCAN_ERR_CNT_TFN_SHIFT);
FTCAN_LOGMSG("%s: rx error count %d, tx error count %d\n",
__func__, rxerr,txerr,4,5,6);
FTCAN_LOGMSG("%s: error status register:0x%x\n",
__func__, (CSR_READ_4(pDev,FTCAN_INTR_OFFSET) & FTCAN_INTR_STATUS_MASK),3,4,5,6);
}
/*****************************************************************************
*
* ftcan_tx_interrupt - Tx Done Isr
*
* This is the CAN Tx Done interrupt
*
* RETURNS: N/A
*
* ERRNO: N/A
*/
LOCAL void ftcan_tx_interrupt(VXB_DEVICE_ID pDev, UINT isr)
{
FTCAN_DRV_CTRL * pFtCanCtrl;
pFtCanCtrl = pDev->pDrvCtrl;
CSR_SETBIT_4(pDev, FTCAN_INTR_OFFSET, FTCAN_INTR_TEIC_MASK | FTCAN_INTR_REIC_MASK);
pFtCanCtrl->txCfCnt--;
if(pFtCanCtrl->txCfCnt != 0){
CSR_CLRBIT_4(pDev, FTCAN_CTRL_OFFSET, FTCAN_CTRL_XFER_MASK);
CSR_SETBIT_4(pDev, FTCAN_CTRL_OFFSET, FTCAN_CTRL_TXREQ_MASK);
CSR_SETBIT_4(pDev, FTCAN_CTRL_OFFSET, FTCAN_CTRL_XFER_MASK);
}
else{
if (semGive (pFtCanCtrl->canTxSem) == ERROR)
{
FTCAN_LOGMSG ("semGive error\n", 0, 0, 0, 0, 0, 0);
}
}
}
/*****************************************************************************
*
* ftcan_rx_interrupt - Rx Done Isr
*
* This is the CAN Rx Done interrupt
*
* RETURNS: N/A
*
* ERRNO: N/A
*/
LOCAL void ftcan_rx_interrupt(FTCAN_DRV_CTRL* pDrvCtrl, UINT isr)
{
UINT id_ftcan,data[2] = {0, 0};
UINT dlc = 0;
FTCAN_FRAME ftCanFrame;
VXB_DEVICE_ID pDev;
pDev= pDrvCtrl->ftCanDev;
while((CSR_READ_4(pDev, FTCAN_FIFO_CNT_OFFSET)&0x3f) > 0)
{
/* Read a frame from FT2000/4 CAN */
id_ftcan = CSR_READ_4(pDev, FTCAN_RX_FIFO_OFFSET);
id_ftcan = be32_to_cpup(id_ftcan);
/* Change FT2000/4 CAN ID format to socketCAN ID format */
if (id_ftcan & FTCAN_IDR_IDE_MASK) {
/* The received frame is an Extended format frame */
dlc = CSR_READ_4(pDev, FTCAN_RX_FIFO_OFFSET);
dlc = be32_to_cpup(dlc);
dlc = ((dlc & FTCAN_IDR_EDLC_MASK ) >> FTCAN_IDR_EDLC_SHIFT);
ftCanFrame.can_id = (id_ftcan & FTCAN_IDR_ID1_MASK) >> 3;
ftCanFrame.can_id |= (id_ftcan & FTCAN_IDR_ID2_MASK) >> FTCAN_IDR_ID2_SHIFT;
ftCanFrame.can_id |= CAN_EFF_FLAG;
if (id_ftcan & FTCAN_IDR_RTR_MASK)
ftCanFrame.can_id |= CAN_RTR_FLAG;
} else {
dlc = ((id_ftcan & FTCAN_IDR_DLC_MASK ) >> FTCAN_IDR_SDLC_SHIFT);
/* The received frame is a standard format frame */
ftCanFrame.can_id = (id_ftcan & FTCAN_IDR_ID1_MASK) >> FTCAN_IDR_ID1_SHIFT;
if (id_ftcan & FTCAN_IDR_SRR_MASK)
ftCanFrame.can_id |= CAN_RTR_FLAG;
}
/* Change FT2000/4 CAN data length format to socketCAN data format */
ftCanFrame.can_dlc = get_can_dlc(dlc);
if (!(ftCanFrame.can_id & CAN_RTR_FLAG)) {
/* Change FT2000/4 CAN data format to socketCAN data format */
if (ftCanFrame.can_dlc > 0){
data[0] = CSR_READ_4(pDev, FTCAN_RX_FIFO_OFFSET);
*(UINT *)(ftCanFrame.data) = (data[0]);
}
if (ftCanFrame.can_dlc > 4){
data[1] = CSR_READ_4(pDev, FTCAN_RX_FIFO_OFFSET);
*(UINT *)(ftCanFrame.data + 4) = (data[1]);
}
}
FTCAN_LOGMSG("recevied canid %x can_dlc %d data 0x%2x 0x%2x 0x%2x 0x%2x ",
ftCanFrame.can_id & CAN_EFF_MASK,ftCanFrame.can_dlc,
ftCanFrame.data[0],ftCanFrame.data[1],ftCanFrame.data[2],
ftCanFrame.data[3]);
FTCAN_LOGMSG("0x%2x 0x%2x 0x%2x 0x%2x\r\n",ftCanFrame.data[4],
ftCanFrame.data[5],ftCanFrame.data[6],ftCanFrame.data[7],5,6);
msgQSend(pDrvCtrl->ftcanQueue,(char * )&ftCanFrame,sizeof(ftCanFrame),NO_WAIT,MSG_PRI_NORMAL);
}
}
/*****************************************************************************
*
* ftCanInt - VxBus interrupt handler
*
* RETURNS: N/A
*
* ERRNO: N/A
*/
LOCAL void ftCanInt (FTCAN_DRV_CTRL* pDrvCtrl)
{
VXB_DEVICE_ID pDev;
UINT isr;
pDev = pDrvCtrl->ftCanDev;
/* Get the interrupt status from FT2000/4 CAN */
isr = (CSR_READ_4(pDev, FTCAN_INTR_OFFSET) & FTCAN_INTR_STATUS_MASK);
if (!isr)
return;
/*Check for Tx interrupt and Processing it */
if ((isr & FTCAN_INTR_TEIS_MASK)){
isr &= (~FTCAN_INTR_REIS_MASK);
ftcan_tx_interrupt(pDev, isr);
}
/* Check for the type of error interrupt and Processing it */
if (isr & (FTCAN_INTR_EIS_MASK | FTCAN_INTR_RFIS_MASK |
FTCAN_INTR_BOIS_MASK | FTCAN_INTR_PEIS_MASK | FTCAN_INTR_PWIS_MASK)) {
CSR_SETBIT_4(pDev, FTCAN_INTR_OFFSET, (FTCAN_INTR_EIC_MASK |
FTCAN_INTR_RFIC_MASK | FTCAN_INTR_BOIC_MASK |
FTCAN_INTR_PEIC_MASK | FTCAN_INTR_PWIC_MASK));
ftcan_err_interrupt(pDev, isr);
}
/* Check for the type of receive interrupt and Processing it */
if (isr & (FTCAN_INTR_REIS_MASK)) {
CSR_CLRBIT_4(pDev, FTCAN_INTR_OFFSET,FTCAN_INTR_REIE_MASK);
ftcan_rx_interrupt(pDrvCtrl,isr);
CSR_SETBIT_4(pDev, FTCAN_INTR_OFFSET, FTCAN_INTR_REIC_MASK);
CSR_SETBIT_4(pDev, FTCAN_INTR_OFFSET, FTCAN_INTR_REIE_MASK);
}
return;
}
/*****************************************************************************
*
* ftCanInstConnect - VxBus instConnect handler
*
* This function implements the VxBus instConnect handler for an template
* device instance.
*
* RETURNS: N/A
*
* ERRNO: N/A
*/
LOCAL void ftCanInstConnect
(
VXB_DEVICE_ID pDev
)
{
int st;
FTCAN_DRV_CTRL * pFtCanCtrl;
char task_name[32];
TASK_ID tid;
pFtCanCtrl = pDev->pDrvCtrl;
bzero(task_name, sizeof(task_name));
sprintf(task_name, "tCanRx%d", pDev->unitNumber);
tid = taskSpawn(task_name, 100, 0, 0x20000,
(FUNCPTR) ftCanRecvTask,(_Vx_usr_arg_t) pFtCanCtrl,
2,3,4,5,6,7,8,9,10);
if(TASK_ID_ERROR == tid)
{
FTCAN_LOGMSG("failed to spawn can recv task!\n",1,2,3,4,5,6);
return;
}
/*
* Attach our ISR. For PCI, the index value is always
* 0, since the PCI bus controller dynamically sets
* up interrupts for us.
*/
st = vxbIntConnect (pDev, 0, ftCanInt, pFtCanCtrl);
st |= vxbIntEnable (pDev, 0, ftCanInt, pFtCanCtrl);
if(st != OK)
{
FTCAN_LOGMSG("interrupt cn/en failed!\n",1,2,3,4,5,6);
return;
}
/* Enable interrupts */
CSR_WRITE_4(pDev, FTCAN_INTR_OFFSET, FTCAN_INTR_EN);
/*Enable Transfer*/
CSR_WRITE_4(pDev, FTCAN_CTRL_OFFSET, FTCAN_CTRL_XFER_MASK);
FTCAN_LOGMSG("status:#x%08x\n",CSR_READ_4(pDev,FTCAN_XFER_STS_OFFSET),2,3,4,5,6);
return;
}
/*****************************************************************************
*
* ftcan_send_txfifo - Write the Frame to CAN TX FIFO
*
* Write the Frame to CAN TX FIFO
*
* RETURNS: Sent frame number
*
* ERRNO: N/A
*/
LOCAL unsigned int ftcan_send_txfifo(VXB_DEVICE_ID pDev, struct can_frame *df)
{
struct can_frame *cf = df;
UINT id, dlc, frame_head[2] = {0, 0}, data[8] = {0, 0};
/* Watch carefully on the bit sequence */
if (cf->can_id & CAN_EFF_FLAG) {
/* Extended CAN ID format */
id = ((cf->can_id & CAN_EFF_MASK) << FTCAN_IDR_ID2_SHIFT) &
FTCAN_IDR_ID2_MASK;
id |= (((cf->can_id & CAN_EFF_MASK) >>
(CAN_EFF_ID_BITS-CAN_SFF_ID_BITS)) <<
FTCAN_IDR_ID1_SHIFT) & FTCAN_IDR_ID1_MASK;
id |= FTCAN_IDR_IDE_MASK | FTCAN_IDR_SRR_MASK;
if (cf->can_id & CAN_RTR_FLAG)
/* Extended frames remote TX request */
id |= FTCAN_IDR_RTR_MASK;
dlc = cf->can_dlc << FTCAN_IDR_EDLC_SHIFT;
frame_head[0] = cpu_to_be32p(id);
frame_head[1] = cpu_to_be32p(dlc);
/* Write the Frame to FT2000/4 CAN TX FIFO */
CSR_WRITE_4(pDev, FTCAN_TX_FIFO_OFFSET, frame_head[0]);
CSR_WRITE_4(pDev, FTCAN_TX_FIFO_OFFSET, frame_head[1]);
} else {
/* Standard CAN ID format */
id = ((cf->can_id & CAN_SFF_MASK) << FTCAN_IDR_ID1_SHIFT) &
FTCAN_IDR_ID1_MASK;
if (cf->can_id & CAN_RTR_FLAG)
/* Standard frames remote TX request */
id |= FTCAN_IDR_SRR_MASK;
dlc = ((cf->can_dlc << FTCAN_IDR_SDLC_SHIFT) | FTCAN_IDR_PAD_MASK);
id |= dlc;
frame_head[0] = cpu_to_be32p(id);
/* Write the Frame to CAN TX FIFO */
CSR_WRITE_4(pDev,FTCAN_TX_FIFO_OFFSET, frame_head[0]);
}
if (!(cf->can_id & CAN_RTR_FLAG)) {
if (cf->can_dlc > 0){
data[0] = (*(UINT*)(cf->data + 0));
CSR_WRITE_4(pDev, FTCAN_TX_FIFO_OFFSET, data[0]);
}
if (cf->can_dlc > 4){
data[1] = (*(UINT*)(cf->data + 4));
CSR_WRITE_4(pDev, FTCAN_TX_FIFO_OFFSET, data[1]);
}
}
return 1;
}
/*****************************************************************************
*
* ftcan_write - Sending the CAN data
*
* RETURNS: Sent frame number
*
* ERRNO: N/A
*/
LOCAL UINT ftcan_write(VXB_DEVICE_ID pDev, char *buf, size_t len)
{
FTCAN_DRV_CTRL * pFtCanCtrl;
struct can_frame *ftCanFrame;
UINT frameNum,frameLeft,sendNumOnce,i;
pFtCanCtrl = pDev->pDrvCtrl;
if(buf == NULL)
{
FTCAN_LOGMSG("Send Buff is NULL.\r\n", 1,2,3,4,5,6);
return 0;
}
if(len%sizeof(struct can_frame))
{
FTCAN_LOGMSG("Lenth is invalid\n",1,2,3,4,5,6);
return 0;
}
frameNum = len/sizeof(struct can_frame);
for(frameLeft = frameNum; frameLeft > 0; )
{
if(frameLeft/pFtCanCtrl->txCfMax >= 1)
{
sendNumOnce = pFtCanCtrl->txCfMax;
frameLeft -= pFtCanCtrl->txCfMax;
}
else
{
sendNumOnce = frameLeft;
frameLeft = 0;
}
/*shut down tranmission*/
CSR_CLRBIT_4(pDev, FTCAN_CTRL_OFFSET, FTCAN_CTRL_XFER_MASK);
CSR_CLRBIT_4(pDev, FTCAN_CTRL_OFFSET, FTCAN_CTRL_TXREQ_MASK);
CSR_SETBIT_4(pDev, FTCAN_CTRL_OFFSET, FTCAN_CTRL_XFER_MASK);
for(i=0;i<sendNumOnce;i++){
ftCanFrame = (struct can_frame *)buf;
ftcan_send_txfifo(pDev, ftCanFrame);
buf = buf + sizeof(struct can_frame);
}
pFtCanCtrl->txCfCnt = sendNumOnce;
/* triggers tranmission */
CSR_CLRBIT_4(pDev, FTCAN_CTRL_OFFSET, FTCAN_CTRL_XFER_MASK);
CSR_SETBIT_4(pDev, FTCAN_CTRL_OFFSET, FTCAN_CTRL_TXREQ_MASK);
CSR_SETBIT_4(pDev, FTCAN_CTRL_OFFSET, FTCAN_CTRL_TXREQ_MASK|FTCAN_CTRL_XFER_MASK);
if (semTake (pFtCanCtrl->canTxSem, WAIT_FOREVER) == ERROR)
{
FTCAN_LOGMSG ("SemTake Error\n",1 ,2, 3, 4, 5, 6);
}
}
return frameNum;
}
/*****************************************************************************
*
* ftCanSend - send can msg. API function.
*
* This function is used to send data
*
* RETURNS: OK/ERROR
*
* ERRNO: N/A
*/
STATUS ftCanSend
(
unsigned char ctlNo,
char * sendbuff,
int lenth
)
{
VXB_DEVICE_ID pDev;
UINT sentLenth;
if(ctlNo >= CAN_MAX_CTL)
{
printf("ctlNo %d is invalid.\r\n", ctlNo);
return ERROR;
}
pDev= glftCanDev[ctlNo];
if(pDev == NULL)
{
printf("pDev is NULL!\r\n");
return ERROR;
}
sentLenth = ftcan_write(pDev,sendbuff,lenth);
if (sentLenth > 0)
{
return OK;
}
else
{
return ERROR;
}
}
/*****************************************************************************
*
* ftCanRecvCallback - receive callback
*
* Registers the receive processing function for the specified CAN controller
* ftCanRecvRtn:The pointer function of the receiving process
*
* RETURNS: OK/ERROR
*
* ERRNO: N/A
*/
STATUS ftCanRecvCallback
(
unsigned char ctlNo,
void (*ftCanRecvRtn)(FTCAN_FRAME* pFtCanFrame)
)
{
VXB_DEVICE_ID pDev;
FTCAN_DRV_CTRL * pDrvCtrl;
if(ctlNo >= CAN_MAX_CTL)
{
FTCAN_LOGMSG("ctlNo %d is invalid.\r\n", ctlNo,2,3,4,5,6);
return ERROR;
}
pDev= glftCanDev[ctlNo];
if(pDev == NULL)
{
FTCAN_LOGMSG("pDev is NULL!\r\n",1,2,3,4,5,6);
return ERROR;
}
pDrvCtrl = (FTCAN_DRV_CTRL * )pDev->pDrvCtrl;
if(pDrvCtrl == NULL)
{
FTCAN_LOGMSG("pDrvCtrl is NULL for %d!\r\n", pDev->unitNumber,2,3,4,5,6);
return ERROR;
}
pDrvCtrl->ftCanRecvRtn = ftCanRecvRtn;
return OK;
}
/*****************************************************************************
*
* ftCanSetBitrate - set bitrate
*
* This function Set Bitrate for can
*
* RETURNS: OK/ERROR
*
* ERRNO: N/A
*/
STATUS ftCanSetBitrate
(
unsigned char ctlNo,
UINT bitrate
)
{
VXB_DEVICE_ID pDev;
struct can_bittiming bt;
int ret;
FTCAN_DRV_CTRL * pDrvCtrl;
pDev= glftCanDev[ctlNo];
pDrvCtrl= pDev->pDrvCtrl;
bt.bitrate = bitrate;
bt.sjw = 0;
bt.sample_point = 0;
ret = can_calc_bittiming(&bt,&ftcan_bittiming_const);
if(ret<0){
return -ret;
}
if (ftcan_set_bittiming(pDev,&bt)) {
return -EFAULT;
}
pDrvCtrl->ftcan_bittiming.bitrate = bitrate;
pDrvCtrl->ftcan_bittiming.brp=bt.brp;
pDrvCtrl->ftcan_bittiming.sjw=bt.sjw;
pDrvCtrl->ftcan_bittiming.tq =bt.tq;
pDrvCtrl->ftcan_bittiming.phase_seg1= bt.phase_seg1;
pDrvCtrl->ftcan_bittiming.phase_seg2= bt.phase_seg2;
pDrvCtrl->ftcan_bittiming.sample_point=bt.sample_point;
return OK;
}
/*****************************************************************************
*
* ftCanSetIDFilter - set id filter
*
* This function Set set id filter for can
*
* RETURNS: OK/ERROR
*
* ERRNO: N/A
*/
STATUS ftCanSetIDFilter
(
unsigned char ctlNo,
UINT filterNo,
UINT id,
UINT mask,
UINT type
)
{
VXB_DEVICE_ID pDev;
UINT32 idRegOffset[4] = {FTCAN_ACC_ID0_OFFSET, FTCAN_ACC_ID1_OFFSET, FTCAN_ACC_ID2_OFFSET, FTCAN_ACC_ID3_OFFSET};
UINT32 maskRegOffset[4] = {FTCAN_ACC_ID0_MASK_OFFSET, FTCAN_ACC_ID1_MASK_OFFSET, FTCAN_ACC_ID2_MASK_OFFSET, FTCAN_ACC_ID3_MASK_OFFSET} ;
pDev= glftCanDev[ctlNo];
CSR_WRITE_4(pDev, FTCAN_CTRL_OFFSET, (0x1<<6));/*bit 6 is internal reset**/
if(type == STANDARD_FRAME)
{
id = id << FTCAN_ACC_IDN_SHIFT;
mask = mask << FTCAN_ACC_IDN_SHIFT;
}
CSR_WRITE_4(pDev, idRegOffset[filterNo], id);
CSR_WRITE_4(pDev, maskRegOffset[filterNo], mask);
CSR_CLRBIT_4(pDev, FTCAN_CTRL_OFFSET, (0x1<<6));
CSR_SETBIT_4(pDev, FTCAN_CTRL_OFFSET, FTCAN_CTRL_XFER_MASK | FTCAN_CTRL_AIME_MASK);
return OK;
}
/*****************************************************************************
*
* ftCanRecvTask - Receiving work
*
* This function receives data
* printf : canid,can_dlc,data.
* RETURNS: N/A
*
* ERRNO: N/A
*/
LOCAL void ftCanRecvTask
(
FTCAN_DRV_CTRL * pDrvCtrl
)
{
FTCAN_FRAME ftCanFrame;
UINT maxNBytes = sizeof(ftCanFrame);
ULONG cnt = 1;
for(;;)
{
bzero((char *)&ftCanFrame, sizeof(ftCanFrame));
msgQReceive(pDrvCtrl->ftcanQueue,(char *)&ftCanFrame,maxNBytes,WAIT_FOREVER);
if(pDrvCtrl->ftCanRecvRtn != NULL)
{
pDrvCtrl->ftCanRecvRtn(&ftCanFrame);
}
else
{
printf("%s count:%d \r\n"
" recevied canid:%x can_dlc:%d. data:%02x %02x %02x %02x %02x %02x %02x %02x\r\n",
__FUNCTION__,cnt, ftCanFrame.can_id & CAN_EFF_MASK,ftCanFrame.can_dlc,
ftCanFrame.data[0],ftCanFrame.data[1],
ftCanFrame.data[2],ftCanFrame.data[3],
ftCanFrame.data[4],ftCanFrame.data[5],
ftCanFrame.data[6],ftCanFrame.data[7]);
cnt++;
}
}
return ;
}
#define FTCAN_DEBUG
#ifdef FTCAN_DEBUG
/*****************************************************************************
*
* testCanRecvCallback - Test Receive Callback
*
* This function is used to test the Receive Callback
*
* RETURNS: N/A
*
* ERRNO: N/A
*/
void testCanRecvCallback(struct can_frame* ftCanFrame)
{
ULONG cnt = 1;
if(NULL == ftCanFrame)
{
printf("%s,%d NULL can frame, no recv\r\n", __FUNCTION__, __LINE__);
return;
}
printf("%s count:%d \r\n"
" recevied canid:%x can_dlc:%d. data:%02x %02x %02x %02x %02x %02x %02x %02x\r\n",
__FUNCTION__, cnt, ftCanFrame->can_id & CAN_EFF_MASK,ftCanFrame->can_dlc,
ftCanFrame->data[0],ftCanFrame->data[1],
ftCanFrame->data[2],ftCanFrame->data[3],
ftCanFrame->data[4],ftCanFrame->data[5],
ftCanFrame->data[6],ftCanFrame->data[7]);
cnt++;
}
/*****************************************************************************
*
* testCanRecv - Test Receive Callback
*
* This function is used to test the Receive Callback
*
* RETURNS: N/A
*
* ERRNO: N/A
*/
void testCanRecv(void)
{
ftCanRecvCallback(0, testCanRecvCallback);
}
/*****************************************************************************
*
* testCanSend - TEST CAN0
*
* This function is used to test the transmitted data
*
* RETURNS: N/A
*
* ERRNO: N/A
*/
void testCanSend(int channel, int cnt)
{
struct can_frame canFrame[10];
int i = 0;
if(0 == cnt) cnt=1;
for(i=0;i<10;i++)
{
canFrame[i].can_id = (i) | CAN_EFF_FLAG;
canFrame[i].can_dlc = 8;
canFrame[i].data[0]=0;
canFrame[i].data[1]=1;
canFrame[i].data[2]=2;
canFrame[i].data[3]=3;
canFrame[i].data[4]=4;
canFrame[i].data[5]=5;
canFrame[i].data[6]=6;
canFrame[i].data[7]=7;
}
for(i=0;i<cnt;i++)
ftCanSend(channel,(char * )&canFrame,10 * sizeof(struct can_frame));
}
/*****************************************************************************
*
* dumpFtCanReg
*
* This function is used to print the value of the can register
*
* RETURNS: N/A
*
* ERRNO: N/A
*/
void dumpFtCanReg(unsigned char ctlNo)
{
VXB_DEVICE_ID pDev;
int i =0;
FTCAN_DRV_CTRL * pDrvCtrl;
pDev= glftCanDev[ctlNo];
pDrvCtrl= pDev->pDrvCtrl;
for ( i=0; i<0x40;)
{
printf("addr 0x%x value 0x%x\r\n",i,CSR_READ_4(pDev,i));
i=i+4;
}
}
#endif /* FTCAN_DEBUG */