blob: e233f464d55a792c67009c40cb65ee24b359e0ca [file] [log] [blame]
Wilson Ding30530792016-02-16 19:14:53 +01001/*
2* ***************************************************************************
Paul Gortmaker89ebc272016-03-13 19:48:52 -04003* Marvell Armada-3700 Serial Driver
4* Author: Wilson Ding <dingwei@marvell.com>
Wilson Ding30530792016-02-16 19:14:53 +01005* Copyright (C) 2015 Marvell International Ltd.
6* ***************************************************************************
7* This program is free software: you can redistribute it and/or modify it
8* under the terms of the GNU General Public License as published by the Free
9* Software Foundation, either version 2 of the License, or any later version.
10*
11* This program is distributed in the hope that it will be useful,
12* but WITHOUT ANY WARRANTY; without even the implied warranty of
13* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14* GNU General Public License for more details.
15*
16* You should have received a copy of the GNU General Public License
17* along with this program. If not, see <http://www.gnu.org/licenses/>.
18* ***************************************************************************
19*/
20
21#include <linux/clk.h>
22#include <linux/console.h>
23#include <linux/delay.h>
24#include <linux/device.h>
25#include <linux/init.h>
26#include <linux/io.h>
27#include <linux/iopoll.h>
Wilson Ding30530792016-02-16 19:14:53 +010028#include <linux/of.h>
29#include <linux/of_address.h>
30#include <linux/of_device.h>
31#include <linux/of_irq.h>
32#include <linux/of_platform.h>
33#include <linux/platform_device.h>
34#include <linux/serial.h>
35#include <linux/serial_core.h>
36#include <linux/slab.h>
37#include <linux/tty.h>
38#include <linux/tty_flip.h>
39
40/* Register Map */
Miquel Raynal5218d762017-10-13 11:01:49 +020041#define UART_STD_RBR 0x00
Wilson Ding30530792016-02-16 19:14:53 +010042
Miquel Raynal5218d762017-10-13 11:01:49 +020043#define UART_STD_TSH 0x04
Wilson Ding30530792016-02-16 19:14:53 +010044
Miquel Raynal5218d762017-10-13 11:01:49 +020045#define UART_STD_CTRL1 0x08
Wilson Ding30530792016-02-16 19:14:53 +010046#define CTRL_SOFT_RST BIT(31)
47#define CTRL_TXFIFO_RST BIT(15)
48#define CTRL_RXFIFO_RST BIT(14)
Wilson Ding30530792016-02-16 19:14:53 +010049#define CTRL_SND_BRK_SEQ BIT(11)
Wilson Ding30530792016-02-16 19:14:53 +010050#define CTRL_BRK_DET_INT BIT(3)
51#define CTRL_FRM_ERR_INT BIT(2)
52#define CTRL_PAR_ERR_INT BIT(1)
53#define CTRL_OVR_ERR_INT BIT(0)
Miquel Raynal5218d762017-10-13 11:01:49 +020054#define CTRL_BRK_INT (CTRL_BRK_DET_INT | CTRL_FRM_ERR_INT | \
55 CTRL_PAR_ERR_INT | CTRL_OVR_ERR_INT)
Wilson Ding30530792016-02-16 19:14:53 +010056
Miquel Raynal5218d762017-10-13 11:01:49 +020057#define UART_STD_CTRL2 UART_STD_CTRL1
58#define CTRL_STD_TX_RDY_INT BIT(5)
59#define CTRL_STD_RX_RDY_INT BIT(4)
60
61#define UART_STAT 0x0C
Wilson Ding30530792016-02-16 19:14:53 +010062#define STAT_TX_FIFO_EMP BIT(13)
Wilson Ding30530792016-02-16 19:14:53 +010063#define STAT_TX_FIFO_FUL BIT(11)
Wilson Ding30530792016-02-16 19:14:53 +010064#define STAT_TX_EMP BIT(6)
Miquel Raynal5218d762017-10-13 11:01:49 +020065#define STAT_STD_TX_RDY BIT(5)
66#define STAT_STD_RX_RDY BIT(4)
Wilson Ding30530792016-02-16 19:14:53 +010067#define STAT_BRK_DET BIT(3)
68#define STAT_FRM_ERR BIT(2)
69#define STAT_PAR_ERR BIT(1)
70#define STAT_OVR_ERR BIT(0)
71#define STAT_BRK_ERR (STAT_BRK_DET | STAT_FRM_ERR | STAT_FRM_ERR\
72 | STAT_PAR_ERR | STAT_OVR_ERR)
73
74#define UART_BRDV 0x10
75
76#define MVEBU_NR_UARTS 1
77
78#define MVEBU_UART_TYPE "mvebu-uart"
Yehuda Yitschak02c33332017-10-13 11:01:47 +020079#define DRIVER_NAME "mvebu_serial"
Wilson Ding30530792016-02-16 19:14:53 +010080
Miquel Raynal5218d762017-10-13 11:01:49 +020081/* Register offsets, different depending on the UART */
82struct uart_regs_layout {
83 unsigned int rbr;
84 unsigned int tsh;
85 unsigned int ctrl;
86 unsigned int intr;
Wilson Ding30530792016-02-16 19:14:53 +010087};
88
Miquel Raynal5218d762017-10-13 11:01:49 +020089/* Diverging flags */
90struct uart_flags {
91 unsigned int ctrl_tx_rdy_int;
92 unsigned int ctrl_rx_rdy_int;
93 unsigned int stat_tx_rdy;
94 unsigned int stat_rx_rdy;
95};
96
97/* Driver data, a structure for each UART port */
98struct mvebu_uart_driver_data {
99 bool is_ext;
100 struct uart_regs_layout regs;
101 struct uart_flags flags;
102};
103
104/* MVEBU UART driver structure */
105struct mvebu_uart {
106 struct uart_port *port;
107 struct clk *clk;
108 struct mvebu_uart_driver_data *data;
109};
110
111static struct mvebu_uart *to_mvuart(struct uart_port *port)
112{
113 return (struct mvebu_uart *)port->private_data;
114}
115
116#define IS_EXTENDED(port) (to_mvuart(port)->data->is_ext)
117
118#define UART_RBR(port) (to_mvuart(port)->data->regs.rbr)
119#define UART_TSH(port) (to_mvuart(port)->data->regs.tsh)
120#define UART_CTRL(port) (to_mvuart(port)->data->regs.ctrl)
121#define UART_INTR(port) (to_mvuart(port)->data->regs.intr)
122
123#define CTRL_TX_RDY_INT(port) (to_mvuart(port)->data->flags.ctrl_tx_rdy_int)
124#define CTRL_RX_RDY_INT(port) (to_mvuart(port)->data->flags.ctrl_rx_rdy_int)
125#define STAT_TX_RDY(port) (to_mvuart(port)->data->flags.stat_tx_rdy)
126#define STAT_RX_RDY(port) (to_mvuart(port)->data->flags.stat_rx_rdy)
127
128static struct uart_port mvebu_uart_ports[MVEBU_NR_UARTS];
129
Wilson Ding30530792016-02-16 19:14:53 +0100130/* Core UART Driver Operations */
131static unsigned int mvebu_uart_tx_empty(struct uart_port *port)
132{
133 unsigned long flags;
134 unsigned int st;
135
136 spin_lock_irqsave(&port->lock, flags);
137 st = readl(port->membase + UART_STAT);
138 spin_unlock_irqrestore(&port->lock, flags);
139
140 return (st & STAT_TX_FIFO_EMP) ? TIOCSER_TEMT : 0;
141}
142
143static unsigned int mvebu_uart_get_mctrl(struct uart_port *port)
144{
145 return TIOCM_CTS | TIOCM_DSR | TIOCM_CAR;
146}
147
148static void mvebu_uart_set_mctrl(struct uart_port *port,
149 unsigned int mctrl)
150{
151/*
152 * Even if we do not support configuring the modem control lines, this
153 * function must be proided to the serial core
154 */
155}
156
157static void mvebu_uart_stop_tx(struct uart_port *port)
158{
Miquel Raynal5218d762017-10-13 11:01:49 +0200159 unsigned int ctl = readl(port->membase + UART_INTR(port));
Wilson Ding30530792016-02-16 19:14:53 +0100160
Miquel Raynal5218d762017-10-13 11:01:49 +0200161 ctl &= ~CTRL_TX_RDY_INT(port);
162 writel(ctl, port->membase + UART_INTR(port));
Wilson Ding30530792016-02-16 19:14:53 +0100163}
164
165static void mvebu_uart_start_tx(struct uart_port *port)
166{
Miquel Raynal5218d762017-10-13 11:01:49 +0200167 unsigned int ctl = readl(port->membase + UART_INTR(port));
Wilson Ding30530792016-02-16 19:14:53 +0100168
Miquel Raynal5218d762017-10-13 11:01:49 +0200169 ctl |= CTRL_TX_RDY_INT(port);
170 writel(ctl, port->membase + UART_INTR(port));
Wilson Ding30530792016-02-16 19:14:53 +0100171}
172
173static void mvebu_uart_stop_rx(struct uart_port *port)
174{
Miquel Raynal5218d762017-10-13 11:01:49 +0200175 unsigned int ctl;
Wilson Ding30530792016-02-16 19:14:53 +0100176
Miquel Raynal5218d762017-10-13 11:01:49 +0200177 ctl = readl(port->membase + UART_CTRL(port));
178 ctl &= ~CTRL_BRK_INT;
179 writel(ctl, port->membase + UART_CTRL(port));
180
181 ctl = readl(port->membase + UART_INTR(port));
182 ctl &= ~CTRL_RX_RDY_INT(port);
183 writel(ctl, port->membase + UART_INTR(port));
Wilson Ding30530792016-02-16 19:14:53 +0100184}
185
186static void mvebu_uart_break_ctl(struct uart_port *port, int brk)
187{
188 unsigned int ctl;
189 unsigned long flags;
190
191 spin_lock_irqsave(&port->lock, flags);
Miquel Raynal5218d762017-10-13 11:01:49 +0200192 ctl = readl(port->membase + UART_CTRL(port));
Wilson Ding30530792016-02-16 19:14:53 +0100193 if (brk == -1)
194 ctl |= CTRL_SND_BRK_SEQ;
195 else
196 ctl &= ~CTRL_SND_BRK_SEQ;
Miquel Raynal5218d762017-10-13 11:01:49 +0200197 writel(ctl, port->membase + UART_CTRL(port));
Wilson Ding30530792016-02-16 19:14:53 +0100198 spin_unlock_irqrestore(&port->lock, flags);
199}
200
201static void mvebu_uart_rx_chars(struct uart_port *port, unsigned int status)
202{
203 struct tty_port *tport = &port->state->port;
204 unsigned char ch = 0;
205 char flag = 0;
206
207 do {
Miquel Raynal5218d762017-10-13 11:01:49 +0200208 if (status & STAT_RX_RDY(port)) {
209 ch = readl(port->membase + UART_RBR(port));
Wilson Ding30530792016-02-16 19:14:53 +0100210 ch &= 0xff;
211 flag = TTY_NORMAL;
212 port->icount.rx++;
213
214 if (status & STAT_PAR_ERR)
215 port->icount.parity++;
216 }
217
218 if (status & STAT_BRK_DET) {
219 port->icount.brk++;
220 status &= ~(STAT_FRM_ERR | STAT_PAR_ERR);
221 if (uart_handle_break(port))
222 goto ignore_char;
223 }
224
225 if (status & STAT_OVR_ERR)
226 port->icount.overrun++;
227
228 if (status & STAT_FRM_ERR)
229 port->icount.frame++;
230
231 if (uart_handle_sysrq_char(port, ch))
232 goto ignore_char;
233
234 if (status & port->ignore_status_mask & STAT_PAR_ERR)
Miquel Raynal5218d762017-10-13 11:01:49 +0200235 status &= ~STAT_RX_RDY(port);
Wilson Ding30530792016-02-16 19:14:53 +0100236
237 status &= port->read_status_mask;
238
239 if (status & STAT_PAR_ERR)
240 flag = TTY_PARITY;
241
242 status &= ~port->ignore_status_mask;
243
Miquel Raynal5218d762017-10-13 11:01:49 +0200244 if (status & STAT_RX_RDY(port))
Wilson Ding30530792016-02-16 19:14:53 +0100245 tty_insert_flip_char(tport, ch, flag);
246
247 if (status & STAT_BRK_DET)
248 tty_insert_flip_char(tport, 0, TTY_BREAK);
249
250 if (status & STAT_FRM_ERR)
251 tty_insert_flip_char(tport, 0, TTY_FRAME);
252
253 if (status & STAT_OVR_ERR)
254 tty_insert_flip_char(tport, 0, TTY_OVERRUN);
255
256ignore_char:
257 status = readl(port->membase + UART_STAT);
Miquel Raynal5218d762017-10-13 11:01:49 +0200258 } while (status & (STAT_RX_RDY(port) | STAT_BRK_DET));
Wilson Ding30530792016-02-16 19:14:53 +0100259
260 tty_flip_buffer_push(tport);
261}
262
263static void mvebu_uart_tx_chars(struct uart_port *port, unsigned int status)
264{
265 struct circ_buf *xmit = &port->state->xmit;
266 unsigned int count;
267 unsigned int st;
268
269 if (port->x_char) {
Miquel Raynal5218d762017-10-13 11:01:49 +0200270 writel(port->x_char, port->membase + UART_TSH(port));
Wilson Ding30530792016-02-16 19:14:53 +0100271 port->icount.tx++;
272 port->x_char = 0;
273 return;
274 }
275
276 if (uart_circ_empty(xmit) || uart_tx_stopped(port)) {
277 mvebu_uart_stop_tx(port);
278 return;
279 }
280
281 for (count = 0; count < port->fifosize; count++) {
Miquel Raynal5218d762017-10-13 11:01:49 +0200282 writel(xmit->buf[xmit->tail], port->membase + UART_TSH(port));
Wilson Ding30530792016-02-16 19:14:53 +0100283 xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
284 port->icount.tx++;
285
286 if (uart_circ_empty(xmit))
287 break;
288
289 st = readl(port->membase + UART_STAT);
290 if (st & STAT_TX_FIFO_FUL)
291 break;
292 }
293
294 if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
295 uart_write_wakeup(port);
296
297 if (uart_circ_empty(xmit))
298 mvebu_uart_stop_tx(port);
299}
300
301static irqreturn_t mvebu_uart_isr(int irq, void *dev_id)
302{
303 struct uart_port *port = (struct uart_port *)dev_id;
304 unsigned int st = readl(port->membase + UART_STAT);
305
Miquel Raynal5218d762017-10-13 11:01:49 +0200306 if (st & (STAT_RX_RDY(port) | STAT_OVR_ERR | STAT_FRM_ERR |
307 STAT_BRK_DET))
Wilson Ding30530792016-02-16 19:14:53 +0100308 mvebu_uart_rx_chars(port, st);
309
Miquel Raynal5218d762017-10-13 11:01:49 +0200310 if (st & STAT_TX_RDY(port))
Wilson Ding30530792016-02-16 19:14:53 +0100311 mvebu_uart_tx_chars(port, st);
312
313 return IRQ_HANDLED;
314}
315
316static int mvebu_uart_startup(struct uart_port *port)
317{
Miquel Raynal5218d762017-10-13 11:01:49 +0200318 unsigned int ctl;
Wilson Ding30530792016-02-16 19:14:53 +0100319 int ret;
320
321 writel(CTRL_TXFIFO_RST | CTRL_RXFIFO_RST,
Miquel Raynal5218d762017-10-13 11:01:49 +0200322 port->membase + UART_CTRL(port));
Wilson Ding30530792016-02-16 19:14:53 +0100323 udelay(1);
Miquel Raynal5218d762017-10-13 11:01:49 +0200324 writel(CTRL_BRK_INT, port->membase + UART_CTRL(port));
325
326 ctl = readl(port->membase + UART_INTR(port));
327 ctl |= CTRL_RX_RDY_INT(port);
328 writel(ctl, port->membase + UART_INTR(port));
Wilson Ding30530792016-02-16 19:14:53 +0100329
Yehuda Yitschak02c33332017-10-13 11:01:47 +0200330 ret = request_irq(port->irq, mvebu_uart_isr, port->irqflags,
331 DRIVER_NAME, port);
Wilson Ding30530792016-02-16 19:14:53 +0100332 if (ret) {
333 dev_err(port->dev, "failed to request irq\n");
334 return ret;
335 }
336
337 return 0;
338}
339
340static void mvebu_uart_shutdown(struct uart_port *port)
341{
Miquel Raynal5218d762017-10-13 11:01:49 +0200342 writel(0, port->membase + UART_INTR(port));
Thomas Petazzonic2c16592016-06-16 16:48:52 +0200343
344 free_irq(port->irq, port);
Wilson Ding30530792016-02-16 19:14:53 +0100345}
346
347static void mvebu_uart_set_termios(struct uart_port *port,
348 struct ktermios *termios,
349 struct ktermios *old)
350{
351 unsigned long flags;
352 unsigned int baud;
353
354 spin_lock_irqsave(&port->lock, flags);
355
Miquel Raynal5218d762017-10-13 11:01:49 +0200356 port->read_status_mask = STAT_RX_RDY(port) | STAT_OVR_ERR |
357 STAT_TX_RDY(port) | STAT_TX_FIFO_FUL;
Wilson Ding30530792016-02-16 19:14:53 +0100358
359 if (termios->c_iflag & INPCK)
360 port->read_status_mask |= STAT_FRM_ERR | STAT_PAR_ERR;
361
362 port->ignore_status_mask = 0;
363 if (termios->c_iflag & IGNPAR)
364 port->ignore_status_mask |=
365 STAT_FRM_ERR | STAT_PAR_ERR | STAT_OVR_ERR;
366
367 if ((termios->c_cflag & CREAD) == 0)
Miquel Raynal5218d762017-10-13 11:01:49 +0200368 port->ignore_status_mask |= STAT_RX_RDY(port) | STAT_BRK_ERR;
Wilson Ding30530792016-02-16 19:14:53 +0100369
370 if (old)
371 tty_termios_copy_hw(termios, old);
372
373 baud = uart_get_baud_rate(port, termios, old, 0, 460800);
374 uart_update_timeout(port, termios->c_cflag, baud);
375
376 spin_unlock_irqrestore(&port->lock, flags);
377}
378
379static const char *mvebu_uart_type(struct uart_port *port)
380{
381 return MVEBU_UART_TYPE;
382}
383
384static void mvebu_uart_release_port(struct uart_port *port)
385{
386 /* Nothing to do here */
387}
388
389static int mvebu_uart_request_port(struct uart_port *port)
390{
391 return 0;
392}
393
394#ifdef CONFIG_CONSOLE_POLL
395static int mvebu_uart_get_poll_char(struct uart_port *port)
396{
397 unsigned int st = readl(port->membase + UART_STAT);
398
Miquel Raynal5218d762017-10-13 11:01:49 +0200399 if (!(st & STAT_RX_RDY(port)))
Wilson Ding30530792016-02-16 19:14:53 +0100400 return NO_POLL_CHAR;
401
Miquel Raynal5218d762017-10-13 11:01:49 +0200402 return readl(port->membase + UART_RBR(port));
Wilson Ding30530792016-02-16 19:14:53 +0100403}
404
405static void mvebu_uart_put_poll_char(struct uart_port *port, unsigned char c)
406{
407 unsigned int st;
408
409 for (;;) {
410 st = readl(port->membase + UART_STAT);
411
412 if (!(st & STAT_TX_FIFO_FUL))
413 break;
414
415 udelay(1);
416 }
417
Miquel Raynal5218d762017-10-13 11:01:49 +0200418 writel(c, port->membase + UART_TSH(port));
Wilson Ding30530792016-02-16 19:14:53 +0100419}
420#endif
421
422static const struct uart_ops mvebu_uart_ops = {
423 .tx_empty = mvebu_uart_tx_empty,
424 .set_mctrl = mvebu_uart_set_mctrl,
425 .get_mctrl = mvebu_uart_get_mctrl,
426 .stop_tx = mvebu_uart_stop_tx,
427 .start_tx = mvebu_uart_start_tx,
428 .stop_rx = mvebu_uart_stop_rx,
429 .break_ctl = mvebu_uart_break_ctl,
430 .startup = mvebu_uart_startup,
431 .shutdown = mvebu_uart_shutdown,
432 .set_termios = mvebu_uart_set_termios,
433 .type = mvebu_uart_type,
434 .release_port = mvebu_uart_release_port,
435 .request_port = mvebu_uart_request_port,
436#ifdef CONFIG_CONSOLE_POLL
437 .poll_get_char = mvebu_uart_get_poll_char,
438 .poll_put_char = mvebu_uart_put_poll_char,
439#endif
440};
441
442/* Console Driver Operations */
443
444#ifdef CONFIG_SERIAL_MVEBU_CONSOLE
445/* Early Console */
446static void mvebu_uart_putc(struct uart_port *port, int c)
447{
448 unsigned int st;
449
450 for (;;) {
451 st = readl(port->membase + UART_STAT);
452 if (!(st & STAT_TX_FIFO_FUL))
453 break;
454 }
455
Miquel Raynal5218d762017-10-13 11:01:49 +0200456 /* At early stage, DT is not parsed yet, only use UART0 */
457 writel(c, port->membase + UART_STD_TSH);
Wilson Ding30530792016-02-16 19:14:53 +0100458
459 for (;;) {
460 st = readl(port->membase + UART_STAT);
461 if (st & STAT_TX_FIFO_EMP)
462 break;
463 }
464}
465
466static void mvebu_uart_putc_early_write(struct console *con,
467 const char *s,
468 unsigned n)
469{
470 struct earlycon_device *dev = con->data;
471
472 uart_console_write(&dev->port, s, n, mvebu_uart_putc);
473}
474
475static int __init
476mvebu_uart_early_console_setup(struct earlycon_device *device,
477 const char *opt)
478{
479 if (!device->port.membase)
480 return -ENODEV;
481
482 device->con->write = mvebu_uart_putc_early_write;
483
484 return 0;
485}
486
487EARLYCON_DECLARE(ar3700_uart, mvebu_uart_early_console_setup);
488OF_EARLYCON_DECLARE(ar3700_uart, "marvell,armada-3700-uart",
489 mvebu_uart_early_console_setup);
490
491static void wait_for_xmitr(struct uart_port *port)
492{
493 u32 val;
494
495 readl_poll_timeout_atomic(port->membase + UART_STAT, val,
496 (val & STAT_TX_EMP), 1, 10000);
497}
498
499static void mvebu_uart_console_putchar(struct uart_port *port, int ch)
500{
501 wait_for_xmitr(port);
Miquel Raynal5218d762017-10-13 11:01:49 +0200502 writel(ch, port->membase + UART_TSH(port));
Wilson Ding30530792016-02-16 19:14:53 +0100503}
504
505static void mvebu_uart_console_write(struct console *co, const char *s,
506 unsigned int count)
507{
508 struct uart_port *port = &mvebu_uart_ports[co->index];
509 unsigned long flags;
Miquel Raynal5218d762017-10-13 11:01:49 +0200510 unsigned int ier, intr, ctl;
Wilson Ding30530792016-02-16 19:14:53 +0100511 int locked = 1;
512
513 if (oops_in_progress)
514 locked = spin_trylock_irqsave(&port->lock, flags);
515 else
516 spin_lock_irqsave(&port->lock, flags);
517
Miquel Raynal5218d762017-10-13 11:01:49 +0200518 ier = readl(port->membase + UART_CTRL(port)) & CTRL_BRK_INT;
519 intr = readl(port->membase + UART_INTR(port)) &
520 (CTRL_RX_RDY_INT(port) | CTRL_TX_RDY_INT(port));
521 writel(0, port->membase + UART_CTRL(port));
522 writel(0, port->membase + UART_INTR(port));
Wilson Ding30530792016-02-16 19:14:53 +0100523
524 uart_console_write(port, s, count, mvebu_uart_console_putchar);
525
526 wait_for_xmitr(port);
527
528 if (ier)
Miquel Raynal5218d762017-10-13 11:01:49 +0200529 writel(ier, port->membase + UART_CTRL(port));
530
531 if (intr) {
532 ctl = intr | readl(port->membase + UART_INTR(port));
533 writel(ctl, port->membase + UART_INTR(port));
534 }
Wilson Ding30530792016-02-16 19:14:53 +0100535
536 if (locked)
537 spin_unlock_irqrestore(&port->lock, flags);
538}
539
540static int mvebu_uart_console_setup(struct console *co, char *options)
541{
542 struct uart_port *port;
543 int baud = 9600;
544 int bits = 8;
545 int parity = 'n';
546 int flow = 'n';
547
548 if (co->index < 0 || co->index >= MVEBU_NR_UARTS)
549 return -EINVAL;
550
551 port = &mvebu_uart_ports[co->index];
552
553 if (!port->mapbase || !port->membase) {
554 pr_debug("console on ttyMV%i not present\n", co->index);
555 return -ENODEV;
556 }
557
558 if (options)
559 uart_parse_options(options, &baud, &parity, &bits, &flow);
560
561 return uart_set_options(port, co, baud, parity, bits, flow);
562}
563
564static struct uart_driver mvebu_uart_driver;
565
566static struct console mvebu_uart_console = {
567 .name = "ttyMV",
568 .write = mvebu_uart_console_write,
569 .device = uart_console_device,
570 .setup = mvebu_uart_console_setup,
571 .flags = CON_PRINTBUFFER,
572 .index = -1,
573 .data = &mvebu_uart_driver,
574};
575
576static int __init mvebu_uart_console_init(void)
577{
578 register_console(&mvebu_uart_console);
579 return 0;
580}
581
582console_initcall(mvebu_uart_console_init);
583
584
585#endif /* CONFIG_SERIAL_MVEBU_CONSOLE */
586
587static struct uart_driver mvebu_uart_driver = {
588 .owner = THIS_MODULE,
Yehuda Yitschak02c33332017-10-13 11:01:47 +0200589 .driver_name = DRIVER_NAME,
Wilson Ding30530792016-02-16 19:14:53 +0100590 .dev_name = "ttyMV",
591 .nr = MVEBU_NR_UARTS,
592#ifdef CONFIG_SERIAL_MVEBU_CONSOLE
593 .cons = &mvebu_uart_console,
594#endif
595};
596
Miquel Raynal5218d762017-10-13 11:01:49 +0200597static const struct of_device_id mvebu_uart_of_match[];
598
Allen Yan94228f92017-10-13 11:01:48 +0200599/* Counter to keep track of each UART port id when not using CONFIG_OF */
600static int uart_num_counter;
601
Wilson Ding30530792016-02-16 19:14:53 +0100602static int mvebu_uart_probe(struct platform_device *pdev)
603{
604 struct resource *reg = platform_get_resource(pdev, IORESOURCE_MEM, 0);
605 struct resource *irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
Miquel Raynal5218d762017-10-13 11:01:49 +0200606 const struct of_device_id *match = of_match_device(mvebu_uart_of_match,
607 &pdev->dev);
Wilson Ding30530792016-02-16 19:14:53 +0100608 struct uart_port *port;
Miquel Raynal5218d762017-10-13 11:01:49 +0200609 struct mvebu_uart *mvuart;
Allen Yan94228f92017-10-13 11:01:48 +0200610 int ret, id;
Wilson Ding30530792016-02-16 19:14:53 +0100611
612 if (!reg || !irq) {
613 dev_err(&pdev->dev, "no registers/irq defined\n");
614 return -EINVAL;
615 }
616
Allen Yan94228f92017-10-13 11:01:48 +0200617 /* Assume that all UART ports have a DT alias or none has */
618 id = of_alias_get_id(pdev->dev.of_node, "serial");
619 if (!pdev->dev.of_node || id < 0)
620 pdev->id = uart_num_counter++;
621 else
622 pdev->id = id;
623
624 if (pdev->id >= MVEBU_NR_UARTS) {
625 dev_err(&pdev->dev, "cannot have more than %d UART ports\n",
626 MVEBU_NR_UARTS);
627 return -EINVAL;
628 }
629
630 port = &mvebu_uart_ports[pdev->id];
Wilson Ding30530792016-02-16 19:14:53 +0100631
632 spin_lock_init(&port->lock);
633
634 port->dev = &pdev->dev;
635 port->type = PORT_MVEBU;
636 port->ops = &mvebu_uart_ops;
637 port->regshift = 0;
638
639 port->fifosize = 32;
640 port->iotype = UPIO_MEM32;
641 port->flags = UPF_FIXED_PORT;
Allen Yan94228f92017-10-13 11:01:48 +0200642 port->line = pdev->id;
Wilson Ding30530792016-02-16 19:14:53 +0100643
644 port->irq = irq->start;
645 port->irqflags = 0;
646 port->mapbase = reg->start;
647
648 port->membase = devm_ioremap_resource(&pdev->dev, reg);
649 if (IS_ERR(port->membase))
650 return -PTR_ERR(port->membase);
651
Miquel Raynal5218d762017-10-13 11:01:49 +0200652 mvuart = devm_kzalloc(&pdev->dev, sizeof(struct mvebu_uart),
653 GFP_KERNEL);
654 if (!mvuart)
Wilson Ding30530792016-02-16 19:14:53 +0100655 return -ENOMEM;
656
Miquel Raynal5218d762017-10-13 11:01:49 +0200657 mvuart->data = (struct mvebu_uart_driver_data *)match->data;
658 mvuart->port = port;
Wilson Ding30530792016-02-16 19:14:53 +0100659
Miquel Raynal5218d762017-10-13 11:01:49 +0200660 port->private_data = mvuart;
661 platform_set_drvdata(pdev, mvuart);
Wilson Ding30530792016-02-16 19:14:53 +0100662
Allen Yan9c3d3ee2017-10-13 11:01:50 +0200663 /* UART Soft Reset*/
664 writel(CTRL_SOFT_RST, port->membase + UART_CTRL(port));
665 udelay(1);
666 writel(0, port->membase + UART_CTRL(port));
667
Wilson Ding30530792016-02-16 19:14:53 +0100668 ret = uart_add_one_port(&mvebu_uart_driver, port);
669 if (ret)
670 return ret;
671 return 0;
672}
673
Miquel Raynal5218d762017-10-13 11:01:49 +0200674static struct mvebu_uart_driver_data uart_std_driver_data = {
675 .is_ext = false,
676 .regs.rbr = UART_STD_RBR,
677 .regs.tsh = UART_STD_TSH,
678 .regs.ctrl = UART_STD_CTRL1,
679 .regs.intr = UART_STD_CTRL2,
680 .flags.ctrl_tx_rdy_int = CTRL_STD_TX_RDY_INT,
681 .flags.ctrl_rx_rdy_int = CTRL_STD_RX_RDY_INT,
682 .flags.stat_tx_rdy = STAT_STD_TX_RDY,
683 .flags.stat_rx_rdy = STAT_STD_RX_RDY,
684};
685
Wilson Ding30530792016-02-16 19:14:53 +0100686/* Match table for of_platform binding */
687static const struct of_device_id mvebu_uart_of_match[] = {
Miquel Raynal5218d762017-10-13 11:01:49 +0200688 {
689 .compatible = "marvell,armada-3700-uart",
690 .data = (void *)&uart_std_driver_data,
691 },
Wilson Ding30530792016-02-16 19:14:53 +0100692 {}
693};
Wilson Ding30530792016-02-16 19:14:53 +0100694
695static struct platform_driver mvebu_uart_platform_driver = {
696 .probe = mvebu_uart_probe,
Wilson Ding30530792016-02-16 19:14:53 +0100697 .driver = {
Wilson Ding30530792016-02-16 19:14:53 +0100698 .name = "mvebu-uart",
699 .of_match_table = of_match_ptr(mvebu_uart_of_match),
Paul Gortmaker89ebc272016-03-13 19:48:52 -0400700 .suppress_bind_attrs = true,
Wilson Ding30530792016-02-16 19:14:53 +0100701 },
702};
703
704static int __init mvebu_uart_init(void)
705{
706 int ret;
707
708 ret = uart_register_driver(&mvebu_uart_driver);
709 if (ret)
710 return ret;
711
712 ret = platform_driver_register(&mvebu_uart_platform_driver);
713 if (ret)
714 uart_unregister_driver(&mvebu_uart_driver);
715
716 return ret;
717}
Wilson Ding30530792016-02-16 19:14:53 +0100718arch_initcall(mvebu_uart_init);