本文源码基于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_tlen[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
",
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
",
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
",
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
",
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
",
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,还要在archarmmach-davinciDma.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_tmapbase;
/* 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_portport;
struct timer_listtimer;
/* "no irq" timer */
struct list_headlist;
/* ports on this IRQ */
unsigned shortcapabilities;
/* port capabilities */
unsigned shortbugs;
/* 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_blockfreq_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
", 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!
",
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
",
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
",
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!
",
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
",
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
",
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!
");
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
");
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
");
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, ¶m);
ret = edma_start(channel);
if(0!=ret){
printk("Error:Edma start fail!
") ;
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
");
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
");
}
}
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!
");
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
");
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)
{