| Feng Tang | 2251099 | 2010-06-16 14:46:09 +0100 | [diff] [blame] | 1 | /* | 
| Feng Tang | ee9b450 | 2010-09-13 15:39:48 +0800 | [diff] [blame] | 2 |  *  mrst_max3110.c - spi uart protocol driver for Maxim 3110 | 
| Feng Tang | 2251099 | 2010-06-16 14:46:09 +0100 | [diff] [blame] | 3 |  * | 
| Feng Tang | ee9b450 | 2010-09-13 15:39:48 +0800 | [diff] [blame] | 4 |  * Copyright (c) 2008-2010, Intel Corporation. | 
| Feng Tang | 2251099 | 2010-06-16 14:46:09 +0100 | [diff] [blame] | 5 |  * | 
 | 6 |  * This program is free software; you can redistribute it and/or modify it | 
 | 7 |  * under the terms and conditions of the GNU General Public License, | 
 | 8 |  * version 2, as published by the Free Software Foundation. | 
 | 9 |  * | 
 | 10 |  * This program is distributed in the hope it will be useful, but WITHOUT | 
 | 11 |  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | 
 | 12 |  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for | 
 | 13 |  * more details. | 
 | 14 |  * | 
 | 15 |  * You should have received a copy of the GNU General Public License along with | 
 | 16 |  * this program; if not, write to the Free Software Foundation, Inc., | 
 | 17 |  * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. | 
 | 18 |  */ | 
 | 19 |  | 
 | 20 | /* | 
 | 21 |  * Note: | 
 | 22 |  * 1. From Max3110 spec, the Rx FIFO has 8 words, while the Tx FIFO only has | 
 | 23 |  *    1 word. If SPI master controller doesn't support sclk frequency change, | 
 | 24 |  *    then the char need be sent out one by one with some delay | 
 | 25 |  * | 
 | 26 |  * 2. Currently only RX availabe interrrupt is used, no need for waiting TXE | 
 | 27 |  *    interrupt for a low speed UART device | 
 | 28 |  */ | 
 | 29 |  | 
 | 30 | #include <linux/module.h> | 
 | 31 | #include <linux/ioport.h> | 
| Andrew Morton | c044391 | 2010-09-30 15:15:29 -0700 | [diff] [blame] | 32 | #include <linux/irq.h> | 
| Feng Tang | 2251099 | 2010-06-16 14:46:09 +0100 | [diff] [blame] | 33 | #include <linux/init.h> | 
 | 34 | #include <linux/console.h> | 
| Feng Tang | 2251099 | 2010-06-16 14:46:09 +0100 | [diff] [blame] | 35 | #include <linux/tty.h> | 
 | 36 | #include <linux/tty_flip.h> | 
 | 37 | #include <linux/serial_core.h> | 
 | 38 | #include <linux/serial_reg.h> | 
 | 39 |  | 
 | 40 | #include <linux/kthread.h> | 
| Feng Tang | 2251099 | 2010-06-16 14:46:09 +0100 | [diff] [blame] | 41 | #include <linux/spi/spi.h> | 
| Feng Tang | 2251099 | 2010-06-16 14:46:09 +0100 | [diff] [blame] | 42 |  | 
 | 43 | #include "mrst_max3110.h" | 
 | 44 |  | 
 | 45 | #define PR_FMT	"mrst_max3110: " | 
 | 46 |  | 
| Arjan van de Ven | d6e679b | 2010-06-17 11:02:15 +0100 | [diff] [blame] | 47 | #define UART_TX_NEEDED 1 | 
 | 48 | #define CON_TX_NEEDED  2 | 
 | 49 | #define BIT_IRQ_PENDING    3 | 
 | 50 |  | 
| Feng Tang | 2251099 | 2010-06-16 14:46:09 +0100 | [diff] [blame] | 51 | struct uart_max3110 { | 
 | 52 | 	struct uart_port port; | 
 | 53 | 	struct spi_device *spi; | 
| Feng Tang | ee9b450 | 2010-09-13 15:39:48 +0800 | [diff] [blame] | 54 | 	char name[24]; | 
| Feng Tang | 2251099 | 2010-06-16 14:46:09 +0100 | [diff] [blame] | 55 |  | 
 | 56 | 	wait_queue_head_t wq; | 
 | 57 | 	struct task_struct *main_thread; | 
 | 58 | 	struct task_struct *read_thread; | 
| Arjan van de Ven | 68c16b4 | 2010-06-17 11:02:06 +0100 | [diff] [blame] | 59 | 	struct mutex thread_mutex;; | 
| Feng Tang | 2251099 | 2010-06-16 14:46:09 +0100 | [diff] [blame] | 60 |  | 
 | 61 | 	u32 baud; | 
 | 62 | 	u16 cur_conf; | 
 | 63 | 	u8 clock; | 
 | 64 | 	u8 parity, word_7bits; | 
| Feng Tang | ee9b450 | 2010-09-13 15:39:48 +0800 | [diff] [blame] | 65 | 	u16 irq; | 
| Feng Tang | 2251099 | 2010-06-16 14:46:09 +0100 | [diff] [blame] | 66 |  | 
| Arjan van de Ven | d6e679b | 2010-06-17 11:02:15 +0100 | [diff] [blame] | 67 | 	unsigned long uart_flags; | 
| Feng Tang | 2251099 | 2010-06-16 14:46:09 +0100 | [diff] [blame] | 68 |  | 
 | 69 | 	/* console related */ | 
 | 70 | 	struct circ_buf con_xmit; | 
| Feng Tang | 2251099 | 2010-06-16 14:46:09 +0100 | [diff] [blame] | 71 | }; | 
 | 72 |  | 
 | 73 | /* global data structure, may need be removed */ | 
| Feng Tang | ee9b450 | 2010-09-13 15:39:48 +0800 | [diff] [blame] | 74 | static struct uart_max3110 *pmax; | 
 | 75 |  | 
| Feng Tang | 2251099 | 2010-06-16 14:46:09 +0100 | [diff] [blame] | 76 | static void receive_chars(struct uart_max3110 *max, | 
 | 77 | 				unsigned char *str, int len); | 
| Feng Tang | ee9b450 | 2010-09-13 15:39:48 +0800 | [diff] [blame] | 78 | static int max3110_read_multi(struct uart_max3110 *max, u8 *buf); | 
 | 79 | static void max3110_con_receive(struct uart_max3110 *max); | 
| Feng Tang | 2251099 | 2010-06-16 14:46:09 +0100 | [diff] [blame] | 80 |  | 
| Feng Tang | ee9b450 | 2010-09-13 15:39:48 +0800 | [diff] [blame] | 81 | static int max3110_write_then_read(struct uart_max3110 *max, | 
 | 82 | 		const void *txbuf, void *rxbuf, unsigned len, int always_fast) | 
| Feng Tang | 2251099 | 2010-06-16 14:46:09 +0100 | [diff] [blame] | 83 | { | 
 | 84 | 	struct spi_device *spi = max->spi; | 
 | 85 | 	struct spi_message	message; | 
 | 86 | 	struct spi_transfer	x; | 
 | 87 | 	int ret; | 
 | 88 |  | 
| Feng Tang | 2251099 | 2010-06-16 14:46:09 +0100 | [diff] [blame] | 89 | 	spi_message_init(&message); | 
 | 90 | 	memset(&x, 0, sizeof x); | 
 | 91 | 	x.len = len; | 
 | 92 | 	x.tx_buf = txbuf; | 
 | 93 | 	x.rx_buf = rxbuf; | 
 | 94 | 	spi_message_add_tail(&x, &message); | 
 | 95 |  | 
 | 96 | 	if (always_fast) | 
| Feng Tang | ee9b450 | 2010-09-13 15:39:48 +0800 | [diff] [blame] | 97 | 		x.speed_hz = spi->max_speed_hz; | 
| Feng Tang | 2251099 | 2010-06-16 14:46:09 +0100 | [diff] [blame] | 98 | 	else if (max->baud) | 
 | 99 | 		x.speed_hz = max->baud; | 
 | 100 |  | 
 | 101 | 	/* Do the i/o */ | 
 | 102 | 	ret = spi_sync(spi, &message); | 
 | 103 | 	return ret; | 
 | 104 | } | 
 | 105 |  | 
| Feng Tang | ee9b450 | 2010-09-13 15:39:48 +0800 | [diff] [blame] | 106 | /* Write a 16b word to the device */ | 
 | 107 | static int max3110_out(struct uart_max3110 *max, const u16 out) | 
