xref: /openbmc/linux/drivers/tty/amiserial.c (revision 01bd730d92bd002adc3f3317d8e3328c629b436c)
1a6afd9f3SGreg Kroah-Hartman /*
2a6afd9f3SGreg Kroah-Hartman  * Serial driver for the amiga builtin port.
3a6afd9f3SGreg Kroah-Hartman  *
4a6afd9f3SGreg Kroah-Hartman  * This code was created by taking serial.c version 4.30 from kernel
5a6afd9f3SGreg Kroah-Hartman  * release 2.3.22, replacing all hardware related stuff with the
6a6afd9f3SGreg Kroah-Hartman  * corresponding amiga hardware actions, and removing all irrelevant
7a6afd9f3SGreg Kroah-Hartman  * code. As a consequence, it uses many of the constants and names
8a6afd9f3SGreg Kroah-Hartman  * associated with the registers and bits of 16550 compatible UARTS -
9a6afd9f3SGreg Kroah-Hartman  * but only to keep track of status, etc in the state variables. It
10a6afd9f3SGreg Kroah-Hartman  * was done this was to make it easier to keep the code in line with
11a6afd9f3SGreg Kroah-Hartman  * (non hardware specific) changes to serial.c.
12a6afd9f3SGreg Kroah-Hartman  *
13a6afd9f3SGreg Kroah-Hartman  * The port is registered with the tty driver as minor device 64, and
14a6afd9f3SGreg Kroah-Hartman  * therefore other ports should should only use 65 upwards.
15a6afd9f3SGreg Kroah-Hartman  *
16a6afd9f3SGreg Kroah-Hartman  * Richard Lucock 28/12/99
17a6afd9f3SGreg Kroah-Hartman  *
18a6afd9f3SGreg Kroah-Hartman  *  Copyright (C) 1991, 1992  Linus Torvalds
19a6afd9f3SGreg Kroah-Hartman  *  Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997,
20a6afd9f3SGreg Kroah-Hartman  * 		1998, 1999  Theodore Ts'o
21a6afd9f3SGreg Kroah-Hartman  *
22a6afd9f3SGreg Kroah-Hartman  */
23a6afd9f3SGreg Kroah-Hartman 
24a6afd9f3SGreg Kroah-Hartman /*
25a6afd9f3SGreg Kroah-Hartman  * Serial driver configuration section.  Here are the various options:
26a6afd9f3SGreg Kroah-Hartman  *
27a6afd9f3SGreg Kroah-Hartman  * SERIAL_PARANOIA_CHECK
28a6afd9f3SGreg Kroah-Hartman  * 		Check the magic number for the async_structure where
29a6afd9f3SGreg Kroah-Hartman  * 		ever possible.
30a6afd9f3SGreg Kroah-Hartman  */
31a6afd9f3SGreg Kroah-Hartman 
32a6afd9f3SGreg Kroah-Hartman #include <linux/delay.h>
33a6afd9f3SGreg Kroah-Hartman 
34a6afd9f3SGreg Kroah-Hartman #undef SERIAL_PARANOIA_CHECK
35a6afd9f3SGreg Kroah-Hartman #define SERIAL_DO_RESTART
36a6afd9f3SGreg Kroah-Hartman 
37a6afd9f3SGreg Kroah-Hartman /* Set of debugging defines */
38a6afd9f3SGreg Kroah-Hartman 
39a6afd9f3SGreg Kroah-Hartman #undef SERIAL_DEBUG_INTR
40a6afd9f3SGreg Kroah-Hartman #undef SERIAL_DEBUG_OPEN
41a6afd9f3SGreg Kroah-Hartman #undef SERIAL_DEBUG_FLOW
42a6afd9f3SGreg Kroah-Hartman #undef SERIAL_DEBUG_RS_WAIT_UNTIL_SENT
43a6afd9f3SGreg Kroah-Hartman 
44a6afd9f3SGreg Kroah-Hartman /* Sanity checks */
45a6afd9f3SGreg Kroah-Hartman 
46a6afd9f3SGreg Kroah-Hartman #if defined(MODULE) && defined(SERIAL_DEBUG_MCOUNT)
47a6afd9f3SGreg Kroah-Hartman #define DBG_CNT(s) printk("(%s): [%x] refc=%d, serc=%d, ttyc=%d -> %s\n", \
48*01bd730dSJiri Slaby  tty->name, (info->tport.flags), serial_driver->refcount,info->count,tty->count,s)
49a6afd9f3SGreg Kroah-Hartman #else
50a6afd9f3SGreg Kroah-Hartman #define DBG_CNT(s)
51a6afd9f3SGreg Kroah-Hartman #endif
52a6afd9f3SGreg Kroah-Hartman 
53a6afd9f3SGreg Kroah-Hartman /*
54a6afd9f3SGreg Kroah-Hartman  * End of serial driver configuration section.
55a6afd9f3SGreg Kroah-Hartman  */
56a6afd9f3SGreg Kroah-Hartman 
57a6afd9f3SGreg Kroah-Hartman #include <linux/module.h>
58a6afd9f3SGreg Kroah-Hartman 
59a6afd9f3SGreg Kroah-Hartman #include <linux/types.h>
60a6afd9f3SGreg Kroah-Hartman #include <linux/serial.h>
61a6afd9f3SGreg Kroah-Hartman #include <linux/serialP.h>
62a6afd9f3SGreg Kroah-Hartman #include <linux/serial_reg.h>
63a6afd9f3SGreg Kroah-Hartman static char *serial_version = "4.30";
64a6afd9f3SGreg Kroah-Hartman 
65a6afd9f3SGreg Kroah-Hartman #include <linux/errno.h>
66a6afd9f3SGreg Kroah-Hartman #include <linux/signal.h>
67a6afd9f3SGreg Kroah-Hartman #include <linux/sched.h>
68a6afd9f3SGreg Kroah-Hartman #include <linux/kernel.h>
69a6afd9f3SGreg Kroah-Hartman #include <linux/timer.h>
70a6afd9f3SGreg Kroah-Hartman #include <linux/interrupt.h>
71a6afd9f3SGreg Kroah-Hartman #include <linux/tty.h>
72a6afd9f3SGreg Kroah-Hartman #include <linux/tty_flip.h>
73a6afd9f3SGreg Kroah-Hartman #include <linux/console.h>
74a6afd9f3SGreg Kroah-Hartman #include <linux/major.h>
75a6afd9f3SGreg Kroah-Hartman #include <linux/string.h>
76a6afd9f3SGreg Kroah-Hartman #include <linux/fcntl.h>
77a6afd9f3SGreg Kroah-Hartman #include <linux/ptrace.h>
78a6afd9f3SGreg Kroah-Hartman #include <linux/ioport.h>
79a6afd9f3SGreg Kroah-Hartman #include <linux/mm.h>
80a6afd9f3SGreg Kroah-Hartman #include <linux/seq_file.h>
81a6afd9f3SGreg Kroah-Hartman #include <linux/slab.h>
82a6afd9f3SGreg Kroah-Hartman #include <linux/init.h>
83a6afd9f3SGreg Kroah-Hartman #include <linux/bitops.h>
84a6afd9f3SGreg Kroah-Hartman #include <linux/platform_device.h>
85a6afd9f3SGreg Kroah-Hartman 
86a6afd9f3SGreg Kroah-Hartman #include <asm/setup.h>
87a6afd9f3SGreg Kroah-Hartman 
88a6afd9f3SGreg Kroah-Hartman #include <asm/system.h>
89a6afd9f3SGreg Kroah-Hartman 
90a6afd9f3SGreg Kroah-Hartman #include <asm/irq.h>
91a6afd9f3SGreg Kroah-Hartman 
92a6afd9f3SGreg Kroah-Hartman #include <asm/amigahw.h>
93a6afd9f3SGreg Kroah-Hartman #include <asm/amigaints.h>
94a6afd9f3SGreg Kroah-Hartman 
95a6afd9f3SGreg Kroah-Hartman #define custom amiga_custom
96a6afd9f3SGreg Kroah-Hartman static char *serial_name = "Amiga-builtin serial driver";
97a6afd9f3SGreg Kroah-Hartman 
98a6afd9f3SGreg Kroah-Hartman static struct tty_driver *serial_driver;
99a6afd9f3SGreg Kroah-Hartman 
100a6afd9f3SGreg Kroah-Hartman /* number of characters left in xmit buffer before we ask for more */
101a6afd9f3SGreg Kroah-Hartman #define WAKEUP_CHARS 256
102a6afd9f3SGreg Kroah-Hartman 
103a6afd9f3SGreg Kroah-Hartman static unsigned char current_ctl_bits;
104a6afd9f3SGreg Kroah-Hartman 
105588993ddSJiri Slaby static void change_speed(struct tty_struct *tty, struct serial_state *info,
106588993ddSJiri Slaby 		struct ktermios *old);
107a6afd9f3SGreg Kroah-Hartman static void rs_wait_until_sent(struct tty_struct *tty, int timeout);
108a6afd9f3SGreg Kroah-Hartman 
109a6afd9f3SGreg Kroah-Hartman 
110a6afd9f3SGreg Kroah-Hartman static struct serial_state rs_table[1];
111a6afd9f3SGreg Kroah-Hartman 
112a6afd9f3SGreg Kroah-Hartman #define NR_PORTS ARRAY_SIZE(rs_table)
113a6afd9f3SGreg Kroah-Hartman 
114a6afd9f3SGreg Kroah-Hartman #include <asm/uaccess.h>
115a6afd9f3SGreg Kroah-Hartman 
116a6afd9f3SGreg Kroah-Hartman #define serial_isroot()	(capable(CAP_SYS_ADMIN))
117a6afd9f3SGreg Kroah-Hartman 
118a6afd9f3SGreg Kroah-Hartman 
119916b7656SJiri Slaby static inline int serial_paranoia_check(struct serial_state *info,
120a6afd9f3SGreg Kroah-Hartman 					char *name, const char *routine)
121a6afd9f3SGreg Kroah-Hartman {
122a6afd9f3SGreg Kroah-Hartman #ifdef SERIAL_PARANOIA_CHECK
123a6afd9f3SGreg Kroah-Hartman 	static const char *badmagic =
124a6afd9f3SGreg Kroah-Hartman 		"Warning: bad magic number for serial struct (%s) in %s\n";
125a6afd9f3SGreg Kroah-Hartman 	static const char *badinfo =
126a6afd9f3SGreg Kroah-Hartman 		"Warning: null async_struct for (%s) in %s\n";
127a6afd9f3SGreg Kroah-Hartman 
128a6afd9f3SGreg Kroah-Hartman 	if (!info) {
129a6afd9f3SGreg Kroah-Hartman 		printk(badinfo, name, routine);
130a6afd9f3SGreg Kroah-Hartman 		return 1;
131a6afd9f3SGreg Kroah-Hartman 	}
132a6afd9f3SGreg Kroah-Hartman 	if (info->magic != SERIAL_MAGIC) {
133a6afd9f3SGreg Kroah-Hartman 		printk(badmagic, name, routine);
134a6afd9f3SGreg Kroah-Hartman 		return 1;
135a6afd9f3SGreg Kroah-Hartman 	}
136a6afd9f3SGreg Kroah-Hartman #endif
137a6afd9f3SGreg Kroah-Hartman 	return 0;
138a6afd9f3SGreg Kroah-Hartman }
139a6afd9f3SGreg Kroah-Hartman 
140a6afd9f3SGreg Kroah-Hartman /* some serial hardware definitions */
141a6afd9f3SGreg Kroah-Hartman #define SDR_OVRUN   (1<<15)
142a6afd9f3SGreg Kroah-Hartman #define SDR_RBF     (1<<14)
143a6afd9f3SGreg Kroah-Hartman #define SDR_TBE     (1<<13)
144a6afd9f3SGreg Kroah-Hartman #define SDR_TSRE    (1<<12)
145a6afd9f3SGreg Kroah-Hartman 
146a6afd9f3SGreg Kroah-Hartman #define SERPER_PARENB    (1<<15)
147a6afd9f3SGreg Kroah-Hartman 
148a6afd9f3SGreg Kroah-Hartman #define AC_SETCLR   (1<<15)
149a6afd9f3SGreg Kroah-Hartman #define AC_UARTBRK  (1<<11)
150a6afd9f3SGreg Kroah-Hartman 
151a6afd9f3SGreg Kroah-Hartman #define SER_DTR     (1<<7)
152a6afd9f3SGreg Kroah-Hartman #define SER_RTS     (1<<6)
153a6afd9f3SGreg Kroah-Hartman #define SER_DCD     (1<<5)
154a6afd9f3SGreg Kroah-Hartman #define SER_CTS     (1<<4)
155a6afd9f3SGreg Kroah-Hartman #define SER_DSR     (1<<3)
156a6afd9f3SGreg Kroah-Hartman 
157a6afd9f3SGreg Kroah-Hartman static __inline__ void rtsdtr_ctrl(int bits)
158a6afd9f3SGreg Kroah-Hartman {
159a6afd9f3SGreg Kroah-Hartman     ciab.pra = ((bits & (SER_RTS | SER_DTR)) ^ (SER_RTS | SER_DTR)) | (ciab.pra & ~(SER_RTS | SER_DTR));
160a6afd9f3SGreg Kroah-Hartman }
161a6afd9f3SGreg Kroah-Hartman 
162a6afd9f3SGreg Kroah-Hartman /*
163a6afd9f3SGreg Kroah-Hartman  * ------------------------------------------------------------
164a6afd9f3SGreg Kroah-Hartman  * rs_stop() and rs_start()
165a6afd9f3SGreg Kroah-Hartman  *
166a6afd9f3SGreg Kroah-Hartman  * This routines are called before setting or resetting tty->stopped.
167a6afd9f3SGreg Kroah-Hartman  * They enable or disable transmitter interrupts, as necessary.
168a6afd9f3SGreg Kroah-Hartman  * ------------------------------------------------------------
169a6afd9f3SGreg Kroah-Hartman  */
170a6afd9f3SGreg Kroah-Hartman static void rs_stop(struct tty_struct *tty)
171a6afd9f3SGreg Kroah-Hartman {
172916b7656SJiri Slaby 	struct serial_state *info = tty->driver_data;
173a6afd9f3SGreg Kroah-Hartman 	unsigned long flags;
174a6afd9f3SGreg Kroah-Hartman 
175a6afd9f3SGreg Kroah-Hartman 	if (serial_paranoia_check(info, tty->name, "rs_stop"))
176a6afd9f3SGreg Kroah-Hartman 		return;
177a6afd9f3SGreg Kroah-Hartman 
178a6afd9f3SGreg Kroah-Hartman 	local_irq_save(flags);
179a6afd9f3SGreg Kroah-Hartman 	if (info->IER & UART_IER_THRI) {
180a6afd9f3SGreg Kroah-Hartman 		info->IER &= ~UART_IER_THRI;
181a6afd9f3SGreg Kroah-Hartman 		/* disable Tx interrupt and remove any pending interrupts */
182a6afd9f3SGreg Kroah-Hartman 		custom.intena = IF_TBE;
183a6afd9f3SGreg Kroah-Hartman 		mb();
184a6afd9f3SGreg Kroah-Hartman 		custom.intreq = IF_TBE;
185a6afd9f3SGreg Kroah-Hartman 		mb();
186a6afd9f3SGreg Kroah-Hartman 	}
187a6afd9f3SGreg Kroah-Hartman 	local_irq_restore(flags);
188a6afd9f3SGreg Kroah-Hartman }
189a6afd9f3SGreg Kroah-Hartman 
190a6afd9f3SGreg Kroah-Hartman static void rs_start(struct tty_struct *tty)
191a6afd9f3SGreg Kroah-Hartman {
192916b7656SJiri Slaby 	struct serial_state *info = tty->driver_data;
193a6afd9f3SGreg Kroah-Hartman 	unsigned long flags;
194a6afd9f3SGreg Kroah-Hartman 
195a6afd9f3SGreg Kroah-Hartman 	if (serial_paranoia_check(info, tty->name, "rs_start"))
196a6afd9f3SGreg Kroah-Hartman 		return;
197a6afd9f3SGreg Kroah-Hartman 
198a6afd9f3SGreg Kroah-Hartman 	local_irq_save(flags);
199a6afd9f3SGreg Kroah-Hartman 	if (info->xmit.head != info->xmit.tail
200a6afd9f3SGreg Kroah-Hartman 	    && info->xmit.buf
201a6afd9f3SGreg Kroah-Hartman 	    && !(info->IER & UART_IER_THRI)) {
202a6afd9f3SGreg Kroah-Hartman 		info->IER |= UART_IER_THRI;
203a6afd9f3SGreg Kroah-Hartman 		custom.intena = IF_SETCLR | IF_TBE;
204a6afd9f3SGreg Kroah-Hartman 		mb();
205a6afd9f3SGreg Kroah-Hartman 		/* set a pending Tx Interrupt, transmitter should restart now */
206a6afd9f3SGreg Kroah-Hartman 		custom.intreq = IF_SETCLR | IF_TBE;
207a6afd9f3SGreg Kroah-Hartman 		mb();
208a6afd9f3SGreg Kroah-Hartman 	}
209a6afd9f3SGreg Kroah-Hartman 	local_irq_restore(flags);
210a6afd9f3SGreg Kroah-Hartman }
211a6afd9f3SGreg Kroah-Hartman 
212a6afd9f3SGreg Kroah-Hartman /*
213a6afd9f3SGreg Kroah-Hartman  * ----------------------------------------------------------------------
214a6afd9f3SGreg Kroah-Hartman  *
215a6afd9f3SGreg Kroah-Hartman  * Here starts the interrupt handling routines.  All of the following
216a6afd9f3SGreg Kroah-Hartman  * subroutines are declared as inline and are folded into
217a6afd9f3SGreg Kroah-Hartman  * rs_interrupt().  They were separated out for readability's sake.
218a6afd9f3SGreg Kroah-Hartman  *
219a6afd9f3SGreg Kroah-Hartman  * Note: rs_interrupt() is a "fast" interrupt, which means that it
220a6afd9f3SGreg Kroah-Hartman  * runs with interrupts turned off.  People who may want to modify
221a6afd9f3SGreg Kroah-Hartman  * rs_interrupt() should try to keep the interrupt handler as fast as
222a6afd9f3SGreg Kroah-Hartman  * possible.  After you are done making modifications, it is not a bad
223a6afd9f3SGreg Kroah-Hartman  * idea to do:
224a6afd9f3SGreg Kroah-Hartman  *
225a6afd9f3SGreg Kroah-Hartman  * gcc -S -DKERNEL -Wall -Wstrict-prototypes -O6 -fomit-frame-pointer serial.c
226a6afd9f3SGreg Kroah-Hartman  *
227a6afd9f3SGreg Kroah-Hartman  * and look at the resulting assemble code in serial.s.
228a6afd9f3SGreg Kroah-Hartman  *
229a6afd9f3SGreg Kroah-Hartman  * 				- Ted Ts'o (tytso@mit.edu), 7-Mar-93
230a6afd9f3SGreg Kroah-Hartman  * -----------------------------------------------------------------------
231a6afd9f3SGreg Kroah-Hartman  */
232a6afd9f3SGreg Kroah-Hartman 
233916b7656SJiri Slaby static void receive_chars(struct serial_state *info)
234a6afd9f3SGreg Kroah-Hartman {
235a6afd9f3SGreg Kroah-Hartman         int status;
236a6afd9f3SGreg Kroah-Hartman 	int serdatr;
23787758791SJiri Slaby 	struct tty_struct *tty = info->tport.tty;
238a6afd9f3SGreg Kroah-Hartman 	unsigned char ch, flag;
239a6afd9f3SGreg Kroah-Hartman 	struct	async_icount *icount;
240a6afd9f3SGreg Kroah-Hartman 	int oe = 0;
241a6afd9f3SGreg Kroah-Hartman 
242916b7656SJiri Slaby 	icount = &info->icount;
243a6afd9f3SGreg Kroah-Hartman 
244a6afd9f3SGreg Kroah-Hartman 	status = UART_LSR_DR; /* We obviously have a character! */
245a6afd9f3SGreg Kroah-Hartman 	serdatr = custom.serdatr;
246a6afd9f3SGreg Kroah-Hartman 	mb();
247a6afd9f3SGreg Kroah-Hartman 	custom.intreq = IF_RBF;
248a6afd9f3SGreg Kroah-Hartman 	mb();
249a6afd9f3SGreg Kroah-Hartman 
250a6afd9f3SGreg Kroah-Hartman 	if((serdatr & 0x1ff) == 0)
251a6afd9f3SGreg Kroah-Hartman 	    status |= UART_LSR_BI;
252a6afd9f3SGreg Kroah-Hartman 	if(serdatr & SDR_OVRUN)
253a6afd9f3SGreg Kroah-Hartman 	    status |= UART_LSR_OE;
254a6afd9f3SGreg Kroah-Hartman 
255a6afd9f3SGreg Kroah-Hartman 	ch = serdatr & 0xff;
256a6afd9f3SGreg Kroah-Hartman 	icount->rx++;
257a6afd9f3SGreg Kroah-Hartman 
258a6afd9f3SGreg Kroah-Hartman #ifdef SERIAL_DEBUG_INTR
259a6afd9f3SGreg Kroah-Hartman 	printk("DR%02x:%02x...", ch, status);
260a6afd9f3SGreg Kroah-Hartman #endif
261a6afd9f3SGreg Kroah-Hartman 	flag = TTY_NORMAL;
262a6afd9f3SGreg Kroah-Hartman 
263a6afd9f3SGreg Kroah-Hartman 	/*
264a6afd9f3SGreg Kroah-Hartman 	 * We don't handle parity or frame errors - but I have left
265a6afd9f3SGreg Kroah-Hartman 	 * the code in, since I'm not sure that the errors can't be
266a6afd9f3SGreg Kroah-Hartman 	 * detected.
267a6afd9f3SGreg Kroah-Hartman 	 */
268a6afd9f3SGreg Kroah-Hartman 
269a6afd9f3SGreg Kroah-Hartman 	if (status & (UART_LSR_BI | UART_LSR_PE |
270a6afd9f3SGreg Kroah-Hartman 		      UART_LSR_FE | UART_LSR_OE)) {
271a6afd9f3SGreg Kroah-Hartman 	  /*
272a6afd9f3SGreg Kroah-Hartman 	   * For statistics only
273a6afd9f3SGreg Kroah-Hartman 	   */
274a6afd9f3SGreg Kroah-Hartman 	  if (status & UART_LSR_BI) {
275a6afd9f3SGreg Kroah-Hartman 	    status &= ~(UART_LSR_FE | UART_LSR_PE);
276a6afd9f3SGreg Kroah-Hartman 	    icount->brk++;
277a6afd9f3SGreg Kroah-Hartman 	  } else if (status & UART_LSR_PE)
278a6afd9f3SGreg Kroah-Hartman 	    icount->parity++;
279a6afd9f3SGreg Kroah-Hartman 	  else if (status & UART_LSR_FE)
280a6afd9f3SGreg Kroah-Hartman 	    icount->frame++;
281a6afd9f3SGreg Kroah-Hartman 	  if (status & UART_LSR_OE)
282a6afd9f3SGreg Kroah-Hartman 	    icount->overrun++;
283a6afd9f3SGreg Kroah-Hartman 
284a6afd9f3SGreg Kroah-Hartman 	  /*
285a6afd9f3SGreg Kroah-Hartman 	   * Now check to see if character should be
286a6afd9f3SGreg Kroah-Hartman 	   * ignored, and mask off conditions which
287a6afd9f3SGreg Kroah-Hartman 	   * should be ignored.
288a6afd9f3SGreg Kroah-Hartman 	   */
289a6afd9f3SGreg Kroah-Hartman 	  if (status & info->ignore_status_mask)
290a6afd9f3SGreg Kroah-Hartman 	    goto out;
291a6afd9f3SGreg Kroah-Hartman 
292a6afd9f3SGreg Kroah-Hartman 	  status &= info->read_status_mask;
293a6afd9f3SGreg Kroah-Hartman 
294a6afd9f3SGreg Kroah-Hartman 	  if (status & (UART_LSR_BI)) {
295a6afd9f3SGreg Kroah-Hartman #ifdef SERIAL_DEBUG_INTR
296a6afd9f3SGreg Kroah-Hartman 	    printk("handling break....");
297a6afd9f3SGreg Kroah-Hartman #endif
298a6afd9f3SGreg Kroah-Hartman 	    flag = TTY_BREAK;
299*01bd730dSJiri Slaby 	    if (info->tport.flags & ASYNC_SAK)
300a6afd9f3SGreg Kroah-Hartman 	      do_SAK(tty);
301a6afd9f3SGreg Kroah-Hartman 	  } else if (status & UART_LSR_PE)
302a6afd9f3SGreg Kroah-Hartman 	    flag = TTY_PARITY;
303a6afd9f3SGreg Kroah-Hartman 	  else if (status & UART_LSR_FE)
304a6afd9f3SGreg Kroah-Hartman 	    flag = TTY_FRAME;
305a6afd9f3SGreg Kroah-Hartman 	  if (status & UART_LSR_OE) {
306a6afd9f3SGreg Kroah-Hartman 	    /*
307a6afd9f3SGreg Kroah-Hartman 	     * Overrun is special, since it's
308a6afd9f3SGreg Kroah-Hartman 	     * reported immediately, and doesn't
309a6afd9f3SGreg Kroah-Hartman 	     * affect the current character
310a6afd9f3SGreg Kroah-Hartman 	     */
311a6afd9f3SGreg Kroah-Hartman 	     oe = 1;
312a6afd9f3SGreg Kroah-Hartman 	  }
313a6afd9f3SGreg Kroah-Hartman 	}
314a6afd9f3SGreg Kroah-Hartman 	tty_insert_flip_char(tty, ch, flag);
315a6afd9f3SGreg Kroah-Hartman 	if (oe == 1)
316a6afd9f3SGreg Kroah-Hartman 		tty_insert_flip_char(tty, 0, TTY_OVERRUN);
317a6afd9f3SGreg Kroah-Hartman 	tty_flip_buffer_push(tty);
318a6afd9f3SGreg Kroah-Hartman out:
319a6afd9f3SGreg Kroah-Hartman 	return;
320a6afd9f3SGreg Kroah-Hartman }
321a6afd9f3SGreg Kroah-Hartman 
322916b7656SJiri Slaby static void transmit_chars(struct serial_state *info)
323a6afd9f3SGreg Kroah-Hartman {
324a6afd9f3SGreg Kroah-Hartman 	custom.intreq = IF_TBE;
325a6afd9f3SGreg Kroah-Hartman 	mb();
326a6afd9f3SGreg Kroah-Hartman 	if (info->x_char) {
327a6afd9f3SGreg Kroah-Hartman 	        custom.serdat = info->x_char | 0x100;
328a6afd9f3SGreg Kroah-Hartman 		mb();
329916b7656SJiri Slaby 		info->icount.tx++;
330a6afd9f3SGreg Kroah-Hartman 		info->x_char = 0;
331a6afd9f3SGreg Kroah-Hartman 		return;
332a6afd9f3SGreg Kroah-Hartman 	}
333a6afd9f3SGreg Kroah-Hartman 	if (info->xmit.head == info->xmit.tail
33487758791SJiri Slaby 	    || info->tport.tty->stopped
33587758791SJiri Slaby 	    || info->tport.tty->hw_stopped) {
336a6afd9f3SGreg Kroah-Hartman 		info->IER &= ~UART_IER_THRI;
337a6afd9f3SGreg Kroah-Hartman 	        custom.intena = IF_TBE;
338a6afd9f3SGreg Kroah-Hartman 		mb();
339a6afd9f3SGreg Kroah-Hartman 		return;
340a6afd9f3SGreg Kroah-Hartman 	}
341a6afd9f3SGreg Kroah-Hartman 
342a6afd9f3SGreg Kroah-Hartman 	custom.serdat = info->xmit.buf[info->xmit.tail++] | 0x100;
343a6afd9f3SGreg Kroah-Hartman 	mb();
344a6afd9f3SGreg Kroah-Hartman 	info->xmit.tail = info->xmit.tail & (SERIAL_XMIT_SIZE-1);
345916b7656SJiri Slaby 	info->icount.tx++;
346a6afd9f3SGreg Kroah-Hartman 
347a6afd9f3SGreg Kroah-Hartman 	if (CIRC_CNT(info->xmit.head,
348a6afd9f3SGreg Kroah-Hartman 		     info->xmit.tail,
349a6afd9f3SGreg Kroah-Hartman 		     SERIAL_XMIT_SIZE) < WAKEUP_CHARS)
35087758791SJiri Slaby 		tty_wakeup(info->tport.tty);
351a6afd9f3SGreg Kroah-Hartman 
352a6afd9f3SGreg Kroah-Hartman #ifdef SERIAL_DEBUG_INTR
353a6afd9f3SGreg Kroah-Hartman 	printk("THRE...");
354a6afd9f3SGreg Kroah-Hartman #endif
355a6afd9f3SGreg Kroah-Hartman 	if (info->xmit.head == info->xmit.tail) {
356a6afd9f3SGreg Kroah-Hartman 	        custom.intena = IF_TBE;
357a6afd9f3SGreg Kroah-Hartman 		mb();
358a6afd9f3SGreg Kroah-Hartman 		info->IER &= ~UART_IER_THRI;
359a6afd9f3SGreg Kroah-Hartman 	}
360a6afd9f3SGreg Kroah-Hartman }
361a6afd9f3SGreg Kroah-Hartman 
362916b7656SJiri Slaby static void check_modem_status(struct serial_state *info)
363a6afd9f3SGreg Kroah-Hartman {
364a6afd9f3SGreg Kroah-Hartman 	unsigned char status = ciab.pra & (SER_DCD | SER_CTS | SER_DSR);
365a6afd9f3SGreg Kroah-Hartman 	unsigned char dstatus;
366a6afd9f3SGreg Kroah-Hartman 	struct	async_icount *icount;
367a6afd9f3SGreg Kroah-Hartman 
368a6afd9f3SGreg Kroah-Hartman 	/* Determine bits that have changed */
369a6afd9f3SGreg Kroah-Hartman 	dstatus = status ^ current_ctl_bits;
370a6afd9f3SGreg Kroah-Hartman 	current_ctl_bits = status;
371a6afd9f3SGreg Kroah-Hartman 
372a6afd9f3SGreg Kroah-Hartman 	if (dstatus) {
373916b7656SJiri Slaby 		icount = &info->icount;
374a6afd9f3SGreg Kroah-Hartman 		/* update input line counters */
375a6afd9f3SGreg Kroah-Hartman 		if (dstatus & SER_DSR)
376a6afd9f3SGreg Kroah-Hartman 			icount->dsr++;
377a6afd9f3SGreg Kroah-Hartman 		if (dstatus & SER_DCD) {
378a6afd9f3SGreg Kroah-Hartman 			icount->dcd++;
379a6afd9f3SGreg Kroah-Hartman #ifdef CONFIG_HARD_PPS
380*01bd730dSJiri Slaby 			if ((info->tport.flags & ASYNC_HARDPPS_CD) &&
381a6afd9f3SGreg Kroah-Hartman 			    !(status & SER_DCD))
382a6afd9f3SGreg Kroah-Hartman 				hardpps();
383a6afd9f3SGreg Kroah-Hartman #endif
384a6afd9f3SGreg Kroah-Hartman 		}
385a6afd9f3SGreg Kroah-Hartman 		if (dstatus & SER_CTS)
386a6afd9f3SGreg Kroah-Hartman 			icount->cts++;
38787758791SJiri Slaby 		wake_up_interruptible(&info->tport.delta_msr_wait);
388a6afd9f3SGreg Kroah-Hartman 	}
389a6afd9f3SGreg Kroah-Hartman 
390*01bd730dSJiri Slaby 	if ((info->tport.flags & ASYNC_CHECK_CD) && (dstatus & SER_DCD)) {
391a6afd9f3SGreg Kroah-Hartman #if (defined(SERIAL_DEBUG_OPEN) || defined(SERIAL_DEBUG_INTR))
392a6afd9f3SGreg Kroah-Hartman 		printk("ttyS%d CD now %s...", info->line,
393a6afd9f3SGreg Kroah-Hartman 		       (!(status & SER_DCD)) ? "on" : "off");
394a6afd9f3SGreg Kroah-Hartman #endif
395a6afd9f3SGreg Kroah-Hartman 		if (!(status & SER_DCD))
39687758791SJiri Slaby 			wake_up_interruptible(&info->tport.open_wait);
397a6afd9f3SGreg Kroah-Hartman 		else {
398a6afd9f3SGreg Kroah-Hartman #ifdef SERIAL_DEBUG_OPEN
399a6afd9f3SGreg Kroah-Hartman 			printk("doing serial hangup...");
400a6afd9f3SGreg Kroah-Hartman #endif
40187758791SJiri Slaby 			if (info->tport.tty)
40287758791SJiri Slaby 				tty_hangup(info->tport.tty);
403a6afd9f3SGreg Kroah-Hartman 		}
404a6afd9f3SGreg Kroah-Hartman 	}
405*01bd730dSJiri Slaby 	if (info->tport.flags & ASYNC_CTS_FLOW) {
40687758791SJiri Slaby 		if (info->tport.tty->hw_stopped) {
407a6afd9f3SGreg Kroah-Hartman 			if (!(status & SER_CTS)) {
408a6afd9f3SGreg Kroah-Hartman #if (defined(SERIAL_DEBUG_INTR) || defined(SERIAL_DEBUG_FLOW))
409a6afd9f3SGreg Kroah-Hartman 				printk("CTS tx start...");
410a6afd9f3SGreg Kroah-Hartman #endif
41187758791SJiri Slaby 				info->tport.tty->hw_stopped = 0;
412a6afd9f3SGreg Kroah-Hartman 				info->IER |= UART_IER_THRI;
413a6afd9f3SGreg Kroah-Hartman 				custom.intena = IF_SETCLR | IF_TBE;
414a6afd9f3SGreg Kroah-Hartman 				mb();
415a6afd9f3SGreg Kroah-Hartman 				/* set a pending Tx Interrupt, transmitter should restart now */
416a6afd9f3SGreg Kroah-Hartman 				custom.intreq = IF_SETCLR | IF_TBE;
417a6afd9f3SGreg Kroah-Hartman 				mb();
41887758791SJiri Slaby 				tty_wakeup(info->tport.tty);
419a6afd9f3SGreg Kroah-Hartman 				return;
420a6afd9f3SGreg Kroah-Hartman 			}
421a6afd9f3SGreg Kroah-Hartman 		} else {
422a6afd9f3SGreg Kroah-Hartman 			if ((status & SER_CTS)) {
423a6afd9f3SGreg Kroah-Hartman #if (defined(SERIAL_DEBUG_INTR) || defined(SERIAL_DEBUG_FLOW))
424a6afd9f3SGreg Kroah-Hartman 				printk("CTS tx stop...");
425a6afd9f3SGreg Kroah-Hartman #endif
42687758791SJiri Slaby 				info->tport.tty->hw_stopped = 1;
427a6afd9f3SGreg Kroah-Hartman 				info->IER &= ~UART_IER_THRI;
428a6afd9f3SGreg Kroah-Hartman 				/* disable Tx interrupt and remove any pending interrupts */
429a6afd9f3SGreg Kroah-Hartman 				custom.intena = IF_TBE;
430a6afd9f3SGreg Kroah-Hartman 				mb();
431a6afd9f3SGreg Kroah-Hartman 				custom.intreq = IF_TBE;
432a6afd9f3SGreg Kroah-Hartman 				mb();
433a6afd9f3SGreg Kroah-Hartman 			}
434a6afd9f3SGreg Kroah-Hartman 		}
435a6afd9f3SGreg Kroah-Hartman 	}
436a6afd9f3SGreg Kroah-Hartman }
437a6afd9f3SGreg Kroah-Hartman 
438a6afd9f3SGreg Kroah-Hartman static irqreturn_t ser_vbl_int( int irq, void *data)
439a6afd9f3SGreg Kroah-Hartman {
440a6afd9f3SGreg Kroah-Hartman         /* vbl is just a periodic interrupt we tie into to update modem status */
441916b7656SJiri Slaby 	struct serial_state *info = data;
442a6afd9f3SGreg Kroah-Hartman 	/*
443a6afd9f3SGreg Kroah-Hartman 	 * TBD - is it better to unregister from this interrupt or to
444a6afd9f3SGreg Kroah-Hartman 	 * ignore it if MSI is clear ?
445a6afd9f3SGreg Kroah-Hartman 	 */
446a6afd9f3SGreg Kroah-Hartman 	if(info->IER & UART_IER_MSI)
447a6afd9f3SGreg Kroah-Hartman 	  check_modem_status(info);
448a6afd9f3SGreg Kroah-Hartman 	return IRQ_HANDLED;
449a6afd9f3SGreg Kroah-Hartman }
450a6afd9f3SGreg Kroah-Hartman 
451a6afd9f3SGreg Kroah-Hartman static irqreturn_t ser_rx_int(int irq, void *dev_id)
452a6afd9f3SGreg Kroah-Hartman {
453916b7656SJiri Slaby 	struct serial_state *info = dev_id;
454a6afd9f3SGreg Kroah-Hartman 
455a6afd9f3SGreg Kroah-Hartman #ifdef SERIAL_DEBUG_INTR
456a6afd9f3SGreg Kroah-Hartman 	printk("ser_rx_int...");
457a6afd9f3SGreg Kroah-Hartman #endif
458a6afd9f3SGreg Kroah-Hartman 
45987758791SJiri Slaby 	if (!info->tport.tty)
460a6afd9f3SGreg Kroah-Hartman 		return IRQ_NONE;
461a6afd9f3SGreg Kroah-Hartman 
462a6afd9f3SGreg Kroah-Hartman 	receive_chars(info);
463a6afd9f3SGreg Kroah-Hartman #ifdef SERIAL_DEBUG_INTR
464a6afd9f3SGreg Kroah-Hartman 	printk("end.\n");
465a6afd9f3SGreg Kroah-Hartman #endif
466a6afd9f3SGreg Kroah-Hartman 	return IRQ_HANDLED;
467a6afd9f3SGreg Kroah-Hartman }
468a6afd9f3SGreg Kroah-Hartman 
469a6afd9f3SGreg Kroah-Hartman static irqreturn_t ser_tx_int(int irq, void *dev_id)
470a6afd9f3SGreg Kroah-Hartman {
471916b7656SJiri Slaby 	struct serial_state *info = dev_id;
472a6afd9f3SGreg Kroah-Hartman 
473a6afd9f3SGreg Kroah-Hartman 	if (custom.serdatr & SDR_TBE) {
474a6afd9f3SGreg Kroah-Hartman #ifdef SERIAL_DEBUG_INTR
475a6afd9f3SGreg Kroah-Hartman 	  printk("ser_tx_int...");
476a6afd9f3SGreg Kroah-Hartman #endif
477a6afd9f3SGreg Kroah-Hartman 
47887758791SJiri Slaby 	  if (!info->tport.tty)
479a6afd9f3SGreg Kroah-Hartman 		return IRQ_NONE;
480a6afd9f3SGreg Kroah-Hartman 
481a6afd9f3SGreg Kroah-Hartman 	  transmit_chars(info);
482a6afd9f3SGreg Kroah-Hartman #ifdef SERIAL_DEBUG_INTR
483a6afd9f3SGreg Kroah-Hartman 	  printk("end.\n");
484a6afd9f3SGreg Kroah-Hartman #endif
485a6afd9f3SGreg Kroah-Hartman 	}
486a6afd9f3SGreg Kroah-Hartman 	return IRQ_HANDLED;
487a6afd9f3SGreg Kroah-Hartman }
488a6afd9f3SGreg Kroah-Hartman 
489a6afd9f3SGreg Kroah-Hartman /*
490a6afd9f3SGreg Kroah-Hartman  * -------------------------------------------------------------------
491a6afd9f3SGreg Kroah-Hartman  * Here ends the serial interrupt routines.
492a6afd9f3SGreg Kroah-Hartman  * -------------------------------------------------------------------
493a6afd9f3SGreg Kroah-Hartman  */
494a6afd9f3SGreg Kroah-Hartman 
495a6afd9f3SGreg Kroah-Hartman /*
496a6afd9f3SGreg Kroah-Hartman  * ---------------------------------------------------------------
497a6afd9f3SGreg Kroah-Hartman  * Low level utility subroutines for the serial driver:  routines to
498a6afd9f3SGreg Kroah-Hartman  * figure out the appropriate timeout for an interrupt chain, routines
499a6afd9f3SGreg Kroah-Hartman  * to initialize and startup a serial port, and routines to shutdown a
500a6afd9f3SGreg Kroah-Hartman  * serial port.  Useful stuff like that.
501a6afd9f3SGreg Kroah-Hartman  * ---------------------------------------------------------------
502a6afd9f3SGreg Kroah-Hartman  */
503a6afd9f3SGreg Kroah-Hartman 
504588993ddSJiri Slaby static int startup(struct tty_struct *tty, struct serial_state *info)
505a6afd9f3SGreg Kroah-Hartman {
506*01bd730dSJiri Slaby 	struct tty_port *port = &info->tport;
507a6afd9f3SGreg Kroah-Hartman 	unsigned long flags;
508a6afd9f3SGreg Kroah-Hartman 	int	retval=0;
509a6afd9f3SGreg Kroah-Hartman 	unsigned long page;
510a6afd9f3SGreg Kroah-Hartman 
511a6afd9f3SGreg Kroah-Hartman 	page = get_zeroed_page(GFP_KERNEL);
512a6afd9f3SGreg Kroah-Hartman 	if (!page)
513a6afd9f3SGreg Kroah-Hartman 		return -ENOMEM;
514a6afd9f3SGreg Kroah-Hartman 
515a6afd9f3SGreg Kroah-Hartman 	local_irq_save(flags);
516a6afd9f3SGreg Kroah-Hartman 
517*01bd730dSJiri Slaby 	if (port->flags & ASYNC_INITIALIZED) {
518a6afd9f3SGreg Kroah-Hartman 		free_page(page);
519a6afd9f3SGreg Kroah-Hartman 		goto errout;
520a6afd9f3SGreg Kroah-Hartman 	}
521a6afd9f3SGreg Kroah-Hartman 
522a6afd9f3SGreg Kroah-Hartman 	if (info->xmit.buf)
523a6afd9f3SGreg Kroah-Hartman 		free_page(page);
524a6afd9f3SGreg Kroah-Hartman 	else
525a6afd9f3SGreg Kroah-Hartman 		info->xmit.buf = (unsigned char *) page;
526a6afd9f3SGreg Kroah-Hartman 
527a6afd9f3SGreg Kroah-Hartman #ifdef SERIAL_DEBUG_OPEN
528a6afd9f3SGreg Kroah-Hartman 	printk("starting up ttys%d ...", info->line);
529a6afd9f3SGreg Kroah-Hartman #endif
530a6afd9f3SGreg Kroah-Hartman 
531a6afd9f3SGreg Kroah-Hartman 	/* Clear anything in the input buffer */
532a6afd9f3SGreg Kroah-Hartman 
533a6afd9f3SGreg Kroah-Hartman 	custom.intreq = IF_RBF;
534a6afd9f3SGreg Kroah-Hartman 	mb();
535a6afd9f3SGreg Kroah-Hartman 
536a6afd9f3SGreg Kroah-Hartman 	retval = request_irq(IRQ_AMIGA_VERTB, ser_vbl_int, 0, "serial status", info);
537a6afd9f3SGreg Kroah-Hartman 	if (retval) {
538a6afd9f3SGreg Kroah-Hartman 	  if (serial_isroot()) {
539588993ddSJiri Slaby 	      set_bit(TTY_IO_ERROR, &tty->flags);
540a6afd9f3SGreg Kroah-Hartman 	    retval = 0;
541a6afd9f3SGreg Kroah-Hartman 	  }
542a6afd9f3SGreg Kroah-Hartman 	  goto errout;
543a6afd9f3SGreg Kroah-Hartman 	}
544a6afd9f3SGreg Kroah-Hartman 
545a6afd9f3SGreg Kroah-Hartman 	/* enable both Rx and Tx interrupts */
546a6afd9f3SGreg Kroah-Hartman 	custom.intena = IF_SETCLR | IF_RBF | IF_TBE;
547a6afd9f3SGreg Kroah-Hartman 	mb();
548a6afd9f3SGreg Kroah-Hartman 	info->IER = UART_IER_MSI;
549a6afd9f3SGreg Kroah-Hartman 
550a6afd9f3SGreg Kroah-Hartman 	/* remember current state of the DCD and CTS bits */
551a6afd9f3SGreg Kroah-Hartman 	current_ctl_bits = ciab.pra & (SER_DCD | SER_CTS | SER_DSR);
552a6afd9f3SGreg Kroah-Hartman 
553a6afd9f3SGreg Kroah-Hartman 	info->MCR = 0;
554588993ddSJiri Slaby 	if (C_BAUD(tty))
555a6afd9f3SGreg Kroah-Hartman 	  info->MCR = SER_DTR | SER_RTS;
556a6afd9f3SGreg Kroah-Hartman 	rtsdtr_ctrl(info->MCR);
557a6afd9f3SGreg Kroah-Hartman 
558588993ddSJiri Slaby 	clear_bit(TTY_IO_ERROR, &tty->flags);
559a6afd9f3SGreg Kroah-Hartman 	info->xmit.head = info->xmit.tail = 0;
560a6afd9f3SGreg Kroah-Hartman 
561a6afd9f3SGreg Kroah-Hartman 	/*
562a6afd9f3SGreg Kroah-Hartman 	 * Set up the tty->alt_speed kludge
563a6afd9f3SGreg Kroah-Hartman 	 */
564*01bd730dSJiri Slaby 	if ((port->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI)
565588993ddSJiri Slaby 		tty->alt_speed = 57600;
566*01bd730dSJiri Slaby 	if ((port->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI)
567588993ddSJiri Slaby 		tty->alt_speed = 115200;
568*01bd730dSJiri Slaby 	if ((port->flags & ASYNC_SPD_MASK) == ASYNC_SPD_SHI)
569588993ddSJiri Slaby 		tty->alt_speed = 230400;
570*01bd730dSJiri Slaby 	if ((port->flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP)
571588993ddSJiri Slaby 		tty->alt_speed = 460800;
572a6afd9f3SGreg Kroah-Hartman 
573a6afd9f3SGreg Kroah-Hartman 	/*
574a6afd9f3SGreg Kroah-Hartman 	 * and set the speed of the serial port
575a6afd9f3SGreg Kroah-Hartman 	 */
576588993ddSJiri Slaby 	change_speed(tty, info, NULL);
577a6afd9f3SGreg Kroah-Hartman 
578*01bd730dSJiri Slaby 	port->flags |= ASYNC_INITIALIZED;
579a6afd9f3SGreg Kroah-Hartman 	local_irq_restore(flags);
580a6afd9f3SGreg Kroah-Hartman 	return 0;
581a6afd9f3SGreg Kroah-Hartman 
582a6afd9f3SGreg Kroah-Hartman errout:
583a6afd9f3SGreg Kroah-Hartman 	local_irq_restore(flags);
584a6afd9f3SGreg Kroah-Hartman 	return retval;
585a6afd9f3SGreg Kroah-Hartman }
586a6afd9f3SGreg Kroah-Hartman 
587a6afd9f3SGreg Kroah-Hartman /*
588a6afd9f3SGreg Kroah-Hartman  * This routine will shutdown a serial port; interrupts are disabled, and
589a6afd9f3SGreg Kroah-Hartman  * DTR is dropped if the hangup on close termio flag is on.
590a6afd9f3SGreg Kroah-Hartman  */
591588993ddSJiri Slaby static void shutdown(struct tty_struct *tty, struct serial_state *info)
592a6afd9f3SGreg Kroah-Hartman {
593a6afd9f3SGreg Kroah-Hartman 	unsigned long	flags;
594a6afd9f3SGreg Kroah-Hartman 	struct serial_state *state;
595a6afd9f3SGreg Kroah-Hartman 
596*01bd730dSJiri Slaby 	if (!(info->tport.flags & ASYNC_INITIALIZED))
597a6afd9f3SGreg Kroah-Hartman 		return;
598a6afd9f3SGreg Kroah-Hartman 
599916b7656SJiri Slaby 	state = info;
600a6afd9f3SGreg Kroah-Hartman 
601a6afd9f3SGreg Kroah-Hartman #ifdef SERIAL_DEBUG_OPEN
602a6afd9f3SGreg Kroah-Hartman 	printk("Shutting down serial port %d ....\n", info->line);
603a6afd9f3SGreg Kroah-Hartman #endif
604a6afd9f3SGreg Kroah-Hartman 
605a6afd9f3SGreg Kroah-Hartman 	local_irq_save(flags); /* Disable interrupts */
606a6afd9f3SGreg Kroah-Hartman 
607a6afd9f3SGreg Kroah-Hartman 	/*
608a6afd9f3SGreg Kroah-Hartman 	 * clear delta_msr_wait queue to avoid mem leaks: we may free the irq
609a6afd9f3SGreg Kroah-Hartman 	 * here so the queue might never be waken up
610a6afd9f3SGreg Kroah-Hartman 	 */
61187758791SJiri Slaby 	wake_up_interruptible(&info->tport.delta_msr_wait);
612a6afd9f3SGreg Kroah-Hartman 
613a6afd9f3SGreg Kroah-Hartman 	/*
614a6afd9f3SGreg Kroah-Hartman 	 * Free the IRQ, if necessary
615a6afd9f3SGreg Kroah-Hartman 	 */
616a6afd9f3SGreg Kroah-Hartman 	free_irq(IRQ_AMIGA_VERTB, info);
617a6afd9f3SGreg Kroah-Hartman 
618a6afd9f3SGreg Kroah-Hartman 	if (info->xmit.buf) {
619a6afd9f3SGreg Kroah-Hartman 		free_page((unsigned long) info->xmit.buf);
620a6afd9f3SGreg Kroah-Hartman 		info->xmit.buf = NULL;
621a6afd9f3SGreg Kroah-Hartman 	}
622a6afd9f3SGreg Kroah-Hartman 
623a6afd9f3SGreg Kroah-Hartman 	info->IER = 0;
624a6afd9f3SGreg Kroah-Hartman 	custom.intena = IF_RBF | IF_TBE;
625a6afd9f3SGreg Kroah-Hartman 	mb();
626a6afd9f3SGreg Kroah-Hartman 
627a6afd9f3SGreg Kroah-Hartman 	/* disable break condition */
628a6afd9f3SGreg Kroah-Hartman 	custom.adkcon = AC_UARTBRK;
629a6afd9f3SGreg Kroah-Hartman 	mb();
630a6afd9f3SGreg Kroah-Hartman 
631588993ddSJiri Slaby 	if (tty->termios->c_cflag & HUPCL)
632a6afd9f3SGreg Kroah-Hartman 		info->MCR &= ~(SER_DTR|SER_RTS);
633a6afd9f3SGreg Kroah-Hartman 	rtsdtr_ctrl(info->MCR);
634a6afd9f3SGreg Kroah-Hartman 
635588993ddSJiri Slaby 	set_bit(TTY_IO_ERROR, &tty->flags);
636a6afd9f3SGreg Kroah-Hartman 
637*01bd730dSJiri Slaby 	info->tport.flags &= ~ASYNC_INITIALIZED;
638a6afd9f3SGreg Kroah-Hartman 	local_irq_restore(flags);
639a6afd9f3SGreg Kroah-Hartman }
640a6afd9f3SGreg Kroah-Hartman 
641a6afd9f3SGreg Kroah-Hartman 
642a6afd9f3SGreg Kroah-Hartman /*
643a6afd9f3SGreg Kroah-Hartman  * This routine is called to set the UART divisor registers to match
644a6afd9f3SGreg Kroah-Hartman  * the specified baud rate for a serial port.
645a6afd9f3SGreg Kroah-Hartman  */
646588993ddSJiri Slaby static void change_speed(struct tty_struct *tty, struct serial_state *info,
647a6afd9f3SGreg Kroah-Hartman 			 struct ktermios *old_termios)
648a6afd9f3SGreg Kroah-Hartman {
649*01bd730dSJiri Slaby 	struct tty_port *port = &info->tport;
650a6afd9f3SGreg Kroah-Hartman 	int	quot = 0, baud_base, baud;
651a6afd9f3SGreg Kroah-Hartman 	unsigned cflag, cval = 0;
652a6afd9f3SGreg Kroah-Hartman 	int	bits;
653a6afd9f3SGreg Kroah-Hartman 	unsigned long	flags;
654a6afd9f3SGreg Kroah-Hartman 
655588993ddSJiri Slaby 	cflag = tty->termios->c_cflag;
656a6afd9f3SGreg Kroah-Hartman 
657a6afd9f3SGreg Kroah-Hartman 	/* Byte size is always 8 bits plus parity bit if requested */
658a6afd9f3SGreg Kroah-Hartman 
659a6afd9f3SGreg Kroah-Hartman 	cval = 3; bits = 10;
660a6afd9f3SGreg Kroah-Hartman 	if (cflag & CSTOPB) {
661a6afd9f3SGreg Kroah-Hartman 		cval |= 0x04;
662a6afd9f3SGreg Kroah-Hartman 		bits++;
663a6afd9f3SGreg Kroah-Hartman 	}
664a6afd9f3SGreg Kroah-Hartman 	if (cflag & PARENB) {
665a6afd9f3SGreg Kroah-Hartman 		cval |= UART_LCR_PARITY;
666a6afd9f3SGreg Kroah-Hartman 		bits++;
667a6afd9f3SGreg Kroah-Hartman 	}
668a6afd9f3SGreg Kroah-Hartman 	if (!(cflag & PARODD))
669a6afd9f3SGreg Kroah-Hartman 		cval |= UART_LCR_EPAR;
670a6afd9f3SGreg Kroah-Hartman #ifdef CMSPAR
671a6afd9f3SGreg Kroah-Hartman 	if (cflag & CMSPAR)
672a6afd9f3SGreg Kroah-Hartman 		cval |= UART_LCR_SPAR;
673a6afd9f3SGreg Kroah-Hartman #endif
674a6afd9f3SGreg Kroah-Hartman 
675a6afd9f3SGreg Kroah-Hartman 	/* Determine divisor based on baud rate */
676588993ddSJiri Slaby 	baud = tty_get_baud_rate(tty);
677a6afd9f3SGreg Kroah-Hartman 	if (!baud)
678a6afd9f3SGreg Kroah-Hartman 		baud = 9600;	/* B0 transition handled in rs_set_termios */
679916b7656SJiri Slaby 	baud_base = info->baud_base;
680*01bd730dSJiri Slaby 	if (baud == 38400 && (port->flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST)
681916b7656SJiri Slaby 		quot = info->custom_divisor;
682a6afd9f3SGreg Kroah-Hartman 	else {
683a6afd9f3SGreg Kroah-Hartman 		if (baud == 134)
684a6afd9f3SGreg Kroah-Hartman 			/* Special case since 134 is really 134.5 */
685a6afd9f3SGreg Kroah-Hartman 			quot = (2*baud_base / 269);
686a6afd9f3SGreg Kroah-Hartman 		else if (baud)
687a6afd9f3SGreg Kroah-Hartman 			quot = baud_base / baud;
688a6afd9f3SGreg Kroah-Hartman 	}
689a6afd9f3SGreg Kroah-Hartman 	/* If the quotient is zero refuse the change */
690a6afd9f3SGreg Kroah-Hartman 	if (!quot && old_termios) {
691a6afd9f3SGreg Kroah-Hartman 		/* FIXME: Will need updating for new tty in the end */
692588993ddSJiri Slaby 		tty->termios->c_cflag &= ~CBAUD;
693588993ddSJiri Slaby 		tty->termios->c_cflag |= (old_termios->c_cflag & CBAUD);
694588993ddSJiri Slaby 		baud = tty_get_baud_rate(tty);
695a6afd9f3SGreg Kroah-Hartman 		if (!baud)
696a6afd9f3SGreg Kroah-Hartman 			baud = 9600;
697a6afd9f3SGreg Kroah-Hartman 		if (baud == 38400 &&
698*01bd730dSJiri Slaby 		    (port->flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST)
699916b7656SJiri Slaby 			quot = info->custom_divisor;
700a6afd9f3SGreg Kroah-Hartman 		else {
701a6afd9f3SGreg Kroah-Hartman 			if (baud == 134)
702a6afd9f3SGreg Kroah-Hartman 				/* Special case since 134 is really 134.5 */
703a6afd9f3SGreg Kroah-Hartman 				quot = (2*baud_base / 269);
704a6afd9f3SGreg Kroah-Hartman 			else if (baud)
705a6afd9f3SGreg Kroah-Hartman 				quot = baud_base / baud;
706a6afd9f3SGreg Kroah-Hartman 		}
707a6afd9f3SGreg Kroah-Hartman 	}
708a6afd9f3SGreg Kroah-Hartman 	/* As a last resort, if the quotient is zero, default to 9600 bps */
709a6afd9f3SGreg Kroah-Hartman 	if (!quot)
710a6afd9f3SGreg Kroah-Hartman 		quot = baud_base / 9600;
711a6afd9f3SGreg Kroah-Hartman 	info->quot = quot;
712916b7656SJiri Slaby 	info->timeout = ((info->xmit_fifo_size*HZ*bits*quot) / baud_base);
713a6afd9f3SGreg Kroah-Hartman 	info->timeout += HZ/50;		/* Add .02 seconds of slop */
714a6afd9f3SGreg Kroah-Hartman 
715a6afd9f3SGreg Kroah-Hartman 	/* CTS flow control flag and modem status interrupts */
716a6afd9f3SGreg Kroah-Hartman 	info->IER &= ~UART_IER_MSI;
717*01bd730dSJiri Slaby 	if (port->flags & ASYNC_HARDPPS_CD)
718a6afd9f3SGreg Kroah-Hartman 		info->IER |= UART_IER_MSI;
719a6afd9f3SGreg Kroah-Hartman 	if (cflag & CRTSCTS) {
720*01bd730dSJiri Slaby 		port->flags |= ASYNC_CTS_FLOW;
721a6afd9f3SGreg Kroah-Hartman 		info->IER |= UART_IER_MSI;
722a6afd9f3SGreg Kroah-Hartman 	} else
723*01bd730dSJiri Slaby 		port->flags &= ~ASYNC_CTS_FLOW;
724a6afd9f3SGreg Kroah-Hartman 	if (cflag & CLOCAL)
725*01bd730dSJiri Slaby 		port->flags &= ~ASYNC_CHECK_CD;
726a6afd9f3SGreg Kroah-Hartman 	else {
727*01bd730dSJiri Slaby 		port->flags |= ASYNC_CHECK_CD;
728a6afd9f3SGreg Kroah-Hartman 		info->IER |= UART_IER_MSI;
729a6afd9f3SGreg Kroah-Hartman 	}
730a6afd9f3SGreg Kroah-Hartman 	/* TBD:
731a6afd9f3SGreg Kroah-Hartman 	 * Does clearing IER_MSI imply that we should disable the VBL interrupt ?
732a6afd9f3SGreg Kroah-Hartman 	 */
733a6afd9f3SGreg Kroah-Hartman 
734a6afd9f3SGreg Kroah-Hartman 	/*
735a6afd9f3SGreg Kroah-Hartman 	 * Set up parity check flag
736a6afd9f3SGreg Kroah-Hartman 	 */
737a6afd9f3SGreg Kroah-Hartman 
738a6afd9f3SGreg Kroah-Hartman 	info->read_status_mask = UART_LSR_OE | UART_LSR_DR;
739588993ddSJiri Slaby 	if (I_INPCK(tty))
740a6afd9f3SGreg Kroah-Hartman 		info->read_status_mask |= UART_LSR_FE | UART_LSR_PE;
741588993ddSJiri Slaby 	if (I_BRKINT(tty) || I_PARMRK(tty))
742a6afd9f3SGreg Kroah-Hartman 		info->read_status_mask |= UART_LSR_BI;
743a6afd9f3SGreg Kroah-Hartman 
744a6afd9f3SGreg Kroah-Hartman 	/*
745a6afd9f3SGreg Kroah-Hartman 	 * Characters to ignore
746a6afd9f3SGreg Kroah-Hartman 	 */
747a6afd9f3SGreg Kroah-Hartman 	info->ignore_status_mask = 0;
748588993ddSJiri Slaby 	if (I_IGNPAR(tty))
749a6afd9f3SGreg Kroah-Hartman 		info->ignore_status_mask |= UART_LSR_PE | UART_LSR_FE;
750588993ddSJiri Slaby 	if (I_IGNBRK(tty)) {
751a6afd9f3SGreg Kroah-Hartman 		info->ignore_status_mask |= UART_LSR_BI;
752a6afd9f3SGreg Kroah-Hartman 		/*
753a6afd9f3SGreg Kroah-Hartman 		 * If we're ignore parity and break indicators, ignore
754a6afd9f3SGreg Kroah-Hartman 		 * overruns too.  (For real raw support).
755a6afd9f3SGreg Kroah-Hartman 		 */
756588993ddSJiri Slaby 		if (I_IGNPAR(tty))
757a6afd9f3SGreg Kroah-Hartman 			info->ignore_status_mask |= UART_LSR_OE;
758a6afd9f3SGreg Kroah-Hartman 	}
759a6afd9f3SGreg Kroah-Hartman 	/*
760a6afd9f3SGreg Kroah-Hartman 	 * !!! ignore all characters if CREAD is not set
761a6afd9f3SGreg Kroah-Hartman 	 */
762a6afd9f3SGreg Kroah-Hartman 	if ((cflag & CREAD) == 0)
763a6afd9f3SGreg Kroah-Hartman 		info->ignore_status_mask |= UART_LSR_DR;
764a6afd9f3SGreg Kroah-Hartman 	local_irq_save(flags);
765a6afd9f3SGreg Kroah-Hartman 
766a6afd9f3SGreg Kroah-Hartman 	{
767a6afd9f3SGreg Kroah-Hartman 	  short serper;
768a6afd9f3SGreg Kroah-Hartman 
769a6afd9f3SGreg Kroah-Hartman 	/* Set up the baud rate */
770a6afd9f3SGreg Kroah-Hartman 	  serper = quot - 1;
771a6afd9f3SGreg Kroah-Hartman 
772a6afd9f3SGreg Kroah-Hartman 	/* Enable or disable parity bit */
773a6afd9f3SGreg Kroah-Hartman 
774a6afd9f3SGreg Kroah-Hartman 	if(cval & UART_LCR_PARITY)
775a6afd9f3SGreg Kroah-Hartman 	  serper |= (SERPER_PARENB);
776a6afd9f3SGreg Kroah-Hartman 
777a6afd9f3SGreg Kroah-Hartman 	custom.serper = serper;
778a6afd9f3SGreg Kroah-Hartman 	mb();
779a6afd9f3SGreg Kroah-Hartman 	}
780a6afd9f3SGreg Kroah-Hartman 
781a6afd9f3SGreg Kroah-Hartman 	local_irq_restore(flags);
782a6afd9f3SGreg Kroah-Hartman }
783a6afd9f3SGreg Kroah-Hartman 
784a6afd9f3SGreg Kroah-Hartman static int rs_put_char(struct tty_struct *tty, unsigned char ch)
785a6afd9f3SGreg Kroah-Hartman {
786916b7656SJiri Slaby 	struct serial_state *info;
787a6afd9f3SGreg Kroah-Hartman 	unsigned long flags;
788a6afd9f3SGreg Kroah-Hartman 
789a6afd9f3SGreg Kroah-Hartman 	info = tty->driver_data;
790a6afd9f3SGreg Kroah-Hartman 
791a6afd9f3SGreg Kroah-Hartman 	if (serial_paranoia_check(info, tty->name, "rs_put_char"))
792a6afd9f3SGreg Kroah-Hartman 		return 0;
793a6afd9f3SGreg Kroah-Hartman 
794a6afd9f3SGreg Kroah-Hartman 	if (!info->xmit.buf)
795a6afd9f3SGreg Kroah-Hartman 		return 0;
796a6afd9f3SGreg Kroah-Hartman 
797a6afd9f3SGreg Kroah-Hartman 	local_irq_save(flags);
798a6afd9f3SGreg Kroah-Hartman 	if (CIRC_SPACE(info->xmit.head,
799a6afd9f3SGreg Kroah-Hartman 		       info->xmit.tail,
800a6afd9f3SGreg Kroah-Hartman 		       SERIAL_XMIT_SIZE) == 0) {
801a6afd9f3SGreg Kroah-Hartman 		local_irq_restore(flags);
802a6afd9f3SGreg Kroah-Hartman 		return 0;
803a6afd9f3SGreg Kroah-Hartman 	}
804a6afd9f3SGreg Kroah-Hartman 
805a6afd9f3SGreg Kroah-Hartman 	info->xmit.buf[info->xmit.head++] = ch;
806a6afd9f3SGreg Kroah-Hartman 	info->xmit.head &= SERIAL_XMIT_SIZE-1;
807a6afd9f3SGreg Kroah-Hartman 	local_irq_restore(flags);
808a6afd9f3SGreg Kroah-Hartman 	return 1;
809a6afd9f3SGreg Kroah-Hartman }
810a6afd9f3SGreg Kroah-Hartman 
811a6afd9f3SGreg Kroah-Hartman static void rs_flush_chars(struct tty_struct *tty)
812a6afd9f3SGreg Kroah-Hartman {
813916b7656SJiri Slaby 	struct serial_state *info = tty->driver_data;
814a6afd9f3SGreg Kroah-Hartman 	unsigned long flags;
815a6afd9f3SGreg Kroah-Hartman 
816a6afd9f3SGreg Kroah-Hartman 	if (serial_paranoia_check(info, tty->name, "rs_flush_chars"))
817a6afd9f3SGreg Kroah-Hartman 		return;
818a6afd9f3SGreg Kroah-Hartman 
819a6afd9f3SGreg Kroah-Hartman 	if (info->xmit.head == info->xmit.tail
820a6afd9f3SGreg Kroah-Hartman 	    || tty->stopped
821a6afd9f3SGreg Kroah-Hartman 	    || tty->hw_stopped
822a6afd9f3SGreg Kroah-Hartman 	    || !info->xmit.buf)
823a6afd9f3SGreg Kroah-Hartman 		return;
824a6afd9f3SGreg Kroah-Hartman 
825a6afd9f3SGreg Kroah-Hartman 	local_irq_save(flags);
826a6afd9f3SGreg Kroah-Hartman 	info->IER |= UART_IER_THRI;
827a6afd9f3SGreg Kroah-Hartman 	custom.intena = IF_SETCLR | IF_TBE;
828a6afd9f3SGreg Kroah-Hartman 	mb();
829a6afd9f3SGreg Kroah-Hartman 	/* set a pending Tx Interrupt, transmitter should restart now */
830a6afd9f3SGreg Kroah-Hartman 	custom.intreq = IF_SETCLR | IF_TBE;
831a6afd9f3SGreg Kroah-Hartman 	mb();
832a6afd9f3SGreg Kroah-Hartman 	local_irq_restore(flags);
833a6afd9f3SGreg Kroah-Hartman }
834a6afd9f3SGreg Kroah-Hartman 
835a6afd9f3SGreg Kroah-Hartman static int rs_write(struct tty_struct * tty, const unsigned char *buf, int count)
836a6afd9f3SGreg Kroah-Hartman {
837a6afd9f3SGreg Kroah-Hartman 	int	c, ret = 0;
838916b7656SJiri Slaby 	struct serial_state *info = tty->driver_data;
839a6afd9f3SGreg Kroah-Hartman 	unsigned long flags;
840a6afd9f3SGreg Kroah-Hartman 
841a6afd9f3SGreg Kroah-Hartman 	if (serial_paranoia_check(info, tty->name, "rs_write"))
842a6afd9f3SGreg Kroah-Hartman 		return 0;
843a6afd9f3SGreg Kroah-Hartman 
844a6afd9f3SGreg Kroah-Hartman 	if (!info->xmit.buf)
845a6afd9f3SGreg Kroah-Hartman 		return 0;
846a6afd9f3SGreg Kroah-Hartman 
847a6afd9f3SGreg Kroah-Hartman 	local_irq_save(flags);
848a6afd9f3SGreg Kroah-Hartman 	while (1) {
849a6afd9f3SGreg Kroah-Hartman 		c = CIRC_SPACE_TO_END(info->xmit.head,
850a6afd9f3SGreg Kroah-Hartman 				      info->xmit.tail,
851a6afd9f3SGreg Kroah-Hartman 				      SERIAL_XMIT_SIZE);
852a6afd9f3SGreg Kroah-Hartman 		if (count < c)
853a6afd9f3SGreg Kroah-Hartman 			c = count;
854a6afd9f3SGreg Kroah-Hartman 		if (c <= 0) {
855a6afd9f3SGreg Kroah-Hartman 			break;
856a6afd9f3SGreg Kroah-Hartman 		}
857a6afd9f3SGreg Kroah-Hartman 		memcpy(info->xmit.buf + info->xmit.head, buf, c);
858a6afd9f3SGreg Kroah-Hartman 		info->xmit.head = ((info->xmit.head + c) &
859a6afd9f3SGreg Kroah-Hartman 				   (SERIAL_XMIT_SIZE-1));
860a6afd9f3SGreg Kroah-Hartman 		buf += c;
861a6afd9f3SGreg Kroah-Hartman 		count -= c;
862a6afd9f3SGreg Kroah-Hartman 		ret += c;
863a6afd9f3SGreg Kroah-Hartman 	}
864a6afd9f3SGreg Kroah-Hartman 	local_irq_restore(flags);
865a6afd9f3SGreg Kroah-Hartman 
866a6afd9f3SGreg Kroah-Hartman 	if (info->xmit.head != info->xmit.tail
867a6afd9f3SGreg Kroah-Hartman 	    && !tty->stopped
868a6afd9f3SGreg Kroah-Hartman 	    && !tty->hw_stopped
869a6afd9f3SGreg Kroah-Hartman 	    && !(info->IER & UART_IER_THRI)) {
870a6afd9f3SGreg Kroah-Hartman 		info->IER |= UART_IER_THRI;
871a6afd9f3SGreg Kroah-Hartman 		local_irq_disable();
872a6afd9f3SGreg Kroah-Hartman 		custom.intena = IF_SETCLR | IF_TBE;
873a6afd9f3SGreg Kroah-Hartman 		mb();
874a6afd9f3SGreg Kroah-Hartman 		/* set a pending Tx Interrupt, transmitter should restart now */
875a6afd9f3SGreg Kroah-Hartman 		custom.intreq = IF_SETCLR | IF_TBE;
876a6afd9f3SGreg Kroah-Hartman 		mb();
877a6afd9f3SGreg Kroah-Hartman 		local_irq_restore(flags);
878a6afd9f3SGreg Kroah-Hartman 	}
879a6afd9f3SGreg Kroah-Hartman 	return ret;
880a6afd9f3SGreg Kroah-Hartman }
881a6afd9f3SGreg Kroah-Hartman 
882a6afd9f3SGreg Kroah-Hartman static int rs_write_room(struct tty_struct *tty)
883a6afd9f3SGreg Kroah-Hartman {
884916b7656SJiri Slaby 	struct serial_state *info = tty->driver_data;
885a6afd9f3SGreg Kroah-Hartman 
886a6afd9f3SGreg Kroah-Hartman 	if (serial_paranoia_check(info, tty->name, "rs_write_room"))
887a6afd9f3SGreg Kroah-Hartman 		return 0;
888a6afd9f3SGreg Kroah-Hartman 	return CIRC_SPACE(info->xmit.head, info->xmit.tail, SERIAL_XMIT_SIZE);
889a6afd9f3SGreg Kroah-Hartman }
890a6afd9f3SGreg Kroah-Hartman 
891a6afd9f3SGreg Kroah-Hartman static int rs_chars_in_buffer(struct tty_struct *tty)
892a6afd9f3SGreg Kroah-Hartman {
893916b7656SJiri Slaby 	struct serial_state *info = tty->driver_data;
894a6afd9f3SGreg Kroah-Hartman 
895a6afd9f3SGreg Kroah-Hartman 	if (serial_paranoia_check(info, tty->name, "rs_chars_in_buffer"))
896a6afd9f3SGreg Kroah-Hartman 		return 0;
897a6afd9f3SGreg Kroah-Hartman 	return CIRC_CNT(info->xmit.head, info->xmit.tail, SERIAL_XMIT_SIZE);
898a6afd9f3SGreg Kroah-Hartman }
899a6afd9f3SGreg Kroah-Hartman 
900a6afd9f3SGreg Kroah-Hartman static void rs_flush_buffer(struct tty_struct *tty)
901a6afd9f3SGreg Kroah-Hartman {
902916b7656SJiri Slaby 	struct serial_state *info = tty->driver_data;
903a6afd9f3SGreg Kroah-Hartman 	unsigned long flags;
904a6afd9f3SGreg Kroah-Hartman 
905a6afd9f3SGreg Kroah-Hartman 	if (serial_paranoia_check(info, tty->name, "rs_flush_buffer"))
906a6afd9f3SGreg Kroah-Hartman 		return;
907a6afd9f3SGreg Kroah-Hartman 	local_irq_save(flags);
908a6afd9f3SGreg Kroah-Hartman 	info->xmit.head = info->xmit.tail = 0;
909a6afd9f3SGreg Kroah-Hartman 	local_irq_restore(flags);
910a6afd9f3SGreg Kroah-Hartman 	tty_wakeup(tty);
911a6afd9f3SGreg Kroah-Hartman }
912a6afd9f3SGreg Kroah-Hartman 
913a6afd9f3SGreg Kroah-Hartman /*
914a6afd9f3SGreg Kroah-Hartman  * This function is used to send a high-priority XON/XOFF character to
915a6afd9f3SGreg Kroah-Hartman  * the device
916a6afd9f3SGreg Kroah-Hartman  */
917a6afd9f3SGreg Kroah-Hartman static void rs_send_xchar(struct tty_struct *tty, char ch)
918a6afd9f3SGreg Kroah-Hartman {
919916b7656SJiri Slaby 	struct serial_state *info = tty->driver_data;
920a6afd9f3SGreg Kroah-Hartman         unsigned long flags;
921a6afd9f3SGreg Kroah-Hartman 
922a6afd9f3SGreg Kroah-Hartman 	if (serial_paranoia_check(info, tty->name, "rs_send_char"))
923a6afd9f3SGreg Kroah-Hartman 		return;
924a6afd9f3SGreg Kroah-Hartman 
925a6afd9f3SGreg Kroah-Hartman 	info->x_char = ch;
926a6afd9f3SGreg Kroah-Hartman 	if (ch) {
927a6afd9f3SGreg Kroah-Hartman 		/* Make sure transmit interrupts are on */
928a6afd9f3SGreg Kroah-Hartman 
929a6afd9f3SGreg Kroah-Hartman 	        /* Check this ! */
930a6afd9f3SGreg Kroah-Hartman 	        local_irq_save(flags);
931a6afd9f3SGreg Kroah-Hartman 		if(!(custom.intenar & IF_TBE)) {
932a6afd9f3SGreg Kroah-Hartman 		    custom.intena = IF_SETCLR | IF_TBE;
933a6afd9f3SGreg Kroah-Hartman 		    mb();
934a6afd9f3SGreg Kroah-Hartman 		    /* set a pending Tx Interrupt, transmitter should restart now */
935a6afd9f3SGreg Kroah-Hartman 		    custom.intreq = IF_SETCLR | IF_TBE;
936a6afd9f3SGreg Kroah-Hartman 		    mb();
937a6afd9f3SGreg Kroah-Hartman 		}
938a6afd9f3SGreg Kroah-Hartman 		local_irq_restore(flags);
939a6afd9f3SGreg Kroah-Hartman 
940a6afd9f3SGreg Kroah-Hartman 		info->IER |= UART_IER_THRI;
941a6afd9f3SGreg Kroah-Hartman 	}
942a6afd9f3SGreg Kroah-Hartman }
943a6afd9f3SGreg Kroah-Hartman 
944a6afd9f3SGreg Kroah-Hartman /*
945a6afd9f3SGreg Kroah-Hartman  * ------------------------------------------------------------
946a6afd9f3SGreg Kroah-Hartman  * rs_throttle()
947a6afd9f3SGreg Kroah-Hartman  *
948a6afd9f3SGreg Kroah-Hartman  * This routine is called by the upper-layer tty layer to signal that
949a6afd9f3SGreg Kroah-Hartman  * incoming characters should be throttled.
950a6afd9f3SGreg Kroah-Hartman  * ------------------------------------------------------------
951a6afd9f3SGreg Kroah-Hartman  */
952a6afd9f3SGreg Kroah-Hartman static void rs_throttle(struct tty_struct * tty)
953a6afd9f3SGreg Kroah-Hartman {
954916b7656SJiri Slaby 	struct serial_state *info = tty->driver_data;
955a6afd9f3SGreg Kroah-Hartman 	unsigned long flags;
956a6afd9f3SGreg Kroah-Hartman #ifdef SERIAL_DEBUG_THROTTLE
957a6afd9f3SGreg Kroah-Hartman 	char	buf[64];
958a6afd9f3SGreg Kroah-Hartman 
959a6afd9f3SGreg Kroah-Hartman 	printk("throttle %s: %d....\n", tty_name(tty, buf),
960a6afd9f3SGreg Kroah-Hartman 	       tty->ldisc.chars_in_buffer(tty));
961a6afd9f3SGreg Kroah-Hartman #endif
962a6afd9f3SGreg Kroah-Hartman 
963a6afd9f3SGreg Kroah-Hartman 	if (serial_paranoia_check(info, tty->name, "rs_throttle"))
964a6afd9f3SGreg Kroah-Hartman 		return;
965a6afd9f3SGreg Kroah-Hartman 
966a6afd9f3SGreg Kroah-Hartman 	if (I_IXOFF(tty))
967a6afd9f3SGreg Kroah-Hartman 		rs_send_xchar(tty, STOP_CHAR(tty));
968a6afd9f3SGreg Kroah-Hartman 
969a6afd9f3SGreg Kroah-Hartman 	if (tty->termios->c_cflag & CRTSCTS)
970a6afd9f3SGreg Kroah-Hartman 		info->MCR &= ~SER_RTS;
971a6afd9f3SGreg Kroah-Hartman 
972a6afd9f3SGreg Kroah-Hartman 	local_irq_save(flags);
973a6afd9f3SGreg Kroah-Hartman 	rtsdtr_ctrl(info->MCR);
974a6afd9f3SGreg Kroah-Hartman 	local_irq_restore(flags);
975a6afd9f3SGreg Kroah-Hartman }
976a6afd9f3SGreg Kroah-Hartman 
977a6afd9f3SGreg Kroah-Hartman static void rs_unthrottle(struct tty_struct * tty)
978a6afd9f3SGreg Kroah-Hartman {
979916b7656SJiri Slaby 	struct serial_state *info = tty->driver_data;
980a6afd9f3SGreg Kroah-Hartman 	unsigned long flags;
981a6afd9f3SGreg Kroah-Hartman #ifdef SERIAL_DEBUG_THROTTLE
982a6afd9f3SGreg Kroah-Hartman 	char	buf[64];
983a6afd9f3SGreg Kroah-Hartman 
984a6afd9f3SGreg Kroah-Hartman 	printk("unthrottle %s: %d....\n", tty_name(tty, buf),
985a6afd9f3SGreg Kroah-Hartman 	       tty->ldisc.chars_in_buffer(tty));
986a6afd9f3SGreg Kroah-Hartman #endif
987a6afd9f3SGreg Kroah-Hartman 
988a6afd9f3SGreg Kroah-Hartman 	if (serial_paranoia_check(info, tty->name, "rs_unthrottle"))
989a6afd9f3SGreg Kroah-Hartman 		return;
990a6afd9f3SGreg Kroah-Hartman 
991a6afd9f3SGreg Kroah-Hartman 	if (I_IXOFF(tty)) {
992a6afd9f3SGreg Kroah-Hartman 		if (info->x_char)
993a6afd9f3SGreg Kroah-Hartman 			info->x_char = 0;
994a6afd9f3SGreg Kroah-Hartman 		else
995a6afd9f3SGreg Kroah-Hartman 			rs_send_xchar(tty, START_CHAR(tty));
996a6afd9f3SGreg Kroah-Hartman 	}
997a6afd9f3SGreg Kroah-Hartman 	if (tty->termios->c_cflag & CRTSCTS)
998a6afd9f3SGreg Kroah-Hartman 		info->MCR |= SER_RTS;
999a6afd9f3SGreg Kroah-Hartman 	local_irq_save(flags);
1000a6afd9f3SGreg Kroah-Hartman 	rtsdtr_ctrl(info->MCR);
1001a6afd9f3SGreg Kroah-Hartman 	local_irq_restore(flags);
1002a6afd9f3SGreg Kroah-Hartman }
1003a6afd9f3SGreg Kroah-Hartman 
1004a6afd9f3SGreg Kroah-Hartman /*
1005a6afd9f3SGreg Kroah-Hartman  * ------------------------------------------------------------
1006a6afd9f3SGreg Kroah-Hartman  * rs_ioctl() and friends
1007a6afd9f3SGreg Kroah-Hartman  * ------------------------------------------------------------
1008a6afd9f3SGreg Kroah-Hartman  */
1009a6afd9f3SGreg Kroah-Hartman 
1010916b7656SJiri Slaby static int get_serial_info(struct serial_state *state,
1011a6afd9f3SGreg Kroah-Hartman 			   struct serial_struct __user * retinfo)
1012a6afd9f3SGreg Kroah-Hartman {
1013a6afd9f3SGreg Kroah-Hartman 	struct serial_struct tmp;
1014a6afd9f3SGreg Kroah-Hartman 
1015a6afd9f3SGreg Kroah-Hartman 	if (!retinfo)
1016a6afd9f3SGreg Kroah-Hartman 		return -EFAULT;
1017a6afd9f3SGreg Kroah-Hartman 	memset(&tmp, 0, sizeof(tmp));
1018a6afd9f3SGreg Kroah-Hartman 	tty_lock();
1019a6afd9f3SGreg Kroah-Hartman 	tmp.type = state->type;
1020a6afd9f3SGreg Kroah-Hartman 	tmp.line = state->line;
1021a6afd9f3SGreg Kroah-Hartman 	tmp.port = state->port;
1022a6afd9f3SGreg Kroah-Hartman 	tmp.irq = state->irq;
1023*01bd730dSJiri Slaby 	tmp.flags = state->tport.flags;
1024a6afd9f3SGreg Kroah-Hartman 	tmp.xmit_fifo_size = state->xmit_fifo_size;
1025a6afd9f3SGreg Kroah-Hartman 	tmp.baud_base = state->baud_base;
1026799be6ffSJiri Slaby 	tmp.close_delay = state->tport.close_delay;
1027799be6ffSJiri Slaby 	tmp.closing_wait = state->tport.closing_wait;
1028a6afd9f3SGreg Kroah-Hartman 	tmp.custom_divisor = state->custom_divisor;
1029a6afd9f3SGreg Kroah-Hartman 	tty_unlock();
1030a6afd9f3SGreg Kroah-Hartman 	if (copy_to_user(retinfo,&tmp,sizeof(*retinfo)))
1031a6afd9f3SGreg Kroah-Hartman 		return -EFAULT;
1032a6afd9f3SGreg Kroah-Hartman 	return 0;
1033a6afd9f3SGreg Kroah-Hartman }
1034a6afd9f3SGreg Kroah-Hartman 
1035588993ddSJiri Slaby static int set_serial_info(struct tty_struct *tty, struct serial_state *state,
1036a6afd9f3SGreg Kroah-Hartman 			   struct serial_struct __user * new_info)
1037a6afd9f3SGreg Kroah-Hartman {
1038*01bd730dSJiri Slaby 	struct tty_port *port = &state->tport;
1039a6afd9f3SGreg Kroah-Hartman 	struct serial_struct new_serial;
10400f9b9684SJiri Slaby 	bool change_spd;
1041a6afd9f3SGreg Kroah-Hartman 	int 			retval = 0;
1042a6afd9f3SGreg Kroah-Hartman 
1043a6afd9f3SGreg Kroah-Hartman 	if (copy_from_user(&new_serial,new_info,sizeof(new_serial)))
1044a6afd9f3SGreg Kroah-Hartman 		return -EFAULT;
1045a6afd9f3SGreg Kroah-Hartman 
1046a6afd9f3SGreg Kroah-Hartman 	tty_lock();
1047*01bd730dSJiri Slaby 	change_spd = ((new_serial.flags ^ port->flags) & ASYNC_SPD_MASK) ||
10480f9b9684SJiri Slaby 		new_serial.custom_divisor != state->custom_divisor;
10490f9b9684SJiri Slaby 	if (new_serial.irq != state->irq || new_serial.port != state->port ||
10500f9b9684SJiri Slaby 			new_serial.xmit_fifo_size != state->xmit_fifo_size) {
1051a6afd9f3SGreg Kroah-Hartman 		tty_unlock();
1052a6afd9f3SGreg Kroah-Hartman 		return -EINVAL;
1053a6afd9f3SGreg Kroah-Hartman 	}
1054a6afd9f3SGreg Kroah-Hartman 
1055a6afd9f3SGreg Kroah-Hartman 	if (!serial_isroot()) {
1056a6afd9f3SGreg Kroah-Hartman 		if ((new_serial.baud_base != state->baud_base) ||
1057*01bd730dSJiri Slaby 		    (new_serial.close_delay != port->close_delay) ||
1058a6afd9f3SGreg Kroah-Hartman 		    (new_serial.xmit_fifo_size != state->xmit_fifo_size) ||
1059a6afd9f3SGreg Kroah-Hartman 		    ((new_serial.flags & ~ASYNC_USR_MASK) !=
1060*01bd730dSJiri Slaby 		     (port->flags & ~ASYNC_USR_MASK)))
1061a6afd9f3SGreg Kroah-Hartman 			return -EPERM;
1062*01bd730dSJiri Slaby 		port->flags = ((port->flags & ~ASYNC_USR_MASK) |
1063a6afd9f3SGreg Kroah-Hartman 			       (new_serial.flags & ASYNC_USR_MASK));
1064a6afd9f3SGreg Kroah-Hartman 		state->custom_divisor = new_serial.custom_divisor;
1065a6afd9f3SGreg Kroah-Hartman 		goto check_and_exit;
1066a6afd9f3SGreg Kroah-Hartman 	}
1067a6afd9f3SGreg Kroah-Hartman 
1068a6afd9f3SGreg Kroah-Hartman 	if (new_serial.baud_base < 9600) {
1069a6afd9f3SGreg Kroah-Hartman 		tty_unlock();
1070a6afd9f3SGreg Kroah-Hartman 		return -EINVAL;
1071a6afd9f3SGreg Kroah-Hartman 	}
1072a6afd9f3SGreg Kroah-Hartman 
1073a6afd9f3SGreg Kroah-Hartman 	/*
1074a6afd9f3SGreg Kroah-Hartman 	 * OK, past this point, all the error checking has been done.
1075a6afd9f3SGreg Kroah-Hartman 	 * At this point, we start making changes.....
1076a6afd9f3SGreg Kroah-Hartman 	 */
1077a6afd9f3SGreg Kroah-Hartman 
1078a6afd9f3SGreg Kroah-Hartman 	state->baud_base = new_serial.baud_base;
1079*01bd730dSJiri Slaby 	port->flags = ((port->flags & ~ASYNC_FLAGS) |
1080a6afd9f3SGreg Kroah-Hartman 			(new_serial.flags & ASYNC_FLAGS));
1081a6afd9f3SGreg Kroah-Hartman 	state->custom_divisor = new_serial.custom_divisor;
1082*01bd730dSJiri Slaby 	port->close_delay = new_serial.close_delay * HZ/100;
1083*01bd730dSJiri Slaby 	port->closing_wait = new_serial.closing_wait * HZ/100;
1084*01bd730dSJiri Slaby 	tty->low_latency = (port->flags & ASYNC_LOW_LATENCY) ? 1 : 0;
1085a6afd9f3SGreg Kroah-Hartman 
1086a6afd9f3SGreg Kroah-Hartman check_and_exit:
1087*01bd730dSJiri Slaby 	if (port->flags & ASYNC_INITIALIZED) {
10880f9b9684SJiri Slaby 		if (change_spd) {
1089*01bd730dSJiri Slaby 			if ((port->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI)
1090588993ddSJiri Slaby 				tty->alt_speed = 57600;
1091*01bd730dSJiri Slaby 			if ((port->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI)
1092588993ddSJiri Slaby 				tty->alt_speed = 115200;
1093*01bd730dSJiri Slaby 			if ((port->flags & ASYNC_SPD_MASK) == ASYNC_SPD_SHI)
1094588993ddSJiri Slaby 				tty->alt_speed = 230400;
1095*01bd730dSJiri Slaby 			if ((port->flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP)
1096588993ddSJiri Slaby 				tty->alt_speed = 460800;
1097588993ddSJiri Slaby 			change_speed(tty, state, NULL);
1098a6afd9f3SGreg Kroah-Hartman 		}
1099a6afd9f3SGreg Kroah-Hartman 	} else
1100588993ddSJiri Slaby 		retval = startup(tty, state);
1101a6afd9f3SGreg Kroah-Hartman 	tty_unlock();
1102a6afd9f3SGreg Kroah-Hartman 	return retval;
1103a6afd9f3SGreg Kroah-Hartman }
1104a6afd9f3SGreg Kroah-Hartman 
1105a6afd9f3SGreg Kroah-Hartman 
1106a6afd9f3SGreg Kroah-Hartman /*
1107a6afd9f3SGreg Kroah-Hartman  * get_lsr_info - get line status register info
1108a6afd9f3SGreg Kroah-Hartman  *
1109a6afd9f3SGreg Kroah-Hartman  * Purpose: Let user call ioctl() to get info when the UART physically
1110a6afd9f3SGreg Kroah-Hartman  * 	    is emptied.  On bus types like RS485, the transmitter must
1111a6afd9f3SGreg Kroah-Hartman  * 	    release the bus after transmitting. This must be done when
1112a6afd9f3SGreg Kroah-Hartman  * 	    the transmit shift register is empty, not be done when the
1113a6afd9f3SGreg Kroah-Hartman  * 	    transmit holding register is empty.  This functionality
1114a6afd9f3SGreg Kroah-Hartman  * 	    allows an RS485 driver to be written in user space.
1115a6afd9f3SGreg Kroah-Hartman  */
1116916b7656SJiri Slaby static int get_lsr_info(struct serial_state *info, unsigned int __user *value)
1117a6afd9f3SGreg Kroah-Hartman {
1118a6afd9f3SGreg Kroah-Hartman 	unsigned char status;
1119a6afd9f3SGreg Kroah-Hartman 	unsigned int result;
1120a6afd9f3SGreg Kroah-Hartman 	unsigned long flags;
1121a6afd9f3SGreg Kroah-Hartman 
1122a6afd9f3SGreg Kroah-Hartman 	local_irq_save(flags);
1123a6afd9f3SGreg Kroah-Hartman 	status = custom.serdatr;
1124a6afd9f3SGreg Kroah-Hartman 	mb();
1125a6afd9f3SGreg Kroah-Hartman 	local_irq_restore(flags);
1126a6afd9f3SGreg Kroah-Hartman 	result = ((status & SDR_TSRE) ? TIOCSER_TEMT : 0);
1127a6afd9f3SGreg Kroah-Hartman 	if (copy_to_user(value, &result, sizeof(int)))
1128a6afd9f3SGreg Kroah-Hartman 		return -EFAULT;
1129a6afd9f3SGreg Kroah-Hartman 	return 0;
1130a6afd9f3SGreg Kroah-Hartman }
1131a6afd9f3SGreg Kroah-Hartman 
1132a6afd9f3SGreg Kroah-Hartman 
1133a6afd9f3SGreg Kroah-Hartman static int rs_tiocmget(struct tty_struct *tty)
1134a6afd9f3SGreg Kroah-Hartman {
1135916b7656SJiri Slaby 	struct serial_state *info = tty->driver_data;
1136a6afd9f3SGreg Kroah-Hartman 	unsigned char control, status;
1137a6afd9f3SGreg Kroah-Hartman 	unsigned long flags;
1138a6afd9f3SGreg Kroah-Hartman 
1139a6afd9f3SGreg Kroah-Hartman 	if (serial_paranoia_check(info, tty->name, "rs_ioctl"))
1140a6afd9f3SGreg Kroah-Hartman 		return -ENODEV;
1141a6afd9f3SGreg Kroah-Hartman 	if (tty->flags & (1 << TTY_IO_ERROR))
1142a6afd9f3SGreg Kroah-Hartman 		return -EIO;
1143a6afd9f3SGreg Kroah-Hartman 
1144a6afd9f3SGreg Kroah-Hartman 	control = info->MCR;
1145a6afd9f3SGreg Kroah-Hartman 	local_irq_save(flags);
1146a6afd9f3SGreg Kroah-Hartman 	status = ciab.pra;
1147a6afd9f3SGreg Kroah-Hartman 	local_irq_restore(flags);
1148a6afd9f3SGreg Kroah-Hartman 	return    ((control & SER_RTS) ? TIOCM_RTS : 0)
1149a6afd9f3SGreg Kroah-Hartman 		| ((control & SER_DTR) ? TIOCM_DTR : 0)
1150a6afd9f3SGreg Kroah-Hartman 		| (!(status  & SER_DCD) ? TIOCM_CAR : 0)
1151a6afd9f3SGreg Kroah-Hartman 		| (!(status  & SER_DSR) ? TIOCM_DSR : 0)
1152a6afd9f3SGreg Kroah-Hartman 		| (!(status  & SER_CTS) ? TIOCM_CTS : 0);
1153a6afd9f3SGreg Kroah-Hartman }
1154a6afd9f3SGreg Kroah-Hartman 
1155a6afd9f3SGreg Kroah-Hartman static int rs_tiocmset(struct tty_struct *tty, unsigned int set,
1156a6afd9f3SGreg Kroah-Hartman 						unsigned int clear)
1157a6afd9f3SGreg Kroah-Hartman {
1158916b7656SJiri Slaby 	struct serial_state *info = tty->driver_data;
1159a6afd9f3SGreg Kroah-Hartman 	unsigned long flags;
1160a6afd9f3SGreg Kroah-Hartman 
1161a6afd9f3SGreg Kroah-Hartman 	if (serial_paranoia_check(info, tty->name, "rs_ioctl"))
1162a6afd9f3SGreg Kroah-Hartman 		return -ENODEV;
1163a6afd9f3SGreg Kroah-Hartman 	if (tty->flags & (1 << TTY_IO_ERROR))
1164a6afd9f3SGreg Kroah-Hartman 		return -EIO;
1165a6afd9f3SGreg Kroah-Hartman 
1166a6afd9f3SGreg Kroah-Hartman 	local_irq_save(flags);
1167a6afd9f3SGreg Kroah-Hartman 	if (set & TIOCM_RTS)
1168a6afd9f3SGreg Kroah-Hartman 		info->MCR |= SER_RTS;
1169a6afd9f3SGreg Kroah-Hartman 	if (set & TIOCM_DTR)
1170a6afd9f3SGreg Kroah-Hartman 		info->MCR |= SER_DTR;
1171a6afd9f3SGreg Kroah-Hartman 	if (clear & TIOCM_RTS)
1172a6afd9f3SGreg Kroah-Hartman 		info->MCR &= ~SER_RTS;
1173a6afd9f3SGreg Kroah-Hartman 	if (clear & TIOCM_DTR)
1174a6afd9f3SGreg Kroah-Hartman 		info->MCR &= ~SER_DTR;
1175a6afd9f3SGreg Kroah-Hartman 	rtsdtr_ctrl(info->MCR);
1176a6afd9f3SGreg Kroah-Hartman 	local_irq_restore(flags);
1177a6afd9f3SGreg Kroah-Hartman 	return 0;
1178a6afd9f3SGreg Kroah-Hartman }
1179a6afd9f3SGreg Kroah-Hartman 
1180a6afd9f3SGreg Kroah-Hartman /*
1181a6afd9f3SGreg Kroah-Hartman  * rs_break() --- routine which turns the break handling on or off
1182a6afd9f3SGreg Kroah-Hartman  */
1183a6afd9f3SGreg Kroah-Hartman static int rs_break(struct tty_struct *tty, int break_state)
1184a6afd9f3SGreg Kroah-Hartman {
1185916b7656SJiri Slaby 	struct serial_state *info = tty->driver_data;
1186a6afd9f3SGreg Kroah-Hartman 	unsigned long flags;
1187a6afd9f3SGreg Kroah-Hartman 
1188a6afd9f3SGreg Kroah-Hartman 	if (serial_paranoia_check(info, tty->name, "rs_break"))
1189a6afd9f3SGreg Kroah-Hartman 		return -EINVAL;
1190a6afd9f3SGreg Kroah-Hartman 
1191a6afd9f3SGreg Kroah-Hartman 	local_irq_save(flags);
1192a6afd9f3SGreg Kroah-Hartman 	if (break_state == -1)
1193a6afd9f3SGreg Kroah-Hartman 	  custom.adkcon = AC_SETCLR | AC_UARTBRK;
1194a6afd9f3SGreg Kroah-Hartman 	else
1195a6afd9f3SGreg Kroah-Hartman 	  custom.adkcon = AC_UARTBRK;
1196a6afd9f3SGreg Kroah-Hartman 	mb();
1197a6afd9f3SGreg Kroah-Hartman 	local_irq_restore(flags);
1198a6afd9f3SGreg Kroah-Hartman 	return 0;
1199a6afd9f3SGreg Kroah-Hartman }
1200a6afd9f3SGreg Kroah-Hartman 
1201a6afd9f3SGreg Kroah-Hartman /*
1202a6afd9f3SGreg Kroah-Hartman  * Get counter of input serial line interrupts (DCD,RI,DSR,CTS)
1203a6afd9f3SGreg Kroah-Hartman  * Return: write counters to the user passed counter struct
1204a6afd9f3SGreg Kroah-Hartman  * NB: both 1->0 and 0->1 transitions are counted except for
1205a6afd9f3SGreg Kroah-Hartman  *     RI where only 0->1 is counted.
1206a6afd9f3SGreg Kroah-Hartman  */
1207a6afd9f3SGreg Kroah-Hartman static int rs_get_icount(struct tty_struct *tty,
1208a6afd9f3SGreg Kroah-Hartman 				struct serial_icounter_struct *icount)
1209a6afd9f3SGreg Kroah-Hartman {
1210916b7656SJiri Slaby 	struct serial_state *info = tty->driver_data;
1211a6afd9f3SGreg Kroah-Hartman 	struct async_icount cnow;
1212a6afd9f3SGreg Kroah-Hartman 	unsigned long flags;
1213a6afd9f3SGreg Kroah-Hartman 
1214a6afd9f3SGreg Kroah-Hartman 	local_irq_save(flags);
1215916b7656SJiri Slaby 	cnow = info->icount;
1216a6afd9f3SGreg Kroah-Hartman 	local_irq_restore(flags);
1217a6afd9f3SGreg Kroah-Hartman 	icount->cts = cnow.cts;
1218a6afd9f3SGreg Kroah-Hartman 	icount->dsr = cnow.dsr;
1219a6afd9f3SGreg Kroah-Hartman 	icount->rng = cnow.rng;
1220a6afd9f3SGreg Kroah-Hartman 	icount->dcd = cnow.dcd;
1221a6afd9f3SGreg Kroah-Hartman 	icount->rx = cnow.rx;
1222a6afd9f3SGreg Kroah-Hartman 	icount->tx = cnow.tx;
1223a6afd9f3SGreg Kroah-Hartman 	icount->frame = cnow.frame;
1224a6afd9f3SGreg Kroah-Hartman 	icount->overrun = cnow.overrun;
1225a6afd9f3SGreg Kroah-Hartman 	icount->parity = cnow.parity;
1226a6afd9f3SGreg Kroah-Hartman 	icount->brk = cnow.brk;
1227a6afd9f3SGreg Kroah-Hartman 	icount->buf_overrun = cnow.buf_overrun;
1228a6afd9f3SGreg Kroah-Hartman 
1229a6afd9f3SGreg Kroah-Hartman 	return 0;
1230a6afd9f3SGreg Kroah-Hartman }
1231a6afd9f3SGreg Kroah-Hartman 
1232a6afd9f3SGreg Kroah-Hartman static int rs_ioctl(struct tty_struct *tty,
1233a6afd9f3SGreg Kroah-Hartman 		    unsigned int cmd, unsigned long arg)
1234a6afd9f3SGreg Kroah-Hartman {
1235916b7656SJiri Slaby 	struct serial_state *info = tty->driver_data;
1236a6afd9f3SGreg Kroah-Hartman 	struct async_icount cprev, cnow;	/* kernel counter temps */
1237a6afd9f3SGreg Kroah-Hartman 	void __user *argp = (void __user *)arg;
1238a6afd9f3SGreg Kroah-Hartman 	unsigned long flags;
1239a6afd9f3SGreg Kroah-Hartman 
1240a6afd9f3SGreg Kroah-Hartman 	if (serial_paranoia_check(info, tty->name, "rs_ioctl"))
1241a6afd9f3SGreg Kroah-Hartman 		return -ENODEV;
1242a6afd9f3SGreg Kroah-Hartman 
1243a6afd9f3SGreg Kroah-Hartman 	if ((cmd != TIOCGSERIAL) && (cmd != TIOCSSERIAL) &&
1244a6afd9f3SGreg Kroah-Hartman 	    (cmd != TIOCSERCONFIG) && (cmd != TIOCSERGSTRUCT) &&
1245a6afd9f3SGreg Kroah-Hartman 	    (cmd != TIOCMIWAIT) && (cmd != TIOCGICOUNT)) {
1246a6afd9f3SGreg Kroah-Hartman 		if (tty->flags & (1 << TTY_IO_ERROR))
1247a6afd9f3SGreg Kroah-Hartman 		    return -EIO;
1248a6afd9f3SGreg Kroah-Hartman 	}
1249a6afd9f3SGreg Kroah-Hartman 
1250a6afd9f3SGreg Kroah-Hartman 	switch (cmd) {
1251a6afd9f3SGreg Kroah-Hartman 		case TIOCGSERIAL:
1252a6afd9f3SGreg Kroah-Hartman 			return get_serial_info(info, argp);
1253a6afd9f3SGreg Kroah-Hartman 		case TIOCSSERIAL:
1254588993ddSJiri Slaby 			return set_serial_info(tty, info, argp);
1255a6afd9f3SGreg Kroah-Hartman 		case TIOCSERCONFIG:
1256a6afd9f3SGreg Kroah-Hartman 			return 0;
1257a6afd9f3SGreg Kroah-Hartman 
1258a6afd9f3SGreg Kroah-Hartman 		case TIOCSERGETLSR: /* Get line status register */
1259a6afd9f3SGreg Kroah-Hartman 			return get_lsr_info(info, argp);
1260a6afd9f3SGreg Kroah-Hartman 
1261a6afd9f3SGreg Kroah-Hartman 		case TIOCSERGSTRUCT:
1262a6afd9f3SGreg Kroah-Hartman 			if (copy_to_user(argp,
1263916b7656SJiri Slaby 					 info, sizeof(struct serial_state)))
1264a6afd9f3SGreg Kroah-Hartman 				return -EFAULT;
1265a6afd9f3SGreg Kroah-Hartman 			return 0;
1266a6afd9f3SGreg Kroah-Hartman 
1267a6afd9f3SGreg Kroah-Hartman 		/*
1268a6afd9f3SGreg Kroah-Hartman 		 * Wait for any of the 4 modem inputs (DCD,RI,DSR,CTS) to change
1269a6afd9f3SGreg Kroah-Hartman 		 * - mask passed in arg for lines of interest
1270a6afd9f3SGreg Kroah-Hartman  		 *   (use |'ed TIOCM_RNG/DSR/CD/CTS for masking)
1271a6afd9f3SGreg Kroah-Hartman 		 * Caller should use TIOCGICOUNT to see which one it was
1272a6afd9f3SGreg Kroah-Hartman 		 */
1273a6afd9f3SGreg Kroah-Hartman 		case TIOCMIWAIT:
1274a6afd9f3SGreg Kroah-Hartman 			local_irq_save(flags);
1275a6afd9f3SGreg Kroah-Hartman 			/* note the counters on entry */
1276916b7656SJiri Slaby 			cprev = info->icount;
1277a6afd9f3SGreg Kroah-Hartman 			local_irq_restore(flags);
1278a6afd9f3SGreg Kroah-Hartman 			while (1) {
127987758791SJiri Slaby 				interruptible_sleep_on(&info->tport.delta_msr_wait);
1280a6afd9f3SGreg Kroah-Hartman 				/* see if a signal did it */
1281a6afd9f3SGreg Kroah-Hartman 				if (signal_pending(current))
1282a6afd9f3SGreg Kroah-Hartman 					return -ERESTARTSYS;
1283a6afd9f3SGreg Kroah-Hartman 				local_irq_save(flags);
1284916b7656SJiri Slaby 				cnow = info->icount; /* atomic copy */
1285a6afd9f3SGreg Kroah-Hartman 				local_irq_restore(flags);
1286a6afd9f3SGreg Kroah-Hartman 				if (cnow.rng == cprev.rng && cnow.dsr == cprev.dsr &&
1287a6afd9f3SGreg Kroah-Hartman 				    cnow.dcd == cprev.dcd && cnow.cts == cprev.cts)
1288a6afd9f3SGreg Kroah-Hartman 					return -EIO; /* no change => error */
1289a6afd9f3SGreg Kroah-Hartman 				if ( ((arg & TIOCM_RNG) && (cnow.rng != cprev.rng)) ||
1290a6afd9f3SGreg Kroah-Hartman 				     ((arg & TIOCM_DSR) && (cnow.dsr != cprev.dsr)) ||
1291a6afd9f3SGreg Kroah-Hartman 				     ((arg & TIOCM_CD)  && (cnow.dcd != cprev.dcd)) ||
1292a6afd9f3SGreg Kroah-Hartman 				     ((arg & TIOCM_CTS) && (cnow.cts != cprev.cts)) ) {
1293a6afd9f3SGreg Kroah-Hartman 					return 0;
1294a6afd9f3SGreg Kroah-Hartman 				}
1295a6afd9f3SGreg Kroah-Hartman 				cprev = cnow;
1296a6afd9f3SGreg Kroah-Hartman 			}
1297a6afd9f3SGreg Kroah-Hartman 			/* NOTREACHED */
1298a6afd9f3SGreg Kroah-Hartman 
1299a6afd9f3SGreg Kroah-Hartman 		case TIOCSERGWILD:
1300a6afd9f3SGreg Kroah-Hartman 		case TIOCSERSWILD:
1301a6afd9f3SGreg Kroah-Hartman 			/* "setserial -W" is called in Debian boot */
1302a6afd9f3SGreg Kroah-Hartman 			printk ("TIOCSER?WILD ioctl obsolete, ignored.\n");
1303a6afd9f3SGreg Kroah-Hartman 			return 0;
1304a6afd9f3SGreg Kroah-Hartman 
1305a6afd9f3SGreg Kroah-Hartman 		default:
1306a6afd9f3SGreg Kroah-Hartman 			return -ENOIOCTLCMD;
1307a6afd9f3SGreg Kroah-Hartman 		}
1308a6afd9f3SGreg Kroah-Hartman 	return 0;
1309a6afd9f3SGreg Kroah-Hartman }
1310a6afd9f3SGreg Kroah-Hartman 
1311a6afd9f3SGreg Kroah-Hartman static void rs_set_termios(struct tty_struct *tty, struct ktermios *old_termios)
1312a6afd9f3SGreg Kroah-Hartman {
1313916b7656SJiri Slaby 	struct serial_state *info = tty->driver_data;
1314a6afd9f3SGreg Kroah-Hartman 	unsigned long flags;
1315a6afd9f3SGreg Kroah-Hartman 	unsigned int cflag = tty->termios->c_cflag;
1316a6afd9f3SGreg Kroah-Hartman 
1317588993ddSJiri Slaby 	change_speed(tty, info, old_termios);
1318a6afd9f3SGreg Kroah-Hartman 
1319a6afd9f3SGreg Kroah-Hartman 	/* Handle transition to B0 status */
1320a6afd9f3SGreg Kroah-Hartman 	if ((old_termios->c_cflag & CBAUD) &&
1321a6afd9f3SGreg Kroah-Hartman 	    !(cflag & CBAUD)) {
1322a6afd9f3SGreg Kroah-Hartman 		info->MCR &= ~(SER_DTR|SER_RTS);
1323a6afd9f3SGreg Kroah-Hartman 		local_irq_save(flags);
1324a6afd9f3SGreg Kroah-Hartman 		rtsdtr_ctrl(info->MCR);
1325a6afd9f3SGreg Kroah-Hartman 		local_irq_restore(flags);
1326a6afd9f3SGreg Kroah-Hartman 	}
1327a6afd9f3SGreg Kroah-Hartman 
1328a6afd9f3SGreg Kroah-Hartman 	/* Handle transition away from B0 status */
1329a6afd9f3SGreg Kroah-Hartman 	if (!(old_termios->c_cflag & CBAUD) &&
1330a6afd9f3SGreg Kroah-Hartman 	    (cflag & CBAUD)) {
1331a6afd9f3SGreg Kroah-Hartman 		info->MCR |= SER_DTR;
1332a6afd9f3SGreg Kroah-Hartman 		if (!(tty->termios->c_cflag & CRTSCTS) ||
1333a6afd9f3SGreg Kroah-Hartman 		    !test_bit(TTY_THROTTLED, &tty->flags)) {
1334a6afd9f3SGreg Kroah-Hartman 			info->MCR |= SER_RTS;
1335a6afd9f3SGreg Kroah-Hartman 		}
1336a6afd9f3SGreg Kroah-Hartman 		local_irq_save(flags);
1337a6afd9f3SGreg Kroah-Hartman 		rtsdtr_ctrl(info->MCR);
1338a6afd9f3SGreg Kroah-Hartman 		local_irq_restore(flags);
1339a6afd9f3SGreg Kroah-Hartman 	}
1340a6afd9f3SGreg Kroah-Hartman 
1341a6afd9f3SGreg Kroah-Hartman 	/* Handle turning off CRTSCTS */
1342a6afd9f3SGreg Kroah-Hartman 	if ((old_termios->c_cflag & CRTSCTS) &&
1343a6afd9f3SGreg Kroah-Hartman 	    !(tty->termios->c_cflag & CRTSCTS)) {
1344a6afd9f3SGreg Kroah-Hartman 		tty->hw_stopped = 0;
1345a6afd9f3SGreg Kroah-Hartman 		rs_start(tty);
1346a6afd9f3SGreg Kroah-Hartman 	}
1347a6afd9f3SGreg Kroah-Hartman 
1348a6afd9f3SGreg Kroah-Hartman #if 0
1349a6afd9f3SGreg Kroah-Hartman 	/*
1350a6afd9f3SGreg Kroah-Hartman 	 * No need to wake up processes in open wait, since they
1351a6afd9f3SGreg Kroah-Hartman 	 * sample the CLOCAL flag once, and don't recheck it.
1352a6afd9f3SGreg Kroah-Hartman 	 * XXX  It's not clear whether the current behavior is correct
1353a6afd9f3SGreg Kroah-Hartman 	 * or not.  Hence, this may change.....
1354a6afd9f3SGreg Kroah-Hartman 	 */
1355a6afd9f3SGreg Kroah-Hartman 	if (!(old_termios->c_cflag & CLOCAL) &&
1356a6afd9f3SGreg Kroah-Hartman 	    (tty->termios->c_cflag & CLOCAL))
1357a6afd9f3SGreg Kroah-Hartman 		wake_up_interruptible(&info->open_wait);
1358a6afd9f3SGreg Kroah-Hartman #endif
1359a6afd9f3SGreg Kroah-Hartman }
1360a6afd9f3SGreg Kroah-Hartman 
1361a6afd9f3SGreg Kroah-Hartman /*
1362a6afd9f3SGreg Kroah-Hartman  * ------------------------------------------------------------
1363a6afd9f3SGreg Kroah-Hartman  * rs_close()
1364a6afd9f3SGreg Kroah-Hartman  *
1365a6afd9f3SGreg Kroah-Hartman  * This routine is called when the serial port gets closed.  First, we
1366a6afd9f3SGreg Kroah-Hartman  * wait for the last remaining data to be sent.  Then, we unlink its
1367a6afd9f3SGreg Kroah-Hartman  * async structure from the interrupt chain if necessary, and we free
1368a6afd9f3SGreg Kroah-Hartman  * that IRQ if nothing is left in the chain.
1369a6afd9f3SGreg Kroah-Hartman  * ------------------------------------------------------------
1370a6afd9f3SGreg Kroah-Hartman  */
1371a6afd9f3SGreg Kroah-Hartman static void rs_close(struct tty_struct *tty, struct file * filp)
1372a6afd9f3SGreg Kroah-Hartman {
1373916b7656SJiri Slaby 	struct serial_state *state = tty->driver_data;
1374a6afd9f3SGreg Kroah-Hartman 	unsigned long flags;
1375a6afd9f3SGreg Kroah-Hartman 
1376916b7656SJiri Slaby 	if (!state || serial_paranoia_check(state, tty->name, "rs_close"))
1377a6afd9f3SGreg Kroah-Hartman 		return;
1378a6afd9f3SGreg Kroah-Hartman 
1379a6afd9f3SGreg Kroah-Hartman 	local_irq_save(flags);
1380a6afd9f3SGreg Kroah-Hartman 
1381a6afd9f3SGreg Kroah-Hartman 	if (tty_hung_up_p(filp)) {
1382a6afd9f3SGreg Kroah-Hartman 		DBG_CNT("before DEC-hung");
1383a6afd9f3SGreg Kroah-Hartman 		local_irq_restore(flags);
1384a6afd9f3SGreg Kroah-Hartman 		return;
1385a6afd9f3SGreg Kroah-Hartman 	}
1386a6afd9f3SGreg Kroah-Hartman 
1387a6afd9f3SGreg Kroah-Hartman #ifdef SERIAL_DEBUG_OPEN
138812c80354SJiri Slaby 	printk("rs_close ttys%d, count = %d\n", state->line, state->tport.count);
1389a6afd9f3SGreg Kroah-Hartman #endif
139012c80354SJiri Slaby 	if ((tty->count == 1) && (state->tport.count != 1)) {
1391a6afd9f3SGreg Kroah-Hartman 		/*
1392a6afd9f3SGreg Kroah-Hartman 		 * Uh, oh.  tty->count is 1, which means that the tty
139312c80354SJiri Slaby 		 * structure will be freed.  state->tport.count should always
1394a6afd9f3SGreg Kroah-Hartman 		 * be one in these conditions.  If it's greater than
1395a6afd9f3SGreg Kroah-Hartman 		 * one, we've got real problems, since it means the
1396a6afd9f3SGreg Kroah-Hartman 		 * serial port won't be shutdown.
1397a6afd9f3SGreg Kroah-Hartman 		 */
1398a6afd9f3SGreg Kroah-Hartman 		printk("rs_close: bad serial port count; tty->count is 1, "
139912c80354SJiri Slaby 		       "state->tport.count is %d\n", state->tport.count);
140012c80354SJiri Slaby 		state->tport.count = 1;
1401a6afd9f3SGreg Kroah-Hartman 	}
140212c80354SJiri Slaby 	if (--state->tport.count < 0) {
1403a6afd9f3SGreg Kroah-Hartman 		printk("rs_close: bad serial port count for ttys%d: %d\n",
140412c80354SJiri Slaby 		       state->line, state->tport.count);
140512c80354SJiri Slaby 		state->tport.count = 0;
1406a6afd9f3SGreg Kroah-Hartman 	}
140712c80354SJiri Slaby 	if (state->tport.count) {
1408a6afd9f3SGreg Kroah-Hartman 		DBG_CNT("before DEC-2");
1409a6afd9f3SGreg Kroah-Hartman 		local_irq_restore(flags);
1410a6afd9f3SGreg Kroah-Hartman 		return;
1411a6afd9f3SGreg Kroah-Hartman 	}
1412*01bd730dSJiri Slaby 	state->tport.flags |= ASYNC_CLOSING;
1413a6afd9f3SGreg Kroah-Hartman 	/*
1414a6afd9f3SGreg Kroah-Hartman 	 * Now we wait for the transmit buffer to clear; and we notify
1415a6afd9f3SGreg Kroah-Hartman 	 * the line discipline to only process XON/XOFF characters.
1416a6afd9f3SGreg Kroah-Hartman 	 */
1417a6afd9f3SGreg Kroah-Hartman 	tty->closing = 1;
1418799be6ffSJiri Slaby 	if (state->tport.closing_wait != ASYNC_CLOSING_WAIT_NONE)
1419799be6ffSJiri Slaby 		tty_wait_until_sent(tty, state->tport.closing_wait);
1420a6afd9f3SGreg Kroah-Hartman 	/*
1421a6afd9f3SGreg Kroah-Hartman 	 * At this point we stop accepting input.  To do this, we
1422a6afd9f3SGreg Kroah-Hartman 	 * disable the receive line status interrupts, and tell the
1423a6afd9f3SGreg Kroah-Hartman 	 * interrupt driver to stop checking the data ready bit in the
1424a6afd9f3SGreg Kroah-Hartman 	 * line status register.
1425a6afd9f3SGreg Kroah-Hartman 	 */
1426916b7656SJiri Slaby 	state->read_status_mask &= ~UART_LSR_DR;
1427*01bd730dSJiri Slaby 	if (state->tport.flags & ASYNC_INITIALIZED) {
1428a6afd9f3SGreg Kroah-Hartman 	        /* disable receive interrupts */
1429a6afd9f3SGreg Kroah-Hartman 	        custom.intena = IF_RBF;
1430a6afd9f3SGreg Kroah-Hartman 		mb();
1431a6afd9f3SGreg Kroah-Hartman 		/* clear any pending receive interrupt */
1432a6afd9f3SGreg Kroah-Hartman 		custom.intreq = IF_RBF;
1433a6afd9f3SGreg Kroah-Hartman 		mb();
1434a6afd9f3SGreg Kroah-Hartman 
1435a6afd9f3SGreg Kroah-Hartman 		/*
1436a6afd9f3SGreg Kroah-Hartman 		 * Before we drop DTR, make sure the UART transmitter
1437a6afd9f3SGreg Kroah-Hartman 		 * has completely drained; this is especially
1438a6afd9f3SGreg Kroah-Hartman 		 * important if there is a transmit FIFO!
1439a6afd9f3SGreg Kroah-Hartman 		 */
1440916b7656SJiri Slaby 		rs_wait_until_sent(tty, state->timeout);
1441a6afd9f3SGreg Kroah-Hartman 	}
1442588993ddSJiri Slaby 	shutdown(tty, state);
1443a6afd9f3SGreg Kroah-Hartman 	rs_flush_buffer(tty);
1444a6afd9f3SGreg Kroah-Hartman 
1445a6afd9f3SGreg Kroah-Hartman 	tty_ldisc_flush(tty);
1446a6afd9f3SGreg Kroah-Hartman 	tty->closing = 0;
144787758791SJiri Slaby 	state->tport.tty = NULL;
144887758791SJiri Slaby 	if (state->tport.blocked_open) {
1449799be6ffSJiri Slaby 		if (state->tport.close_delay) {
1450799be6ffSJiri Slaby 			msleep_interruptible(jiffies_to_msecs(state->tport.close_delay));
1451a6afd9f3SGreg Kroah-Hartman 		}
145287758791SJiri Slaby 		wake_up_interruptible(&state->tport.open_wait);
1453a6afd9f3SGreg Kroah-Hartman 	}
1454*01bd730dSJiri Slaby 	state->tport.flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CLOSING);
145587758791SJiri Slaby 	wake_up_interruptible(&state->tport.close_wait);
1456a6afd9f3SGreg Kroah-Hartman 	local_irq_restore(flags);
1457a6afd9f3SGreg Kroah-Hartman }
1458a6afd9f3SGreg Kroah-Hartman 
1459a6afd9f3SGreg Kroah-Hartman /*
1460a6afd9f3SGreg Kroah-Hartman  * rs_wait_until_sent() --- wait until the transmitter is empty
1461a6afd9f3SGreg Kroah-Hartman  */
1462a6afd9f3SGreg Kroah-Hartman static void rs_wait_until_sent(struct tty_struct *tty, int timeout)
1463a6afd9f3SGreg Kroah-Hartman {
1464916b7656SJiri Slaby 	struct serial_state *info = tty->driver_data;
1465a6afd9f3SGreg Kroah-Hartman 	unsigned long orig_jiffies, char_time;
1466a6afd9f3SGreg Kroah-Hartman 	int lsr;
1467a6afd9f3SGreg Kroah-Hartman 
1468a6afd9f3SGreg Kroah-Hartman 	if (serial_paranoia_check(info, tty->name, "rs_wait_until_sent"))
1469a6afd9f3SGreg Kroah-Hartman 		return;
1470a6afd9f3SGreg Kroah-Hartman 
1471916b7656SJiri Slaby 	if (info->xmit_fifo_size == 0)
1472a6afd9f3SGreg Kroah-Hartman 		return; /* Just in case.... */
1473a6afd9f3SGreg Kroah-Hartman 
1474a6afd9f3SGreg Kroah-Hartman 	orig_jiffies = jiffies;
1475a6afd9f3SGreg Kroah-Hartman 
1476a6afd9f3SGreg Kroah-Hartman 	/*
1477a6afd9f3SGreg Kroah-Hartman 	 * Set the check interval to be 1/5 of the estimated time to
1478a6afd9f3SGreg Kroah-Hartman 	 * send a single character, and make it at least 1.  The check
1479a6afd9f3SGreg Kroah-Hartman 	 * interval should also be less than the timeout.
1480a6afd9f3SGreg Kroah-Hartman 	 *
1481a6afd9f3SGreg Kroah-Hartman 	 * Note: we have to use pretty tight timings here to satisfy
1482a6afd9f3SGreg Kroah-Hartman 	 * the NIST-PCTS.
1483a6afd9f3SGreg Kroah-Hartman 	 */
1484916b7656SJiri Slaby 	char_time = (info->timeout - HZ/50) / info->xmit_fifo_size;
1485a6afd9f3SGreg Kroah-Hartman 	char_time = char_time / 5;
1486a6afd9f3SGreg Kroah-Hartman 	if (char_time == 0)
1487a6afd9f3SGreg Kroah-Hartman 		char_time = 1;
1488a6afd9f3SGreg Kroah-Hartman 	if (timeout)
1489a6afd9f3SGreg Kroah-Hartman 	  char_time = min_t(unsigned long, char_time, timeout);
1490a6afd9f3SGreg Kroah-Hartman 	/*
1491a6afd9f3SGreg Kroah-Hartman 	 * If the transmitter hasn't cleared in twice the approximate
1492a6afd9f3SGreg Kroah-Hartman 	 * amount of time to send the entire FIFO, it probably won't
1493a6afd9f3SGreg Kroah-Hartman 	 * ever clear.  This assumes the UART isn't doing flow
1494a6afd9f3SGreg Kroah-Hartman 	 * control, which is currently the case.  Hence, if it ever
1495a6afd9f3SGreg Kroah-Hartman 	 * takes longer than info->timeout, this is probably due to a
1496a6afd9f3SGreg Kroah-Hartman 	 * UART bug of some kind.  So, we clamp the timeout parameter at
1497a6afd9f3SGreg Kroah-Hartman 	 * 2*info->timeout.
1498a6afd9f3SGreg Kroah-Hartman 	 */
1499a6afd9f3SGreg Kroah-Hartman 	if (!timeout || timeout > 2*info->timeout)
1500a6afd9f3SGreg Kroah-Hartman 		timeout = 2*info->timeout;
1501a6afd9f3SGreg Kroah-Hartman #ifdef SERIAL_DEBUG_RS_WAIT_UNTIL_SENT
1502a6afd9f3SGreg Kroah-Hartman 	printk("In rs_wait_until_sent(%d) check=%lu...", timeout, char_time);
1503a6afd9f3SGreg Kroah-Hartman 	printk("jiff=%lu...", jiffies);
1504a6afd9f3SGreg Kroah-Hartman #endif
1505a6afd9f3SGreg Kroah-Hartman 	while(!((lsr = custom.serdatr) & SDR_TSRE)) {
1506a6afd9f3SGreg Kroah-Hartman #ifdef SERIAL_DEBUG_RS_WAIT_UNTIL_SENT
1507a6afd9f3SGreg Kroah-Hartman 		printk("serdatr = %d (jiff=%lu)...", lsr, jiffies);
1508a6afd9f3SGreg Kroah-Hartman #endif
1509a6afd9f3SGreg Kroah-Hartman 		msleep_interruptible(jiffies_to_msecs(char_time));
1510a6afd9f3SGreg Kroah-Hartman 		if (signal_pending(current))
1511a6afd9f3SGreg Kroah-Hartman 			break;
1512a6afd9f3SGreg Kroah-Hartman 		if (timeout && time_after(jiffies, orig_jiffies + timeout))
1513a6afd9f3SGreg Kroah-Hartman 			break;
1514a6afd9f3SGreg Kroah-Hartman 	}
1515a6afd9f3SGreg Kroah-Hartman 	__set_current_state(TASK_RUNNING);
1516eff4b0b9SJiri Slaby 
1517a6afd9f3SGreg Kroah-Hartman #ifdef SERIAL_DEBUG_RS_WAIT_UNTIL_SENT
1518a6afd9f3SGreg Kroah-Hartman 	printk("lsr = %d (jiff=%lu)...done\n", lsr, jiffies);
1519a6afd9f3SGreg Kroah-Hartman #endif
1520a6afd9f3SGreg Kroah-Hartman }
1521a6afd9f3SGreg Kroah-Hartman 
1522a6afd9f3SGreg Kroah-Hartman /*
1523a6afd9f3SGreg Kroah-Hartman  * rs_hangup() --- called by tty_hangup() when a hangup is signaled.
1524a6afd9f3SGreg Kroah-Hartman  */
1525a6afd9f3SGreg Kroah-Hartman static void rs_hangup(struct tty_struct *tty)
1526a6afd9f3SGreg Kroah-Hartman {
1527916b7656SJiri Slaby 	struct serial_state *info = tty->driver_data;
1528a6afd9f3SGreg Kroah-Hartman 
1529a6afd9f3SGreg Kroah-Hartman 	if (serial_paranoia_check(info, tty->name, "rs_hangup"))
1530a6afd9f3SGreg Kroah-Hartman 		return;
1531a6afd9f3SGreg Kroah-Hartman 
1532a6afd9f3SGreg Kroah-Hartman 	rs_flush_buffer(tty);
1533588993ddSJiri Slaby 	shutdown(tty, info);
153412c80354SJiri Slaby 	info->tport.count = 0;
1535*01bd730dSJiri Slaby 	info->tport.flags &= ~ASYNC_NORMAL_ACTIVE;
153687758791SJiri Slaby 	info->tport.tty = NULL;
153787758791SJiri Slaby 	wake_up_interruptible(&info->tport.open_wait);
1538a6afd9f3SGreg Kroah-Hartman }
1539a6afd9f3SGreg Kroah-Hartman 
1540a6afd9f3SGreg Kroah-Hartman /*
1541a6afd9f3SGreg Kroah-Hartman  * ------------------------------------------------------------
1542a6afd9f3SGreg Kroah-Hartman  * rs_open() and friends
1543a6afd9f3SGreg Kroah-Hartman  * ------------------------------------------------------------
1544a6afd9f3SGreg Kroah-Hartman  */
1545a6afd9f3SGreg Kroah-Hartman static int block_til_ready(struct tty_struct *tty, struct file * filp,
1546916b7656SJiri Slaby 			   struct serial_state *info)
1547a6afd9f3SGreg Kroah-Hartman {
1548a6afd9f3SGreg Kroah-Hartman #ifdef DECLARE_WAITQUEUE
1549a6afd9f3SGreg Kroah-Hartman 	DECLARE_WAITQUEUE(wait, current);
1550a6afd9f3SGreg Kroah-Hartman #else
1551a6afd9f3SGreg Kroah-Hartman 	struct wait_queue wait = { current, NULL };
1552a6afd9f3SGreg Kroah-Hartman #endif
1553*01bd730dSJiri Slaby 	struct tty_port *port = &info->tport;
1554a6afd9f3SGreg Kroah-Hartman 	int		retval;
1555a6afd9f3SGreg Kroah-Hartman 	int		do_clocal = 0, extra_count = 0;
1556a6afd9f3SGreg Kroah-Hartman 	unsigned long	flags;
1557a6afd9f3SGreg Kroah-Hartman 
1558a6afd9f3SGreg Kroah-Hartman 	/*
1559a6afd9f3SGreg Kroah-Hartman 	 * If the device is in the middle of being closed, then block
1560a6afd9f3SGreg Kroah-Hartman 	 * until it's done, and then try again.
1561a6afd9f3SGreg Kroah-Hartman 	 */
1562a6afd9f3SGreg Kroah-Hartman 	if (tty_hung_up_p(filp) ||
1563*01bd730dSJiri Slaby 	    (port->flags & ASYNC_CLOSING)) {
1564*01bd730dSJiri Slaby 		if (port->flags & ASYNC_CLOSING)
1565*01bd730dSJiri Slaby 			interruptible_sleep_on(&port->close_wait);
1566a6afd9f3SGreg Kroah-Hartman #ifdef SERIAL_DO_RESTART
1567*01bd730dSJiri Slaby 		return ((port->flags & ASYNC_HUP_NOTIFY) ?
1568a6afd9f3SGreg Kroah-Hartman 			-EAGAIN : -ERESTARTSYS);
1569a6afd9f3SGreg Kroah-Hartman #else
1570a6afd9f3SGreg Kroah-Hartman 		return -EAGAIN;
1571a6afd9f3SGreg Kroah-Hartman #endif
1572a6afd9f3SGreg Kroah-Hartman 	}
1573a6afd9f3SGreg Kroah-Hartman 
1574a6afd9f3SGreg Kroah-Hartman 	/*
1575a6afd9f3SGreg Kroah-Hartman 	 * If non-blocking mode is set, or the port is not enabled,
1576a6afd9f3SGreg Kroah-Hartman 	 * then make the check up front and then exit.
1577a6afd9f3SGreg Kroah-Hartman 	 */
1578a6afd9f3SGreg Kroah-Hartman 	if ((filp->f_flags & O_NONBLOCK) ||
1579a6afd9f3SGreg Kroah-Hartman 	    (tty->flags & (1 << TTY_IO_ERROR))) {
1580*01bd730dSJiri Slaby 		port->flags |= ASYNC_NORMAL_ACTIVE;
1581a6afd9f3SGreg Kroah-Hartman 		return 0;
1582a6afd9f3SGreg Kroah-Hartman 	}
1583a6afd9f3SGreg Kroah-Hartman 
1584a6afd9f3SGreg Kroah-Hartman 	if (tty->termios->c_cflag & CLOCAL)
1585a6afd9f3SGreg Kroah-Hartman 		do_clocal = 1;
1586a6afd9f3SGreg Kroah-Hartman 
1587a6afd9f3SGreg Kroah-Hartman 	/*
1588a6afd9f3SGreg Kroah-Hartman 	 * Block waiting for the carrier detect and the line to become
1589a6afd9f3SGreg Kroah-Hartman 	 * free (i.e., not in use by the callout).  While we are in
1590*01bd730dSJiri Slaby 	 * this loop, port->count is dropped by one, so that
1591a6afd9f3SGreg Kroah-Hartman 	 * rs_close() knows when to free things.  We restore it upon
1592a6afd9f3SGreg Kroah-Hartman 	 * exit, either normal or abnormal.
1593a6afd9f3SGreg Kroah-Hartman 	 */
1594a6afd9f3SGreg Kroah-Hartman 	retval = 0;
1595*01bd730dSJiri Slaby 	add_wait_queue(&port->open_wait, &wait);
1596a6afd9f3SGreg Kroah-Hartman #ifdef SERIAL_DEBUG_OPEN
1597a6afd9f3SGreg Kroah-Hartman 	printk("block_til_ready before block: ttys%d, count = %d\n",
1598*01bd730dSJiri Slaby 	       info->line, port->count);
1599a6afd9f3SGreg Kroah-Hartman #endif
1600a6afd9f3SGreg Kroah-Hartman 	local_irq_save(flags);
1601a6afd9f3SGreg Kroah-Hartman 	if (!tty_hung_up_p(filp)) {
1602a6afd9f3SGreg Kroah-Hartman 		extra_count = 1;
1603*01bd730dSJiri Slaby 		port->count--;
1604a6afd9f3SGreg Kroah-Hartman 	}
1605a6afd9f3SGreg Kroah-Hartman 	local_irq_restore(flags);
1606*01bd730dSJiri Slaby 	port->blocked_open++;
1607a6afd9f3SGreg Kroah-Hartman 	while (1) {
1608a6afd9f3SGreg Kroah-Hartman 		local_irq_save(flags);
1609a6afd9f3SGreg Kroah-Hartman 		if (tty->termios->c_cflag & CBAUD)
1610a6afd9f3SGreg Kroah-Hartman 		        rtsdtr_ctrl(SER_DTR|SER_RTS);
1611a6afd9f3SGreg Kroah-Hartman 		local_irq_restore(flags);
1612a6afd9f3SGreg Kroah-Hartman 		set_current_state(TASK_INTERRUPTIBLE);
1613a6afd9f3SGreg Kroah-Hartman 		if (tty_hung_up_p(filp) ||
1614*01bd730dSJiri Slaby 		    !(port->flags & ASYNC_INITIALIZED)) {
1615a6afd9f3SGreg Kroah-Hartman #ifdef SERIAL_DO_RESTART
1616*01bd730dSJiri Slaby 			if (port->flags & ASYNC_HUP_NOTIFY)
1617a6afd9f3SGreg Kroah-Hartman 				retval = -EAGAIN;
1618a6afd9f3SGreg Kroah-Hartman 			else
1619a6afd9f3SGreg Kroah-Hartman 				retval = -ERESTARTSYS;
1620a6afd9f3SGreg Kroah-Hartman #else
1621a6afd9f3SGreg Kroah-Hartman 			retval = -EAGAIN;
1622a6afd9f3SGreg Kroah-Hartman #endif
1623a6afd9f3SGreg Kroah-Hartman 			break;
1624a6afd9f3SGreg Kroah-Hartman 		}
1625*01bd730dSJiri Slaby 		if (!(port->flags & ASYNC_CLOSING) &&
1626a6afd9f3SGreg Kroah-Hartman 		    (do_clocal || (!(ciab.pra & SER_DCD)) ))
1627a6afd9f3SGreg Kroah-Hartman 			break;
1628a6afd9f3SGreg Kroah-Hartman 		if (signal_pending(current)) {
1629a6afd9f3SGreg Kroah-Hartman 			retval = -ERESTARTSYS;
1630a6afd9f3SGreg Kroah-Hartman 			break;
1631a6afd9f3SGreg Kroah-Hartman 		}
1632a6afd9f3SGreg Kroah-Hartman #ifdef SERIAL_DEBUG_OPEN
1633a6afd9f3SGreg Kroah-Hartman 		printk("block_til_ready blocking: ttys%d, count = %d\n",
1634*01bd730dSJiri Slaby 		       info->line, port->count);
1635a6afd9f3SGreg Kroah-Hartman #endif
1636a6afd9f3SGreg Kroah-Hartman 		tty_unlock();
1637a6afd9f3SGreg Kroah-Hartman 		schedule();
1638a6afd9f3SGreg Kroah-Hartman 		tty_lock();
1639a6afd9f3SGreg Kroah-Hartman 	}
1640a6afd9f3SGreg Kroah-Hartman 	__set_current_state(TASK_RUNNING);
1641*01bd730dSJiri Slaby 	remove_wait_queue(&port->open_wait, &wait);
1642a6afd9f3SGreg Kroah-Hartman 	if (extra_count)
1643*01bd730dSJiri Slaby 		port->count++;
1644*01bd730dSJiri Slaby 	port->blocked_open--;
1645a6afd9f3SGreg Kroah-Hartman #ifdef SERIAL_DEBUG_OPEN
1646a6afd9f3SGreg Kroah-Hartman 	printk("block_til_ready after blocking: ttys%d, count = %d\n",
1647*01bd730dSJiri Slaby 	       info->line, port->count);
1648a6afd9f3SGreg Kroah-Hartman #endif
1649a6afd9f3SGreg Kroah-Hartman 	if (retval)
1650a6afd9f3SGreg Kroah-Hartman 		return retval;
1651*01bd730dSJiri Slaby 	port->flags |= ASYNC_NORMAL_ACTIVE;
1652a6afd9f3SGreg Kroah-Hartman 	return 0;
1653a6afd9f3SGreg Kroah-Hartman }
1654a6afd9f3SGreg Kroah-Hartman 
1655a6afd9f3SGreg Kroah-Hartman /*
1656a6afd9f3SGreg Kroah-Hartman  * This routine is called whenever a serial port is opened.  It
1657a6afd9f3SGreg Kroah-Hartman  * enables interrupts for a serial port, linking in its async structure into
1658a6afd9f3SGreg Kroah-Hartman  * the IRQ chain.   It also performs the serial-specific
1659a6afd9f3SGreg Kroah-Hartman  * initialization for the tty structure.
1660a6afd9f3SGreg Kroah-Hartman  */
1661a6afd9f3SGreg Kroah-Hartman static int rs_open(struct tty_struct *tty, struct file * filp)
1662a6afd9f3SGreg Kroah-Hartman {
1663916b7656SJiri Slaby 	struct serial_state *info = rs_table + tty->index;
1664410235fdSJiri Slaby 	int retval;
1665a6afd9f3SGreg Kroah-Hartman 
166612c80354SJiri Slaby 	info->tport.count++;
166787758791SJiri Slaby 	info->tport.tty = tty;
1668916b7656SJiri Slaby 	tty->driver_data = info;
166987758791SJiri Slaby 	tty->port = &info->tport;
1670a6afd9f3SGreg Kroah-Hartman 	if (serial_paranoia_check(info, tty->name, "rs_open"))
1671a6afd9f3SGreg Kroah-Hartman 		return -ENODEV;
1672a6afd9f3SGreg Kroah-Hartman 
1673a6afd9f3SGreg Kroah-Hartman #ifdef SERIAL_DEBUG_OPEN
1674916b7656SJiri Slaby 	printk("rs_open %s, count = %d\n", tty->name, info->count);
1675a6afd9f3SGreg Kroah-Hartman #endif
1676*01bd730dSJiri Slaby 	tty->low_latency = (info->tport.flags & ASYNC_LOW_LATENCY) ? 1 : 0;
1677a6afd9f3SGreg Kroah-Hartman 
1678a6afd9f3SGreg Kroah-Hartman 	/*
1679a6afd9f3SGreg Kroah-Hartman 	 * If the port is the middle of closing, bail out now
1680a6afd9f3SGreg Kroah-Hartman 	 */
1681a6afd9f3SGreg Kroah-Hartman 	if (tty_hung_up_p(filp) ||
1682*01bd730dSJiri Slaby 	    (info->tport.flags & ASYNC_CLOSING)) {
1683*01bd730dSJiri Slaby 		if (info->tport.flags & ASYNC_CLOSING)
168487758791SJiri Slaby 			interruptible_sleep_on(&info->tport.close_wait);
1685a6afd9f3SGreg Kroah-Hartman #ifdef SERIAL_DO_RESTART
1686*01bd730dSJiri Slaby 		return ((info->tport.flags & ASYNC_HUP_NOTIFY) ?
1687a6afd9f3SGreg Kroah-Hartman 			-EAGAIN : -ERESTARTSYS);
1688a6afd9f3SGreg Kroah-Hartman #else
1689a6afd9f3SGreg Kroah-Hartman 		return -EAGAIN;
1690a6afd9f3SGreg Kroah-Hartman #endif
1691a6afd9f3SGreg Kroah-Hartman 	}
1692a6afd9f3SGreg Kroah-Hartman 
1693a6afd9f3SGreg Kroah-Hartman 	/*
1694a6afd9f3SGreg Kroah-Hartman 	 * Start up serial port
1695a6afd9f3SGreg Kroah-Hartman 	 */
1696588993ddSJiri Slaby 	retval = startup(tty, info);
1697a6afd9f3SGreg Kroah-Hartman 	if (retval) {
1698a6afd9f3SGreg Kroah-Hartman 		return retval;
1699a6afd9f3SGreg Kroah-Hartman 	}
1700a6afd9f3SGreg Kroah-Hartman 
1701a6afd9f3SGreg Kroah-Hartman 	retval = block_til_ready(tty, filp, info);
1702a6afd9f3SGreg Kroah-Hartman 	if (retval) {
1703a6afd9f3SGreg Kroah-Hartman #ifdef SERIAL_DEBUG_OPEN
1704a6afd9f3SGreg Kroah-Hartman 		printk("rs_open returning after block_til_ready with %d\n",
1705a6afd9f3SGreg Kroah-Hartman 		       retval);
1706a6afd9f3SGreg Kroah-Hartman #endif
1707a6afd9f3SGreg Kroah-Hartman 		return retval;
1708a6afd9f3SGreg Kroah-Hartman 	}
1709a6afd9f3SGreg Kroah-Hartman 
1710a6afd9f3SGreg Kroah-Hartman #ifdef SERIAL_DEBUG_OPEN
1711a6afd9f3SGreg Kroah-Hartman 	printk("rs_open %s successful...", tty->name);
1712a6afd9f3SGreg Kroah-Hartman #endif
1713a6afd9f3SGreg Kroah-Hartman 	return 0;
1714a6afd9f3SGreg Kroah-Hartman }
1715a6afd9f3SGreg Kroah-Hartman 
1716a6afd9f3SGreg Kroah-Hartman /*
1717a6afd9f3SGreg Kroah-Hartman  * /proc fs routines....
1718a6afd9f3SGreg Kroah-Hartman  */
1719a6afd9f3SGreg Kroah-Hartman 
1720a6afd9f3SGreg Kroah-Hartman static inline void line_info(struct seq_file *m, struct serial_state *state)
1721a6afd9f3SGreg Kroah-Hartman {
1722a6afd9f3SGreg Kroah-Hartman 	char	stat_buf[30], control, status;
1723a6afd9f3SGreg Kroah-Hartman 	unsigned long flags;
1724a6afd9f3SGreg Kroah-Hartman 
1725a6afd9f3SGreg Kroah-Hartman 	seq_printf(m, "%d: uart:amiga_builtin",state->line);
1726a6afd9f3SGreg Kroah-Hartman 
1727a6afd9f3SGreg Kroah-Hartman 	local_irq_save(flags);
1728a6afd9f3SGreg Kroah-Hartman 	status = ciab.pra;
1729*01bd730dSJiri Slaby 	control = (state->tport.flags & ASYNC_INITIALIZED) ? state->MCR : status;
1730a6afd9f3SGreg Kroah-Hartman 	local_irq_restore(flags);
1731a6afd9f3SGreg Kroah-Hartman 
1732a6afd9f3SGreg Kroah-Hartman 	stat_buf[0] = 0;
1733a6afd9f3SGreg Kroah-Hartman 	stat_buf[1] = 0;
1734a6afd9f3SGreg Kroah-Hartman 	if(!(control & SER_RTS))
1735a6afd9f3SGreg Kroah-Hartman 		strcat(stat_buf, "|RTS");
1736a6afd9f3SGreg Kroah-Hartman 	if(!(status & SER_CTS))
1737a6afd9f3SGreg Kroah-Hartman 		strcat(stat_buf, "|CTS");
1738a6afd9f3SGreg Kroah-Hartman 	if(!(control & SER_DTR))
1739a6afd9f3SGreg Kroah-Hartman 		strcat(stat_buf, "|DTR");
1740a6afd9f3SGreg Kroah-Hartman 	if(!(status & SER_DSR))
1741a6afd9f3SGreg Kroah-Hartman 		strcat(stat_buf, "|DSR");
1742a6afd9f3SGreg Kroah-Hartman 	if(!(status & SER_DCD))
1743a6afd9f3SGreg Kroah-Hartman 		strcat(stat_buf, "|CD");
1744a6afd9f3SGreg Kroah-Hartman 
1745916b7656SJiri Slaby 	if (state->quot)
1746916b7656SJiri Slaby 		seq_printf(m, " baud:%d", state->baud_base / state->quot);
1747a6afd9f3SGreg Kroah-Hartman 
1748a6afd9f3SGreg Kroah-Hartman 	seq_printf(m, " tx:%d rx:%d", state->icount.tx, state->icount.rx);
1749a6afd9f3SGreg Kroah-Hartman 
1750a6afd9f3SGreg Kroah-Hartman 	if (state->icount.frame)
1751a6afd9f3SGreg Kroah-Hartman 		seq_printf(m, " fe:%d", state->icount.frame);
1752a6afd9f3SGreg Kroah-Hartman 
1753a6afd9f3SGreg Kroah-Hartman 	if (state->icount.parity)
1754a6afd9f3SGreg Kroah-Hartman 		seq_printf(m, " pe:%d", state->icount.parity);
1755a6afd9f3SGreg Kroah-Hartman 
1756a6afd9f3SGreg Kroah-Hartman 	if (state->icount.brk)
1757a6afd9f3SGreg Kroah-Hartman 		seq_printf(m, " brk:%d", state->icount.brk);
1758a6afd9f3SGreg Kroah-Hartman 
1759a6afd9f3SGreg Kroah-Hartman 	if (state->icount.overrun)
1760a6afd9f3SGreg Kroah-Hartman 		seq_printf(m, " oe:%d", state->icount.overrun);
1761a6afd9f3SGreg Kroah-Hartman 
1762a6afd9f3SGreg Kroah-Hartman 	/*
1763a6afd9f3SGreg Kroah-Hartman 	 * Last thing is the RS-232 status lines
1764a6afd9f3SGreg Kroah-Hartman 	 */
1765a6afd9f3SGreg Kroah-Hartman 	seq_printf(m, " %s\n", stat_buf+1);
1766a6afd9f3SGreg Kroah-Hartman }
1767a6afd9f3SGreg Kroah-Hartman 
1768a6afd9f3SGreg Kroah-Hartman static int rs_proc_show(struct seq_file *m, void *v)
1769a6afd9f3SGreg Kroah-Hartman {
1770a6afd9f3SGreg Kroah-Hartman 	seq_printf(m, "serinfo:1.0 driver:%s\n", serial_version);
1771a6afd9f3SGreg Kroah-Hartman 	line_info(m, &rs_table[0]);
1772a6afd9f3SGreg Kroah-Hartman 	return 0;
1773a6afd9f3SGreg Kroah-Hartman }
1774a6afd9f3SGreg Kroah-Hartman 
1775a6afd9f3SGreg Kroah-Hartman static int rs_proc_open(struct inode *inode, struct file *file)
1776a6afd9f3SGreg Kroah-Hartman {
1777a6afd9f3SGreg Kroah-Hartman 	return single_open(file, rs_proc_show, NULL);
1778a6afd9f3SGreg Kroah-Hartman }
1779a6afd9f3SGreg Kroah-Hartman 
1780a6afd9f3SGreg Kroah-Hartman static const struct file_operations rs_proc_fops = {
1781a6afd9f3SGreg Kroah-Hartman 	.owner		= THIS_MODULE,
1782a6afd9f3SGreg Kroah-Hartman 	.open		= rs_proc_open,
1783a6afd9f3SGreg Kroah-Hartman 	.read		= seq_read,
1784a6afd9f3SGreg Kroah-Hartman 	.llseek		= seq_lseek,
1785a6afd9f3SGreg Kroah-Hartman 	.release	= single_release,
1786a6afd9f3SGreg Kroah-Hartman };
1787a6afd9f3SGreg Kroah-Hartman 
1788a6afd9f3SGreg Kroah-Hartman /*
1789a6afd9f3SGreg Kroah-Hartman  * ---------------------------------------------------------------------
1790a6afd9f3SGreg Kroah-Hartman  * rs_init() and friends
1791a6afd9f3SGreg Kroah-Hartman  *
1792a6afd9f3SGreg Kroah-Hartman  * rs_init() is called at boot-time to initialize the serial driver.
1793a6afd9f3SGreg Kroah-Hartman  * ---------------------------------------------------------------------
1794a6afd9f3SGreg Kroah-Hartman  */
1795a6afd9f3SGreg Kroah-Hartman 
1796a6afd9f3SGreg Kroah-Hartman /*
1797a6afd9f3SGreg Kroah-Hartman  * This routine prints out the appropriate serial driver version
1798a6afd9f3SGreg Kroah-Hartman  * number, and identifies which options were configured into this
1799a6afd9f3SGreg Kroah-Hartman  * driver.
1800a6afd9f3SGreg Kroah-Hartman  */
1801a6afd9f3SGreg Kroah-Hartman static void show_serial_version(void)
1802a6afd9f3SGreg Kroah-Hartman {
1803a6afd9f3SGreg Kroah-Hartman  	printk(KERN_INFO "%s version %s\n", serial_name, serial_version);
1804a6afd9f3SGreg Kroah-Hartman }
1805a6afd9f3SGreg Kroah-Hartman 
1806a6afd9f3SGreg Kroah-Hartman 
1807a6afd9f3SGreg Kroah-Hartman static const struct tty_operations serial_ops = {
1808a6afd9f3SGreg Kroah-Hartman 	.open = rs_open,
1809a6afd9f3SGreg Kroah-Hartman 	.close = rs_close,
1810a6afd9f3SGreg Kroah-Hartman 	.write = rs_write,
1811a6afd9f3SGreg Kroah-Hartman 	.put_char = rs_put_char,
1812a6afd9f3SGreg Kroah-Hartman 	.flush_chars = rs_flush_chars,
1813a6afd9f3SGreg Kroah-Hartman 	.write_room = rs_write_room,
1814a6afd9f3SGreg Kroah-Hartman 	.chars_in_buffer = rs_chars_in_buffer,
1815a6afd9f3SGreg Kroah-Hartman 	.flush_buffer = rs_flush_buffer,
1816a6afd9f3SGreg Kroah-Hartman 	.ioctl = rs_ioctl,
1817a6afd9f3SGreg Kroah-Hartman 	.throttle = rs_throttle,
1818a6afd9f3SGreg Kroah-Hartman 	.unthrottle = rs_unthrottle,
1819a6afd9f3SGreg Kroah-Hartman 	.set_termios = rs_set_termios,
1820a6afd9f3SGreg Kroah-Hartman 	.stop = rs_stop,
1821a6afd9f3SGreg Kroah-Hartman 	.start = rs_start,
1822a6afd9f3SGreg Kroah-Hartman 	.hangup = rs_hangup,
1823a6afd9f3SGreg Kroah-Hartman 	.break_ctl = rs_break,
1824a6afd9f3SGreg Kroah-Hartman 	.send_xchar = rs_send_xchar,
1825a6afd9f3SGreg Kroah-Hartman 	.wait_until_sent = rs_wait_until_sent,
1826a6afd9f3SGreg Kroah-Hartman 	.tiocmget = rs_tiocmget,
1827a6afd9f3SGreg Kroah-Hartman 	.tiocmset = rs_tiocmset,
1828a6afd9f3SGreg Kroah-Hartman 	.get_icount = rs_get_icount,
1829a6afd9f3SGreg Kroah-Hartman 	.proc_fops = &rs_proc_fops,
1830a6afd9f3SGreg Kroah-Hartman };
1831a6afd9f3SGreg Kroah-Hartman 
1832a6afd9f3SGreg Kroah-Hartman /*
1833a6afd9f3SGreg Kroah-Hartman  * The serial driver boot-time initialization code!
1834a6afd9f3SGreg Kroah-Hartman  */
1835a6afd9f3SGreg Kroah-Hartman static int __init amiga_serial_probe(struct platform_device *pdev)
1836a6afd9f3SGreg Kroah-Hartman {
1837a6afd9f3SGreg Kroah-Hartman 	unsigned long flags;
1838a6afd9f3SGreg Kroah-Hartman 	struct serial_state * state;
1839a6afd9f3SGreg Kroah-Hartman 	int error;
1840a6afd9f3SGreg Kroah-Hartman 
1841410235fdSJiri Slaby 	serial_driver = alloc_tty_driver(NR_PORTS);
1842a6afd9f3SGreg Kroah-Hartman 	if (!serial_driver)
1843a6afd9f3SGreg Kroah-Hartman 		return -ENOMEM;
1844a6afd9f3SGreg Kroah-Hartman 
1845a6afd9f3SGreg Kroah-Hartman 	show_serial_version();
1846a6afd9f3SGreg Kroah-Hartman 
1847a6afd9f3SGreg Kroah-Hartman 	/* Initialize the tty_driver structure */
1848a6afd9f3SGreg Kroah-Hartman 
1849a6afd9f3SGreg Kroah-Hartman 	serial_driver->driver_name = "amiserial";
1850a6afd9f3SGreg Kroah-Hartman 	serial_driver->name = "ttyS";
1851a6afd9f3SGreg Kroah-Hartman 	serial_driver->major = TTY_MAJOR;
1852a6afd9f3SGreg Kroah-Hartman 	serial_driver->minor_start = 64;
1853a6afd9f3SGreg Kroah-Hartman 	serial_driver->type = TTY_DRIVER_TYPE_SERIAL;
1854a6afd9f3SGreg Kroah-Hartman 	serial_driver->subtype = SERIAL_TYPE_NORMAL;
1855a6afd9f3SGreg Kroah-Hartman 	serial_driver->init_termios = tty_std_termios;
1856a6afd9f3SGreg Kroah-Hartman 	serial_driver->init_termios.c_cflag =
1857a6afd9f3SGreg Kroah-Hartman 		B9600 | CS8 | CREAD | HUPCL | CLOCAL;
1858a6afd9f3SGreg Kroah-Hartman 	serial_driver->flags = TTY_DRIVER_REAL_RAW;
1859a6afd9f3SGreg Kroah-Hartman 	tty_set_operations(serial_driver, &serial_ops);
1860a6afd9f3SGreg Kroah-Hartman 
1861a6afd9f3SGreg Kroah-Hartman 	error = tty_register_driver(serial_driver);
1862a6afd9f3SGreg Kroah-Hartman 	if (error)
1863a6afd9f3SGreg Kroah-Hartman 		goto fail_put_tty_driver;
1864a6afd9f3SGreg Kroah-Hartman 
1865a6afd9f3SGreg Kroah-Hartman 	state = rs_table;
1866a6afd9f3SGreg Kroah-Hartman 	state->port = (int)&custom.serdatr; /* Just to give it a value */
1867a6afd9f3SGreg Kroah-Hartman 	state->line = 0;
1868a6afd9f3SGreg Kroah-Hartman 	state->custom_divisor = 0;
1869a6afd9f3SGreg Kroah-Hartman 	state->icount.cts = state->icount.dsr =
1870a6afd9f3SGreg Kroah-Hartman 	  state->icount.rng = state->icount.dcd = 0;
1871a6afd9f3SGreg Kroah-Hartman 	state->icount.rx = state->icount.tx = 0;
1872a6afd9f3SGreg Kroah-Hartman 	state->icount.frame = state->icount.parity = 0;
1873a6afd9f3SGreg Kroah-Hartman 	state->icount.overrun = state->icount.brk = 0;
187487758791SJiri Slaby 	tty_port_init(&state->tport);
1875a6afd9f3SGreg Kroah-Hartman 
1876a6afd9f3SGreg Kroah-Hartman 	printk(KERN_INFO "ttyS%d is the amiga builtin serial port\n",
1877a6afd9f3SGreg Kroah-Hartman 		       state->line);
1878a6afd9f3SGreg Kroah-Hartman 
1879a6afd9f3SGreg Kroah-Hartman 	/* Hardware set up */
1880a6afd9f3SGreg Kroah-Hartman 
1881a6afd9f3SGreg Kroah-Hartman 	state->baud_base = amiga_colorclock;
1882a6afd9f3SGreg Kroah-Hartman 	state->xmit_fifo_size = 1;
1883a6afd9f3SGreg Kroah-Hartman 
1884a6afd9f3SGreg Kroah-Hartman 	/* set ISRs, and then disable the rx interrupts */
1885a6afd9f3SGreg Kroah-Hartman 	error = request_irq(IRQ_AMIGA_TBE, ser_tx_int, 0, "serial TX", state);
1886a6afd9f3SGreg Kroah-Hartman 	if (error)
1887a6afd9f3SGreg Kroah-Hartman 		goto fail_unregister;
1888a6afd9f3SGreg Kroah-Hartman 
18899cfb5c05SYong Zhang 	error = request_irq(IRQ_AMIGA_RBF, ser_rx_int, 0,
1890a6afd9f3SGreg Kroah-Hartman 			    "serial RX", state);
1891a6afd9f3SGreg Kroah-Hartman 	if (error)
1892a6afd9f3SGreg Kroah-Hartman 		goto fail_free_irq;
1893a6afd9f3SGreg Kroah-Hartman 
1894a6afd9f3SGreg Kroah-Hartman 	local_irq_save(flags);
1895a6afd9f3SGreg Kroah-Hartman 
1896a6afd9f3SGreg Kroah-Hartman 	/* turn off Rx and Tx interrupts */
1897a6afd9f3SGreg Kroah-Hartman 	custom.intena = IF_RBF | IF_TBE;
1898a6afd9f3SGreg Kroah-Hartman 	mb();
1899a6afd9f3SGreg Kroah-Hartman 
1900a6afd9f3SGreg Kroah-Hartman 	/* clear any pending interrupt */
1901a6afd9f3SGreg Kroah-Hartman 	custom.intreq = IF_RBF | IF_TBE;
1902a6afd9f3SGreg Kroah-Hartman 	mb();
1903a6afd9f3SGreg Kroah-Hartman 
1904a6afd9f3SGreg Kroah-Hartman 	local_irq_restore(flags);
1905a6afd9f3SGreg Kroah-Hartman 
1906a6afd9f3SGreg Kroah-Hartman 	/*
1907a6afd9f3SGreg Kroah-Hartman 	 * set the appropriate directions for the modem control flags,
1908a6afd9f3SGreg Kroah-Hartman 	 * and clear RTS and DTR
1909a6afd9f3SGreg Kroah-Hartman 	 */
1910a6afd9f3SGreg Kroah-Hartman 	ciab.ddra |= (SER_DTR | SER_RTS);   /* outputs */
1911a6afd9f3SGreg Kroah-Hartman 	ciab.ddra &= ~(SER_DCD | SER_CTS | SER_DSR);  /* inputs */
1912a6afd9f3SGreg Kroah-Hartman 
1913a6afd9f3SGreg Kroah-Hartman 	platform_set_drvdata(pdev, state);
1914a6afd9f3SGreg Kroah-Hartman 
1915a6afd9f3SGreg Kroah-Hartman 	return 0;
1916a6afd9f3SGreg Kroah-Hartman 
1917a6afd9f3SGreg Kroah-Hartman fail_free_irq:
1918a6afd9f3SGreg Kroah-Hartman 	free_irq(IRQ_AMIGA_TBE, state);
1919a6afd9f3SGreg Kroah-Hartman fail_unregister:
1920a6afd9f3SGreg Kroah-Hartman 	tty_unregister_driver(serial_driver);
1921a6afd9f3SGreg Kroah-Hartman fail_put_tty_driver:
1922a6afd9f3SGreg Kroah-Hartman 	put_tty_driver(serial_driver);
1923a6afd9f3SGreg Kroah-Hartman 	return error;
1924a6afd9f3SGreg Kroah-Hartman }
1925a6afd9f3SGreg Kroah-Hartman 
1926a6afd9f3SGreg Kroah-Hartman static int __exit amiga_serial_remove(struct platform_device *pdev)
1927a6afd9f3SGreg Kroah-Hartman {
1928a6afd9f3SGreg Kroah-Hartman 	int error;
1929a6afd9f3SGreg Kroah-Hartman 	struct serial_state *state = platform_get_drvdata(pdev);
1930a6afd9f3SGreg Kroah-Hartman 
1931a6afd9f3SGreg Kroah-Hartman 	/* printk("Unloading %s: version %s\n", serial_name, serial_version); */
1932a6afd9f3SGreg Kroah-Hartman 	if ((error = tty_unregister_driver(serial_driver)))
1933a6afd9f3SGreg Kroah-Hartman 		printk("SERIAL: failed to unregister serial driver (%d)\n",
1934a6afd9f3SGreg Kroah-Hartman 		       error);
1935a6afd9f3SGreg Kroah-Hartman 	put_tty_driver(serial_driver);
1936a6afd9f3SGreg Kroah-Hartman 
1937916b7656SJiri Slaby 	free_irq(IRQ_AMIGA_TBE, state);
1938916b7656SJiri Slaby 	free_irq(IRQ_AMIGA_RBF, state);
1939a6afd9f3SGreg Kroah-Hartman 
1940a6afd9f3SGreg Kroah-Hartman 	platform_set_drvdata(pdev, NULL);
1941a6afd9f3SGreg Kroah-Hartman 
1942a6afd9f3SGreg Kroah-Hartman 	return error;
1943a6afd9f3SGreg Kroah-Hartman }
1944a6afd9f3SGreg Kroah-Hartman 
1945a6afd9f3SGreg Kroah-Hartman static struct platform_driver amiga_serial_driver = {
1946a6afd9f3SGreg Kroah-Hartman 	.remove = __exit_p(amiga_serial_remove),
1947a6afd9f3SGreg Kroah-Hartman 	.driver   = {
1948a6afd9f3SGreg Kroah-Hartman 		.name	= "amiga-serial",
1949a6afd9f3SGreg Kroah-Hartman 		.owner	= THIS_MODULE,
1950a6afd9f3SGreg Kroah-Hartman 	},
1951a6afd9f3SGreg Kroah-Hartman };
1952a6afd9f3SGreg Kroah-Hartman 
1953a6afd9f3SGreg Kroah-Hartman static int __init amiga_serial_init(void)
1954a6afd9f3SGreg Kroah-Hartman {
1955a6afd9f3SGreg Kroah-Hartman 	return platform_driver_probe(&amiga_serial_driver, amiga_serial_probe);
1956a6afd9f3SGreg Kroah-Hartman }
1957a6afd9f3SGreg Kroah-Hartman 
1958a6afd9f3SGreg Kroah-Hartman module_init(amiga_serial_init);
1959a6afd9f3SGreg Kroah-Hartman 
1960a6afd9f3SGreg Kroah-Hartman static void __exit amiga_serial_exit(void)
1961a6afd9f3SGreg Kroah-Hartman {
1962a6afd9f3SGreg Kroah-Hartman 	platform_driver_unregister(&amiga_serial_driver);
1963a6afd9f3SGreg Kroah-Hartman }
1964a6afd9f3SGreg Kroah-Hartman 
1965a6afd9f3SGreg Kroah-Hartman module_exit(amiga_serial_exit);
1966a6afd9f3SGreg Kroah-Hartman 
1967a6afd9f3SGreg Kroah-Hartman 
1968a6afd9f3SGreg Kroah-Hartman #if defined(CONFIG_SERIAL_CONSOLE) && !defined(MODULE)
1969a6afd9f3SGreg Kroah-Hartman 
1970a6afd9f3SGreg Kroah-Hartman /*
1971a6afd9f3SGreg Kroah-Hartman  * ------------------------------------------------------------
1972a6afd9f3SGreg Kroah-Hartman  * Serial console driver
1973a6afd9f3SGreg Kroah-Hartman  * ------------------------------------------------------------
1974a6afd9f3SGreg Kroah-Hartman  */
1975a6afd9f3SGreg Kroah-Hartman 
1976a6afd9f3SGreg Kroah-Hartman static void amiga_serial_putc(char c)
1977a6afd9f3SGreg Kroah-Hartman {
1978a6afd9f3SGreg Kroah-Hartman 	custom.serdat = (unsigned char)c | 0x100;
1979a6afd9f3SGreg Kroah-Hartman 	while (!(custom.serdatr & 0x2000))
1980a6afd9f3SGreg Kroah-Hartman 		barrier();
1981a6afd9f3SGreg Kroah-Hartman }
1982a6afd9f3SGreg Kroah-Hartman 
1983a6afd9f3SGreg Kroah-Hartman /*
1984a6afd9f3SGreg Kroah-Hartman  *	Print a string to the serial port trying not to disturb
1985a6afd9f3SGreg Kroah-Hartman  *	any possible real use of the port...
1986a6afd9f3SGreg Kroah-Hartman  *
1987a6afd9f3SGreg Kroah-Hartman  *	The console must be locked when we get here.
1988a6afd9f3SGreg Kroah-Hartman  */
1989a6afd9f3SGreg Kroah-Hartman static void serial_console_write(struct console *co, const char *s,
1990a6afd9f3SGreg Kroah-Hartman 				unsigned count)
1991a6afd9f3SGreg Kroah-Hartman {
1992a6afd9f3SGreg Kroah-Hartman 	unsigned short intena = custom.intenar;
1993a6afd9f3SGreg Kroah-Hartman 
1994a6afd9f3SGreg Kroah-Hartman 	custom.intena = IF_TBE;
1995a6afd9f3SGreg Kroah-Hartman 
1996a6afd9f3SGreg Kroah-Hartman 	while (count--) {
1997a6afd9f3SGreg Kroah-Hartman 		if (*s == '\n')
1998a6afd9f3SGreg Kroah-Hartman 			amiga_serial_putc('\r');
1999a6afd9f3SGreg Kroah-Hartman 		amiga_serial_putc(*s++);
2000a6afd9f3SGreg Kroah-Hartman 	}
2001a6afd9f3SGreg Kroah-Hartman 
2002a6afd9f3SGreg Kroah-Hartman 	custom.intena = IF_SETCLR | (intena & IF_TBE);
2003a6afd9f3SGreg Kroah-Hartman }
2004a6afd9f3SGreg Kroah-Hartman 
2005a6afd9f3SGreg Kroah-Hartman static struct tty_driver *serial_console_device(struct console *c, int *index)
2006a6afd9f3SGreg Kroah-Hartman {
2007a6afd9f3SGreg Kroah-Hartman 	*index = 0;
2008a6afd9f3SGreg Kroah-Hartman 	return serial_driver;
2009a6afd9f3SGreg Kroah-Hartman }
2010a6afd9f3SGreg Kroah-Hartman 
2011a6afd9f3SGreg Kroah-Hartman static struct console sercons = {
2012a6afd9f3SGreg Kroah-Hartman 	.name =		"ttyS",
2013a6afd9f3SGreg Kroah-Hartman 	.write =	serial_console_write,
2014a6afd9f3SGreg Kroah-Hartman 	.device =	serial_console_device,
2015a6afd9f3SGreg Kroah-Hartman 	.flags =	CON_PRINTBUFFER,
2016a6afd9f3SGreg Kroah-Hartman 	.index =	-1,
2017a6afd9f3SGreg Kroah-Hartman };
2018a6afd9f3SGreg Kroah-Hartman 
2019a6afd9f3SGreg Kroah-Hartman /*
2020a6afd9f3SGreg Kroah-Hartman  *	Register console.
2021a6afd9f3SGreg Kroah-Hartman  */
2022a6afd9f3SGreg Kroah-Hartman static int __init amiserial_console_init(void)
2023a6afd9f3SGreg Kroah-Hartman {
2024a6afd9f3SGreg Kroah-Hartman 	register_console(&sercons);
2025a6afd9f3SGreg Kroah-Hartman 	return 0;
2026a6afd9f3SGreg Kroah-Hartman }
2027a6afd9f3SGreg Kroah-Hartman console_initcall(amiserial_console_init);
2028a6afd9f3SGreg Kroah-Hartman 
2029a6afd9f3SGreg Kroah-Hartman #endif /* CONFIG_SERIAL_CONSOLE && !MODULE */
2030a6afd9f3SGreg Kroah-Hartman 
2031a6afd9f3SGreg Kroah-Hartman MODULE_LICENSE("GPL");
2032a6afd9f3SGreg Kroah-Hartman MODULE_ALIAS("platform:amiga-serial");
2033