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