| Feng Tang | 2251099 | 2010-06-16 14:46:09 +0100 | [diff] [blame] | 108 | { | 
| Feng Tang | ee9b450 | 2010-09-13 15:39:48 +0800 | [diff] [blame] | 109 | 	void *buf; | 
 | 110 | 	u16 *obuf, *ibuf; | 
 | 111 | 	u8  ch; | 
| Feng Tang | 2251099 | 2010-06-16 14:46:09 +0100 | [diff] [blame] | 112 | 	int ret; | 
 | 113 |  | 
| Feng Tang | ee9b450 | 2010-09-13 15:39:48 +0800 | [diff] [blame] | 114 | 	buf = kzalloc(8, GFP_KERNEL | GFP_DMA); | 
 | 115 | 	if (!buf) | 
 | 116 | 		return -ENOMEM; | 
 | 117 |  | 
 | 118 | 	obuf = buf; | 
 | 119 | 	ibuf = buf + 4; | 
 | 120 | 	*obuf = out; | 
 | 121 | 	ret = max3110_write_then_read(max, obuf, ibuf, 2, 1); | 
 | 122 | 	if (ret) { | 
 | 123 | 		pr_warning(PR_FMT "%s(): get err msg %d when sending 0x%x\n", | 
 | 124 | 				__func__, ret, out); | 
 | 125 | 		goto exit; | 
 | 126 | 	} | 
| Feng Tang | 2251099 | 2010-06-16 14:46:09 +0100 | [diff] [blame] | 127 |  | 
 | 128 | 	/* If some valid data is read back */ | 
| Feng Tang | ee9b450 | 2010-09-13 15:39:48 +0800 | [diff] [blame] | 129 | 	if (*ibuf & MAX3110_READ_DATA_AVAILABLE) { | 
 | 130 | 		ch = *ibuf & 0xff; | 
 | 131 | 		receive_chars(max, &ch, 1); | 
 | 132 | 	} | 
| Feng Tang | 2251099 | 2010-06-16 14:46:09 +0100 | [diff] [blame] | 133 |  | 
| Feng Tang | ee9b450 | 2010-09-13 15:39:48 +0800 | [diff] [blame] | 134 | exit: | 
 | 135 | 	kfree(buf); | 
| Feng Tang | 2251099 | 2010-06-16 14:46:09 +0100 | [diff] [blame] | 136 | 	return ret; | 
 | 137 | } | 
 | 138 |  | 
| Feng Tang | 2251099 | 2010-06-16 14:46:09 +0100 | [diff] [blame] | 139 | /* | 
 | 140 |  * This is usually used to read data from SPIC RX FIFO, which doesn't | 
| Feng Tang | ee9b450 | 2010-09-13 15:39:48 +0800 | [diff] [blame] | 141 |  * need any delay like flushing character out. | 
 | 142 |  * | 
 | 143 |  * Return how many valide bytes are read back | 
| Feng Tang | 2251099 | 2010-06-16 14:46:09 +0100 | [diff] [blame] | 144 |  */ | 
| Feng Tang | ee9b450 | 2010-09-13 15:39:48 +0800 | [diff] [blame] | 145 | static int max3110_read_multi(struct uart_max3110 *max, u8 *rxbuf) | 
| Feng Tang | 2251099 | 2010-06-16 14:46:09 +0100 | [diff] [blame] | 146 | { | 
| Feng Tang | ee9b450 | 2010-09-13 15:39:48 +0800 | [diff] [blame] | 147 | 	void *buf; | 
 | 148 | 	u16 *obuf, *ibuf; | 
 | 149 | 	u8 *pbuf, valid_str[M3110_RX_FIFO_DEPTH]; | 
 | 150 | 	int i, j, blen; | 
| Feng Tang | 2251099 | 2010-06-16 14:46:09 +0100 | [diff] [blame] | 151 |  | 
| Feng Tang | ee9b450 | 2010-09-13 15:39:48 +0800 | [diff] [blame] | 152 | 	blen = M3110_RX_FIFO_DEPTH * sizeof(u16); | 
 | 153 | 	buf = kzalloc(blen * 2, GFP_KERNEL | GFP_DMA); | 
 | 154 | 	if (!buf) { | 
 | 155 | 		pr_warning(PR_FMT "%s(): fail to alloc dma buffer\n", __func__); | 
| Feng Tang | 2251099 | 2010-06-16 14:46:09 +0100 | [diff] [blame] | 156 | 		return 0; | 
 | 157 | 	} | 
 | 158 |  | 
| Feng Tang | ee9b450 | 2010-09-13 15:39:48 +0800 | [diff] [blame] | 159 | 	/* tx/rx always have the same length */ | 
 | 160 | 	obuf = buf; | 
 | 161 | 	ibuf = buf + blen; | 
| Feng Tang | 2251099 | 2010-06-16 14:46:09 +0100 | [diff] [blame] | 162 |  | 
| Feng Tang | ee9b450 | 2010-09-13 15:39:48 +0800 | [diff] [blame] | 163 | 	if (max3110_write_then_read(max, obuf, ibuf, blen, 1)) { | 
 | 164 | 		kfree(buf); | 
| Feng Tang | 2251099 | 2010-06-16 14:46:09 +0100 | [diff] [blame] | 165 | 		return 0; | 
| Feng Tang | ee9b450 | 2010-09-13 15:39:48 +0800 | [diff] [blame] | 166 | 	} | 
| Feng Tang | 2251099 | 2010-06-16 14:46:09 +0100 | [diff] [blame] | 167 |  | 
| Feng Tang | ee9b450 | 2010-09-13 15:39:48 +0800 | [diff] [blame] | 168 | 	/* If caller doesn't provide a buffer, then handle received char */ | 
 | 169 | 	pbuf = rxbuf ? rxbuf : valid_str; | 
| Feng Tang | 2251099 | 2010-06-16 14:46:09 +0100 | [diff] [blame] | 170 |  | 
| Feng Tang | ee9b450 | 2010-09-13 15:39:48 +0800 | [diff] [blame] | 171 | 	for (i = 0, j = 0; i < M3110_RX_FIFO_DEPTH; i++) { | 
 | 172 | 		if (ibuf[i] & MAX3110_READ_DATA_AVAILABLE) | 
 | 173 | 			pbuf[j++] = ibuf[i] & 0xff; | 
| Feng Tang | 2251099 | 2010-06-16 14:46:09 +0100 | [diff] [blame] | 174 | 	} | 
 | 175 |  | 
 | 176 | 	if (j && (pbuf == valid_str)) | 
 | 177 | 		receive_chars(max, valid_str, j); | 
 | 178 |  | 
| Feng Tang | ee9b450 | 2010-09-13 15:39:48 +0800 | [diff] [blame] | 179 | 	kfree(buf); | 
| Feng Tang | 2251099 | 2010-06-16 14:46:09 +0100 | [diff] [blame] | 180 | 	return j; | 
 | 181 | } | 
 | 182 |  | 
 | 183 | static void serial_m3110_con_putchar(struct uart_port *port, int ch) | 
 | 184 | { | 
 | 185 | 	struct uart_max3110 *max = | 
 | 186 | 		container_of(port, struct uart_max3110, port); | 
 | 187 | 	struct circ_buf *xmit = &max->con_xmit; | 
 | 188 |  | 
 | 189 | 	if (uart_circ_chars_free(xmit)) { | 
 | 190 | 		xmit->buf[xmit->head] = (char)ch; | 
 | 191 | 		xmit->head = (xmit->head + 1) & (PAGE_SIZE - 1); | 
 | 192 | 	} | 
| Feng Tang | 2251099 | 2010-06-16 14:46:09 +0100 | [diff] [blame] | 193 | } | 
 | 194 |  | 
 | 195 | /* | 
 | 196 |  * Print a string to the serial port trying not to disturb | 
 | 197 |  * any possible real use of the port... | 
 | 198 |  * | 
 | 199 |  *	The console_lock must be held when we get here. | 
 | 200 |  */ | 
 | 201 | static void serial_m3110_con_write(struct console *co, | 
 | 202 | 				const char *s, unsigned int count) | 
 | 203 | { | 
 | 204 | 	if (!pmax) | 
 | 205 | 		return; | 
 | 206 |  | 
 | 207 | 	uart_console_write(&pmax->port, s, count, serial_m3110_con_putchar); | 
| Feng Tang | ee9b450 | 2010-09-13 15:39:48 +0800 | [diff] [blame] | 208 |  | 
 | 209 | 	if (!test_and_set_bit(CON_TX_NEEDED, &pmax->uart_flags)) | 
 | 210 | 		wake_up_process(pmax->main_thread); | 
| Feng Tang | 2251099 | 2010-06-16 14:46:09 +0100 | [diff] [blame] | 211 | } | 
 | 212 |  | 
 | 213 | static int __init | 
 | 214 | serial_m3110_con_setup(struct console *co, char *options) | 
 | 215 | { | 
 | 216 | 	struct uart_max3110 *max = pmax; | 
 | 217 | 	int baud = 115200; | 
 | 218 | 	int bits = 8; | 
 | 219 | 	int parity = 'n'; | 
 | 220 | 	int flow = 'n'; | 
 | 221 |  | 
 | 222 | 	pr_info(PR_FMT "setting up console\n"); | 
 | 223 |  | 
| Feng Tang | ee9b450 | 2010-09-13 15:39:48 +0800 | [diff] [blame] | 224 | 	if (co->index == -1) | 
 | 225 | 		co->index = 0; | 
 | 226 |  | 
| Feng Tang | 2251099 | 2010-06-16 14:46:09 +0100 | [diff] [blame] | 227 | 	if (!max) { | 
 | 228 | 		pr_err(PR_FMT "pmax is NULL, return"); | 
 | 229 | 		return -ENODEV; | 
 | 230 | 	} | 
 | 231 |  | 
 | 232 | 	if (options) | 
 | 233 | 		uart_parse_options(options, &baud, &parity, &bits, &flow); | 
 | 234 |  | 
 | 235 | 	return uart_set_options(&max->port, co, baud, parity, bits, flow); | 
 | 236 | } | 
 | 237 |  | 
 | 238 | static struct tty_driver *serial_m3110_con_device(struct console *co, | 
 | 239 | 							int *index) | 
 | 240 | { | 
 | 241 | 	struct uart_driver *p = co->data; | 
 | 242 | 	*index = co->index; | 
 | 243 | 	return p->tty_driver; | 
 | 244 | } | 
 | 245 |  | 
 | 246 | static struct uart_driver serial_m3110_reg; | 
 | 247 | static struct console serial_m3110_console = { | 
 | 248 | 	.name		= "ttyS", | 
 | 249 | 	.write		= serial_m3110_con_write, | 
 | 250 | 	.device		= serial_m3110_con_device, | 
 | 251 | 	.setup		= serial_m3110_con_setup, | 
 | 252 | 	.flags		= CON_PRINTBUFFER, | 
 | 253 | 	.index		= -1, | 
 | 254 | 	.data		= &serial_m3110_reg, | 
 | 255 | }; | 
 | 256 |  | 
