基于OMAPL138的linux平台8250快速串口实现--UART+EDMA

2023-05-16


  本文源码基于dvsdk_omapl138-evm_04_03_00_06_setuplinux程序包里的linux3.3.0版本的内核。实现EDMA支持UART功能主要修改dma和8250的源码。
  首先为OMAPL138的arm和DSP分配dma通道资源,此处理器有三个UART,UART1占用EDMA0cc0的channel8(RX)、channel9(TX)通道以及slot8(RX)和slot9(TX);
UART2占用EDMA0cc0的channel12(RX)、channel13(TX)通道以及slot12(RX)和slot13(TX);UART2占用EDMA0cc0的channel30(RX)、channel31(TX)通道以及slot30(RX)和slot31(TX)。我的板子其余的EDMA资源全部分给DSP。因此可以设置dma的通道资源分配结构体为:
static const s16 da850_dma0_rsv_chans[][2] = {
/* (offset, number) */
       { 0, 32},//表示所有的endm0cc0通道都已经使用,其中通道8、9、12、13、30、31是后边8250中通过直接获取通道时使用。
{-1, -1}
};


static const s16 da850_dma0_rsv_slots[][2] = {
/* (offset, number) */
{0,   8},//表示slot0~slot7分配给DSP,如何实现在下边分析
{10,  2},//表示slot10、slot11分配给DSP
{14, 16},//表示slot14~slot30分配给DSP
{32, 96},//剩余的slot全部分给DSP
{-1, -1}//此结构体表示slot8、9、12、13、30、31预留给arm
};


static const s16 da850_dma1_rsv_chans[][2] = {
/* (offset, number) */
{0 , 32},//endm1cc0的32个通道全留给DSP
{-1, -1}
};


static const s16 da850_dma1_rsv_slots[][2] = {
/* (offset, number) */
{0 ,128},//endm1cc0所有的slot全部留给DSP
{-1, -1}
};
现在分析如何实现通道的分配:


static int __init edma_probe(struct platform_device *pdev)
{
struct edma_soc_info **info = pdev->dev.platform_data;
const s8 (*queue_priority_mapping)[2];
const s8 (*queue_tc_mapping)[2];
int i, j, off, ln, found = 0;
int status = -1;
const s16 (*rsv_chans)[2];
const s16 (*rsv_slots)[2];
int irq[EDMA_MAX_CC] = {0, 0};
int err_irq[EDMA_MAX_CC] = {0, 0};
struct resource *r[EDMA_MAX_CC] = {NULL};
resource_size_t len[EDMA_MAX_CC];
char res_name[10];
char irq_name[10];


if (!info)
return -ENODEV;
#if 0
    edma30_tc0cfg_reg = ioremap(EDMA30_TC0_CFG, 4);
    if (!edma30_tc0cfg_reg) {
status = -EBUSY;
goto fail2;
}


    edma_set_buswidth(1);
#endif
for (j = 0; j < EDMA_MAX_CC; j++) {
sprintf(res_name, "edma_cc%d", j);
r[j] = platform_get_resource_byname(pdev, IORESOURCE_MEM,
res_name);
if (!r[j] || !info[j]) {
if (found)
break;
else
return -ENODEV;
} else {
found = 1;
}


len[j] = resource_size(r[j]);


r[j] = request_mem_region(r[j]->start, len[j],
dev_name(&pdev->dev));
if (!r[j]) {
status = -EBUSY;
goto fail1;
}


edmacc_regs_base[j] = ioremap(r[j]->start, len[j]);
if (!edmacc_regs_base[j]) {
status = -EBUSY;
goto fail1;
}


edma_cc[j] = kzalloc(sizeof(struct edma), GFP_KERNEL);
if (!edma_cc[j]) {
status = -ENOMEM;
goto fail1;
}


edma_cc[j]->num_channels = min_t(unsigned, info[j]->n_channel,
EDMA_MAX_DMACH);
edma_cc[j]->num_slots = min_t(unsigned, info[j]->n_slot,
EDMA_MAX_PARAMENTRY);
edma_cc[j]->num_cc = min_t(unsigned, info[j]->n_cc,
EDMA_MAX_CC);


edma_cc[j]->default_queue = info[j]->default_queue;


dev_dbg(&pdev->dev, "DMA REG BASE ADDR=%p\n",
edmacc_regs_base[j]);


for (i = 0; i < edma_cc[j]->num_slots; i++)
memcpy_toio(edmacc_regs_base[j] + PARM_OFFSET(i),
&dummy_paramset, PARM_SIZE);


/* Mark all channels as unused */
memset(edma_cc[j]->edma_unused, 0xff, //此处先将dma的所有通道设为未使用
sizeof(edma_cc[j]->edma_unused));


if (info[j]->rsv) {


/* Clear the reserved channels in unused list */
rsv_chans = info[j]->rsv->rsv_chans;
if (rsv_chans) {
for (i = 0; rsv_chans[i][0] != -1; i++) {
off = rsv_chans[i][0];
ln = rsv_chans[i][1];
clear_bits(off, ln,
edma_cc[j]->edma_unused);        //此处将前边结da850_dma0_rsv_chans、da850_dma1_rsv_chans构体中的通道标记为已经使用。
}
}


/* Set the reserved slots in inuse list */
rsv_slots = info[j]->rsv->rsv_slots;
if (rsv_slots) {
for (i = 0; rsv_slots[i][0] != -1; i++) {
off = rsv_slots[i][0];
ln = rsv_slots[i][1];
set_bits(off, ln,
edma_cc[j]->edma_inuse);          //此处将前边结da850_dma0_rsv_slots、da850_dma1_rsv_slots构体中的slot标记为已经使用。
}
}
}


sprintf(irq_name, "edma%d", j);
irq[j] = platform_get_irq_byname(pdev, irq_name);
edma_cc[j]->irq_res_start = irq[j];
status = request_irq(irq[j], dma_irq_handler, 0, "edma",
&pdev->dev);
if (status < 0) {
dev_dbg(&pdev->dev, "request_irq %d failed --> %d\n",
irq[j], status);
goto fail;
}


sprintf(irq_name, "edma%d_err", j);
err_irq[j] = platform_get_irq_byname(pdev, irq_name);
edma_cc[j]->irq_res_end = err_irq[j];
status = request_irq(err_irq[j], dma_ccerr_handler, 0,
"edma_error", &pdev->dev);
if (status < 0) {
dev_dbg(&pdev->dev, "request_irq %d failed --> %d\n",
err_irq[j], status);
goto fail;
}


for (i = 0; i < edma_cc[j]->num_channels; i++)
map_dmach_queue(j, i, info[j]->default_queue);


queue_tc_mapping = info[j]->queue_tc_mapping;
queue_priority_mapping = info[j]->queue_priority_mapping;


/* Event queue to TC mapping */
for (i = 0; queue_tc_mapping[i][0] != -1; i++)
map_queue_tc(j, queue_tc_mapping[i][0],
queue_tc_mapping[i][1]);


/* Event queue priority mapping */
for (i = 0; queue_priority_mapping[i][0] != -1; i++)
assign_priority_to_queue(j,
queue_priority_mapping[i][0],
queue_priority_mapping[i][1]);


/* Map the channel to param entry if channel mapping logic
* exist
*/
if (edma_read(j, EDMA_CCCFG) & CHMAP_EXIST)
map_dmach_param(j);


for (i = 0; i < info[j]->n_region; i++) {
edma_write_array2(j, EDMA_DRAE, i, 0, 0x0);
edma_write_array2(j, EDMA_DRAE, i, 1, 0x0);
edma_write_array(j, EDMA_QRAE, i, 0x0);
}
arch_num_cc++;
}


if (tc_errs_handled) {
status = request_irq(IRQ_TCERRINT0, dma_tc0err_handler, 0,
"edma_tc0", &pdev->dev);
if (status < 0) {
dev_dbg(&pdev->dev, "request_irq %d failed --> %d\n",
IRQ_TCERRINT0, status);
return status;
}
status = request_irq(IRQ_TCERRINT, dma_tc1err_handler, 0,
"edma_tc1", &pdev->dev);
if (status < 0) {
dev_dbg(&pdev->dev, "request_irq %d --> %d\n",
IRQ_TCERRINT, status);
return status;
}
}


return 0;


fail:
for (i = 0; i < EDMA_MAX_CC; i++) {
if (err_irq[i])
free_irq(err_irq[i], &pdev->dev);
if (irq[i])
free_irq(irq[i], &pdev->dev);
}
fail1:
for (i = 0; i < EDMA_MAX_CC; i++) {
if (r[i])
release_mem_region(r[i]->start, len[i]);
if (edmacc_regs_base[i])
iounmap(edmacc_regs_base[i]);
kfree(edma_cc[i]);
}
#if 0    
fail2:
    iounmap(edma30_tc0cfg_reg);
#endif    
return status;
}
liuning 20:29:57


