forked from Imagelibrary/rtems
2027 lines
56 KiB
C
2027 lines
56 KiB
C
/*
|
|
* GRPCI2 DMA Driver
|
|
*
|
|
* COPYRIGHT (c) 2017
|
|
* 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 <stdlib.h>
|
|
#include <string.h>
|
|
#include <stddef.h>
|
|
#include <drvmgr/drvmgr.h>
|
|
#include <rtems.h>
|
|
#include <rtems/bspIo.h> /* for printk */
|
|
#include <bsp.h>
|
|
#include <grlib/grpci2dma.h>
|
|
|
|
#include <grlib/grlib_impl.h>
|
|
|
|
/* This driver has been prepared for SMP operation
|
|
*/
|
|
|
|
/*#define STATIC*/
|
|
#define STATIC static
|
|
|
|
/*#define INLINE*/
|
|
#define INLINE inline
|
|
|
|
/*#define UNUSED*/
|
|
#define UNUSED __attribute__((unused))
|
|
|
|
/*#define DEBUG 1*/
|
|
|
|
#ifdef DEBUG
|
|
#define DBG(x...) printf(x)
|
|
#else
|
|
#define DBG(x...)
|
|
#endif
|
|
|
|
#define BD_CHAN_EN (1<<BD_CHAN_EN_BIT)
|
|
#define BD_CHAN_ID (0x3<<BD_CHAN_ID_BIT)
|
|
#define BD_CHAN_TYPE (0x3<<BD_CHAN_TYPE_BIT)
|
|
#define BD_CHAN_TYPE_DMA (0x1<<BD_CHAN_TYPE_BIT)
|
|
#define BD_CHAN_BDCNT (0xffff<<BD_CHAN_BDCNT_BIT)
|
|
#define BD_CHAN_EN_BIT 31
|
|
#define BD_CHAN_ID_BIT 22
|
|
#define BD_CHAN_TYPE_BIT 20
|
|
#define BD_CHAN_BDCNT_BIT 0
|
|
|
|
#define BD_DATA_EN (0x1<<BD_DATA_EN_BIT)
|
|
#define BD_DATA_IE (0x1<<BD_DATA_IE_BIT)
|
|
#define BD_DATA_DR (0x1<<BD_DATA_DR_BIT)
|
|
#define BD_DATA_BE (0x1<<BD_DATA_BE_BIT)
|
|
#define BD_DATA_TYPE (0x3<<BD_DATA_TYPE_BIT)
|
|
#define BD_DATA_TYPE_DATA (0x0<<BD_DATA_TYPE_BIT)
|
|
#define BD_DATA_ER (0x1<<BD_DATA_ER_BIT)
|
|
#define BD_DATA_LEN (0xffff<<BD_DATA_LEN_BIT)
|
|
#define BD_DATA_EN_BIT 31
|
|
#define BD_DATA_IE_BIT 30
|
|
#define BD_DATA_DR_BIT 29
|
|
#define BD_DATA_BE_BIT 28
|
|
#define BD_DATA_TYPE_BIT 20
|
|
#define BD_DATA_ER_BIT 19
|
|
#define BD_DATA_LEN_BIT 0
|
|
|
|
#define DMACTRL_SAFE (0x1<<DMACTRL_SAFE_BIT)
|
|
#define DMACTRL_WCLEAR (0x1fff<<DMACTRL_ERR_BIT)
|
|
#define DMACTRL_ERR (0x1f<<DMACTRL_ERR_BIT)
|
|
#define DMACTRL_CHIRQ (0xff<<DMACTRL_CHIRQ_BIT)
|
|
#define DMACTRL_ERR (0x1f<<DMACTRL_ERR_BIT)
|
|
#define DMACTRL_NUMCH (0x7<<DMACTRL_NUMCH_BIT)
|
|
#define DMACTRL_DIS (0x1<<DMACTRL_DIS_BIT)
|
|
#define DMACTRL_IE (0x1<<DMACTRL_IE_BIT)
|
|
#define DMACTRL_ACT (0x1<<DMACTRL_ACT_BIT)
|
|
#define DMACTRL_EN (0x1<<DMACTRL_EN_BIT)
|
|
|
|
#define DMACTRL_SAFE_BIT 31
|
|
#define DMACTRL_CHIRQ_BIT 12
|
|
#define DMACTRL_ERR_BIT 7
|
|
#define DMACTRL_NUMCH_BIT 4
|
|
#define DMACTRL_DIS_BIT 2
|
|
#define DMACTRL_IE_BIT 1
|
|
#define DMACTRL_ACT_BIT 3
|
|
#define DMACTRL_EN_BIT 0
|
|
|
|
/* GRPCI2 DMA does not allow more than 8 DMA chans */
|
|
#define MAX_DMA_CHANS 8
|
|
|
|
/* GRPCI2 DMA does not allow transfer of more than 0x10000 words */
|
|
#define MAX_DMA_TRANSFER_SIZE (0x10000*4)
|
|
|
|
/* We use the following limits as default */
|
|
#define MAX_DMA_DATA 128
|
|
|
|
/* Memory and HW Registers Access routines. All 32-bit access routines */
|
|
#define BD_WRITE(addr, val) (*(volatile unsigned int *)(addr) = (unsigned int)(val))
|
|
/*#define BD_READ(addr) (*(volatile unsigned int *)(addr))*/
|
|
#define BD_READ(addr) grlib_read_uncached32((unsigned long)(addr))
|
|
#define REG_WRITE(addr, val) (*(volatile unsigned int *)(addr) = (unsigned int)(val))
|
|
#define REG_READ(addr) (*(volatile unsigned int *)(addr))
|
|
|
|
/*
|
|
* GRPCI2 DMA Channel descriptor
|
|
*/
|
|
struct grpci2_bd_chan {
|
|
volatile unsigned int ctrl; /* 0x00 DMA Control */
|
|
volatile unsigned int nchan; /* 0x04 Next DMA Channel Address */
|
|
volatile unsigned int nbd; /* 0x08 Next Data Descriptor in channel */
|
|
volatile unsigned int res; /* 0x0C Reserved */
|
|
};
|
|
|
|
/*
|
|
* GRPCI2 DMA Data descriptor
|
|
*/
|
|
struct grpci2_bd_data {
|
|
volatile unsigned int ctrl; /* 0x00 DMA Data Control */
|
|
volatile unsigned int pci_adr; /* 0x04 PCI Start Address */
|
|
volatile unsigned int ahb_adr; /* 0x08 AHB Start address */
|
|
volatile unsigned int next; /* 0x0C Next Data Descriptor in channel */
|
|
};
|
|
|
|
|
|
/*
|
|
* GRPCI2 DMA APB Register MAP
|
|
*/
|
|
struct grpci2dma_regs {
|
|
volatile unsigned int dma_ctrl; /* 0x00 */
|
|
volatile unsigned int dma_bdbase; /* 0x04 */
|
|
volatile unsigned int dma_chact; /* 0x08 */
|
|
};
|
|
|
|
#define DEVNAME_LEN 11
|
|
/*
|
|
* GRPCI2 DMA Driver private data struture
|
|
*/
|
|
struct grpci2dma_priv {
|
|
/* DMA control registers */
|
|
struct grpci2dma_regs *regs;
|
|
char devname[DEVNAME_LEN];
|
|
|
|
/* Channel info */
|
|
struct {
|
|
/* Channel pointer. Indicates the assigned channel
|
|
* for a given cid (used as index). NULL if not assigned.
|
|
*/
|
|
struct grpci2_bd_chan * ptr;
|
|
/* Is this channel allocated by the driver */
|
|
int allocated;
|
|
/* Last added data descriptor for each channel.
|
|
* This simplifies/speeds up adding data descriptors
|
|
* to the channel*/
|
|
struct grpci2_bd_data * lastdata;
|
|
/* Is this channel active */
|
|
int active;
|
|
/* Interrupt-code Handling
|
|
* - isr: Holds the ISR for each channel
|
|
* - isr_arg: Holds the ISR arg for each channel
|
|
*/
|
|
grpci2dma_isr_t isr;
|
|
void * isr_arg;
|
|
|
|
/* DMA Channel Semaphore */
|
|
rtems_id sem;
|
|
} channel[MAX_DMA_CHANS];
|
|
|
|
/* Indicates the number of channels. */
|
|
int nchans;
|
|
|
|
/* Indicates the number of active channels. */
|
|
int nactive;
|
|
|
|
/* Indicates if the number of DMA ISR that have been registered
|
|
* into the GRPCI2 DRIVER */
|
|
int isr_registered;
|
|
|
|
/* Callback to register the DMA ISR into the GRPCI2 DRIVER */
|
|
void (*isr_register)( void (*isr)(void*), void * arg);
|
|
|
|
/* Spin-lock ISR protection */
|
|
SPIN_DECLARE(devlock);
|
|
};
|
|
|
|
/* The GRPCI2 DMA semaphore */
|
|
rtems_id grpci2dma_sem;
|
|
|
|
/*
|
|
* GRPCI2 DMA internal prototypes
|
|
*/
|
|
/* -Descriptor linked-list functions*/
|
|
STATIC int grpci2dma_channel_list_add(struct grpci2_bd_chan * list,
|
|
struct grpci2_bd_chan * chan);
|
|
STATIC int grpci2dma_channel_list_remove(struct grpci2_bd_chan * chan);
|
|
STATIC int grpci2dma_data_list_add(struct grpci2_bd_chan * chan,
|
|
struct grpci2_bd_data * data, struct grpci2_bd_data * last_chan_data);
|
|
STATIC int grpci2dma_data_list_remove(struct grpci2_bd_chan * chan,
|
|
struct grpci2_bd_data * data);
|
|
STATIC int grpci2dma_channel_list_foreach(struct grpci2_bd_chan * chan,
|
|
int func( struct grpci2_bd_chan * chan), int maxindex);
|
|
STATIC int grpci2dma_data_list_foreach(struct grpci2_bd_data * data,
|
|
int func( struct grpci2_bd_data * data), int maxindex);
|
|
|
|
/* -DMA ctrl access functions */
|
|
STATIC INLINE int grpci2dma_ctrl_init(void);
|
|
STATIC INLINE int grpci2dma_ctrl_start(struct grpci2_bd_chan * chan);
|
|
STATIC INLINE int grpci2dma_ctrl_stop(void);
|
|
STATIC INLINE int grpci2dma_ctrl_resume(void);
|
|
STATIC INLINE unsigned int grpci2dma_ctrl_status(void);
|
|
STATIC INLINE unsigned int grpci2dma_ctrl_base(void);
|
|
STATIC INLINE unsigned int grpci2dma_ctrl_active(void);
|
|
STATIC INLINE int grpci2dma_ctrl_numch_set(int numch);
|
|
STATIC INLINE int grpci2dma_ctrl_interrupt_status(void);
|
|
STATIC INLINE int grpci2dma_ctrl_interrupt_enable(void);
|
|
STATIC INLINE int grpci2dma_ctrl_interrupt_disable(void);
|
|
STATIC INLINE int grpci2dma_ctrl_interrupt_clear(void);
|
|
|
|
/* -Descriptor access functions */
|
|
STATIC int grpci2dma_channel_bd_init(struct grpci2_bd_chan * chan);
|
|
STATIC int grpci2dma_data_bd_init(struct grpci2_bd_data * data,
|
|
uint32_t pci_adr, uint32_t ahb_adr, int dir, int endianness,
|
|
int size, struct grpci2_bd_data * next);
|
|
STATIC int grpci2dma_channel_bd_enable(struct grpci2_bd_chan * chan,
|
|
unsigned int options);
|
|
STATIC int grpci2dma_channel_bd_disable(struct grpci2_bd_chan * chan);
|
|
STATIC void grpci2dma_channel_bd_set_cid(struct grpci2_bd_chan * chan,
|
|
int cid);
|
|
STATIC int grpci2dma_channel_bd_get_cid(struct grpci2_bd_chan * chan);
|
|
STATIC int grpci2dma_data_bd_status(struct grpci2_bd_data *data);
|
|
STATIC int grpci2dma_data_bd_disable(struct grpci2_bd_data *desc);
|
|
STATIC int grpci2dma_data_bd_interrupt_enable(struct grpci2_bd_data * data);
|
|
STATIC struct grpci2_bd_data * grpci2dma_channel_bd_get_data(
|
|
struct grpci2_bd_chan * chan);
|
|
STATIC void grpci2dma_channel_bd_set_data(struct grpci2_bd_chan * chan,
|
|
struct grpci2_bd_data * data);
|
|
STATIC struct grpci2_bd_chan * grpci2dma_channel_bd_get_next(
|
|
struct grpci2_bd_chan * chan);
|
|
STATIC struct grpci2_bd_data * grpci2dma_data_bd_get_next(
|
|
struct grpci2_bd_data * data);
|
|
STATIC void grpci2dma_channel_bd_set_next(struct grpci2_bd_chan * chan,
|
|
struct grpci2_bd_chan * next);
|
|
STATIC void grpci2dma_data_bd_set_next(struct grpci2_bd_data * data,
|
|
struct grpci2_bd_data * next);
|
|
|
|
/* -Channel functions */
|
|
STATIC int grpci2dma_channel_open(struct grpci2_bd_chan * chan, int cid);
|
|
STATIC int grpci2dma_channel_free_id(void);
|
|
STATIC struct grpci2_bd_chan * grpci2dma_channel_get_active_list(void);
|
|
STATIC int grpci2dma_channel_start(int chan_no, int options);
|
|
STATIC int grpci2dma_channel_stop(int chan_no);
|
|
STATIC int grpci2dma_channel_push(int chan_no, void *dataptr, int index,
|
|
int ndata);
|
|
STATIC int grpci2dma_channel_close(int chan_no);
|
|
STATIC int grpci2dma_channel_isr_unregister(int chan_no);
|
|
|
|
/* -ISR functions*/
|
|
STATIC void grpci2dma_isr(void *arg);
|
|
|
|
/* -Init function called by GRPCI2*/
|
|
int grpci2dma_init(void * regs,
|
|
void isr_register( void (*isr)(void*), void * arg));
|
|
|
|
|
|
#ifdef DEBUG
|
|
STATIC int grpci2dma_channel_print(struct grpci2_bd_chan * chan);
|
|
STATIC int grpci2dma_data_print(struct grpci2_bd_data * data);
|
|
#endif
|
|
|
|
static struct grpci2dma_priv *grpci2dmapriv = NULL;
|
|
|
|
/* All data linked list must point to a disabled descriptor at the end.
|
|
* We use this DISABLED_DESCRIPTOR as a list end for all channels.
|
|
*/
|
|
#define ALIGNED __attribute__((aligned(GRPCI2DMA_BD_DATA_ALIGN)))
|
|
static ALIGNED struct grpci2_bd_data disabled_data = {
|
|
/*.ctrl=*/0,
|
|
/*.pci_adr=*/0,
|
|
/*.ahb_adr=*/0,
|
|
/*.next=*/0
|
|
};
|
|
#define DISABLED_DESCRIPTOR (&disabled_data)
|
|
|
|
/*** START OF DESCRIPTOR LINKED-LIST HELPER FUNCTIONS ***/
|
|
|
|
/* This functions adds a channel descriptor to the DMA channel
|
|
* linked list. It assumes that someone has check the input
|
|
* parameters already.
|
|
*/
|
|
STATIC int grpci2dma_channel_list_add(struct grpci2_bd_chan * list,
|
|
struct grpci2_bd_chan * chan)
|
|
{
|
|
DBG("Adding channel (0x%08x) to GRPCI2 DMA driver\n", (unsigned int) chan);
|
|
|
|
/* Add channel to the linnked list */
|
|
if (list == chan) {
|
|
/* No previous channels. Finish. */
|
|
return GRPCI2DMA_ERR_OK;
|
|
} else {
|
|
/* Get next chan from list */
|
|
struct grpci2_bd_chan * nchan = grpci2dma_channel_bd_get_next(list);
|
|
/* Close the circular linked list */
|
|
grpci2dma_channel_bd_set_next(chan,nchan);
|
|
/* Attach the new channel in the middle */
|
|
grpci2dma_channel_bd_set_next(list, chan);
|
|
return GRPCI2DMA_ERR_OK;
|
|
}
|
|
}
|
|
|
|
/* This functions removes a channel descriptor from the DMA channel
|
|
* linked list. It assumes that someone has check the input
|
|
* parameters already.
|
|
* It returns 0 if successfull. Otherwise,
|
|
* it can return:
|
|
* - ERROR: Different causes:
|
|
* x Number of channels is corrupted.
|
|
*/
|
|
STATIC int grpci2dma_channel_list_remove(struct grpci2_bd_chan * chan)
|
|
{
|
|
DBG("Removing channel (0x%08x) from GRPCI2 DMA driver\n",
|
|
(unsigned int) chan);
|
|
|
|
/* Remove channel from the linnked list */
|
|
struct grpci2_bd_chan * nchan = grpci2dma_channel_bd_get_next(chan);
|
|
if (nchan != chan){
|
|
/* There are more channels */
|
|
/* Since this is a circular linked list, we need to find last channel
|
|
* and update the pointer to the next element */
|
|
/* Use index to avoid having an infinite loop in case of corrupted
|
|
* channels */
|
|
struct grpci2_bd_chan * new_first_chan = nchan;
|
|
struct grpci2_bd_chan * curr_chan;
|
|
int i=1;
|
|
while((nchan != chan) && (i<MAX_DMA_CHANS)){
|
|
curr_chan = nchan;
|
|
nchan = grpci2dma_channel_bd_get_next(curr_chan);
|
|
i++;
|
|
}
|
|
if (nchan != chan) {
|
|
DBG("Maximum DMA channels exceeded. Maybe corrupted?\n");
|
|
return GRPCI2DMA_ERR_ERROR;
|
|
} else {
|
|
/* Update the pointer */
|
|
grpci2dma_channel_bd_set_next(curr_chan, new_first_chan);
|
|
return GRPCI2DMA_ERR_OK;
|
|
}
|
|
}else{
|
|
/* There are no more channels */
|
|
return GRPCI2DMA_ERR_OK;
|
|
}
|
|
}
|
|
|
|
/* This functions adds a data descriptor to the channel's data
|
|
* linked list. The function assumes, that the data descriptor
|
|
* points to either a DISABLED_DESCRIPTOR or to linked list of
|
|
* data descriptors that ends with a DISABLED_DESCRIPTOR.
|
|
* It returns the number of active data descriptors
|
|
* if successfull. Otherwise, it can return:
|
|
* - ERROR: Different causes:
|
|
* x Number of channels is corrupted.
|
|
* x Last linked list element is not pointing to the first.
|
|
*/
|
|
STATIC int grpci2dma_data_list_add(struct grpci2_bd_chan * chan,
|
|
struct grpci2_bd_data * data, struct grpci2_bd_data * last_chan_data)
|
|
{
|
|
DBG("Adding data (0x%08x) to channel (0x%08x)\n",
|
|
(unsigned int) data, (unsigned int) chan);
|
|
|
|
/* Add data to the linnked list */
|
|
/* 1st- Get current data */
|
|
struct grpci2_bd_data * first_data = grpci2dma_channel_bd_get_data(chan);
|
|
if (first_data == NULL) {
|
|
/* Channel should always be pointing to a disabled descriptor */
|
|
DBG("Channel not pointing to disabled descpriptor\n");
|
|
return GRPCI2DMA_ERR_ERROR;
|
|
} else if (first_data == DISABLED_DESCRIPTOR){
|
|
/* No previous data. Assign this one and finish. */
|
|
grpci2dma_channel_bd_set_data(chan, data);
|
|
return GRPCI2DMA_ERR_OK;
|
|
} else {
|
|
/* Let's add the data to the last data pointer added to this channel */
|
|
/* Attach the new data */
|
|
grpci2dma_data_bd_set_next(last_chan_data, data);
|
|
/* 2nd- Let's check again to make sure that the DMA did not finished
|
|
* while we were inserting the new data */
|
|
first_data = grpci2dma_channel_bd_get_data(chan);
|
|
if (first_data == DISABLED_DESCRIPTOR){
|
|
grpci2dma_channel_bd_set_data(chan, data);
|
|
}
|
|
return GRPCI2DMA_ERR_OK;
|
|
}
|
|
}
|
|
|
|
/* This functions removes a data descriptor from the channel's data
|
|
* linked list. Note that in a normal execution, the DMA will remove
|
|
* the data descriptors from the linked list, so there is no need to
|
|
* use this function. It returns 0 if successfull. Otherwise,
|
|
* it can return:
|
|
* - WRONGPTR: The chan (or data) pointer is either NULL or not aligned to
|
|
* 0x10.
|
|
* - STOPDMA: The DMA is running, cannot add channels while DMA is running.
|
|
* - TOOMANY: The max number of data is reached.
|
|
* - ERROR: Different causes:
|
|
* x There are no free channel id numbers.
|
|
* x Number of channels is corrupted.
|
|
* x Last linked list element is not pointing to the first.
|
|
*/
|
|
UNUSED STATIC int grpci2dma_data_list_remove(struct grpci2_bd_chan * chan,
|
|
struct grpci2_bd_data * data)
|
|
{
|
|
DBG("Removing data (0x%08x) from channel (0x%08x)\n",
|
|
(unsigned int) data, (unsigned int) chan);
|
|
|
|
/* Remove data from the linked list */
|
|
/* 1st- Get current DMA data */
|
|
struct grpci2_bd_data * first_data = grpci2dma_channel_bd_get_data(chan);
|
|
if (first_data == NULL) {
|
|
/* Channel should always be pointing to a disabled descriptor */
|
|
DBG("Channel not pointing to disabled descpriptor\n");
|
|
return GRPCI2DMA_ERR_ERROR;
|
|
} else if (first_data == DISABLED_DESCRIPTOR){
|
|
/* No previous data. Cannot detach */
|
|
DBG("No data to detach.\n");
|
|
return GRPCI2DMA_ERR_NOTFOUND;
|
|
} else {
|
|
/* 2nd- Already available data, let's find the data */
|
|
if (first_data == data) {
|
|
/* 3rd- It is the first one. */
|
|
struct grpci2_bd_data *current = first_data;
|
|
struct grpci2_bd_data *next = grpci2dma_data_bd_get_next(current);
|
|
if (next != DISABLED_DESCRIPTOR){
|
|
/* There are more data */
|
|
/* Set channel next data descriptor to data*/
|
|
grpci2dma_channel_bd_set_data(chan, next);
|
|
/* Update the removed data */
|
|
grpci2dma_data_bd_set_next(data, DISABLED_DESCRIPTOR);
|
|
return GRPCI2DMA_ERR_OK;
|
|
}else{
|
|
/* No more data */
|
|
/* Clear DMA NBD */
|
|
grpci2dma_channel_bd_set_data(chan, DISABLED_DESCRIPTOR);
|
|
/* Update the removed data */
|
|
grpci2dma_data_bd_set_next(data, DISABLED_DESCRIPTOR);
|
|
return GRPCI2DMA_ERR_OK;
|
|
}
|
|
} else {
|
|
/* It is not the first data. Let's find it */
|
|
struct grpci2_bd_data * current = first_data;
|
|
struct grpci2_bd_data * next = grpci2dma_data_bd_get_next(current);
|
|
while( (next != data) && (next != DISABLED_DESCRIPTOR) &&
|
|
(next != NULL)){
|
|
current = next;
|
|
next = grpci2dma_data_bd_get_next(current);
|
|
}
|
|
if (next != data) {
|
|
DBG("Maximum DMA data exceeded. Maybe corrupted?\n");
|
|
return GRPCI2DMA_ERR_NOTFOUND;
|
|
} else {
|
|
/* Detach the data */
|
|
next = grpci2dma_data_bd_get_next(data);
|
|
grpci2dma_data_bd_set_next(current, next);
|
|
/* Update the removed data */
|
|
grpci2dma_data_bd_set_next(data, DISABLED_DESCRIPTOR);
|
|
return GRPCI2DMA_ERR_OK;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Iterate through all channel starting in FIRST_CHAN up to MAXINDEX
|
|
* and execute FUNC*/
|
|
UNUSED STATIC int grpci2dma_channel_list_foreach(
|
|
struct grpci2_bd_chan * first_chan,
|
|
int func( struct grpci2_bd_chan * chan), int maxindex)
|
|
{
|
|
if (maxindex <= 0) return 0;
|
|
if (first_chan == NULL) {
|
|
/* No previous channels */
|
|
return 0;
|
|
} else {
|
|
/* Available channels */
|
|
/* Iterate through next channels */
|
|
/* Use index to avoid having an infinite loop in case of corrupted
|
|
* channels */
|
|
int i=0;
|
|
int ret;
|
|
struct grpci2_bd_chan * curr_chan = first_chan;
|
|
struct grpci2_bd_chan * nchan;
|
|
do{
|
|
if (curr_chan == NULL) return GRPCI2DMA_ERR_WRONGPTR;
|
|
ret = func(curr_chan);
|
|
if (ret < 0){
|
|
/* error */
|
|
return ret;
|
|
}
|
|
nchan = grpci2dma_channel_bd_get_next(curr_chan);
|
|
curr_chan = nchan;
|
|
i++;
|
|
}while((curr_chan != first_chan) && (i < maxindex));
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/* Iterate through all data starting in FIRST_DATA up to MAXINDEX
|
|
* and execute FUNC*/
|
|
STATIC int grpci2dma_data_list_foreach(struct grpci2_bd_data * first_data,
|
|
int func( struct grpci2_bd_data * data), int maxindex)
|
|
{
|
|
if (maxindex <= 0) return 0;
|
|
if (first_data == NULL) return GRPCI2DMA_ERR_WRONGPTR;
|
|
/* Available data */
|
|
/* Iterate through next data */
|
|
/* Use index to avoid having an infinite loop in case of corrupted
|
|
* channels */
|
|
int i=0;
|
|
int ret;
|
|
struct grpci2_bd_data * curr_data = first_data;
|
|
struct grpci2_bd_data * ndata;
|
|
while((curr_data != DISABLED_DESCRIPTOR) && (i < maxindex)){
|
|
if (curr_data == NULL) return GRPCI2DMA_ERR_WRONGPTR;
|
|
ret = func(curr_data);
|
|
if (ret < 0){
|
|
/* error */
|
|
return ret;
|
|
}
|
|
ndata = grpci2dma_data_bd_get_next(curr_data);
|
|
curr_data = ndata;
|
|
i++;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*** END OF DESCRIPTOR LINKED-LIST HELPER FUNCTIONS ***/
|
|
|
|
/*** START OF DMACTRL ACCESS FUNCTIONS ***/
|
|
|
|
/* Initialize the DMA Ctrl*/
|
|
STATIC INLINE int grpci2dma_ctrl_init()
|
|
{
|
|
struct grpci2dma_priv *priv = grpci2dmapriv;
|
|
|
|
/* Clear DMA Control: clear IRQ and ERR status */
|
|
REG_WRITE(&priv->regs->dma_ctrl, 0|DMACTRL_SAFE|DMACTRL_CHIRQ|DMACTRL_ERR);
|
|
|
|
/* Clear DMA BASE */
|
|
REG_WRITE(&priv->regs->dma_bdbase, 0);
|
|
|
|
/* Clear DMA Chan */
|
|
REG_WRITE(&priv->regs->dma_chact, 0);
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/* Stop the DMA */
|
|
STATIC INLINE int grpci2dma_ctrl_stop( void )
|
|
{
|
|
struct grpci2dma_priv *priv = grpci2dmapriv;
|
|
|
|
/* Stop DMA */
|
|
unsigned int ctrl = REG_READ(&priv->regs->dma_ctrl);
|
|
REG_WRITE(&priv->regs->dma_ctrl, (ctrl & ~(DMACTRL_WCLEAR | DMACTRL_EN)) |
|
|
DMACTRL_DIS);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* Start the DMA */
|
|
STATIC INLINE int grpci2dma_ctrl_start( struct grpci2_bd_chan * chan)
|
|
{
|
|
struct grpci2dma_priv *priv = grpci2dmapriv;
|
|
|
|
/* Set BDBASE to linked list of chans */
|
|
REG_WRITE(&priv->regs->dma_bdbase, (unsigned int) chan);
|
|
|
|
/* Start DMA */
|
|
unsigned int ctrl = REG_READ(&priv->regs->dma_ctrl);
|
|
REG_WRITE(&priv->regs->dma_ctrl, (ctrl & ~(DMACTRL_WCLEAR | DMACTRL_DIS)) |
|
|
DMACTRL_EN);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* Resume the DMA */
|
|
STATIC INLINE int grpci2dma_ctrl_resume( void )
|
|
{
|
|
struct grpci2dma_priv *priv = grpci2dmapriv;
|
|
|
|
/* Resume DMA */
|
|
unsigned int ctrl = REG_READ(&priv->regs->dma_ctrl);
|
|
REG_WRITE(&priv->regs->dma_ctrl, (ctrl & ~(DMACTRL_WCLEAR | DMACTRL_DIS)) |
|
|
DMACTRL_EN);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* Interrupt status*/
|
|
STATIC INLINE int grpci2dma_ctrl_interrupt_status(void)
|
|
{
|
|
struct grpci2dma_priv *priv = grpci2dmapriv;
|
|
|
|
unsigned int ctrl = REG_READ(&priv->regs->dma_ctrl);
|
|
return (ctrl & DMACTRL_IE);
|
|
}
|
|
|
|
/* Enable interrupts */
|
|
STATIC INLINE int grpci2dma_ctrl_interrupt_enable(void)
|
|
{
|
|
struct grpci2dma_priv *priv = grpci2dmapriv;
|
|
|
|
unsigned int ctrl = REG_READ(&priv->regs->dma_ctrl);
|
|
if (ctrl & DMACTRL_IE){
|
|
/* Nothing to do. Already enabled */
|
|
return 0;
|
|
}
|
|
|
|
/* Clear pending CHIRQ and errors */
|
|
ctrl = ctrl | (DMACTRL_CHIRQ | DMACTRL_ERR);
|
|
|
|
/* Enable interrupts */
|
|
ctrl = ctrl | DMACTRL_IE;
|
|
|
|
REG_WRITE(&priv->regs->dma_ctrl, ctrl );
|
|
return 0;
|
|
}
|
|
|
|
/* Disable interrupts */
|
|
STATIC INLINE int grpci2dma_ctrl_interrupt_disable(void)
|
|
{
|
|
struct grpci2dma_priv *priv = grpci2dmapriv;
|
|
|
|
unsigned int ctrl = REG_READ(&priv->regs->dma_ctrl);
|
|
if ((ctrl & DMACTRL_IE) == 0){
|
|
/* Nothing to do. Already disabled */
|
|
return 0;
|
|
}
|
|
|
|
/* Clear pending CHIRQ and errors */
|
|
ctrl = ctrl | (DMACTRL_CHIRQ | DMACTRL_ERR);
|
|
|
|
/* Disable interrupts */
|
|
ctrl = ctrl & ~(DMACTRL_IE);
|
|
|
|
REG_WRITE(&priv->regs->dma_ctrl, ctrl );
|
|
return 0;
|
|
}
|
|
|
|
/* Clear interrupts */
|
|
STATIC INLINE int grpci2dma_ctrl_interrupt_clear(void)
|
|
{
|
|
struct grpci2dma_priv *priv = grpci2dmapriv;
|
|
|
|
unsigned int ctrl = REG_READ(&priv->regs->dma_ctrl);
|
|
REG_WRITE(&priv->regs->dma_ctrl, (ctrl | DMACTRL_ERR | DMACTRL_CHIRQ));
|
|
return 0;
|
|
}
|
|
|
|
STATIC INLINE unsigned int grpci2dma_ctrl_status()
|
|
{
|
|
struct grpci2dma_priv *priv = grpci2dmapriv;
|
|
|
|
/* Read DMA */
|
|
return (REG_READ(&priv->regs->dma_ctrl));
|
|
}
|
|
|
|
STATIC INLINE unsigned int grpci2dma_ctrl_base()
|
|
{
|
|
struct grpci2dma_priv *priv = grpci2dmapriv;
|
|
|
|
/* Read DMA */
|
|
return (REG_READ(&priv->regs->dma_bdbase));
|
|
}
|
|
|
|
UNUSED STATIC INLINE unsigned int grpci2dma_ctrl_active()
|
|
{
|
|
struct grpci2dma_priv *priv = grpci2dmapriv;
|
|
|
|
/* Read DMA */
|
|
return (REG_READ(&priv->regs->dma_chact));
|
|
}
|
|
|
|
/* Set the DMA CTRL register NUMCH field */
|
|
STATIC INLINE int grpci2dma_ctrl_numch_set(int numch)
|
|
{
|
|
struct grpci2dma_priv *priv = grpci2dmapriv;
|
|
|
|
unsigned int ctrl = REG_READ(&priv->regs->dma_ctrl);
|
|
|
|
/* Clear old value */
|
|
ctrl = (ctrl & ~(DMACTRL_NUMCH));
|
|
|
|
/* Put new value */
|
|
ctrl = (ctrl | ( (numch << DMACTRL_NUMCH_BIT) & DMACTRL_NUMCH));
|
|
|
|
REG_WRITE(&priv->regs->dma_ctrl, ctrl & ~(DMACTRL_WCLEAR));
|
|
return 0;
|
|
}
|
|
|
|
/*** END OF DMACTRL ACCESS FUNCTIONS ***/
|
|
|
|
/*** START OF DESCRIPTOR ACCESS FUNCTIONS ***/
|
|
|
|
STATIC int grpci2dma_data_bd_init(struct grpci2_bd_data * data,
|
|
uint32_t pci_adr, uint32_t ahb_adr, int dir, int endianness, int size,
|
|
struct grpci2_bd_data * next)
|
|
{
|
|
BD_WRITE(&data->ctrl, 0 |
|
|
(BD_DATA_EN) |
|
|
(BD_DATA_TYPE_DATA) |
|
|
(dir == GRPCI2DMA_AHBTOPCI? BD_DATA_DR:0) |
|
|
(endianness == GRPCI2DMA_LITTLEENDIAN? BD_DATA_BE:0) |
|
|
( (size << BD_DATA_LEN_BIT) & BD_DATA_LEN )
|
|
);
|
|
BD_WRITE(&data->pci_adr, pci_adr);
|
|
BD_WRITE(&data->ahb_adr, ahb_adr);
|
|
BD_WRITE(&data->next, (unsigned int) next);
|
|
return 0;
|
|
}
|
|
|
|
STATIC int grpci2dma_channel_bd_init(struct grpci2_bd_chan * chan)
|
|
{
|
|
BD_WRITE(&chan->ctrl, 0 | BD_CHAN_TYPE_DMA | BD_CHAN_EN);
|
|
BD_WRITE(&chan->nchan, (unsigned int) chan);
|
|
BD_WRITE(&chan->nbd, (unsigned int) DISABLED_DESCRIPTOR);
|
|
return 0;
|
|
}
|
|
|
|
/* Enable a channel with options.
|
|
* options include:
|
|
* - options & 0xFFFF: Maximum data descriptor count before
|
|
* moving to next DMA channel.
|
|
*/
|
|
STATIC int grpci2dma_channel_bd_enable(struct grpci2_bd_chan * chan,
|
|
unsigned int options)
|
|
{
|
|
unsigned int ctrl = BD_READ(&chan->ctrl);
|
|
ctrl = (ctrl & ~(BD_CHAN_BDCNT));
|
|
BD_WRITE(&chan->ctrl, (ctrl | BD_CHAN_EN |
|
|
( (options << BD_CHAN_BDCNT_BIT) & BD_CHAN_BDCNT)));
|
|
return 0;
|
|
}
|
|
|
|
/* Disable channel.
|
|
*/
|
|
STATIC int grpci2dma_channel_bd_disable(struct grpci2_bd_chan * chan)
|
|
{
|
|
unsigned int ctrl = BD_READ(&chan->ctrl);
|
|
BD_WRITE(&chan->ctrl, (ctrl & ~(BD_CHAN_EN)));
|
|
return 0;
|
|
}
|
|
|
|
/* Get the CID of a channel.
|
|
*/
|
|
UNUSED STATIC int grpci2dma_channel_bd_get_cid(struct grpci2_bd_chan * chan)
|
|
{
|
|
/* Get cid from chan */
|
|
unsigned ctrl = BD_READ(&chan->ctrl);
|
|
unsigned cid = (ctrl & (BD_CHAN_ID)) >> BD_CHAN_ID_BIT;
|
|
return cid;
|
|
}
|
|
|
|
/* Set the CID of a channel. */
|
|
STATIC void grpci2dma_channel_bd_set_cid(struct grpci2_bd_chan * chan, int cid)
|
|
{
|
|
/* Set cid from chan */
|
|
unsigned ctrl = BD_READ(&chan->ctrl);
|
|
ctrl = (ctrl & ~(BD_CHAN_ID)) | ((cid << BD_CHAN_ID_BIT) & BD_CHAN_ID);
|
|
BD_WRITE(&chan->ctrl,ctrl);
|
|
return;
|
|
}
|
|
|
|
/* Disable data descriptor*/
|
|
UNUSED STATIC int grpci2dma_data_bd_disable(struct grpci2_bd_data *desc)
|
|
{
|
|
BD_WRITE(&desc->ctrl,0);
|
|
return 0;
|
|
}
|
|
|
|
/* Return status of data descriptor*/
|
|
STATIC int grpci2dma_data_bd_status(struct grpci2_bd_data *desc)
|
|
{
|
|
int status = BD_READ(&desc->ctrl);
|
|
if (status & BD_DATA_ER) {
|
|
return GRPCI2DMA_BD_STATUS_ERR;
|
|
}else if (status & BD_DATA_EN) {
|
|
return GRPCI2DMA_BD_STATUS_ENABLED;
|
|
}else {
|
|
return GRPCI2DMA_BD_STATUS_DISABLED;
|
|
}
|
|
return GRPCI2DMA_BD_STATUS_ERR;
|
|
}
|
|
|
|
/* Enable interrupts in data descriptor*/
|
|
STATIC int grpci2dma_data_bd_interrupt_enable(struct grpci2_bd_data * data)
|
|
{
|
|
unsigned int ctrl = BD_READ(&data->ctrl);
|
|
BD_WRITE(&data->ctrl, ctrl | BD_DATA_IE);
|
|
return 0;
|
|
}
|
|
|
|
/* Get data descriptor */
|
|
STATIC struct grpci2_bd_data * grpci2dma_channel_bd_get_data(
|
|
struct grpci2_bd_chan * chan)
|
|
{
|
|
return (struct grpci2_bd_data *) BD_READ(&chan->nbd);
|
|
}
|
|
|
|
/* Set data descriptorl */
|
|
STATIC void grpci2dma_channel_bd_set_data(struct grpci2_bd_chan * chan,
|
|
struct grpci2_bd_data * data)
|
|
{
|
|
BD_WRITE(&chan->nbd, (unsigned int) data);
|
|
}
|
|
|
|
/* Get next channel */
|
|
STATIC struct grpci2_bd_chan * grpci2dma_channel_bd_get_next(
|
|
struct grpci2_bd_chan * chan)
|
|
{
|
|
return (struct grpci2_bd_chan *) BD_READ(&chan->nchan);
|
|
}
|
|
|
|
/* Get next data */
|
|
STATIC struct grpci2_bd_data * grpci2dma_data_bd_get_next(
|
|
struct grpci2_bd_data * data)
|
|
{
|
|
return (struct grpci2_bd_data *) BD_READ(&data->next);
|
|
}
|
|
|
|
/* Set next channel */
|
|
STATIC void grpci2dma_channel_bd_set_next(struct grpci2_bd_chan * chan,
|
|
struct grpci2_bd_chan * next)
|
|
{
|
|
BD_WRITE(&chan->nchan,(unsigned int) next);
|
|
}
|
|
|
|
/* Set next data */
|
|
STATIC void grpci2dma_data_bd_set_next(struct grpci2_bd_data * data,
|
|
struct grpci2_bd_data * next)
|
|
{
|
|
BD_WRITE(&data->next,(unsigned int) next);
|
|
}
|
|
|
|
/*** END OF DESCRIPTOR ACCESS FUNCTIONS ***/
|
|
|
|
/*** START OF CHANNEL FUNCTIONS ***/
|
|
|
|
STATIC int grpci2dma_channel_open(struct grpci2_bd_chan * chan, int cid)
|
|
{
|
|
struct grpci2dma_priv *priv = grpci2dmapriv;
|
|
int allocated = 0;
|
|
|
|
/* Get pointer */
|
|
if (chan == NULL) {
|
|
/* User does not provide channel, let's create it */
|
|
chan = grpci2dma_channel_new(1);
|
|
allocated = 1;
|
|
}else{
|
|
/* Make sure the pointer is not already on the linked list */
|
|
int i;
|
|
for (i=0; i<MAX_DMA_CHANS; i++){
|
|
if (priv->channel[i].ptr == chan){
|
|
return GRPCI2DMA_ERR_WRONGPTR;
|
|
}
|
|
}
|
|
}
|
|
|
|
DBG("Opening channel %d (0x%08x)\n", cid, (unsigned int) chan);
|
|
|
|
/* Init channel descriptor */
|
|
grpci2dma_channel_bd_init(chan);
|
|
|
|
/* Assign cid to chan */
|
|
priv->channel[cid].ptr = chan;
|
|
grpci2dma_channel_bd_set_cid(chan, cid);
|
|
|
|
/* Increase number of channels */
|
|
priv->nchans++;
|
|
|
|
DBG("number of channels: %d\n", priv->nchans);
|
|
|
|
/* Initialize channel data */
|
|
priv->channel[cid].allocated = allocated;
|
|
priv->channel[cid].active = 0;
|
|
|
|
/* Initialize record of last added data */
|
|
priv->channel[cid].lastdata = DISABLED_DESCRIPTOR;
|
|
|
|
return cid;
|
|
}
|
|
|
|
/* Get first free CID.
|
|
*/
|
|
STATIC int grpci2dma_channel_free_id()
|
|
{
|
|
struct grpci2dma_priv *priv = grpci2dmapriv;
|
|
|
|
/* Find the first free CID */
|
|
int i;
|
|
for (i=0; i<MAX_DMA_CHANS; i++){
|
|
if (priv->channel[i].ptr == NULL){
|
|
return i;
|
|
}
|
|
}
|
|
return GRPCI2DMA_ERR_TOOMANY;
|
|
}
|
|
|
|
/* Get the active channel circular linked list */
|
|
STATIC struct grpci2_bd_chan * grpci2dma_channel_get_active_list()
|
|
{
|
|
struct grpci2dma_priv *priv = grpci2dmapriv;
|
|
int i;
|
|
/* Just get the first non NULL associated cid */
|
|
for (i=0; i< MAX_DMA_CHANS; i++){
|
|
if ((priv->channel[i].ptr != NULL) && (priv->channel[i].active)){
|
|
return priv->channel[i].ptr;
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
/* Start a channel */
|
|
STATIC int grpci2dma_channel_start(int chan_no, int options)
|
|
{
|
|
struct grpci2dma_priv *priv = grpci2dmapriv;
|
|
struct grpci2_bd_chan *chan;
|
|
SPIN_IRQFLAGS(irqflags);
|
|
|
|
/* Get chan pointer */
|
|
chan = priv->channel[chan_no].ptr;
|
|
|
|
/* Check if channel is active */
|
|
if (priv->channel[chan_no].active){
|
|
/* nothing to do */
|
|
return GRPCI2DMA_ERR_OK;
|
|
}
|
|
|
|
/* Get the max descriptor count */
|
|
unsigned int desccnt;
|
|
if (options == 0){
|
|
/* Default */
|
|
desccnt = 0xffff;
|
|
}else{
|
|
desccnt = options & 0xffff;
|
|
}
|
|
|
|
/* Start the channel by enabling it.
|
|
* HWNOTE: In GRPCI2 this bit does not work as it is supposed.
|
|
* So we better add/remove the channel from the active linked
|
|
* list. */
|
|
grpci2dma_channel_bd_enable(chan, desccnt);
|
|
priv->channel[chan_no].active = 1;
|
|
priv->nactive++;
|
|
/* Get active linked list */
|
|
struct grpci2_bd_chan * list = grpci2dma_channel_get_active_list();
|
|
if (list == NULL){
|
|
/* No previous channels. New list */
|
|
list = chan;
|
|
}
|
|
/* Add channel from the linked list */
|
|
if (grpci2dma_channel_list_add(list, chan) < 0){
|
|
return GRPCI2DMA_ERR_ERROR;
|
|
}
|
|
|
|
/* Increase NUMCH in DMA ctrl */
|
|
SPIN_LOCK_IRQ(&priv->devlock, irqflags);
|
|
grpci2dma_ctrl_numch_set( (priv->nactive? priv->nactive -1:0));
|
|
|
|
/* Check if DMA is active */
|
|
if (!grpci2dma_active()){
|
|
/* Start DMA */
|
|
grpci2dma_ctrl_start(chan);
|
|
}
|
|
SPIN_UNLOCK_IRQ(&priv->devlock, irqflags);
|
|
|
|
DBG("Channel %d started (0x%08x)\n", chan_no, (unsigned int) chan);
|
|
|
|
return GRPCI2DMA_ERR_OK;
|
|
}
|
|
|
|
/* Stop a channel */
|
|
STATIC int grpci2dma_channel_stop(int chan_no)
|
|
{
|
|
struct grpci2dma_priv *priv = grpci2dmapriv;
|
|
struct grpci2_bd_chan *chan;
|
|
SPIN_IRQFLAGS(irqflags);
|
|
int resume;
|
|
|
|
/* Get chan pointer */
|
|
chan = priv->channel[chan_no].ptr;
|
|
|
|
/* Check if channel is active */
|
|
if (!priv->channel[chan_no].active){
|
|
/* nothing to do */
|
|
return GRPCI2DMA_ERR_OK;
|
|
}
|
|
|
|
/* First remove channel from the linked list */
|
|
if (grpci2dma_channel_list_remove(chan) < 0){
|
|
return GRPCI2DMA_ERR_ERROR;
|
|
}
|
|
|
|
/* Update driver struct */
|
|
priv->channel[chan_no].active = 0;
|
|
priv->nactive--;
|
|
|
|
/* Check if DMA is active and it the removed
|
|
* channel is the active */
|
|
resume = 0;
|
|
SPIN_LOCK_IRQ(&priv->devlock, irqflags);
|
|
if (grpci2dma_active() && (grpci2dma_ctrl_active() == (unsigned int)chan)){
|
|
/* We need to stop the DMA */
|
|
grpci2dma_ctrl_stop();
|
|
SPIN_UNLOCK_IRQ(&priv->devlock, irqflags);
|
|
/* Wait until DMA stops */
|
|
while (grpci2dma_active()){}
|
|
/* We need to check later to resume the DMA */
|
|
resume = 1;
|
|
}else{
|
|
SPIN_UNLOCK_IRQ(&priv->devlock, irqflags);
|
|
}
|
|
|
|
|
|
/* Now either the DMA is stopped, or it is processing
|
|
* a different channel and the removed channel is no
|
|
* longer in the linked list */
|
|
|
|
/* Now is safe to update the removed channel */
|
|
grpci2dma_channel_bd_set_next(chan, chan);
|
|
|
|
/* Stop the channel by disabling it.
|
|
* HWNOTE: In GRPCI2 this bit does not work as it is supposed.
|
|
* So we better remove the channel from the active linked
|
|
* list. */
|
|
grpci2dma_channel_bd_disable(chan);
|
|
|
|
/* Point channel to disabled descriptor */
|
|
grpci2dma_channel_bd_set_data(chan, DISABLED_DESCRIPTOR);
|
|
|
|
DBG("Channel %d stoped (0x%08x)\n", chan_no, (unsigned int) chan);
|
|
|
|
/* Decrease NUMCH in DMA ctrl */
|
|
SPIN_LOCK_IRQ(&priv->devlock, irqflags);
|
|
grpci2dma_ctrl_numch_set( (priv->nactive? priv->nactive -1:0));
|
|
|
|
/* Reactivate DMA only if we stopped */
|
|
if (resume){
|
|
/* We have two options, either we stopped when the active
|
|
* channel was still the active one, or we stopped when
|
|
* the active channel was a different one */
|
|
if (grpci2dma_ctrl_active() == (unsigned int) chan){
|
|
/* In this case, we need to start the DMA with
|
|
* any active channel on the list */
|
|
int i;
|
|
for (i=0; i<MAX_DMA_CHANS; i++){
|
|
if (priv->channel[i].active){
|
|
grpci2dma_ctrl_start(priv->channel[i].ptr);
|
|
break;
|
|
}
|
|
}
|
|
}else{
|
|
/* In this case, we need to resume the DMA operation */
|
|
/* HWNOTE: The GRPCI2 core does not update the channel next
|
|
* data descriptor if we stopped a channel. This means that
|
|
* we need to resume the DMA from the descriptor is was,
|
|
* by only setting the enable bit, and not changing the
|
|
* base register */
|
|
grpci2dma_ctrl_resume();
|
|
}
|
|
}
|
|
SPIN_UNLOCK_IRQ(&priv->devlock, irqflags);
|
|
|
|
return GRPCI2DMA_ERR_OK;
|
|
}
|
|
|
|
STATIC int grpci2dma_channel_push(int chan_no, void *dataptr, int index,
|
|
int ndata)
|
|
{
|
|
struct grpci2dma_priv *priv = grpci2dmapriv;
|
|
struct grpci2_bd_chan * chan;
|
|
struct grpci2_bd_data * data = dataptr;
|
|
|
|
/* Get channel */
|
|
chan = priv->channel[chan_no].ptr;
|
|
|
|
DBG("Pushing %d data (starting at 0x%08x) to channel %d (0x%08x)\n",
|
|
ndata, (unsigned int) &data[index], chan_no, (unsigned int) chan);
|
|
|
|
/* Get last added data */
|
|
struct grpci2_bd_data * last_added = priv->channel[chan_no].lastdata;
|
|
|
|
/* Add data to channel */
|
|
grpci2dma_data_list_add(chan, &data[index], last_added);
|
|
|
|
/* Update last added */
|
|
priv->channel[chan_no].lastdata = &data[index + ndata-1];
|
|
|
|
return GRPCI2DMA_ERR_OK;
|
|
}
|
|
|
|
STATIC int grpci2dma_channel_close(int chan_no)
|
|
{
|
|
struct grpci2dma_priv *priv = grpci2dmapriv;
|
|
struct grpci2_bd_chan * chan;
|
|
|
|
/* Get channel */
|
|
chan = priv->channel[chan_no].ptr;
|
|
|
|
DBG("Closing channel %d (0x%08x)\n", chan_no, (unsigned int) chan);
|
|
|
|
/* Stop channel */
|
|
if (grpci2dma_channel_stop(chan_no) != GRPCI2DMA_ERR_OK ){
|
|
DBG("Cannot stop channel!.\n");
|
|
return GRPCI2DMA_ERR_STOPDMA;
|
|
}
|
|
|
|
/* Unregister channel ISR */
|
|
grpci2dma_channel_isr_unregister(chan_no);
|
|
|
|
/* Free the cid */
|
|
priv->channel[chan_no].ptr = NULL;
|
|
|
|
/* Remove the ISR */
|
|
priv->channel[chan_no].isr = NULL;
|
|
priv->channel[chan_no].isr_arg = NULL;
|
|
|
|
/* Deallocate channel if needed */
|
|
if (priv->channel[chan_no].allocated){
|
|
grpci2dma_channel_delete((void *)chan);
|
|
}
|
|
|
|
/* Decrease number of channels */
|
|
priv->nchans--;
|
|
|
|
DBG("number of channels: %d\n", priv->nchans);
|
|
|
|
/* Everything OK */
|
|
return GRPCI2DMA_ERR_OK;
|
|
}
|
|
|
|
/* Register channel ISR */
|
|
STATIC int grpci2dma_channel_isr_unregister(int chan_no)
|
|
{
|
|
struct grpci2dma_priv *priv = grpci2dmapriv;
|
|
SPIN_IRQFLAGS(irqflags);
|
|
|
|
/* Unregister channel ISR */
|
|
priv->channel[chan_no].isr = NULL;
|
|
priv->channel[chan_no].isr_arg = NULL;
|
|
|
|
/* Unregister DMA ISR in GRPCI2 if needed */
|
|
priv->isr_registered--;
|
|
if(priv->isr_registered == 0){
|
|
/* Disable DMA Interrupts */
|
|
SPIN_LOCK_IRQ(&priv->devlock, irqflags);
|
|
grpci2dma_ctrl_interrupt_disable();
|
|
SPIN_UNLOCK_IRQ(&priv->devlock, irqflags);
|
|
(priv->isr_register)( NULL, NULL);
|
|
}
|
|
|
|
/* Everything OK */
|
|
return GRPCI2DMA_ERR_OK;
|
|
}
|
|
|
|
/*** END OF CHANNEL FUNCTIONS ***/
|
|
|
|
/*** START OF ISR FUNCTIONS ***/
|
|
|
|
/* PCI DMA Interrupt handler, called when there is a PCI DMA interrupt */
|
|
STATIC void grpci2dma_isr(void *arg)
|
|
{
|
|
struct grpci2dma_priv *priv = arg;
|
|
SPIN_ISR_IRQFLAGS(irqflags);
|
|
unsigned int ctrl = grpci2dma_ctrl_status();
|
|
/* Clear Interrupts */
|
|
SPIN_LOCK(&priv->devlock, irqflags);
|
|
grpci2dma_ctrl_interrupt_clear();
|
|
SPIN_UNLOCK(&priv->devlock, irqflags);
|
|
unsigned int sts = (ctrl & DMACTRL_CHIRQ) >> DMACTRL_CHIRQ_BIT;
|
|
unsigned int errsts = (ctrl & DMACTRL_ERR);
|
|
|
|
/* Error interrupt */
|
|
if(errsts){
|
|
/* Find which channels had the error.
|
|
* The GRPCI2DMA core does not indicate which channel
|
|
* had the error, so we need to get 1st the base descriptor register
|
|
* and see if it a channel. If is not a channel, then the active
|
|
* channel register tells us which channel is.
|
|
* After having the channel we need to find out which channel was. */
|
|
struct grpci2_bd_chan * chan =
|
|
(struct grpci2_bd_chan *) grpci2dma_ctrl_base();
|
|
/* Check if the base is a channel descriptor */
|
|
if ((BD_READ(&chan->ctrl) & BD_CHAN_TYPE) != BD_CHAN_TYPE_DMA){
|
|
/* Is not a channel, so the channel is in the channel active
|
|
* register */
|
|
chan = (struct grpci2_bd_chan *) grpci2dma_ctrl_active();
|
|
}
|
|
int i;
|
|
for (i=0; i<MAX_DMA_CHANS; i++){
|
|
if (chan == priv->channel[i].ptr){
|
|
/* Found */
|
|
if (priv->channel[i].isr != NULL){
|
|
(priv->channel[i].isr)(priv->channel[i].isr_arg,i,errsts);
|
|
}else{
|
|
printk("Unhandled GRPCI2 DMA error interrupt, sts:0x%02x\n", errsts);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
if (i == MAX_DMA_CHANS){
|
|
printk("Unhandled GRPCI2 DMA error interrupt , sts:0x%02x\n", errsts);
|
|
}
|
|
}
|
|
|
|
/* Normal packet interrupt */
|
|
int cid=0;
|
|
/* Find which channels have interrupts */
|
|
while(sts){
|
|
/* Find if current channel has an interrupt*/
|
|
if(sts & 0x1){
|
|
/* Find if current channel has an ISR */
|
|
if (priv->channel[cid].isr != NULL){
|
|
(priv->channel[cid].isr)(
|
|
priv->channel[cid].isr_arg, cid, errsts);
|
|
}else{
|
|
printk("Unhandled GRPCI2 DMA interrupt in channel %d, sts:0x%02x\n", cid, 0);
|
|
}
|
|
}
|
|
/* Next channel */
|
|
sts = sts >> 1;
|
|
cid++;
|
|
}
|
|
}
|
|
|
|
/*** END OF ISR FUNCTIONS ***/
|
|
|
|
/*** START OF DEBUG HELPERS ***/
|
|
#ifdef DEBUG
|
|
STATIC int grpci2dma_channel_print(struct grpci2_bd_chan * chan)
|
|
{
|
|
printf(" GRPCI2 DMA channel descriptor\n");
|
|
printf(" 0x%08x DMA channel control 0x%08x\n", (unsigned int) chan, chan->ctrl);
|
|
printf(" 31 en 0x%01x Channel descriptor enable.\n", (chan->ctrl >> 31) & (0x1));
|
|
printf(" 24:22 cid 0x%01x Channel ID.\n", (chan->ctrl >> 22) & (0x7));
|
|
printf(" 21:20 type 0x%01x Descriptor type. 01=DMA channel descriptor.\n", (chan->ctrl >> 20) & (0x3));
|
|
printf(" 15:0 dlen 0x%04x Data descriptor count.\n", (chan->ctrl >> 0) & (0xffff));
|
|
printf("\n");
|
|
printf(" 0x%08x Next DMA channel 0x%08x\n", (unsigned int) &(chan->nchan), chan->nchan);
|
|
printf(" 31:0 nc 0x%08x Next DMA channel.\n", chan->nchan);
|
|
printf("\n");
|
|
printf(" 0x%08x Next data descriptor 0x%08x\n" , (unsigned int) &(chan->nbd), chan->nbd);
|
|
printf(" 31:0 nd 0x%08x Next data descriptor.\n", chan->nbd);
|
|
printf("\n");
|
|
return 0;
|
|
}
|
|
|
|
STATIC int grpci2dma_data_print(struct grpci2_bd_data * data)
|
|
{
|
|
printf(" GRPCI2 DMA data descriptor\n");
|
|
printf(" 0x%08x DMA data control 0x%08x\n", (unsigned int) data, data->ctrl);
|
|
printf(" 31 en 0x%01x Data descriptor enable.\n" , (data->ctrl >> 31) & (0x1));
|
|
printf(" 30 ie 0x%01x Interrupt generation enable.\n" , (data->ctrl >> 30) & (0x1));
|
|
printf(" 29 dr 0x%01x Tranfer direction.\n" , (data->ctrl >> 29) & (0x1));
|
|
printf(" 28 be 0x%01x Bus endianess.\n" , (data->ctrl >> 28) & (0x1));
|
|
printf(" 21:20 type 0x%01x Descriptor type. 00=DMA data descriptor.\n" , (data->ctrl >> 20) & (0x3));
|
|
printf(" 19 er 0x%01x Error status.\n" , (data->ctrl >> 19) & (0x1));
|
|
printf(" 15:0 len 0x%04x Transfer lenght (in words) - 1.\n" , (data->ctrl >> 0) & (0xffff));
|
|
printf("\n");
|
|
printf(" 0x%08x 32-bit PCI start address 0x%08x\n" , (unsigned int) &(data->pci_adr), data->pci_adr);
|
|
printf(" 31:0 pa 0x%08x PCI address.\n" , data->pci_adr);
|
|
printf("\n");
|
|
printf(" 0x%08x 32-bit AHB start address 0x%08x\n" , (unsigned int) &(data->ahb_adr), data->ahb_adr);
|
|
printf(" 31:0 aa 0x%08x AHB address.\n" , data->ahb_adr);
|
|
printf("\n");
|
|
printf(" 0x%08x Next data descriptor 0x%08x\n" , (unsigned int) &(data->next), data->next);
|
|
printf(" 31:0 nd 0x%08x Next data descriptor.\n" , data->next);
|
|
printf("\n");
|
|
return 0;
|
|
}
|
|
#endif
|
|
/*** END OF DEBUG HELPERS ***/
|
|
|
|
/*** START OF MEMORY ALLOCATION FUNCTIONS ***/
|
|
|
|
void * grpci2dma_channel_new(int number)
|
|
{
|
|
/* Allocate memory */
|
|
unsigned int * orig_ptr = (unsigned int *) grlib_malloc(
|
|
(GRPCI2DMA_BD_CHAN_SIZE)*number + GRPCI2DMA_BD_CHAN_ALIGN);
|
|
if (orig_ptr == NULL) return NULL;
|
|
|
|
/* Get the aligned pointer */
|
|
unsigned int aligned_ptr = (
|
|
((unsigned int) orig_ptr + GRPCI2DMA_BD_CHAN_ALIGN) &
|
|
~(GRPCI2DMA_BD_CHAN_ALIGN - 1));
|
|
|
|
/* Save the original pointer just before the aligned pointer */
|
|
unsigned int ** tmp_ptr =
|
|
(unsigned int **) (aligned_ptr - sizeof(orig_ptr));
|
|
*tmp_ptr= orig_ptr;
|
|
|
|
/* Return aligned pointer */
|
|
return (void *) aligned_ptr;
|
|
}
|
|
|
|
void grpci2dma_channel_delete(void * chan)
|
|
{
|
|
/* Recover orignal pointer placed just before the aligned pointer */
|
|
unsigned int * orig_ptr;
|
|
unsigned int ** tmp_ptr = (unsigned int **) (chan - sizeof(orig_ptr));
|
|
orig_ptr = *tmp_ptr;
|
|
|
|
/* Deallocate memory */
|
|
free(orig_ptr);
|
|
}
|
|
|
|
void * grpci2dma_data_new(int number)
|
|
{
|
|
/* Allocate memory */
|
|
unsigned int * orig_ptr = (unsigned int *) grlib_malloc(
|
|
(GRPCI2DMA_BD_DATA_SIZE)*number + GRPCI2DMA_BD_DATA_ALIGN);
|
|
if (orig_ptr == NULL) return NULL;
|
|
|
|
/* Get the aligned pointer */
|
|
unsigned int aligned_ptr = (
|
|
((unsigned int) orig_ptr + GRPCI2DMA_BD_DATA_ALIGN) &
|
|
~(GRPCI2DMA_BD_DATA_ALIGN - 1));
|
|
|
|
/* Save the original pointer before the aligned pointer */
|
|
unsigned int ** tmp_ptr =
|
|
(unsigned int **) (aligned_ptr - sizeof(orig_ptr));
|
|
*tmp_ptr= orig_ptr;
|
|
|
|
/* Return aligned pointer */
|
|
return (void *) aligned_ptr;
|
|
}
|
|
|
|
void grpci2dma_data_delete(void * data)
|
|
{
|
|
/* Recover orignal pointer placed just before the aligned pointer */
|
|
unsigned int * orig_ptr;
|
|
unsigned int ** tmp_ptr = (unsigned int **) (data - sizeof(orig_ptr));
|
|
orig_ptr = *tmp_ptr;
|
|
|
|
/* Deallocate memory */
|
|
free(orig_ptr);
|
|
}
|
|
|
|
/*** END OF MEMORY ALLOCATION FUNCTIONS ***/
|
|
|
|
/*** START OF USER API ***/
|
|
|
|
/* Initialize GRPCI2 DMA: GRPCI2 DRIVER calls this
|
|
* using a weak function definition */
|
|
int grpci2dma_init(
|
|
void * regs, void isr_register( void (*isr)(void*), void * arg))
|
|
{
|
|
struct grpci2dma_priv *priv;
|
|
int i;
|
|
|
|
DBG("Registering GRPCI2 DMA driver with arg: 0x%08x\n",
|
|
(unsigned int) regs);
|
|
|
|
/* We only allow one GRPCI2 DMA */
|
|
if (grpci2dmapriv) {
|
|
DBG("Driver only supports one PCI DMA core\n");
|
|
return DRVMGR_FAIL;
|
|
}
|
|
|
|
/* Device Semaphore created with count = 1 */
|
|
if (rtems_semaphore_create(rtems_build_name('G', 'P', '2', 'D'), 1,
|
|
RTEMS_FIFO | RTEMS_SIMPLE_BINARY_SEMAPHORE | \
|
|
RTEMS_NO_INHERIT_PRIORITY | RTEMS_LOCAL | \
|
|
RTEMS_NO_PRIORITY_CEILING, 0, &grpci2dma_sem) != RTEMS_SUCCESSFUL)
|
|
return -1;
|
|
|
|
/* Allocate and init Memory for DMA */
|
|
priv = grlib_calloc(1, sizeof(*priv));
|
|
if (priv == NULL)
|
|
return DRVMGR_NOMEM;
|
|
|
|
priv->regs = regs;
|
|
strncpy(&priv->devname[0], "grpci2dma0", DEVNAME_LEN);
|
|
|
|
/* Initialize Spin-lock for GRPCI2dma Device. */
|
|
SPIN_INIT(&priv->devlock, priv->devname);
|
|
|
|
/* Channel Sempahores */
|
|
for (i=0; i<MAX_DMA_CHANS; i++){
|
|
/* set to NULL, they are created when openning channels */
|
|
priv->channel[i].sem = RTEMS_ID_NONE;
|
|
}
|
|
|
|
/* Register device */
|
|
grpci2dmapriv = priv;
|
|
|
|
/* Initialize Ctrl regs */
|
|
grpci2dma_ctrl_init();
|
|
|
|
/* Install DMA ISR */
|
|
priv->isr_register = isr_register;
|
|
|
|
/* Startup actions:
|
|
* - stop DMA
|
|
*/
|
|
grpci2dma_ctrl_stop();
|
|
|
|
return DRVMGR_OK;
|
|
}
|
|
|
|
/* Assign ISR Function to DMA IRQ */
|
|
int grpci2dma_isr_register(int chan_no, grpci2dma_isr_t dmaisr, void *data)
|
|
{
|
|
struct grpci2dma_priv *priv = grpci2dmapriv;
|
|
SPIN_IRQFLAGS(irqflags);
|
|
|
|
if (!priv){
|
|
/* DMA not initialized */
|
|
return GRPCI2DMA_ERR_NOINIT;
|
|
}
|
|
|
|
/* Check isr */
|
|
if (dmaisr == NULL){
|
|
/* No ISR */
|
|
return GRPCI2DMA_ERR_WRONGPTR;
|
|
}
|
|
|
|
/* Get chan pointer */
|
|
if ((chan_no < 0 ) || (chan_no >= MAX_DMA_CHANS)) {
|
|
/* Wrong channel id */
|
|
return GRPCI2DMA_ERR_WRONGPTR;
|
|
}
|
|
|
|
/* Check chan is open */
|
|
if (priv->channel[chan_no].ptr == NULL){
|
|
/* No channel */
|
|
return GRPCI2DMA_ERR_NOTFOUND;
|
|
}
|
|
|
|
/* Take driver lock - Wait until we get semaphore */
|
|
if (rtems_semaphore_obtain(grpci2dma_sem, RTEMS_WAIT, RTEMS_NO_TIMEOUT)
|
|
!= RTEMS_SUCCESSFUL){
|
|
return GRPCI2DMA_ERR_ERROR;
|
|
}
|
|
|
|
/* Take channel lock - Wait until we get semaphore */
|
|
if (rtems_semaphore_obtain(priv->channel[chan_no].sem, RTEMS_WAIT, RTEMS_NO_TIMEOUT)
|
|
!= RTEMS_SUCCESSFUL){
|
|
rtems_semaphore_release(grpci2dma_sem);
|
|
return GRPCI2DMA_ERR_ERROR;
|
|
}
|
|
|
|
/* Register channel ISR */
|
|
priv->channel[chan_no].isr_arg = data;
|
|
priv->channel[chan_no].isr = dmaisr;
|
|
|
|
/* Register DMA ISR in GRPCI2 if not done yet */
|
|
if(priv->isr_registered == 0){
|
|
(priv->isr_register)( grpci2dma_isr, (void *) priv);
|
|
/* Enable DMA Interrupts */
|
|
SPIN_LOCK_IRQ(&priv->devlock, irqflags);
|
|
grpci2dma_ctrl_interrupt_enable();
|
|
SPIN_UNLOCK_IRQ(&priv->devlock, irqflags);
|
|
}
|
|
priv->isr_registered++;
|
|
|
|
/* Release channel sempahore */
|
|
rtems_semaphore_release(priv->channel[chan_no].sem);
|
|
|
|
/* Release driver sempahore */
|
|
rtems_semaphore_release(grpci2dma_sem);
|
|
|
|
return GRPCI2DMA_ERR_OK;
|
|
}
|
|
|
|
/* Assign ISR Function to DMA IRQ */
|
|
int grpci2dma_isr_unregister(int chan_no)
|
|
{
|
|
struct grpci2dma_priv *priv = grpci2dmapriv;
|
|
int ret;
|
|
|
|
if (!priv){
|
|
/* DMA not initialized */
|
|
return GRPCI2DMA_ERR_NOINIT;
|
|
}
|
|
|
|
/* Get chan pointer */
|
|
if ((chan_no < 0 ) || (chan_no >= MAX_DMA_CHANS)) {
|
|
/* Wrong channel id */
|
|
return GRPCI2DMA_ERR_WRONGPTR;
|
|
}
|
|
|
|
/* Check chan is open */
|
|
if (priv->channel[chan_no].ptr == NULL){
|
|
/* No channel */
|
|
return GRPCI2DMA_ERR_NOTFOUND;
|
|
}
|
|
|
|
/* Get chan ISR */
|
|
if (priv->channel[chan_no].isr == NULL){
|
|
/* Nothing to do */
|
|
return GRPCI2DMA_ERR_OK;
|
|
}
|
|
|
|
/* Take driver lock - Wait until we get semaphore */
|
|
if (rtems_semaphore_obtain(grpci2dma_sem, RTEMS_WAIT, RTEMS_NO_TIMEOUT)
|
|
!= RTEMS_SUCCESSFUL){
|
|
return GRPCI2DMA_ERR_ERROR;
|
|
}
|
|
|
|
/* Take channel lock - Wait until we get semaphore */
|
|
if (rtems_semaphore_obtain(priv->channel[chan_no].sem, RTEMS_WAIT, RTEMS_NO_TIMEOUT)
|
|
!= RTEMS_SUCCESSFUL){
|
|
rtems_semaphore_release(grpci2dma_sem);
|
|
return GRPCI2DMA_ERR_ERROR;
|
|
}
|
|
|
|
/* Unregister channel ISR */
|
|
ret = grpci2dma_channel_isr_unregister(chan_no);
|
|
|
|
/* Release channel sempahore */
|
|
rtems_semaphore_release(priv->channel[chan_no].sem);
|
|
|
|
/* Release driver sempahore */
|
|
rtems_semaphore_release(grpci2dma_sem);
|
|
|
|
return ret;
|
|
}
|
|
|
|
int grpci2dma_open(void * chanptr)
|
|
{
|
|
struct grpci2dma_priv *priv = grpci2dmapriv;
|
|
int cid;
|
|
int ret;
|
|
|
|
if (!priv){
|
|
/* DMA not initialized */
|
|
return GRPCI2DMA_ERR_NOINIT;
|
|
}
|
|
|
|
/* Check alignment */
|
|
if (((unsigned int ) chanptr) & (GRPCI2DMA_BD_CHAN_ALIGN-1)) {
|
|
/* Channel is not properly aligned */
|
|
return GRPCI2DMA_ERR_WRONGPTR;
|
|
}
|
|
|
|
/* Take driver lock - Wait until we get semaphore */
|
|
if (rtems_semaphore_obtain(grpci2dma_sem, RTEMS_WAIT, RTEMS_NO_TIMEOUT)
|
|
!= RTEMS_SUCCESSFUL){
|
|
return GRPCI2DMA_ERR_ERROR;
|
|
}
|
|
|
|
/* Get free channel id */
|
|
cid = grpci2dma_channel_free_id();
|
|
if (cid < 0 ){
|
|
rtems_semaphore_release(grpci2dma_sem);
|
|
return GRPCI2DMA_ERR_TOOMANY;
|
|
}
|
|
|
|
/* Open channel */
|
|
ret = grpci2dma_channel_open((struct grpci2_bd_chan *) chanptr, cid);
|
|
|
|
/* Create channel semaphore with count = 1 */
|
|
if (ret >= 0){
|
|
if (rtems_semaphore_create(
|
|
rtems_build_name('P', 'D', '0', '0' + cid), 1,
|
|
RTEMS_FIFO | RTEMS_SIMPLE_BINARY_SEMAPHORE | \
|
|
RTEMS_NO_INHERIT_PRIORITY | RTEMS_LOCAL | \
|
|
RTEMS_NO_PRIORITY_CEILING, 0, &priv->channel[cid].sem
|
|
) != RTEMS_SUCCESSFUL) {
|
|
priv->channel[cid].sem = RTEMS_ID_NONE;
|
|
rtems_semaphore_release(grpci2dma_sem);
|
|
return GRPCI2DMA_ERR_ERROR;
|
|
}
|
|
}
|
|
|
|
/* Release driver semaphore */
|
|
rtems_semaphore_release(grpci2dma_sem);
|
|
|
|
/* Return channel id */
|
|
return ret;
|
|
}
|
|
|
|
int grpci2dma_close(int chan_no)
|
|
{
|
|
struct grpci2dma_priv *priv = grpci2dmapriv;
|
|
int ret;
|
|
|
|
if (!priv){
|
|
/* DMA not initialized */
|
|
return GRPCI2DMA_ERR_NOINIT;
|
|
}
|
|
|
|
/* Get chan pointer */
|
|
if ((chan_no < 0) || (chan_no >= MAX_DMA_CHANS)){
|
|
/* Wrong channel id */
|
|
return GRPCI2DMA_ERR_WRONGPTR;
|
|
}
|
|
|
|
/* Check chan is open */
|
|
if (priv->channel[chan_no].ptr == NULL){
|
|
/* No channel */
|
|
return GRPCI2DMA_ERR_NOTFOUND;
|
|
}
|
|
|
|
/* Take driver lock - Wait until we get semaphore */
|
|
if (rtems_semaphore_obtain(grpci2dma_sem, RTEMS_WAIT, RTEMS_NO_TIMEOUT)
|
|
!= RTEMS_SUCCESSFUL){
|
|
return GRPCI2DMA_ERR_ERROR;
|
|
}
|
|
|
|
/* Take channel lock - Wait until we get semaphore */
|
|
if (rtems_semaphore_obtain(priv->channel[chan_no].sem, RTEMS_WAIT, RTEMS_NO_TIMEOUT)
|
|
!= RTEMS_SUCCESSFUL){
|
|
rtems_semaphore_release(grpci2dma_sem);
|
|
return GRPCI2DMA_ERR_ERROR;
|
|
}
|
|
|
|
/* Close channel */
|
|
ret = grpci2dma_channel_close(chan_no);
|
|
|
|
/* Release channel sempahore */
|
|
rtems_semaphore_release(priv->channel[chan_no].sem);
|
|
|
|
/* Delete channel semaphore */
|
|
if (ret == GRPCI2DMA_ERR_OK){
|
|
if (rtems_semaphore_delete(priv->channel[chan_no].sem)
|
|
!= RTEMS_SUCCESSFUL){
|
|
/* Release driver semaphore */
|
|
rtems_semaphore_release(grpci2dma_sem);
|
|
return GRPCI2DMA_ERR_ERROR;
|
|
}
|
|
}
|
|
|
|
/* Release driver semaphore */
|
|
rtems_semaphore_release(grpci2dma_sem);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/* Transfer_size =0 means maximum */
|
|
int grpci2dma_prepare(
|
|
uint32_t pci_start, uint32_t ahb_start, int dir, int endianness,
|
|
int size, void * dataptr, int index, int ndata, int transfer_size)
|
|
{
|
|
struct grpci2_bd_data * data = dataptr;
|
|
|
|
/* Check data pointer */
|
|
if ((data == NULL) ||
|
|
(((unsigned int ) data) & (GRPCI2DMA_BD_DATA_ALIGN-1))){
|
|
return GRPCI2DMA_ERR_WRONGPTR;
|
|
}
|
|
|
|
/* Check indexes */
|
|
int maxdata = ndata - index;
|
|
if ((maxdata < 1) || (index < 0)){
|
|
/* No data descriptors to use */
|
|
return GRPCI2DMA_ERR_WRONGPTR;
|
|
}
|
|
|
|
/* Check PCI transfer size */
|
|
if ( (transfer_size < 0) ||
|
|
(transfer_size > MAX_DMA_TRANSFER_SIZE) ||
|
|
(transfer_size%4 != 0) ) {
|
|
return GRPCI2DMA_ERR_WRONGPTR;
|
|
}
|
|
if (transfer_size == 0){
|
|
transfer_size = MAX_DMA_TRANSFER_SIZE;
|
|
}
|
|
|
|
/* Check total size */
|
|
if ( (size <=0) || (size % 4 != 0)){
|
|
return GRPCI2DMA_ERR_WRONGPTR;
|
|
}
|
|
|
|
/* Calculate number of data descriptors needed */
|
|
int words = size/4;
|
|
int blocksize = transfer_size/4;
|
|
int datacnt = words/blocksize + (words%blocksize != 0? 1: 0);
|
|
/* Check that we can transfer the data */
|
|
if (datacnt > maxdata) {
|
|
return GRPCI2DMA_ERR_TOOMANY;
|
|
}
|
|
|
|
/* Prepare data descriptors */
|
|
int i;
|
|
uint32_t pci_adr;
|
|
uint32_t ahb_adr;
|
|
int remaining=words;
|
|
int datasize;
|
|
struct grpci2_bd_data * next;
|
|
for (i=0; i<datacnt; i++){
|
|
/* Get PCI and AHB start addresses */
|
|
pci_adr = pci_start + i*blocksize;
|
|
ahb_adr = ahb_start + i*blocksize;
|
|
/* Get current data size */
|
|
if (remaining >= blocksize){
|
|
datasize = blocksize - 1;
|
|
remaining -= blocksize;
|
|
} else {
|
|
datasize = remaining -1;
|
|
remaining = 0;
|
|
}
|
|
/* Get linked list pointers */
|
|
if (i == datacnt - 1){
|
|
/* Last transfer */
|
|
next = DISABLED_DESCRIPTOR;
|
|
}else{
|
|
next = &data[i+index+1];
|
|
}
|
|
/* Set Data descriptor */
|
|
grpci2dma_data_bd_init(&data[i+index], pci_adr, ahb_adr, dir, endianness, datasize, next);
|
|
}
|
|
/* Return number of transfers used */
|
|
return datacnt;
|
|
}
|
|
|
|
int grpci2dma_status(void *dataptr, int index, int ndata)
|
|
{
|
|
struct grpci2_bd_data * data = dataptr;
|
|
int i;
|
|
|
|
/* Check data pointer */
|
|
if ((data == NULL) ||
|
|
(((unsigned int ) data) & (GRPCI2DMA_BD_DATA_ALIGN-1))){
|
|
return GRPCI2DMA_ERR_WRONGPTR;
|
|
}
|
|
|
|
/* Check maxdata */
|
|
int maxdata = ndata - index;
|
|
if ((maxdata < 1) || (index < 0)){
|
|
/* No data descriptors to use */
|
|
return GRPCI2DMA_ERR_WRONGPTR;
|
|
}
|
|
|
|
/* Check status of all packets in transfer */
|
|
int status;
|
|
for (i=0; i< maxdata; i++){
|
|
status = grpci2dma_data_bd_status(&data[i+index]);
|
|
if (status == GRPCI2DMA_BD_STATUS_ERR){
|
|
/* Error in one packet, means error in transfer */
|
|
return status;
|
|
} else if (status == GRPCI2DMA_BD_STATUS_ENABLED){
|
|
/* If one packet is enabled, means transfer is not done */
|
|
return status;
|
|
}
|
|
}
|
|
|
|
/* If we reach here it means they are all disabled */
|
|
return status;
|
|
}
|
|
|
|
int grpci2dma_print(int chan_no)
|
|
{
|
|
struct grpci2dma_priv *priv = grpci2dmapriv;
|
|
struct grpci2_bd_chan * chan;
|
|
|
|
if (!priv){
|
|
/* DMA not initialized */
|
|
DBG("DMA not initialized.\n");
|
|
return GRPCI2DMA_ERR_NOINIT;
|
|
}
|
|
|
|
if ( (chan_no < 0) || (chan_no >= MAX_DMA_CHANS )){
|
|
/* Wrong chan no*/
|
|
return GRPCI2DMA_ERR_WRONGPTR;
|
|
}
|
|
|
|
chan = priv->channel[chan_no].ptr;
|
|
if (chan == NULL) {
|
|
return GRPCI2DMA_ERR_WRONGPTR;
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
/* Print channel state */
|
|
grpci2dma_channel_print(chan);
|
|
|
|
/* Get current DATA desc */
|
|
struct grpci2_bd_data * first_data = (struct grpci2_bd_data *) BD_READ(&chan->nbd);
|
|
|
|
/* Print data state */
|
|
grpci2dma_data_list_foreach(first_data, grpci2dma_data_print, MAX_DMA_DATA);
|
|
#endif
|
|
return GRPCI2DMA_ERR_OK;
|
|
}
|
|
|
|
int grpci2dma_print_bd(void * dataptr)
|
|
{
|
|
struct grpci2dma_priv *priv = grpci2dmapriv;
|
|
struct grpci2_bd_data * data = (struct grpci2_bd_data *) dataptr;
|
|
|
|
if (!priv){
|
|
/* DMA not initialized */
|
|
DBG("DMA not initialized.\n");
|
|
return GRPCI2DMA_ERR_NOINIT;
|
|
}
|
|
|
|
if ( data == NULL ){
|
|
/* Wrong chan no*/
|
|
return GRPCI2DMA_ERR_WRONGPTR;
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
/* Print data state */
|
|
grpci2dma_data_list_foreach(data, grpci2dma_data_print, MAX_DMA_DATA);
|
|
#endif
|
|
return GRPCI2DMA_ERR_OK;
|
|
}
|
|
|
|
int grpci2dma_interrupt_enable(
|
|
void *dataptr, int index, int maxindex, int options)
|
|
{
|
|
struct grpci2_bd_data * data = dataptr;
|
|
struct grpci2dma_priv *priv = grpci2dmapriv;
|
|
SPIN_IRQFLAGS(irqflags);
|
|
|
|
if (!priv){
|
|
/* DMA not initialized */
|
|
return GRPCI2DMA_ERR_NOINIT;
|
|
}
|
|
|
|
/* Check data pointer */
|
|
if ((data == NULL) ||
|
|
(((unsigned int ) data) & (GRPCI2DMA_BD_DATA_ALIGN-1))){
|
|
return GRPCI2DMA_ERR_WRONGPTR;
|
|
}
|
|
|
|
/* Check index */
|
|
if ((index < 0) || (maxindex < 1) || (index >= maxindex)){
|
|
/* No data descriptors to use */
|
|
return GRPCI2DMA_ERR_WRONGPTR;
|
|
}
|
|
|
|
if (options & GRPCI2DMA_OPTIONS_ALL){
|
|
/* Enable all interrupts */
|
|
if (grpci2dma_data_list_foreach(
|
|
&data[index],
|
|
grpci2dma_data_bd_interrupt_enable, maxindex -index)){
|
|
return GRPCI2DMA_ERR_ERROR;
|
|
}
|
|
}else{
|
|
/* Enable one packet interrupts */
|
|
grpci2dma_data_bd_interrupt_enable(&data[index]);
|
|
}
|
|
|
|
/* Finally enable DMA interrupts if they are not already enabled */
|
|
if (grpci2dma_ctrl_interrupt_status()==0){
|
|
SPIN_LOCK_IRQ(&priv->devlock, irqflags);
|
|
grpci2dma_ctrl_interrupt_enable();
|
|
SPIN_UNLOCK_IRQ(&priv->devlock, irqflags);
|
|
}
|
|
|
|
DBG("Interrupts enabled for data (0x%08x), index:%d, maxindex:%d, %s.\n",
|
|
(unsigned int) data, index, maxindex,
|
|
(options & GRPCI2DMA_OPTIONS_ALL)? "ALL":"ONE" );
|
|
|
|
return GRPCI2DMA_ERR_OK;
|
|
}
|
|
|
|
int grpci2dma_push(int chan_no, void *dataptr, int index, int ndata)
|
|
{
|
|
struct grpci2dma_priv *priv = grpci2dmapriv;
|
|
SPIN_IRQFLAGS(irqflags);
|
|
int ret;
|
|
|
|
if (!priv){
|
|
/* DMA not initialized */
|
|
return GRPCI2DMA_ERR_NOINIT;
|
|
}
|
|
|
|
/* Check data pointer */
|
|
if ((dataptr == NULL) ||
|
|
(((unsigned int ) dataptr) & (GRPCI2DMA_BD_DATA_ALIGN-1))){
|
|
return GRPCI2DMA_ERR_WRONGPTR;
|
|
}
|
|
|
|
/* Check index */
|
|
if ((ndata < 1) || (index < 0)){
|
|
/* No data descriptors to use */
|
|
return GRPCI2DMA_ERR_WRONGPTR;
|
|
}
|
|
|
|
/* Check chan_no */
|
|
if ( (chan_no < 0) || (chan_no >= MAX_DMA_CHANS )){
|
|
/* Wrong chan no*/
|
|
return GRPCI2DMA_ERR_WRONGPTR;
|
|
}
|
|
|
|
/* Check chan is open */
|
|
if (priv->channel[chan_no].ptr == NULL){
|
|
/* No channel */
|
|
return GRPCI2DMA_ERR_NOTFOUND;
|
|
}
|
|
|
|
/* Take channel lock - Wait until we get semaphore */
|
|
if (rtems_semaphore_obtain(priv->channel[chan_no].sem, RTEMS_WAIT, RTEMS_NO_TIMEOUT)
|
|
!= RTEMS_SUCCESSFUL){
|
|
return GRPCI2DMA_ERR_ERROR;
|
|
}
|
|
|
|
/* push data to channel */
|
|
ret = grpci2dma_channel_push(chan_no, dataptr, index, ndata);
|
|
|
|
if (ret != GRPCI2DMA_ERR_OK){
|
|
/* Release channel lock */
|
|
rtems_semaphore_release(priv->channel[chan_no].sem);
|
|
return ret;
|
|
}
|
|
|
|
/* Start DMA if it is not active and channel is active*/
|
|
SPIN_LOCK_IRQ(&priv->devlock, irqflags);
|
|
if ((!grpci2dma_active()) && (priv->channel[chan_no].active)){
|
|
grpci2dma_ctrl_start(priv->channel[chan_no].ptr);
|
|
}
|
|
SPIN_UNLOCK_IRQ(&priv->devlock, irqflags);
|
|
|
|
/* Release channel lock */
|
|
rtems_semaphore_release(priv->channel[chan_no].sem);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/* Start the channel */
|
|
int grpci2dma_start(int chan_no, int options)
|
|
{
|
|
struct grpci2dma_priv *priv = grpci2dmapriv;
|
|
int ret;
|
|
|
|
if (!priv){
|
|
/* DMA not initialized */
|
|
return GRPCI2DMA_ERR_NOINIT;
|
|
}
|
|
|
|
if ((chan_no < 0 ) || (chan_no >= MAX_DMA_CHANS )) {
|
|
/* Wrong channel id */
|
|
return GRPCI2DMA_ERR_WRONGPTR;
|
|
}
|
|
|
|
if ( options < 0 ) {
|
|
/* Wrong options */
|
|
return GRPCI2DMA_ERR_WRONGPTR;
|
|
}
|
|
|
|
/* Check chan is open */
|
|
if (priv->channel[chan_no].ptr == NULL){
|
|
/* No channel */
|
|
return GRPCI2DMA_ERR_NOTFOUND;
|
|
}
|
|
|
|
/* Take driver lock - Wait until we get semaphore */
|
|
if (rtems_semaphore_obtain(grpci2dma_sem, RTEMS_WAIT, RTEMS_NO_TIMEOUT)
|
|
!= RTEMS_SUCCESSFUL){
|
|
return GRPCI2DMA_ERR_ERROR;
|
|
}
|
|
|
|
/* Take channel lock - Wait until we get semaphore */
|
|
if (rtems_semaphore_obtain(priv->channel[chan_no].sem, RTEMS_WAIT, RTEMS_NO_TIMEOUT)
|
|
!= RTEMS_SUCCESSFUL){
|
|
rtems_semaphore_release(grpci2dma_sem);
|
|
return GRPCI2DMA_ERR_ERROR;
|
|
}
|
|
|
|
/* Start the channel */
|
|
ret = grpci2dma_channel_start(chan_no, options);
|
|
|
|
/* Release channel lock */
|
|
rtems_semaphore_release(priv->channel[chan_no].sem);
|
|
|
|
/* Release driver lock */
|
|
rtems_semaphore_release(grpci2dma_sem);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/* Stop the channel, but don't stop ongoing transfers! */
|
|
int grpci2dma_stop(int chan_no)
|
|
{
|
|
struct grpci2dma_priv *priv = grpci2dmapriv;
|
|
int ret;
|
|
|
|
if (!priv){
|
|
/* DMA not initialized */
|
|
return GRPCI2DMA_ERR_NOINIT;
|
|
}
|
|
|
|
if ((chan_no < 0 ) || (chan_no >= MAX_DMA_CHANS)) {
|
|
/* Wrong channel id */
|
|
return GRPCI2DMA_ERR_WRONGPTR;
|
|
}
|
|
|
|
/* Check chan is open */
|
|
if (priv->channel[chan_no].ptr == NULL){
|
|
/* No channel */
|
|
return GRPCI2DMA_ERR_NOTFOUND;
|
|
}
|
|
|
|
/* Take driver lock - Wait until we get semaphore */
|
|
if (rtems_semaphore_obtain(grpci2dma_sem, RTEMS_WAIT, RTEMS_NO_TIMEOUT)
|
|
!= RTEMS_SUCCESSFUL){
|
|
return GRPCI2DMA_ERR_ERROR;
|
|
}
|
|
|
|
/* Take channel lock - Wait until we get semaphore */
|
|
if (rtems_semaphore_obtain(priv->channel[chan_no].sem, RTEMS_WAIT, RTEMS_NO_TIMEOUT)
|
|
!= RTEMS_SUCCESSFUL){
|
|
rtems_semaphore_release(grpci2dma_sem);
|
|
return GRPCI2DMA_ERR_ERROR;
|
|
}
|
|
|
|
/* Stop the channel */
|
|
ret = grpci2dma_channel_stop(chan_no);
|
|
|
|
/* Release channel lock */
|
|
rtems_semaphore_release(priv->channel[chan_no].sem);
|
|
|
|
/* Release driver lock */
|
|
rtems_semaphore_release(grpci2dma_sem);
|
|
|
|
return ret;
|
|
}
|
|
|
|
int grpci2dma_active()
|
|
{
|
|
return ((grpci2dma_ctrl_status()) & DMACTRL_ACT) >> DMACTRL_ACT_BIT;
|
|
}
|
|
|