xref: /openbmc/linux/drivers/tty/serial/ip22zilog.c (revision c900529f3d9161bfde5cca0754f83b4d3c3e0220)
1e3b3d0f5SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
2ab4382d2SGreg Kroah-Hartman /*
3ab4382d2SGreg Kroah-Hartman  * Driver for Zilog serial chips found on SGI workstations and
4ab4382d2SGreg Kroah-Hartman  * servers.  This driver could actually be made more generic.
5ab4382d2SGreg Kroah-Hartman  *
6ab4382d2SGreg Kroah-Hartman  * This is based on the drivers/serial/sunzilog.c code as of 2.6.0-test7 and the
7ab4382d2SGreg Kroah-Hartman  * old drivers/sgi/char/sgiserial.c code which itself is based of the original
8ab4382d2SGreg Kroah-Hartman  * drivers/sbus/char/zs.c code.  A lot of code has been simply moved over
9ab4382d2SGreg Kroah-Hartman  * directly from there but much has been rewritten.  Credits therefore go out
10ab4382d2SGreg Kroah-Hartman  * to David S. Miller, Eddie C. Dost, Pete Zaitcev, Ted Ts'o and Alex Buell
11ab4382d2SGreg Kroah-Hartman  * for their work there.
12ab4382d2SGreg Kroah-Hartman  *
13ab4382d2SGreg Kroah-Hartman  *  Copyright (C) 2002 Ralf Baechle (ralf@linux-mips.org)
14ab4382d2SGreg Kroah-Hartman  *  Copyright (C) 2002 David S. Miller (davem@redhat.com)
15ab4382d2SGreg Kroah-Hartman  */
16ab4382d2SGreg Kroah-Hartman #include <linux/module.h>
17ab4382d2SGreg Kroah-Hartman #include <linux/kernel.h>
18ab4382d2SGreg Kroah-Hartman #include <linux/errno.h>
19ab4382d2SGreg Kroah-Hartman #include <linux/delay.h>
20ab4382d2SGreg Kroah-Hartman #include <linux/tty.h>
21ab4382d2SGreg Kroah-Hartman #include <linux/tty_flip.h>
22ab4382d2SGreg Kroah-Hartman #include <linux/major.h>
23ab4382d2SGreg Kroah-Hartman #include <linux/string.h>
24ab4382d2SGreg Kroah-Hartman #include <linux/ptrace.h>
25ab4382d2SGreg Kroah-Hartman #include <linux/ioport.h>
26ab4382d2SGreg Kroah-Hartman #include <linux/slab.h>
27ab4382d2SGreg Kroah-Hartman #include <linux/circ_buf.h>
28ab4382d2SGreg Kroah-Hartman #include <linux/serial.h>
29ab4382d2SGreg Kroah-Hartman #include <linux/sysrq.h>
30ab4382d2SGreg Kroah-Hartman #include <linux/console.h>
31ab4382d2SGreg Kroah-Hartman #include <linux/spinlock.h>
32ab4382d2SGreg Kroah-Hartman #include <linux/init.h>
33ab4382d2SGreg Kroah-Hartman 
343dccc357SZihao Tang #include <linux/io.h>
35ab4382d2SGreg Kroah-Hartman #include <asm/irq.h>
36ab4382d2SGreg Kroah-Hartman #include <asm/sgialib.h>
37ab4382d2SGreg Kroah-Hartman #include <asm/sgi/ioc.h>
38ab4382d2SGreg Kroah-Hartman #include <asm/sgi/hpc3.h>
39ab4382d2SGreg Kroah-Hartman #include <asm/sgi/ip22.h>
40ab4382d2SGreg Kroah-Hartman 
41ab4382d2SGreg Kroah-Hartman #include <linux/serial_core.h>
42ab4382d2SGreg Kroah-Hartman 
43ab4382d2SGreg Kroah-Hartman #include "ip22zilog.h"
44ab4382d2SGreg Kroah-Hartman 
45ab4382d2SGreg Kroah-Hartman /*
46ab4382d2SGreg Kroah-Hartman  * On IP22 we need to delay after register accesses but we do not need to
47ab4382d2SGreg Kroah-Hartman  * flush writes.
48ab4382d2SGreg Kroah-Hartman  */
49ab4382d2SGreg Kroah-Hartman #define ZSDELAY()		udelay(5)
50ab4382d2SGreg Kroah-Hartman #define ZSDELAY_LONG()		udelay(20)
51ab4382d2SGreg Kroah-Hartman #define ZS_WSYNC(channel)	do { } while (0)
52ab4382d2SGreg Kroah-Hartman 
53ab4382d2SGreg Kroah-Hartman #define NUM_IP22ZILOG		1
54ab4382d2SGreg Kroah-Hartman #define NUM_CHANNELS		(NUM_IP22ZILOG * 2)
55ab4382d2SGreg Kroah-Hartman 
56ab4382d2SGreg Kroah-Hartman #define ZS_CLOCK		3672000	/* Zilog input clock rate. */
57ab4382d2SGreg Kroah-Hartman #define ZS_CLOCK_DIVISOR	16      /* Divisor this driver uses. */
58ab4382d2SGreg Kroah-Hartman 
59ab4382d2SGreg Kroah-Hartman /*
60ab4382d2SGreg Kroah-Hartman  * We wrap our port structure around the generic uart_port.
61ab4382d2SGreg Kroah-Hartman  */
62ab4382d2SGreg Kroah-Hartman struct uart_ip22zilog_port {
63ab4382d2SGreg Kroah-Hartman 	struct uart_port		port;
64ab4382d2SGreg Kroah-Hartman 
65ab4382d2SGreg Kroah-Hartman 	/* IRQ servicing chain.  */
66ab4382d2SGreg Kroah-Hartman 	struct uart_ip22zilog_port	*next;
67ab4382d2SGreg Kroah-Hartman 
68ab4382d2SGreg Kroah-Hartman 	/* Current values of Zilog write registers.  */
69ab4382d2SGreg Kroah-Hartman 	unsigned char			curregs[NUM_ZSREGS];
70ab4382d2SGreg Kroah-Hartman 
71ab4382d2SGreg Kroah-Hartman 	unsigned int			flags;
72ab4382d2SGreg Kroah-Hartman #define IP22ZILOG_FLAG_IS_CONS		0x00000004
73ab4382d2SGreg Kroah-Hartman #define IP22ZILOG_FLAG_IS_KGDB		0x00000008
74ab4382d2SGreg Kroah-Hartman #define IP22ZILOG_FLAG_MODEM_STATUS	0x00000010
75ab4382d2SGreg Kroah-Hartman #define IP22ZILOG_FLAG_IS_CHANNEL_A	0x00000020
76ab4382d2SGreg Kroah-Hartman #define IP22ZILOG_FLAG_REGS_HELD	0x00000040
77ab4382d2SGreg Kroah-Hartman #define IP22ZILOG_FLAG_TX_STOPPED	0x00000080
78ab4382d2SGreg Kroah-Hartman #define IP22ZILOG_FLAG_TX_ACTIVE	0x00000100
79ab4382d2SGreg Kroah-Hartman #define IP22ZILOG_FLAG_RESET_DONE	0x00000200
80ab4382d2SGreg Kroah-Hartman 
81ab4382d2SGreg Kroah-Hartman 	unsigned int			tty_break;
82ab4382d2SGreg Kroah-Hartman 
83ab4382d2SGreg Kroah-Hartman 	unsigned char			parity_mask;
84ab4382d2SGreg Kroah-Hartman 	unsigned char			prev_status;
85ab4382d2SGreg Kroah-Hartman };
86ab4382d2SGreg Kroah-Hartman 
87ab4382d2SGreg Kroah-Hartman #define ZILOG_CHANNEL_FROM_PORT(PORT)	((struct zilog_channel *)((PORT)->membase))
88ab4382d2SGreg Kroah-Hartman #define UART_ZILOG(PORT)		((struct uart_ip22zilog_port *)(PORT))
89ab4382d2SGreg Kroah-Hartman #define IP22ZILOG_GET_CURR_REG(PORT, REGNUM)		\
90ab4382d2SGreg Kroah-Hartman 	(UART_ZILOG(PORT)->curregs[REGNUM])
91ab4382d2SGreg Kroah-Hartman #define IP22ZILOG_SET_CURR_REG(PORT, REGNUM, REGVAL)	\
92ab4382d2SGreg Kroah-Hartman 	((UART_ZILOG(PORT)->curregs[REGNUM]) = (REGVAL))
93ab4382d2SGreg Kroah-Hartman #define ZS_IS_CONS(UP)	((UP)->flags & IP22ZILOG_FLAG_IS_CONS)
94ab4382d2SGreg Kroah-Hartman #define ZS_IS_KGDB(UP)	((UP)->flags & IP22ZILOG_FLAG_IS_KGDB)
95ab4382d2SGreg Kroah-Hartman #define ZS_WANTS_MODEM_STATUS(UP)	((UP)->flags & IP22ZILOG_FLAG_MODEM_STATUS)
96ab4382d2SGreg Kroah-Hartman #define ZS_IS_CHANNEL_A(UP)	((UP)->flags & IP22ZILOG_FLAG_IS_CHANNEL_A)
97ab4382d2SGreg Kroah-Hartman #define ZS_REGS_HELD(UP)	((UP)->flags & IP22ZILOG_FLAG_REGS_HELD)
98ab4382d2SGreg Kroah-Hartman #define ZS_TX_STOPPED(UP)	((UP)->flags & IP22ZILOG_FLAG_TX_STOPPED)
99ab4382d2SGreg Kroah-Hartman #define ZS_TX_ACTIVE(UP)	((UP)->flags & IP22ZILOG_FLAG_TX_ACTIVE)
100ab4382d2SGreg Kroah-Hartman 
101ab4382d2SGreg Kroah-Hartman /* Reading and writing Zilog8530 registers.  The delays are to make this
102ab4382d2SGreg Kroah-Hartman  * driver work on the IP22 which needs a settling delay after each chip
103ab4382d2SGreg Kroah-Hartman  * register access, other machines handle this in hardware via auxiliary
104ab4382d2SGreg Kroah-Hartman  * flip-flops which implement the settle time we do in software.
105ab4382d2SGreg Kroah-Hartman  *
106ab4382d2SGreg Kroah-Hartman  * The port lock must be held and local IRQs must be disabled
107ab4382d2SGreg Kroah-Hartman  * when {read,write}_zsreg is invoked.
108ab4382d2SGreg Kroah-Hartman  */
read_zsreg(struct zilog_channel * channel,unsigned char reg)109ab4382d2SGreg Kroah-Hartman static unsigned char read_zsreg(struct zilog_channel *channel,
110ab4382d2SGreg Kroah-Hartman 				unsigned char reg)
111ab4382d2SGreg Kroah-Hartman {
112ab4382d2SGreg Kroah-Hartman 	unsigned char retval;
113ab4382d2SGreg Kroah-Hartman 
114ab4382d2SGreg Kroah-Hartman 	writeb(reg, &channel->control);
115ab4382d2SGreg Kroah-Hartman 	ZSDELAY();
116ab4382d2SGreg Kroah-Hartman 	retval = readb(&channel->control);
117ab4382d2SGreg Kroah-Hartman 	ZSDELAY();
118ab4382d2SGreg Kroah-Hartman 
119ab4382d2SGreg Kroah-Hartman 	return retval;
120ab4382d2SGreg Kroah-Hartman }
121ab4382d2SGreg Kroah-Hartman 
write_zsreg(struct zilog_channel * channel,unsigned char reg,unsigned char value)122ab4382d2SGreg Kroah-Hartman static void write_zsreg(struct zilog_channel *channel,
123ab4382d2SGreg Kroah-Hartman 			unsigned char reg, unsigned char value)
124ab4382d2SGreg Kroah-Hartman {
125ab4382d2SGreg Kroah-Hartman 	writeb(reg, &channel->control);
126ab4382d2SGreg Kroah-Hartman 	ZSDELAY();
127ab4382d2SGreg Kroah-Hartman 	writeb(value, &channel->control);
128ab4382d2SGreg Kroah-Hartman 	ZSDELAY();
129ab4382d2SGreg Kroah-Hartman }
130ab4382d2SGreg Kroah-Hartman 
ip22zilog_clear_fifo(struct zilog_channel * channel)131ab4382d2SGreg Kroah-Hartman static void ip22zilog_clear_fifo(struct zilog_channel *channel)
132ab4382d2SGreg Kroah-Hartman {
133ab4382d2SGreg Kroah-Hartman 	int i;
134ab4382d2SGreg Kroah-Hartman 
135ab4382d2SGreg Kroah-Hartman 	for (i = 0; i < 32; i++) {
136ab4382d2SGreg Kroah-Hartman 		unsigned char regval;
137ab4382d2SGreg Kroah-Hartman 
138ab4382d2SGreg Kroah-Hartman 		regval = readb(&channel->control);
139ab4382d2SGreg Kroah-Hartman 		ZSDELAY();
140ab4382d2SGreg Kroah-Hartman 		if (regval & Rx_CH_AV)
141ab4382d2SGreg Kroah-Hartman 			break;
142ab4382d2SGreg Kroah-Hartman 
143ab4382d2SGreg Kroah-Hartman 		regval = read_zsreg(channel, R1);
144ab4382d2SGreg Kroah-Hartman 		readb(&channel->data);
145ab4382d2SGreg Kroah-Hartman 		ZSDELAY();
146ab4382d2SGreg Kroah-Hartman 
147ab4382d2SGreg Kroah-Hartman 		if (regval & (PAR_ERR | Rx_OVR | CRC_ERR)) {
148ab4382d2SGreg Kroah-Hartman 			writeb(ERR_RES, &channel->control);
149ab4382d2SGreg Kroah-Hartman 			ZSDELAY();
150ab4382d2SGreg Kroah-Hartman 			ZS_WSYNC(channel);
151ab4382d2SGreg Kroah-Hartman 		}
152ab4382d2SGreg Kroah-Hartman 	}
153ab4382d2SGreg Kroah-Hartman }
154ab4382d2SGreg Kroah-Hartman 
155ab4382d2SGreg Kroah-Hartman /* This function must only be called when the TX is not busy.  The UART
156ab4382d2SGreg Kroah-Hartman  * port lock must be held and local interrupts disabled.
157ab4382d2SGreg Kroah-Hartman  */
__load_zsregs(struct zilog_channel * channel,unsigned char * regs)158ab4382d2SGreg Kroah-Hartman static void __load_zsregs(struct zilog_channel *channel, unsigned char *regs)
159ab4382d2SGreg Kroah-Hartman {
160ab4382d2SGreg Kroah-Hartman 	int i;
161ab4382d2SGreg Kroah-Hartman 
162ab4382d2SGreg Kroah-Hartman 	/* Let pending transmits finish.  */
163ab4382d2SGreg Kroah-Hartman 	for (i = 0; i < 1000; i++) {
164ab4382d2SGreg Kroah-Hartman 		unsigned char stat = read_zsreg(channel, R1);
165ab4382d2SGreg Kroah-Hartman 		if (stat & ALL_SNT)
166ab4382d2SGreg Kroah-Hartman 			break;
167ab4382d2SGreg Kroah-Hartman 		udelay(100);
168ab4382d2SGreg Kroah-Hartman 	}
169ab4382d2SGreg Kroah-Hartman 
170ab4382d2SGreg Kroah-Hartman 	writeb(ERR_RES, &channel->control);
171ab4382d2SGreg Kroah-Hartman 	ZSDELAY();
172ab4382d2SGreg Kroah-Hartman 	ZS_WSYNC(channel);
173ab4382d2SGreg Kroah-Hartman 
174ab4382d2SGreg Kroah-Hartman 	ip22zilog_clear_fifo(channel);
175ab4382d2SGreg Kroah-Hartman 
176ab4382d2SGreg Kroah-Hartman 	/* Disable all interrupts.  */
177ab4382d2SGreg Kroah-Hartman 	write_zsreg(channel, R1,
178ab4382d2SGreg Kroah-Hartman 		    regs[R1] & ~(RxINT_MASK | TxINT_ENAB | EXT_INT_ENAB));
179ab4382d2SGreg Kroah-Hartman 
180ab4382d2SGreg Kroah-Hartman 	/* Set parity, sync config, stop bits, and clock divisor.  */
181ab4382d2SGreg Kroah-Hartman 	write_zsreg(channel, R4, regs[R4]);
182ab4382d2SGreg Kroah-Hartman 
183ab4382d2SGreg Kroah-Hartman 	/* Set misc. TX/RX control bits.  */
184ab4382d2SGreg Kroah-Hartman 	write_zsreg(channel, R10, regs[R10]);
185ab4382d2SGreg Kroah-Hartman 
186ab4382d2SGreg Kroah-Hartman 	/* Set TX/RX controls sans the enable bits.  */
187ab4382d2SGreg Kroah-Hartman 	write_zsreg(channel, R3, regs[R3] & ~RxENAB);
188ab4382d2SGreg Kroah-Hartman 	write_zsreg(channel, R5, regs[R5] & ~TxENAB);
189ab4382d2SGreg Kroah-Hartman 
190ab4382d2SGreg Kroah-Hartman 	/* Synchronous mode config.  */
191ab4382d2SGreg Kroah-Hartman 	write_zsreg(channel, R6, regs[R6]);
192ab4382d2SGreg Kroah-Hartman 	write_zsreg(channel, R7, regs[R7]);
193ab4382d2SGreg Kroah-Hartman 
194ab4382d2SGreg Kroah-Hartman 	/* Don't mess with the interrupt vector (R2, unused by us) and
195ab4382d2SGreg Kroah-Hartman 	 * master interrupt control (R9).  We make sure this is setup
196ab4382d2SGreg Kroah-Hartman 	 * properly at probe time then never touch it again.
197ab4382d2SGreg Kroah-Hartman 	 */
198ab4382d2SGreg Kroah-Hartman 
199ab4382d2SGreg Kroah-Hartman 	/* Disable baud generator.  */
200ab4382d2SGreg Kroah-Hartman 	write_zsreg(channel, R14, regs[R14] & ~BRENAB);
201ab4382d2SGreg Kroah-Hartman 
202ab4382d2SGreg Kroah-Hartman 	/* Clock mode control.  */
203ab4382d2SGreg Kroah-Hartman 	write_zsreg(channel, R11, regs[R11]);
204ab4382d2SGreg Kroah-Hartman 
205ab4382d2SGreg Kroah-Hartman 	/* Lower and upper byte of baud rate generator divisor.  */
206ab4382d2SGreg Kroah-Hartman 	write_zsreg(channel, R12, regs[R12]);
207ab4382d2SGreg Kroah-Hartman 	write_zsreg(channel, R13, regs[R13]);
208ab4382d2SGreg Kroah-Hartman 
209ab4382d2SGreg Kroah-Hartman 	/* Now rewrite R14, with BRENAB (if set).  */
210ab4382d2SGreg Kroah-Hartman 	write_zsreg(channel, R14, regs[R14]);
211ab4382d2SGreg Kroah-Hartman 
212ab4382d2SGreg Kroah-Hartman 	/* External status interrupt control.  */
213ab4382d2SGreg Kroah-Hartman 	write_zsreg(channel, R15, regs[R15]);
214ab4382d2SGreg Kroah-Hartman 
215ab4382d2SGreg Kroah-Hartman 	/* Reset external status interrupts.  */
216ab4382d2SGreg Kroah-Hartman 	write_zsreg(channel, R0, RES_EXT_INT);
217ab4382d2SGreg Kroah-Hartman 	write_zsreg(channel, R0, RES_EXT_INT);
218ab4382d2SGreg Kroah-Hartman 
219ab4382d2SGreg Kroah-Hartman 	/* Rewrite R3/R5, this time without enables masked.  */
220ab4382d2SGreg Kroah-Hartman 	write_zsreg(channel, R3, regs[R3]);
221ab4382d2SGreg Kroah-Hartman 	write_zsreg(channel, R5, regs[R5]);
222ab4382d2SGreg Kroah-Hartman 
223ab4382d2SGreg Kroah-Hartman 	/* Rewrite R1, this time without IRQ enabled masked.  */
224ab4382d2SGreg Kroah-Hartman 	write_zsreg(channel, R1, regs[R1]);
225ab4382d2SGreg Kroah-Hartman }
226ab4382d2SGreg Kroah-Hartman 
227ab4382d2SGreg Kroah-Hartman /* Reprogram the Zilog channel HW registers with the copies found in the
228ab4382d2SGreg Kroah-Hartman  * software state struct.  If the transmitter is busy, we defer this update
229ab4382d2SGreg Kroah-Hartman  * until the next TX complete interrupt.  Else, we do it right now.
230ab4382d2SGreg Kroah-Hartman  *
231ab4382d2SGreg Kroah-Hartman  * The UART port lock must be held and local interrupts disabled.
232ab4382d2SGreg Kroah-Hartman  */
ip22zilog_maybe_update_regs(struct uart_ip22zilog_port * up,struct zilog_channel * channel)233ab4382d2SGreg Kroah-Hartman static void ip22zilog_maybe_update_regs(struct uart_ip22zilog_port *up,
234ab4382d2SGreg Kroah-Hartman 				       struct zilog_channel *channel)
235ab4382d2SGreg Kroah-Hartman {
236ab4382d2SGreg Kroah-Hartman 	if (!ZS_REGS_HELD(up)) {
237ab4382d2SGreg Kroah-Hartman 		if (ZS_TX_ACTIVE(up)) {
238ab4382d2SGreg Kroah-Hartman 			up->flags |= IP22ZILOG_FLAG_REGS_HELD;
239ab4382d2SGreg Kroah-Hartman 		} else {
240ab4382d2SGreg Kroah-Hartman 			__load_zsregs(channel, up->curregs);
241ab4382d2SGreg Kroah-Hartman 		}
242ab4382d2SGreg Kroah-Hartman 	}
243ab4382d2SGreg Kroah-Hartman }
244ab4382d2SGreg Kroah-Hartman 
245ab4382d2SGreg Kroah-Hartman #define Rx_BRK 0x0100                   /* BREAK event software flag.  */
246ab4382d2SGreg Kroah-Hartman #define Rx_SYS 0x0200                   /* SysRq event software flag.  */
247ab4382d2SGreg Kroah-Hartman 
ip22zilog_receive_chars(struct uart_ip22zilog_port * up,struct zilog_channel * channel)2482894500dSJiri Slaby static bool ip22zilog_receive_chars(struct uart_ip22zilog_port *up,
249ab4382d2SGreg Kroah-Hartman 						  struct zilog_channel *channel)
250ab4382d2SGreg Kroah-Hartman {
251ab4382d2SGreg Kroah-Hartman 	unsigned int r1;
252*fd2b55f8SJiri Slaby 	u8 ch, flag;
2532894500dSJiri Slaby 	bool push = up->port.state != NULL;
254ab4382d2SGreg Kroah-Hartman 
255ab4382d2SGreg Kroah-Hartman 	for (;;) {
256ab4382d2SGreg Kroah-Hartman 		ch = readb(&channel->control);
257ab4382d2SGreg Kroah-Hartman 		ZSDELAY();
258ab4382d2SGreg Kroah-Hartman 		if (!(ch & Rx_CH_AV))
259ab4382d2SGreg Kroah-Hartman 			break;
260ab4382d2SGreg Kroah-Hartman 
261ab4382d2SGreg Kroah-Hartman 		r1 = read_zsreg(channel, R1);
262ab4382d2SGreg Kroah-Hartman 		if (r1 & (PAR_ERR | Rx_OVR | CRC_ERR)) {
263ab4382d2SGreg Kroah-Hartman 			writeb(ERR_RES, &channel->control);
264ab4382d2SGreg Kroah-Hartman 			ZSDELAY();
265ab4382d2SGreg Kroah-Hartman 			ZS_WSYNC(channel);
266ab4382d2SGreg Kroah-Hartman 		}
267ab4382d2SGreg Kroah-Hartman 
268ab4382d2SGreg Kroah-Hartman 		ch = readb(&channel->data);
269ab4382d2SGreg Kroah-Hartman 		ZSDELAY();
270ab4382d2SGreg Kroah-Hartman 
271ab4382d2SGreg Kroah-Hartman 		ch &= up->parity_mask;
272ab4382d2SGreg Kroah-Hartman 
273ab4382d2SGreg Kroah-Hartman 		/* Handle the null char got when BREAK is removed.  */
274ab4382d2SGreg Kroah-Hartman 		if (!ch)
275ab4382d2SGreg Kroah-Hartman 			r1 |= up->tty_break;
276ab4382d2SGreg Kroah-Hartman 
277ab4382d2SGreg Kroah-Hartman 		/* A real serial line, record the character and status.  */
278ab4382d2SGreg Kroah-Hartman 		flag = TTY_NORMAL;
279ab4382d2SGreg Kroah-Hartman 		up->port.icount.rx++;
280ab4382d2SGreg Kroah-Hartman 		if (r1 & (PAR_ERR | Rx_OVR | CRC_ERR | Rx_SYS | Rx_BRK)) {
281ab4382d2SGreg Kroah-Hartman 			up->tty_break = 0;
282ab4382d2SGreg Kroah-Hartman 
283ab4382d2SGreg Kroah-Hartman 			if (r1 & (Rx_SYS | Rx_BRK)) {
284ab4382d2SGreg Kroah-Hartman 				up->port.icount.brk++;
285ab4382d2SGreg Kroah-Hartman 				if (r1 & Rx_SYS)
286ab4382d2SGreg Kroah-Hartman 					continue;
287ab4382d2SGreg Kroah-Hartman 				r1 &= ~(PAR_ERR | CRC_ERR);
288ab4382d2SGreg Kroah-Hartman 			}
289ab4382d2SGreg Kroah-Hartman 			else if (r1 & PAR_ERR)
290ab4382d2SGreg Kroah-Hartman 				up->port.icount.parity++;
291ab4382d2SGreg Kroah-Hartman 			else if (r1 & CRC_ERR)
292ab4382d2SGreg Kroah-Hartman 				up->port.icount.frame++;
293ab4382d2SGreg Kroah-Hartman 			if (r1 & Rx_OVR)
294ab4382d2SGreg Kroah-Hartman 				up->port.icount.overrun++;
295ab4382d2SGreg Kroah-Hartman 			r1 &= up->port.read_status_mask;
296ab4382d2SGreg Kroah-Hartman 			if (r1 & Rx_BRK)
297ab4382d2SGreg Kroah-Hartman 				flag = TTY_BREAK;
298ab4382d2SGreg Kroah-Hartman 			else if (r1 & PAR_ERR)
299ab4382d2SGreg Kroah-Hartman 				flag = TTY_PARITY;
300ab4382d2SGreg Kroah-Hartman 			else if (r1 & CRC_ERR)
301ab4382d2SGreg Kroah-Hartman 				flag = TTY_FRAME;
302ab4382d2SGreg Kroah-Hartman 		}
303ab4382d2SGreg Kroah-Hartman 
304ab4382d2SGreg Kroah-Hartman 		if (uart_handle_sysrq_char(&up->port, ch))
305ab4382d2SGreg Kroah-Hartman 			continue;
306ab4382d2SGreg Kroah-Hartman 
3072894500dSJiri Slaby 		if (push)
308ab4382d2SGreg Kroah-Hartman 			uart_insert_char(&up->port, r1, Rx_OVR, ch, flag);
309ab4382d2SGreg Kroah-Hartman 	}
3102894500dSJiri Slaby 	return push;
311ab4382d2SGreg Kroah-Hartman }
312ab4382d2SGreg Kroah-Hartman 
ip22zilog_status_handle(struct uart_ip22zilog_port * up,struct zilog_channel * channel)313ab4382d2SGreg Kroah-Hartman static void ip22zilog_status_handle(struct uart_ip22zilog_port *up,
314ab4382d2SGreg Kroah-Hartman 				   struct zilog_channel *channel)
315ab4382d2SGreg Kroah-Hartman {
316ab4382d2SGreg Kroah-Hartman 	unsigned char status;
317ab4382d2SGreg Kroah-Hartman 
318ab4382d2SGreg Kroah-Hartman 	status = readb(&channel->control);
319ab4382d2SGreg Kroah-Hartman 	ZSDELAY();
320ab4382d2SGreg Kroah-Hartman 
321ab4382d2SGreg Kroah-Hartman 	writeb(RES_EXT_INT, &channel->control);
322ab4382d2SGreg Kroah-Hartman 	ZSDELAY();
323ab4382d2SGreg Kroah-Hartman 	ZS_WSYNC(channel);
324ab4382d2SGreg Kroah-Hartman 
325ab4382d2SGreg Kroah-Hartman 	if (up->curregs[R15] & BRKIE) {
326ab4382d2SGreg Kroah-Hartman 		if ((status & BRK_ABRT) && !(up->prev_status & BRK_ABRT)) {
327ab4382d2SGreg Kroah-Hartman 			if (uart_handle_break(&up->port))
328ab4382d2SGreg Kroah-Hartman 				up->tty_break = Rx_SYS;
329ab4382d2SGreg Kroah-Hartman 			else
330ab4382d2SGreg Kroah-Hartman 				up->tty_break = Rx_BRK;
331ab4382d2SGreg Kroah-Hartman 		}
332ab4382d2SGreg Kroah-Hartman 	}
333ab4382d2SGreg Kroah-Hartman 
334ab4382d2SGreg Kroah-Hartman 	if (ZS_WANTS_MODEM_STATUS(up)) {
335ab4382d2SGreg Kroah-Hartman 		if (status & SYNC)
336ab4382d2SGreg Kroah-Hartman 			up->port.icount.dsr++;
337ab4382d2SGreg Kroah-Hartman 
338ab4382d2SGreg Kroah-Hartman 		/* The Zilog just gives us an interrupt when DCD/CTS/etc. change.
339ab4382d2SGreg Kroah-Hartman 		 * But it does not tell us which bit has changed, we have to keep
340ab4382d2SGreg Kroah-Hartman 		 * track of this ourselves.
341ab4382d2SGreg Kroah-Hartman 		 */
342ab4382d2SGreg Kroah-Hartman 		if ((status ^ up->prev_status) ^ DCD)
343ab4382d2SGreg Kroah-Hartman 			uart_handle_dcd_change(&up->port,
344ab4382d2SGreg Kroah-Hartman 					       (status & DCD));
345ab4382d2SGreg Kroah-Hartman 		if ((status ^ up->prev_status) ^ CTS)
346ab4382d2SGreg Kroah-Hartman 			uart_handle_cts_change(&up->port,
347ab4382d2SGreg Kroah-Hartman 					       (status & CTS));
348ab4382d2SGreg Kroah-Hartman 
349ab4382d2SGreg Kroah-Hartman 		wake_up_interruptible(&up->port.state->port.delta_msr_wait);
350ab4382d2SGreg Kroah-Hartman 	}
351ab4382d2SGreg Kroah-Hartman 
352ab4382d2SGreg Kroah-Hartman 	up->prev_status = status;
353ab4382d2SGreg Kroah-Hartman }
354ab4382d2SGreg Kroah-Hartman 
ip22zilog_transmit_chars(struct uart_ip22zilog_port * up,struct zilog_channel * channel)355ab4382d2SGreg Kroah-Hartman static void ip22zilog_transmit_chars(struct uart_ip22zilog_port *up,
356ab4382d2SGreg Kroah-Hartman 				    struct zilog_channel *channel)
357ab4382d2SGreg Kroah-Hartman {
358ab4382d2SGreg Kroah-Hartman 	struct circ_buf *xmit;
359ab4382d2SGreg Kroah-Hartman 
360ab4382d2SGreg Kroah-Hartman 	if (ZS_IS_CONS(up)) {
361ab4382d2SGreg Kroah-Hartman 		unsigned char status = readb(&channel->control);
362ab4382d2SGreg Kroah-Hartman 		ZSDELAY();
363ab4382d2SGreg Kroah-Hartman 
364ab4382d2SGreg Kroah-Hartman 		/* TX still busy?  Just wait for the next TX done interrupt.
365ab4382d2SGreg Kroah-Hartman 		 *
366ab4382d2SGreg Kroah-Hartman 		 * It can occur because of how we do serial console writes.  It would
367ab4382d2SGreg Kroah-Hartman 		 * be nice to transmit console writes just like we normally would for
368ab4382d2SGreg Kroah-Hartman 		 * a TTY line. (ie. buffered and TX interrupt driven).  That is not
369ab4382d2SGreg Kroah-Hartman 		 * easy because console writes cannot sleep.  One solution might be
37025985edcSLucas De Marchi 		 * to poll on enough port->xmit space becoming free.  -DaveM
371ab4382d2SGreg Kroah-Hartman 		 */
372ab4382d2SGreg Kroah-Hartman 		if (!(status & Tx_BUF_EMP))
373ab4382d2SGreg Kroah-Hartman 			return;
374ab4382d2SGreg Kroah-Hartman 	}
375ab4382d2SGreg Kroah-Hartman 
376ab4382d2SGreg Kroah-Hartman 	up->flags &= ~IP22ZILOG_FLAG_TX_ACTIVE;
377ab4382d2SGreg Kroah-Hartman 
378ab4382d2SGreg Kroah-Hartman 	if (ZS_REGS_HELD(up)) {
379ab4382d2SGreg Kroah-Hartman 		__load_zsregs(channel, up->curregs);
380ab4382d2SGreg Kroah-Hartman 		up->flags &= ~IP22ZILOG_FLAG_REGS_HELD;
381ab4382d2SGreg Kroah-Hartman 	}
382ab4382d2SGreg Kroah-Hartman 
383ab4382d2SGreg Kroah-Hartman 	if (ZS_TX_STOPPED(up)) {
384ab4382d2SGreg Kroah-Hartman 		up->flags &= ~IP22ZILOG_FLAG_TX_STOPPED;
385ab4382d2SGreg Kroah-Hartman 		goto ack_tx_int;
386ab4382d2SGreg Kroah-Hartman 	}
387ab4382d2SGreg Kroah-Hartman 
388ab4382d2SGreg Kroah-Hartman 	if (up->port.x_char) {
389ab4382d2SGreg Kroah-Hartman 		up->flags |= IP22ZILOG_FLAG_TX_ACTIVE;
390ab4382d2SGreg Kroah-Hartman 		writeb(up->port.x_char, &channel->data);
391ab4382d2SGreg Kroah-Hartman 		ZSDELAY();
392ab4382d2SGreg Kroah-Hartman 		ZS_WSYNC(channel);
393ab4382d2SGreg Kroah-Hartman 
394ab4382d2SGreg Kroah-Hartman 		up->port.icount.tx++;
395ab4382d2SGreg Kroah-Hartman 		up->port.x_char = 0;
396ab4382d2SGreg Kroah-Hartman 		return;
397ab4382d2SGreg Kroah-Hartman 	}
398ab4382d2SGreg Kroah-Hartman 
399ab4382d2SGreg Kroah-Hartman 	if (up->port.state == NULL)
400ab4382d2SGreg Kroah-Hartman 		goto ack_tx_int;
401ab4382d2SGreg Kroah-Hartman 	xmit = &up->port.state->xmit;
402ab4382d2SGreg Kroah-Hartman 	if (uart_circ_empty(xmit))
403ab4382d2SGreg Kroah-Hartman 		goto ack_tx_int;
404ab4382d2SGreg Kroah-Hartman 	if (uart_tx_stopped(&up->port))
405ab4382d2SGreg Kroah-Hartman 		goto ack_tx_int;
406ab4382d2SGreg Kroah-Hartman 
407ab4382d2SGreg Kroah-Hartman 	up->flags |= IP22ZILOG_FLAG_TX_ACTIVE;
408ab4382d2SGreg Kroah-Hartman 	writeb(xmit->buf[xmit->tail], &channel->data);
409ab4382d2SGreg Kroah-Hartman 	ZSDELAY();
410ab4382d2SGreg Kroah-Hartman 	ZS_WSYNC(channel);
411ab4382d2SGreg Kroah-Hartman 
412daf63432SIlpo Järvinen 	uart_xmit_advance(&up->port, 1);
413ab4382d2SGreg Kroah-Hartman 
414ab4382d2SGreg Kroah-Hartman 	if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
415ab4382d2SGreg Kroah-Hartman 		uart_write_wakeup(&up->port);
416ab4382d2SGreg Kroah-Hartman 
417ab4382d2SGreg Kroah-Hartman 	return;
418ab4382d2SGreg Kroah-Hartman 
419ab4382d2SGreg Kroah-Hartman ack_tx_int:
420ab4382d2SGreg Kroah-Hartman 	writeb(RES_Tx_P, &channel->control);
421ab4382d2SGreg Kroah-Hartman 	ZSDELAY();
422ab4382d2SGreg Kroah-Hartman 	ZS_WSYNC(channel);
423ab4382d2SGreg Kroah-Hartman }
424ab4382d2SGreg Kroah-Hartman 
ip22zilog_interrupt(int irq,void * dev_id)425ab4382d2SGreg Kroah-Hartman static irqreturn_t ip22zilog_interrupt(int irq, void *dev_id)
426ab4382d2SGreg Kroah-Hartman {
427ab4382d2SGreg Kroah-Hartman 	struct uart_ip22zilog_port *up = dev_id;
428ab4382d2SGreg Kroah-Hartman 
429ab4382d2SGreg Kroah-Hartman 	while (up) {
430ab4382d2SGreg Kroah-Hartman 		struct zilog_channel *channel
431ab4382d2SGreg Kroah-Hartman 			= ZILOG_CHANNEL_FROM_PORT(&up->port);
432ab4382d2SGreg Kroah-Hartman 		unsigned char r3;
4332894500dSJiri Slaby 		bool push = false;
434ab4382d2SGreg Kroah-Hartman 
435ab4382d2SGreg Kroah-Hartman 		spin_lock(&up->port.lock);
436ab4382d2SGreg Kroah-Hartman 		r3 = read_zsreg(channel, R3);
437ab4382d2SGreg Kroah-Hartman 
438ab4382d2SGreg Kroah-Hartman 		/* Channel A */
439ab4382d2SGreg Kroah-Hartman 		if (r3 & (CHAEXT | CHATxIP | CHARxIP)) {
440ab4382d2SGreg Kroah-Hartman 			writeb(RES_H_IUS, &channel->control);
441ab4382d2SGreg Kroah-Hartman 			ZSDELAY();
442ab4382d2SGreg Kroah-Hartman 			ZS_WSYNC(channel);
443ab4382d2SGreg Kroah-Hartman 
444ab4382d2SGreg Kroah-Hartman 			if (r3 & CHARxIP)
4452894500dSJiri Slaby 				push = ip22zilog_receive_chars(up, channel);
446ab4382d2SGreg Kroah-Hartman 			if (r3 & CHAEXT)
447ab4382d2SGreg Kroah-Hartman 				ip22zilog_status_handle(up, channel);
448ab4382d2SGreg Kroah-Hartman 			if (r3 & CHATxIP)
449ab4382d2SGreg Kroah-Hartman 				ip22zilog_transmit_chars(up, channel);
450ab4382d2SGreg Kroah-Hartman 		}
451ab4382d2SGreg Kroah-Hartman 		spin_unlock(&up->port.lock);
452ab4382d2SGreg Kroah-Hartman 
4532894500dSJiri Slaby 		if (push)
4542894500dSJiri Slaby 			tty_flip_buffer_push(&up->port.state->port);
455ab4382d2SGreg Kroah-Hartman 
456ab4382d2SGreg Kroah-Hartman 		/* Channel B */
457ab4382d2SGreg Kroah-Hartman 		up = up->next;
458ab4382d2SGreg Kroah-Hartman 		channel = ZILOG_CHANNEL_FROM_PORT(&up->port);
4592894500dSJiri Slaby 		push = false;
460ab4382d2SGreg Kroah-Hartman 
461ab4382d2SGreg Kroah-Hartman 		spin_lock(&up->port.lock);
462ab4382d2SGreg Kroah-Hartman 		if (r3 & (CHBEXT | CHBTxIP | CHBRxIP)) {
463ab4382d2SGreg Kroah-Hartman 			writeb(RES_H_IUS, &channel->control);
464ab4382d2SGreg Kroah-Hartman 			ZSDELAY();
465ab4382d2SGreg Kroah-Hartman 			ZS_WSYNC(channel);
466ab4382d2SGreg Kroah-Hartman 
467ab4382d2SGreg Kroah-Hartman 			if (r3 & CHBRxIP)
4682894500dSJiri Slaby 				push = ip22zilog_receive_chars(up, channel);
469ab4382d2SGreg Kroah-Hartman 			if (r3 & CHBEXT)
470ab4382d2SGreg Kroah-Hartman 				ip22zilog_status_handle(up, channel);
471ab4382d2SGreg Kroah-Hartman 			if (r3 & CHBTxIP)
472ab4382d2SGreg Kroah-Hartman 				ip22zilog_transmit_chars(up, channel);
473ab4382d2SGreg Kroah-Hartman 		}
474ab4382d2SGreg Kroah-Hartman 		spin_unlock(&up->port.lock);
475ab4382d2SGreg Kroah-Hartman 
4762894500dSJiri Slaby 		if (push)
4772894500dSJiri Slaby 			tty_flip_buffer_push(&up->port.state->port);
478ab4382d2SGreg Kroah-Hartman 
479ab4382d2SGreg Kroah-Hartman 		up = up->next;
480ab4382d2SGreg Kroah-Hartman 	}
481ab4382d2SGreg Kroah-Hartman 
482ab4382d2SGreg Kroah-Hartman 	return IRQ_HANDLED;
483ab4382d2SGreg Kroah-Hartman }
484ab4382d2SGreg Kroah-Hartman 
485ab4382d2SGreg Kroah-Hartman /* A convenient way to quickly get R0 status.  The caller must _not_ hold the
486ab4382d2SGreg Kroah-Hartman  * port lock, it is acquired here.
487ab4382d2SGreg Kroah-Hartman  */
ip22zilog_read_channel_status(struct uart_port * port)488ab4382d2SGreg Kroah-Hartman static __inline__ unsigned char ip22zilog_read_channel_status(struct uart_port *port)
489ab4382d2SGreg Kroah-Hartman {
490ab4382d2SGreg Kroah-Hartman 	struct zilog_channel *channel;
491ab4382d2SGreg Kroah-Hartman 	unsigned char status;
492ab4382d2SGreg Kroah-Hartman 
493ab4382d2SGreg Kroah-Hartman 	channel = ZILOG_CHANNEL_FROM_PORT(port);
494ab4382d2SGreg Kroah-Hartman 	status = readb(&channel->control);
495ab4382d2SGreg Kroah-Hartman 	ZSDELAY();
496ab4382d2SGreg Kroah-Hartman 
497ab4382d2SGreg Kroah-Hartman 	return status;
498ab4382d2SGreg Kroah-Hartman }
499ab4382d2SGreg Kroah-Hartman 
500ab4382d2SGreg Kroah-Hartman /* The port lock is not held.  */
ip22zilog_tx_empty(struct uart_port * port)501ab4382d2SGreg Kroah-Hartman static unsigned int ip22zilog_tx_empty(struct uart_port *port)
502ab4382d2SGreg Kroah-Hartman {
503ab4382d2SGreg Kroah-Hartman 	unsigned long flags;
504ab4382d2SGreg Kroah-Hartman 	unsigned char status;
505ab4382d2SGreg Kroah-Hartman 	unsigned int ret;
506ab4382d2SGreg Kroah-Hartman 
507ab4382d2SGreg Kroah-Hartman 	spin_lock_irqsave(&port->lock, flags);
508ab4382d2SGreg Kroah-Hartman 
509ab4382d2SGreg Kroah-Hartman 	status = ip22zilog_read_channel_status(port);
510ab4382d2SGreg Kroah-Hartman 
511ab4382d2SGreg Kroah-Hartman 	spin_unlock_irqrestore(&port->lock, flags);
512ab4382d2SGreg Kroah-Hartman 
513ab4382d2SGreg Kroah-Hartman 	if (status & Tx_BUF_EMP)
514ab4382d2SGreg Kroah-Hartman 		ret = TIOCSER_TEMT;
515ab4382d2SGreg Kroah-Hartman 	else
516ab4382d2SGreg Kroah-Hartman 		ret = 0;
517ab4382d2SGreg Kroah-Hartman 
518ab4382d2SGreg Kroah-Hartman 	return ret;
519ab4382d2SGreg Kroah-Hartman }
520ab4382d2SGreg Kroah-Hartman 
521ab4382d2SGreg Kroah-Hartman /* The port lock is held and interrupts are disabled.  */
ip22zilog_get_mctrl(struct uart_port * port)522ab4382d2SGreg Kroah-Hartman static unsigned int ip22zilog_get_mctrl(struct uart_port *port)
523ab4382d2SGreg Kroah-Hartman {
524ab4382d2SGreg Kroah-Hartman 	unsigned char status;
525ab4382d2SGreg Kroah-Hartman 	unsigned int ret;
526ab4382d2SGreg Kroah-Hartman 
527ab4382d2SGreg Kroah-Hartman 	status = ip22zilog_read_channel_status(port);
528ab4382d2SGreg Kroah-Hartman 
529ab4382d2SGreg Kroah-Hartman 	ret = 0;
530ab4382d2SGreg Kroah-Hartman 	if (status & DCD)
531ab4382d2SGreg Kroah-Hartman 		ret |= TIOCM_CAR;
532ab4382d2SGreg Kroah-Hartman 	if (status & SYNC)
533ab4382d2SGreg Kroah-Hartman 		ret |= TIOCM_DSR;
534ab4382d2SGreg Kroah-Hartman 	if (status & CTS)
535ab4382d2SGreg Kroah-Hartman 		ret |= TIOCM_CTS;
536ab4382d2SGreg Kroah-Hartman 
537ab4382d2SGreg Kroah-Hartman 	return ret;
538ab4382d2SGreg Kroah-Hartman }
539ab4382d2SGreg Kroah-Hartman 
540ab4382d2SGreg Kroah-Hartman /* The port lock is held and interrupts are disabled.  */
ip22zilog_set_mctrl(struct uart_port * port,unsigned int mctrl)541ab4382d2SGreg Kroah-Hartman static void ip22zilog_set_mctrl(struct uart_port *port, unsigned int mctrl)
542ab4382d2SGreg Kroah-Hartman {
5432413b320SFabian Frederick 	struct uart_ip22zilog_port *up =
5442413b320SFabian Frederick 		container_of(port, struct uart_ip22zilog_port, port);
545ab4382d2SGreg Kroah-Hartman 	struct zilog_channel *channel = ZILOG_CHANNEL_FROM_PORT(port);
546ab4382d2SGreg Kroah-Hartman 	unsigned char set_bits, clear_bits;
547ab4382d2SGreg Kroah-Hartman 
548ab4382d2SGreg Kroah-Hartman 	set_bits = clear_bits = 0;
549ab4382d2SGreg Kroah-Hartman 
550ab4382d2SGreg Kroah-Hartman 	if (mctrl & TIOCM_RTS)
551ab4382d2SGreg Kroah-Hartman 		set_bits |= RTS;
552ab4382d2SGreg Kroah-Hartman 	else
553ab4382d2SGreg Kroah-Hartman 		clear_bits |= RTS;
554ab4382d2SGreg Kroah-Hartman 	if (mctrl & TIOCM_DTR)
555ab4382d2SGreg Kroah-Hartman 		set_bits |= DTR;
556ab4382d2SGreg Kroah-Hartman 	else
557ab4382d2SGreg Kroah-Hartman 		clear_bits |= DTR;
558ab4382d2SGreg Kroah-Hartman 
559ab4382d2SGreg Kroah-Hartman 	/* NOTE: Not subject to 'transmitter active' rule.  */
560ab4382d2SGreg Kroah-Hartman 	up->curregs[R5] |= set_bits;
561ab4382d2SGreg Kroah-Hartman 	up->curregs[R5] &= ~clear_bits;
562ab4382d2SGreg Kroah-Hartman 	write_zsreg(channel, R5, up->curregs[R5]);
563ab4382d2SGreg Kroah-Hartman }
564ab4382d2SGreg Kroah-Hartman 
565ab4382d2SGreg Kroah-Hartman /* The port lock is held and interrupts are disabled.  */
ip22zilog_stop_tx(struct uart_port * port)566ab4382d2SGreg Kroah-Hartman static void ip22zilog_stop_tx(struct uart_port *port)
567ab4382d2SGreg Kroah-Hartman {
5682413b320SFabian Frederick 	struct uart_ip22zilog_port *up =
5692413b320SFabian Frederick 		container_of(port, struct uart_ip22zilog_port, port);
570ab4382d2SGreg Kroah-Hartman 
571ab4382d2SGreg Kroah-Hartman 	up->flags |= IP22ZILOG_FLAG_TX_STOPPED;
572ab4382d2SGreg Kroah-Hartman }
573ab4382d2SGreg Kroah-Hartman 
574ab4382d2SGreg Kroah-Hartman /* The port lock is held and interrupts are disabled.  */
ip22zilog_start_tx(struct uart_port * port)575ab4382d2SGreg Kroah-Hartman static void ip22zilog_start_tx(struct uart_port *port)
576ab4382d2SGreg Kroah-Hartman {
5772413b320SFabian Frederick 	struct uart_ip22zilog_port *up =
5782413b320SFabian Frederick 		container_of(port, struct uart_ip22zilog_port, port);
579ab4382d2SGreg Kroah-Hartman 	struct zilog_channel *channel = ZILOG_CHANNEL_FROM_PORT(port);
580ab4382d2SGreg Kroah-Hartman 	unsigned char status;
581ab4382d2SGreg Kroah-Hartman 
582ab4382d2SGreg Kroah-Hartman 	up->flags |= IP22ZILOG_FLAG_TX_ACTIVE;
583ab4382d2SGreg Kroah-Hartman 	up->flags &= ~IP22ZILOG_FLAG_TX_STOPPED;
584ab4382d2SGreg Kroah-Hartman 
585ab4382d2SGreg Kroah-Hartman 	status = readb(&channel->control);
586ab4382d2SGreg Kroah-Hartman 	ZSDELAY();
587ab4382d2SGreg Kroah-Hartman 
588ab4382d2SGreg Kroah-Hartman 	/* TX busy?  Just wait for the TX done interrupt.  */
589ab4382d2SGreg Kroah-Hartman 	if (!(status & Tx_BUF_EMP))
590ab4382d2SGreg Kroah-Hartman 		return;
591ab4382d2SGreg Kroah-Hartman 
592ab4382d2SGreg Kroah-Hartman 	/* Send the first character to jump-start the TX done
593ab4382d2SGreg Kroah-Hartman 	 * IRQ sending engine.
594ab4382d2SGreg Kroah-Hartman 	 */
595ab4382d2SGreg Kroah-Hartman 	if (port->x_char) {
596ab4382d2SGreg Kroah-Hartman 		writeb(port->x_char, &channel->data);
597ab4382d2SGreg Kroah-Hartman 		ZSDELAY();
598ab4382d2SGreg Kroah-Hartman 		ZS_WSYNC(channel);
599ab4382d2SGreg Kroah-Hartman 
600ab4382d2SGreg Kroah-Hartman 		port->icount.tx++;
601ab4382d2SGreg Kroah-Hartman 		port->x_char = 0;
602ab4382d2SGreg Kroah-Hartman 	} else {
603ab4382d2SGreg Kroah-Hartman 		struct circ_buf *xmit = &port->state->xmit;
604ab4382d2SGreg Kroah-Hartman 
605c557d392SPeter Hurley 		if (uart_circ_empty(xmit))
606c557d392SPeter Hurley 			return;
607ab4382d2SGreg Kroah-Hartman 		writeb(xmit->buf[xmit->tail], &channel->data);
608ab4382d2SGreg Kroah-Hartman 		ZSDELAY();
609ab4382d2SGreg Kroah-Hartman 		ZS_WSYNC(channel);
610ab4382d2SGreg Kroah-Hartman 
611daf63432SIlpo Järvinen 		uart_xmit_advance(port, 1);
612ab4382d2SGreg Kroah-Hartman 
613ab4382d2SGreg Kroah-Hartman 		if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
614ab4382d2SGreg Kroah-Hartman 			uart_write_wakeup(&up->port);
615ab4382d2SGreg Kroah-Hartman 	}
616ab4382d2SGreg Kroah-Hartman }
617ab4382d2SGreg Kroah-Hartman 
618ab4382d2SGreg Kroah-Hartman /* The port lock is held and interrupts are disabled.  */
ip22zilog_stop_rx(struct uart_port * port)619ab4382d2SGreg Kroah-Hartman static void ip22zilog_stop_rx(struct uart_port *port)
620ab4382d2SGreg Kroah-Hartman {
621ab4382d2SGreg Kroah-Hartman 	struct uart_ip22zilog_port *up = UART_ZILOG(port);
622ab4382d2SGreg Kroah-Hartman 	struct zilog_channel *channel;
623ab4382d2SGreg Kroah-Hartman 
624ab4382d2SGreg Kroah-Hartman 	if (ZS_IS_CONS(up))
625ab4382d2SGreg Kroah-Hartman 		return;
626ab4382d2SGreg Kroah-Hartman 
627ab4382d2SGreg Kroah-Hartman 	channel = ZILOG_CHANNEL_FROM_PORT(port);
628ab4382d2SGreg Kroah-Hartman 
629ab4382d2SGreg Kroah-Hartman 	/* Disable all RX interrupts.  */
630ab4382d2SGreg Kroah-Hartman 	up->curregs[R1] &= ~RxINT_MASK;
631ab4382d2SGreg Kroah-Hartman 	ip22zilog_maybe_update_regs(up, channel);
632ab4382d2SGreg Kroah-Hartman }
633ab4382d2SGreg Kroah-Hartman 
634ab4382d2SGreg Kroah-Hartman /* The port lock is held.  */
ip22zilog_enable_ms(struct uart_port * port)635ab4382d2SGreg Kroah-Hartman static void ip22zilog_enable_ms(struct uart_port *port)
636ab4382d2SGreg Kroah-Hartman {
6372413b320SFabian Frederick 	struct uart_ip22zilog_port *up =
6382413b320SFabian Frederick 		container_of(port, struct uart_ip22zilog_port, port);
639ab4382d2SGreg Kroah-Hartman 	struct zilog_channel *channel = ZILOG_CHANNEL_FROM_PORT(port);
640ab4382d2SGreg Kroah-Hartman 	unsigned char new_reg;
641ab4382d2SGreg Kroah-Hartman 
642ab4382d2SGreg Kroah-Hartman 	new_reg = up->curregs[R15] | (DCDIE | SYNCIE | CTSIE);
643ab4382d2SGreg Kroah-Hartman 	if (new_reg != up->curregs[R15]) {
644ab4382d2SGreg Kroah-Hartman 		up->curregs[R15] = new_reg;
645ab4382d2SGreg Kroah-Hartman 
646ab4382d2SGreg Kroah-Hartman 		/* NOTE: Not subject to 'transmitter active' rule.  */
647ab4382d2SGreg Kroah-Hartman 		write_zsreg(channel, R15, up->curregs[R15]);
648ab4382d2SGreg Kroah-Hartman 	}
649ab4382d2SGreg Kroah-Hartman }
650ab4382d2SGreg Kroah-Hartman 
651ab4382d2SGreg Kroah-Hartman /* The port lock is not held.  */
ip22zilog_break_ctl(struct uart_port * port,int break_state)652ab4382d2SGreg Kroah-Hartman static void ip22zilog_break_ctl(struct uart_port *port, int break_state)
653ab4382d2SGreg Kroah-Hartman {
6542413b320SFabian Frederick 	struct uart_ip22zilog_port *up =
6552413b320SFabian Frederick 		container_of(port, struct uart_ip22zilog_port, port);
656ab4382d2SGreg Kroah-Hartman 	struct zilog_channel *channel = ZILOG_CHANNEL_FROM_PORT(port);
657ab4382d2SGreg Kroah-Hartman 	unsigned char set_bits, clear_bits, new_reg;
658ab4382d2SGreg Kroah-Hartman 	unsigned long flags;
659ab4382d2SGreg Kroah-Hartman 
660ab4382d2SGreg Kroah-Hartman 	set_bits = clear_bits = 0;
661ab4382d2SGreg Kroah-Hartman 
662ab4382d2SGreg Kroah-Hartman 	if (break_state)
663ab4382d2SGreg Kroah-Hartman 		set_bits |= SND_BRK;
664ab4382d2SGreg Kroah-Hartman 	else
665ab4382d2SGreg Kroah-Hartman 		clear_bits |= SND_BRK;
666ab4382d2SGreg Kroah-Hartman 
667ab4382d2SGreg Kroah-Hartman 	spin_lock_irqsave(&port->lock, flags);
668ab4382d2SGreg Kroah-Hartman 
669ab4382d2SGreg Kroah-Hartman 	new_reg = (up->curregs[R5] | set_bits) & ~clear_bits;
670ab4382d2SGreg Kroah-Hartman 	if (new_reg != up->curregs[R5]) {
671ab4382d2SGreg Kroah-Hartman 		up->curregs[R5] = new_reg;
672ab4382d2SGreg Kroah-Hartman 
673ab4382d2SGreg Kroah-Hartman 		/* NOTE: Not subject to 'transmitter active' rule.  */
674ab4382d2SGreg Kroah-Hartman 		write_zsreg(channel, R5, up->curregs[R5]);
675ab4382d2SGreg Kroah-Hartman 	}
676ab4382d2SGreg Kroah-Hartman 
677ab4382d2SGreg Kroah-Hartman 	spin_unlock_irqrestore(&port->lock, flags);
678ab4382d2SGreg Kroah-Hartman }
679ab4382d2SGreg Kroah-Hartman 
__ip22zilog_reset(struct uart_ip22zilog_port * up)680ab4382d2SGreg Kroah-Hartman static void __ip22zilog_reset(struct uart_ip22zilog_port *up)
681ab4382d2SGreg Kroah-Hartman {
682ab4382d2SGreg Kroah-Hartman 	struct zilog_channel *channel;
683ab4382d2SGreg Kroah-Hartman 	int i;
684ab4382d2SGreg Kroah-Hartman 
685ab4382d2SGreg Kroah-Hartman 	if (up->flags & IP22ZILOG_FLAG_RESET_DONE)
686ab4382d2SGreg Kroah-Hartman 		return;
687ab4382d2SGreg Kroah-Hartman 
688ab4382d2SGreg Kroah-Hartman 	/* Let pending transmits finish.  */
689ab4382d2SGreg Kroah-Hartman 	channel = ZILOG_CHANNEL_FROM_PORT(&up->port);
690ab4382d2SGreg Kroah-Hartman 	for (i = 0; i < 1000; i++) {
691ab4382d2SGreg Kroah-Hartman 		unsigned char stat = read_zsreg(channel, R1);
692ab4382d2SGreg Kroah-Hartman 		if (stat & ALL_SNT)
693ab4382d2SGreg Kroah-Hartman 			break;
694ab4382d2SGreg Kroah-Hartman 		udelay(100);
695ab4382d2SGreg Kroah-Hartman 	}
696ab4382d2SGreg Kroah-Hartman 
697ab4382d2SGreg Kroah-Hartman 	if (!ZS_IS_CHANNEL_A(up)) {
698ab4382d2SGreg Kroah-Hartman 		up++;
699ab4382d2SGreg Kroah-Hartman 		channel = ZILOG_CHANNEL_FROM_PORT(&up->port);
700ab4382d2SGreg Kroah-Hartman 	}
701ab4382d2SGreg Kroah-Hartman 	write_zsreg(channel, R9, FHWRES);
702ab4382d2SGreg Kroah-Hartman 	ZSDELAY_LONG();
703ab4382d2SGreg Kroah-Hartman 	(void) read_zsreg(channel, R0);
704ab4382d2SGreg Kroah-Hartman 
705ab4382d2SGreg Kroah-Hartman 	up->flags |= IP22ZILOG_FLAG_RESET_DONE;
706ab4382d2SGreg Kroah-Hartman 	up->next->flags |= IP22ZILOG_FLAG_RESET_DONE;
707ab4382d2SGreg Kroah-Hartman }
708ab4382d2SGreg Kroah-Hartman 
__ip22zilog_startup(struct uart_ip22zilog_port * up)709ab4382d2SGreg Kroah-Hartman static void __ip22zilog_startup(struct uart_ip22zilog_port *up)
710ab4382d2SGreg Kroah-Hartman {
711ab4382d2SGreg Kroah-Hartman 	struct zilog_channel *channel;
712ab4382d2SGreg Kroah-Hartman 
713ab4382d2SGreg Kroah-Hartman 	channel = ZILOG_CHANNEL_FROM_PORT(&up->port);
714ab4382d2SGreg Kroah-Hartman 
715ab4382d2SGreg Kroah-Hartman 	__ip22zilog_reset(up);
716ab4382d2SGreg Kroah-Hartman 
717ab4382d2SGreg Kroah-Hartman 	__load_zsregs(channel, up->curregs);
718ab4382d2SGreg Kroah-Hartman 	/* set master interrupt enable */
719ab4382d2SGreg Kroah-Hartman 	write_zsreg(channel, R9, up->curregs[R9]);
720ab4382d2SGreg Kroah-Hartman 	up->prev_status = readb(&channel->control);
721ab4382d2SGreg Kroah-Hartman 
722ab4382d2SGreg Kroah-Hartman 	/* Enable receiver and transmitter.  */
723ab4382d2SGreg Kroah-Hartman 	up->curregs[R3] |= RxENAB;
724ab4382d2SGreg Kroah-Hartman 	up->curregs[R5] |= TxENAB;
725ab4382d2SGreg Kroah-Hartman 
726ab4382d2SGreg Kroah-Hartman 	up->curregs[R1] |= EXT_INT_ENAB | INT_ALL_Rx | TxINT_ENAB;
727ab4382d2SGreg Kroah-Hartman 	ip22zilog_maybe_update_regs(up, channel);
728ab4382d2SGreg Kroah-Hartman }
729ab4382d2SGreg Kroah-Hartman 
ip22zilog_startup(struct uart_port * port)730ab4382d2SGreg Kroah-Hartman static int ip22zilog_startup(struct uart_port *port)
731ab4382d2SGreg Kroah-Hartman {
732ab4382d2SGreg Kroah-Hartman 	struct uart_ip22zilog_port *up = UART_ZILOG(port);
733ab4382d2SGreg Kroah-Hartman 	unsigned long flags;
734ab4382d2SGreg Kroah-Hartman 
735ab4382d2SGreg Kroah-Hartman 	if (ZS_IS_CONS(up))
736ab4382d2SGreg Kroah-Hartman 		return 0;
737ab4382d2SGreg Kroah-Hartman 
738ab4382d2SGreg Kroah-Hartman 	spin_lock_irqsave(&port->lock, flags);
739ab4382d2SGreg Kroah-Hartman 	__ip22zilog_startup(up);
740ab4382d2SGreg Kroah-Hartman 	spin_unlock_irqrestore(&port->lock, flags);
741ab4382d2SGreg Kroah-Hartman 	return 0;
742ab4382d2SGreg Kroah-Hartman }
743ab4382d2SGreg Kroah-Hartman 
744ab4382d2SGreg Kroah-Hartman /*
745ab4382d2SGreg Kroah-Hartman  * The test for ZS_IS_CONS is explained by the following e-mail:
746ab4382d2SGreg Kroah-Hartman  *****
747ab4382d2SGreg Kroah-Hartman  * From: Russell King <rmk@arm.linux.org.uk>
748ab4382d2SGreg Kroah-Hartman  * Date: Sun, 8 Dec 2002 10:18:38 +0000
749ab4382d2SGreg Kroah-Hartman  *
750ab4382d2SGreg Kroah-Hartman  * On Sun, Dec 08, 2002 at 02:43:36AM -0500, Pete Zaitcev wrote:
751ab4382d2SGreg Kroah-Hartman  * > I boot my 2.5 boxes using "console=ttyS0,9600" argument,
752ab4382d2SGreg Kroah-Hartman  * > and I noticed that something is not right with reference
753ab4382d2SGreg Kroah-Hartman  * > counting in this case. It seems that when the console
754ab4382d2SGreg Kroah-Hartman  * > is open by kernel initially, this is not accounted
755ab4382d2SGreg Kroah-Hartman  * > as an open, and uart_startup is not called.
756ab4382d2SGreg Kroah-Hartman  *
757ab4382d2SGreg Kroah-Hartman  * That is correct.  We are unable to call uart_startup when the serial
758ab4382d2SGreg Kroah-Hartman  * console is initialised because it may need to allocate memory (as
759ab4382d2SGreg Kroah-Hartman  * request_irq does) and the memory allocators may not have been
760ab4382d2SGreg Kroah-Hartman  * initialised.
761ab4382d2SGreg Kroah-Hartman  *
762ab4382d2SGreg Kroah-Hartman  * 1. initialise the port into a state where it can send characters in the
763ab4382d2SGreg Kroah-Hartman  *    console write method.
764ab4382d2SGreg Kroah-Hartman  *
765ab4382d2SGreg Kroah-Hartman  * 2. don't do the actual hardware shutdown in your shutdown() method (but
766ab4382d2SGreg Kroah-Hartman  *    do the normal software shutdown - ie, free irqs etc)
767ab4382d2SGreg Kroah-Hartman  *****
768ab4382d2SGreg Kroah-Hartman  */
ip22zilog_shutdown(struct uart_port * port)769ab4382d2SGreg Kroah-Hartman static void ip22zilog_shutdown(struct uart_port *port)
770ab4382d2SGreg Kroah-Hartman {
771ab4382d2SGreg Kroah-Hartman 	struct uart_ip22zilog_port *up = UART_ZILOG(port);
772ab4382d2SGreg Kroah-Hartman 	struct zilog_channel *channel;
773ab4382d2SGreg Kroah-Hartman 	unsigned long flags;
774ab4382d2SGreg Kroah-Hartman 
775ab4382d2SGreg Kroah-Hartman 	if (ZS_IS_CONS(up))
776ab4382d2SGreg Kroah-Hartman 		return;
777ab4382d2SGreg Kroah-Hartman 
778ab4382d2SGreg Kroah-Hartman 	spin_lock_irqsave(&port->lock, flags);
779ab4382d2SGreg Kroah-Hartman 
780ab4382d2SGreg Kroah-Hartman 	channel = ZILOG_CHANNEL_FROM_PORT(port);
781ab4382d2SGreg Kroah-Hartman 
782ab4382d2SGreg Kroah-Hartman 	/* Disable receiver and transmitter.  */
783ab4382d2SGreg Kroah-Hartman 	up->curregs[R3] &= ~RxENAB;
784ab4382d2SGreg Kroah-Hartman 	up->curregs[R5] &= ~TxENAB;
785ab4382d2SGreg Kroah-Hartman 
786ab4382d2SGreg Kroah-Hartman 	/* Disable all interrupts and BRK assertion.  */
787ab4382d2SGreg Kroah-Hartman 	up->curregs[R1] &= ~(EXT_INT_ENAB | TxINT_ENAB | RxINT_MASK);
788ab4382d2SGreg Kroah-Hartman 	up->curregs[R5] &= ~SND_BRK;
789ab4382d2SGreg Kroah-Hartman 	ip22zilog_maybe_update_regs(up, channel);
790ab4382d2SGreg Kroah-Hartman 
791ab4382d2SGreg Kroah-Hartman 	spin_unlock_irqrestore(&port->lock, flags);
792ab4382d2SGreg Kroah-Hartman }
793ab4382d2SGreg Kroah-Hartman 
794ab4382d2SGreg Kroah-Hartman /* Shared by TTY driver and serial console setup.  The port lock is held
795ab4382d2SGreg Kroah-Hartman  * and local interrupts are disabled.
796ab4382d2SGreg Kroah-Hartman  */
797ab4382d2SGreg Kroah-Hartman static void
ip22zilog_convert_to_zs(struct uart_ip22zilog_port * up,unsigned int cflag,unsigned int iflag,int brg)798ab4382d2SGreg Kroah-Hartman ip22zilog_convert_to_zs(struct uart_ip22zilog_port *up, unsigned int cflag,
799ab4382d2SGreg Kroah-Hartman 		       unsigned int iflag, int brg)
800ab4382d2SGreg Kroah-Hartman {
801ab4382d2SGreg Kroah-Hartman 
802ab4382d2SGreg Kroah-Hartman 	up->curregs[R10] = NRZ;
803ab4382d2SGreg Kroah-Hartman 	up->curregs[R11] = TCBR | RCBR;
804ab4382d2SGreg Kroah-Hartman 
805ab4382d2SGreg Kroah-Hartman 	/* Program BAUD and clock source. */
806ab4382d2SGreg Kroah-Hartman 	up->curregs[R4] &= ~XCLK_MASK;
807ab4382d2SGreg Kroah-Hartman 	up->curregs[R4] |= X16CLK;
808ab4382d2SGreg Kroah-Hartman 	up->curregs[R12] = brg & 0xff;
809ab4382d2SGreg Kroah-Hartman 	up->curregs[R13] = (brg >> 8) & 0xff;
810ab4382d2SGreg Kroah-Hartman 	up->curregs[R14] = BRENAB;
811ab4382d2SGreg Kroah-Hartman 
812ab4382d2SGreg Kroah-Hartman 	/* Character size, stop bits, and parity. */
813ab4382d2SGreg Kroah-Hartman 	up->curregs[3] &= ~RxN_MASK;
814ab4382d2SGreg Kroah-Hartman 	up->curregs[5] &= ~TxN_MASK;
815ab4382d2SGreg Kroah-Hartman 	switch (cflag & CSIZE) {
816ab4382d2SGreg Kroah-Hartman 	case CS5:
817ab4382d2SGreg Kroah-Hartman 		up->curregs[3] |= Rx5;
818ab4382d2SGreg Kroah-Hartman 		up->curregs[5] |= Tx5;
819ab4382d2SGreg Kroah-Hartman 		up->parity_mask = 0x1f;
820ab4382d2SGreg Kroah-Hartman 		break;
821ab4382d2SGreg Kroah-Hartman 	case CS6:
822ab4382d2SGreg Kroah-Hartman 		up->curregs[3] |= Rx6;
823ab4382d2SGreg Kroah-Hartman 		up->curregs[5] |= Tx6;
824ab4382d2SGreg Kroah-Hartman 		up->parity_mask = 0x3f;
825ab4382d2SGreg Kroah-Hartman 		break;
826ab4382d2SGreg Kroah-Hartman 	case CS7:
827ab4382d2SGreg Kroah-Hartman 		up->curregs[3] |= Rx7;
828ab4382d2SGreg Kroah-Hartman 		up->curregs[5] |= Tx7;
829ab4382d2SGreg Kroah-Hartman 		up->parity_mask = 0x7f;
830ab4382d2SGreg Kroah-Hartman 		break;
831ab4382d2SGreg Kroah-Hartman 	case CS8:
832ab4382d2SGreg Kroah-Hartman 	default:
833ab4382d2SGreg Kroah-Hartman 		up->curregs[3] |= Rx8;
834ab4382d2SGreg Kroah-Hartman 		up->curregs[5] |= Tx8;
835ab4382d2SGreg Kroah-Hartman 		up->parity_mask = 0xff;
836ab4382d2SGreg Kroah-Hartman 		break;
837fc811472SJoe Perches 	}
838ab4382d2SGreg Kroah-Hartman 	up->curregs[4] &= ~0x0c;
839ab4382d2SGreg Kroah-Hartman 	if (cflag & CSTOPB)
840ab4382d2SGreg Kroah-Hartman 		up->curregs[4] |= SB2;
841ab4382d2SGreg Kroah-Hartman 	else
842ab4382d2SGreg Kroah-Hartman 		up->curregs[4] |= SB1;
843ab4382d2SGreg Kroah-Hartman 	if (cflag & PARENB)
844ab4382d2SGreg Kroah-Hartman 		up->curregs[4] |= PAR_ENAB;
845ab4382d2SGreg Kroah-Hartman 	else
846ab4382d2SGreg Kroah-Hartman 		up->curregs[4] &= ~PAR_ENAB;
847ab4382d2SGreg Kroah-Hartman 	if (!(cflag & PARODD))
848ab4382d2SGreg Kroah-Hartman 		up->curregs[4] |= PAR_EVEN;
849ab4382d2SGreg Kroah-Hartman 	else
850ab4382d2SGreg Kroah-Hartman 		up->curregs[4] &= ~PAR_EVEN;
851ab4382d2SGreg Kroah-Hartman 
852ab4382d2SGreg Kroah-Hartman 	up->port.read_status_mask = Rx_OVR;
853ab4382d2SGreg Kroah-Hartman 	if (iflag & INPCK)
854ab4382d2SGreg Kroah-Hartman 		up->port.read_status_mask |= CRC_ERR | PAR_ERR;
855ef8b9ddcSPeter Hurley 	if (iflag & (IGNBRK | BRKINT | PARMRK))
856ab4382d2SGreg Kroah-Hartman 		up->port.read_status_mask |= BRK_ABRT;
857ab4382d2SGreg Kroah-Hartman 
858ab4382d2SGreg Kroah-Hartman 	up->port.ignore_status_mask = 0;
859ab4382d2SGreg Kroah-Hartman 	if (iflag & IGNPAR)
860ab4382d2SGreg Kroah-Hartman 		up->port.ignore_status_mask |= CRC_ERR | PAR_ERR;
861ab4382d2SGreg Kroah-Hartman 	if (iflag & IGNBRK) {
862ab4382d2SGreg Kroah-Hartman 		up->port.ignore_status_mask |= BRK_ABRT;
863ab4382d2SGreg Kroah-Hartman 		if (iflag & IGNPAR)
864ab4382d2SGreg Kroah-Hartman 			up->port.ignore_status_mask |= Rx_OVR;
865ab4382d2SGreg Kroah-Hartman 	}
866ab4382d2SGreg Kroah-Hartman 
867ab4382d2SGreg Kroah-Hartman 	if ((cflag & CREAD) == 0)
868ab4382d2SGreg Kroah-Hartman 		up->port.ignore_status_mask = 0xff;
869ab4382d2SGreg Kroah-Hartman }
870ab4382d2SGreg Kroah-Hartman 
871ab4382d2SGreg Kroah-Hartman /* The port lock is not held.  */
872ab4382d2SGreg Kroah-Hartman static void
ip22zilog_set_termios(struct uart_port * port,struct ktermios * termios,const struct ktermios * old)873ab4382d2SGreg Kroah-Hartman ip22zilog_set_termios(struct uart_port *port, struct ktermios *termios,
874bec5b814SIlpo Järvinen 		      const struct ktermios *old)
875ab4382d2SGreg Kroah-Hartman {
8762413b320SFabian Frederick 	struct uart_ip22zilog_port *up =
8772413b320SFabian Frederick 		container_of(port, struct uart_ip22zilog_port, port);
878ab4382d2SGreg Kroah-Hartman 	unsigned long flags;
879ab4382d2SGreg Kroah-Hartman 	int baud, brg;
880ab4382d2SGreg Kroah-Hartman 
881ab4382d2SGreg Kroah-Hartman 	baud = uart_get_baud_rate(port, termios, old, 1200, 76800);
882ab4382d2SGreg Kroah-Hartman 
883ab4382d2SGreg Kroah-Hartman 	spin_lock_irqsave(&up->port.lock, flags);
884ab4382d2SGreg Kroah-Hartman 
885ab4382d2SGreg Kroah-Hartman 	brg = BPS_TO_BRG(baud, ZS_CLOCK / ZS_CLOCK_DIVISOR);
886ab4382d2SGreg Kroah-Hartman 
887ab4382d2SGreg Kroah-Hartman 	ip22zilog_convert_to_zs(up, termios->c_cflag, termios->c_iflag, brg);
888ab4382d2SGreg Kroah-Hartman 
889ab4382d2SGreg Kroah-Hartman 	if (UART_ENABLE_MS(&up->port, termios->c_cflag))
890ab4382d2SGreg Kroah-Hartman 		up->flags |= IP22ZILOG_FLAG_MODEM_STATUS;
891ab4382d2SGreg Kroah-Hartman 	else
892ab4382d2SGreg Kroah-Hartman 		up->flags &= ~IP22ZILOG_FLAG_MODEM_STATUS;
893ab4382d2SGreg Kroah-Hartman 
894ab4382d2SGreg Kroah-Hartman 	ip22zilog_maybe_update_regs(up, ZILOG_CHANNEL_FROM_PORT(port));
895ab4382d2SGreg Kroah-Hartman 	uart_update_timeout(port, termios->c_cflag, baud);
896ab4382d2SGreg Kroah-Hartman 
897ab4382d2SGreg Kroah-Hartman 	spin_unlock_irqrestore(&up->port.lock, flags);
898ab4382d2SGreg Kroah-Hartman }
899ab4382d2SGreg Kroah-Hartman 
ip22zilog_type(struct uart_port * port)900ab4382d2SGreg Kroah-Hartman static const char *ip22zilog_type(struct uart_port *port)
901ab4382d2SGreg Kroah-Hartman {
902ab4382d2SGreg Kroah-Hartman 	return "IP22-Zilog";
903ab4382d2SGreg Kroah-Hartman }
904ab4382d2SGreg Kroah-Hartman 
905ab4382d2SGreg Kroah-Hartman /* We do not request/release mappings of the registers here, this
906ab4382d2SGreg Kroah-Hartman  * happens at early serial probe time.
907ab4382d2SGreg Kroah-Hartman  */
ip22zilog_release_port(struct uart_port * port)908ab4382d2SGreg Kroah-Hartman static void ip22zilog_release_port(struct uart_port *port)
909ab4382d2SGreg Kroah-Hartman {
910ab4382d2SGreg Kroah-Hartman }
911ab4382d2SGreg Kroah-Hartman 
ip22zilog_request_port(struct uart_port * port)912ab4382d2SGreg Kroah-Hartman static int ip22zilog_request_port(struct uart_port *port)
913ab4382d2SGreg Kroah-Hartman {
914ab4382d2SGreg Kroah-Hartman 	return 0;
915ab4382d2SGreg Kroah-Hartman }
916ab4382d2SGreg Kroah-Hartman 
917ab4382d2SGreg Kroah-Hartman /* These do not need to do anything interesting either.  */
ip22zilog_config_port(struct uart_port * port,int flags)918ab4382d2SGreg Kroah-Hartman static void ip22zilog_config_port(struct uart_port *port, int flags)
919ab4382d2SGreg Kroah-Hartman {
920ab4382d2SGreg Kroah-Hartman }
921ab4382d2SGreg Kroah-Hartman 
922ab4382d2SGreg Kroah-Hartman /* We do not support letting the user mess with the divisor, IRQ, etc. */
ip22zilog_verify_port(struct uart_port * port,struct serial_struct * ser)923ab4382d2SGreg Kroah-Hartman static int ip22zilog_verify_port(struct uart_port *port, struct serial_struct *ser)
924ab4382d2SGreg Kroah-Hartman {
925ab4382d2SGreg Kroah-Hartman 	return -EINVAL;
926ab4382d2SGreg Kroah-Hartman }
927ab4382d2SGreg Kroah-Hartman 
9282331e068SBhumika Goyal static const struct uart_ops ip22zilog_pops = {
929ab4382d2SGreg Kroah-Hartman 	.tx_empty	=	ip22zilog_tx_empty,
930ab4382d2SGreg Kroah-Hartman 	.set_mctrl	=	ip22zilog_set_mctrl,
931ab4382d2SGreg Kroah-Hartman 	.get_mctrl	=	ip22zilog_get_mctrl,
932ab4382d2SGreg Kroah-Hartman 	.stop_tx	=	ip22zilog_stop_tx,
933ab4382d2SGreg Kroah-Hartman 	.start_tx	=	ip22zilog_start_tx,
934ab4382d2SGreg Kroah-Hartman 	.stop_rx	=	ip22zilog_stop_rx,
935ab4382d2SGreg Kroah-Hartman 	.enable_ms	=	ip22zilog_enable_ms,
936ab4382d2SGreg Kroah-Hartman 	.break_ctl	=	ip22zilog_break_ctl,
937ab4382d2SGreg Kroah-Hartman 	.startup	=	ip22zilog_startup,
938ab4382d2SGreg Kroah-Hartman 	.shutdown	=	ip22zilog_shutdown,
939ab4382d2SGreg Kroah-Hartman 	.set_termios	=	ip22zilog_set_termios,
940ab4382d2SGreg Kroah-Hartman 	.type		=	ip22zilog_type,
941ab4382d2SGreg Kroah-Hartman 	.release_port	=	ip22zilog_release_port,
942ab4382d2SGreg Kroah-Hartman 	.request_port	=	ip22zilog_request_port,
943ab4382d2SGreg Kroah-Hartman 	.config_port	=	ip22zilog_config_port,
944ab4382d2SGreg Kroah-Hartman 	.verify_port	=	ip22zilog_verify_port,
945ab4382d2SGreg Kroah-Hartman };
946ab4382d2SGreg Kroah-Hartman 
947ab4382d2SGreg Kroah-Hartman static struct uart_ip22zilog_port *ip22zilog_port_table;
948ab4382d2SGreg Kroah-Hartman static struct zilog_layout **ip22zilog_chip_regs;
949ab4382d2SGreg Kroah-Hartman 
950ab4382d2SGreg Kroah-Hartman static struct uart_ip22zilog_port *ip22zilog_irq_chain;
951ab4382d2SGreg Kroah-Hartman static int zilog_irq = -1;
952ab4382d2SGreg Kroah-Hartman 
alloc_one_table(unsigned long size)953ab4382d2SGreg Kroah-Hartman static void * __init alloc_one_table(unsigned long size)
954ab4382d2SGreg Kroah-Hartman {
955ab4382d2SGreg Kroah-Hartman 	return kzalloc(size, GFP_KERNEL);
956ab4382d2SGreg Kroah-Hartman }
957ab4382d2SGreg Kroah-Hartman 
ip22zilog_alloc_tables(void)958ab4382d2SGreg Kroah-Hartman static void __init ip22zilog_alloc_tables(void)
959ab4382d2SGreg Kroah-Hartman {
960ab4382d2SGreg Kroah-Hartman 	ip22zilog_port_table = (struct uart_ip22zilog_port *)
961ab4382d2SGreg Kroah-Hartman 		alloc_one_table(NUM_CHANNELS * sizeof(struct uart_ip22zilog_port));
962ab4382d2SGreg Kroah-Hartman 	ip22zilog_chip_regs = (struct zilog_layout **)
963ab4382d2SGreg Kroah-Hartman 		alloc_one_table(NUM_IP22ZILOG * sizeof(struct zilog_layout *));
964ab4382d2SGreg Kroah-Hartman 
965ab4382d2SGreg Kroah-Hartman 	if (ip22zilog_port_table == NULL || ip22zilog_chip_regs == NULL) {
966ab4382d2SGreg Kroah-Hartman 		panic("IP22-Zilog: Cannot allocate IP22-Zilog tables.");
967ab4382d2SGreg Kroah-Hartman 	}
968ab4382d2SGreg Kroah-Hartman }
969ab4382d2SGreg Kroah-Hartman 
970ab4382d2SGreg Kroah-Hartman /* Get the address of the registers for IP22-Zilog instance CHIP.  */
get_zs(int chip)971ab4382d2SGreg Kroah-Hartman static struct zilog_layout * __init get_zs(int chip)
972ab4382d2SGreg Kroah-Hartman {
973ab4382d2SGreg Kroah-Hartman 	unsigned long base;
974ab4382d2SGreg Kroah-Hartman 
975ab4382d2SGreg Kroah-Hartman 	if (chip < 0 || chip >= NUM_IP22ZILOG) {
976ab4382d2SGreg Kroah-Hartman 		panic("IP22-Zilog: Illegal chip number %d in get_zs.", chip);
977ab4382d2SGreg Kroah-Hartman 	}
978ab4382d2SGreg Kroah-Hartman 
979ab4382d2SGreg Kroah-Hartman 	/* Not probe-able, hard code it. */
980ab4382d2SGreg Kroah-Hartman 	base = (unsigned long) &sgioc->uart;
981ab4382d2SGreg Kroah-Hartman 
982ab4382d2SGreg Kroah-Hartman 	zilog_irq = SGI_SERIAL_IRQ;
983ab4382d2SGreg Kroah-Hartman 	request_mem_region(base, 8, "IP22-Zilog");
984ab4382d2SGreg Kroah-Hartman 
985ab4382d2SGreg Kroah-Hartman 	return (struct zilog_layout *) base;
986ab4382d2SGreg Kroah-Hartman }
987ab4382d2SGreg Kroah-Hartman 
988ab4382d2SGreg Kroah-Hartman #define ZS_PUT_CHAR_MAX_DELAY	2000	/* 10 ms */
989ab4382d2SGreg Kroah-Hartman 
990ab4382d2SGreg Kroah-Hartman #ifdef CONFIG_SERIAL_IP22_ZILOG_CONSOLE
ip22zilog_put_char(struct uart_port * port,unsigned char ch)9913f8bab17SJiri Slaby static void ip22zilog_put_char(struct uart_port *port, unsigned char ch)
992ab4382d2SGreg Kroah-Hartman {
993ab4382d2SGreg Kroah-Hartman 	struct zilog_channel *channel = ZILOG_CHANNEL_FROM_PORT(port);
994ab4382d2SGreg Kroah-Hartman 	int loops = ZS_PUT_CHAR_MAX_DELAY;
995ab4382d2SGreg Kroah-Hartman 
996ab4382d2SGreg Kroah-Hartman 	/* This is a timed polling loop so do not switch the explicit
997ab4382d2SGreg Kroah-Hartman 	 * udelay with ZSDELAY as that is a NOP on some platforms.  -DaveM
998ab4382d2SGreg Kroah-Hartman 	 */
999ab4382d2SGreg Kroah-Hartman 	do {
1000ab4382d2SGreg Kroah-Hartman 		unsigned char val = readb(&channel->control);
1001ab4382d2SGreg Kroah-Hartman 		if (val & Tx_BUF_EMP) {
1002ab4382d2SGreg Kroah-Hartman 			ZSDELAY();
1003ab4382d2SGreg Kroah-Hartman 			break;
1004ab4382d2SGreg Kroah-Hartman 		}
1005ab4382d2SGreg Kroah-Hartman 		udelay(5);
1006ab4382d2SGreg Kroah-Hartman 	} while (--loops);
1007ab4382d2SGreg Kroah-Hartman 
1008ab4382d2SGreg Kroah-Hartman 	writeb(ch, &channel->data);
1009ab4382d2SGreg Kroah-Hartman 	ZSDELAY();
1010ab4382d2SGreg Kroah-Hartman 	ZS_WSYNC(channel);
1011ab4382d2SGreg Kroah-Hartman }
1012ab4382d2SGreg Kroah-Hartman 
1013ab4382d2SGreg Kroah-Hartman static void
ip22zilog_console_write(struct console * con,const char * s,unsigned int count)1014ab4382d2SGreg Kroah-Hartman ip22zilog_console_write(struct console *con, const char *s, unsigned int count)
1015ab4382d2SGreg Kroah-Hartman {
1016ab4382d2SGreg Kroah-Hartman 	struct uart_ip22zilog_port *up = &ip22zilog_port_table[con->index];
1017ab4382d2SGreg Kroah-Hartman 	unsigned long flags;
1018ab4382d2SGreg Kroah-Hartman 
1019ab4382d2SGreg Kroah-Hartman 	spin_lock_irqsave(&up->port.lock, flags);
1020ab4382d2SGreg Kroah-Hartman 	uart_console_write(&up->port, s, count, ip22zilog_put_char);
1021ab4382d2SGreg Kroah-Hartman 	udelay(2);
1022ab4382d2SGreg Kroah-Hartman 	spin_unlock_irqrestore(&up->port.lock, flags);
1023ab4382d2SGreg Kroah-Hartman }
1024ab4382d2SGreg Kroah-Hartman 
ip22zilog_console_setup(struct console * con,char * options)1025ab4382d2SGreg Kroah-Hartman static int __init ip22zilog_console_setup(struct console *con, char *options)
1026ab4382d2SGreg Kroah-Hartman {
1027ab4382d2SGreg Kroah-Hartman 	struct uart_ip22zilog_port *up = &ip22zilog_port_table[con->index];
1028ab4382d2SGreg Kroah-Hartman 	unsigned long flags;
1029ab4382d2SGreg Kroah-Hartman 	int baud = 9600, bits = 8;
1030ab4382d2SGreg Kroah-Hartman 	int parity = 'n';
1031ab4382d2SGreg Kroah-Hartman 	int flow = 'n';
1032ab4382d2SGreg Kroah-Hartman 
1033ab4382d2SGreg Kroah-Hartman 	up->flags |= IP22ZILOG_FLAG_IS_CONS;
1034ab4382d2SGreg Kroah-Hartman 
1035ab4382d2SGreg Kroah-Hartman 	printk(KERN_INFO "Console: ttyS%d (IP22-Zilog)\n", con->index);
1036ab4382d2SGreg Kroah-Hartman 
1037ab4382d2SGreg Kroah-Hartman 	spin_lock_irqsave(&up->port.lock, flags);
1038ab4382d2SGreg Kroah-Hartman 
1039ab4382d2SGreg Kroah-Hartman 	up->curregs[R15] |= BRKIE;
1040ab4382d2SGreg Kroah-Hartman 
1041ab4382d2SGreg Kroah-Hartman 	__ip22zilog_startup(up);
1042ab4382d2SGreg Kroah-Hartman 
1043ab4382d2SGreg Kroah-Hartman 	spin_unlock_irqrestore(&up->port.lock, flags);
1044ab4382d2SGreg Kroah-Hartman 
1045ab4382d2SGreg Kroah-Hartman 	if (options)
1046ab4382d2SGreg Kroah-Hartman 		uart_parse_options(options, &baud, &parity, &bits, &flow);
1047ab4382d2SGreg Kroah-Hartman 	return uart_set_options(&up->port, con, baud, parity, bits, flow);
1048ab4382d2SGreg Kroah-Hartman }
1049ab4382d2SGreg Kroah-Hartman 
1050ab4382d2SGreg Kroah-Hartman static struct uart_driver ip22zilog_reg;
1051ab4382d2SGreg Kroah-Hartman 
1052ab4382d2SGreg Kroah-Hartman static struct console ip22zilog_console = {
1053ab4382d2SGreg Kroah-Hartman 	.name	=	"ttyS",
1054ab4382d2SGreg Kroah-Hartman 	.write	=	ip22zilog_console_write,
1055ab4382d2SGreg Kroah-Hartman 	.device	=	uart_console_device,
1056ab4382d2SGreg Kroah-Hartman 	.setup	=	ip22zilog_console_setup,
1057ab4382d2SGreg Kroah-Hartman 	.flags	=	CON_PRINTBUFFER,
1058ab4382d2SGreg Kroah-Hartman 	.index	=	-1,
1059ab4382d2SGreg Kroah-Hartman 	.data	=	&ip22zilog_reg,
1060ab4382d2SGreg Kroah-Hartman };
1061ab4382d2SGreg Kroah-Hartman #endif /* CONFIG_SERIAL_IP22_ZILOG_CONSOLE */
1062ab4382d2SGreg Kroah-Hartman 
1063ab4382d2SGreg Kroah-Hartman static struct uart_driver ip22zilog_reg = {
1064ab4382d2SGreg Kroah-Hartman 	.owner		= THIS_MODULE,
1065ab4382d2SGreg Kroah-Hartman 	.driver_name	= "serial",
1066ab4382d2SGreg Kroah-Hartman 	.dev_name	= "ttyS",
1067ab4382d2SGreg Kroah-Hartman 	.major		= TTY_MAJOR,
1068ab4382d2SGreg Kroah-Hartman 	.minor		= 64,
1069ab4382d2SGreg Kroah-Hartman 	.nr		= NUM_CHANNELS,
1070ab4382d2SGreg Kroah-Hartman #ifdef CONFIG_SERIAL_IP22_ZILOG_CONSOLE
1071ab4382d2SGreg Kroah-Hartman 	.cons		= &ip22zilog_console,
1072ab4382d2SGreg Kroah-Hartman #endif
1073ab4382d2SGreg Kroah-Hartman };
1074ab4382d2SGreg Kroah-Hartman 
ip22zilog_prepare(void)1075ab4382d2SGreg Kroah-Hartman static void __init ip22zilog_prepare(void)
1076ab4382d2SGreg Kroah-Hartman {
107779307e05SDmitry Safonov 	unsigned char sysrq_on = IS_ENABLED(CONFIG_SERIAL_IP22_ZILOG_CONSOLE);
1078ab4382d2SGreg Kroah-Hartman 	struct uart_ip22zilog_port *up;
1079ab4382d2SGreg Kroah-Hartman 	struct zilog_layout *rp;
1080ab4382d2SGreg Kroah-Hartman 	int channel, chip;
1081ab4382d2SGreg Kroah-Hartman 
1082ab4382d2SGreg Kroah-Hartman 	/*
1083ab4382d2SGreg Kroah-Hartman 	 * Temporary fix.
1084ab4382d2SGreg Kroah-Hartman 	 */
1085ab4382d2SGreg Kroah-Hartman 	for (channel = 0; channel < NUM_CHANNELS; channel++)
1086ab4382d2SGreg Kroah-Hartman 		spin_lock_init(&ip22zilog_port_table[channel].port.lock);
1087ab4382d2SGreg Kroah-Hartman 
1088ab4382d2SGreg Kroah-Hartman 	ip22zilog_irq_chain = &ip22zilog_port_table[NUM_CHANNELS - 1];
1089ab4382d2SGreg Kroah-Hartman         up = &ip22zilog_port_table[0];
1090ab4382d2SGreg Kroah-Hartman 	for (channel = NUM_CHANNELS - 1 ; channel > 0; channel--)
1091ab4382d2SGreg Kroah-Hartman 		up[channel].next = &up[channel - 1];
1092ab4382d2SGreg Kroah-Hartman 	up[channel].next = NULL;
1093ab4382d2SGreg Kroah-Hartman 
1094ab4382d2SGreg Kroah-Hartman 	for (chip = 0; chip < NUM_IP22ZILOG; chip++) {
1095ab4382d2SGreg Kroah-Hartman 		if (!ip22zilog_chip_regs[chip]) {
1096ab4382d2SGreg Kroah-Hartman 			ip22zilog_chip_regs[chip] = rp = get_zs(chip);
1097ab4382d2SGreg Kroah-Hartman 
1098ab4382d2SGreg Kroah-Hartman 			up[(chip * 2) + 0].port.membase = (char *) &rp->channelB;
1099ab4382d2SGreg Kroah-Hartman 			up[(chip * 2) + 1].port.membase = (char *) &rp->channelA;
1100ab4382d2SGreg Kroah-Hartman 
1101ab4382d2SGreg Kroah-Hartman 			/* In theory mapbase is the physical address ...  */
1102ab4382d2SGreg Kroah-Hartman 			up[(chip * 2) + 0].port.mapbase =
1103ab4382d2SGreg Kroah-Hartman 				(unsigned long) ioremap((unsigned long) &rp->channelB, 8);
1104ab4382d2SGreg Kroah-Hartman 			up[(chip * 2) + 1].port.mapbase =
1105ab4382d2SGreg Kroah-Hartman 				(unsigned long) ioremap((unsigned long) &rp->channelA, 8);
1106ab4382d2SGreg Kroah-Hartman 		}
1107ab4382d2SGreg Kroah-Hartman 
1108ab4382d2SGreg Kroah-Hartman 		/* Channel A */
1109ab4382d2SGreg Kroah-Hartman 		up[(chip * 2) + 0].port.iotype = UPIO_MEM;
1110ab4382d2SGreg Kroah-Hartman 		up[(chip * 2) + 0].port.irq = zilog_irq;
1111ab4382d2SGreg Kroah-Hartman 		up[(chip * 2) + 0].port.uartclk = ZS_CLOCK;
1112ab4382d2SGreg Kroah-Hartman 		up[(chip * 2) + 0].port.fifosize = 1;
111379307e05SDmitry Safonov 		up[(chip * 2) + 0].port.has_sysrq = sysrq_on;
1114ab4382d2SGreg Kroah-Hartman 		up[(chip * 2) + 0].port.ops = &ip22zilog_pops;
1115ab4382d2SGreg Kroah-Hartman 		up[(chip * 2) + 0].port.type = PORT_IP22ZILOG;
1116ab4382d2SGreg Kroah-Hartman 		up[(chip * 2) + 0].port.flags = 0;
1117ab4382d2SGreg Kroah-Hartman 		up[(chip * 2) + 0].port.line = (chip * 2) + 0;
1118ab4382d2SGreg Kroah-Hartman 		up[(chip * 2) + 0].flags = 0;
1119ab4382d2SGreg Kroah-Hartman 
1120ab4382d2SGreg Kroah-Hartman 		/* Channel B */
1121ab4382d2SGreg Kroah-Hartman 		up[(chip * 2) + 1].port.iotype = UPIO_MEM;
1122ab4382d2SGreg Kroah-Hartman 		up[(chip * 2) + 1].port.irq = zilog_irq;
1123ab4382d2SGreg Kroah-Hartman 		up[(chip * 2) + 1].port.uartclk = ZS_CLOCK;
1124ab4382d2SGreg Kroah-Hartman 		up[(chip * 2) + 1].port.fifosize = 1;
112579307e05SDmitry Safonov 		up[(chip * 2) + 1].port.has_sysrq = sysrq_on;
1126ab4382d2SGreg Kroah-Hartman 		up[(chip * 2) + 1].port.ops = &ip22zilog_pops;
1127ab4382d2SGreg Kroah-Hartman 		up[(chip * 2) + 1].port.type = PORT_IP22ZILOG;
1128ab4382d2SGreg Kroah-Hartman 		up[(chip * 2) + 1].port.line = (chip * 2) + 1;
1129ab4382d2SGreg Kroah-Hartman 		up[(chip * 2) + 1].flags |= IP22ZILOG_FLAG_IS_CHANNEL_A;
1130ab4382d2SGreg Kroah-Hartman 	}
1131ab4382d2SGreg Kroah-Hartman 
1132ab4382d2SGreg Kroah-Hartman 	for (channel = 0; channel < NUM_CHANNELS; channel++) {
1133ab4382d2SGreg Kroah-Hartman 		struct uart_ip22zilog_port *up = &ip22zilog_port_table[channel];
1134ab4382d2SGreg Kroah-Hartman 		int brg;
1135ab4382d2SGreg Kroah-Hartman 
1136ab4382d2SGreg Kroah-Hartman 		/* Normal serial TTY. */
1137ab4382d2SGreg Kroah-Hartman 		up->parity_mask = 0xff;
1138ab4382d2SGreg Kroah-Hartman 		up->curregs[R1] = EXT_INT_ENAB | INT_ALL_Rx | TxINT_ENAB;
1139ab4382d2SGreg Kroah-Hartman 		up->curregs[R4] = PAR_EVEN | X16CLK | SB1;
1140ab4382d2SGreg Kroah-Hartman 		up->curregs[R3] = RxENAB | Rx8;
1141ab4382d2SGreg Kroah-Hartman 		up->curregs[R5] = TxENAB | Tx8;
1142ab4382d2SGreg Kroah-Hartman 		up->curregs[R9] = NV | MIE;
1143ab4382d2SGreg Kroah-Hartman 		up->curregs[R10] = NRZ;
1144ab4382d2SGreg Kroah-Hartman 		up->curregs[R11] = TCBR | RCBR;
1145ab4382d2SGreg Kroah-Hartman 		brg = BPS_TO_BRG(9600, ZS_CLOCK / ZS_CLOCK_DIVISOR);
1146ab4382d2SGreg Kroah-Hartman 		up->curregs[R12] = (brg & 0xff);
1147ab4382d2SGreg Kroah-Hartman 		up->curregs[R13] = (brg >> 8) & 0xff;
1148ab4382d2SGreg Kroah-Hartman 		up->curregs[R14] = BRENAB;
1149ab4382d2SGreg Kroah-Hartman 	}
1150ab4382d2SGreg Kroah-Hartman }
1151ab4382d2SGreg Kroah-Hartman 
ip22zilog_ports_init(void)1152ab4382d2SGreg Kroah-Hartman static int __init ip22zilog_ports_init(void)
1153ab4382d2SGreg Kroah-Hartman {
1154ab4382d2SGreg Kroah-Hartman 	int ret;
1155ab4382d2SGreg Kroah-Hartman 
1156ab4382d2SGreg Kroah-Hartman 	printk(KERN_INFO "Serial: IP22 Zilog driver (%d chips).\n", NUM_IP22ZILOG);
1157ab4382d2SGreg Kroah-Hartman 
1158ab4382d2SGreg Kroah-Hartman 	ip22zilog_prepare();
1159ab4382d2SGreg Kroah-Hartman 
1160ab4382d2SGreg Kroah-Hartman 	if (request_irq(zilog_irq, ip22zilog_interrupt, 0,
1161ab4382d2SGreg Kroah-Hartman 			"IP22-Zilog", ip22zilog_irq_chain)) {
1162ab4382d2SGreg Kroah-Hartman 		panic("IP22-Zilog: Unable to register zs interrupt handler.\n");
1163ab4382d2SGreg Kroah-Hartman 	}
1164ab4382d2SGreg Kroah-Hartman 
1165ab4382d2SGreg Kroah-Hartman 	ret = uart_register_driver(&ip22zilog_reg);
1166ab4382d2SGreg Kroah-Hartman 	if (ret == 0) {
1167ab4382d2SGreg Kroah-Hartman 		int i;
1168ab4382d2SGreg Kroah-Hartman 
1169ab4382d2SGreg Kroah-Hartman 		for (i = 0; i < NUM_CHANNELS; i++) {
1170ab4382d2SGreg Kroah-Hartman 			struct uart_ip22zilog_port *up = &ip22zilog_port_table[i];
1171ab4382d2SGreg Kroah-Hartman 
1172ab4382d2SGreg Kroah-Hartman 			uart_add_one_port(&ip22zilog_reg, &up->port);
1173ab4382d2SGreg Kroah-Hartman 		}
1174ab4382d2SGreg Kroah-Hartman 	}
1175ab4382d2SGreg Kroah-Hartman 
1176ab4382d2SGreg Kroah-Hartman 	return ret;
1177ab4382d2SGreg Kroah-Hartman }
1178ab4382d2SGreg Kroah-Hartman 
ip22zilog_init(void)1179ab4382d2SGreg Kroah-Hartman static int __init ip22zilog_init(void)
1180ab4382d2SGreg Kroah-Hartman {
1181ab4382d2SGreg Kroah-Hartman 	/* IP22 Zilog setup is hard coded, no probing to do.  */
1182ab4382d2SGreg Kroah-Hartman 	ip22zilog_alloc_tables();
1183ab4382d2SGreg Kroah-Hartman 	ip22zilog_ports_init();
1184ab4382d2SGreg Kroah-Hartman 
1185ab4382d2SGreg Kroah-Hartman 	return 0;
1186ab4382d2SGreg Kroah-Hartman }
1187ab4382d2SGreg Kroah-Hartman 
ip22zilog_exit(void)1188ab4382d2SGreg Kroah-Hartman static void __exit ip22zilog_exit(void)
1189ab4382d2SGreg Kroah-Hartman {
1190ab4382d2SGreg Kroah-Hartman 	int i;
1191ab4382d2SGreg Kroah-Hartman 	struct uart_ip22zilog_port *up;
1192ab4382d2SGreg Kroah-Hartman 
1193ab4382d2SGreg Kroah-Hartman 	for (i = 0; i < NUM_CHANNELS; i++) {
1194ab4382d2SGreg Kroah-Hartman 		up = &ip22zilog_port_table[i];
1195ab4382d2SGreg Kroah-Hartman 
1196ab4382d2SGreg Kroah-Hartman 		uart_remove_one_port(&ip22zilog_reg, &up->port);
1197ab4382d2SGreg Kroah-Hartman 	}
1198ab4382d2SGreg Kroah-Hartman 
1199ab4382d2SGreg Kroah-Hartman 	/* Free IO mem */
1200ab4382d2SGreg Kroah-Hartman 	up = &ip22zilog_port_table[0];
1201ab4382d2SGreg Kroah-Hartman 	for (i = 0; i < NUM_IP22ZILOG; i++) {
1202ab4382d2SGreg Kroah-Hartman 		if (up[(i * 2) + 0].port.mapbase) {
1203ab4382d2SGreg Kroah-Hartman 		   iounmap((void*)up[(i * 2) + 0].port.mapbase);
1204ab4382d2SGreg Kroah-Hartman 		   up[(i * 2) + 0].port.mapbase = 0;
1205ab4382d2SGreg Kroah-Hartman 		}
1206ab4382d2SGreg Kroah-Hartman 		if (up[(i * 2) + 1].port.mapbase) {
1207ab4382d2SGreg Kroah-Hartman 			iounmap((void*)up[(i * 2) + 1].port.mapbase);
1208ab4382d2SGreg Kroah-Hartman 			up[(i * 2) + 1].port.mapbase = 0;
1209ab4382d2SGreg Kroah-Hartman 		}
1210ab4382d2SGreg Kroah-Hartman 	}
1211ab4382d2SGreg Kroah-Hartman 
1212ab4382d2SGreg Kroah-Hartman 	uart_unregister_driver(&ip22zilog_reg);
1213ab4382d2SGreg Kroah-Hartman }
1214ab4382d2SGreg Kroah-Hartman 
1215ab4382d2SGreg Kroah-Hartman module_init(ip22zilog_init);
1216ab4382d2SGreg Kroah-Hartman module_exit(ip22zilog_exit);
1217ab4382d2SGreg Kroah-Hartman 
1218ab4382d2SGreg Kroah-Hartman /* David wrote it but I'm to blame for the bugs ...  */
1219ab4382d2SGreg Kroah-Hartman MODULE_AUTHOR("Ralf Baechle <ralf@linux-mips.org>");
1220ab4382d2SGreg Kroah-Hartman MODULE_DESCRIPTION("SGI Zilog serial port driver");
1221ab4382d2SGreg Kroah-Hartman MODULE_LICENSE("GPL");
1222