此函数完成标记以后,在获取dma通道时就不会再获取已经标记为已经使用的通道,但是如果获取通道时获取固定通道,则不受上边标记的限制,具体分析如下:
int edma_alloc_channel(int channel,
void (*callback)(unsigned channel, u16 ch_status, void *data),
void *data,
enum dma_event_q eventq_no)
{
unsigned i, done = 0, ctlr = 0;
int ret = 0;


if (!unused_chan_list_done) {
/*
* Scan all the platform devices to find out the EDMA channels
* used and clear them in the unused list, making the rest
* available for ARM usage.
*/
ret = bus_for_each_dev(&platform_bus_type, NULL, NULL,
prepare_unused_channel_list);
if (ret < 0)
return ret;


unused_chan_list_done = true;
}


if (channel >= 0) {
ctlr = EDMA_CTLR(channel);
channel = EDMA_CHAN_SLOT(channel);
}


if (channel < 0) { //由此处可见,只有channel值小于零时,才会在未使用的通道中找到最靠前的通道,
for (i = 0; i < arch_num_cc; i++) {
channel = 0;
for (;;) { //次for循环寻找通道
channel = find_next_bit(edma_cc[i]->edma_unused,
edma_cc[i]->num_channels,
channel);
if (channel == edma_cc[i]->num_channels)
break;
if (!test_and_set_bit(channel,
edma_cc[i]->edma_inuse)) {       //找到之后将其对应的slot标记为已经使用
done = 1;
ctlr = i;
break;
}
channel++;
}
if (done)
break;
}
if (!done)
return -ENOMEM;
} else if (channel >= edma_cc[ctlr]->num_channels) {
return -EINVAL;
} else if (test_and_set_bit(channel, edma_cc[ctlr]->edma_inuse)) {/*此处将其对应的slot做是否使用检测并标记为已经使用,所以da850_dma0_rsv_slots中UART用的slot一定不能分配给DSP,不然后获取信道失败*/
return -EBUSY;
}


/* ensure access through shadow region 0 */
edma_or_array2(ctlr, EDMA_DRAE, 0, channel >> 5, BIT(channel & 0x1f)); /*如果通道是大于零的,则不用寻找,直接对此通道进行设置,注意,此处没有把此通道标记为已经使用,这就是结构体da850_dma0_rsv_chans中将其分配给DSP的原因,其实就是为了在edma_probe函数中将其标记为已经使用。*/


/* ensure no events are pending */
edma_stop(EDMA_CTLR_CHAN(ctlr, channel));
memcpy_toio(edmacc_regs_base[ctlr] + PARM_OFFSET(channel),
&dummy_paramset, PARM_SIZE);


if (callback)
setup_dma_interrupt(EDMA_CTLR_CHAN(ctlr, channel),
callback, data);


map_dmach_queue(ctlr, channel, eventq_no);


return EDMA_CTLR_CHAN(ctlr, channel);
}
为了支持8250使用EDMA,还要在\arch\arm\mach-davinci\Dma.c
void edma_trig_manual(unsigned channel)
{
unsigned ctlr;


ctlr = EDMA_CTLR(channel);
channel = EDMA_CHAN_SLOT(channel);
    int j = channel >> 5;
    unsigned int mask = BIT(channel & 0x1f);
    edma_shadow0_write_array(ctlr, SH_ESR, j, mask);
}
此函数配合edma_start接口使用,因为OMAPL138的UART串口TX触发EDMA事件有两种,一种是THRE为空时触发,一种是pwremu_mgmt寄存器使能发射的时候触发。现在的问题是1、使能发射是在uboot阶段就完成了;2、UART开启dma发射时并没有将数据先拷贝到THRE,也就不会有THRE为空触发EDMA的条件。所以此处添加一个函数,在edma_start后调用edma_trig_manual函数实现手动触发。


此时EDMA的源码才支持8250实现EDMA功能。现在一步一步实现。
一、扩充plat_serial8250_port结构体,添加EDMA的支持
struct serial8250_dma {
    int  rx_dma_enable;//使能接收dma功能标志,次出为了实现统一个UART的接收和发射单独添加dma支持,所以分开
    int  tx_dma_enable;//使能发射dma功能标志
    int  rx_dma_channel;//接收dma通道号
    int  tx_dma_channel;//发射dma通道号
    unsigned long dma_phy_membase;//UART的起始物理地址。
 };
struct plat_serial8250_port {
unsigned long iobase; /* io base address */
void __iomem *membase; /* ioremap cookie or NULL */
resource_size_t mapbase; /* resource base */
unsigned int irq; /* interrupt number */
unsigned long irqflags; /* request_irq flags */
struct clk *clk;
unsigned int uartclk; /* UART clock rate */
void            *private_data;
unsigned char regshift; /* register shift */
unsigned char iotype; /* UPIO_* */
unsigned char hub6;
upf_t flags; /* UPF_* flags */
unsigned int type; /* If UPF_FIXED_TYPE */
unsigned int (*serial_in)(struct uart_port *, int);
void (*serial_out)(struct uart_port *, int, int);
void (*set_termios)(struct uart_port *,
              struct ktermios *new,
              struct ktermios *old);
int (*handle_irq)(struct uart_port *);
void (*pm)(struct uart_port *, unsigned int state,
     unsigned old);
    struct serial8250_dma serial8250_dma;
};
然后对设备资源进行dma支持扩充:


static struct plat_serial8250_port da8xx_serial_pdata[] = {
{
.mapbase = DA8XX_UART0_BASE,
.irq = IRQ_DA8XX_UARTINT0,
.flags = UPF_BOOT_AUTOCONF | UPF_SKIP_TEST |
UPF_IOREMAP | UPF_FIXED_TYPE,
.type = PORT_AR7,
.iotype = UPIO_MEM,
.regshift = 2,
.serial8250_dma.rx_dma_channel = DA8XX_DMA_UART0_RX,
.serial8250_dma.tx_dma_channel = DA8XX_DMA_UART0_TX,
.serial8250_dma.tx_dma_enable    = false,
.serial8250_dma.rx_dma_enable    = false,
.serial8250_dma.dma_phy_membase  = DA8XX_UART0_BASE,
},
{
.mapbase = DA8XX_UART1_BASE,
.irq = IRQ_DA8XX_UARTINT1,
.flags = UPF_BOOT_AUTOCONF | UPF_SKIP_TEST |
UPF_IOREMAP | UPF_FIXED_TYPE,
.type = PORT_8250,
.iotype = UPIO_MEM,
.regshift = 2,
.serial8250_dma.rx_dma_channel = DA8XX_DMA_UART1_RX,
.serial8250_dma.tx_dma_channel = DA8XX_DMA_UART1_TX,
.serial8250_dma.rx_dma_enable    = true,
.serial8250_dma.tx_dma_enable    = true,
.serial8250_dma.dma_phy_membase  = DA8XX_UART1_BASE,
},
{
.mapbase = DA8XX_UART2_BASE,
.irq = IRQ_DA8XX_UARTINT2,
.flags = UPF_BOOT_AUTOCONF | UPF_SKIP_TEST |
UPF_IOREMAP | UPF_FIXED_TYPE,
.type = PORT_AR7,
.iotype = UPIO_MEM,
.regshift = 2,
.serial8250_dma.rx_dma_channel = DA8XX_DMA_UART2_RX,
.serial8250_dma.tx_dma_channel = DA8XX_DMA_UART2_TX,
.serial8250_dma.rx_dma_enable    = false,
.serial8250_dma.tx_dma_enable    = false,
.serial8250_dma.dma_phy_membase  = DA8XX_UART2_BASE,
},
{
.flags = 0,
},
};
liuning 20:30:41


