|
static void autoconfig_irq(struct uart_8250_port *up)
{
unsigned char save_mcr, save_ier;
unsigned char save_ICP = 0;
unsigned int ICP = 0;
unsigned long irqs;
int irq;
if (up->port.flags & UPF_FOURPORT) {
ICP = (up->port.iobase & 0xfe0) | 0x1f;
save_ICP = inb_p(ICP);
outb_p(0x80, ICP);
(void) inb_p(ICP);
}
/* forget possible initially masked and pending IRQ */
probe_irq_off(probe_irq_on());
save_mcr = serial_inp(up, UART_MCR);
save_ier = serial_inp(up, UART_IER);
serial_outp(up, UART_MCR, UART_MCR_OUT1 | UART_MCR_OUT2);
irqs = probe_irq_on();
serial_outp(up, UART_MCR, 0);
udelay(10);
if (up->port.flags & UPF_FOURPORT) {
serial_outp(up, UART_MCR,
UART_MCR_DTR | UART_MCR_RTS);
} else {
serial_outp(up, UART_MCR,
UART_MCR_DTR | UART_MCR_RTS | UART_MCR_OUT2);
}
serial_outp(up, UART_IER, 0x0f); /* enable all intrs */
(void)serial_inp(up, UART_LSR);
(void)serial_inp(up, UART_RX);
(void)serial_inp(up, UART_IIR);
(void)serial_inp(up, UART_MSR);
serial_outp(up, UART_TX, 0xFF);
udelay(20);
irq = probe_irq_off(irqs);
serial_outp(up, UART_MCR, save_mcr);
serial_outp(up, UART_IER, save_ier);
if (up->port.flags & UPF_FOURPORT)
outb_p(save_ICP, ICP);
up->port.irq = (irq > 0) ? irq : 0;
}
|
在上述代码的操作中,先将8250相关中断允许寄存器全打开.然后调用驱动使用的函数, 当它不得不探测来决定哪个中断线被设备在使用.
probe_irq_on()将中断暂时关掉,然后配置MCR寄存器使之发送DTR和RTS.之后再用probe_irq_off()来检测IRQ号.如
果检测成功,则值赋值给port->irq.
进行到这里,conifg_port动作就完成了.
经过这个config_port过程后,我们发现,并没有对serial8250_isa_devs->dev->
platform_data赋值,也就是说platform_driver->probe函数并无实质性的处理.在第一次for循环的时,就会因条件不符而退出.
四:
startup操作
在前面分析uart驱动架构的时候,曾说过,在open的时候,会调用port->startup().在本次分析的驱动中,对应接口为serial8250_startup().分段分析如下:
static int serial8250_startup(struct uart_port *port)
{
struct uart_8250_port *up = (struct uart_8250_port *)port;
unsigned long flags;
unsigned char lsr, iir;
int retval;
up->capabilities = uart_config[up->port.type].flags;
up->mcr = 0;
if (up->port.type == PORT_16C950) {
/* Wake up and initialize UART */
up->acr = 0;
serial_outp(up, UART_LCR, 0xBF);
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, 0xBF);
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);
上面的代码都不是对应8250芯片的情况
/*
* 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);
复位LSR,RX,IIR,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("ttyS%d: LSR safety check engaged!\n", up->port.line);
return -ENODEV;
}
若LSR寄存器中的值为0xFF.异常
/*
* For a XR16C850, we need to set the trigger levels
*/
if (up->port.type == PORT_16850) {
unsigned char fctr;
serial_outp(up, UART_LCR, 0xbf);
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);
}
16850系列芯片的处理,忽略
if (is_real_interrupt(up->port.irq)) {
/*
* 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);
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("ttyS%d - using backup timer\n", port->line);
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 "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 + 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("ttyS%d - enabling bad tx status workarounds\n",
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("serial8250_interrupt(%d)...", irq);
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. */
printk(KERN_ERR "serial8250: too much work for "
"irq%d\n", irq);
break;
}
} while (l != end);
spin_unlock(&i->lock);
DEBUG_INTR("end.\n");
return IRQ_RETVAL(handled);
}
|
这里可能有个疑问的地方,挂在这个链表上的到底是什么.这我们要从serial_link_irq_chain()来说起.该函数代码如下:
static int serial_link_irq_chain(struct uart_8250_port *up)
{
struct irq_info *i = irq_lists + up->port.irq;
int ret, irq_flags = up->port.flags & UPF_SHARE_IRQ ? IRQF_SHARED : 0;
spin_lock_irq(&i->lock);
if (i->head) {
list_add(&up->list, i->head);
spin_unlock_irq(&i->lock);
ret = 0;
} else {
INIT_LIST_HEAD(&up->list);
i->head = &up->list;
spin_unlock_irq(&i->lock);
ret = request_irq(up->port.irq, serial8250_interrupt,
irq_flags, "serial", i);
if (ret < 0)
serial_do_unlink(i, up);
}
return ret;
}
|
从这里看到,注册中断处理函数的参数i就是对应irq_lists + up->port.irq.即对应在irq_lists数组中的port->irq项.随后,将注册的uart_8250_port添加到了这个链表.
奇怪了,为什么要这么做了?我们返回old_serial_port的定义看看:
static const struct old_serial_port old_serial_port[] = {
SERIAL_PORT_DFNS /* defined in asm/serial.h */
};
#define SERIAL_PORT_DFNS
/* UART CLK PORT IRQ FLAGS */
{ 0, BASE_BAUD, 0x3F8, 4, STD_COM_FLAGS }, /* ttyS0 */
{ 0, BASE_BAUD, 0x2F8, 3, STD_COM_FLAGS }, /* ttyS1 */
{ 0, BASE_BAUD, 0x3E8, 4, STD_COM_FLAGS }, /* ttyS2 */
{ 0, BASE_BAUD, 0x2E8, 3, STD_COM4_FLAGS }, /* ttyS3 */
|
在这里,注意到同一个IRQ号会对应两个port.
IRQ中发生中断的时候,怎么去判断是哪一个port所引起的.当然方法有多种多样.在这里,8250驱动的作者是将不同的port链入到IRQ对应的链
表来完成的.这样,如果IRQ产生了中断了,就判断挂在该链表中的port,看中断是否由它产生.
经过这个分析之后,我们应该很清楚serial8250_interrupt()中的处理流程了.对应产生IRQ的port,流程会转入serial8250_handle_port()中.代码如下:
static inline void
serial8250_handle_port(struct uart_8250_port *up)
{
unsigned int status;
unsigned long flags;
spin_lock_irqsave(&up->port.lock, flags);
status = serial_inp(up, UART_LSR);
DEBUG_INTR("status = %x...", status);
if (status & UART_LSR_DR)
receive_chars(up, &status);
check_modem_status(up);
if (status & UART_LSR_THRE)
transmit_chars(up);
spin_unlock_irqrestore(&up->port.lock, flags);
}
|
对于产生中断的情况下,判断发送缓存区是否为空,如果为空,就可以发送数据了.对应的处理在transmit_chars(up).如果接收缓存区满,就那接收数据,这是在receive_chars()中处理的.对于接收数据,我们在下一节再分析.
transmit_chars()代码如下:
static void transmit_chars(struct uart_8250_port *up)
{
struct circ_buf *xmit = &up->port.info->xmit;
int count;
if (up->port.x_char) {
serial_outp(up, UART_TX, up->port.x_char);
up->port.icount.tx++;
up->port.x_char = 0;
return;
}
if (uart_tx_stopped(&up->port)) {
serial8250_stop_tx(&up->port);
return;
}
if (uart_circ_empty(xmit)) {
__stop_tx(up);
return;
}
count = up->tx_loadsz;
do {
serial_out(up, UART_TX, xmit->buf[xmit->tail]);
xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
up->port.icount.tx++;
if (uart_circ_empty(xmit))
break;
} while (--count > 0);
if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
uart_write_wakeup(&up->port);
DEBUG_INTR("THRE...");
if (uart_circ_empty(xmit))
__stop_tx(up);
}
|
从上面的代码看出.会从xmit中取出数据,然后将其写入到发送寄存器中.特别的,在8250芯片的情况下, up->tx_loadsz等于1.也就是说,一次只能传送1个字节.
如果缓存区的数据传输玩了之后,就会调用__stop_tx().代码如下:
static inline void __stop_tx(struct uart_8250_port *p)
{
if (p->ier & UART_IER_THRI) {
p->ier &= ~UART_IER_THRI;
serial_out(p, UART_IER, p->ier);
}
}
|
对应的,在IER中,将发送缓存区空的中断关掉.
六:数据读取操作
在前面的tty驱动架构分析中,曾说过,在
tty_driver中并末提供read接口.上层的read操作是直接到ldsic的缓存区中读数据的.那ldsic的数据是怎么送入进去的呢?继续看
中断处理中的数据接收流程.即为: receive_chars().代码片段如下:
tatic void
receive_chars(struct uart_8250_port *up, unsigned int *status)
{
……
……
uart_insert_char(&up->port, lsr, UART_LSR_OE, ch, flag);
}
最后流据会转入uart_inset_char().这个函数是uart层提供的一个接口,代码如下:
static inline void
uart_insert_char(struct uart_port *port, unsigned int status,
unsigned int overrun, unsigned int ch, unsigned int flag)
{
struct tty_struct *tty = port->info->tty;
if ((status & port->ignore_status_mask & ~overrun) == 0)
tty_insert_flip_char(tty, ch, flag);
/*
* Overrun is special. Since it's reported immediately,
* it doesn't affect the current character.
*/
if (status & ~port->ignore_status_mask & overrun)
tty_insert_flip_char(tty, 0, TTY_OVERRUN);
}
|
Tty_insert_filp()函数的代码我们在之前已经分析过,这里不再赘述.就这样,数据就直接交给了ldisc.
七:轮询操作
在前面已经分析到,如果没有定义irq或者没有控测到irq号,就会采用轮询.在代码,采用定时器的方式.去判断是否有数据到来,或者将数据写入8250.定时器对应的运行函数为serial8250_backup_timeout().代码如下:
static void serial8250_backup_timeout(unsigned long data)
{
struct uart_8250_port *up = (struct uart_8250_port *)data;
unsigned int iir, ier = 0, lsr;
unsigned long flags;
/*
* Must disable interrupts or else we risk racing with the interrupt
* based handler.
*/
if (is_real_interrupt(up->port.irq)) {
ier = serial_in(up, UART_IER);
serial_out(up, UART_IER, 0);
}
iir = serial_in(up, UART_IIR);
/*
* This should be a safe test for anyone who doesn't trust the
* IIR bits on their UART, but it's specifically designed for
* the "Diva" UART used on the management processor on many HP
* ia64 and parisc boxes.
*/
spin_lock_irqsave(&up->port.lock, flags);
lsr = serial_in(up, UART_LSR);
up->lsr_saved_flags |= lsr & LSR_SAVE_FLAGS;
spin_unlock_irqrestore(&up->port.lock, flags);
if ((iir & UART_IIR_NO_INT) && (up->ier & UART_IER_THRI) &&
(!uart_circ_empty(&up->port.info->xmit) || up->port.x_char) &&
(lsr & UART_LSR_THRE)) {
iir &= ~(UART_IIR_ID | UART_IIR_NO_INT);
iir |= UART_IIR_THRI;
}
if (!(iir & UART_IIR_NO_INT))
serial8250_handle_port(up);
if (is_real_interrupt(up->port.irq))
serial_out(up, UART_IER, ier);
/* Standard timer interval plus 0.2s to keep the port running */
mod_timer(&up->timer,
jiffies + poll_timeout(up->port.timeout) + HZ / 5);
}
|
如果IRQ线有效,先在IER中禁用全部中断.等定时器处理函数处理完后,再恢复IER中的内容.这样主要是为了防止会产生发送缓存区空的中断.
流程最后还是会转入到serial8250_handle_port()中.这个函数我们在上面已经分析过了.
八:小结
分析完了这个驱动,我们可以看到.专业的开发人员思维是多么的缜密.真是滴水不漏.该代码里有很多非常精彩的处理,需要细细揣摩.