| Feng Tang | 2251099 | 2010-06-16 14:46:09 +0100 | [diff] [blame] | 257 | static unsigned int serial_m3110_tx_empty(struct uart_port *port) | 
 | 258 | { | 
 | 259 | 	return 1; | 
 | 260 | } | 
 | 261 |  | 
 | 262 | static void serial_m3110_stop_tx(struct uart_port *port) | 
 | 263 | { | 
 | 264 | 	return; | 
 | 265 | } | 
 | 266 |  | 
 | 267 | /* stop_rx will be called in spin_lock env */ | 
 | 268 | static void serial_m3110_stop_rx(struct uart_port *port) | 
 | 269 | { | 
 | 270 | 	return; | 
 | 271 | } | 
 | 272 |  | 
 | 273 | #define WORDS_PER_XFER	128 | 
| Feng Tang | ee9b450 | 2010-09-13 15:39:48 +0800 | [diff] [blame] | 274 | static void send_circ_buf(struct uart_max3110 *max, | 
| Feng Tang | 2251099 | 2010-06-16 14:46:09 +0100 | [diff] [blame] | 275 | 				struct circ_buf *xmit) | 
 | 276 | { | 
| Feng Tang | ee9b450 | 2010-09-13 15:39:48 +0800 | [diff] [blame] | 277 | 	void *buf; | 
 | 278 | 	u16 *obuf, *ibuf; | 
| Feng Tang | 2251099 | 2010-06-16 14:46:09 +0100 | [diff] [blame] | 279 | 	u8 valid_str[WORDS_PER_XFER]; | 
| Feng Tang | ee9b450 | 2010-09-13 15:39:48 +0800 | [diff] [blame] | 280 | 	int i, j, len, blen, dma_size, left, ret = 0; | 
 | 281 |  | 
 | 282 |  | 
 | 283 | 	dma_size = WORDS_PER_XFER * sizeof(u16) * 2; | 
 | 284 | 	buf = kzalloc(dma_size, GFP_KERNEL | GFP_DMA); | 
 | 285 | 	if (!buf) | 
 | 286 | 		return; | 
 | 287 | 	obuf = buf; | 
 | 288 | 	ibuf = buf + dma_size/2; | 
| Feng Tang | 2251099 | 2010-06-16 14:46:09 +0100 | [diff] [blame] | 289 |  | 
 | 290 | 	while (!uart_circ_empty(xmit)) { | 
 | 291 | 		left = uart_circ_chars_pending(xmit); | 
 | 292 | 		while (left) { | 
| Feng Tang | ee9b450 | 2010-09-13 15:39:48 +0800 | [diff] [blame] | 293 | 			len = min(left, WORDS_PER_XFER); | 
 | 294 | 			blen = len * sizeof(u16); | 
 | 295 | 			memset(ibuf, 0, blen); | 
| Feng Tang | 2251099 | 2010-06-16 14:46:09 +0100 | [diff] [blame] | 296 |  | 
| Feng Tang | 2251099 | 2010-06-16 14:46:09 +0100 | [diff] [blame] | 297 | 			for (i = 0; i < len; i++) { | 
 | 298 | 				obuf[i] = (u8)xmit->buf[xmit->tail] | WD_TAG; | 
 | 299 | 				xmit->tail = (xmit->tail + 1) & | 
 | 300 | 						(UART_XMIT_SIZE - 1); | 
 | 301 | 			} | 
| Feng Tang | ee9b450 | 2010-09-13 15:39:48 +0800 | [diff] [blame] | 302 |  | 
 | 303 | 			/* Fail to send msg to console is not very critical */ | 
 | 304 | 			ret = max3110_write_then_read(max, obuf, ibuf, blen, 0); | 
 | 305 | 			if (ret) | 
 | 306 | 				pr_warning(PR_FMT "%s(): get err msg %d\n", | 
 | 307 | 						__func__, ret); | 
| Feng Tang | 2251099 | 2010-06-16 14:46:09 +0100 | [diff] [blame] | 308 |  | 
 | 309 | 			for (i = 0, j = 0; i < len; i++) { | 
 | 310 | 				if (ibuf[i] & MAX3110_READ_DATA_AVAILABLE) | 
| Feng Tang | ee9b450 | 2010-09-13 15:39:48 +0800 | [diff] [blame] | 311 | 					valid_str[j++] = ibuf[i] & 0xff; | 
| Feng Tang | 2251099 | 2010-06-16 14:46:09 +0100 | [diff] [blame] | 312 | 			} | 
 | 313 |  | 
 | 314 | 			if (j) | 
 | 315 | 				receive_chars(max, valid_str, j); | 
 | 316 |  | 
 | 317 | 			max->port.icount.tx += len; | 
 | 318 | 			left -= len; | 
 | 319 | 		} | 
 | 320 | 	} | 
| Feng Tang | ee9b450 | 2010-09-13 15:39:48 +0800 | [diff] [blame] | 321 |  | 
 | 322 | 	kfree(buf); | 
| Feng Tang | 2251099 | 2010-06-16 14:46:09 +0100 | [diff] [blame] | 323 | } | 
 | 324 |  | 
 | 325 | static void transmit_char(struct uart_max3110 *max) | 
 | 326 | { | 
 | 327 | 	struct uart_port *port = &max->port; | 
 | 328 | 	struct circ_buf *xmit = &port->state->xmit; | 
 | 329 |  | 
 | 330 | 	if (uart_circ_empty(xmit) || uart_tx_stopped(port)) | 
 | 331 | 		return; | 
 | 332 |  | 
 | 333 | 	send_circ_buf(max, xmit); | 
 | 334 |  | 
 | 335 | 	if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) | 
 | 336 | 		uart_write_wakeup(port); | 
 | 337 |  | 
 | 338 | 	if (uart_circ_empty(xmit)) | 
 | 339 | 		serial_m3110_stop_tx(port); | 
 | 340 | } | 
 | 341 |  | 
| Feng Tang | ee9b450 | 2010-09-13 15:39:48 +0800 | [diff] [blame] | 342 | /* | 
 | 343 |  * This will be called by uart_write() and tty_write, can't | 
 | 344 |  * go to sleep | 
 | 345 |  */ | 
