2083 lines · 48.6 KB · C
RawDownload
/* * SuperH on-chip serial module support.  (SCI with no FIFO / with FIFO) * *  Copyright (C) 2002 - 2011  Paul Mundt *  Modified to support SH7720 SCIF. Markus Brunner, Mark Jonas (Jul 2007). * * based off of the old drivers/char/sh-sci.c by: * *   Copyright (C) 1999, 2000  Niibe Yutaka *   Copyright (C) 2000  Sugioka Toshinobu *   Modified to support multiple serial ports. Stuart Menefy (May 2000). *   Modified to support SecureEdge. David McCullough (2002) *   Modified to support SH7300 SCIF. Takashi Kusuda (Jun 2003). *   Removed SH7300 support (Jul 2007). * * This file is subject to the terms and conditions of the GNU General Public * License.  See the file "COPYING" in the main directory of this archive * for more details. */#if defined(CONFIG_SERIAL_SH_SCI_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)#define SUPPORT_SYSRQ#endif #undef DEBUG #include <linux/module.h>#include <linux/errno.h>#include <linux/timer.h>#include <linux/interrupt.h>#include <linux/tty.h>#include <linux/tty_flip.h>#include <linux/serial.h>#include <linux/major.h>#include <linux/string.h>#include <linux/sysrq.h>#include <linux/ioport.h>#include <linux/mm.h>#include <linux/init.h>#include <linux/delay.h>#include <linux/console.h>#include <linux/platform_device.h>#include <linux/serial_sci.h>#include <linux/notifier.h>#include <linux/pm_runtime.h>#include <linux/cpufreq.h>#include <linux/clk.h>#include <linux/ctype.h>#include <linux/err.h>#include <linux/dmaengine.h>#include <linux/scatterlist.h>#include <linux/slab.h> #ifdef CONFIG_SUPERH#include <asm/sh_bios.h>#endif #ifdef CONFIG_H8300#include <asm/gpio.h>#endif #include "sh-sci.h" struct sci_port {	struct uart_port	port; 	/* Platform configuration */	struct plat_sci_port	*cfg; 	/* Port enable callback */	void			(*enable)(struct uart_port *port); 	/* Port disable callback */	void			(*disable)(struct uart_port *port); 	/* Break timer */	struct timer_list	break_timer;	int			break_flag; 	/* Interface clock */	struct clk		*iclk;	/* Function clock */	struct clk		*fclk; 	struct dma_chan			*chan_tx;	struct dma_chan			*chan_rx; #ifdef CONFIG_SERIAL_SH_SCI_DMA	struct dma_async_tx_descriptor	*desc_tx;	struct dma_async_tx_descriptor	*desc_rx[2];	dma_cookie_t			cookie_tx;	dma_cookie_t			cookie_rx[2];	dma_cookie_t			active_rx;	struct scatterlist		sg_tx;	unsigned int			sg_len_tx;	struct scatterlist		sg_rx[2];	size_t				buf_len_rx;	struct sh_dmae_slave		param_tx;	struct sh_dmae_slave		param_rx;	struct work_struct		work_tx;	struct work_struct		work_rx;	struct timer_list		rx_timer;	unsigned int			rx_timeout;#endif 	struct notifier_block		freq_transition;}; /* Function prototypes */static void sci_start_tx(struct uart_port *port);static void sci_stop_tx(struct uart_port *port);static void sci_start_rx(struct uart_port *port); #define SCI_NPORTS CONFIG_SERIAL_SH_SCI_NR_UARTS static struct sci_port sci_ports[SCI_NPORTS];static struct uart_driver sci_uart_driver; static inline struct sci_port *to_sci_port(struct uart_port *uart){	return container_of(uart, struct sci_port, port);} #if defined(CONFIG_CONSOLE_POLL) || defined(CONFIG_SERIAL_SH_SCI_CONSOLE) #ifdef CONFIG_CONSOLE_POLLstatic int sci_poll_get_char(struct uart_port *port){	unsigned short status;	int c; 	do {		status = sci_in(port, SCxSR);		if (status & SCxSR_ERRORS(port)) {			sci_out(port, SCxSR, SCxSR_ERROR_CLEAR(port));			continue;		}		break;	} while (1); 	if (!(status & SCxSR_RDxF(port)))		return NO_POLL_CHAR; 	c = sci_in(port, SCxRDR); 	/* Dummy read */	sci_in(port, SCxSR);	sci_out(port, SCxSR, SCxSR_RDxF_CLEAR(port)); 	return c;}#endif static void sci_poll_put_char(struct uart_port *port, unsigned char c){	unsigned short status; 	do {		status = sci_in(port, SCxSR);	} while (!(status & SCxSR_TDxE(port))); 	sci_out(port, SCxTDR, c);	sci_out(port, SCxSR, SCxSR_TDxE_CLEAR(port) & ~SCxSR_TEND(port));}#endif /* CONFIG_CONSOLE_POLL || CONFIG_SERIAL_SH_SCI_CONSOLE */ #if defined(__H8300H__) || defined(__H8300S__)static void sci_init_pins(struct uart_port *port, unsigned int cflag){	int ch = (port->mapbase - SMR0) >> 3; 	/* set DDR regs */	H8300_GPIO_DDR(h8300_sci_pins[ch].port,		       h8300_sci_pins[ch].rx,		       H8300_GPIO_INPUT);	H8300_GPIO_DDR(h8300_sci_pins[ch].port,		       h8300_sci_pins[ch].tx,		       H8300_GPIO_OUTPUT); 	/* tx mark output*/	H8300_SCI_DR(ch) |= h8300_sci_pins[ch].tx;}#elif defined(CONFIG_CPU_SUBTYPE_SH7710) || defined(CONFIG_CPU_SUBTYPE_SH7712)static inline void sci_init_pins(struct uart_port *port, unsigned int cflag){	if (port->mapbase == 0xA4400000) {		__raw_writew(__raw_readw(PACR) & 0xffc0, PACR);		__raw_writew(__raw_readw(PBCR) & 0x0fff, PBCR);	} else if (port->mapbase == 0xA4410000)		__raw_writew(__raw_readw(PBCR) & 0xf003, PBCR);}#elif defined(CONFIG_CPU_SUBTYPE_SH7720) || defined(CONFIG_CPU_SUBTYPE_SH7721)static inline void sci_init_pins(struct uart_port *port, unsigned int cflag){	unsigned short data; 	if (cflag & CRTSCTS) {		/* enable RTS/CTS */		if (port->mapbase == 0xa4430000) { /* SCIF0 */			/* Clear PTCR bit 9-2; enable all scif pins but sck */			data = __raw_readw(PORT_PTCR);			__raw_writew((data & 0xfc03), PORT_PTCR);		} else if (port->mapbase == 0xa4438000) { /* SCIF1 */			/* Clear PVCR bit 9-2 */			data = __raw_readw(PORT_PVCR);			__raw_writew((data & 0xfc03), PORT_PVCR);		}	} else {		if (port->mapbase == 0xa4430000) { /* SCIF0 */			/* Clear PTCR bit 5-2; enable only tx and rx  */			data = __raw_readw(PORT_PTCR);			__raw_writew((data & 0xffc3), PORT_PTCR);		} else if (port->mapbase == 0xa4438000) { /* SCIF1 */			/* Clear PVCR bit 5-2 */			data = __raw_readw(PORT_PVCR);			__raw_writew((data & 0xffc3), PORT_PVCR);		}	}}#elif defined(CONFIG_CPU_SH3)/* For SH7705, SH7706, SH7707, SH7709, SH7709A, SH7729 */static inline void sci_init_pins(struct uart_port *port, unsigned int cflag){	unsigned short data; 	/* We need to set SCPCR to enable RTS/CTS */	data = __raw_readw(SCPCR);	/* Clear out SCP7MD1,0, SCP6MD1,0, SCP4MD1,0*/	__raw_writew(data & 0x0fcf, SCPCR); 	if (!(cflag & CRTSCTS)) {		/* We need to set SCPCR to enable RTS/CTS */		data = __raw_readw(SCPCR);		/* Clear out SCP7MD1,0, SCP4MD1,0,		   Set SCP6MD1,0 = {01} (output)  */		__raw_writew((data & 0x0fcf) | 0x1000, SCPCR); 		data = __raw_readb(SCPDR);		/* Set /RTS2 (bit6) = 0 */		__raw_writeb(data & 0xbf, SCPDR);	}}#elif defined(CONFIG_CPU_SUBTYPE_SH7722)static inline void sci_init_pins(struct uart_port *port, unsigned int cflag){	unsigned short data; 	if (port->mapbase == 0xffe00000) {		data = __raw_readw(PSCR);		data &= ~0x03cf;		if (!(cflag & CRTSCTS))			data |= 0x0340; 		__raw_writew(data, PSCR);	}}#elif defined(CONFIG_CPU_SUBTYPE_SH7757) || \      defined(CONFIG_CPU_SUBTYPE_SH7763) || \      defined(CONFIG_CPU_SUBTYPE_SH7780) || \      defined(CONFIG_CPU_SUBTYPE_SH7785) || \      defined(CONFIG_CPU_SUBTYPE_SH7786) || \      defined(CONFIG_CPU_SUBTYPE_SHX3)static inline void sci_init_pins(struct uart_port *port, unsigned int cflag){	if (!(cflag & CRTSCTS))		__raw_writew(0x0080, SCSPTR0); /* Set RTS = 1 */}#elif defined(CONFIG_CPU_SH4) && !defined(CONFIG_CPU_SH4A)static inline void sci_init_pins(struct uart_port *port, unsigned int cflag){	if (!(cflag & CRTSCTS))		__raw_writew(0x0080, SCSPTR2); /* Set RTS = 1 */}#elsestatic inline void sci_init_pins(struct uart_port *port, unsigned int cflag){	/* Nothing to do */}#endif #if defined(CONFIG_CPU_SUBTYPE_SH7760) || \    defined(CONFIG_CPU_SUBTYPE_SH7780) || \    defined(CONFIG_CPU_SUBTYPE_SH7785) || \    defined(CONFIG_CPU_SUBTYPE_SH7786)static int scif_txfill(struct uart_port *port){	return sci_in(port, SCTFDR) & 0xff;} static int scif_txroom(struct uart_port *port){	return SCIF_TXROOM_MAX - scif_txfill(port);} static int scif_rxfill(struct uart_port *port){	return sci_in(port, SCRFDR) & 0xff;}#elif defined(CONFIG_CPU_SUBTYPE_SH7763)static int scif_txfill(struct uart_port *port){	if (port->mapbase == 0xffe00000 ||	    port->mapbase == 0xffe08000)		/* SCIF0/1*/		return sci_in(port, SCTFDR) & 0xff;	else		/* SCIF2 */		return sci_in(port, SCFDR) >> 8;} static int scif_txroom(struct uart_port *port){	if (port->mapbase == 0xffe00000 ||	    port->mapbase == 0xffe08000)		/* SCIF0/1*/		return SCIF_TXROOM_MAX - scif_txfill(port);	else		/* SCIF2 */		return SCIF2_TXROOM_MAX - scif_txfill(port);} static int scif_rxfill(struct uart_port *port){	if ((port->mapbase == 0xffe00000) ||	    (port->mapbase == 0xffe08000)) {		/* SCIF0/1*/		return sci_in(port, SCRFDR) & 0xff;	} else {		/* SCIF2 */		return sci_in(port, SCFDR) & SCIF2_RFDC_MASK;	}}#elif defined(CONFIG_ARCH_SH7372)static int scif_txfill(struct uart_port *port){	if (port->type == PORT_SCIFA)		return sci_in(port, SCFDR) >> 8;	else		return sci_in(port, SCTFDR);} static int scif_txroom(struct uart_port *port){	return port->fifosize - scif_txfill(port);} static int scif_rxfill(struct uart_port *port){	if (port->type == PORT_SCIFA)		return sci_in(port, SCFDR) & SCIF_RFDC_MASK;	else		return sci_in(port, SCRFDR);}#elsestatic int scif_txfill(struct uart_port *port){	return sci_in(port, SCFDR) >> 8;} static int scif_txroom(struct uart_port *port){	return SCIF_TXROOM_MAX - scif_txfill(port);} static int scif_rxfill(struct uart_port *port){	return sci_in(port, SCFDR) & SCIF_RFDC_MASK;}#endif static int sci_txfill(struct uart_port *port){	return !(sci_in(port, SCxSR) & SCI_TDRE);} static int sci_txroom(struct uart_port *port){	return !sci_txfill(port);} static int sci_rxfill(struct uart_port *port){	return (sci_in(port, SCxSR) & SCxSR_RDxF(port)) != 0;} /* ********************************************************************** * *                   the interrupt related routines                       * * ********************************************************************** */ static void sci_transmit_chars(struct uart_port *port){	struct circ_buf *xmit = &port->state->xmit;	unsigned int stopped = uart_tx_stopped(port);	unsigned short status;	unsigned short ctrl;	int count; 	status = sci_in(port, SCxSR);	if (!(status & SCxSR_TDxE(port))) {		ctrl = sci_in(port, SCSCR);		if (uart_circ_empty(xmit))			ctrl &= ~SCSCR_TIE;		else			ctrl |= SCSCR_TIE;		sci_out(port, SCSCR, ctrl);		return;	} 	if (port->type == PORT_SCI)		count = sci_txroom(port);	else		count = scif_txroom(port); 	do {		unsigned char c; 		if (port->x_char) {			c = port->x_char;			port->x_char = 0;		} else if (!uart_circ_empty(xmit) && !stopped) {			c = xmit->buf[xmit->tail];			xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);		} else {			break;		} 		sci_out(port, SCxTDR, c); 		port->icount.tx++;	} while (--count > 0); 	sci_out(port, SCxSR, SCxSR_TDxE_CLEAR(port)); 	if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)		uart_write_wakeup(port);	if (uart_circ_empty(xmit)) {		sci_stop_tx(port);	} else {		ctrl = sci_in(port, SCSCR); 		if (port->type != PORT_SCI) {			sci_in(port, SCxSR); /* Dummy read */			sci_out(port, SCxSR, SCxSR_TDxE_CLEAR(port));		} 		ctrl |= SCSCR_TIE;		sci_out(port, SCSCR, ctrl);	}} /* On SH3, SCIF may read end-of-break as a space->mark char */#define STEPFN(c)  ({int __c = (c); (((__c-1)|(__c)) == -1); }) static void sci_receive_chars(struct uart_port *port){	struct sci_port *sci_port = to_sci_port(port);	struct tty_struct *tty = port->state->port.tty;	int i, count, copied = 0;	unsigned short status;	unsigned char flag; 	status = sci_in(port, SCxSR);	if (!(status & SCxSR_RDxF(port)))		return; 	while (1) {		if (port->type == PORT_SCI)			count = sci_rxfill(port);		else			count = scif_rxfill(port); 		/* Don't copy more bytes than there is room for in the buffer */		count = tty_buffer_request_room(tty, count); 		/* If for any reason we can't copy more data, we're done! */		if (count == 0)			break; 		if (port->type == PORT_SCI) {			char c = sci_in(port, SCxRDR);			if (uart_handle_sysrq_char(port, c) ||			    sci_port->break_flag)				count = 0;			else				tty_insert_flip_char(tty, c, TTY_NORMAL);		} else {			for (i = 0; i < count; i++) {				char c = sci_in(port, SCxRDR);				status = sci_in(port, SCxSR);#if defined(CONFIG_CPU_SH3)				/* Skip "chars" during break */				if (sci_port->break_flag) {					if ((c == 0) &&					    (status & SCxSR_FER(port))) {						count--; i--;						continue;					} 					/* Nonzero => end-of-break */					dev_dbg(port->dev, "debounce<%02x>\n", c);

Lines 1–500 of 2083.

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t