| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1 | /* | 
 | 2 |  * drivers/serial/sh-sci.c | 
 | 3 |  * | 
 | 4 |  * SuperH on-chip serial module support.  (SCI with no FIFO / with FIFO) | 
 | 5 |  * | 
| Paul Mundt | 7ff731a | 2008-10-01 15:46:58 +0900 | [diff] [blame] | 6 |  *  Copyright (C) 2002 - 2008  Paul Mundt | 
| Markus Brunner | 3ea6bc3 | 2007-08-20 08:59:33 +0900 | [diff] [blame] | 7 |  *  Modified to support SH7720 SCIF. Markus Brunner, Mark Jonas (Jul 2007). | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 8 |  * | 
 | 9 |  * based off of the old drivers/char/sh-sci.c by: | 
 | 10 |  * | 
 | 11 |  *   Copyright (C) 1999, 2000  Niibe Yutaka | 
 | 12 |  *   Copyright (C) 2000  Sugioka Toshinobu | 
 | 13 |  *   Modified to support multiple serial ports. Stuart Menefy (May 2000). | 
 | 14 |  *   Modified to support SecureEdge. David McCullough (2002) | 
 | 15 |  *   Modified to support SH7300 SCIF. Takashi Kusuda (Jun 2003). | 
| Magnus Damm | d89ddd1 | 2007-07-25 11:42:56 +0900 | [diff] [blame] | 16 |  *   Removed SH7300 support (Jul 2007). | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 17 |  * | 
 | 18 |  * This file is subject to the terms and conditions of the GNU General Public | 
 | 19 |  * License.  See the file "COPYING" in the main directory of this archive | 
 | 20 |  * for more details. | 
 | 21 |  */ | 
| Paul Mundt | 0b3d4ef | 2007-03-14 13:22:37 +0900 | [diff] [blame] | 22 | #if defined(CONFIG_SERIAL_SH_SCI_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) | 
 | 23 | #define SUPPORT_SYSRQ | 
 | 24 | #endif | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 25 |  | 
 | 26 | #undef DEBUG | 
 | 27 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 28 | #include <linux/module.h> | 
 | 29 | #include <linux/errno.h> | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 30 | #include <linux/timer.h> | 
 | 31 | #include <linux/interrupt.h> | 
 | 32 | #include <linux/tty.h> | 
 | 33 | #include <linux/tty_flip.h> | 
 | 34 | #include <linux/serial.h> | 
 | 35 | #include <linux/major.h> | 
 | 36 | #include <linux/string.h> | 
 | 37 | #include <linux/sysrq.h> | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 38 | #include <linux/ioport.h> | 
 | 39 | #include <linux/mm.h> | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 40 | #include <linux/init.h> | 
 | 41 | #include <linux/delay.h> | 
 | 42 | #include <linux/console.h> | 
| Paul Mundt | e108b2c | 2006-09-27 16:32:13 +0900 | [diff] [blame] | 43 | #include <linux/platform_device.h> | 
| Paul Mundt | 96de1a8 | 2008-02-26 14:52:45 +0900 | [diff] [blame] | 44 | #include <linux/serial_sci.h> | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 45 | #include <linux/notifier.h> | 
 | 46 | #include <linux/cpufreq.h> | 
| Paul Mundt | 85f094e | 2008-04-25 16:04:20 +0900 | [diff] [blame] | 47 | #include <linux/clk.h> | 
| Paul Mundt | fa5da2f | 2007-03-08 17:27:37 +0900 | [diff] [blame] | 48 | #include <linux/ctype.h> | 
| Paul Mundt | 7ff731a | 2008-10-01 15:46:58 +0900 | [diff] [blame] | 49 | #include <linux/err.h> | 
| Magnus Damm | e552de2 | 2009-01-21 15:13:42 +0000 | [diff] [blame] | 50 | #include <linux/list.h> | 
| Guennadi Liakhovetski | 73a19e4 | 2010-03-02 11:39:15 +0900 | [diff] [blame] | 51 | #include <linux/dmaengine.h> | 
 | 52 | #include <linux/scatterlist.h> | 
| Paul Mundt | 85f094e | 2008-04-25 16:04:20 +0900 | [diff] [blame] | 53 |  | 
 | 54 | #ifdef CONFIG_SUPERH | 
| Paul Mundt | e108b2c | 2006-09-27 16:32:13 +0900 | [diff] [blame] | 55 | #include <asm/sh_bios.h> | 
| Paul Mundt | b7a76e4 | 2006-02-01 03:06:06 -0800 | [diff] [blame] | 56 | #endif | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 57 |  | 
| Yoshinori Sato | 168f362 | 2009-04-28 04:40:15 +0000 | [diff] [blame] | 58 | #ifdef CONFIG_H8300 | 
 | 59 | #include <asm/gpio.h> | 
 | 60 | #endif | 
 | 61 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 62 | #include "sh-sci.h" | 
 | 63 |  | 
| Paul Mundt | e108b2c | 2006-09-27 16:32:13 +0900 | [diff] [blame] | 64 | struct sci_port { | 
 | 65 | 	struct uart_port	port; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 66 |  | 
| Paul Mundt | e108b2c | 2006-09-27 16:32:13 +0900 | [diff] [blame] | 67 | 	/* Port type */ | 
 | 68 | 	unsigned int		type; | 
 | 69 |  | 
 | 70 | 	/* Port IRQs: ERI, RXI, TXI, BRI (optional) */ | 
| Paul Mundt | 32351a2 | 2007-03-12 14:38:59 +0900 | [diff] [blame] | 71 | 	unsigned int		irqs[SCIx_NR_IRQS]; | 
| Paul Mundt | e108b2c | 2006-09-27 16:32:13 +0900 | [diff] [blame] | 72 |  | 
| Paul Mundt | e108b2c | 2006-09-27 16:32:13 +0900 | [diff] [blame] | 73 | 	/* Port enable callback */ | 
 | 74 | 	void			(*enable)(struct uart_port *port); | 
 | 75 |  | 
 | 76 | 	/* Port disable callback */ | 
 | 77 | 	void			(*disable)(struct uart_port *port); | 
 | 78 |  | 
 | 79 | 	/* Break timer */ | 
 | 80 | 	struct timer_list	break_timer; | 
 | 81 | 	int			break_flag; | 
| dmitry pervushin | 1534a3b | 2007-04-24 13:41:12 +0900 | [diff] [blame] | 82 |  | 
| Magnus Damm | 501b825 | 2009-01-21 15:14:30 +0000 | [diff] [blame] | 83 | 	/* Interface clock */ | 
 | 84 | 	struct clk		*iclk; | 
 | 85 | 	/* Data clock */ | 
 | 86 | 	struct clk		*dclk; | 
| Paul Mundt | edad1f2 | 2009-11-25 16:23:35 +0900 | [diff] [blame] | 87 |  | 
| Magnus Damm | e552de2 | 2009-01-21 15:13:42 +0000 | [diff] [blame] | 88 | 	struct list_head	node; | 
| Guennadi Liakhovetski | 73a19e4 | 2010-03-02 11:39:15 +0900 | [diff] [blame] | 89 | 	struct dma_chan			*chan_tx; | 
 | 90 | 	struct dma_chan			*chan_rx; | 
 | 91 | #ifdef CONFIG_SERIAL_SH_SCI_DMA | 
 | 92 | 	struct device			*dma_dev; | 
| Magnus Damm | 4bab9d4 | 2010-03-19 04:46:38 +0000 | [diff] [blame] | 93 | 	unsigned int			slave_tx; | 
 | 94 | 	unsigned int			slave_rx; | 
| Guennadi Liakhovetski | 73a19e4 | 2010-03-02 11:39:15 +0900 | [diff] [blame] | 95 | 	struct dma_async_tx_descriptor	*desc_tx; | 
 | 96 | 	struct dma_async_tx_descriptor	*desc_rx[2]; | 
 | 97 | 	dma_cookie_t			cookie_tx; | 
 | 98 | 	dma_cookie_t			cookie_rx[2]; | 
 | 99 | 	dma_cookie_t			active_rx; | 
 | 100 | 	struct scatterlist		sg_tx; | 
 | 101 | 	unsigned int			sg_len_tx; | 
 | 102 | 	struct scatterlist		sg_rx[2]; | 
 | 103 | 	size_t				buf_len_rx; | 
 | 104 | 	struct sh_dmae_slave		param_tx; | 
 | 105 | 	struct sh_dmae_slave		param_rx; | 
 | 106 | 	struct work_struct		work_tx; | 
 | 107 | 	struct work_struct		work_rx; | 
 | 108 | 	struct timer_list		rx_timer; | 
| Guennadi Liakhovetski | 3089f38 | 2010-03-19 13:53:04 +0000 | [diff] [blame^] | 109 | 	unsigned int			rx_timeout; | 
| Guennadi Liakhovetski | 73a19e4 | 2010-03-02 11:39:15 +0900 | [diff] [blame] | 110 | #endif | 
| Magnus Damm | e552de2 | 2009-01-21 15:13:42 +0000 | [diff] [blame] | 111 | }; | 
 | 112 |  | 
 | 113 | struct sh_sci_priv { | 
 | 114 | 	spinlock_t lock; | 
 | 115 | 	struct list_head ports; | 
| Magnus Damm | e552de2 | 2009-01-21 15:13:42 +0000 | [diff] [blame] | 116 | 	struct notifier_block clk_nb; | 
| Paul Mundt | e108b2c | 2006-09-27 16:32:13 +0900 | [diff] [blame] | 117 | }; | 
 | 118 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 119 | /* Function prototypes */ | 
| Russell King | b129a8c | 2005-08-31 10:12:14 +0100 | [diff] [blame] | 120 | static void sci_stop_tx(struct uart_port *port); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 121 |  | 
| Paul Mundt | e108b2c | 2006-09-27 16:32:13 +0900 | [diff] [blame] | 122 | #define SCI_NPORTS CONFIG_SERIAL_SH_SCI_NR_UARTS | 
 | 123 |  | 
 | 124 | static struct sci_port sci_ports[SCI_NPORTS]; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 125 | static struct uart_driver sci_uart_driver; | 
 | 126 |  | 
| Michael Trimarchi | e7c98dc | 2008-11-13 18:18:35 +0900 | [diff] [blame] | 127 | static inline struct sci_port * | 
 | 128 | to_sci_port(struct uart_port *uart) | 
 | 129 | { | 
 | 130 | 	return container_of(uart, struct sci_port, port); | 
 | 131 | } | 
 | 132 |  | 
| Paul Mundt | 07d2a1a | 2008-12-11 19:06:43 +0900 | [diff] [blame] | 133 | #if defined(CONFIG_CONSOLE_POLL) || defined(CONFIG_SERIAL_SH_SCI_CONSOLE) | 
| Paul Mundt | 1f6fd5c | 2008-12-17 14:53:24 +0900 | [diff] [blame] | 134 |  | 
 | 135 | #ifdef CONFIG_CONSOLE_POLL | 
| Paul Mundt | e108b2c | 2006-09-27 16:32:13 +0900 | [diff] [blame] | 136 | static inline void handle_error(struct uart_port *port) | 
 | 137 | { | 
 | 138 | 	/* Clear error flags */ | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 139 | 	sci_out(port, SCxSR, SCxSR_ERROR_CLEAR(port)); | 
 | 140 | } | 
 | 141 |  | 
| Paul Mundt | 07d2a1a | 2008-12-11 19:06:43 +0900 | [diff] [blame] | 142 | static int sci_poll_get_char(struct uart_port *port) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 143 | { | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 144 | 	unsigned short status; | 
 | 145 | 	int c; | 
 | 146 |  | 
| Paul Mundt | e108b2c | 2006-09-27 16:32:13 +0900 | [diff] [blame] | 147 | 	do { | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 148 | 		status = sci_in(port, SCxSR); | 
 | 149 | 		if (status & SCxSR_ERRORS(port)) { | 
 | 150 | 			handle_error(port); | 
 | 151 | 			continue; | 
 | 152 | 		} | 
 | 153 | 	} while (!(status & SCxSR_RDxF(port))); | 
| Paul Mundt | 07d2a1a | 2008-12-11 19:06:43 +0900 | [diff] [blame] | 154 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 155 | 	c = sci_in(port, SCxRDR); | 
| Paul Mundt | 07d2a1a | 2008-12-11 19:06:43 +0900 | [diff] [blame] | 156 |  | 
| Michael Trimarchi | e7c98dc | 2008-11-13 18:18:35 +0900 | [diff] [blame] | 157 | 	/* Dummy read */ | 
 | 158 | 	sci_in(port, SCxSR); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 159 | 	sci_out(port, SCxSR, SCxSR_RDxF_CLEAR(port)); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 160 |  | 
 | 161 | 	return c; | 
 | 162 | } | 
| Paul Mundt | 1f6fd5c | 2008-12-17 14:53:24 +0900 | [diff] [blame] | 163 | #endif | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 164 |  | 
| Paul Mundt | 07d2a1a | 2008-12-11 19:06:43 +0900 | [diff] [blame] | 165 | static void sci_poll_put_char(struct uart_port *port, unsigned char c) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 166 | { | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 167 | 	unsigned short status; | 
 | 168 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 169 | 	do { | 
 | 170 | 		status = sci_in(port, SCxSR); | 
 | 171 | 	} while (!(status & SCxSR_TDxE(port))); | 
 | 172 |  | 
| Paul Mundt | 272966c | 2008-11-13 17:46:06 +0900 | [diff] [blame] | 173 | 	sci_out(port, SCxTDR, c); | 
| SUGIOKA Toshinobu | dd0a3e7 | 2009-06-01 03:53:41 +0000 | [diff] [blame] | 174 | 	sci_out(port, SCxSR, SCxSR_TDxE_CLEAR(port) & ~SCxSR_TEND(port)); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 175 | } | 
| Paul Mundt | 07d2a1a | 2008-12-11 19:06:43 +0900 | [diff] [blame] | 176 | #endif /* CONFIG_CONSOLE_POLL || CONFIG_SERIAL_SH_SCI_CONSOLE */ | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 177 |  | 
| Paul Mundt | 15c73aa | 2008-10-02 19:47:12 +0900 | [diff] [blame] | 178 | #if defined(__H8300H__) || defined(__H8300S__) | 
| Paul Mundt | d570164 | 2008-12-16 20:07:27 +0900 | [diff] [blame] | 179 | static void sci_init_pins(struct uart_port *port, unsigned int cflag) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 180 | { | 
 | 181 | 	int ch = (port->mapbase - SMR0) >> 3; | 
 | 182 |  | 
 | 183 | 	/* set DDR regs */ | 
| Paul Mundt | e108b2c | 2006-09-27 16:32:13 +0900 | [diff] [blame] | 184 | 	H8300_GPIO_DDR(h8300_sci_pins[ch].port, | 
 | 185 | 		       h8300_sci_pins[ch].rx, | 
 | 186 | 		       H8300_GPIO_INPUT); | 
 | 187 | 	H8300_GPIO_DDR(h8300_sci_pins[ch].port, | 
 | 188 | 		       h8300_sci_pins[ch].tx, | 
 | 189 | 		       H8300_GPIO_OUTPUT); | 
 | 190 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 191 | 	/* tx mark output*/ | 
 | 192 | 	H8300_SCI_DR(ch) |= h8300_sci_pins[ch].tx; | 
 | 193 | } | 
| Paul Mundt | d570164 | 2008-12-16 20:07:27 +0900 | [diff] [blame] | 194 | #elif defined(CONFIG_CPU_SUBTYPE_SH7710) || defined(CONFIG_CPU_SUBTYPE_SH7712) | 
 | 195 | static inline void sci_init_pins(struct uart_port *port, unsigned int cflag) | 