| Feng Tang | 2251099 | 2010-06-16 14:46:09 +0100 | [diff] [blame] | 346 | static void serial_m3110_start_tx(struct uart_port *port) | 
 | 347 | { | 
 | 348 | 	struct uart_max3110 *max = | 
 | 349 | 		container_of(port, struct uart_max3110, port); | 
 | 350 |  | 
| Arjan van de Ven | d6e679b | 2010-06-17 11:02:15 +0100 | [diff] [blame] | 351 | 	if (!test_and_set_bit(UART_TX_NEEDED, &max->uart_flags)) | 
| Feng Tang | 2251099 | 2010-06-16 14:46:09 +0100 | [diff] [blame] | 352 | 		wake_up_process(max->main_thread); | 
| Feng Tang | 2251099 | 2010-06-16 14:46:09 +0100 | [diff] [blame] | 353 | } | 
 | 354 |  | 
 | 355 | static void receive_chars(struct uart_max3110 *max, unsigned char *str, int len) | 
 | 356 | { | 
 | 357 | 	struct uart_port *port = &max->port; | 
 | 358 | 	struct tty_struct *tty; | 
 | 359 | 	int usable; | 
 | 360 |  | 
 | 361 | 	/* If uart is not opened, just return */ | 
 | 362 | 	if (!port->state) | 
 | 363 | 		return; | 
 | 364 |  | 
 | 365 | 	tty = port->state->port.tty; | 
 | 366 | 	if (!tty) | 
| Feng Tang | ee9b450 | 2010-09-13 15:39:48 +0800 | [diff] [blame] | 367 | 		return; | 
| Feng Tang | 2251099 | 2010-06-16 14:46:09 +0100 | [diff] [blame] | 368 |  | 
 | 369 | 	while (len) { | 
 | 370 | 		usable = tty_buffer_request_room(tty, len); | 
 | 371 | 		if (usable) { | 
 | 372 | 			tty_insert_flip_string(tty, str, usable); | 
 | 373 | 			str += usable; | 
 | 374 | 			port->icount.rx += usable; | 
| Feng Tang | 2251099 | 2010-06-16 14:46:09 +0100 | [diff] [blame] | 375 | 		} | 
 | 376 | 		len -= usable; | 
 | 377 | 	} | 
| Feng Tang | ee9b450 | 2010-09-13 15:39:48 +0800 | [diff] [blame] | 378 | 	tty_flip_buffer_push(tty); | 
| Feng Tang | 2251099 | 2010-06-16 14:46:09 +0100 | [diff] [blame] | 379 | } | 
 | 380 |  | 
| Feng Tang | ee9b450 | 2010-09-13 15:39:48 +0800 | [diff] [blame] | 381 | /* | 
 | 382 |  * This routine will be used in read_thread or RX IRQ handling, | 
 | 383 |  * it will first do one round buffer read(8 words), if there is some | 
 | 384 |  * valid RX data, will try to read 5 more rounds till all data | 
 | 385 |  * is read out. | 
 | 386 |  * | 
 | 387 |  * Use stack space as data buffer to save some system load, and chose | 
 | 388 |  * 504 Btyes as a threadhold to do a bulk push to upper tty layer when | 
 | 389 |  * receiving bulk data, a much bigger buffer may cause stack overflow | 
 | 390 |  */ | 
 | 391 | static void max3110_con_receive(struct uart_max3110 *max) | 
| Feng Tang | 2251099 | 2010-06-16 14:46:09 +0100 | [diff] [blame] | 392 | { | 
 | 393 | 	int loop = 1, num, total = 0; | 
 | 394 | 	u8 recv_buf[512], *pbuf; | 
 | 395 |  | 
 | 396 | 	pbuf = recv_buf; | 
 | 397 | 	do { | 
| Feng Tang | ee9b450 | 2010-09-13 15:39:48 +0800 | [diff] [blame] | 398 | 		num = max3110_read_multi(max, pbuf); | 
| Feng Tang | 2251099 | 2010-06-16 14:46:09 +0100 | [diff] [blame] | 399 |  | 
 | 400 | 		if (num) { | 
| Feng Tang | ee9b450 | 2010-09-13 15:39:48 +0800 | [diff] [blame] | 401 | 			loop = 5; | 
| Feng Tang | 2251099 | 2010-06-16 14:46:09 +0100 | [diff] [blame] | 402 | 			pbuf += num; | 
 | 403 | 			total += num; | 
 | 404 |  | 
| Feng Tang | ee9b450 | 2010-09-13 15:39:48 +0800 | [diff] [blame] | 405 | 			if (total >= 504) { | 
| Feng Tang | 2251099 | 2010-06-16 14:46:09 +0100 | [diff] [blame] | 406 | 				receive_chars(max, recv_buf, total); | 
 | 407 | 				pbuf = recv_buf; | 
 | 408 | 				total = 0; | 
 | 409 | 			} | 
 | 410 | 		} | 
 | 411 | 	} while (--loop); | 
 | 412 |  | 
 | 413 | 	if (total) | 
 | 414 | 		receive_chars(max, recv_buf, total); | 
 | 415 | } | 
 | 416 |  | 
 | 417 | static int max3110_main_thread(void *_max) | 
 | 418 | { | 
 | 419 | 	struct uart_max3110 *max = _max; | 
 | 420 | 	wait_queue_head_t *wq = &max->wq; | 
 | 421 | 	int ret = 0; | 
 | 422 | 	struct circ_buf *xmit = &max->con_xmit; | 
 | 423 |  | 
 | 424 | 	init_waitqueue_head(wq); | 
 | 425 | 	pr_info(PR_FMT "start main thread\n"); | 
 | 426 |  | 
 | 427 | 	do { | 
| Arjan van de Ven | d6e679b | 2010-06-17 11:02:15 +0100 | [diff] [blame] | 428 | 		wait_event_interruptible(*wq, max->uart_flags || kthread_should_stop()); | 
| Arjan van de Ven | 68c16b4 | 2010-06-17 11:02:06 +0100 | [diff] [blame] | 429 |  | 
 | 430 | 		mutex_lock(&max->thread_mutex); | 
| Feng Tang | 2251099 | 2010-06-16 14:46:09 +0100 | [diff] [blame] | 431 |  | 
| Arjan van de Ven | d6e679b | 2010-06-17 11:02:15 +0100 | [diff] [blame] | 432 | 		if (test_and_clear_bit(BIT_IRQ_PENDING, &max->uart_flags)) | 
| Feng Tang | ee9b450 | 2010-09-13 15:39:48 +0800 | [diff] [blame] | 433 | 			max3110_con_receive(max); | 
| Feng Tang | 2251099 | 2010-06-16 14:46:09 +0100 | [diff] [blame] | 434 |  | 
 | 435 | 		/* first handle console output */ | 
| Arjan van de Ven | d6e679b | 2010-06-17 11:02:15 +0100 | [diff] [blame] | 436 | 		if (test_and_clear_bit(CON_TX_NEEDED, &max->uart_flags)) | 
| Feng Tang | 2251099 | 2010-06-16 14:46:09 +0100 | [diff] [blame] | 437 | 			send_circ_buf(max, xmit); | 
| Feng Tang | 2251099 | 2010-06-16 14:46:09 +0100 | [diff] [blame] | 438 |  | 
 | 439 | 		/* handle uart output */ | 
| Arjan van de Ven | d6e679b | 2010-06-17 11:02:15 +0100 | [diff] [blame] | 440 | 		if (test_and_clear_bit(UART_TX_NEEDED, &max->uart_flags)) | 
| Feng Tang | 2251099 | 2010-06-16 14:46:09 +0100 | [diff] [blame] | 441 | 			transmit_char(max); | 
| Arjan van de Ven | d6e679b | 2010-06-17 11:02:15 +0100 | [diff] [blame] | 442 |  | 
| Arjan van de Ven | 68c16b4 | 2010-06-17 11:02:06 +0100 | [diff] [blame] | 443 | 		mutex_unlock(&max->thread_mutex); | 
| Arjan van de Ven | d6e679b | 2010-06-17 11:02:15 +0100 | [diff] [blame] | 444 |  | 
| Feng Tang | 2251099 | 2010-06-16 14:46:09 +0100 | [diff] [blame] | 445 | 	} while (!kthread_should_stop()); | 
 | 446 |  | 
 | 447 | 	return ret; | 
 | 448 | } | 
 | 449 |  | 
