* 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. */