定义dma通道号:
#define DA8XX_DMA_UART0_RX EDMA_CTLR_CHAN(0, 8)
#define DA8XX_DMA_UART0_TX EDMA_CTLR_CHAN(0, 9)
#define DA8XX_DMA_UART1_RX EDMA_CTLR_CHAN(0, 12)
#define DA8XX_DMA_UART1_TX EDMA_CTLR_CHAN(0, 13)
#define DA8XX_DMA_UART2_RX EDMA_CTLR_CHAN(0, 30)
#define DA8XX_DMA_UART2_TX EDMA_CTLR_CHAN(0, 31)
此处就完成了8250的dev修改,然后对drv进行修改。
先扩充uart_8250_port 结构体对dma的支持
struct uart_8250_dma {
    int         dma_used; //dma使用标志,接收和发射任意一个启用dma功能,此处都胡置为
    int         tx_dma_enable;  //发射使能dma标志
    int         rx_dma_enable;  //接收使能dma标志
    int tx_dma_used;    //发射dma正在工作标志
    int rx_dma_used;    //接收dma正在工作标志
    spinlock_t tx_lock;//锁
    spinlock_t rx_lock;
    int tx_done;
    unsigned int tx_count;      //发射长度
    wait_queue_head_t tx_wait_queue; //UART发送队列
    dma_addr_t rx_dma_physbuf;  //接收buf的物理地址
    struct circ_buf rx_dma_buf; //接收buf
    struct timer_list rx_dma_timer; //接收定时器
    unsigned int rx_poll_rate;//接收轮询周期
    unsigned int rx_timeout;//接收超时时间
    unsigned long port_activity;
    unsigned int tx_dma_channel; //发送DMA通道
    unsigned int rx_dma_channel; //接收DMA通道
    int         dummy_param_slot;  /* 傀儡 ParamentSet 的索引,用来link */
    int         b_cnt_tag;  //接收dma设置的总长度
    dma_addr_t tx_dma_physbuf; //发射buf的物理地址
    dma_addr_t dma_phy_database; //UART的起始物理地址
};


struct uart_8250_port {
struct uart_port port;
struct timer_list timer; /* "no irq" timer */
struct list_head list; /* ports on this IRQ */
unsigned short capabilities; /* port capabilities */
unsigned short bugs; /* port bugs */
unsigned int tx_loadsz; /* transmit fifo load size */
unsigned char acr;
unsigned char ier;
unsigned char lcr;
unsigned char mcr;
unsigned char mcr_mask; /* mask of user bits */
unsigned char mcr_force; /* mask of forced bits */
unsigned char cur_iotype; /* Running I/O type */


/*
* Some bits in registers are cleared on a read, so they must
* be saved whenever the register is read but the bits will not
* be immediately processed.
*/
#define LSR_SAVE_FLAGS UART_LSR_BRK_ERROR_BITS
unsigned char lsr_saved_flags;
#define MSR_SAVE_FLAGS UART_MSR_ANY_DELTA
unsigned char msr_saved_flags;
struct clk *clk;
#ifdef CONFIG_CPU_FREQ
struct notifier_block freq_transition;
#endif
struct uart_8250_dma   uart_8250_dma; 


};


drv源码修改以支持dma
1、参数填充
static int __devinit serial8250_probe(struct platform_device *dev)
{
struct plat_serial8250_port *p = dev->dev.platform_data;
struct uart_port port;
int ret, i, irqflag = 0;


memset(&port, 0, sizeof(struct uart_port));


if (share_irqs)
irqflag = IRQF_SHARED;


for (i = 0; p && p->flags != 0; p++, i++) {
port.iobase = p->iobase;
port.membase = p->membase;
port.irq = p->irq;
port.irqflags = p->irqflags;
port.uartclk = p->uartclk;
port.regshift = p->regshift;
port.iotype = p->iotype;
port.flags = p->flags;
port.mapbase = p->mapbase;
port.hub6 = p->hub6;
port.private_data = p->private_data;
port.type = p->type;
port.serial_in = p->serial_in;
port.serial_out = p->serial_out;
port.handle_irq = p->handle_irq;
port.set_termios = p->set_termios;
port.pm = p->pm;
port.dev = &dev->dev;
port.irqflags |= irqflag;
if (p->clk)
serial8250_ports[i].clk = p->clk;


        if(p->serial8250_dma.tx_dma_enable || p->serial8250_dma.rx_dma_enable){//关于dma的参数填充。
            serial8250_ports[i].uart_8250_dma.dma_used       = true;
            serial8250_ports[i].uart_8250_dma.dma_phy_database = p->serial8250_dma.dma_phy_membase;
            if(p->serial8250_dma.tx_dma_enable){
                serial8250_ports[i].uart_8250_dma.tx_dma_enable    = p->serial8250_dma.tx_dma_enable;
                serial8250_ports[i].uart_8250_dma.tx_dma_channel   = p->serial8250_dma.tx_dma_channel;
                spin_lock_init(&(serial8250_ports[i].uart_8250_dma.tx_lock));
       
            }else{
                serial8250_ports[i].uart_8250_dma.tx_dma_enable    = p->serial8250_dma.tx_dma_enable;
                serial8250_ports[i].uart_8250_dma.tx_dma_channel   = -1;
            }
            if(p->serial8250_dma.rx_dma_enable){
                serial8250_ports[i].uart_8250_dma.rx_dma_enable    = p->serial8250_dma.rx_dma_enable;
                serial8250_ports[i].uart_8250_dma.rx_dma_channel   = p->serial8250_dma.rx_dma_channel;
                spin_lock_init(&(serial8250_ports[i].uart_8250_dma.rx_lock));
            }else{
                serial8250_ports[i].uart_8250_dma.rx_dma_enable    = p->serial8250_dma.rx_dma_enable;
                serial8250_ports[i].uart_8250_dma.rx_dma_channel   = -1;
            }
        }else{
            serial8250_ports[i].uart_8250_dma.dma_used       = false;
        }
            
ret = serial8250_register_port(&port);
if (ret < 0) {
dev_err(&dev->dev, "unable to register port at index %d "
"(IO%lx MEM%llx IRQ%d): %d\n", i,
p->iobase, (unsigned long long)p->mapbase,
p->irq, ret);
}
}
return 0;
}
2、EDMA设置
liuning 20:31:13


