xref: /linux/drivers/tty/serial/pxa.c (revision 6c62cc0db7d9f459c7453adc88a30ab6a626f15c)
1ab4382d2SGreg Kroah-Hartman /*
2ab4382d2SGreg Kroah-Hartman  *  Based on drivers/serial/8250.c by Russell King.
3ab4382d2SGreg Kroah-Hartman  *
4ab4382d2SGreg Kroah-Hartman  *  Author:	Nicolas Pitre
5ab4382d2SGreg Kroah-Hartman  *  Created:	Feb 20, 2003
6ab4382d2SGreg Kroah-Hartman  *  Copyright:	(C) 2003 Monta Vista Software, Inc.
7ab4382d2SGreg Kroah-Hartman  *
8ab4382d2SGreg Kroah-Hartman  * This program is free software; you can redistribute it and/or modify
9ab4382d2SGreg Kroah-Hartman  * it under the terms of the GNU General Public License as published by
10ab4382d2SGreg Kroah-Hartman  * the Free Software Foundation; either version 2 of the License, or
11ab4382d2SGreg Kroah-Hartman  * (at your option) any later version.
12ab4382d2SGreg Kroah-Hartman  *
13ab4382d2SGreg Kroah-Hartman  * Note 1: This driver is made separate from the already too overloaded
14ab4382d2SGreg Kroah-Hartman  * 8250.c because it needs some kirks of its own and that'll make it
15ab4382d2SGreg Kroah-Hartman  * easier to add DMA support.
16ab4382d2SGreg Kroah-Hartman  *
17ab4382d2SGreg Kroah-Hartman  * Note 2: I'm too sick of device allocation policies for serial ports.
18ab4382d2SGreg Kroah-Hartman  * If someone else wants to request an "official" allocation of major/minor
19ab4382d2SGreg Kroah-Hartman  * for this driver please be my guest.  And don't forget that new hardware
20ab4382d2SGreg Kroah-Hartman  * to come from Intel might have more than 3 or 4 of those UARTs.  Let's
21ab4382d2SGreg Kroah-Hartman  * hope for a better port registration and dynamic device allocation scheme
22ab4382d2SGreg Kroah-Hartman  * with the serial core maintainer satisfaction to appear soon.
23ab4382d2SGreg Kroah-Hartman  */
24ab4382d2SGreg Kroah-Hartman 
25ab4382d2SGreg Kroah-Hartman 
26ab4382d2SGreg Kroah-Hartman #if defined(CONFIG_SERIAL_PXA_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)
27ab4382d2SGreg Kroah-Hartman #define SUPPORT_SYSRQ
28ab4382d2SGreg Kroah-Hartman #endif
29ab4382d2SGreg Kroah-Hartman 
30ab4382d2SGreg Kroah-Hartman #include <linux/module.h>
31ab4382d2SGreg Kroah-Hartman #include <linux/ioport.h>
32ab4382d2SGreg Kroah-Hartman #include <linux/init.h>
33ab4382d2SGreg Kroah-Hartman #include <linux/console.h>
34ab4382d2SGreg Kroah-Hartman #include <linux/sysrq.h>
35ab4382d2SGreg Kroah-Hartman #include <linux/serial_reg.h>
36ab4382d2SGreg Kroah-Hartman #include <linux/circ_buf.h>
37ab4382d2SGreg Kroah-Hartman #include <linux/delay.h>
38ab4382d2SGreg Kroah-Hartman #include <linux/interrupt.h>
39699c20f3SHaojian Zhuang #include <linux/of.h>
40ab4382d2SGreg Kroah-Hartman #include <linux/platform_device.h>
41ab4382d2SGreg Kroah-Hartman #include <linux/tty.h>
42ab4382d2SGreg Kroah-Hartman #include <linux/tty_flip.h>
43ab4382d2SGreg Kroah-Hartman #include <linux/serial_core.h>
44ab4382d2SGreg Kroah-Hartman #include <linux/clk.h>
45ab4382d2SGreg Kroah-Hartman #include <linux/io.h>
46ab4382d2SGreg Kroah-Hartman #include <linux/slab.h>
47ab4382d2SGreg Kroah-Hartman 
48699c20f3SHaojian Zhuang #define PXA_NAME_LEN		8
49699c20f3SHaojian Zhuang 
50ab4382d2SGreg Kroah-Hartman struct uart_pxa_port {
51ab4382d2SGreg Kroah-Hartman 	struct uart_port        port;
52ab4382d2SGreg Kroah-Hartman 	unsigned char           ier;
53ab4382d2SGreg Kroah-Hartman 	unsigned char           lcr;
54ab4382d2SGreg Kroah-Hartman 	unsigned char           mcr;
55ab4382d2SGreg Kroah-Hartman 	unsigned int            lsr_break_flag;
56ab4382d2SGreg Kroah-Hartman 	struct clk		*clk;
57699c20f3SHaojian Zhuang 	char			name[PXA_NAME_LEN];
58ab4382d2SGreg Kroah-Hartman };
59ab4382d2SGreg Kroah-Hartman 
60ab4382d2SGreg Kroah-Hartman static inline unsigned int serial_in(struct uart_pxa_port *up, int offset)
61ab4382d2SGreg Kroah-Hartman {
62ab4382d2SGreg Kroah-Hartman 	offset <<= 2;
63ab4382d2SGreg Kroah-Hartman 	return readl(up->port.membase + offset);
64ab4382d2SGreg Kroah-Hartman }
65ab4382d2SGreg Kroah-Hartman 
66ab4382d2SGreg Kroah-Hartman static inline void serial_out(struct uart_pxa_port *up, int offset, int value)
67ab4382d2SGreg Kroah-Hartman {
68ab4382d2SGreg Kroah-Hartman 	offset <<= 2;
69ab4382d2SGreg Kroah-Hartman 	writel(value, up->port.membase + offset);
70ab4382d2SGreg Kroah-Hartman }
71ab4382d2SGreg Kroah-Hartman 
72ab4382d2SGreg Kroah-Hartman static void serial_pxa_enable_ms(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 	up->ier |= UART_IER_MSI;
77ab4382d2SGreg Kroah-Hartman 	serial_out(up, UART_IER, up->ier);
78ab4382d2SGreg Kroah-Hartman }
79ab4382d2SGreg Kroah-Hartman 
80ab4382d2SGreg Kroah-Hartman static void serial_pxa_stop_tx(struct uart_port *port)
81ab4382d2SGreg Kroah-Hartman {
82ab4382d2SGreg Kroah-Hartman 	struct uart_pxa_port *up = (struct uart_pxa_port *)port;
83ab4382d2SGreg Kroah-Hartman 
84ab4382d2SGreg Kroah-Hartman 	if (up->ier & UART_IER_THRI) {
85ab4382d2SGreg Kroah-Hartman 		up->ier &= ~UART_IER_THRI;
86ab4382d2SGreg Kroah-Hartman 		serial_out(up, UART_IER, up->ier);
87ab4382d2SGreg Kroah-Hartman 	}
88ab4382d2SGreg Kroah-Hartman }
89ab4382d2SGreg Kroah-Hartman 
90ab4382d2SGreg Kroah-Hartman static void serial_pxa_stop_rx(struct uart_port *port)
91ab4382d2SGreg Kroah-Hartman {
92ab4382d2SGreg Kroah-Hartman 	struct uart_pxa_port *up = (struct uart_pxa_port *)port;
93ab4382d2SGreg Kroah-Hartman 
94ab4382d2SGreg Kroah-Hartman 	up->ier &= ~UART_IER_RLSI;
95ab4382d2SGreg Kroah-Hartman 	up->port.read_status_mask &= ~UART_LSR_DR;
96ab4382d2SGreg Kroah-Hartman 	serial_out(up, UART_IER, up->ier);
97ab4382d2SGreg Kroah-Hartman }
98ab4382d2SGreg Kroah-Hartman 
99ab4382d2SGreg Kroah-Hartman static inline void receive_chars(struct uart_pxa_port *up, int *status)
100ab4382d2SGreg Kroah-Hartman {
101ab4382d2SGreg Kroah-Hartman 	unsigned int ch, flag;
102ab4382d2SGreg Kroah-Hartman 	int max_count = 256;
103ab4382d2SGreg Kroah-Hartman 
104ab4382d2SGreg Kroah-Hartman 	do {
105e44aabd6SMarcus Folkesson 		/* work around Errata #20 according to
106e44aabd6SMarcus Folkesson 		 * Intel(R) PXA27x Processor Family
107e44aabd6SMarcus Folkesson 		 * Specification Update (May 2005)
108e44aabd6SMarcus Folkesson 		 *
109e44aabd6SMarcus Folkesson 		 * Step 2
110e44aabd6SMarcus Folkesson 		 * Disable the Reciever Time Out Interrupt via IER[RTOEI]
111e44aabd6SMarcus Folkesson 		 */
112e44aabd6SMarcus Folkesson 		up->ier &= ~UART_IER_RTOIE;
113e44aabd6SMarcus Folkesson 		serial_out(up, UART_IER, up->ier);
114e44aabd6SMarcus Folkesson 
115ab4382d2SGreg Kroah-Hartman 		ch = serial_in(up, UART_RX);
116ab4382d2SGreg Kroah-Hartman 		flag = TTY_NORMAL;
117ab4382d2SGreg Kroah-Hartman 		up->port.icount.rx++;
118ab4382d2SGreg Kroah-Hartman 
119ab4382d2SGreg Kroah-Hartman 		if (unlikely(*status & (UART_LSR_BI | UART_LSR_PE |
120ab4382d2SGreg Kroah-Hartman 				       UART_LSR_FE | UART_LSR_OE))) {
121ab4382d2SGreg Kroah-Hartman 			/*
122ab4382d2SGreg Kroah-Hartman 			 * For statistics only
123ab4382d2SGreg Kroah-Hartman 			 */
124ab4382d2SGreg Kroah-Hartman 			if (*status & UART_LSR_BI) {
125ab4382d2SGreg Kroah-Hartman 				*status &= ~(UART_LSR_FE | UART_LSR_PE);
126ab4382d2SGreg Kroah-Hartman 				up->port.icount.brk++;
127ab4382d2SGreg Kroah-Hartman 				/*
128ab4382d2SGreg Kroah-Hartman 				 * We do the SysRQ and SAK checking
129ab4382d2SGreg Kroah-Hartman 				 * here because otherwise the break
130ab4382d2SGreg Kroah-Hartman 				 * may get masked by ignore_status_mask
131ab4382d2SGreg Kroah-Hartman 				 * or read_status_mask.
132ab4382d2SGreg Kroah-Hartman 				 */
133ab4382d2SGreg Kroah-Hartman 				if (uart_handle_break(&up->port))
134ab4382d2SGreg Kroah-Hartman 					goto ignore_char;
135ab4382d2SGreg Kroah-Hartman 			} else if (*status & UART_LSR_PE)
136ab4382d2SGreg Kroah-Hartman 				up->port.icount.parity++;
137ab4382d2SGreg Kroah-Hartman 			else if (*status & UART_LSR_FE)
138ab4382d2SGreg Kroah-Hartman 				up->port.icount.frame++;
139ab4382d2SGreg Kroah-Hartman 			if (*status & UART_LSR_OE)
140ab4382d2SGreg Kroah-Hartman 				up->port.icount.overrun++;
141ab4382d2SGreg Kroah-Hartman 
142ab4382d2SGreg Kroah-Hartman 			/*
143ab4382d2SGreg Kroah-Hartman 			 * Mask off conditions which should be ignored.
144ab4382d2SGreg Kroah-Hartman 			 */
145ab4382d2SGreg Kroah-Hartman 			*status &= up->port.read_status_mask;
146ab4382d2SGreg Kroah-Hartman 
147ab4382d2SGreg Kroah-Hartman #ifdef CONFIG_SERIAL_PXA_CONSOLE
148ab4382d2SGreg Kroah-Hartman 			if (up->port.line == up->port.cons->index) {
149ab4382d2SGreg Kroah-Hartman 				/* Recover the break flag from console xmit */
150ab4382d2SGreg Kroah-Hartman 				*status |= up->lsr_break_flag;
151ab4382d2SGreg Kroah-Hartman 				up->lsr_break_flag = 0;
152ab4382d2SGreg Kroah-Hartman 			}
153ab4382d2SGreg Kroah-Hartman #endif
154ab4382d2SGreg Kroah-Hartman 			if (*status & UART_LSR_BI) {
155ab4382d2SGreg Kroah-Hartman 				flag = TTY_BREAK;
156ab4382d2SGreg Kroah-Hartman 			} else if (*status & UART_LSR_PE)
157ab4382d2SGreg Kroah-Hartman 				flag = TTY_PARITY;
158ab4382d2SGreg Kroah-Hartman 			else if (*status & UART_LSR_FE)
159ab4382d2SGreg Kroah-Hartman 				flag = TTY_FRAME;
160ab4382d2SGreg Kroah-Hartman 		}
161ab4382d2SGreg Kroah-Hartman 
162ab4382d2SGreg Kroah-Hartman 		if (uart_handle_sysrq_char(&up->port, ch))
163ab4382d2SGreg Kroah-Hartman 			goto ignore_char;
164ab4382d2SGreg Kroah-Hartman 
165ab4382d2SGreg Kroah-Hartman 		uart_insert_char(&up->port, *status, UART_LSR_OE, ch, flag);
166ab4382d2SGreg Kroah-Hartman 
167ab4382d2SGreg Kroah-Hartman 	ignore_char:
168ab4382d2SGreg Kroah-Hartman 		*status = serial_in(up, UART_LSR);
169ab4382d2SGreg Kroah-Hartman 	} while ((*status & UART_LSR_DR) && (max_count-- > 0));
1702e124b4aSJiri Slaby 	tty_flip_buffer_push(&up->port.state->port);
171e44aabd6SMarcus Folkesson 
172e44aabd6SMarcus Folkesson 	/* work around Errata #20 according to
173e44aabd6SMarcus Folkesson 	 * Intel(R) PXA27x Processor Family
174e44aabd6SMarcus Folkesson 	 * Specification Update (May 2005)
175e44aabd6SMarcus Folkesson 	 *
176e44aabd6SMarcus Folkesson 	 * Step 6:
177e44aabd6SMarcus Folkesson 	 * No more data in FIFO: Re-enable RTO interrupt via IER[RTOIE]
178e44aabd6SMarcus Folkesson 	 */
179e44aabd6SMarcus Folkesson 	up->ier |= UART_IER_RTOIE;
180e44aabd6SMarcus Folkesson 	serial_out(up, UART_IER, up->ier);
181ab4382d2SGreg Kroah-Hartman }
182ab4382d2SGreg Kroah-Hartman 
183ab4382d2SGreg Kroah-Hartman static void transmit_chars(struct uart_pxa_port *up)
184ab4382d2SGreg Kroah-Hartman {
185ab4382d2SGreg Kroah-Hartman 	struct circ_buf *xmit = &up->port.state->xmit;
186ab4382d2SGreg Kroah-Hartman 	int count;
187ab4382d2SGreg Kroah-Hartman 
188ab4382d2SGreg Kroah-Hartman 	if (up->port.x_char) {
189ab4382d2SGreg Kroah-Hartman 		serial_out(up, UART_TX, up->port.x_char);
190ab4382d2SGreg Kroah-Hartman 		up->port.icount.tx++;
191ab4382d2SGreg Kroah-Hartman 		up->port.x_char = 0;
192ab4382d2SGreg Kroah-Hartman 		return;
193ab4382d2SGreg Kroah-Hartman 	}
194ab4382d2SGreg Kroah-Hartman 	if (uart_circ_empty(xmit) || uart_tx_stopped(&up->port)) {
195ab4382d2SGreg Kroah-Hartman 		serial_pxa_stop_tx(&up->port);
196ab4382d2SGreg Kroah-Hartman 		return;
197ab4382d2SGreg Kroah-Hartman 	}
198ab4382d2SGreg Kroah-Hartman 
199ab4382d2SGreg Kroah-Hartman 	count = up->port.fifosize / 2;
200ab4382d2SGreg Kroah-Hartman 	do {
201ab4382d2SGreg Kroah-Hartman 		serial_out(up, UART_TX, xmit->buf[xmit->tail]);
202ab4382d2SGreg Kroah-Hartman 		xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
203ab4382d2SGreg Kroah-Hartman 		up->port.icount.tx++;
204ab4382d2SGreg Kroah-Hartman 		if (uart_circ_empty(xmit))
205ab4382d2SGreg Kroah-Hartman 			break;
206ab4382d2SGreg Kroah-Hartman 	} while (--count > 0);
207ab4382d2SGreg Kroah-Hartman 
208ab4382d2SGreg Kroah-Hartman 	if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
209ab4382d2SGreg Kroah-Hartman 		uart_write_wakeup(&up->port);
210ab4382d2SGreg Kroah-Hartman 
211ab4382d2SGreg Kroah-Hartman 
212ab4382d2SGreg Kroah-Hartman 	if (uart_circ_empty(xmit))
213ab4382d2SGreg Kroah-Hartman 		serial_pxa_stop_tx(&up->port);
214ab4382d2SGreg Kroah-Hartman }
215ab4382d2SGreg Kroah-Hartman 
216ab4382d2SGreg Kroah-Hartman static void serial_pxa_start_tx(struct uart_port *port)
217ab4382d2SGreg Kroah-Hartman {
218ab4382d2SGreg Kroah-Hartman 	struct uart_pxa_port *up = (struct uart_pxa_port *)port;
219ab4382d2SGreg Kroah-Hartman 
220ab4382d2SGreg Kroah-Hartman 	if (!(up->ier & UART_IER_THRI)) {
221ab4382d2SGreg Kroah-Hartman 		up->ier |= UART_IER_THRI;
222ab4382d2SGreg Kroah-Hartman 		serial_out(up, UART_IER, up->ier);
223ab4382d2SGreg Kroah-Hartman 	}
224ab4382d2SGreg Kroah-Hartman }
225ab4382d2SGreg Kroah-Hartman 
226ab4382d2SGreg Kroah-Hartman static inline void check_modem_status(struct uart_pxa_port *up)
227ab4382d2SGreg Kroah-Hartman {
228ab4382d2SGreg Kroah-Hartman 	int status;
229ab4382d2SGreg Kroah-Hartman 
230ab4382d2SGreg Kroah-Hartman 	status = serial_in(up, UART_MSR);
231ab4382d2SGreg Kroah-Hartman 
232ab4382d2SGreg Kroah-Hartman 	if ((status & UART_MSR_ANY_DELTA) == 0)
233ab4382d2SGreg Kroah-Hartman 		return;
234ab4382d2SGreg Kroah-Hartman 
235ab4382d2SGreg Kroah-Hartman 	if (status & UART_MSR_TERI)
236ab4382d2SGreg Kroah-Hartman 		up->port.icount.rng++;
237ab4382d2SGreg Kroah-Hartman 	if (status & UART_MSR_DDSR)
238ab4382d2SGreg Kroah-Hartman 		up->port.icount.dsr++;
239ab4382d2SGreg Kroah-Hartman 	if (status & UART_MSR_DDCD)
240ab4382d2SGreg Kroah-Hartman 		uart_handle_dcd_change(&up->port, status & UART_MSR_DCD);
241ab4382d2SGreg Kroah-Hartman 	if (status & UART_MSR_DCTS)
242ab4382d2SGreg Kroah-Hartman 		uart_handle_cts_change(&up->port, status & UART_MSR_CTS);
243ab4382d2SGreg Kroah-Hartman 
244ab4382d2SGreg Kroah-Hartman 	wake_up_interruptible(&up->port.state->port.delta_msr_wait);
245ab4382d2SGreg Kroah-Hartman }
246ab4382d2SGreg Kroah-Hartman 
247ab4382d2SGreg Kroah-Hartman /*
248ab4382d2SGreg Kroah-Hartman  * This handles the interrupt from one port.
249ab4382d2SGreg Kroah-Hartman  */
250ab4382d2SGreg Kroah-Hartman static inline irqreturn_t serial_pxa_irq(int irq, void *dev_id)
251ab4382d2SGreg Kroah-Hartman {
252ab4382d2SGreg Kroah-Hartman 	struct uart_pxa_port *up = dev_id;
253ab4382d2SGreg Kroah-Hartman 	unsigned int iir, lsr;
254ab4382d2SGreg Kroah-Hartman 
255ab4382d2SGreg Kroah-Hartman 	iir = serial_in(up, UART_IIR);
256ab4382d2SGreg Kroah-Hartman 	if (iir & UART_IIR_NO_INT)
257ab4382d2SGreg Kroah-Hartman 		return IRQ_NONE;
258ab4382d2SGreg Kroah-Hartman 	lsr = serial_in(up, UART_LSR);
259ab4382d2SGreg Kroah-Hartman 	if (lsr & UART_LSR_DR)
260ab4382d2SGreg Kroah-Hartman 		receive_chars(up, &lsr);
261ab4382d2SGreg Kroah-Hartman 	check_modem_status(up);
262ab4382d2SGreg Kroah-Hartman 	if (lsr & UART_LSR_THRE)
263ab4382d2SGreg Kroah-Hartman 		transmit_chars(up);
264ab4382d2SGreg Kroah-Hartman 	return IRQ_HANDLED;
265ab4382d2SGreg Kroah-Hartman }
266ab4382d2SGreg Kroah-Hartman 
267ab4382d2SGreg Kroah-Hartman static unsigned int serial_pxa_tx_empty(struct uart_port *port)
268ab4382d2SGreg Kroah-Hartman {
269ab4382d2SGreg Kroah-Hartman 	struct uart_pxa_port *up = (struct uart_pxa_port *)port;
270ab4382d2SGreg Kroah-Hartman 	unsigned long flags;
271ab4382d2SGreg Kroah-Hartman 	unsigned int ret;
272ab4382d2SGreg Kroah-Hartman 
273ab4382d2SGreg Kroah-Hartman 	spin_lock_irqsave(&up->port.lock, flags);
274ab4382d2SGreg Kroah-Hartman 	ret = serial_in(up, UART_LSR) & UART_LSR_TEMT ? TIOCSER_TEMT : 0;
275ab4382d2SGreg Kroah-Hartman 	spin_unlock_irqrestore(&up->port.lock, flags);
276ab4382d2SGreg Kroah-Hartman 
277ab4382d2SGreg Kroah-Hartman 	return ret;
278ab4382d2SGreg Kroah-Hartman }
279ab4382d2SGreg Kroah-Hartman 
280ab4382d2SGreg Kroah-Hartman static unsigned int serial_pxa_get_mctrl(struct uart_port *port)
281ab4382d2SGreg Kroah-Hartman {
282ab4382d2SGreg Kroah-Hartman 	struct uart_pxa_port *up = (struct uart_pxa_port *)port;
283ab4382d2SGreg Kroah-Hartman 	unsigned char status;
284ab4382d2SGreg Kroah-Hartman 	unsigned int ret;
285ab4382d2SGreg Kroah-Hartman 
286ab4382d2SGreg Kroah-Hartman 	status = serial_in(up, UART_MSR);
287ab4382d2SGreg Kroah-Hartman 
288ab4382d2SGreg Kroah-Hartman 	ret = 0;
289ab4382d2SGreg Kroah-Hartman 	if (status & UART_MSR_DCD)
290ab4382d2SGreg Kroah-Hartman 		ret |= TIOCM_CAR;
291ab4382d2SGreg Kroah-Hartman 	if (status & UART_MSR_RI)
292ab4382d2SGreg Kroah-Hartman 		ret |= TIOCM_RNG;
293ab4382d2SGreg Kroah-Hartman 	if (status & UART_MSR_DSR)
294ab4382d2SGreg Kroah-Hartman 		ret |= TIOCM_DSR;
295ab4382d2SGreg Kroah-Hartman 	if (status & UART_MSR_CTS)
296ab4382d2SGreg Kroah-Hartman 		ret |= TIOCM_CTS;
297ab4382d2SGreg Kroah-Hartman 	return ret;
298ab4382d2SGreg Kroah-Hartman }
299ab4382d2SGreg Kroah-Hartman 
300ab4382d2SGreg Kroah-Hartman static void serial_pxa_set_mctrl(struct uart_port *port, unsigned int mctrl)
301ab4382d2SGreg Kroah-Hartman {
302ab4382d2SGreg Kroah-Hartman 	struct uart_pxa_port *up = (struct uart_pxa_port *)port;
303ab4382d2SGreg Kroah-Hartman 	unsigned char mcr = 0;
304ab4382d2SGreg Kroah-Hartman 
305ab4382d2SGreg Kroah-Hartman 	if (mctrl & TIOCM_RTS)
306ab4382d2SGreg Kroah-Hartman 		mcr |= UART_MCR_RTS;
307ab4382d2SGreg Kroah-Hartman 	if (mctrl & TIOCM_DTR)
308ab4382d2SGreg Kroah-Hartman 		mcr |= UART_MCR_DTR;
309ab4382d2SGreg Kroah-Hartman 	if (mctrl & TIOCM_OUT1)
310ab4382d2SGreg Kroah-Hartman 		mcr |= UART_MCR_OUT1;
311ab4382d2SGreg Kroah-Hartman 	if (mctrl & TIOCM_OUT2)
312ab4382d2SGreg Kroah-Hartman 		mcr |= UART_MCR_OUT2;
313ab4382d2SGreg Kroah-Hartman 	if (mctrl & TIOCM_LOOP)
314ab4382d2SGreg Kroah-Hartman 		mcr |= UART_MCR_LOOP;
315ab4382d2SGreg Kroah-Hartman 
316ab4382d2SGreg Kroah-Hartman 	mcr |= up->mcr;
317ab4382d2SGreg Kroah-Hartman 
318ab4382d2SGreg Kroah-Hartman 	serial_out(up, UART_MCR, mcr);
319ab4382d2SGreg Kroah-Hartman }
320ab4382d2SGreg Kroah-Hartman 
321ab4382d2SGreg Kroah-Hartman static void serial_pxa_break_ctl(struct uart_port *port, int break_state)
322ab4382d2SGreg Kroah-Hartman {
323ab4382d2SGreg Kroah-Hartman 	struct uart_pxa_port *up = (struct uart_pxa_port *)port;
324ab4382d2SGreg Kroah-Hartman 	unsigned long flags;
325ab4382d2SGreg Kroah-Hartman 
326ab4382d2SGreg Kroah-Hartman 	spin_lock_irqsave(&up->port.lock, flags);
327ab4382d2SGreg Kroah-Hartman 	if (break_state == -1)
328ab4382d2SGreg Kroah-Hartman 		up->lcr |= UART_LCR_SBC;
329ab4382d2SGreg Kroah-Hartman 	else
330ab4382d2SGreg Kroah-Hartman 		up->lcr &= ~UART_LCR_SBC;
331ab4382d2SGreg Kroah-Hartman 	serial_out(up, UART_LCR, up->lcr);
332ab4382d2SGreg Kroah-Hartman 	spin_unlock_irqrestore(&up->port.lock, flags);
333ab4382d2SGreg Kroah-Hartman }
334ab4382d2SGreg Kroah-Hartman 
335ab4382d2SGreg Kroah-Hartman static int serial_pxa_startup(struct uart_port *port)
336ab4382d2SGreg Kroah-Hartman {
337ab4382d2SGreg Kroah-Hartman 	struct uart_pxa_port *up = (struct uart_pxa_port *)port;
338ab4382d2SGreg Kroah-Hartman 	unsigned long flags;
339ab4382d2SGreg Kroah-Hartman 	int retval;
340ab4382d2SGreg Kroah-Hartman 
341ab4382d2SGreg Kroah-Hartman 	if (port->line == 3) /* HWUART */
342ab4382d2SGreg Kroah-Hartman 		up->mcr |= UART_MCR_AFE;
343ab4382d2SGreg Kroah-Hartman 	else
344ab4382d2SGreg Kroah-Hartman 		up->mcr = 0;
345ab4382d2SGreg Kroah-Hartman 
346ab4382d2SGreg Kroah-Hartman 	up->port.uartclk = clk_get_rate(up->clk);
347ab4382d2SGreg Kroah-Hartman 
348ab4382d2SGreg Kroah-Hartman 	/*
349ab4382d2SGreg Kroah-Hartman 	 * Allocate the IRQ
350ab4382d2SGreg Kroah-Hartman 	 */
351ab4382d2SGreg Kroah-Hartman 	retval = request_irq(up->port.irq, serial_pxa_irq, 0, up->name, up);
352ab4382d2SGreg Kroah-Hartman 	if (retval)
353ab4382d2SGreg Kroah-Hartman 		return retval;
354ab4382d2SGreg Kroah-Hartman 
355ab4382d2SGreg Kroah-Hartman 	/*
356ab4382d2SGreg Kroah-Hartman 	 * Clear the FIFO buffers and disable them.
357ab4382d2SGreg Kroah-Hartman 	 * (they will be reenabled in set_termios())
358ab4382d2SGreg Kroah-Hartman 	 */
359ab4382d2SGreg Kroah-Hartman 	serial_out(up, UART_FCR, UART_FCR_ENABLE_FIFO);
360ab4382d2SGreg Kroah-Hartman 	serial_out(up, UART_FCR, UART_FCR_ENABLE_FIFO |
361ab4382d2SGreg Kroah-Hartman 			UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT);
362ab4382d2SGreg Kroah-Hartman 	serial_out(up, UART_FCR, 0);
363ab4382d2SGreg Kroah-Hartman 
364ab4382d2SGreg Kroah-Hartman 	/*
365ab4382d2SGreg Kroah-Hartman 	 * Clear the interrupt registers.
366ab4382d2SGreg Kroah-Hartman 	 */
367ab4382d2SGreg Kroah-Hartman 	(void) serial_in(up, UART_LSR);
368ab4382d2SGreg Kroah-Hartman 	(void) serial_in(up, UART_RX);
369ab4382d2SGreg Kroah-Hartman 	(void) serial_in(up, UART_IIR);
370ab4382d2SGreg Kroah-Hartman 	(void) serial_in(up, UART_MSR);
371ab4382d2SGreg Kroah-Hartman 
372ab4382d2SGreg Kroah-Hartman 	/*
373ab4382d2SGreg Kroah-Hartman 	 * Now, initialize the UART
374ab4382d2SGreg Kroah-Hartman 	 */
375ab4382d2SGreg Kroah-Hartman 	serial_out(up, UART_LCR, UART_LCR_WLEN8);
376ab4382d2SGreg Kroah-Hartman 
377ab4382d2SGreg Kroah-Hartman 	spin_lock_irqsave(&up->port.lock, flags);
378ab4382d2SGreg Kroah-Hartman 	up->port.mctrl |= TIOCM_OUT2;
379ab4382d2SGreg Kroah-Hartman 	serial_pxa_set_mctrl(&up->port, up->port.mctrl);
380ab4382d2SGreg Kroah-Hartman 	spin_unlock_irqrestore(&up->port.lock, flags);
381ab4382d2SGreg Kroah-Hartman 
382ab4382d2SGreg Kroah-Hartman 	/*
383ab4382d2SGreg Kroah-Hartman 	 * Finally, enable interrupts.  Note: Modem status interrupts
384ab4382d2SGreg Kroah-Hartman 	 * are set via set_termios(), which will be occurring imminently
385ab4382d2SGreg Kroah-Hartman 	 * anyway, so we don't enable them here.
386ab4382d2SGreg Kroah-Hartman 	 */
387ab4382d2SGreg Kroah-Hartman 	up->ier = UART_IER_RLSI | UART_IER_RDI | UART_IER_RTOIE | UART_IER_UUE;
388ab4382d2SGreg Kroah-Hartman 	serial_out(up, UART_IER, up->ier);
389ab4382d2SGreg Kroah-Hartman 
390ab4382d2SGreg Kroah-Hartman 	/*
391ab4382d2SGreg Kroah-Hartman 	 * And clear the interrupt registers again for luck.
392ab4382d2SGreg Kroah-Hartman 	 */
393ab4382d2SGreg Kroah-Hartman 	(void) serial_in(up, UART_LSR);
394ab4382d2SGreg Kroah-Hartman 	(void) serial_in(up, UART_RX);
395ab4382d2SGreg Kroah-Hartman 	(void) serial_in(up, UART_IIR);
396ab4382d2SGreg Kroah-Hartman 	(void) serial_in(up, UART_MSR);
397ab4382d2SGreg Kroah-Hartman 
398ab4382d2SGreg Kroah-Hartman 	return 0;
399ab4382d2SGreg Kroah-Hartman }
400ab4382d2SGreg Kroah-Hartman 
401ab4382d2SGreg Kroah-Hartman static void serial_pxa_shutdown(struct uart_port *port)
402ab4382d2SGreg Kroah-Hartman {
403ab4382d2SGreg Kroah-Hartman 	struct uart_pxa_port *up = (struct uart_pxa_port *)port;
404ab4382d2SGreg Kroah-Hartman 	unsigned long flags;
405ab4382d2SGreg Kroah-Hartman 
406ab4382d2SGreg Kroah-Hartman 	free_irq(up->port.irq, up);
407ab4382d2SGreg Kroah-Hartman 
408ab4382d2SGreg Kroah-Hartman 	/*
409ab4382d2SGreg Kroah-Hartman 	 * Disable interrupts from this port
410ab4382d2SGreg Kroah-Hartman 	 */
411ab4382d2SGreg Kroah-Hartman 	up->ier = 0;
412ab4382d2SGreg Kroah-Hartman 	serial_out(up, UART_IER, 0);
413ab4382d2SGreg Kroah-Hartman 
414ab4382d2SGreg Kroah-Hartman 	spin_lock_irqsave(&up->port.lock, flags);
415ab4382d2SGreg Kroah-Hartman 	up->port.mctrl &= ~TIOCM_OUT2;
416ab4382d2SGreg Kroah-Hartman 	serial_pxa_set_mctrl(&up->port, up->port.mctrl);
417ab4382d2SGreg Kroah-Hartman 	spin_unlock_irqrestore(&up->port.lock, flags);
418ab4382d2SGreg Kroah-Hartman 
419ab4382d2SGreg Kroah-Hartman 	/*
420ab4382d2SGreg Kroah-Hartman 	 * Disable break condition and FIFOs
421ab4382d2SGreg Kroah-Hartman 	 */
422ab4382d2SGreg Kroah-Hartman 	serial_out(up, UART_LCR, serial_in(up, UART_LCR) & ~UART_LCR_SBC);
423ab4382d2SGreg Kroah-Hartman 	serial_out(up, UART_FCR, UART_FCR_ENABLE_FIFO |
424ab4382d2SGreg Kroah-Hartman 				  UART_FCR_CLEAR_RCVR |
425ab4382d2SGreg Kroah-Hartman 				  UART_FCR_CLEAR_XMIT);
426ab4382d2SGreg Kroah-Hartman 	serial_out(up, UART_FCR, 0);
427ab4382d2SGreg Kroah-Hartman }
428ab4382d2SGreg Kroah-Hartman 
429ab4382d2SGreg Kroah-Hartman static void
430ab4382d2SGreg Kroah-Hartman serial_pxa_set_termios(struct uart_port *port, struct ktermios *termios,
431ab4382d2SGreg Kroah-Hartman 		       struct ktermios *old)
432ab4382d2SGreg Kroah-Hartman {
433ab4382d2SGreg Kroah-Hartman 	struct uart_pxa_port *up = (struct uart_pxa_port *)port;
434ab4382d2SGreg Kroah-Hartman 	unsigned char cval, fcr = 0;
435ab4382d2SGreg Kroah-Hartman 	unsigned long flags;
436ab4382d2SGreg Kroah-Hartman 	unsigned int baud, quot;
437ab4382d2SGreg Kroah-Hartman 	unsigned int dll;
438ab4382d2SGreg Kroah-Hartman 
439ab4382d2SGreg Kroah-Hartman 	switch (termios->c_cflag & CSIZE) {
440ab4382d2SGreg Kroah-Hartman 	case CS5:
441ab4382d2SGreg Kroah-Hartman 		cval = UART_LCR_WLEN5;
442ab4382d2SGreg Kroah-Hartman 		break;
443ab4382d2SGreg Kroah-Hartman 	case CS6:
444ab4382d2SGreg Kroah-Hartman 		cval = UART_LCR_WLEN6;
445ab4382d2SGreg Kroah-Hartman 		break;
446ab4382d2SGreg Kroah-Hartman 	case CS7:
447ab4382d2SGreg Kroah-Hartman 		cval = UART_LCR_WLEN7;
448ab4382d2SGreg Kroah-Hartman 		break;
449ab4382d2SGreg Kroah-Hartman 	default:
450ab4382d2SGreg Kroah-Hartman 	case CS8:
451ab4382d2SGreg Kroah-Hartman 		cval = UART_LCR_WLEN8;
452ab4382d2SGreg Kroah-Hartman 		break;
453ab4382d2SGreg Kroah-Hartman 	}
454ab4382d2SGreg Kroah-Hartman 
455ab4382d2SGreg Kroah-Hartman 	if (termios->c_cflag & CSTOPB)
456ab4382d2SGreg Kroah-Hartman 		cval |= UART_LCR_STOP;
457ab4382d2SGreg Kroah-Hartman 	if (termios->c_cflag & PARENB)
458ab4382d2SGreg Kroah-Hartman 		cval |= UART_LCR_PARITY;
459ab4382d2SGreg Kroah-Hartman 	if (!(termios->c_cflag & PARODD))
460ab4382d2SGreg Kroah-Hartman 		cval |= UART_LCR_EPAR;
461ab4382d2SGreg Kroah-Hartman 
462ab4382d2SGreg Kroah-Hartman 	/*
463ab4382d2SGreg Kroah-Hartman 	 * Ask the core to calculate the divisor for us.
464ab4382d2SGreg Kroah-Hartman 	 */
465ab4382d2SGreg Kroah-Hartman 	baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk/16);
466ab4382d2SGreg Kroah-Hartman 	quot = uart_get_divisor(port, baud);
467ab4382d2SGreg Kroah-Hartman 
468ab4382d2SGreg Kroah-Hartman 	if ((up->port.uartclk / quot) < (2400 * 16))
469ab4382d2SGreg Kroah-Hartman 		fcr = UART_FCR_ENABLE_FIFO | UART_FCR_PXAR1;
470ab4382d2SGreg Kroah-Hartman 	else if ((up->port.uartclk / quot) < (230400 * 16))
471ab4382d2SGreg Kroah-Hartman 		fcr = UART_FCR_ENABLE_FIFO | UART_FCR_PXAR8;
472ab4382d2SGreg Kroah-Hartman 	else
473ab4382d2SGreg Kroah-Hartman 		fcr = UART_FCR_ENABLE_FIFO | UART_FCR_PXAR32;
474ab4382d2SGreg Kroah-Hartman 
475ab4382d2SGreg Kroah-Hartman 	/*
476ab4382d2SGreg Kroah-Hartman 	 * Ok, we're now changing the port state.  Do it with
477ab4382d2SGreg Kroah-Hartman 	 * interrupts disabled.
478ab4382d2SGreg Kroah-Hartman 	 */
479ab4382d2SGreg Kroah-Hartman 	spin_lock_irqsave(&up->port.lock, flags);
480ab4382d2SGreg Kroah-Hartman 
481ab4382d2SGreg Kroah-Hartman 	/*
482ab4382d2SGreg Kroah-Hartman 	 * Ensure the port will be enabled.
483ab4382d2SGreg Kroah-Hartman 	 * This is required especially for serial console.
484ab4382d2SGreg Kroah-Hartman 	 */
485ab4382d2SGreg Kroah-Hartman 	up->ier |= UART_IER_UUE;
486ab4382d2SGreg Kroah-Hartman 
487ab4382d2SGreg Kroah-Hartman 	/*
488ab4382d2SGreg Kroah-Hartman 	 * Update the per-port timeout.
489ab4382d2SGreg Kroah-Hartman 	 */
490ab4382d2SGreg Kroah-Hartman 	uart_update_timeout(port, termios->c_cflag, baud);
491ab4382d2SGreg Kroah-Hartman 
492ab4382d2SGreg Kroah-Hartman 	up->port.read_status_mask = UART_LSR_OE | UART_LSR_THRE | UART_LSR_DR;
493ab4382d2SGreg Kroah-Hartman 	if (termios->c_iflag & INPCK)
494ab4382d2SGreg Kroah-Hartman 		up->port.read_status_mask |= UART_LSR_FE | UART_LSR_PE;
495ab4382d2SGreg Kroah-Hartman 	if (termios->c_iflag & (BRKINT | PARMRK))
496ab4382d2SGreg Kroah-Hartman 		up->port.read_status_mask |= UART_LSR_BI;
497ab4382d2SGreg Kroah-Hartman 
498ab4382d2SGreg Kroah-Hartman 	/*
499ab4382d2SGreg Kroah-Hartman 	 * Characters to ignore
500ab4382d2SGreg Kroah-Hartman 	 */
501ab4382d2SGreg Kroah-Hartman 	up->port.ignore_status_mask = 0;
502ab4382d2SGreg Kroah-Hartman 	if (termios->c_iflag & IGNPAR)
503ab4382d2SGreg Kroah-Hartman 		up->port.ignore_status_mask |= UART_LSR_PE | UART_LSR_FE;
504ab4382d2SGreg Kroah-Hartman 	if (termios->c_iflag & IGNBRK) {
505ab4382d2SGreg Kroah-Hartman 		up->port.ignore_status_mask |= UART_LSR_BI;
506ab4382d2SGreg Kroah-Hartman 		/*
507ab4382d2SGreg Kroah-Hartman 		 * If we're ignoring parity and break indicators,
508ab4382d2SGreg Kroah-Hartman 		 * ignore overruns too (for real raw support).
509ab4382d2SGreg Kroah-Hartman 		 */
510ab4382d2SGreg Kroah-Hartman 		if (termios->c_iflag & IGNPAR)
511ab4382d2SGreg Kroah-Hartman 			up->port.ignore_status_mask |= UART_LSR_OE;
512ab4382d2SGreg Kroah-Hartman 	}
513ab4382d2SGreg Kroah-Hartman 
514ab4382d2SGreg Kroah-Hartman 	/*
515ab4382d2SGreg Kroah-Hartman 	 * ignore all characters if CREAD is not set
516ab4382d2SGreg Kroah-Hartman 	 */
517ab4382d2SGreg Kroah-Hartman 	if ((termios->c_cflag & CREAD) == 0)
518ab4382d2SGreg Kroah-Hartman 		up->port.ignore_status_mask |= UART_LSR_DR;
519ab4382d2SGreg Kroah-Hartman 
520ab4382d2SGreg Kroah-Hartman 	/*
521ab4382d2SGreg Kroah-Hartman 	 * CTS flow control flag and modem status interrupts
522ab4382d2SGreg Kroah-Hartman 	 */
523ab4382d2SGreg Kroah-Hartman 	up->ier &= ~UART_IER_MSI;
524ab4382d2SGreg Kroah-Hartman 	if (UART_ENABLE_MS(&up->port, termios->c_cflag))
525ab4382d2SGreg Kroah-Hartman 		up->ier |= UART_IER_MSI;
526ab4382d2SGreg Kroah-Hartman 
527ab4382d2SGreg Kroah-Hartman 	serial_out(up, UART_IER, up->ier);
528ab4382d2SGreg Kroah-Hartman 
529ab4382d2SGreg Kroah-Hartman 	if (termios->c_cflag & CRTSCTS)
530ab4382d2SGreg Kroah-Hartman 		up->mcr |= UART_MCR_AFE;
531ab4382d2SGreg Kroah-Hartman 	else
532ab4382d2SGreg Kroah-Hartman 		up->mcr &= ~UART_MCR_AFE;
533ab4382d2SGreg Kroah-Hartman 
534ab4382d2SGreg Kroah-Hartman 	serial_out(up, UART_LCR, cval | UART_LCR_DLAB);	/* set DLAB */
535ab4382d2SGreg Kroah-Hartman 	serial_out(up, UART_DLL, quot & 0xff);		/* LS of divisor */
536ab4382d2SGreg Kroah-Hartman 
537ab4382d2SGreg Kroah-Hartman 	/*
538ab4382d2SGreg Kroah-Hartman 	 * work around Errata #75 according to Intel(R) PXA27x Processor Family
539ab4382d2SGreg Kroah-Hartman 	 * Specification Update (Nov 2005)
540ab4382d2SGreg Kroah-Hartman 	 */
541ab4382d2SGreg Kroah-Hartman 	dll = serial_in(up, UART_DLL);
542ab4382d2SGreg Kroah-Hartman 	WARN_ON(dll != (quot & 0xff));
543ab4382d2SGreg Kroah-Hartman 
544ab4382d2SGreg Kroah-Hartman 	serial_out(up, UART_DLM, quot >> 8);		/* MS of divisor */
545ab4382d2SGreg Kroah-Hartman 	serial_out(up, UART_LCR, cval);			/* reset DLAB */
546ab4382d2SGreg Kroah-Hartman 	up->lcr = cval;					/* Save LCR */
547ab4382d2SGreg Kroah-Hartman 	serial_pxa_set_mctrl(&up->port, up->port.mctrl);
548ab4382d2SGreg Kroah-Hartman 	serial_out(up, UART_FCR, fcr);
549ab4382d2SGreg Kroah-Hartman 	spin_unlock_irqrestore(&up->port.lock, flags);
550ab4382d2SGreg Kroah-Hartman }
551ab4382d2SGreg Kroah-Hartman 
552ab4382d2SGreg Kroah-Hartman static void
553ab4382d2SGreg Kroah-Hartman serial_pxa_pm(struct uart_port *port, unsigned int state,
554ab4382d2SGreg Kroah-Hartman 	      unsigned int oldstate)
555ab4382d2SGreg Kroah-Hartman {
556ab4382d2SGreg Kroah-Hartman 	struct uart_pxa_port *up = (struct uart_pxa_port *)port;
557ab4382d2SGreg Kroah-Hartman 
558ab4382d2SGreg Kroah-Hartman 	if (!state)
559fb8ebec0SPhilipp Zabel 		clk_prepare_enable(up->clk);
560ab4382d2SGreg Kroah-Hartman 	else
561fb8ebec0SPhilipp Zabel 		clk_disable_unprepare(up->clk);
562ab4382d2SGreg Kroah-Hartman }
563ab4382d2SGreg Kroah-Hartman 
564ab4382d2SGreg Kroah-Hartman static void serial_pxa_release_port(struct uart_port *port)
565ab4382d2SGreg Kroah-Hartman {
566ab4382d2SGreg Kroah-Hartman }
567ab4382d2SGreg Kroah-Hartman 
568ab4382d2SGreg Kroah-Hartman static int serial_pxa_request_port(struct uart_port *port)
569ab4382d2SGreg Kroah-Hartman {
570ab4382d2SGreg Kroah-Hartman 	return 0;
571ab4382d2SGreg Kroah-Hartman }
572ab4382d2SGreg Kroah-Hartman 
573ab4382d2SGreg Kroah-Hartman static void serial_pxa_config_port(struct uart_port *port, int flags)
574ab4382d2SGreg Kroah-Hartman {
575ab4382d2SGreg Kroah-Hartman 	struct uart_pxa_port *up = (struct uart_pxa_port *)port;
576ab4382d2SGreg Kroah-Hartman 	up->port.type = PORT_PXA;
577ab4382d2SGreg Kroah-Hartman }
578ab4382d2SGreg Kroah-Hartman 
579ab4382d2SGreg Kroah-Hartman static int
580ab4382d2SGreg Kroah-Hartman serial_pxa_verify_port(struct uart_port *port, struct serial_struct *ser)
581ab4382d2SGreg Kroah-Hartman {
582ab4382d2SGreg Kroah-Hartman 	/* we don't want the core code to modify any port params */
583ab4382d2SGreg Kroah-Hartman 	return -EINVAL;
584ab4382d2SGreg Kroah-Hartman }
585ab4382d2SGreg Kroah-Hartman 
586ab4382d2SGreg Kroah-Hartman static const char *
587ab4382d2SGreg Kroah-Hartman serial_pxa_type(struct uart_port *port)
588ab4382d2SGreg Kroah-Hartman {
589ab4382d2SGreg Kroah-Hartman 	struct uart_pxa_port *up = (struct uart_pxa_port *)port;
590ab4382d2SGreg Kroah-Hartman 	return up->name;
591ab4382d2SGreg Kroah-Hartman }
592ab4382d2SGreg Kroah-Hartman 
593ab4382d2SGreg Kroah-Hartman static struct uart_pxa_port *serial_pxa_ports[4];
594ab4382d2SGreg Kroah-Hartman static struct uart_driver serial_pxa_reg;
595ab4382d2SGreg Kroah-Hartman 
596ab4382d2SGreg Kroah-Hartman #ifdef CONFIG_SERIAL_PXA_CONSOLE
597ab4382d2SGreg Kroah-Hartman 
598ab4382d2SGreg Kroah-Hartman #define BOTH_EMPTY (UART_LSR_TEMT | UART_LSR_THRE)
599ab4382d2SGreg Kroah-Hartman 
600ab4382d2SGreg Kroah-Hartman /*
601ab4382d2SGreg Kroah-Hartman  *	Wait for transmitter & holding register to empty
602ab4382d2SGreg Kroah-Hartman  */
603ab4382d2SGreg Kroah-Hartman static inline void wait_for_xmitr(struct uart_pxa_port *up)
604ab4382d2SGreg Kroah-Hartman {
605ab4382d2SGreg Kroah-Hartman 	unsigned int status, tmout = 10000;
606ab4382d2SGreg Kroah-Hartman 
607ab4382d2SGreg Kroah-Hartman 	/* Wait up to 10ms for the character(s) to be sent. */
608ab4382d2SGreg Kroah-Hartman 	do {
609ab4382d2SGreg Kroah-Hartman 		status = serial_in(up, UART_LSR);
610ab4382d2SGreg Kroah-Hartman 
611ab4382d2SGreg Kroah-Hartman 		if (status & UART_LSR_BI)
612ab4382d2SGreg Kroah-Hartman 			up->lsr_break_flag = UART_LSR_BI;
613ab4382d2SGreg Kroah-Hartman 
614ab4382d2SGreg Kroah-Hartman 		if (--tmout == 0)
615ab4382d2SGreg Kroah-Hartman 			break;
616ab4382d2SGreg Kroah-Hartman 		udelay(1);
617ab4382d2SGreg Kroah-Hartman 	} while ((status & BOTH_EMPTY) != BOTH_EMPTY);
618ab4382d2SGreg Kroah-Hartman 
619ab4382d2SGreg Kroah-Hartman 	/* Wait up to 1s for flow control if necessary */
620ab4382d2SGreg Kroah-Hartman 	if (up->port.flags & UPF_CONS_FLOW) {
621ab4382d2SGreg Kroah-Hartman 		tmout = 1000000;
622ab4382d2SGreg Kroah-Hartman 		while (--tmout &&
623ab4382d2SGreg Kroah-Hartman 		       ((serial_in(up, UART_MSR) & UART_MSR_CTS) == 0))
624ab4382d2SGreg Kroah-Hartman 			udelay(1);
625ab4382d2SGreg Kroah-Hartman 	}
626ab4382d2SGreg Kroah-Hartman }
627ab4382d2SGreg Kroah-Hartman 
628ab4382d2SGreg Kroah-Hartman static void serial_pxa_console_putchar(struct uart_port *port, int ch)
629ab4382d2SGreg Kroah-Hartman {
630ab4382d2SGreg Kroah-Hartman 	struct uart_pxa_port *up = (struct uart_pxa_port *)port;
631ab4382d2SGreg Kroah-Hartman 
632ab4382d2SGreg Kroah-Hartman 	wait_for_xmitr(up);
633ab4382d2SGreg Kroah-Hartman 	serial_out(up, UART_TX, ch);
634ab4382d2SGreg Kroah-Hartman }
635ab4382d2SGreg Kroah-Hartman 
636ab4382d2SGreg Kroah-Hartman /*
637ab4382d2SGreg Kroah-Hartman  * Print a string to the serial port trying not to disturb
638ab4382d2SGreg Kroah-Hartman  * any possible real use of the port...
639ab4382d2SGreg Kroah-Hartman  *
640ab4382d2SGreg Kroah-Hartman  *	The console_lock must be held when we get here.
641ab4382d2SGreg Kroah-Hartman  */
642ab4382d2SGreg Kroah-Hartman static void
643ab4382d2SGreg Kroah-Hartman serial_pxa_console_write(struct console *co, const char *s, unsigned int count)
644ab4382d2SGreg Kroah-Hartman {
645ab4382d2SGreg Kroah-Hartman 	struct uart_pxa_port *up = serial_pxa_ports[co->index];
646ab4382d2SGreg Kroah-Hartman 	unsigned int ier;
647cfe275c2SChao Xie 	unsigned long flags;
648cfe275c2SChao Xie 	int locked = 1;
649ab4382d2SGreg Kroah-Hartman 
6509429ccbfSYi Zhang 	clk_enable(up->clk);
651cfe275c2SChao Xie 	local_irq_save(flags);
652cfe275c2SChao Xie 	if (up->port.sysrq)
653cfe275c2SChao Xie 		locked = 0;
654cfe275c2SChao Xie 	else if (oops_in_progress)
655cfe275c2SChao Xie 		locked = spin_trylock(&up->port.lock);
656cfe275c2SChao Xie 	else
657cfe275c2SChao Xie 		spin_lock(&up->port.lock);
658cfe275c2SChao Xie 
659ab4382d2SGreg Kroah-Hartman 	/*
660ab4382d2SGreg Kroah-Hartman 	 *	First save the IER then disable the interrupts
661ab4382d2SGreg Kroah-Hartman 	 */
662ab4382d2SGreg Kroah-Hartman 	ier = serial_in(up, UART_IER);
663ab4382d2SGreg Kroah-Hartman 	serial_out(up, UART_IER, UART_IER_UUE);
664ab4382d2SGreg Kroah-Hartman 
665ab4382d2SGreg Kroah-Hartman 	uart_console_write(&up->port, s, count, serial_pxa_console_putchar);
666ab4382d2SGreg Kroah-Hartman 
667ab4382d2SGreg Kroah-Hartman 	/*
668ab4382d2SGreg Kroah-Hartman 	 *	Finally, wait for transmitter to become empty
669ab4382d2SGreg Kroah-Hartman 	 *	and restore the IER
670ab4382d2SGreg Kroah-Hartman 	 */
671ab4382d2SGreg Kroah-Hartman 	wait_for_xmitr(up);
672ab4382d2SGreg Kroah-Hartman 	serial_out(up, UART_IER, ier);
673ab4382d2SGreg Kroah-Hartman 
674cfe275c2SChao Xie 	if (locked)
675cfe275c2SChao Xie 		spin_unlock(&up->port.lock);
676cfe275c2SChao Xie 	local_irq_restore(flags);
6779429ccbfSYi Zhang 	clk_disable(up->clk);
678cfe275c2SChao Xie 
679ab4382d2SGreg Kroah-Hartman }
680ab4382d2SGreg Kroah-Hartman 
681e1a9c179SDenis V. Lunev #ifdef CONFIG_CONSOLE_POLL
682e1a9c179SDenis V. Lunev /*
683e1a9c179SDenis V. Lunev  * Console polling routines for writing and reading from the uart while
684e1a9c179SDenis V. Lunev  * in an interrupt or debug context.
685e1a9c179SDenis V. Lunev  */
686e1a9c179SDenis V. Lunev 
687e1a9c179SDenis V. Lunev static int serial_pxa_get_poll_char(struct uart_port *port)
688e1a9c179SDenis V. Lunev {
689e1a9c179SDenis V. Lunev 	struct uart_pxa_port *up = (struct uart_pxa_port *)port;
690e1a9c179SDenis V. Lunev 	unsigned char lsr = serial_in(up, UART_LSR);
691e1a9c179SDenis V. Lunev 
692e1a9c179SDenis V. Lunev 	while (!(lsr & UART_LSR_DR))
693e1a9c179SDenis V. Lunev 		lsr = serial_in(up, UART_LSR);
694e1a9c179SDenis V. Lunev 
695e1a9c179SDenis V. Lunev 	return serial_in(up, UART_RX);
696e1a9c179SDenis V. Lunev }
697e1a9c179SDenis V. Lunev 
698e1a9c179SDenis V. Lunev 
699e1a9c179SDenis V. Lunev static void serial_pxa_put_poll_char(struct uart_port *port,
700e1a9c179SDenis V. Lunev 			 unsigned char c)
701e1a9c179SDenis V. Lunev {
702e1a9c179SDenis V. Lunev 	unsigned int ier;
703e1a9c179SDenis V. Lunev 	struct uart_pxa_port *up = (struct uart_pxa_port *)port;
704e1a9c179SDenis V. Lunev 
705e1a9c179SDenis V. Lunev 	/*
706e1a9c179SDenis V. Lunev 	 *	First save the IER then disable the interrupts
707e1a9c179SDenis V. Lunev 	 */
708e1a9c179SDenis V. Lunev 	ier = serial_in(up, UART_IER);
709e1a9c179SDenis V. Lunev 	serial_out(up, UART_IER, UART_IER_UUE);
710e1a9c179SDenis V. Lunev 
711e1a9c179SDenis V. Lunev 	wait_for_xmitr(up);
712e1a9c179SDenis V. Lunev 	/*
713e1a9c179SDenis V. Lunev 	 *	Send the character out.
714e1a9c179SDenis V. Lunev 	 *	If a LF, also do CR...
715e1a9c179SDenis V. Lunev 	 */
716e1a9c179SDenis V. Lunev 	serial_out(up, UART_TX, c);
717e1a9c179SDenis V. Lunev 	if (c == 10) {
718e1a9c179SDenis V. Lunev 		wait_for_xmitr(up);
719e1a9c179SDenis V. Lunev 		serial_out(up, UART_TX, 13);
720e1a9c179SDenis V. Lunev 	}
721e1a9c179SDenis V. Lunev 
722e1a9c179SDenis V. Lunev 	/*
723e1a9c179SDenis V. Lunev 	 *	Finally, wait for transmitter to become empty
724e1a9c179SDenis V. Lunev 	 *	and restore the IER
725e1a9c179SDenis V. Lunev 	 */
726e1a9c179SDenis V. Lunev 	wait_for_xmitr(up);
727e1a9c179SDenis V. Lunev 	serial_out(up, UART_IER, ier);
728e1a9c179SDenis V. Lunev }
729e1a9c179SDenis V. Lunev 
730e1a9c179SDenis V. Lunev #endif /* CONFIG_CONSOLE_POLL */
731e1a9c179SDenis V. Lunev 
732ab4382d2SGreg Kroah-Hartman static int __init
733ab4382d2SGreg Kroah-Hartman serial_pxa_console_setup(struct console *co, char *options)
734ab4382d2SGreg Kroah-Hartman {
735ab4382d2SGreg Kroah-Hartman 	struct uart_pxa_port *up;
736ab4382d2SGreg Kroah-Hartman 	int baud = 9600;
737ab4382d2SGreg Kroah-Hartman 	int bits = 8;
738ab4382d2SGreg Kroah-Hartman 	int parity = 'n';
739ab4382d2SGreg Kroah-Hartman 	int flow = 'n';
740ab4382d2SGreg Kroah-Hartman 
741ab4382d2SGreg Kroah-Hartman 	if (co->index == -1 || co->index >= serial_pxa_reg.nr)
742ab4382d2SGreg Kroah-Hartman 		co->index = 0;
743ab4382d2SGreg Kroah-Hartman 	up = serial_pxa_ports[co->index];
744ab4382d2SGreg Kroah-Hartman 	if (!up)
745ab4382d2SGreg Kroah-Hartman 		return -ENODEV;
746ab4382d2SGreg Kroah-Hartman 
747ab4382d2SGreg Kroah-Hartman 	if (options)
748ab4382d2SGreg Kroah-Hartman 		uart_parse_options(options, &baud, &parity, &bits, &flow);
749ab4382d2SGreg Kroah-Hartman 
750ab4382d2SGreg Kroah-Hartman 	return uart_set_options(&up->port, co, baud, parity, bits, flow);
751ab4382d2SGreg Kroah-Hartman }
752ab4382d2SGreg Kroah-Hartman 
753ab4382d2SGreg Kroah-Hartman static struct console serial_pxa_console = {
754ab4382d2SGreg Kroah-Hartman 	.name		= "ttyS",
755ab4382d2SGreg Kroah-Hartman 	.write		= serial_pxa_console_write,
756ab4382d2SGreg Kroah-Hartman 	.device		= uart_console_device,
757ab4382d2SGreg Kroah-Hartman 	.setup		= serial_pxa_console_setup,
758ab4382d2SGreg Kroah-Hartman 	.flags		= CON_PRINTBUFFER,
759ab4382d2SGreg Kroah-Hartman 	.index		= -1,
760ab4382d2SGreg Kroah-Hartman 	.data		= &serial_pxa_reg,
761ab4382d2SGreg Kroah-Hartman };
762ab4382d2SGreg Kroah-Hartman 
763ab4382d2SGreg Kroah-Hartman #define PXA_CONSOLE	&serial_pxa_console
764ab4382d2SGreg Kroah-Hartman #else
765ab4382d2SGreg Kroah-Hartman #define PXA_CONSOLE	NULL
766ab4382d2SGreg Kroah-Hartman #endif
767ab4382d2SGreg Kroah-Hartman 
768*6c62cc0dSJingoo Han static struct uart_ops serial_pxa_pops = {
769ab4382d2SGreg Kroah-Hartman 	.tx_empty	= serial_pxa_tx_empty,
770ab4382d2SGreg Kroah-Hartman 	.set_mctrl	= serial_pxa_set_mctrl,
771ab4382d2SGreg Kroah-Hartman 	.get_mctrl	= serial_pxa_get_mctrl,
772ab4382d2SGreg Kroah-Hartman 	.stop_tx	= serial_pxa_stop_tx,
773ab4382d2SGreg Kroah-Hartman 	.start_tx	= serial_pxa_start_tx,
774ab4382d2SGreg Kroah-Hartman 	.stop_rx	= serial_pxa_stop_rx,
775ab4382d2SGreg Kroah-Hartman 	.enable_ms	= serial_pxa_enable_ms,
776ab4382d2SGreg Kroah-Hartman 	.break_ctl	= serial_pxa_break_ctl,
777ab4382d2SGreg Kroah-Hartman 	.startup	= serial_pxa_startup,
778ab4382d2SGreg Kroah-Hartman 	.shutdown	= serial_pxa_shutdown,
779ab4382d2SGreg Kroah-Hartman 	.set_termios	= serial_pxa_set_termios,
780ab4382d2SGreg Kroah-Hartman 	.pm		= serial_pxa_pm,
781ab4382d2SGreg Kroah-Hartman 	.type		= serial_pxa_type,
782ab4382d2SGreg Kroah-Hartman 	.release_port	= serial_pxa_release_port,
783ab4382d2SGreg Kroah-Hartman 	.request_port	= serial_pxa_request_port,
784ab4382d2SGreg Kroah-Hartman 	.config_port	= serial_pxa_config_port,
785ab4382d2SGreg Kroah-Hartman 	.verify_port	= serial_pxa_verify_port,
786e1a9c179SDenis V. Lunev #ifdef CONFIG_CONSOLE_POLL
787e1a9c179SDenis V. Lunev 	.poll_get_char = serial_pxa_get_poll_char,
788e1a9c179SDenis V. Lunev 	.poll_put_char = serial_pxa_put_poll_char,
789e1a9c179SDenis V. Lunev #endif
790ab4382d2SGreg Kroah-Hartman };
791ab4382d2SGreg Kroah-Hartman 
792ab4382d2SGreg Kroah-Hartman static struct uart_driver serial_pxa_reg = {
793ab4382d2SGreg Kroah-Hartman 	.owner		= THIS_MODULE,
794ab4382d2SGreg Kroah-Hartman 	.driver_name	= "PXA serial",
795ab4382d2SGreg Kroah-Hartman 	.dev_name	= "ttyS",
796ab4382d2SGreg Kroah-Hartman 	.major		= TTY_MAJOR,
797ab4382d2SGreg Kroah-Hartman 	.minor		= 64,
798ab4382d2SGreg Kroah-Hartman 	.nr		= 4,
799ab4382d2SGreg Kroah-Hartman 	.cons		= PXA_CONSOLE,
800ab4382d2SGreg Kroah-Hartman };
801ab4382d2SGreg Kroah-Hartman 
802ab4382d2SGreg Kroah-Hartman #ifdef CONFIG_PM
803ab4382d2SGreg Kroah-Hartman static int serial_pxa_suspend(struct device *dev)
804ab4382d2SGreg Kroah-Hartman {
805ab4382d2SGreg Kroah-Hartman         struct uart_pxa_port *sport = dev_get_drvdata(dev);
806ab4382d2SGreg Kroah-Hartman 
807ab4382d2SGreg Kroah-Hartman         if (sport)
808ab4382d2SGreg Kroah-Hartman                 uart_suspend_port(&serial_pxa_reg, &sport->port);
809ab4382d2SGreg Kroah-Hartman 
810ab4382d2SGreg Kroah-Hartman         return 0;
811ab4382d2SGreg Kroah-Hartman }
812ab4382d2SGreg Kroah-Hartman 
813ab4382d2SGreg Kroah-Hartman static int serial_pxa_resume(struct device *dev)
814ab4382d2SGreg Kroah-Hartman {
815ab4382d2SGreg Kroah-Hartman         struct uart_pxa_port *sport = dev_get_drvdata(dev);
816ab4382d2SGreg Kroah-Hartman 
817ab4382d2SGreg Kroah-Hartman         if (sport)
818ab4382d2SGreg Kroah-Hartman                 uart_resume_port(&serial_pxa_reg, &sport->port);
819ab4382d2SGreg Kroah-Hartman 
820ab4382d2SGreg Kroah-Hartman         return 0;
821ab4382d2SGreg Kroah-Hartman }
822ab4382d2SGreg Kroah-Hartman 
823ab4382d2SGreg Kroah-Hartman static const struct dev_pm_ops serial_pxa_pm_ops = {
824ab4382d2SGreg Kroah-Hartman 	.suspend	= serial_pxa_suspend,
825ab4382d2SGreg Kroah-Hartman 	.resume		= serial_pxa_resume,
826ab4382d2SGreg Kroah-Hartman };
827ab4382d2SGreg Kroah-Hartman #endif
828ab4382d2SGreg Kroah-Hartman 
829699c20f3SHaojian Zhuang static struct of_device_id serial_pxa_dt_ids[] = {
830699c20f3SHaojian Zhuang 	{ .compatible = "mrvl,pxa-uart", },
831699c20f3SHaojian Zhuang 	{ .compatible = "mrvl,mmp-uart", },
832699c20f3SHaojian Zhuang 	{}
833699c20f3SHaojian Zhuang };
834699c20f3SHaojian Zhuang MODULE_DEVICE_TABLE(of, serial_pxa_dt_ids);
835699c20f3SHaojian Zhuang 
836699c20f3SHaojian Zhuang static int serial_pxa_probe_dt(struct platform_device *pdev,
837699c20f3SHaojian Zhuang 			       struct uart_pxa_port *sport)
838699c20f3SHaojian Zhuang {
839699c20f3SHaojian Zhuang 	struct device_node *np = pdev->dev.of_node;
840699c20f3SHaojian Zhuang 	int ret;
841699c20f3SHaojian Zhuang 
842699c20f3SHaojian Zhuang 	if (!np)
843699c20f3SHaojian Zhuang 		return 1;
844699c20f3SHaojian Zhuang 
845699c20f3SHaojian Zhuang 	ret = of_alias_get_id(np, "serial");
846699c20f3SHaojian Zhuang 	if (ret < 0) {
847699c20f3SHaojian Zhuang 		dev_err(&pdev->dev, "failed to get alias id, errno %d\n", ret);
848699c20f3SHaojian Zhuang 		return ret;
849699c20f3SHaojian Zhuang 	}
850699c20f3SHaojian Zhuang 	sport->port.line = ret;
851699c20f3SHaojian Zhuang 	return 0;
852699c20f3SHaojian Zhuang }
853699c20f3SHaojian Zhuang 
854ab4382d2SGreg Kroah-Hartman static int serial_pxa_probe(struct platform_device *dev)
855ab4382d2SGreg Kroah-Hartman {
856ab4382d2SGreg Kroah-Hartman 	struct uart_pxa_port *sport;
857ab4382d2SGreg Kroah-Hartman 	struct resource *mmres, *irqres;
858ab4382d2SGreg Kroah-Hartman 	int ret;
859ab4382d2SGreg Kroah-Hartman 
860ab4382d2SGreg Kroah-Hartman 	mmres = platform_get_resource(dev, IORESOURCE_MEM, 0);
861ab4382d2SGreg Kroah-Hartman 	irqres = platform_get_resource(dev, IORESOURCE_IRQ, 0);
862ab4382d2SGreg Kroah-Hartman 	if (!mmres || !irqres)
863ab4382d2SGreg Kroah-Hartman 		return -ENODEV;
864ab4382d2SGreg Kroah-Hartman 
865ab4382d2SGreg Kroah-Hartman 	sport = kzalloc(sizeof(struct uart_pxa_port), GFP_KERNEL);
866ab4382d2SGreg Kroah-Hartman 	if (!sport)
867ab4382d2SGreg Kroah-Hartman 		return -ENOMEM;
868ab4382d2SGreg Kroah-Hartman 
869ab4382d2SGreg Kroah-Hartman 	sport->clk = clk_get(&dev->dev, NULL);
870ab4382d2SGreg Kroah-Hartman 	if (IS_ERR(sport->clk)) {
871ab4382d2SGreg Kroah-Hartman 		ret = PTR_ERR(sport->clk);
872ab4382d2SGreg Kroah-Hartman 		goto err_free;
873ab4382d2SGreg Kroah-Hartman 	}
874ab4382d2SGreg Kroah-Hartman 
8759429ccbfSYi Zhang 	ret = clk_prepare(sport->clk);
8769429ccbfSYi Zhang 	if (ret) {
8779429ccbfSYi Zhang 		clk_put(sport->clk);
8789429ccbfSYi Zhang 		goto err_free;
8799429ccbfSYi Zhang 	}
8809429ccbfSYi Zhang 
881ab4382d2SGreg Kroah-Hartman 	sport->port.type = PORT_PXA;
882ab4382d2SGreg Kroah-Hartman 	sport->port.iotype = UPIO_MEM;
883ab4382d2SGreg Kroah-Hartman 	sport->port.mapbase = mmres->start;
884ab4382d2SGreg Kroah-Hartman 	sport->port.irq = irqres->start;
885ab4382d2SGreg Kroah-Hartman 	sport->port.fifosize = 64;
886ab4382d2SGreg Kroah-Hartman 	sport->port.ops = &serial_pxa_pops;
887ab4382d2SGreg Kroah-Hartman 	sport->port.dev = &dev->dev;
888ab4382d2SGreg Kroah-Hartman 	sport->port.flags = UPF_IOREMAP | UPF_BOOT_AUTOCONF;
889ab4382d2SGreg Kroah-Hartman 	sport->port.uartclk = clk_get_rate(sport->clk);
890ab4382d2SGreg Kroah-Hartman 
891699c20f3SHaojian Zhuang 	ret = serial_pxa_probe_dt(dev, sport);
892699c20f3SHaojian Zhuang 	if (ret > 0)
893699c20f3SHaojian Zhuang 		sport->port.line = dev->id;
894699c20f3SHaojian Zhuang 	else if (ret < 0)
895699c20f3SHaojian Zhuang 		goto err_clk;
896699c20f3SHaojian Zhuang 	snprintf(sport->name, PXA_NAME_LEN - 1, "UART%d", sport->port.line + 1);
897ab4382d2SGreg Kroah-Hartman 
89828f65c11SJoe Perches 	sport->port.membase = ioremap(mmres->start, resource_size(mmres));
899ab4382d2SGreg Kroah-Hartman 	if (!sport->port.membase) {
900ab4382d2SGreg Kroah-Hartman 		ret = -ENOMEM;
901ab4382d2SGreg Kroah-Hartman 		goto err_clk;
902ab4382d2SGreg Kroah-Hartman 	}
903ab4382d2SGreg Kroah-Hartman 
904699c20f3SHaojian Zhuang 	serial_pxa_ports[sport->port.line] = sport;
905ab4382d2SGreg Kroah-Hartman 
906ab4382d2SGreg Kroah-Hartman 	uart_add_one_port(&serial_pxa_reg, &sport->port);
907ab4382d2SGreg Kroah-Hartman 	platform_set_drvdata(dev, sport);
908ab4382d2SGreg Kroah-Hartman 
909ab4382d2SGreg Kroah-Hartman 	return 0;
910ab4382d2SGreg Kroah-Hartman 
911ab4382d2SGreg Kroah-Hartman  err_clk:
9129429ccbfSYi Zhang 	clk_unprepare(sport->clk);
913ab4382d2SGreg Kroah-Hartman 	clk_put(sport->clk);
914ab4382d2SGreg Kroah-Hartman  err_free:
915ab4382d2SGreg Kroah-Hartman 	kfree(sport);
916ab4382d2SGreg Kroah-Hartman 	return ret;
917ab4382d2SGreg Kroah-Hartman }
918ab4382d2SGreg Kroah-Hartman 
919ab4382d2SGreg Kroah-Hartman static int serial_pxa_remove(struct platform_device *dev)
920ab4382d2SGreg Kroah-Hartman {
921ab4382d2SGreg Kroah-Hartman 	struct uart_pxa_port *sport = platform_get_drvdata(dev);
922ab4382d2SGreg Kroah-Hartman 
923ab4382d2SGreg Kroah-Hartman 	uart_remove_one_port(&serial_pxa_reg, &sport->port);
9249429ccbfSYi Zhang 
9259429ccbfSYi Zhang 	clk_unprepare(sport->clk);
926ab4382d2SGreg Kroah-Hartman 	clk_put(sport->clk);
927ab4382d2SGreg Kroah-Hartman 	kfree(sport);
928ab4382d2SGreg Kroah-Hartman 
929ab4382d2SGreg Kroah-Hartman 	return 0;
930ab4382d2SGreg Kroah-Hartman }
931ab4382d2SGreg Kroah-Hartman 
932ab4382d2SGreg Kroah-Hartman static struct platform_driver serial_pxa_driver = {
933ab4382d2SGreg Kroah-Hartman         .probe          = serial_pxa_probe,
934ab4382d2SGreg Kroah-Hartman         .remove         = serial_pxa_remove,
935ab4382d2SGreg Kroah-Hartman 
936ab4382d2SGreg Kroah-Hartman 	.driver		= {
937ab4382d2SGreg Kroah-Hartman 	        .name	= "pxa2xx-uart",
938ab4382d2SGreg Kroah-Hartman 		.owner	= THIS_MODULE,
939ab4382d2SGreg Kroah-Hartman #ifdef CONFIG_PM
940ab4382d2SGreg Kroah-Hartman 		.pm	= &serial_pxa_pm_ops,
941ab4382d2SGreg Kroah-Hartman #endif
942699c20f3SHaojian Zhuang 		.of_match_table = serial_pxa_dt_ids,
943ab4382d2SGreg Kroah-Hartman 	},
944ab4382d2SGreg Kroah-Hartman };
945ab4382d2SGreg Kroah-Hartman 
946*6c62cc0dSJingoo Han static int __init serial_pxa_init(void)
947ab4382d2SGreg Kroah-Hartman {
948ab4382d2SGreg Kroah-Hartman 	int ret;
949ab4382d2SGreg Kroah-Hartman 
950ab4382d2SGreg Kroah-Hartman 	ret = uart_register_driver(&serial_pxa_reg);
951ab4382d2SGreg Kroah-Hartman 	if (ret != 0)
952ab4382d2SGreg Kroah-Hartman 		return ret;
953ab4382d2SGreg Kroah-Hartman 
954ab4382d2SGreg Kroah-Hartman 	ret = platform_driver_register(&serial_pxa_driver);
955ab4382d2SGreg Kroah-Hartman 	if (ret != 0)
956ab4382d2SGreg Kroah-Hartman 		uart_unregister_driver(&serial_pxa_reg);
957ab4382d2SGreg Kroah-Hartman 
958ab4382d2SGreg Kroah-Hartman 	return ret;
959ab4382d2SGreg Kroah-Hartman }
960ab4382d2SGreg Kroah-Hartman 
961*6c62cc0dSJingoo Han static void __exit serial_pxa_exit(void)
962ab4382d2SGreg Kroah-Hartman {
963ab4382d2SGreg Kroah-Hartman 	platform_driver_unregister(&serial_pxa_driver);
964ab4382d2SGreg Kroah-Hartman 	uart_unregister_driver(&serial_pxa_reg);
965ab4382d2SGreg Kroah-Hartman }
966ab4382d2SGreg Kroah-Hartman 
967ab4382d2SGreg Kroah-Hartman module_init(serial_pxa_init);
968ab4382d2SGreg Kroah-Hartman module_exit(serial_pxa_exit);
969ab4382d2SGreg Kroah-Hartman 
970ab4382d2SGreg Kroah-Hartman MODULE_LICENSE("GPL");
971ab4382d2SGreg Kroah-Hartman MODULE_ALIAS("platform:pxa2xx-uart");
972