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