| Paul Mundt | e108b2c | 2006-09-27 16:32:13 +0900 | [diff] [blame] | 196 | { | 
| Paul Mundt | d570164 | 2008-12-16 20:07:27 +0900 | [diff] [blame] | 197 | 	if (port->mapbase == 0xA4400000) { | 
 | 198 | 		__raw_writew(__raw_readw(PACR) & 0xffc0, PACR); | 
 | 199 | 		__raw_writew(__raw_readw(PBCR) & 0x0fff, PBCR); | 
 | 200 | 	} else if (port->mapbase == 0xA4410000) | 
 | 201 | 		__raw_writew(__raw_readw(PBCR) & 0xf003, PBCR); | 
| Nobuhiro Iwamatsu | 9465a54 | 2007-03-27 18:13:51 +0900 | [diff] [blame] | 202 | } | 
| Yoshihiro Shimoda | 31a49c4 | 2007-12-26 11:45:06 +0900 | [diff] [blame] | 203 | #elif defined(CONFIG_CPU_SUBTYPE_SH7720) || defined(CONFIG_CPU_SUBTYPE_SH7721) | 
| Paul Mundt | d570164 | 2008-12-16 20:07:27 +0900 | [diff] [blame] | 204 | static inline void sci_init_pins(struct uart_port *port, unsigned int cflag) | 
| Markus Brunner | 3ea6bc3 | 2007-08-20 08:59:33 +0900 | [diff] [blame] | 205 | { | 
| Markus Brunner | 3ea6bc3 | 2007-08-20 08:59:33 +0900 | [diff] [blame] | 206 | 	unsigned short data; | 
 | 207 |  | 
 | 208 | 	if (cflag & CRTSCTS) { | 
 | 209 | 		/* enable RTS/CTS */ | 
 | 210 | 		if (port->mapbase == 0xa4430000) { /* SCIF0 */ | 
 | 211 | 			/* Clear PTCR bit 9-2; enable all scif pins but sck */ | 
| Paul Mundt | d570164 | 2008-12-16 20:07:27 +0900 | [diff] [blame] | 212 | 			data = __raw_readw(PORT_PTCR); | 
 | 213 | 			__raw_writew((data & 0xfc03), PORT_PTCR); | 
| Markus Brunner | 3ea6bc3 | 2007-08-20 08:59:33 +0900 | [diff] [blame] | 214 | 		} else if (port->mapbase == 0xa4438000) { /* SCIF1 */ | 
 | 215 | 			/* Clear PVCR bit 9-2 */ | 
| Paul Mundt | d570164 | 2008-12-16 20:07:27 +0900 | [diff] [blame] | 216 | 			data = __raw_readw(PORT_PVCR); | 
 | 217 | 			__raw_writew((data & 0xfc03), PORT_PVCR); | 
| Markus Brunner | 3ea6bc3 | 2007-08-20 08:59:33 +0900 | [diff] [blame] | 218 | 		} | 
| Markus Brunner | 3ea6bc3 | 2007-08-20 08:59:33 +0900 | [diff] [blame] | 219 | 	} else { | 
 | 220 | 		if (port->mapbase == 0xa4430000) { /* SCIF0 */ | 
 | 221 | 			/* Clear PTCR bit 5-2; enable only tx and rx  */ | 
| Paul Mundt | d570164 | 2008-12-16 20:07:27 +0900 | [diff] [blame] | 222 | 			data = __raw_readw(PORT_PTCR); | 
 | 223 | 			__raw_writew((data & 0xffc3), PORT_PTCR); | 
| Markus Brunner | 3ea6bc3 | 2007-08-20 08:59:33 +0900 | [diff] [blame] | 224 | 		} else if (port->mapbase == 0xa4438000) { /* SCIF1 */ | 
 | 225 | 			/* Clear PVCR bit 5-2 */ | 
| Paul Mundt | d570164 | 2008-12-16 20:07:27 +0900 | [diff] [blame] | 226 | 			data = __raw_readw(PORT_PVCR); | 
 | 227 | 			__raw_writew((data & 0xffc3), PORT_PVCR); | 
| Markus Brunner | 3ea6bc3 | 2007-08-20 08:59:33 +0900 | [diff] [blame] | 228 | 		} | 
 | 229 | 	} | 
| Markus Brunner | 3ea6bc3 | 2007-08-20 08:59:33 +0900 | [diff] [blame] | 230 | } | 
| Paul Mundt | b7a76e4 | 2006-02-01 03:06:06 -0800 | [diff] [blame] | 231 | #elif defined(CONFIG_CPU_SH3) | 
| Paul Mundt | e108b2c | 2006-09-27 16:32:13 +0900 | [diff] [blame] | 232 | /* For SH7705, SH7706, SH7707, SH7709, SH7709A, SH7729 */ | 
| Paul Mundt | d570164 | 2008-12-16 20:07:27 +0900 | [diff] [blame] | 233 | static inline void sci_init_pins(struct uart_port *port, unsigned int cflag) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 234 | { | 
| Paul Mundt | b7a76e4 | 2006-02-01 03:06:06 -0800 | [diff] [blame] | 235 | 	unsigned short data; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 236 |  | 
| Paul Mundt | b7a76e4 | 2006-02-01 03:06:06 -0800 | [diff] [blame] | 237 | 	/* We need to set SCPCR to enable RTS/CTS */ | 
| Paul Mundt | d570164 | 2008-12-16 20:07:27 +0900 | [diff] [blame] | 238 | 	data = __raw_readw(SCPCR); | 
| Paul Mundt | b7a76e4 | 2006-02-01 03:06:06 -0800 | [diff] [blame] | 239 | 	/* Clear out SCP7MD1,0, SCP6MD1,0, SCP4MD1,0*/ | 
| Paul Mundt | d570164 | 2008-12-16 20:07:27 +0900 | [diff] [blame] | 240 | 	__raw_writew(data & 0x0fcf, SCPCR); | 
| Paul Mundt | b7a76e4 | 2006-02-01 03:06:06 -0800 | [diff] [blame] | 241 |  | 
| Paul Mundt | d570164 | 2008-12-16 20:07:27 +0900 | [diff] [blame] | 242 | 	if (!(cflag & CRTSCTS)) { | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 243 | 		/* We need to set SCPCR to enable RTS/CTS */ | 
| Paul Mundt | d570164 | 2008-12-16 20:07:27 +0900 | [diff] [blame] | 244 | 		data = __raw_readw(SCPCR); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 245 | 		/* Clear out SCP7MD1,0, SCP4MD1,0, | 
 | 246 | 		   Set SCP6MD1,0 = {01} (output)  */ | 
| Paul Mundt | d570164 | 2008-12-16 20:07:27 +0900 | [diff] [blame] | 247 | 		__raw_writew((data & 0x0fcf) | 0x1000, SCPCR); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 248 |  | 
| Paul Mundt | 32b5307 | 2009-12-24 14:52:43 +0900 | [diff] [blame] | 249 | 		data = __raw_readb(SCPDR); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 250 | 		/* Set /RTS2 (bit6) = 0 */ | 
| Paul Mundt | 32b5307 | 2009-12-24 14:52:43 +0900 | [diff] [blame] | 251 | 		__raw_writeb(data & 0xbf, SCPDR); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 252 | 	} | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 253 | } | 
| Paul Mundt | 41504c3 | 2006-12-11 20:28:03 +0900 | [diff] [blame] | 254 | #elif defined(CONFIG_CPU_SUBTYPE_SH7722) | 
| Paul Mundt | d570164 | 2008-12-16 20:07:27 +0900 | [diff] [blame] | 255 | static inline void sci_init_pins(struct uart_port *port, unsigned int cflag) | 
| Paul Mundt | 41504c3 | 2006-12-11 20:28:03 +0900 | [diff] [blame] | 256 | { | 
| Magnus Damm | 346b746 | 2008-04-23 21:25:29 +0900 | [diff] [blame] | 257 | 	unsigned short data; | 
| Paul Mundt | 41504c3 | 2006-12-11 20:28:03 +0900 | [diff] [blame] | 258 |  | 
| Magnus Damm | 346b746 | 2008-04-23 21:25:29 +0900 | [diff] [blame] | 259 | 	if (port->mapbase == 0xffe00000) { | 
| Paul Mundt | d570164 | 2008-12-16 20:07:27 +0900 | [diff] [blame] | 260 | 		data = __raw_readw(PSCR); | 
| Magnus Damm | 346b746 | 2008-04-23 21:25:29 +0900 | [diff] [blame] | 261 | 		data &= ~0x03cf; | 
| Paul Mundt | d570164 | 2008-12-16 20:07:27 +0900 | [diff] [blame] | 262 | 		if (!(cflag & CRTSCTS)) | 
| Magnus Damm | 346b746 | 2008-04-23 21:25:29 +0900 | [diff] [blame] | 263 | 			data |= 0x0340; | 
| Paul Mundt | 41504c3 | 2006-12-11 20:28:03 +0900 | [diff] [blame] | 264 |  | 
| Paul Mundt | d570164 | 2008-12-16 20:07:27 +0900 | [diff] [blame] | 265 | 		__raw_writew(data, PSCR); | 
| Paul Mundt | 41504c3 | 2006-12-11 20:28:03 +0900 | [diff] [blame] | 266 | 	} | 
| Paul Mundt | 41504c3 | 2006-12-11 20:28:03 +0900 | [diff] [blame] | 267 | } | 
| Yoshihiro Shimoda | c01f0f1 | 2009-08-21 16:30:28 +0900 | [diff] [blame] | 268 | #elif defined(CONFIG_CPU_SUBTYPE_SH7757) || \ | 
 | 269 |       defined(CONFIG_CPU_SUBTYPE_SH7763) || \ | 
| Yoshihiro Shimoda | 7d740a0 | 2008-01-07 14:40:07 +0900 | [diff] [blame] | 270 |       defined(CONFIG_CPU_SUBTYPE_SH7780) || \ | 
| Paul Mundt | 2b1bd1a | 2007-06-20 18:27:10 +0900 | [diff] [blame] | 271 |       defined(CONFIG_CPU_SUBTYPE_SH7785) || \ | 
| Kuninori Morimoto | 55ba99e | 2009-03-03 15:40:25 +0900 | [diff] [blame] | 272 |       defined(CONFIG_CPU_SUBTYPE_SH7786) || \ | 
| Paul Mundt | 2b1bd1a | 2007-06-20 18:27:10 +0900 | [diff] [blame] | 273 |       defined(CONFIG_CPU_SUBTYPE_SHX3) | 
| Paul Mundt | d570164 | 2008-12-16 20:07:27 +0900 | [diff] [blame] | 274 | static inline void sci_init_pins(struct uart_port *port, unsigned int cflag) | 
 | 275 | { | 
 | 276 | 	if (!(cflag & CRTSCTS)) | 
 | 277 | 		__raw_writew(0x0080, SCSPTR0); /* Set RTS = 1 */ | 
 | 278 | } | 
| Paul Mundt | b0c50ad | 2008-12-22 03:40:10 +0900 | [diff] [blame] | 279 | #elif defined(CONFIG_CPU_SH4) && !defined(CONFIG_CPU_SH4A) | 
| Paul Mundt | d570164 | 2008-12-16 20:07:27 +0900 | [diff] [blame] | 280 | static inline void sci_init_pins(struct uart_port *port, unsigned int cflag) | 
 | 281 | { | 
 | 282 | 	if (!(cflag & CRTSCTS)) | 
 | 283 | 		__raw_writew(0x0080, SCSPTR2); /* Set RTS = 1 */ | 
 | 284 | } | 
| Paul Mundt | b7a76e4 | 2006-02-01 03:06:06 -0800 | [diff] [blame] | 285 | #else | 
| Paul Mundt | d570164 | 2008-12-16 20:07:27 +0900 | [diff] [blame] | 286 | static inline void sci_init_pins(struct uart_port *port, unsigned int cflag) | 
 | 287 | { | 
 | 288 | 	/* Nothing to do */ | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 289 | } | 
| Paul Mundt | e108b2c | 2006-09-27 16:32:13 +0900 | [diff] [blame] | 290 | #endif | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 291 |  | 
| Paul Mundt | 32351a2 | 2007-03-12 14:38:59 +0900 | [diff] [blame] | 292 | #if defined(CONFIG_CPU_SUBTYPE_SH7760) || \ | 
 | 293 |     defined(CONFIG_CPU_SUBTYPE_SH7780) || \ | 
| Kuninori Morimoto | 55ba99e | 2009-03-03 15:40:25 +0900 | [diff] [blame] | 294 |     defined(CONFIG_CPU_SUBTYPE_SH7785) || \ | 
 | 295 |     defined(CONFIG_CPU_SUBTYPE_SH7786) | 
| Guennadi Liakhovetski | 73a19e4 | 2010-03-02 11:39:15 +0900 | [diff] [blame] | 296 | static int scif_txfill(struct uart_port *port) | 
| Paul Mundt | e108b2c | 2006-09-27 16:32:13 +0900 | [diff] [blame] | 297 | { | 
| Guennadi Liakhovetski | 73a19e4 | 2010-03-02 11:39:15 +0900 | [diff] [blame] | 298 | 	return sci_in(port, SCTFDR) & 0xff; | 
| Paul Mundt | e108b2c | 2006-09-27 16:32:13 +0900 | [diff] [blame] | 299 | } | 
 | 300 |  | 
| Guennadi Liakhovetski | 73a19e4 | 2010-03-02 11:39:15 +0900 | [diff] [blame] | 301 | static int scif_txroom(struct uart_port *port) | 
 | 302 | { | 
 | 303 | 	return SCIF_TXROOM_MAX - scif_txfill(port); | 
 | 304 | } | 
 | 305 |  | 
 | 306 | static int scif_rxfill(struct uart_port *port) | 
| Paul Mundt | e108b2c | 2006-09-27 16:32:13 +0900 | [diff] [blame] | 307 | { | 
| Yutaro Ebihara | cae167d | 2008-03-11 13:58:50 +0900 | [diff] [blame] | 308 | 	return sci_in(port, SCRFDR) & 0xff; | 
| Paul Mundt | e108b2c | 2006-09-27 16:32:13 +0900 | [diff] [blame] | 309 | } | 
| Nobuhiro Iwamatsu | c63847a | 2008-06-06 17:04:08 +0900 | [diff] [blame] | 310 | #elif defined(CONFIG_CPU_SUBTYPE_SH7763) | 
| Guennadi Liakhovetski | 73a19e4 | 2010-03-02 11:39:15 +0900 | [diff] [blame] | 311 | static int scif_txfill(struct uart_port *port) | 
| Nobuhiro Iwamatsu | c63847a | 2008-06-06 17:04:08 +0900 | [diff] [blame] | 312 | { | 
| Guennadi Liakhovetski | 73a19e4 | 2010-03-02 11:39:15 +0900 | [diff] [blame] | 313 | 	if (port->mapbase == 0xffe00000 || | 
 | 314 | 	    port->mapbase == 0xffe08000) | 
| Michael Trimarchi | e7c98dc | 2008-11-13 18:18:35 +0900 | [diff] [blame] | 315 | 		/* SCIF0/1*/ | 
| Guennadi Liakhovetski | 73a19e4 | 2010-03-02 11:39:15 +0900 | [diff] [blame] | 316 | 		return sci_in(port, SCTFDR) & 0xff; | 
 | 317 | 	else | 
| Michael Trimarchi | e7c98dc | 2008-11-13 18:18:35 +0900 | [diff] [blame] | 318 | 		/* SCIF2 */ | 
| Guennadi Liakhovetski | 73a19e4 | 2010-03-02 11:39:15 +0900 | [diff] [blame] | 319 | 		return sci_in(port, SCFDR) >> 8; | 
| Nobuhiro Iwamatsu | c63847a | 2008-06-06 17:04:08 +0900 | [diff] [blame] | 320 | } | 
 | 321 |  | 
| Guennadi Liakhovetski | 73a19e4 | 2010-03-02 11:39:15 +0900 | [diff] [blame] | 322 | static int scif_txroom(struct uart_port *port) | 
 | 323 | { | 
 | 324 | 	if (port->mapbase == 0xffe00000 || | 
 | 325 | 	    port->mapbase == 0xffe08000) | 
 | 326 | 		/* SCIF0/1*/ | 
 | 327 | 		return SCIF_TXROOM_MAX - scif_txfill(port); | 
 | 328 | 	else | 
 | 329 | 		/* SCIF2 */ | 
 | 330 | 		return SCIF2_TXROOM_MAX - scif_txfill(port); | 
 | 331 | } | 
 | 332 |  | 
 | 333 | static int scif_rxfill(struct uart_port *port) | 
| Nobuhiro Iwamatsu | c63847a | 2008-06-06 17:04:08 +0900 | [diff] [blame] | 334 | { | 
| Michael Trimarchi | e7c98dc | 2008-11-13 18:18:35 +0900 | [diff] [blame] | 335 | 	if ((port->mapbase == 0xffe00000) || | 
 | 336 | 	    (port->mapbase == 0xffe08000)) { | 
 | 337 | 		/* SCIF0/1*/ | 
| Nobuhiro Iwamatsu | c63847a | 2008-06-06 17:04:08 +0900 | [diff] [blame] | 338 | 		return sci_in(port, SCRFDR) & 0xff; | 
| Michael Trimarchi | e7c98dc | 2008-11-13 18:18:35 +0900 | [diff] [blame] | 339 | 	} else { | 
 | 340 | 		/* SCIF2 */ | 
| Nobuhiro Iwamatsu | c63847a | 2008-06-06 17:04:08 +0900 | [diff] [blame] | 341 | 		return sci_in(port, SCFDR) & SCIF2_RFDC_MASK; | 
| Michael Trimarchi | e7c98dc | 2008-11-13 18:18:35 +0900 | [diff] [blame] | 342 | 	} | 
| Nobuhiro Iwamatsu | c63847a | 2008-06-06 17:04:08 +0900 | [diff] [blame] | 343 | } | 
| Paul Mundt | e108b2c | 2006-09-27 16:32:13 +0900 | [diff] [blame] | 344 | #else | 
| Guennadi Liakhovetski | 73a19e4 | 2010-03-02 11:39:15 +0900 | [diff] [blame] | 345 | static int scif_txfill(struct uart_port *port) | 
| Paul Mundt | e108b2c | 2006-09-27 16:32:13 +0900 | [diff] [blame] | 346 | { | 
| Guennadi Liakhovetski | 73a19e4 | 2010-03-02 11:39:15 +0900 | [diff] [blame] | 347 | 	return sci_in(port, SCFDR) >> 8; | 
| Paul Mundt | e108b2c | 2006-09-27 16:32:13 +0900 | [diff] [blame] | 348 | } | 
 | 349 |  | 
| Guennadi Liakhovetski | 73a19e4 | 2010-03-02 11:39:15 +0900 | [diff] [blame] | 350 | static int scif_txroom(struct uart_port *port) | 
 | 351 | { | 
 | 352 | 	return SCIF_TXROOM_MAX - scif_txfill(port); | 
 | 353 | } | 
 | 354 |  | 
 | 355 | static int scif_rxfill(struct uart_port *port) | 
| Paul Mundt | e108b2c | 2006-09-27 16:32:13 +0900 | [diff] [blame] | 356 | { | 
 | 357 | 	return sci_in(port, SCFDR) & SCIF_RFDC_MASK; | 
 | 358 | } | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 359 | #endif | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 360 |  | 
| Guennadi Liakhovetski | 73a19e4 | 2010-03-02 11:39:15 +0900 | [diff] [blame] | 361 | static int sci_txfill(struct uart_port *port) | 
| Paul Mundt | e108b2c | 2006-09-27 16:32:13 +0900 | [diff] [blame] | 362 | { | 
| Guennadi Liakhovetski | 73a19e4 | 2010-03-02 11:39:15 +0900 | [diff] [blame] | 363 | 	return !(sci_in(port, SCxSR) & SCI_TDRE); | 
| Paul Mundt | e108b2c | 2006-09-27 16:32:13 +0900 | [diff] [blame] | 364 | } | 
 | 365 |  | 
| Guennadi Liakhovetski | 73a19e4 | 2010-03-02 11:39:15 +0900 | [diff] [blame] | 366 | static int sci_txroom(struct uart_port *port) | 
 | 367 | { | 
 | 368 | 	return !sci_txfill(port); | 
 | 369 | } | 
 | 370 |  | 
 | 371 | static int sci_rxfill(struct uart_port *port) | 
| Paul Mundt | e108b2c | 2006-09-27 16:32:13 +0900 | [diff] [blame] | 372 | { | 
| Michael Trimarchi | e7c98dc | 2008-11-13 18:18:35 +0900 | [diff] [blame] | 373 | 	return (sci_in(port, SCxSR) & SCxSR_RDxF(port)) != 0; | 
| Paul Mundt | e108b2c | 2006-09-27 16:32:13 +0900 | [diff] [blame] | 374 | } | 
 | 375 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 376 | /* ********************************************************************** * | 
 | 377 |  *                   the interrupt related routines                       * | 
 | 378 |  * ********************************************************************** */ | 
 | 379 |  | 
 | 380 | static void sci_transmit_chars(struct uart_port *port) | 
 | 381 | { | 
| Alan Cox | ebd2c8f | 2009-09-19 13:13:28 -0700 | [diff] [blame] | 382 | 	struct circ_buf *xmit = &port->state->xmit; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 383 | 	unsigned int stopped = uart_tx_stopped(port); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 384 | 	unsigned short status; | 
 | 385 | 	unsigned short ctrl; | 
| Paul Mundt | e108b2c | 2006-09-27 16:32:13 +0900 | [diff] [blame] | 386 | 	int count; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 387 |  | 
 | 388 | 	status = sci_in(port, SCxSR); | 
 | 389 | 	if (!(status & SCxSR_TDxE(port))) { | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 390 | 		ctrl = sci_in(port, SCSCR); | 
| Michael Trimarchi | e7c98dc | 2008-11-13 18:18:35 +0900 | [diff] [blame] | 391 | 		if (uart_circ_empty(xmit)) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 392 | 			ctrl &= ~SCI_CTRL_FLAGS_TIE; | 
| Michael Trimarchi | e7c98dc | 2008-11-13 18:18:35 +0900 | [diff] [blame] | 393 | 		else | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 394 | 			ctrl |= SCI_CTRL_FLAGS_TIE; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 395 | 		sci_out(port, SCSCR, ctrl); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 396 | 		return; | 
 | 397 | 	} | 
 | 398 |  | 
| Yoshihiro Shimoda | 1a22f08 | 2008-11-11 12:19:05 +0900 | [diff] [blame] | 399 | 	if (port->type == PORT_SCI) | 
| Paul Mundt | e108b2c | 2006-09-27 16:32:13 +0900 | [diff] [blame] | 400 | 		count = sci_txroom(port); | 
| Yoshihiro Shimoda | 1a22f08 | 2008-11-11 12:19:05 +0900 | [diff] [blame] | 401 | 	else | 
 | 402 | 		count = scif_txroom(port); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 403 |  | 
 | 404 | 	do { | 
 | 405 | 		unsigned char c; | 
 | 406 |  | 
 | 407 | 		if (port->x_char) { | 
 | 408 | 			c = port->x_char; | 
 | 409 | 			port->x_char = 0; | 
 | 410 | 		} else if (!uart_circ_empty(xmit) && !stopped) { | 
 | 411 | 			c = xmit->buf[xmit->tail]; | 
 | 412 | 			xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); | 
 | 413 | 		} else { | 
 | 414 | 			break; | 
 | 415 | 		} | 
 | 416 |  | 
 | 417 | 		sci_out(port, SCxTDR, c); | 
 | 418 |  | 
 | 419 | 		port->icount.tx++; | 
 | 420 | 	} while (--count > 0); | 
 | 421 |  | 
 | 422 | 	sci_out(port, SCxSR, SCxSR_TDxE_CLEAR(port)); | 
 | 423 |  | 
 | 424 | 	if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) | 
 | 425 | 		uart_write_wakeup(port); | 
 | 426 | 	if (uart_circ_empty(xmit)) { | 
| Russell King | b129a8c | 2005-08-31 10:12:14 +0100 | [diff] [blame] | 427 | 		sci_stop_tx(port); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 428 | 	} else { | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 429 | 		ctrl = sci_in(port, SCSCR); | 
 | 430 |  | 
| Yoshihiro Shimoda | 1a22f08 | 2008-11-11 12:19:05 +0900 | [diff] [blame] | 431 | 		if (port->type != PORT_SCI) { | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 432 | 			sci_in(port, SCxSR); /* Dummy read */ | 
 | 433 | 			sci_out(port, SCxSR, SCxSR_TDxE_CLEAR(port)); | 
 | 434 | 		} | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 435 |  | 
 | 436 | 		ctrl |= SCI_CTRL_FLAGS_TIE; | 
 | 437 | 		sci_out(port, SCSCR, ctrl); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 438 | 	} | 
 | 439 | } | 
 | 440 |  | 
 | 441 | /* On SH3, SCIF may read end-of-break as a space->mark char */ | 
