forked from Imagelibrary/rtems
711 lines
20 KiB
C
711 lines
20 KiB
C
/*
|
|
* SatCAN FPGA driver
|
|
*
|
|
* COPYRIGHT (c) 2008.
|
|
* 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 <rtems/libio.h>
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <bsp.h>
|
|
#include <rtems/bspIo.h> /* printk */
|
|
|
|
#include <grlib/satcan.h>
|
|
#include <grlib/ambapp.h>
|
|
|
|
#include <grlib/grlib_impl.h>
|
|
|
|
#ifndef GAISLER_SATCAN
|
|
#define GAISLER_SATCAN 0x080
|
|
#endif
|
|
|
|
#if !defined(SATCAN_DEVNAME)
|
|
#undef SATCAN_DEVNAME
|
|
#define SATCAN_DEVNAME "/dev/satcan"
|
|
#endif
|
|
|
|
/* Enable debug output? */
|
|
/* #define DEBUG */
|
|
|
|
#ifdef DEBUG
|
|
#define DBG(x...) printk(x)
|
|
#else
|
|
#define DBG(x...)
|
|
#endif
|
|
|
|
|
|
/* Defines related to DMA */
|
|
#define ALIGN_2KMEM 32*1024
|
|
#define ALIGN_8KMEM 128*1024
|
|
|
|
#define OFFSET_2K_LOW_POS 15
|
|
#define OFFSET_8K_LOW_POS 17
|
|
|
|
#define DMA_2K_DATA_SELECT (1 << 14)
|
|
#define DMA_8K_DATA_SELECT (1 << 16)
|
|
|
|
#define DMA_2K_DATA_OFFSET 16*1024
|
|
#define DMA_8K_DATA_OFFSET 64*1024
|
|
|
|
/* Core register structures and defines */
|
|
|
|
/* Indexes to SatCAN registers in satcan array are declared in satcan.h*/
|
|
/* Fields for some of the SatCAN FPGA registers */
|
|
|
|
/* CmdReg0 */
|
|
#define CAN_TODn_Int_sel (1 << 5)
|
|
|
|
/* CmdReg1 */
|
|
#define Sel_2k_8kN (1 << 0)
|
|
|
|
/* Read FIFO */
|
|
#define FIFO_Full (1 << 8)
|
|
#define FIFO_Empty (1 << 9)
|
|
|
|
/* DMA Ch_Enable */
|
|
#define DMA_AutoInitDmaTx (1 << 3)
|
|
#define DMA_EnTx2 (1 << 2)
|
|
#define DMA_EnTx1 (1 << 1)
|
|
#define DMA_EnRx (1 << 0)
|
|
|
|
/* SatCAN wrapper register fields */
|
|
#define CTRL_BT_P 9
|
|
#define CTRL_NODENO_P 5
|
|
#define CTRL_DIS (1 << 2)
|
|
#define CTRL_DPS_P 1
|
|
#define CTRL_RST (1 << 0)
|
|
|
|
#define IRQ_AHB (1 << 8)
|
|
#define IRQ_PPS (1 << 7)
|
|
#define IRQ_M5 (1 << 6)
|
|
#define IRQ_M4 (1 << 5)
|
|
#define IRQ_M3 (1 << 4)
|
|
#define IRQ_M2 (1 << 3)
|
|
#define IRQ_M1 (1 << 2)
|
|
#define IRQ_SYNC (1 << 1)
|
|
#define IRQ_CAN (1 << 0)
|
|
|
|
#define MSK_AHB (1 << 8)
|
|
#define MSK_PPS (1 << 7)
|
|
#define MSK_M5 (1 << 6)
|
|
#define MSK_M4 (1 << 5)
|
|
#define MSK_M3 (1 << 4)
|
|
#define MSK_M2 (1 << 3)
|
|
#define MSK_M1 (1 << 2)
|
|
#define MSK_SYNC (1 << 1)
|
|
#define MSK_CAN (1 << 0)
|
|
|
|
|
|
|
|
struct satcan_regs {
|
|
volatile unsigned int satcan[32];
|
|
volatile unsigned int ctrl;
|
|
volatile unsigned int irqpend;
|
|
volatile unsigned int irqmask;
|
|
volatile unsigned int membase;
|
|
};
|
|
|
|
|
|
struct satcan_priv {
|
|
/* config */
|
|
void *dmaptr;
|
|
unsigned char *alptr;
|
|
satcan_config *cfg;
|
|
|
|
/* driver state */
|
|
rtems_id devsem;
|
|
rtems_id txsem;
|
|
int open;
|
|
int txactive;
|
|
int dmaen;
|
|
int doff;
|
|
rtems_interval timeout;
|
|
int dmamode;
|
|
};
|
|
|
|
static struct satcan_regs *regs;
|
|
static struct satcan_priv *priv;
|
|
|
|
static rtems_device_driver satcan_ioctl(rtems_device_major_number major, rtems_device_minor_number minor, void *arg);
|
|
static rtems_device_driver satcan_write(rtems_device_major_number major, rtems_device_minor_number minor, void *arg);
|
|
static rtems_device_driver satcan_read(rtems_device_major_number major, rtems_device_minor_number minor, void *arg);
|
|
static rtems_device_driver satcan_close(rtems_device_major_number major, rtems_device_minor_number minor, void *arg);
|
|
static rtems_device_driver satcan_open(rtems_device_major_number major, rtems_device_minor_number minor, void *arg);
|
|
static rtems_device_driver satcan_initialize(rtems_device_major_number major, rtems_device_minor_number unused, void *arg);
|
|
|
|
|
|
/*
|
|
* almalloc: allocate memory area of size sz aligned on sz boundary
|
|
* alptr: Utilized to return aligned pointer
|
|
* ptr: Unaligned pointer
|
|
* sz: Size of memory area
|
|
*/
|
|
static void almalloc(unsigned char **alptr, void **ptr, int sz)
|
|
{
|
|
*ptr = rtems_calloc(1,2*sz);
|
|
*alptr = (unsigned char *) (((int)*ptr+sz) & ~(sz-1));
|
|
}
|
|
|
|
static rtems_isr satcan_interrupt_handler(void *v)
|
|
{
|
|
unsigned int irq;
|
|
unsigned int fifo;
|
|
|
|
irq = regs->irqpend;
|
|
|
|
if (irq & IRQ_AHB && priv->cfg->ahb_irq_callback) {
|
|
priv->cfg->ahb_irq_callback();
|
|
}
|
|
if (irq & IRQ_PPS && priv->cfg->pps_irq_callback) {
|
|
priv->cfg->pps_irq_callback();
|
|
}
|
|
if (irq & IRQ_M5 && priv->cfg->m5_irq_callback) {
|
|
priv->cfg->m5_irq_callback();
|
|
}
|
|
if (irq & IRQ_M4 && priv->cfg->m4_irq_callback) {
|
|
priv->cfg->m4_irq_callback();
|
|
}
|
|
if (irq & IRQ_M3 && priv->cfg->m3_irq_callback) {
|
|
priv->cfg->m3_irq_callback();
|
|
}
|
|
if (irq & IRQ_M2 && priv->cfg->m2_irq_callback) {
|
|
priv->cfg->m2_irq_callback();
|
|
}
|
|
if (irq & IRQ_M1 && priv->cfg->m1_irq_callback) {
|
|
priv->cfg->m1_irq_callback();
|
|
}
|
|
if (irq & IRQ_SYNC && priv->cfg->sync_irq_callback) {
|
|
priv->cfg->sync_irq_callback();
|
|
}
|
|
if (irq & IRQ_CAN) {
|
|
fifo = regs->satcan[SATCAN_FIFO];
|
|
if (!(fifo & FIFO_Empty) && priv->txactive &&
|
|
(((fifo & 0xff) == SATCAN_IRQ_EOD1) || ((fifo & 0xff) == SATCAN_IRQ_EOD2))) {
|
|
rtems_semaphore_release(priv->txsem);
|
|
}
|
|
if (priv->cfg->can_irq_callback)
|
|
priv->cfg->can_irq_callback(fifo);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
static rtems_device_driver satcan_ioctl(rtems_device_major_number major, rtems_device_minor_number minor, void *arg)
|
|
{
|
|
rtems_libio_ioctl_args_t *ioarg = (rtems_libio_ioctl_args_t*)arg;
|
|
int *value;
|
|
rtems_interval *timeout;
|
|
satcan_regmod *regmod;
|
|
|
|
DBG("SatCAN: IOCTL %d\n\r", ioarg->command);
|
|
|
|
ioarg->ioctl_return = 0;
|
|
switch(ioarg->command) {
|
|
case SATCAN_IOC_DMA_2K:
|
|
DBG("SatCAN: ioctl: setting 2K DMA mode\n\r");
|
|
free(priv->dmaptr);
|
|
almalloc(&priv->alptr, &priv->dmaptr, ALIGN_2KMEM);
|
|
if (priv->dmaptr == NULL) {
|
|
printk("SatCAN: Failed to allocate DMA memory\n\r");
|
|
return RTEMS_NO_MEMORY;
|
|
}
|
|
|
|
regs->membase = (unsigned int)priv->alptr;
|
|
regs->satcan[SATCAN_RAM_BASE] = (unsigned int)priv->alptr >> OFFSET_2K_LOW_POS;
|
|
regs->satcan[SATCAN_CMD1] = regs->satcan[SATCAN_CMD1] | Sel_2k_8kN;
|
|
break;
|
|
|
|
case SATCAN_IOC_DMA_8K:
|
|
DBG("SatCAN: ioctl: setting 8K DMA mode\n\r");
|
|
free(priv->dmaptr);
|
|
almalloc(&priv->alptr, &priv->dmaptr, ALIGN_8KMEM);
|
|
if (priv->dmaptr == NULL) {
|
|
printk("SatCAN: Failed to allocate DMA memory\n\r");
|
|
return RTEMS_NO_MEMORY;
|
|
}
|
|
|
|
regs->membase = (unsigned int)priv->alptr;
|
|
regs->satcan[SATCAN_RAM_BASE] = (unsigned int)priv->alptr >> OFFSET_8K_LOW_POS;
|
|
regs->satcan[SATCAN_CMD1] = regs->satcan[SATCAN_CMD1] & ~Sel_2k_8kN;
|
|
break;
|
|
|
|
case SATCAN_IOC_GET_REG:
|
|
/* Get regmod structure from argument */
|
|
regmod = (satcan_regmod*)ioarg->buffer;
|
|
DBG("SatCAN: ioctl: getting register %d\n\r", regmod->reg);
|
|
if (regmod->reg <= SATCAN_FILTER_STOP)
|
|
regmod->val = regs->satcan[regmod->reg];
|
|
else if (regmod->reg == SATCAN_WCTRL)
|
|
regmod->val = regs->ctrl;
|
|
else if (regmod->reg == SATCAN_WIPEND)
|
|
regmod->val = regs->irqpend;
|
|
else if (regmod->reg == SATCAN_WIMASK)
|
|
regmod->val = regs->irqmask;
|
|
else if (regmod->reg == SATCAN_WAHBADDR)
|
|
regmod->val = regs->membase;
|
|
else
|
|
return RTEMS_INVALID_NAME;
|
|
break;
|
|
|
|
case SATCAN_IOC_SET_REG:
|
|
/* Get regmod structure from argument */
|
|
regmod = (satcan_regmod*)ioarg->buffer;
|
|
DBG("SatCAN: ioctl: setting register %d, value %x\n\r",
|
|
regmod->reg, regmod->val);
|
|
if (regmod->reg <= SATCAN_FILTER_STOP)
|
|
regs->satcan[regmod->reg] = regmod->val;
|
|
else if (regmod->reg == SATCAN_WCTRL)
|
|
regs->ctrl = regmod->val;
|
|
else if (regmod->reg == SATCAN_WIPEND)
|
|
regs->irqpend = regmod->val;
|
|
else if (regmod->reg == SATCAN_WIMASK)
|
|
regs->irqmask = regmod->val;
|
|
else if (regmod->reg == SATCAN_WAHBADDR)
|
|
regs->membase = regmod->val;
|
|
else
|
|
return RTEMS_INVALID_NAME;
|
|
break;
|
|
|
|
case SATCAN_IOC_OR_REG:
|
|
/* Get regmod structure from argument */
|
|
regmod = (satcan_regmod*)ioarg->buffer;
|
|
DBG("SatCAN: ioctl: or:ing register %d, with value %x\n\r",
|
|
regmod->reg, regmod->val);
|
|
if (regmod->reg <= SATCAN_FILTER_STOP)
|
|
regs->satcan[regmod->reg] |= regmod->val;
|
|
else if (regmod->reg == SATCAN_WCTRL)
|
|
regs->ctrl |= regmod->val;
|
|
else if (regmod->reg == SATCAN_WIPEND)
|
|
regs->irqpend |= regmod->val;
|
|
else if (regmod->reg == SATCAN_WIMASK)
|
|
regs->irqmask |= regmod->val;
|
|
else if (regmod->reg == SATCAN_WAHBADDR)
|
|
regs->membase |= regmod->val;
|
|
else
|
|
return RTEMS_INVALID_NAME;
|
|
break;
|
|
|
|
case SATCAN_IOC_AND_REG:
|
|
/* Get regmod structure from argument */
|
|
regmod = (satcan_regmod*)ioarg->buffer;
|
|
DBG("SatCAN: ioctl: masking register %d, with value %x\n\r",
|
|
regmod->reg, regmod->val);
|
|
if (regmod->reg <= SATCAN_FILTER_STOP)
|
|
regs->satcan[regmod->reg] &= regmod->val;
|
|
else if (regmod->reg == SATCAN_WCTRL)
|
|
regs->ctrl &= regmod->val;
|
|
else if (regmod->reg == SATCAN_WIPEND)
|
|
regs->irqpend &= regmod->val;
|
|
else if (regmod->reg == SATCAN_WIMASK)
|
|
regs->irqmask &= regmod->val;
|
|
else if (regmod->reg == SATCAN_WAHBADDR)
|
|
regs->membase &= regmod->val;
|
|
else
|
|
return RTEMS_INVALID_NAME;
|
|
break;
|
|
|
|
case SATCAN_IOC_EN_TX1_DIS_TX2:
|
|
priv->dmaen = SATCAN_DMA_ENABLE_TX1;
|
|
break;
|
|
|
|
case SATCAN_IOC_EN_TX2_DIS_TX1:
|
|
priv->dmaen = SATCAN_DMA_ENABLE_TX2;
|
|
break;
|
|
|
|
case SATCAN_IOC_GET_DMA_MODE:
|
|
value = (int*)ioarg->buffer;
|
|
*value = priv->dmamode;
|
|
break;
|
|
|
|
case SATCAN_IOC_SET_DMA_MODE:
|
|
value = (int*)ioarg->buffer;
|
|
if (*value != SATCAN_DMA_MODE_USER && *value != SATCAN_DMA_MODE_SYSTEM) {
|
|
DBG("SatCAN: ioctl: invalid DMA mode\n\r");
|
|
return RTEMS_INVALID_NAME;
|
|
}
|
|
priv->dmamode = *value;
|
|
break;
|
|
|
|
case SATCAN_IOC_ACTIVATE_DMA:
|
|
if (priv->dmamode != SATCAN_DMA_MODE_USER) {
|
|
DBG("SatCAN: ioctl: ACTIVATE_DMA: not in user mode\n\r");
|
|
return RTEMS_INVALID_NAME;
|
|
}
|
|
value = (int*)ioarg->buffer;
|
|
if (*value != SATCAN_DMA_ENABLE_TX1 && *value != SATCAN_DMA_ENABLE_TX2) {
|
|
DBG("SatCAN: ioctl: ACTIVATE_DMA: Illegal channel\n\r");
|
|
return RTEMS_INVALID_NAME;
|
|
}
|
|
regs->satcan[SATCAN_DMA] |= *value << 1;
|
|
break;
|
|
|
|
case SATCAN_IOC_DEACTIVATE_DMA:
|
|
if (priv->dmamode != SATCAN_DMA_MODE_USER) {
|
|
DBG("SatCAN: ioctl: DEACTIVATE_DMA: not in user mode\n\r");
|
|
return RTEMS_INVALID_NAME;
|
|
}
|
|
value = (int*)ioarg->buffer;
|
|
if (*value != SATCAN_DMA_ENABLE_TX1 && *value != SATCAN_DMA_ENABLE_TX2) {
|
|
DBG("SatCAN: ioctl: DEACTIVATE_DMA: Illegal channel\n\r");
|
|
return RTEMS_INVALID_NAME;
|
|
}
|
|
regs->satcan[SATCAN_DMA] &= ~(*value << 1);
|
|
break;
|
|
|
|
case SATCAN_IOC_GET_DOFFSET:
|
|
value = (int*)ioarg->buffer;
|
|
*value = priv->doff;
|
|
break;
|
|
|
|
case SATCAN_IOC_SET_DOFFSET:
|
|
value = (int*)ioarg->buffer;
|
|
priv->doff = *value;
|
|
break;
|
|
|
|
case SATCAN_IOC_GET_TIMEOUT:
|
|
timeout = (rtems_interval*)ioarg->buffer;
|
|
*timeout = priv->timeout;
|
|
break;
|
|
|
|
case SATCAN_IOC_SET_TIMEOUT:
|
|
timeout = (rtems_interval*)ioarg->buffer;
|
|
priv->timeout = *timeout;
|
|
break;
|
|
|
|
default:
|
|
return RTEMS_NOT_DEFINED;
|
|
}
|
|
|
|
return RTEMS_SUCCESSFUL;
|
|
}
|
|
|
|
static rtems_device_driver satcan_write(rtems_device_major_number major, rtems_device_minor_number minor, void *arg)
|
|
{
|
|
int i;
|
|
int doff;
|
|
int msgindex;
|
|
int messages;
|
|
rtems_libio_rw_args_t *rw_args=(rtems_libio_rw_args_t *) arg;
|
|
satcan_msg *msgs;
|
|
rtems_status_code status;
|
|
|
|
DBG("SatCAN: Writing %d bytes from %p\n\r",rw_args->count,rw_args->buffer);
|
|
|
|
if ((rw_args->count < sizeof(satcan_msg)) || (!rw_args->buffer)) {
|
|
DBG("SatCAN: write: returning EINVAL\n\r");
|
|
return RTEMS_INVALID_NAME; /* EINVAL */
|
|
}
|
|
|
|
messages = rw_args->count / sizeof(satcan_msg);
|
|
msgs = (satcan_msg*)rw_args->buffer;
|
|
|
|
/* Check that size matches any number of satcan_msg */
|
|
if (rw_args->count % sizeof(satcan_msg)) {
|
|
DBG("SatCAN: write: count can not be evenly divided with satcan_msg size\n\r");
|
|
return RTEMS_INVALID_NAME; /* EINVAL */
|
|
}
|
|
|
|
|
|
/* DMA channel must be set if we are in system DMA mode */
|
|
DBG("SatCAN: write: dma channel select is %x\n\r", priv->dmaen);
|
|
if (!priv->dmaen && priv->dmamode == SATCAN_DMA_MODE_SYSTEM)
|
|
return RTEMS_INVALID_NAME; /* EINVAL */
|
|
|
|
/* DMA must not be active */
|
|
if (regs->satcan[SATCAN_DMA] & (DMA_EnTx1 | DMA_EnTx2 | DMA_AutoInitDmaTx)) {
|
|
DBG("SatCAN: write: DMA was active\n\r");
|
|
rw_args->bytes_moved = 0;
|
|
return RTEMS_IO_ERROR; /* EIO */
|
|
}
|
|
|
|
doff = regs->satcan[SATCAN_CMD1] & Sel_2k_8kN ? DMA_2K_DATA_OFFSET : DMA_8K_DATA_OFFSET;
|
|
|
|
for (msgindex = 0; msgindex < messages; msgindex++) {
|
|
/* Place header in DMA area */
|
|
for (i = 0; i < SATCAN_HEADER_SIZE; i++) {
|
|
priv->alptr[priv->doff+8*msgindex+i] = msgs[msgindex].header[i];
|
|
}
|
|
|
|
/* Place data in DMA area */
|
|
for (i = 0; i < SATCAN_PAYLOAD_SIZE; i++)
|
|
priv->alptr[priv->doff+doff+8*msgindex+i] = msgs[msgindex].payload[i];
|
|
}
|
|
|
|
if ((priv->dmaen & SATCAN_DMA_ENABLE_TX1) || priv->dmamode == SATCAN_DMA_MODE_USER) {
|
|
regs->satcan[SATCAN_DMA_TX_1_CUR] = 0;
|
|
regs->satcan[SATCAN_DMA_TX_1_END] = messages<<3;
|
|
}
|
|
|
|
if ((priv->dmaen & SATCAN_DMA_ENABLE_TX2) || priv->dmamode == SATCAN_DMA_MODE_USER) {
|
|
regs->satcan[SATCAN_DMA_TX_2_CUR] = 0;
|
|
regs->satcan[SATCAN_DMA_TX_2_END] = messages<<3;
|
|
}
|
|
|
|
/* If we are in DMA user mode we are done here, otherwise we block */
|
|
if (priv->dmamode == SATCAN_DMA_MODE_SYSTEM) {
|
|
priv->txactive = 1;
|
|
|
|
/* Enable DMA */
|
|
regs->satcan[SATCAN_DMA] |= priv->dmaen << 1;
|
|
|
|
/* Wait for TX interrupt */
|
|
status = rtems_semaphore_obtain(priv->txsem, RTEMS_WAIT, priv->timeout);
|
|
|
|
priv->txactive = 0;
|
|
|
|
/* Disable activated Tx DMA */
|
|
regs->satcan[SATCAN_DMA] &= ~(priv->dmaen << 1);
|
|
|
|
if (status != RTEMS_SUCCESSFUL) {
|
|
rw_args->bytes_moved = 0;
|
|
return status;
|
|
}
|
|
}
|
|
|
|
rw_args->bytes_moved = rw_args->count;
|
|
|
|
return RTEMS_SUCCESSFUL;
|
|
}
|
|
|
|
static rtems_device_driver satcan_read(rtems_device_major_number major, rtems_device_minor_number minor, void *arg)
|
|
{
|
|
char *buf;
|
|
int i;
|
|
int canid;
|
|
int messages;
|
|
rtems_libio_rw_args_t *rw_args = (rtems_libio_rw_args_t*)arg;
|
|
satcan_msg *ret;
|
|
|
|
/* Check that there is room for the return */
|
|
if (rw_args->count < sizeof(satcan_msg)) {
|
|
DBG("SatCAN: read: length of buffer must be at least %d, current is %d\n\r",
|
|
sizeof(satcan_msg) + sizeof(int), rw_args->count);
|
|
return RTEMS_INVALID_NAME; /* -EINVAL */
|
|
}
|
|
|
|
/* Check that size matches any number of satcan_msg */
|
|
if (rw_args->count % sizeof(satcan_msg)) {
|
|
DBG("SatCAN: read: count can not be evenly divided with satcan_msg size\n\r");
|
|
return RTEMS_INVALID_NAME; /* EINVAL */
|
|
}
|
|
|
|
messages = rw_args->count / sizeof(satcan_msg);
|
|
ret = (satcan_msg*)rw_args->buffer;
|
|
|
|
DBG("SatCAN: read: reading %d messages to %p\n\r", messages, ret);
|
|
|
|
for (i = 0; i < messages; i++) {
|
|
canid = (ret[i].header[1] << 8) | ret[i].header[0];
|
|
|
|
/* Copy message header from DMA header area to buffer */
|
|
buf = (char*)((int)priv->alptr | (canid << 3));
|
|
memcpy(ret[i].header, buf, SATCAN_HEADER_SIZE);
|
|
|
|
DBG("SatCAN: read: copied header from %p to %p\n\r", buf, ret[i].header);
|
|
|
|
/* Clear New Message Marker */
|
|
buf[SATCAN_HEADER_NMM_POS] = 0;
|
|
|
|
/* Copy message payload from DMA data area to buffer */
|
|
buf = (char*)((int)buf |
|
|
(regs->satcan[SATCAN_CMD1] & Sel_2k_8kN ? DMA_2K_DATA_SELECT : DMA_8K_DATA_SELECT));
|
|
memcpy(ret[i].payload, buf, SATCAN_PAYLOAD_SIZE);
|
|
|
|
DBG("SatCAN: read: copied payload from %p to %p\n\r", buf, ret[i].payload);
|
|
}
|
|
rw_args->bytes_moved = rw_args->count;
|
|
|
|
return RTEMS_SUCCESSFUL;
|
|
}
|
|
|
|
|
|
static rtems_device_driver satcan_close(rtems_device_major_number major, rtems_device_minor_number minor, void *arg)
|
|
{
|
|
DBG("SatCAN: Closing %d\n\r",minor);
|
|
|
|
if (priv->open) {
|
|
regs->irqmask = 0;
|
|
regs->satcan[SATCAN_INT_EN] = 0;
|
|
regs->satcan[SATCAN_RX] = 0;
|
|
regs->satcan[SATCAN_DMA] = 0;
|
|
priv->open = 0;
|
|
priv->dmaen = 0;
|
|
priv->doff = 0;
|
|
priv->timeout = RTEMS_NO_TIMEOUT;
|
|
priv->dmamode = SATCAN_DMA_MODE_SYSTEM;
|
|
}
|
|
|
|
return RTEMS_SUCCESSFUL;
|
|
}
|
|
|
|
|
|
static rtems_device_driver satcan_open(rtems_device_major_number major, rtems_device_minor_number minor, void *arg)
|
|
{
|
|
DBG("SatCAN: Opening %d\n\r",minor);
|
|
|
|
rtems_semaphore_obtain(priv->devsem,RTEMS_WAIT, RTEMS_NO_TIMEOUT);
|
|
if (priv->open) {
|
|
rtems_semaphore_release(priv->devsem);
|
|
return RTEMS_RESOURCE_IN_USE; /* EBUSY */
|
|
}
|
|
priv->open = 1;
|
|
rtems_semaphore_release(priv->devsem);
|
|
|
|
/* Enable AHB and CAN IRQs in wrapper and EOD1, EOD2 and CAN critical IRQs in SatCAN core */
|
|
regs->irqmask = MSK_AHB | MSK_CAN;
|
|
regs->satcan[SATCAN_INT_EN] = ((1 << SATCAN_IRQ_EOD1) | (1 << SATCAN_IRQ_EOD2) |
|
|
(1 << SATCAN_IRQ_CRITICAL));
|
|
|
|
/* Select can_int as IRQ source */
|
|
regs->satcan[SATCAN_CMD0] = CAN_TODn_Int_sel;
|
|
/* CAN RX DMA Enable */
|
|
regs->satcan[SATCAN_DMA] = 1;
|
|
/* CAN RX Enable */
|
|
regs->satcan[SATCAN_RX] = 1;
|
|
|
|
DBG("SatCAN: Opening %d success\n\r",minor);
|
|
|
|
return RTEMS_SUCCESSFUL;
|
|
}
|
|
|
|
static rtems_device_driver satcan_initialize(rtems_device_major_number major, rtems_device_minor_number minor, void *arg)
|
|
{
|
|
struct ambapp_ahb_info d;
|
|
char fs_name[20];
|
|
rtems_status_code status;
|
|
|
|
DBG("SatCAN: Initialize..\n\r");
|
|
|
|
strcpy(fs_name, SATCAN_DEVNAME);
|
|
|
|
/* Find core and initialize register pointer */
|
|
if (!ambapp_find_ahbslv(&ambapp_plb, VENDOR_GAISLER, GAISLER_SATCAN, &d)) {
|
|
printk("SatCAN: Failed to find SatCAN core\n\r");
|
|
return -1;
|
|
}
|
|
|
|
status = rtems_io_register_name(fs_name, major, minor);
|
|
if (RTEMS_SUCCESSFUL != status)
|
|
rtems_fatal_error_occurred(status);
|
|
|
|
regs = (struct satcan_regs*)d.start[0];
|
|
|
|
/* Set node number and DPS */
|
|
regs->ctrl |= ((priv->cfg->nodeno & 0xf) << 5) | (priv->cfg->dps << 1);
|
|
|
|
/* Reset core */
|
|
regs->ctrl |= CTRL_RST;
|
|
|
|
/* Allocate DMA area */
|
|
almalloc(&priv->alptr, &priv->dmaptr, ALIGN_2KMEM);
|
|
if (priv->dmaptr == NULL) {
|
|
printk("SatCAN: Failed to allocate DMA memory\n\r");
|
|
free(priv->cfg);
|
|
free(priv);
|
|
return -1;
|
|
}
|
|
|
|
/* Wait until core reset has completed */
|
|
while (regs->ctrl & CTRL_RST)
|
|
;
|
|
|
|
/* Initialize core registers, default is 2K messages */
|
|
regs->membase = (unsigned int)priv->alptr;
|
|
regs->satcan[SATCAN_RAM_BASE] = (unsigned int)priv->alptr >> 15;
|
|
|
|
DBG("regs->membase = %x\n\r", (unsigned int)priv->alptr);
|
|
DBG("regs->satcan[SATCAN_RAM_BASE] = %x\n\r", (unsigned int)priv->alptr >> 15);
|
|
|
|
status = rtems_semaphore_create(
|
|
rtems_build_name('S', 'd', 'v', '0'),
|
|
1,
|
|
RTEMS_FIFO | RTEMS_SIMPLE_BINARY_SEMAPHORE | RTEMS_NO_INHERIT_PRIORITY | \
|
|
RTEMS_NO_PRIORITY_CEILING,
|
|
0,
|
|
&priv->devsem);
|
|
if (status != RTEMS_SUCCESSFUL) {
|
|
printk("SatCAN: Failed to create dev semaphore (%d)\n\r", status);
|
|
free(priv->cfg);
|
|
free(priv);
|
|
return RTEMS_UNSATISFIED;
|
|
}
|
|
status = rtems_semaphore_create(
|
|
rtems_build_name('S', 't', 'x', '0'),
|
|
0,
|
|
RTEMS_FIFO | RTEMS_SIMPLE_BINARY_SEMAPHORE | RTEMS_NO_INHERIT_PRIORITY | \
|
|
RTEMS_NO_PRIORITY_CEILING,
|
|
0,
|
|
&priv->txsem);
|
|
if (status != RTEMS_SUCCESSFUL) {
|
|
printk("SatCAN: Failed to create tx semaphore (%d)\n\r", status);
|
|
free(priv->cfg);
|
|
free(priv);
|
|
return RTEMS_UNSATISFIED;
|
|
}
|
|
|
|
priv->txactive = 0;
|
|
priv->open = 0;
|
|
priv->dmaen = 0;
|
|
priv->doff = 0;
|
|
priv->timeout = RTEMS_NO_TIMEOUT;
|
|
priv->dmamode = SATCAN_DMA_MODE_SYSTEM;
|
|
|
|
/* Register interrupt handler */
|
|
rtems_interrupt_handler_install(d.common.irq, "satcan",
|
|
RTEMS_INTERRUPT_SHARED,
|
|
satcan_interrupt_handler, NULL);
|
|
|
|
return RTEMS_SUCCESSFUL;
|
|
}
|
|
|
|
|
|
|
|
#define SATCAN_DRIVER_TABLE_ENTRY { satcan_initialize, satcan_open, satcan_close, satcan_read, satcan_write, satcan_ioctl }
|
|
|
|
static rtems_driver_address_table satcan_driver = SATCAN_DRIVER_TABLE_ENTRY;
|
|
|
|
int satcan_register(satcan_config *conf)
|
|
{
|
|
rtems_status_code r;
|
|
rtems_device_major_number m;
|
|
|
|
DBG("SatCAN: satcan_register called\n\r");
|
|
|
|
/* Create private structure */
|
|
if ((priv = grlib_malloc(sizeof(*priv))) == NULL) {
|
|
printk("SatCAN driver could not allocate memory for priv structure\n\r");
|
|
return -1;
|
|
}
|
|
|
|
DBG("SatCAN: Creating local copy of config structure\n\r");
|
|
if ((priv->cfg = grlib_malloc(sizeof(*priv->cfg))) == NULL) {
|
|
printk("SatCAN driver could not allocate memory for cfg structure\n\r");
|
|
return 1;
|
|
}
|
|
memcpy(priv->cfg, conf, sizeof(satcan_config));
|
|
|
|
if ((r = rtems_io_register_driver(0, &satcan_driver, &m)) == RTEMS_SUCCESSFUL) {
|
|
DBG("SatCAN driver successfully registered, major: %d\n\r", m);
|
|
} else {
|
|
switch(r) {
|
|
case RTEMS_TOO_MANY:
|
|
printk("SatCAN rtems_io_register_driver failed: RTEMS_TOO_MANY\n\r"); break;
|
|
case RTEMS_INVALID_NUMBER:
|
|
printk("SatCAN rtems_io_register_driver failed: RTEMS_INVALID_NUMBER\n\r"); break;
|
|
case RTEMS_RESOURCE_IN_USE:
|
|
printk("SatCAN rtems_io_register_driver failed: RTEMS_RESOURCE_IN_USE\n\r"); break;
|
|
default:
|
|
printk("SatCAN rtems_io_register_driver failed\n\r");
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|