forked from Imagelibrary/rtems
leon,grcanfd: split out GRCANFD specific support in separate file
Update #4324.
This commit is contained in:
@@ -23,6 +23,7 @@ librtemsbsp_a_SOURCES += ../../../../../../bsps/shared/grlib/btimer/tlib_ckinit.
|
|||||||
librtemsbsp_a_SOURCES += ../../../../../../bsps/shared/grlib/can/canbtrs.c
|
librtemsbsp_a_SOURCES += ../../../../../../bsps/shared/grlib/can/canbtrs.c
|
||||||
librtemsbsp_a_SOURCES += ../../../../../../bsps/shared/grlib/can/canmux.c
|
librtemsbsp_a_SOURCES += ../../../../../../bsps/shared/grlib/can/canmux.c
|
||||||
librtemsbsp_a_SOURCES += ../../../../../../bsps/shared/grlib/can/grcan.c
|
librtemsbsp_a_SOURCES += ../../../../../../bsps/shared/grlib/can/grcan.c
|
||||||
|
librtemsbsp_a_SOURCES += ../../../../../../bsps/shared/grlib/can/grcanfd.c
|
||||||
librtemsbsp_a_SOURCES += ../../../../../../bsps/shared/grlib/can/occan.c
|
librtemsbsp_a_SOURCES += ../../../../../../bsps/shared/grlib/can/occan.c
|
||||||
librtemsbsp_a_SOURCES += ../../../../../../bsps/shared/grlib/can/satcan.c
|
librtemsbsp_a_SOURCES += ../../../../../../bsps/shared/grlib/can/satcan.c
|
||||||
librtemsbsp_a_SOURCES += ../../../../../../bsps/shared/grlib/drvmgr/ambapp_bus.c
|
librtemsbsp_a_SOURCES += ../../../../../../bsps/shared/grlib/drvmgr/ambapp_bus.c
|
||||||
|
|||||||
@@ -24,13 +24,11 @@
|
|||||||
#include <grlib/ambapp.h>
|
#include <grlib/ambapp.h>
|
||||||
|
|
||||||
#include <grlib/grlib_impl.h>
|
#include <grlib/grlib_impl.h>
|
||||||
|
#include "grcan_internal.h"
|
||||||
|
|
||||||
/* Maximum number of GRCAN devices supported by driver */
|
/* Maximum number of GRCAN devices supported by driver */
|
||||||
#define GRCAN_COUNT_MAX 8
|
#define GRCAN_COUNT_MAX 8
|
||||||
|
|
||||||
#define WRAP_AROUND_TX_MSGS 1
|
|
||||||
#define WRAP_AROUND_RX_MSGS 2
|
|
||||||
#define GRCAN_MSG_SIZE sizeof(struct grcan_msg)
|
|
||||||
#define BLOCK_SIZE (16*4)
|
#define BLOCK_SIZE (16*4)
|
||||||
|
|
||||||
/* grcan needs to have it buffers aligned to 1k boundaries */
|
/* grcan needs to have it buffers aligned to 1k boundaries */
|
||||||
@@ -58,15 +56,6 @@
|
|||||||
#define IRQ_MASK(irqno)
|
#define IRQ_MASK(irqno)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifndef GRCAN_DEFAULT_BAUD
|
|
||||||
/* default to 500kbits/s */
|
|
||||||
#define GRCAN_DEFAULT_BAUD 500000
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef GRCAN_SAMPLING_POINT
|
|
||||||
#define GRCAN_SAMPLING_POINT 80
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* Uncomment for debug output */
|
/* Uncomment for debug output */
|
||||||
/****************** DEBUG Definitions ********************/
|
/****************** DEBUG Definitions ********************/
|
||||||
#define DBG_TX 2
|
#define DBG_TX 2
|
||||||
@@ -89,65 +78,6 @@ int state2err[4] = {
|
|||||||
/* STATE_AHBERR */ GRCAN_RET_AHBERR
|
/* STATE_AHBERR */ GRCAN_RET_AHBERR
|
||||||
};
|
};
|
||||||
|
|
||||||
struct grcan_msg {
|
|
||||||
unsigned int head[2];
|
|
||||||
unsigned char data[8];
|
|
||||||
};
|
|
||||||
|
|
||||||
struct grcanfd_bd0 {
|
|
||||||
uint32_t head[2];
|
|
||||||
uint64_t data0; /* variable size, from 1 to 8 dwords */
|
|
||||||
};
|
|
||||||
|
|
||||||
struct grcanfd_bd1 {
|
|
||||||
unsigned long long data[2];
|
|
||||||
};
|
|
||||||
|
|
||||||
struct grcan_config {
|
|
||||||
struct grcan_timing timing;
|
|
||||||
struct grcanfd_timing timing_fd;
|
|
||||||
struct grcan_selection selection;
|
|
||||||
int abort;
|
|
||||||
int silent;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct grcan_priv {
|
|
||||||
struct drvmgr_dev *dev; /* Driver manager device */
|
|
||||||
char devName[32]; /* Device Name */
|
|
||||||
unsigned int baseaddr, ram_base;
|
|
||||||
struct grcan_regs *regs;
|
|
||||||
int irq;
|
|
||||||
int minor;
|
|
||||||
int open;
|
|
||||||
int started;
|
|
||||||
unsigned int channel;
|
|
||||||
int flushing;
|
|
||||||
unsigned int corefreq_hz;
|
|
||||||
int fd_capable;
|
|
||||||
|
|
||||||
/* Circular DMA buffers */
|
|
||||||
void *_rx, *_rx_hw;
|
|
||||||
void *_tx, *_tx_hw;
|
|
||||||
void *txbuf_adr;
|
|
||||||
void *rxbuf_adr;
|
|
||||||
struct grcan_msg *rx;
|
|
||||||
struct grcan_msg *tx;
|
|
||||||
unsigned int rxbuf_size; /* requested RX buf size in bytes */
|
|
||||||
unsigned int txbuf_size; /* requested TX buf size in bytes */
|
|
||||||
|
|
||||||
int txblock, rxblock;
|
|
||||||
int txcomplete, rxcomplete;
|
|
||||||
|
|
||||||
struct grcan_filter sfilter;
|
|
||||||
struct grcan_filter afilter;
|
|
||||||
int config_changed; /* 0=no changes, 1=changes ==> a Core reset is needed */
|
|
||||||
struct grcan_config config;
|
|
||||||
struct grcan_stats stats;
|
|
||||||
|
|
||||||
rtems_id rx_sem, tx_sem, txempty_sem, dev_sem;
|
|
||||||
SPIN_DECLARE(devlock);
|
|
||||||
};
|
|
||||||
|
|
||||||
static void __inline__ grcan_hw_reset(struct grcan_regs *regs);
|
static void __inline__ grcan_hw_reset(struct grcan_regs *regs);
|
||||||
|
|
||||||
static int grcan_hw_read_try(
|
static int grcan_hw_read_try(
|
||||||
@@ -162,12 +92,6 @@ static int grcan_hw_write_try(
|
|||||||
CANMsg *buffer,
|
CANMsg *buffer,
|
||||||
int count);
|
int count);
|
||||||
|
|
||||||
static int grcan_hw_write_try_fd(
|
|
||||||
struct grcan_priv *pDev,
|
|
||||||
struct grcan_regs *regs,
|
|
||||||
CANFDMsg *buffer,
|
|
||||||
int count);
|
|
||||||
|
|
||||||
static void grcan_hw_config(
|
static void grcan_hw_config(
|
||||||
struct grcan_priv *pDev,
|
struct grcan_priv *pDev,
|
||||||
struct grcan_config *conf);
|
struct grcan_config *conf);
|
||||||
@@ -182,26 +106,10 @@ static void grcan_hw_sync(
|
|||||||
|
|
||||||
static void grcan_interrupt(void *arg);
|
static void grcan_interrupt(void *arg);
|
||||||
|
|
||||||
#ifdef GRCAN_REG_BYPASS_CACHE
|
|
||||||
#define READ_REG(address) grlib_read_uncached32((unsigned int)(address))
|
|
||||||
#else
|
|
||||||
#define READ_REG(address) (*(volatile unsigned int *)(address))
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef GRCAN_DMA_BYPASS_CACHE
|
|
||||||
#define READ_DMA_DOUBLE(address) grlib_read_uncached64((uint64_t *)(address))
|
|
||||||
#define READ_DMA_WORD(address) grlib_read_uncached32((unsigned int)(address))
|
|
||||||
#define READ_DMA_BYTE(address) grlib_read_uncached8((unsigned int)(address))
|
|
||||||
#else
|
|
||||||
#define READ_DMA_DOUBLE(address) (*(volatile uint64_t *)(address))
|
|
||||||
#define READ_DMA_WORD(address) (*(volatile unsigned int *)(address))
|
|
||||||
#define READ_DMA_BYTE(address) (*(volatile unsigned char *)(address))
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#define NELEM(a) ((int) (sizeof (a) / sizeof (a[0])))
|
#define NELEM(a) ((int) (sizeof (a) / sizeof (a[0])))
|
||||||
|
|
||||||
/* GRCAN nominal boundaries for baud-rate paramters */
|
/* GRCAN nominal boundaries for baud-rate paramters */
|
||||||
static struct grlib_canbtrs_ranges grcan_btrs_ranges = {
|
struct grlib_canbtrs_ranges grcan_btrs_ranges = {
|
||||||
.max_scaler = 256*8, /* scaler is multiplied by BPR in steps 1,2,4,8 */
|
.max_scaler = 256*8, /* scaler is multiplied by BPR in steps 1,2,4,8 */
|
||||||
.has_bpr = 1,
|
.has_bpr = 1,
|
||||||
.divfactor = 2,
|
.divfactor = 2,
|
||||||
@@ -593,50 +501,6 @@ static void grcan_hw_sync(struct grcan_regs *regs, struct grcan_filter *sfilter)
|
|||||||
regs->smask = sfilter->mask;
|
regs->smask = sfilter->mask;
|
||||||
}
|
}
|
||||||
|
|
||||||
static unsigned int grcan_hw_rxavail(
|
|
||||||
unsigned int rp,
|
|
||||||
unsigned int wp, unsigned int size
|
|
||||||
)
|
|
||||||
{
|
|
||||||
if (rp == wp) {
|
|
||||||
/* read pointer and write pointer is equal only
|
|
||||||
* when RX buffer is empty.
|
|
||||||
*/
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (wp > rp) {
|
|
||||||
return (wp - rp) / GRCAN_MSG_SIZE;
|
|
||||||
} else {
|
|
||||||
return (size - (rp - wp)) / GRCAN_MSG_SIZE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static unsigned int grcan_hw_txspace(
|
|
||||||
unsigned int rp,
|
|
||||||
unsigned int wp,
|
|
||||||
unsigned int size
|
|
||||||
)
|
|
||||||
{
|
|
||||||
unsigned int left;
|
|
||||||
|
|
||||||
if (rp == wp) {
|
|
||||||
/* read pointer and write pointer is equal only
|
|
||||||
* when TX buffer is empty.
|
|
||||||
*/
|
|
||||||
return size / GRCAN_MSG_SIZE - WRAP_AROUND_TX_MSGS;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* size - 4 - abs(read-write) */
|
|
||||||
if (wp > rp) {
|
|
||||||
left = size - (wp - rp);
|
|
||||||
} else {
|
|
||||||
left = rp - wp;
|
|
||||||
}
|
|
||||||
|
|
||||||
return left / GRCAN_MSG_SIZE - WRAP_AROUND_TX_MSGS;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int grcan_hw_read_try(
|
static int grcan_hw_read_try(
|
||||||
struct grcan_priv *pDev,
|
struct grcan_priv *pDev,
|
||||||
struct grcan_regs *regs,
|
struct grcan_regs *regs,
|
||||||
@@ -813,241 +677,7 @@ static int grcan_hw_write_try(
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static uint8_t dlc2len[16] = {
|
int grcan_wait_rxdata(struct grcan_priv *pDev, int min)
|
||||||
0, 1, 2, 3,
|
|
||||||
4, 5, 6, 7,
|
|
||||||
8, 12, 16, 20,
|
|
||||||
24, 32, 48, 64
|
|
||||||
};
|
|
||||||
|
|
||||||
static uint8_t len2fddlc[14] = {
|
|
||||||
/* 12,13 */ 0x9,
|
|
||||||
/* 16,17 */ 0xA,
|
|
||||||
/* 20,21 */ 0xB,
|
|
||||||
/* 24,25 */ 0xC,
|
|
||||||
/* 28,29 */ -1,
|
|
||||||
/* 32,33 */ 0xD,
|
|
||||||
/* 36,37 */ -1,
|
|
||||||
/* 40,41 */ -1,
|
|
||||||
/* 44,45 */ -1,
|
|
||||||
/* 48,49 */ 0xE,
|
|
||||||
/* 52,53 */ -1,
|
|
||||||
/* 56,57 */ -1,
|
|
||||||
/* 60,61 */ -1,
|
|
||||||
/* 64,65 */ 0xF,
|
|
||||||
};
|
|
||||||
|
|
||||||
/* Convert length in bytes to descriptor length field */
|
|
||||||
static inline uint8_t grcan_len2dlc(int len)
|
|
||||||
{
|
|
||||||
if (len <= 8)
|
|
||||||
return len;
|
|
||||||
if (len > 64)
|
|
||||||
return -1;
|
|
||||||
if (len & 0x3)
|
|
||||||
return -1;
|
|
||||||
return len2fddlc[(len - 12) >> 2];
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline int grcan_numbds(int len)
|
|
||||||
{
|
|
||||||
return 1 + ((len + 7) >> 4);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int grcan_hw_read_try_fd(
|
|
||||||
struct grcan_priv *pDev,
|
|
||||||
struct grcan_regs *regs,
|
|
||||||
CANFDMsg * buffer,
|
|
||||||
int max)
|
|
||||||
{
|
|
||||||
int j;
|
|
||||||
CANFDMsg *dest;
|
|
||||||
struct grcanfd_bd0 *source, tmp, *rxmax;
|
|
||||||
unsigned int wp, rp, size, addr;
|
|
||||||
int bds_hw_avail, bds_tot, bds, ret, dlc;
|
|
||||||
uint64_t *dp;
|
|
||||||
SPIN_IRQFLAGS(oldLevel);
|
|
||||||
|
|
||||||
FUNCDBG();
|
|
||||||
|
|
||||||
wp = READ_REG(®s->rx0wr);
|
|
||||||
rp = READ_REG(®s->rx0rd);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Due to hardware wrap around simplification write pointer will
|
|
||||||
* never reach the read pointer, at least a gap of 8 bytes.
|
|
||||||
* The only time they are equal is when the read pointer has
|
|
||||||
* reached the write pointer (empty buffer)
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
if (wp != rp) {
|
|
||||||
/* Not empty, we have received chars...
|
|
||||||
* Read as much as possible from DMA buffer
|
|
||||||
*/
|
|
||||||
size = READ_REG(®s->rx0size);
|
|
||||||
|
|
||||||
/* Get number of bytes available in RX buffer */
|
|
||||||
bds_hw_avail = grcan_hw_rxavail(rp, wp, size);
|
|
||||||
|
|
||||||
addr = (unsigned int)pDev->rx;
|
|
||||||
source = (struct grcanfd_bd0 *)(addr + rp);
|
|
||||||
dest = buffer;
|
|
||||||
rxmax = (struct grcanfd_bd0 *)(addr + size);
|
|
||||||
ret = bds_tot = 0;
|
|
||||||
|
|
||||||
/* Read as many can messages as possible */
|
|
||||||
while ((ret < max) && (bds_tot < bds_hw_avail)) {
|
|
||||||
/* Read CAN message from DMA buffer */
|
|
||||||
*(uint64_t *)&tmp = READ_DMA_DOUBLE(source);
|
|
||||||
if (tmp.head[1] & 0x4) {
|
|
||||||
DBGC(DBG_RX, "overrun\n");
|
|
||||||
}
|
|
||||||
if (tmp.head[1] & 0x2) {
|
|
||||||
DBGC(DBG_RX, "bus-off mode\n");
|
|
||||||
}
|
|
||||||
if (tmp.head[1] & 0x1) {
|
|
||||||
DBGC(DBG_RX, "error-passive mode\n");
|
|
||||||
}
|
|
||||||
/* Convert one grcan CAN message to one "software" CAN message */
|
|
||||||
dest->extended = tmp.head[0] >> 31;
|
|
||||||
dest->rtr = (tmp.head[0] >> 30) & 0x1;
|
|
||||||
if (dest->extended) {
|
|
||||||
dest->id = tmp.head[0] & 0x3fffffff;
|
|
||||||
} else {
|
|
||||||
dest->id = (tmp.head[0] >> 18) & 0xfff;
|
|
||||||
}
|
|
||||||
dest->fdopts = (tmp.head[1] >> 25) & GRCAN_FDMASK;
|
|
||||||
dlc = tmp.head[1] >> 28;
|
|
||||||
if (dest->fdopts & GRCAN_FDOPT_FDFRM) {
|
|
||||||
dest->len = dlc2len[dlc];
|
|
||||||
} else {
|
|
||||||
dest->len = dlc;
|
|
||||||
if (dlc > 8)
|
|
||||||
dest->len = 8;
|
|
||||||
}
|
|
||||||
|
|
||||||
dp = (uint64_t *)&source->data0;
|
|
||||||
for (j = 0; j < ((dest->len + 7) / 8); j++) {
|
|
||||||
dest->data.dwords[j] = READ_DMA_DOUBLE(dp);
|
|
||||||
if (++dp >= (uint64_t *)rxmax)
|
|
||||||
dp = (uint64_t *)addr; /* wrap around */
|
|
||||||
}
|
|
||||||
|
|
||||||
/* wrap around if neccessary */
|
|
||||||
bds = grcan_numbds(dest->len);
|
|
||||||
source += bds;
|
|
||||||
if (source >= rxmax) {
|
|
||||||
source = (struct grcanfd_bd0 *)
|
|
||||||
((void *)source - size);
|
|
||||||
}
|
|
||||||
dest++; /* straight user buffer */
|
|
||||||
ret++;
|
|
||||||
bds_tot += bds;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* A bus off interrupt may have occured after checking pDev->started */
|
|
||||||
SPIN_LOCK_IRQ(&pDev->devlock, oldLevel);
|
|
||||||
if (pDev->started == STATE_STARTED) {
|
|
||||||
regs->rx0rd = (unsigned int) source - addr;
|
|
||||||
regs->rx0ctrl = GRCAN_RXCTRL_ENABLE;
|
|
||||||
} else {
|
|
||||||
DBGC(DBG_STATE, "cancelled due to a BUS OFF error\n");
|
|
||||||
ret = state2err[pDev->started];
|
|
||||||
}
|
|
||||||
SPIN_UNLOCK_IRQ(&pDev->devlock, oldLevel);
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int grcan_hw_write_try_fd(
|
|
||||||
struct grcan_priv *pDev,
|
|
||||||
struct grcan_regs *regs,
|
|
||||||
CANFDMsg *buffer,
|
|
||||||
int count)
|
|
||||||
{
|
|
||||||
unsigned int rp, wp, size, addr;
|
|
||||||
int ret;
|
|
||||||
struct grcanfd_bd0 *dest, *txmax;
|
|
||||||
CANFDMsg *source = (CANFDMsg *) buffer;
|
|
||||||
int space_left;
|
|
||||||
unsigned int tmp;
|
|
||||||
int i, bds;
|
|
||||||
uint64_t *dp;
|
|
||||||
uint8_t dlc;
|
|
||||||
SPIN_IRQFLAGS(oldLevel);
|
|
||||||
|
|
||||||
DBGC(DBG_TX, "\n");
|
|
||||||
|
|
||||||
rp = READ_REG(®s->tx0rd);
|
|
||||||
wp = READ_REG(®s->tx0wr);
|
|
||||||
size = READ_REG(®s->tx0size);
|
|
||||||
space_left = grcan_hw_txspace(rp, wp, size);
|
|
||||||
|
|
||||||
addr = (unsigned int)pDev->tx;
|
|
||||||
dest = (struct grcanfd_bd0 *)(addr + wp);
|
|
||||||
txmax = (struct grcanfd_bd0 *)(addr + size);
|
|
||||||
ret = 0;
|
|
||||||
|
|
||||||
while (source < &buffer[count]) {
|
|
||||||
/* Get the number of descriptors to wait for */
|
|
||||||
if (source->fdopts & GRCAN_FDOPT_FDFRM)
|
|
||||||
bds = grcan_numbds(source->len); /* next msg's buffers */
|
|
||||||
else
|
|
||||||
bds = 1;
|
|
||||||
if (space_left < bds)
|
|
||||||
break;
|
|
||||||
|
|
||||||
/* Convert and write CAN message to DMA buffer */
|
|
||||||
dlc = grcan_len2dlc(source->len);
|
|
||||||
if (dlc < 0) {
|
|
||||||
/* Bad user input. Report the number of written messages
|
|
||||||
* or an error when non sent.
|
|
||||||
*/
|
|
||||||
if (ret <= 0)
|
|
||||||
return GRCAN_RET_INVARG;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
dest->head[1] = (dlc << 28) |
|
|
||||||
((source->fdopts & GRCAN_FDMASK) << 25);
|
|
||||||
dp = &dest->data0;
|
|
||||||
for (i = 0; i < ((source->len + 7) / 8); i++) {
|
|
||||||
*dp++ = source->data.dwords[i];
|
|
||||||
if (dp >= (uint64_t *)txmax)
|
|
||||||
dp = (uint64_t *)addr; /* wrap around */
|
|
||||||
}
|
|
||||||
if (source->extended) {
|
|
||||||
tmp = (1 << 31) | (source->id & 0x3fffffff);
|
|
||||||
} else {
|
|
||||||
tmp = (source->id & 0xfff) << 18;
|
|
||||||
}
|
|
||||||
if (source->rtr)
|
|
||||||
tmp |= (1 << 30);
|
|
||||||
dest->head[0] = tmp;
|
|
||||||
source++; /* straight user buffer */
|
|
||||||
dest += bds;
|
|
||||||
if (dest >= txmax)
|
|
||||||
dest = (struct grcanfd_bd0 *)((void *)dest - size);
|
|
||||||
space_left -= bds;
|
|
||||||
ret++;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* A bus off interrupt may have occured after checking pDev->started */
|
|
||||||
SPIN_LOCK_IRQ(&pDev->devlock, oldLevel);
|
|
||||||
if (pDev->started == STATE_STARTED) {
|
|
||||||
regs->tx0wr = (unsigned int) dest - addr;
|
|
||||||
regs->tx0ctrl = GRCAN_TXCTRL_ENABLE;
|
|
||||||
} else {
|
|
||||||
DBGC(DBG_STATE, "cancelled due to a BUS OFF error\n");
|
|
||||||
ret = state2err[pDev->started];
|
|
||||||
}
|
|
||||||
SPIN_UNLOCK_IRQ(&pDev->devlock, oldLevel);
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int grcan_wait_rxdata(struct grcan_priv *pDev, int min)
|
|
||||||
{
|
{
|
||||||
unsigned int wp, rp, size, irq;
|
unsigned int wp, rp, size, irq;
|
||||||
unsigned int irq_trunk, dataavail;
|
unsigned int irq_trunk, dataavail;
|
||||||
@@ -1130,7 +760,7 @@ static int grcan_wait_rxdata(struct grcan_priv *pDev, int min)
|
|||||||
* min must be at least WRAP_AROUND_TX_MSGS less than max buffer capacity
|
* min must be at least WRAP_AROUND_TX_MSGS less than max buffer capacity
|
||||||
* (pDev->txbuf_size/GRCAN_MSG_SIZE) for this algo to work.
|
* (pDev->txbuf_size/GRCAN_MSG_SIZE) for this algo to work.
|
||||||
*/
|
*/
|
||||||
static int grcan_wait_txspace(struct grcan_priv *pDev, int min)
|
int grcan_wait_txspace(struct grcan_priv *pDev, int min)
|
||||||
{
|
{
|
||||||
int wait, state;
|
int wait, state;
|
||||||
unsigned int irq, rp, wp, size, space_left;
|
unsigned int irq, rp, wp, size, space_left;
|
||||||
@@ -1407,6 +1037,7 @@ void *grcan_open(int dev_no)
|
|||||||
struct grcan_priv *pDev;
|
struct grcan_priv *pDev;
|
||||||
void *ret;
|
void *ret;
|
||||||
union drvmgr_key_value *value;
|
union drvmgr_key_value *value;
|
||||||
|
struct grlib_canbtrs_ranges *br;
|
||||||
|
|
||||||
FUNCDBG();
|
FUNCDBG();
|
||||||
|
|
||||||
@@ -1478,10 +1109,13 @@ void *grcan_open(int dev_no)
|
|||||||
pDev->sfilter.code = 0x00000000;
|
pDev->sfilter.code = 0x00000000;
|
||||||
|
|
||||||
/* Calculate default timing register values */
|
/* Calculate default timing register values */
|
||||||
|
if (pDev->fd_capable)
|
||||||
|
br = &grcanfd_nom_btrs_ranges;
|
||||||
|
else
|
||||||
|
br = &grcan_btrs_ranges;
|
||||||
grlib_canbtrs_calc_timing(
|
grlib_canbtrs_calc_timing(
|
||||||
GRCAN_DEFAULT_BAUD, pDev->corefreq_hz,
|
GRCAN_DEFAULT_BAUD, pDev->corefreq_hz, GRCAN_SAMPLING_POINT,
|
||||||
GRCAN_SAMPLING_POINT, &grcan_btrs_ranges,
|
br, (struct grlib_canbtrs_timing *)&pDev->config.timing);
|
||||||
(struct grlib_canbtrs_timing *)&pDev->config.timing);
|
|
||||||
|
|
||||||
if ( grcan_alloc_buffers(pDev,1,1) ) {
|
if ( grcan_alloc_buffers(pDev,1,1) ) {
|
||||||
ret = NULL;
|
ret = NULL;
|
||||||
@@ -1604,88 +1238,6 @@ int grcan_read(void *d, CANMsg *msg, size_t ucount)
|
|||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
|
|
||||||
int grcanfd_read(void *d, CANFDMsg *msg, size_t ucount)
|
|
||||||
{
|
|
||||||
struct grcan_priv *pDev = d;
|
|
||||||
CANFDMsg *dest;
|
|
||||||
unsigned int count, left;
|
|
||||||
int nread;
|
|
||||||
int req_cnt;
|
|
||||||
|
|
||||||
FUNCDBG();
|
|
||||||
|
|
||||||
dest = msg;
|
|
||||||
req_cnt = ucount;
|
|
||||||
|
|
||||||
if ( (!dest) || (req_cnt<1) )
|
|
||||||
return GRCAN_RET_INVARG;
|
|
||||||
|
|
||||||
if (pDev->started != STATE_STARTED) {
|
|
||||||
return GRCAN_RET_NOTSTARTED;
|
|
||||||
}
|
|
||||||
|
|
||||||
DBGC(DBG_RX, "grcan_read [%p]: buf: %p len: %u\n", d, msg, (unsigned int) ucount);
|
|
||||||
|
|
||||||
nread = grcan_hw_read_try_fd(pDev,pDev->regs,dest,req_cnt);
|
|
||||||
if (nread < 0) {
|
|
||||||
return nread;
|
|
||||||
}
|
|
||||||
count = nread;
|
|
||||||
if ( !( pDev->rxblock && pDev->rxcomplete && (count!=req_cnt) ) ){
|
|
||||||
if ( count > 0 ) {
|
|
||||||
/* Successfully received messages (at least one) */
|
|
||||||
return count;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* nothing read, shall we block? */
|
|
||||||
if ( !pDev->rxblock ) {
|
|
||||||
/* non-blocking mode */
|
|
||||||
return GRCAN_RET_TIMEOUT;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
while (count == 0 || (pDev->rxcomplete && (count!=req_cnt))) {
|
|
||||||
if (!pDev->rxcomplete) {
|
|
||||||
left = 1; /* return as soon as there is one message available */
|
|
||||||
} else {
|
|
||||||
left = req_cnt - count; /* return as soon as all data are available */
|
|
||||||
|
|
||||||
/* never wait for more than the half the maximum size of the receive buffer
|
|
||||||
* Why? We need some time to copy buffer before to catch up with hw,
|
|
||||||
* otherwise we would have to copy everything when the data has been
|
|
||||||
* received.
|
|
||||||
*/
|
|
||||||
if (left > ((pDev->rxbuf_size/GRCAN_MSG_SIZE) / 2)){
|
|
||||||
left = (pDev->rxbuf_size/GRCAN_MSG_SIZE) / 2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
nread = grcan_wait_rxdata(pDev, left);
|
|
||||||
if (nread) {
|
|
||||||
/* The wait has been aborted, probably due to
|
|
||||||
* the device driver has been closed by another
|
|
||||||
* thread or a bus-off. Return error code.
|
|
||||||
*/
|
|
||||||
return nread;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Try read bytes from circular buffer */
|
|
||||||
nread = grcan_hw_read_try_fd(
|
|
||||||
pDev,
|
|
||||||
pDev->regs,
|
|
||||||
dest+count,
|
|
||||||
req_cnt-count);
|
|
||||||
|
|
||||||
if (nread < 0) {
|
|
||||||
/* The read was aborted by bus-off. */
|
|
||||||
return nread;
|
|
||||||
}
|
|
||||||
count += nread;
|
|
||||||
}
|
|
||||||
/* no need to unmask IRQ as IRQ Handler do that for us. */
|
|
||||||
return count;
|
|
||||||
}
|
|
||||||
|
|
||||||
int grcan_write(void *d, CANMsg *msg, size_t ucount)
|
int grcan_write(void *d, CANMsg *msg, size_t ucount)
|
||||||
{
|
{
|
||||||
struct grcan_priv *pDev = d;
|
struct grcan_priv *pDev = d;
|
||||||
@@ -1774,103 +1326,6 @@ int grcan_write(void *d, CANMsg *msg, size_t ucount)
|
|||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
|
|
||||||
int grcanfd_write(
|
|
||||||
void *d,
|
|
||||||
CANFDMsg *msg,
|
|
||||||
size_t ucount)
|
|
||||||
{
|
|
||||||
struct grcan_priv *pDev = d;
|
|
||||||
CANFDMsg *source, *curr;
|
|
||||||
unsigned int count, left;
|
|
||||||
int nwritten;
|
|
||||||
int req_cnt;
|
|
||||||
|
|
||||||
DBGC(DBG_TX,"\n");
|
|
||||||
|
|
||||||
if ((pDev->started != STATE_STARTED) || pDev->config.silent || pDev->flushing)
|
|
||||||
return GRCAN_RET_NOTSTARTED;
|
|
||||||
|
|
||||||
req_cnt = ucount;
|
|
||||||
curr = source = (CANFDMsg *) msg;
|
|
||||||
|
|
||||||
/* check proper length and buffer pointer */
|
|
||||||
if (( req_cnt < 1) || (source == NULL) ){
|
|
||||||
return GRCAN_RET_INVARG;
|
|
||||||
}
|
|
||||||
|
|
||||||
nwritten = grcan_hw_write_try_fd(pDev,pDev->regs,source,req_cnt);
|
|
||||||
if (nwritten < 0) {
|
|
||||||
return nwritten;
|
|
||||||
}
|
|
||||||
count = nwritten;
|
|
||||||
if ( !(pDev->txblock && pDev->txcomplete && (count!=req_cnt)) ) {
|
|
||||||
if ( count > 0 ) {
|
|
||||||
/* Successfully transmitted chars (at least one char) */
|
|
||||||
return count;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* nothing written, shall we block? */
|
|
||||||
if ( !pDev->txblock ) {
|
|
||||||
/* non-blocking mode */
|
|
||||||
return GRCAN_RET_TIMEOUT;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* if in txcomplete mode we need to transmit all chars */
|
|
||||||
while((count == 0) || (pDev->txcomplete && (count!=req_cnt)) ){
|
|
||||||
/*** block until room to fit all or as much of transmit buffer
|
|
||||||
* as possible before IRQ comes. Set up a valid IRQ point so
|
|
||||||
* that an IRQ is triggered when we can put a chunk of data
|
|
||||||
* into transmit fifo.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* Get the number of descriptors to wait for */
|
|
||||||
curr = &source[count];
|
|
||||||
if (curr->fdopts & GRCAN_FDOPT_FDFRM)
|
|
||||||
left = grcan_numbds(curr->len); /* next msg's buffers */
|
|
||||||
else
|
|
||||||
left = 1;
|
|
||||||
|
|
||||||
if (pDev->txcomplete) {
|
|
||||||
/* Wait for all messages to fit into descriptor table.
|
|
||||||
* Assume all following msgs are single descriptors.
|
|
||||||
*/
|
|
||||||
left += req_cnt - count - 1;
|
|
||||||
if (left > ((pDev->txbuf_size/GRCAN_MSG_SIZE)/2)) {
|
|
||||||
left = (pDev->txbuf_size/GRCAN_MSG_SIZE)/2;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
nwritten = grcan_wait_txspace(pDev,left);
|
|
||||||
/* Wait until more room in transmit buffer */
|
|
||||||
if ( nwritten ) {
|
|
||||||
/* The wait has been aborted, probably due to
|
|
||||||
* the device driver has been closed by another
|
|
||||||
* thread. To avoid deadlock we return directly
|
|
||||||
* with error status.
|
|
||||||
*/
|
|
||||||
return nwritten;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Try read bytes from circular buffer */
|
|
||||||
nwritten = grcan_hw_write_try_fd(
|
|
||||||
pDev,
|
|
||||||
pDev->regs,
|
|
||||||
source+count,
|
|
||||||
req_cnt-count);
|
|
||||||
|
|
||||||
if (nwritten < 0) {
|
|
||||||
/* Write was aborted by bus-off. */
|
|
||||||
return nwritten;
|
|
||||||
}
|
|
||||||
count += nwritten;
|
|
||||||
}
|
|
||||||
/* no need to unmask IRQ as IRQ Handler do that for us. */
|
|
||||||
|
|
||||||
return count;
|
|
||||||
}
|
|
||||||
|
|
||||||
int grcan_start(void *d)
|
int grcan_start(void *d)
|
||||||
{
|
{
|
||||||
struct grcan_priv *pDev = d;
|
struct grcan_priv *pDev = d;
|
||||||
@@ -2144,77 +1599,6 @@ int grcan_set_btrs(void *d, const struct grcan_timing *timing)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int grcanfd_set_speed(void *d, unsigned int nom_hz, unsigned int fd_hz)
|
|
||||||
{
|
|
||||||
struct grcan_priv *pDev = d;
|
|
||||||
struct grlib_canbtrs_timing nom, fd;
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
FUNCDBG();
|
|
||||||
|
|
||||||
/* cannot change speed during run mode */
|
|
||||||
if ((pDev->started == STATE_STARTED) || !pDev->fd_capable)
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
/* get speed rate from argument */
|
|
||||||
ret = grlib_canbtrs_calc_timing(
|
|
||||||
nom_hz, pDev->corefreq_hz, GRCAN_SAMPLING_POINT,
|
|
||||||
&grcanfd_nom_btrs_ranges, &nom);
|
|
||||||
if ( ret )
|
|
||||||
return -2;
|
|
||||||
ret = grlib_canbtrs_calc_timing(
|
|
||||||
fd_hz, pDev->corefreq_hz, GRCAN_SAMPLING_POINT,
|
|
||||||
&grcanfd_fd_btrs_ranges, &fd);
|
|
||||||
if ( ret )
|
|
||||||
return -2;
|
|
||||||
|
|
||||||
/* save timing/speed */
|
|
||||||
pDev->config.timing = *(struct grcan_timing *)&nom;
|
|
||||||
pDev->config.timing_fd.scaler = fd.scaler;
|
|
||||||
pDev->config.timing_fd.ps1 = fd.ps1;
|
|
||||||
pDev->config.timing_fd.ps2 = fd.ps2;
|
|
||||||
pDev->config.timing_fd.sjw = fd.rsj;
|
|
||||||
pDev->config.timing_fd.resv_zero = 0;
|
|
||||||
pDev->config_changed = 1;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
int grcanfd_set_btrs(
|
|
||||||
void *d,
|
|
||||||
const struct grcanfd_timing *nominal,
|
|
||||||
const struct grcanfd_timing *fd)
|
|
||||||
{
|
|
||||||
struct grcan_priv *pDev = d;
|
|
||||||
|
|
||||||
FUNCDBG();
|
|
||||||
|
|
||||||
/* Set BTR registers manually
|
|
||||||
* Read GRCAN/HurriCANe Manual.
|
|
||||||
*/
|
|
||||||
if ((pDev->started == STATE_STARTED) || !pDev->fd_capable)
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
if (!nominal)
|
|
||||||
return -2;
|
|
||||||
|
|
||||||
pDev->config.timing.scaler = nominal->scaler;
|
|
||||||
pDev->config.timing.ps1 = nominal->ps1;
|
|
||||||
pDev->config.timing.ps2 = nominal->ps2;
|
|
||||||
pDev->config.timing.rsj = nominal->sjw;
|
|
||||||
pDev->config.timing.bpr = 0;
|
|
||||||
if (fd) {
|
|
||||||
pDev->config.timing_fd = *fd;
|
|
||||||
} else {
|
|
||||||
memset(&pDev->config.timing_fd, 0,
|
|
||||||
sizeof(struct grcanfd_timing));
|
|
||||||
}
|
|
||||||
pDev->config_changed = 1;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int grcan_set_afilter(void *d, const struct grcan_filter *filter)
|
int grcan_set_afilter(void *d, const struct grcan_filter *filter)
|
||||||
{
|
{
|
||||||
struct grcan_priv *pDev = d;
|
struct grcan_priv *pDev = d;
|
||||||
|
|||||||
140
bsps/shared/grlib/can/grcan_internal.h
Normal file
140
bsps/shared/grlib/can/grcan_internal.h
Normal file
@@ -0,0 +1,140 @@
|
|||||||
|
/*
|
||||||
|
* GRCAN driver
|
||||||
|
*
|
||||||
|
* COPYRIGHT (c) 2007-2019.
|
||||||
|
* Cobham Gaisler AB.
|
||||||
|
*
|
||||||
|
* The license and distribution terms for this file may be
|
||||||
|
* found in the file LICENSE in this distribution or at
|
||||||
|
* http://www.rtems.org/license/LICENSE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef GRCAN_DEFAULT_BAUD
|
||||||
|
/* default to 500kbits/s */
|
||||||
|
#define GRCAN_DEFAULT_BAUD 500000
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef GRCAN_SAMPLING_POINT
|
||||||
|
#define GRCAN_SAMPLING_POINT 80
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define WRAP_AROUND_TX_MSGS 1
|
||||||
|
#define WRAP_AROUND_RX_MSGS 2
|
||||||
|
#define GRCAN_MSG_SIZE sizeof(struct grcan_msg)
|
||||||
|
|
||||||
|
struct grcan_msg {
|
||||||
|
unsigned int head[2];
|
||||||
|
unsigned char data[8];
|
||||||
|
};
|
||||||
|
|
||||||
|
struct grcan_config {
|
||||||
|
struct grcan_timing timing;
|
||||||
|
struct grcanfd_timing timing_fd;
|
||||||
|
struct grcan_selection selection;
|
||||||
|
int abort;
|
||||||
|
int silent;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct grcan_priv {
|
||||||
|
struct drvmgr_dev *dev; /* Driver manager device */
|
||||||
|
char devName[32]; /* Device Name */
|
||||||
|
unsigned int baseaddr, ram_base;
|
||||||
|
struct grcan_regs *regs;
|
||||||
|
int irq;
|
||||||
|
int minor;
|
||||||
|
int open;
|
||||||
|
int started;
|
||||||
|
unsigned int channel;
|
||||||
|
int flushing;
|
||||||
|
unsigned int corefreq_hz;
|
||||||
|
int fd_capable;
|
||||||
|
|
||||||
|
/* Circular DMA buffers */
|
||||||
|
void *_rx, *_rx_hw;
|
||||||
|
void *_tx, *_tx_hw;
|
||||||
|
void *txbuf_adr;
|
||||||
|
void *rxbuf_adr;
|
||||||
|
struct grcan_msg *rx;
|
||||||
|
struct grcan_msg *tx;
|
||||||
|
unsigned int rxbuf_size; /* requested RX buf size in bytes */
|
||||||
|
unsigned int txbuf_size; /* requested TX buf size in bytes */
|
||||||
|
|
||||||
|
int txblock, rxblock;
|
||||||
|
int txcomplete, rxcomplete;
|
||||||
|
|
||||||
|
struct grcan_filter sfilter;
|
||||||
|
struct grcan_filter afilter;
|
||||||
|
int config_changed; /* 0=no changes, 1=changes ==> a Core reset is needed */
|
||||||
|
struct grcan_config config;
|
||||||
|
struct grcan_stats stats;
|
||||||
|
|
||||||
|
rtems_id rx_sem, tx_sem, txempty_sem, dev_sem;
|
||||||
|
SPIN_DECLARE(devlock);
|
||||||
|
};
|
||||||
|
|
||||||
|
#ifdef GRCAN_REG_BYPASS_CACHE
|
||||||
|
#define READ_REG(address) grlib_read_uncached32((unsigned int)(address))
|
||||||
|
#else
|
||||||
|
#define READ_REG(address) (*(volatile unsigned int *)(address))
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef GRCAN_DMA_BYPASS_CACHE
|
||||||
|
#define READ_DMA_DOUBLE(address) grlib_read_uncached64((uint64_t *)(address))
|
||||||
|
#define READ_DMA_WORD(address) grlib_read_uncached32((unsigned int)(address))
|
||||||
|
#define READ_DMA_BYTE(address) grlib_read_uncached8((unsigned int)(address))
|
||||||
|
#else
|
||||||
|
#define READ_DMA_DOUBLE(address) (*(volatile uint64_t *)(address))
|
||||||
|
#define READ_DMA_WORD(address) (*(volatile unsigned int *)(address))
|
||||||
|
#define READ_DMA_BYTE(address) (*(volatile unsigned char *)(address))
|
||||||
|
#endif
|
||||||
|
|
||||||
|
extern int state2err[4];
|
||||||
|
extern struct grlib_canbtrs_ranges grcan_btrs_ranges;
|
||||||
|
extern struct grlib_canbtrs_ranges grcanfd_nom_btrs_ranges;
|
||||||
|
extern struct grlib_canbtrs_ranges grcanfd_fd_btrs_ranges;
|
||||||
|
|
||||||
|
int grcan_wait_rxdata(struct grcan_priv *pDev, int min);
|
||||||
|
int grcan_wait_txspace(struct grcan_priv *pDev, int min);
|
||||||
|
|
||||||
|
static inline unsigned int grcan_hw_rxavail(
|
||||||
|
unsigned int rp,
|
||||||
|
unsigned int wp,
|
||||||
|
unsigned int size)
|
||||||
|
{
|
||||||
|
if (rp == wp) {
|
||||||
|
/* read pointer and write pointer is equal only
|
||||||
|
* when RX buffer is empty.
|
||||||
|
*/
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (wp > rp) {
|
||||||
|
return (wp - rp) / GRCAN_MSG_SIZE;
|
||||||
|
} else {
|
||||||
|
return (size - (rp - wp)) / GRCAN_MSG_SIZE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline unsigned int grcan_hw_txspace(
|
||||||
|
unsigned int rp,
|
||||||
|
unsigned int wp,
|
||||||
|
unsigned int size)
|
||||||
|
{
|
||||||
|
unsigned int left;
|
||||||
|
|
||||||
|
if (rp == wp) {
|
||||||
|
/* read pointer and write pointer is equal only
|
||||||
|
* when TX buffer is empty.
|
||||||
|
*/
|
||||||
|
return size / GRCAN_MSG_SIZE - WRAP_AROUND_TX_MSGS;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* size - 4 - abs(read-write) */
|
||||||
|
if (wp > rp) {
|
||||||
|
left = size - (wp - rp);
|
||||||
|
} else {
|
||||||
|
left = rp - wp;
|
||||||
|
}
|
||||||
|
|
||||||
|
return left / GRCAN_MSG_SIZE - WRAP_AROUND_TX_MSGS;
|
||||||
|
}
|
||||||
535
bsps/shared/grlib/can/grcanfd.c
Normal file
535
bsps/shared/grlib/can/grcanfd.c
Normal file
@@ -0,0 +1,535 @@
|
|||||||
|
/*
|
||||||
|
* FD extenstions to the GRCAN driver
|
||||||
|
*
|
||||||
|
* COPYRIGHT (c) 2007-2019.
|
||||||
|
* Cobham Gaisler AB.
|
||||||
|
*
|
||||||
|
* The license and distribution terms for this file may be
|
||||||
|
* found in the file LICENSE in this distribution or at
|
||||||
|
* http://www.rtems.org/license/LICENSE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <bsp.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <assert.h>
|
||||||
|
#include <ctype.h>
|
||||||
|
#include <rtems/bspIo.h>
|
||||||
|
|
||||||
|
#include <grlib/grcan.h>
|
||||||
|
#include <grlib/canbtrs.h>
|
||||||
|
#include <drvmgr/drvmgr.h>
|
||||||
|
#include <grlib/ambapp_bus.h>
|
||||||
|
#include <grlib/ambapp.h>
|
||||||
|
|
||||||
|
#include <grlib/grlib_impl.h>
|
||||||
|
#include "grcan_internal.h"
|
||||||
|
|
||||||
|
/* Uncomment for debug output */
|
||||||
|
/****************** DEBUG Definitions ********************/
|
||||||
|
#define DBG_TX 2
|
||||||
|
#define DBG_RX 4
|
||||||
|
#define DBG_STATE 8
|
||||||
|
|
||||||
|
#define DEBUG_FLAGS (DBG_STATE | DBG_RX | DBG_TX )
|
||||||
|
/*
|
||||||
|
#define DEBUG
|
||||||
|
#define DEBUGFUNCS
|
||||||
|
*/
|
||||||
|
#include <grlib/debug_defs.h>
|
||||||
|
|
||||||
|
/*********************************************************/
|
||||||
|
|
||||||
|
struct grcanfd_bd0 {
|
||||||
|
uint32_t head[2];
|
||||||
|
uint64_t data0; /* variable size, from 1 to 8 dwords */
|
||||||
|
};
|
||||||
|
|
||||||
|
struct grcanfd_bd1 {
|
||||||
|
unsigned long long data[2];
|
||||||
|
};
|
||||||
|
|
||||||
|
static uint8_t dlc2len[16] = {
|
||||||
|
0, 1, 2, 3,
|
||||||
|
4, 5, 6, 7,
|
||||||
|
8, 12, 16, 20,
|
||||||
|
24, 32, 48, 64
|
||||||
|
};
|
||||||
|
|
||||||
|
static uint8_t len2fddlc[14] = {
|
||||||
|
/* 12,13 */ 0x9,
|
||||||
|
/* 16,17 */ 0xA,
|
||||||
|
/* 20,21 */ 0xB,
|
||||||
|
/* 24,25 */ 0xC,
|
||||||
|
/* 28,29 */ -1,
|
||||||
|
/* 32,33 */ 0xD,
|
||||||
|
/* 36,37 */ -1,
|
||||||
|
/* 40,41 */ -1,
|
||||||
|
/* 44,45 */ -1,
|
||||||
|
/* 48,49 */ 0xE,
|
||||||
|
/* 52,53 */ -1,
|
||||||
|
/* 56,57 */ -1,
|
||||||
|
/* 60,61 */ -1,
|
||||||
|
/* 64,65 */ 0xF,
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Convert length in bytes to descriptor length field */
|
||||||
|
static inline uint8_t grcan_len2dlc(int len)
|
||||||
|
{
|
||||||
|
if (len <= 8)
|
||||||
|
return len;
|
||||||
|
if (len > 64)
|
||||||
|
return -1;
|
||||||
|
if (len & 0x3)
|
||||||
|
return -1;
|
||||||
|
return len2fddlc[(len - 12) >> 2];
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int grcan_numbds(int len)
|
||||||
|
{
|
||||||
|
return 1 + ((len + 7) >> 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int grcan_hw_read_try_fd(
|
||||||
|
struct grcan_priv *pDev,
|
||||||
|
struct grcan_regs *regs,
|
||||||
|
CANFDMsg * buffer,
|
||||||
|
int max)
|
||||||
|
{
|
||||||
|
int j;
|
||||||
|
CANFDMsg *dest;
|
||||||
|
struct grcanfd_bd0 *source, tmp, *rxmax;
|
||||||
|
unsigned int wp, rp, size, addr;
|
||||||
|
int bds_hw_avail, bds_tot, bds, ret, dlc;
|
||||||
|
uint64_t *dp;
|
||||||
|
SPIN_IRQFLAGS(oldLevel);
|
||||||
|
|
||||||
|
FUNCDBG();
|
||||||
|
|
||||||
|
wp = READ_REG(®s->rx0wr);
|
||||||
|
rp = READ_REG(®s->rx0rd);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Due to hardware wrap around simplification write pointer will
|
||||||
|
* never reach the read pointer, at least a gap of 8 bytes.
|
||||||
|
* The only time they are equal is when the read pointer has
|
||||||
|
* reached the write pointer (empty buffer)
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
if (wp != rp) {
|
||||||
|
/* Not empty, we have received chars...
|
||||||
|
* Read as much as possible from DMA buffer
|
||||||
|
*/
|
||||||
|
size = READ_REG(®s->rx0size);
|
||||||
|
|
||||||
|
/* Get number of bytes available in RX buffer */
|
||||||
|
bds_hw_avail = grcan_hw_rxavail(rp, wp, size);
|
||||||
|
|
||||||
|
addr = (unsigned int)pDev->rx;
|
||||||
|
source = (struct grcanfd_bd0 *)(addr + rp);
|
||||||
|
dest = buffer;
|
||||||
|
rxmax = (struct grcanfd_bd0 *)(addr + size);
|
||||||
|
ret = bds_tot = 0;
|
||||||
|
|
||||||
|
/* Read as many can messages as possible */
|
||||||
|
while ((ret < max) && (bds_tot < bds_hw_avail)) {
|
||||||
|
/* Read CAN message from DMA buffer */
|
||||||
|
*(uint64_t *)&tmp = READ_DMA_DOUBLE(source);
|
||||||
|
if (tmp.head[1] & 0x4) {
|
||||||
|
DBGC(DBG_RX, "overrun\n");
|
||||||
|
}
|
||||||
|
if (tmp.head[1] & 0x2) {
|
||||||
|
DBGC(DBG_RX, "bus-off mode\n");
|
||||||
|
}
|
||||||
|
if (tmp.head[1] & 0x1) {
|
||||||
|
DBGC(DBG_RX, "error-passive mode\n");
|
||||||
|
}
|
||||||
|
/* Convert one grcan CAN message to one "software" CAN message */
|
||||||
|
dest->extended = tmp.head[0] >> 31;
|
||||||
|
dest->rtr = (tmp.head[0] >> 30) & 0x1;
|
||||||
|
if (dest->extended) {
|
||||||
|
dest->id = tmp.head[0] & 0x3fffffff;
|
||||||
|
} else {
|
||||||
|
dest->id = (tmp.head[0] >> 18) & 0xfff;
|
||||||
|
}
|
||||||
|
dest->fdopts = (tmp.head[1] >> 25) & GRCAN_FDMASK;
|
||||||
|
dlc = tmp.head[1] >> 28;
|
||||||
|
if (dest->fdopts & GRCAN_FDOPT_FDFRM) {
|
||||||
|
dest->len = dlc2len[dlc];
|
||||||
|
} else {
|
||||||
|
dest->len = dlc;
|
||||||
|
if (dlc > 8)
|
||||||
|
dest->len = 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
dp = (uint64_t *)&source->data0;
|
||||||
|
for (j = 0; j < ((dest->len + 7) / 8); j++) {
|
||||||
|
dest->data.dwords[j] = READ_DMA_DOUBLE(dp);
|
||||||
|
if (++dp >= (uint64_t *)rxmax)
|
||||||
|
dp = (uint64_t *)addr; /* wrap around */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* wrap around if neccessary */
|
||||||
|
bds = grcan_numbds(dest->len);
|
||||||
|
source += bds;
|
||||||
|
if (source >= rxmax) {
|
||||||
|
source = (struct grcanfd_bd0 *)
|
||||||
|
((void *)source - size);
|
||||||
|
}
|
||||||
|
dest++; /* straight user buffer */
|
||||||
|
ret++;
|
||||||
|
bds_tot += bds;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* A bus off interrupt may have occured after checking pDev->started */
|
||||||
|
SPIN_LOCK_IRQ(&pDev->devlock, oldLevel);
|
||||||
|
if (pDev->started == STATE_STARTED) {
|
||||||
|
regs->rx0rd = (unsigned int) source - addr;
|
||||||
|
regs->rx0ctrl = GRCAN_RXCTRL_ENABLE;
|
||||||
|
} else {
|
||||||
|
DBGC(DBG_STATE, "cancelled due to a BUS OFF error\n");
|
||||||
|
ret = state2err[pDev->started];
|
||||||
|
}
|
||||||
|
SPIN_UNLOCK_IRQ(&pDev->devlock, oldLevel);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int grcanfd_read(void *d, CANFDMsg *msg, size_t ucount)
|
||||||
|
{
|
||||||
|
struct grcan_priv *pDev = d;
|
||||||
|
CANFDMsg *dest;
|
||||||
|
unsigned int count, left;
|
||||||
|
int nread;
|
||||||
|
int req_cnt;
|
||||||
|
|
||||||
|
FUNCDBG();
|
||||||
|
|
||||||
|
dest = msg;
|
||||||
|
req_cnt = ucount;
|
||||||
|
|
||||||
|
if ( (!dest) || (req_cnt<1) )
|
||||||
|
return GRCAN_RET_INVARG;
|
||||||
|
|
||||||
|
if (pDev->started != STATE_STARTED) {
|
||||||
|
return GRCAN_RET_NOTSTARTED;
|
||||||
|
}
|
||||||
|
|
||||||
|
DBGC(DBG_RX, "grcan_read [%p]: buf: %p len: %u\n", d, msg, (unsigned int) ucount);
|
||||||
|
|
||||||
|
nread = grcan_hw_read_try_fd(pDev,pDev->regs,dest,req_cnt);
|
||||||
|
if (nread < 0) {
|
||||||
|
return nread;
|
||||||
|
}
|
||||||
|
count = nread;
|
||||||
|
if ( !( pDev->rxblock && pDev->rxcomplete && (count!=req_cnt) ) ){
|
||||||
|
if ( count > 0 ) {
|
||||||
|
/* Successfully received messages (at least one) */
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* nothing read, shall we block? */
|
||||||
|
if ( !pDev->rxblock ) {
|
||||||
|
/* non-blocking mode */
|
||||||
|
return GRCAN_RET_TIMEOUT;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
while (count == 0 || (pDev->rxcomplete && (count!=req_cnt))) {
|
||||||
|
if (!pDev->rxcomplete) {
|
||||||
|
left = 1; /* return as soon as there is one message available */
|
||||||
|
} else {
|
||||||
|
left = req_cnt - count; /* return as soon as all data are available */
|
||||||
|
|
||||||
|
/* never wait for more than the half the maximum size of the receive buffer
|
||||||
|
* Why? We need some time to copy buffer before to catch up with hw,
|
||||||
|
* otherwise we would have to copy everything when the data has been
|
||||||
|
* received.
|
||||||
|
*/
|
||||||
|
if (left > ((pDev->rxbuf_size/GRCAN_MSG_SIZE) / 2)){
|
||||||
|
left = (pDev->rxbuf_size/GRCAN_MSG_SIZE) / 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
nread = grcan_wait_rxdata(pDev, left);
|
||||||
|
if (nread) {
|
||||||
|
/* The wait has been aborted, probably due to
|
||||||
|
* the device driver has been closed by another
|
||||||
|
* thread or a bus-off. Return error code.
|
||||||
|
*/
|
||||||
|
return nread;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Try read bytes from circular buffer */
|
||||||
|
nread = grcan_hw_read_try_fd(
|
||||||
|
pDev,
|
||||||
|
pDev->regs,
|
||||||
|
dest+count,
|
||||||
|
req_cnt-count);
|
||||||
|
|
||||||
|
if (nread < 0) {
|
||||||
|
/* The read was aborted by bus-off. */
|
||||||
|
return nread;
|
||||||
|
}
|
||||||
|
count += nread;
|
||||||
|
}
|
||||||
|
/* no need to unmask IRQ as IRQ Handler do that for us. */
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int grcan_hw_write_try_fd(
|
||||||
|
struct grcan_priv *pDev,
|
||||||
|
struct grcan_regs *regs,
|
||||||
|
CANFDMsg *buffer,
|
||||||
|
int count)
|
||||||
|
{
|
||||||
|
unsigned int rp, wp, size, addr;
|
||||||
|
int ret;
|
||||||
|
struct grcanfd_bd0 *dest, *txmax;
|
||||||
|
CANFDMsg *source = (CANFDMsg *) buffer;
|
||||||
|
int space_left;
|
||||||
|
unsigned int tmp;
|
||||||
|
int i, bds;
|
||||||
|
uint64_t *dp;
|
||||||
|
uint8_t dlc;
|
||||||
|
SPIN_IRQFLAGS(oldLevel);
|
||||||
|
|
||||||
|
DBGC(DBG_TX, "\n");
|
||||||
|
|
||||||
|
rp = READ_REG(®s->tx0rd);
|
||||||
|
wp = READ_REG(®s->tx0wr);
|
||||||
|
size = READ_REG(®s->tx0size);
|
||||||
|
space_left = grcan_hw_txspace(rp, wp, size);
|
||||||
|
|
||||||
|
addr = (unsigned int)pDev->tx;
|
||||||
|
dest = (struct grcanfd_bd0 *)(addr + wp);
|
||||||
|
txmax = (struct grcanfd_bd0 *)(addr + size);
|
||||||
|
ret = 0;
|
||||||
|
|
||||||
|
while (source < &buffer[count]) {
|
||||||
|
/* Get the number of descriptors to wait for */
|
||||||
|
if (source->fdopts & GRCAN_FDOPT_FDFRM)
|
||||||
|
bds = grcan_numbds(source->len); /* next msg's buffers */
|
||||||
|
else
|
||||||
|
bds = 1;
|
||||||
|
if (space_left < bds)
|
||||||
|
break;
|
||||||
|
|
||||||
|
/* Convert and write CAN message to DMA buffer */
|
||||||
|
dlc = grcan_len2dlc(source->len);
|
||||||
|
if (dlc < 0) {
|
||||||
|
/* Bad user input. Report the number of written messages
|
||||||
|
* or an error when non sent.
|
||||||
|
*/
|
||||||
|
if (ret <= 0)
|
||||||
|
return GRCAN_RET_INVARG;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
dest->head[1] = (dlc << 28) |
|
||||||
|
((source->fdopts & GRCAN_FDMASK) << 25);
|
||||||
|
dp = &dest->data0;
|
||||||
|
for (i = 0; i < ((source->len + 7) / 8); i++) {
|
||||||
|
*dp++ = source->data.dwords[i];
|
||||||
|
if (dp >= (uint64_t *)txmax)
|
||||||
|
dp = (uint64_t *)addr; /* wrap around */
|
||||||
|
}
|
||||||
|
if (source->extended) {
|
||||||
|
tmp = (1 << 31) | (source->id & 0x3fffffff);
|
||||||
|
} else {
|
||||||
|
tmp = (source->id & 0xfff) << 18;
|
||||||
|
}
|
||||||
|
if (source->rtr)
|
||||||
|
tmp |= (1 << 30);
|
||||||
|
dest->head[0] = tmp;
|
||||||
|
source++; /* straight user buffer */
|
||||||
|
dest += bds;
|
||||||
|
if (dest >= txmax)
|
||||||
|
dest = (struct grcanfd_bd0 *)((void *)dest - size);
|
||||||
|
space_left -= bds;
|
||||||
|
ret++;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* A bus off interrupt may have occured after checking pDev->started */
|
||||||
|
SPIN_LOCK_IRQ(&pDev->devlock, oldLevel);
|
||||||
|
if (pDev->started == STATE_STARTED) {
|
||||||
|
regs->tx0wr = (unsigned int) dest - addr;
|
||||||
|
regs->tx0ctrl = GRCAN_TXCTRL_ENABLE;
|
||||||
|
} else {
|
||||||
|
DBGC(DBG_STATE, "cancelled due to a BUS OFF error\n");
|
||||||
|
ret = state2err[pDev->started];
|
||||||
|
}
|
||||||
|
SPIN_UNLOCK_IRQ(&pDev->devlock, oldLevel);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
int grcanfd_write(
|
||||||
|
void *d,
|
||||||
|
CANFDMsg *msg,
|
||||||
|
size_t ucount)
|
||||||
|
{
|
||||||
|
struct grcan_priv *pDev = d;
|
||||||
|
CANFDMsg *source, *curr;
|
||||||
|
unsigned int count, left;
|
||||||
|
int nwritten;
|
||||||
|
int req_cnt;
|
||||||
|
|
||||||
|
DBGC(DBG_TX,"\n");
|
||||||
|
|
||||||
|
if ((pDev->started != STATE_STARTED) || pDev->config.silent || pDev->flushing)
|
||||||
|
return GRCAN_RET_NOTSTARTED;
|
||||||
|
|
||||||
|
req_cnt = ucount;
|
||||||
|
curr = source = (CANFDMsg *) msg;
|
||||||
|
|
||||||
|
/* check proper length and buffer pointer */
|
||||||
|
if (( req_cnt < 1) || (source == NULL) ){
|
||||||
|
return GRCAN_RET_INVARG;
|
||||||
|
}
|
||||||
|
|
||||||
|
nwritten = grcan_hw_write_try_fd(pDev,pDev->regs,source,req_cnt);
|
||||||
|
if (nwritten < 0) {
|
||||||
|
return nwritten;
|
||||||
|
}
|
||||||
|
count = nwritten;
|
||||||
|
if ( !(pDev->txblock && pDev->txcomplete && (count!=req_cnt)) ) {
|
||||||
|
if ( count > 0 ) {
|
||||||
|
/* Successfully transmitted chars (at least one char) */
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* nothing written, shall we block? */
|
||||||
|
if ( !pDev->txblock ) {
|
||||||
|
/* non-blocking mode */
|
||||||
|
return GRCAN_RET_TIMEOUT;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* if in txcomplete mode we need to transmit all chars */
|
||||||
|
while((count == 0) || (pDev->txcomplete && (count!=req_cnt)) ){
|
||||||
|
/*** block until room to fit all or as much of transmit buffer
|
||||||
|
* as possible before IRQ comes. Set up a valid IRQ point so
|
||||||
|
* that an IRQ is triggered when we can put a chunk of data
|
||||||
|
* into transmit fifo.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Get the number of descriptors to wait for */
|
||||||
|
curr = &source[count];
|
||||||
|
if (curr->fdopts & GRCAN_FDOPT_FDFRM)
|
||||||
|
left = grcan_numbds(curr->len); /* next msg's buffers */
|
||||||
|
else
|
||||||
|
left = 1;
|
||||||
|
|
||||||
|
if (pDev->txcomplete) {
|
||||||
|
/* Wait for all messages to fit into descriptor table.
|
||||||
|
* Assume all following msgs are single descriptors.
|
||||||
|
*/
|
||||||
|
left += req_cnt - count - 1;
|
||||||
|
if (left > ((pDev->txbuf_size/GRCAN_MSG_SIZE)/2)) {
|
||||||
|
left = (pDev->txbuf_size/GRCAN_MSG_SIZE)/2;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
nwritten = grcan_wait_txspace(pDev,left);
|
||||||
|
/* Wait until more room in transmit buffer */
|
||||||
|
if ( nwritten ) {
|
||||||
|
/* The wait has been aborted, probably due to
|
||||||
|
* the device driver has been closed by another
|
||||||
|
* thread. To avoid deadlock we return directly
|
||||||
|
* with error status.
|
||||||
|
*/
|
||||||
|
return nwritten;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Try read bytes from circular buffer */
|
||||||
|
nwritten = grcan_hw_write_try_fd(
|
||||||
|
pDev,
|
||||||
|
pDev->regs,
|
||||||
|
source+count,
|
||||||
|
req_cnt-count);
|
||||||
|
|
||||||
|
if (nwritten < 0) {
|
||||||
|
/* Write was aborted by bus-off. */
|
||||||
|
return nwritten;
|
||||||
|
}
|
||||||
|
count += nwritten;
|
||||||
|
}
|
||||||
|
/* no need to unmask IRQ as IRQ Handler do that for us. */
|
||||||
|
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
int grcanfd_set_speed(void *d, unsigned int nom_hz, unsigned int fd_hz)
|
||||||
|
{
|
||||||
|
struct grcan_priv *pDev = d;
|
||||||
|
struct grlib_canbtrs_timing nom, fd;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
FUNCDBG();
|
||||||
|
|
||||||
|
/* cannot change speed during run mode */
|
||||||
|
if ((pDev->started == STATE_STARTED) || !pDev->fd_capable)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
/* get speed rate from argument */
|
||||||
|
ret = grlib_canbtrs_calc_timing(
|
||||||
|
nom_hz, pDev->corefreq_hz, GRCAN_SAMPLING_POINT,
|
||||||
|
&grcanfd_nom_btrs_ranges, &nom);
|
||||||
|
if ( ret )
|
||||||
|
return -2;
|
||||||
|
ret = grlib_canbtrs_calc_timing(
|
||||||
|
fd_hz, pDev->corefreq_hz, GRCAN_SAMPLING_POINT,
|
||||||
|
&grcanfd_fd_btrs_ranges, &fd);
|
||||||
|
if ( ret )
|
||||||
|
return -2;
|
||||||
|
|
||||||
|
/* save timing/speed */
|
||||||
|
pDev->config.timing = *(struct grcan_timing *)&nom;
|
||||||
|
pDev->config.timing_fd.scaler = fd.scaler;
|
||||||
|
pDev->config.timing_fd.ps1 = fd.ps1;
|
||||||
|
pDev->config.timing_fd.ps2 = fd.ps2;
|
||||||
|
pDev->config.timing_fd.sjw = fd.rsj;
|
||||||
|
pDev->config.timing_fd.resv_zero = 0;
|
||||||
|
pDev->config_changed = 1;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
int grcanfd_set_btrs(
|
||||||
|
void *d,
|
||||||
|
const struct grcanfd_timing *nominal,
|
||||||
|
const struct grcanfd_timing *fd)
|
||||||
|
{
|
||||||
|
struct grcan_priv *pDev = d;
|
||||||
|
|
||||||
|
FUNCDBG();
|
||||||
|
|
||||||
|
/* Set BTR registers manually
|
||||||
|
* Read GRCAN/HurriCANe Manual.
|
||||||
|
*/
|
||||||
|
if ((pDev->started == STATE_STARTED) || !pDev->fd_capable)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
if (!nominal)
|
||||||
|
return -2;
|
||||||
|
|
||||||
|
pDev->config.timing.scaler = nominal->scaler;
|
||||||
|
pDev->config.timing.ps1 = nominal->ps1;
|
||||||
|
pDev->config.timing.ps2 = nominal->ps2;
|
||||||
|
pDev->config.timing.rsj = nominal->sjw;
|
||||||
|
pDev->config.timing.bpr = 0;
|
||||||
|
if (fd) {
|
||||||
|
pDev->config.timing_fd = *fd;
|
||||||
|
} else {
|
||||||
|
memset(&pDev->config.timing_fd, 0,
|
||||||
|
sizeof(struct grcanfd_timing));
|
||||||
|
}
|
||||||
|
pDev->config_changed = 1;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
@@ -103,6 +103,7 @@ source:
|
|||||||
- bsps/shared/grlib/can/canbtrs.c
|
- bsps/shared/grlib/can/canbtrs.c
|
||||||
- bsps/shared/grlib/can/canmux.c
|
- bsps/shared/grlib/can/canmux.c
|
||||||
- bsps/shared/grlib/can/grcan.c
|
- bsps/shared/grlib/can/grcan.c
|
||||||
|
- bsps/shared/grlib/can/grcanfd.c
|
||||||
- bsps/shared/grlib/can/occan.c
|
- bsps/shared/grlib/can/occan.c
|
||||||
- bsps/shared/grlib/can/satcan.c
|
- bsps/shared/grlib/can/satcan.c
|
||||||
- bsps/shared/grlib/drvmgr/ambapp_bus.c
|
- bsps/shared/grlib/drvmgr/ambapp_bus.c
|
||||||
|
|||||||
Reference in New Issue
Block a user