| Feng Tang | 2251099 | 2010-06-16 14:46:09 +0100 | [diff] [blame] | 450 | static irqreturn_t serial_m3110_irq(int irq, void *dev_id) | 
 | 451 | { | 
 | 452 | 	struct uart_max3110 *max = dev_id; | 
 | 453 |  | 
 | 454 | 	/* max3110's irq is a falling edge, not level triggered, | 
 | 455 | 	 * so no need to disable the irq */ | 
| Arjan van de Ven | d6e679b | 2010-06-17 11:02:15 +0100 | [diff] [blame] | 456 | 	if (!test_and_set_bit(BIT_IRQ_PENDING, &max->uart_flags)) | 
| Feng Tang | 2251099 | 2010-06-16 14:46:09 +0100 | [diff] [blame] | 457 | 		wake_up_process(max->main_thread); | 
| Arjan van de Ven | d6e679b | 2010-06-17 11:02:15 +0100 | [diff] [blame] | 458 |  | 
| Feng Tang | 2251099 | 2010-06-16 14:46:09 +0100 | [diff] [blame] | 459 | 	return IRQ_HANDLED; | 
 | 460 | } | 
| Alan Cox | 91efa75 | 2010-09-13 15:39:56 +0800 | [diff] [blame] | 461 |  | 
| Feng Tang | 2251099 | 2010-06-16 14:46:09 +0100 | [diff] [blame] | 462 | /* if don't use RX IRQ, then need a thread to polling read */ | 
 | 463 | static int max3110_read_thread(void *_max) | 
 | 464 | { | 
 | 465 | 	struct uart_max3110 *max = _max; | 
 | 466 |  | 
 | 467 | 	pr_info(PR_FMT "start read thread\n"); | 
 | 468 | 	do { | 
| Feng Tang | ee9b450 | 2010-09-13 15:39:48 +0800 | [diff] [blame] | 469 | 		/* | 
 | 470 | 		 * If can't acquire the mutex, it means the main thread | 
 | 471 | 		 * is running which will also perform the rx job | 
 | 472 | 		 */ | 
 | 473 | 		if (mutex_trylock(&max->thread_mutex)) { | 
 | 474 | 			max3110_con_receive(max); | 
 | 475 | 			mutex_unlock(&max->thread_mutex); | 
 | 476 | 		} | 
| Feng Tang | 2251099 | 2010-06-16 14:46:09 +0100 | [diff] [blame] | 477 |  | 
 | 478 | 		set_current_state(TASK_INTERRUPTIBLE); | 
 | 479 | 		schedule_timeout(HZ / 20); | 
 | 480 | 	} while (!kthread_should_stop()); | 
 | 481 |  | 
 | 482 | 	return 0; | 
 | 483 | } | 
