mirror of
https://gitlab.rtems.org/rtems/rtos/rtems.git
synced 2025-12-05 15:15:44 +00:00
Use RTEMS_FALL_THROUGH() to annotate case statements which intentionally do not have a break statement.
689 lines
19 KiB
C
689 lines
19 KiB
C
/* SPDX-License-Identifier: BSD-2-Clause */
|
|
|
|
/*
|
|
* This file contains the RTEMS GRSLINK SLINK master driver
|
|
*
|
|
* COPYRIGHT (c) 2009.
|
|
* Cobham Gaisler AB.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
|
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
* POSSIBILITY OF SUCH DAMAGE.
|
|
*
|
|
* Comments concerning current driver implementation:
|
|
*
|
|
* The SLINK specification says that there are three IO cards that are capable
|
|
* of transmitting data. But these IO cards can have the address range 0 to 3,
|
|
* and an 'For information only' comment explains that the current
|
|
* implementation has receive buffers for ".. x 4 (IO cards)".
|
|
* Because of this the driver has four queues, one for each IO card 0 - 3.
|
|
* When the addressing convention used for the IO cards is known, the number of
|
|
* queues may be lowered to three.
|
|
*
|
|
*/
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <bsp.h>
|
|
#include <grlib/grslink.h>
|
|
#include <grlib/ambapp.h>
|
|
#include <grlib/grlib.h>
|
|
|
|
#include <grlib/grlib_impl.h>
|
|
|
|
#ifndef GAISLER_SLINK
|
|
#define GAISLER_SLINK 0x02F
|
|
#endif
|
|
|
|
/* Enable debug output? */
|
|
/* #define DEBUG */
|
|
|
|
#ifdef DEBUG
|
|
#define DBG(x...) printk(x)
|
|
#else
|
|
#define DBG(x...)
|
|
#endif
|
|
|
|
/* Bits and fields in SLINK transmit word */
|
|
#define SLINK_RW (1 << 23)
|
|
#define SLINK_CHAN_POS 16
|
|
|
|
/* Local types */
|
|
typedef struct {
|
|
volatile unsigned int clockscale;
|
|
volatile unsigned int ctrl;
|
|
volatile unsigned int nullwrd;
|
|
volatile unsigned int sts;
|
|
volatile unsigned int msk;
|
|
volatile unsigned int abase;
|
|
volatile unsigned int bbase;
|
|
volatile unsigned int td;
|
|
volatile unsigned int rd;
|
|
} SLINK_regs;
|
|
|
|
typedef struct {
|
|
char readstat; /* Status of READ operation */
|
|
char seqstat; /* Status of SEQUENCE operation */
|
|
unsigned char scnt; /* Number of SEQUENCE words transferred */
|
|
} SLINK_status;
|
|
|
|
typedef struct {
|
|
int size;
|
|
unsigned int *buf;
|
|
unsigned int *first;
|
|
unsigned int *last;
|
|
unsigned int *max;
|
|
int full;
|
|
} SLINK_queue;
|
|
|
|
typedef struct {
|
|
SLINK_regs *reg; /* Pointer to core registers */
|
|
SLINK_status *status; /* Driver status information */
|
|
void (*slink_irq_handler)(int); /* Handler for INTERRUPT */
|
|
void (*slink_seq_change)(int); /* Callback on SEQUENCE change */
|
|
int rword; /* Placeholder for READ response */
|
|
rtems_id read_sem; /* Semaphore for blocking SLINK_read */
|
|
SLINK_queue *queues; /* Receive queues */
|
|
#ifdef SLINK_COLLECT_STATISTICS
|
|
SLINK_stats *stats; /* Core statistics, optional */
|
|
#endif
|
|
} SLINK_cfg;
|
|
|
|
|
|
static SLINK_cfg *cfg = NULL;
|
|
|
|
/**** SLINK driver queues for unsolicited and INTERRUPT requests ****/
|
|
|
|
/* Function: SLINK_createqueues
|
|
* Arguments: size: Number of elements in each queue
|
|
* Returns: 0 on success, -1 on failure
|
|
* Description: Creates SLINK_NUMQUEUES queues, one for each IO card
|
|
* that can send data. The pointers to the queues is saved in the driver
|
|
* config structure.
|
|
*/
|
|
static int SLINK_createqueues(int size)
|
|
{
|
|
SLINK_queue *q;
|
|
int i, j;
|
|
|
|
if ((q = grlib_malloc(SLINK_NUMQUEUES*sizeof(*q))) == NULL)
|
|
goto slink_qiniterr1;
|
|
|
|
for (i = 0; i < SLINK_NUMQUEUES; i++) {
|
|
q[i].size = size;
|
|
if ((q[i].buf = grlib_malloc(size*sizeof(int))) == NULL)
|
|
goto slink_qiniterr2;
|
|
q[i].first = q[i].last = q[i].buf;
|
|
q[i].max = q[i].buf + (size-1);
|
|
q[i].full = 0;
|
|
}
|
|
|
|
cfg->queues = q;
|
|
|
|
return 0;
|
|
|
|
slink_qiniterr2:
|
|
for (j = 0; j < i; j++)
|
|
free(q[i].buf);
|
|
free(q);
|
|
slink_qiniterr1:
|
|
return -1;
|
|
}
|
|
|
|
/*
|
|
* Function: SLINK_destroyqueues
|
|
* Arguments: None
|
|
* Returns: Nothing
|
|
* Description: Frees the memory occupied by the queues in cfg->queues
|
|
*/
|
|
/*
|
|
static void SLINK_destroyqueues(void)
|
|
{
|
|
int i;
|
|
|
|
for(i = 0; i < SLINK_NUMQUEUES; i++)
|
|
free(cfg->queues[i].buf);
|
|
|
|
free(cfg->queues);
|
|
}
|
|
*/
|
|
|
|
/*
|
|
* Function: SLINK_enqueue
|
|
* Arguments: Received SLINK word
|
|
* Returns: Nothing
|
|
* Description:
|
|
*/
|
|
static void SLINK_enqueue(unsigned int slink_wrd)
|
|
{
|
|
SLINK_queue *ioq = cfg->queues + SLINK_WRD_CARDNUM(slink_wrd);
|
|
|
|
if (!ioq->full && SLINK_WRD_CARDNUM(slink_wrd) < SLINK_NUMQUEUES) {
|
|
*ioq->last = slink_wrd;
|
|
ioq->last = (ioq->last >= ioq->max) ? ioq->buf : ioq->last+1;
|
|
ioq->full = ioq->last == ioq->first;
|
|
return;
|
|
}
|
|
#ifdef SLINK_COLLECT_STATISTICS
|
|
cfg->stats->lostwords++;
|
|
#endif
|
|
}
|
|
|
|
/**** SLINK driver helper functions ****/
|
|
|
|
/*
|
|
* Function: SLINK_getaddr
|
|
* Arguments: amba_conf
|
|
* base: assigned to base of core registers
|
|
* irq: assigned to core irq lines
|
|
* Returns: Base address and IRQ via arguments, 0 if core is found, else -1
|
|
* Description: See above.
|
|
*/
|
|
static int SLINK_getaddr(int *base, int *irq)
|
|
{
|
|
struct ambapp_apb_info c;
|
|
|
|
if (ambapp_find_apbslv(ambapp_plb(),VENDOR_GAISLER,GAISLER_SLINK,&c) == 1) {
|
|
*base = c.start;
|
|
*irq = c.common.irq;
|
|
return 0;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
/* Function: SLINK_calcscaler
|
|
* Arguments: sysfreq: System frequency in Hz
|
|
* Returns: Clock scaler register value
|
|
* Description: Calculates value for SLINK clock scaler register to attain
|
|
* a SLINK bus frequency as close to 6 MHz as possible. Please see the IP core
|
|
* documentation for a description of how clock scaling is implemented.
|
|
*/
|
|
static int SLINK_calcscaler(int sysfreq)
|
|
{
|
|
int fact = sysfreq / SLINK_FREQ_HZ;
|
|
return ((fact/2-1) << 16) | (fact % 2 ? fact/2 : fact/2-1);
|
|
}
|
|
|
|
|
|
/*
|
|
* Function: SLINK_getsysfreq
|
|
* Arguments: None
|
|
* Returns: System frequency in Hz, or 0 if system timer is not found.
|
|
* Description: Looks at the timer to determine system frequency. Makes use
|
|
* of AMBA Plug'n'Play.
|
|
*/
|
|
static int SLINK_getsysfreq(void)
|
|
{
|
|
struct ambapp_apb_info t;
|
|
struct gptimer_regs *tregs;
|
|
|
|
if (ambapp_find_apbslv(ambapp_plb(),VENDOR_GAISLER,GAISLER_GPTIMER,&t)==1) {
|
|
tregs = (struct gptimer_regs *)t.start;
|
|
DBG("SLINK_getsysfreq returning %d\n",
|
|
(tregs->scaler_reload+1)*1000*1000);
|
|
return (tregs->scaler_reload+1)*1000*1000;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Function: SLINK_interrupt_handler
|
|
* Arguments: v: not used
|
|
* Returns: Nothing
|
|
* Description: Interrupt handles checks RNE, SEQUENCE and error status
|
|
* bits. Reads word from receive queue and distinguishes between INTERRUPT,
|
|
* READ responses and SLAVE-WORD-SEND. When an INTERRUPT transfer is detected
|
|
* the handler calls the user specified slink_irq_handler with the received
|
|
* word. READ responses are saved and given to SLINK_read via a private
|
|
* variable. SLAVE-WORD-SEND transfers are placed in the IO card's receive
|
|
* queue.
|
|
*/
|
|
static rtems_isr SLINK_interrupt_handler(void *v)
|
|
{
|
|
unsigned int sts;
|
|
unsigned int wrd;
|
|
|
|
/* Read all words from Receive queue */
|
|
while ((sts = cfg->reg->sts) & SLINK_S_RNE) {
|
|
|
|
/* Read first word in receive queue */
|
|
wrd = cfg->reg->rd;
|
|
|
|
/* Check channel value to determine action */
|
|
switch (SLINK_WRD_CHAN(wrd)) {
|
|
case 0: /* Interrupt */
|
|
cfg->slink_irq_handler(wrd);
|
|
#ifdef SLINK_COLLECT_STATISTICS
|
|
cfg->stats->interrupts++;
|
|
#endif
|
|
break;
|
|
case 3: /* Read response, if no active READ, fall-through */
|
|
if (cfg->status->readstat == SLINK_ACTIVE) {
|
|
rtems_semaphore_release(cfg->read_sem);
|
|
cfg->status->readstat = SLINK_COMPLETED;
|
|
cfg->rword = wrd;
|
|
break;
|
|
}
|
|
#ifdef __rtems__
|
|
RTEMS_FALL_THROUGH();
|
|
#endif
|
|
default: /* Unsolicited request */
|
|
SLINK_enqueue(wrd);
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* Check sequence operation */
|
|
if (sts & SLINK_S_SC) {
|
|
/* SEQUENCE completed */
|
|
cfg->status->seqstat = SLINK_COMPLETED;
|
|
if (cfg->slink_seq_change)
|
|
cfg->slink_seq_change(SLINK_COMPLETED);
|
|
#ifdef SLINK_COLLECT_STATISTICS
|
|
cfg->stats->seqcomp++;
|
|
#endif
|
|
} else if (sts & SLINK_S_SA) {
|
|
/* SEQUENCE aborted */
|
|
cfg->status->seqstat = SLINK_ABORTED;
|
|
cfg->status->scnt = (sts >> SLINK_S_SI_POS);
|
|
if (cfg->slink_seq_change)
|
|
cfg->slink_seq_change(SLINK_ABORTED);
|
|
}
|
|
|
|
/* Check error conditions */
|
|
if (sts & SLINK_S_PERR) {
|
|
/*
|
|
Parity error detected, set seqstat if there is an ongoing
|
|
sequence so that the calling application can decide if the
|
|
sequence should be aborted
|
|
*/
|
|
if (cfg->status->seqstat == SLINK_ACTIVE) {
|
|
cfg->status->seqstat = SLINK_PARERR;
|
|
if (cfg->slink_seq_change)
|
|
cfg->slink_seq_change(SLINK_PARERR);
|
|
}
|
|
/* Abort READ operation */
|
|
if (cfg->status->readstat == SLINK_ACTIVE) {
|
|
cfg->status->readstat = SLINK_PARERR;
|
|
rtems_semaphore_release(cfg->read_sem);
|
|
}
|
|
#ifdef SLINK_COLLECT_STATISTICS
|
|
cfg->stats->parerr++;
|
|
#endif
|
|
}
|
|
if (sts & SLINK_S_AERR) {
|
|
/* AMBA error response, sequence aborted */
|
|
cfg->status->seqstat = SLINK_AMBAERR;
|
|
cfg->status->scnt = sts >> SLINK_S_SI_POS;
|
|
if (cfg->slink_seq_change)
|
|
cfg->slink_seq_change(SLINK_AMBAERR);
|
|
}
|
|
if (sts & SLINK_S_ROV) {
|
|
/* Receive overflow, abort any ongoing READ */
|
|
if (cfg->status->readstat == SLINK_ACTIVE) {
|
|
cfg->status->readstat = SLINK_ROV;
|
|
rtems_semaphore_release(cfg->read_sem);
|
|
}
|
|
#ifdef SLINK_COLLECT_STATISICS
|
|
cfg->status->recov++;
|
|
#endif
|
|
}
|
|
|
|
/* Clear processed bits */
|
|
cfg->reg->sts = sts;
|
|
}
|
|
|
|
/**** SLINK driver interface starts here ****/
|
|
|
|
/* Function: SLINK_init
|
|
* Arguments: nullwrd: NULL word
|
|
* parity: Even (0) or Odd (1) parity
|
|
* interrupt_trans_handler: Function that handles interrupt requests
|
|
* sequence_callback: Callback on SEQUENCE status changes
|
|
* qsize: Size of each receive queue
|
|
* Returns: 0 on success, -1 on failure
|
|
* Description: Initializes the SLINK core
|
|
*/
|
|
int SLINK_init(unsigned int nullwrd, int parity, int qsize,
|
|
void (*interrupt_trans_handler)(int),
|
|
void (*sequence_callback)(int))
|
|
{
|
|
int base;
|
|
int irq;
|
|
rtems_status_code st;
|
|
|
|
/* Allocate private config structure */
|
|
if (cfg == NULL && (cfg = grlib_malloc(sizeof(*cfg))) == NULL) {
|
|
DBG("SLINK_init: Could not allocate cfg structure\n");
|
|
goto slink_initerr1;
|
|
}
|
|
|
|
/* Create simple binary semaphore for blocking SLINK_read */
|
|
st = rtems_semaphore_create(rtems_build_name('S', 'L', 'R', '0'), 0,
|
|
(RTEMS_FIFO|RTEMS_SIMPLE_BINARY_SEMAPHORE|
|
|
RTEMS_NO_INHERIT_PRIORITY|RTEMS_LOCAL|
|
|
RTEMS_NO_PRIORITY_CEILING), 0,
|
|
&cfg->read_sem);
|
|
if (st != RTEMS_SUCCESSFUL) {
|
|
DBG("SLINK_init: Could not create semaphore\n");
|
|
goto slink_initerr1;
|
|
}
|
|
|
|
/* Initialize pointer to SLINK core registers and get IRQ line */
|
|
if (SLINK_getaddr(&base, &irq) == -1) {
|
|
DBG("SLINK_init: Could not find core\n");
|
|
goto slink_initerr2;
|
|
}
|
|
cfg->reg = (SLINK_regs*)base;
|
|
|
|
/* Allocate status structure and initialize members */
|
|
if ((cfg->status = grlib_calloc(1, sizeof(*cfg->status))) == NULL) {
|
|
DBG("SLINK_init: Could not allocate status structure\n");
|
|
goto slink_initerr2;
|
|
}
|
|
cfg->status->seqstat = SLINK_COMPLETED;
|
|
cfg->status->readstat = SLINK_COMPLETED;
|
|
|
|
#ifdef SLINK_COLLECT_STATISTICS
|
|
/* Allocate statistics structure and initialize members */
|
|
if ((cfg->stats = grlib_calloc(1, sizeof(*cfg->stats))) == NULL) {
|
|
DBG("SLINK_init: Could not allocate statistics structure\n");
|
|
goto slink_initerr3;
|
|
}
|
|
#endif
|
|
|
|
/* Allocate and initialize queues */
|
|
if (SLINK_createqueues(qsize) == -1) {
|
|
DBG("SLINK_init: Could not create queues\n");
|
|
goto slink_initerr3;
|
|
}
|
|
|
|
/* Configure core registers */
|
|
cfg->reg->clockscale = SLINK_calcscaler(SLINK_getsysfreq());
|
|
cfg->reg->ctrl = parity ? SLINK_C_PAR : 0;
|
|
cfg->reg->nullwrd = nullwrd;
|
|
cfg->reg->msk = (SLINK_M_PERRE | SLINK_M_AERRE | SLINK_M_ROVE |
|
|
SLINK_M_RNEE | SLINK_M_SAE | SLINK_M_SCE);
|
|
|
|
/* Set-up INTERRUPT transfer handling */
|
|
cfg->slink_irq_handler = interrupt_trans_handler;
|
|
|
|
/* Save SEQUENCE callback */
|
|
cfg->slink_seq_change = sequence_callback;
|
|
|
|
/* Set-up IRQ handling */
|
|
rtems_interrupt_handler_install(irq, "slink",
|
|
RTEMS_INTERRUPT_SHARED,
|
|
SLINK_interrupt_handler, NULL);
|
|
|
|
return 0;
|
|
|
|
slink_initerr3:
|
|
free(cfg->status);
|
|
slink_initerr2:
|
|
free(cfg);
|
|
slink_initerr1:
|
|
return -1;
|
|
}
|
|
|
|
/* Function: SLINK_start
|
|
* Description: Enables the core
|
|
*/
|
|
void SLINK_start(void)
|
|
{
|
|
if (cfg != NULL)
|
|
cfg->reg->ctrl |= SLINK_C_SLE;
|
|
}
|
|
|
|
/* Function: SLINK_stop
|
|
* Description: Disables the core
|
|
*/
|
|
void SLINK_stop(void)
|
|
{
|
|
if (cfg != NULL)
|
|
cfg->reg->ctrl &= ~SLINK_C_SLE;
|
|
}
|
|
|
|
/*
|
|
* Function: SLINK_read
|
|
* Arguments: data: Payload of data word
|
|
* channel: -
|
|
* reply: Reply from IO card
|
|
* Returns: 0 on success
|
|
* -(SLINK_PARERR, SLINK_ROV) on error or -SLINK_QFULL if transmit queue
|
|
* is full and software should try again.
|
|
* Description: Reads one word and returns the response in *reply unless there
|
|
* is an error. This function blocks until the READ operation is
|
|
* completed or aborted.
|
|
*/
|
|
int SLINK_read(int data, int channel, int *reply)
|
|
{
|
|
DBG("SLINK_read: called..");
|
|
|
|
if (cfg->reg->sts & SLINK_S_TNF) {
|
|
cfg->status->readstat = SLINK_ACTIVE;
|
|
cfg->reg->td = SLINK_RW | channel << SLINK_CHAN_POS | data;
|
|
} else {
|
|
DBG("queue FULL\n");
|
|
return -SLINK_QFULL; /* Transmit queue full */
|
|
}
|
|
|
|
/* Block until the operation has completed or has been aborted */
|
|
rtems_semaphore_obtain(cfg->read_sem, RTEMS_WAIT, RTEMS_NO_TIMEOUT);
|
|
|
|
if (cfg->status->readstat == SLINK_COMPLETED) {
|
|
*reply = cfg->rword;
|
|
#ifdef SLINK_COLLECT_STATISTICS
|
|
cfg->stats->reads++;
|
|
#endif
|
|
DBG("returning 0\n");
|
|
return 0;
|
|
} else {
|
|
DBG("returning error code\n");
|
|
return -cfg->status->readstat;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Function: SLINK_write
|
|
* Arguments: data: Payload of SLINK data word
|
|
* channel: Channel value (bits 22 downto 16) of receive
|
|
* register word
|
|
* Returns: 0 if command was placed in transmit queue
|
|
* -SLINK_QFULL if transmit queue was full (software should retry)
|
|
* Description: See above.
|
|
*/
|
|
int SLINK_write(int data, int channel)
|
|
{
|
|
if (cfg->reg->sts & SLINK_S_TNF) {
|
|
cfg->reg->td = channel << SLINK_CHAN_POS | data;
|
|
#ifdef SLINK_COLLECT_STATISTICS
|
|
cfg->stats->writes++;
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
return -SLINK_QFULL;
|
|
}
|
|
|
|
/*
|
|
* Function: SLINK_sequence
|
|
* Arguments: a: Array containing sequence commands
|
|
* b: Array where SEQUENCE responses will be stored
|
|
* n: Number of commands in a array
|
|
* channel: Sequence Channel Number
|
|
* reconly: Set to 1 if the SEQUENCE operation is receive only
|
|
* Returns: 0 if SEQUENCE could be started (SUCCESS)
|
|
* -1 if SEQUNCE was not started due to ongoing SEQUENCE
|
|
*/
|
|
int SLINK_seqstart(int *a, int *b, int n, int channel, int reconly)
|
|
{
|
|
/* Only start a new SEQUENCE of the former SEQUENCE has completed */
|
|
if (cfg->status->seqstat == SLINK_ACTIVE ||
|
|
cfg->status->seqstat == SLINK_PARERR)
|
|
return -1;
|
|
|
|
/* Tell core about arrays */
|
|
cfg->reg->abase = (int)a;
|
|
cfg->reg->bbase = (int)b;
|
|
|
|
/* As far as software is concerned the sequence is now active */
|
|
cfg->status->seqstat = SLINK_ACTIVE;
|
|
|
|
/* Enable SEQUENCE operation with SCN = channel and SLEN = n-1 */
|
|
if (reconly == 1) {
|
|
cfg->reg->ctrl = (((n-1) << SLINK_C_SLEN_POS) | SLINK_C_SRO |
|
|
(channel << SLINK_C_SCN_POS) |
|
|
SLINK_C_SE | (cfg->reg->ctrl & 0xC000000F));
|
|
} else {
|
|
cfg->reg->ctrl = (((n-1) << SLINK_C_SLEN_POS) |
|
|
(channel << SLINK_C_SCN_POS) |
|
|
SLINK_C_SE | (cfg->reg->ctrl & 0xC000000F));
|
|
}
|
|
|
|
#ifdef SLINK_COLLECT_STATISTICS
|
|
cfg->stats->sequences++;
|
|
#endif
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/* Function: SLINK_seqabort
|
|
* Description: This function aborts an ongoing SEQUENCE. Software can tell
|
|
* when the SEQUENCE is aborted by polling SLINK_seqstat().
|
|
*/
|
|
void SLINK_seqabort(void)
|
|
{
|
|
cfg->reg->ctrl = cfg->reg->ctrl | SLINK_C_AS;
|
|
}
|
|
|
|
|
|
/*
|
|
* Function: SLINK_seqstatus
|
|
* Returns: The current or status of the SEQUENCE operation:
|
|
* SLINK_COMPLETED, SLINK_ACTIVE, SLINK_PARERR, SLINK_AMBAERR,
|
|
* SLINK_ABORTED (these are defined in bsp/grslink.h)
|
|
* Description: Meaning of returned values:
|
|
* SLINK_ABORTED: Aborted before all operations completed.
|
|
* SLINK_ACTIVE: The core is busy processing the SEQUENCE
|
|
* SLINK_AMBAERR: The last SEQUENCE was aborted by an AMBA ERROR
|
|
* SLINK_COMPLETED: All words were transferred in the last SEQUENCE
|
|
* SLINK_PARERR: Parity error detected. Software may want to abort
|
|
*
|
|
* If the SEQUENCE was aborted SLINK_seqwrds() can be used to
|
|
* determine the number of completed operations.
|
|
*/
|
|
int SLINK_seqstatus(void)
|
|
{
|
|
return cfg->status->seqstat;
|
|
}
|
|
|
|
/*
|
|
* Function: SLINK_seqwrds
|
|
* Returns: -1 for ongoing sequence
|
|
* 0 if all words were transferred in the last sequence
|
|
* number of words if the last SEQUENCE did not complete
|
|
* (SLINK_AMBAERR or SLINK_ABORTED is reported ny SLINK_seqstatus())
|
|
*/
|
|
int SLINK_seqwrds(void)
|
|
{
|
|
switch (cfg->status->seqstat) {
|
|
case SLINK_COMPLETED: return 0;
|
|
case SLINK_ACTIVE | SLINK_PARERR: return -1;
|
|
default: return cfg->status->scnt;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Function: SLINK_hwstatus
|
|
* Returns: The SLINK core's status register. The register values can be
|
|
* interpreted with the help of macros defined in bsp/grslink.h.
|
|
*/
|
|
int SLINK_hwstatus(void)
|
|
{
|
|
return cfg->reg->sts;
|
|
}
|
|
|
|
/*
|
|
* Function: SLINK_queuestatus
|
|
* Arguments: iocard: Queue which to check status for
|
|
* Returns: Number of elements in queue or -1 on non-existent queue
|
|
* Description: SLINK_queuestatus(queue) returns the number of elements in
|
|
* queue 'iocard'
|
|
*/
|
|
int SLINK_queuestatus(int iocard)
|
|
{
|
|
unsigned int first, last;
|
|
SLINK_queue *ioq;
|
|
|
|
if (iocard >= SLINK_NUMQUEUES)
|
|
return -1;
|
|
|
|
ioq = cfg->queues + iocard;
|
|
|
|
if (ioq->full)
|
|
return ioq->size;
|
|
if (ioq->first == ioq->last)
|
|
return 0;
|
|
|
|
first = ((unsigned int)ioq->first)/sizeof(unsigned int);
|
|
last = ((unsigned int)ioq->last)/sizeof(unsigned int);
|
|
|
|
return first < last ? last - first : ioq->size - first + last;
|
|
}
|
|
|
|
/*
|
|
* Function: SLINK_dequeue
|
|
* Arguments: iocard: IO card number
|
|
* elem: First element in IO card queue
|
|
* Returns: 0 on success or -1 on empty or non-existent queue
|
|
* Description:
|
|
*/
|
|
int SLINK_dequeue(int iocard, int *elem)
|
|
{
|
|
if (iocard >= SLINK_NUMQUEUES)
|
|
return -1;
|
|
|
|
SLINK_queue *ioq = cfg->queues + iocard;
|
|
|
|
if (ioq->last != ioq->first || ioq->full) {
|
|
*elem = *ioq->first;
|
|
ioq->first = (ioq->first >= ioq->max) ? ioq->buf : ioq->first+1;
|
|
ioq->full = 0;
|
|
return 0;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
/*
|
|
* Function: SLINK_statistics
|
|
* Returns: If the core has statistics colletion enabled this function returns
|
|
* a pointer to a struct containing statistics information, otherwise NULL.
|
|
*/
|
|
SLINK_stats *SLINK_statistics(void)
|
|
{
|
|
#ifdef SLINK_COLLECT_STATISTICS
|
|
return cfg->stats;
|
|
#else
|
|
return NULL;
|
|
#endif
|
|
}
|