summaryrefslogtreecommitdiffstats
path: root/c/src/lib/libbsp/sparc/shared/can/grcan.c
diff options
context:
space:
mode:
authorJoel Sherrill <joel.sherrill@OARcorp.com>2007-11-30 16:48:13 +0000
committerJoel Sherrill <joel.sherrill@OARcorp.com>2007-11-30 16:48:13 +0000
commite3481dcd4f6eef47aa63653cb8071c52ba030250 (patch)
tree6ae743e7c7abc44012202b3a702ae44f4f3b1ce4 /c/src/lib/libbsp/sparc/shared/can/grcan.c
parent2007-11-29 Till Straumann <strauman@slac.stanford.edu> (diff)
downloadrtems-e3481dcd4f6eef47aa63653cb8071c52ba030250.tar.bz2
2007-11-30 Daniel Hellstrom <daniel@gaisler.com>
* shared/can/grcan.c, shared/can/grcan_rasta.c, shared/include/ambapp.h: GRCAN CAN driver. Fixes Interrupt enabling/disabling in the driver, interrupt may not be restored correctly. Implements the baud rate calculation routine. Removed unnecessary printk. Fixed scanning to support GRCAN and GRHCAN hardware. Added GRCAN device number to ambapp.h.
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;
}