summaryrefslogtreecommitdiffstats
path: root/c/src/lib/libbsp/sparc/shared/can/grcan.c
diff options
context:
space:
mode:
Diffstat (limited to 'c/src/lib/libbsp/sparc/shared/can/grcan.c')
-rw-r--r--c/src/lib/libbsp/sparc/shared/can/grcan.c203
1 files changed, 166 insertions, 37 deletions
diff --git a/c/src/lib/libbsp/sparc/shared/can/grcan.c b/c/src/lib/libbsp/sparc/shared/can/grcan.c
index a21d71f01a..0eb08585be 100644
--- a/c/src/lib/libbsp/sparc/shared/can/grcan.c
+++ b/c/src/lib/libbsp/sparc/shared/can/grcan.c
@@ -26,7 +26,6 @@
#include <grcan.h>
#include <ambapp.h>
-#include <pci.h>
#define WRAP_AROUND_TX_MSGS 1
#define WRAP_AROUND_RX_MSGS 2
@@ -43,12 +42,16 @@
#define RX_BUF_SIZE ((3*BLOCK_SIZE)*16)
#endif
+#ifndef IRQ_GLOBAL_PREPARE
+ #define IRQ_GLOBAL_PREPARE(level) rtems_interrupt_level level
+#endif
+
#ifndef IRQ_GLOBAL_DISABLE
- #define IRQ_GLOBAL_DISABLE() sparc_disable_interrupts()
+ #define IRQ_GLOBAL_DISABLE(level) rtems_interrupt_disable(level)
#endif
#ifndef IRQ_GLOBAL_ENABLE
- #define IRQ_GLOBAL_ENABLE() sparc_enable_interrupts()
+ #define IRQ_GLOBAL_ENABLE(level) rtems_interrupt_enable(level)
#endif
#ifndef IRQ_CLEAR_PENDING
@@ -90,6 +93,10 @@
#define GRCAN_DEFAULT_BAUD 500000
#endif
+#ifndef GRCAN_SAMPLING_POINT
+#define GRCAN_SAMPLING_POINT 80
+#endif
+
/* Uncomment for debug output */
/****************** DEBUG Definitions ********************/
#define DBG_IOCTRL 1
@@ -97,9 +104,10 @@
#define DBG_RX 4
#define DEBUG_FLAGS (DBG_IOCTRL | DBG_RX | DBG_TX )
-/*#define DEBUG
-#define DEBUGFUNCS*/
-
+/*
+#define DEBUG
+#define DEBUGFUNCS
+*/
#include <debug_defs.h>
/*********************************************************/
@@ -250,7 +258,7 @@ static void grcan_interrupt(struct grcan_priv *pDev);
#ifdef GRCAN_REG_BYPASS_CACHE
#define READ_REG(address) _grcan_read_nocache((unsigned int)(address))
#else
-#define READ_REG(address) (*(unsigned int *)(address))
+#define READ_REG(address) (*(volatile unsigned int *)(address))
#endif
#ifdef GRCAN_DMA_BYPASS_CACHE
@@ -266,8 +274,8 @@ static unsigned char __inline__ _grcan_read_nocache_byte(unsigned int address)
return tmp;
}
#else
-#define READ_DMA_WORD(address) (*(unsigned int *)(address))
-#define READ_DMA_BYTE(address) (*(unsigned char *)(address))
+#define READ_DMA_WORD(address) (*(volatile unsigned int *)(address))
+#define READ_DMA_BYTE(address) (*(volatile unsigned char *)(address))
#endif
#if defined(GRCAN_REG_BYPASS_CACHE) || defined(GRCAN_DMA_BYPASS_CACHE)
@@ -293,6 +301,8 @@ static void __inline__ grcan_hw_reset(struct grcan_regs *regs)
static rtems_device_driver grcan_start(struct grcan_priv *pDev)
{
unsigned int tmp;
+ IRQ_GLOBAL_PREPARE(oldLevel);
+
FUNCDBG();
/* Check that memory has been allocated successfully */
@@ -335,11 +345,11 @@ static rtems_device_driver grcan_start(struct grcan_priv *pDev)
pDev->regs->imr = 0x1601f;
/* Enable routing of the IRQs */
- IRQ_GLOBAL_DISABLE();
+ IRQ_GLOBAL_DISABLE(oldLevel);
IRQ_UNMASK(pDev->irq+GRCAN_IRQ_TXSYNC);
IRQ_UNMASK(pDev->irq+GRCAN_IRQ_RXSYNC);
IRQ_UNMASK(pDev->irq+GRCAN_IRQ_IRQ);
- IRQ_GLOBAL_ENABLE();
+ IRQ_GLOBAL_ENABLE(oldLevel);
/* Reset some software data */
/*pDev->txerror = 0;
@@ -507,13 +517,121 @@ static int grcan_hw_tx_ongoing(struct grcan_regs *regs)
return READ_REG(&regs->tx0ctrl) & GRCAN_TXCTRL_ONGOING;
};
+
+#define MIN_TSEG1 1
+#define MIN_TSEG2 2
+#define MAX_TSEG1 14
+#define MAX_TSEG2 8
+
static int grcan_calc_timing(
unsigned int baud, /* The requested BAUD to calculate timing for */
unsigned int core_hz, /* Frequency in Hz of GRCAN Core */
+ unsigned int sampl_pt,
struct grcan_timing *timing /* result is placed here */
)
{
- return -1; /* not implemented yet */
+ int best_error = 1000000000;
+ int error;
+ int best_tseg=0, best_brp=0, best_rate=0, brp=0;
+ int tseg=0, tseg1=0, tseg2=0;
+ int sjw = 1;
+
+ /* Default to 90% */
+ if ( (sampl_pt < 50) || (sampl_pt>99) ){
+ sampl_pt = GRCAN_SAMPLING_POINT;
+ }
+
+ if ( (baud<5000) || (baud>1000000) ){
+ /* invalid speed mode */
+ return -1;
+ }
+
+ /* find best match, return -2 if no good reg
+ * combination is available for this frequency
+ */
+
+ /* some heuristic specials */
+ if (baud > ((1000000 + 500000) / 2))
+ sampl_pt = 75;
+
+ if (baud < ((12500 + 10000) / 2))
+ sampl_pt = 75;
+
+ /* tseg even = round down, odd = round up */
+ for (tseg = (MIN_TSEG1 + MIN_TSEG2 + 2) * 2;
+ tseg <= (MAX_TSEG2 + MAX_TSEG1 + 2) * 2 + 1;
+ tseg++)
+ {
+ brp = core_hz / ((1 + tseg / 2) * baud) + tseg % 2;
+ if ((brp <= 0) ||
+ ( (brp > 256*1) && (brp <= 256*2) && (brp&0x1) ) ||
+ ( (brp > 256*2) && (brp <= 256*4) && (brp&0x3) ) ||
+ ( (brp > 256*4) && (brp <= 256*8) && (brp&0x7) ) ||
+ (brp > 256*8)
+ )
+ continue;
+
+ error = baud - core_hz / (brp * (1 + tseg / 2));
+ if (error < 0)
+ {
+ error = -error;
+ }
+
+ if (error <= best_error)
+ {
+ best_error = error;
+ best_tseg = tseg/2;
+ best_brp = brp-1;
+ best_rate = core_hz/(brp*(1+tseg/2));
+ }
+ }
+
+ if (best_error && (baud / best_error < 10))
+ {
+ return -2;
+ }else if ( !timing )
+ return 0; /* nothing to store result in, but a valid bitrate can be calculated */
+
+ tseg2 = best_tseg - (sampl_pt * (best_tseg + 1)) / 100;
+
+ if (tseg2 < MIN_TSEG2)
+ {
+ tseg2 = MIN_TSEG2;
+ }
+
+ if (tseg2 > MAX_TSEG2)
+ {
+ tseg2 = MAX_TSEG2;
+ }
+
+ tseg1 = best_tseg - tseg2 - 2;
+
+ if (tseg1 > MAX_TSEG1)
+ {
+ tseg1 = MAX_TSEG1;
+ tseg2 = best_tseg - tseg1 - 2;
+ }
+
+ /* Get scaler and BRP from pseudo BRP */
+ if ( best_brp <= 256 ){
+ timing->scaler = best_brp;
+ timing->bpr = 0;
+ }else if ( best_brp <= 256*2 ){
+ timing->scaler = ((best_brp+1)>>1) -1;
+ timing->bpr = 1;
+ }else if ( best_brp <= 256*4 ){
+ timing->scaler = ((best_brp+1)>>2) -1;
+ timing->bpr = 2;
+ }else{
+ timing->scaler = ((best_brp+1)>>3) -1;
+ timing->bpr = 3;
+ }
+
+ timing->ps1 = tseg1+1;
+ timing->ps2 = tseg2;
+ timing->rsj = sjw;
+
+ return 0;
}
static unsigned int grcan_hw_read_try(
@@ -663,7 +781,6 @@ static unsigned int grcan_hw_write_try(
i=0;
while( (grcan_hw_tx_ongoing(regs)) && i<1000 ){
i++;
- printk("ongoing tx\n");
}
regs->tx0wr = (unsigned int)dest - addr; /* Update write pointer */
regs->tx0ctrl = GRCAN_TXCTRL_ENABLE; /* ENABLE_TX_CHANNEL */
@@ -678,9 +795,16 @@ static int grcan_wait_rxdata(
unsigned int wp, rp, size, irq;
unsigned int irq_trunk, dataavail;
int wait;
+ IRQ_GLOBAL_PREPARE(oldLevel);
FUNCDBG();
-
+
+ /*** block until receive IRQ received
+ * Set up a valid IRQ point so that an IRQ is received
+ * when one or more messages are received
+ */
+ IRQ_GLOBAL_DISABLE(oldLevel);
+
size = READ_REG(&pDev->regs->rx0size);
rp = READ_REG(&pDev->regs->rx0rd);
wp = READ_REG(&pDev->regs->rx0wr);
@@ -692,12 +816,6 @@ static int grcan_wait_rxdata(
irq_trunk = irq-size;
}else
irq_trunk = irq;
-
- /*** block until receive IRQ received
- * Set up a valid IRQ point so that an IRQ is received
- * when one or more messages are received
- */
- IRQ_GLOBAL_DISABLE();
/* init IRQ HW */
pDev->regs->rx0irq = irq_trunk;
@@ -718,7 +836,7 @@ static int grcan_wait_rxdata(
/* enough message has been received, abort sleep - don't unmask interrupt */
wait=0;
}
- IRQ_GLOBAL_ENABLE();
+ IRQ_GLOBAL_ENABLE(oldLevel);
/* Wait for IRQ to fire only if has been triggered */
if ( wait ){
@@ -744,14 +862,17 @@ static int grcan_wait_txspace(
int wait;
unsigned int irq, rp, wp, size, space_left;
unsigned int irq_trunk;
+ IRQ_GLOBAL_PREPARE(oldLevel);
DBGC(DBG_TX,"\n");
/*FUNCDBG();*/
+ IRQ_GLOBAL_DISABLE(oldLevel);
+
+ /*pDev->regs->tx0ctrl = GRCAN_TXCTRL_ENABLE;*/
+
size = READ_REG(&pDev->regs->tx0size);
wp = READ_REG(&pDev->regs->tx0wr);
-
- IRQ_GLOBAL_DISABLE();
rp = READ_REG(&pDev->regs->tx0rd);
@@ -788,11 +909,11 @@ static int grcan_wait_txspace(
/* There are enough room in buffer, abort wait - don't unmask interrupt */
wait=0;
}
- IRQ_GLOBAL_ENABLE();
+ IRQ_GLOBAL_ENABLE(oldLevel);
/* Wait for IRQ to fire only if it has been triggered */
if ( wait ){
- if ( rtems_semaphore_obtain(pDev->tx_sem, RTEMS_WAIT, RTEMS_NO_TIMEOUT) ==
+ if ( rtems_semaphore_obtain(pDev->tx_sem, RTEMS_WAIT, 100) ==
RTEMS_UNSATISFIED ){
/* Device driver has flushed us, this may be due to another thread has
* closed the device, this is to avoid deadlock */
@@ -808,6 +929,7 @@ static int grcan_tx_flush(struct grcan_priv *pDev)
{
int wait;
unsigned int rp, wp;
+ IRQ_GLOBAL_PREPARE(oldLevel);
FUNCDBG();
/* loop until all data in circular buffer has been read by hw.
@@ -817,7 +939,7 @@ static int grcan_tx_flush(struct grcan_priv *pDev)
*/
while ( (wp=READ_REG(&pDev->regs->tx0wr)) != (rp=READ_REG(&pDev->regs->tx0rd)) ) {
/* Wait for TX empty IRQ */
- IRQ_GLOBAL_DISABLE();
+ IRQ_GLOBAL_DISABLE(oldLevel);
/* Clear pending TXEmpty IRQ */
pDev->regs->picr = GRCAN_TXEMPTY_IRQ;
@@ -829,7 +951,7 @@ static int grcan_tx_flush(struct grcan_priv *pDev)
/* TX fifo is empty */
wait = 0;
}
- IRQ_GLOBAL_ENABLE();
+ IRQ_GLOBAL_ENABLE(oldLevel);
if ( !wait )
break;
@@ -932,6 +1054,7 @@ static rtems_device_driver grcan_initialize(
rtems_status_code status;
char fs_name[20];
unsigned int sys_freq_hz;
+ unsigned int deviceid = GAISLER_GRHCAN;
printk("grcan_initialize()\n\r");
@@ -939,10 +1062,16 @@ static rtems_device_driver grcan_initialize(
/* find GRCAN cores */
if ( !grcan_cores ) {
- grcan_core_cnt = amba_get_number_apbslv_devices(amba_bus,VENDOR_GAISLER,GAISLER_GRHCAN);
+ grcan_core_cnt = amba_get_number_apbslv_devices(amba_bus,VENDOR_GAISLER,deviceid);
+ if ( grcan_core_cnt < 1 ){
+ deviceid = GAISLER_GRCAN;
+ grcan_core_cnt = amba_get_number_apbslv_devices(amba_bus,VENDOR_GAISLER,deviceid);
+ if ( grcan_core_cnt < 1 ) {
+ DBG("GRCAN: Using AMBA Plug&Play, found %d cores\n",grcan_core_cnt);
+ return RTEMS_UNSATISFIED;
+ }
+ }
DBG("GRCAN: Using AMBA Plug&Play, found %d cores\n",grcan_core_cnt);
- if ( grcan_core_cnt < 1 )
- return RTEMS_UNSATISFIED;
}
#ifdef GRCAN_MAX_CORENR
@@ -1003,7 +1132,7 @@ static rtems_device_driver grcan_initialize(
/* Find core address & IRQ */
if ( !grcan_cores ) {
- amba_find_next_apbslv(amba_bus,VENDOR_GAISLER,GAISLER_GRHCAN,&dev,minor);
+ amba_find_next_apbslv(amba_bus,VENDOR_GAISLER,deviceid,&dev,minor);
pDev->irq = dev.irq;
pDev->regs = (struct grcan_regs *)dev.start;
}else{
@@ -1011,7 +1140,6 @@ static rtems_device_driver grcan_initialize(
pDev->regs = (struct grcan_regs *)grcan_cores[minor].base_address;
}
- DBG("Registering GRCAN core at [0x%x] irq %d, minor %d as %s\n",pDev->regs,pDev->irq,minor,fs_name);
printk("Registering GRCAN core at [0x%x] irq %d, minor %d as %s\n\r",pDev->regs,pDev->irq,minor,fs_name);
status = rtems_io_register_name(fs_name, major, 0);
@@ -1121,7 +1249,7 @@ static rtems_device_driver grcan_open(rtems_device_major_number major, rtems_dev
pDev->sfilter.code = 0x00000000;
/* Calculate default timing register values */
- grcan_calc_timing(GRCAN_DEFAULT_BAUD,pDev->corefreq_hz,&pDev->config.timing);
+ grcan_calc_timing(GRCAN_DEFAULT_BAUD,pDev->corefreq_hz,GRCAN_SAMPLING_POINT,&pDev->config.timing);
if ( grcan_alloc_buffers(pDev,1,1) ) {
ret=RTEMS_NO_MEMORY;
@@ -1333,6 +1461,7 @@ static rtems_device_driver grcan_ioctl(rtems_device_major_number major, rtems_de
rtems_device_driver status;
struct grcan_stats *stats;
struct grcan_filter *filter;
+ IRQ_GLOBAL_PREPARE(oldLevel);
FUNCDBG();
@@ -1448,9 +1577,9 @@ static rtems_device_driver grcan_ioctl(rtems_device_major_number major, rtems_de
break;
case GRCAN_IOC_CLR_STATS:
- IRQ_GLOBAL_DISABLE();
+ IRQ_GLOBAL_DISABLE(oldLevel);
memset(&pDev->stats,0,sizeof(struct grcan_stats));
- IRQ_GLOBAL_ENABLE();
+ IRQ_GLOBAL_ENABLE(oldLevel);
break;
case GRCAN_IOC_SET_SPEED:
@@ -1461,7 +1590,7 @@ static rtems_device_driver grcan_ioctl(rtems_device_major_number major, rtems_de
/* get speed rate from argument */
speed = (unsigned int)ioarg->buffer;
- ret = grcan_calc_timing(pDev->corefreq_hz,speed,&timing);
+ ret = grcan_calc_timing(speed,pDev->corefreq_hz,GRCAN_SAMPLING_POINT,&timing);
if ( ret )
return RTEMS_INVALID_NAME; /* EINVAL */
@@ -1537,7 +1666,7 @@ static rtems_isr grcan_interrupt_handler(rtems_vector_number v)
{
int minor=0;
while ( minor < grcan_core_cnt ){
- if ( grcans[minor].irq == (v+0x10) ){
+ if ( (grcans[minor].irq+0x10) == v ){
grcan_interrupt(&grcans[minor]);
break;
}