static int serial8250_startup(struct uart_port *port)
{
struct uart_8250_port *up =
container_of(port, struct uart_8250_port, port);
unsigned long flags;
unsigned char lsr, iir;
int retval;
    
    up->port.fifosize = uart_config[up->port.type].fifo_size;
    up->tx_loadsz = uart_config[up->port.type].tx_loadsz;
    up->capabilities = uart_config[up->port.type].flags;
    up->mcr = 0;


    if( up->uart_8250_dma.dma_used == false){


    if (up->port.iotype != up->cur_iotype)
    set_io_from_upio(port);


    if (up->port.type == PORT_16C950) {
    /* Wake up and initialize UART */
    up->acr = 0;
    serial_outp(up, UART_LCR, UART_LCR_CONF_MODE_B);
    serial_outp(up, UART_EFR, UART_EFR_ECB);
    serial_outp(up, UART_IER, 0);
    serial_outp(up, UART_LCR, 0);
    serial_icr_write(up, UART_CSR, 0); /* Reset the UART */
    serial_outp(up, UART_LCR, UART_LCR_CONF_MODE_B);
    serial_outp(up, UART_EFR, UART_EFR_ECB);
    serial_outp(up, UART_LCR, 0);
    }


#ifdef CONFIG_SERIAL_8250_RSA
    /*
    * If this is an RSA port, see if we can kick it up to the
    * higher speed clock.
    */
    enable_rsa(up);
#endif


    /*
    * Clear the FIFO buffers and disable them.
    * (they will be reenabled in set_termios())
    */
    serial8250_clear_fifos(up);


    /*
    * Clear the interrupt registers.
    */
    (void) serial_inp(up, UART_LSR);
    (void) serial_inp(up, UART_RX);
    (void) serial_inp(up, UART_IIR);
    (void) serial_inp(up, UART_MSR);


    /*
    * At this point, there's no way the LSR could still be 0xff;
    * if it is, then bail out, because there's likely no UART
    * here.
    */
    if (!(up->port.flags & UPF_BUGGY_UART) &&
       (serial_inp(up, UART_LSR) == 0xff)) {
    printk_ratelimited(KERN_INFO "ttyS%d: LSR safety check engaged!\n",
      serial_index(&up->port));
    return -ENODEV;
    }


    /*
    * For a XR16C850, we need to set the trigger levels
    */
    if (up->port.type == PORT_16850) {
    unsigned char fctr;


    serial_outp(up, UART_LCR, UART_LCR_CONF_MODE_B);


    fctr = serial_inp(up, UART_FCTR) & ~(UART_FCTR_RX|UART_FCTR_TX);
    serial_outp(up, UART_FCTR, fctr | UART_FCTR_TRGD | UART_FCTR_RX);
    serial_outp(up, UART_TRG, UART_TRG_96);
    serial_outp(up, UART_FCTR, fctr | UART_FCTR_TRGD | UART_FCTR_TX);
    serial_outp(up, UART_TRG, UART_TRG_96);


    serial_outp(up, UART_LCR, 0);
    }


    if (is_real_interrupt(up->port.irq)) {
    unsigned char iir1;
    /*
    * Test for UARTs that do not reassert THRE when the
    * transmitter is idle and the interrupt has already
    * been cleared.  Real 16550s should always reassert
    * this interrupt whenever the transmitter is idle and
    * the interrupt is enabled.  Delays are necessary to
    * allow register changes to become visible.
    */
    spin_lock_irqsave(&up->port.lock, flags);
    if (up->port.irqflags & IRQF_SHARED)
    disable_irq_nosync(up->port.irq);


    wait_for_xmitr(up, UART_LSR_THRE);
    serial_out_sync(up, UART_IER, UART_IER_THRI);
    udelay(1); /* allow THRE to set */
    iir1 = serial_in(up, UART_IIR);
    serial_out(up, UART_IER, 0);
    serial_out_sync(up, UART_IER, UART_IER_THRI);
    udelay(1); /* allow a working UART time to re-assert THRE */
    iir = serial_in(up, UART_IIR);
    serial_out(up, UART_IER, 0);


    if (up->port.irqflags & IRQF_SHARED)
    enable_irq(up->port.irq);
    spin_unlock_irqrestore(&up->port.lock, flags);


    /*
    * If the interrupt is not reasserted, setup a timer to
    * kick the UART on a regular basis.
    */
    if (!(iir1 & UART_IIR_NO_INT) && (iir & UART_IIR_NO_INT)) {
    up->bugs |= UART_BUG_THRE;
    pr_debug("ttyS%d - using backup timer\n",
    serial_index(port));
    }
    }
liuning 20:32:13


    /*
    * The above check will only give an accurate result the first time
    * the port is opened so this value needs to be preserved.
    */
    if (up->bugs & UART_BUG_THRE) {
    up->timer.function = serial8250_backup_timeout;
    up->timer.data = (unsigned long)up;
    mod_timer(&up->timer, jiffies +
    uart_poll_timeout(port) + HZ / 5);
    }


    /*
    * If the "interrupt" for this port doesn't correspond with any
    * hardware interrupt, we use a timer-based system.  The original
    * driver used to do this with IRQ0.
    */
    if (!is_real_interrupt(up->port.irq)) {
    up->timer.data = (unsigned long)up;
    mod_timer(&up->timer, jiffies + uart_poll_timeout(port));
    } else {
    retval = serial_link_irq_chain(up);
    if (retval)
    return retval;
    }


    /*
    * Now, initialize the UART
    */
    serial_outp(up, UART_LCR, UART_LCR_WLEN8);


    spin_lock_irqsave(&up->port.lock, flags);
    if (up->port.flags & UPF_FOURPORT) {
    if (!is_real_interrupt(up->port.irq))
    up->port.mctrl |= TIOCM_OUT1;
    } else
    /*
    * Most PC uarts need OUT2 raised to enable interrupts.
    */
    if (is_real_interrupt(up->port.irq))
    up->port.mctrl |= TIOCM_OUT2;


    serial8250_set_mctrl(&up->port, up->port.mctrl);


    /* Serial over Lan (SoL) hack:
      Intel 8257x Gigabit ethernet chips have a
      16550 emulation, to be used for Serial Over Lan.
      Those chips take a longer time than a normal
      serial device to signalize that a transmission
      data was queued. Due to that, the above test generally
      fails. One solution would be to delay the reading of
      iir. However, this is not reliable, since the timeout
      is variable. So, let's just don't test if we receive
      TX irq. This way, we'll never enable UART_BUG_TXEN.
    */
    if (skip_txen_test || up->port.flags & UPF_NO_TXEN_TEST)
    goto dont_test_tx_en;


    /*
    * Do a quick test to see if we receive an
    * interrupt when we enable the TX irq.
    */
    serial_outp(up, UART_IER, UART_IER_THRI);
    lsr = serial_in(up, UART_LSR);
    iir = serial_in(up, UART_IIR);
    serial_outp(up, UART_IER, 0);


    if (lsr & UART_LSR_TEMT && iir & UART_IIR_NO_INT) {
    if (!(up->bugs & UART_BUG_TXEN)) {
    up->bugs |= UART_BUG_TXEN;
    pr_debug("ttyS%d - enabling bad tx status workarounds\n",
    serial_index(port));
    }
    } else {
    up->bugs &= ~UART_BUG_TXEN;
    }
liuning 20:32:25


dont_test_tx_en:
    spin_unlock_irqrestore(&up->port.lock, flags);


    /*
    * Clear the interrupt registers again for luck, and clear the
    * saved flags to avoid getting false values from polling
    * routines or the previous session.
    */
    serial_inp(up, UART_LSR);
    serial_inp(up, UART_RX);
    serial_inp(up, UART_IIR);
    serial_inp(up, UART_MSR);
    up->lsr_saved_flags = 0;
    up->msr_saved_flags = 0;


    /*
    * Finally, enable interrupts.  Note: Modem status interrupts
    * are set via set_termios(), which will be occurring imminently
    * anyway, so we don't enable them here.
    */


        up->ier = UART_IER_RLSI | UART_IER_RDI;
        serial_outp(up, UART_IER, up->ier);


    if (up->port.flags & UPF_FOURPORT) {
    unsigned int icp;
    /*
    * Enable interrupts on the AST Fourport board
    */
    icp = (up->port.iobase & 0xfe0) | 0x01f;
    outb_p(0x80, icp);
    (void) inb_p(icp);
    }
        return 0;
    }else{
         serial8250_clear_fifos(up);
         serial_outp(up, UART_LCR, UART_LCR_WLEN8);


    /*
    * Clear the interrupt registers.
    */
    (void) serial_inp(up, UART_LSR);
    (void) serial_inp(up, UART_RX);
    (void) serial_inp(up, UART_IIR);
    (void) serial_inp(up, UART_MSR);
         
/*******************************************************************************************************/
    /*
    * At this point, there's no way the LSR could still be 0xff;
    * if it is, then bail out, because there's likely no UART
    * here.
    */
    if (!(up->port.flags & UPF_BUGGY_UART) &&
       (serial_inp(up, UART_LSR) == 0xff)) {
    printk_ratelimited(KERN_INFO "ttyS%d: LSR safety check engaged!\n",
      serial_index(&up->port));
    return -ENODEV;
    }


    /*
    * For a XR16C850, we need to set the trigger levels
    */
    if (up->port.type == PORT_16850) {
    unsigned char fctr;


    serial_outp(up, UART_LCR, UART_LCR_CONF_MODE_B);


    fctr = serial_inp(up, UART_FCTR) & ~(UART_FCTR_RX|UART_FCTR_TX);
    serial_outp(up, UART_FCTR, fctr | UART_FCTR_TRGD | UART_FCTR_RX);
    serial_outp(up, UART_TRG, UART_TRG_96);
    serial_outp(up, UART_FCTR, fctr | UART_FCTR_TRGD | UART_FCTR_TX);
    serial_outp(up, UART_TRG, UART_TRG_96);


    serial_outp(up, UART_LCR, 0);
    }


    if (is_real_interrupt(up->port.irq)) {
    unsigned char iir1;
    /*
    * Test for UARTs that do not reassert THRE when the
    * transmitter is idle and the interrupt has already
    * been cleared.  Real 16550s should always reassert
    * this interrupt whenever the transmitter is idle and
    * the interrupt is enabled.  Delays are necessary to
    * allow register changes to become visible.
    */
    spin_lock_irqsave(&up->port.lock, flags);
    if (up->port.irqflags & IRQF_SHARED)
    disable_irq_nosync(up->port.irq);


    wait_for_xmitr(up, UART_LSR_THRE);
    serial_out_sync(up, UART_IER, UART_IER_THRI);
    udelay(1); /* allow THRE to set */
    iir1 = serial_in(up, UART_IIR);
    serial_out(up, UART_IER, 0);
    serial_out_sync(up, UART_IER, UART_IER_THRI);
    udelay(1); /* allow a working UART time to re-assert THRE */
    iir = serial_in(up, UART_IIR);
    serial_out(up, UART_IER, 0);


    if (up->port.irqflags & IRQF_SHARED)
    enable_irq(up->port.irq);
    spin_unlock_irqrestore(&up->port.lock, flags);


    /*
    * If the interrupt is not reasserted, setup a timer to
    * kick the UART on a regular basis.
    */
    if (!(iir1 & UART_IIR_NO_INT) && (iir & UART_IIR_NO_INT)) {
    up->bugs |= UART_BUG_THRE;
    pr_debug("ttyS%d - using backup timer\n",
    serial_index(port));
    }
    }
liuning 20:32:37


    /*
    * The above check will only give an accurate result the first time
    * the port is opened so this value needs to be preserved.
    */
    if (up->bugs & UART_BUG_THRE) {
    up->timer.function = serial8250_backup_timeout;
    up->timer.data = (unsigned long)up;
    mod_timer(&up->timer, jiffies +
    uart_poll_timeout(port) + HZ / 5);
    }


    /*
    * If the "interrupt" for this port doesn't correspond with any
    * hardware interrupt, we use a timer-based system.  The original
    * driver used to do this with IRQ0.
    */
    if (!is_real_interrupt(up->port.irq)) {
    up->timer.data = (unsigned long)up;
    mod_timer(&up->timer, jiffies + uart_poll_timeout(port));
    } else {
    retval = serial_link_irq_chain(up);
    if (retval)
    return retval;
    }


    /*
    * Now, initialize the UART
    */
    serial_outp(up, UART_LCR, UART_LCR_WLEN8);


    spin_lock_irqsave(&up->port.lock, flags);
    if (up->port.flags & UPF_FOURPORT) {
    if (!is_real_interrupt(up->port.irq))
    up->port.mctrl |= TIOCM_OUT1;
    } else
    /*
    * Most PC uarts need OUT2 raised to enable interrupts.
    */
    if (is_real_interrupt(up->port.irq))
    up->port.mctrl |= TIOCM_OUT2;


    serial8250_set_mctrl(&up->port, up->port.mctrl);


    /* Serial over Lan (SoL) hack:
      Intel 8257x Gigabit ethernet chips have a
      16550 emulation, to be used for Serial Over Lan.
      Those chips take a longer time than a normal
      serial device to signalize that a transmission
      data was queued. Due to that, the above test generally
      fails. One solution would be to delay the reading of
      iir. However, this is not reliable, since the timeout
      is variable. So, let's just don't test if we receive
      TX irq. This way, we'll never enable UART_BUG_TXEN.
    */


    /*
    * Do a quick test to see if we receive an
    * interrupt when we enable the TX irq.
    */
    serial_outp(up, UART_IER, UART_IER_THRI);
    lsr = serial_in(up, UART_LSR);
    iir = serial_in(up, UART_IIR);
    serial_outp(up, UART_IER, 0);


    if (lsr & UART_LSR_TEMT && iir & UART_IIR_NO_INT) {
    if (!(up->bugs & UART_BUG_TXEN)) {
    up->bugs |= UART_BUG_TXEN;
    pr_debug("ttyS%d - enabling bad tx status workarounds\n",
    serial_index(port));
    }
    } else {
    up->bugs &= ~UART_BUG_TXEN;
    }




    spin_unlock_irqrestore(&up->port.lock, flags);


    /*
    * Clear the interrupt registers again for luck, and clear the
    * saved flags to avoid getting false values from polling
    * routines or the previous session.
    */
    serial_inp(up, UART_LSR);
    serial_inp(up, UART_RX);
    serial_inp(up, UART_IIR);
    serial_inp(up, UART_MSR);
/*******************************************************************************************************/


        up->lsr_saved_flags = 0;
        up->msr_saved_flags = 0;
        
        if(up->uart_8250_dma.tx_dma_enable){ //发射设置EDMA支持
            up->uart_8250_dma.tx_done  = 1;
            up->uart_8250_dma.tx_count = 0;
            
            retval = edma_alloc_channel(up->uart_8250_dma.tx_dma_channel,(void *)uart_tx_dma_callback,up,0);
            if(retval < 0){
                printk("Error:Unable alloc slot for tx channel!\n");
                return 0;
            }


            free_page((unsigned long)up->port.state->xmit.buf);
            up->port.state->xmit.buf = dma_alloc_coherent(NULL,
       UART_XMIT_SIZE,
       (dma_addr_t *)&(up->uart_8250_dma.tx_dma_physbuf),
       GFP_DMA);
            if(NULL == up->port.state->xmit.buf){
                printk("Error:Unable alloc channel_Tx_buffer\n");
                edma_free_channel(up->uart_8250_dma.tx_dma_channel);
                return 0;
            }


        }    


        if(up->uart_8250_dma.rx_dma_enable){ //接收设置EDMA支持
            
            up->uart_8250_dma.b_cnt_tag                = PAGE_SIZE;


            up->uart_8250_dma.rx_dma_buf.head = 0;
            up->uart_8250_dma.rx_dma_buf.tail = 0;
            
            up->uart_8250_dma.rx_dma_buf.buf = (unsigned char *)dma_alloc_coherent(NULL,
            PAGE_SIZE,
            &up->uart_8250_dma.rx_dma_physbuf,
            GFP_DMA);
            if(NULL==up->uart_8250_dma.rx_dma_buf.buf){
                printk("Error:Unable alloc channel_rx_buffer\n");


                return 0;
            }
liuning 20:32:45


            init_timer(&up->uart_8250_dma.rx_dma_timer);
            up->uart_8250_dma.rx_dma_timer.data        = (unsigned long)up;
            up->uart_8250_dma.rx_dma_timer.function    = (void *)serial_rx_dma_timeout;
            up->uart_8250_dma.rx_dma_timer.expires     = jiffies + HZ/20;//50ms chaoshi


            up->uart_8250_dma.port_activity = jiffies;
            up->uart_8250_dma.rx_poll_rate = RXDMA_POLLRATE;
            up->uart_8250_dma.rx_timeout = RXDMA_TIMEOUT;
                
        }
        
        up->ier = UART_IER_RLSI | UART_IER_RDI;
   serial_outp(up, UART_IER, up->ier);
    if (up->port.flags & UPF_FOURPORT) {
    unsigned int icp;
    /*
    * Enable interrupts on the AST Fourport board
    */
    icp = (up->port.iobase & 0xfe0) | 0x01f;
    outb_p(0x80, icp);
    (void) inb_p(icp);
    }


        return 0;
    }
}
3、发射部分处理


