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