| Michael Trimarchi | e7c98dc | 2008-11-13 18:18:35 +0900 | [diff] [blame] | 442 | #define STEPFN(c)  ({int __c = (c); (((__c-1)|(__c)) == -1); }) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 443 |  | 
| David Howells | 7d12e78 | 2006-10-05 14:55:46 +0100 | [diff] [blame] | 444 | static inline void sci_receive_chars(struct uart_port *port) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 445 | { | 
| Michael Trimarchi | e7c98dc | 2008-11-13 18:18:35 +0900 | [diff] [blame] | 446 | 	struct sci_port *sci_port = to_sci_port(port); | 
| Alan Cox | ebd2c8f | 2009-09-19 13:13:28 -0700 | [diff] [blame] | 447 | 	struct tty_struct *tty = port->state->port.tty; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 448 | 	int i, count, copied = 0; | 
 | 449 | 	unsigned short status; | 
| Alan Cox | 33f0f88 | 2006-01-09 20:54:13 -0800 | [diff] [blame] | 450 | 	unsigned char flag; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 451 |  | 
 | 452 | 	status = sci_in(port, SCxSR); | 
 | 453 | 	if (!(status & SCxSR_RDxF(port))) | 
 | 454 | 		return; | 
 | 455 |  | 
 | 456 | 	while (1) { | 
| Yoshihiro Shimoda | 1a22f08 | 2008-11-11 12:19:05 +0900 | [diff] [blame] | 457 | 		if (port->type == PORT_SCI) | 
| Guennadi Liakhovetski | 73a19e4 | 2010-03-02 11:39:15 +0900 | [diff] [blame] | 458 | 			count = sci_rxfill(port); | 
| Yoshihiro Shimoda | 1a22f08 | 2008-11-11 12:19:05 +0900 | [diff] [blame] | 459 | 		else | 
| Guennadi Liakhovetski | 73a19e4 | 2010-03-02 11:39:15 +0900 | [diff] [blame] | 460 | 			count = scif_rxfill(port); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 461 |  | 
 | 462 | 		/* Don't copy more bytes than there is room for in the buffer */ | 
| Alan Cox | 33f0f88 | 2006-01-09 20:54:13 -0800 | [diff] [blame] | 463 | 		count = tty_buffer_request_room(tty, count); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 464 |  | 
 | 465 | 		/* If for any reason we can't copy more data, we're done! */ | 
 | 466 | 		if (count == 0) | 
 | 467 | 			break; | 
 | 468 |  | 
 | 469 | 		if (port->type == PORT_SCI) { | 
 | 470 | 			char c = sci_in(port, SCxRDR); | 
| Michael Trimarchi | e7c98dc | 2008-11-13 18:18:35 +0900 | [diff] [blame] | 471 | 			if (uart_handle_sysrq_char(port, c) || | 
 | 472 | 			    sci_port->break_flag) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 473 | 				count = 0; | 
| Michael Trimarchi | e7c98dc | 2008-11-13 18:18:35 +0900 | [diff] [blame] | 474 | 			else | 
| Paul Mundt | e108b2c | 2006-09-27 16:32:13 +0900 | [diff] [blame] | 475 | 				tty_insert_flip_char(tty, c, TTY_NORMAL); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 476 | 		} else { | 
| Michael Trimarchi | e7c98dc | 2008-11-13 18:18:35 +0900 | [diff] [blame] | 477 | 			for (i = 0; i < count; i++) { | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 478 | 				char c = sci_in(port, SCxRDR); | 
 | 479 | 				status = sci_in(port, SCxSR); | 
 | 480 | #if defined(CONFIG_CPU_SH3) | 
 | 481 | 				/* Skip "chars" during break */ | 
| Paul Mundt | e108b2c | 2006-09-27 16:32:13 +0900 | [diff] [blame] | 482 | 				if (sci_port->break_flag) { | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 483 | 					if ((c == 0) && | 
 | 484 | 					    (status & SCxSR_FER(port))) { | 
 | 485 | 						count--; i--; | 
 | 486 | 						continue; | 
 | 487 | 					} | 
| Paul Mundt | e108b2c | 2006-09-27 16:32:13 +0900 | [diff] [blame] | 488 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 489 | 					/* Nonzero => end-of-break */ | 
| Paul Mundt | 762c69e | 2008-12-16 18:55:26 +0900 | [diff] [blame] | 490 | 					dev_dbg(port->dev, "debounce<%02x>\n", c); | 
| Paul Mundt | e108b2c | 2006-09-27 16:32:13 +0900 | [diff] [blame] | 491 | 					sci_port->break_flag = 0; | 
 | 492 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 493 | 					if (STEPFN(c)) { | 
 | 494 | 						count--; i--; | 
 | 495 | 						continue; | 
 | 496 | 					} | 
 | 497 | 				} | 
 | 498 | #endif /* CONFIG_CPU_SH3 */ | 
| David Howells | 7d12e78 | 2006-10-05 14:55:46 +0100 | [diff] [blame] | 499 | 				if (uart_handle_sysrq_char(port, c)) { | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 500 | 					count--; i--; | 
 | 501 | 					continue; | 
 | 502 | 				} | 
 | 503 |  | 
 | 504 | 				/* Store data and status */ | 
| Guennadi Liakhovetski | 73a19e4 | 2010-03-02 11:39:15 +0900 | [diff] [blame] | 505 | 				if (status & SCxSR_FER(port)) { | 
| Alan Cox | 33f0f88 | 2006-01-09 20:54:13 -0800 | [diff] [blame] | 506 | 					flag = TTY_FRAME; | 
| Paul Mundt | 762c69e | 2008-12-16 18:55:26 +0900 | [diff] [blame] | 507 | 					dev_notice(port->dev, "frame error\n"); | 
| Guennadi Liakhovetski | 73a19e4 | 2010-03-02 11:39:15 +0900 | [diff] [blame] | 508 | 				} else if (status & SCxSR_PER(port)) { | 
| Alan Cox | 33f0f88 | 2006-01-09 20:54:13 -0800 | [diff] [blame] | 509 | 					flag = TTY_PARITY; | 
| Paul Mundt | 762c69e | 2008-12-16 18:55:26 +0900 | [diff] [blame] | 510 | 					dev_notice(port->dev, "parity error\n"); | 
| Alan Cox | 33f0f88 | 2006-01-09 20:54:13 -0800 | [diff] [blame] | 511 | 				} else | 
 | 512 | 					flag = TTY_NORMAL; | 
| Paul Mundt | 762c69e | 2008-12-16 18:55:26 +0900 | [diff] [blame] | 513 |  | 
| Alan Cox | 33f0f88 | 2006-01-09 20:54:13 -0800 | [diff] [blame] | 514 | 				tty_insert_flip_char(tty, c, flag); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 515 | 			} | 
 | 516 | 		} | 
 | 517 |  | 
 | 518 | 		sci_in(port, SCxSR); /* dummy read */ | 
 | 519 | 		sci_out(port, SCxSR, SCxSR_RDxF_CLEAR(port)); | 
 | 520 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 521 | 		copied += count; | 
 | 522 | 		port->icount.rx += count; | 
 | 523 | 	} | 
 | 524 |  | 
 | 525 | 	if (copied) { | 
 | 526 | 		/* Tell the rest of the system the news. New characters! */ | 
 | 527 | 		tty_flip_buffer_push(tty); | 
 | 528 | 	} else { | 
 | 529 | 		sci_in(port, SCxSR); /* dummy read */ | 
 | 530 | 		sci_out(port, SCxSR, SCxSR_RDxF_CLEAR(port)); | 
 | 531 | 	} | 
 | 532 | } | 
 | 533 |  | 
 | 534 | #define SCI_BREAK_JIFFIES (HZ/20) | 
 | 535 | /* The sci generates interrupts during the break, | 
 | 536 |  * 1 per millisecond or so during the break period, for 9600 baud. | 
 | 537 |  * So dont bother disabling interrupts. | 
 | 538 |  * But dont want more than 1 break event. | 
 | 539 |  * Use a kernel timer to periodically poll the rx line until | 
 | 540 |  * the break is finished. | 
 | 541 |  */ | 
 | 542 | static void sci_schedule_break_timer(struct sci_port *port) | 
 | 543 | { | 
 | 544 | 	port->break_timer.expires = jiffies + SCI_BREAK_JIFFIES; | 
 | 545 | 	add_timer(&port->break_timer); | 
 | 546 | } | 
 | 547 | /* Ensure that two consecutive samples find the break over. */ | 
 | 548 | static void sci_break_timer(unsigned long data) | 
 | 549 | { | 
| Paul Mundt | e108b2c | 2006-09-27 16:32:13 +0900 | [diff] [blame] | 550 | 	struct sci_port *port = (struct sci_port *)data; | 
 | 551 |  | 
 | 552 | 	if (sci_rxd_in(&port->port) == 0) { | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 553 | 		port->break_flag = 1; | 
| Paul Mundt | e108b2c | 2006-09-27 16:32:13 +0900 | [diff] [blame] | 554 | 		sci_schedule_break_timer(port); | 
 | 555 | 	} else if (port->break_flag == 1) { | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 556 | 		/* break is over. */ | 
 | 557 | 		port->break_flag = 2; | 
| Paul Mundt | e108b2c | 2006-09-27 16:32:13 +0900 | [diff] [blame] | 558 | 		sci_schedule_break_timer(port); | 
 | 559 | 	} else | 
 | 560 | 		port->break_flag = 0; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 561 | } | 
 | 562 |  | 
 | 563 | static inline int sci_handle_errors(struct uart_port *port) | 
 | 564 | { | 
 | 565 | 	int copied = 0; | 
 | 566 | 	unsigned short status = sci_in(port, SCxSR); | 
| Alan Cox | ebd2c8f | 2009-09-19 13:13:28 -0700 | [diff] [blame] | 567 | 	struct tty_struct *tty = port->state->port.tty; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 568 |  | 
| Paul Mundt | e108b2c | 2006-09-27 16:32:13 +0900 | [diff] [blame] | 569 | 	if (status & SCxSR_ORER(port)) { | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 570 | 		/* overrun error */ | 
| Paul Mundt | e108b2c | 2006-09-27 16:32:13 +0900 | [diff] [blame] | 571 | 		if (tty_insert_flip_char(tty, 0, TTY_OVERRUN)) | 
| Alan Cox | 33f0f88 | 2006-01-09 20:54:13 -0800 | [diff] [blame] | 572 | 			copied++; | 
| Paul Mundt | 762c69e | 2008-12-16 18:55:26 +0900 | [diff] [blame] | 573 |  | 
 | 574 | 		dev_notice(port->dev, "overrun error"); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 575 | 	} | 
 | 576 |  | 
| Paul Mundt | e108b2c | 2006-09-27 16:32:13 +0900 | [diff] [blame] | 577 | 	if (status & SCxSR_FER(port)) { | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 578 | 		if (sci_rxd_in(port) == 0) { | 
 | 579 | 			/* Notify of BREAK */ | 
| Michael Trimarchi | e7c98dc | 2008-11-13 18:18:35 +0900 | [diff] [blame] | 580 | 			struct sci_port *sci_port = to_sci_port(port); | 
| Paul Mundt | e108b2c | 2006-09-27 16:32:13 +0900 | [diff] [blame] | 581 |  | 
 | 582 | 			if (!sci_port->break_flag) { | 
 | 583 | 				sci_port->break_flag = 1; | 
 | 584 | 				sci_schedule_break_timer(sci_port); | 
 | 585 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 586 | 				/* Do sysrq handling. */ | 
| Paul Mundt | e108b2c | 2006-09-27 16:32:13 +0900 | [diff] [blame] | 587 | 				if (uart_handle_break(port)) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 588 | 					return 0; | 
| Paul Mundt | 762c69e | 2008-12-16 18:55:26 +0900 | [diff] [blame] | 589 |  | 
 | 590 | 				dev_dbg(port->dev, "BREAK detected\n"); | 
 | 591 |  | 
| Paul Mundt | e108b2c | 2006-09-27 16:32:13 +0900 | [diff] [blame] | 592 | 				if (tty_insert_flip_char(tty, 0, TTY_BREAK)) | 
| Michael Trimarchi | e7c98dc | 2008-11-13 18:18:35 +0900 | [diff] [blame] | 593 | 					copied++; | 
 | 594 | 			} | 
 | 595 |  | 
| Paul Mundt | e108b2c | 2006-09-27 16:32:13 +0900 | [diff] [blame] | 596 | 		} else { | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 597 | 			/* frame error */ | 
| Paul Mundt | e108b2c | 2006-09-27 16:32:13 +0900 | [diff] [blame] | 598 | 			if (tty_insert_flip_char(tty, 0, TTY_FRAME)) | 
| Alan Cox | 33f0f88 | 2006-01-09 20:54:13 -0800 | [diff] [blame] | 599 | 				copied++; | 
| Paul Mundt | 762c69e | 2008-12-16 18:55:26 +0900 | [diff] [blame] | 600 |  | 
 | 601 | 			dev_notice(port->dev, "frame error\n"); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 602 | 		} | 
 | 603 | 	} | 
 | 604 |  | 
| Paul Mundt | e108b2c | 2006-09-27 16:32:13 +0900 | [diff] [blame] | 605 | 	if (status & SCxSR_PER(port)) { | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 606 | 		/* parity error */ | 
| Paul Mundt | e108b2c | 2006-09-27 16:32:13 +0900 | [diff] [blame] | 607 | 		if (tty_insert_flip_char(tty, 0, TTY_PARITY)) | 
 | 608 | 			copied++; | 
| Paul Mundt | 762c69e | 2008-12-16 18:55:26 +0900 | [diff] [blame] | 609 |  | 
 | 610 | 		dev_notice(port->dev, "parity error"); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 611 | 	} | 
 | 612 |  | 
| Alan Cox | 33f0f88 | 2006-01-09 20:54:13 -0800 | [diff] [blame] | 613 | 	if (copied) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 614 | 		tty_flip_buffer_push(tty); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 615 |  | 
 | 616 | 	return copied; | 
 | 617 | } | 
 | 618 |  | 
| Paul Mundt | d830fa4 | 2008-12-16 19:29:38 +0900 | [diff] [blame] | 619 | static inline int sci_handle_fifo_overrun(struct uart_port *port) | 
 | 620 | { | 
| Alan Cox | ebd2c8f | 2009-09-19 13:13:28 -0700 | [diff] [blame] | 621 | 	struct tty_struct *tty = port->state->port.tty; | 
| Paul Mundt | d830fa4 | 2008-12-16 19:29:38 +0900 | [diff] [blame] | 622 | 	int copied = 0; | 
 | 623 |  | 
 | 624 | 	if (port->type != PORT_SCIF) | 
 | 625 | 		return 0; | 
 | 626 |  | 
 | 627 | 	if ((sci_in(port, SCLSR) & SCIF_ORER) != 0) { | 
 | 628 | 		sci_out(port, SCLSR, 0); | 
 | 629 |  | 
 | 630 | 		tty_insert_flip_char(tty, 0, TTY_OVERRUN); | 
 | 631 | 		tty_flip_buffer_push(tty); | 
 | 632 |  | 
 | 633 | 		dev_notice(port->dev, "overrun error\n"); | 
 | 634 | 		copied++; | 
 | 635 | 	} | 
 | 636 |  | 
 | 637 | 	return copied; | 
 | 638 | } | 
 | 639 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 640 | static inline int sci_handle_breaks(struct uart_port *port) | 
 | 641 | { | 
 | 642 | 	int copied = 0; | 
 | 643 | 	unsigned short status = sci_in(port, SCxSR); | 
| Alan Cox | ebd2c8f | 2009-09-19 13:13:28 -0700 | [diff] [blame] | 644 | 	struct tty_struct *tty = port->state->port.tty; | 
| Magnus Damm | a5660ad | 2009-01-21 15:14:38 +0000 | [diff] [blame] | 645 | 	struct sci_port *s = to_sci_port(port); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 646 |  | 
| Paul Mundt | 0b3d4ef | 2007-03-14 13:22:37 +0900 | [diff] [blame] | 647 | 	if (uart_handle_break(port)) | 
 | 648 | 		return 0; | 
 | 649 |  | 
| Paul Mundt | b7a76e4 | 2006-02-01 03:06:06 -0800 | [diff] [blame] | 650 | 	if (!s->break_flag && status & SCxSR_BRK(port)) { | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 651 | #if defined(CONFIG_CPU_SH3) | 
 | 652 | 		/* Debounce break */ | 
 | 653 | 		s->break_flag = 1; | 
 | 654 | #endif | 
 | 655 | 		/* Notify of BREAK */ | 
| Paul Mundt | e108b2c | 2006-09-27 16:32:13 +0900 | [diff] [blame] | 656 | 		if (tty_insert_flip_char(tty, 0, TTY_BREAK)) | 
| Alan Cox | 33f0f88 | 2006-01-09 20:54:13 -0800 | [diff] [blame] | 657 | 			copied++; | 
| Paul Mundt | 762c69e | 2008-12-16 18:55:26 +0900 | [diff] [blame] | 658 |  | 
 | 659 | 		dev_dbg(port->dev, "BREAK detected\n"); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 660 | 	} | 
 | 661 |  | 
| Alan Cox | 33f0f88 | 2006-01-09 20:54:13 -0800 | [diff] [blame] | 662 | 	if (copied) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 663 | 		tty_flip_buffer_push(tty); | 
| Paul Mundt | e108b2c | 2006-09-27 16:32:13 +0900 | [diff] [blame] | 664 |  | 
| Paul Mundt | d830fa4 | 2008-12-16 19:29:38 +0900 | [diff] [blame] | 665 | 	copied += sci_handle_fifo_overrun(port); | 
 | 666 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 667 | 	return copied; | 
 | 668 | } | 
 | 669 |  | 