static void serial8250_start_tx(struct uart_port *port)
{
struct uart_8250_port *up =
container_of(port, struct uart_8250_port, port);
    struct circ_buf *xmit;
    unsigned int start;
    if(up->uart_8250_dma.tx_dma_enable == false){
        if (!(up->ier & UART_IER_THRI)) {
            up->ier |= UART_IER_THRI;
            serial_out(up, UART_IER, up->ier);
        
            if (up->bugs & UART_BUG_TXEN) {
                unsigned char lsr;
                lsr = serial_in(up, UART_LSR);
                up->lsr_saved_flags |= lsr & LSR_SAVE_FLAGS;
                if ((up->port.type == PORT_RM9000) ?
                    (lsr & UART_LSR_THRE) :
                    (lsr & UART_LSR_TEMT))
                    serial8250_tx_chars(up);
            }
        }
        
        /*
         * Re-enable the transmitter if we disabled it.
         */
        if (up->port.type == PORT_16C950 && up->acr & UART_ACR_TXDIS) {
            up->acr &= ~UART_ACR_TXDIS;
            serial_icr_write(up, UART_ACR, up->acr);
        }
    }else{ //如果使能了发射EDMA,dma设置
        if (up->uart_8250_dma.tx_dma_used)
   return;
        xmit = &up->port.state->xmit;
        spin_lock(&(up->uart_8250_dma.tx_lock));
   up->uart_8250_dma.tx_dma_used = true;
   spin_unlock(&(up->uart_8250_dma.tx_lock));
        start = up->uart_8250_dma.tx_dma_physbuf +
                    (xmit->tail & (UART_XMIT_SIZE - 1));
        up->uart_8250_dma.tx_count = uart_circ_chars_pending(xmit);
        if (start + up->uart_8250_dma.tx_count >=
       up->uart_8250_dma.tx_dma_physbuf + UART_XMIT_SIZE)
   up->uart_8250_dma.tx_count =
   (up->uart_8250_dma.tx_dma_physbuf +
   UART_XMIT_SIZE) - start;
        uart_start_dma(up->uart_8250_dma.tx_dma_channel,
                       DIR_DMA_TX,
                       (unsigned int)(up->uart_8250_dma.dma_phy_database+UART_TX)/*0x01D0D000*/,
                       start,
                       up->uart_8250_dma.tx_count);
    }


}
liuning 20:32:53




