xref: /openbmc/linux/drivers/mmc/core/sdio_uart.c (revision 95713967)
12874c5fdSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
2f397c8d8SUlf Hansson /*
3f397c8d8SUlf Hansson  * SDIO UART/GPS driver
4f397c8d8SUlf Hansson  *
5f397c8d8SUlf Hansson  * Based on drivers/serial/8250.c and drivers/serial/serial_core.c
6f397c8d8SUlf Hansson  * by Russell King.
7f397c8d8SUlf Hansson  *
8f397c8d8SUlf Hansson  * Author:	Nicolas Pitre
9f397c8d8SUlf Hansson  * Created:	June 15, 2007
10f397c8d8SUlf Hansson  * Copyright:	MontaVista Software, Inc.
11f397c8d8SUlf Hansson  */
12f397c8d8SUlf Hansson 
13f397c8d8SUlf Hansson /*
14f397c8d8SUlf Hansson  * Note: Although this driver assumes a 16550A-like UART implementation,
15f397c8d8SUlf Hansson  * it is not possible to leverage the common 8250/16550 driver, nor the
16f397c8d8SUlf Hansson  * core UART infrastructure, as they assumes direct access to the hardware
17f397c8d8SUlf Hansson  * registers, often under a spinlock.  This is not possible in the SDIO
18f397c8d8SUlf Hansson  * context as SDIO access functions must be able to sleep.
19f397c8d8SUlf Hansson  *
20f397c8d8SUlf Hansson  * Because we need to lock the SDIO host to ensure an exclusive access to
21f397c8d8SUlf Hansson  * the card, we simply rely on that lock to also prevent and serialize
22f397c8d8SUlf Hansson  * concurrent access to the same port.
23f397c8d8SUlf Hansson  */
24f397c8d8SUlf Hansson 
25f397c8d8SUlf Hansson #include <linux/module.h>
26f397c8d8SUlf Hansson #include <linux/init.h>
27f397c8d8SUlf Hansson #include <linux/kernel.h>
28f397c8d8SUlf Hansson #include <linux/sched.h>
29f397c8d8SUlf Hansson #include <linux/mutex.h>
30f397c8d8SUlf Hansson #include <linux/seq_file.h>
31834119f5SJiri Slaby #include <linux/serial.h>
32f397c8d8SUlf Hansson #include <linux/serial_reg.h>
33f397c8d8SUlf Hansson #include <linux/circ_buf.h>
34f397c8d8SUlf Hansson #include <linux/tty.h>
35f397c8d8SUlf Hansson #include <linux/tty_flip.h>
36f397c8d8SUlf Hansson #include <linux/kfifo.h>
37f397c8d8SUlf Hansson #include <linux/slab.h>
38f397c8d8SUlf Hansson 
39f397c8d8SUlf Hansson #include <linux/mmc/core.h>
40f397c8d8SUlf Hansson #include <linux/mmc/card.h>
41f397c8d8SUlf Hansson #include <linux/mmc/sdio_func.h>
42f397c8d8SUlf Hansson #include <linux/mmc/sdio_ids.h>
43f397c8d8SUlf Hansson 
44f397c8d8SUlf Hansson 
45f397c8d8SUlf Hansson #define UART_NR		8	/* Number of UARTs this driver can handle */
46f397c8d8SUlf Hansson 
47f397c8d8SUlf Hansson 
48f397c8d8SUlf Hansson #define FIFO_SIZE	PAGE_SIZE
49f397c8d8SUlf Hansson #define WAKEUP_CHARS	256
50f397c8d8SUlf Hansson 
51f397c8d8SUlf Hansson struct uart_icount {
52f397c8d8SUlf Hansson 	__u32	cts;
53f397c8d8SUlf Hansson 	__u32	dsr;
54f397c8d8SUlf Hansson 	__u32	rng;
55f397c8d8SUlf Hansson 	__u32	dcd;
56f397c8d8SUlf Hansson 	__u32	rx;
57f397c8d8SUlf Hansson 	__u32	tx;
58f397c8d8SUlf Hansson 	__u32	frame;
59f397c8d8SUlf Hansson 	__u32	overrun;
60f397c8d8SUlf Hansson 	__u32	parity;
61f397c8d8SUlf Hansson 	__u32	brk;
62f397c8d8SUlf Hansson };
63f397c8d8SUlf Hansson 
64f397c8d8SUlf Hansson struct sdio_uart_port {
65f397c8d8SUlf Hansson 	struct tty_port		port;
66f397c8d8SUlf Hansson 	unsigned int		index;
67f397c8d8SUlf Hansson 	struct sdio_func	*func;
68f397c8d8SUlf Hansson 	struct mutex		func_lock;
69f397c8d8SUlf Hansson 	struct task_struct	*in_sdio_uart_irq;
70f397c8d8SUlf Hansson 	unsigned int		regs_offset;
71f397c8d8SUlf Hansson 	struct kfifo		xmit_fifo;
72f397c8d8SUlf Hansson 	spinlock_t		write_lock;
73f397c8d8SUlf Hansson 	struct uart_icount	icount;
74f397c8d8SUlf Hansson 	unsigned int		uartclk;
75f397c8d8SUlf Hansson 	unsigned int		mctrl;
76f397c8d8SUlf Hansson 	unsigned int		rx_mctrl;
77f397c8d8SUlf Hansson 	unsigned int		read_status_mask;
78f397c8d8SUlf Hansson 	unsigned int		ignore_status_mask;
79f397c8d8SUlf Hansson 	unsigned char		x_char;
80f397c8d8SUlf Hansson 	unsigned char           ier;
81f397c8d8SUlf Hansson 	unsigned char           lcr;
82f397c8d8SUlf Hansson };
83f397c8d8SUlf Hansson 
84f397c8d8SUlf Hansson static struct sdio_uart_port *sdio_uart_table[UART_NR];
85f397c8d8SUlf Hansson static DEFINE_SPINLOCK(sdio_uart_table_lock);
86f397c8d8SUlf Hansson 
sdio_uart_add_port(struct sdio_uart_port * port)87f397c8d8SUlf Hansson static int sdio_uart_add_port(struct sdio_uart_port *port)
88f397c8d8SUlf Hansson {
89f397c8d8SUlf Hansson 	int index, ret = -EBUSY;
90f397c8d8SUlf Hansson 
91f397c8d8SUlf Hansson 	mutex_init(&port->func_lock);
92f397c8d8SUlf Hansson 	spin_lock_init(&port->write_lock);
93f397c8d8SUlf Hansson 	if (kfifo_alloc(&port->xmit_fifo, FIFO_SIZE, GFP_KERNEL))
94f397c8d8SUlf Hansson 		return -ENOMEM;
95f397c8d8SUlf Hansson 
96f397c8d8SUlf Hansson 	spin_lock(&sdio_uart_table_lock);
97f397c8d8SUlf Hansson 	for (index = 0; index < UART_NR; index++) {
98f397c8d8SUlf Hansson 		if (!sdio_uart_table[index]) {
99f397c8d8SUlf Hansson 			port->index = index;
100f397c8d8SUlf Hansson 			sdio_uart_table[index] = port;
101f397c8d8SUlf Hansson 			ret = 0;
102f397c8d8SUlf Hansson 			break;
103f397c8d8SUlf Hansson 		}
104f397c8d8SUlf Hansson 	}
105f397c8d8SUlf Hansson 	spin_unlock(&sdio_uart_table_lock);
106f397c8d8SUlf Hansson 
107f397c8d8SUlf Hansson 	return ret;
108f397c8d8SUlf Hansson }
109f397c8d8SUlf Hansson 
sdio_uart_port_get(unsigned index)110f397c8d8SUlf Hansson static struct sdio_uart_port *sdio_uart_port_get(unsigned index)
111f397c8d8SUlf Hansson {
112f397c8d8SUlf Hansson 	struct sdio_uart_port *port;
113f397c8d8SUlf Hansson 
114f397c8d8SUlf Hansson 	if (index >= UART_NR)
115f397c8d8SUlf Hansson 		return NULL;
116f397c8d8SUlf Hansson 
117f397c8d8SUlf Hansson 	spin_lock(&sdio_uart_table_lock);
118f397c8d8SUlf Hansson 	port = sdio_uart_table[index];
119f397c8d8SUlf Hansson 	if (port)
120f397c8d8SUlf Hansson 		tty_port_get(&port->port);
121f397c8d8SUlf Hansson 	spin_unlock(&sdio_uart_table_lock);
122f397c8d8SUlf Hansson 
123f397c8d8SUlf Hansson 	return port;
124f397c8d8SUlf Hansson }
125f397c8d8SUlf Hansson 
sdio_uart_port_put(struct sdio_uart_port * port)126f397c8d8SUlf Hansson static void sdio_uart_port_put(struct sdio_uart_port *port)
127f397c8d8SUlf Hansson {
128f397c8d8SUlf Hansson 	tty_port_put(&port->port);
129f397c8d8SUlf Hansson }
130f397c8d8SUlf Hansson 
sdio_uart_port_remove(struct sdio_uart_port * port)131f397c8d8SUlf Hansson static void sdio_uart_port_remove(struct sdio_uart_port *port)
132f397c8d8SUlf Hansson {
133f397c8d8SUlf Hansson 	struct sdio_func *func;
134f397c8d8SUlf Hansson 
135f397c8d8SUlf Hansson 	spin_lock(&sdio_uart_table_lock);
136f397c8d8SUlf Hansson 	sdio_uart_table[port->index] = NULL;
137f397c8d8SUlf Hansson 	spin_unlock(&sdio_uart_table_lock);
138f397c8d8SUlf Hansson 
139f397c8d8SUlf Hansson 	/*
140f397c8d8SUlf Hansson 	 * We're killing a port that potentially still is in use by
141f397c8d8SUlf Hansson 	 * the tty layer. Be careful to prevent any further access
142f397c8d8SUlf Hansson 	 * to the SDIO function and arrange for the tty layer to
143f397c8d8SUlf Hansson 	 * give up on that port ASAP.
144f397c8d8SUlf Hansson 	 * Beware: the lock ordering is critical.
145f397c8d8SUlf Hansson 	 */
146f397c8d8SUlf Hansson 	mutex_lock(&port->port.mutex);
147f397c8d8SUlf Hansson 	mutex_lock(&port->func_lock);
148f397c8d8SUlf Hansson 	func = port->func;
149f397c8d8SUlf Hansson 	sdio_claim_host(func);
150f397c8d8SUlf Hansson 	port->func = NULL;
151f397c8d8SUlf Hansson 	mutex_unlock(&port->func_lock);
152f397c8d8SUlf Hansson 	/* tty_hangup is async so is this safe as is ?? */
153f397c8d8SUlf Hansson 	tty_port_tty_hangup(&port->port, false);
154f397c8d8SUlf Hansson 	mutex_unlock(&port->port.mutex);
155f397c8d8SUlf Hansson 	sdio_release_irq(func);
156f397c8d8SUlf Hansson 	sdio_disable_func(func);
157f397c8d8SUlf Hansson 	sdio_release_host(func);
158f397c8d8SUlf Hansson 
159f397c8d8SUlf Hansson 	sdio_uart_port_put(port);
160f397c8d8SUlf Hansson }
161f397c8d8SUlf Hansson 
sdio_uart_claim_func(struct sdio_uart_port * port)162f397c8d8SUlf Hansson static int sdio_uart_claim_func(struct sdio_uart_port *port)
163f397c8d8SUlf Hansson {
164f397c8d8SUlf Hansson 	mutex_lock(&port->func_lock);
165f397c8d8SUlf Hansson 	if (unlikely(!port->func)) {
166f397c8d8SUlf Hansson 		mutex_unlock(&port->func_lock);
167f397c8d8SUlf Hansson 		return -ENODEV;
168f397c8d8SUlf Hansson 	}
169f397c8d8SUlf Hansson 	if (likely(port->in_sdio_uart_irq != current))
170f397c8d8SUlf Hansson 		sdio_claim_host(port->func);
171f397c8d8SUlf Hansson 	mutex_unlock(&port->func_lock);
172f397c8d8SUlf Hansson 	return 0;
173f397c8d8SUlf Hansson }
174f397c8d8SUlf Hansson 
sdio_uart_release_func(struct sdio_uart_port * port)175f397c8d8SUlf Hansson static inline void sdio_uart_release_func(struct sdio_uart_port *port)
176f397c8d8SUlf Hansson {
177f397c8d8SUlf Hansson 	if (likely(port->in_sdio_uart_irq != current))
178f397c8d8SUlf Hansson 		sdio_release_host(port->func);
179f397c8d8SUlf Hansson }
180f397c8d8SUlf Hansson 
sdio_in(struct sdio_uart_port * port,int offset)181f397c8d8SUlf Hansson static inline unsigned int sdio_in(struct sdio_uart_port *port, int offset)
182f397c8d8SUlf Hansson {
183f397c8d8SUlf Hansson 	unsigned char c;
184f397c8d8SUlf Hansson 	c = sdio_readb(port->func, port->regs_offset + offset, NULL);
185f397c8d8SUlf Hansson 	return c;
186f397c8d8SUlf Hansson }
187f397c8d8SUlf Hansson 
sdio_out(struct sdio_uart_port * port,int offset,int value)188f397c8d8SUlf Hansson static inline void sdio_out(struct sdio_uart_port *port, int offset, int value)
189f397c8d8SUlf Hansson {
190f397c8d8SUlf Hansson 	sdio_writeb(port->func, value, port->regs_offset + offset, NULL);
191f397c8d8SUlf Hansson }
192f397c8d8SUlf Hansson 
sdio_uart_get_mctrl(struct sdio_uart_port * port)193f397c8d8SUlf Hansson static unsigned int sdio_uart_get_mctrl(struct sdio_uart_port *port)
194f397c8d8SUlf Hansson {
195f397c8d8SUlf Hansson 	unsigned char status;
196f397c8d8SUlf Hansson 	unsigned int ret;
197f397c8d8SUlf Hansson 
198f397c8d8SUlf Hansson 	/* FIXME: What stops this losing the delta bits and breaking
199f397c8d8SUlf Hansson 	   sdio_uart_check_modem_status ? */
200f397c8d8SUlf Hansson 	status = sdio_in(port, UART_MSR);
201f397c8d8SUlf Hansson 
202f397c8d8SUlf Hansson 	ret = 0;
203f397c8d8SUlf Hansson 	if (status & UART_MSR_DCD)
204f397c8d8SUlf Hansson 		ret |= TIOCM_CAR;
205f397c8d8SUlf Hansson 	if (status & UART_MSR_RI)
206f397c8d8SUlf Hansson 		ret |= TIOCM_RNG;
207f397c8d8SUlf Hansson 	if (status & UART_MSR_DSR)
208f397c8d8SUlf Hansson 		ret |= TIOCM_DSR;
209f397c8d8SUlf Hansson 	if (status & UART_MSR_CTS)
210f397c8d8SUlf Hansson 		ret |= TIOCM_CTS;
211f397c8d8SUlf Hansson 	return ret;
212f397c8d8SUlf Hansson }
213f397c8d8SUlf Hansson 
sdio_uart_write_mctrl(struct sdio_uart_port * port,unsigned int mctrl)214f397c8d8SUlf Hansson static void sdio_uart_write_mctrl(struct sdio_uart_port *port,
215f397c8d8SUlf Hansson 				  unsigned int mctrl)
216f397c8d8SUlf Hansson {
217f397c8d8SUlf Hansson 	unsigned char mcr = 0;
218f397c8d8SUlf Hansson 
219f397c8d8SUlf Hansson 	if (mctrl & TIOCM_RTS)
220f397c8d8SUlf Hansson 		mcr |= UART_MCR_RTS;
221f397c8d8SUlf Hansson 	if (mctrl & TIOCM_DTR)
222f397c8d8SUlf Hansson 		mcr |= UART_MCR_DTR;
223f397c8d8SUlf Hansson 	if (mctrl & TIOCM_OUT1)
224f397c8d8SUlf Hansson 		mcr |= UART_MCR_OUT1;
225f397c8d8SUlf Hansson 	if (mctrl & TIOCM_OUT2)
226f397c8d8SUlf Hansson 		mcr |= UART_MCR_OUT2;
227f397c8d8SUlf Hansson 	if (mctrl & TIOCM_LOOP)
228f397c8d8SUlf Hansson 		mcr |= UART_MCR_LOOP;
229f397c8d8SUlf Hansson 
230f397c8d8SUlf Hansson 	sdio_out(port, UART_MCR, mcr);
231f397c8d8SUlf Hansson }
232f397c8d8SUlf Hansson 
sdio_uart_update_mctrl(struct sdio_uart_port * port,unsigned int set,unsigned int clear)233f397c8d8SUlf Hansson static inline void sdio_uart_update_mctrl(struct sdio_uart_port *port,
234f397c8d8SUlf Hansson 					  unsigned int set, unsigned int clear)
235f397c8d8SUlf Hansson {
236f397c8d8SUlf Hansson 	unsigned int old;
237f397c8d8SUlf Hansson 
238f397c8d8SUlf Hansson 	old = port->mctrl;
239f397c8d8SUlf Hansson 	port->mctrl = (old & ~clear) | set;
240f397c8d8SUlf Hansson 	if (old != port->mctrl)
241f397c8d8SUlf Hansson 		sdio_uart_write_mctrl(port, port->mctrl);
242f397c8d8SUlf Hansson }
243f397c8d8SUlf Hansson 
244f397c8d8SUlf Hansson #define sdio_uart_set_mctrl(port, x)	sdio_uart_update_mctrl(port, x, 0)
245f397c8d8SUlf Hansson #define sdio_uart_clear_mctrl(port, x)	sdio_uart_update_mctrl(port, 0, x)
246f397c8d8SUlf Hansson 
sdio_uart_change_speed(struct sdio_uart_port * port,struct ktermios * termios,const struct ktermios * old)247f397c8d8SUlf Hansson static void sdio_uart_change_speed(struct sdio_uart_port *port,
248f397c8d8SUlf Hansson 				   struct ktermios *termios,
249a8c11c15SIlpo Järvinen 				   const struct ktermios *old)
250f397c8d8SUlf Hansson {
251f397c8d8SUlf Hansson 	unsigned char cval, fcr = 0;
252f397c8d8SUlf Hansson 	unsigned int baud, quot;
253f397c8d8SUlf Hansson 
254834119f5SJiri Slaby 	cval = UART_LCR_WLEN(tty_get_char_size(termios->c_cflag));
255f397c8d8SUlf Hansson 
256f397c8d8SUlf Hansson 	if (termios->c_cflag & CSTOPB)
257f397c8d8SUlf Hansson 		cval |= UART_LCR_STOP;
258f397c8d8SUlf Hansson 	if (termios->c_cflag & PARENB)
259f397c8d8SUlf Hansson 		cval |= UART_LCR_PARITY;
260f397c8d8SUlf Hansson 	if (!(termios->c_cflag & PARODD))
261f397c8d8SUlf Hansson 		cval |= UART_LCR_EPAR;
262f397c8d8SUlf Hansson 
263f397c8d8SUlf Hansson 	for (;;) {
264f397c8d8SUlf Hansson 		baud = tty_termios_baud_rate(termios);
265f397c8d8SUlf Hansson 		if (baud == 0)
266f397c8d8SUlf Hansson 			baud = 9600;  /* Special case: B0 rate. */
267f397c8d8SUlf Hansson 		if (baud <= port->uartclk)
268f397c8d8SUlf Hansson 			break;
269f397c8d8SUlf Hansson 		/*
270f397c8d8SUlf Hansson 		 * Oops, the quotient was zero.  Try again with the old
271f397c8d8SUlf Hansson 		 * baud rate if possible, otherwise default to 9600.
272f397c8d8SUlf Hansson 		 */
273f397c8d8SUlf Hansson 		termios->c_cflag &= ~CBAUD;
274f397c8d8SUlf Hansson 		if (old) {
275f397c8d8SUlf Hansson 			termios->c_cflag |= old->c_cflag & CBAUD;
276f397c8d8SUlf Hansson 			old = NULL;
277f397c8d8SUlf Hansson 		} else
278f397c8d8SUlf Hansson 			termios->c_cflag |= B9600;
279f397c8d8SUlf Hansson 	}
280f397c8d8SUlf Hansson 	quot = (2 * port->uartclk + baud) / (2 * baud);
281f397c8d8SUlf Hansson 
282f397c8d8SUlf Hansson 	if (baud < 2400)
283f397c8d8SUlf Hansson 		fcr = UART_FCR_ENABLE_FIFO | UART_FCR_TRIGGER_1;
284f397c8d8SUlf Hansson 	else
285f397c8d8SUlf Hansson 		fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_10;
286f397c8d8SUlf Hansson 
287f397c8d8SUlf Hansson 	port->read_status_mask = UART_LSR_OE | UART_LSR_THRE | UART_LSR_DR;
288f397c8d8SUlf Hansson 	if (termios->c_iflag & INPCK)
289f397c8d8SUlf Hansson 		port->read_status_mask |= UART_LSR_FE | UART_LSR_PE;
290f397c8d8SUlf Hansson 	if (termios->c_iflag & (BRKINT | PARMRK))
291f397c8d8SUlf Hansson 		port->read_status_mask |= UART_LSR_BI;
292f397c8d8SUlf Hansson 
293f397c8d8SUlf Hansson 	/*
294f397c8d8SUlf Hansson 	 * Characters to ignore
295f397c8d8SUlf Hansson 	 */
296f397c8d8SUlf Hansson 	port->ignore_status_mask = 0;
297f397c8d8SUlf Hansson 	if (termios->c_iflag & IGNPAR)
298f397c8d8SUlf Hansson 		port->ignore_status_mask |= UART_LSR_PE | UART_LSR_FE;
299f397c8d8SUlf Hansson 	if (termios->c_iflag & IGNBRK) {
300f397c8d8SUlf Hansson 		port->ignore_status_mask |= UART_LSR_BI;
301f397c8d8SUlf Hansson 		/*
302f397c8d8SUlf Hansson 		 * If we're ignoring parity and break indicators,
303f397c8d8SUlf Hansson 		 * ignore overruns too (for real raw support).
304f397c8d8SUlf Hansson 		 */
305f397c8d8SUlf Hansson 		if (termios->c_iflag & IGNPAR)
306f397c8d8SUlf Hansson 			port->ignore_status_mask |= UART_LSR_OE;
307f397c8d8SUlf Hansson 	}
308f397c8d8SUlf Hansson 
309f397c8d8SUlf Hansson 	/*
310f397c8d8SUlf Hansson 	 * ignore all characters if CREAD is not set
311f397c8d8SUlf Hansson 	 */
312f397c8d8SUlf Hansson 	if ((termios->c_cflag & CREAD) == 0)
313f397c8d8SUlf Hansson 		port->ignore_status_mask |= UART_LSR_DR;
314f397c8d8SUlf Hansson 
315f397c8d8SUlf Hansson 	/*
316f397c8d8SUlf Hansson 	 * CTS flow control flag and modem status interrupts
317f397c8d8SUlf Hansson 	 */
318f397c8d8SUlf Hansson 	port->ier &= ~UART_IER_MSI;
319f397c8d8SUlf Hansson 	if ((termios->c_cflag & CRTSCTS) || !(termios->c_cflag & CLOCAL))
320f397c8d8SUlf Hansson 		port->ier |= UART_IER_MSI;
321f397c8d8SUlf Hansson 
322f397c8d8SUlf Hansson 	port->lcr = cval;
323f397c8d8SUlf Hansson 
324f397c8d8SUlf Hansson 	sdio_out(port, UART_IER, port->ier);
325f397c8d8SUlf Hansson 	sdio_out(port, UART_LCR, cval | UART_LCR_DLAB);
326f397c8d8SUlf Hansson 	sdio_out(port, UART_DLL, quot & 0xff);
327f397c8d8SUlf Hansson 	sdio_out(port, UART_DLM, quot >> 8);
328f397c8d8SUlf Hansson 	sdio_out(port, UART_LCR, cval);
329f397c8d8SUlf Hansson 	sdio_out(port, UART_FCR, fcr);
330f397c8d8SUlf Hansson 
331f397c8d8SUlf Hansson 	sdio_uart_write_mctrl(port, port->mctrl);
332f397c8d8SUlf Hansson }
333f397c8d8SUlf Hansson 
sdio_uart_start_tx(struct sdio_uart_port * port)334f397c8d8SUlf Hansson static void sdio_uart_start_tx(struct sdio_uart_port *port)
335f397c8d8SUlf Hansson {
336f397c8d8SUlf Hansson 	if (!(port->ier & UART_IER_THRI)) {
337f397c8d8SUlf Hansson 		port->ier |= UART_IER_THRI;
338f397c8d8SUlf Hansson 		sdio_out(port, UART_IER, port->ier);
339f397c8d8SUlf Hansson 	}
340f397c8d8SUlf Hansson }
341f397c8d8SUlf Hansson 
sdio_uart_stop_tx(struct sdio_uart_port * port)342f397c8d8SUlf Hansson static void sdio_uart_stop_tx(struct sdio_uart_port *port)
343f397c8d8SUlf Hansson {
344f397c8d8SUlf Hansson 	if (port->ier & UART_IER_THRI) {
345f397c8d8SUlf Hansson 		port->ier &= ~UART_IER_THRI;
346f397c8d8SUlf Hansson 		sdio_out(port, UART_IER, port->ier);
347f397c8d8SUlf Hansson 	}
348f397c8d8SUlf Hansson }
349f397c8d8SUlf Hansson 
sdio_uart_stop_rx(struct sdio_uart_port * port)350f397c8d8SUlf Hansson static void sdio_uart_stop_rx(struct sdio_uart_port *port)
351f397c8d8SUlf Hansson {
352f397c8d8SUlf Hansson 	port->ier &= ~UART_IER_RLSI;
353f397c8d8SUlf Hansson 	port->read_status_mask &= ~UART_LSR_DR;
354f397c8d8SUlf Hansson 	sdio_out(port, UART_IER, port->ier);
355f397c8d8SUlf Hansson }
356f397c8d8SUlf Hansson 
sdio_uart_receive_chars(struct sdio_uart_port * port,unsigned int * status)357f397c8d8SUlf Hansson static void sdio_uart_receive_chars(struct sdio_uart_port *port,
358f397c8d8SUlf Hansson 				    unsigned int *status)
359f397c8d8SUlf Hansson {
360f397c8d8SUlf Hansson 	unsigned int ch, flag;
361f397c8d8SUlf Hansson 	int max_count = 256;
362f397c8d8SUlf Hansson 
363f397c8d8SUlf Hansson 	do {
364f397c8d8SUlf Hansson 		ch = sdio_in(port, UART_RX);
365f397c8d8SUlf Hansson 		flag = TTY_NORMAL;
366f397c8d8SUlf Hansson 		port->icount.rx++;
367f397c8d8SUlf Hansson 
368f397c8d8SUlf Hansson 		if (unlikely(*status & (UART_LSR_BI | UART_LSR_PE |
369f397c8d8SUlf Hansson 					UART_LSR_FE | UART_LSR_OE))) {
370f397c8d8SUlf Hansson 			/*
371f397c8d8SUlf Hansson 			 * For statistics only
372f397c8d8SUlf Hansson 			 */
373f397c8d8SUlf Hansson 			if (*status & UART_LSR_BI) {
374f397c8d8SUlf Hansson 				*status &= ~(UART_LSR_FE | UART_LSR_PE);
375f397c8d8SUlf Hansson 				port->icount.brk++;
376f397c8d8SUlf Hansson 			} else if (*status & UART_LSR_PE)
377f397c8d8SUlf Hansson 				port->icount.parity++;
378f397c8d8SUlf Hansson 			else if (*status & UART_LSR_FE)
379f397c8d8SUlf Hansson 				port->icount.frame++;
380f397c8d8SUlf Hansson 			if (*status & UART_LSR_OE)
381f397c8d8SUlf Hansson 				port->icount.overrun++;
382f397c8d8SUlf Hansson 
383f397c8d8SUlf Hansson 			/*
384f397c8d8SUlf Hansson 			 * Mask off conditions which should be ignored.
385f397c8d8SUlf Hansson 			 */
386f397c8d8SUlf Hansson 			*status &= port->read_status_mask;
387f397c8d8SUlf Hansson 			if (*status & UART_LSR_BI)
388f397c8d8SUlf Hansson 				flag = TTY_BREAK;
389f397c8d8SUlf Hansson 			else if (*status & UART_LSR_PE)
390f397c8d8SUlf Hansson 				flag = TTY_PARITY;
391f397c8d8SUlf Hansson 			else if (*status & UART_LSR_FE)
392f397c8d8SUlf Hansson 				flag = TTY_FRAME;
393f397c8d8SUlf Hansson 		}
394f397c8d8SUlf Hansson 
395f397c8d8SUlf Hansson 		if ((*status & port->ignore_status_mask & ~UART_LSR_OE) == 0)
396f397c8d8SUlf Hansson 			tty_insert_flip_char(&port->port, ch, flag);
397f397c8d8SUlf Hansson 
398f397c8d8SUlf Hansson 		/*
399f397c8d8SUlf Hansson 		 * Overrun is special.  Since it's reported immediately,
400f397c8d8SUlf Hansson 		 * it doesn't affect the current character.
401f397c8d8SUlf Hansson 		 */
402f397c8d8SUlf Hansson 		if (*status & ~port->ignore_status_mask & UART_LSR_OE)
403f397c8d8SUlf Hansson 			tty_insert_flip_char(&port->port, 0, TTY_OVERRUN);
404f397c8d8SUlf Hansson 
405f397c8d8SUlf Hansson 		*status = sdio_in(port, UART_LSR);
406f397c8d8SUlf Hansson 	} while ((*status & UART_LSR_DR) && (max_count-- > 0));
407f397c8d8SUlf Hansson 
408f397c8d8SUlf Hansson 	tty_flip_buffer_push(&port->port);
409f397c8d8SUlf Hansson }
410f397c8d8SUlf Hansson 
sdio_uart_transmit_chars(struct sdio_uart_port * port)411f397c8d8SUlf Hansson static void sdio_uart_transmit_chars(struct sdio_uart_port *port)
412f397c8d8SUlf Hansson {
413f397c8d8SUlf Hansson 	struct kfifo *xmit = &port->xmit_fifo;
414f397c8d8SUlf Hansson 	int count;
415f397c8d8SUlf Hansson 	struct tty_struct *tty;
416f397c8d8SUlf Hansson 	u8 iobuf[16];
417f397c8d8SUlf Hansson 	int len;
418f397c8d8SUlf Hansson 
419f397c8d8SUlf Hansson 	if (port->x_char) {
420f397c8d8SUlf Hansson 		sdio_out(port, UART_TX, port->x_char);
421f397c8d8SUlf Hansson 		port->icount.tx++;
422f397c8d8SUlf Hansson 		port->x_char = 0;
423f397c8d8SUlf Hansson 		return;
424f397c8d8SUlf Hansson 	}
425f397c8d8SUlf Hansson 
426f397c8d8SUlf Hansson 	tty = tty_port_tty_get(&port->port);
427f397c8d8SUlf Hansson 
428f397c8d8SUlf Hansson 	if (tty == NULL || !kfifo_len(xmit) ||
4296e94dbc7SJiri Slaby 				tty->flow.stopped || tty->hw_stopped) {
430f397c8d8SUlf Hansson 		sdio_uart_stop_tx(port);
431f397c8d8SUlf Hansson 		tty_kref_put(tty);
432f397c8d8SUlf Hansson 		return;
433f397c8d8SUlf Hansson 	}
434f397c8d8SUlf Hansson 
435f397c8d8SUlf Hansson 	len = kfifo_out_locked(xmit, iobuf, 16, &port->write_lock);
436f397c8d8SUlf Hansson 	for (count = 0; count < len; count++) {
437f397c8d8SUlf Hansson 		sdio_out(port, UART_TX, iobuf[count]);
438f397c8d8SUlf Hansson 		port->icount.tx++;
439f397c8d8SUlf Hansson 	}
440f397c8d8SUlf Hansson 
441f397c8d8SUlf Hansson 	len = kfifo_len(xmit);
442f397c8d8SUlf Hansson 	if (len < WAKEUP_CHARS) {
443f397c8d8SUlf Hansson 		tty_wakeup(tty);
444f397c8d8SUlf Hansson 		if (len == 0)
445f397c8d8SUlf Hansson 			sdio_uart_stop_tx(port);
446f397c8d8SUlf Hansson 	}
447f397c8d8SUlf Hansson 	tty_kref_put(tty);
448f397c8d8SUlf Hansson }
449f397c8d8SUlf Hansson 
sdio_uart_check_modem_status(struct sdio_uart_port * port)450f397c8d8SUlf Hansson static void sdio_uart_check_modem_status(struct sdio_uart_port *port)
451f397c8d8SUlf Hansson {
452f397c8d8SUlf Hansson 	int status;
453f397c8d8SUlf Hansson 	struct tty_struct *tty;
454f397c8d8SUlf Hansson 
455f397c8d8SUlf Hansson 	status = sdio_in(port, UART_MSR);
456f397c8d8SUlf Hansson 
457f397c8d8SUlf Hansson 	if ((status & UART_MSR_ANY_DELTA) == 0)
458f397c8d8SUlf Hansson 		return;
459f397c8d8SUlf Hansson 
460f397c8d8SUlf Hansson 	if (status & UART_MSR_TERI)
461f397c8d8SUlf Hansson 		port->icount.rng++;
462f397c8d8SUlf Hansson 	if (status & UART_MSR_DDSR)
463f397c8d8SUlf Hansson 		port->icount.dsr++;
464f397c8d8SUlf Hansson 	if (status & UART_MSR_DDCD) {
465f397c8d8SUlf Hansson 		port->icount.dcd++;
466f397c8d8SUlf Hansson 		/* DCD raise - wake for open */
467f397c8d8SUlf Hansson 		if (status & UART_MSR_DCD)
468f397c8d8SUlf Hansson 			wake_up_interruptible(&port->port.open_wait);
469f397c8d8SUlf Hansson 		else {
470f397c8d8SUlf Hansson 			/* DCD drop - hang up if tty attached */
471f397c8d8SUlf Hansson 			tty_port_tty_hangup(&port->port, false);
472f397c8d8SUlf Hansson 		}
473f397c8d8SUlf Hansson 	}
474f397c8d8SUlf Hansson 	if (status & UART_MSR_DCTS) {
475f397c8d8SUlf Hansson 		port->icount.cts++;
476f397c8d8SUlf Hansson 		tty = tty_port_tty_get(&port->port);
477f397c8d8SUlf Hansson 		if (tty && C_CRTSCTS(tty)) {
478f397c8d8SUlf Hansson 			int cts = (status & UART_MSR_CTS);
479f397c8d8SUlf Hansson 			if (tty->hw_stopped) {
480f397c8d8SUlf Hansson 				if (cts) {
481035173c9SIlpo Järvinen 					tty->hw_stopped = false;
482f397c8d8SUlf Hansson 					sdio_uart_start_tx(port);
483f397c8d8SUlf Hansson 					tty_wakeup(tty);
484f397c8d8SUlf Hansson 				}
485f397c8d8SUlf Hansson 			} else {
486f397c8d8SUlf Hansson 				if (!cts) {
487035173c9SIlpo Järvinen 					tty->hw_stopped = true;
488f397c8d8SUlf Hansson 					sdio_uart_stop_tx(port);
489f397c8d8SUlf Hansson 				}
490f397c8d8SUlf Hansson 			}
491f397c8d8SUlf Hansson 		}
492f397c8d8SUlf Hansson 		tty_kref_put(tty);
493f397c8d8SUlf Hansson 	}
494f397c8d8SUlf Hansson }
495f397c8d8SUlf Hansson 
496f397c8d8SUlf Hansson /*
497f397c8d8SUlf Hansson  * This handles the interrupt from one port.
498f397c8d8SUlf Hansson  */
sdio_uart_irq(struct sdio_func * func)499f397c8d8SUlf Hansson static void sdio_uart_irq(struct sdio_func *func)
500f397c8d8SUlf Hansson {
501f397c8d8SUlf Hansson 	struct sdio_uart_port *port = sdio_get_drvdata(func);
502f397c8d8SUlf Hansson 	unsigned int iir, lsr;
503f397c8d8SUlf Hansson 
504f397c8d8SUlf Hansson 	/*
505f397c8d8SUlf Hansson 	 * In a few places sdio_uart_irq() is called directly instead of
506f397c8d8SUlf Hansson 	 * waiting for the actual interrupt to be raised and the SDIO IRQ
507f397c8d8SUlf Hansson 	 * thread scheduled in order to reduce latency.  However, some
508f397c8d8SUlf Hansson 	 * interaction with the tty core may end up calling us back
509f397c8d8SUlf Hansson 	 * (serial echo, flow control, etc.) through those same places
510f397c8d8SUlf Hansson 	 * causing undesirable effects.  Let's stop the recursion here.
511f397c8d8SUlf Hansson 	 */
512f397c8d8SUlf Hansson 	if (unlikely(port->in_sdio_uart_irq == current))
513f397c8d8SUlf Hansson 		return;
514f397c8d8SUlf Hansson 
515f397c8d8SUlf Hansson 	iir = sdio_in(port, UART_IIR);
516f397c8d8SUlf Hansson 	if (iir & UART_IIR_NO_INT)
517f397c8d8SUlf Hansson 		return;
518f397c8d8SUlf Hansson 
519f397c8d8SUlf Hansson 	port->in_sdio_uart_irq = current;
520f397c8d8SUlf Hansson 	lsr = sdio_in(port, UART_LSR);
521f397c8d8SUlf Hansson 	if (lsr & UART_LSR_DR)
522f397c8d8SUlf Hansson 		sdio_uart_receive_chars(port, &lsr);
523f397c8d8SUlf Hansson 	sdio_uart_check_modem_status(port);
524f397c8d8SUlf Hansson 	if (lsr & UART_LSR_THRE)
525f397c8d8SUlf Hansson 		sdio_uart_transmit_chars(port);
526f397c8d8SUlf Hansson 	port->in_sdio_uart_irq = NULL;
527f397c8d8SUlf Hansson }
528f397c8d8SUlf Hansson 
uart_carrier_raised(struct tty_port * tport)529b300fb26SIlpo Järvinen static bool uart_carrier_raised(struct tty_port *tport)
530f397c8d8SUlf Hansson {
531f397c8d8SUlf Hansson 	struct sdio_uart_port *port =
532f397c8d8SUlf Hansson 			container_of(tport, struct sdio_uart_port, port);
533f397c8d8SUlf Hansson 	unsigned int ret = sdio_uart_claim_func(port);
534f397c8d8SUlf Hansson 	if (ret)	/* Missing hardware shouldn't block for carrier */
535f397c8d8SUlf Hansson 		return 1;
536f397c8d8SUlf Hansson 	ret = sdio_uart_get_mctrl(port);
537f397c8d8SUlf Hansson 	sdio_uart_release_func(port);
538b300fb26SIlpo Järvinen 
539b300fb26SIlpo Järvinen 	return ret & TIOCM_CAR;
540f397c8d8SUlf Hansson }
541f397c8d8SUlf Hansson 
542f397c8d8SUlf Hansson /**
543f397c8d8SUlf Hansson  *	uart_dtr_rts		-	 port helper to set uart signals
544f397c8d8SUlf Hansson  *	@tport: tty port to be updated
5455701cb8bSIlpo Järvinen  *	@active: set to turn on DTR/RTS
546f397c8d8SUlf Hansson  *
547f397c8d8SUlf Hansson  *	Called by the tty port helpers when the modem signals need to be
548f397c8d8SUlf Hansson  *	adjusted during an open, close and hangup.
549f397c8d8SUlf Hansson  */
550f397c8d8SUlf Hansson 
uart_dtr_rts(struct tty_port * tport,bool active)5515701cb8bSIlpo Järvinen static void uart_dtr_rts(struct tty_port *tport, bool active)
552f397c8d8SUlf Hansson {
553f397c8d8SUlf Hansson 	struct sdio_uart_port *port =
554f397c8d8SUlf Hansson 			container_of(tport, struct sdio_uart_port, port);
555f397c8d8SUlf Hansson 	int ret = sdio_uart_claim_func(port);
556f397c8d8SUlf Hansson 	if (ret)
557f397c8d8SUlf Hansson 		return;
5585701cb8bSIlpo Järvinen 	if (!active)
559f397c8d8SUlf Hansson 		sdio_uart_clear_mctrl(port, TIOCM_DTR | TIOCM_RTS);
560f397c8d8SUlf Hansson 	else
561f397c8d8SUlf Hansson 		sdio_uart_set_mctrl(port, TIOCM_DTR | TIOCM_RTS);
562f397c8d8SUlf Hansson 	sdio_uart_release_func(port);
563f397c8d8SUlf Hansson }
564f397c8d8SUlf Hansson 
565f397c8d8SUlf Hansson /**
566f397c8d8SUlf Hansson  *	sdio_uart_activate	-	start up hardware
567f397c8d8SUlf Hansson  *	@tport: tty port to activate
568f397c8d8SUlf Hansson  *	@tty: tty bound to this port
569f397c8d8SUlf Hansson  *
570f397c8d8SUlf Hansson  *	Activate a tty port. The port locking guarantees us this will be
571f397c8d8SUlf Hansson  *	run exactly once per set of opens, and if successful will see the
572f397c8d8SUlf Hansson  *	shutdown method run exactly once to match. Start up and shutdown are
573f397c8d8SUlf Hansson  *	protected from each other by the internal locking and will not run
574f397c8d8SUlf Hansson  *	at the same time even during a hangup event.
575f397c8d8SUlf Hansson  *
576f397c8d8SUlf Hansson  *	If we successfully start up the port we take an extra kref as we
577f397c8d8SUlf Hansson  *	will keep it around until shutdown when the kref is dropped.
578f397c8d8SUlf Hansson  */
579f397c8d8SUlf Hansson 
sdio_uart_activate(struct tty_port * tport,struct tty_struct * tty)580f397c8d8SUlf Hansson static int sdio_uart_activate(struct tty_port *tport, struct tty_struct *tty)
581f397c8d8SUlf Hansson {
582f397c8d8SUlf Hansson 	struct sdio_uart_port *port =
583f397c8d8SUlf Hansson 			container_of(tport, struct sdio_uart_port, port);
584f397c8d8SUlf Hansson 	int ret;
585f397c8d8SUlf Hansson 
586f397c8d8SUlf Hansson 	/*
587f397c8d8SUlf Hansson 	 * Set the TTY IO error marker - we will only clear this
588f397c8d8SUlf Hansson 	 * once we have successfully opened the port.
589f397c8d8SUlf Hansson 	 */
590f397c8d8SUlf Hansson 	set_bit(TTY_IO_ERROR, &tty->flags);
591f397c8d8SUlf Hansson 
592f397c8d8SUlf Hansson 	kfifo_reset(&port->xmit_fifo);
593f397c8d8SUlf Hansson 
594f397c8d8SUlf Hansson 	ret = sdio_uart_claim_func(port);
595f397c8d8SUlf Hansson 	if (ret)
596f397c8d8SUlf Hansson 		return ret;
597f397c8d8SUlf Hansson 	ret = sdio_enable_func(port->func);
598f397c8d8SUlf Hansson 	if (ret)
599f397c8d8SUlf Hansson 		goto err1;
600f397c8d8SUlf Hansson 	ret = sdio_claim_irq(port->func, sdio_uart_irq);
601f397c8d8SUlf Hansson 	if (ret)
602f397c8d8SUlf Hansson 		goto err2;
603f397c8d8SUlf Hansson 
604f397c8d8SUlf Hansson 	/*
605f397c8d8SUlf Hansson 	 * Clear the FIFO buffers and disable them.
606f397c8d8SUlf Hansson 	 * (they will be reenabled in sdio_change_speed())
607f397c8d8SUlf Hansson 	 */
608f397c8d8SUlf Hansson 	sdio_out(port, UART_FCR, UART_FCR_ENABLE_FIFO);
609f397c8d8SUlf Hansson 	sdio_out(port, UART_FCR, UART_FCR_ENABLE_FIFO |
610f397c8d8SUlf Hansson 		       UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT);
611f397c8d8SUlf Hansson 	sdio_out(port, UART_FCR, 0);
612f397c8d8SUlf Hansson 
613f397c8d8SUlf Hansson 	/*
614f397c8d8SUlf Hansson 	 * Clear the interrupt registers.
615f397c8d8SUlf Hansson 	 */
616f397c8d8SUlf Hansson 	(void) sdio_in(port, UART_LSR);
617f397c8d8SUlf Hansson 	(void) sdio_in(port, UART_RX);
618f397c8d8SUlf Hansson 	(void) sdio_in(port, UART_IIR);
619f397c8d8SUlf Hansson 	(void) sdio_in(port, UART_MSR);
620f397c8d8SUlf Hansson 
621f397c8d8SUlf Hansson 	/*
622f397c8d8SUlf Hansson 	 * Now, initialize the UART
623f397c8d8SUlf Hansson 	 */
624f397c8d8SUlf Hansson 	sdio_out(port, UART_LCR, UART_LCR_WLEN8);
625f397c8d8SUlf Hansson 
626f397c8d8SUlf Hansson 	port->ier = UART_IER_RLSI|UART_IER_RDI|UART_IER_RTOIE|UART_IER_UUE;
627f397c8d8SUlf Hansson 	port->mctrl = TIOCM_OUT2;
628f397c8d8SUlf Hansson 
629f397c8d8SUlf Hansson 	sdio_uart_change_speed(port, &tty->termios, NULL);
630f397c8d8SUlf Hansson 
631f397c8d8SUlf Hansson 	if (C_BAUD(tty))
632f397c8d8SUlf Hansson 		sdio_uart_set_mctrl(port, TIOCM_RTS | TIOCM_DTR);
633f397c8d8SUlf Hansson 
634f397c8d8SUlf Hansson 	if (C_CRTSCTS(tty))
635f397c8d8SUlf Hansson 		if (!(sdio_uart_get_mctrl(port) & TIOCM_CTS))
636035173c9SIlpo Järvinen 			tty->hw_stopped = true;
637f397c8d8SUlf Hansson 
638f397c8d8SUlf Hansson 	clear_bit(TTY_IO_ERROR, &tty->flags);
639f397c8d8SUlf Hansson 
640f397c8d8SUlf Hansson 	/* Kick the IRQ handler once while we're still holding the host lock */
641f397c8d8SUlf Hansson 	sdio_uart_irq(port->func);
642f397c8d8SUlf Hansson 
643f397c8d8SUlf Hansson 	sdio_uart_release_func(port);
644f397c8d8SUlf Hansson 	return 0;
645f397c8d8SUlf Hansson 
646f397c8d8SUlf Hansson err2:
647f397c8d8SUlf Hansson 	sdio_disable_func(port->func);
648f397c8d8SUlf Hansson err1:
649f397c8d8SUlf Hansson 	sdio_uart_release_func(port);
650f397c8d8SUlf Hansson 	return ret;
651f397c8d8SUlf Hansson }
652f397c8d8SUlf Hansson 
653f397c8d8SUlf Hansson /**
654f397c8d8SUlf Hansson  *	sdio_uart_shutdown	-	stop hardware
655f397c8d8SUlf Hansson  *	@tport: tty port to shut down
656f397c8d8SUlf Hansson  *
657f397c8d8SUlf Hansson  *	Deactivate a tty port. The port locking guarantees us this will be
658f397c8d8SUlf Hansson  *	run only if a successful matching activate already ran. The two are
659f397c8d8SUlf Hansson  *	protected from each other by the internal locking and will not run
660f397c8d8SUlf Hansson  *	at the same time even during a hangup event.
661f397c8d8SUlf Hansson  */
662f397c8d8SUlf Hansson 
sdio_uart_shutdown(struct tty_port * tport)663f397c8d8SUlf Hansson static void sdio_uart_shutdown(struct tty_port *tport)
664f397c8d8SUlf Hansson {
665f397c8d8SUlf Hansson 	struct sdio_uart_port *port =
666f397c8d8SUlf Hansson 			container_of(tport, struct sdio_uart_port, port);
667f397c8d8SUlf Hansson 	int ret;
668f397c8d8SUlf Hansson 
669f397c8d8SUlf Hansson 	ret = sdio_uart_claim_func(port);
670f397c8d8SUlf Hansson 	if (ret)
671f397c8d8SUlf Hansson 		return;
672f397c8d8SUlf Hansson 
673f397c8d8SUlf Hansson 	sdio_uart_stop_rx(port);
674f397c8d8SUlf Hansson 
675f397c8d8SUlf Hansson 	/* Disable interrupts from this port */
676f397c8d8SUlf Hansson 	sdio_release_irq(port->func);
677f397c8d8SUlf Hansson 	port->ier = 0;
678f397c8d8SUlf Hansson 	sdio_out(port, UART_IER, 0);
679f397c8d8SUlf Hansson 
680f397c8d8SUlf Hansson 	sdio_uart_clear_mctrl(port, TIOCM_OUT2);
681f397c8d8SUlf Hansson 
682f397c8d8SUlf Hansson 	/* Disable break condition and FIFOs. */
683f397c8d8SUlf Hansson 	port->lcr &= ~UART_LCR_SBC;
684f397c8d8SUlf Hansson 	sdio_out(port, UART_LCR, port->lcr);
685f397c8d8SUlf Hansson 	sdio_out(port, UART_FCR, UART_FCR_ENABLE_FIFO |
686f397c8d8SUlf Hansson 				 UART_FCR_CLEAR_RCVR |
687f397c8d8SUlf Hansson 				 UART_FCR_CLEAR_XMIT);
688f397c8d8SUlf Hansson 	sdio_out(port, UART_FCR, 0);
689f397c8d8SUlf Hansson 
690f397c8d8SUlf Hansson 	sdio_disable_func(port->func);
691f397c8d8SUlf Hansson 
692f397c8d8SUlf Hansson 	sdio_uart_release_func(port);
693f397c8d8SUlf Hansson }
694f397c8d8SUlf Hansson 
sdio_uart_port_destroy(struct tty_port * tport)695f397c8d8SUlf Hansson static void sdio_uart_port_destroy(struct tty_port *tport)
696f397c8d8SUlf Hansson {
697f397c8d8SUlf Hansson 	struct sdio_uart_port *port =
698f397c8d8SUlf Hansson 		container_of(tport, struct sdio_uart_port, port);
699f397c8d8SUlf Hansson 	kfifo_free(&port->xmit_fifo);
700f397c8d8SUlf Hansson 	kfree(port);
701f397c8d8SUlf Hansson }
702f397c8d8SUlf Hansson 
703f397c8d8SUlf Hansson /**
704f397c8d8SUlf Hansson  *	sdio_uart_install	-	install method
705f397c8d8SUlf Hansson  *	@driver: the driver in use (sdio_uart in our case)
706f397c8d8SUlf Hansson  *	@tty: the tty being bound
707f397c8d8SUlf Hansson  *
708f397c8d8SUlf Hansson  *	Look up and bind the tty and the driver together. Initialize
709f397c8d8SUlf Hansson  *	any needed private data (in our case the termios)
710f397c8d8SUlf Hansson  */
711f397c8d8SUlf Hansson 
sdio_uart_install(struct tty_driver * driver,struct tty_struct * tty)712f397c8d8SUlf Hansson static int sdio_uart_install(struct tty_driver *driver, struct tty_struct *tty)
713f397c8d8SUlf Hansson {
714f397c8d8SUlf Hansson 	int idx = tty->index;
715f397c8d8SUlf Hansson 	struct sdio_uart_port *port = sdio_uart_port_get(idx);
716f397c8d8SUlf Hansson 	int ret = tty_standard_install(driver, tty);
717f397c8d8SUlf Hansson 
718f397c8d8SUlf Hansson 	if (ret == 0)
719f397c8d8SUlf Hansson 		/* This is the ref sdio_uart_port get provided */
720f397c8d8SUlf Hansson 		tty->driver_data = port;
721f397c8d8SUlf Hansson 	else
722f397c8d8SUlf Hansson 		sdio_uart_port_put(port);
723f397c8d8SUlf Hansson 	return ret;
724f397c8d8SUlf Hansson }
725f397c8d8SUlf Hansson 
726f397c8d8SUlf Hansson /**
727f397c8d8SUlf Hansson  *	sdio_uart_cleanup	-	called on the last tty kref drop
728f397c8d8SUlf Hansson  *	@tty: the tty being destroyed
729f397c8d8SUlf Hansson  *
730f397c8d8SUlf Hansson  *	Called asynchronously when the last reference to the tty is dropped.
731f397c8d8SUlf Hansson  *	We cannot destroy the tty->driver_data port kref until this point
732f397c8d8SUlf Hansson  */
733f397c8d8SUlf Hansson 
sdio_uart_cleanup(struct tty_struct * tty)734f397c8d8SUlf Hansson static void sdio_uart_cleanup(struct tty_struct *tty)
735f397c8d8SUlf Hansson {
736f397c8d8SUlf Hansson 	struct sdio_uart_port *port = tty->driver_data;
737f397c8d8SUlf Hansson 	tty->driver_data = NULL;	/* Bug trap */
738f397c8d8SUlf Hansson 	sdio_uart_port_put(port);
739f397c8d8SUlf Hansson }
740f397c8d8SUlf Hansson 
741f397c8d8SUlf Hansson /*
742f397c8d8SUlf Hansson  *	Open/close/hangup is now entirely boilerplate
743f397c8d8SUlf Hansson  */
744f397c8d8SUlf Hansson 
sdio_uart_open(struct tty_struct * tty,struct file * filp)745f397c8d8SUlf Hansson static int sdio_uart_open(struct tty_struct *tty, struct file *filp)
746f397c8d8SUlf Hansson {
747f397c8d8SUlf Hansson 	struct sdio_uart_port *port = tty->driver_data;
748f397c8d8SUlf Hansson 	return tty_port_open(&port->port, tty, filp);
749f397c8d8SUlf Hansson }
750f397c8d8SUlf Hansson 
sdio_uart_close(struct tty_struct * tty,struct file * filp)751f397c8d8SUlf Hansson static void sdio_uart_close(struct tty_struct *tty, struct file * filp)
752f397c8d8SUlf Hansson {
753f397c8d8SUlf Hansson 	struct sdio_uart_port *port = tty->driver_data;
754f397c8d8SUlf Hansson 	tty_port_close(&port->port, tty, filp);
755f397c8d8SUlf Hansson }
756f397c8d8SUlf Hansson 
sdio_uart_hangup(struct tty_struct * tty)757f397c8d8SUlf Hansson static void sdio_uart_hangup(struct tty_struct *tty)
758f397c8d8SUlf Hansson {
759f397c8d8SUlf Hansson 	struct sdio_uart_port *port = tty->driver_data;
760f397c8d8SUlf Hansson 	tty_port_hangup(&port->port);
761f397c8d8SUlf Hansson }
762f397c8d8SUlf Hansson 
sdio_uart_write(struct tty_struct * tty,const u8 * buf,size_t count)763*95713967SJiri Slaby (SUSE) static ssize_t sdio_uart_write(struct tty_struct *tty, const u8 *buf,
764*95713967SJiri Slaby (SUSE) 			      size_t count)
765f397c8d8SUlf Hansson {
766f397c8d8SUlf Hansson 	struct sdio_uart_port *port = tty->driver_data;
767f397c8d8SUlf Hansson 	int ret;
768f397c8d8SUlf Hansson 
769f397c8d8SUlf Hansson 	if (!port->func)
770f397c8d8SUlf Hansson 		return -ENODEV;
771f397c8d8SUlf Hansson 
772f397c8d8SUlf Hansson 	ret = kfifo_in_locked(&port->xmit_fifo, buf, count, &port->write_lock);
773f397c8d8SUlf Hansson 	if (!(port->ier & UART_IER_THRI)) {
774f397c8d8SUlf Hansson 		int err = sdio_uart_claim_func(port);
775f397c8d8SUlf Hansson 		if (!err) {
776f397c8d8SUlf Hansson 			sdio_uart_start_tx(port);
777f397c8d8SUlf Hansson 			sdio_uart_irq(port->func);
778f397c8d8SUlf Hansson 			sdio_uart_release_func(port);
779f397c8d8SUlf Hansson 		} else
780f397c8d8SUlf Hansson 			ret = err;
781f397c8d8SUlf Hansson 	}
782f397c8d8SUlf Hansson 
783f397c8d8SUlf Hansson 	return ret;
784f397c8d8SUlf Hansson }
785f397c8d8SUlf Hansson 
sdio_uart_write_room(struct tty_struct * tty)78603b3b1a2SJiri Slaby static unsigned int sdio_uart_write_room(struct tty_struct *tty)
787f397c8d8SUlf Hansson {
788f397c8d8SUlf Hansson 	struct sdio_uart_port *port = tty->driver_data;
789f397c8d8SUlf Hansson 	return FIFO_SIZE - kfifo_len(&port->xmit_fifo);
790f397c8d8SUlf Hansson }
791f397c8d8SUlf Hansson 
sdio_uart_chars_in_buffer(struct tty_struct * tty)792fff4ef17SJiri Slaby static unsigned int sdio_uart_chars_in_buffer(struct tty_struct *tty)
793f397c8d8SUlf Hansson {
794f397c8d8SUlf Hansson 	struct sdio_uart_port *port = tty->driver_data;
795f397c8d8SUlf Hansson 	return kfifo_len(&port->xmit_fifo);
796f397c8d8SUlf Hansson }
797f397c8d8SUlf Hansson 
sdio_uart_send_xchar(struct tty_struct * tty,char ch)798f397c8d8SUlf Hansson static void sdio_uart_send_xchar(struct tty_struct *tty, char ch)
799f397c8d8SUlf Hansson {
800f397c8d8SUlf Hansson 	struct sdio_uart_port *port = tty->driver_data;
801f397c8d8SUlf Hansson 
802f397c8d8SUlf Hansson 	port->x_char = ch;
803f397c8d8SUlf Hansson 	if (ch && !(port->ier & UART_IER_THRI)) {
804f397c8d8SUlf Hansson 		if (sdio_uart_claim_func(port) != 0)
805f397c8d8SUlf Hansson 			return;
806f397c8d8SUlf Hansson 		sdio_uart_start_tx(port);
807f397c8d8SUlf Hansson 		sdio_uart_irq(port->func);
808f397c8d8SUlf Hansson 		sdio_uart_release_func(port);
809f397c8d8SUlf Hansson 	}
810f397c8d8SUlf Hansson }
811f397c8d8SUlf Hansson 
sdio_uart_throttle(struct tty_struct * tty)812f397c8d8SUlf Hansson static void sdio_uart_throttle(struct tty_struct *tty)
813f397c8d8SUlf Hansson {
814f397c8d8SUlf Hansson 	struct sdio_uart_port *port = tty->driver_data;
815f397c8d8SUlf Hansson 
816f397c8d8SUlf Hansson 	if (!I_IXOFF(tty) && !C_CRTSCTS(tty))
817f397c8d8SUlf Hansson 		return;
818f397c8d8SUlf Hansson 
819f397c8d8SUlf Hansson 	if (sdio_uart_claim_func(port) != 0)
820f397c8d8SUlf Hansson 		return;
821f397c8d8SUlf Hansson 
822f397c8d8SUlf Hansson 	if (I_IXOFF(tty)) {
823f397c8d8SUlf Hansson 		port->x_char = STOP_CHAR(tty);
824f397c8d8SUlf Hansson 		sdio_uart_start_tx(port);
825f397c8d8SUlf Hansson 	}
826f397c8d8SUlf Hansson 
827f397c8d8SUlf Hansson 	if (C_CRTSCTS(tty))
828f397c8d8SUlf Hansson 		sdio_uart_clear_mctrl(port, TIOCM_RTS);
829f397c8d8SUlf Hansson 
830f397c8d8SUlf Hansson 	sdio_uart_irq(port->func);
831f397c8d8SUlf Hansson 	sdio_uart_release_func(port);
832f397c8d8SUlf Hansson }
833f397c8d8SUlf Hansson 
sdio_uart_unthrottle(struct tty_struct * tty)834f397c8d8SUlf Hansson static void sdio_uart_unthrottle(struct tty_struct *tty)
835f397c8d8SUlf Hansson {
836f397c8d8SUlf Hansson 	struct sdio_uart_port *port = tty->driver_data;
837f397c8d8SUlf Hansson 
838f397c8d8SUlf Hansson 	if (!I_IXOFF(tty) && !C_CRTSCTS(tty))
839f397c8d8SUlf Hansson 		return;
840f397c8d8SUlf Hansson 
841f397c8d8SUlf Hansson 	if (sdio_uart_claim_func(port) != 0)
842f397c8d8SUlf Hansson 		return;
843f397c8d8SUlf Hansson 
844f397c8d8SUlf Hansson 	if (I_IXOFF(tty)) {
845f397c8d8SUlf Hansson 		if (port->x_char) {
846f397c8d8SUlf Hansson 			port->x_char = 0;
847f397c8d8SUlf Hansson 		} else {
848f397c8d8SUlf Hansson 			port->x_char = START_CHAR(tty);
849f397c8d8SUlf Hansson 			sdio_uart_start_tx(port);
850f397c8d8SUlf Hansson 		}
851f397c8d8SUlf Hansson 	}
852f397c8d8SUlf Hansson 
853f397c8d8SUlf Hansson 	if (C_CRTSCTS(tty))
854f397c8d8SUlf Hansson 		sdio_uart_set_mctrl(port, TIOCM_RTS);
855f397c8d8SUlf Hansson 
856f397c8d8SUlf Hansson 	sdio_uart_irq(port->func);
857f397c8d8SUlf Hansson 	sdio_uart_release_func(port);
858f397c8d8SUlf Hansson }
859f397c8d8SUlf Hansson 
sdio_uart_set_termios(struct tty_struct * tty,const struct ktermios * old_termios)860f397c8d8SUlf Hansson static void sdio_uart_set_termios(struct tty_struct *tty,
861a8c11c15SIlpo Järvinen 				  const struct ktermios *old_termios)
862f397c8d8SUlf Hansson {
863f397c8d8SUlf Hansson 	struct sdio_uart_port *port = tty->driver_data;
864f397c8d8SUlf Hansson 	unsigned int cflag = tty->termios.c_cflag;
865f397c8d8SUlf Hansson 
866f397c8d8SUlf Hansson 	if (sdio_uart_claim_func(port) != 0)
867f397c8d8SUlf Hansson 		return;
868f397c8d8SUlf Hansson 
869f397c8d8SUlf Hansson 	sdio_uart_change_speed(port, &tty->termios, old_termios);
870f397c8d8SUlf Hansson 
871f397c8d8SUlf Hansson 	/* Handle transition to B0 status */
872f397c8d8SUlf Hansson 	if ((old_termios->c_cflag & CBAUD) && !(cflag & CBAUD))
873f397c8d8SUlf Hansson 		sdio_uart_clear_mctrl(port, TIOCM_RTS | TIOCM_DTR);
874f397c8d8SUlf Hansson 
875f397c8d8SUlf Hansson 	/* Handle transition away from B0 status */
876f397c8d8SUlf Hansson 	if (!(old_termios->c_cflag & CBAUD) && (cflag & CBAUD)) {
877f397c8d8SUlf Hansson 		unsigned int mask = TIOCM_DTR;
878f397c8d8SUlf Hansson 		if (!(cflag & CRTSCTS) || !tty_throttled(tty))
879f397c8d8SUlf Hansson 			mask |= TIOCM_RTS;
880f397c8d8SUlf Hansson 		sdio_uart_set_mctrl(port, mask);
881f397c8d8SUlf Hansson 	}
882f397c8d8SUlf Hansson 
883f397c8d8SUlf Hansson 	/* Handle turning off CRTSCTS */
884f397c8d8SUlf Hansson 	if ((old_termios->c_cflag & CRTSCTS) && !(cflag & CRTSCTS)) {
885035173c9SIlpo Järvinen 		tty->hw_stopped = false;
886f397c8d8SUlf Hansson 		sdio_uart_start_tx(port);
887f397c8d8SUlf Hansson 	}
888f397c8d8SUlf Hansson 
889f397c8d8SUlf Hansson 	/* Handle turning on CRTSCTS */
890f397c8d8SUlf Hansson 	if (!(old_termios->c_cflag & CRTSCTS) && (cflag & CRTSCTS)) {
891f397c8d8SUlf Hansson 		if (!(sdio_uart_get_mctrl(port) & TIOCM_CTS)) {
892035173c9SIlpo Järvinen 			tty->hw_stopped = true;
893f397c8d8SUlf Hansson 			sdio_uart_stop_tx(port);
894f397c8d8SUlf Hansson 		}
895f397c8d8SUlf Hansson 	}
896f397c8d8SUlf Hansson 
897f397c8d8SUlf Hansson 	sdio_uart_release_func(port);
898f397c8d8SUlf Hansson }
899f397c8d8SUlf Hansson 
sdio_uart_break_ctl(struct tty_struct * tty,int break_state)900f397c8d8SUlf Hansson static int sdio_uart_break_ctl(struct tty_struct *tty, int break_state)
901f397c8d8SUlf Hansson {
902f397c8d8SUlf Hansson 	struct sdio_uart_port *port = tty->driver_data;
903f397c8d8SUlf Hansson 	int result;
904f397c8d8SUlf Hansson 
905f397c8d8SUlf Hansson 	result = sdio_uart_claim_func(port);
906f397c8d8SUlf Hansson 	if (result != 0)
907f397c8d8SUlf Hansson 		return result;
908f397c8d8SUlf Hansson 
909f397c8d8SUlf Hansson 	if (break_state == -1)
910f397c8d8SUlf Hansson 		port->lcr |= UART_LCR_SBC;
911f397c8d8SUlf Hansson 	else
912f397c8d8SUlf Hansson 		port->lcr &= ~UART_LCR_SBC;
913f397c8d8SUlf Hansson 	sdio_out(port, UART_LCR, port->lcr);
914f397c8d8SUlf Hansson 
915f397c8d8SUlf Hansson 	sdio_uart_release_func(port);
916f397c8d8SUlf Hansson 	return 0;
917f397c8d8SUlf Hansson }
918f397c8d8SUlf Hansson 
sdio_uart_tiocmget(struct tty_struct * tty)919f397c8d8SUlf Hansson static int sdio_uart_tiocmget(struct tty_struct *tty)
920f397c8d8SUlf Hansson {
921f397c8d8SUlf Hansson 	struct sdio_uart_port *port = tty->driver_data;
922f397c8d8SUlf Hansson 	int result;
923f397c8d8SUlf Hansson 
924f397c8d8SUlf Hansson 	result = sdio_uart_claim_func(port);
925f397c8d8SUlf Hansson 	if (!result) {
926f397c8d8SUlf Hansson 		result = port->mctrl | sdio_uart_get_mctrl(port);
927f397c8d8SUlf Hansson 		sdio_uart_release_func(port);
928f397c8d8SUlf Hansson 	}
929f397c8d8SUlf Hansson 
930f397c8d8SUlf Hansson 	return result;
931f397c8d8SUlf Hansson }
932f397c8d8SUlf Hansson 
sdio_uart_tiocmset(struct tty_struct * tty,unsigned int set,unsigned int clear)933f397c8d8SUlf Hansson static int sdio_uart_tiocmset(struct tty_struct *tty,
934f397c8d8SUlf Hansson 			      unsigned int set, unsigned int clear)
935f397c8d8SUlf Hansson {
936f397c8d8SUlf Hansson 	struct sdio_uart_port *port = tty->driver_data;
937f397c8d8SUlf Hansson 	int result;
938f397c8d8SUlf Hansson 
939f397c8d8SUlf Hansson 	result = sdio_uart_claim_func(port);
940f397c8d8SUlf Hansson 	if (!result) {
941f397c8d8SUlf Hansson 		sdio_uart_update_mctrl(port, set, clear);
942f397c8d8SUlf Hansson 		sdio_uart_release_func(port);
943f397c8d8SUlf Hansson 	}
944f397c8d8SUlf Hansson 
945f397c8d8SUlf Hansson 	return result;
946f397c8d8SUlf Hansson }
947f397c8d8SUlf Hansson 
sdio_uart_proc_show(struct seq_file * m,void * v)948f397c8d8SUlf Hansson static int sdio_uart_proc_show(struct seq_file *m, void *v)
949f397c8d8SUlf Hansson {
950f397c8d8SUlf Hansson 	int i;
951f397c8d8SUlf Hansson 
952f397c8d8SUlf Hansson 	seq_printf(m, "serinfo:1.0 driver%s%s revision:%s\n",
953f397c8d8SUlf Hansson 		       "", "", "");
954f397c8d8SUlf Hansson 	for (i = 0; i < UART_NR; i++) {
955f397c8d8SUlf Hansson 		struct sdio_uart_port *port = sdio_uart_port_get(i);
956f397c8d8SUlf Hansson 		if (port) {
957f397c8d8SUlf Hansson 			seq_printf(m, "%d: uart:SDIO", i);
958f397c8d8SUlf Hansson 			if (capable(CAP_SYS_ADMIN)) {
959f397c8d8SUlf Hansson 				seq_printf(m, " tx:%d rx:%d",
960f397c8d8SUlf Hansson 					      port->icount.tx, port->icount.rx);
961f397c8d8SUlf Hansson 				if (port->icount.frame)
962f397c8d8SUlf Hansson 					seq_printf(m, " fe:%d",
963f397c8d8SUlf Hansson 						      port->icount.frame);
964f397c8d8SUlf Hansson 				if (port->icount.parity)
965f397c8d8SUlf Hansson 					seq_printf(m, " pe:%d",
966f397c8d8SUlf Hansson 						      port->icount.parity);
967f397c8d8SUlf Hansson 				if (port->icount.brk)
968f397c8d8SUlf Hansson 					seq_printf(m, " brk:%d",
969f397c8d8SUlf Hansson 						      port->icount.brk);
970f397c8d8SUlf Hansson 				if (port->icount.overrun)
971f397c8d8SUlf Hansson 					seq_printf(m, " oe:%d",
972f397c8d8SUlf Hansson 						      port->icount.overrun);
973f397c8d8SUlf Hansson 				if (port->icount.cts)
974f397c8d8SUlf Hansson 					seq_printf(m, " cts:%d",
975f397c8d8SUlf Hansson 						      port->icount.cts);
976f397c8d8SUlf Hansson 				if (port->icount.dsr)
977f397c8d8SUlf Hansson 					seq_printf(m, " dsr:%d",
978f397c8d8SUlf Hansson 						      port->icount.dsr);
979f397c8d8SUlf Hansson 				if (port->icount.rng)
980f397c8d8SUlf Hansson 					seq_printf(m, " rng:%d",
981f397c8d8SUlf Hansson 						      port->icount.rng);
982f397c8d8SUlf Hansson 				if (port->icount.dcd)
983f397c8d8SUlf Hansson 					seq_printf(m, " dcd:%d",
984f397c8d8SUlf Hansson 						      port->icount.dcd);
985f397c8d8SUlf Hansson 			}
986f397c8d8SUlf Hansson 			sdio_uart_port_put(port);
987f397c8d8SUlf Hansson 			seq_putc(m, '\n');
988f397c8d8SUlf Hansson 		}
989f397c8d8SUlf Hansson 	}
990f397c8d8SUlf Hansson 	return 0;
991f397c8d8SUlf Hansson }
992f397c8d8SUlf Hansson 
993f397c8d8SUlf Hansson static const struct tty_port_operations sdio_uart_port_ops = {
994f397c8d8SUlf Hansson 	.dtr_rts = uart_dtr_rts,
995f397c8d8SUlf Hansson 	.carrier_raised = uart_carrier_raised,
996f397c8d8SUlf Hansson 	.shutdown = sdio_uart_shutdown,
997f397c8d8SUlf Hansson 	.activate = sdio_uart_activate,
998f397c8d8SUlf Hansson 	.destruct = sdio_uart_port_destroy,
999f397c8d8SUlf Hansson };
1000f397c8d8SUlf Hansson 
1001f397c8d8SUlf Hansson static const struct tty_operations sdio_uart_ops = {
1002f397c8d8SUlf Hansson 	.open			= sdio_uart_open,
1003f397c8d8SUlf Hansson 	.close			= sdio_uart_close,
1004f397c8d8SUlf Hansson 	.write			= sdio_uart_write,
1005f397c8d8SUlf Hansson 	.write_room		= sdio_uart_write_room,
1006f397c8d8SUlf Hansson 	.chars_in_buffer	= sdio_uart_chars_in_buffer,
1007f397c8d8SUlf Hansson 	.send_xchar		= sdio_uart_send_xchar,
1008f397c8d8SUlf Hansson 	.throttle		= sdio_uart_throttle,
1009f397c8d8SUlf Hansson 	.unthrottle		= sdio_uart_unthrottle,
1010f397c8d8SUlf Hansson 	.set_termios		= sdio_uart_set_termios,
1011f397c8d8SUlf Hansson 	.hangup			= sdio_uart_hangup,
1012f397c8d8SUlf Hansson 	.break_ctl		= sdio_uart_break_ctl,
1013f397c8d8SUlf Hansson 	.tiocmget		= sdio_uart_tiocmget,
1014f397c8d8SUlf Hansson 	.tiocmset		= sdio_uart_tiocmset,
1015f397c8d8SUlf Hansson 	.install		= sdio_uart_install,
1016f397c8d8SUlf Hansson 	.cleanup		= sdio_uart_cleanup,
10178a8dcabfSChristoph Hellwig 	.proc_show		= sdio_uart_proc_show,
1018f397c8d8SUlf Hansson };
1019f397c8d8SUlf Hansson 
1020f397c8d8SUlf Hansson static struct tty_driver *sdio_uart_tty_driver;
1021f397c8d8SUlf Hansson 
sdio_uart_probe(struct sdio_func * func,const struct sdio_device_id * id)1022f397c8d8SUlf Hansson static int sdio_uart_probe(struct sdio_func *func,
1023f397c8d8SUlf Hansson 			   const struct sdio_device_id *id)
1024f397c8d8SUlf Hansson {
1025f397c8d8SUlf Hansson 	struct sdio_uart_port *port;
1026f397c8d8SUlf Hansson 	int ret;
1027f397c8d8SUlf Hansson 
1028f397c8d8SUlf Hansson 	port = kzalloc(sizeof(struct sdio_uart_port), GFP_KERNEL);
1029f397c8d8SUlf Hansson 	if (!port)
1030f397c8d8SUlf Hansson 		return -ENOMEM;
1031f397c8d8SUlf Hansson 
1032f397c8d8SUlf Hansson 	if (func->class == SDIO_CLASS_UART) {
1033f397c8d8SUlf Hansson 		pr_warn("%s: need info on UART class basic setup\n",
1034f397c8d8SUlf Hansson 			sdio_func_id(func));
1035f397c8d8SUlf Hansson 		kfree(port);
1036f397c8d8SUlf Hansson 		return -ENOSYS;
1037f397c8d8SUlf Hansson 	} else if (func->class == SDIO_CLASS_GPS) {
1038f397c8d8SUlf Hansson 		/*
1039f397c8d8SUlf Hansson 		 * We need tuple 0x91.  It contains SUBTPL_SIOREG
1040f397c8d8SUlf Hansson 		 * and SUBTPL_RCVCAPS.
1041f397c8d8SUlf Hansson 		 */
1042f397c8d8SUlf Hansson 		struct sdio_func_tuple *tpl;
1043f397c8d8SUlf Hansson 		for (tpl = func->tuples; tpl; tpl = tpl->next) {
1044f397c8d8SUlf Hansson 			if (tpl->code != 0x91)
1045f397c8d8SUlf Hansson 				continue;
1046f397c8d8SUlf Hansson 			if (tpl->size < 10)
1047f397c8d8SUlf Hansson 				continue;
1048f397c8d8SUlf Hansson 			if (tpl->data[1] == 0)  /* SUBTPL_SIOREG */
1049f397c8d8SUlf Hansson 				break;
1050f397c8d8SUlf Hansson 		}
1051f397c8d8SUlf Hansson 		if (!tpl) {
1052f397c8d8SUlf Hansson 			pr_warn("%s: can't find tuple 0x91 subtuple 0 (SUBTPL_SIOREG) for GPS class\n",
1053f397c8d8SUlf Hansson 				sdio_func_id(func));
1054f397c8d8SUlf Hansson 			kfree(port);
1055f397c8d8SUlf Hansson 			return -EINVAL;
1056f397c8d8SUlf Hansson 		}
1057f397c8d8SUlf Hansson 		pr_debug("%s: Register ID = 0x%02x, Exp ID = 0x%02x\n",
1058f397c8d8SUlf Hansson 		       sdio_func_id(func), tpl->data[2], tpl->data[3]);
1059f397c8d8SUlf Hansson 		port->regs_offset = (tpl->data[4] << 0) |
1060f397c8d8SUlf Hansson 				    (tpl->data[5] << 8) |
1061f397c8d8SUlf Hansson 				    (tpl->data[6] << 16);
1062f397c8d8SUlf Hansson 		pr_debug("%s: regs offset = 0x%x\n",
1063f397c8d8SUlf Hansson 		       sdio_func_id(func), port->regs_offset);
1064f397c8d8SUlf Hansson 		port->uartclk = tpl->data[7] * 115200;
1065f397c8d8SUlf Hansson 		if (port->uartclk == 0)
1066f397c8d8SUlf Hansson 			port->uartclk = 115200;
1067f397c8d8SUlf Hansson 		pr_debug("%s: clk %d baudcode %u 4800-div %u\n",
1068f397c8d8SUlf Hansson 		       sdio_func_id(func), port->uartclk,
1069f397c8d8SUlf Hansson 		       tpl->data[7], tpl->data[8] | (tpl->data[9] << 8));
1070f397c8d8SUlf Hansson 	} else {
1071f397c8d8SUlf Hansson 		kfree(port);
1072f397c8d8SUlf Hansson 		return -EINVAL;
1073f397c8d8SUlf Hansson 	}
1074f397c8d8SUlf Hansson 
1075f397c8d8SUlf Hansson 	port->func = func;
1076f397c8d8SUlf Hansson 	sdio_set_drvdata(func, port);
1077f397c8d8SUlf Hansson 	tty_port_init(&port->port);
1078f397c8d8SUlf Hansson 	port->port.ops = &sdio_uart_port_ops;
1079f397c8d8SUlf Hansson 
1080f397c8d8SUlf Hansson 	ret = sdio_uart_add_port(port);
1081f397c8d8SUlf Hansson 	if (ret) {
1082f397c8d8SUlf Hansson 		kfree(port);
1083f397c8d8SUlf Hansson 	} else {
1084f397c8d8SUlf Hansson 		struct device *dev;
1085f397c8d8SUlf Hansson 		dev = tty_port_register_device(&port->port,
1086f397c8d8SUlf Hansson 				sdio_uart_tty_driver, port->index, &func->dev);
1087f397c8d8SUlf Hansson 		if (IS_ERR(dev)) {
1088f397c8d8SUlf Hansson 			sdio_uart_port_remove(port);
1089f397c8d8SUlf Hansson 			ret = PTR_ERR(dev);
1090f397c8d8SUlf Hansson 		}
1091f397c8d8SUlf Hansson 	}
1092f397c8d8SUlf Hansson 
1093f397c8d8SUlf Hansson 	return ret;
1094f397c8d8SUlf Hansson }
1095f397c8d8SUlf Hansson 
sdio_uart_remove(struct sdio_func * func)1096f397c8d8SUlf Hansson static void sdio_uart_remove(struct sdio_func *func)
1097f397c8d8SUlf Hansson {
1098f397c8d8SUlf Hansson 	struct sdio_uart_port *port = sdio_get_drvdata(func);
1099f397c8d8SUlf Hansson 
1100f397c8d8SUlf Hansson 	tty_unregister_device(sdio_uart_tty_driver, port->index);
1101f397c8d8SUlf Hansson 	sdio_uart_port_remove(port);
1102f397c8d8SUlf Hansson }
1103f397c8d8SUlf Hansson 
1104f397c8d8SUlf Hansson static const struct sdio_device_id sdio_uart_ids[] = {
1105f397c8d8SUlf Hansson 	{ SDIO_DEVICE_CLASS(SDIO_CLASS_UART)		},
1106f397c8d8SUlf Hansson 	{ SDIO_DEVICE_CLASS(SDIO_CLASS_GPS)		},
1107f397c8d8SUlf Hansson 	{ /* end: all zeroes */				},
1108f397c8d8SUlf Hansson };
1109f397c8d8SUlf Hansson 
1110f397c8d8SUlf Hansson MODULE_DEVICE_TABLE(sdio, sdio_uart_ids);
1111f397c8d8SUlf Hansson 
1112f397c8d8SUlf Hansson static struct sdio_driver sdio_uart_driver = {
1113f397c8d8SUlf Hansson 	.probe		= sdio_uart_probe,
1114f397c8d8SUlf Hansson 	.remove		= sdio_uart_remove,
1115f397c8d8SUlf Hansson 	.name		= "sdio_uart",
1116f397c8d8SUlf Hansson 	.id_table	= sdio_uart_ids,
1117f397c8d8SUlf Hansson };
1118f397c8d8SUlf Hansson 
sdio_uart_init(void)1119f397c8d8SUlf Hansson static int __init sdio_uart_init(void)
1120f397c8d8SUlf Hansson {
1121f397c8d8SUlf Hansson 	int ret;
1122f397c8d8SUlf Hansson 	struct tty_driver *tty_drv;
1123f397c8d8SUlf Hansson 
112439b7b42bSJiri Slaby 	sdio_uart_tty_driver = tty_drv = tty_alloc_driver(UART_NR,
112539b7b42bSJiri Slaby 			TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV);
112639b7b42bSJiri Slaby 	if (IS_ERR(tty_drv))
112739b7b42bSJiri Slaby 		return PTR_ERR(tty_drv);
1128f397c8d8SUlf Hansson 
1129f397c8d8SUlf Hansson 	tty_drv->driver_name = "sdio_uart";
1130f397c8d8SUlf Hansson 	tty_drv->name =   "ttySDIO";
1131f397c8d8SUlf Hansson 	tty_drv->major = 0;  /* dynamically allocated */
1132f397c8d8SUlf Hansson 	tty_drv->minor_start = 0;
1133f397c8d8SUlf Hansson 	tty_drv->type = TTY_DRIVER_TYPE_SERIAL;
1134f397c8d8SUlf Hansson 	tty_drv->subtype = SERIAL_TYPE_NORMAL;
1135f397c8d8SUlf Hansson 	tty_drv->init_termios = tty_std_termios;
1136f397c8d8SUlf Hansson 	tty_drv->init_termios.c_cflag = B4800 | CS8 | CREAD | HUPCL | CLOCAL;
1137f397c8d8SUlf Hansson 	tty_drv->init_termios.c_ispeed = 4800;
1138f397c8d8SUlf Hansson 	tty_drv->init_termios.c_ospeed = 4800;
1139f397c8d8SUlf Hansson 	tty_set_operations(tty_drv, &sdio_uart_ops);
1140f397c8d8SUlf Hansson 
1141f397c8d8SUlf Hansson 	ret = tty_register_driver(tty_drv);
1142f397c8d8SUlf Hansson 	if (ret)
1143f397c8d8SUlf Hansson 		goto err1;
1144f397c8d8SUlf Hansson 
1145f397c8d8SUlf Hansson 	ret = sdio_register_driver(&sdio_uart_driver);
1146f397c8d8SUlf Hansson 	if (ret)
1147f397c8d8SUlf Hansson 		goto err2;
1148f397c8d8SUlf Hansson 
1149f397c8d8SUlf Hansson 	return 0;
1150f397c8d8SUlf Hansson 
1151f397c8d8SUlf Hansson err2:
1152f397c8d8SUlf Hansson 	tty_unregister_driver(tty_drv);
1153f397c8d8SUlf Hansson err1:
11549f90a4ddSJiri Slaby 	tty_driver_kref_put(tty_drv);
1155f397c8d8SUlf Hansson 	return ret;
1156f397c8d8SUlf Hansson }
1157f397c8d8SUlf Hansson 
sdio_uart_exit(void)1158f397c8d8SUlf Hansson static void __exit sdio_uart_exit(void)
1159f397c8d8SUlf Hansson {
1160f397c8d8SUlf Hansson 	sdio_unregister_driver(&sdio_uart_driver);
1161f397c8d8SUlf Hansson 	tty_unregister_driver(sdio_uart_tty_driver);
11629f90a4ddSJiri Slaby 	tty_driver_kref_put(sdio_uart_tty_driver);
1163f397c8d8SUlf Hansson }
1164f397c8d8SUlf Hansson 
1165f397c8d8SUlf Hansson module_init(sdio_uart_init);
1166f397c8d8SUlf Hansson module_exit(sdio_uart_exit);
1167f397c8d8SUlf Hansson 
1168f397c8d8SUlf Hansson MODULE_AUTHOR("Nicolas Pitre");
1169f397c8d8SUlf Hansson MODULE_LICENSE("GPL");
1170