| Guennadi Liakhovetski | 73a19e4 | 2010-03-02 11:39:15 +0900 | [diff] [blame] | 670 | static irqreturn_t sci_rx_interrupt(int irq, void *ptr) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 671 | { | 
| Guennadi Liakhovetski | 73a19e4 | 2010-03-02 11:39:15 +0900 | [diff] [blame] | 672 | #ifdef CONFIG_SERIAL_SH_SCI_DMA | 
 | 673 | 	struct uart_port *port = ptr; | 
 | 674 | 	struct sci_port *s = to_sci_port(port); | 
 | 675 |  | 
 | 676 | 	if (s->chan_rx) { | 
| Guennadi Liakhovetski | 73a19e4 | 2010-03-02 11:39:15 +0900 | [diff] [blame] | 677 | 		u16 scr = sci_in(port, SCSCR); | 
 | 678 | 		u16 ssr = sci_in(port, SCxSR); | 
 | 679 |  | 
 | 680 | 		/* Disable future Rx interrupts */ | 
| Guennadi Liakhovetski | 3089f38 | 2010-03-19 13:53:04 +0000 | [diff] [blame^] | 681 | 		if (port->type == PORT_SCIFA) { | 
 | 682 | 			disable_irq_nosync(irq); | 
 | 683 | 			scr |= 0x4000; | 
 | 684 | 		} else { | 
 | 685 | 			scr &= ~SCI_CTRL_FLAGS_RIE; | 
 | 686 | 		} | 
 | 687 | 		sci_out(port, SCSCR, scr); | 
| Guennadi Liakhovetski | 73a19e4 | 2010-03-02 11:39:15 +0900 | [diff] [blame] | 688 | 		/* Clear current interrupt */ | 
 | 689 | 		sci_out(port, SCxSR, ssr & ~(1 | SCxSR_RDxF(port))); | 
| Guennadi Liakhovetski | 3089f38 | 2010-03-19 13:53:04 +0000 | [diff] [blame^] | 690 | 		dev_dbg(port->dev, "Rx IRQ %lu: setup t-out in %u jiffies\n", | 
 | 691 | 			jiffies, s->rx_timeout); | 
 | 692 | 		mod_timer(&s->rx_timer, jiffies + s->rx_timeout); | 
| Guennadi Liakhovetski | 73a19e4 | 2010-03-02 11:39:15 +0900 | [diff] [blame] | 693 |  | 
 | 694 | 		return IRQ_HANDLED; | 
 | 695 | 	} | 
 | 696 | #endif | 
 | 697 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 698 | 	/* I think sci_receive_chars has to be called irrespective | 
 | 699 | 	 * of whether the I_IXOFF is set, otherwise, how is the interrupt | 
 | 700 | 	 * to be disabled? | 
 | 701 | 	 */ | 
| Guennadi Liakhovetski | 73a19e4 | 2010-03-02 11:39:15 +0900 | [diff] [blame] | 702 | 	sci_receive_chars(ptr); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 703 |  | 
 | 704 | 	return IRQ_HANDLED; | 
 | 705 | } | 
 | 706 |  | 
| David Howells | 7d12e78 | 2006-10-05 14:55:46 +0100 | [diff] [blame] | 707 | static irqreturn_t sci_tx_interrupt(int irq, void *ptr) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 708 | { | 
 | 709 | 	struct uart_port *port = ptr; | 
| Stuart Menefy | fd78a76 | 2009-07-29 23:01:24 +0900 | [diff] [blame] | 710 | 	unsigned long flags; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 711 |  | 
| Stuart Menefy | fd78a76 | 2009-07-29 23:01:24 +0900 | [diff] [blame] | 712 | 	spin_lock_irqsave(&port->lock, flags); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 713 | 	sci_transmit_chars(port); | 
| Stuart Menefy | fd78a76 | 2009-07-29 23:01:24 +0900 | [diff] [blame] | 714 | 	spin_unlock_irqrestore(&port->lock, flags); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 715 |  | 
 | 716 | 	return IRQ_HANDLED; | 
 | 717 | } | 
 | 718 |  | 
| David Howells | 7d12e78 | 2006-10-05 14:55:46 +0100 | [diff] [blame] | 719 | static irqreturn_t sci_er_interrupt(int irq, void *ptr) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 720 | { | 
 | 721 | 	struct uart_port *port = ptr; | 
 | 722 |  | 
 | 723 | 	/* Handle errors */ | 
 | 724 | 	if (port->type == PORT_SCI) { | 
 | 725 | 		if (sci_handle_errors(port)) { | 
 | 726 | 			/* discard character in rx buffer */ | 
 | 727 | 			sci_in(port, SCxSR); | 
 | 728 | 			sci_out(port, SCxSR, SCxSR_RDxF_CLEAR(port)); | 
 | 729 | 		} | 
 | 730 | 	} else { | 
| Paul Mundt | d830fa4 | 2008-12-16 19:29:38 +0900 | [diff] [blame] | 731 | 		sci_handle_fifo_overrun(port); | 
| David Howells | 7d12e78 | 2006-10-05 14:55:46 +0100 | [diff] [blame] | 732 | 		sci_rx_interrupt(irq, ptr); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 733 | 	} | 
 | 734 |  | 
 | 735 | 	sci_out(port, SCxSR, SCxSR_ERROR_CLEAR(port)); | 
 | 736 |  | 
 | 737 | 	/* Kick the transmission */ | 
| David Howells | 7d12e78 | 2006-10-05 14:55:46 +0100 | [diff] [blame] | 738 | 	sci_tx_interrupt(irq, ptr); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 739 |  | 
 | 740 | 	return IRQ_HANDLED; | 
 | 741 | } | 
 | 742 |  | 
| David Howells | 7d12e78 | 2006-10-05 14:55:46 +0100 | [diff] [blame] | 743 | static irqreturn_t sci_br_interrupt(int irq, void *ptr) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 744 | { | 
 | 745 | 	struct uart_port *port = ptr; | 
 | 746 |  | 
 | 747 | 	/* Handle BREAKs */ | 
 | 748 | 	sci_handle_breaks(port); | 
 | 749 | 	sci_out(port, SCxSR, SCxSR_BREAK_CLEAR(port)); | 
 | 750 |  | 
 | 751 | 	return IRQ_HANDLED; | 
 | 752 | } | 
 | 753 |  | 
| David Howells | 7d12e78 | 2006-10-05 14:55:46 +0100 | [diff] [blame] | 754 | static irqreturn_t sci_mpxed_interrupt(int irq, void *ptr) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 755 | { | 
| Magnus Damm | 44e18e9 | 2009-07-03 08:39:34 +0000 | [diff] [blame] | 756 | 	unsigned short ssr_status, scr_status, err_enabled; | 
| Michael Trimarchi | a8884e3 | 2008-10-31 16:10:23 +0900 | [diff] [blame] | 757 | 	struct uart_port *port = ptr; | 
| Guennadi Liakhovetski | 73a19e4 | 2010-03-02 11:39:15 +0900 | [diff] [blame] | 758 | 	struct sci_port *s = to_sci_port(port); | 
| Michael Trimarchi | a8884e3 | 2008-10-31 16:10:23 +0900 | [diff] [blame] | 759 | 	irqreturn_t ret = IRQ_NONE; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 760 |  | 
| Michael Trimarchi | e7c98dc | 2008-11-13 18:18:35 +0900 | [diff] [blame] | 761 | 	ssr_status = sci_in(port, SCxSR); | 
 | 762 | 	scr_status = sci_in(port, SCSCR); | 
| Magnus Damm | 44e18e9 | 2009-07-03 08:39:34 +0000 | [diff] [blame] | 763 | 	err_enabled = scr_status & (SCI_CTRL_FLAGS_REIE | SCI_CTRL_FLAGS_RIE); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 764 |  | 
 | 765 | 	/* Tx Interrupt */ | 
| Guennadi Liakhovetski | 73a19e4 | 2010-03-02 11:39:15 +0900 | [diff] [blame] | 766 | 	if ((ssr_status & SCxSR_TDxE(port)) && (scr_status & SCI_CTRL_FLAGS_TIE) && | 
 | 767 | 	    !s->chan_tx) | 
| Michael Trimarchi | a8884e3 | 2008-10-31 16:10:23 +0900 | [diff] [blame] | 768 | 		ret = sci_tx_interrupt(irq, ptr); | 
| Guennadi Liakhovetski | 73a19e4 | 2010-03-02 11:39:15 +0900 | [diff] [blame] | 769 | 	/* | 
 | 770 | 	 * Rx Interrupt: if we're using DMA, the DMA controller clears RDF / | 
 | 771 | 	 * DR flags | 
 | 772 | 	 */ | 
 | 773 | 	if (((ssr_status & SCxSR_RDxF(port)) || s->chan_rx) && | 
 | 774 | 	    (scr_status & SCI_CTRL_FLAGS_RIE)) | 
| Michael Trimarchi | a8884e3 | 2008-10-31 16:10:23 +0900 | [diff] [blame] | 775 | 		ret = sci_rx_interrupt(irq, ptr); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 776 | 	/* Error Interrupt */ | 
| SUGIOKA Toshinobu | dd4da3a | 2009-07-07 05:32:07 +0000 | [diff] [blame] | 777 | 	if ((ssr_status & SCxSR_ERRORS(port)) && err_enabled) | 
| Michael Trimarchi | a8884e3 | 2008-10-31 16:10:23 +0900 | [diff] [blame] | 778 | 		ret = sci_er_interrupt(irq, ptr); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 779 | 	/* Break Interrupt */ | 
| SUGIOKA Toshinobu | dd4da3a | 2009-07-07 05:32:07 +0000 | [diff] [blame] | 780 | 	if ((ssr_status & SCxSR_BRK(port)) && err_enabled) | 
| Michael Trimarchi | a8884e3 | 2008-10-31 16:10:23 +0900 | [diff] [blame] | 781 | 		ret = sci_br_interrupt(irq, ptr); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 782 |  | 
| Guennadi Liakhovetski | 73a19e4 | 2010-03-02 11:39:15 +0900 | [diff] [blame] | 783 | 	WARN_ONCE(ret == IRQ_NONE, | 
 | 784 | 		  "%s: %d IRQ %d, status %x, control %x\n", __func__, | 
 | 785 | 		  irq, port->line, ssr_status, scr_status); | 
 | 786 |  | 
| Michael Trimarchi | a8884e3 | 2008-10-31 16:10:23 +0900 | [diff] [blame] | 787 | 	return ret; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 788 | } | 
 | 789 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 790 | /* | 
 | 791 |  * Here we define a transistion notifier so that we can update all of our | 
 | 792 |  * ports' baud rate when the peripheral clock changes. | 
 | 793 |  */ | 
| Paul Mundt | e108b2c | 2006-09-27 16:32:13 +0900 | [diff] [blame] | 794 | static int sci_notifier(struct notifier_block *self, | 
 | 795 | 			unsigned long phase, void *p) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 796 | { | 
| Magnus Damm | e552de2 | 2009-01-21 15:13:42 +0000 | [diff] [blame] | 797 | 	struct sh_sci_priv *priv = container_of(self, | 
 | 798 | 						struct sh_sci_priv, clk_nb); | 
 | 799 | 	struct sci_port *sci_port; | 
 | 800 | 	unsigned long flags; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 801 |  | 
 | 802 | 	if ((phase == CPUFREQ_POSTCHANGE) || | 
| Magnus Damm | e552de2 | 2009-01-21 15:13:42 +0000 | [diff] [blame] | 803 | 	    (phase == CPUFREQ_RESUMECHANGE)) { | 
 | 804 | 		spin_lock_irqsave(&priv->lock, flags); | 
 | 805 | 		list_for_each_entry(sci_port, &priv->ports, node) | 
| Magnus Damm | 501b825 | 2009-01-21 15:14:30 +0000 | [diff] [blame] | 806 | 			sci_port->port.uartclk = clk_get_rate(sci_port->dclk); | 
| Magnus Damm | e552de2 | 2009-01-21 15:13:42 +0000 | [diff] [blame] | 807 | 		spin_unlock_irqrestore(&priv->lock, flags); | 
 | 808 | 	} | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 809 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 810 | 	return NOTIFY_OK; | 
 | 811 | } | 
| Magnus Damm | 501b825 | 2009-01-21 15:14:30 +0000 | [diff] [blame] | 812 |  | 
 | 813 | static void sci_clk_enable(struct uart_port *port) | 
 | 814 | { | 
 | 815 | 	struct sci_port *sci_port = to_sci_port(port); | 
 | 816 |  | 
 | 817 | 	clk_enable(sci_port->dclk); | 
 | 818 | 	sci_port->port.uartclk = clk_get_rate(sci_port->dclk); | 
 | 819 |  | 
 | 820 | 	if (sci_port->iclk) | 
 | 821 | 		clk_enable(sci_port->iclk); | 
 | 822 | } | 
 | 823 |  | 
 | 824 | static void sci_clk_disable(struct uart_port *port) | 
 | 825 | { | 
 | 826 | 	struct sci_port *sci_port = to_sci_port(port); | 
 | 827 |  | 
 | 828 | 	if (sci_port->iclk) | 
 | 829 | 		clk_disable(sci_port->iclk); | 
 | 830 |  | 
 | 831 | 	clk_disable(sci_port->dclk); | 
 | 832 | } | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 833 |  | 
 | 834 | static int sci_request_irq(struct sci_port *port) | 
 | 835 | { | 
 | 836 | 	int i; | 
| David Howells | 7d12e78 | 2006-10-05 14:55:46 +0100 | [diff] [blame] | 837 | 	irqreturn_t (*handlers[4])(int irq, void *ptr) = { | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 838 | 		sci_er_interrupt, sci_rx_interrupt, sci_tx_interrupt, | 
 | 839 | 		sci_br_interrupt, | 
 | 840 | 	}; | 
 | 841 | 	const char *desc[] = { "SCI Receive Error", "SCI Receive Data Full", | 
 | 842 | 			       "SCI Transmit Data Empty", "SCI Break" }; | 
 | 843 |  | 
 | 844 | 	if (port->irqs[0] == port->irqs[1]) { | 
| Paul Mundt | 762c69e | 2008-12-16 18:55:26 +0900 | [diff] [blame] | 845 | 		if (unlikely(!port->irqs[0])) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 846 | 			return -ENODEV; | 
| Paul Mundt | e108b2c | 2006-09-27 16:32:13 +0900 | [diff] [blame] | 847 |  | 
 | 848 | 		if (request_irq(port->irqs[0], sci_mpxed_interrupt, | 
| Paul Mundt | 35f3c51 | 2006-10-06 15:31:16 +0900 | [diff] [blame] | 849 | 				IRQF_DISABLED, "sci", port)) { | 
| Paul Mundt | 762c69e | 2008-12-16 18:55:26 +0900 | [diff] [blame] | 850 | 			dev_err(port->port.dev, "Can't allocate IRQ\n"); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 851 | 			return -ENODEV; | 
 | 852 | 		} | 
 | 853 | 	} else { | 
 | 854 | 		for (i = 0; i < ARRAY_SIZE(handlers); i++) { | 
| Paul Mundt | 762c69e | 2008-12-16 18:55:26 +0900 | [diff] [blame] | 855 | 			if (unlikely(!port->irqs[i])) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 856 | 				continue; | 
| Paul Mundt | 762c69e | 2008-12-16 18:55:26 +0900 | [diff] [blame] | 857 |  | 
| Paul Mundt | e108b2c | 2006-09-27 16:32:13 +0900 | [diff] [blame] | 858 | 			if (request_irq(port->irqs[i], handlers[i], | 
| Paul Mundt | 35f3c51 | 2006-10-06 15:31:16 +0900 | [diff] [blame] | 859 | 					IRQF_DISABLED, desc[i], port)) { | 
| Paul Mundt | 762c69e | 2008-12-16 18:55:26 +0900 | [diff] [blame] | 860 | 				dev_err(port->port.dev, "Can't allocate IRQ\n"); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 861 | 				return -ENODEV; | 
 | 862 | 			} | 
 | 863 | 		} | 
 | 864 | 	} | 
 | 865 |  | 
 | 866 | 	return 0; | 
 | 867 | } | 
 | 868 |  | 
 | 869 | static void sci_free_irq(struct sci_port *port) | 
 | 870 | { | 
 | 871 | 	int i; | 
 | 872 |  | 
| Paul Mundt | 762c69e | 2008-12-16 18:55:26 +0900 | [diff] [blame] | 873 | 	if (port->irqs[0] == port->irqs[1]) | 
 | 874 | 		free_irq(port->irqs[0], port); | 
 | 875 | 	else { | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 876 | 		for (i = 0; i < ARRAY_SIZE(port->irqs); i++) { | 
 | 877 | 			if (!port->irqs[i]) | 
 | 878 | 				continue; | 
 | 879 |  | 
 | 880 | 			free_irq(port->irqs[i], port); | 
 | 881 | 		} | 
 | 882 | 	} | 
 | 883 | } | 
 | 884 |  | 
 | 885 | static unsigned int sci_tx_empty(struct uart_port *port) | 
 | 886 | { | 
| Guennadi Liakhovetski | b151680 | 2009-12-01 09:54:46 +0000 | [diff] [blame] | 887 | 	unsigned short status = sci_in(port, SCxSR); | 
| Guennadi Liakhovetski | 73a19e4 | 2010-03-02 11:39:15 +0900 | [diff] [blame] | 888 | 	unsigned short in_tx_fifo = scif_txfill(port); | 
 | 889 |  | 
 | 890 | 	return (status & SCxSR_TEND(port)) && !in_tx_fifo ? TIOCSER_TEMT : 0; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 891 | } | 
 | 892 |  | 
 | 893 | static void sci_set_mctrl(struct uart_port *port, unsigned int mctrl) | 
 | 894 | { | 
 | 895 | 	/* This routine is used for seting signals of: DTR, DCD, CTS/RTS */ | 
 | 896 | 	/* We use SCIF's hardware for CTS/RTS, so don't need any for that. */ | 
 | 897 | 	/* If you have signals for DTR and DCD, please implement here. */ | 
 | 898 | } | 
 | 899 |  | 
 | 900 | static unsigned int sci_get_mctrl(struct uart_port *port) | 
 | 901 | { | 
| Guennadi Liakhovetski | 73a19e4 | 2010-03-02 11:39:15 +0900 | [diff] [blame] | 902 | 	/* This routine is used for getting signals of: DTR, DCD, DSR, RI, | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 903 | 	   and CTS/RTS */ | 
 | 904 |  | 
 | 905 | 	return TIOCM_DTR | TIOCM_RTS | TIOCM_DSR; | 
 | 906 | } | 
 | 907 |  | 
| Guennadi Liakhovetski | 73a19e4 | 2010-03-02 11:39:15 +0900 | [diff] [blame] | 908 | #ifdef CONFIG_SERIAL_SH_SCI_DMA | 
 | 909 | static void sci_dma_tx_complete(void *arg) | 
 | 910 | { | 
 | 911 | 	struct sci_port *s = arg; | 
 | 912 | 	struct uart_port *port = &s->port; | 
 | 913 | 	struct circ_buf *xmit = &port->state->xmit; | 
 | 914 | 	unsigned long flags; | 
 | 915 |  | 
 | 916 | 	dev_dbg(port->dev, "%s(%d)\n", __func__, port->line); | 
 | 917 |  | 
 | 918 | 	spin_lock_irqsave(&port->lock, flags); | 
 | 919 |  | 
| Magnus Damm | f354a38 | 2010-03-19 04:47:01 +0000 | [diff] [blame] | 920 | 	xmit->tail += sg_dma_len(&s->sg_tx); | 
| Guennadi Liakhovetski | 73a19e4 | 2010-03-02 11:39:15 +0900 | [diff] [blame] | 921 | 	xmit->tail &= UART_XMIT_SIZE - 1; | 
 | 922 |  | 
| Magnus Damm | f354a38 | 2010-03-19 04:47:01 +0000 | [diff] [blame] | 923 | 	port->icount.tx += sg_dma_len(&s->sg_tx); | 
| Guennadi Liakhovetski | 73a19e4 | 2010-03-02 11:39:15 +0900 | [diff] [blame] | 924 |  | 
 | 925 | 	async_tx_ack(s->desc_tx); | 
 | 926 | 	s->cookie_tx = -EINVAL; | 
 | 927 | 	s->desc_tx = NULL; | 
 | 928 |  | 
| Guennadi Liakhovetski | 73a19e4 | 2010-03-02 11:39:15 +0900 | [diff] [blame] | 929 | 	if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) | 
 | 930 | 		uart_write_wakeup(port); | 
 | 931 |  | 