static int uart_start_dma(int channel, unsigned int dir, unsigned int dst_phyaddr, unsigned int src_phyaddr, unsigned int lenth)
{
    int ret;
    struct edmacc_param param;
    param.opt = 0x0;
    if(dir == DIR_DMA_RX){
        param.opt = TCINTEN | EDMA_TCC(channel) | SAM;
        param.src_dst_bidx = 0x00010000;
    }else{
        param.opt = TCINTEN | EDMA_TCC(channel) | DAM;
        param.src_dst_bidx = 0x00000001;
    }
    param.src = src_phyaddr;
    param.dst = dst_phyaddr;
    param.a_b_cnt = (lenth)<<16 | 0x00000001;
    param.link_bcntrld = 0x0000FFFF;
    param.src_dst_cidx = 0;
    param.ccnt = 1;
    edma_write_slot(channel, &param);


    ret = edma_start(channel); 
    if(0!=ret){
        printk("Error:Edma start fail!\n") ;
        return -1;
    }
    if(dir == DIR_DMA_TX)//如果是发射通道,手动触发EDMA事件
        edma_trig_manual(channel);
    return 0;
}




static void uart_tx_dma_callback(int lch, u16 ch_status, void *data)
{
struct uart_8250_port *up = (struct uart_8250_port *)data;
struct circ_buf *xmit = &up->port.state->xmit;
    
    //printk("uart_tx_dma_callback_in\n");
    spin_lock(&up->port.lock);


xmit->tail = (xmit->tail + up->uart_8250_dma.tx_count) & \
(UART_XMIT_SIZE - 1);
up->port.icount.tx += up->uart_8250_dma.tx_count;


if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
uart_write_wakeup(&up->port);


if (uart_circ_empty(xmit)) {
        spin_lock(&(up->uart_8250_dma.tx_lock));
serial8250_stop_tx(&up->port);
up->uart_8250_dma.tx_dma_used = false;
        spin_unlock(&(up->uart_8250_dma.tx_lock));
} else {
edma_stop(up->uart_8250_dma.tx_dma_channel);
serial_dma_continue_tx(up);
}


    spin_unlock(&up->port.lock);


}
liuning 20:33:02


static void serial_dma_continue_tx(struct uart_8250_port *up)
{
    struct circ_buf *xmit;
    unsigned int start;
    
    xmit = &up->port.state->xmit;
if (uart_circ_empty(xmit))
return;
    
    start = up->uart_8250_dma.tx_dma_physbuf +
                (xmit->tail & (UART_XMIT_SIZE - 1));
    up->uart_8250_dma.tx_count = uart_circ_chars_pending(xmit);
    if (start + up->uart_8250_dma.tx_count >=
       up->uart_8250_dma.tx_dma_physbuf + UART_XMIT_SIZE)
   up->uart_8250_dma.tx_count =
   (up->uart_8250_dma.tx_dma_physbuf +
   UART_XMIT_SIZE) - start;


    uart_start_dma(up->uart_8250_dma.tx_dma_channel,
                   DIR_DMA_TX,
                   (unsigned long)(up->uart_8250_dma.dma_phy_database+UART_TX)/*0x01D0D000*/,
                   start,
                   up->uart_8250_dma.tx_count);
}


static void serial8250_stop_tx(struct uart_port *port)
{
struct uart_8250_port *up =
container_of(port, struct uart_8250_port, port);
    if(up->uart_8250_dma.dma_used == false){
    __stop_tx(up);


    /*
    * We really want to stop the transmitter from sending.
    */
    if (up->port.type == PORT_16C950) {
    up->acr |= UART_ACR_TXDIS;
    serial_icr_write(up, UART_ACR, up->acr);
    }        
    }else{
        edma_stop(up->uart_8250_dma.tx_dma_channel);
        //printk("serial8250_stop_tx\n");
    }


}
4、接收部分设置:
中断支持dma和中断:
int serial8250_handle_irq(struct uart_port *port, unsigned int iir)
{
unsigned char status;
unsigned long flags;
struct uart_8250_port *up =
container_of(port, struct uart_8250_port, port);


if (iir & UART_IIR_NO_INT)
return 0;


spin_lock_irqsave(&up->port.lock, flags);


status = serial_inp(up, UART_LSR);


DEBUG_INTR("status = %x...", status);


    if(up->uart_8250_dma.dma_used){
        if(up->uart_8250_dma.rx_dma_enable){ /*此处可知,当前仅仅支持发射dma开接收dma关,或者发射dma关接收dma关,还不支持发射dma关接收dma关,因为当前的接收dma开还有缺陷,测试发现接收dma开是命令行不支持上下左右方向键,原因还没有查清*/
            if(iir & UART_IIR_RLSI){
                up->ier &= ~(UART_IER_RDI | UART_IER_RLSI);
                serial_out(up, UART_IER, up->ier);
    if ((serial_8250_start_rxdma(up) != 0) &&
    (status & UART_LSR_DR))
                    serial8250_rx_chars(up, status);
            }
            up->uart_8250_dma.port_activity = jiffies;
        }else{
            if (status & (UART_LSR_DR | UART_LSR_BI))
                status = serial8250_rx_chars(up, status);
            serial8250_modem_status(up);
        }
    }else{
    if (status & (UART_LSR_DR | UART_LSR_BI))
    status = serial8250_rx_chars(up, status);
    serial8250_modem_status(up);
    if (status & UART_LSR_THRE)
    serial8250_tx_chars(up);
    }
spin_unlock_irqrestore(&up->port.lock, flags);
return 1;
}
liuning 20:33:42


static int serial_8250_start_rxdma(struct uart_8250_port *up)
{
    int retval = 0;
    retval = edma_alloc_channel(up->uart_8250_dma.rx_dma_channel,(void *)uart_rx_dma_callback,up,0);
    if(retval < 0){
        printk("Error:Unable alloc slot for rx channel!\n");
        return retval;
    }
    up->uart_8250_dma.rx_dma_buf.head = 0;
    up->uart_8250_dma.rx_dma_buf.tail = 0;
    up->uart_8250_dma.b_cnt_tag = B_CNT;
    retval = uart_start_dma(up->uart_8250_dma.rx_dma_channel,
             DIR_DMA_RX,
             up->uart_8250_dma.rx_dma_physbuf,
             (unsigned int)(up->uart_8250_dma.dma_phy_database+UART_RX)/*0x01D0D000*/,
             B_CNT);
    if(0 != retval){
        printk("Error:call uart_start_dma() fail\n");
        return retval;
    }
    mod_timer(&up->uart_8250_dma.rx_dma_timer, jiffies +
usecs_to_jiffies(up->uart_8250_dma.rx_poll_rate));
    up->uart_8250_dma.rx_dma_used = true;


    return retval;
}


static void uart_rx_dma_callback(int lch, u16 ch_status, void *data)
{
    return;
}


static void serial_rx_dma_timeout(struct uart_8250_port* up)
{
    struct edmacc_param r_param ;
    int ret;
    unsigned int b_cnt;
    
    edma_read_slot(up->uart_8250_dma.rx_dma_channel, &r_param);
    b_cnt = r_param.a_b_cnt >> 16;
    if(up->uart_8250_dma.b_cnt_tag == b_cnt){
        if(jiffies_to_msecs(jiffies - up->uart_8250_dma.port_activity) <
            up->uart_8250_dma.rx_timeout){
mod_timer(&up->uart_8250_dma.rx_dma_timer, jiffies +
usecs_to_jiffies(up->uart_8250_dma.rx_poll_rate));
        }else{
serial_8250_stop_rxdma(up);
up->ier |= (UART_IER_RDI | UART_IER_RLSI);
serial_out(up, UART_IER, up->ier);
        }
        return;
    }
    up->uart_8250_dma.rx_dma_buf.tail += up->uart_8250_dma.b_cnt_tag - b_cnt;
    while(up->uart_8250_dma.rx_dma_buf.head< up->uart_8250_dma.rx_dma_buf.tail){
        uart_insert_char(&up->port, 0, UART_LSR_OE,
            up->uart_8250_dma.rx_dma_buf.buf[up->uart_8250_dma.rx_dma_buf.head], TTY_NORMAL);
        up->uart_8250_dma.rx_dma_buf.head = up->uart_8250_dma.rx_dma_buf.head + 1;
        up->port.icount.rx++;        
    }
    up->uart_8250_dma.b_cnt_tag = b_cnt;
    tty_flip_buffer_push(up->port.state->port.tty);


    if(b_cnt == 0){
        ret = serial_8250_start_rxdma(up);
        if(ret < 0){
            serial_8250_stop_rxdma(up);
   up->ier |= (UART_IER_RDI | UART_IER_RLSI);
   serial_out(up, UART_IER, up->ier);
        }
    }else{
        mod_timer(&up->uart_8250_dma.rx_dma_timer, jiffies +
            usecs_to_jiffies(up->uart_8250_dma.rx_poll_rate));
    }
    
    up->uart_8250_dma.port_activity = jiffies;
}


