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