| Guennadi Liakhovetski | 3089f38 | 2010-03-19 13:53:04 +0000 | [diff] [blame^] | 932 | 	if (!uart_circ_empty(xmit)) { | 
| Guennadi Liakhovetski | 73a19e4 | 2010-03-02 11:39:15 +0900 | [diff] [blame] | 933 | 		schedule_work(&s->work_tx); | 
| Guennadi Liakhovetski | 3089f38 | 2010-03-19 13:53:04 +0000 | [diff] [blame^] | 934 | 	} else if (port->type == PORT_SCIFA) { | 
 | 935 | 		u16 ctrl = sci_in(port, SCSCR); | 
 | 936 | 		sci_out(port, SCSCR, ctrl & ~SCI_CTRL_FLAGS_TIE); | 
 | 937 | 	} | 
 | 938 |  | 
 | 939 | 	spin_unlock_irqrestore(&port->lock, flags); | 
| Guennadi Liakhovetski | 73a19e4 | 2010-03-02 11:39:15 +0900 | [diff] [blame] | 940 | } | 
 | 941 |  | 
 | 942 | /* Locking: called with port lock held */ | 
 | 943 | static int sci_dma_rx_push(struct sci_port *s, struct tty_struct *tty, | 
 | 944 | 			   size_t count) | 
 | 945 | { | 
 | 946 | 	struct uart_port *port = &s->port; | 
 | 947 | 	int i, active, room; | 
 | 948 |  | 
 | 949 | 	room = tty_buffer_request_room(tty, count); | 
 | 950 |  | 
 | 951 | 	if (s->active_rx == s->cookie_rx[0]) { | 
 | 952 | 		active = 0; | 
 | 953 | 	} else if (s->active_rx == s->cookie_rx[1]) { | 
 | 954 | 		active = 1; | 
 | 955 | 	} else { | 
 | 956 | 		dev_err(port->dev, "cookie %d not found!\n", s->active_rx); | 
 | 957 | 		return 0; | 
 | 958 | 	} | 
 | 959 |  | 
 | 960 | 	if (room < count) | 
 | 961 | 		dev_warn(port->dev, "Rx overrun: dropping %u bytes\n", | 
 | 962 | 			 count - room); | 
 | 963 | 	if (!room) | 
 | 964 | 		return room; | 
 | 965 |  | 
 | 966 | 	for (i = 0; i < room; i++) | 
 | 967 | 		tty_insert_flip_char(tty, ((u8 *)sg_virt(&s->sg_rx[active]))[i], | 
 | 968 | 				     TTY_NORMAL); | 
 | 969 |  | 
 | 970 | 	port->icount.rx += room; | 
 | 971 |  | 
 | 972 | 	return room; | 
 | 973 | } | 
 | 974 |  | 
 | 975 | static void sci_dma_rx_complete(void *arg) | 
 | 976 | { | 
 | 977 | 	struct sci_port *s = arg; | 
 | 978 | 	struct uart_port *port = &s->port; | 
 | 979 | 	struct tty_struct *tty = port->state->port.tty; | 
 | 980 | 	unsigned long flags; | 
 | 981 | 	int count; | 
 | 982 |  | 
| Guennadi Liakhovetski | 3089f38 | 2010-03-19 13:53:04 +0000 | [diff] [blame^] | 983 | 	dev_dbg(port->dev, "%s(%d) active #%d\n", __func__, port->line, s->active_rx); | 
| Guennadi Liakhovetski | 73a19e4 | 2010-03-02 11:39:15 +0900 | [diff] [blame] | 984 |  | 
 | 985 | 	spin_lock_irqsave(&port->lock, flags); | 
 | 986 |  | 
 | 987 | 	count = sci_dma_rx_push(s, tty, s->buf_len_rx); | 
 | 988 |  | 
| Guennadi Liakhovetski | 3089f38 | 2010-03-19 13:53:04 +0000 | [diff] [blame^] | 989 | 	mod_timer(&s->rx_timer, jiffies + s->rx_timeout); | 
| Guennadi Liakhovetski | 73a19e4 | 2010-03-02 11:39:15 +0900 | [diff] [blame] | 990 |  | 
 | 991 | 	spin_unlock_irqrestore(&port->lock, flags); | 
 | 992 |  | 
 | 993 | 	if (count) | 
 | 994 | 		tty_flip_buffer_push(tty); | 
 | 995 |  | 
 | 996 | 	schedule_work(&s->work_rx); | 
 | 997 | } | 
 | 998 |  | 
 | 999 | static void sci_start_rx(struct uart_port *port); | 
 | 1000 | static void sci_start_tx(struct uart_port *port); | 
 | 1001 |  | 
 | 1002 | static void sci_rx_dma_release(struct sci_port *s, bool enable_pio) | 
 | 1003 | { | 
 | 1004 | 	struct dma_chan *chan = s->chan_rx; | 
 | 1005 | 	struct uart_port *port = &s->port; | 
| Guennadi Liakhovetski | 73a19e4 | 2010-03-02 11:39:15 +0900 | [diff] [blame] | 1006 |  | 
 | 1007 | 	s->chan_rx = NULL; | 
 | 1008 | 	s->cookie_rx[0] = s->cookie_rx[1] = -EINVAL; | 
 | 1009 | 	dma_release_channel(chan); | 
 | 1010 | 	dma_free_coherent(port->dev, s->buf_len_rx * 2, | 
 | 1011 | 			  sg_virt(&s->sg_rx[0]), sg_dma_address(&s->sg_rx[0])); | 
 | 1012 | 	if (enable_pio) | 
 | 1013 | 		sci_start_rx(port); | 
 | 1014 | } | 
 | 1015 |  | 
 | 1016 | static void sci_tx_dma_release(struct sci_port *s, bool enable_pio) | 
 | 1017 | { | 
 | 1018 | 	struct dma_chan *chan = s->chan_tx; | 
 | 1019 | 	struct uart_port *port = &s->port; | 
| Guennadi Liakhovetski | 73a19e4 | 2010-03-02 11:39:15 +0900 | [diff] [blame] | 1020 |  | 
 | 1021 | 	s->chan_tx = NULL; | 
 | 1022 | 	s->cookie_tx = -EINVAL; | 
 | 1023 | 	dma_release_channel(chan); | 
 | 1024 | 	if (enable_pio) | 
 | 1025 | 		sci_start_tx(port); | 
 | 1026 | } | 
 | 1027 |  | 
 | 1028 | static void sci_submit_rx(struct sci_port *s) | 
 | 1029 | { | 
 | 1030 | 	struct dma_chan *chan = s->chan_rx; | 
 | 1031 | 	int i; | 
 | 1032 |  | 
 | 1033 | 	for (i = 0; i < 2; i++) { | 
 | 1034 | 		struct scatterlist *sg = &s->sg_rx[i]; | 
 | 1035 | 		struct dma_async_tx_descriptor *desc; | 
 | 1036 |  | 
 | 1037 | 		desc = chan->device->device_prep_slave_sg(chan, | 
 | 1038 | 			sg, 1, DMA_FROM_DEVICE, DMA_PREP_INTERRUPT); | 
 | 1039 |  | 
 | 1040 | 		if (desc) { | 
 | 1041 | 			s->desc_rx[i] = desc; | 
 | 1042 | 			desc->callback = sci_dma_rx_complete; | 
 | 1043 | 			desc->callback_param = s; | 
 | 1044 | 			s->cookie_rx[i] = desc->tx_submit(desc); | 
 | 1045 | 		} | 
 | 1046 |  | 
 | 1047 | 		if (!desc || s->cookie_rx[i] < 0) { | 
 | 1048 | 			if (i) { | 
 | 1049 | 				async_tx_ack(s->desc_rx[0]); | 
 | 1050 | 				s->cookie_rx[0] = -EINVAL; | 
 | 1051 | 			} | 
 | 1052 | 			if (desc) { | 
 | 1053 | 				async_tx_ack(desc); | 
 | 1054 | 				s->cookie_rx[i] = -EINVAL; | 
 | 1055 | 			} | 
 | 1056 | 			dev_warn(s->port.dev, | 
 | 1057 | 				 "failed to re-start DMA, using PIO\n"); | 
 | 1058 | 			sci_rx_dma_release(s, true); | 
 | 1059 | 			return; | 
 | 1060 | 		} | 
| Guennadi Liakhovetski | 3089f38 | 2010-03-19 13:53:04 +0000 | [diff] [blame^] | 1061 | 		dev_dbg(s->port.dev, "%s(): cookie %d to #%d\n", __func__, | 
 | 1062 | 			s->cookie_rx[i], i); | 
| Guennadi Liakhovetski | 73a19e4 | 2010-03-02 11:39:15 +0900 | [diff] [blame] | 1063 | 	} | 
 | 1064 |  | 
 | 1065 | 	s->active_rx = s->cookie_rx[0]; | 
 | 1066 |  | 
 | 1067 | 	dma_async_issue_pending(chan); | 
 | 1068 | } | 
 | 1069 |  | 
 | 1070 | static void work_fn_rx(struct work_struct *work) | 
 | 1071 | { | 
 | 1072 | 	struct sci_port *s = container_of(work, struct sci_port, work_rx); | 
 | 1073 | 	struct uart_port *port = &s->port; | 
 | 1074 | 	struct dma_async_tx_descriptor *desc; | 
 | 1075 | 	int new; | 
 | 1076 |  | 
 | 1077 | 	if (s->active_rx == s->cookie_rx[0]) { | 
 | 1078 | 		new = 0; | 
 | 1079 | 	} else if (s->active_rx == s->cookie_rx[1]) { | 
 | 1080 | 		new = 1; | 
 | 1081 | 	} else { | 
 | 1082 | 		dev_err(port->dev, "cookie %d not found!\n", s->active_rx); | 
 | 1083 | 		return; | 
 | 1084 | 	} | 
 | 1085 | 	desc = s->desc_rx[new]; | 
 | 1086 |  | 
 | 1087 | 	if (dma_async_is_tx_complete(s->chan_rx, s->active_rx, NULL, NULL) != | 
 | 1088 | 	    DMA_SUCCESS) { | 
 | 1089 | 		/* Handle incomplete DMA receive */ | 
 | 1090 | 		struct tty_struct *tty = port->state->port.tty; | 
 | 1091 | 		struct dma_chan *chan = s->chan_rx; | 
 | 1092 | 		struct sh_desc *sh_desc = container_of(desc, struct sh_desc, | 
 | 1093 | 						       async_tx); | 
 | 1094 | 		unsigned long flags; | 
 | 1095 | 		int count; | 
 | 1096 |  | 
 | 1097 | 		chan->device->device_terminate_all(chan); | 
 | 1098 | 		dev_dbg(port->dev, "Read %u bytes with cookie %d\n", | 
 | 1099 | 			sh_desc->partial, sh_desc->cookie); | 
 | 1100 |  | 
 | 1101 | 		spin_lock_irqsave(&port->lock, flags); | 
 | 1102 | 		count = sci_dma_rx_push(s, tty, sh_desc->partial); | 
 | 1103 | 		spin_unlock_irqrestore(&port->lock, flags); | 
 | 1104 |  | 
 | 1105 | 		if (count) | 
 | 1106 | 			tty_flip_buffer_push(tty); | 
 | 1107 |  | 
 | 1108 | 		sci_submit_rx(s); | 
 | 1109 |  | 
 | 1110 | 		return; | 
 | 1111 | 	} | 
 | 1112 |  | 
 | 1113 | 	s->cookie_rx[new] = desc->tx_submit(desc); | 
 | 1114 | 	if (s->cookie_rx[new] < 0) { | 
 | 1115 | 		dev_warn(port->dev, "Failed submitting Rx DMA descriptor\n"); | 
 | 1116 | 		sci_rx_dma_release(s, true); | 
 | 1117 | 		return; | 
 | 1118 | 	} | 
 | 1119 |  | 
| Guennadi Liakhovetski | 73a19e4 | 2010-03-02 11:39:15 +0900 | [diff] [blame] | 1120 | 	s->active_rx = s->cookie_rx[!new]; | 
| Guennadi Liakhovetski | 3089f38 | 2010-03-19 13:53:04 +0000 | [diff] [blame^] | 1121 |  | 
 | 1122 | 	dev_dbg(port->dev, "%s: cookie %d #%d, new active #%d\n", __func__, | 
 | 1123 | 		s->cookie_rx[new], new, s->active_rx); | 
| Guennadi Liakhovetski | 73a19e4 | 2010-03-02 11:39:15 +0900 | [diff] [blame] | 1124 | } | 
 | 1125 |  | 
 | 1126 | static void work_fn_tx(struct work_struct *work) | 
 | 1127 | { | 
 | 1128 | 	struct sci_port *s = container_of(work, struct sci_port, work_tx); | 
 | 1129 | 	struct dma_async_tx_descriptor *desc; | 
 | 1130 | 	struct dma_chan *chan = s->chan_tx; | 
 | 1131 | 	struct uart_port *port = &s->port; | 
 | 1132 | 	struct circ_buf *xmit = &port->state->xmit; | 
 | 1133 | 	struct scatterlist *sg = &s->sg_tx; | 
 | 1134 |  | 
 | 1135 | 	/* | 
 | 1136 | 	 * DMA is idle now. | 
 | 1137 | 	 * Port xmit buffer is already mapped, and it is one page... Just adjust | 
 | 1138 | 	 * offsets and lengths. Since it is a circular buffer, we have to | 
 | 1139 | 	 * transmit till the end, and then the rest. Take the port lock to get a | 
 | 1140 | 	 * consistent xmit buffer state. | 
 | 1141 | 	 */ | 
 | 1142 | 	spin_lock_irq(&port->lock); | 
 | 1143 | 	sg->offset = xmit->tail & (UART_XMIT_SIZE - 1); | 
| Magnus Damm | f354a38 | 2010-03-19 04:47:01 +0000 | [diff] [blame] | 1144 | 	sg_dma_address(sg) = (sg_dma_address(sg) & ~(UART_XMIT_SIZE - 1)) + | 
| Guennadi Liakhovetski | 73a19e4 | 2010-03-02 11:39:15 +0900 | [diff] [blame] | 1145 | 		sg->offset; | 
| Magnus Damm | f354a38 | 2010-03-19 04:47:01 +0000 | [diff] [blame] | 1146 | 	sg_dma_len(sg) = min((int)CIRC_CNT(xmit->head, xmit->tail, UART_XMIT_SIZE), | 
| Guennadi Liakhovetski | 73a19e4 | 2010-03-02 11:39:15 +0900 | [diff] [blame] | 1147 | 		CIRC_CNT_TO_END(xmit->head, xmit->tail, UART_XMIT_SIZE)); | 
| Guennadi Liakhovetski | 73a19e4 | 2010-03-02 11:39:15 +0900 | [diff] [blame] | 1148 | 	spin_unlock_irq(&port->lock); | 
 | 1149 |  | 
| Magnus Damm | f354a38 | 2010-03-19 04:47:01 +0000 | [diff] [blame] | 1150 | 	BUG_ON(!sg_dma_len(sg)); | 
| Guennadi Liakhovetski | 73a19e4 | 2010-03-02 11:39:15 +0900 | [diff] [blame] | 1151 |  | 
 | 1152 | 	desc = chan->device->device_prep_slave_sg(chan, | 
 | 1153 | 			sg, s->sg_len_tx, DMA_TO_DEVICE, | 
 | 1154 | 			DMA_PREP_INTERRUPT | DMA_CTRL_ACK); | 
 | 1155 | 	if (!desc) { | 
 | 1156 | 		/* switch to PIO */ | 
 | 1157 | 		sci_tx_dma_release(s, true); | 
 | 1158 | 		return; | 
 | 1159 | 	} | 
 | 1160 |  | 
 | 1161 | 	dma_sync_sg_for_device(port->dev, sg, 1, DMA_TO_DEVICE); | 
 | 1162 |  | 
 | 1163 | 	spin_lock_irq(&port->lock); | 
 | 1164 | 	s->desc_tx = desc; | 
 | 1165 | 	desc->callback = sci_dma_tx_complete; | 
 | 1166 | 	desc->callback_param = s; | 
 | 1167 | 	spin_unlock_irq(&port->lock); | 
 | 1168 | 	s->cookie_tx = desc->tx_submit(desc); | 
 | 1169 | 	if (s->cookie_tx < 0) { | 
 | 1170 | 		dev_warn(port->dev, "Failed submitting Tx DMA descriptor\n"); | 
 | 1171 | 		/* switch to PIO */ | 
 | 1172 | 		sci_tx_dma_release(s, true); | 
 | 1173 | 		return; | 
 | 1174 | 	} | 
 | 1175 |  | 
 | 1176 | 	dev_dbg(port->dev, "%s: %p: %d...%d, cookie %d\n", __func__, | 
 | 1177 | 		xmit->buf, xmit->tail, xmit->head, s->cookie_tx); | 
 | 1178 |  | 
 | 1179 | 	dma_async_issue_pending(chan); | 
 | 1180 | } | 
 | 1181 | #endif | 
 | 1182 |  | 
| Russell King | b129a8c | 2005-08-31 10:12:14 +0100 | [diff] [blame] | 1183 | static void sci_start_tx(struct uart_port *port) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1184 | { | 
| Guennadi Liakhovetski | 3089f38 | 2010-03-19 13:53:04 +0000 | [diff] [blame^] | 1185 | 	struct sci_port *s = to_sci_port(port); | 
| Paul Mundt | e108b2c | 2006-09-27 16:32:13 +0900 | [diff] [blame] | 1186 | 	unsigned short ctrl; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1187 |  | 
| Guennadi Liakhovetski | 73a19e4 | 2010-03-02 11:39:15 +0900 | [diff] [blame] | 1188 | #ifdef CONFIG_SERIAL_SH_SCI_DMA | 
| Guennadi Liakhovetski | 3089f38 | 2010-03-19 13:53:04 +0000 | [diff] [blame^] | 1189 | 	if (port->type == PORT_SCIFA) { | 
 | 1190 | 		u16 new, scr = sci_in(port, SCSCR); | 
 | 1191 | 		if (s->chan_tx) | 
 | 1192 | 			new = scr | 0x8000; | 
 | 1193 | 		else | 
 | 1194 | 			new = scr & ~0x8000; | 
 | 1195 | 		if (new != scr) | 
 | 1196 | 			sci_out(port, SCSCR, new); | 
| Guennadi Liakhovetski | 73a19e4 | 2010-03-02 11:39:15 +0900 | [diff] [blame] | 1197 | 	} | 
| Guennadi Liakhovetski | 3089f38 | 2010-03-19 13:53:04 +0000 | [diff] [blame^] | 1198 | 	if (s->chan_tx && !uart_circ_empty(&s->port.state->xmit) && | 
 | 1199 | 	    s->cookie_tx < 0) | 
 | 1200 | 		schedule_work(&s->work_tx); | 
| Guennadi Liakhovetski | 73a19e4 | 2010-03-02 11:39:15 +0900 | [diff] [blame] | 1201 | #endif | 
| Guennadi Liakhovetski | 3089f38 | 2010-03-19 13:53:04 +0000 | [diff] [blame^] | 1202 | 	if (!s->chan_tx || port->type == PORT_SCIFA) { | 
 | 1203 | 		/* Set TIE (Transmit Interrupt Enable) bit in SCSCR */ | 
 | 1204 | 		ctrl = sci_in(port, SCSCR); | 
 | 1205 | 		sci_out(port, SCSCR, ctrl | SCI_CTRL_FLAGS_TIE); | 
 | 1206 | 	} | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1207 | } | 
 | 1208 |  | 
