linux设备驱动之8250串口驱动(4)

2019-04-08 22:11

* 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);

wait_for_xmitr(up, UART_LSR_THRE);

serial_out_sync(up, UART_IER, UART_IER_THRI); udelay(1); /* allow THRE to set */ 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);

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 (iir & UART_IIR_NO_INT) {

pr_debug(\ up->timer.function = serial8250_backup_timeout; up->timer.data = (unsigned long)up; mod_timer(&up->timer, jiffies +

poll_timeout(up->port.timeout) + HZ / 5); } }

如果中断号有效,还要进一步判断这个中断号是否有效.具体操作为,先等待8250发送寄存器空.然后允许发送中断空的中断.然后判断IIR寄存器是否收到中断.如果有没有收到中断,则说明这根中断线无效.只能采用轮询的方式.关于轮询方式,我们在之后再以独立章节的形式给出分析

/*

* If the \ * 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 + poll_timeout(up->port.timeout)); } else {

retval = serial_link_irq_chain(up);

if (retval)

return retval; }

如果没有设置中断号,则采用轮询方式.如果中断后有效.流程转入serial_link_irq_chain().在这个里面.会注册中断处理函数. /*

* 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);

/*

* 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(\ port->line); } } 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;

/*

* 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;

}

最后,就是对8250芯片的初始化了.包括:在LCR中设置数据格式,在MCR中设置允许中断到8259.在IER中设置相关允许位.

另外在open的时候,还会调用port-> enable_ms ()接口,在本例中对应为: serial8250_enable_ms().代码如下:

static void serial8250_enable_ms(struct uart_port *port) {

struct uart_8250_port *up = (struct uart_8250_port *)port;

/* no MSR capabilities */

if (up->bugs & UART_BUG_NOMSR) return;

up->ier |= UART_IER_MSI;

serial_out(up, UART_IER, up->ier); }

即允许moden中断

五:数据发送的操作

在uart驱动架构中分析过,在发送数据的时候,uart层先会将数据放入circ_buffer.最后再调用port-> start_tx().

在这里,这个接口对应为serial8250_start_tx().代码如下: static void serial8250_start_tx(struct uart_port *port) {

struct uart_8250_port *up = (struct uart_8250_port *)port;

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, iir; lsr = serial_in(up, UART_LSR);

up->lsr_saved_flags |= lsr & LSR_SAVE_FLAGS; iir = serial_in(up, UART_IIR) & 0x0f; if ((up->port.type == PORT_RM9000) ? (lsr & UART_LSR_THRE &&

(iir == UART_IIR_NO_INT || iir == UART_IIR_THRI)) : (lsr & UART_LSR_TEMT && iir & UART_IIR_NO_INT)) transmit_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);

} }

这个函数非常简单.如果没有定义发送空中断.则在IER中打开这个中断.关于TXEN上的bug修复和16C950类型的芯片不是我们所关注的部份.

那,这里只是打开了这个中断.写数据到芯片的这个过程是在什么地方完成的呢?

是在中断处理中.如果是发送空的中断,就将circ buffer中的数据写出发送寄存器.跟踪一下代码.中断处理函数为serial8250_interrupt().

static irqreturn_t serial8250_interrupt(int irq, void *dev_id) {

struct irq_info *i = dev_id;

struct list_head *l, *end = NULL; int pass_counter = 0, handled = 0;

DEBUG_INTR(\

spin_lock(&i->lock);

l = i->head; do {

struct uart_8250_port *up; unsigned int iir;

up = list_entry(l, struct uart_8250_port, list);

iir = serial_in(up, UART_IIR); if (!(iir & UART_IIR_NO_INT)) { serial8250_handle_port(up);

handled = 1;

end = NULL;

} else if (up->port.iotype == UPIO_DWAPB &&

(iir & UART_IIR_BUSY) == UART_IIR_BUSY) { /* The DesignWare APB UART has an Busy Detect (0x07) * interrupt meaning an LCR write attempt occured while the * UART was busy. The interrupt must be cleared by reading * the UART status register (USR) and the LCR re-written. */ unsigned int status;

status = *(volatile u32 *)up->port.private_data; serial_out(up, UART_LCR, up->lcr);

handled = 1;

end = NULL; } else if (end == NULL) end = l;

l = l->next;

if (l == i->head && pass_counter++ > PASS_LIMIT) { /* If we hit this, we're dead. */


linux设备驱动之8250串口驱动(4).doc 将本文的Word文档下载到电脑 下载失败或者文档不完整,请联系客服人员解决!

下一篇:曾仕强经典语录

相关阅读
本类排行
× 注册会员免费下载(下载后可以自由复制和排版)

马上注册会员

注:下载文档有可能“只有目录或者内容不全”等情况,请下载之前注意辨别,如果您已付费且无法下载或内容有问题,请联系我们协助你处理。
微信: QQ: