2573 lines · 59.3 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/sh_dma.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/dma-mapping.h>#include <linux/scatterlist.h>#include <linux/slab.h>#include <linux/gpio.h> #ifdef CONFIG_SUPERH#include <asm/sh_bios.h>#endif #include "sh-sci.h" struct sci_port {	struct uart_port	port; 	/* Platform configuration */	struct plat_sci_port	*cfg; 	/* Break timer */	struct timer_list	break_timer;	int			break_flag; 	/* Interface clock */	struct clk		*iclk;	/* Function clock */	struct clk		*fclk; 	char			*irqstr[SCIx_NR_IRQS];	char			*gpiostr[SCIx_NR_FNS]; 	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);} struct plat_sci_reg {	u8 offset, size;}; /* Helper for invalidating specific entries of an inherited map. */#define sci_reg_invalid	{ .offset = 0, .size = 0 } static struct plat_sci_reg sci_regmap[SCIx_NR_REGTYPES][SCIx_NR_REGS] = {	[SCIx_PROBE_REGTYPE] = {		[0 ... SCIx_NR_REGS - 1] = sci_reg_invalid,	}, 	/*	 * Common SCI definitions, dependent on the port's regshift	 * value.	 */	[SCIx_SCI_REGTYPE] = {		[SCSMR]		= { 0x00,  8 },		[SCBRR]		= { 0x01,  8 },		[SCSCR]		= { 0x02,  8 },		[SCxTDR]	= { 0x03,  8 },		[SCxSR]		= { 0x04,  8 },		[SCxRDR]	= { 0x05,  8 },		[SCFCR]		= sci_reg_invalid,		[SCFDR]		= sci_reg_invalid,		[SCTFDR]	= sci_reg_invalid,		[SCRFDR]	= sci_reg_invalid,		[SCSPTR]	= sci_reg_invalid,		[SCLSR]		= sci_reg_invalid,		[HSSRR]		= sci_reg_invalid,	}, 	/*	 * Common definitions for legacy IrDA ports, dependent on	 * regshift value.	 */	[SCIx_IRDA_REGTYPE] = {		[SCSMR]		= { 0x00,  8 },		[SCBRR]		= { 0x01,  8 },		[SCSCR]		= { 0x02,  8 },		[SCxTDR]	= { 0x03,  8 },		[SCxSR]		= { 0x04,  8 },		[SCxRDR]	= { 0x05,  8 },		[SCFCR]		= { 0x06,  8 },		[SCFDR]		= { 0x07, 16 },		[SCTFDR]	= sci_reg_invalid,		[SCRFDR]	= sci_reg_invalid,		[SCSPTR]	= sci_reg_invalid,		[SCLSR]		= sci_reg_invalid,		[HSSRR]		= sci_reg_invalid,	}, 	/*	 * Common SCIFA definitions.	 */	[SCIx_SCIFA_REGTYPE] = {		[SCSMR]		= { 0x00, 16 },		[SCBRR]		= { 0x04,  8 },		[SCSCR]		= { 0x08, 16 },		[SCxTDR]	= { 0x20,  8 },		[SCxSR]		= { 0x14, 16 },		[SCxRDR]	= { 0x24,  8 },		[SCFCR]		= { 0x18, 16 },		[SCFDR]		= { 0x1c, 16 },		[SCTFDR]	= sci_reg_invalid,		[SCRFDR]	= sci_reg_invalid,		[SCSPTR]	= sci_reg_invalid,		[SCLSR]		= sci_reg_invalid,		[HSSRR]		= sci_reg_invalid,	}, 	/*	 * Common SCIFB definitions.	 */	[SCIx_SCIFB_REGTYPE] = {		[SCSMR]		= { 0x00, 16 },		[SCBRR]		= { 0x04,  8 },		[SCSCR]		= { 0x08, 16 },		[SCxTDR]	= { 0x40,  8 },		[SCxSR]		= { 0x14, 16 },		[SCxRDR]	= { 0x60,  8 },		[SCFCR]		= { 0x18, 16 },		[SCFDR]		= sci_reg_invalid,		[SCTFDR]	= { 0x38, 16 },		[SCRFDR]	= { 0x3c, 16 },		[SCSPTR]	= sci_reg_invalid,		[SCLSR]		= sci_reg_invalid,		[HSSRR]		= sci_reg_invalid,	}, 	/*	 * Common SH-2(A) SCIF definitions for ports with FIFO data	 * count registers.	 */	[SCIx_SH2_SCIF_FIFODATA_REGTYPE] = {		[SCSMR]		= { 0x00, 16 },		[SCBRR]		= { 0x04,  8 },		[SCSCR]		= { 0x08, 16 },		[SCxTDR]	= { 0x0c,  8 },		[SCxSR]		= { 0x10, 16 },		[SCxRDR]	= { 0x14,  8 },		[SCFCR]		= { 0x18, 16 },		[SCFDR]		= { 0x1c, 16 },		[SCTFDR]	= sci_reg_invalid,		[SCRFDR]	= sci_reg_invalid,		[SCSPTR]	= { 0x20, 16 },		[SCLSR]		= { 0x24, 16 },		[HSSRR]		= sci_reg_invalid,	}, 	/*	 * Common SH-3 SCIF definitions.	 */	[SCIx_SH3_SCIF_REGTYPE] = {		[SCSMR]		= { 0x00,  8 },		[SCBRR]		= { 0x02,  8 },		[SCSCR]		= { 0x04,  8 },		[SCxTDR]	= { 0x06,  8 },		[SCxSR]		= { 0x08, 16 },		[SCxRDR]	= { 0x0a,  8 },		[SCFCR]		= { 0x0c,  8 },		[SCFDR]		= { 0x0e, 16 },		[SCTFDR]	= sci_reg_invalid,		[SCRFDR]	= sci_reg_invalid,		[SCSPTR]	= sci_reg_invalid,		[SCLSR]		= sci_reg_invalid,		[HSSRR]		= sci_reg_invalid,	}, 	/*	 * Common SH-4(A) SCIF(B) definitions.	 */	[SCIx_SH4_SCIF_REGTYPE] = {		[SCSMR]		= { 0x00, 16 },		[SCBRR]		= { 0x04,  8 },		[SCSCR]		= { 0x08, 16 },		[SCxTDR]	= { 0x0c,  8 },		[SCxSR]		= { 0x10, 16 },		[SCxRDR]	= { 0x14,  8 },		[SCFCR]		= { 0x18, 16 },		[SCFDR]		= { 0x1c, 16 },		[SCTFDR]	= sci_reg_invalid,		[SCRFDR]	= sci_reg_invalid,		[SCSPTR]	= { 0x20, 16 },		[SCLSR]		= { 0x24, 16 },		[HSSRR]		= sci_reg_invalid,	}, 	/*	 * Common HSCIF definitions.	 */	[SCIx_HSCIF_REGTYPE] = {		[SCSMR]		= { 0x00, 16 },		[SCBRR]		= { 0x04,  8 },		[SCSCR]		= { 0x08, 16 },		[SCxTDR]	= { 0x0c,  8 },		[SCxSR]		= { 0x10, 16 },		[SCxRDR]	= { 0x14,  8 },		[SCFCR]		= { 0x18, 16 },		[SCFDR]		= { 0x1c, 16 },		[SCTFDR]	= sci_reg_invalid,		[SCRFDR]	= sci_reg_invalid,		[SCSPTR]	= { 0x20, 16 },		[SCLSR]		= { 0x24, 16 },		[HSSRR]		= { 0x40, 16 },	}, 	/*	 * Common SH-4(A) SCIF(B) definitions for ports without an SCSPTR	 * register.	 */	[SCIx_SH4_SCIF_NO_SCSPTR_REGTYPE] = {		[SCSMR]		= { 0x00, 16 },		[SCBRR]		= { 0x04,  8 },		[SCSCR]		= { 0x08, 16 },		[SCxTDR]	= { 0x0c,  8 },		[SCxSR]		= { 0x10, 16 },		[SCxRDR]	= { 0x14,  8 },		[SCFCR]		= { 0x18, 16 },		[SCFDR]		= { 0x1c, 16 },		[SCTFDR]	= sci_reg_invalid,		[SCRFDR]	= sci_reg_invalid,		[SCSPTR]	= sci_reg_invalid,		[SCLSR]		= { 0x24, 16 },		[HSSRR]		= sci_reg_invalid,	}, 	/*	 * Common SH-4(A) SCIF(B) definitions for ports with FIFO data	 * count registers.	 */	[SCIx_SH4_SCIF_FIFODATA_REGTYPE] = {		[SCSMR]		= { 0x00, 16 },		[SCBRR]		= { 0x04,  8 },		[SCSCR]		= { 0x08, 16 },		[SCxTDR]	= { 0x0c,  8 },		[SCxSR]		= { 0x10, 16 },		[SCxRDR]	= { 0x14,  8 },		[SCFCR]		= { 0x18, 16 },		[SCFDR]		= { 0x1c, 16 },		[SCTFDR]	= { 0x1c, 16 },	/* aliased to SCFDR */		[SCRFDR]	= { 0x20, 16 },		[SCSPTR]	= { 0x24, 16 },		[SCLSR]		= { 0x28, 16 },		[HSSRR]		= sci_reg_invalid,	}, 	/*	 * SH7705-style SCIF(B) ports, lacking both SCSPTR and SCLSR	 * registers.	 */	[SCIx_SH7705_SCIF_REGTYPE] = {		[SCSMR]		= { 0x00, 16 },		[SCBRR]		= { 0x04,  8 },		[SCSCR]		= { 0x08, 16 },		[SCxTDR]	= { 0x20,  8 },		[SCxSR]		= { 0x14, 16 },		[SCxRDR]	= { 0x24,  8 },		[SCFCR]		= { 0x18, 16 },		[SCFDR]		= { 0x1c, 16 },		[SCTFDR]	= sci_reg_invalid,		[SCRFDR]	= sci_reg_invalid,		[SCSPTR]	= sci_reg_invalid,		[SCLSR]		= sci_reg_invalid,		[HSSRR]		= sci_reg_invalid,	},}; #define sci_getreg(up, offset)		(sci_regmap[to_sci_port(up)->cfg->regtype] + offset) /* * The "offset" here is rather misleading, in that it refers to an enum * value relative to the port mapping rather than the fixed offset * itself, which needs to be manually retrieved from the platform's * register map for the given port. */static unsigned int sci_serial_in(struct uart_port *p, int offset){	struct plat_sci_reg *reg = sci_getreg(p, offset); 	if (reg->size == 8)		return ioread8(p->membase + (reg->offset << p->regshift));	else if (reg->size == 16)		return ioread16(p->membase + (reg->offset << p->regshift));	else		WARN(1, "Invalid register access\n"); 	return 0;} static void sci_serial_out(struct uart_port *p, int offset, int value){	struct plat_sci_reg *reg = sci_getreg(p, offset); 	if (reg->size == 8)		iowrite8(value, p->membase + (reg->offset << p->regshift));	else if (reg->size == 16)		iowrite16(value, p->membase + (reg->offset << p->regshift));	else		WARN(1, "Invalid register access\n");} static int sci_probe_regmap(struct plat_sci_port *cfg){	switch (cfg->type) {	case PORT_SCI:		cfg->regtype = SCIx_SCI_REGTYPE;		break;	case PORT_IRDA:		cfg->regtype = SCIx_IRDA_REGTYPE;		break;	case PORT_SCIFA:		cfg->regtype = SCIx_SCIFA_REGTYPE;		break;	case PORT_SCIFB:		cfg->regtype = SCIx_SCIFB_REGTYPE;		break;	case PORT_SCIF:		/*		 * The SH-4 is a bit of a misnomer here, although that's		 * where this particular port layout originated. This		 * configuration (or some slight variation thereof)		 * remains the dominant model for all SCIFs.		 */		cfg->regtype = SCIx_SH4_SCIF_REGTYPE;		break;	case PORT_HSCIF:		cfg->regtype = SCIx_HSCIF_REGTYPE;		break;	default:		printk(KERN_ERR "Can't probe register map for given port\n");		return -EINVAL;	} 	return 0;} static void sci_port_enable(struct sci_port *sci_port){	if (!sci_port->port.dev)		return; 	pm_runtime_get_sync(sci_port->port.dev); 	clk_enable(sci_port->iclk);	sci_port->port.uartclk = clk_get_rate(sci_port->iclk);	clk_enable(sci_port->fclk);} static void sci_port_disable(struct sci_port *sci_port){	if (!sci_port->port.dev)		return; 	clk_disable(sci_port->fclk);	clk_disable(sci_port->iclk); 	pm_runtime_put_sync(sci_port->port.dev);} #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 = serial_port_in(port, SCxSR);		if (status & SCxSR_ERRORS(port)) {			serial_port_out(port, SCxSR, SCxSR_ERROR_CLEAR(port));			continue;		}		break;	} while (1); 	if (!(status & SCxSR_RDxF(port)))		return NO_POLL_CHAR; 	c = serial_port_in(port, SCxRDR); 	/* Dummy read */	serial_port_in(port, SCxSR);	serial_port_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 = serial_port_in(port, SCxSR);	} while (!(status & SCxSR_TDxE(port))); 	serial_port_out(port, SCxTDR, c);	serial_port_out(port, SCxSR, SCxSR_TDxE_CLEAR(port) & ~SCxSR_TEND(port));}#endif /* CONFIG_CONSOLE_POLL || CONFIG_SERIAL_SH_SCI_CONSOLE */ static void sci_init_pins(struct uart_port *port, unsigned int cflag){	struct sci_port *s = to_sci_port(port);	struct plat_sci_reg *reg = sci_regmap[s->cfg->regtype] + SCSPTR; 	/*	 * Use port-specific handler if provided.	 */	if (s->cfg->ops && s->cfg->ops->init_pins) {		s->cfg->ops->init_pins(port, cflag);		return;	} 	/*	 * For the generic path SCSPTR is necessary. Bail out if that's	 * unavailable, too.	 */	if (!reg->size)

Lines 1–500 of 2573.