pxa.c

来自「Linux Kernel 2.6.9 for OMAP1710」· C语言 代码 · 共 887 行 · 第 1/2 页

C
887
字号
/* *  linux/drivers/serial/pxa.c * *  Based on drivers/serial/8250.c by Russell King. * *  Author:	Nicolas Pitre *  Created:	Feb 20, 2003 *  Copyright:	(C) 2003 Monta Vista Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Note 1: This driver is made separate from the already too overloaded * 8250.c because it needs some kirks of its own and that'll make it * easier to add DMA support. * * Note 2: I'm too sick of device allocation policies for serial ports. * If someone else wants to request an "official" allocation of major/minor * for this driver please be my guest.  And don't forget that new hardware * to come from Intel might have more than 3 or 4 of those UARTs.  Let's * hope for a better port registration and dynamic device allocation scheme * with the serial core maintainer satisfaction to appear soon. */#include <linux/config.h>#include <linux/module.h>#include <linux/tty.h>#include <linux/ioport.h>#include <linux/init.h>#include <linux/console.h>#include <linux/sysrq.h>#include <linux/serial_reg.h>#include <linux/circ_buf.h>#include <linux/delay.h>#include <linux/interrupt.h>#include <linux/device.h>#include <asm/io.h>#include <asm/hardware.h>#include <asm/irq.h>#include <asm/arch/pxa-regs.h>#if defined(CONFIG_SERIAL_PXA_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)#define SUPPORT_SYSRQ#endif#include <linux/serial_core.h>struct uart_pxa_port {	struct uart_port        port;	unsigned char           ier;	unsigned char           lcr;	unsigned char           mcr;	unsigned int            lsr_break_flag;	unsigned int		cken;	char			*name;};static inline unsigned int serial_in(struct uart_pxa_port *up, int offset){	offset <<= 2;	return readl(up->port.membase + offset);}static inline void serial_out(struct uart_pxa_port *up, int offset, int value){	offset <<= 2;	writel(value, up->port.membase + offset);}static void serial_pxa_enable_ms(struct uart_port *port){	struct uart_pxa_port *up = (struct uart_pxa_port *)port;	up->ier |= UART_IER_MSI;	serial_out(up, UART_IER, up->ier);}static void serial_pxa_stop_tx(struct uart_port *port, unsigned int tty_stop){	struct uart_pxa_port *up = (struct uart_pxa_port *)port;	if (up->ier & UART_IER_THRI) {		up->ier &= ~UART_IER_THRI;		serial_out(up, UART_IER, up->ier);	}}static void serial_pxa_stop_rx(struct uart_port *port){	struct uart_pxa_port *up = (struct uart_pxa_port *)port;	up->ier &= ~UART_IER_RLSI;	up->port.read_status_mask &= ~UART_LSR_DR;	serial_out(up, UART_IER, up->ier);}static inline voidreceive_chars(struct uart_pxa_port *up, int *status, struct pt_regs *regs){	struct tty_struct *tty = up->port.info->tty;	unsigned char ch;	int max_count = 256;	do {		if (unlikely(tty->flip.count >= TTY_FLIPBUF_SIZE)) {			/*			 * FIXME: Deadlock can happen here if we're a			 * low-latency port.  We're holding the per-port			 * spinlock, and we call flush_to_ldisc->			 * n_tty_receive_buf->n_tty_receive_char->			 * opost->uart_put_char.			 */			tty->flip.work.func((void *)tty);			if (tty->flip.count >= TTY_FLIPBUF_SIZE)				return; // if TTY_DONT_FLIP is set		}		ch = serial_in(up, UART_RX);		*tty->flip.char_buf_ptr = ch;		*tty->flip.flag_buf_ptr = TTY_NORMAL;		up->port.icount.rx++;		if (unlikely(*status & (UART_LSR_BI | UART_LSR_PE |				       UART_LSR_FE | UART_LSR_OE))) {			/*			 * For statistics only			 */			if (*status & UART_LSR_BI) {				*status &= ~(UART_LSR_FE | UART_LSR_PE);				up->port.icount.brk++;				/*				 * We do the SysRQ and SAK checking				 * here because otherwise the break				 * may get masked by ignore_status_mask				 * or read_status_mask.				 */				if (uart_handle_break(&up->port))					goto ignore_char;			} else if (*status & UART_LSR_PE)				up->port.icount.parity++;			else if (*status & UART_LSR_FE)				up->port.icount.frame++;			if (*status & UART_LSR_OE)				up->port.icount.overrun++;			/*			 * Mask off conditions which should be ignored.			 */			*status &= up->port.read_status_mask;#ifdef CONFIG_SERIAL_PXA_CONSOLE			if (up->port.line == up->port.cons->index) {				/* Recover the break flag from console xmit */				*status |= up->lsr_break_flag;				up->lsr_break_flag = 0;			}#endif			if (*status & UART_LSR_BI) {				*tty->flip.flag_buf_ptr = TTY_BREAK;			} else if (*status & UART_LSR_PE)				*tty->flip.flag_buf_ptr = TTY_PARITY;			else if (*status & UART_LSR_FE)				*tty->flip.flag_buf_ptr = TTY_FRAME;		}		if (uart_handle_sysrq_char(&up->port, ch, regs))			goto ignore_char;		if ((*status & up->port.ignore_status_mask) == 0) {			tty->flip.flag_buf_ptr++;			tty->flip.char_buf_ptr++;			tty->flip.count++;		}		if ((*status & UART_LSR_OE) &&		    tty->flip.count < TTY_FLIPBUF_SIZE) {			/*			 * Overrun is special, since it's reported			 * immediately, and doesn't affect the current			 * character.			 */			*tty->flip.flag_buf_ptr = TTY_OVERRUN;			tty->flip.flag_buf_ptr++;			tty->flip.char_buf_ptr++;			tty->flip.count++;		}	ignore_char:		*status = serial_in(up, UART_LSR);	} while ((*status & UART_LSR_DR) && (max_count-- > 0));	tty_flip_buffer_push(tty);}static void transmit_chars(struct uart_pxa_port *up){	struct circ_buf *xmit = &up->port.info->xmit;	int count;	if (up->port.x_char) {		serial_out(up, UART_TX, up->port.x_char);		up->port.icount.tx++;		up->port.x_char = 0;		return;	}	if (uart_circ_empty(xmit) || uart_tx_stopped(&up->port)) {		serial_pxa_stop_tx(&up->port, 0);		return;	}	count = up->port.fifosize / 2;	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);	if (uart_circ_empty(xmit))		serial_pxa_stop_tx(&up->port, 0);}static void serial_pxa_start_tx(struct uart_port *port, unsigned int tty_start){	struct uart_pxa_port *up = (struct uart_pxa_port *)port;	if (!(up->ier & UART_IER_THRI)) {		up->ier |= UART_IER_THRI;		serial_out(up, UART_IER, up->ier);	}}static inline void check_modem_status(struct uart_pxa_port *up){	int status;	status = serial_in(up, UART_MSR);	if ((status & UART_MSR_ANY_DELTA) == 0)		return;	if (status & UART_MSR_TERI)		up->port.icount.rng++;	if (status & UART_MSR_DDSR)		up->port.icount.dsr++;	if (status & UART_MSR_DDCD)		uart_handle_dcd_change(&up->port, status & UART_MSR_DCD);	if (status & UART_MSR_DCTS)		uart_handle_cts_change(&up->port, status & UART_MSR_CTS);	wake_up_interruptible(&up->port.info->delta_msr_wait);}/* * This handles the interrupt from one port. */static inline irqreturn_tserial_pxa_irq(int irq, void *dev_id, struct pt_regs *regs){	struct uart_pxa_port *up = (struct uart_pxa_port *)dev_id;	unsigned int iir, lsr;	iir = serial_in(up, UART_IIR);	if (iir & UART_IIR_NO_INT)		return IRQ_NONE;	lsr = serial_in(up, UART_LSR);	if (lsr & UART_LSR_DR)		receive_chars(up, &lsr, regs);	check_modem_status(up);	if (lsr & UART_LSR_THRE)		transmit_chars(up);	return IRQ_HANDLED;}static unsigned int serial_pxa_tx_empty(struct uart_port *port){	struct uart_pxa_port *up = (struct uart_pxa_port *)port;	unsigned long flags;	unsigned int ret;	spin_lock_irqsave(&up->port.lock, flags);	ret = serial_in(up, UART_LSR) & UART_LSR_TEMT ? TIOCSER_TEMT : 0;	spin_unlock_irqrestore(&up->port.lock, flags);	return ret;}static unsigned int serial_pxa_get_mctrl(struct uart_port *port){	struct uart_pxa_port *up = (struct uart_pxa_port *)port;	unsigned long flags;	unsigned char status;	unsigned int ret;return TIOCM_CTS | TIOCM_DSR | TIOCM_CAR;	spin_lock_irqsave(&up->port.lock, flags);	status = serial_in(up, UART_MSR);	spin_unlock_irqrestore(&up->port.lock, flags);	ret = 0;	if (status & UART_MSR_DCD)		ret |= TIOCM_CAR;	if (status & UART_MSR_RI)		ret |= TIOCM_RNG;	if (status & UART_MSR_DSR)		ret |= TIOCM_DSR;	if (status & UART_MSR_CTS)		ret |= TIOCM_CTS;	return ret;}static void serial_pxa_set_mctrl(struct uart_port *port, unsigned int mctrl){	struct uart_pxa_port *up = (struct uart_pxa_port *)port;	unsigned char mcr = 0;	if (mctrl & TIOCM_RTS)		mcr |= UART_MCR_RTS;	if (mctrl & TIOCM_DTR)		mcr |= UART_MCR_DTR;	if (mctrl & TIOCM_OUT1)		mcr |= UART_MCR_OUT1;	if (mctrl & TIOCM_OUT2)		mcr |= UART_MCR_OUT2;	if (mctrl & TIOCM_LOOP)		mcr |= UART_MCR_LOOP;	mcr |= up->mcr;	serial_out(up, UART_MCR, mcr);}static void serial_pxa_break_ctl(struct uart_port *port, int break_state){	struct uart_pxa_port *up = (struct uart_pxa_port *)port;	unsigned long flags;	spin_lock_irqsave(&up->port.lock, flags);	if (break_state == -1)		up->lcr |= UART_LCR_SBC;	else		up->lcr &= ~UART_LCR_SBC;	serial_out(up, UART_LCR, up->lcr);	spin_unlock_irqrestore(&up->port.lock, flags);}#if 0static void serial_pxa_dma_init(struct pxa_uart *up){	up->rxdma =		pxa_request_dma(up->name, DMA_PRIO_LOW, pxa_receive_dma, up);	if (up->rxdma < 0)		goto out;	up->txdma =		pxa_request_dma(up->name, DMA_PRIO_LOW, pxa_transmit_dma, up);	if (up->txdma < 0)		goto err_txdma;	up->dmadesc = kmalloc(4 * sizeof(pxa_dma_desc), GFP_KERNEL);	if (!up->dmadesc)		goto err_alloc;	/* ... */err_alloc:	pxa_free_dma(up->txdma);err_rxdma:	pxa_free_dma(up->rxdma);out:	return;}#endifstatic int serial_pxa_startup(struct uart_port *port){	struct uart_pxa_port *up = (struct uart_pxa_port *)port;	unsigned long flags;	int retval;	up->mcr = 0;	/*	 * Allocate the IRQ	 */	retval = request_irq(up->port.irq, serial_pxa_irq, 0, up->name, up);	if (retval)		return retval;	/*	 * Clear the FIFO buffers and disable them.	 * (they will be reenabled in set_termios())	 */	serial_out(up, UART_FCR, UART_FCR_ENABLE_FIFO);	serial_out(up, UART_FCR, UART_FCR_ENABLE_FIFO |			UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT);	serial_out(up, UART_FCR, 0);	/*	 * Clear the interrupt registers.	 */	(void) serial_in(up, UART_LSR);	(void) serial_in(up, UART_RX);	(void) serial_in(up, UART_IIR);	(void) serial_in(up, UART_MSR);	/*	 * Now, initialize the UART	 */	serial_out(up, UART_LCR, UART_LCR_WLEN8);	spin_lock_irqsave(&up->port.lock, flags);	up->port.mctrl |= TIOCM_OUT2;	serial_pxa_set_mctrl(&up->port, up->port.mctrl);	spin_unlock_irqrestore(&up->port.lock, flags);	/*	 * Finally, enable interrupts.  Note: Modem status interrupts	 * are set via set_termios(), which will be occuring imminently	 * anyway, so we don't enable them here.	 */	up->ier = UART_IER_RLSI | UART_IER_RDI | UART_IER_RTOIE | UART_IER_UUE;	serial_out(up, UART_IER, up->ier);	/*	 * And clear the interrupt registers again for luck.	 */	(void) serial_in(up, UART_LSR);	(void) serial_in(up, UART_RX);	(void) serial_in(up, UART_IIR);	(void) serial_in(up, UART_MSR);	return 0;}static void serial_pxa_shutdown(struct uart_port *port){	struct uart_pxa_port *up = (struct uart_pxa_port *)port;	unsigned long flags;	free_irq(up->port.irq, up);	/*	 * Disable interrupts from this port

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?