| Russell King | b129a8c | 2005-08-31 10:12:14 +0100 | [diff] [blame] | 1209 | static void sci_stop_tx(struct uart_port *port) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1210 | { | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1211 | 	unsigned short ctrl; | 
 | 1212 |  | 
 | 1213 | 	/* Clear TIE (Transmit Interrupt Enable) bit in SCSCR */ | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1214 | 	ctrl = sci_in(port, SCSCR); | 
| Guennadi Liakhovetski | 3089f38 | 2010-03-19 13:53:04 +0000 | [diff] [blame^] | 1215 | 	if (port->type == PORT_SCIFA) | 
 | 1216 | 		ctrl &= ~0x8000; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1217 | 	ctrl &= ~SCI_CTRL_FLAGS_TIE; | 
 | 1218 | 	sci_out(port, SCSCR, ctrl); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1219 | } | 
 | 1220 |  | 
| Guennadi Liakhovetski | 73a19e4 | 2010-03-02 11:39:15 +0900 | [diff] [blame] | 1221 | static void sci_start_rx(struct uart_port *port) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1222 | { | 
| Guennadi Liakhovetski | 73a19e4 | 2010-03-02 11:39:15 +0900 | [diff] [blame] | 1223 | 	unsigned short ctrl = SCI_CTRL_FLAGS_RIE | SCI_CTRL_FLAGS_REIE; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1224 |  | 
 | 1225 | 	/* Set RIE (Receive Interrupt Enable) bit in SCSCR */ | 
| Guennadi Liakhovetski | 73a19e4 | 2010-03-02 11:39:15 +0900 | [diff] [blame] | 1226 | 	ctrl |= sci_in(port, SCSCR); | 
| Guennadi Liakhovetski | 3089f38 | 2010-03-19 13:53:04 +0000 | [diff] [blame^] | 1227 | 	if (port->type == PORT_SCIFA) | 
 | 1228 | 		ctrl &= ~0x4000; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1229 | 	sci_out(port, SCSCR, ctrl); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1230 | } | 
 | 1231 |  | 
 | 1232 | static void sci_stop_rx(struct uart_port *port) | 
 | 1233 | { | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1234 | 	unsigned short ctrl; | 
 | 1235 |  | 
 | 1236 | 	/* Clear RIE (Receive Interrupt Enable) bit in SCSCR */ | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1237 | 	ctrl = sci_in(port, SCSCR); | 
| Guennadi Liakhovetski | 3089f38 | 2010-03-19 13:53:04 +0000 | [diff] [blame^] | 1238 | 	if (port->type == PORT_SCIFA) | 
 | 1239 | 		ctrl &= ~0x4000; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1240 | 	ctrl &= ~(SCI_CTRL_FLAGS_RIE | SCI_CTRL_FLAGS_REIE); | 
 | 1241 | 	sci_out(port, SCSCR, ctrl); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1242 | } | 
 | 1243 |  | 
 | 1244 | static void sci_enable_ms(struct uart_port *port) | 
 | 1245 | { | 
 | 1246 | 	/* Nothing here yet .. */ | 
 | 1247 | } | 
 | 1248 |  | 
 | 1249 | static void sci_break_ctl(struct uart_port *port, int break_state) | 
 | 1250 | { | 
 | 1251 | 	/* Nothing here yet .. */ | 
 | 1252 | } | 
 | 1253 |  | 
| Guennadi Liakhovetski | 73a19e4 | 2010-03-02 11:39:15 +0900 | [diff] [blame] | 1254 | #ifdef CONFIG_SERIAL_SH_SCI_DMA | 
 | 1255 | static bool filter(struct dma_chan *chan, void *slave) | 
 | 1256 | { | 
 | 1257 | 	struct sh_dmae_slave *param = slave; | 
 | 1258 |  | 
 | 1259 | 	dev_dbg(chan->device->dev, "%s: slave ID %d\n", __func__, | 
 | 1260 | 		param->slave_id); | 
 | 1261 |  | 
 | 1262 | 	if (param->dma_dev == chan->device->dev) { | 
 | 1263 | 		chan->private = param; | 
 | 1264 | 		return true; | 
 | 1265 | 	} else { | 
 | 1266 | 		return false; | 
 | 1267 | 	} | 
 | 1268 | } | 
 | 1269 |  | 
 | 1270 | static void rx_timer_fn(unsigned long arg) | 
 | 1271 | { | 
 | 1272 | 	struct sci_port *s = (struct sci_port *)arg; | 
 | 1273 | 	struct uart_port *port = &s->port; | 
| Guennadi Liakhovetski | 73a19e4 | 2010-03-02 11:39:15 +0900 | [diff] [blame] | 1274 | 	u16 scr = sci_in(port, SCSCR); | 
| Guennadi Liakhovetski | 3089f38 | 2010-03-19 13:53:04 +0000 | [diff] [blame^] | 1275 |  | 
 | 1276 | 	if (port->type == PORT_SCIFA) { | 
 | 1277 | 		scr &= ~0x4000; | 
 | 1278 | 		enable_irq(s->irqs[1]); | 
 | 1279 | 	} | 
| Guennadi Liakhovetski | 73a19e4 | 2010-03-02 11:39:15 +0900 | [diff] [blame] | 1280 | 	sci_out(port, SCSCR, scr | SCI_CTRL_FLAGS_RIE); | 
 | 1281 | 	dev_dbg(port->dev, "DMA Rx timed out\n"); | 
 | 1282 | 	schedule_work(&s->work_rx); | 
 | 1283 | } | 
 | 1284 |  | 
 | 1285 | static void sci_request_dma(struct uart_port *port) | 
 | 1286 | { | 
 | 1287 | 	struct sci_port *s = to_sci_port(port); | 
 | 1288 | 	struct sh_dmae_slave *param; | 
 | 1289 | 	struct dma_chan *chan; | 
 | 1290 | 	dma_cap_mask_t mask; | 
 | 1291 | 	int nent; | 
 | 1292 |  | 
 | 1293 | 	dev_dbg(port->dev, "%s: port %d DMA %p\n", __func__, | 
 | 1294 | 		port->line, s->dma_dev); | 
 | 1295 |  | 
 | 1296 | 	if (!s->dma_dev) | 
 | 1297 | 		return; | 
 | 1298 |  | 
 | 1299 | 	dma_cap_zero(mask); | 
 | 1300 | 	dma_cap_set(DMA_SLAVE, mask); | 
 | 1301 |  | 
 | 1302 | 	param = &s->param_tx; | 
 | 1303 |  | 
 | 1304 | 	/* Slave ID, e.g., SHDMA_SLAVE_SCIF0_TX */ | 
 | 1305 | 	param->slave_id = s->slave_tx; | 
 | 1306 | 	param->dma_dev = s->dma_dev; | 
 | 1307 |  | 
 | 1308 | 	s->cookie_tx = -EINVAL; | 
 | 1309 | 	chan = dma_request_channel(mask, filter, param); | 
 | 1310 | 	dev_dbg(port->dev, "%s: TX: got channel %p\n", __func__, chan); | 
 | 1311 | 	if (chan) { | 
 | 1312 | 		s->chan_tx = chan; | 
 | 1313 | 		sg_init_table(&s->sg_tx, 1); | 
 | 1314 | 		/* UART circular tx buffer is an aligned page. */ | 
 | 1315 | 		BUG_ON((int)port->state->xmit.buf & ~PAGE_MASK); | 
 | 1316 | 		sg_set_page(&s->sg_tx, virt_to_page(port->state->xmit.buf), | 
 | 1317 | 			    UART_XMIT_SIZE, (int)port->state->xmit.buf & ~PAGE_MASK); | 
 | 1318 | 		nent = dma_map_sg(port->dev, &s->sg_tx, 1, DMA_TO_DEVICE); | 
 | 1319 | 		if (!nent) | 
 | 1320 | 			sci_tx_dma_release(s, false); | 
 | 1321 | 		else | 
 | 1322 | 			dev_dbg(port->dev, "%s: mapped %d@%p to %x\n", __func__, | 
 | 1323 | 				sg_dma_len(&s->sg_tx), | 
 | 1324 | 				port->state->xmit.buf, sg_dma_address(&s->sg_tx)); | 
 | 1325 |  | 
 | 1326 | 		s->sg_len_tx = nent; | 
 | 1327 |  | 
 | 1328 | 		INIT_WORK(&s->work_tx, work_fn_tx); | 
 | 1329 | 	} | 
 | 1330 |  | 
 | 1331 | 	param = &s->param_rx; | 
 | 1332 |  | 
 | 1333 | 	/* Slave ID, e.g., SHDMA_SLAVE_SCIF0_RX */ | 
 | 1334 | 	param->slave_id = s->slave_rx; | 
 | 1335 | 	param->dma_dev = s->dma_dev; | 
 | 1336 |  | 
 | 1337 | 	chan = dma_request_channel(mask, filter, param); | 
 | 1338 | 	dev_dbg(port->dev, "%s: RX: got channel %p\n", __func__, chan); | 
 | 1339 | 	if (chan) { | 
 | 1340 | 		dma_addr_t dma[2]; | 
 | 1341 | 		void *buf[2]; | 
 | 1342 | 		int i; | 
 | 1343 |  | 
 | 1344 | 		s->chan_rx = chan; | 
 | 1345 |  | 
 | 1346 | 		s->buf_len_rx = 2 * max(16, (int)port->fifosize); | 
 | 1347 | 		buf[0] = dma_alloc_coherent(port->dev, s->buf_len_rx * 2, | 
 | 1348 | 					    &dma[0], GFP_KERNEL); | 
 | 1349 |  | 
 | 1350 | 		if (!buf[0]) { | 
 | 1351 | 			dev_warn(port->dev, | 
 | 1352 | 				 "failed to allocate dma buffer, using PIO\n"); | 
 | 1353 | 			sci_rx_dma_release(s, true); | 
 | 1354 | 			return; | 
 | 1355 | 		} | 
 | 1356 |  | 
 | 1357 | 		buf[1] = buf[0] + s->buf_len_rx; | 
 | 1358 | 		dma[1] = dma[0] + s->buf_len_rx; | 
 | 1359 |  | 
 | 1360 | 		for (i = 0; i < 2; i++) { | 
 | 1361 | 			struct scatterlist *sg = &s->sg_rx[i]; | 
 | 1362 |  | 
 | 1363 | 			sg_init_table(sg, 1); | 
 | 1364 | 			sg_set_page(sg, virt_to_page(buf[i]), s->buf_len_rx, | 
 | 1365 | 				    (int)buf[i] & ~PAGE_MASK); | 
| Magnus Damm | f354a38 | 2010-03-19 04:47:01 +0000 | [diff] [blame] | 1366 | 			sg_dma_address(sg) = dma[i]; | 
| Guennadi Liakhovetski | 73a19e4 | 2010-03-02 11:39:15 +0900 | [diff] [blame] | 1367 | 		} | 
 | 1368 |  | 
 | 1369 | 		INIT_WORK(&s->work_rx, work_fn_rx); | 
 | 1370 | 		setup_timer(&s->rx_timer, rx_timer_fn, (unsigned long)s); | 
 | 1371 |  | 
 | 1372 | 		sci_submit_rx(s); | 
 | 1373 | 	} | 
 | 1374 | } | 
 | 1375 |  | 
 | 1376 | static void sci_free_dma(struct uart_port *port) | 
 | 1377 | { | 
 | 1378 | 	struct sci_port *s = to_sci_port(port); | 
 | 1379 |  | 
 | 1380 | 	if (!s->dma_dev) | 
 | 1381 | 		return; | 
 | 1382 |  | 
 | 1383 | 	if (s->chan_tx) | 
 | 1384 | 		sci_tx_dma_release(s, false); | 
 | 1385 | 	if (s->chan_rx) | 
 | 1386 | 		sci_rx_dma_release(s, false); | 
 | 1387 | } | 
 | 1388 | #endif | 
 | 1389 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1390 | static int sci_startup(struct uart_port *port) | 
 | 1391 | { | 
| Magnus Damm | a5660ad | 2009-01-21 15:14:38 +0000 | [diff] [blame] | 1392 | 	struct sci_port *s = to_sci_port(port); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1393 |  | 
| Guennadi Liakhovetski | 73a19e4 | 2010-03-02 11:39:15 +0900 | [diff] [blame] | 1394 | 	dev_dbg(port->dev, "%s(%d)\n", __func__, port->line); | 
 | 1395 |  | 
| Paul Mundt | e108b2c | 2006-09-27 16:32:13 +0900 | [diff] [blame] | 1396 | 	if (s->enable) | 
 | 1397 | 		s->enable(port); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1398 |  | 
 | 1399 | 	sci_request_irq(s); | 
| Guennadi Liakhovetski | 73a19e4 | 2010-03-02 11:39:15 +0900 | [diff] [blame] | 1400 | #ifdef CONFIG_SERIAL_SH_SCI_DMA | 
 | 1401 | 	sci_request_dma(port); | 
 | 1402 | #endif | 
| Yoshinori Sato | d656901 | 2005-10-14 15:59:12 -0700 | [diff] [blame] | 1403 | 	sci_start_tx(port); | 
| Guennadi Liakhovetski | 73a19e4 | 2010-03-02 11:39:15 +0900 | [diff] [blame] | 1404 | 	sci_start_rx(port); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1405 |  | 
 | 1406 | 	return 0; | 
 | 1407 | } | 
 | 1408 |  | 
 | 1409 | static void sci_shutdown(struct uart_port *port) | 
 | 1410 | { | 
| Magnus Damm | a5660ad | 2009-01-21 15:14:38 +0000 | [diff] [blame] | 1411 | 	struct sci_port *s = to_sci_port(port); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1412 |  | 
| Guennadi Liakhovetski | 73a19e4 | 2010-03-02 11:39:15 +0900 | [diff] [blame] | 1413 | 	dev_dbg(port->dev, "%s(%d)\n", __func__, port->line); | 
 | 1414 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1415 | 	sci_stop_rx(port); | 
| Russell King | b129a8c | 2005-08-31 10:12:14 +0100 | [diff] [blame] | 1416 | 	sci_stop_tx(port); | 
| Guennadi Liakhovetski | 73a19e4 | 2010-03-02 11:39:15 +0900 | [diff] [blame] | 1417 | #ifdef CONFIG_SERIAL_SH_SCI_DMA | 
 | 1418 | 	sci_free_dma(port); | 
 | 1419 | #endif | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1420 | 	sci_free_irq(s); | 
 | 1421 |  | 
| Paul Mundt | e108b2c | 2006-09-27 16:32:13 +0900 | [diff] [blame] | 1422 | 	if (s->disable) | 
 | 1423 | 		s->disable(port); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1424 | } | 
 | 1425 |  | 
