blob: 34db16715efc90e4cb760b27d198ca9c6e74d3a1 [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;
Ben Dooksb4975492008-07-03 12:32:51 +0100242 int max_count = 64;
243
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;
Ben Dooksb4975492008-07-03 12:32:51 +0100333 int count = 256;
334
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;
Ben Dooksb4975492008-07-03 12:32:51 +01001212
1213 ourport->clk = clk_get(&platdev->dev, "uart");
Chander Kashyap60e93572013-05-28 18:32:07 +05301214 if (IS_ERR(ourport->clk)) {
1215 pr_err("%s: Controller clock not found\n",
1216 dev_name(&platdev->dev));
1217 return PTR_ERR(ourport->clk);
1218 }
1219
1220 ret = clk_prepare_enable(ourport->clk);
1221 if (ret) {
1222 pr_err("uart: clock failed to prepare+enable: %d\n", ret);
1223 clk_put(ourport->clk);
1224 return ret;
1225 }
Ben Dooksb4975492008-07-03 12:32:51 +01001226
Thomas Abraham88bb4ea2011-08-10 15:51:19 +05301227 /* Keep all interrupts masked and cleared */
1228 if (s3c24xx_serial_has_interrupt_mask(port)) {
1229 wr_regl(port, S3C64XX_UINTM, 0xf);
1230 wr_regl(port, S3C64XX_UINTP, 0xf);
1231 wr_regl(port, S3C64XX_UINTSP, 0xf);
1232 }
1233
Fabio Estevam1ff5b642014-06-04 20:06:41 -03001234 dbg("port: map=%pa, mem=%p, irq=%d (%d,%d), clock=%u\n",
1235 &port->mapbase, port->membase, port->irq,
Ben Dooksb73c289c2008-10-21 14:07:04 +01001236 ourport->rx_irq, ourport->tx_irq, port->uartclk);
Ben Dooksb4975492008-07-03 12:32:51 +01001237
1238 /* reset the fifos (and setup the uart) */
1239 s3c24xx_serial_resetport(port, cfg);
1240 return 0;
1241}
1242
Ben Dooksb4975492008-07-03 12:32:51 +01001243/* Device driver serial port probe */
1244
Thomas Abraham26c919e2011-11-06 22:10:44 +05301245static const struct of_device_id s3c24xx_uart_dt_match[];
Ben Dooksb4975492008-07-03 12:32:51 +01001246static int probe_index;
1247
Thomas Abraham26c919e2011-11-06 22:10:44 +05301248static inline struct s3c24xx_serial_drv_data *s3c24xx_get_driver_data(
1249 struct platform_device *pdev)
1250{
1251#ifdef CONFIG_OF
1252 if (pdev->dev.of_node) {
1253 const struct of_device_id *match;
1254 match = of_match_node(s3c24xx_uart_dt_match, pdev->dev.of_node);
1255 return (struct s3c24xx_serial_drv_data *)match->data;
1256 }
1257#endif
1258 return (struct s3c24xx_serial_drv_data *)
1259 platform_get_device_id(pdev)->driver_data;
1260}
1261
Thomas Abrahamda121502011-11-02 19:23:25 +09001262static int s3c24xx_serial_probe(struct platform_device *pdev)
Ben Dooksb4975492008-07-03 12:32:51 +01001263{
Naveen Krishna Chatradhi4622eb62014-07-14 17:07:18 +05301264 struct device_node *np = pdev->dev.of_node;
Ben Dooksb4975492008-07-03 12:32:51 +01001265 struct s3c24xx_uart_port *ourport;
Tomasz Figa13a9f6c62014-06-26 13:24:34 +02001266 int index = probe_index;
Ben Dooksb4975492008-07-03 12:32:51 +01001267 int ret;
1268
Naveen Krishna Chatradhi4622eb62014-07-14 17:07:18 +05301269 if (np) {
1270 ret = of_alias_get_id(np, "serial");
Tomasz Figa13a9f6c62014-06-26 13:24:34 +02001271 if (ret >= 0)
1272 index = ret;
1273 }
Ben Dooksb4975492008-07-03 12:32:51 +01001274
Tomasz Figa13a9f6c62014-06-26 13:24:34 +02001275 dbg("s3c24xx_serial_probe(%p) %d\n", pdev, index);
1276
1277 ourport = &s3c24xx_serial_ports[index];
Thomas Abrahamda121502011-11-02 19:23:25 +09001278
Thomas Abraham26c919e2011-11-06 22:10:44 +05301279 ourport->drv_data = s3c24xx_get_driver_data(pdev);
1280 if (!ourport->drv_data) {
1281 dev_err(&pdev->dev, "could not find driver data\n");
1282 return -ENODEV;
1283 }
Thomas Abrahamda121502011-11-02 19:23:25 +09001284
Kyoungil Kim7cd88832012-05-20 17:45:54 +09001285 ourport->baudclk = ERR_PTR(-EINVAL);
Thomas Abrahamda121502011-11-02 19:23:25 +09001286 ourport->info = ourport->drv_data->info;
Jingoo Han574de552013-07-30 17:06:57 +09001287 ourport->cfg = (dev_get_platdata(&pdev->dev)) ?
Jingoo Hand4aab202013-09-09 14:10:30 +09001288 dev_get_platdata(&pdev->dev) :
Thomas Abrahamda121502011-11-02 19:23:25 +09001289 ourport->drv_data->def_cfg;
1290
Naveen Krishna Chatradhi4622eb62014-07-14 17:07:18 +05301291 if (np)
1292 of_property_read_u32(np,
Naveen Krishna Chatradhi135f07c2014-07-14 17:07:16 +05301293 "samsung,uart-fifosize", &ourport->port.fifosize);
1294
Robert Baldyga2f1ba722014-11-24 07:56:23 +01001295 if (ourport->drv_data->fifosize[index])
1296 ourport->port.fifosize = ourport->drv_data->fifosize[index];
1297 else if (ourport->info->fifosize)
1298 ourport->port.fifosize = ourport->info->fifosize;
Thomas Abrahamda121502011-11-02 19:23:25 +09001299
Ben Dooksb4975492008-07-03 12:32:51 +01001300 probe_index++;
1301
1302 dbg("%s: initialising port %p...\n", __func__, ourport);
1303
Thomas Abrahamda121502011-11-02 19:23:25 +09001304 ret = s3c24xx_serial_init_port(ourport, pdev);
Ben Dooksb4975492008-07-03 12:32:51 +01001305 if (ret < 0)
Tushar Behera8ad711a2014-06-23 11:32:14 +05301306 return ret;
Ben Dooksb4975492008-07-03 12:32:51 +01001307
Tushar Behera6f134c3c2014-01-20 14:32:34 +05301308 if (!s3c24xx_uart_drv.state) {
1309 ret = uart_register_driver(&s3c24xx_uart_drv);
1310 if (ret < 0) {
1311 pr_err("Failed to register Samsung UART driver\n");
1312 return ret;
1313 }
1314 }
1315
Ben Dooksb4975492008-07-03 12:32:51 +01001316 dbg("%s: adding port\n", __func__);
1317 uart_add_one_port(&s3c24xx_uart_drv, &ourport->port);
Thomas Abrahamda121502011-11-02 19:23:25 +09001318 platform_set_drvdata(pdev, &ourport->port);
Ben Dooksb4975492008-07-03 12:32:51 +01001319
Heiko Stübner0da33362013-12-05 00:54:38 +01001320 /*
1321 * Deactivate the clock enabled in s3c24xx_serial_init_port here,
1322 * so that a potential re-enablement through the pm-callback overlaps
1323 * and keeps the clock enabled in this case.
1324 */
1325 clk_disable_unprepare(ourport->clk);
1326
Ben Dooks30555472008-10-21 14:06:36 +01001327 ret = s3c24xx_serial_cpufreq_register(ourport);
1328 if (ret < 0)
Thomas Abrahamda121502011-11-02 19:23:25 +09001329 dev_err(&pdev->dev, "failed to add cpufreq notifier\n");
Ben Dooks30555472008-10-21 14:06:36 +01001330
Ben Dooksb4975492008-07-03 12:32:51 +01001331 return 0;
Ben Dooksb4975492008-07-03 12:32:51 +01001332}
1333
Bill Pembertonae8d8a12012-11-19 13:26:18 -05001334static int s3c24xx_serial_remove(struct platform_device *dev)
Ben Dooksb4975492008-07-03 12:32:51 +01001335{
1336 struct uart_port *port = s3c24xx_dev_to_port(&dev->dev);
1337
1338 if (port) {
Ben Dooks30555472008-10-21 14:06:36 +01001339 s3c24xx_serial_cpufreq_deregister(to_ourport(port));
Ben Dooksb4975492008-07-03 12:32:51 +01001340 uart_remove_one_port(&s3c24xx_uart_drv, port);
1341 }
1342
Tushar Behera6f134c3c2014-01-20 14:32:34 +05301343 uart_unregister_driver(&s3c24xx_uart_drv);
1344
Ben Dooksb4975492008-07-03 12:32:51 +01001345 return 0;
1346}
1347
Ben Dooksb4975492008-07-03 12:32:51 +01001348/* UART power management code */
MyungJoo Hamaef7fe52011-06-29 15:28:24 +09001349#ifdef CONFIG_PM_SLEEP
1350static int s3c24xx_serial_suspend(struct device *dev)
Ben Dooksb4975492008-07-03 12:32:51 +01001351{
MyungJoo Hamaef7fe52011-06-29 15:28:24 +09001352 struct uart_port *port = s3c24xx_dev_to_port(dev);
Ben Dooksb4975492008-07-03 12:32:51 +01001353
1354 if (port)
1355 uart_suspend_port(&s3c24xx_uart_drv, port);
1356
1357 return 0;
1358}
1359
MyungJoo Hamaef7fe52011-06-29 15:28:24 +09001360static int s3c24xx_serial_resume(struct device *dev)
Ben Dooksb4975492008-07-03 12:32:51 +01001361{
MyungJoo Hamaef7fe52011-06-29 15:28:24 +09001362 struct uart_port *port = s3c24xx_dev_to_port(dev);
Ben Dooksb4975492008-07-03 12:32:51 +01001363 struct s3c24xx_uart_port *ourport = to_ourport(port);
1364
1365 if (port) {
Thomas Abraham9484b002012-10-03 07:40:04 +09001366 clk_prepare_enable(ourport->clk);
Ben Dooksb4975492008-07-03 12:32:51 +01001367 s3c24xx_serial_resetport(port, s3c24xx_port_to_cfg(port));
Thomas Abraham9484b002012-10-03 07:40:04 +09001368 clk_disable_unprepare(ourport->clk);
Ben Dooksb4975492008-07-03 12:32:51 +01001369
1370 uart_resume_port(&s3c24xx_uart_drv, port);
1371 }
1372
1373 return 0;
1374}
MyungJoo Hamaef7fe52011-06-29 15:28:24 +09001375
Michael Spangd09a7302013-03-27 19:34:24 -04001376static int s3c24xx_serial_resume_noirq(struct device *dev)
1377{
1378 struct uart_port *port = s3c24xx_dev_to_port(dev);
1379
1380 if (port) {
1381 /* restore IRQ mask */
1382 if (s3c24xx_serial_has_interrupt_mask(port)) {
1383 unsigned int uintm = 0xf;
1384 if (tx_enabled(port))
1385 uintm &= ~S3C64XX_UINTM_TXD_MSK;
1386 if (rx_enabled(port))
1387 uintm &= ~S3C64XX_UINTM_RXD_MSK;
1388 wr_regl(port, S3C64XX_UINTM, uintm);
1389 }
1390 }
1391
1392 return 0;
1393}
1394
MyungJoo Hamaef7fe52011-06-29 15:28:24 +09001395static const struct dev_pm_ops s3c24xx_serial_pm_ops = {
1396 .suspend = s3c24xx_serial_suspend,
1397 .resume = s3c24xx_serial_resume,
Michael Spangd09a7302013-03-27 19:34:24 -04001398 .resume_noirq = s3c24xx_serial_resume_noirq,
MyungJoo Hamaef7fe52011-06-29 15:28:24 +09001399};
Kukjin Kimb882fc12011-07-28 08:50:38 +09001400#define SERIAL_SAMSUNG_PM_OPS (&s3c24xx_serial_pm_ops)
1401
MyungJoo Hamaef7fe52011-06-29 15:28:24 +09001402#else /* !CONFIG_PM_SLEEP */
Kukjin Kimb882fc12011-07-28 08:50:38 +09001403
1404#define SERIAL_SAMSUNG_PM_OPS NULL
MyungJoo Hamaef7fe52011-06-29 15:28:24 +09001405#endif /* CONFIG_PM_SLEEP */
Ben Dooksb4975492008-07-03 12:32:51 +01001406
Ben Dooksb4975492008-07-03 12:32:51 +01001407/* Console code */
1408
1409#ifdef CONFIG_SERIAL_SAMSUNG_CONSOLE
1410
1411static struct uart_port *cons_uart;
1412
1413static int
1414s3c24xx_serial_console_txrdy(struct uart_port *port, unsigned int ufcon)
1415{
1416 struct s3c24xx_uart_info *info = s3c24xx_port_to_info(port);
1417 unsigned long ufstat, utrstat;
1418
1419 if (ufcon & S3C2410_UFCON_FIFOMODE) {
Uwe Kleine-König9ddc5b62010-01-20 17:02:24 +01001420 /* fifo mode - check amount of data in fifo registers... */
Ben Dooksb4975492008-07-03 12:32:51 +01001421
1422 ufstat = rd_regl(port, S3C2410_UFSTAT);
1423 return (ufstat & info->tx_fifofull) ? 0 : 1;
1424 }
1425
1426 /* in non-fifo mode, we go and use the tx buffer empty */
1427
1428 utrstat = rd_regl(port, S3C2410_UTRSTAT);
1429 return (utrstat & S3C2410_UTRSTAT_TXE) ? 1 : 0;
1430}
1431
Michael Spang38adbc52013-03-27 19:34:25 -04001432static bool
1433s3c24xx_port_configured(unsigned int ucon)
1434{
1435 /* consider the serial port configured if the tx/rx mode set */
1436 return (ucon & 0xf) != 0;
1437}
1438
Julien Pichon93b5c032012-09-21 23:22:31 -07001439#ifdef CONFIG_CONSOLE_POLL
1440/*
1441 * Console polling routines for writing and reading from the uart while
1442 * in an interrupt or debug context.
1443 */
1444
1445static int s3c24xx_serial_get_poll_char(struct uart_port *port)
1446{
1447 struct s3c24xx_uart_port *ourport = to_ourport(port);
1448 unsigned int ufstat;
1449
1450 ufstat = rd_regl(port, S3C2410_UFSTAT);
1451 if (s3c24xx_serial_rx_fifocnt(ourport, ufstat) == 0)
1452 return NO_POLL_CHAR;
1453
1454 return rd_regb(port, S3C2410_URXH);
1455}
1456
1457static void s3c24xx_serial_put_poll_char(struct uart_port *port,
1458 unsigned char c)
1459{
Doug Andersonbb7f09b2014-04-21 09:40:34 -07001460 unsigned int ufcon = rd_regl(port, S3C2410_UFCON);
1461 unsigned int ucon = rd_regl(port, S3C2410_UCON);
Michael Spang38adbc52013-03-27 19:34:25 -04001462
1463 /* not possible to xmit on unconfigured port */
1464 if (!s3c24xx_port_configured(ucon))
1465 return;
Julien Pichon93b5c032012-09-21 23:22:31 -07001466
1467 while (!s3c24xx_serial_console_txrdy(port, ufcon))
1468 cpu_relax();
Doug Andersonbb7f09b2014-04-21 09:40:34 -07001469 wr_regb(port, S3C2410_UTXH, c);
Julien Pichon93b5c032012-09-21 23:22:31 -07001470}
1471
1472#endif /* CONFIG_CONSOLE_POLL */
1473
Ben Dooksb4975492008-07-03 12:32:51 +01001474static void
1475s3c24xx_serial_console_putchar(struct uart_port *port, int ch)
1476{
Doug Andersonbb7f09b2014-04-21 09:40:34 -07001477 unsigned int ufcon = rd_regl(port, S3C2410_UFCON);
Michael Spang38adbc52013-03-27 19:34:25 -04001478
Ben Dooksb4975492008-07-03 12:32:51 +01001479 while (!s3c24xx_serial_console_txrdy(port, ufcon))
Doug Andersonf94b0572014-04-21 09:40:36 -07001480 cpu_relax();
Doug Andersonbb7f09b2014-04-21 09:40:34 -07001481 wr_regb(port, S3C2410_UTXH, ch);
Ben Dooksb4975492008-07-03 12:32:51 +01001482}
1483
1484static void
1485s3c24xx_serial_console_write(struct console *co, const char *s,
1486 unsigned int count)
1487{
Doug Andersonab88c8d2014-04-21 09:40:35 -07001488 unsigned int ucon = rd_regl(cons_uart, S3C2410_UCON);
1489
1490 /* not possible to xmit on unconfigured port */
1491 if (!s3c24xx_port_configured(ucon))
1492 return;
1493
Ben Dooksb4975492008-07-03 12:32:51 +01001494 uart_console_write(cons_uart, s, count, s3c24xx_serial_console_putchar);
1495}
1496
1497static void __init
1498s3c24xx_serial_get_options(struct uart_port *port, int *baud,
1499 int *parity, int *bits)
1500{
Ben Dooksb4975492008-07-03 12:32:51 +01001501 struct clk *clk;
1502 unsigned int ulcon;
1503 unsigned int ucon;
1504 unsigned int ubrdiv;
1505 unsigned long rate;
Thomas Abraham5f5a7a52011-10-24 11:47:46 +02001506 unsigned int clk_sel;
1507 char clk_name[MAX_CLK_NAME_LENGTH];
Ben Dooksb4975492008-07-03 12:32:51 +01001508
1509 ulcon = rd_regl(port, S3C2410_ULCON);
1510 ucon = rd_regl(port, S3C2410_UCON);
1511 ubrdiv = rd_regl(port, S3C2410_UBRDIV);
1512
1513 dbg("s3c24xx_serial_get_options: port=%p\n"
1514 "registers: ulcon=%08x, ucon=%08x, ubdriv=%08x\n",
1515 port, ulcon, ucon, ubrdiv);
1516
Michael Spang38adbc52013-03-27 19:34:25 -04001517 if (s3c24xx_port_configured(ucon)) {
Ben Dooksb4975492008-07-03 12:32:51 +01001518 switch (ulcon & S3C2410_LCON_CSMASK) {
1519 case S3C2410_LCON_CS5:
1520 *bits = 5;
1521 break;
1522 case S3C2410_LCON_CS6:
1523 *bits = 6;
1524 break;
1525 case S3C2410_LCON_CS7:
1526 *bits = 7;
1527 break;
Ben Dooksb4975492008-07-03 12:32:51 +01001528 case S3C2410_LCON_CS8:
Naveen Krishna Chatradhi3bcce592014-07-14 17:07:17 +05301529 default:
Ben Dooksb4975492008-07-03 12:32:51 +01001530 *bits = 8;
1531 break;
1532 }
1533
1534 switch (ulcon & S3C2410_LCON_PMASK) {
1535 case S3C2410_LCON_PEVEN:
1536 *parity = 'e';
1537 break;
1538
1539 case S3C2410_LCON_PODD:
1540 *parity = 'o';
1541 break;
1542
1543 case S3C2410_LCON_PNONE:
1544 default:
1545 *parity = 'n';
1546 }
1547
1548 /* now calculate the baud rate */
1549
Thomas Abraham5f5a7a52011-10-24 11:47:46 +02001550 clk_sel = s3c24xx_serial_getsource(port);
1551 sprintf(clk_name, "clk_uart_baud%d", clk_sel);
Ben Dooksb4975492008-07-03 12:32:51 +01001552
Thomas Abraham5f5a7a52011-10-24 11:47:46 +02001553 clk = clk_get(port->dev, clk_name);
Kyoungil Kim7cd88832012-05-20 17:45:54 +09001554 if (!IS_ERR(clk))
Thomas Abraham5f5a7a52011-10-24 11:47:46 +02001555 rate = clk_get_rate(clk);
Ben Dooksb4975492008-07-03 12:32:51 +01001556 else
1557 rate = 1;
1558
Ben Dooksb4975492008-07-03 12:32:51 +01001559 *baud = rate / (16 * (ubrdiv + 1));
1560 dbg("calculated baud %d\n", *baud);
1561 }
1562
1563}
1564
Ben Dooksb4975492008-07-03 12:32:51 +01001565static int __init
1566s3c24xx_serial_console_setup(struct console *co, char *options)
1567{
1568 struct uart_port *port;
1569 int baud = 9600;
1570 int bits = 8;
1571 int parity = 'n';
1572 int flow = 'n';
1573
1574 dbg("s3c24xx_serial_console_setup: co=%p (%d), %s\n",
1575 co, co->index, options);
1576
1577 /* is this a valid port */
1578
Ben Dooks03d5e772008-11-03 09:21:23 +00001579 if (co->index == -1 || co->index >= CONFIG_SERIAL_SAMSUNG_UARTS)
Ben Dooksb4975492008-07-03 12:32:51 +01001580 co->index = 0;
1581
1582 port = &s3c24xx_serial_ports[co->index].port;
1583
1584 /* is the port configured? */
1585
Thomas Abrahamee430f12011-06-14 19:12:26 +09001586 if (port->mapbase == 0x0)
1587 return -ENODEV;
Ben Dooksb4975492008-07-03 12:32:51 +01001588
1589 cons_uart = port;
1590
1591 dbg("s3c24xx_serial_console_setup: port=%p (%d)\n", port, co->index);
1592
1593 /*
1594 * Check whether an invalid uart number has been specified, and
1595 * if so, search for the first available port that does have
1596 * console support.
1597 */
1598 if (options)
1599 uart_parse_options(options, &baud, &parity, &bits, &flow);
1600 else
1601 s3c24xx_serial_get_options(port, &baud, &parity, &bits);
1602
1603 dbg("s3c24xx_serial_console_setup: baud %d\n", baud);
1604
1605 return uart_set_options(port, co, baud, parity, bits, flow);
1606}
1607
Ben Dooksb4975492008-07-03 12:32:51 +01001608static struct console s3c24xx_serial_console = {
1609 .name = S3C24XX_SERIAL_NAME,
1610 .device = uart_console_device,
1611 .flags = CON_PRINTBUFFER,
1612 .index = -1,
1613 .write = s3c24xx_serial_console_write,
Thomas Abraham5822a5d2011-06-14 19:12:26 +09001614 .setup = s3c24xx_serial_console_setup,
1615 .data = &s3c24xx_uart_drv,
Ben Dooksb4975492008-07-03 12:32:51 +01001616};
Ben Dooksb4975492008-07-03 12:32:51 +01001617#endif /* CONFIG_SERIAL_SAMSUNG_CONSOLE */
1618
Thomas Abrahamda121502011-11-02 19:23:25 +09001619#ifdef CONFIG_CPU_S3C2410
1620static struct s3c24xx_serial_drv_data s3c2410_serial_drv_data = {
1621 .info = &(struct s3c24xx_uart_info) {
1622 .name = "Samsung S3C2410 UART",
1623 .type = PORT_S3C2410,
1624 .fifosize = 16,
1625 .rx_fifomask = S3C2410_UFSTAT_RXMASK,
1626 .rx_fifoshift = S3C2410_UFSTAT_RXSHIFT,
1627 .rx_fifofull = S3C2410_UFSTAT_RXFULL,
1628 .tx_fifofull = S3C2410_UFSTAT_TXFULL,
1629 .tx_fifomask = S3C2410_UFSTAT_TXMASK,
1630 .tx_fifoshift = S3C2410_UFSTAT_TXSHIFT,
1631 .def_clk_sel = S3C2410_UCON_CLKSEL0,
1632 .num_clks = 2,
1633 .clksel_mask = S3C2410_UCON_CLKMASK,
1634 .clksel_shift = S3C2410_UCON_CLKSHIFT,
1635 },
1636 .def_cfg = &(struct s3c2410_uartcfg) {
1637 .ucon = S3C2410_UCON_DEFAULT,
1638 .ufcon = S3C2410_UFCON_DEFAULT,
1639 },
1640};
1641#define S3C2410_SERIAL_DRV_DATA ((kernel_ulong_t)&s3c2410_serial_drv_data)
1642#else
1643#define S3C2410_SERIAL_DRV_DATA (kernel_ulong_t)NULL
1644#endif
1645
1646#ifdef CONFIG_CPU_S3C2412
1647static struct s3c24xx_serial_drv_data s3c2412_serial_drv_data = {
1648 .info = &(struct s3c24xx_uart_info) {
1649 .name = "Samsung S3C2412 UART",
1650 .type = PORT_S3C2412,
1651 .fifosize = 64,
1652 .has_divslot = 1,
1653 .rx_fifomask = S3C2440_UFSTAT_RXMASK,
1654 .rx_fifoshift = S3C2440_UFSTAT_RXSHIFT,
1655 .rx_fifofull = S3C2440_UFSTAT_RXFULL,
1656 .tx_fifofull = S3C2440_UFSTAT_TXFULL,
1657 .tx_fifomask = S3C2440_UFSTAT_TXMASK,
1658 .tx_fifoshift = S3C2440_UFSTAT_TXSHIFT,
1659 .def_clk_sel = S3C2410_UCON_CLKSEL2,
1660 .num_clks = 4,
1661 .clksel_mask = S3C2412_UCON_CLKMASK,
1662 .clksel_shift = S3C2412_UCON_CLKSHIFT,
1663 },
1664 .def_cfg = &(struct s3c2410_uartcfg) {
1665 .ucon = S3C2410_UCON_DEFAULT,
1666 .ufcon = S3C2410_UFCON_DEFAULT,
1667 },
1668};
1669#define S3C2412_SERIAL_DRV_DATA ((kernel_ulong_t)&s3c2412_serial_drv_data)
1670#else
1671#define S3C2412_SERIAL_DRV_DATA (kernel_ulong_t)NULL
1672#endif
1673
1674#if defined(CONFIG_CPU_S3C2440) || defined(CONFIG_CPU_S3C2416) || \
Denis 'GNUtoo' Cariklib26469a2012-02-23 08:23:52 +01001675 defined(CONFIG_CPU_S3C2443) || defined(CONFIG_CPU_S3C2442)
Thomas Abrahamda121502011-11-02 19:23:25 +09001676static struct s3c24xx_serial_drv_data s3c2440_serial_drv_data = {
1677 .info = &(struct s3c24xx_uart_info) {
1678 .name = "Samsung S3C2440 UART",
1679 .type = PORT_S3C2440,
1680 .fifosize = 64,
1681 .has_divslot = 1,
1682 .rx_fifomask = S3C2440_UFSTAT_RXMASK,
1683 .rx_fifoshift = S3C2440_UFSTAT_RXSHIFT,
1684 .rx_fifofull = S3C2440_UFSTAT_RXFULL,
1685 .tx_fifofull = S3C2440_UFSTAT_TXFULL,
1686 .tx_fifomask = S3C2440_UFSTAT_TXMASK,
1687 .tx_fifoshift = S3C2440_UFSTAT_TXSHIFT,
1688 .def_clk_sel = S3C2410_UCON_CLKSEL2,
1689 .num_clks = 4,
1690 .clksel_mask = S3C2412_UCON_CLKMASK,
1691 .clksel_shift = S3C2412_UCON_CLKSHIFT,
1692 },
1693 .def_cfg = &(struct s3c2410_uartcfg) {
1694 .ucon = S3C2410_UCON_DEFAULT,
1695 .ufcon = S3C2410_UFCON_DEFAULT,
1696 },
1697};
1698#define S3C2440_SERIAL_DRV_DATA ((kernel_ulong_t)&s3c2440_serial_drv_data)
1699#else
1700#define S3C2440_SERIAL_DRV_DATA (kernel_ulong_t)NULL
1701#endif
1702
Kukjin Kim953b53a2014-07-01 06:32:22 +09001703#if defined(CONFIG_CPU_S3C6400) || defined(CONFIG_CPU_S3C6410)
Thomas Abrahamda121502011-11-02 19:23:25 +09001704static struct s3c24xx_serial_drv_data s3c6400_serial_drv_data = {
1705 .info = &(struct s3c24xx_uart_info) {
1706 .name = "Samsung S3C6400 UART",
1707 .type = PORT_S3C6400,
1708 .fifosize = 64,
1709 .has_divslot = 1,
1710 .rx_fifomask = S3C2440_UFSTAT_RXMASK,
1711 .rx_fifoshift = S3C2440_UFSTAT_RXSHIFT,
1712 .rx_fifofull = S3C2440_UFSTAT_RXFULL,
1713 .tx_fifofull = S3C2440_UFSTAT_TXFULL,
1714 .tx_fifomask = S3C2440_UFSTAT_TXMASK,
1715 .tx_fifoshift = S3C2440_UFSTAT_TXSHIFT,
1716 .def_clk_sel = S3C2410_UCON_CLKSEL2,
1717 .num_clks = 4,
1718 .clksel_mask = S3C6400_UCON_CLKMASK,
1719 .clksel_shift = S3C6400_UCON_CLKSHIFT,
1720 },
1721 .def_cfg = &(struct s3c2410_uartcfg) {
1722 .ucon = S3C2410_UCON_DEFAULT,
1723 .ufcon = S3C2410_UFCON_DEFAULT,
1724 },
1725};
1726#define S3C6400_SERIAL_DRV_DATA ((kernel_ulong_t)&s3c6400_serial_drv_data)
1727#else
1728#define S3C6400_SERIAL_DRV_DATA (kernel_ulong_t)NULL
1729#endif
1730
1731#ifdef CONFIG_CPU_S5PV210
1732static struct s3c24xx_serial_drv_data s5pv210_serial_drv_data = {
1733 .info = &(struct s3c24xx_uart_info) {
1734 .name = "Samsung S5PV210 UART",
1735 .type = PORT_S3C6400,
1736 .has_divslot = 1,
1737 .rx_fifomask = S5PV210_UFSTAT_RXMASK,
1738 .rx_fifoshift = S5PV210_UFSTAT_RXSHIFT,
1739 .rx_fifofull = S5PV210_UFSTAT_RXFULL,
1740 .tx_fifofull = S5PV210_UFSTAT_TXFULL,
1741 .tx_fifomask = S5PV210_UFSTAT_TXMASK,
1742 .tx_fifoshift = S5PV210_UFSTAT_TXSHIFT,
1743 .def_clk_sel = S3C2410_UCON_CLKSEL0,
1744 .num_clks = 2,
1745 .clksel_mask = S5PV210_UCON_CLKMASK,
1746 .clksel_shift = S5PV210_UCON_CLKSHIFT,
1747 },
1748 .def_cfg = &(struct s3c2410_uartcfg) {
1749 .ucon = S5PV210_UCON_DEFAULT,
1750 .ufcon = S5PV210_UFCON_DEFAULT,
1751 },
1752 .fifosize = { 256, 64, 16, 16 },
1753};
1754#define S5PV210_SERIAL_DRV_DATA ((kernel_ulong_t)&s5pv210_serial_drv_data)
1755#else
1756#define S5PV210_SERIAL_DRV_DATA (kernel_ulong_t)NULL
1757#endif
1758
Chander Kashyap33f88132013-06-19 00:29:34 +09001759#if defined(CONFIG_ARCH_EXYNOS)
Thomas Abrahamda121502011-11-02 19:23:25 +09001760static struct s3c24xx_serial_drv_data exynos4210_serial_drv_data = {
1761 .info = &(struct s3c24xx_uart_info) {
1762 .name = "Samsung Exynos4 UART",
1763 .type = PORT_S3C6400,
1764 .has_divslot = 1,
1765 .rx_fifomask = S5PV210_UFSTAT_RXMASK,
1766 .rx_fifoshift = S5PV210_UFSTAT_RXSHIFT,
1767 .rx_fifofull = S5PV210_UFSTAT_RXFULL,
1768 .tx_fifofull = S5PV210_UFSTAT_TXFULL,
1769 .tx_fifomask = S5PV210_UFSTAT_TXMASK,
1770 .tx_fifoshift = S5PV210_UFSTAT_TXSHIFT,
1771 .def_clk_sel = S3C2410_UCON_CLKSEL0,
1772 .num_clks = 1,
1773 .clksel_mask = 0,
1774 .clksel_shift = 0,
1775 },
1776 .def_cfg = &(struct s3c2410_uartcfg) {
1777 .ucon = S5PV210_UCON_DEFAULT,
1778 .ufcon = S5PV210_UFCON_DEFAULT,
1779 .has_fracval = 1,
1780 },
1781 .fifosize = { 256, 64, 16, 16 },
1782};
1783#define EXYNOS4210_SERIAL_DRV_DATA ((kernel_ulong_t)&exynos4210_serial_drv_data)
1784#else
1785#define EXYNOS4210_SERIAL_DRV_DATA (kernel_ulong_t)NULL
1786#endif
1787
1788static struct platform_device_id s3c24xx_serial_driver_ids[] = {
1789 {
1790 .name = "s3c2410-uart",
1791 .driver_data = S3C2410_SERIAL_DRV_DATA,
1792 }, {
1793 .name = "s3c2412-uart",
1794 .driver_data = S3C2412_SERIAL_DRV_DATA,
1795 }, {
1796 .name = "s3c2440-uart",
1797 .driver_data = S3C2440_SERIAL_DRV_DATA,
1798 }, {
1799 .name = "s3c6400-uart",
1800 .driver_data = S3C6400_SERIAL_DRV_DATA,
1801 }, {
1802 .name = "s5pv210-uart",
1803 .driver_data = S5PV210_SERIAL_DRV_DATA,
1804 }, {
1805 .name = "exynos4210-uart",
1806 .driver_data = EXYNOS4210_SERIAL_DRV_DATA,
1807 },
1808 { },
1809};
1810MODULE_DEVICE_TABLE(platform, s3c24xx_serial_driver_ids);
1811
Thomas Abraham26c919e2011-11-06 22:10:44 +05301812#ifdef CONFIG_OF
1813static const struct of_device_id s3c24xx_uart_dt_match[] = {
Heiko Stübner666ca0b2012-11-22 11:37:44 +01001814 { .compatible = "samsung,s3c2410-uart",
1815 .data = (void *)S3C2410_SERIAL_DRV_DATA },
1816 { .compatible = "samsung,s3c2412-uart",
1817 .data = (void *)S3C2412_SERIAL_DRV_DATA },
1818 { .compatible = "samsung,s3c2440-uart",
1819 .data = (void *)S3C2440_SERIAL_DRV_DATA },
1820 { .compatible = "samsung,s3c6400-uart",
1821 .data = (void *)S3C6400_SERIAL_DRV_DATA },
1822 { .compatible = "samsung,s5pv210-uart",
1823 .data = (void *)S5PV210_SERIAL_DRV_DATA },
Thomas Abraham26c919e2011-11-06 22:10:44 +05301824 { .compatible = "samsung,exynos4210-uart",
Mark Browna169a882011-11-08 17:00:14 +09001825 .data = (void *)EXYNOS4210_SERIAL_DRV_DATA },
Thomas Abraham26c919e2011-11-06 22:10:44 +05301826 {},
1827};
1828MODULE_DEVICE_TABLE(of, s3c24xx_uart_dt_match);
Thomas Abraham26c919e2011-11-06 22:10:44 +05301829#endif
1830
Thomas Abrahamda121502011-11-02 19:23:25 +09001831static struct platform_driver samsung_serial_driver = {
1832 .probe = s3c24xx_serial_probe,
Bill Pemberton2d47b712012-11-19 13:21:34 -05001833 .remove = s3c24xx_serial_remove,
Thomas Abrahamda121502011-11-02 19:23:25 +09001834 .id_table = s3c24xx_serial_driver_ids,
1835 .driver = {
1836 .name = "samsung-uart",
1837 .owner = THIS_MODULE,
1838 .pm = SERIAL_SAMSUNG_PM_OPS,
Sachin Kamat905f4ba2013-01-07 09:50:42 +05301839 .of_match_table = of_match_ptr(s3c24xx_uart_dt_match),
Thomas Abrahamda121502011-11-02 19:23:25 +09001840 },
1841};
1842
Tushar Behera6f134c3c2014-01-20 14:32:34 +05301843module_platform_driver(samsung_serial_driver);
Thomas Abrahamda121502011-11-02 19:23:25 +09001844
1845MODULE_ALIAS("platform:samsung-uart");
Ben Dooksb4975492008-07-03 12:32:51 +01001846MODULE_DESCRIPTION("Samsung SoC Serial port driver");
1847MODULE_AUTHOR("Ben Dooks <ben@simtec.co.uk>");
1848MODULE_LICENSE("GPL v2");