Support functions forthe Intel 80310 DMA channels
==================================================

Dave Jiang <dave.jiang@intel.com>
Last updated: 09/18/2001

The Intel 80310 XScale chipset provides 3 DMA channels via the 80312 I/O
companion chip. Two of them resides on the primary PCI bus and one on the 
secondary PCI bus. 

The DMA API provided is not compatible with the generic interface in the 
ARM tree unfortunately due to how the 80312 DMACs work. Hopefully some time 
in the near future a software interface can be done to bridge the differences.
The DMA API has been modeled after Nicholas Pitre's SA11x0 DMA API therefore 
they will look somewhat similar.  


80310 DMA API
-------------

int dma_request(dmach_t channel, const char *device_id);

This function will attempt to allocate the channel depending on what the 
user requests:

IOP310_DMA_P0: PCI Primary 1
IOP310_DMA_P1: PCI Primary 2
IOP310_DMA_S0: PCI Secondary 1 
/*EOF*/

Once the user allocates the DMA channel it is owned until released. Although 
other users can also use the same DMA channel, but no new resources will be
allocated. The function will return the allocated channel number if successful.

int dma_queue_buffer(dmach_t channel, dma_sghead_t *listhead);

The user will construct a SGL in the form of below:
/*
 * Scattered Gather DMA List for user
 */
typedef struct _dma_desc
{
    u32  NDAR;       /* next descriptor adress [READONLY] */
    u32  PDAR;       /* PCI address */
    u32  PUADR;      /* upper PCI address */
    u32  LADR;       /* local address */
    u32  BC;         /* byte count */
    u32  DC;         /* descriptor control */
} dma_desc_t;

typedef struct _dma_sgl
{
    dma_desc_t      dma_desc;     /* DMA descriptor */
    u32             status;       /* descriptor status [READONLY] */
    u32	    	    data;	  /* user defined data */
    struct _dma_sgl *next;	  /* next descriptor [READONLY] */
} dma_sgl_t;

/* dma sgl head */
typedef struct _dma_head
{
    u32		    total;	/* total elements in SGL */
    u32		    status;	/* status of sgl */
    u32		    mode;	/* read or write mode */
    dma_sgl_t	    *list;	/* pointer to list */
    dma_callback_t  callback;   /* callback function */
} dma_head_t;


The user shall allocate user SGL elements by calling the function:
dma_get_buffer(). This function will give the user an SGL element. The user
is responsible for creating the SGL head however. The user is also
responsible for allocating the memory for DMA data. The following code segment
shows how a DMA operation can be performed:

#include <asm/arch/iop310-dma.h>

void dma_test(void)
{    
	char dev_id[] = "Primary 0";
	dma_head_t *sgl_head = NULL;
	dma_sgl_t *sgl = NULL;
	int err = 0;
	int channel = -1;
	u32 *test_ptr = 0;
	DECLARE_WAIT_QUEUE_HEAD(wait_q);


	*(IOP310_ATUCR) = (IOP310_ATUCR_PRIM_OUT_ENAB | 
			IOP310_ATUCR_DIR_ADDR_ENAB); 
  
	channel = dma_request(IOP310_DMA_P0, dev_id);

	sgl_head = (dma_head_t *)kmalloc(sizeof(dma_head_t), GFP_KERNEL);
	sgl_head->callback = NULL;      /* no callback created */
	sgl_head->total = 2; /* allocating 2 DMA descriptors */
	sgl_head->mode = (DMA_MOD_WRITE);
	sgl_head->status = 0;
    
	/* now we get the two descriptors */
	sgl = dma_get_buffer(channel, 2);
	
    	/* we set the header to point to the list we allocated */
    	sgl_head->list = sgl;

	/* allocate 1k of DMA data */
    	sgl->data = (u32)kmalloc(1024, GFP_KERNEL);

    	/* Local address is physical */
	sgl->dma_desc.LADR = (u32)virt_to_phys(sgl->data);

	/* write to arbitrary location over the PCI bus */
    	sgl->dma_desc.PDAR = 0x00600000;
	sgl->dma_desc.PUADR = 0;
	sgl->dma_desc.BC = 1024;

    	/* set write & invalidate PCI command */
	sgl->dma_desc.DC = DMA_DCR_PCI_MWI;
	sgl->status = 0;

    	/* set a pattern */
    	memset(sgl->data, 0xFF, 1024);

	/* User's responsibility to keep buffers cached coherent */
	cpu_dcache_clean(sgl->data, sgl->data + 1024);

	sgl = sgl->next;

	sgl->data = (u32)kmalloc(1024, GFP_KERNEL);
	sgl->dma_desc.LADR = (u32)virt_to_phys(sgl->data);
	sgl->dma_desc.PDAR = 0x00610000;
	sgl->dma_desc.PUADR = 0;
	sgl->dma_desc.BC = 1024;

	/* second descriptor has interrupt flag enabled */
	sgl->dma_desc.DC = (DMA_DCR_PCI_MWI | DMA_DCR_IE);

	/* must set end of chain flag */
	sgl->status = DMA_END_CHAIN; /* DO NOT FORGET THIS!!!! */

    	memset(sgl->data, 0x0f, 1024);
	/* User's responsibility to keep buffers cached coherent */
	cpu_dcache_clean(sgl->data, sgl->data + 1024);
	
    	/* queing the buffer, this function will sleep since no callback */
    	err = dma_queue_buffer(channel, sgl_head); 
   
    	/* now we are woken from DMA complete */
    
    	/* do data operations here */
    
    	/* free DMA data if necessary */
    
	/* return the descriptors */
	dma_return_buffer(channel, sgl_head->list);

	/* free the DMA */
	dma_free(channel);

	kfree((void *)sgl_head);
} 


dma_sgl_t * dma_get_buffer(dmach_t channel, int buf_num);

This call allocates DMA descriptors for the user.


void dma_return_buffer(dmach_t channel, dma_sgl_t *list);

This call returns the allocated descriptors back to the API. 


int dma_suspend(dmach_t channel);

This call suspends any DMA transfer on the given channel.



int dma_resume(dmach_t channel);

This call resumes a DMA transfer which would have been stopped through
dma_suspend().


int dma_flush_all(dmach_t channel);

This completely flushes all queued buffers and on-going DMA transfers on a
given channel. This is called when DMA channel errors have occured.


void dma_free(dmach_t channel);

This clears all activities on a given DMA channel and releases it for future 
requests.



Buffer Allocation
-----------------
It is the user's responsibility to allocate, free, and keep track of the 
allocated DMA data memory. Upon calling dma_queue_buffer() the user must
relinquish the control of the buffers to the kernel and not change the 
state of the buffers that it has passed to the kernel. The user will regain
the control of the buffers when it has been woken up by the bottom half of
the DMA interrupt handler. The user can allocate cached buffers or non-cached
via pci_alloc_consistent(). It is the user's responsibility to ensure that
the data is cache coherent.

*Reminder*
The user is responsble to ensure the ATU is setup properly for DMA transfers.

All Disclaimers apply. Use this at your own discretion. Neither Intel nor I
will be responsible ifanything goes wrong.