| Alan Cox | 606d099 | 2006-12-08 02:38:45 -0800 | [diff] [blame] | 1426 | static void sci_set_termios(struct uart_port *port, struct ktermios *termios, | 
 | 1427 | 			    struct ktermios *old) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1428 | { | 
| Guennadi Liakhovetski | 3089f38 | 2010-03-19 13:53:04 +0000 | [diff] [blame^] | 1429 | #ifdef CONFIG_SERIAL_SH_SCI_DMA | 
 | 1430 | 	struct sci_port *s = to_sci_port(port); | 
 | 1431 | #endif | 
| Magnus Damm | 154280f | 2009-12-22 03:37:28 +0000 | [diff] [blame] | 1432 | 	unsigned int status, baud, smr_val, max_baud; | 
| Paul Mundt | a2159b5 | 2008-10-02 19:09:13 +0900 | [diff] [blame] | 1433 | 	int t = -1; | 
| Guennadi Liakhovetski | 3089f38 | 2010-03-19 13:53:04 +0000 | [diff] [blame^] | 1434 | 	u16 scfcr = 0; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1435 |  | 
| Magnus Damm | 154280f | 2009-12-22 03:37:28 +0000 | [diff] [blame] | 1436 | 	/* | 
 | 1437 | 	 * earlyprintk comes here early on with port->uartclk set to zero. | 
 | 1438 | 	 * the clock framework is not up and running at this point so here | 
 | 1439 | 	 * we assume that 115200 is the maximum baud rate. please note that | 
 | 1440 | 	 * the baud rate is not programmed during earlyprintk - it is assumed | 
 | 1441 | 	 * that the previous boot loader has enabled required clocks and | 
 | 1442 | 	 * setup the baud rate generator hardware for us already. | 
 | 1443 | 	 */ | 
 | 1444 | 	max_baud = port->uartclk ? port->uartclk / 16 : 115200; | 
 | 1445 |  | 
 | 1446 | 	baud = uart_get_baud_rate(port, termios, old, 0, max_baud); | 
 | 1447 | 	if (likely(baud && port->uartclk)) | 
| Paul Mundt | a2159b5 | 2008-10-02 19:09:13 +0900 | [diff] [blame] | 1448 | 		t = SCBRR_VALUE(baud, port->uartclk); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1449 |  | 
| Paul Mundt | e108b2c | 2006-09-27 16:32:13 +0900 | [diff] [blame] | 1450 | 	do { | 
 | 1451 | 		status = sci_in(port, SCxSR); | 
 | 1452 | 	} while (!(status & SCxSR_TEND(port))); | 
 | 1453 |  | 
 | 1454 | 	sci_out(port, SCSCR, 0x00);	/* TE=0, RE=0, CKE1=0 */ | 
 | 1455 |  | 
| Yoshihiro Shimoda | 1a22f08 | 2008-11-11 12:19:05 +0900 | [diff] [blame] | 1456 | 	if (port->type != PORT_SCI) | 
| Guennadi Liakhovetski | 3089f38 | 2010-03-19 13:53:04 +0000 | [diff] [blame^] | 1457 | 		sci_out(port, SCFCR, scfcr | SCFCR_RFRST | SCFCR_TFRST); | 
| Paul Mundt | e108b2c | 2006-09-27 16:32:13 +0900 | [diff] [blame] | 1458 |  | 
 | 1459 | 	smr_val = sci_in(port, SCSMR) & 3; | 
 | 1460 | 	if ((termios->c_cflag & CSIZE) == CS7) | 
 | 1461 | 		smr_val |= 0x40; | 
 | 1462 | 	if (termios->c_cflag & PARENB) | 
 | 1463 | 		smr_val |= 0x20; | 
 | 1464 | 	if (termios->c_cflag & PARODD) | 
 | 1465 | 		smr_val |= 0x30; | 
 | 1466 | 	if (termios->c_cflag & CSTOPB) | 
 | 1467 | 		smr_val |= 0x08; | 
 | 1468 |  | 
 | 1469 | 	uart_update_timeout(port, termios->c_cflag, baud); | 
 | 1470 |  | 
 | 1471 | 	sci_out(port, SCSMR, smr_val); | 
 | 1472 |  | 
| Guennadi Liakhovetski | 73a19e4 | 2010-03-02 11:39:15 +0900 | [diff] [blame] | 1473 | 	dev_dbg(port->dev, "%s: SMR %x, t %x, SCSCR %x\n", __func__, smr_val, t, | 
 | 1474 | 		SCSCR_INIT(port)); | 
 | 1475 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1476 | 	if (t > 0) { | 
| Michael Trimarchi | e7c98dc | 2008-11-13 18:18:35 +0900 | [diff] [blame] | 1477 | 		if (t >= 256) { | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1478 | 			sci_out(port, SCSMR, (sci_in(port, SCSMR) & ~3) | 1); | 
 | 1479 | 			t >>= 2; | 
| Michael Trimarchi | e7c98dc | 2008-11-13 18:18:35 +0900 | [diff] [blame] | 1480 | 		} else | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1481 | 			sci_out(port, SCSMR, sci_in(port, SCSMR) & ~3); | 
| Michael Trimarchi | e7c98dc | 2008-11-13 18:18:35 +0900 | [diff] [blame] | 1482 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1483 | 		sci_out(port, SCBRR, t); | 
 | 1484 | 		udelay((1000000+(baud-1)) / baud); /* Wait one bit interval */ | 
 | 1485 | 	} | 
 | 1486 |  | 
| Paul Mundt | d570164 | 2008-12-16 20:07:27 +0900 | [diff] [blame] | 1487 | 	sci_init_pins(port, termios->c_cflag); | 
| Guennadi Liakhovetski | 3089f38 | 2010-03-19 13:53:04 +0000 | [diff] [blame^] | 1488 | 	sci_out(port, SCFCR, scfcr | ((termios->c_cflag & CRTSCTS) ? SCFCR_MCE : 0)); | 
| Paul Mundt | b7a76e4 | 2006-02-01 03:06:06 -0800 | [diff] [blame] | 1489 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1490 | 	sci_out(port, SCSCR, SCSCR_INIT(port)); | 
 | 1491 |  | 
| Guennadi Liakhovetski | 3089f38 | 2010-03-19 13:53:04 +0000 | [diff] [blame^] | 1492 | #ifdef CONFIG_SERIAL_SH_SCI_DMA | 
 | 1493 | 	/* | 
 | 1494 | 	 * Calculate delay for 1.5 DMA buffers: see | 
 | 1495 | 	 * drivers/serial/serial_core.c::uart_update_timeout(). With 10 bits | 
 | 1496 | 	 * (CS8), 250Hz, 115200 baud and 64 bytes FIFO, the above function | 
 | 1497 | 	 * calculates 1 jiffie for the data plus 5 jiffies for the "slop(e)." | 
 | 1498 | 	 * Then below we calculate 3 jiffies (12ms) for 1.5 DMA buffers (3 FIFO | 
 | 1499 | 	 * sizes), but it has been found out experimentally, that this is not | 
 | 1500 | 	 * enough: the driver too often needlessly runs on a DMA timeout. 20ms | 
 | 1501 | 	 * as a minimum seem to work perfectly. | 
 | 1502 | 	 */ | 
 | 1503 | 	if (s->chan_rx) { | 
 | 1504 | 		s->rx_timeout = (port->timeout - HZ / 50) * s->buf_len_rx * 3 / | 
 | 1505 | 			port->fifosize / 2; | 
 | 1506 | 		dev_dbg(port->dev, | 
 | 1507 | 			"DMA Rx t-out %ums, tty t-out %u jiffies\n", | 
 | 1508 | 			s->rx_timeout * 1000 / HZ, port->timeout); | 
 | 1509 | 		if (s->rx_timeout < msecs_to_jiffies(20)) | 
 | 1510 | 			s->rx_timeout = msecs_to_jiffies(20); | 
 | 1511 | 	} | 
 | 1512 | #endif | 
 | 1513 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1514 | 	if ((termios->c_cflag & CREAD) != 0) | 
| Guennadi Liakhovetski | 73a19e4 | 2010-03-02 11:39:15 +0900 | [diff] [blame] | 1515 | 		sci_start_rx(port); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1516 | } | 
 | 1517 |  | 
 | 1518 | static const char *sci_type(struct uart_port *port) | 
 | 1519 | { | 
 | 1520 | 	switch (port->type) { | 
| Michael Trimarchi | e7c98dc | 2008-11-13 18:18:35 +0900 | [diff] [blame] | 1521 | 	case PORT_IRDA: | 
 | 1522 | 		return "irda"; | 
 | 1523 | 	case PORT_SCI: | 
 | 1524 | 		return "sci"; | 
 | 1525 | 	case PORT_SCIF: | 
 | 1526 | 		return "scif"; | 
 | 1527 | 	case PORT_SCIFA: | 
 | 1528 | 		return "scifa"; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1529 | 	} | 
 | 1530 |  | 
| Paul Mundt | fa43972 | 2008-09-04 18:53:58 +0900 | [diff] [blame] | 1531 | 	return NULL; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1532 | } | 
 | 1533 |  | 
 | 1534 | static void sci_release_port(struct uart_port *port) | 
 | 1535 | { | 
 | 1536 | 	/* Nothing here yet .. */ | 
 | 1537 | } | 
 | 1538 |  | 
 | 1539 | static int sci_request_port(struct uart_port *port) | 
 | 1540 | { | 
 | 1541 | 	/* Nothing here yet .. */ | 
 | 1542 | 	return 0; | 
 | 1543 | } | 
 | 1544 |  | 
 | 1545 | static void sci_config_port(struct uart_port *port, int flags) | 
 | 1546 | { | 
| Magnus Damm | a5660ad | 2009-01-21 15:14:38 +0000 | [diff] [blame] | 1547 | 	struct sci_port *s = to_sci_port(port); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1548 |  | 
 | 1549 | 	port->type = s->type; | 
 | 1550 |  | 
| Magnus Damm | 08f8cb3 | 2009-01-21 15:14:22 +0000 | [diff] [blame] | 1551 | 	if (port->membase) | 
 | 1552 | 		return; | 
 | 1553 |  | 
 | 1554 | 	if (port->flags & UPF_IOREMAP) { | 
| Paul Mundt | 7ff731a | 2008-10-01 15:46:58 +0900 | [diff] [blame] | 1555 | 		port->membase = ioremap_nocache(port->mapbase, 0x40); | 
| Magnus Damm | 08f8cb3 | 2009-01-21 15:14:22 +0000 | [diff] [blame] | 1556 |  | 
 | 1557 | 		if (IS_ERR(port->membase)) | 
 | 1558 | 			dev_err(port->dev, "can't remap port#%d\n", port->line); | 
 | 1559 | 	} else { | 
 | 1560 | 		/* | 
 | 1561 | 		 * For the simple (and majority of) cases where we don't | 
 | 1562 | 		 * need to do any remapping, just cast the cookie | 
 | 1563 | 		 * directly. | 
 | 1564 | 		 */ | 
 | 1565 | 		port->membase = (void __iomem *)port->mapbase; | 
| Paul Mundt | 7ff731a | 2008-10-01 15:46:58 +0900 | [diff] [blame] | 1566 | 	} | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1567 | } | 
 | 1568 |  | 
 | 1569 | static int sci_verify_port(struct uart_port *port, struct serial_struct *ser) | 
 | 1570 | { | 
| Magnus Damm | a5660ad | 2009-01-21 15:14:38 +0000 | [diff] [blame] | 1571 | 	struct sci_port *s = to_sci_port(port); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1572 |  | 
| Yinghai Lu | a62c413 | 2008-08-19 20:49:55 -0700 | [diff] [blame] | 1573 | 	if (ser->irq != s->irqs[SCIx_TXI_IRQ] || ser->irq > nr_irqs) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1574 | 		return -EINVAL; | 
 | 1575 | 	if (ser->baud_base < 2400) | 
 | 1576 | 		/* No paper tape reader for Mitch.. */ | 
 | 1577 | 		return -EINVAL; | 
 | 1578 |  | 
 | 1579 | 	return 0; | 
 | 1580 | } | 
 | 1581 |  | 
 | 1582 | static struct uart_ops sci_uart_ops = { | 
 | 1583 | 	.tx_empty	= sci_tx_empty, | 
 | 1584 | 	.set_mctrl	= sci_set_mctrl, | 
 | 1585 | 	.get_mctrl	= sci_get_mctrl, | 
 | 1586 | 	.start_tx	= sci_start_tx, | 
 | 1587 | 	.stop_tx	= sci_stop_tx, | 
 | 1588 | 	.stop_rx	= sci_stop_rx, | 
 | 1589 | 	.enable_ms	= sci_enable_ms, | 
 | 1590 | 	.break_ctl	= sci_break_ctl, | 
 | 1591 | 	.startup	= sci_startup, | 
 | 1592 | 	.shutdown	= sci_shutdown, | 
 | 1593 | 	.set_termios	= sci_set_termios, | 
 | 1594 | 	.type		= sci_type, | 
 | 1595 | 	.release_port	= sci_release_port, | 
 | 1596 | 	.request_port	= sci_request_port, | 
 | 1597 | 	.config_port	= sci_config_port, | 
 | 1598 | 	.verify_port	= sci_verify_port, | 
| Paul Mundt | 07d2a1a | 2008-12-11 19:06:43 +0900 | [diff] [blame] | 1599 | #ifdef CONFIG_CONSOLE_POLL | 
 | 1600 | 	.poll_get_char	= sci_poll_get_char, | 
 | 1601 | 	.poll_put_char	= sci_poll_put_char, | 
 | 1602 | #endif | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1603 | }; | 
 | 1604 |  | 
| Magnus Damm | 501b825 | 2009-01-21 15:14:30 +0000 | [diff] [blame] | 1605 | static void __devinit sci_init_single(struct platform_device *dev, | 
 | 1606 | 				      struct sci_port *sci_port, | 
| Magnus Damm | 08f8cb3 | 2009-01-21 15:14:22 +0000 | [diff] [blame] | 1607 | 				      unsigned int index, | 
 | 1608 | 				      struct plat_sci_port *p) | 
| Paul Mundt | e108b2c | 2006-09-27 16:32:13 +0900 | [diff] [blame] | 1609 | { | 
| Guennadi Liakhovetski | 73a19e4 | 2010-03-02 11:39:15 +0900 | [diff] [blame] | 1610 | 	struct uart_port *port = &sci_port->port; | 
 | 1611 |  | 
 | 1612 | 	port->ops	= &sci_uart_ops; | 
 | 1613 | 	port->iotype	= UPIO_MEM; | 
 | 1614 | 	port->line	= index; | 
| Markus Pietrek | 75136d4 | 2010-01-15 08:33:20 +0900 | [diff] [blame] | 1615 |  | 
 | 1616 | 	switch (p->type) { | 
 | 1617 | 	case PORT_SCIFA: | 
| Guennadi Liakhovetski | 73a19e4 | 2010-03-02 11:39:15 +0900 | [diff] [blame] | 1618 | 		port->fifosize = 64; | 
| Markus Pietrek | 75136d4 | 2010-01-15 08:33:20 +0900 | [diff] [blame] | 1619 | 		break; | 
 | 1620 | 	case PORT_SCIF: | 
| Guennadi Liakhovetski | 73a19e4 | 2010-03-02 11:39:15 +0900 | [diff] [blame] | 1621 | 		port->fifosize = 16; | 
| Markus Pietrek | 75136d4 | 2010-01-15 08:33:20 +0900 | [diff] [blame] | 1622 | 		break; | 
 | 1623 | 	default: | 
| Guennadi Liakhovetski | 73a19e4 | 2010-03-02 11:39:15 +0900 | [diff] [blame] | 1624 | 		port->fifosize = 1; | 
| Markus Pietrek | 75136d4 | 2010-01-15 08:33:20 +0900 | [diff] [blame] | 1625 | 		break; | 
 | 1626 | 	} | 
| Magnus Damm | 7b6fd3b | 2009-12-14 10:24:42 +0000 | [diff] [blame] | 1627 |  | 
 | 1628 | 	if (dev) { | 
 | 1629 | 		sci_port->iclk = p->clk ? clk_get(&dev->dev, p->clk) : NULL; | 
 | 1630 | 		sci_port->dclk = clk_get(&dev->dev, "peripheral_clk"); | 
 | 1631 | 		sci_port->enable = sci_clk_enable; | 
 | 1632 | 		sci_port->disable = sci_clk_disable; | 
| Guennadi Liakhovetski | 73a19e4 | 2010-03-02 11:39:15 +0900 | [diff] [blame] | 1633 | 		port->dev = &dev->dev; | 
| Magnus Damm | 7b6fd3b | 2009-12-14 10:24:42 +0000 | [diff] [blame] | 1634 | 	} | 
| Paul Mundt | e108b2c | 2006-09-27 16:32:13 +0900 | [diff] [blame] | 1635 |  | 
| Magnus Damm | 7ed7e07 | 2009-01-21 15:14:14 +0000 | [diff] [blame] | 1636 | 	sci_port->break_timer.data = (unsigned long)sci_port; | 
 | 1637 | 	sci_port->break_timer.function = sci_break_timer; | 
 | 1638 | 	init_timer(&sci_port->break_timer); | 
| Paul Mundt | e108b2c | 2006-09-27 16:32:13 +0900 | [diff] [blame] | 1639 |  | 
| Guennadi Liakhovetski | 73a19e4 | 2010-03-02 11:39:15 +0900 | [diff] [blame] | 1640 | 	port->mapbase	= p->mapbase; | 
 | 1641 | 	port->membase	= p->membase; | 
| Magnus Damm | 7ed7e07 | 2009-01-21 15:14:14 +0000 | [diff] [blame] | 1642 |  | 
| Guennadi Liakhovetski | 73a19e4 | 2010-03-02 11:39:15 +0900 | [diff] [blame] | 1643 | 	port->irq	= p->irqs[SCIx_TXI_IRQ]; | 
 | 1644 | 	port->flags	= p->flags; | 
 | 1645 | 	sci_port->type	= port->type = p->type; | 
 | 1646 |  | 
 | 1647 | #ifdef CONFIG_SERIAL_SH_SCI_DMA | 
 | 1648 | 	sci_port->dma_dev	= p->dma_dev; | 
 | 1649 | 	sci_port->slave_tx	= p->dma_slave_tx; | 
 | 1650 | 	sci_port->slave_rx	= p->dma_slave_rx; | 
 | 1651 |  | 
 | 1652 | 	dev_dbg(port->dev, "%s: DMA device %p, tx %d, rx %d\n", __func__, | 
 | 1653 | 		p->dma_dev, p->dma_slave_tx, p->dma_slave_rx); | 
 | 1654 | #endif | 
| Magnus Damm | 7ed7e07 | 2009-01-21 15:14:14 +0000 | [diff] [blame] | 1655 |  | 
 | 1656 | 	memcpy(&sci_port->irqs, &p->irqs, sizeof(p->irqs)); | 
| Paul Mundt | e108b2c | 2006-09-27 16:32:13 +0900 | [diff] [blame] | 1657 | } | 
 | 1658 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1659 | #ifdef CONFIG_SERIAL_SH_SCI_CONSOLE | 
| Magnus Damm | dc8e6f5 | 2009-01-21 15:14:06 +0000 | [diff] [blame] | 1660 | static struct tty_driver *serial_console_device(struct console *co, int *index) | 
 | 1661 | { | 
 | 1662 | 	struct uart_driver *p = &sci_uart_driver; | 
 | 1663 | 	*index = co->index; | 
 | 1664 | 	return p->tty_driver; | 
 | 1665 | } | 
 | 1666 |  | 
 | 1667 | static void serial_console_putchar(struct uart_port *port, int ch) | 
 | 1668 | { | 
 | 1669 | 	sci_poll_put_char(port, ch); | 
 | 1670 | } | 
 | 1671 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1672 | /* | 
 | 1673 |  *	Print a string to the serial port trying not to disturb | 
 | 1674 |  *	any possible real use of the port... | 
 | 1675 |  */ | 
 | 1676 | static void serial_console_write(struct console *co, const char *s, | 
 | 1677 | 				 unsigned count) | 
 | 1678 | { | 
| Magnus Damm | dc8e6f5 | 2009-01-21 15:14:06 +0000 | [diff] [blame] | 1679 | 	struct uart_port *port = co->data; | 
| Magnus Damm | 501b825 | 2009-01-21 15:14:30 +0000 | [diff] [blame] | 1680 | 	struct sci_port *sci_port = to_sci_port(port); | 
| Magnus Damm | 973e5d5 | 2009-02-24 15:57:12 +0900 | [diff] [blame] | 1681 | 	unsigned short bits; | 
| Paul Mundt | 07d2a1a | 2008-12-11 19:06:43 +0900 | [diff] [blame] | 1682 |  | 
| Magnus Damm | 501b825 | 2009-01-21 15:14:30 +0000 | [diff] [blame] | 1683 | 	if (sci_port->enable) | 
 | 1684 | 		sci_port->enable(port); | 
 | 1685 |  | 
 | 1686 | 	uart_console_write(port, s, count, serial_console_putchar); | 
| Magnus Damm | 973e5d5 | 2009-02-24 15:57:12 +0900 | [diff] [blame] | 1687 |  | 
 | 1688 | 	/* wait until fifo is empty and last bit has been transmitted */ | 
 | 1689 | 	bits = SCxSR_TDxE(port) | SCxSR_TEND(port); | 
 | 1690 | 	while ((sci_in(port, SCxSR) & bits) != bits) | 
 | 1691 | 		cpu_relax(); | 
| Magnus Damm | 501b825 | 2009-01-21 15:14:30 +0000 | [diff] [blame] | 1692 |  | 
| Magnus Damm | 345e5a7 | 2009-11-05 14:34:57 +0000 | [diff] [blame] | 1693 | 	if (sci_port->disable) | 
| Magnus Damm | 501b825 | 2009-01-21 15:14:30 +0000 | [diff] [blame] | 1694 | 		sci_port->disable(port); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1695 | } | 
 | 1696 |  | 
| Magnus Damm | 7b6fd3b | 2009-12-14 10:24:42 +0000 | [diff] [blame] | 1697 | static int __devinit serial_console_setup(struct console *co, char *options) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1698 | { | 
| Magnus Damm | dc8e6f5 | 2009-01-21 15:14:06 +0000 | [diff] [blame] | 1699 | 	struct sci_port *sci_port; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1700 | 	struct uart_port *port; | 
 | 1701 | 	int baud = 115200; | 
 | 1702 | 	int bits = 8; | 
 | 1703 | 	int parity = 'n'; | 
 | 1704 | 	int flow = 'n'; | 
 | 1705 | 	int ret; | 
 | 1706 |  | 
| Paul Mundt | e108b2c | 2006-09-27 16:32:13 +0900 | [diff] [blame] | 1707 | 	/* | 
 | 1708 | 	 * Check whether an invalid uart number has been specified, and | 
 | 1709 | 	 * if so, search for the first available port that does have | 
 | 1710 | 	 * console support. | 
 | 1711 | 	 */ | 
 | 1712 | 	if (co->index >= SCI_NPORTS) | 
 | 1713 | 		co->index = 0; | 
 | 1714 |  | 
| Magnus Damm | 7b6fd3b | 2009-12-14 10:24:42 +0000 | [diff] [blame] | 1715 | 	if (co->data) { | 
 | 1716 | 		port = co->data; | 
 | 1717 | 		sci_port = to_sci_port(port); | 
 | 1718 | 	} else { | 
 | 1719 | 		sci_port = &sci_ports[co->index]; | 
 | 1720 | 		port = &sci_port->port; | 
 | 1721 | 		co->data = port; | 
 | 1722 | 	} | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1723 |  | 
 | 1724 | 	/* | 
| Paul Mundt | e108b2c | 2006-09-27 16:32:13 +0900 | [diff] [blame] | 1725 | 	 * Also need to check port->type, we don't actually have any | 
 | 1726 | 	 * UPIO_PORT ports, but uart_report_port() handily misreports | 
 | 1727 | 	 * it anyways if we don't have a port available by the time this is | 
 | 1728 | 	 * called. | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1729 | 	 */ | 
| Paul Mundt | e108b2c | 2006-09-27 16:32:13 +0900 | [diff] [blame] | 1730 | 	if (!port->type) | 
 | 1731 | 		return -ENODEV; | 
| Paul Mundt | e108b2c | 2006-09-27 16:32:13 +0900 | [diff] [blame] | 1732 |  | 
| Magnus Damm | 08f8cb3 | 2009-01-21 15:14:22 +0000 | [diff] [blame] | 1733 | 	sci_config_port(port, 0); | 
| Paul Mundt | e108b2c | 2006-09-27 16:32:13 +0900 | [diff] [blame] | 1734 |  | 
| Magnus Damm | dc8e6f5 | 2009-01-21 15:14:06 +0000 | [diff] [blame] | 1735 | 	if (sci_port->enable) | 
 | 1736 | 		sci_port->enable(port); | 
| Paul Mundt | e108b2c | 2006-09-27 16:32:13 +0900 | [diff] [blame] | 1737 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1738 | 	if (options) | 
 | 1739 | 		uart_parse_options(options, &baud, &parity, &bits, &flow); | 
 | 1740 |  | 
 | 1741 | 	ret = uart_set_options(port, co, baud, parity, bits, flow); | 
 | 1742 | #if defined(__H8300H__) || defined(__H8300S__) | 
 | 1743 | 	/* disable rx interrupt */ | 
 | 1744 | 	if (ret == 0) | 
 | 1745 | 		sci_stop_rx(port); | 
 | 1746 | #endif | 
| Magnus Damm | 501b825 | 2009-01-21 15:14:30 +0000 | [diff] [blame] | 1747 | 	/* TODO: disable clock */ | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1748 | 	return ret; | 
 | 1749 | } | 
 | 1750 |  | 
 | 1751 | static struct console serial_console = { | 
 | 1752 | 	.name		= "ttySC", | 
| Magnus Damm | dc8e6f5 | 2009-01-21 15:14:06 +0000 | [diff] [blame] | 1753 | 	.device		= serial_console_device, | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1754 | 	.write		= serial_console_write, | 
 | 1755 | 	.setup		= serial_console_setup, | 
| Paul Mundt | fa5da2f | 2007-03-08 17:27:37 +0900 | [diff] [blame] | 1756 | 	.flags		= CON_PRINTBUFFER, | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1757 | 	.index		= -1, | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1758 | }; | 
 | 1759 |  | 
 | 1760 | static int __init sci_console_init(void) | 
 | 1761 | { | 
 | 1762 | 	register_console(&serial_console); | 
 | 1763 | 	return 0; | 
 | 1764 | } | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1765 | console_initcall(sci_console_init); | 
| Magnus Damm | 7b6fd3b | 2009-12-14 10:24:42 +0000 | [diff] [blame] | 1766 |  | 
 | 1767 | static struct sci_port early_serial_port; | 
 | 1768 | static struct console early_serial_console = { | 
 | 1769 | 	.name           = "early_ttySC", | 
 | 1770 | 	.write          = serial_console_write, | 
 | 1771 | 	.flags          = CON_PRINTBUFFER, | 
 | 1772 | }; | 
 | 1773 | static char early_serial_buf[32]; | 
 | 1774 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1775 | #endif /* CONFIG_SERIAL_SH_SCI_CONSOLE */ | 
 | 1776 |  | 
| Paul Mundt | 07d2a1a | 2008-12-11 19:06:43 +0900 | [diff] [blame] | 1777 | #if defined(CONFIG_SERIAL_SH_SCI_CONSOLE) | 
| Michael Trimarchi | e7c98dc | 2008-11-13 18:18:35 +0900 | [diff] [blame] | 1778 | #define SCI_CONSOLE	(&serial_console) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1779 | #else | 
| Paul Mundt | b7a76e4 | 2006-02-01 03:06:06 -0800 | [diff] [blame] | 1780 | #define SCI_CONSOLE	0 | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1781 | #endif | 
 | 1782 |  | 
 | 1783 | static char banner[] __initdata = | 
 | 1784 | 	KERN_INFO "SuperH SCI(F) driver initialized\n"; | 
 | 1785 |  | 
 | 1786 | static struct uart_driver sci_uart_driver = { | 
 | 1787 | 	.owner		= THIS_MODULE, | 
 | 1788 | 	.driver_name	= "sci", | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1789 | 	.dev_name	= "ttySC", | 
 | 1790 | 	.major		= SCI_MAJOR, | 
 | 1791 | 	.minor		= SCI_MINOR_START, | 
| Paul Mundt | e108b2c | 2006-09-27 16:32:13 +0900 | [diff] [blame] | 1792 | 	.nr		= SCI_NPORTS, | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1793 | 	.cons		= SCI_CONSOLE, | 
 | 1794 | }; | 
 | 1795 |  | 
| Magnus Damm | e552de2 | 2009-01-21 15:13:42 +0000 | [diff] [blame] | 1796 |  | 
| Paul Mundt | 54507f6 | 2009-05-08 23:48:33 +0900 | [diff] [blame] | 1797 | static int sci_remove(struct platform_device *dev) | 
| Magnus Damm | e552de2 | 2009-01-21 15:13:42 +0000 | [diff] [blame] | 1798 | { | 
 | 1799 | 	struct sh_sci_priv *priv = platform_get_drvdata(dev); | 
 | 1800 | 	struct sci_port *p; | 
 | 1801 | 	unsigned long flags; | 
 | 1802 |  | 
| Magnus Damm | e552de2 | 2009-01-21 15:13:42 +0000 | [diff] [blame] | 1803 | 	cpufreq_unregister_notifier(&priv->clk_nb, CPUFREQ_TRANSITION_NOTIFIER); | 
| Magnus Damm | e552de2 | 2009-01-21 15:13:42 +0000 | [diff] [blame] | 1804 |  | 
 | 1805 | 	spin_lock_irqsave(&priv->lock, flags); | 
 | 1806 | 	list_for_each_entry(p, &priv->ports, node) | 
 | 1807 | 		uart_remove_one_port(&sci_uart_driver, &p->port); | 
| Magnus Damm | e552de2 | 2009-01-21 15:13:42 +0000 | [diff] [blame] | 1808 | 	spin_unlock_irqrestore(&priv->lock, flags); | 
 | 1809 |  | 
 | 1810 | 	kfree(priv); | 
 | 1811 | 	return 0; | 
 | 1812 | } | 
 | 1813 |  | 
| Magnus Damm | 0ee7071 | 2009-01-21 15:13:50 +0000 | [diff] [blame] | 1814 | static int __devinit sci_probe_single(struct platform_device *dev, | 
 | 1815 | 				      unsigned int index, | 
 | 1816 | 				      struct plat_sci_port *p, | 
 | 1817 | 				      struct sci_port *sciport) | 
 | 1818 | { | 
 | 1819 | 	struct sh_sci_priv *priv = platform_get_drvdata(dev); | 
 | 1820 | 	unsigned long flags; | 
 | 1821 | 	int ret; | 
 | 1822 |  | 
 | 1823 | 	/* Sanity check */ | 
 | 1824 | 	if (unlikely(index >= SCI_NPORTS)) { | 
 | 1825 | 		dev_notice(&dev->dev, "Attempting to register port " | 
 | 1826 | 			   "%d when only %d are available.\n", | 
 | 1827 | 			   index+1, SCI_NPORTS); | 
 | 1828 | 		dev_notice(&dev->dev, "Consider bumping " | 
 | 1829 | 			   "CONFIG_SERIAL_SH_SCI_NR_UARTS!\n"); | 
 | 1830 | 		return 0; | 
 | 1831 | 	} | 
 | 1832 |  | 
| Magnus Damm | 501b825 | 2009-01-21 15:14:30 +0000 | [diff] [blame] | 1833 | 	sci_init_single(dev, sciport, index, p); | 
| Magnus Damm | 0ee7071 | 2009-01-21 15:13:50 +0000 | [diff] [blame] | 1834 |  | 
 | 1835 | 	ret = uart_add_one_port(&sci_uart_driver, &sciport->port); | 
| Magnus Damm | 08f8cb3 | 2009-01-21 15:14:22 +0000 | [diff] [blame] | 1836 | 	if (ret) | 
| Magnus Damm | 0ee7071 | 2009-01-21 15:13:50 +0000 | [diff] [blame] | 1837 | 		return ret; | 
| Magnus Damm | 0ee7071 | 2009-01-21 15:13:50 +0000 | [diff] [blame] | 1838 |  | 
 | 1839 | 	INIT_LIST_HEAD(&sciport->node); | 
 | 1840 |  | 
 | 1841 | 	spin_lock_irqsave(&priv->lock, flags); | 
 | 1842 | 	list_add(&sciport->node, &priv->ports); | 
 | 1843 | 	spin_unlock_irqrestore(&priv->lock, flags); | 
 | 1844 |  | 
 | 1845 | 	return 0; | 
 | 1846 | } | 
 | 1847 |  | 
| Paul Mundt | e108b2c | 2006-09-27 16:32:13 +0900 | [diff] [blame] | 1848 | /* | 
 | 1849 |  * Register a set of serial devices attached to a platform device.  The | 
 | 1850 |  * list is terminated with a zero flags entry, which means we expect | 
 | 1851 |  * all entries to have at least UPF_BOOT_AUTOCONF set. Platforms that need | 
 | 1852 |  * remapping (such as sh64) should also set UPF_IOREMAP. | 
 | 1853 |  */ | 
 | 1854 | static int __devinit sci_probe(struct platform_device *dev) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1855 | { | 
| Paul Mundt | e108b2c | 2006-09-27 16:32:13 +0900 | [diff] [blame] | 1856 | 	struct plat_sci_port *p = dev->dev.platform_data; | 
| Magnus Damm | e552de2 | 2009-01-21 15:13:42 +0000 | [diff] [blame] | 1857 | 	struct sh_sci_priv *priv; | 
| Paul Mundt | 7ff731a | 2008-10-01 15:46:58 +0900 | [diff] [blame] | 1858 | 	int i, ret = -EINVAL; | 
| Magnus Damm | e552de2 | 2009-01-21 15:13:42 +0000 | [diff] [blame] | 1859 |  | 
| Magnus Damm | 7b6fd3b | 2009-12-14 10:24:42 +0000 | [diff] [blame] | 1860 | #ifdef CONFIG_SERIAL_SH_SCI_CONSOLE | 
 | 1861 | 	if (is_early_platform_device(dev)) { | 
 | 1862 | 		if (dev->id == -1) | 
 | 1863 | 			return -ENOTSUPP; | 
 | 1864 | 		early_serial_console.index = dev->id; | 
 | 1865 | 		early_serial_console.data = &early_serial_port.port; | 
 | 1866 | 		sci_init_single(NULL, &early_serial_port, dev->id, p); | 
 | 1867 | 		serial_console_setup(&early_serial_console, early_serial_buf); | 
 | 1868 | 		if (!strstr(early_serial_buf, "keep")) | 
 | 1869 | 			early_serial_console.flags |= CON_BOOT; | 
 | 1870 | 		register_console(&early_serial_console); | 
 | 1871 | 		return 0; | 
 | 1872 | 	} | 
 | 1873 | #endif | 
 | 1874 |  | 
| Magnus Damm | e552de2 | 2009-01-21 15:13:42 +0000 | [diff] [blame] | 1875 | 	priv = kzalloc(sizeof(*priv), GFP_KERNEL); | 
 | 1876 | 	if (!priv) | 
 | 1877 | 		return -ENOMEM; | 
 | 1878 |  | 
 | 1879 | 	INIT_LIST_HEAD(&priv->ports); | 
 | 1880 | 	spin_lock_init(&priv->lock); | 
 | 1881 | 	platform_set_drvdata(dev, priv); | 
 | 1882 |  | 
| Magnus Damm | e552de2 | 2009-01-21 15:13:42 +0000 | [diff] [blame] | 1883 | 	priv->clk_nb.notifier_call = sci_notifier; | 
 | 1884 | 	cpufreq_register_notifier(&priv->clk_nb, CPUFREQ_TRANSITION_NOTIFIER); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1885 |  | 
| Magnus Damm | 0ee7071 | 2009-01-21 15:13:50 +0000 | [diff] [blame] | 1886 | 	if (dev->id != -1) { | 
 | 1887 | 		ret = sci_probe_single(dev, dev->id, p, &sci_ports[dev->id]); | 
 | 1888 | 		if (ret) | 
| Magnus Damm | e552de2 | 2009-01-21 15:13:42 +0000 | [diff] [blame] | 1889 | 			goto err_unreg; | 
| Magnus Damm | 0ee7071 | 2009-01-21 15:13:50 +0000 | [diff] [blame] | 1890 | 	} else { | 
 | 1891 | 		for (i = 0; p && p->flags != 0; p++, i++) { | 
 | 1892 | 			ret = sci_probe_single(dev, i, p, &sci_ports[i]); | 
 | 1893 | 			if (ret) | 
 | 1894 | 				goto err_unreg; | 
| Magnus Damm | e552de2 | 2009-01-21 15:13:42 +0000 | [diff] [blame] | 1895 | 		} | 
| Magnus Damm | e552de2 | 2009-01-21 15:13:42 +0000 | [diff] [blame] | 1896 | 	} | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1897 |  | 
 | 1898 | #ifdef CONFIG_SH_STANDARD_BIOS | 
 | 1899 | 	sh_bios_gdb_detach(); | 
 | 1900 | #endif | 
 | 1901 |  | 
| Paul Mundt | e108b2c | 2006-09-27 16:32:13 +0900 | [diff] [blame] | 1902 | 	return 0; | 
| Paul Mundt | 7ff731a | 2008-10-01 15:46:58 +0900 | [diff] [blame] | 1903 |  | 
 | 1904 | err_unreg: | 
| Magnus Damm | e552de2 | 2009-01-21 15:13:42 +0000 | [diff] [blame] | 1905 | 	sci_remove(dev); | 
| Paul Mundt | 7ff731a | 2008-10-01 15:46:58 +0900 | [diff] [blame] | 1906 | 	return ret; | 
| Paul Mundt | e108b2c | 2006-09-27 16:32:13 +0900 | [diff] [blame] | 1907 | } | 
 | 1908 |  | 
| Paul Mundt | 6daa79b | 2009-06-15 07:07:38 +0900 | [diff] [blame] | 1909 | static int sci_suspend(struct device *dev) | 
| Paul Mundt | e108b2c | 2006-09-27 16:32:13 +0900 | [diff] [blame] | 1910 | { | 
| Paul Mundt | 6daa79b | 2009-06-15 07:07:38 +0900 | [diff] [blame] | 1911 | 	struct sh_sci_priv *priv = dev_get_drvdata(dev); | 
| Magnus Damm | e552de2 | 2009-01-21 15:13:42 +0000 | [diff] [blame] | 1912 | 	struct sci_port *p; | 
 | 1913 | 	unsigned long flags; | 
| Paul Mundt | e108b2c | 2006-09-27 16:32:13 +0900 | [diff] [blame] | 1914 |  | 
| Magnus Damm | e552de2 | 2009-01-21 15:13:42 +0000 | [diff] [blame] | 1915 | 	spin_lock_irqsave(&priv->lock, flags); | 
 | 1916 | 	list_for_each_entry(p, &priv->ports, node) | 
 | 1917 | 		uart_suspend_port(&sci_uart_driver, &p->port); | 
| Magnus Damm | e552de2 | 2009-01-21 15:13:42 +0000 | [diff] [blame] | 1918 | 	spin_unlock_irqrestore(&priv->lock, flags); | 
| Paul Mundt | e108b2c | 2006-09-27 16:32:13 +0900 | [diff] [blame] | 1919 |  | 
 | 1920 | 	return 0; | 
 | 1921 | } | 
 | 1922 |  | 
| Paul Mundt | 6daa79b | 2009-06-15 07:07:38 +0900 | [diff] [blame] | 1923 | static int sci_resume(struct device *dev) | 
| Paul Mundt | e108b2c | 2006-09-27 16:32:13 +0900 | [diff] [blame] | 1924 | { | 
| Paul Mundt | 6daa79b | 2009-06-15 07:07:38 +0900 | [diff] [blame] | 1925 | 	struct sh_sci_priv *priv = dev_get_drvdata(dev); | 
| Magnus Damm | e552de2 | 2009-01-21 15:13:42 +0000 | [diff] [blame] | 1926 | 	struct sci_port *p; | 
 | 1927 | 	unsigned long flags; | 
| Paul Mundt | e108b2c | 2006-09-27 16:32:13 +0900 | [diff] [blame] | 1928 |  | 
| Magnus Damm | e552de2 | 2009-01-21 15:13:42 +0000 | [diff] [blame] | 1929 | 	spin_lock_irqsave(&priv->lock, flags); | 
 | 1930 | 	list_for_each_entry(p, &priv->ports, node) | 
 | 1931 | 		uart_resume_port(&sci_uart_driver, &p->port); | 
| Magnus Damm | e552de2 | 2009-01-21 15:13:42 +0000 | [diff] [blame] | 1932 | 	spin_unlock_irqrestore(&priv->lock, flags); | 
| Paul Mundt | e108b2c | 2006-09-27 16:32:13 +0900 | [diff] [blame] | 1933 |  | 
 | 1934 | 	return 0; | 
 | 1935 | } | 
 | 1936 |  | 
| Alexey Dobriyan | 4714521 | 2009-12-14 18:00:08 -0800 | [diff] [blame] | 1937 | static const struct dev_pm_ops sci_dev_pm_ops = { | 
| Paul Mundt | 6daa79b | 2009-06-15 07:07:38 +0900 | [diff] [blame] | 1938 | 	.suspend	= sci_suspend, | 
 | 1939 | 	.resume		= sci_resume, | 
 | 1940 | }; | 
 | 1941 |  | 
| Paul Mundt | e108b2c | 2006-09-27 16:32:13 +0900 | [diff] [blame] | 1942 | static struct platform_driver sci_driver = { | 
 | 1943 | 	.probe		= sci_probe, | 
| Uwe Kleine-König | b9e39c8 | 2009-11-24 22:07:32 +0100 | [diff] [blame] | 1944 | 	.remove		= sci_remove, | 
| Paul Mundt | e108b2c | 2006-09-27 16:32:13 +0900 | [diff] [blame] | 1945 | 	.driver		= { | 
 | 1946 | 		.name	= "sh-sci", | 
 | 1947 | 		.owner	= THIS_MODULE, | 
| Paul Mundt | 6daa79b | 2009-06-15 07:07:38 +0900 | [diff] [blame] | 1948 | 		.pm	= &sci_dev_pm_ops, | 
| Paul Mundt | e108b2c | 2006-09-27 16:32:13 +0900 | [diff] [blame] | 1949 | 	}, | 
 | 1950 | }; | 
 | 1951 |  | 
 | 1952 | static int __init sci_init(void) | 
 | 1953 | { | 
 | 1954 | 	int ret; | 
 | 1955 |  | 
 | 1956 | 	printk(banner); | 
 | 1957 |  | 
| Paul Mundt | e108b2c | 2006-09-27 16:32:13 +0900 | [diff] [blame] | 1958 | 	ret = uart_register_driver(&sci_uart_driver); | 
 | 1959 | 	if (likely(ret == 0)) { | 
 | 1960 | 		ret = platform_driver_register(&sci_driver); | 
 | 1961 | 		if (unlikely(ret)) | 
 | 1962 | 			uart_unregister_driver(&sci_uart_driver); | 
 | 1963 | 	} | 
 | 1964 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1965 | 	return ret; | 
 | 1966 | } | 
 | 1967 |  | 
 | 1968 | static void __exit sci_exit(void) | 
 | 1969 | { | 
| Paul Mundt | e108b2c | 2006-09-27 16:32:13 +0900 | [diff] [blame] | 1970 | 	platform_driver_unregister(&sci_driver); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1971 | 	uart_unregister_driver(&sci_uart_driver); | 
 | 1972 | } | 
 | 1973 |  | 
| Magnus Damm | 7b6fd3b | 2009-12-14 10:24:42 +0000 | [diff] [blame] | 1974 | #ifdef CONFIG_SERIAL_SH_SCI_CONSOLE | 
 | 1975 | early_platform_init_buffer("earlyprintk", &sci_driver, | 
 | 1976 | 			   early_serial_buf, ARRAY_SIZE(early_serial_buf)); | 
 | 1977 | #endif | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1978 | module_init(sci_init); | 
 | 1979 | module_exit(sci_exit); | 
 | 1980 |  | 
| Paul Mundt | e108b2c | 2006-09-27 16:32:13 +0900 | [diff] [blame] | 1981 | MODULE_LICENSE("GPL"); | 
| Kay Sievers | e169c13 | 2008-04-15 14:34:35 -0700 | [diff] [blame] | 1982 | MODULE_ALIAS("platform:sh-sci"); |