📄 at91_pdc.h
字号:
/*----------------------------------------------------------
** linux/drivers/at91/at91_pdc.h(DMA)
**
**
** Copyright (C) 2006 Hyesco Technology Co.,Ltd
**
** Author: casiawu <wujh@hyesco.com>
**
** History:
**
** 2006.4 casiawu <wujh@hyesco.com>
** Original version
**---------------------------------------------------------*/
#ifndef __AT91_PDC_H_
#define __AT91_PDC_H_
#include <linux/types.h>
#include <linux/dma-mapping.h>
#include <asm/semaphore.h>
#include <asm/mach-types.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#include <asm/arch/hardware.h>
#include <asm/arch/AT91RM9200_PDC.h>
#include <asm/irq.h>
#undef DEBUG
//#define DEBUG 1
#ifdef DEBUG
#define DPRINTK( x... ) printk(x)
#else
#define DPRINTK( x... )
#endif
#define dma_regs_t AT91PS_PDC
/* Maximum physical DMA buffer size */
//#define MAX_DMA_SIZE (16*1024)
/* Typedefs for integer types */
typedef unsigned char U8; /* unsigned 8 bit data */
typedef unsigned short U16; /* unsigned 16 bit data */
typedef unsigned long U32; /* unsigned 32 bit data */
typedef signed char S8; /* signed 8 bit data */
typedef short S16; /* signed 16 bit data */
typedef long S32; /* signed 32 bit data */
typedef void (*dma_callback_t)( void *buf_id, int size );
typedef void (*dma_readstat_t)( U8 * size );
typedef void (*dma_irq_t)( U8 size );
/*
* DMA buffer structure
*/
typedef struct dma_buf_s {
U32 size; /* buffer size */
dma_addr_t dma_start; /* starting DMA address */
dma_addr_t dma_ptr; /* next DMA pointer to use */
void *id; /* to identify buffer from outside */
struct dma_buf_s *next; /* next buffer to process */
} dma_buf_t;
/*
* DMA channel structure.
*/
typedef struct {
dma_buf_t *head; /* where to insert buffers */
dma_buf_t *tail; /* where to remove buffers */
dma_buf_t *curr; /* buffer currently DMA'ed */
dma_regs_t regs; /* points to appropriate DMA registers */
dma_irq_t irq_on_off; /* ... enable or disable irq */
dma_callback_t callback; /* ... to call when buffers are done */
U8 flag; /* 0 : wtite dma channel; 1: read dma channel */
U8 type; /* dma transmit type,1 : byte; 2: half word 4: word*/
U8 dma_channel; /* dma channel,0 : 1 : 2 */
} at91rm9200_dma_t;
/*
* DMA channel user state stucture
*/
typedef struct {
U32 in_use; /* Device is allocated */
const char * device_id; /* Device name */
U8 irq; /* IRQ used by the channel */
at91rm9200_dma_t* out_chan; /* DMA output channel */
at91rm9200_dma_t* in_chan; /* DMA output channel */
dma_readstat_t readstatback ; /* read device status reg */
} at91rm9200_dma_state_t;
/*-----------------------------------------------------
* Name : at91rm9200_start_dma()
* Function: Process the dma transmition
* Last rework data: 06-04-13 wujh@hyesco.com
*----------------------------------------------------*/
static int at91rm9200_start_dma(at91rm9200_dma_t * dma, dma_buf_t *buf1, dma_buf_t *buf2, int flag)
{
dma_regs_t regs = dma->regs;
U8 channel = dma->flag ;
int type=0;
type = dma->type;
DPRINTK("coming into at91rm9200_start_dma\n\r");
//rx
if(channel)
{
if(buf2 != NULL)
{
regs->PDC_RPR = buf1->dma_ptr;
regs->PDC_RCR = buf1->size / type;
regs->PDC_RNPR = buf2->dma_ptr;
regs->PDC_RNCR = buf2->size / type;
}
else
{
if(flag == 2)
{
regs->PDC_RPR = buf1->dma_ptr;
regs->PDC_RCR = buf1->size / type;
}
else
{
regs->PDC_RNPR = buf1->dma_ptr;
regs->PDC_RNCR = buf1->size / type;
}
}
}
//tx
else
{
if(buf2 != NULL)
{
regs->PDC_TPR = buf1->dma_ptr;
regs->PDC_TCR = buf1->size / type;
regs->PDC_TNPR = buf2->dma_ptr;
regs->PDC_TNCR = buf2->size / type;
}
else
{
if(flag == 2)
{
regs->PDC_TPR = buf1->dma_ptr;
regs->PDC_TCR = buf1->size / type;
}
else
{
regs->PDC_TNPR = buf1->dma_ptr;
regs->PDC_TNCR = buf1->size / type;
}
}
}
wmb();
if ((dma->irq_on_off) && (flag == 2))
dma->irq_on_off(1);
return 0;
}
/*-----------------------------------------------------
* Name : process_dma()
* Function: Set the callback function for dma
* Last rework data: 06-04-13 wujh@hyesco.com
*----------------------------------------------------*/
/* This must be called with IRQ disabled */
static void process_dma(at91rm9200_dma_t * dma)
{
dma_buf_t *buf;
int type=0;
type = dma->type;
DPRINTK("coming into process_dma\n\r");
for (;;) {
buf = dma->tail;
if (buf == NULL ) break; // exit the cycle
/*
* Let's try to start DMA on the current buffer.
* If DMA is busy then we break here.
*/
switch(dma->dma_channel)
{
case 2:
if(buf->next == NULL)
{
at91rm9200_start_dma(dma, buf,NULL,2);
dma->tail = buf->next;
}
else
{
at91rm9200_start_dma(dma, buf,buf->next,2);
dma->tail = buf->next->next;
}
dma->dma_channel = 0;
break;
case 1:
at91rm9200_start_dma(dma, buf,NULL,1);
dma->tail = buf->next;
dma->dma_channel = 0;
break;
case 0:
default:
return;
}
if (!dma->curr)
dma->curr = buf;
}
DPRINTK("exit process_dma\n\r");
}
/*-----------------------------------------------------
* Name : at91rm9200_dma_done()
* Function: Do some addition work after the transmit have done
* Last rework data: 06-04-13 wujh@hyesco.com
*----------------------------------------------------*/
void at91rm9200_dma_done(at91rm9200_dma_t *dma)
{
dma_buf_t *buf = dma->curr;
DPRINTK("coming into at91rm9200_dma_done\n\r");
if ((dma->irq_on_off) &&(dma->dma_channel == 2))
dma->irq_on_off(0);
if (buf != NULL)
{
/*
* Current buffer is done.
* Move current reference to the next one and send
* the processed buffer to the callback function,
* then discard it.
*/
dma->curr = buf->next;
if (dma->head == buf)
dma->head = NULL;
if (dma->callback)
{
dma->callback(buf->id, buf->size);
}
kfree(buf);
}
wmb();
process_dma(dma);
}
/*-----------------------------------------------------
* Name : at91rm9200_dma_queue_buffer()
* Function: Place the buffer to DMA handle queue
* Last rework data: 06-04-13 wujh@hyesco.com
*----------------------------------------------------*/
int at91rm9200_dma_queue_buffer(at91rm9200_dma_t *dma, void *buf_id,
dma_addr_t data, int size)
{
dma_buf_t *buf;
int flags;
DPRINTK("at91rm9200_dma_queue_buffer\n\r");
buf = kmalloc(sizeof(*buf), GFP_ATOMIC);
if (!buf)
return -ENOMEM;
buf->next = NULL;
buf->dma_ptr = buf->dma_start = data;
buf->size = size;
buf->id = buf_id;
local_irq_save(flags);
if (dma->head)
dma->head->next = buf;
dma->head = buf;
if (!dma->tail)
dma->tail = buf;
wmb();
process_dma(dma);
local_irq_restore(flags);
return 0;
}
/*-----------------------------------------------------
* Name : at91rm9200_dma_flush_all()
* Function: flush out at91rm9200_dma_t struct before free it
* Last rework data: 06-04-13 wujh@hyesco.com
*----------------------------------------------------*/
int at91rm9200_dma_flush_all(at91rm9200_dma_t *dma)
{
int flags;
dma_buf_t *buf, *next_buf;
DPRINTK("at91rm9200_dma_flush_all\n\r");
local_irq_save(flags);
buf = dma->curr;
if (!buf)
buf = dma->tail;
dma->head = dma->tail = dma->curr = NULL;
//process_dma(dma);
local_irq_restore(flags);
while (buf) {
next_buf = buf->next;
kfree(buf);
buf = next_buf;
}
return 0;
}
/*-----------------------------------------------------
* Name : dma_irq_handler()
* Function: dma irq servive function
* Last rework data: 06-04-13 wujh@hyesco.com
*----------------------------------------------------*/
static irqreturn_t dma_irq_handler(int irq, void *dev_id, struct pt_regs *regs)
{
at91rm9200_dma_state_t *state = (at91rm9200_dma_state_t *) dev_id;
U8 status = 0;
DPRINTK("dma_irq_handler\n\r");
if(state->readstatback)
state->readstatback(&status);
switch(status)
{
case 1:
state->out_chan -> dma_channel = 1;
at91rm9200_dma_done (state->out_chan);
break;
case 2:
state->out_chan -> dma_channel = 2;
at91rm9200_dma_done (state->out_chan);
break;
case 3:
state->in_chan -> dma_channel = 1;
at91rm9200_dma_done (state->in_chan);
break;
case 4:
state->in_chan -> dma_channel = 2;
at91rm9200_dma_done (state->in_chan);
break;
default:
break;
}
return IRQ_HANDLED;
}
/*-----------------------------------------------------
* Name : at91rm9200_dma_init()
* Function: Init the at91rm9200_dma_t struct for the device
* Last rework data: 06-04-13 wujh@hyesco.com
*----------------------------------------------------*/
int at91rm9200_dma_init(at91rm9200_dma_state_t *state)
{
int err = 0;
state->in_use = 1;
DPRINTK("at91rm9200_dma_init\n\r");
err = request_irq(state->irq, dma_irq_handler, SA_INTERRUPT,
state->device_id, (void *) state);
if (err) {
printk(KERN_ERR
"%s: unable to request IRQ %d for DMA channel\n",
state->device_id, state->irq);
return err;
}
return 0;
}
/*-----------------------------------------------------
* Name : at91rm9200_free_dma()
* Function: free the dma irq for the device
* Last rework data: 06-04-13 wujh@hyesco.com
*----------------------------------------------------*/
void at91rm9200_free_dma(at91rm9200_dma_state_t *state)
{
if (!state->in_use) {
//printk(KERN_ERR "Trying to free free DMA%d\n", state->irq);
return;
}
DPRINTK("at91rm9200_free_dma\n\r");
at91rm9200_dma_flush_all(state->out_chan);
at91rm9200_dma_flush_all(state->in_chan);
free_irq(state->irq, (void *) state);
state->in_use = 0;
}
#endif // __AT91_PDC_H_
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -