xref: /openbmc/linux/drivers/tty/serial/amba-pl010.c (revision fd2b55f8)
1e3b3d0f5SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0+
2ab4382d2SGreg Kroah-Hartman /*
3ab4382d2SGreg Kroah-Hartman  *  Driver for AMBA serial ports
4ab4382d2SGreg Kroah-Hartman  *
5ab4382d2SGreg Kroah-Hartman  *  Based on drivers/char/serial.c, by Linus Torvalds, Theodore Ts'o.
6ab4382d2SGreg Kroah-Hartman  *
7ab4382d2SGreg Kroah-Hartman  *  Copyright 1999 ARM Limited
8ab4382d2SGreg Kroah-Hartman  *  Copyright (C) 2000 Deep Blue Solutions Ltd.
9ab4382d2SGreg Kroah-Hartman  *
10ab4382d2SGreg Kroah-Hartman  * This is a generic driver for ARM AMBA-type serial ports.  They
11ab4382d2SGreg Kroah-Hartman  * have a lot of 16550-like features, but are not register compatible.
12ab4382d2SGreg Kroah-Hartman  * Note that although they do have CTS, DCD and DSR inputs, they do
13ab4382d2SGreg Kroah-Hartman  * not have an RI input, nor do they have DTR or RTS outputs.  If
14ab4382d2SGreg Kroah-Hartman  * required, these have to be supplied via some other means (eg, GPIO)
15ab4382d2SGreg Kroah-Hartman  * and hooked into this driver.
16ab4382d2SGreg Kroah-Hartman  */
17ab4382d2SGreg Kroah-Hartman 
18ab4382d2SGreg Kroah-Hartman #include <linux/module.h>
19ab4382d2SGreg Kroah-Hartman #include <linux/ioport.h>
20ab4382d2SGreg Kroah-Hartman #include <linux/init.h>
21ab4382d2SGreg Kroah-Hartman #include <linux/console.h>
22ab4382d2SGreg Kroah-Hartman #include <linux/sysrq.h>
23ab4382d2SGreg Kroah-Hartman #include <linux/device.h>
24ab4382d2SGreg Kroah-Hartman #include <linux/tty.h>
25ab4382d2SGreg Kroah-Hartman #include <linux/tty_flip.h>
26ab4382d2SGreg Kroah-Hartman #include <linux/serial_core.h>
27ab4382d2SGreg Kroah-Hartman #include <linux/serial.h>
28ab4382d2SGreg Kroah-Hartman #include <linux/amba/bus.h>
29ab4382d2SGreg Kroah-Hartman #include <linux/amba/serial.h>
30ab4382d2SGreg Kroah-Hartman #include <linux/clk.h>
31ab4382d2SGreg Kroah-Hartman #include <linux/slab.h>
3244acd260STushar Behera #include <linux/io.h>
33ab4382d2SGreg Kroah-Hartman 
34ab4382d2SGreg Kroah-Hartman #define UART_NR		8
35ab4382d2SGreg Kroah-Hartman 
36ab4382d2SGreg Kroah-Hartman #define SERIAL_AMBA_MAJOR	204
37ab4382d2SGreg Kroah-Hartman #define SERIAL_AMBA_MINOR	16
38ab4382d2SGreg Kroah-Hartman #define SERIAL_AMBA_NR		UART_NR
39ab4382d2SGreg Kroah-Hartman 
40ab4382d2SGreg Kroah-Hartman #define AMBA_ISR_PASS_LIMIT	256
41ab4382d2SGreg Kroah-Hartman 
42ab4382d2SGreg Kroah-Hartman #define UART_RX_DATA(s)		(((s) & UART01x_FR_RXFE) == 0)
43ab4382d2SGreg Kroah-Hartman #define UART_TX_READY(s)	(((s) & UART01x_FR_TXFF) == 0)
44ab4382d2SGreg Kroah-Hartman 
45ab4382d2SGreg Kroah-Hartman #define UART_DUMMY_RSR_RX	256
46ab4382d2SGreg Kroah-Hartman #define UART_PORT_SIZE		64
47ab4382d2SGreg Kroah-Hartman 
48ab4382d2SGreg Kroah-Hartman /*
49ab4382d2SGreg Kroah-Hartman  * We wrap our port structure around the generic uart_port.
50ab4382d2SGreg Kroah-Hartman  */
51ab4382d2SGreg Kroah-Hartman struct uart_amba_port {
52ab4382d2SGreg Kroah-Hartman 	struct uart_port	port;
53ab4382d2SGreg Kroah-Hartman 	struct clk		*clk;
54ab4382d2SGreg Kroah-Hartman 	struct amba_device	*dev;
55ab4382d2SGreg Kroah-Hartman 	struct amba_pl010_data	*data;
56ab4382d2SGreg Kroah-Hartman 	unsigned int		old_status;
57ab4382d2SGreg Kroah-Hartman };
58ab4382d2SGreg Kroah-Hartman 
pl010_stop_tx(struct uart_port * port)59ab4382d2SGreg Kroah-Hartman static void pl010_stop_tx(struct uart_port *port)
60ab4382d2SGreg Kroah-Hartman {
61b70e5e9dSFabian Frederick 	struct uart_amba_port *uap =
62b70e5e9dSFabian Frederick 		container_of(port, struct uart_amba_port, port);
63ab4382d2SGreg Kroah-Hartman 	unsigned int cr;
64ab4382d2SGreg Kroah-Hartman 
65ab4382d2SGreg Kroah-Hartman 	cr = readb(uap->port.membase + UART010_CR);
66ab4382d2SGreg Kroah-Hartman 	cr &= ~UART010_CR_TIE;
67ab4382d2SGreg Kroah-Hartman 	writel(cr, uap->port.membase + UART010_CR);
68ab4382d2SGreg Kroah-Hartman }
69ab4382d2SGreg Kroah-Hartman 
pl010_start_tx(struct uart_port * port)70ab4382d2SGreg Kroah-Hartman static void pl010_start_tx(struct uart_port *port)
71ab4382d2SGreg Kroah-Hartman {
72b70e5e9dSFabian Frederick 	struct uart_amba_port *uap =
73b70e5e9dSFabian Frederick 		container_of(port, struct uart_amba_port, port);
74ab4382d2SGreg Kroah-Hartman 	unsigned int cr;
75ab4382d2SGreg Kroah-Hartman 
76ab4382d2SGreg Kroah-Hartman 	cr = readb(uap->port.membase + UART010_CR);
77ab4382d2SGreg Kroah-Hartman 	cr |= UART010_CR_TIE;
78ab4382d2SGreg Kroah-Hartman 	writel(cr, uap->port.membase + UART010_CR);
79ab4382d2SGreg Kroah-Hartman }
80ab4382d2SGreg Kroah-Hartman 
pl010_stop_rx(struct uart_port * port)81ab4382d2SGreg Kroah-Hartman static void pl010_stop_rx(struct uart_port *port)
82ab4382d2SGreg Kroah-Hartman {
83b70e5e9dSFabian Frederick 	struct uart_amba_port *uap =
84b70e5e9dSFabian Frederick 		container_of(port, struct uart_amba_port, port);
85ab4382d2SGreg Kroah-Hartman 	unsigned int cr;
86ab4382d2SGreg Kroah-Hartman 
87ab4382d2SGreg Kroah-Hartman 	cr = readb(uap->port.membase + UART010_CR);
88ab4382d2SGreg Kroah-Hartman 	cr &= ~(UART010_CR_RIE | UART010_CR_RTIE);
89ab4382d2SGreg Kroah-Hartman 	writel(cr, uap->port.membase + UART010_CR);
90ab4382d2SGreg Kroah-Hartman }
91ab4382d2SGreg Kroah-Hartman 
pl010_disable_ms(struct uart_port * port)92cab68f89SPeter Hurley static void pl010_disable_ms(struct uart_port *port)
93cab68f89SPeter Hurley {
94cab68f89SPeter Hurley 	struct uart_amba_port *uap = (struct uart_amba_port *)port;
95cab68f89SPeter Hurley 	unsigned int cr;
96cab68f89SPeter Hurley 
97cab68f89SPeter Hurley 	cr = readb(uap->port.membase + UART010_CR);
98cab68f89SPeter Hurley 	cr &= ~UART010_CR_MSIE;
99cab68f89SPeter Hurley 	writel(cr, uap->port.membase + UART010_CR);
100cab68f89SPeter Hurley }
101cab68f89SPeter Hurley 
pl010_enable_ms(struct uart_port * port)102ab4382d2SGreg Kroah-Hartman static void pl010_enable_ms(struct uart_port *port)
103ab4382d2SGreg Kroah-Hartman {
104b70e5e9dSFabian Frederick 	struct uart_amba_port *uap =
105b70e5e9dSFabian Frederick 		container_of(port, struct uart_amba_port, port);
106ab4382d2SGreg Kroah-Hartman 	unsigned int cr;
107ab4382d2SGreg Kroah-Hartman 
108ab4382d2SGreg Kroah-Hartman 	cr = readb(uap->port.membase + UART010_CR);
109ab4382d2SGreg Kroah-Hartman 	cr |= UART010_CR_MSIE;
110ab4382d2SGreg Kroah-Hartman 	writel(cr, uap->port.membase + UART010_CR);
111ab4382d2SGreg Kroah-Hartman }
112ab4382d2SGreg Kroah-Hartman 
pl010_rx_chars(struct uart_port * port)113f166d19fSJiri Slaby static void pl010_rx_chars(struct uart_port *port)
114ab4382d2SGreg Kroah-Hartman {
115*fd2b55f8SJiri Slaby 	unsigned int status, rsr, max_count = 256;
116*fd2b55f8SJiri Slaby 	u8 ch, flag;
117ab4382d2SGreg Kroah-Hartman 
118f166d19fSJiri Slaby 	status = readb(port->membase + UART01x_FR);
119ab4382d2SGreg Kroah-Hartman 	while (UART_RX_DATA(status) && max_count--) {
120f166d19fSJiri Slaby 		ch = readb(port->membase + UART01x_DR);
121ab4382d2SGreg Kroah-Hartman 		flag = TTY_NORMAL;
122ab4382d2SGreg Kroah-Hartman 
123f166d19fSJiri Slaby 		port->icount.rx++;
124ab4382d2SGreg Kroah-Hartman 
125ab4382d2SGreg Kroah-Hartman 		/*
126ab4382d2SGreg Kroah-Hartman 		 * Note that the error handling code is
127ab4382d2SGreg Kroah-Hartman 		 * out of the main execution path
128ab4382d2SGreg Kroah-Hartman 		 */
129f166d19fSJiri Slaby 		rsr = readb(port->membase + UART01x_RSR) | UART_DUMMY_RSR_RX;
130ab4382d2SGreg Kroah-Hartman 		if (unlikely(rsr & UART01x_RSR_ANY)) {
131f166d19fSJiri Slaby 			writel(0, port->membase + UART01x_ECR);
132ab4382d2SGreg Kroah-Hartman 
133ab4382d2SGreg Kroah-Hartman 			if (rsr & UART01x_RSR_BE) {
134ab4382d2SGreg Kroah-Hartman 				rsr &= ~(UART01x_RSR_FE | UART01x_RSR_PE);
135f166d19fSJiri Slaby 				port->icount.brk++;
136f166d19fSJiri Slaby 				if (uart_handle_break(port))
137ab4382d2SGreg Kroah-Hartman 					goto ignore_char;
138ab4382d2SGreg Kroah-Hartman 			} else if (rsr & UART01x_RSR_PE)
139f166d19fSJiri Slaby 				port->icount.parity++;
140ab4382d2SGreg Kroah-Hartman 			else if (rsr & UART01x_RSR_FE)
141f166d19fSJiri Slaby 				port->icount.frame++;
142ab4382d2SGreg Kroah-Hartman 			if (rsr & UART01x_RSR_OE)
143f166d19fSJiri Slaby 				port->icount.overrun++;
144ab4382d2SGreg Kroah-Hartman 
145f166d19fSJiri Slaby 			rsr &= port->read_status_mask;
146ab4382d2SGreg Kroah-Hartman 
147ab4382d2SGreg Kroah-Hartman 			if (rsr & UART01x_RSR_BE)
148ab4382d2SGreg Kroah-Hartman 				flag = TTY_BREAK;
149ab4382d2SGreg Kroah-Hartman 			else if (rsr & UART01x_RSR_PE)
150ab4382d2SGreg Kroah-Hartman 				flag = TTY_PARITY;
151ab4382d2SGreg Kroah-Hartman 			else if (rsr & UART01x_RSR_FE)
152ab4382d2SGreg Kroah-Hartman 				flag = TTY_FRAME;
153ab4382d2SGreg Kroah-Hartman 		}
154ab4382d2SGreg Kroah-Hartman 
155f166d19fSJiri Slaby 		if (uart_handle_sysrq_char(port, ch))
156ab4382d2SGreg Kroah-Hartman 			goto ignore_char;
157ab4382d2SGreg Kroah-Hartman 
158f166d19fSJiri Slaby 		uart_insert_char(port, rsr, UART01x_RSR_OE, ch, flag);
159ab4382d2SGreg Kroah-Hartman 
160ab4382d2SGreg Kroah-Hartman 	ignore_char:
161f166d19fSJiri Slaby 		status = readb(port->membase + UART01x_FR);
162ab4382d2SGreg Kroah-Hartman 	}
163f166d19fSJiri Slaby 	tty_flip_buffer_push(&port->state->port);
164ab4382d2SGreg Kroah-Hartman }
165ab4382d2SGreg Kroah-Hartman 
pl010_tx_chars(struct uart_port * port)166f166d19fSJiri Slaby static void pl010_tx_chars(struct uart_port *port)
167ab4382d2SGreg Kroah-Hartman {
168d11cc8c3SJiri Slaby (SUSE) 	u8 ch;
169ab4382d2SGreg Kroah-Hartman 
170d11cc8c3SJiri Slaby (SUSE) 	uart_port_tx_limited(port, ch, port->fifosize >> 1,
171d11cc8c3SJiri Slaby (SUSE) 		true,
172d11cc8c3SJiri Slaby (SUSE) 		writel(ch, port->membase + UART01x_DR),
173d11cc8c3SJiri Slaby (SUSE) 		({}));
174ab4382d2SGreg Kroah-Hartman }
175ab4382d2SGreg Kroah-Hartman 
pl010_modem_status(struct uart_amba_port * uap)176ab4382d2SGreg Kroah-Hartman static void pl010_modem_status(struct uart_amba_port *uap)
177ab4382d2SGreg Kroah-Hartman {
178f166d19fSJiri Slaby 	struct uart_port *port = &uap->port;
179ab4382d2SGreg Kroah-Hartman 	unsigned int status, delta;
180ab4382d2SGreg Kroah-Hartman 
181f166d19fSJiri Slaby 	writel(0, port->membase + UART010_ICR);
182ab4382d2SGreg Kroah-Hartman 
183f166d19fSJiri Slaby 	status = readb(port->membase + UART01x_FR) & UART01x_FR_MODEM_ANY;
184ab4382d2SGreg Kroah-Hartman 
185ab4382d2SGreg Kroah-Hartman 	delta = status ^ uap->old_status;
186ab4382d2SGreg Kroah-Hartman 	uap->old_status = status;
187ab4382d2SGreg Kroah-Hartman 
188ab4382d2SGreg Kroah-Hartman 	if (!delta)
189ab4382d2SGreg Kroah-Hartman 		return;
190ab4382d2SGreg Kroah-Hartman 
191ab4382d2SGreg Kroah-Hartman 	if (delta & UART01x_FR_DCD)
192f166d19fSJiri Slaby 		uart_handle_dcd_change(port, status & UART01x_FR_DCD);
193ab4382d2SGreg Kroah-Hartman 
194ab4382d2SGreg Kroah-Hartman 	if (delta & UART01x_FR_DSR)
195f166d19fSJiri Slaby 		port->icount.dsr++;
196ab4382d2SGreg Kroah-Hartman 
197ab4382d2SGreg Kroah-Hartman 	if (delta & UART01x_FR_CTS)
198f166d19fSJiri Slaby 		uart_handle_cts_change(port, status & UART01x_FR_CTS);
199ab4382d2SGreg Kroah-Hartman 
200f166d19fSJiri Slaby 	wake_up_interruptible(&port->state->port.delta_msr_wait);
201ab4382d2SGreg Kroah-Hartman }
202ab4382d2SGreg Kroah-Hartman 
pl010_int(int irq,void * dev_id)203ab4382d2SGreg Kroah-Hartman static irqreturn_t pl010_int(int irq, void *dev_id)
204ab4382d2SGreg Kroah-Hartman {
205ab4382d2SGreg Kroah-Hartman 	struct uart_amba_port *uap = dev_id;
206f166d19fSJiri Slaby 	struct uart_port *port = &uap->port;
207ab4382d2SGreg Kroah-Hartman 	unsigned int status, pass_counter = AMBA_ISR_PASS_LIMIT;
208ab4382d2SGreg Kroah-Hartman 	int handled = 0;
209ab4382d2SGreg Kroah-Hartman 
210f166d19fSJiri Slaby 	spin_lock(&port->lock);
211ab4382d2SGreg Kroah-Hartman 
212f166d19fSJiri Slaby 	status = readb(port->membase + UART010_IIR);
213ab4382d2SGreg Kroah-Hartman 	if (status) {
214ab4382d2SGreg Kroah-Hartman 		do {
215ab4382d2SGreg Kroah-Hartman 			if (status & (UART010_IIR_RTIS | UART010_IIR_RIS))
216f166d19fSJiri Slaby 				pl010_rx_chars(port);
217ab4382d2SGreg Kroah-Hartman 			if (status & UART010_IIR_MIS)
218ab4382d2SGreg Kroah-Hartman 				pl010_modem_status(uap);
219ab4382d2SGreg Kroah-Hartman 			if (status & UART010_IIR_TIS)
220f166d19fSJiri Slaby 				pl010_tx_chars(port);
221ab4382d2SGreg Kroah-Hartman 
222ab4382d2SGreg Kroah-Hartman 			if (pass_counter-- == 0)
223ab4382d2SGreg Kroah-Hartman 				break;
224ab4382d2SGreg Kroah-Hartman 
225f166d19fSJiri Slaby 			status = readb(port->membase + UART010_IIR);
226ab4382d2SGreg Kroah-Hartman 		} while (status & (UART010_IIR_RTIS | UART010_IIR_RIS |
227ab4382d2SGreg Kroah-Hartman 				   UART010_IIR_TIS));
228ab4382d2SGreg Kroah-Hartman 		handled = 1;
229ab4382d2SGreg Kroah-Hartman 	}
230ab4382d2SGreg Kroah-Hartman 
231f166d19fSJiri Slaby 	spin_unlock(&port->lock);
232ab4382d2SGreg Kroah-Hartman 
233ab4382d2SGreg Kroah-Hartman 	return IRQ_RETVAL(handled);
234ab4382d2SGreg Kroah-Hartman }
235ab4382d2SGreg Kroah-Hartman 
pl010_tx_empty(struct uart_port * port)236ab4382d2SGreg Kroah-Hartman static unsigned int pl010_tx_empty(struct uart_port *port)
237ab4382d2SGreg Kroah-Hartman {
238f166d19fSJiri Slaby 	unsigned int status = readb(port->membase + UART01x_FR);
239f166d19fSJiri Slaby 
240ab4382d2SGreg Kroah-Hartman 	return status & UART01x_FR_BUSY ? 0 : TIOCSER_TEMT;
241ab4382d2SGreg Kroah-Hartman }
242ab4382d2SGreg Kroah-Hartman 
pl010_get_mctrl(struct uart_port * port)243ab4382d2SGreg Kroah-Hartman static unsigned int pl010_get_mctrl(struct uart_port *port)
244ab4382d2SGreg Kroah-Hartman {
245ab4382d2SGreg Kroah-Hartman 	unsigned int result = 0;
246ab4382d2SGreg Kroah-Hartman 	unsigned int status;
247ab4382d2SGreg Kroah-Hartman 
248f166d19fSJiri Slaby 	status = readb(port->membase + UART01x_FR);
249ab4382d2SGreg Kroah-Hartman 	if (status & UART01x_FR_DCD)
250ab4382d2SGreg Kroah-Hartman 		result |= TIOCM_CAR;
251ab4382d2SGreg Kroah-Hartman 	if (status & UART01x_FR_DSR)
252ab4382d2SGreg Kroah-Hartman 		result |= TIOCM_DSR;
253ab4382d2SGreg Kroah-Hartman 	if (status & UART01x_FR_CTS)
254ab4382d2SGreg Kroah-Hartman 		result |= TIOCM_CTS;
255ab4382d2SGreg Kroah-Hartman 
256ab4382d2SGreg Kroah-Hartman 	return result;
257ab4382d2SGreg Kroah-Hartman }
258ab4382d2SGreg Kroah-Hartman 
pl010_set_mctrl(struct uart_port * port,unsigned int mctrl)259ab4382d2SGreg Kroah-Hartman static void pl010_set_mctrl(struct uart_port *port, unsigned int mctrl)
260ab4382d2SGreg Kroah-Hartman {
261b70e5e9dSFabian Frederick 	struct uart_amba_port *uap =
262b70e5e9dSFabian Frederick 		container_of(port, struct uart_amba_port, port);
263ab4382d2SGreg Kroah-Hartman 
264ab4382d2SGreg Kroah-Hartman 	if (uap->data)
265f166d19fSJiri Slaby 		uap->data->set_mctrl(uap->dev, port->membase, mctrl);
266ab4382d2SGreg Kroah-Hartman }
267ab4382d2SGreg Kroah-Hartman 
pl010_break_ctl(struct uart_port * port,int break_state)268ab4382d2SGreg Kroah-Hartman static void pl010_break_ctl(struct uart_port *port, int break_state)
269ab4382d2SGreg Kroah-Hartman {
270ab4382d2SGreg Kroah-Hartman 	unsigned long flags;
271ab4382d2SGreg Kroah-Hartman 	unsigned int lcr_h;
272ab4382d2SGreg Kroah-Hartman 
273f166d19fSJiri Slaby 	spin_lock_irqsave(&port->lock, flags);
274f166d19fSJiri Slaby 	lcr_h = readb(port->membase + UART010_LCRH);
275ab4382d2SGreg Kroah-Hartman 	if (break_state == -1)
276ab4382d2SGreg Kroah-Hartman 		lcr_h |= UART01x_LCRH_BRK;
277ab4382d2SGreg Kroah-Hartman 	else
278ab4382d2SGreg Kroah-Hartman 		lcr_h &= ~UART01x_LCRH_BRK;
279f166d19fSJiri Slaby 	writel(lcr_h, port->membase + UART010_LCRH);
280f166d19fSJiri Slaby 	spin_unlock_irqrestore(&port->lock, flags);
281ab4382d2SGreg Kroah-Hartman }
282ab4382d2SGreg Kroah-Hartman 
pl010_startup(struct uart_port * port)283ab4382d2SGreg Kroah-Hartman static int pl010_startup(struct uart_port *port)
284ab4382d2SGreg Kroah-Hartman {
285b70e5e9dSFabian Frederick 	struct uart_amba_port *uap =
286b70e5e9dSFabian Frederick 		container_of(port, struct uart_amba_port, port);
287ab4382d2SGreg Kroah-Hartman 	int retval;
288ab4382d2SGreg Kroah-Hartman 
289ab4382d2SGreg Kroah-Hartman 	/*
290ab4382d2SGreg Kroah-Hartman 	 * Try to enable the clock producer.
291ab4382d2SGreg Kroah-Hartman 	 */
2921c4c4394SJulia Lawall 	retval = clk_prepare_enable(uap->clk);
293ab4382d2SGreg Kroah-Hartman 	if (retval)
2941c4c4394SJulia Lawall 		goto out;
295ab4382d2SGreg Kroah-Hartman 
296f166d19fSJiri Slaby 	port->uartclk = clk_get_rate(uap->clk);
297ab4382d2SGreg Kroah-Hartman 
298ab4382d2SGreg Kroah-Hartman 	/*
299ab4382d2SGreg Kroah-Hartman 	 * Allocate the IRQ
300ab4382d2SGreg Kroah-Hartman 	 */
301f166d19fSJiri Slaby 	retval = request_irq(port->irq, pl010_int, 0, "uart-pl010", uap);
302ab4382d2SGreg Kroah-Hartman 	if (retval)
303ab4382d2SGreg Kroah-Hartman 		goto clk_dis;
304ab4382d2SGreg Kroah-Hartman 
305ab4382d2SGreg Kroah-Hartman 	/*
306ab4382d2SGreg Kroah-Hartman 	 * initialise the old status of the modem signals
307ab4382d2SGreg Kroah-Hartman 	 */
308f166d19fSJiri Slaby 	uap->old_status = readb(port->membase + UART01x_FR) & UART01x_FR_MODEM_ANY;
309ab4382d2SGreg Kroah-Hartman 
310ab4382d2SGreg Kroah-Hartman 	/*
311ab4382d2SGreg Kroah-Hartman 	 * Finally, enable interrupts
312ab4382d2SGreg Kroah-Hartman 	 */
313ab4382d2SGreg Kroah-Hartman 	writel(UART01x_CR_UARTEN | UART010_CR_RIE | UART010_CR_RTIE,
314f166d19fSJiri Slaby 	       port->membase + UART010_CR);
315ab4382d2SGreg Kroah-Hartman 
316ab4382d2SGreg Kroah-Hartman 	return 0;
317ab4382d2SGreg Kroah-Hartman 
318ab4382d2SGreg Kroah-Hartman  clk_dis:
3191c4c4394SJulia Lawall 	clk_disable_unprepare(uap->clk);
320ab4382d2SGreg Kroah-Hartman  out:
321ab4382d2SGreg Kroah-Hartman 	return retval;
322ab4382d2SGreg Kroah-Hartman }
323ab4382d2SGreg Kroah-Hartman 
pl010_shutdown(struct uart_port * port)324ab4382d2SGreg Kroah-Hartman static void pl010_shutdown(struct uart_port *port)
325ab4382d2SGreg Kroah-Hartman {
326b70e5e9dSFabian Frederick 	struct uart_amba_port *uap =
327b70e5e9dSFabian Frederick 		container_of(port, struct uart_amba_port, port);
328ab4382d2SGreg Kroah-Hartman 
329ab4382d2SGreg Kroah-Hartman 	/*
330ab4382d2SGreg Kroah-Hartman 	 * Free the interrupt
331ab4382d2SGreg Kroah-Hartman 	 */
332f166d19fSJiri Slaby 	free_irq(port->irq, uap);
333ab4382d2SGreg Kroah-Hartman 
334ab4382d2SGreg Kroah-Hartman 	/*
335ab4382d2SGreg Kroah-Hartman 	 * disable all interrupts, disable the port
336ab4382d2SGreg Kroah-Hartman 	 */
337f166d19fSJiri Slaby 	writel(0, port->membase + UART010_CR);
338ab4382d2SGreg Kroah-Hartman 
339ab4382d2SGreg Kroah-Hartman 	/* disable break condition and fifos */
340f166d19fSJiri Slaby 	writel(readb(port->membase + UART010_LCRH) &
341ab4382d2SGreg Kroah-Hartman 		~(UART01x_LCRH_BRK | UART01x_LCRH_FEN),
342f166d19fSJiri Slaby 	       port->membase + UART010_LCRH);
343ab4382d2SGreg Kroah-Hartman 
344ab4382d2SGreg Kroah-Hartman 	/*
345ab4382d2SGreg Kroah-Hartman 	 * Shut down the clock producer
346ab4382d2SGreg Kroah-Hartman 	 */
3471c4c4394SJulia Lawall 	clk_disable_unprepare(uap->clk);
348ab4382d2SGreg Kroah-Hartman }
349ab4382d2SGreg Kroah-Hartman 
350ab4382d2SGreg Kroah-Hartman static void
pl010_set_termios(struct uart_port * port,struct ktermios * termios,const struct ktermios * old)351ab4382d2SGreg Kroah-Hartman pl010_set_termios(struct uart_port *port, struct ktermios *termios,
352bec5b814SIlpo Järvinen 		  const struct ktermios *old)
353ab4382d2SGreg Kroah-Hartman {
354ab4382d2SGreg Kroah-Hartman 	unsigned int lcr_h, old_cr;
355ab4382d2SGreg Kroah-Hartman 	unsigned long flags;
356ab4382d2SGreg Kroah-Hartman 	unsigned int baud, quot;
357ab4382d2SGreg Kroah-Hartman 
358ab4382d2SGreg Kroah-Hartman 	/*
359ab4382d2SGreg Kroah-Hartman 	 * Ask the core to calculate the divisor for us.
360ab4382d2SGreg Kroah-Hartman 	 */
361f166d19fSJiri Slaby 	baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk / 16);
362ab4382d2SGreg Kroah-Hartman 	quot = uart_get_divisor(port, baud);
363ab4382d2SGreg Kroah-Hartman 
364ab4382d2SGreg Kroah-Hartman 	switch (termios->c_cflag & CSIZE) {
365ab4382d2SGreg Kroah-Hartman 	case CS5:
366ab4382d2SGreg Kroah-Hartman 		lcr_h = UART01x_LCRH_WLEN_5;
367ab4382d2SGreg Kroah-Hartman 		break;
368ab4382d2SGreg Kroah-Hartman 	case CS6:
369ab4382d2SGreg Kroah-Hartman 		lcr_h = UART01x_LCRH_WLEN_6;
370ab4382d2SGreg Kroah-Hartman 		break;
371ab4382d2SGreg Kroah-Hartman 	case CS7:
372ab4382d2SGreg Kroah-Hartman 		lcr_h = UART01x_LCRH_WLEN_7;
373ab4382d2SGreg Kroah-Hartman 		break;
374ab4382d2SGreg Kroah-Hartman 	default: // CS8
375ab4382d2SGreg Kroah-Hartman 		lcr_h = UART01x_LCRH_WLEN_8;
376ab4382d2SGreg Kroah-Hartman 		break;
377ab4382d2SGreg Kroah-Hartman 	}
378ab4382d2SGreg Kroah-Hartman 	if (termios->c_cflag & CSTOPB)
379ab4382d2SGreg Kroah-Hartman 		lcr_h |= UART01x_LCRH_STP2;
380ab4382d2SGreg Kroah-Hartman 	if (termios->c_cflag & PARENB) {
381ab4382d2SGreg Kroah-Hartman 		lcr_h |= UART01x_LCRH_PEN;
382ab4382d2SGreg Kroah-Hartman 		if (!(termios->c_cflag & PARODD))
383ab4382d2SGreg Kroah-Hartman 			lcr_h |= UART01x_LCRH_EPS;
384ab4382d2SGreg Kroah-Hartman 	}
385f166d19fSJiri Slaby 	if (port->fifosize > 1)
386ab4382d2SGreg Kroah-Hartman 		lcr_h |= UART01x_LCRH_FEN;
387ab4382d2SGreg Kroah-Hartman 
388f166d19fSJiri Slaby 	spin_lock_irqsave(&port->lock, flags);
389ab4382d2SGreg Kroah-Hartman 
390ab4382d2SGreg Kroah-Hartman 	/*
391ab4382d2SGreg Kroah-Hartman 	 * Update the per-port timeout.
392ab4382d2SGreg Kroah-Hartman 	 */
393ab4382d2SGreg Kroah-Hartman 	uart_update_timeout(port, termios->c_cflag, baud);
394ab4382d2SGreg Kroah-Hartman 
395f166d19fSJiri Slaby 	port->read_status_mask = UART01x_RSR_OE;
396ab4382d2SGreg Kroah-Hartman 	if (termios->c_iflag & INPCK)
397f166d19fSJiri Slaby 		port->read_status_mask |= UART01x_RSR_FE | UART01x_RSR_PE;
398ef8b9ddcSPeter Hurley 	if (termios->c_iflag & (IGNBRK | BRKINT | PARMRK))
399f166d19fSJiri Slaby 		port->read_status_mask |= UART01x_RSR_BE;
400ab4382d2SGreg Kroah-Hartman 
401ab4382d2SGreg Kroah-Hartman 	/*
402ab4382d2SGreg Kroah-Hartman 	 * Characters to ignore
403ab4382d2SGreg Kroah-Hartman 	 */
404f166d19fSJiri Slaby 	port->ignore_status_mask = 0;
405ab4382d2SGreg Kroah-Hartman 	if (termios->c_iflag & IGNPAR)
406f166d19fSJiri Slaby 		port->ignore_status_mask |= UART01x_RSR_FE | UART01x_RSR_PE;
407ab4382d2SGreg Kroah-Hartman 	if (termios->c_iflag & IGNBRK) {
408f166d19fSJiri Slaby 		port->ignore_status_mask |= UART01x_RSR_BE;
409ab4382d2SGreg Kroah-Hartman 		/*
410ab4382d2SGreg Kroah-Hartman 		 * If we're ignoring parity and break indicators,
411ab4382d2SGreg Kroah-Hartman 		 * ignore overruns too (for real raw support).
412ab4382d2SGreg Kroah-Hartman 		 */
413ab4382d2SGreg Kroah-Hartman 		if (termios->c_iflag & IGNPAR)
414f166d19fSJiri Slaby 			port->ignore_status_mask |= UART01x_RSR_OE;
415ab4382d2SGreg Kroah-Hartman 	}
416ab4382d2SGreg Kroah-Hartman 
417ab4382d2SGreg Kroah-Hartman 	/*
418ab4382d2SGreg Kroah-Hartman 	 * Ignore all characters if CREAD is not set.
419ab4382d2SGreg Kroah-Hartman 	 */
420ab4382d2SGreg Kroah-Hartman 	if ((termios->c_cflag & CREAD) == 0)
421f166d19fSJiri Slaby 		port->ignore_status_mask |= UART_DUMMY_RSR_RX;
422ab4382d2SGreg Kroah-Hartman 
423f166d19fSJiri Slaby 	old_cr = readb(port->membase + UART010_CR) & ~UART010_CR_MSIE;
424ab4382d2SGreg Kroah-Hartman 
425ab4382d2SGreg Kroah-Hartman 	if (UART_ENABLE_MS(port, termios->c_cflag))
426ab4382d2SGreg Kroah-Hartman 		old_cr |= UART010_CR_MSIE;
427ab4382d2SGreg Kroah-Hartman 
428ab4382d2SGreg Kroah-Hartman 	/* Set baud rate */
429ab4382d2SGreg Kroah-Hartman 	quot -= 1;
430f166d19fSJiri Slaby 	writel((quot & 0xf00) >> 8, port->membase + UART010_LCRM);
431f166d19fSJiri Slaby 	writel(quot & 0xff, port->membase + UART010_LCRL);
432ab4382d2SGreg Kroah-Hartman 
433ab4382d2SGreg Kroah-Hartman 	/*
434ab4382d2SGreg Kroah-Hartman 	 * ----------v----------v----------v----------v-----
435ab4382d2SGreg Kroah-Hartman 	 * NOTE: MUST BE WRITTEN AFTER UARTLCR_M & UARTLCR_L
436ab4382d2SGreg Kroah-Hartman 	 * ----------^----------^----------^----------^-----
437ab4382d2SGreg Kroah-Hartman 	 */
438f166d19fSJiri Slaby 	writel(lcr_h, port->membase + UART010_LCRH);
439f166d19fSJiri Slaby 	writel(old_cr, port->membase + UART010_CR);
440ab4382d2SGreg Kroah-Hartman 
441f166d19fSJiri Slaby 	spin_unlock_irqrestore(&port->lock, flags);
442ab4382d2SGreg Kroah-Hartman }
443ab4382d2SGreg Kroah-Hartman 
pl010_set_ldisc(struct uart_port * port,struct ktermios * termios)444732a84a0SPeter Hurley static void pl010_set_ldisc(struct uart_port *port, struct ktermios *termios)
445ab4382d2SGreg Kroah-Hartman {
446732a84a0SPeter Hurley 	if (termios->c_line == N_PPS) {
447ab4382d2SGreg Kroah-Hartman 		port->flags |= UPF_HARDPPS_CD;
448d41510ceSPeter Hurley 		spin_lock_irq(&port->lock);
449ab4382d2SGreg Kroah-Hartman 		pl010_enable_ms(port);
450d41510ceSPeter Hurley 		spin_unlock_irq(&port->lock);
451cab68f89SPeter Hurley 	} else {
452ab4382d2SGreg Kroah-Hartman 		port->flags &= ~UPF_HARDPPS_CD;
453cab68f89SPeter Hurley 		if (!UART_ENABLE_MS(port, termios->c_cflag)) {
454cab68f89SPeter Hurley 			spin_lock_irq(&port->lock);
455cab68f89SPeter Hurley 			pl010_disable_ms(port);
456cab68f89SPeter Hurley 			spin_unlock_irq(&port->lock);
457cab68f89SPeter Hurley 		}
458cab68f89SPeter Hurley 	}
459ab4382d2SGreg Kroah-Hartman }
460ab4382d2SGreg Kroah-Hartman 
pl010_type(struct uart_port * port)461ab4382d2SGreg Kroah-Hartman static const char *pl010_type(struct uart_port *port)
462ab4382d2SGreg Kroah-Hartman {
463ab4382d2SGreg Kroah-Hartman 	return port->type == PORT_AMBA ? "AMBA" : NULL;
464ab4382d2SGreg Kroah-Hartman }
465ab4382d2SGreg Kroah-Hartman 
466ab4382d2SGreg Kroah-Hartman /*
467ab4382d2SGreg Kroah-Hartman  * Release the memory region(s) being used by 'port'
468ab4382d2SGreg Kroah-Hartman  */
pl010_release_port(struct uart_port * port)469ab4382d2SGreg Kroah-Hartman static void pl010_release_port(struct uart_port *port)
470ab4382d2SGreg Kroah-Hartman {
471ab4382d2SGreg Kroah-Hartman 	release_mem_region(port->mapbase, UART_PORT_SIZE);
472ab4382d2SGreg Kroah-Hartman }
473ab4382d2SGreg Kroah-Hartman 
474ab4382d2SGreg Kroah-Hartman /*
475ab4382d2SGreg Kroah-Hartman  * Request the memory region(s) being used by 'port'
476ab4382d2SGreg Kroah-Hartman  */
pl010_request_port(struct uart_port * port)477ab4382d2SGreg Kroah-Hartman static int pl010_request_port(struct uart_port *port)
478ab4382d2SGreg Kroah-Hartman {
479ab4382d2SGreg Kroah-Hartman 	return request_mem_region(port->mapbase, UART_PORT_SIZE, "uart-pl010")
480ab4382d2SGreg Kroah-Hartman 			!= NULL ? 0 : -EBUSY;
481ab4382d2SGreg Kroah-Hartman }
482ab4382d2SGreg Kroah-Hartman 
483ab4382d2SGreg Kroah-Hartman /*
484ab4382d2SGreg Kroah-Hartman  * Configure/autoconfigure the port.
485ab4382d2SGreg Kroah-Hartman  */
pl010_config_port(struct uart_port * port,int flags)486ab4382d2SGreg Kroah-Hartman static void pl010_config_port(struct uart_port *port, int flags)
487ab4382d2SGreg Kroah-Hartman {
488ab4382d2SGreg Kroah-Hartman 	if (flags & UART_CONFIG_TYPE) {
489ab4382d2SGreg Kroah-Hartman 		port->type = PORT_AMBA;
490ab4382d2SGreg Kroah-Hartman 		pl010_request_port(port);
491ab4382d2SGreg Kroah-Hartman 	}
492ab4382d2SGreg Kroah-Hartman }
493ab4382d2SGreg Kroah-Hartman 
494ab4382d2SGreg Kroah-Hartman /*
495ab4382d2SGreg Kroah-Hartman  * verify the new serial_struct (for TIOCSSERIAL).
496ab4382d2SGreg Kroah-Hartman  */
pl010_verify_port(struct uart_port * port,struct serial_struct * ser)497ab4382d2SGreg Kroah-Hartman static int pl010_verify_port(struct uart_port *port, struct serial_struct *ser)
498ab4382d2SGreg Kroah-Hartman {
499ab4382d2SGreg Kroah-Hartman 	int ret = 0;
500ab4382d2SGreg Kroah-Hartman 	if (ser->type != PORT_UNKNOWN && ser->type != PORT_AMBA)
501ab4382d2SGreg Kroah-Hartman 		ret = -EINVAL;
502ab4382d2SGreg Kroah-Hartman 	if (ser->irq < 0 || ser->irq >= nr_irqs)
503ab4382d2SGreg Kroah-Hartman 		ret = -EINVAL;
504ab4382d2SGreg Kroah-Hartman 	if (ser->baud_base < 9600)
505ab4382d2SGreg Kroah-Hartman 		ret = -EINVAL;
506ab4382d2SGreg Kroah-Hartman 	return ret;
507ab4382d2SGreg Kroah-Hartman }
508ab4382d2SGreg Kroah-Hartman 
5092331e068SBhumika Goyal static const struct uart_ops amba_pl010_pops = {
510ab4382d2SGreg Kroah-Hartman 	.tx_empty	= pl010_tx_empty,
511ab4382d2SGreg Kroah-Hartman 	.set_mctrl	= pl010_set_mctrl,
512ab4382d2SGreg Kroah-Hartman 	.get_mctrl	= pl010_get_mctrl,
513ab4382d2SGreg Kroah-Hartman 	.stop_tx	= pl010_stop_tx,
514ab4382d2SGreg Kroah-Hartman 	.start_tx	= pl010_start_tx,
515ab4382d2SGreg Kroah-Hartman 	.stop_rx	= pl010_stop_rx,
516ab4382d2SGreg Kroah-Hartman 	.enable_ms	= pl010_enable_ms,
517ab4382d2SGreg Kroah-Hartman 	.break_ctl	= pl010_break_ctl,
518ab4382d2SGreg Kroah-Hartman 	.startup	= pl010_startup,
519ab4382d2SGreg Kroah-Hartman 	.shutdown	= pl010_shutdown,
520ab4382d2SGreg Kroah-Hartman 	.set_termios	= pl010_set_termios,
521ab4382d2SGreg Kroah-Hartman 	.set_ldisc	= pl010_set_ldisc,
522ab4382d2SGreg Kroah-Hartman 	.type		= pl010_type,
523ab4382d2SGreg Kroah-Hartman 	.release_port	= pl010_release_port,
524ab4382d2SGreg Kroah-Hartman 	.request_port	= pl010_request_port,
525ab4382d2SGreg Kroah-Hartman 	.config_port	= pl010_config_port,
526ab4382d2SGreg Kroah-Hartman 	.verify_port	= pl010_verify_port,
527ab4382d2SGreg Kroah-Hartman };
528ab4382d2SGreg Kroah-Hartman 
529ab4382d2SGreg Kroah-Hartman static struct uart_amba_port *amba_ports[UART_NR];
530ab4382d2SGreg Kroah-Hartman 
531ab4382d2SGreg Kroah-Hartman #ifdef CONFIG_SERIAL_AMBA_PL010_CONSOLE
532ab4382d2SGreg Kroah-Hartman 
pl010_console_putchar(struct uart_port * port,unsigned char ch)5333f8bab17SJiri Slaby static void pl010_console_putchar(struct uart_port *port, unsigned char ch)
534ab4382d2SGreg Kroah-Hartman {
535ab4382d2SGreg Kroah-Hartman 	unsigned int status;
536ab4382d2SGreg Kroah-Hartman 
537ab4382d2SGreg Kroah-Hartman 	do {
538f166d19fSJiri Slaby 		status = readb(port->membase + UART01x_FR);
539ab4382d2SGreg Kroah-Hartman 		barrier();
540ab4382d2SGreg Kroah-Hartman 	} while (!UART_TX_READY(status));
541f166d19fSJiri Slaby 	writel(ch, port->membase + UART01x_DR);
542ab4382d2SGreg Kroah-Hartman }
543ab4382d2SGreg Kroah-Hartman 
544ab4382d2SGreg Kroah-Hartman static void
pl010_console_write(struct console * co,const char * s,unsigned int count)545ab4382d2SGreg Kroah-Hartman pl010_console_write(struct console *co, const char *s, unsigned int count)
546ab4382d2SGreg Kroah-Hartman {
547ab4382d2SGreg Kroah-Hartman 	struct uart_amba_port *uap = amba_ports[co->index];
548f166d19fSJiri Slaby 	struct uart_port *port = &uap->port;
549ab4382d2SGreg Kroah-Hartman 	unsigned int status, old_cr;
550ab4382d2SGreg Kroah-Hartman 
551ab4382d2SGreg Kroah-Hartman 	clk_enable(uap->clk);
552ab4382d2SGreg Kroah-Hartman 
553ab4382d2SGreg Kroah-Hartman 	/*
554ab4382d2SGreg Kroah-Hartman 	 *	First save the CR then disable the interrupts
555ab4382d2SGreg Kroah-Hartman 	 */
556f166d19fSJiri Slaby 	old_cr = readb(port->membase + UART010_CR);
557f166d19fSJiri Slaby 	writel(UART01x_CR_UARTEN, port->membase + UART010_CR);
558ab4382d2SGreg Kroah-Hartman 
559f166d19fSJiri Slaby 	uart_console_write(port, s, count, pl010_console_putchar);
560ab4382d2SGreg Kroah-Hartman 
561ab4382d2SGreg Kroah-Hartman 	/*
562ab4382d2SGreg Kroah-Hartman 	 *	Finally, wait for transmitter to become empty
563ab4382d2SGreg Kroah-Hartman 	 *	and restore the TCR
564ab4382d2SGreg Kroah-Hartman 	 */
565ab4382d2SGreg Kroah-Hartman 	do {
566f166d19fSJiri Slaby 		status = readb(port->membase + UART01x_FR);
567ab4382d2SGreg Kroah-Hartman 		barrier();
568ab4382d2SGreg Kroah-Hartman 	} while (status & UART01x_FR_BUSY);
569f166d19fSJiri Slaby 	writel(old_cr, port->membase + UART010_CR);
570ab4382d2SGreg Kroah-Hartman 
571ab4382d2SGreg Kroah-Hartman 	clk_disable(uap->clk);
572ab4382d2SGreg Kroah-Hartman }
573ab4382d2SGreg Kroah-Hartman 
574ab4382d2SGreg Kroah-Hartman static void __init
pl010_console_get_options(struct uart_amba_port * uap,int * baud,int * parity,int * bits)575ab4382d2SGreg Kroah-Hartman pl010_console_get_options(struct uart_amba_port *uap, int *baud,
576ab4382d2SGreg Kroah-Hartman 			     int *parity, int *bits)
577ab4382d2SGreg Kroah-Hartman {
578ab4382d2SGreg Kroah-Hartman 	if (readb(uap->port.membase + UART010_CR) & UART01x_CR_UARTEN) {
579ab4382d2SGreg Kroah-Hartman 		unsigned int lcr_h, quot;
580ab4382d2SGreg Kroah-Hartman 		lcr_h = readb(uap->port.membase + UART010_LCRH);
581ab4382d2SGreg Kroah-Hartman 
582ab4382d2SGreg Kroah-Hartman 		*parity = 'n';
583ab4382d2SGreg Kroah-Hartman 		if (lcr_h & UART01x_LCRH_PEN) {
584ab4382d2SGreg Kroah-Hartman 			if (lcr_h & UART01x_LCRH_EPS)
585ab4382d2SGreg Kroah-Hartman 				*parity = 'e';
586ab4382d2SGreg Kroah-Hartman 			else
587ab4382d2SGreg Kroah-Hartman 				*parity = 'o';
588ab4382d2SGreg Kroah-Hartman 		}
589ab4382d2SGreg Kroah-Hartman 
590ab4382d2SGreg Kroah-Hartman 		if ((lcr_h & 0x60) == UART01x_LCRH_WLEN_7)
591ab4382d2SGreg Kroah-Hartman 			*bits = 7;
592ab4382d2SGreg Kroah-Hartman 		else
593ab4382d2SGreg Kroah-Hartman 			*bits = 8;
594ab4382d2SGreg Kroah-Hartman 
595ab4382d2SGreg Kroah-Hartman 		quot = readb(uap->port.membase + UART010_LCRL) |
596ab4382d2SGreg Kroah-Hartman 		       readb(uap->port.membase + UART010_LCRM) << 8;
597ab4382d2SGreg Kroah-Hartman 		*baud = uap->port.uartclk / (16 * (quot + 1));
598ab4382d2SGreg Kroah-Hartman 	}
599ab4382d2SGreg Kroah-Hartman }
600ab4382d2SGreg Kroah-Hartman 
pl010_console_setup(struct console * co,char * options)601ab4382d2SGreg Kroah-Hartman static int __init pl010_console_setup(struct console *co, char *options)
602ab4382d2SGreg Kroah-Hartman {
603ab4382d2SGreg Kroah-Hartman 	struct uart_amba_port *uap;
604ab4382d2SGreg Kroah-Hartman 	int baud = 38400;
605ab4382d2SGreg Kroah-Hartman 	int bits = 8;
606ab4382d2SGreg Kroah-Hartman 	int parity = 'n';
607ab4382d2SGreg Kroah-Hartman 	int flow = 'n';
60836b8f1e2SRussell King 	int ret;
609ab4382d2SGreg Kroah-Hartman 
610ab4382d2SGreg Kroah-Hartman 	/*
611ab4382d2SGreg Kroah-Hartman 	 * Check whether an invalid uart number has been specified, and
612ab4382d2SGreg Kroah-Hartman 	 * if so, search for the first available port that does have
613ab4382d2SGreg Kroah-Hartman 	 * console support.
614ab4382d2SGreg Kroah-Hartman 	 */
615ab4382d2SGreg Kroah-Hartman 	if (co->index >= UART_NR)
616ab4382d2SGreg Kroah-Hartman 		co->index = 0;
617ab4382d2SGreg Kroah-Hartman 	uap = amba_ports[co->index];
618ab4382d2SGreg Kroah-Hartman 	if (!uap)
619ab4382d2SGreg Kroah-Hartman 		return -ENODEV;
620ab4382d2SGreg Kroah-Hartman 
62136b8f1e2SRussell King 	ret = clk_prepare(uap->clk);
62236b8f1e2SRussell King 	if (ret)
62336b8f1e2SRussell King 		return ret;
62436b8f1e2SRussell King 
625ab4382d2SGreg Kroah-Hartman 	uap->port.uartclk = clk_get_rate(uap->clk);
626ab4382d2SGreg Kroah-Hartman 
627ab4382d2SGreg Kroah-Hartman 	if (options)
628ab4382d2SGreg Kroah-Hartman 		uart_parse_options(options, &baud, &parity, &bits, &flow);
629ab4382d2SGreg Kroah-Hartman 	else
630ab4382d2SGreg Kroah-Hartman 		pl010_console_get_options(uap, &baud, &parity, &bits);
631ab4382d2SGreg Kroah-Hartman 
632ab4382d2SGreg Kroah-Hartman 	return uart_set_options(&uap->port, co, baud, parity, bits, flow);
633ab4382d2SGreg Kroah-Hartman }
634ab4382d2SGreg Kroah-Hartman 
635ab4382d2SGreg Kroah-Hartman static struct uart_driver amba_reg;
636ab4382d2SGreg Kroah-Hartman static struct console amba_console = {
637ab4382d2SGreg Kroah-Hartman 	.name		= "ttyAM",
638ab4382d2SGreg Kroah-Hartman 	.write		= pl010_console_write,
639ab4382d2SGreg Kroah-Hartman 	.device		= uart_console_device,
640ab4382d2SGreg Kroah-Hartman 	.setup		= pl010_console_setup,
641ab4382d2SGreg Kroah-Hartman 	.flags		= CON_PRINTBUFFER,
642ab4382d2SGreg Kroah-Hartman 	.index		= -1,
643ab4382d2SGreg Kroah-Hartman 	.data		= &amba_reg,
644ab4382d2SGreg Kroah-Hartman };
645ab4382d2SGreg Kroah-Hartman 
646ab4382d2SGreg Kroah-Hartman #define AMBA_CONSOLE	&amba_console
647ab4382d2SGreg Kroah-Hartman #else
648ab4382d2SGreg Kroah-Hartman #define AMBA_CONSOLE	NULL
649ab4382d2SGreg Kroah-Hartman #endif
650ab4382d2SGreg Kroah-Hartman 
651bd8766b1SSjoerd Simons static DEFINE_MUTEX(amba_reg_lock);
652ab4382d2SGreg Kroah-Hartman static struct uart_driver amba_reg = {
653ab4382d2SGreg Kroah-Hartman 	.owner			= THIS_MODULE,
654ab4382d2SGreg Kroah-Hartman 	.driver_name		= "ttyAM",
655ab4382d2SGreg Kroah-Hartman 	.dev_name		= "ttyAM",
656ab4382d2SGreg Kroah-Hartman 	.major			= SERIAL_AMBA_MAJOR,
657ab4382d2SGreg Kroah-Hartman 	.minor			= SERIAL_AMBA_MINOR,
658ab4382d2SGreg Kroah-Hartman 	.nr			= UART_NR,
659ab4382d2SGreg Kroah-Hartman 	.cons			= AMBA_CONSOLE,
660ab4382d2SGreg Kroah-Hartman };
661ab4382d2SGreg Kroah-Hartman 
pl010_probe(struct amba_device * dev,const struct amba_id * id)662aa25afadSRussell King static int pl010_probe(struct amba_device *dev, const struct amba_id *id)
663ab4382d2SGreg Kroah-Hartman {
664ab4382d2SGreg Kroah-Hartman 	struct uart_amba_port *uap;
665ab4382d2SGreg Kroah-Hartman 	void __iomem *base;
666ab4382d2SGreg Kroah-Hartman 	int i, ret;
667ab4382d2SGreg Kroah-Hartman 
668ab4382d2SGreg Kroah-Hartman 	for (i = 0; i < ARRAY_SIZE(amba_ports); i++)
669ab4382d2SGreg Kroah-Hartman 		if (amba_ports[i] == NULL)
670ab4382d2SGreg Kroah-Hartman 			break;
671ab4382d2SGreg Kroah-Hartman 
67244acd260STushar Behera 	if (i == ARRAY_SIZE(amba_ports))
67344acd260STushar Behera 		return -EBUSY;
674ab4382d2SGreg Kroah-Hartman 
67544acd260STushar Behera 	uap = devm_kzalloc(&dev->dev, sizeof(struct uart_amba_port),
67644acd260STushar Behera 			   GFP_KERNEL);
67744acd260STushar Behera 	if (!uap)
67844acd260STushar Behera 		return -ENOMEM;
679ab4382d2SGreg Kroah-Hartman 
68044acd260STushar Behera 	base = devm_ioremap(&dev->dev, dev->res.start,
68144acd260STushar Behera 			    resource_size(&dev->res));
68244acd260STushar Behera 	if (!base)
68344acd260STushar Behera 		return -ENOMEM;
684ab4382d2SGreg Kroah-Hartman 
68544acd260STushar Behera 	uap->clk = devm_clk_get(&dev->dev, NULL);
68644acd260STushar Behera 	if (IS_ERR(uap->clk))
68744acd260STushar Behera 		return PTR_ERR(uap->clk);
688ab4382d2SGreg Kroah-Hartman 
689ab4382d2SGreg Kroah-Hartman 	uap->port.dev = &dev->dev;
690ab4382d2SGreg Kroah-Hartman 	uap->port.mapbase = dev->res.start;
691ab4382d2SGreg Kroah-Hartman 	uap->port.membase = base;
692ab4382d2SGreg Kroah-Hartman 	uap->port.iotype = UPIO_MEM;
693ab4382d2SGreg Kroah-Hartman 	uap->port.irq = dev->irq[0];
694ab4382d2SGreg Kroah-Hartman 	uap->port.fifosize = 16;
6955f99fca9SDmitry Safonov 	uap->port.has_sysrq = IS_ENABLED(CONFIG_SERIAL_AMBA_PL010_CONSOLE);
696ab4382d2SGreg Kroah-Hartman 	uap->port.ops = &amba_pl010_pops;
697ab4382d2SGreg Kroah-Hartman 	uap->port.flags = UPF_BOOT_AUTOCONF;
698ab4382d2SGreg Kroah-Hartman 	uap->port.line = i;
699ab4382d2SGreg Kroah-Hartman 	uap->dev = dev;
700574de559SJingoo Han 	uap->data = dev_get_platdata(&dev->dev);
701ab4382d2SGreg Kroah-Hartman 
702ab4382d2SGreg Kroah-Hartman 	amba_ports[i] = uap;
703ab4382d2SGreg Kroah-Hartman 
704ab4382d2SGreg Kroah-Hartman 	amba_set_drvdata(dev, uap);
705bd8766b1SSjoerd Simons 
706bd8766b1SSjoerd Simons 	mutex_lock(&amba_reg_lock);
707bd8766b1SSjoerd Simons 	if (!amba_reg.state) {
708bd8766b1SSjoerd Simons 		ret = uart_register_driver(&amba_reg);
709bd8766b1SSjoerd Simons 		if (ret < 0) {
710bd8766b1SSjoerd Simons 			mutex_unlock(&amba_reg_lock);
711bd8766b1SSjoerd Simons 			dev_err(uap->port.dev,
712bd8766b1SSjoerd Simons 				"Failed to register AMBA-PL010 driver\n");
713bd8766b1SSjoerd Simons 			return ret;
714bd8766b1SSjoerd Simons 		}
715bd8766b1SSjoerd Simons 	}
716bd8766b1SSjoerd Simons 	mutex_unlock(&amba_reg_lock);
717bd8766b1SSjoerd Simons 
718ab4382d2SGreg Kroah-Hartman 	ret = uart_add_one_port(&amba_reg, &uap->port);
71944acd260STushar Behera 	if (ret)
720ab4382d2SGreg Kroah-Hartman 		amba_ports[i] = NULL;
72144acd260STushar Behera 
722ab4382d2SGreg Kroah-Hartman 	return ret;
723ab4382d2SGreg Kroah-Hartman }
724ab4382d2SGreg Kroah-Hartman 
pl010_remove(struct amba_device * dev)7253fd269e7SUwe Kleine-König static void pl010_remove(struct amba_device *dev)
726ab4382d2SGreg Kroah-Hartman {
727ab4382d2SGreg Kroah-Hartman 	struct uart_amba_port *uap = amba_get_drvdata(dev);
728ab4382d2SGreg Kroah-Hartman 	int i;
729bd8766b1SSjoerd Simons 	bool busy = false;
730ab4382d2SGreg Kroah-Hartman 
731ab4382d2SGreg Kroah-Hartman 	uart_remove_one_port(&amba_reg, &uap->port);
732ab4382d2SGreg Kroah-Hartman 
733ab4382d2SGreg Kroah-Hartman 	for (i = 0; i < ARRAY_SIZE(amba_ports); i++)
734ab4382d2SGreg Kroah-Hartman 		if (amba_ports[i] == uap)
735ab4382d2SGreg Kroah-Hartman 			amba_ports[i] = NULL;
736bd8766b1SSjoerd Simons 		else if (amba_ports[i])
737bd8766b1SSjoerd Simons 			busy = true;
738bd8766b1SSjoerd Simons 
739bd8766b1SSjoerd Simons 	if (!busy)
740bd8766b1SSjoerd Simons 		uart_unregister_driver(&amba_reg);
741ab4382d2SGreg Kroah-Hartman }
742ab4382d2SGreg Kroah-Hartman 
74395468240SUlf Hansson #ifdef CONFIG_PM_SLEEP
pl010_suspend(struct device * dev)74495468240SUlf Hansson static int pl010_suspend(struct device *dev)
745ab4382d2SGreg Kroah-Hartman {
74695468240SUlf Hansson 	struct uart_amba_port *uap = dev_get_drvdata(dev);
747ab4382d2SGreg Kroah-Hartman 
748ab4382d2SGreg Kroah-Hartman 	if (uap)
749ab4382d2SGreg Kroah-Hartman 		uart_suspend_port(&amba_reg, &uap->port);
750ab4382d2SGreg Kroah-Hartman 
751ab4382d2SGreg Kroah-Hartman 	return 0;
752ab4382d2SGreg Kroah-Hartman }
753ab4382d2SGreg Kroah-Hartman 
pl010_resume(struct device * dev)75495468240SUlf Hansson static int pl010_resume(struct device *dev)
755ab4382d2SGreg Kroah-Hartman {
75695468240SUlf Hansson 	struct uart_amba_port *uap = dev_get_drvdata(dev);
757ab4382d2SGreg Kroah-Hartman 
758ab4382d2SGreg Kroah-Hartman 	if (uap)
759ab4382d2SGreg Kroah-Hartman 		uart_resume_port(&amba_reg, &uap->port);
760ab4382d2SGreg Kroah-Hartman 
761ab4382d2SGreg Kroah-Hartman 	return 0;
762ab4382d2SGreg Kroah-Hartman }
76395468240SUlf Hansson #endif
76495468240SUlf Hansson 
76595468240SUlf Hansson static SIMPLE_DEV_PM_OPS(pl010_dev_pm_ops, pl010_suspend, pl010_resume);
766ab4382d2SGreg Kroah-Hartman 
7675337e549SArvind Yadav static const struct amba_id pl010_ids[] = {
768ab4382d2SGreg Kroah-Hartman 	{
769ab4382d2SGreg Kroah-Hartman 		.id	= 0x00041010,
770ab4382d2SGreg Kroah-Hartman 		.mask	= 0x000fffff,
771ab4382d2SGreg Kroah-Hartman 	},
772ab4382d2SGreg Kroah-Hartman 	{ 0, 0 },
773ab4382d2SGreg Kroah-Hartman };
774ab4382d2SGreg Kroah-Hartman 
775a664a119SDave Martin MODULE_DEVICE_TABLE(amba, pl010_ids);
776a664a119SDave Martin 
777ab4382d2SGreg Kroah-Hartman static struct amba_driver pl010_driver = {
778ab4382d2SGreg Kroah-Hartman 	.drv = {
779ab4382d2SGreg Kroah-Hartman 		.name	= "uart-pl010",
78095468240SUlf Hansson 		.pm	= &pl010_dev_pm_ops,
781ab4382d2SGreg Kroah-Hartman 	},
782ab4382d2SGreg Kroah-Hartman 	.id_table	= pl010_ids,
783ab4382d2SGreg Kroah-Hartman 	.probe		= pl010_probe,
784ab4382d2SGreg Kroah-Hartman 	.remove		= pl010_remove,
785ab4382d2SGreg Kroah-Hartman };
786ab4382d2SGreg Kroah-Hartman 
pl010_init(void)787ab4382d2SGreg Kroah-Hartman static int __init pl010_init(void)
788ab4382d2SGreg Kroah-Hartman {
789ab4382d2SGreg Kroah-Hartman 	printk(KERN_INFO "Serial: AMBA driver\n");
790ab4382d2SGreg Kroah-Hartman 
791bd8766b1SSjoerd Simons 	return  amba_driver_register(&pl010_driver);
792ab4382d2SGreg Kroah-Hartman }
793ab4382d2SGreg Kroah-Hartman 
pl010_exit(void)794ab4382d2SGreg Kroah-Hartman static void __exit pl010_exit(void)
795ab4382d2SGreg Kroah-Hartman {
796ab4382d2SGreg Kroah-Hartman 	amba_driver_unregister(&pl010_driver);
797ab4382d2SGreg Kroah-Hartman }
798ab4382d2SGreg Kroah-Hartman 
799ab4382d2SGreg Kroah-Hartman module_init(pl010_init);
800ab4382d2SGreg Kroah-Hartman module_exit(pl010_exit);
801ab4382d2SGreg Kroah-Hartman 
802ab4382d2SGreg Kroah-Hartman MODULE_AUTHOR("ARM Ltd/Deep Blue Solutions Ltd");
803ab4382d2SGreg Kroah-Hartman MODULE_DESCRIPTION("ARM AMBA serial port driver");
804ab4382d2SGreg Kroah-Hartman MODULE_LICENSE("GPL");
805