| Feng Tang | 2251099 | 2010-06-16 14:46:09 +0100 | [diff] [blame] | 484 |  | 
 | 485 | static int serial_m3110_startup(struct uart_port *port) | 
 | 486 | { | 
 | 487 | 	struct uart_max3110 *max = | 
 | 488 | 		container_of(port, struct uart_max3110, port); | 
 | 489 | 	u16 config = 0; | 
 | 490 | 	int ret = 0; | 
 | 491 |  | 
| Feng Tang | ee9b450 | 2010-09-13 15:39:48 +0800 | [diff] [blame] | 492 | 	if (port->line != 0) { | 
| Feng Tang | 2251099 | 2010-06-16 14:46:09 +0100 | [diff] [blame] | 493 | 		pr_err(PR_FMT "uart port startup failed\n"); | 
| Feng Tang | ee9b450 | 2010-09-13 15:39:48 +0800 | [diff] [blame] | 494 | 		return -1; | 
 | 495 | 	} | 
| Feng Tang | 2251099 | 2010-06-16 14:46:09 +0100 | [diff] [blame] | 496 |  | 
| Feng Tang | ee9b450 | 2010-09-13 15:39:48 +0800 | [diff] [blame] | 497 | 	/* Disable all IRQ and config it to 115200, 8n1 */ | 
| Feng Tang | 2251099 | 2010-06-16 14:46:09 +0100 | [diff] [blame] | 498 | 	config = WC_TAG | WC_FIFO_ENABLE | 
 | 499 | 			| WC_1_STOPBITS | 
 | 500 | 			| WC_8BIT_WORD | 
 | 501 | 			| WC_BAUD_DR2; | 
| Feng Tang | 2251099 | 2010-06-16 14:46:09 +0100 | [diff] [blame] | 502 |  | 
 | 503 | 	/* as we use thread to handle tx/rx, need set low latency */ | 
 | 504 | 	port->state->port.tty->low_latency = 1; | 
 | 505 |  | 
| Alan Cox | 91efa75 | 2010-09-13 15:39:56 +0800 | [diff] [blame] | 506 | 	if (max->irq) { | 
| Feng Tang | ee9b450 | 2010-09-13 15:39:48 +0800 | [diff] [blame] | 507 | 		max->read_thread = NULL; | 
| Alan Cox | 91efa75 | 2010-09-13 15:39:56 +0800 | [diff] [blame] | 508 | 		ret = request_irq(max->irq, serial_m3110_irq, | 
 | 509 | 				IRQ_TYPE_EDGE_FALLING, "max3110", max); | 
 | 510 | 		if (ret) { | 
 | 511 | 			max->irq = 0; | 
 | 512 | 			pr_err(PR_FMT "unable to allocate IRQ, polling\n"); | 
 | 513 | 		}  else { | 
 | 514 | 			/* Enable RX IRQ only */ | 
 | 515 | 			config |= WC_RXA_IRQ_ENABLE; | 
 | 516 | 		} | 
| Feng Tang | ee9b450 | 2010-09-13 15:39:48 +0800 | [diff] [blame] | 517 | 	} | 
| Alan Cox | 91efa75 | 2010-09-13 15:39:56 +0800 | [diff] [blame] | 518 |  | 
 | 519 | 	if (max->irq == 0) { | 
 | 520 | 		/* If IRQ is disabled, start a read thread for input data */ | 
 | 521 | 		max->read_thread = | 
 | 522 | 			kthread_run(max3110_read_thread, max, "max3110_read"); | 
 | 523 | 		if (IS_ERR(max->read_thread)) { | 
 | 524 | 			ret = PTR_ERR(max->read_thread); | 
 | 525 | 			max->read_thread = NULL; | 
 | 526 | 			pr_err(PR_FMT "Can't create read thread!\n"); | 
 | 527 | 			return ret; | 
 | 528 | 		} | 
 | 529 | 	} | 
| Feng Tang | 2251099 | 2010-06-16 14:46:09 +0100 | [diff] [blame] | 530 |  | 
| Feng Tang | ee9b450 | 2010-09-13 15:39:48 +0800 | [diff] [blame] | 531 | 	ret = max3110_out(max, config); | 
 | 532 | 	if (ret) { | 
| Alan Cox | 91efa75 | 2010-09-13 15:39:56 +0800 | [diff] [blame] | 533 | 		if (max->irq) | 
 | 534 | 			free_irq(max->irq, max); | 
 | 535 | 		if (max->read_thread) | 
 | 536 | 			kthread_stop(max->read_thread); | 
| Feng Tang | ee9b450 | 2010-09-13 15:39:48 +0800 | [diff] [blame] | 537 | 		max->read_thread = NULL; | 
| Feng Tang | ee9b450 | 2010-09-13 15:39:48 +0800 | [diff] [blame] | 538 | 		return ret; | 
 | 539 | 	} | 
 | 540 |  | 
| Feng Tang | 2251099 | 2010-06-16 14:46:09 +0100 | [diff] [blame] | 541 | 	max->cur_conf = config; | 
 | 542 | 	return 0; | 
 | 543 | } | 
 | 544 |  | 
 | 545 | static void serial_m3110_shutdown(struct uart_port *port) | 
 | 546 | { | 
 | 547 | 	struct uart_max3110 *max = | 
 | 548 | 		container_of(port, struct uart_max3110, port); | 
 | 549 | 	u16 config; | 
 | 550 |  | 
 | 551 | 	if (max->read_thread) { | 
 | 552 | 		kthread_stop(max->read_thread); | 
 | 553 | 		max->read_thread = NULL; | 
 | 554 | 	} | 
 | 555 |  | 
| Alan Cox | 91efa75 | 2010-09-13 15:39:56 +0800 | [diff] [blame] | 556 | 	if (max->irq) | 
 | 557 | 		free_irq(max->irq, max); | 
| Feng Tang | 2251099 | 2010-06-16 14:46:09 +0100 | [diff] [blame] | 558 |  | 
 | 559 | 	/* Disable interrupts from this port */ | 
 | 560 | 	config = WC_TAG | WC_SW_SHDI; | 
 | 561 | 	max3110_out(max, config); | 
 | 562 | } | 
 | 563 |  | 
 | 564 | static void serial_m3110_release_port(struct uart_port *port) | 
 | 565 | { | 
 | 566 | } | 
 | 567 |  | 
 | 568 | static int serial_m3110_request_port(struct uart_port *port) | 
 | 569 | { | 
 | 570 | 	return 0; | 
 | 571 | } | 
 | 572 |  | 
 | 573 | static void serial_m3110_config_port(struct uart_port *port, int flags) | 
 | 574 | { | 
| Feng Tang | ee9b450 | 2010-09-13 15:39:48 +0800 | [diff] [blame] | 575 | 	port->type = PORT_MAX3100; | 
| Feng Tang | 2251099 | 2010-06-16 14:46:09 +0100 | [diff] [blame] | 576 | } | 
 | 577 |  | 
 | 578 | static int | 
 | 579 | serial_m3110_verify_port(struct uart_port *port, struct serial_struct *ser) | 
 | 580 | { | 
 | 581 | 	/* we don't want the core code to modify any port params */ | 
 | 582 | 	return -EINVAL; | 
 | 583 | } | 
 | 584 |  | 
 | 585 |  | 
 | 586 | static const char *serial_m3110_type(struct uart_port *port) | 
 | 587 | { | 
 | 588 | 	struct uart_max3110 *max = | 
 | 589 | 		container_of(port, struct uart_max3110, port); | 
 | 590 | 	return max->name; | 
 | 591 | } | 
 | 592 |  | 
 | 593 | static void | 
 | 594 | serial_m3110_set_termios(struct uart_port *port, struct ktermios *termios, | 
 | 595 | 		       struct ktermios *old) | 
 | 596 | { | 
 | 597 | 	struct uart_max3110 *max = | 
 | 598 | 		container_of(port, struct uart_max3110, port); | 
 | 599 | 	unsigned char cval; | 
 | 600 | 	unsigned int baud, parity = 0; | 
 | 601 | 	int clk_div = -1; | 
 | 602 | 	u16 new_conf = max->cur_conf; | 
 | 603 |  | 
 | 604 | 	switch (termios->c_cflag & CSIZE) { | 
 | 605 | 	case CS7: | 
 | 606 | 		cval = UART_LCR_WLEN7; | 
 | 607 | 		new_conf |= WC_7BIT_WORD; | 
 | 608 | 		break; | 
 | 609 | 	default: | 
| Feng Tang | ee9b450 | 2010-09-13 15:39:48 +0800 | [diff] [blame] | 610 | 		/* We only support CS7 & CS8 */ | 
 | 611 | 		termios->c_cflag &= ~CSIZE; | 
 | 612 | 		termios->c_cflag |= CS8; | 
| Feng Tang | 2251099 | 2010-06-16 14:46:09 +0100 | [diff] [blame] | 613 | 	case CS8: | 
 | 614 | 		cval = UART_LCR_WLEN8; | 
 | 615 | 		new_conf |= WC_8BIT_WORD; | 
 | 616 | 		break; | 
 | 617 | 	} | 
 | 618 |  | 
 | 619 | 	baud = uart_get_baud_rate(port, termios, old, 0, 230400); | 
 | 620 |  | 
| Feng Tang | ee9b450 | 2010-09-13 15:39:48 +0800 | [diff] [blame] | 621 | 	/* First calc the div for 1.8MHZ clock case */ | 
| Feng Tang | 2251099 | 2010-06-16 14:46:09 +0100 | [diff] [blame] | 622 | 	switch (baud) { | 
 | 623 | 	case 300: | 
 | 624 | 		clk_div = WC_BAUD_DR384; | 
 | 625 | 		break; | 
 | 626 | 	case 600: | 
 | 627 | 		clk_div = WC_BAUD_DR192; | 
 | 628 | 		break; | 
 | 629 | 	case 1200: | 
 | 630 | 		clk_div = WC_BAUD_DR96; | 
 | 631 | 		break; | 
 | 632 | 	case 2400: | 
 | 633 | 		clk_div = WC_BAUD_DR48; | 
 | 634 | 		break; | 
 | 635 | 	case 4800: | 
 | 636 | 		clk_div = WC_BAUD_DR24; | 
 | 637 | 		break; | 
 | 638 | 	case 9600: | 
 | 639 | 		clk_div = WC_BAUD_DR12; | 
 | 640 | 		break; | 
 | 641 | 	case 19200: | 
 | 642 | 		clk_div = WC_BAUD_DR6; | 
 | 643 | 		break; | 
 | 644 | 	case 38400: | 
 | 645 | 		clk_div = WC_BAUD_DR3; | 
 | 646 | 		break; | 
 | 647 | 	case 57600: | 
 | 648 | 		clk_div = WC_BAUD_DR2; | 
 | 649 | 		break; | 
 | 650 | 	case 115200: | 
 | 651 | 		clk_div = WC_BAUD_DR1; | 
 | 652 | 		break; | 
 | 653 | 	case 230400: | 
 | 654 | 		if (max->clock & MAX3110_HIGH_CLK) | 
 | 655 | 			break; | 
 | 656 | 	default: | 
| Feng Tang | ee9b450 | 2010-09-13 15:39:48 +0800 | [diff] [blame] | 657 | 		/* Pick the previous baud rate */ | 
| Feng Tang | 2251099 | 2010-06-16 14:46:09 +0100 | [diff] [blame] | 658 | 		baud = max->baud; | 
 | 659 | 		clk_div = max->cur_conf & WC_BAUD_DIV_MASK; | 
 | 660 | 		tty_termios_encode_baud_rate(termios, baud, baud); | 
 | 661 | 	} | 
 | 662 |  | 
 | 663 | 	if (max->clock & MAX3110_HIGH_CLK) { | 
 | 664 | 		clk_div += 1; | 
| Feng Tang | ee9b450 | 2010-09-13 15:39:48 +0800 | [diff] [blame] | 665 | 		/* High clk version max3110 doesn't support B300 */ | 
 | 666 | 		if (baud == 300) { | 
| Feng Tang | 2251099 | 2010-06-16 14:46:09 +0100 | [diff] [blame] | 667 | 			baud = 600; | 
| Feng Tang | ee9b450 | 2010-09-13 15:39:48 +0800 | [diff] [blame] | 668 | 			clk_div = WC_BAUD_DR384; | 
 | 669 | 		} | 
| Feng Tang | 2251099 | 2010-06-16 14:46:09 +0100 | [diff] [blame] | 670 | 		if (baud == 230400) | 
 | 671 | 			clk_div = WC_BAUD_DR1; | 
 | 672 | 		tty_termios_encode_baud_rate(termios, baud, baud); | 
 | 673 | 	} | 
 | 674 |  | 
 | 675 | 	new_conf = (new_conf & ~WC_BAUD_DIV_MASK) | clk_div; | 
| Feng Tang | ee9b450 | 2010-09-13 15:39:48 +0800 | [diff] [blame] | 676 |  | 
 | 677 | 	if (unlikely(termios->c_cflag & CMSPAR)) | 
 | 678 | 		termios->c_cflag &= ~CMSPAR; | 
 | 679 |  | 
| Feng Tang | 2251099 | 2010-06-16 14:46:09 +0100 | [diff] [blame] | 680 | 	if (termios->c_cflag & CSTOPB) | 
 | 681 | 		new_conf |= WC_2_STOPBITS; | 
 | 682 | 	else | 
 | 683 | 		new_conf &= ~WC_2_STOPBITS; | 
 | 684 |  | 
 | 685 | 	if (termios->c_cflag & PARENB) { | 
 | 686 | 		new_conf |= WC_PARITY_ENABLE; | 
 | 687 | 		parity |= UART_LCR_PARITY; | 
 | 688 | 	} else | 
 | 689 | 		new_conf &= ~WC_PARITY_ENABLE; | 
 | 690 |  | 
 | 691 | 	if (!(termios->c_cflag & PARODD)) | 
 | 692 | 		parity |= UART_LCR_EPAR; | 
 | 693 | 	max->parity = parity; | 
 | 694 |  | 
 | 695 | 	uart_update_timeout(port, termios->c_cflag, baud); | 
 | 696 |  | 
 | 697 | 	new_conf |= WC_TAG; | 
 | 698 | 	if (new_conf != max->cur_conf) { | 
| Feng Tang | ee9b450 | 2010-09-13 15:39:48 +0800 | [diff] [blame] | 699 | 		if (!max3110_out(max, new_conf)) { | 
 | 700 | 			max->cur_conf = new_conf; | 
 | 701 | 			max->baud = baud; | 
 | 702 | 		} | 
| Feng Tang | 2251099 | 2010-06-16 14:46:09 +0100 | [diff] [blame] | 703 | 	} | 
 | 704 | } | 
 | 705 |  | 
