blob: 7196a3e586cf8851c1564db027808b55f57296cf [file] [log] [blame]
Jovi Zhang99edb3d2011-03-30 05:30:41 -04001/*
Ben Dooksb4975492008-07-03 12:32:51 +01002 * Driver core for Samsung SoC onboard UARTs.
3 *
Ben Dooksccae9412009-11-13 22:54:14 +00004 * Ben Dooks, Copyright (c) 2003-2008 Simtec Electronics
Ben Dooksb4975492008-07-03 12:32:51 +01005 * http://armlinux.simtec.co.uk/
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License version 2 as
9 * published by the Free Software Foundation.
10*/
11
12/* Hote on 2410 error handling
13 *
14 * The s3c2410 manual has a love/hate affair with the contents of the
15 * UERSTAT register in the UART blocks, and keeps marking some of the
16 * error bits as reserved. Having checked with the s3c2410x01,
17 * it copes with BREAKs properly, so I am happy to ignore the RESERVED
18 * feature from the latter versions of the manual.
19 *
20 * If it becomes aparrent that latter versions of the 2410 remove these
21 * bits, then action will have to be taken to differentiate the versions
22 * and change the policy on BREAK
23 *
24 * BJD, 04-Nov-2004
25*/
26
27#if defined(CONFIG_SERIAL_SAMSUNG_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)
28#define SUPPORT_SYSRQ
29#endif
30
31#include <linux/module.h>
32#include <linux/ioport.h>
33#include <linux/io.h>
34#include <linux/platform_device.h>
35#include <linux/init.h>
36#include <linux/sysrq.h>
37#include <linux/console.h>
38#include <linux/tty.h>
39#include <linux/tty_flip.h>
40#include <linux/serial_core.h>
41#include <linux/serial.h>
Arnd Bergmann9ee51f02013-04-11 02:04:48 +020042#include <linux/serial_s3c.h>
Ben Dooksb4975492008-07-03 12:32:51 +010043#include <linux/delay.h>
44#include <linux/clk.h>
Ben Dooks30555472008-10-21 14:06:36 +010045#include <linux/cpufreq.h>
Thomas Abraham26c919e2011-11-06 22:10:44 +053046#include <linux/of.h>
Ben Dooksb4975492008-07-03 12:32:51 +010047
48#include <asm/irq.h>
49
Ben Dooksb4975492008-07-03 12:32:51 +010050#include "samsung.h"
51
Joe Perchese4ac92d2014-05-20 14:05:50 -070052#if defined(CONFIG_SERIAL_SAMSUNG_DEBUG) && \
53 defined(CONFIG_DEBUG_LL) && \
54 !defined(MODULE)
55
56extern void printascii(const char *);
57
58__printf(1, 2)
59static void dbg(const char *fmt, ...)
60{
61 va_list va;
62 char buff[256];
63
64 va_start(va, fmt);
Sachin Kamata859c8b2014-06-03 11:56:25 +053065 vscnprintf(buff, sizeof(buff), fmt, va);
Joe Perchese4ac92d2014-05-20 14:05:50 -070066 va_end(va);
67
68 printascii(buff);
69}
70
71#else
72#define dbg(fmt, ...) do { if (0) no_printk(fmt, ##__VA_ARGS__); } while (0)
73#endif
74
Ben Dooksb4975492008-07-03 12:32:51 +010075/* UART name and device definitions */
76
77#define S3C24XX_SERIAL_NAME "ttySAC"
78#define S3C24XX_SERIAL_MAJOR 204
79#define S3C24XX_SERIAL_MINOR 64
80
Ben Dooksb4975492008-07-03 12:32:51 +010081/* macros to change one thing to another */
82
83#define tx_enabled(port) ((port)->unused[0])
84#define rx_enabled(port) ((port)->unused[1])
85
Lucas De Marchi25985ed2011-03-30 22:57:33 -030086/* flag to ignore all characters coming in */
Ben Dooksb4975492008-07-03 12:32:51 +010087#define RXSTAT_DUMMY_READ (0x10000000)
88
89static inline struct s3c24xx_uart_port *to_ourport(struct uart_port *port)
90{
91 return container_of(port, struct s3c24xx_uart_port, port);
92}
93
94/* translate a port to the device name */
95
96static inline const char *s3c24xx_serial_portname(struct uart_port *port)
97{
98 return to_platform_device(port->dev)->name;
99}
100
101static int s3c24xx_serial_txempty_nofifo(struct uart_port *port)
102{
Sachin Kamat9303ac12012-09-05 10:30:11 +0530103 return rd_regl(port, S3C2410_UTRSTAT) & S3C2410_UTRSTAT_TXE;
Ben Dooksb4975492008-07-03 12:32:51 +0100104}
105
Thomas Abraham88bb4ea2011-08-10 15:51:19 +0530106/*
107 * s3c64xx and later SoC's include the interrupt mask and status registers in
108 * the controller itself, unlike the s3c24xx SoC's which have these registers
109 * in the interrupt controller. Check if the port type is s3c64xx or higher.
110 */
111static int s3c24xx_serial_has_interrupt_mask(struct uart_port *port)
112{
113 return to_ourport(port)->info->type == PORT_S3C6400;
114}
115
Ben Dooksb4975492008-07-03 12:32:51 +0100116static void s3c24xx_serial_rx_enable(struct uart_port *port)
117{
118 unsigned long flags;
119 unsigned int ucon, ufcon;
120 int count = 10000;
121
122 spin_lock_irqsave(&port->lock, flags);
123
124 while (--count && !s3c24xx_serial_txempty_nofifo(port))
125 udelay(100);
126
127 ufcon = rd_regl(port, S3C2410_UFCON);
128 ufcon |= S3C2410_UFCON_RESETRX;
129 wr_regl(port, S3C2410_UFCON, ufcon);
130
131 ucon = rd_regl(port, S3C2410_UCON);
132 ucon |= S3C2410_UCON_RXIRQMODE;
133 wr_regl(port, S3C2410_UCON, ucon);
134
135 rx_enabled(port) = 1;
136 spin_unlock_irqrestore(&port->lock, flags);
137}
138
139static void s3c24xx_serial_rx_disable(struct uart_port *port)
140{
141 unsigned long flags;
142 unsigned int ucon;
143
144 spin_lock_irqsave(&port->lock, flags);
145
146 ucon = rd_regl(port, S3C2410_UCON);
147 ucon &= ~S3C2410_UCON_RXIRQMODE;
148 wr_regl(port, S3C2410_UCON, ucon);
149
150 rx_enabled(port) = 0;
151 spin_unlock_irqrestore(&port->lock, flags);
152}
153
154static void s3c24xx_serial_stop_tx(struct uart_port *port)
155{
Ben Dooksb73c289c2008-10-21 14:07:04 +0100156 struct s3c24xx_uart_port *ourport = to_ourport(port);
157
Ben Dooksb4975492008-07-03 12:32:51 +0100158 if (tx_enabled(port)) {
Thomas Abraham88bb4ea2011-08-10 15:51:19 +0530159 if (s3c24xx_serial_has_interrupt_mask(port))
160 __set_bit(S3C64XX_UINTM_TXD,
161 portaddrl(port, S3C64XX_UINTM));
162 else
163 disable_irq_nosync(ourport->tx_irq);
Ben Dooksb4975492008-07-03 12:32:51 +0100164 tx_enabled(port) = 0;
165 if (port->flags & UPF_CONS_FLOW)
166 s3c24xx_serial_rx_enable(port);
167 }
168}
169
170static void s3c24xx_serial_start_tx(struct uart_port *port)
171{
Ben Dooksb73c289c2008-10-21 14:07:04 +0100172 struct s3c24xx_uart_port *ourport = to_ourport(port);
173
Ben Dooksb4975492008-07-03 12:32:51 +0100174 if (!tx_enabled(port)) {
175 if (port->flags & UPF_CONS_FLOW)
176 s3c24xx_serial_rx_disable(port);
177
Thomas Abraham88bb4ea2011-08-10 15:51:19 +0530178 if (s3c24xx_serial_has_interrupt_mask(port))
179 __clear_bit(S3C64XX_UINTM_TXD,
180 portaddrl(port, S3C64XX_UINTM));
181 else
182 enable_irq(ourport->tx_irq);
Ben Dooksb4975492008-07-03 12:32:51 +0100183 tx_enabled(port) = 1;
184 }
185}
186
Ben Dooksb4975492008-07-03 12:32:51 +0100187static void s3c24xx_serial_stop_rx(struct uart_port *port)
188{
Ben Dooksb73c289c2008-10-21 14:07:04 +0100189 struct s3c24xx_uart_port *ourport = to_ourport(port);
190
Ben Dooksb4975492008-07-03 12:32:51 +0100191 if (rx_enabled(port)) {
192 dbg("s3c24xx_serial_stop_rx: port=%p\n", port);
Thomas Abraham88bb4ea2011-08-10 15:51:19 +0530193 if (s3c24xx_serial_has_interrupt_mask(port))
194 __set_bit(S3C64XX_UINTM_RXD,
195 portaddrl(port, S3C64XX_UINTM));
196 else
197 disable_irq_nosync(ourport->rx_irq);
Ben Dooksb4975492008-07-03 12:32:51 +0100198 rx_enabled(port) = 0;
199 }
200}
201
Robert Baldygaef4aca72014-11-24 07:56:22 +0100202static inline struct s3c24xx_uart_info
203 *s3c24xx_port_to_info(struct uart_port *port)
Ben Dooksb4975492008-07-03 12:32:51 +0100204{
205 return to_ourport(port)->info;
206}
207
Robert Baldygaef4aca72014-11-24 07:56:22 +0100208static inline struct s3c2410_uartcfg
209 *s3c24xx_port_to_cfg(struct uart_port *port)
Ben Dooksb4975492008-07-03 12:32:51 +0100210{
Thomas Abraham4d84e972011-10-24 11:47:25 +0200211 struct s3c24xx_uart_port *ourport;
212
Ben Dooksb4975492008-07-03 12:32:51 +0100213 if (port->dev == NULL)
214 return NULL;
215
Thomas Abraham4d84e972011-10-24 11:47:25 +0200216 ourport = container_of(port, struct s3c24xx_uart_port, port);
217 return ourport->cfg;
Ben Dooksb4975492008-07-03 12:32:51 +0100218}
219
220static int s3c24xx_serial_rx_fifocnt(struct s3c24xx_uart_port *ourport,
221 unsigned long ufstat)
222{
223 struct s3c24xx_uart_info *info = ourport->info;
224
225 if (ufstat & info->rx_fifofull)
Thomas Abrahamda121502011-11-02 19:23:25 +0900226 return ourport->port.fifosize;
Ben Dooksb4975492008-07-03 12:32:51 +0100227
228 return (ufstat & info->rx_fifomask) >> info->rx_fifoshift;
229}
230
231
232/* ? - where has parity gone?? */
233#define S3C2410_UERSTAT_PARITY (0x1000)
234
235static irqreturn_t
236s3c24xx_serial_rx_chars(int irq, void *dev_id)
237{
238 struct s3c24xx_uart_port *ourport = dev_id;
239 struct uart_port *port = &ourport->port;
Ben Dooksb4975492008-07-03 12:32:51 +0100240 unsigned int ufcon, ch, flag, ufstat, uerstat;
Thomas Abrahamc15c3742012-11-22 18:06:28 +0530241 unsigned long flags;
Robert Baldyga57850a52014-11-24 07:56:24 +0100242 int max_count = port->fifosize;
Ben Dooksb4975492008-07-03 12:32:51 +0100243
Thomas Abrahamc15c3742012-11-22 18:06:28 +0530244 spin_lock_irqsave(&port->lock, flags);
245
Ben Dooksb4975492008-07-03 12:32:51 +0100246 while (max_count-- > 0) {
247 ufcon = rd_regl(port, S3C2410_UFCON);
248 ufstat = rd_regl(port, S3C2410_UFSTAT);
249
250 if (s3c24xx_serial_rx_fifocnt(ourport, ufstat) == 0)
251 break;
252
253 uerstat = rd_regl(port, S3C2410_UERSTAT);
254 ch = rd_regb(port, S3C2410_URXH);
255
256 if (port->flags & UPF_CONS_FLOW) {
257 int txe = s3c24xx_serial_txempty_nofifo(port);
258
259 if (rx_enabled(port)) {
260 if (!txe) {
261 rx_enabled(port) = 0;
262 continue;
263 }
264 } else {
265 if (txe) {
266 ufcon |= S3C2410_UFCON_RESETRX;
267 wr_regl(port, S3C2410_UFCON, ufcon);
268 rx_enabled(port) = 1;
Viresh Kumarf5693ea2013-08-19 20:14:26 +0530269 spin_unlock_irqrestore(&port->lock,
270 flags);
Ben Dooksb4975492008-07-03 12:32:51 +0100271 goto out;
272 }
273 continue;
274 }
275 }
276
277 /* insert the character into the buffer */
278
279 flag = TTY_NORMAL;
280 port->icount.rx++;
281
282 if (unlikely(uerstat & S3C2410_UERSTAT_ANY)) {
283 dbg("rxerr: port ch=0x%02x, rxs=0x%08x\n",
284 ch, uerstat);
285
286 /* check for break */
287 if (uerstat & S3C2410_UERSTAT_BREAK) {
288 dbg("break!\n");
289 port->icount.brk++;
290 if (uart_handle_break(port))
Sachin Kamat9303ac12012-09-05 10:30:11 +0530291 goto ignore_char;
Ben Dooksb4975492008-07-03 12:32:51 +0100292 }
293
294 if (uerstat & S3C2410_UERSTAT_FRAME)
295 port->icount.frame++;
296 if (uerstat & S3C2410_UERSTAT_OVERRUN)
297 port->icount.overrun++;
298
299 uerstat &= port->read_status_mask;
300
301 if (uerstat & S3C2410_UERSTAT_BREAK)
302 flag = TTY_BREAK;
303 else if (uerstat & S3C2410_UERSTAT_PARITY)
304 flag = TTY_PARITY;
305 else if (uerstat & (S3C2410_UERSTAT_FRAME |
306 S3C2410_UERSTAT_OVERRUN))
307 flag = TTY_FRAME;
308 }
309
310 if (uart_handle_sysrq_char(port, ch))
311 goto ignore_char;
312
313 uart_insert_char(port, uerstat, S3C2410_UERSTAT_OVERRUN,
314 ch, flag);
315
Robert Baldygaef4aca72014-11-24 07:56:22 +0100316ignore_char:
Ben Dooksb4975492008-07-03 12:32:51 +0100317 continue;
318 }
Viresh Kumarf5693ea2013-08-19 20:14:26 +0530319
320 spin_unlock_irqrestore(&port->lock, flags);
Jiri Slaby2e124b42013-01-03 15:53:06 +0100321 tty_flip_buffer_push(&port->state->port);
Ben Dooksb4975492008-07-03 12:32:51 +0100322
Robert Baldygaef4aca72014-11-24 07:56:22 +0100323out:
Ben Dooksb4975492008-07-03 12:32:51 +0100324 return IRQ_HANDLED;
325}
326
327static irqreturn_t s3c24xx_serial_tx_chars(int irq, void *id)
328{
329 struct s3c24xx_uart_port *ourport = id;
330 struct uart_port *port = &ourport->port;
Alan Coxebd2c8f2009-09-19 13:13:28 -0700331 struct circ_buf *xmit = &port->state->xmit;
Thomas Abrahamc15c3742012-11-22 18:06:28 +0530332 unsigned long flags;
Robert Baldyga57850a52014-11-24 07:56:24 +0100333 int count = port->fifosize;
Ben Dooksb4975492008-07-03 12:32:51 +0100334
Thomas Abrahamc15c3742012-11-22 18:06:28 +0530335 spin_lock_irqsave(&port->lock, flags);
336
Ben Dooksb4975492008-07-03 12:32:51 +0100337 if (port->x_char) {
338 wr_regb(port, S3C2410_UTXH, port->x_char);
339 port->icount.tx++;
340 port->x_char = 0;
341 goto out;
342 }
343
Lucas De Marchi25985ed2011-03-30 22:57:33 -0300344 /* if there isn't anything more to transmit, or the uart is now
Ben Dooksb4975492008-07-03 12:32:51 +0100345 * stopped, disable the uart and exit
346 */
347
348 if (uart_circ_empty(xmit) || uart_tx_stopped(port)) {
349 s3c24xx_serial_stop_tx(port);
350 goto out;
351 }
352
353 /* try and drain the buffer... */
354
355 while (!uart_circ_empty(xmit) && count-- > 0) {
356 if (rd_regl(port, S3C2410_UFSTAT) & ourport->info->tx_fifofull)
357 break;
358
359 wr_regb(port, S3C2410_UTXH, xmit->buf[xmit->tail]);
360 xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
361 port->icount.tx++;
362 }
363
Thomas Abrahamc15c3742012-11-22 18:06:28 +0530364 if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) {
365 spin_unlock(&port->lock);
Ben Dooksb4975492008-07-03 12:32:51 +0100366 uart_write_wakeup(port);
Thomas Abrahamc15c3742012-11-22 18:06:28 +0530367 spin_lock(&port->lock);
368 }
Ben Dooksb4975492008-07-03 12:32:51 +0100369
370 if (uart_circ_empty(xmit))
371 s3c24xx_serial_stop_tx(port);
372
Robert Baldygaef4aca72014-11-24 07:56:22 +0100373out:
Thomas Abrahamc15c3742012-11-22 18:06:28 +0530374 spin_unlock_irqrestore(&port->lock, flags);
Ben Dooksb4975492008-07-03 12:32:51 +0100375 return IRQ_HANDLED;
376}
377
Thomas Abraham88bb4ea2011-08-10 15:51:19 +0530378/* interrupt handler for s3c64xx and later SoC's.*/
379static irqreturn_t s3c64xx_serial_handle_irq(int irq, void *id)
380{
381 struct s3c24xx_uart_port *ourport = id;
382 struct uart_port *port = &ourport->port;
383 unsigned int pend = rd_regl(port, S3C64XX_UINTP);
Thomas Abraham88bb4ea2011-08-10 15:51:19 +0530384 irqreturn_t ret = IRQ_HANDLED;
385
Thomas Abraham88bb4ea2011-08-10 15:51:19 +0530386 if (pend & S3C64XX_UINTM_RXD_MSK) {
387 ret = s3c24xx_serial_rx_chars(irq, id);
388 wr_regl(port, S3C64XX_UINTP, S3C64XX_UINTM_RXD_MSK);
389 }
390 if (pend & S3C64XX_UINTM_TXD_MSK) {
391 ret = s3c24xx_serial_tx_chars(irq, id);
392 wr_regl(port, S3C64XX_UINTP, S3C64XX_UINTM_TXD_MSK);
393 }
Thomas Abraham88bb4ea2011-08-10 15:51:19 +0530394 return ret;
395}
396
Ben Dooksb4975492008-07-03 12:32:51 +0100397static unsigned int s3c24xx_serial_tx_empty(struct uart_port *port)
398{
399 struct s3c24xx_uart_info *info = s3c24xx_port_to_info(port);
400 unsigned long ufstat = rd_regl(port, S3C2410_UFSTAT);
401 unsigned long ufcon = rd_regl(port, S3C2410_UFCON);
402
403 if (ufcon & S3C2410_UFCON_FIFOMODE) {
404 if ((ufstat & info->tx_fifomask) != 0 ||
405 (ufstat & info->tx_fifofull))
406 return 0;
407
408 return 1;
409 }
410
411 return s3c24xx_serial_txempty_nofifo(port);
412}
413
414/* no modem control lines */
415static unsigned int s3c24xx_serial_get_mctrl(struct uart_port *port)
416{
417 unsigned int umstat = rd_regb(port, S3C2410_UMSTAT);
418
419 if (umstat & S3C2410_UMSTAT_CTS)
420 return TIOCM_CAR | TIOCM_DSR | TIOCM_CTS;
421 else
422 return TIOCM_CAR | TIOCM_DSR;
423}
424
425static void s3c24xx_serial_set_mctrl(struct uart_port *port, unsigned int mctrl)
426{
José Miguel Gonçalves2d1e5a42013-09-18 16:52:49 +0100427 unsigned int umcon = rd_regl(port, S3C2410_UMCON);
428
429 if (mctrl & TIOCM_RTS)
430 umcon |= S3C2410_UMCOM_RTS_LOW;
431 else
432 umcon &= ~S3C2410_UMCOM_RTS_LOW;
433
434 wr_regl(port, S3C2410_UMCON, umcon);
Ben Dooksb4975492008-07-03 12:32:51 +0100435}
436
437static void s3c24xx_serial_break_ctl(struct uart_port *port, int break_state)
438{
439 unsigned long flags;
440 unsigned int ucon;
441
442 spin_lock_irqsave(&port->lock, flags);
443
444 ucon = rd_regl(port, S3C2410_UCON);
445
446 if (break_state)
447 ucon |= S3C2410_UCON_SBREAK;
448 else
449 ucon &= ~S3C2410_UCON_SBREAK;
450
451 wr_regl(port, S3C2410_UCON, ucon);
452
453 spin_unlock_irqrestore(&port->lock, flags);
454}
455
456static void s3c24xx_serial_shutdown(struct uart_port *port)
457{
458 struct s3c24xx_uart_port *ourport = to_ourport(port);
459
460 if (ourport->tx_claimed) {
Thomas Abraham88bb4ea2011-08-10 15:51:19 +0530461 if (!s3c24xx_serial_has_interrupt_mask(port))
462 free_irq(ourport->tx_irq, ourport);
Ben Dooksb4975492008-07-03 12:32:51 +0100463 tx_enabled(port) = 0;
464 ourport->tx_claimed = 0;
465 }
466
467 if (ourport->rx_claimed) {
Thomas Abraham88bb4ea2011-08-10 15:51:19 +0530468 if (!s3c24xx_serial_has_interrupt_mask(port))
469 free_irq(ourport->rx_irq, ourport);
Ben Dooksb4975492008-07-03 12:32:51 +0100470 ourport->rx_claimed = 0;
471 rx_enabled(port) = 0;
472 }
Ben Dooksb4975492008-07-03 12:32:51 +0100473
Thomas Abraham88bb4ea2011-08-10 15:51:19 +0530474 /* Clear pending interrupts and mask all interrupts */
475 if (s3c24xx_serial_has_interrupt_mask(port)) {
Tomasz Figab6ad2932013-03-26 15:57:35 +0100476 free_irq(port->irq, ourport);
477
Thomas Abraham88bb4ea2011-08-10 15:51:19 +0530478 wr_regl(port, S3C64XX_UINTP, 0xf);
479 wr_regl(port, S3C64XX_UINTM, 0xf);
480 }
481}
Ben Dooksb4975492008-07-03 12:32:51 +0100482
483static int s3c24xx_serial_startup(struct uart_port *port)
484{
485 struct s3c24xx_uart_port *ourport = to_ourport(port);
486 int ret;
487
Joe Perchese4ac92d2014-05-20 14:05:50 -0700488 dbg("s3c24xx_serial_startup: port=%p (%08llx,%p)\n",
489 port, (unsigned long long)port->mapbase, port->membase);
Ben Dooksb4975492008-07-03 12:32:51 +0100490
491 rx_enabled(port) = 1;
492
Ben Dooksb73c289c2008-10-21 14:07:04 +0100493 ret = request_irq(ourport->rx_irq, s3c24xx_serial_rx_chars, 0,
Ben Dooksb4975492008-07-03 12:32:51 +0100494 s3c24xx_serial_portname(port), ourport);
495
496 if (ret != 0) {
Sachin Kamatd20925e2012-09-05 10:30:10 +0530497 dev_err(port->dev, "cannot get irq %d\n", ourport->rx_irq);
Ben Dooksb4975492008-07-03 12:32:51 +0100498 return ret;
499 }
500
501 ourport->rx_claimed = 1;
502
503 dbg("requesting tx irq...\n");
504
505 tx_enabled(port) = 1;
506
Ben Dooksb73c289c2008-10-21 14:07:04 +0100507 ret = request_irq(ourport->tx_irq, s3c24xx_serial_tx_chars, 0,
Ben Dooksb4975492008-07-03 12:32:51 +0100508 s3c24xx_serial_portname(port), ourport);
509
510 if (ret) {
Sachin Kamatd20925e2012-09-05 10:30:10 +0530511 dev_err(port->dev, "cannot get irq %d\n", ourport->tx_irq);
Ben Dooksb4975492008-07-03 12:32:51 +0100512 goto err;
513 }
514
515 ourport->tx_claimed = 1;
516
517 dbg("s3c24xx_serial_startup ok\n");
518
519 /* the port reset code should have done the correct
520 * register setup for the port controls */
521
522 return ret;
523
Robert Baldygaef4aca72014-11-24 07:56:22 +0100524err:
Ben Dooksb4975492008-07-03 12:32:51 +0100525 s3c24xx_serial_shutdown(port);
526 return ret;
527}
528
Thomas Abraham88bb4ea2011-08-10 15:51:19 +0530529static int s3c64xx_serial_startup(struct uart_port *port)
530{
531 struct s3c24xx_uart_port *ourport = to_ourport(port);
532 int ret;
533
Joe Perchese4ac92d2014-05-20 14:05:50 -0700534 dbg("s3c64xx_serial_startup: port=%p (%08llx,%p)\n",
535 port, (unsigned long long)port->mapbase, port->membase);
Thomas Abraham88bb4ea2011-08-10 15:51:19 +0530536
Tomasz Figab6ad2932013-03-26 15:57:35 +0100537 wr_regl(port, S3C64XX_UINTM, 0xf);
538
Thomas Abraham88bb4ea2011-08-10 15:51:19 +0530539 ret = request_irq(port->irq, s3c64xx_serial_handle_irq, IRQF_SHARED,
540 s3c24xx_serial_portname(port), ourport);
541 if (ret) {
Sachin Kamatd20925e2012-09-05 10:30:10 +0530542 dev_err(port->dev, "cannot get irq %d\n", port->irq);
Thomas Abraham88bb4ea2011-08-10 15:51:19 +0530543 return ret;
544 }
545
546 /* For compatibility with s3c24xx Soc's */
547 rx_enabled(port) = 1;
548 ourport->rx_claimed = 1;
549 tx_enabled(port) = 0;
550 ourport->tx_claimed = 1;
551
552 /* Enable Rx Interrupt */
553 __clear_bit(S3C64XX_UINTM_RXD, portaddrl(port, S3C64XX_UINTM));
554 dbg("s3c64xx_serial_startup ok\n");
555 return ret;
556}
557
Ben Dooksb4975492008-07-03 12:32:51 +0100558/* power power management control */
559
560static void s3c24xx_serial_pm(struct uart_port *port, unsigned int level,
561 unsigned int old)
562{
563 struct s3c24xx_uart_port *ourport = to_ourport(port);
Robert Baldyga1ff383a2014-11-24 07:56:21 +0100564 int timeout = 10000;
Ben Dooksb4975492008-07-03 12:32:51 +0100565
Ben Dooks30555472008-10-21 14:06:36 +0100566 ourport->pm_level = level;
567
Ben Dooksb4975492008-07-03 12:32:51 +0100568 switch (level) {
569 case 3:
Robert Baldyga1ff383a2014-11-24 07:56:21 +0100570 while (--timeout && !s3c24xx_serial_txempty_nofifo(port))
571 udelay(100);
572
Kyoungil Kim7cd88832012-05-20 17:45:54 +0900573 if (!IS_ERR(ourport->baudclk))
Thomas Abraham9484b002012-10-03 07:40:04 +0900574 clk_disable_unprepare(ourport->baudclk);
Ben Dooksb4975492008-07-03 12:32:51 +0100575
Thomas Abraham9484b002012-10-03 07:40:04 +0900576 clk_disable_unprepare(ourport->clk);
Ben Dooksb4975492008-07-03 12:32:51 +0100577 break;
578
579 case 0:
Thomas Abraham9484b002012-10-03 07:40:04 +0900580 clk_prepare_enable(ourport->clk);
Ben Dooksb4975492008-07-03 12:32:51 +0100581
Kyoungil Kim7cd88832012-05-20 17:45:54 +0900582 if (!IS_ERR(ourport->baudclk))
Thomas Abraham9484b002012-10-03 07:40:04 +0900583 clk_prepare_enable(ourport->baudclk);
Ben Dooksb4975492008-07-03 12:32:51 +0100584
585 break;
586 default:
Sachin Kamatd20925e2012-09-05 10:30:10 +0530587 dev_err(port->dev, "s3c24xx_serial: unknown pm %d\n", level);
Ben Dooksb4975492008-07-03 12:32:51 +0100588 }
589}
590
591/* baud rate calculation
592 *
593 * The UARTs on the S3C2410/S3C2440 can take their clocks from a number
594 * of different sources, including the peripheral clock ("pclk") and an
595 * external clock ("uclk"). The S3C2440 also adds the core clock ("fclk")
596 * with a programmable extra divisor.
597 *
598 * The following code goes through the clock sources, and calculates the
599 * baud clocks (and the resultant actual baud rates) and then tries to
600 * pick the closest one and select that.
601 *
602*/
603
Thomas Abraham5f5a7a52011-10-24 11:47:46 +0200604#define MAX_CLK_NAME_LENGTH 15
Ben Dooksb4975492008-07-03 12:32:51 +0100605
Thomas Abraham5f5a7a52011-10-24 11:47:46 +0200606static inline int s3c24xx_serial_getsource(struct uart_port *port)
Ben Dooksb4975492008-07-03 12:32:51 +0100607{
608 struct s3c24xx_uart_info *info = s3c24xx_port_to_info(port);
Thomas Abraham5f5a7a52011-10-24 11:47:46 +0200609 unsigned int ucon;
Ben Dooksb4975492008-07-03 12:32:51 +0100610
Thomas Abraham5f5a7a52011-10-24 11:47:46 +0200611 if (info->num_clks == 1)
Ben Dooksb4975492008-07-03 12:32:51 +0100612 return 0;
613
Thomas Abraham5f5a7a52011-10-24 11:47:46 +0200614 ucon = rd_regl(port, S3C2410_UCON);
615 ucon &= info->clksel_mask;
616 return ucon >> info->clksel_shift;
Ben Dooksb4975492008-07-03 12:32:51 +0100617}
618
Thomas Abraham5f5a7a52011-10-24 11:47:46 +0200619static void s3c24xx_serial_setsource(struct uart_port *port,
620 unsigned int clk_sel)
Ben Dooksb4975492008-07-03 12:32:51 +0100621{
Thomas Abraham5f5a7a52011-10-24 11:47:46 +0200622 struct s3c24xx_uart_info *info = s3c24xx_port_to_info(port);
623 unsigned int ucon;
Ben Dooksb4975492008-07-03 12:32:51 +0100624
Thomas Abraham5f5a7a52011-10-24 11:47:46 +0200625 if (info->num_clks == 1)
626 return;
Ben Dooksb4975492008-07-03 12:32:51 +0100627
Thomas Abraham5f5a7a52011-10-24 11:47:46 +0200628 ucon = rd_regl(port, S3C2410_UCON);
629 if ((ucon & info->clksel_mask) >> info->clksel_shift == clk_sel)
630 return;
Ben Dooksb4975492008-07-03 12:32:51 +0100631
Thomas Abraham5f5a7a52011-10-24 11:47:46 +0200632 ucon &= ~info->clksel_mask;
633 ucon |= clk_sel << info->clksel_shift;
634 wr_regl(port, S3C2410_UCON, ucon);
635}
Ben Dooksb4975492008-07-03 12:32:51 +0100636
Thomas Abraham5f5a7a52011-10-24 11:47:46 +0200637static unsigned int s3c24xx_serial_getclk(struct s3c24xx_uart_port *ourport,
638 unsigned int req_baud, struct clk **best_clk,
639 unsigned int *clk_num)
640{
641 struct s3c24xx_uart_info *info = ourport->info;
642 struct clk *clk;
643 unsigned long rate;
644 unsigned int cnt, baud, quot, clk_sel, best_quot = 0;
645 char clkname[MAX_CLK_NAME_LENGTH];
646 int calc_deviation, deviation = (1 << 30) - 1;
647
Thomas Abraham5f5a7a52011-10-24 11:47:46 +0200648 clk_sel = (ourport->cfg->clk_sel) ? ourport->cfg->clk_sel :
649 ourport->info->def_clk_sel;
650 for (cnt = 0; cnt < info->num_clks; cnt++) {
651 if (!(clk_sel & (1 << cnt)))
652 continue;
653
654 sprintf(clkname, "clk_uart_baud%d", cnt);
655 clk = clk_get(ourport->port.dev, clkname);
Kyoungil Kim7cd88832012-05-20 17:45:54 +0900656 if (IS_ERR(clk))
Thomas Abraham5f5a7a52011-10-24 11:47:46 +0200657 continue;
658
659 rate = clk_get_rate(clk);
660 if (!rate)
661 continue;
662
663 if (ourport->info->has_divslot) {
664 unsigned long div = rate / req_baud;
665
666 /* The UDIVSLOT register on the newer UARTs allows us to
667 * get a divisor adjustment of 1/16th on the baud clock.
668 *
669 * We don't keep the UDIVSLOT value (the 16ths we
670 * calculated by not multiplying the baud by 16) as it
671 * is easy enough to recalculate.
672 */
673
674 quot = div / 16;
675 baud = rate / div;
676 } else {
677 quot = (rate + (8 * req_baud)) / (16 * req_baud);
678 baud = rate / (quot * 16);
679 }
680 quot--;
681
682 calc_deviation = req_baud - baud;
683 if (calc_deviation < 0)
684 calc_deviation = -calc_deviation;
685
686 if (calc_deviation < deviation) {
687 *best_clk = clk;
688 best_quot = quot;
689 *clk_num = cnt;
690 deviation = calc_deviation;
Ben Dooksb4975492008-07-03 12:32:51 +0100691 }
692 }
693
Thomas Abraham5f5a7a52011-10-24 11:47:46 +0200694 return best_quot;
Ben Dooksb4975492008-07-03 12:32:51 +0100695}
696
Ben Dooks090f8482008-12-12 00:24:21 +0000697/* udivslot_table[]
698 *
699 * This table takes the fractional value of the baud divisor and gives
700 * the recommended setting for the UDIVSLOT register.
701 */
702static u16 udivslot_table[16] = {
703 [0] = 0x0000,
704 [1] = 0x0080,
705 [2] = 0x0808,
706 [3] = 0x0888,
707 [4] = 0x2222,
708 [5] = 0x4924,
709 [6] = 0x4A52,
710 [7] = 0x54AA,
711 [8] = 0x5555,
712 [9] = 0xD555,
713 [10] = 0xD5D5,
714 [11] = 0xDDD5,
715 [12] = 0xDDDD,
716 [13] = 0xDFDD,
717 [14] = 0xDFDF,
718 [15] = 0xFFDF,
719};
720
Ben Dooksb4975492008-07-03 12:32:51 +0100721static void s3c24xx_serial_set_termios(struct uart_port *port,
722 struct ktermios *termios,
723 struct ktermios *old)
724{
725 struct s3c2410_uartcfg *cfg = s3c24xx_port_to_cfg(port);
726 struct s3c24xx_uart_port *ourport = to_ourport(port);
Kyoungil Kim7cd88832012-05-20 17:45:54 +0900727 struct clk *clk = ERR_PTR(-EINVAL);
Ben Dooksb4975492008-07-03 12:32:51 +0100728 unsigned long flags;
Thomas Abraham5f5a7a52011-10-24 11:47:46 +0200729 unsigned int baud, quot, clk_sel = 0;
Ben Dooksb4975492008-07-03 12:32:51 +0100730 unsigned int ulcon;
731 unsigned int umcon;
Ben Dooks090f8482008-12-12 00:24:21 +0000732 unsigned int udivslot = 0;
Ben Dooksb4975492008-07-03 12:32:51 +0100733
734 /*
735 * We don't support modem control lines.
736 */
737 termios->c_cflag &= ~(HUPCL | CMSPAR);
738 termios->c_cflag |= CLOCAL;
739
740 /*
741 * Ask the core to calculate the divisor for us.
742 */
743
744 baud = uart_get_baud_rate(port, termios, old, 0, 115200*8);
Thomas Abraham5f5a7a52011-10-24 11:47:46 +0200745 quot = s3c24xx_serial_getclk(ourport, baud, &clk, &clk_sel);
Ben Dooksb4975492008-07-03 12:32:51 +0100746 if (baud == 38400 && (port->flags & UPF_SPD_MASK) == UPF_SPD_CUST)
747 quot = port->custom_divisor;
Kyoungil Kim7cd88832012-05-20 17:45:54 +0900748 if (IS_ERR(clk))
Thomas Abraham5f5a7a52011-10-24 11:47:46 +0200749 return;
Ben Dooksb4975492008-07-03 12:32:51 +0100750
751 /* check to see if we need to change clock source */
752
Thomas Abraham5f5a7a52011-10-24 11:47:46 +0200753 if (ourport->baudclk != clk) {
754 s3c24xx_serial_setsource(port, clk_sel);
Ben Dooksb4975492008-07-03 12:32:51 +0100755
Kyoungil Kim7cd88832012-05-20 17:45:54 +0900756 if (!IS_ERR(ourport->baudclk)) {
Thomas Abraham9484b002012-10-03 07:40:04 +0900757 clk_disable_unprepare(ourport->baudclk);
Kyoungil Kim7cd88832012-05-20 17:45:54 +0900758 ourport->baudclk = ERR_PTR(-EINVAL);
Ben Dooksb4975492008-07-03 12:32:51 +0100759 }
760
Thomas Abraham9484b002012-10-03 07:40:04 +0900761 clk_prepare_enable(clk);
Ben Dooksb4975492008-07-03 12:32:51 +0100762
Ben Dooksb4975492008-07-03 12:32:51 +0100763 ourport->baudclk = clk;
Ben Dooks30555472008-10-21 14:06:36 +0100764 ourport->baudclk_rate = clk ? clk_get_rate(clk) : 0;
Ben Dooksb4975492008-07-03 12:32:51 +0100765 }
766
Ben Dooks090f8482008-12-12 00:24:21 +0000767 if (ourport->info->has_divslot) {
768 unsigned int div = ourport->baudclk_rate / baud;
769
Jongpill Lee8b526ae2010-07-16 10:19:41 +0900770 if (cfg->has_fracval) {
771 udivslot = (div & 15);
772 dbg("fracval = %04x\n", udivslot);
773 } else {
774 udivslot = udivslot_table[div & 15];
775 dbg("udivslot = %04x (div %d)\n", udivslot, div & 15);
776 }
Ben Dooks090f8482008-12-12 00:24:21 +0000777 }
778
Ben Dooksb4975492008-07-03 12:32:51 +0100779 switch (termios->c_cflag & CSIZE) {
780 case CS5:
781 dbg("config: 5bits/char\n");
782 ulcon = S3C2410_LCON_CS5;
783 break;
784 case CS6:
785 dbg("config: 6bits/char\n");
786 ulcon = S3C2410_LCON_CS6;
787 break;
788 case CS7:
789 dbg("config: 7bits/char\n");
790 ulcon = S3C2410_LCON_CS7;
791 break;
792 case CS8:
793 default:
794 dbg("config: 8bits/char\n");
795 ulcon = S3C2410_LCON_CS8;
796 break;
797 }
798
799 /* preserve original lcon IR settings */
800 ulcon |= (cfg->ulcon & S3C2410_LCON_IRM);
801
802 if (termios->c_cflag & CSTOPB)
803 ulcon |= S3C2410_LCON_STOPB;
804
Ben Dooksb4975492008-07-03 12:32:51 +0100805 if (termios->c_cflag & PARENB) {
806 if (termios->c_cflag & PARODD)
807 ulcon |= S3C2410_LCON_PODD;
808 else
809 ulcon |= S3C2410_LCON_PEVEN;
810 } else {
811 ulcon |= S3C2410_LCON_PNONE;
812 }
813
814 spin_lock_irqsave(&port->lock, flags);
815
Ben Dooks090f8482008-12-12 00:24:21 +0000816 dbg("setting ulcon to %08x, brddiv to %d, udivslot %08x\n",
817 ulcon, quot, udivslot);
Ben Dooksb4975492008-07-03 12:32:51 +0100818
819 wr_regl(port, S3C2410_ULCON, ulcon);
820 wr_regl(port, S3C2410_UBRDIV, quot);
José Miguel Gonçalves2d1e5a42013-09-18 16:52:49 +0100821
822 umcon = rd_regl(port, S3C2410_UMCON);
823 if (termios->c_cflag & CRTSCTS) {
824 umcon |= S3C2410_UMCOM_AFC;
825 /* Disable RTS when RX FIFO contains 63 bytes */
826 umcon &= ~S3C2412_UMCON_AFC_8;
827 } else {
828 umcon &= ~S3C2410_UMCOM_AFC;
829 }
Ben Dooksb4975492008-07-03 12:32:51 +0100830 wr_regl(port, S3C2410_UMCON, umcon);
831
Ben Dooks090f8482008-12-12 00:24:21 +0000832 if (ourport->info->has_divslot)
833 wr_regl(port, S3C2443_DIVSLOT, udivslot);
834
Ben Dooksb4975492008-07-03 12:32:51 +0100835 dbg("uart: ulcon = 0x%08x, ucon = 0x%08x, ufcon = 0x%08x\n",
836 rd_regl(port, S3C2410_ULCON),
837 rd_regl(port, S3C2410_UCON),
838 rd_regl(port, S3C2410_UFCON));
839
840 /*
841 * Update the per-port timeout.
842 */
843 uart_update_timeout(port, termios->c_cflag, baud);
844
845 /*
846 * Which character status flags are we interested in?
847 */
848 port->read_status_mask = S3C2410_UERSTAT_OVERRUN;
849 if (termios->c_iflag & INPCK)
Robert Baldygaef4aca72014-11-24 07:56:22 +0100850 port->read_status_mask |= S3C2410_UERSTAT_FRAME |
851 S3C2410_UERSTAT_PARITY;
Ben Dooksb4975492008-07-03 12:32:51 +0100852 /*
853 * Which character status flags should we ignore?
854 */
855 port->ignore_status_mask = 0;
856 if (termios->c_iflag & IGNPAR)
857 port->ignore_status_mask |= S3C2410_UERSTAT_OVERRUN;
858 if (termios->c_iflag & IGNBRK && termios->c_iflag & IGNPAR)
859 port->ignore_status_mask |= S3C2410_UERSTAT_FRAME;
860
861 /*
862 * Ignore all characters if CREAD is not set.
863 */
864 if ((termios->c_cflag & CREAD) == 0)
865 port->ignore_status_mask |= RXSTAT_DUMMY_READ;
866
867 spin_unlock_irqrestore(&port->lock, flags);
868}
869
870static const char *s3c24xx_serial_type(struct uart_port *port)
871{
872 switch (port->type) {
873 case PORT_S3C2410:
874 return "S3C2410";
875 case PORT_S3C2440:
876 return "S3C2440";
877 case PORT_S3C2412:
878 return "S3C2412";
Ben Dooksb690ace2008-10-21 14:07:03 +0100879 case PORT_S3C6400:
880 return "S3C6400/10";
Ben Dooksb4975492008-07-03 12:32:51 +0100881 default:
882 return NULL;
883 }
884}
885
886#define MAP_SIZE (0x100)
887
888static void s3c24xx_serial_release_port(struct uart_port *port)
889{
890 release_mem_region(port->mapbase, MAP_SIZE);
891}
892
893static int s3c24xx_serial_request_port(struct uart_port *port)
894{
895 const char *name = s3c24xx_serial_portname(port);
896 return request_mem_region(port->mapbase, MAP_SIZE, name) ? 0 : -EBUSY;
897}
898
899static void s3c24xx_serial_config_port(struct uart_port *port, int flags)
900{
901 struct s3c24xx_uart_info *info = s3c24xx_port_to_info(port);
902
903 if (flags & UART_CONFIG_TYPE &&
904 s3c24xx_serial_request_port(port) == 0)
905 port->type = info->type;
906}
907
908/*
909 * verify the new serial_struct (for TIOCSSERIAL).
910 */
911static int
912s3c24xx_serial_verify_port(struct uart_port *port, struct serial_struct *ser)
913{
914 struct s3c24xx_uart_info *info = s3c24xx_port_to_info(port);
915
916 if (ser->type != PORT_UNKNOWN && ser->type != info->type)
917 return -EINVAL;
918
919 return 0;
920}
921
922
923#ifdef CONFIG_SERIAL_SAMSUNG_CONSOLE
924
925static struct console s3c24xx_serial_console;
926
Julien Pichon93b5c032012-09-21 23:22:31 -0700927static int __init s3c24xx_serial_console_init(void)
928{
929 register_console(&s3c24xx_serial_console);
930 return 0;
931}
932console_initcall(s3c24xx_serial_console_init);
933
Ben Dooksb4975492008-07-03 12:32:51 +0100934#define S3C24XX_SERIAL_CONSOLE &s3c24xx_serial_console
935#else
936#define S3C24XX_SERIAL_CONSOLE NULL
937#endif
938
Arnd Bergmann84f57d92013-04-11 02:04:49 +0200939#if defined(CONFIG_SERIAL_SAMSUNG_CONSOLE) && defined(CONFIG_CONSOLE_POLL)
Julien Pichon93b5c032012-09-21 23:22:31 -0700940static int s3c24xx_serial_get_poll_char(struct uart_port *port);
941static void s3c24xx_serial_put_poll_char(struct uart_port *port,
942 unsigned char c);
943#endif
944
Ben Dooksb4975492008-07-03 12:32:51 +0100945static struct uart_ops s3c24xx_serial_ops = {
946 .pm = s3c24xx_serial_pm,
947 .tx_empty = s3c24xx_serial_tx_empty,
948 .get_mctrl = s3c24xx_serial_get_mctrl,
949 .set_mctrl = s3c24xx_serial_set_mctrl,
950 .stop_tx = s3c24xx_serial_stop_tx,
951 .start_tx = s3c24xx_serial_start_tx,
952 .stop_rx = s3c24xx_serial_stop_rx,
Ben Dooksb4975492008-07-03 12:32:51 +0100953 .break_ctl = s3c24xx_serial_break_ctl,
954 .startup = s3c24xx_serial_startup,
955 .shutdown = s3c24xx_serial_shutdown,
956 .set_termios = s3c24xx_serial_set_termios,
957 .type = s3c24xx_serial_type,
958 .release_port = s3c24xx_serial_release_port,
959 .request_port = s3c24xx_serial_request_port,
960 .config_port = s3c24xx_serial_config_port,
961 .verify_port = s3c24xx_serial_verify_port,
Arnd Bergmann84f57d92013-04-11 02:04:49 +0200962#if defined(CONFIG_SERIAL_SAMSUNG_CONSOLE) && defined(CONFIG_CONSOLE_POLL)
Julien Pichon93b5c032012-09-21 23:22:31 -0700963 .poll_get_char = s3c24xx_serial_get_poll_char,
964 .poll_put_char = s3c24xx_serial_put_poll_char,
965#endif
Ben Dooksb4975492008-07-03 12:32:51 +0100966};
967
Ben Dooksb4975492008-07-03 12:32:51 +0100968static struct uart_driver s3c24xx_uart_drv = {
969 .owner = THIS_MODULE,
Darius Augulis2cf0c582011-01-12 14:50:51 +0900970 .driver_name = "s3c2410_serial",
Ben Dooksbdd49152008-11-03 19:51:42 +0000971 .nr = CONFIG_SERIAL_SAMSUNG_UARTS,
Ben Dooksb4975492008-07-03 12:32:51 +0100972 .cons = S3C24XX_SERIAL_CONSOLE,
Darius Augulis2cf0c582011-01-12 14:50:51 +0900973 .dev_name = S3C24XX_SERIAL_NAME,
Ben Dooksb4975492008-07-03 12:32:51 +0100974 .major = S3C24XX_SERIAL_MAJOR,
975 .minor = S3C24XX_SERIAL_MINOR,
976};
977
Robert Baldygaef4aca72014-11-24 07:56:22 +0100978#define __PORT_LOCK_UNLOCKED(i) \
979 __SPIN_LOCK_UNLOCKED(s3c24xx_serial_ports[i].port.lock)
980static struct s3c24xx_uart_port
981s3c24xx_serial_ports[CONFIG_SERIAL_SAMSUNG_UARTS] = {
Ben Dooksb4975492008-07-03 12:32:51 +0100982 [0] = {
983 .port = {
Robert Baldygaef4aca72014-11-24 07:56:22 +0100984 .lock = __PORT_LOCK_UNLOCKED(0),
Ben Dooksb4975492008-07-03 12:32:51 +0100985 .iotype = UPIO_MEM,
Ben Dooksb4975492008-07-03 12:32:51 +0100986 .uartclk = 0,
987 .fifosize = 16,
988 .ops = &s3c24xx_serial_ops,
989 .flags = UPF_BOOT_AUTOCONF,
990 .line = 0,
991 }
992 },
993 [1] = {
994 .port = {
Robert Baldygaef4aca72014-11-24 07:56:22 +0100995 .lock = __PORT_LOCK_UNLOCKED(1),
Ben Dooksb4975492008-07-03 12:32:51 +0100996 .iotype = UPIO_MEM,
Ben Dooksb4975492008-07-03 12:32:51 +0100997 .uartclk = 0,
998 .fifosize = 16,
999 .ops = &s3c24xx_serial_ops,
1000 .flags = UPF_BOOT_AUTOCONF,
1001 .line = 1,
1002 }
1003 },
Ben Dooks03d5e772008-11-03 09:21:23 +00001004#if CONFIG_SERIAL_SAMSUNG_UARTS > 2
Ben Dooksb4975492008-07-03 12:32:51 +01001005
1006 [2] = {
1007 .port = {
Robert Baldygaef4aca72014-11-24 07:56:22 +01001008 .lock = __PORT_LOCK_UNLOCKED(2),
Ben Dooksb4975492008-07-03 12:32:51 +01001009 .iotype = UPIO_MEM,
Ben Dooksb4975492008-07-03 12:32:51 +01001010 .uartclk = 0,
1011 .fifosize = 16,
1012 .ops = &s3c24xx_serial_ops,
1013 .flags = UPF_BOOT_AUTOCONF,
1014 .line = 2,
1015 }
Ben Dooks03d5e772008-11-03 09:21:23 +00001016 },
1017#endif
1018#if CONFIG_SERIAL_SAMSUNG_UARTS > 3
1019 [3] = {
1020 .port = {
Robert Baldygaef4aca72014-11-24 07:56:22 +01001021 .lock = __PORT_LOCK_UNLOCKED(3),
Ben Dooks03d5e772008-11-03 09:21:23 +00001022 .iotype = UPIO_MEM,
Ben Dooks03d5e772008-11-03 09:21:23 +00001023 .uartclk = 0,
1024 .fifosize = 16,
1025 .ops = &s3c24xx_serial_ops,
1026 .flags = UPF_BOOT_AUTOCONF,
1027 .line = 3,
1028 }
Ben Dooksb4975492008-07-03 12:32:51 +01001029 }
1030#endif
1031};
Robert Baldygaef4aca72014-11-24 07:56:22 +01001032#undef __PORT_LOCK_UNLOCKED
Ben Dooksb4975492008-07-03 12:32:51 +01001033
1034/* s3c24xx_serial_resetport
1035 *
Thomas Abraham0dfb3b42011-10-24 11:48:21 +02001036 * reset the fifos and other the settings.
Ben Dooksb4975492008-07-03 12:32:51 +01001037*/
1038
Thomas Abraham0dfb3b42011-10-24 11:48:21 +02001039static void s3c24xx_serial_resetport(struct uart_port *port,
1040 struct s3c2410_uartcfg *cfg)
Ben Dooksb4975492008-07-03 12:32:51 +01001041{
1042 struct s3c24xx_uart_info *info = s3c24xx_port_to_info(port);
Thomas Abraham0dfb3b42011-10-24 11:48:21 +02001043 unsigned long ucon = rd_regl(port, S3C2410_UCON);
1044 unsigned int ucon_mask;
Ben Dooksb4975492008-07-03 12:32:51 +01001045
Thomas Abraham0dfb3b42011-10-24 11:48:21 +02001046 ucon_mask = info->clksel_mask;
1047 if (info->type == PORT_S3C2440)
1048 ucon_mask |= S3C2440_UCON0_DIVMASK;
1049
1050 ucon &= ucon_mask;
1051 wr_regl(port, S3C2410_UCON, ucon | cfg->ucon);
1052
1053 /* reset both fifos */
1054 wr_regl(port, S3C2410_UFCON, cfg->ufcon | S3C2410_UFCON_RESETBOTH);
1055 wr_regl(port, S3C2410_UFCON, cfg->ufcon);
1056
1057 /* some delay is required after fifo reset */
1058 udelay(1);
Ben Dooksb4975492008-07-03 12:32:51 +01001059}
1060
Ben Dooks30555472008-10-21 14:06:36 +01001061
1062#ifdef CONFIG_CPU_FREQ
1063
1064static int s3c24xx_serial_cpufreq_transition(struct notifier_block *nb,
1065 unsigned long val, void *data)
1066{
1067 struct s3c24xx_uart_port *port;
1068 struct uart_port *uport;
1069
1070 port = container_of(nb, struct s3c24xx_uart_port, freq_transition);
1071 uport = &port->port;
1072
1073 /* check to see if port is enabled */
1074
1075 if (port->pm_level != 0)
1076 return 0;
1077
1078 /* try and work out if the baudrate is changing, we can detect
1079 * a change in rate, but we do not have support for detecting
1080 * a disturbance in the clock-rate over the change.
1081 */
1082
Kyoungil Kim25f04ad2012-05-20 17:49:31 +09001083 if (IS_ERR(port->baudclk))
Ben Dooks30555472008-10-21 14:06:36 +01001084 goto exit;
1085
Kyoungil Kim25f04ad2012-05-20 17:49:31 +09001086 if (port->baudclk_rate == clk_get_rate(port->baudclk))
Ben Dooks30555472008-10-21 14:06:36 +01001087 goto exit;
1088
1089 if (val == CPUFREQ_PRECHANGE) {
1090 /* we should really shut the port down whilst the
1091 * frequency change is in progress. */
1092
1093 } else if (val == CPUFREQ_POSTCHANGE) {
1094 struct ktermios *termios;
1095 struct tty_struct *tty;
1096
Alan Coxebd2c8f2009-09-19 13:13:28 -07001097 if (uport->state == NULL)
Ben Dooks30555472008-10-21 14:06:36 +01001098 goto exit;
Ben Dooks30555472008-10-21 14:06:36 +01001099
Alan Coxebd2c8f2009-09-19 13:13:28 -07001100 tty = uport->state->port.tty;
Ben Dooks30555472008-10-21 14:06:36 +01001101
Ben Dooks7de40c22008-12-14 23:11:02 +00001102 if (tty == NULL)
Ben Dooks30555472008-10-21 14:06:36 +01001103 goto exit;
Ben Dooks30555472008-10-21 14:06:36 +01001104
Alan Coxadc8d742012-07-14 15:31:47 +01001105 termios = &tty->termios;
Ben Dooks30555472008-10-21 14:06:36 +01001106
1107 if (termios == NULL) {
Sachin Kamatd20925e2012-09-05 10:30:10 +05301108 dev_warn(uport->dev, "%s: no termios?\n", __func__);
Ben Dooks30555472008-10-21 14:06:36 +01001109 goto exit;
1110 }
1111
1112 s3c24xx_serial_set_termios(uport, termios, NULL);
1113 }
1114
Robert Baldygaef4aca72014-11-24 07:56:22 +01001115exit:
Ben Dooks30555472008-10-21 14:06:36 +01001116 return 0;
1117}
1118
Robert Baldygaef4aca72014-11-24 07:56:22 +01001119static inline int
1120s3c24xx_serial_cpufreq_register(struct s3c24xx_uart_port *port)
Ben Dooks30555472008-10-21 14:06:36 +01001121{
1122 port->freq_transition.notifier_call = s3c24xx_serial_cpufreq_transition;
1123
1124 return cpufreq_register_notifier(&port->freq_transition,
1125 CPUFREQ_TRANSITION_NOTIFIER);
1126}
1127
Robert Baldygaef4aca72014-11-24 07:56:22 +01001128static inline void
1129s3c24xx_serial_cpufreq_deregister(struct s3c24xx_uart_port *port)
Ben Dooks30555472008-10-21 14:06:36 +01001130{
1131 cpufreq_unregister_notifier(&port->freq_transition,
1132 CPUFREQ_TRANSITION_NOTIFIER);
1133}
1134
1135#else
Robert Baldygaef4aca72014-11-24 07:56:22 +01001136static inline int
1137s3c24xx_serial_cpufreq_register(struct s3c24xx_uart_port *port)
Ben Dooks30555472008-10-21 14:06:36 +01001138{
1139 return 0;
1140}
1141
Robert Baldygaef4aca72014-11-24 07:56:22 +01001142static inline void
1143s3c24xx_serial_cpufreq_deregister(struct s3c24xx_uart_port *port)
Ben Dooks30555472008-10-21 14:06:36 +01001144{
1145}
1146#endif
1147
Ben Dooksb4975492008-07-03 12:32:51 +01001148/* s3c24xx_serial_init_port
1149 *
1150 * initialise a single serial port from the platform device given
1151 */
1152
1153static int s3c24xx_serial_init_port(struct s3c24xx_uart_port *ourport,
Ben Dooksb4975492008-07-03 12:32:51 +01001154 struct platform_device *platdev)
1155{
1156 struct uart_port *port = &ourport->port;
Thomas Abrahamda121502011-11-02 19:23:25 +09001157 struct s3c2410_uartcfg *cfg = ourport->cfg;
Ben Dooksb4975492008-07-03 12:32:51 +01001158 struct resource *res;
1159 int ret;
1160
1161 dbg("s3c24xx_serial_init_port: port=%p, platdev=%p\n", port, platdev);
1162
1163 if (platdev == NULL)
1164 return -ENODEV;
1165
Ben Dooksb4975492008-07-03 12:32:51 +01001166 if (port->mapbase != 0)
1167 return 0;
1168
Ben Dooksb4975492008-07-03 12:32:51 +01001169 /* setup info for port */
1170 port->dev = &platdev->dev;
Ben Dooksb4975492008-07-03 12:32:51 +01001171
Thomas Abraham88bb4ea2011-08-10 15:51:19 +05301172 /* Startup sequence is different for s3c64xx and higher SoC's */
1173 if (s3c24xx_serial_has_interrupt_mask(port))
1174 s3c24xx_serial_ops.startup = s3c64xx_serial_startup;
1175
Ben Dooksb4975492008-07-03 12:32:51 +01001176 port->uartclk = 1;
1177
1178 if (cfg->uart_flags & UPF_CONS_FLOW) {
1179 dbg("s3c24xx_serial_init_port: enabling flow control\n");
1180 port->flags |= UPF_CONS_FLOW;
1181 }
1182
1183 /* sort our the physical and virtual addresses for each UART */
1184
1185 res = platform_get_resource(platdev, IORESOURCE_MEM, 0);
1186 if (res == NULL) {
Sachin Kamatd20925e2012-09-05 10:30:10 +05301187 dev_err(port->dev, "failed to find memory resource for uart\n");
Ben Dooksb4975492008-07-03 12:32:51 +01001188 return -EINVAL;
1189 }
1190
Joe Perchese4ac92d2014-05-20 14:05:50 -07001191 dbg("resource %pR)\n", res);
Ben Dooksb4975492008-07-03 12:32:51 +01001192
Thomas Abraham41147bf2013-01-01 00:21:55 -08001193 port->membase = devm_ioremap(port->dev, res->start, resource_size(res));
1194 if (!port->membase) {
1195 dev_err(port->dev, "failed to remap controller address\n");
1196 return -EBUSY;
1197 }
1198
Ben Dooksb690ace2008-10-21 14:07:03 +01001199 port->mapbase = res->start;
Ben Dooksb4975492008-07-03 12:32:51 +01001200 ret = platform_get_irq(platdev, 0);
1201 if (ret < 0)
1202 port->irq = 0;
Ben Dooksb73c289c2008-10-21 14:07:04 +01001203 else {
Ben Dooksb4975492008-07-03 12:32:51 +01001204 port->irq = ret;
Ben Dooksb73c289c2008-10-21 14:07:04 +01001205 ourport->rx_irq = ret;
1206 ourport->tx_irq = ret + 1;
1207 }
Sachin Kamat9303ac12012-09-05 10:30:11 +05301208
Ben Dooksb73c289c2008-10-21 14:07:04 +01001209 ret = platform_get_irq(platdev, 1);
1210 if (ret > 0)
1211 ourport->tx_irq = ret;
Robert Baldyga658c9d22014-12-10 12:49:23 +01001212 /*
1213 * DMA is currently supported only on DT platforms, if DMA properties
1214 * are specified.
1215 */
1216 if (platdev->dev.of_node && of_find_property(platdev->dev.of_node,
1217 "dmas", NULL)) {
1218 ourport->dma = devm_kzalloc(port->dev,
1219 sizeof(*ourport->dma),
1220 GFP_KERNEL);
1221 if (!ourport->dma)
1222 return -ENOMEM;
1223 }
Ben Dooksb4975492008-07-03 12:32:51 +01001224
1225 ourport->clk = clk_get(&platdev->dev, "uart");
Chander Kashyap60e93572013-05-28 18:32:07 +05301226 if (IS_ERR(ourport->clk)) {
1227 pr_err("%s: Controller clock not found\n",
1228 dev_name(&platdev->dev));
1229 return PTR_ERR(ourport->clk);
1230 }
1231
1232 ret = clk_prepare_enable(ourport->clk);
1233 if (ret) {
1234 pr_err("uart: clock failed to prepare+enable: %d\n", ret);
1235 clk_put(ourport->clk);
1236 return ret;
1237 }
Ben Dooksb4975492008-07-03 12:32:51 +01001238
Thomas Abraham88bb4ea2011-08-10 15:51:19 +05301239 /* Keep all interrupts masked and cleared */
1240 if (s3c24xx_serial_has_interrupt_mask(port)) {
1241 wr_regl(port, S3C64XX_UINTM, 0xf);
1242 wr_regl(port, S3C64XX_UINTP, 0xf);
1243 wr_regl(port, S3C64XX_UINTSP, 0xf);
1244 }
1245
Fabio Estevam1ff5b642014-06-04 20:06:41 -03001246 dbg("port: map=%pa, mem=%p, irq=%d (%d,%d), clock=%u\n",
1247 &port->mapbase, port->membase, port->irq,
Ben Dooksb73c289c2008-10-21 14:07:04 +01001248 ourport->rx_irq, ourport->tx_irq, port->uartclk);
Ben Dooksb4975492008-07-03 12:32:51 +01001249
1250 /* reset the fifos (and setup the uart) */
1251 s3c24xx_serial_resetport(port, cfg);
1252 return 0;
1253}
1254
Ben Dooksb4975492008-07-03 12:32:51 +01001255/* Device driver serial port probe */
1256
Thomas Abraham26c919e2011-11-06 22:10:44 +05301257static const struct of_device_id s3c24xx_uart_dt_match[];
Ben Dooksb4975492008-07-03 12:32:51 +01001258static int probe_index;
1259
Thomas Abraham26c919e2011-11-06 22:10:44 +05301260static inline struct s3c24xx_serial_drv_data *s3c24xx_get_driver_data(
1261 struct platform_device *pdev)
1262{
1263#ifdef CONFIG_OF
1264 if (pdev->dev.of_node) {
1265 const struct of_device_id *match;
1266 match = of_match_node(s3c24xx_uart_dt_match, pdev->dev.of_node);
1267 return (struct s3c24xx_serial_drv_data *)match->data;
1268 }
1269#endif
1270 return (struct s3c24xx_serial_drv_data *)
1271 platform_get_device_id(pdev)->driver_data;
1272}
1273
Thomas Abrahamda121502011-11-02 19:23:25 +09001274static int s3c24xx_serial_probe(struct platform_device *pdev)
Ben Dooksb4975492008-07-03 12:32:51 +01001275{
Naveen Krishna Chatradhi4622eb62014-07-14 17:07:18 +05301276 struct device_node *np = pdev->dev.of_node;
Ben Dooksb4975492008-07-03 12:32:51 +01001277 struct s3c24xx_uart_port *ourport;
Tomasz Figa13a9f6c62014-06-26 13:24:34 +02001278 int index = probe_index;
Ben Dooksb4975492008-07-03 12:32:51 +01001279 int ret;
1280
Naveen Krishna Chatradhi4622eb62014-07-14 17:07:18 +05301281 if (np) {
1282 ret = of_alias_get_id(np, "serial");
Tomasz Figa13a9f6c62014-06-26 13:24:34 +02001283 if (ret >= 0)
1284 index = ret;
1285 }
Ben Dooksb4975492008-07-03 12:32:51 +01001286
Tomasz Figa13a9f6c62014-06-26 13:24:34 +02001287 dbg("s3c24xx_serial_probe(%p) %d\n", pdev, index);
1288
1289 ourport = &s3c24xx_serial_ports[index];
Thomas Abrahamda121502011-11-02 19:23:25 +09001290
Thomas Abraham26c919e2011-11-06 22:10:44 +05301291 ourport->drv_data = s3c24xx_get_driver_data(pdev);
1292 if (!ourport->drv_data) {
1293 dev_err(&pdev->dev, "could not find driver data\n");
1294 return -ENODEV;
1295 }
Thomas Abrahamda121502011-11-02 19:23:25 +09001296
Kyoungil Kim7cd88832012-05-20 17:45:54 +09001297 ourport->baudclk = ERR_PTR(-EINVAL);
Thomas Abrahamda121502011-11-02 19:23:25 +09001298 ourport->info = ourport->drv_data->info;
Jingoo Han574de552013-07-30 17:06:57 +09001299 ourport->cfg = (dev_get_platdata(&pdev->dev)) ?
Jingoo Hand4aab202013-09-09 14:10:30 +09001300 dev_get_platdata(&pdev->dev) :
Thomas Abrahamda121502011-11-02 19:23:25 +09001301 ourport->drv_data->def_cfg;
1302
Naveen Krishna Chatradhi4622eb62014-07-14 17:07:18 +05301303 if (np)
1304 of_property_read_u32(np,
Naveen Krishna Chatradhi135f07c2014-07-14 17:07:16 +05301305 "samsung,uart-fifosize", &ourport->port.fifosize);
1306
Robert Baldyga2f1ba722014-11-24 07:56:23 +01001307 if (ourport->drv_data->fifosize[index])
1308 ourport->port.fifosize = ourport->drv_data->fifosize[index];
1309 else if (ourport->info->fifosize)
1310 ourport->port.fifosize = ourport->info->fifosize;
Thomas Abrahamda121502011-11-02 19:23:25 +09001311
Ben Dooksb4975492008-07-03 12:32:51 +01001312 probe_index++;
1313
1314 dbg("%s: initialising port %p...\n", __func__, ourport);
1315
Thomas Abrahamda121502011-11-02 19:23:25 +09001316 ret = s3c24xx_serial_init_port(ourport, pdev);
Ben Dooksb4975492008-07-03 12:32:51 +01001317 if (ret < 0)
Tushar Behera8ad711a2014-06-23 11:32:14 +05301318 return ret;
Ben Dooksb4975492008-07-03 12:32:51 +01001319
Tushar Behera6f134c3c2014-01-20 14:32:34 +05301320 if (!s3c24xx_uart_drv.state) {
1321 ret = uart_register_driver(&s3c24xx_uart_drv);
1322 if (ret < 0) {
1323 pr_err("Failed to register Samsung UART driver\n");
1324 return ret;
1325 }
1326 }
1327
Ben Dooksb4975492008-07-03 12:32:51 +01001328 dbg("%s: adding port\n", __func__);
1329 uart_add_one_port(&s3c24xx_uart_drv, &ourport->port);
Thomas Abrahamda121502011-11-02 19:23:25 +09001330 platform_set_drvdata(pdev, &ourport->port);
Ben Dooksb4975492008-07-03 12:32:51 +01001331
Heiko Stübner0da33362013-12-05 00:54:38 +01001332 /*
1333 * Deactivate the clock enabled in s3c24xx_serial_init_port here,
1334 * so that a potential re-enablement through the pm-callback overlaps
1335 * and keeps the clock enabled in this case.
1336 */
1337 clk_disable_unprepare(ourport->clk);
1338
Ben Dooks30555472008-10-21 14:06:36 +01001339 ret = s3c24xx_serial_cpufreq_register(ourport);
1340 if (ret < 0)
Thomas Abrahamda121502011-11-02 19:23:25 +09001341 dev_err(&pdev->dev, "failed to add cpufreq notifier\n");
Ben Dooks30555472008-10-21 14:06:36 +01001342
Ben Dooksb4975492008-07-03 12:32:51 +01001343 return 0;
Ben Dooksb4975492008-07-03 12:32:51 +01001344}
1345
Bill Pembertonae8d8a12012-11-19 13:26:18 -05001346static int s3c24xx_serial_remove(struct platform_device *dev)
Ben Dooksb4975492008-07-03 12:32:51 +01001347{
1348 struct uart_port *port = s3c24xx_dev_to_port(&dev->dev);
1349
1350 if (port) {
Ben Dooks30555472008-10-21 14:06:36 +01001351 s3c24xx_serial_cpufreq_deregister(to_ourport(port));
Ben Dooksb4975492008-07-03 12:32:51 +01001352 uart_remove_one_port(&s3c24xx_uart_drv, port);
1353 }
1354
Tushar Behera6f134c3c2014-01-20 14:32:34 +05301355 uart_unregister_driver(&s3c24xx_uart_drv);
1356
Ben Dooksb4975492008-07-03 12:32:51 +01001357 return 0;
1358}
1359
Ben Dooksb4975492008-07-03 12:32:51 +01001360/* UART power management code */
MyungJoo Hamaef7fe52011-06-29 15:28:24 +09001361#ifdef CONFIG_PM_SLEEP
1362static int s3c24xx_serial_suspend(struct device *dev)
Ben Dooksb4975492008-07-03 12:32:51 +01001363{
MyungJoo Hamaef7fe52011-06-29 15:28:24 +09001364 struct uart_port *port = s3c24xx_dev_to_port(dev);
Ben Dooksb4975492008-07-03 12:32:51 +01001365
1366 if (port)
1367 uart_suspend_port(&s3c24xx_uart_drv, port);
1368
1369 return 0;
1370}
1371
MyungJoo Hamaef7fe52011-06-29 15:28:24 +09001372static int s3c24xx_serial_resume(struct device *dev)
Ben Dooksb4975492008-07-03 12:32:51 +01001373{
MyungJoo Hamaef7fe52011-06-29 15:28:24 +09001374 struct uart_port *port = s3c24xx_dev_to_port(dev);
Ben Dooksb4975492008-07-03 12:32:51 +01001375 struct s3c24xx_uart_port *ourport = to_ourport(port);
1376
1377 if (port) {
Thomas Abraham9484b002012-10-03 07:40:04 +09001378 clk_prepare_enable(ourport->clk);
Ben Dooksb4975492008-07-03 12:32:51 +01001379 s3c24xx_serial_resetport(port, s3c24xx_port_to_cfg(port));
Thomas Abraham9484b002012-10-03 07:40:04 +09001380 clk_disable_unprepare(ourport->clk);
Ben Dooksb4975492008-07-03 12:32:51 +01001381
1382 uart_resume_port(&s3c24xx_uart_drv, port);
1383 }
1384
1385 return 0;
1386}
MyungJoo Hamaef7fe52011-06-29 15:28:24 +09001387
Michael Spangd09a7302013-03-27 19:34:24 -04001388static int s3c24xx_serial_resume_noirq(struct device *dev)
1389{
1390 struct uart_port *port = s3c24xx_dev_to_port(dev);
1391
1392 if (port) {
1393 /* restore IRQ mask */
1394 if (s3c24xx_serial_has_interrupt_mask(port)) {
1395 unsigned int uintm = 0xf;
1396 if (tx_enabled(port))
1397 uintm &= ~S3C64XX_UINTM_TXD_MSK;
1398 if (rx_enabled(port))
1399 uintm &= ~S3C64XX_UINTM_RXD_MSK;
1400 wr_regl(port, S3C64XX_UINTM, uintm);
1401 }
1402 }
1403
1404 return 0;
1405}
1406
MyungJoo Hamaef7fe52011-06-29 15:28:24 +09001407static const struct dev_pm_ops s3c24xx_serial_pm_ops = {
1408 .suspend = s3c24xx_serial_suspend,
1409 .resume = s3c24xx_serial_resume,
Michael Spangd09a7302013-03-27 19:34:24 -04001410 .resume_noirq = s3c24xx_serial_resume_noirq,
MyungJoo Hamaef7fe52011-06-29 15:28:24 +09001411};
Kukjin Kimb882fc12011-07-28 08:50:38 +09001412#define SERIAL_SAMSUNG_PM_OPS (&s3c24xx_serial_pm_ops)
1413
MyungJoo Hamaef7fe52011-06-29 15:28:24 +09001414#else /* !CONFIG_PM_SLEEP */
Kukjin Kimb882fc12011-07-28 08:50:38 +09001415
1416#define SERIAL_SAMSUNG_PM_OPS NULL
MyungJoo Hamaef7fe52011-06-29 15:28:24 +09001417#endif /* CONFIG_PM_SLEEP */
Ben Dooksb4975492008-07-03 12:32:51 +01001418
Ben Dooksb4975492008-07-03 12:32:51 +01001419/* Console code */
1420
1421#ifdef CONFIG_SERIAL_SAMSUNG_CONSOLE
1422
1423static struct uart_port *cons_uart;
1424
1425static int
1426s3c24xx_serial_console_txrdy(struct uart_port *port, unsigned int ufcon)
1427{
1428 struct s3c24xx_uart_info *info = s3c24xx_port_to_info(port);
1429 unsigned long ufstat, utrstat;
1430
1431 if (ufcon & S3C2410_UFCON_FIFOMODE) {
Uwe Kleine-König9ddc5b62010-01-20 17:02:24 +01001432 /* fifo mode - check amount of data in fifo registers... */
Ben Dooksb4975492008-07-03 12:32:51 +01001433
1434 ufstat = rd_regl(port, S3C2410_UFSTAT);
1435 return (ufstat & info->tx_fifofull) ? 0 : 1;
1436 }
1437
1438 /* in non-fifo mode, we go and use the tx buffer empty */
1439
1440 utrstat = rd_regl(port, S3C2410_UTRSTAT);
1441 return (utrstat & S3C2410_UTRSTAT_TXE) ? 1 : 0;
1442}
1443
Michael Spang38adbc52013-03-27 19:34:25 -04001444static bool
1445s3c24xx_port_configured(unsigned int ucon)
1446{
1447 /* consider the serial port configured if the tx/rx mode set */
1448 return (ucon & 0xf) != 0;
1449}
1450
Julien Pichon93b5c032012-09-21 23:22:31 -07001451#ifdef CONFIG_CONSOLE_POLL
1452/*
1453 * Console polling routines for writing and reading from the uart while
1454 * in an interrupt or debug context.
1455 */
1456
1457static int s3c24xx_serial_get_poll_char(struct uart_port *port)
1458{
1459 struct s3c24xx_uart_port *ourport = to_ourport(port);
1460 unsigned int ufstat;
1461
1462 ufstat = rd_regl(port, S3C2410_UFSTAT);
1463 if (s3c24xx_serial_rx_fifocnt(ourport, ufstat) == 0)
1464 return NO_POLL_CHAR;
1465
1466 return rd_regb(port, S3C2410_URXH);
1467}
1468
1469static void s3c24xx_serial_put_poll_char(struct uart_port *port,
1470 unsigned char c)
1471{
Doug Andersonbb7f09b2014-04-21 09:40:34 -07001472 unsigned int ufcon = rd_regl(port, S3C2410_UFCON);
1473 unsigned int ucon = rd_regl(port, S3C2410_UCON);
Michael Spang38adbc52013-03-27 19:34:25 -04001474
1475 /* not possible to xmit on unconfigured port */
1476 if (!s3c24xx_port_configured(ucon))
1477 return;
Julien Pichon93b5c032012-09-21 23:22:31 -07001478
1479 while (!s3c24xx_serial_console_txrdy(port, ufcon))
1480 cpu_relax();
Doug Andersonbb7f09b2014-04-21 09:40:34 -07001481 wr_regb(port, S3C2410_UTXH, c);
Julien Pichon93b5c032012-09-21 23:22:31 -07001482}
1483
1484#endif /* CONFIG_CONSOLE_POLL */
1485
Ben Dooksb4975492008-07-03 12:32:51 +01001486static void
1487s3c24xx_serial_console_putchar(struct uart_port *port, int ch)
1488{
Doug Andersonbb7f09b2014-04-21 09:40:34 -07001489 unsigned int ufcon = rd_regl(port, S3C2410_UFCON);
Michael Spang38adbc52013-03-27 19:34:25 -04001490
Ben Dooksb4975492008-07-03 12:32:51 +01001491 while (!s3c24xx_serial_console_txrdy(port, ufcon))
Doug Andersonf94b0572014-04-21 09:40:36 -07001492 cpu_relax();
Doug Andersonbb7f09b2014-04-21 09:40:34 -07001493 wr_regb(port, S3C2410_UTXH, ch);
Ben Dooksb4975492008-07-03 12:32:51 +01001494}
1495
1496static void
1497s3c24xx_serial_console_write(struct console *co, const char *s,
1498 unsigned int count)
1499{
Doug Andersonab88c8d2014-04-21 09:40:35 -07001500 unsigned int ucon = rd_regl(cons_uart, S3C2410_UCON);
1501
1502 /* not possible to xmit on unconfigured port */
1503 if (!s3c24xx_port_configured(ucon))
1504 return;
1505
Ben Dooksb4975492008-07-03 12:32:51 +01001506 uart_console_write(cons_uart, s, count, s3c24xx_serial_console_putchar);
1507}
1508
1509static void __init
1510s3c24xx_serial_get_options(struct uart_port *port, int *baud,
1511 int *parity, int *bits)
1512{
Ben Dooksb4975492008-07-03 12:32:51 +01001513 struct clk *clk;
1514 unsigned int ulcon;
1515 unsigned int ucon;
1516 unsigned int ubrdiv;
1517 unsigned long rate;
Thomas Abraham5f5a7a52011-10-24 11:47:46 +02001518 unsigned int clk_sel;
1519 char clk_name[MAX_CLK_NAME_LENGTH];
Ben Dooksb4975492008-07-03 12:32:51 +01001520
1521 ulcon = rd_regl(port, S3C2410_ULCON);
1522 ucon = rd_regl(port, S3C2410_UCON);
1523 ubrdiv = rd_regl(port, S3C2410_UBRDIV);
1524
1525 dbg("s3c24xx_serial_get_options: port=%p\n"
1526 "registers: ulcon=%08x, ucon=%08x, ubdriv=%08x\n",
1527 port, ulcon, ucon, ubrdiv);
1528
Michael Spang38adbc52013-03-27 19:34:25 -04001529 if (s3c24xx_port_configured(ucon)) {
Ben Dooksb4975492008-07-03 12:32:51 +01001530 switch (ulcon & S3C2410_LCON_CSMASK) {
1531 case S3C2410_LCON_CS5:
1532 *bits = 5;
1533 break;
1534 case S3C2410_LCON_CS6:
1535 *bits = 6;
1536 break;
1537 case S3C2410_LCON_CS7:
1538 *bits = 7;
1539 break;
Ben Dooksb4975492008-07-03 12:32:51 +01001540 case S3C2410_LCON_CS8:
Naveen Krishna Chatradhi3bcce592014-07-14 17:07:17 +05301541 default:
Ben Dooksb4975492008-07-03 12:32:51 +01001542 *bits = 8;
1543 break;
1544 }
1545
1546 switch (ulcon & S3C2410_LCON_PMASK) {
1547 case S3C2410_LCON_PEVEN:
1548 *parity = 'e';
1549 break;
1550
1551 case S3C2410_LCON_PODD:
1552 *parity = 'o';
1553 break;
1554
1555 case S3C2410_LCON_PNONE:
1556 default:
1557 *parity = 'n';
1558 }
1559
1560 /* now calculate the baud rate */
1561
Thomas Abraham5f5a7a52011-10-24 11:47:46 +02001562 clk_sel = s3c24xx_serial_getsource(port);
1563 sprintf(clk_name, "clk_uart_baud%d", clk_sel);
Ben Dooksb4975492008-07-03 12:32:51 +01001564
Thomas Abraham5f5a7a52011-10-24 11:47:46 +02001565 clk = clk_get(port->dev, clk_name);
Kyoungil Kim7cd88832012-05-20 17:45:54 +09001566 if (!IS_ERR(clk))
Thomas Abraham5f5a7a52011-10-24 11:47:46 +02001567 rate = clk_get_rate(clk);
Ben Dooksb4975492008-07-03 12:32:51 +01001568 else
1569 rate = 1;
1570
Ben Dooksb4975492008-07-03 12:32:51 +01001571 *baud = rate / (16 * (ubrdiv + 1));
1572 dbg("calculated baud %d\n", *baud);
1573 }
1574
1575}
1576
Ben Dooksb4975492008-07-03 12:32:51 +01001577static int __init
1578s3c24xx_serial_console_setup(struct console *co, char *options)
1579{
1580 struct uart_port *port;
1581 int baud = 9600;
1582 int bits = 8;
1583 int parity = 'n';
1584 int flow = 'n';
1585
1586 dbg("s3c24xx_serial_console_setup: co=%p (%d), %s\n",
1587 co, co->index, options);
1588
1589 /* is this a valid port */
1590
Ben Dooks03d5e772008-11-03 09:21:23 +00001591 if (co->index == -1 || co->index >= CONFIG_SERIAL_SAMSUNG_UARTS)
Ben Dooksb4975492008-07-03 12:32:51 +01001592 co->index = 0;
1593
1594 port = &s3c24xx_serial_ports[co->index].port;
1595
1596 /* is the port configured? */
1597
Thomas Abrahamee430f12011-06-14 19:12:26 +09001598 if (port->mapbase == 0x0)
1599 return -ENODEV;
Ben Dooksb4975492008-07-03 12:32:51 +01001600
1601 cons_uart = port;
1602
1603 dbg("s3c24xx_serial_console_setup: port=%p (%d)\n", port, co->index);
1604
1605 /*
1606 * Check whether an invalid uart number has been specified, and
1607 * if so, search for the first available port that does have
1608 * console support.
1609 */
1610 if (options)
1611 uart_parse_options(options, &baud, &parity, &bits, &flow);
1612 else
1613 s3c24xx_serial_get_options(port, &baud, &parity, &bits);
1614
1615 dbg("s3c24xx_serial_console_setup: baud %d\n", baud);
1616
1617 return uart_set_options(port, co, baud, parity, bits, flow);
1618}
1619
Ben Dooksb4975492008-07-03 12:32:51 +01001620static struct console s3c24xx_serial_console = {
1621 .name = S3C24XX_SERIAL_NAME,
1622 .device = uart_console_device,
1623 .flags = CON_PRINTBUFFER,
1624 .index = -1,
1625 .write = s3c24xx_serial_console_write,
Thomas Abraham5822a5d2011-06-14 19:12:26 +09001626 .setup = s3c24xx_serial_console_setup,
1627 .data = &s3c24xx_uart_drv,
Ben Dooksb4975492008-07-03 12:32:51 +01001628};
Ben Dooksb4975492008-07-03 12:32:51 +01001629#endif /* CONFIG_SERIAL_SAMSUNG_CONSOLE */
1630
Thomas Abrahamda121502011-11-02 19:23:25 +09001631#ifdef CONFIG_CPU_S3C2410
1632static struct s3c24xx_serial_drv_data s3c2410_serial_drv_data = {
1633 .info = &(struct s3c24xx_uart_info) {
1634 .name = "Samsung S3C2410 UART",
1635 .type = PORT_S3C2410,
1636 .fifosize = 16,
1637 .rx_fifomask = S3C2410_UFSTAT_RXMASK,
1638 .rx_fifoshift = S3C2410_UFSTAT_RXSHIFT,
1639 .rx_fifofull = S3C2410_UFSTAT_RXFULL,
1640 .tx_fifofull = S3C2410_UFSTAT_TXFULL,
1641 .tx_fifomask = S3C2410_UFSTAT_TXMASK,
1642 .tx_fifoshift = S3C2410_UFSTAT_TXSHIFT,
1643 .def_clk_sel = S3C2410_UCON_CLKSEL0,
1644 .num_clks = 2,
1645 .clksel_mask = S3C2410_UCON_CLKMASK,
1646 .clksel_shift = S3C2410_UCON_CLKSHIFT,
1647 },
1648 .def_cfg = &(struct s3c2410_uartcfg) {
1649 .ucon = S3C2410_UCON_DEFAULT,
1650 .ufcon = S3C2410_UFCON_DEFAULT,
1651 },
1652};
1653#define S3C2410_SERIAL_DRV_DATA ((kernel_ulong_t)&s3c2410_serial_drv_data)
1654#else
1655#define S3C2410_SERIAL_DRV_DATA (kernel_ulong_t)NULL
1656#endif
1657
1658#ifdef CONFIG_CPU_S3C2412
1659static struct s3c24xx_serial_drv_data s3c2412_serial_drv_data = {
1660 .info = &(struct s3c24xx_uart_info) {
1661 .name = "Samsung S3C2412 UART",
1662 .type = PORT_S3C2412,
1663 .fifosize = 64,
1664 .has_divslot = 1,
1665 .rx_fifomask = S3C2440_UFSTAT_RXMASK,
1666 .rx_fifoshift = S3C2440_UFSTAT_RXSHIFT,
1667 .rx_fifofull = S3C2440_UFSTAT_RXFULL,
1668 .tx_fifofull = S3C2440_UFSTAT_TXFULL,
1669 .tx_fifomask = S3C2440_UFSTAT_TXMASK,
1670 .tx_fifoshift = S3C2440_UFSTAT_TXSHIFT,
1671 .def_clk_sel = S3C2410_UCON_CLKSEL2,
1672 .num_clks = 4,
1673 .clksel_mask = S3C2412_UCON_CLKMASK,
1674 .clksel_shift = S3C2412_UCON_CLKSHIFT,
1675 },
1676 .def_cfg = &(struct s3c2410_uartcfg) {
1677 .ucon = S3C2410_UCON_DEFAULT,
1678 .ufcon = S3C2410_UFCON_DEFAULT,
1679 },
1680};
1681#define S3C2412_SERIAL_DRV_DATA ((kernel_ulong_t)&s3c2412_serial_drv_data)
1682#else
1683#define S3C2412_SERIAL_DRV_DATA (kernel_ulong_t)NULL
1684#endif
1685
1686#if defined(CONFIG_CPU_S3C2440) || defined(CONFIG_CPU_S3C2416) || \
Denis 'GNUtoo' Cariklib26469a2012-02-23 08:23:52 +01001687 defined(CONFIG_CPU_S3C2443) || defined(CONFIG_CPU_S3C2442)
Thomas Abrahamda121502011-11-02 19:23:25 +09001688static struct s3c24xx_serial_drv_data s3c2440_serial_drv_data = {
1689 .info = &(struct s3c24xx_uart_info) {
1690 .name = "Samsung S3C2440 UART",
1691 .type = PORT_S3C2440,
1692 .fifosize = 64,
1693 .has_divslot = 1,
1694 .rx_fifomask = S3C2440_UFSTAT_RXMASK,
1695 .rx_fifoshift = S3C2440_UFSTAT_RXSHIFT,
1696 .rx_fifofull = S3C2440_UFSTAT_RXFULL,
1697 .tx_fifofull = S3C2440_UFSTAT_TXFULL,
1698 .tx_fifomask = S3C2440_UFSTAT_TXMASK,
1699 .tx_fifoshift = S3C2440_UFSTAT_TXSHIFT,
1700 .def_clk_sel = S3C2410_UCON_CLKSEL2,
1701 .num_clks = 4,
1702 .clksel_mask = S3C2412_UCON_CLKMASK,
1703 .clksel_shift = S3C2412_UCON_CLKSHIFT,
1704 },
1705 .def_cfg = &(struct s3c2410_uartcfg) {
1706 .ucon = S3C2410_UCON_DEFAULT,
1707 .ufcon = S3C2410_UFCON_DEFAULT,
1708 },
1709};
1710#define S3C2440_SERIAL_DRV_DATA ((kernel_ulong_t)&s3c2440_serial_drv_data)
1711#else
1712#define S3C2440_SERIAL_DRV_DATA (kernel_ulong_t)NULL
1713#endif
1714
Kukjin Kim953b53a2014-07-01 06:32:22 +09001715#if defined(CONFIG_CPU_S3C6400) || defined(CONFIG_CPU_S3C6410)
Thomas Abrahamda121502011-11-02 19:23:25 +09001716static struct s3c24xx_serial_drv_data s3c6400_serial_drv_data = {
1717 .info = &(struct s3c24xx_uart_info) {
1718 .name = "Samsung S3C6400 UART",
1719 .type = PORT_S3C6400,
1720 .fifosize = 64,
1721 .has_divslot = 1,
1722 .rx_fifomask = S3C2440_UFSTAT_RXMASK,
1723 .rx_fifoshift = S3C2440_UFSTAT_RXSHIFT,
1724 .rx_fifofull = S3C2440_UFSTAT_RXFULL,
1725 .tx_fifofull = S3C2440_UFSTAT_TXFULL,
1726 .tx_fifomask = S3C2440_UFSTAT_TXMASK,
1727 .tx_fifoshift = S3C2440_UFSTAT_TXSHIFT,
1728 .def_clk_sel = S3C2410_UCON_CLKSEL2,
1729 .num_clks = 4,
1730 .clksel_mask = S3C6400_UCON_CLKMASK,
1731 .clksel_shift = S3C6400_UCON_CLKSHIFT,
1732 },
1733 .def_cfg = &(struct s3c2410_uartcfg) {
1734 .ucon = S3C2410_UCON_DEFAULT,
1735 .ufcon = S3C2410_UFCON_DEFAULT,
1736 },
1737};
1738#define S3C6400_SERIAL_DRV_DATA ((kernel_ulong_t)&s3c6400_serial_drv_data)
1739#else
1740#define S3C6400_SERIAL_DRV_DATA (kernel_ulong_t)NULL
1741#endif
1742
1743#ifdef CONFIG_CPU_S5PV210
1744static struct s3c24xx_serial_drv_data s5pv210_serial_drv_data = {
1745 .info = &(struct s3c24xx_uart_info) {
1746 .name = "Samsung S5PV210 UART",
1747 .type = PORT_S3C6400,
1748 .has_divslot = 1,
1749 .rx_fifomask = S5PV210_UFSTAT_RXMASK,
1750 .rx_fifoshift = S5PV210_UFSTAT_RXSHIFT,
1751 .rx_fifofull = S5PV210_UFSTAT_RXFULL,
1752 .tx_fifofull = S5PV210_UFSTAT_TXFULL,
1753 .tx_fifomask = S5PV210_UFSTAT_TXMASK,
1754 .tx_fifoshift = S5PV210_UFSTAT_TXSHIFT,
1755 .def_clk_sel = S3C2410_UCON_CLKSEL0,
1756 .num_clks = 2,
1757 .clksel_mask = S5PV210_UCON_CLKMASK,
1758 .clksel_shift = S5PV210_UCON_CLKSHIFT,
1759 },
1760 .def_cfg = &(struct s3c2410_uartcfg) {
1761 .ucon = S5PV210_UCON_DEFAULT,
1762 .ufcon = S5PV210_UFCON_DEFAULT,
1763 },
1764 .fifosize = { 256, 64, 16, 16 },
1765};
1766#define S5PV210_SERIAL_DRV_DATA ((kernel_ulong_t)&s5pv210_serial_drv_data)
1767#else
1768#define S5PV210_SERIAL_DRV_DATA (kernel_ulong_t)NULL
1769#endif
1770
Chander Kashyap33f88132013-06-19 00:29:34 +09001771#if defined(CONFIG_ARCH_EXYNOS)
Thomas Abrahamda121502011-11-02 19:23:25 +09001772static struct s3c24xx_serial_drv_data exynos4210_serial_drv_data = {
1773 .info = &(struct s3c24xx_uart_info) {
1774 .name = "Samsung Exynos4 UART",
1775 .type = PORT_S3C6400,
1776 .has_divslot = 1,
1777 .rx_fifomask = S5PV210_UFSTAT_RXMASK,
1778 .rx_fifoshift = S5PV210_UFSTAT_RXSHIFT,
1779 .rx_fifofull = S5PV210_UFSTAT_RXFULL,
1780 .tx_fifofull = S5PV210_UFSTAT_TXFULL,
1781 .tx_fifomask = S5PV210_UFSTAT_TXMASK,
1782 .tx_fifoshift = S5PV210_UFSTAT_TXSHIFT,
1783 .def_clk_sel = S3C2410_UCON_CLKSEL0,
1784 .num_clks = 1,
1785 .clksel_mask = 0,
1786 .clksel_shift = 0,
1787 },
1788 .def_cfg = &(struct s3c2410_uartcfg) {
1789 .ucon = S5PV210_UCON_DEFAULT,
1790 .ufcon = S5PV210_UFCON_DEFAULT,
1791 .has_fracval = 1,
1792 },
1793 .fifosize = { 256, 64, 16, 16 },
1794};
1795#define EXYNOS4210_SERIAL_DRV_DATA ((kernel_ulong_t)&exynos4210_serial_drv_data)
1796#else
1797#define EXYNOS4210_SERIAL_DRV_DATA (kernel_ulong_t)NULL
1798#endif
1799
1800static struct platform_device_id s3c24xx_serial_driver_ids[] = {
1801 {
1802 .name = "s3c2410-uart",
1803 .driver_data = S3C2410_SERIAL_DRV_DATA,
1804 }, {
1805 .name = "s3c2412-uart",
1806 .driver_data = S3C2412_SERIAL_DRV_DATA,
1807 }, {
1808 .name = "s3c2440-uart",
1809 .driver_data = S3C2440_SERIAL_DRV_DATA,
1810 }, {
1811 .name = "s3c6400-uart",
1812 .driver_data = S3C6400_SERIAL_DRV_DATA,
1813 }, {
1814 .name = "s5pv210-uart",
1815 .driver_data = S5PV210_SERIAL_DRV_DATA,
1816 }, {
1817 .name = "exynos4210-uart",
1818 .driver_data = EXYNOS4210_SERIAL_DRV_DATA,
1819 },
1820 { },
1821};
1822MODULE_DEVICE_TABLE(platform, s3c24xx_serial_driver_ids);
1823
Thomas Abraham26c919e2011-11-06 22:10:44 +05301824#ifdef CONFIG_OF
1825static const struct of_device_id s3c24xx_uart_dt_match[] = {
Heiko Stübner666ca0b2012-11-22 11:37:44 +01001826 { .compatible = "samsung,s3c2410-uart",
1827 .data = (void *)S3C2410_SERIAL_DRV_DATA },
1828 { .compatible = "samsung,s3c2412-uart",
1829 .data = (void *)S3C2412_SERIAL_DRV_DATA },
1830 { .compatible = "samsung,s3c2440-uart",
1831 .data = (void *)S3C2440_SERIAL_DRV_DATA },
1832 { .compatible = "samsung,s3c6400-uart",
1833 .data = (void *)S3C6400_SERIAL_DRV_DATA },
1834 { .compatible = "samsung,s5pv210-uart",
1835 .data = (void *)S5PV210_SERIAL_DRV_DATA },
Thomas Abraham26c919e2011-11-06 22:10:44 +05301836 { .compatible = "samsung,exynos4210-uart",
Mark Browna169a882011-11-08 17:00:14 +09001837 .data = (void *)EXYNOS4210_SERIAL_DRV_DATA },
Thomas Abraham26c919e2011-11-06 22:10:44 +05301838 {},
1839};
1840MODULE_DEVICE_TABLE(of, s3c24xx_uart_dt_match);
Thomas Abraham26c919e2011-11-06 22:10:44 +05301841#endif
1842
Thomas Abrahamda121502011-11-02 19:23:25 +09001843static struct platform_driver samsung_serial_driver = {
1844 .probe = s3c24xx_serial_probe,
Bill Pemberton2d47b712012-11-19 13:21:34 -05001845 .remove = s3c24xx_serial_remove,
Thomas Abrahamda121502011-11-02 19:23:25 +09001846 .id_table = s3c24xx_serial_driver_ids,
1847 .driver = {
1848 .name = "samsung-uart",
Thomas Abrahamda121502011-11-02 19:23:25 +09001849 .pm = SERIAL_SAMSUNG_PM_OPS,
Sachin Kamat905f4ba2013-01-07 09:50:42 +05301850 .of_match_table = of_match_ptr(s3c24xx_uart_dt_match),
Thomas Abrahamda121502011-11-02 19:23:25 +09001851 },
1852};
1853
Tushar Behera6f134c3c2014-01-20 14:32:34 +05301854module_platform_driver(samsung_serial_driver);
Thomas Abrahamda121502011-11-02 19:23:25 +09001855
1856MODULE_ALIAS("platform:samsung-uart");
Ben Dooksb4975492008-07-03 12:32:51 +01001857MODULE_DESCRIPTION("Samsung SoC Serial port driver");
1858MODULE_AUTHOR("Ben Dooks <ben@simtec.co.uk>");
1859MODULE_LICENSE("GPL v2");