summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDaniel Hellstrom <daniel@gaisler.com>2019-04-12 22:41:04 +0200
committerDaniel Hellstrom <daniel@gaisler.com>2021-03-07 16:08:24 +0100
commite180f281abc778d9ca7ca798d4a81d5da1a507da (patch)
tree5a140d1af77fd66d512a8239231e3197a0abadcf
parentc41e7bae9cf8a04be6fdebe4c7ebd327f39faf02 (diff)
downloadrtems-e180f281abc778d9ca7ca798d4a81d5da1a507da.tar.bz2
leon,grcanfd: split out GRCANFD specific support in separate file
Update #4307.
-rw-r--r--bsps/shared/grlib-sources.am1
-rw-r--r--bsps/shared/grlib/can/grcan.c638
-rw-r--r--bsps/shared/grlib/can/grcan_internal.h140
-rw-r--r--bsps/shared/grlib/can/grcanfd.c535
4 files changed, 687 insertions, 627 deletions
diff --git a/bsps/shared/grlib-sources.am b/bsps/shared/grlib-sources.am
index f2ad1bf827..10820dd884 100644
--- a/bsps/shared/grlib-sources.am
+++ b/bsps/shared/grlib-sources.am
@@ -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/canmux.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/satcan.c
librtemsbsp_a_SOURCES += ../../../../../../bsps/shared/grlib/drvmgr/ambapp_bus.c
diff --git a/bsps/shared/grlib/can/grcan.c b/bsps/shared/grlib/can/grcan.c
index 959c84da3f..e2298d3c94 100644
--- a/bsps/shared/grlib/can/grcan.c
+++ b/bsps/shared/grlib/can/grcan.c
@@ -24,13 +24,11 @@
#include <grlib/ambapp.h>
#include <grlib/grlib_impl.h>
+#include "grcan_internal.h"
/* Maximum number of GRCAN devices supported by driver */
#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)
/* grcan needs to have it buffers aligned to 1k boundaries */
@@ -58,15 +56,6 @@
#define IRQ_MASK(irqno)
#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 */
/****************** DEBUG Definitions ********************/
#define DBG_TX 2
@@ -89,65 +78,6 @@ int state2err[4] = {
/* 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 int grcan_hw_read_try(
@@ -162,12 +92,6 @@ static int grcan_hw_write_try(
CANMsg *buffer,
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(
struct grcan_priv *pDev,
struct grcan_config *conf);
@@ -182,26 +106,10 @@ static void grcan_hw_sync(
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])))
/* 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 */
.has_bpr = 1,
.divfactor = 2,
@@ -593,50 +501,6 @@ static void grcan_hw_sync(struct grcan_regs *regs, struct grcan_filter *sfilter)
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(
struct grcan_priv *pDev,
struct grcan_regs *regs,
@@ -813,241 +677,7 @@ static int grcan_hw_write_try(
return ret;
}
-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(&regs->rx0wr);
- rp = READ_REG(&regs->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(&regs->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(&regs->tx0rd);
- wp = READ_REG(&regs->tx0wr);
- size = READ_REG(&regs->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)
+int grcan_wait_rxdata(struct grcan_priv *pDev, int min)
{
unsigned int wp, rp, size, irq;
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
* (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;
unsigned int irq, rp, wp, size, space_left;
@@ -1407,6 +1037,7 @@ void *grcan_open(int dev_no)
struct grcan_priv *pDev;
void *ret;
union drvmgr_key_value *value;
+ struct grlib_canbtrs_ranges *br;
FUNCDBG();
@@ -1478,10 +1109,13 @@ void *grcan_open(int dev_no)
pDev->sfilter.code = 0x00000000;
/* Calculate default timing register values */
+ if (pDev->fd_capable)
+ br = &grcanfd_nom_btrs_ranges;
+ else
+ br = &grcan_btrs_ranges;
grlib_canbtrs_calc_timing(
- GRCAN_DEFAULT_BAUD, pDev->corefreq_hz,
- GRCAN_SAMPLING_POINT, &grcan_btrs_ranges,
- (struct grlib_canbtrs_timing *)&pDev->config.timing);
+ GRCAN_DEFAULT_BAUD, pDev->corefreq_hz, GRCAN_SAMPLING_POINT,
+ br, (struct grlib_canbtrs_timing *)&pDev->config.timing);
if ( grcan_alloc_buffers(pDev,1,1) ) {
ret = NULL;
@@ -1604,88 +1238,6 @@ int grcan_read(void *d, CANMsg *msg, size_t ucount)
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)
{
struct grcan_priv *pDev = d;
@@ -1774,103 +1326,6 @@ int grcan_write(void *d, CANMsg *msg, size_t ucount)
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)
{
struct grcan_priv *pDev = d;
@@ -2144,77 +1599,6 @@ int grcan_set_btrs(void *d, const struct grcan_timing *timing)
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)
{
struct grcan_priv *pDev = d;
diff --git a/bsps/shared/grlib/can/grcan_internal.h b/bsps/shared/grlib/can/grcan_internal.h
new file mode 100644
index 0000000000..86ccda1997
--- /dev/null
+++ b/bsps/shared/grlib/can/grcan_internal.h
@@ -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;
+}
diff --git a/bsps/shared/grlib/can/grcanfd.c b/bsps/shared/grlib/can/grcanfd.c
new file mode 100644
index 0000000000..00eb4b6432
--- /dev/null
+++ b/bsps/shared/grlib/can/grcanfd.c
@@ -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(&regs->rx0wr);
+ rp = READ_REG(&regs->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(&regs->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(&regs->tx0rd);
+ wp = READ_REG(&regs->tx0wr);
+ size = READ_REG(&regs->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;
+}