static void serial_8250_stop_rxdma(struct uart_8250_port *up)
{
if (up->uart_8250_dma.rx_dma_used) {
del_timer(&up->uart_8250_dma.rx_dma_timer);
edma_stop(up->uart_8250_dma.rx_dma_channel);
edma_free_channel(up->uart_8250_dma.rx_dma_channel);
up->uart_8250_dma.rx_dma_channel = SERIAL_8250_DMA_CH_FREE;
up->uart_8250_dma.rx_dma_channel = false;
}
}


最后还有添加修改一些宏和变量


#define B_CNT  PAGE_SIZE
#define DIR_DMA_RX  1
#define DIR_DMA_TX  2


#define RXDMA_POLLRATE 1    /* RX DMA polling rate (ms) */
#define RXDMA_TIMEOUT (1 * HZ)/* RX DMA timeout (jiffies) */
#define SERIAL_8250_DMA_CH_FREE -1


static int uart_start_dma(int channel, unsigned int dir, unsigned int dst_phyaddr, unsigned int src_phyaddr, unsigned int lenth);
static void serial_dma_continue_tx(struct uart_8250_port *up);
static int serial_8250_start_rxdma(struct uart_8250_port *up);


/*
 * Here we define the default xmit fifo size used for each type of UART.
 */
liuning 20:33:52
static const struct serial8250_config uart_config[] = {
[PORT_UNKNOWN] = {
.name = "unknown",
.fifo_size = 1,
.tx_loadsz = 1,
},
[PORT_8250] = {
.name = "8250",
.fifo_size = 1,
.tx_loadsz = 1,
        .fcr        = UART_FCR_ENABLE_FIFO | UART_FCR_DMA_SELECT | UART_FCR_R_TRIG_00,
        .flags      = UART_CAP_FIFO | UART_CAP_AFE,
},
[PORT_16450] = {
.name = "16450",
.fifo_size = 1,
.tx_loadsz = 1,
},
[PORT_16550] = {
.name = "16550",
.fifo_size = 1,
.tx_loadsz = 1,
},
[PORT_16550A] = {
.name = "16550A",
.fifo_size = 16,
.tx_loadsz = 16,
.fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_10,
.flags = UART_CAP_FIFO,
},
[PORT_CIRRUS] = {
.name = "Cirrus",
.fifo_size = 1,
.tx_loadsz = 1,
},
[PORT_16650] = {
.name = "ST16650",
.fifo_size = 1,
.tx_loadsz = 1,
.flags = UART_CAP_FIFO | UART_CAP_EFR | UART_CAP_SLEEP,
},
[PORT_16650V2] = {
.name = "ST16650V2",
.fifo_size = 32,
.tx_loadsz = 16,
.fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_01 |
 UART_FCR_T_TRIG_00,
.flags = UART_CAP_FIFO | UART_CAP_EFR | UART_CAP_SLEEP,
},
[PORT_16750] = {
.name = "TI16750",
.fifo_size = 64,
.tx_loadsz = 64,
.fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_10 |
 UART_FCR7_64BYTE,
.flags = UART_CAP_FIFO | UART_CAP_SLEEP | UART_CAP_AFE,
},
[PORT_STARTECH] = {
.name = "Startech",
.fifo_size = 1,
.tx_loadsz = 1,
},
[PORT_16C950] = {
.name = "16C950/954",
.fifo_size = 128,
.tx_loadsz = 128,
.fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_10,
/* UART_CAP_EFR breaks billionon CF bluetooth card. */
.flags = UART_CAP_FIFO | UART_CAP_SLEEP,
},
[PORT_16654] = {
.name = "ST16654",
.fifo_size = 64,
.tx_loadsz = 32,
.fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_01 |
 UART_FCR_T_TRIG_10,
.flags = UART_CAP_FIFO | UART_CAP_EFR | UART_CAP_SLEEP,
},
[PORT_16850] = {
.name = "XR16850",
.fifo_size = 128,
.tx_loadsz = 128,
.fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_10,
.flags = UART_CAP_FIFO | UART_CAP_EFR | UART_CAP_SLEEP,
},
[PORT_RSA] = {
.name = "RSA",
.fifo_size = 2048,
.tx_loadsz = 2048,
.fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_11,
.flags = UART_CAP_FIFO,
},
[PORT_NS16550A] = {
.name = "NS16550A",
.fifo_size = 16,
.tx_loadsz = 16,
.fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_10,
.flags = UART_CAP_FIFO | UART_NATSEMI,
},
[PORT_XSCALE] = {
.name = "XScale",
.fifo_size = 32,
.tx_loadsz = 32,
.fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_10,
.flags = UART_CAP_FIFO | UART_CAP_UUE | UART_CAP_RTOIE,
},
[PORT_RM9000] = {
.name = "RM9000",
.fifo_size = 16,
.tx_loadsz = 16,
.fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_10,
.flags = UART_CAP_FIFO,
},
[PORT_OCTEON] = {
.name = "OCTEON",
.fifo_size = 64,
.tx_loadsz = 64,
.fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_10,
.flags = UART_CAP_FIFO,
},
[PORT_AR7] = {
.name = "AR7",
.fifo_size = 16,
.tx_loadsz = 16,
.fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_10,
.flags = UART_CAP_FIFO | UART_CAP_AFE,
},
[PORT_U6_16550A] = {
.name = "U6_16550A",
.fifo_size = 64,
.tx_loadsz = 64,
.fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_10,
.flags = UART_CAP_FIFO | UART_CAP_AFE,
},
[PORT_TEGRA] = {
.name = "Tegra",
.fifo_size = 32,
.tx_loadsz = 8,
.fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_01 |
 UART_FCR_T_TRIG_01,
.flags = UART_CAP_FIFO | UART_CAP_RTOIE,
},
[PORT_XR17D15X] = {
.name = "XR17D15X",
.fifo_size = 64,
.tx_loadsz = 64,
.fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_10,
.flags = UART_CAP_FIFO | UART_CAP_AFE | UART_CAP_EFR,
},
};
到此EDMA功能实现。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

