1/* drivers/serial/serial_lh7a40x.c
2 *
3 *  Copyright (C) 2004 Coastal Environmental Systems
4 *
5 *  This program is free software; you can redistribute it and/or
6 *  modify it under the terms of the GNU General Public License
7 *  version 2 as published by the Free Software Foundation.
8 *
9 */
10
11/* Driver for Sharp LH7A40X embedded serial ports
12 *
13 *  Based on drivers/char/serial.c, by Linus Torvalds, Theodore Ts'o.
14 *  Based on drivers/serial/amba.c, by Deep Blue Solutions Ltd.
15 *
16 *  ---
17 *
18 * This driver supports the embedded UARTs of the Sharp LH7A40X series
19 * CPUs.  While similar to the 16550 and other UART chips, there is
20 * nothing close to register compatibility.  Moreover, some of the
21 * modem control lines are not available, either in the chip or they
22 * are lacking in the board-level implementation.
23 *
24 * - Use of SIRDIS
25 *   For simplicity, we disable the IR functions of any UART whenever
26 *   we enable it.
27 *
28 */
29
30
31#if defined(CONFIG_SERIAL_LH7A40X_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)
32#define SUPPORT_SYSRQ
33#endif
34
35#include <linux/module.h>
36#include <linux/ioport.h>
37#include <linux/init.h>
38#include <linux/console.h>
39#include <linux/sysrq.h>
40#include <linux/tty.h>
41#include <linux/tty_flip.h>
42#include <linux/serial_core.h>
43#include <linux/serial.h>
44
45#include <asm/io.h>
46#include <asm/irq.h>
47
48#define DEV_MAJOR	204
49#define DEV_MINOR	16
50#define DEV_NR		3
51
52#define ISR_LOOP_LIMIT	256
53
54#define UR(p,o)	_UR ((p)->membase, o)
55#define _UR(b,o) (*((volatile unsigned int*)(((unsigned char*) b) + (o))))
56#define BIT_CLR(p,o,m)	UR(p,o) = UR(p,o) & (~(unsigned int)m)
57#define BIT_SET(p,o,m)	UR(p,o) = UR(p,o) | ( (unsigned int)m)
58
59#define UART_REG_SIZE	32
60
61#define UART_R_DATA	(0x00)
62#define UART_R_FCON	(0x04)
63#define UART_R_BRCON	(0x08)
64#define UART_R_CON	(0x0c)
65#define UART_R_STATUS	(0x10)
66#define UART_R_RAWISR	(0x14)
67#define UART_R_INTEN	(0x18)
68#define UART_R_ISR	(0x1c)
69
70#define UARTEN		(0x01)		/* UART enable */
71#define SIRDIS		(0x02)		/* Serial IR disable (UART1 only) */
72
73#define RxEmpty		(0x10)
74#define TxEmpty		(0x80)
75#define TxFull		(0x20)
76#define nRxRdy		RxEmpty
77#define nTxRdy		TxFull
78#define TxBusy		(0x08)
79
80#define RxBreak		(0x0800)
81#define RxOverrunError	(0x0400)
82#define RxParityError	(0x0200)
83#define RxFramingError	(0x0100)
84#define RxError     (RxBreak | RxOverrunError | RxParityError | RxFramingError)
85
86#define DCD		(0x04)
87#define DSR		(0x02)
88#define CTS		(0x01)
89
90#define RxInt		(0x01)
91#define TxInt		(0x02)
92#define ModemInt	(0x04)
93#define RxTimeoutInt	(0x08)
94
95#define MSEOI		(0x10)
96
97#define WLEN_8		(0x60)
98#define WLEN_7		(0x40)
99#define WLEN_6		(0x20)
100#define WLEN_5		(0x00)
101#define WLEN		(0x60)	/* Mask for all word-length bits */
102#define STP2		(0x08)
103#define PEN		(0x02)	/* Parity Enable */
104#define EPS		(0x04)	/* Even Parity Set */
105#define FEN		(0x10)	/* FIFO Enable */
106#define BRK		(0x01)	/* Send Break */
107
108
109struct uart_port_lh7a40x {
110	struct uart_port port;
111	unsigned int statusPrev; /* Most recently read modem status */
112};
113
114static void lh7a40xuart_stop_tx (struct uart_port* port)
115{
116	BIT_CLR (port, UART_R_INTEN, TxInt);
117}
118
119static void lh7a40xuart_start_tx (struct uart_port* port)
120{
121	BIT_SET (port, UART_R_INTEN, TxInt);
122
123}
124
125static void lh7a40xuart_stop_rx (struct uart_port* port)
126{
127	BIT_SET (port, UART_R_INTEN, RxTimeoutInt | RxInt);
128}
129
130static void lh7a40xuart_enable_ms (struct uart_port* port)
131{
132	BIT_SET (port, UART_R_INTEN, ModemInt);
133}
134
135static void lh7a40xuart_rx_chars (struct uart_port* port)
136{
137	struct tty_struct* tty = port->info->tty;
138	int cbRxMax = 256;	/* (Gross) limit on receive */
139	unsigned int data;	/* Received data and status */
140	unsigned int flag;
141
142	while (!(UR (port, UART_R_STATUS) & nRxRdy) && --cbRxMax) {
143		data = UR (port, UART_R_DATA);
144		flag = TTY_NORMAL;
145		++port->icount.rx;
146
147		if (unlikely(data & RxError)) {
148			if (data & RxBreak) {
149				data &= ~(RxFramingError | RxParityError);
150				++port->icount.brk;
151				if (uart_handle_break (port))
152					continue;
153			}
154			else if (data & RxParityError)
155				++port->icount.parity;
156			else if (data & RxFramingError)
157				++port->icount.frame;
158			if (data & RxOverrunError)
159				++port->icount.overrun;
160
161				/* Mask by termios, leave Rx'd byte */
162			data &= port->read_status_mask | 0xff;
163
164			if (data & RxBreak)
165				flag = TTY_BREAK;
166			else if (data & RxParityError)
167				flag = TTY_PARITY;
168			else if (data & RxFramingError)
169				flag = TTY_FRAME;
170		}
171
172		if (uart_handle_sysrq_char (port, (unsigned char) data))
173			continue;
174
175		uart_insert_char(port, data, RxOverrunError, data, flag);
176	}
177	tty_flip_buffer_push (tty);
178	return;
179}
180
181static void lh7a40xuart_tx_chars (struct uart_port* port)
182{
183	struct circ_buf* xmit = &port->info->xmit;
184	int cbTxMax = port->fifosize;
185
186	if (port->x_char) {
187		UR (port, UART_R_DATA) = port->x_char;
188		++port->icount.tx;
189		port->x_char = 0;
190		return;
191	}
192	if (uart_circ_empty (xmit) || uart_tx_stopped (port)) {
193		lh7a40xuart_stop_tx (port);
194		return;
195	}
196
197	/* Unlike the AMBA UART, the lh7a40x UART does not guarantee
198	   that at least half of the FIFO is empty.  Instead, we check
199	   status for every character.  Using the AMBA method causes
200	   the transmitter to drop characters. */
201
202	do {
203		UR (port, UART_R_DATA) = xmit->buf[xmit->tail];
204		xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
205		++port->icount.tx;
206		if (uart_circ_empty(xmit))
207			break;
208	} while (!(UR (port, UART_R_STATUS) & nTxRdy)
209		 && cbTxMax--);
210
211	if (uart_circ_chars_pending (xmit) < WAKEUP_CHARS)
212		uart_write_wakeup (port);
213
214	if (uart_circ_empty (xmit))
215		lh7a40xuart_stop_tx (port);
216}
217
218static void lh7a40xuart_modem_status (struct uart_port* port)
219{
220	unsigned int status = UR (port, UART_R_STATUS);
221	unsigned int delta
222		= status ^ ((struct uart_port_lh7a40x*) port)->statusPrev;
223
224	BIT_SET (port, UART_R_RAWISR, MSEOI); /* Clear modem status intr */
225
226	if (!delta)		/* Only happens if we missed 2 transitions */
227		return;
228
229	((struct uart_port_lh7a40x*) port)->statusPrev = status;
230
231	if (delta & DCD)
232		uart_handle_dcd_change (port, status & DCD);
233
234	if (delta & DSR)
235		++port->icount.dsr;
236
237	if (delta & CTS)
238		uart_handle_cts_change (port, status & CTS);
239
240	wake_up_interruptible (&port->info->delta_msr_wait);
241}
242
243static irqreturn_t lh7a40xuart_int (int irq, void* dev_id)
244{
245	struct uart_port* port = dev_id;
246	unsigned int cLoopLimit = ISR_LOOP_LIMIT;
247	unsigned int isr = UR (port, UART_R_ISR);
248
249
250	do {
251		if (isr & (RxInt | RxTimeoutInt))
252			lh7a40xuart_rx_chars(port);
253		if (isr & ModemInt)
254			lh7a40xuart_modem_status (port);
255		if (isr & TxInt)
256			lh7a40xuart_tx_chars (port);
257
258		if (--cLoopLimit == 0)
259			break;
260
261		isr = UR (port, UART_R_ISR);
262	} while (isr & (RxInt | TxInt | RxTimeoutInt));
263
264	return IRQ_HANDLED;
265}
266
267static unsigned int lh7a40xuart_tx_empty (struct uart_port* port)
268{
269	return (UR (port, UART_R_STATUS) & TxEmpty) ? TIOCSER_TEMT : 0;
270}
271
272static unsigned int lh7a40xuart_get_mctrl (struct uart_port* port)
273{
274	unsigned int result = 0;
275	unsigned int status = UR (port, UART_R_STATUS);
276
277	if (status & DCD)
278		result |= TIOCM_CAR;
279	if (status & DSR)
280		result |= TIOCM_DSR;
281	if (status & CTS)
282		result |= TIOCM_CTS;
283
284	return result;
285}
286
287static void lh7a40xuart_set_mctrl (struct uart_port* port, unsigned int mctrl)
288{
289	/* None of the ports supports DTR. UART1 supports RTS through GPIO. */
290	/* Note, kernel appears to be setting DTR and RTS on console. */
291
292}
293
294static void lh7a40xuart_break_ctl (struct uart_port* port, int break_state)
295{
296	unsigned long flags;
297
298	spin_lock_irqsave(&port->lock, flags);
299	if (break_state == -1)
300		BIT_SET (port, UART_R_FCON, BRK); /* Assert break */
301	else
302		BIT_CLR (port, UART_R_FCON, BRK); /* Deassert break */
303	spin_unlock_irqrestore(&port->lock, flags);
304}
305
306static int lh7a40xuart_startup (struct uart_port* port)
307{
308	int retval;
309
310	retval = request_irq (port->irq, lh7a40xuart_int, 0,
311			      "serial_lh7a40x", port);
312	if (retval)
313		return retval;
314
315				/* Initial modem control-line settings */
316	((struct uart_port_lh7a40x*) port)->statusPrev
317		= UR (port, UART_R_STATUS);
318
319	/* There is presently no configuration option to enable IR.
320	   Thus, we always disable it. */
321
322	BIT_SET (port, UART_R_CON, UARTEN | SIRDIS);
323	BIT_SET (port, UART_R_INTEN, RxTimeoutInt | RxInt);
324
325	return 0;
326}
327
328static void lh7a40xuart_shutdown (struct uart_port* port)
329{
330	free_irq (port->irq, port);
331	BIT_CLR (port, UART_R_FCON, BRK | FEN);
332	BIT_CLR (port, UART_R_CON, UARTEN);
333}
334
335static void lh7a40xuart_set_termios (struct uart_port* port,
336				     struct ktermios* termios,
337				     struct ktermios* old)
338{
339	unsigned int con;
340	unsigned int inten;
341	unsigned int fcon;
342	unsigned long flags;
343	unsigned int baud;
344	unsigned int quot;
345
346	baud = uart_get_baud_rate (port, termios, old, 8, port->uartclk/16);
347	quot = uart_get_divisor (port, baud); /* -1 performed elsewhere */
348
349	switch (termios->c_cflag & CSIZE) {
350	case CS5:
351		fcon = WLEN_5;
352		break;
353	case CS6:
354		fcon = WLEN_6;
355		break;
356	case CS7:
357		fcon = WLEN_7;
358		break;
359	case CS8:
360	default:
361		fcon = WLEN_8;
362		break;
363	}
364	if (termios->c_cflag & CSTOPB)
365		fcon |= STP2;
366	if (termios->c_cflag & PARENB) {
367		fcon |= PEN;
368		if (!(termios->c_cflag & PARODD))
369			fcon |= EPS;
370	}
371	if (port->fifosize > 1)
372		fcon |= FEN;
373
374	spin_lock_irqsave (&port->lock, flags);
375
376	uart_update_timeout (port, termios->c_cflag, baud);
377
378	port->read_status_mask = RxOverrunError;
379	if (termios->c_iflag & INPCK)
380		port->read_status_mask |= RxFramingError | RxParityError;
381	if (termios->c_iflag & (BRKINT | PARMRK))
382		port->read_status_mask |= RxBreak;
383
384		/* Figure mask for status we ignore */
385	port->ignore_status_mask = 0;
386	if (termios->c_iflag & IGNPAR)
387		port->ignore_status_mask |= RxFramingError | RxParityError;
388	if (termios->c_iflag & IGNBRK) {
389		port->ignore_status_mask |= RxBreak;
390		/* Ignore overrun when ignorning parity */
391		if (termios->c_iflag & IGNPAR)
392			port->ignore_status_mask |= RxOverrunError;
393	}
394
395		/* Ignore all receive errors when receive disabled */
396	if ((termios->c_cflag & CREAD) == 0)
397		port->ignore_status_mask |= RxError;
398
399	con   = UR (port, UART_R_CON);
400	inten = (UR (port, UART_R_INTEN) & ~ModemInt);
401
402	if (UART_ENABLE_MS (port, termios->c_cflag))
403		inten |= ModemInt;
404
405	BIT_CLR (port, UART_R_CON, UARTEN);	/* Disable UART */
406	UR (port, UART_R_INTEN) = 0;		/* Disable interrupts */
407	UR (port, UART_R_BRCON) = quot - 1;	/* Set baud rate divisor */
408	UR (port, UART_R_FCON)  = fcon;		/* Set FIFO and frame ctrl */
409	UR (port, UART_R_INTEN) = inten;	/* Enable interrupts */
410	UR (port, UART_R_CON)   = con;		/* Restore UART mode */
411
412	spin_unlock_irqrestore(&port->lock, flags);
413}
414
415static const char* lh7a40xuart_type (struct uart_port* port)
416{
417	return port->type == PORT_LH7A40X ? "LH7A40X" : NULL;
418}
419
420static void lh7a40xuart_release_port (struct uart_port* port)
421{
422	release_mem_region (port->mapbase, UART_REG_SIZE);
423}
424
425static int lh7a40xuart_request_port (struct uart_port* port)
426{
427	return request_mem_region (port->mapbase, UART_REG_SIZE,
428				   "serial_lh7a40x") != NULL
429		? 0 : -EBUSY;
430}
431
432static void lh7a40xuart_config_port (struct uart_port* port, int flags)
433{
434	if (flags & UART_CONFIG_TYPE) {
435		port->type = PORT_LH7A40X;
436		lh7a40xuart_request_port (port);
437	}
438}
439
440static int lh7a40xuart_verify_port (struct uart_port* port,
441				    struct serial_struct* ser)
442{
443	int ret = 0;
444
445	if (ser->type != PORT_UNKNOWN && ser->type != PORT_LH7A40X)
446		ret = -EINVAL;
447	if (ser->irq < 0 || ser->irq >= NR_IRQS)
448		ret = -EINVAL;
449	if (ser->baud_base < 9600)
450		ret = -EINVAL;
451	return ret;
452}
453
454static struct uart_ops lh7a40x_uart_ops = {
455	.tx_empty	= lh7a40xuart_tx_empty,
456	.set_mctrl	= lh7a40xuart_set_mctrl,
457	.get_mctrl	= lh7a40xuart_get_mctrl,
458	.stop_tx	= lh7a40xuart_stop_tx,
459	.start_tx	= lh7a40xuart_start_tx,
460	.stop_rx	= lh7a40xuart_stop_rx,
461	.enable_ms	= lh7a40xuart_enable_ms,
462	.break_ctl	= lh7a40xuart_break_ctl,
463	.startup	= lh7a40xuart_startup,
464	.shutdown	= lh7a40xuart_shutdown,
465	.set_termios	= lh7a40xuart_set_termios,
466	.type		= lh7a40xuart_type,
467	.release_port	= lh7a40xuart_release_port,
468	.request_port	= lh7a40xuart_request_port,
469	.config_port	= lh7a40xuart_config_port,
470	.verify_port	= lh7a40xuart_verify_port,
471};
472
473static struct uart_port_lh7a40x lh7a40x_ports[DEV_NR] = {
474	{
475		.port = {
476			.membase	= (void*) io_p2v (UART1_PHYS),
477			.mapbase	= UART1_PHYS,
478			.iotype		= UPIO_MEM,
479			.irq		= IRQ_UART1INTR,
480			.uartclk	= 14745600/2,
481			.fifosize	= 16,
482			.ops		= &lh7a40x_uart_ops,
483			.flags		= UPF_BOOT_AUTOCONF,
484			.line		= 0,
485		},
486	},
487	{
488		.port = {
489			.membase	= (void*) io_p2v (UART2_PHYS),
490			.mapbase	= UART2_PHYS,
491			.iotype		= UPIO_MEM,
492			.irq		= IRQ_UART2INTR,
493			.uartclk	= 14745600/2,
494			.fifosize	= 16,
495			.ops		= &lh7a40x_uart_ops,
496			.flags		= UPF_BOOT_AUTOCONF,
497			.line		= 1,
498		},
499	},
500	{
501		.port = {
502			.membase	= (void*) io_p2v (UART3_PHYS),
503			.mapbase	= UART3_PHYS,
504			.iotype		= UPIO_MEM,
505			.irq		= IRQ_UART3INTR,
506			.uartclk	= 14745600/2,
507			.fifosize	= 16,
508			.ops		= &lh7a40x_uart_ops,
509			.flags		= UPF_BOOT_AUTOCONF,
510			.line		= 2,
511		},
512	},
513};
514
515#ifndef CONFIG_SERIAL_LH7A40X_CONSOLE
516# define LH7A40X_CONSOLE NULL
517#else
518# define LH7A40X_CONSOLE &lh7a40x_console
519
520static void lh7a40xuart_console_putchar(struct uart_port *port, int ch)
521{
522	while (UR(port, UART_R_STATUS) & nTxRdy)
523		;
524	UR(port, UART_R_DATA) = ch;
525}
526
527static void lh7a40xuart_console_write (struct console* co,
528				       const char* s,
529				       unsigned int count)
530{
531	struct uart_port* port = &lh7a40x_ports[co->index].port;
532	unsigned int con = UR (port, UART_R_CON);
533	unsigned int inten = UR (port, UART_R_INTEN);
534
535
536	UR (port, UART_R_INTEN) = 0;		/* Disable all interrupts */
537	BIT_SET (port, UART_R_CON, UARTEN | SIRDIS); /* Enable UART */
538
539	uart_console_write(port, s, count, lh7a40xuart_console_putchar);
540
541				/* Wait until all characters are sent */
542	while (UR (port, UART_R_STATUS) & TxBusy)
543		;
544
545				/* Restore control and interrupt mask */
546	UR (port, UART_R_CON) = con;
547	UR (port, UART_R_INTEN) = inten;
548}
549
550static void __init lh7a40xuart_console_get_options (struct uart_port* port,
551						    int* baud,
552						    int* parity,
553						    int* bits)
554{
555	if (UR (port, UART_R_CON) & UARTEN) {
556		unsigned int fcon = UR (port, UART_R_FCON);
557		unsigned int quot = UR (port, UART_R_BRCON) + 1;
558
559		switch (fcon & (PEN | EPS)) {
560		default:        *parity = 'n'; break;
561		case PEN:       *parity = 'o'; break;
562		case PEN | EPS: *parity = 'e'; break;
563		}
564
565		switch (fcon & WLEN) {
566		default:
567		case WLEN_8: *bits = 8; break;
568		case WLEN_7: *bits = 7; break;
569		case WLEN_6: *bits = 6; break;
570		case WLEN_5: *bits = 5; break;
571		}
572
573		*baud = port->uartclk/(16*quot);
574	}
575}
576
577static int __init lh7a40xuart_console_setup (struct console* co, char* options)
578{
579	struct uart_port* port;
580	int baud = 38400;
581	int bits = 8;
582	int parity = 'n';
583	int flow = 'n';
584
585	if (co->index >= DEV_NR) /* Bounds check on device number */
586		co->index = 0;
587	port = &lh7a40x_ports[co->index].port;
588
589	if (options)
590		uart_parse_options (options, &baud, &parity, &bits, &flow);
591	else
592		lh7a40xuart_console_get_options (port, &baud, &parity, &bits);
593
594	return uart_set_options (port, co, baud, parity, bits, flow);
595}
596
597static struct uart_driver lh7a40x_reg;
598static struct console lh7a40x_console = {
599	.name		= "ttyAM",
600	.write		= lh7a40xuart_console_write,
601	.device		= uart_console_device,
602	.setup		= lh7a40xuart_console_setup,
603	.flags		= CON_PRINTBUFFER,
604	.index		= -1,
605	.data		= &lh7a40x_reg,
606};
607
608static int __init lh7a40xuart_console_init(void)
609{
610	register_console (&lh7a40x_console);
611	return 0;
612}
613
614console_initcall (lh7a40xuart_console_init);
615
616#endif
617
618static struct uart_driver lh7a40x_reg = {
619	.owner			= THIS_MODULE,
620	.driver_name		= "ttyAM",
621	.dev_name		= "ttyAM",
622	.major			= DEV_MAJOR,
623	.minor			= DEV_MINOR,
624	.nr			= DEV_NR,
625	.cons			= LH7A40X_CONSOLE,
626};
627
628static int __init lh7a40xuart_init(void)
629{
630	int ret;
631
632	printk (KERN_INFO "serial: LH7A40X serial driver\n");
633
634	ret = uart_register_driver (&lh7a40x_reg);
635
636	if (ret == 0) {
637		int i;
638
639		for (i = 0; i < DEV_NR; i++) {
640			/* UART3, when used, requires GPIO pin reallocation */
641			if (lh7a40x_ports[i].port.mapbase == UART3_PHYS)
642				GPIO_PINMUX |= 1<<3;
643			uart_add_one_port (&lh7a40x_reg,
644					   &lh7a40x_ports[i].port);
645		}
646	}
647	return ret;
648}
649
650static void __exit lh7a40xuart_exit(void)
651{
652	int i;
653
654	for (i = 0; i < DEV_NR; i++)
655		uart_remove_one_port (&lh7a40x_reg, &lh7a40x_ports[i].port);
656
657	uart_unregister_driver (&lh7a40x_reg);
658}
659
660module_init (lh7a40xuart_init);
661module_exit (lh7a40xuart_exit);
662
663MODULE_AUTHOR ("Marc Singer");
664MODULE_DESCRIPTION ("Sharp LH7A40X serial port driver");
665MODULE_LICENSE ("GPL");
666