| Feng Tang | ee9b450 | 2010-09-13 15:39:48 +0800 | [diff] [blame] | 706 | /* Don't handle hw handshaking */ | 
| Feng Tang | 2251099 | 2010-06-16 14:46:09 +0100 | [diff] [blame] | 707 | static unsigned int serial_m3110_get_mctrl(struct uart_port *port) | 
 | 708 | { | 
 | 709 | 	return TIOCM_DSR | TIOCM_CAR | TIOCM_DSR; | 
 | 710 | } | 
 | 711 |  | 
 | 712 | static void serial_m3110_set_mctrl(struct uart_port *port, unsigned int mctrl) | 
 | 713 | { | 
 | 714 | } | 
 | 715 |  | 
 | 716 | static void serial_m3110_break_ctl(struct uart_port *port, int break_state) | 
 | 717 | { | 
 | 718 | } | 
 | 719 |  | 
 | 720 | static void serial_m3110_pm(struct uart_port *port, unsigned int state, | 
 | 721 | 			unsigned int oldstate) | 
 | 722 | { | 
 | 723 | } | 
 | 724 |  | 
 | 725 | static void serial_m3110_enable_ms(struct uart_port *port) | 
 | 726 | { | 
 | 727 | } | 
 | 728 |  | 
 | 729 | struct uart_ops serial_m3110_ops = { | 
 | 730 | 	.tx_empty	= serial_m3110_tx_empty, | 
 | 731 | 	.set_mctrl	= serial_m3110_set_mctrl, | 
 | 732 | 	.get_mctrl	= serial_m3110_get_mctrl, | 
 | 733 | 	.stop_tx	= serial_m3110_stop_tx, | 
 | 734 | 	.start_tx	= serial_m3110_start_tx, | 
 | 735 | 	.stop_rx	= serial_m3110_stop_rx, | 
 | 736 | 	.enable_ms	= serial_m3110_enable_ms, | 
 | 737 | 	.break_ctl	= serial_m3110_break_ctl, | 
 | 738 | 	.startup	= serial_m3110_startup, | 
 | 739 | 	.shutdown	= serial_m3110_shutdown, | 
| Feng Tang | ee9b450 | 2010-09-13 15:39:48 +0800 | [diff] [blame] | 740 | 	.set_termios	= serial_m3110_set_termios, | 
| Feng Tang | 2251099 | 2010-06-16 14:46:09 +0100 | [diff] [blame] | 741 | 	.pm		= serial_m3110_pm, | 
 | 742 | 	.type		= serial_m3110_type, | 
 | 743 | 	.release_port	= serial_m3110_release_port, | 
 | 744 | 	.request_port	= serial_m3110_request_port, | 
 | 745 | 	.config_port	= serial_m3110_config_port, | 
 | 746 | 	.verify_port	= serial_m3110_verify_port, | 
 | 747 | }; | 
 | 748 |  | 
 | 749 | static struct uart_driver serial_m3110_reg = { | 
 | 750 | 	.owner		= THIS_MODULE, | 
 | 751 | 	.driver_name	= "MRST serial", | 
 | 752 | 	.dev_name	= "ttyS", | 
 | 753 | 	.major		= TTY_MAJOR, | 
 | 754 | 	.minor		= 64, | 
 | 755 | 	.nr		= 1, | 
| Feng Tang | ee9b450 | 2010-09-13 15:39:48 +0800 | [diff] [blame] | 756 | 	.cons		= &serial_m3110_console, | 
| Feng Tang | 2251099 | 2010-06-16 14:46:09 +0100 | [diff] [blame] | 757 | }; | 
 | 758 |  | 
| Feng Tang | ee9b450 | 2010-09-13 15:39:48 +0800 | [diff] [blame] | 759 | #ifdef CONFIG_PM | 
| Feng Tang | 2251099 | 2010-06-16 14:46:09 +0100 | [diff] [blame] | 760 | static int serial_m3110_suspend(struct spi_device *spi, pm_message_t state) | 
 | 761 | { | 
| Feng Tang | ee9b450 | 2010-09-13 15:39:48 +0800 | [diff] [blame] | 762 | 	struct uart_max3110 *max = spi_get_drvdata(spi); | 
 | 763 |  | 
 | 764 | 	disable_irq(max->irq); | 
 | 765 | 	uart_suspend_port(&serial_m3110_reg, &max->port); | 
 | 766 | 	max3110_out(max, max->cur_conf | WC_SW_SHDI); | 
| Feng Tang | 2251099 | 2010-06-16 14:46:09 +0100 | [diff] [blame] | 767 | 	return 0; | 
 | 768 | } | 
 | 769 |  | 
 | 770 | static int serial_m3110_resume(struct spi_device *spi) | 
 | 771 | { | 
| Feng Tang | ee9b450 | 2010-09-13 15:39:48 +0800 | [diff] [blame] | 772 | 	struct uart_max3110 *max = spi_get_drvdata(spi); | 
 | 773 |  | 
 | 774 | 	max3110_out(max, max->cur_conf); | 
 | 775 | 	uart_resume_port(&serial_m3110_reg, &max->port); | 
 | 776 | 	enable_irq(max->irq); | 
| Feng Tang | 2251099 | 2010-06-16 14:46:09 +0100 | [diff] [blame] | 777 | 	return 0; | 
 | 778 | } | 
| Feng Tang | ee9b450 | 2010-09-13 15:39:48 +0800 | [diff] [blame] | 779 | #else | 
 | 780 | #define serial_m3110_suspend	NULL | 
 | 781 | #define serial_m3110_resume	NULL | 
 | 782 | #endif | 