基于OMAPL138的linux平台8250快速串口实现--UART+EDMA 的相关文章

  • 字符串链式队列入队出队

    include 34 queue h 34 include 34 stdio h 34 include 34 malloc h 34 64 brief 链式队列数据结构定义 typedef struct QueueStruct char v
  • AD21几个容易忘记的快捷键

    CTRL 43 D 打开透明模式 适合等长操作 CTRL 43 M 测量距离 SHIFT 43 R 推挤走线 D 43 K 打开层叠管理器 t 43 v 43 g 从板框选择铺铜 设置鼠标滑过显示高亮 取消自动闭合回路 xff0c 用于打多
  • C字符队列

    链式队列数据结构定义 typedef struct QueueStruct char value struct QueueStruct next queueStruct void QueueInit brief 链式队列初始化 void E
  • QT+OpenGL(1)——包含头文件

    关于智能提示建议放弃Cmake生成 xff0c 直接包含头文件 xff0c 包含方法如下 如果找不到头文件 xff0c 直接用everything搜相应文件 xff0c 再给包含进去 需要新建文件夹 xff0c 新建文件 c cpp pro
  • 2d激光雷达(rplidar_s1)与双目摄像头联合标定

    前段时间由于项目需要使用摄像头 xff08 realsense d435i xff09 与单线激光雷达进行融合 xff0c 于是就对这两个传感器进行了标定 xff0c 使用的是CamLaserCalibraTool xff0c 这是别人开源
  • ubuntu生成&设置core文件,调试段错误

    在用ubuntu系统编码c 43 43 程序时 xff0c 经常遇到段错误 xff0c 以下介绍ubuntu系统下如何生成core文件 xff0c 设置core文件路径 xff0c 并进行调试core文件 xff0c 找到段错误原因 1 允
  • ECharts仪表盘设置主题文字颜色

    设置仪表盘中显示字体样式 xff1a 方式如下 xff1a eChartA setOption tooltip formatter 34 a lt br gt b c 34 toolbox show true feature mark sh
  • 【LWC】Resource not found异常的一个可能原因

    Resource not found异常的一个可能原因 场景 在我当前项目中 xff0c 有一个用来创建记录的LWC组件 创建记录的流程大致为以下三步 xff1a 用户输入信息用户点击保存 xff0c 后台处理数据 xff0c 创建记录记录
  • 使用apt-get update命令卡在waiting for headers

    今天 xff0c 想在自己的debian系统里面装下java8 xff0c 添加了一个源update的时候出现的点问题就ctrl c了 没想到之后再update就不行了 一直卡在100 正在读报头waiting for headers 网上
  • 四轴飞行器基本组成及其飞行原理详解

    近日 xff0c 自己组装了一台 四轴飞行器 xff1b 组装完后 xff0c 便想深究其原理 xff1b 避免只是 知其然 xff0c 却不知其所以然 xff1b 查阅资料后 xff0c 便在其他文章的基础上 xff0c 将此文 归纳整理
  • ubuntu20.04分区方案 for deeplearning

    一共分出4个系统分区 1 设置efi引导 因为是u盘的uefi启动 xff0c 因此设置一个efi引导项 具体参数 xff1a 大小 500到1024mb即可 xff08 视自身的存储空间而定 xff09 新分区的类型 xff1a 逻辑分区
  • Linux重定向和管道符

    Linux重定向和管道符 1 Uid gid是什么 xff1f 2 linux中设置环境变量的几种方法3 管道命令符和通配符4 输入输出重定向 1 Uid gid是什么 xff1f 1 1 用户组 UID以及GID概念 用户组 xff1a
  • OpenFlow概念学习

    前言 OpenFlow交换机将原来完全由交换机 路由器控制的报文转发过程转化为由OpenFlow 交换机和控制服务器来共同完成 xff0c 目的交换机要通过of协议 xff08 OpenFlow Protocol xff09 经 安全通道
  • Ubuntu创建用户(组)与权限管理

    Ubuntu创建用户 xff08 组 xff09 与权限管理 创建用户与用户组创建和删除用户及用户组给用户配置sudo权限用户管理相关的命令 创建用户与用户组 在管理服务器时 xff0c 需要注意用户权限分配 xff0c 这样不会造成重大的
  • docker修改容器与宿主机端口映射

    1 查看容器id docker ps 2 进入容器安装目录 cd var lib docker containers 找到容器对应的文件夹 xff0c 容器id与文件夹前面的id是一样的 进入文件夹 cd 593f0dd680d77e901
  • Prometheus源码学习(1) 编译源码

    代码里面看不明白的变量或者函数可以通过两种方式观测它的值来了解其含义 一种是把代码片段摘出来 xff0c 写到一个测试程序里运行一下另一种时日志里打印它的值来观察 第一种比较简单易行 xff0c 但是代码片段要比较独立才好做 xff0c 第
  • ubuntu的命令&操作

    记录ubuntu系统使用中一些常用的操作 1 在ubuntu终端打开图像界面的文件夹 xff1a cd到指定的目录之后执行nautilus即可 参考 xff1a linux系统ubuntu中在命令行如何打开图形界面的文件夹 李照耀 博客园
  • Leetcode解题目录(Python版)

    Leetcode解题目录 xff08 Python版 xff09 题目目录1 排序算法2 哈希表3 动态规划 题目来源于LeetCode官网题库 xff0c 解题思路参考官网各大佬 xff0c 这里做一个目录方便大家查找 xff0c 另外方
  • error while loading shared libraries: libopencv_imgcorecs.so.3.4:: cannot open shared object file:

    ubuntu16 04的环境下 xff0c 编译成功C 43 43 代码 xff0c 同时也安装好了opencv3 4 在运行时却出现error xff1a error while loading shared libraries libo
  • 基于LQR的车辆LKA算法设计

    记录分享一下基于LQR控制算法的车辆LKA设计 通过carsim和simulink进行联合仿真 1控制算法设计 整体思路是通过车辆以及道路模型 得到控制器的输入 v 纵向速度 r 横摆角速度 y 侧向路径偏移 phi 横摆角误差 通过最优控

随机推荐

  • 软件框架

    1 概念 框架 xff08 framework xff09 是一个框子 指其约束性 xff0c 也是一个架子 指其支撑性 是一个基本概念上的结构 xff0c 用于去解决或者处理复杂的问题 框架这个广泛的定义使用的十分流行 xff0c 尤其在
  • 使用C++开发STM32 FreeRTOS工程与添加DSP库

    续上一篇文章的内容 由CubeMX构建的Makefile工程只支持C和汇编的编译 xff0c 而且FreeRTOS的代码也需要作为C代码编译 当我们想使用C 43 43 时 xff0c 需要做一些准备并且修改Makefile xff1b 另
  • CPU分时、中断和上下文切换

    准备知识 xff1a 实时和分时 嵌入式操作系统可以分为实时操作系统和分时操作系统两类 我们现实之中使用的绝大多数是分时操作系统 xff0c 比如windows或者linux 但是比如汽车就必须使用实时操作系统 xff0c 举一个经常使用的
  • 如何从AD导出PDF原理图

    https jingyan baidu com article 7c6fb428d4759080642c9017 html
  • PyQt ——setStyleSheet用法

    这个是CSS的手册 xff0c 所有的东西都可以参考这里 xff1a https css doyoe com 下面是CSS 的东西 字体属性 xff1a font 大小 font size x large 特大 xx small 极小 一般
  • QMessageBox

    span class token triple quoted string string 39 39 39 简介 PyQt5中 QMessage 例子 39 39 39 span span class token keyword impor
  • QInputDialog

    span class token triple quoted string string 39 39 39 简介 PyQt5中 QInputDialog 例子 39 39 39 span span class token keyword i
  • python cherry 用法

    CheeryPy是一个 Pythonic 的 面向对象的 Web 框架 xff0c 能够用于接受POST或者GET请求并进行回复 CheeryPy中文文档 xff1a Cherrypy 一个极简的python web框架 CherryPy
  • PyQt——简单进度条程序

    span class token keyword from span PyQt5 span class token punctuation span QtCore span class token keyword import span Q
  • PyQt——窗口居中

    span class token comment coding utf 8 span span class token keyword import span sys span class token keyword from span P
  • PyQt——分离UI主线程与工作线程

    span class token comment coding utf 8 span span class token keyword import span sys span class token keyword from span P
  • PyQt——信号与槽简介

    信号与槽简介 定义信号 操作信号
  • PyQt——信号与槽基础应用

    信号与槽有三种使用方法 xff0c 第一种是内置信号与槽的使用 xff0c 第二种是自定义信 号与槽的使用 xff0c 第三是装饰器的信号与槽的使用 由于第三种方法本质上是第一 种方法的衍生 内置信号与槽的使用 所谓内置信号与槽的使用 xf
  • PyQt——信号与槽函数快速进阶

    内置信号与槽函数 span class token keyword from span PyQt5 span class token punctuation span QtWidgets span class token keyword i
  • PyQt——自定义信号与槽的高级应用

    高级自定义信号与槽 span class token keyword from span PyQt5 span class token punctuation span QtCore span class token keyword imp
  • PyQt——键盘事件和鼠标事件

    PyQt为事件处理提供了两种机制 xff1a 高级的信号和槽机制 xff0c 以及低级的事件处理程序 PyQt为拦截和处理事件提供了5种不同的方式 xff0c 这里只介绍最常用的头两种方式 第一种是重新实现特定事件 xff0c 如键盘和鼠标
  • PyQt——按钮类控件QAbstractButton

    按钮类控件 QPushButton
  • PyQt——按钮类控件QPushButton

    span class token keyword import span sys span class token keyword from span PyQt5 span class token punctuation span QtCo
  • spss入门基本用法

    一 xff0e 数据 1 个案排序 xff1a 对数据视图中的某个个案进行排序 xff0c 具体排序规则可以点进去选择 2 变量排序 xff1a 对变量视图中某个变量进行排序 xff0c 具体规则可以点进去选择 3 转置 xff1a 行列互
  • 基于OMAPL138的linux平台8250快速串口实现--UART+EDMA

    本文源码基于dvsdk omapl138 evm 04 03 00 06 setuplinux程序包里的linux3 3 0版本的内核 实现EDMA支持UART功能主要修改dma和8250的源码 首先为OMAPL138的arm和DSP分配d