xref: /linux/drivers/tty/serial/pxa.c (revision 51f7ed07)
1e3b3d0f5SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0+
2ab4382d2SGreg Kroah-Hartman /*
3ab4382d2SGreg Kroah-Hartman  *  Based on drivers/serial/8250.c by Russell King.
4ab4382d2SGreg Kroah-Hartman  *
5ab4382d2SGreg Kroah-Hartman  *  Author:	Nicolas Pitre
6ab4382d2SGreg Kroah-Hartman  *  Created:	Feb 20, 2003
7ab4382d2SGreg Kroah-Hartman  *  Copyright:	(C) 2003 Monta Vista Software, Inc.
8ab4382d2SGreg Kroah-Hartman  *
9ab4382d2SGreg Kroah-Hartman  * Note 1: This driver is made separate from the already too overloaded
10ab4382d2SGreg Kroah-Hartman  * 8250.c because it needs some kirks of its own and that'll make it
11ab4382d2SGreg Kroah-Hartman  * easier to add DMA support.
12ab4382d2SGreg Kroah-Hartman  *
13ab4382d2SGreg Kroah-Hartman  * Note 2: I'm too sick of device allocation policies for serial ports.
14ab4382d2SGreg Kroah-Hartman  * If someone else wants to request an "official" allocation of major/minor
15ab4382d2SGreg Kroah-Hartman  * for this driver please be my guest.  And don't forget that new hardware
16ab4382d2SGreg Kroah-Hartman  * to come from Intel might have more than 3 or 4 of those UARTs.  Let's
17ab4382d2SGreg Kroah-Hartman  * hope for a better port registration and dynamic device allocation scheme
18ab4382d2SGreg Kroah-Hartman  * with the serial core maintainer satisfaction to appear soon.
19ab4382d2SGreg Kroah-Hartman  */
20ab4382d2SGreg Kroah-Hartman 
21ab4382d2SGreg Kroah-Hartman 
22ab4382d2SGreg Kroah-Hartman #include <linux/ioport.h>
23ab4382d2SGreg Kroah-Hartman #include <linux/init.h>
24ab4382d2SGreg Kroah-Hartman #include <linux/console.h>
25ab4382d2SGreg Kroah-Hartman #include <linux/sysrq.h>
2634619de1SIlpo Järvinen #include <linux/serial.h>
27ab4382d2SGreg Kroah-Hartman #include <linux/serial_reg.h>
28ab4382d2SGreg Kroah-Hartman #include <linux/circ_buf.h>
29ab4382d2SGreg Kroah-Hartman #include <linux/delay.h>
30ab4382d2SGreg Kroah-Hartman #include <linux/interrupt.h>
31699c20f3SHaojian Zhuang #include <linux/of.h>
32ab4382d2SGreg Kroah-Hartman #include <linux/platform_device.h>
33ab4382d2SGreg Kroah-Hartman #include <linux/tty.h>
34ab4382d2SGreg Kroah-Hartman #include <linux/tty_flip.h>
35ab4382d2SGreg Kroah-Hartman #include <linux/serial_core.h>
36ab4382d2SGreg Kroah-Hartman #include <linux/clk.h>
37ab4382d2SGreg Kroah-Hartman #include <linux/io.h>
38ab4382d2SGreg Kroah-Hartman #include <linux/slab.h>
39ab4382d2SGreg Kroah-Hartman 
40699c20f3SHaojian Zhuang #define PXA_NAME_LEN		8
41699c20f3SHaojian Zhuang 
42ab4382d2SGreg Kroah-Hartman struct uart_pxa_port {
43ab4382d2SGreg Kroah-Hartman 	struct uart_port        port;
44ab4382d2SGreg Kroah-Hartman 	unsigned char           ier;
45ab4382d2SGreg Kroah-Hartman 	unsigned char           lcr;
46ab4382d2SGreg Kroah-Hartman 	unsigned char           mcr;
47ab4382d2SGreg Kroah-Hartman 	unsigned int            lsr_break_flag;
48ab4382d2SGreg Kroah-Hartman 	struct clk		*clk;
49699c20f3SHaojian Zhuang 	char			name[PXA_NAME_LEN];
50ab4382d2SGreg Kroah-Hartman };
51ab4382d2SGreg Kroah-Hartman 
serial_in(struct uart_pxa_port * up,int offset)52ab4382d2SGreg Kroah-Hartman static inline unsigned int serial_in(struct uart_pxa_port *up, int offset)
53ab4382d2SGreg Kroah-Hartman {
54ab4382d2SGreg Kroah-Hartman 	offset <<= 2;
55ab4382d2SGreg Kroah-Hartman 	return readl(up->port.membase + offset);
56ab4382d2SGreg Kroah-Hartman }
57ab4382d2SGreg Kroah-Hartman 
serial_out(struct uart_pxa_port * up,int offset,int value)58ab4382d2SGreg Kroah-Hartman static inline void serial_out(struct uart_pxa_port *up, int offset, int value)
59ab4382d2SGreg Kroah-Hartman {
60ab4382d2SGreg Kroah-Hartman 	offset <<= 2;
61ab4382d2SGreg Kroah-Hartman 	writel(value, up->port.membase + offset);
62ab4382d2SGreg Kroah-Hartman }
63ab4382d2SGreg Kroah-Hartman 
serial_pxa_enable_ms(struct uart_port * port)64ab4382d2SGreg Kroah-Hartman static void serial_pxa_enable_ms(struct uart_port *port)
65ab4382d2SGreg Kroah-Hartman {
66ab4382d2SGreg Kroah-Hartman 	struct uart_pxa_port *up = (struct uart_pxa_port *)port;
67ab4382d2SGreg Kroah-Hartman 
68ab4382d2SGreg Kroah-Hartman 	up->ier |= UART_IER_MSI;
69ab4382d2SGreg Kroah-Hartman 	serial_out(up, UART_IER, up->ier);
70ab4382d2SGreg Kroah-Hartman }
71ab4382d2SGreg Kroah-Hartman 
serial_pxa_stop_tx(struct uart_port * port)72ab4382d2SGreg Kroah-Hartman static void serial_pxa_stop_tx(struct uart_port *port)
73ab4382d2SGreg Kroah-Hartman {
74ab4382d2SGreg Kroah-Hartman 	struct uart_pxa_port *up = (struct uart_pxa_port *)port;
75ab4382d2SGreg Kroah-Hartman 
76ab4382d2SGreg Kroah-Hartman 	if (up->ier & UART_IER_THRI) {
77ab4382d2SGreg Kroah-Hartman 		up->ier &= ~UART_IER_THRI;
78ab4382d2SGreg Kroah-Hartman 		serial_out(up, UART_IER, up->ier);
79ab4382d2SGreg Kroah-Hartman 	}
80ab4382d2SGreg Kroah-Hartman }
81ab4382d2SGreg Kroah-Hartman 
serial_pxa_stop_rx(struct uart_port * port)82ab4382d2SGreg Kroah-Hartman static void serial_pxa_stop_rx(struct uart_port *port)
83ab4382d2SGreg Kroah-Hartman {
84ab4382d2SGreg Kroah-Hartman 	struct uart_pxa_port *up = (struct uart_pxa_port *)port;
85ab4382d2SGreg Kroah-Hartman 
86ab4382d2SGreg Kroah-Hartman 	up->ier &= ~UART_IER_RLSI;
87ab4382d2SGreg Kroah-Hartman 	up->port.read_status_mask &= ~UART_LSR_DR;
88ab4382d2SGreg Kroah-Hartman 	serial_out(up, UART_IER, up->ier);
89ab4382d2SGreg Kroah-Hartman }
90ab4382d2SGreg Kroah-Hartman 
receive_chars(struct uart_pxa_port * up,int * status)91ab4382d2SGreg Kroah-Hartman static inline void receive_chars(struct uart_pxa_port *up, int *status)
92ab4382d2SGreg Kroah-Hartman {
93fd2b55f8SJiri Slaby 	u8 ch, flag;
94ab4382d2SGreg Kroah-Hartman 	int max_count = 256;
95ab4382d2SGreg Kroah-Hartman 
96ab4382d2SGreg Kroah-Hartman 	do {
97e44aabd6SMarcus Folkesson 		/* work around Errata #20 according to
98e44aabd6SMarcus Folkesson 		 * Intel(R) PXA27x Processor Family
99e44aabd6SMarcus Folkesson 		 * Specification Update (May 2005)
100e44aabd6SMarcus Folkesson 		 *
101e44aabd6SMarcus Folkesson 		 * Step 2
102e44aabd6SMarcus Folkesson 		 * Disable the Reciever Time Out Interrupt via IER[RTOEI]
103e44aabd6SMarcus Folkesson 		 */
104e44aabd6SMarcus Folkesson 		up->ier &= ~UART_IER_RTOIE;
105e44aabd6SMarcus Folkesson 		serial_out(up, UART_IER, up->ier);
106e44aabd6SMarcus Folkesson 
107ab4382d2SGreg Kroah-Hartman 		ch = serial_in(up, UART_RX);
108ab4382d2SGreg Kroah-Hartman 		flag = TTY_NORMAL;
109ab4382d2SGreg Kroah-Hartman 		up->port.icount.rx++;
110ab4382d2SGreg Kroah-Hartman 
111ab4382d2SGreg Kroah-Hartman 		if (unlikely(*status & (UART_LSR_BI | UART_LSR_PE |
112ab4382d2SGreg Kroah-Hartman 				       UART_LSR_FE | UART_LSR_OE))) {
113ab4382d2SGreg Kroah-Hartman 			/*
114ab4382d2SGreg Kroah-Hartman 			 * For statistics only
115ab4382d2SGreg Kroah-Hartman 			 */
116ab4382d2SGreg Kroah-Hartman 			if (*status & UART_LSR_BI) {
117ab4382d2SGreg Kroah-Hartman 				*status &= ~(UART_LSR_FE | UART_LSR_PE);
118ab4382d2SGreg Kroah-Hartman 				up->port.icount.brk++;
119ab4382d2SGreg Kroah-Hartman 				/*
120ab4382d2SGreg Kroah-Hartman 				 * We do the SysRQ and SAK checking
121ab4382d2SGreg Kroah-Hartman 				 * here because otherwise the break
122ab4382d2SGreg Kroah-Hartman 				 * may get masked by ignore_status_mask
123ab4382d2SGreg Kroah-Hartman 				 * or read_status_mask.
124ab4382d2SGreg Kroah-Hartman 				 */
125ab4382d2SGreg Kroah-Hartman 				if (uart_handle_break(&up->port))
126ab4382d2SGreg Kroah-Hartman 					goto ignore_char;
127ab4382d2SGreg Kroah-Hartman 			} else if (*status & UART_LSR_PE)
128ab4382d2SGreg Kroah-Hartman 				up->port.icount.parity++;
129ab4382d2SGreg Kroah-Hartman 			else if (*status & UART_LSR_FE)
130ab4382d2SGreg Kroah-Hartman 				up->port.icount.frame++;
131ab4382d2SGreg Kroah-Hartman 			if (*status & UART_LSR_OE)
132ab4382d2SGreg Kroah-Hartman 				up->port.icount.overrun++;
133ab4382d2SGreg Kroah-Hartman 
134ab4382d2SGreg Kroah-Hartman 			/*
135ab4382d2SGreg Kroah-Hartman 			 * Mask off conditions which should be ignored.
136ab4382d2SGreg Kroah-Hartman 			 */
137ab4382d2SGreg Kroah-Hartman 			*status &= up->port.read_status_mask;
138ab4382d2SGreg Kroah-Hartman 
139ab4382d2SGreg Kroah-Hartman #ifdef CONFIG_SERIAL_PXA_CONSOLE
140ab4382d2SGreg Kroah-Hartman 			if (up->port.line == up->port.cons->index) {
141ab4382d2SGreg Kroah-Hartman 				/* Recover the break flag from console xmit */
142ab4382d2SGreg Kroah-Hartman 				*status |= up->lsr_break_flag;
143ab4382d2SGreg Kroah-Hartman 				up->lsr_break_flag = 0;
144ab4382d2SGreg Kroah-Hartman 			}
145ab4382d2SGreg Kroah-Hartman #endif
146ab4382d2SGreg Kroah-Hartman 			if (*status & UART_LSR_BI) {
147ab4382d2SGreg Kroah-Hartman 				flag = TTY_BREAK;
148ab4382d2SGreg Kroah-Hartman 			} else if (*status & UART_LSR_PE)
149ab4382d2SGreg Kroah-Hartman 				flag = TTY_PARITY;
150ab4382d2SGreg Kroah-Hartman 			else if (*status & UART_LSR_FE)
151ab4382d2SGreg Kroah-Hartman 				flag = TTY_FRAME;
152ab4382d2SGreg Kroah-Hartman 		}
153ab4382d2SGreg Kroah-Hartman 
154*51f7ed07SSebastian Andrzej Siewior 		if (uart_prepare_sysrq_char(&up->port, ch))
155ab4382d2SGreg Kroah-Hartman 			goto ignore_char;
156ab4382d2SGreg Kroah-Hartman 
157ab4382d2SGreg Kroah-Hartman 		uart_insert_char(&up->port, *status, UART_LSR_OE, ch, flag);
158ab4382d2SGreg Kroah-Hartman 
159ab4382d2SGreg Kroah-Hartman 	ignore_char:
160ab4382d2SGreg Kroah-Hartman 		*status = serial_in(up, UART_LSR);
161ab4382d2SGreg Kroah-Hartman 	} while ((*status & UART_LSR_DR) && (max_count-- > 0));
1622e124b4aSJiri Slaby 	tty_flip_buffer_push(&up->port.state->port);
163e44aabd6SMarcus Folkesson 
164e44aabd6SMarcus Folkesson 	/* work around Errata #20 according to
165e44aabd6SMarcus Folkesson 	 * Intel(R) PXA27x Processor Family
166e44aabd6SMarcus Folkesson 	 * Specification Update (May 2005)
167e44aabd6SMarcus Folkesson 	 *
168e44aabd6SMarcus Folkesson 	 * Step 6:
169e44aabd6SMarcus Folkesson 	 * No more data in FIFO: Re-enable RTO interrupt via IER[RTOIE]
170e44aabd6SMarcus Folkesson 	 */
171e44aabd6SMarcus Folkesson 	up->ier |= UART_IER_RTOIE;
172e44aabd6SMarcus Folkesson 	serial_out(up, UART_IER, up->ier);
173ab4382d2SGreg Kroah-Hartman }
174ab4382d2SGreg Kroah-Hartman 
transmit_chars(struct uart_pxa_port * up)175ab4382d2SGreg Kroah-Hartman static void transmit_chars(struct uart_pxa_port *up)
176ab4382d2SGreg Kroah-Hartman {
177d11cc8c3SJiri Slaby (SUSE) 	u8 ch;
178ab4382d2SGreg Kroah-Hartman 
179d11cc8c3SJiri Slaby (SUSE) 	uart_port_tx_limited(&up->port, ch, up->port.fifosize / 2,
180d11cc8c3SJiri Slaby (SUSE) 		true,
181d11cc8c3SJiri Slaby (SUSE) 		serial_out(up, UART_TX, ch),
182d11cc8c3SJiri Slaby (SUSE) 		({}));
183ab4382d2SGreg Kroah-Hartman }
184ab4382d2SGreg Kroah-Hartman 
serial_pxa_start_tx(struct uart_port * port)185ab4382d2SGreg Kroah-Hartman static void serial_pxa_start_tx(struct uart_port *port)
186ab4382d2SGreg Kroah-Hartman {
187ab4382d2SGreg Kroah-Hartman 	struct uart_pxa_port *up = (struct uart_pxa_port *)port;
188ab4382d2SGreg Kroah-Hartman 
189ab4382d2SGreg Kroah-Hartman 	if (!(up->ier & UART_IER_THRI)) {
190ab4382d2SGreg Kroah-Hartman 		up->ier |= UART_IER_THRI;
191ab4382d2SGreg Kroah-Hartman 		serial_out(up, UART_IER, up->ier);
192ab4382d2SGreg Kroah-Hartman 	}
193ab4382d2SGreg Kroah-Hartman }
194ab4382d2SGreg Kroah-Hartman 
19550d1e7d1SDmitry Eremin-Solenikov /* should hold up->port.lock */
check_modem_status(struct uart_pxa_port * up)196ab4382d2SGreg Kroah-Hartman static inline void check_modem_status(struct uart_pxa_port *up)
197ab4382d2SGreg Kroah-Hartman {
198ab4382d2SGreg Kroah-Hartman 	int status;
199ab4382d2SGreg Kroah-Hartman 
200ab4382d2SGreg Kroah-Hartman 	status = serial_in(up, UART_MSR);
201ab4382d2SGreg Kroah-Hartman 
202ab4382d2SGreg Kroah-Hartman 	if ((status & UART_MSR_ANY_DELTA) == 0)
203ab4382d2SGreg Kroah-Hartman 		return;
204ab4382d2SGreg Kroah-Hartman 
205ab4382d2SGreg Kroah-Hartman 	if (status & UART_MSR_TERI)
206ab4382d2SGreg Kroah-Hartman 		up->port.icount.rng++;
207ab4382d2SGreg Kroah-Hartman 	if (status & UART_MSR_DDSR)
208ab4382d2SGreg Kroah-Hartman 		up->port.icount.dsr++;
209ab4382d2SGreg Kroah-Hartman 	if (status & UART_MSR_DDCD)
210ab4382d2SGreg Kroah-Hartman 		uart_handle_dcd_change(&up->port, status & UART_MSR_DCD);
211ab4382d2SGreg Kroah-Hartman 	if (status & UART_MSR_DCTS)
212ab4382d2SGreg Kroah-Hartman 		uart_handle_cts_change(&up->port, status & UART_MSR_CTS);
213ab4382d2SGreg Kroah-Hartman 
214ab4382d2SGreg Kroah-Hartman 	wake_up_interruptible(&up->port.state->port.delta_msr_wait);
215ab4382d2SGreg Kroah-Hartman }
216ab4382d2SGreg Kroah-Hartman 
217ab4382d2SGreg Kroah-Hartman /*
218ab4382d2SGreg Kroah-Hartman  * This handles the interrupt from one port.
219ab4382d2SGreg Kroah-Hartman  */
serial_pxa_irq(int irq,void * dev_id)220ab4382d2SGreg Kroah-Hartman static inline irqreturn_t serial_pxa_irq(int irq, void *dev_id)
221ab4382d2SGreg Kroah-Hartman {
222ab4382d2SGreg Kroah-Hartman 	struct uart_pxa_port *up = dev_id;
223ab4382d2SGreg Kroah-Hartman 	unsigned int iir, lsr;
224ab4382d2SGreg Kroah-Hartman 
225ab4382d2SGreg Kroah-Hartman 	iir = serial_in(up, UART_IIR);
226ab4382d2SGreg Kroah-Hartman 	if (iir & UART_IIR_NO_INT)
227ab4382d2SGreg Kroah-Hartman 		return IRQ_NONE;
228ae3c3962SThomas Gleixner 	uart_port_lock(&up->port);
229ab4382d2SGreg Kroah-Hartman 	lsr = serial_in(up, UART_LSR);
230ab4382d2SGreg Kroah-Hartman 	if (lsr & UART_LSR_DR)
231ab4382d2SGreg Kroah-Hartman 		receive_chars(up, &lsr);
232ab4382d2SGreg Kroah-Hartman 	check_modem_status(up);
233ab4382d2SGreg Kroah-Hartman 	if (lsr & UART_LSR_THRE)
234ab4382d2SGreg Kroah-Hartman 		transmit_chars(up);
235*51f7ed07SSebastian Andrzej Siewior 	uart_unlock_and_check_sysrq(&up->port);
236ab4382d2SGreg Kroah-Hartman 	return IRQ_HANDLED;
237ab4382d2SGreg Kroah-Hartman }
238ab4382d2SGreg Kroah-Hartman 
serial_pxa_tx_empty(struct uart_port * port)239ab4382d2SGreg Kroah-Hartman static unsigned int serial_pxa_tx_empty(struct uart_port *port)
240ab4382d2SGreg Kroah-Hartman {
241ab4382d2SGreg Kroah-Hartman 	struct uart_pxa_port *up = (struct uart_pxa_port *)port;
242ab4382d2SGreg Kroah-Hartman 	unsigned long flags;
243ab4382d2SGreg Kroah-Hartman 	unsigned int ret;
244ab4382d2SGreg Kroah-Hartman 
245ae3c3962SThomas Gleixner 	uart_port_lock_irqsave(&up->port, &flags);
246ab4382d2SGreg Kroah-Hartman 	ret = serial_in(up, UART_LSR) & UART_LSR_TEMT ? TIOCSER_TEMT : 0;
247ae3c3962SThomas Gleixner 	uart_port_unlock_irqrestore(&up->port, flags);
248ab4382d2SGreg Kroah-Hartman 
249ab4382d2SGreg Kroah-Hartman 	return ret;
250ab4382d2SGreg Kroah-Hartman }
251ab4382d2SGreg Kroah-Hartman 
serial_pxa_get_mctrl(struct uart_port * port)252ab4382d2SGreg Kroah-Hartman static unsigned int serial_pxa_get_mctrl(struct uart_port *port)
253ab4382d2SGreg Kroah-Hartman {
254ab4382d2SGreg Kroah-Hartman 	struct uart_pxa_port *up = (struct uart_pxa_port *)port;
255ab4382d2SGreg Kroah-Hartman 	unsigned char status;
256ab4382d2SGreg Kroah-Hartman 	unsigned int ret;
257ab4382d2SGreg Kroah-Hartman 
258ab4382d2SGreg Kroah-Hartman 	status = serial_in(up, UART_MSR);
259ab4382d2SGreg Kroah-Hartman 
260ab4382d2SGreg Kroah-Hartman 	ret = 0;
261ab4382d2SGreg Kroah-Hartman 	if (status & UART_MSR_DCD)
262ab4382d2SGreg Kroah-Hartman 		ret |= TIOCM_CAR;
263ab4382d2SGreg Kroah-Hartman 	if (status & UART_MSR_RI)
264ab4382d2SGreg Kroah-Hartman 		ret |= TIOCM_RNG;
265ab4382d2SGreg Kroah-Hartman 	if (status & UART_MSR_DSR)
266ab4382d2SGreg Kroah-Hartman 		ret |= TIOCM_DSR;
267ab4382d2SGreg Kroah-Hartman 	if (status & UART_MSR_CTS)
268ab4382d2SGreg Kroah-Hartman 		ret |= TIOCM_CTS;
269ab4382d2SGreg Kroah-Hartman 	return ret;
270ab4382d2SGreg Kroah-Hartman }
271ab4382d2SGreg Kroah-Hartman 
serial_pxa_set_mctrl(struct uart_port * port,unsigned int mctrl)272ab4382d2SGreg Kroah-Hartman static void serial_pxa_set_mctrl(struct uart_port *port, unsigned int mctrl)
273ab4382d2SGreg Kroah-Hartman {
274ab4382d2SGreg Kroah-Hartman 	struct uart_pxa_port *up = (struct uart_pxa_port *)port;
275ab4382d2SGreg Kroah-Hartman 	unsigned char mcr = 0;
276ab4382d2SGreg Kroah-Hartman 
277ab4382d2SGreg Kroah-Hartman 	if (mctrl & TIOCM_RTS)
278ab4382d2SGreg Kroah-Hartman 		mcr |= UART_MCR_RTS;
279ab4382d2SGreg Kroah-Hartman 	if (mctrl & TIOCM_DTR)
280ab4382d2SGreg Kroah-Hartman 		mcr |= UART_MCR_DTR;
281ab4382d2SGreg Kroah-Hartman 	if (mctrl & TIOCM_OUT1)
282ab4382d2SGreg Kroah-Hartman 		mcr |= UART_MCR_OUT1;
283ab4382d2SGreg Kroah-Hartman 	if (mctrl & TIOCM_OUT2)
284ab4382d2SGreg Kroah-Hartman 		mcr |= UART_MCR_OUT2;
285ab4382d2SGreg Kroah-Hartman 	if (mctrl & TIOCM_LOOP)
286ab4382d2SGreg Kroah-Hartman 		mcr |= UART_MCR_LOOP;
287ab4382d2SGreg Kroah-Hartman 
288ab4382d2SGreg Kroah-Hartman 	mcr |= up->mcr;
289ab4382d2SGreg Kroah-Hartman 
290ab4382d2SGreg Kroah-Hartman 	serial_out(up, UART_MCR, mcr);
291ab4382d2SGreg Kroah-Hartman }
292ab4382d2SGreg Kroah-Hartman 
serial_pxa_break_ctl(struct uart_port * port,int break_state)293ab4382d2SGreg Kroah-Hartman static void serial_pxa_break_ctl(struct uart_port *port, int break_state)
294ab4382d2SGreg Kroah-Hartman {
295ab4382d2SGreg Kroah-Hartman 	struct uart_pxa_port *up = (struct uart_pxa_port *)port;
296ab4382d2SGreg Kroah-Hartman 	unsigned long flags;
297ab4382d2SGreg Kroah-Hartman 
298ae3c3962SThomas Gleixner 	uart_port_lock_irqsave(&up->port, &flags);
299ab4382d2SGreg Kroah-Hartman 	if (break_state == -1)
300ab4382d2SGreg Kroah-Hartman 		up->lcr |= UART_LCR_SBC;
301ab4382d2SGreg Kroah-Hartman 	else
302ab4382d2SGreg Kroah-Hartman 		up->lcr &= ~UART_LCR_SBC;
303ab4382d2SGreg Kroah-Hartman 	serial_out(up, UART_LCR, up->lcr);
304ae3c3962SThomas Gleixner 	uart_port_unlock_irqrestore(&up->port, flags);
305ab4382d2SGreg Kroah-Hartman }
306ab4382d2SGreg Kroah-Hartman 
serial_pxa_startup(struct uart_port * port)307ab4382d2SGreg Kroah-Hartman static int serial_pxa_startup(struct uart_port *port)
308ab4382d2SGreg Kroah-Hartman {
309ab4382d2SGreg Kroah-Hartman 	struct uart_pxa_port *up = (struct uart_pxa_port *)port;
310ab4382d2SGreg Kroah-Hartman 	unsigned long flags;
311ab4382d2SGreg Kroah-Hartman 	int retval;
312ab4382d2SGreg Kroah-Hartman 
313ab4382d2SGreg Kroah-Hartman 	if (port->line == 3) /* HWUART */
314ab4382d2SGreg Kroah-Hartman 		up->mcr |= UART_MCR_AFE;
315ab4382d2SGreg Kroah-Hartman 	else
316ab4382d2SGreg Kroah-Hartman 		up->mcr = 0;
317ab4382d2SGreg Kroah-Hartman 
318ab4382d2SGreg Kroah-Hartman 	up->port.uartclk = clk_get_rate(up->clk);
319ab4382d2SGreg Kroah-Hartman 
320ab4382d2SGreg Kroah-Hartman 	/*
321ab4382d2SGreg Kroah-Hartman 	 * Allocate the IRQ
322ab4382d2SGreg Kroah-Hartman 	 */
323ab4382d2SGreg Kroah-Hartman 	retval = request_irq(up->port.irq, serial_pxa_irq, 0, up->name, up);
324ab4382d2SGreg Kroah-Hartman 	if (retval)
325ab4382d2SGreg Kroah-Hartman 		return retval;
326ab4382d2SGreg Kroah-Hartman 
327ab4382d2SGreg Kroah-Hartman 	/*
328ab4382d2SGreg Kroah-Hartman 	 * Clear the FIFO buffers and disable them.
329ab4382d2SGreg Kroah-Hartman 	 * (they will be reenabled in set_termios())
330ab4382d2SGreg Kroah-Hartman 	 */
331ab4382d2SGreg Kroah-Hartman 	serial_out(up, UART_FCR, UART_FCR_ENABLE_FIFO);
332ab4382d2SGreg Kroah-Hartman 	serial_out(up, UART_FCR, UART_FCR_ENABLE_FIFO |
333ab4382d2SGreg Kroah-Hartman 			UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT);
334ab4382d2SGreg Kroah-Hartman 	serial_out(up, UART_FCR, 0);
335ab4382d2SGreg Kroah-Hartman 
336ab4382d2SGreg Kroah-Hartman 	/*
337ab4382d2SGreg Kroah-Hartman 	 * Clear the interrupt registers.
338ab4382d2SGreg Kroah-Hartman 	 */
339ab4382d2SGreg Kroah-Hartman 	(void) serial_in(up, UART_LSR);
340ab4382d2SGreg Kroah-Hartman 	(void) serial_in(up, UART_RX);
341ab4382d2SGreg Kroah-Hartman 	(void) serial_in(up, UART_IIR);
342ab4382d2SGreg Kroah-Hartman 	(void) serial_in(up, UART_MSR);
343ab4382d2SGreg Kroah-Hartman 
344ab4382d2SGreg Kroah-Hartman 	/*
345ab4382d2SGreg Kroah-Hartman 	 * Now, initialize the UART
346ab4382d2SGreg Kroah-Hartman 	 */
347ab4382d2SGreg Kroah-Hartman 	serial_out(up, UART_LCR, UART_LCR_WLEN8);
348ab4382d2SGreg Kroah-Hartman 
349ae3c3962SThomas Gleixner 	uart_port_lock_irqsave(&up->port, &flags);
350ab4382d2SGreg Kroah-Hartman 	up->port.mctrl |= TIOCM_OUT2;
351ab4382d2SGreg Kroah-Hartman 	serial_pxa_set_mctrl(&up->port, up->port.mctrl);
352ae3c3962SThomas Gleixner 	uart_port_unlock_irqrestore(&up->port, flags);
353ab4382d2SGreg Kroah-Hartman 
354ab4382d2SGreg Kroah-Hartman 	/*
355ab4382d2SGreg Kroah-Hartman 	 * Finally, enable interrupts.  Note: Modem status interrupts
356ab4382d2SGreg Kroah-Hartman 	 * are set via set_termios(), which will be occurring imminently
357ab4382d2SGreg Kroah-Hartman 	 * anyway, so we don't enable them here.
358ab4382d2SGreg Kroah-Hartman 	 */
359ab4382d2SGreg Kroah-Hartman 	up->ier = UART_IER_RLSI | UART_IER_RDI | UART_IER_RTOIE | UART_IER_UUE;
360ab4382d2SGreg Kroah-Hartman 	serial_out(up, UART_IER, up->ier);
361ab4382d2SGreg Kroah-Hartman 
362ab4382d2SGreg Kroah-Hartman 	/*
363ab4382d2SGreg Kroah-Hartman 	 * And clear the interrupt registers again for luck.
364ab4382d2SGreg Kroah-Hartman 	 */
365ab4382d2SGreg Kroah-Hartman 	(void) serial_in(up, UART_LSR);
366ab4382d2SGreg Kroah-Hartman 	(void) serial_in(up, UART_RX);
367ab4382d2SGreg Kroah-Hartman 	(void) serial_in(up, UART_IIR);
368ab4382d2SGreg Kroah-Hartman 	(void) serial_in(up, UART_MSR);
369ab4382d2SGreg Kroah-Hartman 
370ab4382d2SGreg Kroah-Hartman 	return 0;
371ab4382d2SGreg Kroah-Hartman }
372ab4382d2SGreg Kroah-Hartman 
serial_pxa_shutdown(struct uart_port * port)373ab4382d2SGreg Kroah-Hartman static void serial_pxa_shutdown(struct uart_port *port)
374ab4382d2SGreg Kroah-Hartman {
375ab4382d2SGreg Kroah-Hartman 	struct uart_pxa_port *up = (struct uart_pxa_port *)port;
376ab4382d2SGreg Kroah-Hartman 	unsigned long flags;
377ab4382d2SGreg Kroah-Hartman 
378ab4382d2SGreg Kroah-Hartman 	free_irq(up->port.irq, up);
379ab4382d2SGreg Kroah-Hartman 
380ab4382d2SGreg Kroah-Hartman 	/*
381ab4382d2SGreg Kroah-Hartman 	 * Disable interrupts from this port
382ab4382d2SGreg Kroah-Hartman 	 */
383ab4382d2SGreg Kroah-Hartman 	up->ier = 0;
384ab4382d2SGreg Kroah-Hartman 	serial_out(up, UART_IER, 0);
385ab4382d2SGreg Kroah-Hartman 
386ae3c3962SThomas Gleixner 	uart_port_lock_irqsave(&up->port, &flags);
387ab4382d2SGreg Kroah-Hartman 	up->port.mctrl &= ~TIOCM_OUT2;
388ab4382d2SGreg Kroah-Hartman 	serial_pxa_set_mctrl(&up->port, up->port.mctrl);
389ae3c3962SThomas Gleixner 	uart_port_unlock_irqrestore(&up->port, flags);
390ab4382d2SGreg Kroah-Hartman 
391ab4382d2SGreg Kroah-Hartman 	/*
392ab4382d2SGreg Kroah-Hartman 	 * Disable break condition and FIFOs
393ab4382d2SGreg Kroah-Hartman 	 */
394ab4382d2SGreg Kroah-Hartman 	serial_out(up, UART_LCR, serial_in(up, UART_LCR) & ~UART_LCR_SBC);
395ab4382d2SGreg Kroah-Hartman 	serial_out(up, UART_FCR, UART_FCR_ENABLE_FIFO |
396ab4382d2SGreg Kroah-Hartman 				  UART_FCR_CLEAR_RCVR |
397ab4382d2SGreg Kroah-Hartman 				  UART_FCR_CLEAR_XMIT);
398ab4382d2SGreg Kroah-Hartman 	serial_out(up, UART_FCR, 0);
399ab4382d2SGreg Kroah-Hartman }
400ab4382d2SGreg Kroah-Hartman 
401ab4382d2SGreg Kroah-Hartman static void
serial_pxa_set_termios(struct uart_port * port,struct ktermios * termios,const struct ktermios * old)402ab4382d2SGreg Kroah-Hartman serial_pxa_set_termios(struct uart_port *port, struct ktermios *termios,
403bec5b814SIlpo Järvinen 		       const struct ktermios *old)
404ab4382d2SGreg Kroah-Hartman {
405ab4382d2SGreg Kroah-Hartman 	struct uart_pxa_port *up = (struct uart_pxa_port *)port;
406ab4382d2SGreg Kroah-Hartman 	unsigned char cval, fcr = 0;
407ab4382d2SGreg Kroah-Hartman 	unsigned long flags;
408ab4382d2SGreg Kroah-Hartman 	unsigned int baud, quot;
409ab4382d2SGreg Kroah-Hartman 	unsigned int dll;
410ab4382d2SGreg Kroah-Hartman 
411988c5bbeSJiri Slaby 	cval = UART_LCR_WLEN(tty_get_char_size(termios->c_cflag));
412ab4382d2SGreg Kroah-Hartman 
413ab4382d2SGreg Kroah-Hartman 	if (termios->c_cflag & CSTOPB)
414ab4382d2SGreg Kroah-Hartman 		cval |= UART_LCR_STOP;
415ab4382d2SGreg Kroah-Hartman 	if (termios->c_cflag & PARENB)
416ab4382d2SGreg Kroah-Hartman 		cval |= UART_LCR_PARITY;
417ab4382d2SGreg Kroah-Hartman 	if (!(termios->c_cflag & PARODD))
418ab4382d2SGreg Kroah-Hartman 		cval |= UART_LCR_EPAR;
419ab4382d2SGreg Kroah-Hartman 
420ab4382d2SGreg Kroah-Hartman 	/*
421ab4382d2SGreg Kroah-Hartman 	 * Ask the core to calculate the divisor for us.
422ab4382d2SGreg Kroah-Hartman 	 */
423ab4382d2SGreg Kroah-Hartman 	baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk/16);
424ab4382d2SGreg Kroah-Hartman 	quot = uart_get_divisor(port, baud);
425ab4382d2SGreg Kroah-Hartman 
426ab4382d2SGreg Kroah-Hartman 	if ((up->port.uartclk / quot) < (2400 * 16))
427ab4382d2SGreg Kroah-Hartman 		fcr = UART_FCR_ENABLE_FIFO | UART_FCR_PXAR1;
428ab4382d2SGreg Kroah-Hartman 	else if ((up->port.uartclk / quot) < (230400 * 16))
429ab4382d2SGreg Kroah-Hartman 		fcr = UART_FCR_ENABLE_FIFO | UART_FCR_PXAR8;
430ab4382d2SGreg Kroah-Hartman 	else
431ab4382d2SGreg Kroah-Hartman 		fcr = UART_FCR_ENABLE_FIFO | UART_FCR_PXAR32;
432ab4382d2SGreg Kroah-Hartman 
433ab4382d2SGreg Kroah-Hartman 	/*
434ab4382d2SGreg Kroah-Hartman 	 * Ok, we're now changing the port state.  Do it with
435ab4382d2SGreg Kroah-Hartman 	 * interrupts disabled.
436ab4382d2SGreg Kroah-Hartman 	 */
437ae3c3962SThomas Gleixner 	uart_port_lock_irqsave(&up->port, &flags);
438ab4382d2SGreg Kroah-Hartman 
439ab4382d2SGreg Kroah-Hartman 	/*
440ab4382d2SGreg Kroah-Hartman 	 * Ensure the port will be enabled.
441ab4382d2SGreg Kroah-Hartman 	 * This is required especially for serial console.
442ab4382d2SGreg Kroah-Hartman 	 */
443ab4382d2SGreg Kroah-Hartman 	up->ier |= UART_IER_UUE;
444ab4382d2SGreg Kroah-Hartman 
445ab4382d2SGreg Kroah-Hartman 	/*
446ab4382d2SGreg Kroah-Hartman 	 * Update the per-port timeout.
447ab4382d2SGreg Kroah-Hartman 	 */
448ab4382d2SGreg Kroah-Hartman 	uart_update_timeout(port, termios->c_cflag, baud);
449ab4382d2SGreg Kroah-Hartman 
450ab4382d2SGreg Kroah-Hartman 	up->port.read_status_mask = UART_LSR_OE | UART_LSR_THRE | UART_LSR_DR;
451ab4382d2SGreg Kroah-Hartman 	if (termios->c_iflag & INPCK)
452ab4382d2SGreg Kroah-Hartman 		up->port.read_status_mask |= UART_LSR_FE | UART_LSR_PE;
453ef8b9ddcSPeter Hurley 	if (termios->c_iflag & (IGNBRK | BRKINT | PARMRK))
454ab4382d2SGreg Kroah-Hartman 		up->port.read_status_mask |= UART_LSR_BI;
455ab4382d2SGreg Kroah-Hartman 
456ab4382d2SGreg Kroah-Hartman 	/*
457ab4382d2SGreg Kroah-Hartman 	 * Characters to ignore
458ab4382d2SGreg Kroah-Hartman 	 */
459ab4382d2SGreg Kroah-Hartman 	up->port.ignore_status_mask = 0;
460ab4382d2SGreg Kroah-Hartman 	if (termios->c_iflag & IGNPAR)
461ab4382d2SGreg Kroah-Hartman 		up->port.ignore_status_mask |= UART_LSR_PE | UART_LSR_FE;
462ab4382d2SGreg Kroah-Hartman 	if (termios->c_iflag & IGNBRK) {
463ab4382d2SGreg Kroah-Hartman 		up->port.ignore_status_mask |= UART_LSR_BI;
464ab4382d2SGreg Kroah-Hartman 		/*
465ab4382d2SGreg Kroah-Hartman 		 * If we're ignoring parity and break indicators,
466ab4382d2SGreg Kroah-Hartman 		 * ignore overruns too (for real raw support).
467ab4382d2SGreg Kroah-Hartman 		 */
468ab4382d2SGreg Kroah-Hartman 		if (termios->c_iflag & IGNPAR)
469ab4382d2SGreg Kroah-Hartman 			up->port.ignore_status_mask |= UART_LSR_OE;
470ab4382d2SGreg Kroah-Hartman 	}
471ab4382d2SGreg Kroah-Hartman 
472ab4382d2SGreg Kroah-Hartman 	/*
473ab4382d2SGreg Kroah-Hartman 	 * ignore all characters if CREAD is not set
474ab4382d2SGreg Kroah-Hartman 	 */
475ab4382d2SGreg Kroah-Hartman 	if ((termios->c_cflag & CREAD) == 0)
476ab4382d2SGreg Kroah-Hartman 		up->port.ignore_status_mask |= UART_LSR_DR;
477ab4382d2SGreg Kroah-Hartman 
478ab4382d2SGreg Kroah-Hartman 	/*
479ab4382d2SGreg Kroah-Hartman 	 * CTS flow control flag and modem status interrupts
480ab4382d2SGreg Kroah-Hartman 	 */
481ab4382d2SGreg Kroah-Hartman 	up->ier &= ~UART_IER_MSI;
482ab4382d2SGreg Kroah-Hartman 	if (UART_ENABLE_MS(&up->port, termios->c_cflag))
483ab4382d2SGreg Kroah-Hartman 		up->ier |= UART_IER_MSI;
484ab4382d2SGreg Kroah-Hartman 
485ab4382d2SGreg Kroah-Hartman 	serial_out(up, UART_IER, up->ier);
486ab4382d2SGreg Kroah-Hartman 
487ab4382d2SGreg Kroah-Hartman 	if (termios->c_cflag & CRTSCTS)
488ab4382d2SGreg Kroah-Hartman 		up->mcr |= UART_MCR_AFE;
489ab4382d2SGreg Kroah-Hartman 	else
490ab4382d2SGreg Kroah-Hartman 		up->mcr &= ~UART_MCR_AFE;
491ab4382d2SGreg Kroah-Hartman 
492ab4382d2SGreg Kroah-Hartman 	serial_out(up, UART_LCR, cval | UART_LCR_DLAB);	/* set DLAB */
493ab4382d2SGreg Kroah-Hartman 	serial_out(up, UART_DLL, quot & 0xff);		/* LS of divisor */
494ab4382d2SGreg Kroah-Hartman 
495ab4382d2SGreg Kroah-Hartman 	/*
496ab4382d2SGreg Kroah-Hartman 	 * work around Errata #75 according to Intel(R) PXA27x Processor Family
497ab4382d2SGreg Kroah-Hartman 	 * Specification Update (Nov 2005)
498ab4382d2SGreg Kroah-Hartman 	 */
499ab4382d2SGreg Kroah-Hartman 	dll = serial_in(up, UART_DLL);
500ab4382d2SGreg Kroah-Hartman 	WARN_ON(dll != (quot & 0xff));
501ab4382d2SGreg Kroah-Hartman 
502ab4382d2SGreg Kroah-Hartman 	serial_out(up, UART_DLM, quot >> 8);		/* MS of divisor */
503ab4382d2SGreg Kroah-Hartman 	serial_out(up, UART_LCR, cval);			/* reset DLAB */
504ab4382d2SGreg Kroah-Hartman 	up->lcr = cval;					/* Save LCR */
505ab4382d2SGreg Kroah-Hartman 	serial_pxa_set_mctrl(&up->port, up->port.mctrl);
506ab4382d2SGreg Kroah-Hartman 	serial_out(up, UART_FCR, fcr);
507ae3c3962SThomas Gleixner 	uart_port_unlock_irqrestore(&up->port, flags);
508ab4382d2SGreg Kroah-Hartman }
509ab4382d2SGreg Kroah-Hartman 
510ab4382d2SGreg Kroah-Hartman static void
serial_pxa_pm(struct uart_port * port,unsigned int state,unsigned int oldstate)511ab4382d2SGreg Kroah-Hartman serial_pxa_pm(struct uart_port *port, unsigned int state,
512ab4382d2SGreg Kroah-Hartman 	      unsigned int oldstate)
513ab4382d2SGreg Kroah-Hartman {
514ab4382d2SGreg Kroah-Hartman 	struct uart_pxa_port *up = (struct uart_pxa_port *)port;
515ab4382d2SGreg Kroah-Hartman 
516ab4382d2SGreg Kroah-Hartman 	if (!state)
517fb8ebec0SPhilipp Zabel 		clk_prepare_enable(up->clk);
518ab4382d2SGreg Kroah-Hartman 	else
519fb8ebec0SPhilipp Zabel 		clk_disable_unprepare(up->clk);
520ab4382d2SGreg Kroah-Hartman }
521ab4382d2SGreg Kroah-Hartman 
serial_pxa_release_port(struct uart_port * port)522ab4382d2SGreg Kroah-Hartman static void serial_pxa_release_port(struct uart_port *port)
523ab4382d2SGreg Kroah-Hartman {
524ab4382d2SGreg Kroah-Hartman }
525ab4382d2SGreg Kroah-Hartman 
serial_pxa_request_port(struct uart_port * port)526ab4382d2SGreg Kroah-Hartman static int serial_pxa_request_port(struct uart_port *port)
527ab4382d2SGreg Kroah-Hartman {
528ab4382d2SGreg Kroah-Hartman 	return 0;
529ab4382d2SGreg Kroah-Hartman }
530ab4382d2SGreg Kroah-Hartman 
serial_pxa_config_port(struct uart_port * port,int flags)531ab4382d2SGreg Kroah-Hartman static void serial_pxa_config_port(struct uart_port *port, int flags)
532ab4382d2SGreg Kroah-Hartman {
533ab4382d2SGreg Kroah-Hartman 	struct uart_pxa_port *up = (struct uart_pxa_port *)port;
534ab4382d2SGreg Kroah-Hartman 	up->port.type = PORT_PXA;
535ab4382d2SGreg Kroah-Hartman }
536ab4382d2SGreg Kroah-Hartman 
537ab4382d2SGreg Kroah-Hartman static int
serial_pxa_verify_port(struct uart_port * port,struct serial_struct * ser)538ab4382d2SGreg Kroah-Hartman serial_pxa_verify_port(struct uart_port *port, struct serial_struct *ser)
539ab4382d2SGreg Kroah-Hartman {
540ab4382d2SGreg Kroah-Hartman 	/* we don't want the core code to modify any port params */
541ab4382d2SGreg Kroah-Hartman 	return -EINVAL;
542ab4382d2SGreg Kroah-Hartman }
543ab4382d2SGreg Kroah-Hartman 
544ab4382d2SGreg Kroah-Hartman static const char *
serial_pxa_type(struct uart_port * port)545ab4382d2SGreg Kroah-Hartman serial_pxa_type(struct uart_port *port)
546ab4382d2SGreg Kroah-Hartman {
547ab4382d2SGreg Kroah-Hartman 	struct uart_pxa_port *up = (struct uart_pxa_port *)port;
548ab4382d2SGreg Kroah-Hartman 	return up->name;
549ab4382d2SGreg Kroah-Hartman }
550ab4382d2SGreg Kroah-Hartman 
551ab4382d2SGreg Kroah-Hartman static struct uart_pxa_port *serial_pxa_ports[4];
552ab4382d2SGreg Kroah-Hartman static struct uart_driver serial_pxa_reg;
553ab4382d2SGreg Kroah-Hartman 
554ab4382d2SGreg Kroah-Hartman #ifdef CONFIG_SERIAL_PXA_CONSOLE
555ab4382d2SGreg Kroah-Hartman 
556ab4382d2SGreg Kroah-Hartman /*
557ab4382d2SGreg Kroah-Hartman  *	Wait for transmitter & holding register to empty
558ab4382d2SGreg Kroah-Hartman  */
wait_for_xmitr(struct uart_pxa_port * up)559be9ae5d9SDenys Vlasenko static void wait_for_xmitr(struct uart_pxa_port *up)
560ab4382d2SGreg Kroah-Hartman {
561ab4382d2SGreg Kroah-Hartman 	unsigned int status, tmout = 10000;
562ab4382d2SGreg Kroah-Hartman 
563ab4382d2SGreg Kroah-Hartman 	/* Wait up to 10ms for the character(s) to be sent. */
564ab4382d2SGreg Kroah-Hartman 	do {
565ab4382d2SGreg Kroah-Hartman 		status = serial_in(up, UART_LSR);
566ab4382d2SGreg Kroah-Hartman 
567ab4382d2SGreg Kroah-Hartman 		if (status & UART_LSR_BI)
568ab4382d2SGreg Kroah-Hartman 			up->lsr_break_flag = UART_LSR_BI;
569ab4382d2SGreg Kroah-Hartman 
570ab4382d2SGreg Kroah-Hartman 		if (--tmout == 0)
571ab4382d2SGreg Kroah-Hartman 			break;
572ab4382d2SGreg Kroah-Hartman 		udelay(1);
57334619de1SIlpo Järvinen 	} while (!uart_lsr_tx_empty(status));
574ab4382d2SGreg Kroah-Hartman 
575ab4382d2SGreg Kroah-Hartman 	/* Wait up to 1s for flow control if necessary */
576ab4382d2SGreg Kroah-Hartman 	if (up->port.flags & UPF_CONS_FLOW) {
577ab4382d2SGreg Kroah-Hartman 		tmout = 1000000;
578ab4382d2SGreg Kroah-Hartman 		while (--tmout &&
579ab4382d2SGreg Kroah-Hartman 		       ((serial_in(up, UART_MSR) & UART_MSR_CTS) == 0))
580ab4382d2SGreg Kroah-Hartman 			udelay(1);
581ab4382d2SGreg Kroah-Hartman 	}
582ab4382d2SGreg Kroah-Hartman }
583ab4382d2SGreg Kroah-Hartman 
serial_pxa_console_putchar(struct uart_port * port,unsigned char ch)5843f8bab17SJiri Slaby static void serial_pxa_console_putchar(struct uart_port *port, unsigned char ch)
585ab4382d2SGreg Kroah-Hartman {
586ab4382d2SGreg Kroah-Hartman 	struct uart_pxa_port *up = (struct uart_pxa_port *)port;
587ab4382d2SGreg Kroah-Hartman 
588ab4382d2SGreg Kroah-Hartman 	wait_for_xmitr(up);
589ab4382d2SGreg Kroah-Hartman 	serial_out(up, UART_TX, ch);
590ab4382d2SGreg Kroah-Hartman }
591ab4382d2SGreg Kroah-Hartman 
592ab4382d2SGreg Kroah-Hartman /*
593ab4382d2SGreg Kroah-Hartman  * Print a string to the serial port trying not to disturb
594ab4382d2SGreg Kroah-Hartman  * any possible real use of the port...
595ab4382d2SGreg Kroah-Hartman  *
596ab4382d2SGreg Kroah-Hartman  *	The console_lock must be held when we get here.
597ab4382d2SGreg Kroah-Hartman  */
598ab4382d2SGreg Kroah-Hartman static void
serial_pxa_console_write(struct console * co,const char * s,unsigned int count)599ab4382d2SGreg Kroah-Hartman serial_pxa_console_write(struct console *co, const char *s, unsigned int count)
600ab4382d2SGreg Kroah-Hartman {
601ab4382d2SGreg Kroah-Hartman 	struct uart_pxa_port *up = serial_pxa_ports[co->index];
602ab4382d2SGreg Kroah-Hartman 	unsigned int ier;
603cfe275c2SChao Xie 	unsigned long flags;
604cfe275c2SChao Xie 	int locked = 1;
605ab4382d2SGreg Kroah-Hartman 
6069429ccbfSYi Zhang 	clk_enable(up->clk);
607*51f7ed07SSebastian Andrzej Siewior 	if (oops_in_progress)
608*51f7ed07SSebastian Andrzej Siewior 		locked = uart_port_trylock_irqsave(&up->port, &flags);
609cfe275c2SChao Xie 	else
610*51f7ed07SSebastian Andrzej Siewior 		uart_port_lock_irqsave(&up->port, &flags);
611cfe275c2SChao Xie 
612ab4382d2SGreg Kroah-Hartman 	/*
613ab4382d2SGreg Kroah-Hartman 	 *	First save the IER then disable the interrupts
614ab4382d2SGreg Kroah-Hartman 	 */
615ab4382d2SGreg Kroah-Hartman 	ier = serial_in(up, UART_IER);
616ab4382d2SGreg Kroah-Hartman 	serial_out(up, UART_IER, UART_IER_UUE);
617ab4382d2SGreg Kroah-Hartman 
618ab4382d2SGreg Kroah-Hartman 	uart_console_write(&up->port, s, count, serial_pxa_console_putchar);
619ab4382d2SGreg Kroah-Hartman 
620ab4382d2SGreg Kroah-Hartman 	/*
621ab4382d2SGreg Kroah-Hartman 	 *	Finally, wait for transmitter to become empty
622ab4382d2SGreg Kroah-Hartman 	 *	and restore the IER
623ab4382d2SGreg Kroah-Hartman 	 */
624ab4382d2SGreg Kroah-Hartman 	wait_for_xmitr(up);
625ab4382d2SGreg Kroah-Hartman 	serial_out(up, UART_IER, ier);
626ab4382d2SGreg Kroah-Hartman 
627cfe275c2SChao Xie 	if (locked)
628*51f7ed07SSebastian Andrzej Siewior 		uart_port_unlock_irqrestore(&up->port, flags);
6299429ccbfSYi Zhang 	clk_disable(up->clk);
630ab4382d2SGreg Kroah-Hartman }
631ab4382d2SGreg Kroah-Hartman 
632e1a9c179SDenis V. Lunev #ifdef CONFIG_CONSOLE_POLL
633e1a9c179SDenis V. Lunev /*
634e1a9c179SDenis V. Lunev  * Console polling routines for writing and reading from the uart while
635e1a9c179SDenis V. Lunev  * in an interrupt or debug context.
636e1a9c179SDenis V. Lunev  */
637e1a9c179SDenis V. Lunev 
serial_pxa_get_poll_char(struct uart_port * port)638e1a9c179SDenis V. Lunev static int serial_pxa_get_poll_char(struct uart_port *port)
639e1a9c179SDenis V. Lunev {
640e1a9c179SDenis V. Lunev 	struct uart_pxa_port *up = (struct uart_pxa_port *)port;
641e1a9c179SDenis V. Lunev 	unsigned char lsr = serial_in(up, UART_LSR);
642e1a9c179SDenis V. Lunev 
643e1a9c179SDenis V. Lunev 	while (!(lsr & UART_LSR_DR))
644e1a9c179SDenis V. Lunev 		lsr = serial_in(up, UART_LSR);
645e1a9c179SDenis V. Lunev 
646e1a9c179SDenis V. Lunev 	return serial_in(up, UART_RX);
647e1a9c179SDenis V. Lunev }
648e1a9c179SDenis V. Lunev 
649e1a9c179SDenis V. Lunev 
serial_pxa_put_poll_char(struct uart_port * port,unsigned char c)650e1a9c179SDenis V. Lunev static void serial_pxa_put_poll_char(struct uart_port *port,
651e1a9c179SDenis V. Lunev 			 unsigned char c)
652e1a9c179SDenis V. Lunev {
653e1a9c179SDenis V. Lunev 	unsigned int ier;
654e1a9c179SDenis V. Lunev 	struct uart_pxa_port *up = (struct uart_pxa_port *)port;
655e1a9c179SDenis V. Lunev 
656e1a9c179SDenis V. Lunev 	/*
657e1a9c179SDenis V. Lunev 	 *	First save the IER then disable the interrupts
658e1a9c179SDenis V. Lunev 	 */
659e1a9c179SDenis V. Lunev 	ier = serial_in(up, UART_IER);
660e1a9c179SDenis V. Lunev 	serial_out(up, UART_IER, UART_IER_UUE);
661e1a9c179SDenis V. Lunev 
662e1a9c179SDenis V. Lunev 	wait_for_xmitr(up);
663e1a9c179SDenis V. Lunev 	/*
664e1a9c179SDenis V. Lunev 	 *	Send the character out.
665e1a9c179SDenis V. Lunev 	 */
666e1a9c179SDenis V. Lunev 	serial_out(up, UART_TX, c);
667e1a9c179SDenis V. Lunev 
668e1a9c179SDenis V. Lunev 	/*
669e1a9c179SDenis V. Lunev 	 *	Finally, wait for transmitter to become empty
670e1a9c179SDenis V. Lunev 	 *	and restore the IER
671e1a9c179SDenis V. Lunev 	 */
672e1a9c179SDenis V. Lunev 	wait_for_xmitr(up);
673e1a9c179SDenis V. Lunev 	serial_out(up, UART_IER, ier);
674e1a9c179SDenis V. Lunev }
675e1a9c179SDenis V. Lunev 
676e1a9c179SDenis V. Lunev #endif /* CONFIG_CONSOLE_POLL */
677e1a9c179SDenis V. Lunev 
678ab4382d2SGreg Kroah-Hartman static int __init
serial_pxa_console_setup(struct console * co,char * options)679ab4382d2SGreg Kroah-Hartman serial_pxa_console_setup(struct console *co, char *options)
680ab4382d2SGreg Kroah-Hartman {
681ab4382d2SGreg Kroah-Hartman 	struct uart_pxa_port *up;
682ab4382d2SGreg Kroah-Hartman 	int baud = 9600;
683ab4382d2SGreg Kroah-Hartman 	int bits = 8;
684ab4382d2SGreg Kroah-Hartman 	int parity = 'n';
685ab4382d2SGreg Kroah-Hartman 	int flow = 'n';
686ab4382d2SGreg Kroah-Hartman 
687ab4382d2SGreg Kroah-Hartman 	if (co->index == -1 || co->index >= serial_pxa_reg.nr)
688ab4382d2SGreg Kroah-Hartman 		co->index = 0;
689ab4382d2SGreg Kroah-Hartman 	up = serial_pxa_ports[co->index];
690ab4382d2SGreg Kroah-Hartman 	if (!up)
691ab4382d2SGreg Kroah-Hartman 		return -ENODEV;
692ab4382d2SGreg Kroah-Hartman 
693ab4382d2SGreg Kroah-Hartman 	if (options)
694ab4382d2SGreg Kroah-Hartman 		uart_parse_options(options, &baud, &parity, &bits, &flow);
695ab4382d2SGreg Kroah-Hartman 
696ab4382d2SGreg Kroah-Hartman 	return uart_set_options(&up->port, co, baud, parity, bits, flow);
697ab4382d2SGreg Kroah-Hartman }
698ab4382d2SGreg Kroah-Hartman 
699ab4382d2SGreg Kroah-Hartman static struct console serial_pxa_console = {
700ab4382d2SGreg Kroah-Hartman 	.name		= "ttyS",
701ab4382d2SGreg Kroah-Hartman 	.write		= serial_pxa_console_write,
702ab4382d2SGreg Kroah-Hartman 	.device		= uart_console_device,
703ab4382d2SGreg Kroah-Hartman 	.setup		= serial_pxa_console_setup,
704ab4382d2SGreg Kroah-Hartman 	.flags		= CON_PRINTBUFFER,
705ab4382d2SGreg Kroah-Hartman 	.index		= -1,
706ab4382d2SGreg Kroah-Hartman 	.data		= &serial_pxa_reg,
707ab4382d2SGreg Kroah-Hartman };
708ab4382d2SGreg Kroah-Hartman 
709ab4382d2SGreg Kroah-Hartman #define PXA_CONSOLE	&serial_pxa_console
710ab4382d2SGreg Kroah-Hartman #else
711ab4382d2SGreg Kroah-Hartman #define PXA_CONSOLE	NULL
712ab4382d2SGreg Kroah-Hartman #endif
713ab4382d2SGreg Kroah-Hartman 
7142331e068SBhumika Goyal static const struct uart_ops serial_pxa_pops = {
715ab4382d2SGreg Kroah-Hartman 	.tx_empty	= serial_pxa_tx_empty,
716ab4382d2SGreg Kroah-Hartman 	.set_mctrl	= serial_pxa_set_mctrl,
717ab4382d2SGreg Kroah-Hartman 	.get_mctrl	= serial_pxa_get_mctrl,
718ab4382d2SGreg Kroah-Hartman 	.stop_tx	= serial_pxa_stop_tx,
719ab4382d2SGreg Kroah-Hartman 	.start_tx	= serial_pxa_start_tx,
720ab4382d2SGreg Kroah-Hartman 	.stop_rx	= serial_pxa_stop_rx,
721ab4382d2SGreg Kroah-Hartman 	.enable_ms	= serial_pxa_enable_ms,
722ab4382d2SGreg Kroah-Hartman 	.break_ctl	= serial_pxa_break_ctl,
723ab4382d2SGreg Kroah-Hartman 	.startup	= serial_pxa_startup,
724ab4382d2SGreg Kroah-Hartman 	.shutdown	= serial_pxa_shutdown,
725ab4382d2SGreg Kroah-Hartman 	.set_termios	= serial_pxa_set_termios,
726ab4382d2SGreg Kroah-Hartman 	.pm		= serial_pxa_pm,
727ab4382d2SGreg Kroah-Hartman 	.type		= serial_pxa_type,
728ab4382d2SGreg Kroah-Hartman 	.release_port	= serial_pxa_release_port,
729ab4382d2SGreg Kroah-Hartman 	.request_port	= serial_pxa_request_port,
730ab4382d2SGreg Kroah-Hartman 	.config_port	= serial_pxa_config_port,
731ab4382d2SGreg Kroah-Hartman 	.verify_port	= serial_pxa_verify_port,
7322ee881b7SArnd Bergmann #if defined(CONFIG_CONSOLE_POLL) && defined(CONFIG_SERIAL_PXA_CONSOLE)
733e1a9c179SDenis V. Lunev 	.poll_get_char = serial_pxa_get_poll_char,
734e1a9c179SDenis V. Lunev 	.poll_put_char = serial_pxa_put_poll_char,
735e1a9c179SDenis V. Lunev #endif
736ab4382d2SGreg Kroah-Hartman };
737ab4382d2SGreg Kroah-Hartman 
738ab4382d2SGreg Kroah-Hartman static struct uart_driver serial_pxa_reg = {
739ab4382d2SGreg Kroah-Hartman 	.owner		= THIS_MODULE,
740ab4382d2SGreg Kroah-Hartman 	.driver_name	= "PXA serial",
741ab4382d2SGreg Kroah-Hartman 	.dev_name	= "ttyS",
742ab4382d2SGreg Kroah-Hartman 	.major		= TTY_MAJOR,
743ab4382d2SGreg Kroah-Hartman 	.minor		= 64,
744ab4382d2SGreg Kroah-Hartman 	.nr		= 4,
745ab4382d2SGreg Kroah-Hartman 	.cons		= PXA_CONSOLE,
746ab4382d2SGreg Kroah-Hartman };
747ab4382d2SGreg Kroah-Hartman 
748ab4382d2SGreg Kroah-Hartman #ifdef CONFIG_PM
serial_pxa_suspend(struct device * dev)749ab4382d2SGreg Kroah-Hartman static int serial_pxa_suspend(struct device *dev)
750ab4382d2SGreg Kroah-Hartman {
751ab4382d2SGreg Kroah-Hartman         struct uart_pxa_port *sport = dev_get_drvdata(dev);
752ab4382d2SGreg Kroah-Hartman 
753ab4382d2SGreg Kroah-Hartman         if (sport)
754ab4382d2SGreg Kroah-Hartman                 uart_suspend_port(&serial_pxa_reg, &sport->port);
755ab4382d2SGreg Kroah-Hartman 
756ab4382d2SGreg Kroah-Hartman         return 0;
757ab4382d2SGreg Kroah-Hartman }
758ab4382d2SGreg Kroah-Hartman 
serial_pxa_resume(struct device * dev)759ab4382d2SGreg Kroah-Hartman static int serial_pxa_resume(struct device *dev)
760ab4382d2SGreg Kroah-Hartman {
761ab4382d2SGreg Kroah-Hartman         struct uart_pxa_port *sport = dev_get_drvdata(dev);
762ab4382d2SGreg Kroah-Hartman 
763ab4382d2SGreg Kroah-Hartman         if (sport)
764ab4382d2SGreg Kroah-Hartman                 uart_resume_port(&serial_pxa_reg, &sport->port);
765ab4382d2SGreg Kroah-Hartman 
766ab4382d2SGreg Kroah-Hartman         return 0;
767ab4382d2SGreg Kroah-Hartman }
768ab4382d2SGreg Kroah-Hartman 
769ab4382d2SGreg Kroah-Hartman static const struct dev_pm_ops serial_pxa_pm_ops = {
770ab4382d2SGreg Kroah-Hartman 	.suspend	= serial_pxa_suspend,
771ab4382d2SGreg Kroah-Hartman 	.resume		= serial_pxa_resume,
772ab4382d2SGreg Kroah-Hartman };
773ab4382d2SGreg Kroah-Hartman #endif
774ab4382d2SGreg Kroah-Hartman 
775ed0bb232SFabian Frederick static const struct of_device_id serial_pxa_dt_ids[] = {
776699c20f3SHaojian Zhuang 	{ .compatible = "mrvl,pxa-uart", },
777699c20f3SHaojian Zhuang 	{ .compatible = "mrvl,mmp-uart", },
778699c20f3SHaojian Zhuang 	{}
779699c20f3SHaojian Zhuang };
780699c20f3SHaojian Zhuang 
serial_pxa_probe_dt(struct platform_device * pdev,struct uart_pxa_port * sport)781699c20f3SHaojian Zhuang static int serial_pxa_probe_dt(struct platform_device *pdev,
782699c20f3SHaojian Zhuang 			       struct uart_pxa_port *sport)
783699c20f3SHaojian Zhuang {
784699c20f3SHaojian Zhuang 	struct device_node *np = pdev->dev.of_node;
785699c20f3SHaojian Zhuang 	int ret;
786699c20f3SHaojian Zhuang 
787699c20f3SHaojian Zhuang 	if (!np)
788699c20f3SHaojian Zhuang 		return 1;
789699c20f3SHaojian Zhuang 
790699c20f3SHaojian Zhuang 	ret = of_alias_get_id(np, "serial");
791699c20f3SHaojian Zhuang 	if (ret < 0) {
792699c20f3SHaojian Zhuang 		dev_err(&pdev->dev, "failed to get alias id, errno %d\n", ret);
793699c20f3SHaojian Zhuang 		return ret;
794699c20f3SHaojian Zhuang 	}
795699c20f3SHaojian Zhuang 	sport->port.line = ret;
796699c20f3SHaojian Zhuang 	return 0;
797699c20f3SHaojian Zhuang }
798699c20f3SHaojian Zhuang 
serial_pxa_probe(struct platform_device * dev)799ab4382d2SGreg Kroah-Hartman static int serial_pxa_probe(struct platform_device *dev)
800ab4382d2SGreg Kroah-Hartman {
801ab4382d2SGreg Kroah-Hartman 	struct uart_pxa_port *sport;
8026050efacSLad Prabhakar 	struct resource *mmres;
803ab4382d2SGreg Kroah-Hartman 	int ret;
8046050efacSLad Prabhakar 	int irq;
805ab4382d2SGreg Kroah-Hartman 
806ab4382d2SGreg Kroah-Hartman 	mmres = platform_get_resource(dev, IORESOURCE_MEM, 0);
8076050efacSLad Prabhakar 	if (!mmres)
808ab4382d2SGreg Kroah-Hartman 		return -ENODEV;
809ab4382d2SGreg Kroah-Hartman 
8106050efacSLad Prabhakar 	irq = platform_get_irq(dev, 0);
8116050efacSLad Prabhakar 	if (irq < 0)
8126050efacSLad Prabhakar 		return irq;
8136050efacSLad Prabhakar 
814ab4382d2SGreg Kroah-Hartman 	sport = kzalloc(sizeof(struct uart_pxa_port), GFP_KERNEL);
815ab4382d2SGreg Kroah-Hartman 	if (!sport)
816ab4382d2SGreg Kroah-Hartman 		return -ENOMEM;
817ab4382d2SGreg Kroah-Hartman 
818ab4382d2SGreg Kroah-Hartman 	sport->clk = clk_get(&dev->dev, NULL);
819ab4382d2SGreg Kroah-Hartman 	if (IS_ERR(sport->clk)) {
820ab4382d2SGreg Kroah-Hartman 		ret = PTR_ERR(sport->clk);
821ab4382d2SGreg Kroah-Hartman 		goto err_free;
822ab4382d2SGreg Kroah-Hartman 	}
823ab4382d2SGreg Kroah-Hartman 
8249429ccbfSYi Zhang 	ret = clk_prepare(sport->clk);
8259429ccbfSYi Zhang 	if (ret) {
8269429ccbfSYi Zhang 		clk_put(sport->clk);
8279429ccbfSYi Zhang 		goto err_free;
8289429ccbfSYi Zhang 	}
8299429ccbfSYi Zhang 
830ab4382d2SGreg Kroah-Hartman 	sport->port.type = PORT_PXA;
831ab4382d2SGreg Kroah-Hartman 	sport->port.iotype = UPIO_MEM;
832ab4382d2SGreg Kroah-Hartman 	sport->port.mapbase = mmres->start;
8336050efacSLad Prabhakar 	sport->port.irq = irq;
834ab4382d2SGreg Kroah-Hartman 	sport->port.fifosize = 64;
835ab4382d2SGreg Kroah-Hartman 	sport->port.ops = &serial_pxa_pops;
836ab4382d2SGreg Kroah-Hartman 	sport->port.dev = &dev->dev;
837ab4382d2SGreg Kroah-Hartman 	sport->port.flags = UPF_IOREMAP | UPF_BOOT_AUTOCONF;
838ab4382d2SGreg Kroah-Hartman 	sport->port.uartclk = clk_get_rate(sport->clk);
83931b3bee4SDmitry Safonov 	sport->port.has_sysrq = IS_ENABLED(CONFIG_SERIAL_PXA_CONSOLE);
840ab4382d2SGreg Kroah-Hartman 
841699c20f3SHaojian Zhuang 	ret = serial_pxa_probe_dt(dev, sport);
842699c20f3SHaojian Zhuang 	if (ret > 0)
843699c20f3SHaojian Zhuang 		sport->port.line = dev->id;
844699c20f3SHaojian Zhuang 	else if (ret < 0)
845699c20f3SHaojian Zhuang 		goto err_clk;
846afc7851fSGeert Uytterhoeven 	if (sport->port.line >= ARRAY_SIZE(serial_pxa_ports)) {
847afc7851fSGeert Uytterhoeven 		dev_err(&dev->dev, "serial%d out of range\n", sport->port.line);
84895a0e656SChristophe JAILLET 		ret = -EINVAL;
84995a0e656SChristophe JAILLET 		goto err_clk;
850afc7851fSGeert Uytterhoeven 	}
851699c20f3SHaojian Zhuang 	snprintf(sport->name, PXA_NAME_LEN - 1, "UART%d", sport->port.line + 1);
852ab4382d2SGreg Kroah-Hartman 
85328f65c11SJoe Perches 	sport->port.membase = ioremap(mmres->start, resource_size(mmres));
854ab4382d2SGreg Kroah-Hartman 	if (!sport->port.membase) {
855ab4382d2SGreg Kroah-Hartman 		ret = -ENOMEM;
856ab4382d2SGreg Kroah-Hartman 		goto err_clk;
857ab4382d2SGreg Kroah-Hartman 	}
858ab4382d2SGreg Kroah-Hartman 
859699c20f3SHaojian Zhuang 	serial_pxa_ports[sport->port.line] = sport;
860ab4382d2SGreg Kroah-Hartman 
861ab4382d2SGreg Kroah-Hartman 	uart_add_one_port(&serial_pxa_reg, &sport->port);
862ab4382d2SGreg Kroah-Hartman 	platform_set_drvdata(dev, sport);
863ab4382d2SGreg Kroah-Hartman 
864ab4382d2SGreg Kroah-Hartman 	return 0;
865ab4382d2SGreg Kroah-Hartman 
866ab4382d2SGreg Kroah-Hartman  err_clk:
8679429ccbfSYi Zhang 	clk_unprepare(sport->clk);
868ab4382d2SGreg Kroah-Hartman 	clk_put(sport->clk);
869ab4382d2SGreg Kroah-Hartman  err_free:
870ab4382d2SGreg Kroah-Hartman 	kfree(sport);
871ab4382d2SGreg Kroah-Hartman 	return ret;
872ab4382d2SGreg Kroah-Hartman }
873ab4382d2SGreg Kroah-Hartman 
874ab4382d2SGreg Kroah-Hartman static struct platform_driver serial_pxa_driver = {
875ab4382d2SGreg Kroah-Hartman         .probe          = serial_pxa_probe,
876ab4382d2SGreg Kroah-Hartman 
877ab4382d2SGreg Kroah-Hartman 	.driver		= {
878ab4382d2SGreg Kroah-Hartman 	        .name	= "pxa2xx-uart",
879ab4382d2SGreg Kroah-Hartman #ifdef CONFIG_PM
880ab4382d2SGreg Kroah-Hartman 		.pm	= &serial_pxa_pm_ops,
881ab4382d2SGreg Kroah-Hartman #endif
882ca16c5a3SPaul Gortmaker 		.suppress_bind_attrs = true,
883699c20f3SHaojian Zhuang 		.of_match_table = serial_pxa_dt_ids,
884ab4382d2SGreg Kroah-Hartman 	},
885ab4382d2SGreg Kroah-Hartman };
886ab4382d2SGreg Kroah-Hartman 
887ab28f51cSSergey Yanovich 
888ab28f51cSSergey Yanovich /* 8250 driver for PXA serial ports should be used */
serial_pxa_init(void)8899de4153dSArnd Bergmann static int __init serial_pxa_init(void)
890ab4382d2SGreg Kroah-Hartman {
891ab4382d2SGreg Kroah-Hartman 	int ret;
892ab4382d2SGreg Kroah-Hartman 
893ab4382d2SGreg Kroah-Hartman 	ret = uart_register_driver(&serial_pxa_reg);
894ab4382d2SGreg Kroah-Hartman 	if (ret != 0)
895ab4382d2SGreg Kroah-Hartman 		return ret;
896ab4382d2SGreg Kroah-Hartman 
897ab4382d2SGreg Kroah-Hartman 	ret = platform_driver_register(&serial_pxa_driver);
898ab4382d2SGreg Kroah-Hartman 	if (ret != 0)
899ab4382d2SGreg Kroah-Hartman 		uart_unregister_driver(&serial_pxa_reg);
900ab4382d2SGreg Kroah-Hartman 
901ab4382d2SGreg Kroah-Hartman 	return ret;
902ab4382d2SGreg Kroah-Hartman }
903ca16c5a3SPaul Gortmaker device_initcall(serial_pxa_init);
904