| Feng Tang | 2251099 | 2010-06-16 14:46:09 +0100 | [diff] [blame] | 783 |  | 
| Feng Tang | ee9b450 | 2010-09-13 15:39:48 +0800 | [diff] [blame] | 784 | static int __devinit serial_m3110_probe(struct spi_device *spi) | 
| Feng Tang | 2251099 | 2010-06-16 14:46:09 +0100 | [diff] [blame] | 785 | { | 
 | 786 | 	struct uart_max3110 *max; | 
| Feng Tang | ee9b450 | 2010-09-13 15:39:48 +0800 | [diff] [blame] | 787 | 	void *buffer; | 
| jianwei.yang | 99dd3f6 | 2010-06-16 14:46:49 +0100 | [diff] [blame] | 788 | 	u16 res; | 
| Feng Tang | ee9b450 | 2010-09-13 15:39:48 +0800 | [diff] [blame] | 789 | 	int ret = 0; | 
 | 790 |  | 
| Feng Tang | 2251099 | 2010-06-16 14:46:09 +0100 | [diff] [blame] | 791 | 	max = kzalloc(sizeof(*max), GFP_KERNEL); | 
 | 792 | 	if (!max) | 
 | 793 | 		return -ENOMEM; | 
 | 794 |  | 
| Feng Tang | ee9b450 | 2010-09-13 15:39:48 +0800 | [diff] [blame] | 795 | 	/* Set spi info */ | 
| Feng Tang | 2251099 | 2010-06-16 14:46:09 +0100 | [diff] [blame] | 796 | 	spi->bits_per_word = 16; | 
 | 797 | 	max->clock = MAX3110_HIGH_CLK; | 
| Feng Tang | 2251099 | 2010-06-16 14:46:09 +0100 | [diff] [blame] | 798 |  | 
 | 799 | 	spi_setup(spi); | 
 | 800 |  | 
| Feng Tang | ee9b450 | 2010-09-13 15:39:48 +0800 | [diff] [blame] | 801 | 	max->port.type = PORT_MAX3100; | 
 | 802 | 	max->port.fifosize = 2;		/* Only have 16b buffer */ | 
| Feng Tang | 2251099 | 2010-06-16 14:46:09 +0100 | [diff] [blame] | 803 | 	max->port.ops = &serial_m3110_ops; | 
 | 804 | 	max->port.line = 0; | 
 | 805 | 	max->port.dev = &spi->dev; | 
 | 806 | 	max->port.uartclk = 115200; | 
 | 807 |  | 
 | 808 | 	max->spi = spi; | 
| Feng Tang | ee9b450 | 2010-09-13 15:39:48 +0800 | [diff] [blame] | 809 | 	strcpy(max->name, spi->modalias); | 
| Feng Tang | 2251099 | 2010-06-16 14:46:09 +0100 | [diff] [blame] | 810 | 	max->irq = (u16)spi->irq; | 
 | 811 |  | 
| Arjan van de Ven | 68c16b4 | 2010-06-17 11:02:06 +0100 | [diff] [blame] | 812 | 	mutex_init(&max->thread_mutex); | 
| Feng Tang | 2251099 | 2010-06-16 14:46:09 +0100 | [diff] [blame] | 813 |  | 
 | 814 | 	max->word_7bits = 0; | 
 | 815 | 	max->parity = 0; | 
 | 816 | 	max->baud = 0; | 
 | 817 |  | 
 | 818 | 	max->cur_conf = 0; | 
| Arjan van de Ven | d6e679b | 2010-06-17 11:02:15 +0100 | [diff] [blame] | 819 | 	max->uart_flags = 0; | 
 | 820 |  | 
| jianwei.yang | 99dd3f6 | 2010-06-16 14:46:49 +0100 | [diff] [blame] | 821 | 	/* Check if reading configuration register returns something sane */ | 
| Feng Tang | 2251099 | 2010-06-16 14:46:09 +0100 | [diff] [blame] | 822 |  | 
| jianwei.yang | 99dd3f6 | 2010-06-16 14:46:49 +0100 | [diff] [blame] | 823 | 	res = RC_TAG; | 
 | 824 | 	ret = max3110_write_then_read(max, (u8 *)&res, (u8 *)&res, 2, 0); | 
 | 825 | 	if (ret < 0 || res == 0 || res == 0xffff) { | 
 | 826 | 		printk(KERN_ERR "MAX3111 deemed not present (conf reg %04x)", | 
 | 827 | 									res); | 
 | 828 | 		ret = -ENODEV; | 
 | 829 | 		goto err_get_page; | 
 | 830 | 	} | 
| Feng Tang | ee9b450 | 2010-09-13 15:39:48 +0800 | [diff] [blame] | 831 |  | 
 | 832 | 	buffer = (void *)__get_free_page(GFP_KERNEL); | 
| Feng Tang | 2251099 | 2010-06-16 14:46:09 +0100 | [diff] [blame] | 833 | 	if (!buffer) { | 
 | 834 | 		ret = -ENOMEM; | 
 | 835 | 		goto err_get_page; | 
 | 836 | 	} | 
| Feng Tang | ee9b450 | 2010-09-13 15:39:48 +0800 | [diff] [blame] | 837 | 	max->con_xmit.buf = buffer; | 
 | 838 | 	max->con_xmit.head = 0; | 
 | 839 | 	max->con_xmit.tail = 0; | 
| Feng Tang | 2251099 | 2010-06-16 14:46:09 +0100 | [diff] [blame] | 840 |  | 
 | 841 | 	max->main_thread = kthread_run(max3110_main_thread, | 
 | 842 | 					max, "max3110_main"); | 
 | 843 | 	if (IS_ERR(max->main_thread)) { | 
 | 844 | 		ret = PTR_ERR(max->main_thread); | 
 | 845 | 		goto err_kthread; | 
 | 846 | 	} | 
 | 847 |  | 
| Feng Tang | ee9b450 | 2010-09-13 15:39:48 +0800 | [diff] [blame] | 848 | 	spi_set_drvdata(spi, max); | 
| Feng Tang | 2251099 | 2010-06-16 14:46:09 +0100 | [diff] [blame] | 849 | 	pmax = max; | 
| Feng Tang | ee9b450 | 2010-09-13 15:39:48 +0800 | [diff] [blame] | 850 |  | 
 | 851 | 	/* Give membase a psudo value to pass serial_core's check */ | 
| Feng Tang | 2251099 | 2010-06-16 14:46:09 +0100 | [diff] [blame] | 852 | 	max->port.membase = (void *)0xff110000; | 
 | 853 | 	uart_add_one_port(&serial_m3110_reg, &max->port); | 
 | 854 |  | 
 | 855 | 	return 0; | 
 | 856 |  | 
 | 857 | err_kthread: | 
 | 858 | 	free_page((unsigned long)buffer); | 
 | 859 | err_get_page: | 
| Feng Tang | 2251099 | 2010-06-16 14:46:09 +0100 | [diff] [blame] | 860 | 	kfree(max); | 
 | 861 | 	return ret; | 
 | 862 | } | 
 | 863 |  | 
| Feng Tang | ee9b450 | 2010-09-13 15:39:48 +0800 | [diff] [blame] | 864 | static int __devexit serial_m3110_remove(struct spi_device *dev) | 
| Feng Tang | 2251099 | 2010-06-16 14:46:09 +0100 | [diff] [blame] | 865 | { | 
| Feng Tang | ee9b450 | 2010-09-13 15:39:48 +0800 | [diff] [blame] | 866 | 	struct uart_max3110 *max = spi_get_drvdata(dev); | 
| Feng Tang | 2251099 | 2010-06-16 14:46:09 +0100 | [diff] [blame] | 867 |  | 
| Feng Tang | ee9b450 | 2010-09-13 15:39:48 +0800 | [diff] [blame] | 868 | 	if (!max) | 
| Feng Tang | 2251099 | 2010-06-16 14:46:09 +0100 | [diff] [blame] | 869 | 		return 0; | 
 | 870 |  | 
| Feng Tang | 2251099 | 2010-06-16 14:46:09 +0100 | [diff] [blame] | 871 | 	uart_remove_one_port(&serial_m3110_reg, &max->port); | 
 | 872 |  | 
 | 873 | 	free_page((unsigned long)max->con_xmit.buf); | 
 | 874 |  | 
 | 875 | 	if (max->main_thread) | 
 | 876 | 		kthread_stop(max->main_thread); | 
 | 877 |  | 
 | 878 | 	kfree(max); | 
 | 879 | 	return 0; | 
 | 880 | } | 
 | 881 |  | 
 | 882 | static struct spi_driver uart_max3110_driver = { | 
 | 883 | 	.driver = { | 
 | 884 | 			.name	= "spi_max3111", | 
 | 885 | 			.bus	= &spi_bus_type, | 
 | 886 | 			.owner	= THIS_MODULE, | 
 | 887 | 	}, | 
 | 888 | 	.probe		= serial_m3110_probe, | 
| Feng Tang | ee9b450 | 2010-09-13 15:39:48 +0800 | [diff] [blame] | 889 | 	.remove		= __devexit_p(serial_m3110_remove), | 
| Feng Tang | 2251099 | 2010-06-16 14:46:09 +0100 | [diff] [blame] | 890 | 	.suspend	= serial_m3110_suspend, | 
 | 891 | 	.resume		= serial_m3110_resume, | 
 | 892 | }; | 
 | 893 |  | 
| Feng Tang | ee9b450 | 2010-09-13 15:39:48 +0800 | [diff] [blame] | 894 | static int __init serial_m3110_init(void) | 
| Feng Tang | 2251099 | 2010-06-16 14:46:09 +0100 | [diff] [blame] | 895 | { | 
 | 896 | 	int ret = 0; | 
 | 897 |  | 
 | 898 | 	ret = uart_register_driver(&serial_m3110_reg); | 
 | 899 | 	if (ret) | 
 | 900 | 		return ret; | 
 | 901 |  | 
 | 902 | 	ret = spi_register_driver(&uart_max3110_driver); | 
 | 903 | 	if (ret) | 
 | 904 | 		uart_unregister_driver(&serial_m3110_reg); | 
 | 905 |  | 
 | 906 | 	return ret; | 
 | 907 | } | 
 | 908 |  | 
| Feng Tang | ee9b450 | 2010-09-13 15:39:48 +0800 | [diff] [blame] | 909 | static void __exit serial_m3110_exit(void) | 
| Feng Tang | 2251099 | 2010-06-16 14:46:09 +0100 | [diff] [blame] | 910 | { | 
 | 911 | 	spi_unregister_driver(&uart_max3110_driver); | 
 | 912 | 	uart_unregister_driver(&serial_m3110_reg); | 
 | 913 | } | 
 | 914 |  | 
 | 915 | module_init(serial_m3110_init); | 
 | 916 | module_exit(serial_m3110_exit); | 
 | 917 |  | 
| Feng Tang | ee9b450 | 2010-09-13 15:39:48 +0800 | [diff] [blame] | 918 | MODULE_LICENSE("GPL v2"); | 
| Feng Tang | 2251099 | 2010-06-16 14:46:09 +0100 | [diff] [blame] | 919 | MODULE_ALIAS("max3110-uart"); |