📄 at91_tl16c554.c
字号:
/*----------------------------------------------------------** linux/drivers/at91/at91_tl16c554.c** **** Copyright (C) 2006 Hyesco Technology Co.,Ltd**** Author: casiawu <wujh@hyesco.com>**** History:**** 2006.6 casiawu <wujh@hyesco.com>** Original from at91_serial.c**---------------------------------------------------------*/#include <linux/config.h>#include <linux/module.h>#include <linux/tty.h>#include <linux/ioport.h>#include <linux/slab.h>#include <linux/init.h>#include <linux/serial.h>#include <linux/console.h>#include <linux/sysrq.h>#include <asm/mach/serial_at91rm9200.h>#include <asm/io.h>#include <asm/arch/pio.h>#include <linux/serial_core.h>#include "./at91_tl16c554.h"#undef DEBUG /* #define DEBUG 1 */#ifdef DEBUG#define DPRINTK( x... ) printk( ##x )#else#define DPRINTK( x... )#endif#define ISR_PASS_LIMIT 256#define UART_PUT_FCR(port,v) ((TL16C554PS_USART)(port)->membase)->US_FCR_IIR = v#define UART_GET_LCR(port) ((TL16C554PS_USART)(port)->membase)->US_LCR#define UART_PUT_LCR(port,v) ((TL16C554PS_USART)(port)->membase)->US_LCR = v#define UART_PUT_IER(port,v) ((TL16C554PS_USART)(port)->membase)->US_DLM_IER |= v#define UART_GET_IER(port) ((TL16C554PS_USART)(port)->membase)->US_DLM_IER #define UART_PUT_IDR(port,v) ((TL16C554PS_USART)(port)->membase)->US_DLM_IER &= ~v #define UART_GET_IIR(port) ((TL16C554PS_USART)(port)->membase)->US_FCR_IIR#define UART_GET_LSR(port) ((TL16C554PS_USART)(port)->membase)->US_LSR#define UART_GET_CHAR(port) ((TL16C554PS_USART)(port)->membase)->US_RBR_THR_DLL#define UART_PUT_CHAR(port,v) ((TL16C554PS_USART)(port)->membase)->US_RBR_THR_DLL = v#define UART_PUT_BRGR(port,v) { \ ((TL16C554PS_USART)(port)->membase)->US_LCR |= 0x80 ; \ ((TL16C554PS_USART)(port)->membase)->US_RBR_THR_DLL = v & 0xff ; \ ((TL16C554PS_USART)(port)->membase)->US_DLM_IER = (v & 0xff00)>>8 ; \ ((TL16C554PS_USART)(port)->membase)->US_LCR &= 0x7F ; }#define UART_PUT_MCR(port,v) ((TL16C554PS_USART)(port)->membase)->US_MCR = v #define UART_GET_MSR(port) ((TL16C554PS_USART)(port)->membase)->US_MSR static struct uart_port tl16c554_ports[TL16C554_NR_UART];const int tl16c554_serialmap[TL16C554_NR_UART] = TL16C554_UART_MAP;static int (*tl16c554_open)(struct uart_port *);static void (*tl16c554_close)(struct uart_port *);/* * Return TIOCSER_TEMT when transmitter FIFO and Shift register is empty. */static u_int tl16c554_tx_empty(struct uart_port *port){ int ret=0 ,status; DPRINTK("\n\\rtl16c554_tx_empty!\n\r"); status = UART_GET_LSR(port); if((status & TL16C554_US_THRE) || (status & TL16C554_US_TEMT)) ret = TIOCSER_TEMT; return ret;}/* * Set state of the modem control output lines */static void tl16c554_set_mctrl(struct uart_port *port, u_int mctrl){ unsigned int control = 0; DPRINTK("\n\\rtl16c554_set_mctrl!\n\r"); control = UART_GET_LCR(port); if (mctrl & TIOCM_RTS) control |= TL16C554_US_RTSEN; else control &= ~TL16C554_US_RTSEN; if (mctrl & TIOCM_DTR) control |= TL16C554_US_DTREN; else control &= ~TL16C554_US_DTREN; UART_PUT_LCR(port,control);}/* * Get state of the modem control input lines */static u_int tl16c554_get_mctrl(struct uart_port *port){ unsigned int status, ret = 0; DPRINTK("\n\\rtl16c554_get_mctrl!\n\r"); status = UART_GET_MSR(port); if (status & TL16C554_US_DCD) ret |= TIOCM_CD; if (status & TL16C554_US_CTS) ret |= TIOCM_CTS; if (status & TL16C554_US_DSR) ret |= TIOCM_DSR; if (status & TL16C554_US_RI) ret |= TIOCM_RI; return ret;}/* * Stop transmitting. */static void tl16c554_stop_tx(struct uart_port *port, u_int from_tty){// DPRINTK("\n\\rtl16c554_stop_tx!\n\r"); UART_PUT_IDR(port, TL16C554_TX_EMPTY ); port->read_status_mask &= ~(TL16C554_US_THRE | TL16C554_US_TEMT );}/* * Start transmitting. */static void tl16c554_start_tx(struct uart_port *port, u_int from_tty){ unsigned long flags; //DPRINTK("\n\\rtl16c554_start_tx!\n\r"); local_irq_save(flags); port->read_status_mask |= (TL16C554_US_THRE | TL16C554_US_TEMT ); UART_PUT_IER(port, TL16C554_TX_EMPTY ); local_irq_restore(flags);}/* * Stop receiving - port is in process of being closed. */static void tl16c554_stop_rx(struct uart_port *port){ DPRINTK("\n\\rtl16c554_stop_rx!\n\r"); UART_PUT_IDR(port, TL16C554_RX_FULL);}/* * Enable modem status interrupts */static void tl16c554_enable_ms(struct uart_port *port){ DPRINTK("\n\\rtl16c554_enable_ms!\n\r"); UART_PUT_IER(port, TL16C554_MODEM_INTER);}/* * Control the transmission of a break signal */static void tl16c554_break_ctl(struct uart_port *port, int break_state){ unsigned int mode; DPRINTK("\n\\rtl16c554_break_ctl!\n\r"); mode = UART_GET_LCR(port); if (break_state != 0) mode |= TL16C554_US_SETBRK; else mode &= ~(TL16C554_US_SETBRK); UART_PUT_LCR(port, mode); /* stop break */}/* * Characters received (called from interrupt handler) */static void tl16c554_rx_chars(struct uart_port *port, struct pt_regs *regs){ struct tty_struct *tty = port->info->tty; unsigned int status, ch, flg, ignored = 0; DPRINTK("\n\\rtl16c554_rx_chars!\n\r"); status = UART_GET_LSR(port); while (status & TL16C554_US_RXRDY ) { ch = UART_GET_CHAR(port); DPRINTK("\n\\rreceiving the char!\n\r"); if (tty->flip.count >= TTY_FLIPBUF_SIZE) goto ignore_char; port->icount.rx++; flg = TTY_NORMAL; /* * note that the error handling code is * out of the main execution path */ if (status & (TL16C554_US_PARE | TL16C554_US_FRAME | TL16C554_US_OVRE | TL16C554_US_BREAKI)) goto handle_error; if (uart_handle_sysrq_char(port, ch, regs)) goto ignore_char;error_return: *tty->flip.flag_buf_ptr++ = flg; *tty->flip.char_buf_ptr++ = ch; tty->flip.count++;ignore_char: status = UART_GET_LSR(port); } out: tty_flip_buffer_push(tty); return;handle_error: if (status & TL16C554_US_BREAKI) port->icount.brk++; if (status & (TL16C554_US_PARE)) port->icount.parity++; else if (status & (TL16C554_US_FRAME)) port->icount.frame++; if (status & (TL16C554_US_OVRE)) port->icount.overrun++; if (status & port->ignore_status_mask) { if (++ignored > 100) goto out; goto ignore_char; } status &= port->read_status_mask; if (status & TL16C554_US_PARE) flg = TTY_PARITY; else if (status & TL16C554_US_FRAME) flg = TTY_FRAME; if (status & TL16C554_US_OVRE) { /* * overrun does *not* affect the character * we read from the FIFO */ *tty->flip.flag_buf_ptr++ = flg; *tty->flip.char_buf_ptr++ = ch; tty->flip.count++; if (tty->flip.count >= TTY_FLIPBUF_SIZE) goto ignore_char; ch = 0; flg = TTY_OVERRUN; } goto error_return;}/* * Transmit characters (called from interrupt handler) */static void tl16c554_tx_chars(struct uart_port *port){ struct circ_buf *xmit = &port->info->xmit; unsigned int i=0; // DPRINTK("\n\\rtl16c554_tx_chars!\n\r"); if (port->x_char) { UART_PUT_CHAR(port, port->x_char); port->icount.tx++; port->x_char = 0; return; } if (uart_circ_empty(xmit) || uart_tx_stopped(port)) { tl16c554_stop_tx(port, 0); return; } if(UART_GET_LSR(port) & TL16C554_US_THRE) { for(i=0;i<TL16C554_NR_FIFO;i++) { UART_PUT_CHAR(port, xmit->buf[xmit->tail]); xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); port->icount.tx++; if (uart_circ_empty(xmit)) break; } } if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) uart_write_wakeup(port); if (uart_circ_empty(xmit)) tl16c554_stop_tx(port, 0);}/* * Interrupt handler */static void tl16c554_interrupt(int irq, void *dev_id, struct pt_regs *regs){ struct uart_port *port = NULL; unsigned int tmp=0,status,i,pending, pass_counter = 0; // DPRINTK("\n\\rtl16c554_interrupt!\n\r"); AT91_SYS->AIC_ICCR=0x1<<AT91C_ID_PIOA; status = AT91_SYS->PIOA_ISR & (TL16C554_PIO5 | TL16C554_PIO6 | TL16C554_PIO7 | TL16C554_PIO8) ; if(!status) return ; tmp = AT91_SYS->PIOA_PDSR ; for (i = 0; i < TL16C554_NR_UART; i++) { if(tmp & tl16c554_ports[i].irq) { port = &tl16c554_ports[i]; // DPRINTK("\n\\rport = %d\n\r",i); break; } } if(!port) return ; pending = UART_GET_IIR(port); if (!(pending & TL16C554_IIR_NoInter)) { DPRINTK("\n\\rpending = %x\n\r",pending); do { if (pending & TL16C554_IIR_ReDataAvail) { DPRINTK("\n\r rx inter \n\r"); tl16c554_rx_chars(port, regs); } else if(pending & TL16C554_IIR_THRE) tl16c554_tx_chars(port); else { status = UART_GET_MSR(port); // TODO: All reads to CSR will clear these interrupts! if (status & TL16C554_US_RIIC) port->icount.rng++; if (status & TL16C554_US_DSRIC) port->icount.dsr++; if (status & TL16C554_US_DCDIC) { port->icount.dcd++; uart_handle_dcd_change(port, status & TL16C554_US_DCD); } if (pending & TL16C554_US_CTSIC) { port->icount.cts++; uart_handle_cts_change(port, status & TL16C554_US_CTS); } if (pending & (TL16C554_US_RIIC | TL16C554_US_DSRIC | TL16C554_US_DCDIC | TL16C554_US_CTSIC)) wake_up_interruptible(&port->info->delta_msr_wait); } if (pass_counter++ > ISR_PASS_LIMIT) break; pending = UART_GET_IIR(port); } while (!(pending & TL16C554_IIR_NoInter)); }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -