pxa.c

来自「linux 内核源代码」· C语言 代码 · 共 865 行 · 第 1/2 页

C
865
字号
/* *  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. */#if defined(CONFIG_SERIAL_PXA_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)#define SUPPORT_SYSRQ#endif#include <linux/module.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/platform_device.h>#include <linux/tty.h>#include <linux/tty_flip.h>#include <linux/serial_core.h>#include <linux/clk.h>#include <asm/io.h>#include <asm/hardware.h>#include <asm/irq.h>#include <asm/arch/pxa-regs.h>struct uart_pxa_port {	struct uart_port        port;	unsigned char           ier;	unsigned char           lcr;	unsigned char           mcr;	unsigned int            lsr_break_flag;	struct clk		*clk;	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){	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 void receive_chars(struct uart_pxa_port *up, int *status){	struct tty_struct *tty = up->port.info->tty;	unsigned int ch, flag;	int max_count = 256;	do {		ch = serial_in(up, UART_RX);		flag = 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) {				flag = TTY_BREAK;			} else if (*status & UART_LSR_PE)				flag = TTY_PARITY;			else if (*status & UART_LSR_FE)				flag = TTY_FRAME;		}		if (uart_handle_sysrq_char(&up->port, ch))			goto ignore_char;		uart_insert_char(&up->port, *status, UART_LSR_OE, ch, flag);	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);		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);}static void serial_pxa_start_tx(struct uart_port *port){	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_t serial_pxa_irq(int irq, void *dev_id){	struct uart_pxa_port *up = 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);	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 char status;	unsigned int ret;	status = serial_in(up, UART_MSR);	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;	if (port->line == 3) /* HWUART */		up->mcr |= UART_MCR_AFE;	else		up->mcr = 0;	up->port.uartclk = clk_get_rate(up->clk);	/*	 * 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 occurring 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	 */	up->ier = 0;	serial_out(up, UART_IER, 0);	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);	/*	 * Disable break condition and FIFOs	 */	serial_out(up, UART_LCR, serial_in(up, UART_LCR) & ~UART_LCR_SBC);	serial_out(up, UART_FCR, UART_FCR_ENABLE_FIFO |				  UART_FCR_CLEAR_RCVR |

⌨️ 快捷键说明

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