- 論壇徽章:
- 0
|
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相關(guān)中斷允許寄存器全打開.然后調(diào)用驅(qū)動使用的函數(shù), 當(dāng)它不得不探測來決定哪個(gè)中斷線被設(shè)備在使用.
probe_irq_on()將中斷暫時(shí)關(guān)掉,然后配置MCR寄存器使之發(fā)送DTR和RTS.之后再用probe_irq_off()來檢測IRQ號.如
果檢測成功,則值賦值給port->irq.
進(jìn)行到這里,conifg_port動作就完成了.
經(jīng)過這個(gè)config_port過程后,我們發(fā)現(xiàn),并沒有對serial8250_isa_devs->dev-> platform_data賦值,也就是說platform_driver->probe函數(shù)并無實(shí)質(zhì)性的處理.在第一次for循環(huán)的時(shí),就會因條件不符而退出.
四: startup操作
在前面分析uart驅(qū)動架構(gòu)的時(shí)候,曾說過,在open的時(shí)候,會調(diào)用port->startup().在本次分析的驅(qū)動中,對應(yīng)接口為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);
上面的代碼都不是對應(yīng)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);
復(fù)位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);
}
}
如果中斷號有效,還要進(jìn)一步判斷這個(gè)中斷號是否有效.具體操作為,先等待8250發(fā)送寄存器空.然后允許發(fā)送中斷空的中斷.然后判斷IIR寄存器是否收到
中斷.如果有沒有收到中斷,則說明這根中斷線無效.只能采用輪詢的方式.關(guān)于輪詢方式,我們在之后再以獨(dú)立章節(jié)的形式給出分析
/*
* 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;
}
如果沒有設(shè)置中斷號,則采用輪詢方式.如果中斷后有效.流程轉(zhuǎn)入serial_link_irq_chain().在這個(gè)里面.會注冊中斷處理函數(shù).
/*
* 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中設(shè)置數(shù)據(jù)格式,在MCR中設(shè)置允許中斷到8259.在IER中設(shè)置相關(guān)允許位.
另外在open的時(shí)候,還會調(diào)用port-> enable_ms ()接口,在本例中對應(yīng)為: 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中斷
五:數(shù)據(jù)發(fā)送的操作
在uart驅(qū)動架構(gòu)中分析過,在發(fā)送數(shù)據(jù)的時(shí)候,uart層先會將數(shù)據(jù)放入circ_buffer.最后再調(diào)用port-> start_tx().
在這里,這個(gè)接口對應(yīng)為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);
}
}
這個(gè)函數(shù)非常簡單.如果沒有定義發(fā)送空中斷.則在IER中打開這個(gè)中斷.關(guān)于TXEN上的bug修復(fù)和16C950類型的芯片不是我們所關(guān)注的部份.
那,這里只是打開了這個(gè)中斷.寫數(shù)據(jù)到芯片的這個(gè)過程是在什么地方完成的呢?
是在中斷處理中.如果是發(fā)送空的中斷,就將circ buffer中的數(shù)據(jù)寫出發(fā)送寄存器.跟蹤一下代碼.中斷處理函數(shù)為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);
}
這里可能有個(gè)疑問的地方,掛在這個(gè)鏈表上的到底是什么.這我們要從serial_link_irq_chain()來說起.該函數(shù)代碼如下:
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;
}
從這里看到,注冊中斷處理函數(shù)的參數(shù)i就是對應(yīng)irq_lists + up->port.irq.即對應(yīng)在irq_lists數(shù)組中的port->irq項(xiàng).隨后,將注冊的uart_8250_port添加到了這個(gè)鏈表.
奇怪了,為什么要這么做了?我們返回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 */
在這里,注意到同一個(gè)IRQ號會對應(yīng)兩個(gè)port.
IRQ中發(fā)生中斷的時(shí)候,怎么去判斷是哪一個(gè)port所引起的.當(dāng)然方法有多種多樣.在這里,8250驅(qū)動的作者是將不同的port鏈入到IRQ對應(yīng)的鏈
表來完成的.這樣,如果IRQ產(chǎn)生了中斷了,就判斷掛在該鏈表中的port,看中斷是否由它產(chǎn)生.
經(jīng)過這個(gè)分析之后,我們應(yīng)該很清楚serial8250_interrupt()中的處理流程了.對應(yīng)產(chǎn)生IRQ的port,流程會轉(zhuǎn)入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);
}
對于產(chǎn)生中斷的情況下,判斷發(fā)送緩存區(qū)是否為空,如果為空,就可以發(fā)送數(shù)據(jù)了.對應(yīng)的處理在transmit_chars(up).如果接收緩存區(qū)滿,就那接收數(shù)據(jù),這是在receive_chars()中處理的.對于接收數(shù)據(jù),我們在下一節(jié)再分析.
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中取出數(shù)據(jù),然后將其寫入到發(fā)送寄存器中.特別的,在8250芯片的情況下, up->tx_loadsz等于1.也就是說,一次只能傳送1個(gè)字節(jié).
如果緩存區(qū)的數(shù)據(jù)傳輸玩了之后,就會調(diào)用__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);
}
}
對應(yīng)的,在IER中,將發(fā)送緩存區(qū)空的中斷關(guān)掉.
六:數(shù)據(jù)讀取操作
在前面的tty驅(qū)動架構(gòu)分析中,曾說過,在
tty_driver中并末提供read接口.上層的read操作是直接到ldsic的緩存區(qū)中讀數(shù)據(jù)的.那ldsic的數(shù)據(jù)是怎么送入進(jìn)去的呢?繼續(xù)看
中斷處理中的數(shù)據(jù)接收流程.即為: 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);
}
最后流據(jù)會轉(zhuǎn)入uart_inset_char().這個(gè)函數(shù)是uart層提供的一個(gè)接口,代碼如下:
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()函數(shù)的代碼我們在之前已經(jīng)分析過,這里不再贅述.就這樣,數(shù)據(jù)就直接交給了ldisc.
七:輪詢操作
在前面已經(jīng)分析到,如果沒有定義irq或者沒有控測到irq號,就會采用輪詢.在代碼,采用定時(shí)器的方式.去判斷是否有數(shù)據(jù)到來,或者將數(shù)據(jù)寫入8250.定時(shí)器對應(yīng)的運(yùn)行函數(shù)為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中禁用全部中斷.等定時(shí)器處理函數(shù)處理完后,再恢復(fù)IER中的內(nèi)容.這樣主要是為了防止會產(chǎn)生發(fā)送緩存區(qū)空的中斷.
流程最后還是會轉(zhuǎn)入到serial8250_handle_port()中.這個(gè)函數(shù)我們在上面已經(jīng)分析過了.
八:小結(jié)
分析完了這個(gè)驅(qū)動,我們可以看到.專業(yè)的開發(fā)人員思維是多么的縝密.真是滴水不漏.該代碼里有很多非常精彩的處理,需要細(xì)細(xì)揣摩.
本文來自ChinaUnix博客,如果查看原文請點(diǎn):http://blog.chinaunix.net/u3/92500/showart_2159909.html |
|