blob: e514b3a4dc572069da4df72cba488f6be9cd1c98 [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>
42#include <linux/delay.h>
43#include <linux/clk.h>
Ben Dooks305554762008-10-21 14:06:36 +010044#include <linux/cpufreq.h>
Thomas Abraham26c919e2011-11-06 22:10:44 +053045#include <linux/of.h>
Ben Dooksb4975492008-07-03 12:32:51 +010046
47#include <asm/irq.h>
48
Russell Kinga09e64f2008-08-05 16:14:15 +010049#include <mach/hardware.h>
Ben Dooksb690ace2008-10-21 14:07:03 +010050#include <mach/map.h>
Ben Dooksb4975492008-07-03 12:32:51 +010051
Ben Dooksa2b7ba92008-10-07 22:26:09 +010052#include <plat/regs-serial.h>
Thomas Abraham5f5a7a52011-10-24 11:47:46 +020053#include <plat/clock.h>
Ben Dooksb4975492008-07-03 12:32:51 +010054
55#include "samsung.h"
56
57/* UART name and device definitions */
58
59#define S3C24XX_SERIAL_NAME "ttySAC"
60#define S3C24XX_SERIAL_MAJOR 204
61#define S3C24XX_SERIAL_MINOR 64
62
Ben Dooksb4975492008-07-03 12:32:51 +010063/* macros to change one thing to another */
64
65#define tx_enabled(port) ((port)->unused[0])
66#define rx_enabled(port) ((port)->unused[1])
67
Lucas De Marchi25985ed2011-03-30 22:57:33 -030068/* flag to ignore all characters coming in */
Ben Dooksb4975492008-07-03 12:32:51 +010069#define RXSTAT_DUMMY_READ (0x10000000)
70
71static inline struct s3c24xx_uart_port *to_ourport(struct uart_port *port)
72{
73 return container_of(port, struct s3c24xx_uart_port, port);
74}
75
76/* translate a port to the device name */
77
78static inline const char *s3c24xx_serial_portname(struct uart_port *port)
79{
80 return to_platform_device(port->dev)->name;
81}
82
83static int s3c24xx_serial_txempty_nofifo(struct uart_port *port)
84{
Sachin Kamat9303ac12012-09-05 10:30:11 +053085 return rd_regl(port, S3C2410_UTRSTAT) & S3C2410_UTRSTAT_TXE;
Ben Dooksb4975492008-07-03 12:32:51 +010086}
87
Thomas Abraham88bb4ea2011-08-10 15:51:19 +053088/*
89 * s3c64xx and later SoC's include the interrupt mask and status registers in
90 * the controller itself, unlike the s3c24xx SoC's which have these registers
91 * in the interrupt controller. Check if the port type is s3c64xx or higher.
92 */
93static int s3c24xx_serial_has_interrupt_mask(struct uart_port *port)
94{
95 return to_ourport(port)->info->type == PORT_S3C6400;
96}
97
Ben Dooksb4975492008-07-03 12:32:51 +010098static void s3c24xx_serial_rx_enable(struct uart_port *port)
99{
100 unsigned long flags;
101 unsigned int ucon, ufcon;
102 int count = 10000;
103
104 spin_lock_irqsave(&port->lock, flags);
105
106 while (--count && !s3c24xx_serial_txempty_nofifo(port))
107 udelay(100);
108
109 ufcon = rd_regl(port, S3C2410_UFCON);
110 ufcon |= S3C2410_UFCON_RESETRX;
111 wr_regl(port, S3C2410_UFCON, ufcon);
112
113 ucon = rd_regl(port, S3C2410_UCON);
114 ucon |= S3C2410_UCON_RXIRQMODE;
115 wr_regl(port, S3C2410_UCON, ucon);
116
117 rx_enabled(port) = 1;
118 spin_unlock_irqrestore(&port->lock, flags);
119}
120
121static void s3c24xx_serial_rx_disable(struct uart_port *port)
122{
123 unsigned long flags;
124 unsigned int ucon;
125
126 spin_lock_irqsave(&port->lock, flags);
127
128 ucon = rd_regl(port, S3C2410_UCON);
129 ucon &= ~S3C2410_UCON_RXIRQMODE;
130 wr_regl(port, S3C2410_UCON, ucon);
131
132 rx_enabled(port) = 0;
133 spin_unlock_irqrestore(&port->lock, flags);
134}
135
136static void s3c24xx_serial_stop_tx(struct uart_port *port)
137{
Ben Dooksb73c289c2008-10-21 14:07:04 +0100138 struct s3c24xx_uart_port *ourport = to_ourport(port);
139
Ben Dooksb4975492008-07-03 12:32:51 +0100140 if (tx_enabled(port)) {
Thomas Abraham88bb4ea2011-08-10 15:51:19 +0530141 if (s3c24xx_serial_has_interrupt_mask(port))
142 __set_bit(S3C64XX_UINTM_TXD,
143 portaddrl(port, S3C64XX_UINTM));
144 else
145 disable_irq_nosync(ourport->tx_irq);
Ben Dooksb4975492008-07-03 12:32:51 +0100146 tx_enabled(port) = 0;
147 if (port->flags & UPF_CONS_FLOW)
148 s3c24xx_serial_rx_enable(port);
149 }
150}
151
152static void s3c24xx_serial_start_tx(struct uart_port *port)
153{
Ben Dooksb73c289c2008-10-21 14:07:04 +0100154 struct s3c24xx_uart_port *ourport = to_ourport(port);
155
Ben Dooksb4975492008-07-03 12:32:51 +0100156 if (!tx_enabled(port)) {
157 if (port->flags & UPF_CONS_FLOW)
158 s3c24xx_serial_rx_disable(port);
159
Thomas Abraham88bb4ea2011-08-10 15:51:19 +0530160 if (s3c24xx_serial_has_interrupt_mask(port))
161 __clear_bit(S3C64XX_UINTM_TXD,
162 portaddrl(port, S3C64XX_UINTM));
163 else
164 enable_irq(ourport->tx_irq);
Ben Dooksb4975492008-07-03 12:32:51 +0100165 tx_enabled(port) = 1;
166 }
167}
168
Ben Dooksb4975492008-07-03 12:32:51 +0100169static void s3c24xx_serial_stop_rx(struct uart_port *port)
170{
Ben Dooksb73c289c2008-10-21 14:07:04 +0100171 struct s3c24xx_uart_port *ourport = to_ourport(port);
172
Ben Dooksb4975492008-07-03 12:32:51 +0100173 if (rx_enabled(port)) {
174 dbg("s3c24xx_serial_stop_rx: port=%p\n", port);
Thomas Abraham88bb4ea2011-08-10 15:51:19 +0530175 if (s3c24xx_serial_has_interrupt_mask(port))
176 __set_bit(S3C64XX_UINTM_RXD,
177 portaddrl(port, S3C64XX_UINTM));
178 else
179 disable_irq_nosync(ourport->rx_irq);
Ben Dooksb4975492008-07-03 12:32:51 +0100180 rx_enabled(port) = 0;
181 }
182}
183
184static void s3c24xx_serial_enable_ms(struct uart_port *port)
185{
186}
187
188static inline struct s3c24xx_uart_info *s3c24xx_port_to_info(struct uart_port *port)
189{
190 return to_ourport(port)->info;
191}
192
193static inline struct s3c2410_uartcfg *s3c24xx_port_to_cfg(struct uart_port *port)
194{
Thomas Abraham4d84e972011-10-24 11:47:25 +0200195 struct s3c24xx_uart_port *ourport;
196
Ben Dooksb4975492008-07-03 12:32:51 +0100197 if (port->dev == NULL)
198 return NULL;
199
Thomas Abraham4d84e972011-10-24 11:47:25 +0200200 ourport = container_of(port, struct s3c24xx_uart_port, port);
201 return ourport->cfg;
Ben Dooksb4975492008-07-03 12:32:51 +0100202}
203
204static int s3c24xx_serial_rx_fifocnt(struct s3c24xx_uart_port *ourport,
205 unsigned long ufstat)
206{
207 struct s3c24xx_uart_info *info = ourport->info;
208
209 if (ufstat & info->rx_fifofull)
Thomas Abrahamda121502011-11-02 19:23:25 +0900210 return ourport->port.fifosize;
Ben Dooksb4975492008-07-03 12:32:51 +0100211
212 return (ufstat & info->rx_fifomask) >> info->rx_fifoshift;
213}
214
215
216/* ? - where has parity gone?? */
217#define S3C2410_UERSTAT_PARITY (0x1000)
218
219static irqreturn_t
220s3c24xx_serial_rx_chars(int irq, void *dev_id)
221{
222 struct s3c24xx_uart_port *ourport = dev_id;
223 struct uart_port *port = &ourport->port;
Alan Coxebd2c8f2009-09-19 13:13:28 -0700224 struct tty_struct *tty = port->state->port.tty;
Ben Dooksb4975492008-07-03 12:32:51 +0100225 unsigned int ufcon, ch, flag, ufstat, uerstat;
Thomas Abrahamc15c3742012-11-22 18:06:28 +0530226 unsigned long flags;
Ben Dooksb4975492008-07-03 12:32:51 +0100227 int max_count = 64;
228
Thomas Abrahamc15c3742012-11-22 18:06:28 +0530229 spin_lock_irqsave(&port->lock, flags);
230
Ben Dooksb4975492008-07-03 12:32:51 +0100231 while (max_count-- > 0) {
232 ufcon = rd_regl(port, S3C2410_UFCON);
233 ufstat = rd_regl(port, S3C2410_UFSTAT);
234
235 if (s3c24xx_serial_rx_fifocnt(ourport, ufstat) == 0)
236 break;
237
238 uerstat = rd_regl(port, S3C2410_UERSTAT);
239 ch = rd_regb(port, S3C2410_URXH);
240
241 if (port->flags & UPF_CONS_FLOW) {
242 int txe = s3c24xx_serial_txempty_nofifo(port);
243
244 if (rx_enabled(port)) {
245 if (!txe) {
246 rx_enabled(port) = 0;
247 continue;
248 }
249 } else {
250 if (txe) {
251 ufcon |= S3C2410_UFCON_RESETRX;
252 wr_regl(port, S3C2410_UFCON, ufcon);
253 rx_enabled(port) = 1;
254 goto out;
255 }
256 continue;
257 }
258 }
259
260 /* insert the character into the buffer */
261
262 flag = TTY_NORMAL;
263 port->icount.rx++;
264
265 if (unlikely(uerstat & S3C2410_UERSTAT_ANY)) {
266 dbg("rxerr: port ch=0x%02x, rxs=0x%08x\n",
267 ch, uerstat);
268
269 /* check for break */
270 if (uerstat & S3C2410_UERSTAT_BREAK) {
271 dbg("break!\n");
272 port->icount.brk++;
273 if (uart_handle_break(port))
Sachin Kamat9303ac12012-09-05 10:30:11 +0530274 goto ignore_char;
Ben Dooksb4975492008-07-03 12:32:51 +0100275 }
276
277 if (uerstat & S3C2410_UERSTAT_FRAME)
278 port->icount.frame++;
279 if (uerstat & S3C2410_UERSTAT_OVERRUN)
280 port->icount.overrun++;
281
282 uerstat &= port->read_status_mask;
283
284 if (uerstat & S3C2410_UERSTAT_BREAK)
285 flag = TTY_BREAK;
286 else if (uerstat & S3C2410_UERSTAT_PARITY)
287 flag = TTY_PARITY;
288 else if (uerstat & (S3C2410_UERSTAT_FRAME |
289 S3C2410_UERSTAT_OVERRUN))
290 flag = TTY_FRAME;
291 }
292
293 if (uart_handle_sysrq_char(port, ch))
294 goto ignore_char;
295
296 uart_insert_char(port, uerstat, S3C2410_UERSTAT_OVERRUN,
297 ch, flag);
298
299 ignore_char:
300 continue;
301 }
302 tty_flip_buffer_push(tty);
303
304 out:
Thomas Abrahamc15c3742012-11-22 18:06:28 +0530305 spin_unlock_irqrestore(&port->lock, flags);
Ben Dooksb4975492008-07-03 12:32:51 +0100306 return IRQ_HANDLED;
307}
308
309static irqreturn_t s3c24xx_serial_tx_chars(int irq, void *id)
310{
311 struct s3c24xx_uart_port *ourport = id;
312 struct uart_port *port = &ourport->port;
Alan Coxebd2c8f2009-09-19 13:13:28 -0700313 struct circ_buf *xmit = &port->state->xmit;
Thomas Abrahamc15c3742012-11-22 18:06:28 +0530314 unsigned long flags;
Ben Dooksb4975492008-07-03 12:32:51 +0100315 int count = 256;
316
Thomas Abrahamc15c3742012-11-22 18:06:28 +0530317 spin_lock_irqsave(&port->lock, flags);
318
Ben Dooksb4975492008-07-03 12:32:51 +0100319 if (port->x_char) {
320 wr_regb(port, S3C2410_UTXH, port->x_char);
321 port->icount.tx++;
322 port->x_char = 0;
323 goto out;
324 }
325
Lucas De Marchi25985ed2011-03-30 22:57:33 -0300326 /* if there isn't anything more to transmit, or the uart is now
Ben Dooksb4975492008-07-03 12:32:51 +0100327 * stopped, disable the uart and exit
328 */
329
330 if (uart_circ_empty(xmit) || uart_tx_stopped(port)) {
331 s3c24xx_serial_stop_tx(port);
332 goto out;
333 }
334
335 /* try and drain the buffer... */
336
337 while (!uart_circ_empty(xmit) && count-- > 0) {
338 if (rd_regl(port, S3C2410_UFSTAT) & ourport->info->tx_fifofull)
339 break;
340
341 wr_regb(port, S3C2410_UTXH, xmit->buf[xmit->tail]);
342 xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
343 port->icount.tx++;
344 }
345
Thomas Abrahamc15c3742012-11-22 18:06:28 +0530346 if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) {
347 spin_unlock(&port->lock);
Ben Dooksb4975492008-07-03 12:32:51 +0100348 uart_write_wakeup(port);
Thomas Abrahamc15c3742012-11-22 18:06:28 +0530349 spin_lock(&port->lock);
350 }
Ben Dooksb4975492008-07-03 12:32:51 +0100351
352 if (uart_circ_empty(xmit))
353 s3c24xx_serial_stop_tx(port);
354
355 out:
Thomas Abrahamc15c3742012-11-22 18:06:28 +0530356 spin_unlock_irqrestore(&port->lock, flags);
Ben Dooksb4975492008-07-03 12:32:51 +0100357 return IRQ_HANDLED;
358}
359
Thomas Abraham88bb4ea2011-08-10 15:51:19 +0530360/* interrupt handler for s3c64xx and later SoC's.*/
361static irqreturn_t s3c64xx_serial_handle_irq(int irq, void *id)
362{
363 struct s3c24xx_uart_port *ourport = id;
364 struct uart_port *port = &ourport->port;
365 unsigned int pend = rd_regl(port, S3C64XX_UINTP);
Thomas Abraham88bb4ea2011-08-10 15:51:19 +0530366 irqreturn_t ret = IRQ_HANDLED;
367
Thomas Abraham88bb4ea2011-08-10 15:51:19 +0530368 if (pend & S3C64XX_UINTM_RXD_MSK) {
369 ret = s3c24xx_serial_rx_chars(irq, id);
370 wr_regl(port, S3C64XX_UINTP, S3C64XX_UINTM_RXD_MSK);
371 }
372 if (pend & S3C64XX_UINTM_TXD_MSK) {
373 ret = s3c24xx_serial_tx_chars(irq, id);
374 wr_regl(port, S3C64XX_UINTP, S3C64XX_UINTM_TXD_MSK);
375 }
Thomas Abraham88bb4ea2011-08-10 15:51:19 +0530376 return ret;
377}
378
Ben Dooksb4975492008-07-03 12:32:51 +0100379static unsigned int s3c24xx_serial_tx_empty(struct uart_port *port)
380{
381 struct s3c24xx_uart_info *info = s3c24xx_port_to_info(port);
382 unsigned long ufstat = rd_regl(port, S3C2410_UFSTAT);
383 unsigned long ufcon = rd_regl(port, S3C2410_UFCON);
384
385 if (ufcon & S3C2410_UFCON_FIFOMODE) {
386 if ((ufstat & info->tx_fifomask) != 0 ||
387 (ufstat & info->tx_fifofull))
388 return 0;
389
390 return 1;
391 }
392
393 return s3c24xx_serial_txempty_nofifo(port);
394}
395
396/* no modem control lines */
397static unsigned int s3c24xx_serial_get_mctrl(struct uart_port *port)
398{
399 unsigned int umstat = rd_regb(port, S3C2410_UMSTAT);
400
401 if (umstat & S3C2410_UMSTAT_CTS)
402 return TIOCM_CAR | TIOCM_DSR | TIOCM_CTS;
403 else
404 return TIOCM_CAR | TIOCM_DSR;
405}
406
407static void s3c24xx_serial_set_mctrl(struct uart_port *port, unsigned int mctrl)
408{
409 /* todo - possibly remove AFC and do manual CTS */
410}
411
412static void s3c24xx_serial_break_ctl(struct uart_port *port, int break_state)
413{
414 unsigned long flags;
415 unsigned int ucon;
416
417 spin_lock_irqsave(&port->lock, flags);
418
419 ucon = rd_regl(port, S3C2410_UCON);
420
421 if (break_state)
422 ucon |= S3C2410_UCON_SBREAK;
423 else
424 ucon &= ~S3C2410_UCON_SBREAK;
425
426 wr_regl(port, S3C2410_UCON, ucon);
427
428 spin_unlock_irqrestore(&port->lock, flags);
429}
430
431static void s3c24xx_serial_shutdown(struct uart_port *port)
432{
433 struct s3c24xx_uart_port *ourport = to_ourport(port);
434
435 if (ourport->tx_claimed) {
Thomas Abraham88bb4ea2011-08-10 15:51:19 +0530436 if (!s3c24xx_serial_has_interrupt_mask(port))
437 free_irq(ourport->tx_irq, ourport);
Ben Dooksb4975492008-07-03 12:32:51 +0100438 tx_enabled(port) = 0;
439 ourport->tx_claimed = 0;
440 }
441
442 if (ourport->rx_claimed) {
Thomas Abraham88bb4ea2011-08-10 15:51:19 +0530443 if (!s3c24xx_serial_has_interrupt_mask(port))
444 free_irq(ourport->rx_irq, ourport);
Ben Dooksb4975492008-07-03 12:32:51 +0100445 ourport->rx_claimed = 0;
446 rx_enabled(port) = 0;
447 }
Ben Dooksb4975492008-07-03 12:32:51 +0100448
Thomas Abraham88bb4ea2011-08-10 15:51:19 +0530449 /* Clear pending interrupts and mask all interrupts */
450 if (s3c24xx_serial_has_interrupt_mask(port)) {
451 wr_regl(port, S3C64XX_UINTP, 0xf);
452 wr_regl(port, S3C64XX_UINTM, 0xf);
453 }
454}
Ben Dooksb4975492008-07-03 12:32:51 +0100455
456static int s3c24xx_serial_startup(struct uart_port *port)
457{
458 struct s3c24xx_uart_port *ourport = to_ourport(port);
459 int ret;
460
461 dbg("s3c24xx_serial_startup: port=%p (%08lx,%p)\n",
462 port->mapbase, port->membase);
463
464 rx_enabled(port) = 1;
465
Ben Dooksb73c289c2008-10-21 14:07:04 +0100466 ret = request_irq(ourport->rx_irq, s3c24xx_serial_rx_chars, 0,
Ben Dooksb4975492008-07-03 12:32:51 +0100467 s3c24xx_serial_portname(port), ourport);
468
469 if (ret != 0) {
Sachin Kamatd20925e2012-09-05 10:30:10 +0530470 dev_err(port->dev, "cannot get irq %d\n", ourport->rx_irq);
Ben Dooksb4975492008-07-03 12:32:51 +0100471 return ret;
472 }
473
474 ourport->rx_claimed = 1;
475
476 dbg("requesting tx irq...\n");
477
478 tx_enabled(port) = 1;
479
Ben Dooksb73c289c2008-10-21 14:07:04 +0100480 ret = request_irq(ourport->tx_irq, s3c24xx_serial_tx_chars, 0,
Ben Dooksb4975492008-07-03 12:32:51 +0100481 s3c24xx_serial_portname(port), ourport);
482
483 if (ret) {
Sachin Kamatd20925e2012-09-05 10:30:10 +0530484 dev_err(port->dev, "cannot get irq %d\n", ourport->tx_irq);
Ben Dooksb4975492008-07-03 12:32:51 +0100485 goto err;
486 }
487
488 ourport->tx_claimed = 1;
489
490 dbg("s3c24xx_serial_startup ok\n");
491
492 /* the port reset code should have done the correct
493 * register setup for the port controls */
494
495 return ret;
496
497 err:
498 s3c24xx_serial_shutdown(port);
499 return ret;
500}
501
Thomas Abraham88bb4ea2011-08-10 15:51:19 +0530502static int s3c64xx_serial_startup(struct uart_port *port)
503{
504 struct s3c24xx_uart_port *ourport = to_ourport(port);
505 int ret;
506
507 dbg("s3c64xx_serial_startup: port=%p (%08lx,%p)\n",
508 port->mapbase, port->membase);
509
510 ret = request_irq(port->irq, s3c64xx_serial_handle_irq, IRQF_SHARED,
511 s3c24xx_serial_portname(port), ourport);
512 if (ret) {
Sachin Kamatd20925e2012-09-05 10:30:10 +0530513 dev_err(port->dev, "cannot get irq %d\n", port->irq);
Thomas Abraham88bb4ea2011-08-10 15:51:19 +0530514 return ret;
515 }
516
517 /* For compatibility with s3c24xx Soc's */
518 rx_enabled(port) = 1;
519 ourport->rx_claimed = 1;
520 tx_enabled(port) = 0;
521 ourport->tx_claimed = 1;
522
523 /* Enable Rx Interrupt */
524 __clear_bit(S3C64XX_UINTM_RXD, portaddrl(port, S3C64XX_UINTM));
525 dbg("s3c64xx_serial_startup ok\n");
526 return ret;
527}
528
Ben Dooksb4975492008-07-03 12:32:51 +0100529/* power power management control */
530
531static void s3c24xx_serial_pm(struct uart_port *port, unsigned int level,
532 unsigned int old)
533{
534 struct s3c24xx_uart_port *ourport = to_ourport(port);
535
Ben Dooks305554762008-10-21 14:06:36 +0100536 ourport->pm_level = level;
537
Ben Dooksb4975492008-07-03 12:32:51 +0100538 switch (level) {
539 case 3:
Kyoungil Kim7cd88832012-05-20 17:45:54 +0900540 if (!IS_ERR(ourport->baudclk))
Thomas Abraham9484b0092012-10-03 07:40:04 +0900541 clk_disable_unprepare(ourport->baudclk);
Ben Dooksb4975492008-07-03 12:32:51 +0100542
Thomas Abraham9484b0092012-10-03 07:40:04 +0900543 clk_disable_unprepare(ourport->clk);
Ben Dooksb4975492008-07-03 12:32:51 +0100544 break;
545
546 case 0:
Thomas Abraham9484b0092012-10-03 07:40:04 +0900547 clk_prepare_enable(ourport->clk);
Ben Dooksb4975492008-07-03 12:32:51 +0100548
Kyoungil Kim7cd88832012-05-20 17:45:54 +0900549 if (!IS_ERR(ourport->baudclk))
Thomas Abraham9484b0092012-10-03 07:40:04 +0900550 clk_prepare_enable(ourport->baudclk);
Ben Dooksb4975492008-07-03 12:32:51 +0100551
552 break;
553 default:
Sachin Kamatd20925e2012-09-05 10:30:10 +0530554 dev_err(port->dev, "s3c24xx_serial: unknown pm %d\n", level);
Ben Dooksb4975492008-07-03 12:32:51 +0100555 }
556}
557
558/* baud rate calculation
559 *
560 * The UARTs on the S3C2410/S3C2440 can take their clocks from a number
561 * of different sources, including the peripheral clock ("pclk") and an
562 * external clock ("uclk"). The S3C2440 also adds the core clock ("fclk")
563 * with a programmable extra divisor.
564 *
565 * The following code goes through the clock sources, and calculates the
566 * baud clocks (and the resultant actual baud rates) and then tries to
567 * pick the closest one and select that.
568 *
569*/
570
Thomas Abraham5f5a7a52011-10-24 11:47:46 +0200571#define MAX_CLK_NAME_LENGTH 15
Ben Dooksb4975492008-07-03 12:32:51 +0100572
Thomas Abraham5f5a7a52011-10-24 11:47:46 +0200573static inline int s3c24xx_serial_getsource(struct uart_port *port)
Ben Dooksb4975492008-07-03 12:32:51 +0100574{
575 struct s3c24xx_uart_info *info = s3c24xx_port_to_info(port);
Thomas Abraham5f5a7a52011-10-24 11:47:46 +0200576 unsigned int ucon;
Ben Dooksb4975492008-07-03 12:32:51 +0100577
Thomas Abraham5f5a7a52011-10-24 11:47:46 +0200578 if (info->num_clks == 1)
Ben Dooksb4975492008-07-03 12:32:51 +0100579 return 0;
580
Thomas Abraham5f5a7a52011-10-24 11:47:46 +0200581 ucon = rd_regl(port, S3C2410_UCON);
582 ucon &= info->clksel_mask;
583 return ucon >> info->clksel_shift;
Ben Dooksb4975492008-07-03 12:32:51 +0100584}
585
Thomas Abraham5f5a7a52011-10-24 11:47:46 +0200586static void s3c24xx_serial_setsource(struct uart_port *port,
587 unsigned int clk_sel)
Ben Dooksb4975492008-07-03 12:32:51 +0100588{
Thomas Abraham5f5a7a52011-10-24 11:47:46 +0200589 struct s3c24xx_uart_info *info = s3c24xx_port_to_info(port);
590 unsigned int ucon;
Ben Dooksb4975492008-07-03 12:32:51 +0100591
Thomas Abraham5f5a7a52011-10-24 11:47:46 +0200592 if (info->num_clks == 1)
593 return;
Ben Dooksb4975492008-07-03 12:32:51 +0100594
Thomas Abraham5f5a7a52011-10-24 11:47:46 +0200595 ucon = rd_regl(port, S3C2410_UCON);
596 if ((ucon & info->clksel_mask) >> info->clksel_shift == clk_sel)
597 return;
Ben Dooksb4975492008-07-03 12:32:51 +0100598
Thomas Abraham5f5a7a52011-10-24 11:47:46 +0200599 ucon &= ~info->clksel_mask;
600 ucon |= clk_sel << info->clksel_shift;
601 wr_regl(port, S3C2410_UCON, ucon);
602}
Ben Dooksb4975492008-07-03 12:32:51 +0100603
Thomas Abraham5f5a7a52011-10-24 11:47:46 +0200604static unsigned int s3c24xx_serial_getclk(struct s3c24xx_uart_port *ourport,
605 unsigned int req_baud, struct clk **best_clk,
606 unsigned int *clk_num)
607{
608 struct s3c24xx_uart_info *info = ourport->info;
609 struct clk *clk;
610 unsigned long rate;
611 unsigned int cnt, baud, quot, clk_sel, best_quot = 0;
612 char clkname[MAX_CLK_NAME_LENGTH];
613 int calc_deviation, deviation = (1 << 30) - 1;
614
Thomas Abraham5f5a7a52011-10-24 11:47:46 +0200615 clk_sel = (ourport->cfg->clk_sel) ? ourport->cfg->clk_sel :
616 ourport->info->def_clk_sel;
617 for (cnt = 0; cnt < info->num_clks; cnt++) {
618 if (!(clk_sel & (1 << cnt)))
619 continue;
620
621 sprintf(clkname, "clk_uart_baud%d", cnt);
622 clk = clk_get(ourport->port.dev, clkname);
Kyoungil Kim7cd88832012-05-20 17:45:54 +0900623 if (IS_ERR(clk))
Thomas Abraham5f5a7a52011-10-24 11:47:46 +0200624 continue;
625
626 rate = clk_get_rate(clk);
627 if (!rate)
628 continue;
629
630 if (ourport->info->has_divslot) {
631 unsigned long div = rate / req_baud;
632
633 /* The UDIVSLOT register on the newer UARTs allows us to
634 * get a divisor adjustment of 1/16th on the baud clock.
635 *
636 * We don't keep the UDIVSLOT value (the 16ths we
637 * calculated by not multiplying the baud by 16) as it
638 * is easy enough to recalculate.
639 */
640
641 quot = div / 16;
642 baud = rate / div;
643 } else {
644 quot = (rate + (8 * req_baud)) / (16 * req_baud);
645 baud = rate / (quot * 16);
646 }
647 quot--;
648
649 calc_deviation = req_baud - baud;
650 if (calc_deviation < 0)
651 calc_deviation = -calc_deviation;
652
653 if (calc_deviation < deviation) {
654 *best_clk = clk;
655 best_quot = quot;
656 *clk_num = cnt;
657 deviation = calc_deviation;
Ben Dooksb4975492008-07-03 12:32:51 +0100658 }
659 }
660
Thomas Abraham5f5a7a52011-10-24 11:47:46 +0200661 return best_quot;
Ben Dooksb4975492008-07-03 12:32:51 +0100662}
663
Ben Dooks090f8482008-12-12 00:24:21 +0000664/* udivslot_table[]
665 *
666 * This table takes the fractional value of the baud divisor and gives
667 * the recommended setting for the UDIVSLOT register.
668 */
669static u16 udivslot_table[16] = {
670 [0] = 0x0000,
671 [1] = 0x0080,
672 [2] = 0x0808,
673 [3] = 0x0888,
674 [4] = 0x2222,
675 [5] = 0x4924,
676 [6] = 0x4A52,
677 [7] = 0x54AA,
678 [8] = 0x5555,
679 [9] = 0xD555,
680 [10] = 0xD5D5,
681 [11] = 0xDDD5,
682 [12] = 0xDDDD,
683 [13] = 0xDFDD,
684 [14] = 0xDFDF,
685 [15] = 0xFFDF,
686};
687
Ben Dooksb4975492008-07-03 12:32:51 +0100688static void s3c24xx_serial_set_termios(struct uart_port *port,
689 struct ktermios *termios,
690 struct ktermios *old)
691{
692 struct s3c2410_uartcfg *cfg = s3c24xx_port_to_cfg(port);
693 struct s3c24xx_uart_port *ourport = to_ourport(port);
Kyoungil Kim7cd88832012-05-20 17:45:54 +0900694 struct clk *clk = ERR_PTR(-EINVAL);
Ben Dooksb4975492008-07-03 12:32:51 +0100695 unsigned long flags;
Thomas Abraham5f5a7a52011-10-24 11:47:46 +0200696 unsigned int baud, quot, clk_sel = 0;
Ben Dooksb4975492008-07-03 12:32:51 +0100697 unsigned int ulcon;
698 unsigned int umcon;
Ben Dooks090f8482008-12-12 00:24:21 +0000699 unsigned int udivslot = 0;
Ben Dooksb4975492008-07-03 12:32:51 +0100700
701 /*
702 * We don't support modem control lines.
703 */
704 termios->c_cflag &= ~(HUPCL | CMSPAR);
705 termios->c_cflag |= CLOCAL;
706
707 /*
708 * Ask the core to calculate the divisor for us.
709 */
710
711 baud = uart_get_baud_rate(port, termios, old, 0, 115200*8);
Thomas Abraham5f5a7a52011-10-24 11:47:46 +0200712 quot = s3c24xx_serial_getclk(ourport, baud, &clk, &clk_sel);
Ben Dooksb4975492008-07-03 12:32:51 +0100713 if (baud == 38400 && (port->flags & UPF_SPD_MASK) == UPF_SPD_CUST)
714 quot = port->custom_divisor;
Kyoungil Kim7cd88832012-05-20 17:45:54 +0900715 if (IS_ERR(clk))
Thomas Abraham5f5a7a52011-10-24 11:47:46 +0200716 return;
Ben Dooksb4975492008-07-03 12:32:51 +0100717
718 /* check to see if we need to change clock source */
719
Thomas Abraham5f5a7a52011-10-24 11:47:46 +0200720 if (ourport->baudclk != clk) {
721 s3c24xx_serial_setsource(port, clk_sel);
Ben Dooksb4975492008-07-03 12:32:51 +0100722
Kyoungil Kim7cd88832012-05-20 17:45:54 +0900723 if (!IS_ERR(ourport->baudclk)) {
Thomas Abraham9484b0092012-10-03 07:40:04 +0900724 clk_disable_unprepare(ourport->baudclk);
Kyoungil Kim7cd88832012-05-20 17:45:54 +0900725 ourport->baudclk = ERR_PTR(-EINVAL);
Ben Dooksb4975492008-07-03 12:32:51 +0100726 }
727
Thomas Abraham9484b0092012-10-03 07:40:04 +0900728 clk_prepare_enable(clk);
Ben Dooksb4975492008-07-03 12:32:51 +0100729
Ben Dooksb4975492008-07-03 12:32:51 +0100730 ourport->baudclk = clk;
Ben Dooks305554762008-10-21 14:06:36 +0100731 ourport->baudclk_rate = clk ? clk_get_rate(clk) : 0;
Ben Dooksb4975492008-07-03 12:32:51 +0100732 }
733
Ben Dooks090f8482008-12-12 00:24:21 +0000734 if (ourport->info->has_divslot) {
735 unsigned int div = ourport->baudclk_rate / baud;
736
Jongpill Lee8b526ae2010-07-16 10:19:41 +0900737 if (cfg->has_fracval) {
738 udivslot = (div & 15);
739 dbg("fracval = %04x\n", udivslot);
740 } else {
741 udivslot = udivslot_table[div & 15];
742 dbg("udivslot = %04x (div %d)\n", udivslot, div & 15);
743 }
Ben Dooks090f8482008-12-12 00:24:21 +0000744 }
745
Ben Dooksb4975492008-07-03 12:32:51 +0100746 switch (termios->c_cflag & CSIZE) {
747 case CS5:
748 dbg("config: 5bits/char\n");
749 ulcon = S3C2410_LCON_CS5;
750 break;
751 case CS6:
752 dbg("config: 6bits/char\n");
753 ulcon = S3C2410_LCON_CS6;
754 break;
755 case CS7:
756 dbg("config: 7bits/char\n");
757 ulcon = S3C2410_LCON_CS7;
758 break;
759 case CS8:
760 default:
761 dbg("config: 8bits/char\n");
762 ulcon = S3C2410_LCON_CS8;
763 break;
764 }
765
766 /* preserve original lcon IR settings */
767 ulcon |= (cfg->ulcon & S3C2410_LCON_IRM);
768
769 if (termios->c_cflag & CSTOPB)
770 ulcon |= S3C2410_LCON_STOPB;
771
772 umcon = (termios->c_cflag & CRTSCTS) ? S3C2410_UMCOM_AFC : 0;
773
774 if (termios->c_cflag & PARENB) {
775 if (termios->c_cflag & PARODD)
776 ulcon |= S3C2410_LCON_PODD;
777 else
778 ulcon |= S3C2410_LCON_PEVEN;
779 } else {
780 ulcon |= S3C2410_LCON_PNONE;
781 }
782
783 spin_lock_irqsave(&port->lock, flags);
784
Ben Dooks090f8482008-12-12 00:24:21 +0000785 dbg("setting ulcon to %08x, brddiv to %d, udivslot %08x\n",
786 ulcon, quot, udivslot);
Ben Dooksb4975492008-07-03 12:32:51 +0100787
788 wr_regl(port, S3C2410_ULCON, ulcon);
789 wr_regl(port, S3C2410_UBRDIV, quot);
790 wr_regl(port, S3C2410_UMCON, umcon);
791
Ben Dooks090f8482008-12-12 00:24:21 +0000792 if (ourport->info->has_divslot)
793 wr_regl(port, S3C2443_DIVSLOT, udivslot);
794
Ben Dooksb4975492008-07-03 12:32:51 +0100795 dbg("uart: ulcon = 0x%08x, ucon = 0x%08x, ufcon = 0x%08x\n",
796 rd_regl(port, S3C2410_ULCON),
797 rd_regl(port, S3C2410_UCON),
798 rd_regl(port, S3C2410_UFCON));
799
800 /*
801 * Update the per-port timeout.
802 */
803 uart_update_timeout(port, termios->c_cflag, baud);
804
805 /*
806 * Which character status flags are we interested in?
807 */
808 port->read_status_mask = S3C2410_UERSTAT_OVERRUN;
809 if (termios->c_iflag & INPCK)
810 port->read_status_mask |= S3C2410_UERSTAT_FRAME | S3C2410_UERSTAT_PARITY;
811
812 /*
813 * Which character status flags should we ignore?
814 */
815 port->ignore_status_mask = 0;
816 if (termios->c_iflag & IGNPAR)
817 port->ignore_status_mask |= S3C2410_UERSTAT_OVERRUN;
818 if (termios->c_iflag & IGNBRK && termios->c_iflag & IGNPAR)
819 port->ignore_status_mask |= S3C2410_UERSTAT_FRAME;
820
821 /*
822 * Ignore all characters if CREAD is not set.
823 */
824 if ((termios->c_cflag & CREAD) == 0)
825 port->ignore_status_mask |= RXSTAT_DUMMY_READ;
826
827 spin_unlock_irqrestore(&port->lock, flags);
828}
829
830static const char *s3c24xx_serial_type(struct uart_port *port)
831{
832 switch (port->type) {
833 case PORT_S3C2410:
834 return "S3C2410";
835 case PORT_S3C2440:
836 return "S3C2440";
837 case PORT_S3C2412:
838 return "S3C2412";
Ben Dooksb690ace2008-10-21 14:07:03 +0100839 case PORT_S3C6400:
840 return "S3C6400/10";
Ben Dooksb4975492008-07-03 12:32:51 +0100841 default:
842 return NULL;
843 }
844}
845
846#define MAP_SIZE (0x100)
847
848static void s3c24xx_serial_release_port(struct uart_port *port)
849{
850 release_mem_region(port->mapbase, MAP_SIZE);
851}
852
853static int s3c24xx_serial_request_port(struct uart_port *port)
854{
855 const char *name = s3c24xx_serial_portname(port);
856 return request_mem_region(port->mapbase, MAP_SIZE, name) ? 0 : -EBUSY;
857}
858
859static void s3c24xx_serial_config_port(struct uart_port *port, int flags)
860{
861 struct s3c24xx_uart_info *info = s3c24xx_port_to_info(port);
862
863 if (flags & UART_CONFIG_TYPE &&
864 s3c24xx_serial_request_port(port) == 0)
865 port->type = info->type;
866}
867
868/*
869 * verify the new serial_struct (for TIOCSSERIAL).
870 */
871static int
872s3c24xx_serial_verify_port(struct uart_port *port, struct serial_struct *ser)
873{
874 struct s3c24xx_uart_info *info = s3c24xx_port_to_info(port);
875
876 if (ser->type != PORT_UNKNOWN && ser->type != info->type)
877 return -EINVAL;
878
879 return 0;
880}
881
882
883#ifdef CONFIG_SERIAL_SAMSUNG_CONSOLE
884
885static struct console s3c24xx_serial_console;
886
Julien Pichon93b5c032012-09-21 23:22:31 -0700887static int __init s3c24xx_serial_console_init(void)
888{
889 register_console(&s3c24xx_serial_console);
890 return 0;
891}
892console_initcall(s3c24xx_serial_console_init);
893
Ben Dooksb4975492008-07-03 12:32:51 +0100894#define S3C24XX_SERIAL_CONSOLE &s3c24xx_serial_console
895#else
896#define S3C24XX_SERIAL_CONSOLE NULL
897#endif
898
Julien Pichon93b5c032012-09-21 23:22:31 -0700899#ifdef CONFIG_CONSOLE_POLL
900static int s3c24xx_serial_get_poll_char(struct uart_port *port);
901static void s3c24xx_serial_put_poll_char(struct uart_port *port,
902 unsigned char c);
903#endif
904
Ben Dooksb4975492008-07-03 12:32:51 +0100905static struct uart_ops s3c24xx_serial_ops = {
906 .pm = s3c24xx_serial_pm,
907 .tx_empty = s3c24xx_serial_tx_empty,
908 .get_mctrl = s3c24xx_serial_get_mctrl,
909 .set_mctrl = s3c24xx_serial_set_mctrl,
910 .stop_tx = s3c24xx_serial_stop_tx,
911 .start_tx = s3c24xx_serial_start_tx,
912 .stop_rx = s3c24xx_serial_stop_rx,
913 .enable_ms = s3c24xx_serial_enable_ms,
914 .break_ctl = s3c24xx_serial_break_ctl,
915 .startup = s3c24xx_serial_startup,
916 .shutdown = s3c24xx_serial_shutdown,
917 .set_termios = s3c24xx_serial_set_termios,
918 .type = s3c24xx_serial_type,
919 .release_port = s3c24xx_serial_release_port,
920 .request_port = s3c24xx_serial_request_port,
921 .config_port = s3c24xx_serial_config_port,
922 .verify_port = s3c24xx_serial_verify_port,
Julien Pichon93b5c032012-09-21 23:22:31 -0700923#ifdef CONFIG_CONSOLE_POLL
924 .poll_get_char = s3c24xx_serial_get_poll_char,
925 .poll_put_char = s3c24xx_serial_put_poll_char,
926#endif
Ben Dooksb4975492008-07-03 12:32:51 +0100927};
928
Ben Dooksb4975492008-07-03 12:32:51 +0100929static struct uart_driver s3c24xx_uart_drv = {
930 .owner = THIS_MODULE,
Darius Augulis2cf0c582011-01-12 14:50:51 +0900931 .driver_name = "s3c2410_serial",
Ben Dooksbdd49152008-11-03 19:51:42 +0000932 .nr = CONFIG_SERIAL_SAMSUNG_UARTS,
Ben Dooksb4975492008-07-03 12:32:51 +0100933 .cons = S3C24XX_SERIAL_CONSOLE,
Darius Augulis2cf0c582011-01-12 14:50:51 +0900934 .dev_name = S3C24XX_SERIAL_NAME,
Ben Dooksb4975492008-07-03 12:32:51 +0100935 .major = S3C24XX_SERIAL_MAJOR,
936 .minor = S3C24XX_SERIAL_MINOR,
937};
938
Ben Dooks03d5e772008-11-03 09:21:23 +0000939static struct s3c24xx_uart_port s3c24xx_serial_ports[CONFIG_SERIAL_SAMSUNG_UARTS] = {
Ben Dooksb4975492008-07-03 12:32:51 +0100940 [0] = {
941 .port = {
942 .lock = __SPIN_LOCK_UNLOCKED(s3c24xx_serial_ports[0].port.lock),
943 .iotype = UPIO_MEM,
Ben Dooksb4975492008-07-03 12:32:51 +0100944 .uartclk = 0,
945 .fifosize = 16,
946 .ops = &s3c24xx_serial_ops,
947 .flags = UPF_BOOT_AUTOCONF,
948 .line = 0,
949 }
950 },
951 [1] = {
952 .port = {
953 .lock = __SPIN_LOCK_UNLOCKED(s3c24xx_serial_ports[1].port.lock),
954 .iotype = UPIO_MEM,
Ben Dooksb4975492008-07-03 12:32:51 +0100955 .uartclk = 0,
956 .fifosize = 16,
957 .ops = &s3c24xx_serial_ops,
958 .flags = UPF_BOOT_AUTOCONF,
959 .line = 1,
960 }
961 },
Ben Dooks03d5e772008-11-03 09:21:23 +0000962#if CONFIG_SERIAL_SAMSUNG_UARTS > 2
Ben Dooksb4975492008-07-03 12:32:51 +0100963
964 [2] = {
965 .port = {
966 .lock = __SPIN_LOCK_UNLOCKED(s3c24xx_serial_ports[2].port.lock),
967 .iotype = UPIO_MEM,
Ben Dooksb4975492008-07-03 12:32:51 +0100968 .uartclk = 0,
969 .fifosize = 16,
970 .ops = &s3c24xx_serial_ops,
971 .flags = UPF_BOOT_AUTOCONF,
972 .line = 2,
973 }
Ben Dooks03d5e772008-11-03 09:21:23 +0000974 },
975#endif
976#if CONFIG_SERIAL_SAMSUNG_UARTS > 3
977 [3] = {
978 .port = {
979 .lock = __SPIN_LOCK_UNLOCKED(s3c24xx_serial_ports[3].port.lock),
980 .iotype = UPIO_MEM,
Ben Dooks03d5e772008-11-03 09:21:23 +0000981 .uartclk = 0,
982 .fifosize = 16,
983 .ops = &s3c24xx_serial_ops,
984 .flags = UPF_BOOT_AUTOCONF,
985 .line = 3,
986 }
Ben Dooksb4975492008-07-03 12:32:51 +0100987 }
988#endif
989};
990
991/* s3c24xx_serial_resetport
992 *
Thomas Abraham0dfb3b42011-10-24 11:48:21 +0200993 * reset the fifos and other the settings.
Ben Dooksb4975492008-07-03 12:32:51 +0100994*/
995
Thomas Abraham0dfb3b42011-10-24 11:48:21 +0200996static void s3c24xx_serial_resetport(struct uart_port *port,
997 struct s3c2410_uartcfg *cfg)
Ben Dooksb4975492008-07-03 12:32:51 +0100998{
999 struct s3c24xx_uart_info *info = s3c24xx_port_to_info(port);
Thomas Abraham0dfb3b42011-10-24 11:48:21 +02001000 unsigned long ucon = rd_regl(port, S3C2410_UCON);
1001 unsigned int ucon_mask;
Ben Dooksb4975492008-07-03 12:32:51 +01001002
Thomas Abraham0dfb3b42011-10-24 11:48:21 +02001003 ucon_mask = info->clksel_mask;
1004 if (info->type == PORT_S3C2440)
1005 ucon_mask |= S3C2440_UCON0_DIVMASK;
1006
1007 ucon &= ucon_mask;
1008 wr_regl(port, S3C2410_UCON, ucon | cfg->ucon);
1009
1010 /* reset both fifos */
1011 wr_regl(port, S3C2410_UFCON, cfg->ufcon | S3C2410_UFCON_RESETBOTH);
1012 wr_regl(port, S3C2410_UFCON, cfg->ufcon);
1013
1014 /* some delay is required after fifo reset */
1015 udelay(1);
Ben Dooksb4975492008-07-03 12:32:51 +01001016}
1017
Ben Dooks305554762008-10-21 14:06:36 +01001018
1019#ifdef CONFIG_CPU_FREQ
1020
1021static int s3c24xx_serial_cpufreq_transition(struct notifier_block *nb,
1022 unsigned long val, void *data)
1023{
1024 struct s3c24xx_uart_port *port;
1025 struct uart_port *uport;
1026
1027 port = container_of(nb, struct s3c24xx_uart_port, freq_transition);
1028 uport = &port->port;
1029
1030 /* check to see if port is enabled */
1031
1032 if (port->pm_level != 0)
1033 return 0;
1034
1035 /* try and work out if the baudrate is changing, we can detect
1036 * a change in rate, but we do not have support for detecting
1037 * a disturbance in the clock-rate over the change.
1038 */
1039
Kyoungil Kim25f04ad2012-05-20 17:49:31 +09001040 if (IS_ERR(port->baudclk))
Ben Dooks305554762008-10-21 14:06:36 +01001041 goto exit;
1042
Kyoungil Kim25f04ad2012-05-20 17:49:31 +09001043 if (port->baudclk_rate == clk_get_rate(port->baudclk))
Ben Dooks305554762008-10-21 14:06:36 +01001044 goto exit;
1045
1046 if (val == CPUFREQ_PRECHANGE) {
1047 /* we should really shut the port down whilst the
1048 * frequency change is in progress. */
1049
1050 } else if (val == CPUFREQ_POSTCHANGE) {
1051 struct ktermios *termios;
1052 struct tty_struct *tty;
1053
Alan Coxebd2c8f2009-09-19 13:13:28 -07001054 if (uport->state == NULL)
Ben Dooks305554762008-10-21 14:06:36 +01001055 goto exit;
Ben Dooks305554762008-10-21 14:06:36 +01001056
Alan Coxebd2c8f2009-09-19 13:13:28 -07001057 tty = uport->state->port.tty;
Ben Dooks305554762008-10-21 14:06:36 +01001058
Ben Dooks7de40c22008-12-14 23:11:02 +00001059 if (tty == NULL)
Ben Dooks305554762008-10-21 14:06:36 +01001060 goto exit;
Ben Dooks305554762008-10-21 14:06:36 +01001061
Alan Coxadc8d742012-07-14 15:31:47 +01001062 termios = &tty->termios;
Ben Dooks305554762008-10-21 14:06:36 +01001063
1064 if (termios == NULL) {
Sachin Kamatd20925e2012-09-05 10:30:10 +05301065 dev_warn(uport->dev, "%s: no termios?\n", __func__);
Ben Dooks305554762008-10-21 14:06:36 +01001066 goto exit;
1067 }
1068
1069 s3c24xx_serial_set_termios(uport, termios, NULL);
1070 }
1071
1072 exit:
1073 return 0;
1074}
1075
1076static inline int s3c24xx_serial_cpufreq_register(struct s3c24xx_uart_port *port)
1077{
1078 port->freq_transition.notifier_call = s3c24xx_serial_cpufreq_transition;
1079
1080 return cpufreq_register_notifier(&port->freq_transition,
1081 CPUFREQ_TRANSITION_NOTIFIER);
1082}
1083
1084static inline void s3c24xx_serial_cpufreq_deregister(struct s3c24xx_uart_port *port)
1085{
1086 cpufreq_unregister_notifier(&port->freq_transition,
1087 CPUFREQ_TRANSITION_NOTIFIER);
1088}
1089
1090#else
1091static inline int s3c24xx_serial_cpufreq_register(struct s3c24xx_uart_port *port)
1092{
1093 return 0;
1094}
1095
1096static inline void s3c24xx_serial_cpufreq_deregister(struct s3c24xx_uart_port *port)
1097{
1098}
1099#endif
1100
Ben Dooksb4975492008-07-03 12:32:51 +01001101/* s3c24xx_serial_init_port
1102 *
1103 * initialise a single serial port from the platform device given
1104 */
1105
1106static int s3c24xx_serial_init_port(struct s3c24xx_uart_port *ourport,
Ben Dooksb4975492008-07-03 12:32:51 +01001107 struct platform_device *platdev)
1108{
1109 struct uart_port *port = &ourport->port;
Thomas Abrahamda121502011-11-02 19:23:25 +09001110 struct s3c2410_uartcfg *cfg = ourport->cfg;
Ben Dooksb4975492008-07-03 12:32:51 +01001111 struct resource *res;
1112 int ret;
1113
1114 dbg("s3c24xx_serial_init_port: port=%p, platdev=%p\n", port, platdev);
1115
1116 if (platdev == NULL)
1117 return -ENODEV;
1118
Ben Dooksb4975492008-07-03 12:32:51 +01001119 if (port->mapbase != 0)
1120 return 0;
1121
Ben Dooksb4975492008-07-03 12:32:51 +01001122 /* setup info for port */
1123 port->dev = &platdev->dev;
Ben Dooksb4975492008-07-03 12:32:51 +01001124
Thomas Abraham88bb4ea2011-08-10 15:51:19 +05301125 /* Startup sequence is different for s3c64xx and higher SoC's */
1126 if (s3c24xx_serial_has_interrupt_mask(port))
1127 s3c24xx_serial_ops.startup = s3c64xx_serial_startup;
1128
Ben Dooksb4975492008-07-03 12:32:51 +01001129 port->uartclk = 1;
1130
1131 if (cfg->uart_flags & UPF_CONS_FLOW) {
1132 dbg("s3c24xx_serial_init_port: enabling flow control\n");
1133 port->flags |= UPF_CONS_FLOW;
1134 }
1135
1136 /* sort our the physical and virtual addresses for each UART */
1137
1138 res = platform_get_resource(platdev, IORESOURCE_MEM, 0);
1139 if (res == NULL) {
Sachin Kamatd20925e2012-09-05 10:30:10 +05301140 dev_err(port->dev, "failed to find memory resource for uart\n");
Ben Dooksb4975492008-07-03 12:32:51 +01001141 return -EINVAL;
1142 }
1143
1144 dbg("resource %p (%lx..%lx)\n", res, res->start, res->end);
1145
Ben Dooksb690ace2008-10-21 14:07:03 +01001146 port->mapbase = res->start;
Kukjin Kim2555e662010-09-01 15:13:44 +09001147 port->membase = S3C_VA_UART + (res->start & 0xfffff);
Ben Dooksb4975492008-07-03 12:32:51 +01001148 ret = platform_get_irq(platdev, 0);
1149 if (ret < 0)
1150 port->irq = 0;
Ben Dooksb73c289c2008-10-21 14:07:04 +01001151 else {
Ben Dooksb4975492008-07-03 12:32:51 +01001152 port->irq = ret;
Ben Dooksb73c289c2008-10-21 14:07:04 +01001153 ourport->rx_irq = ret;
1154 ourport->tx_irq = ret + 1;
1155 }
Sachin Kamat9303ac12012-09-05 10:30:11 +05301156
Ben Dooksb73c289c2008-10-21 14:07:04 +01001157 ret = platform_get_irq(platdev, 1);
1158 if (ret > 0)
1159 ourport->tx_irq = ret;
Ben Dooksb4975492008-07-03 12:32:51 +01001160
1161 ourport->clk = clk_get(&platdev->dev, "uart");
1162
Thomas Abraham88bb4ea2011-08-10 15:51:19 +05301163 /* Keep all interrupts masked and cleared */
1164 if (s3c24xx_serial_has_interrupt_mask(port)) {
1165 wr_regl(port, S3C64XX_UINTM, 0xf);
1166 wr_regl(port, S3C64XX_UINTP, 0xf);
1167 wr_regl(port, S3C64XX_UINTSP, 0xf);
1168 }
1169
Ben Dooksb73c289c2008-10-21 14:07:04 +01001170 dbg("port: map=%08x, mem=%08x, irq=%d (%d,%d), clock=%ld\n",
1171 port->mapbase, port->membase, port->irq,
1172 ourport->rx_irq, ourport->tx_irq, port->uartclk);
Ben Dooksb4975492008-07-03 12:32:51 +01001173
1174 /* reset the fifos (and setup the uart) */
1175 s3c24xx_serial_resetport(port, cfg);
1176 return 0;
1177}
1178
1179static ssize_t s3c24xx_serial_show_clksrc(struct device *dev,
1180 struct device_attribute *attr,
1181 char *buf)
1182{
1183 struct uart_port *port = s3c24xx_dev_to_port(dev);
1184 struct s3c24xx_uart_port *ourport = to_ourport(port);
1185
Kyoungil Kim7cd88832012-05-20 17:45:54 +09001186 if (IS_ERR(ourport->baudclk))
1187 return -EINVAL;
1188
KeyYoung Park7b15e1d2012-05-30 17:29:55 +09001189 return snprintf(buf, PAGE_SIZE, "* %s\n",
1190 ourport->baudclk->name ?: "(null)");
Ben Dooksb4975492008-07-03 12:32:51 +01001191}
1192
1193static DEVICE_ATTR(clock_source, S_IRUGO, s3c24xx_serial_show_clksrc, NULL);
1194
Thomas Abraham26c919e2011-11-06 22:10:44 +05301195
Ben Dooksb4975492008-07-03 12:32:51 +01001196/* Device driver serial port probe */
1197
Thomas Abraham26c919e2011-11-06 22:10:44 +05301198static const struct of_device_id s3c24xx_uart_dt_match[];
Ben Dooksb4975492008-07-03 12:32:51 +01001199static int probe_index;
1200
Thomas Abraham26c919e2011-11-06 22:10:44 +05301201static inline struct s3c24xx_serial_drv_data *s3c24xx_get_driver_data(
1202 struct platform_device *pdev)
1203{
1204#ifdef CONFIG_OF
1205 if (pdev->dev.of_node) {
1206 const struct of_device_id *match;
1207 match = of_match_node(s3c24xx_uart_dt_match, pdev->dev.of_node);
1208 return (struct s3c24xx_serial_drv_data *)match->data;
1209 }
1210#endif
1211 return (struct s3c24xx_serial_drv_data *)
1212 platform_get_device_id(pdev)->driver_data;
1213}
1214
Thomas Abrahamda121502011-11-02 19:23:25 +09001215static int s3c24xx_serial_probe(struct platform_device *pdev)
Ben Dooksb4975492008-07-03 12:32:51 +01001216{
1217 struct s3c24xx_uart_port *ourport;
1218 int ret;
1219
Thomas Abrahamda121502011-11-02 19:23:25 +09001220 dbg("s3c24xx_serial_probe(%p) %d\n", pdev, probe_index);
Ben Dooksb4975492008-07-03 12:32:51 +01001221
1222 ourport = &s3c24xx_serial_ports[probe_index];
Thomas Abrahamda121502011-11-02 19:23:25 +09001223
Thomas Abraham26c919e2011-11-06 22:10:44 +05301224 ourport->drv_data = s3c24xx_get_driver_data(pdev);
1225 if (!ourport->drv_data) {
1226 dev_err(&pdev->dev, "could not find driver data\n");
1227 return -ENODEV;
1228 }
Thomas Abrahamda121502011-11-02 19:23:25 +09001229
Kyoungil Kim7cd88832012-05-20 17:45:54 +09001230 ourport->baudclk = ERR_PTR(-EINVAL);
Thomas Abrahamda121502011-11-02 19:23:25 +09001231 ourport->info = ourport->drv_data->info;
1232 ourport->cfg = (pdev->dev.platform_data) ?
1233 (struct s3c2410_uartcfg *)pdev->dev.platform_data :
1234 ourport->drv_data->def_cfg;
1235
1236 ourport->port.fifosize = (ourport->info->fifosize) ?
1237 ourport->info->fifosize :
1238 ourport->drv_data->fifosize[probe_index];
1239
Ben Dooksb4975492008-07-03 12:32:51 +01001240 probe_index++;
1241
1242 dbg("%s: initialising port %p...\n", __func__, ourport);
1243
Thomas Abrahamda121502011-11-02 19:23:25 +09001244 ret = s3c24xx_serial_init_port(ourport, pdev);
Ben Dooksb4975492008-07-03 12:32:51 +01001245 if (ret < 0)
1246 goto probe_err;
1247
1248 dbg("%s: adding port\n", __func__);
1249 uart_add_one_port(&s3c24xx_uart_drv, &ourport->port);
Thomas Abrahamda121502011-11-02 19:23:25 +09001250 platform_set_drvdata(pdev, &ourport->port);
Ben Dooksb4975492008-07-03 12:32:51 +01001251
Thomas Abrahamda121502011-11-02 19:23:25 +09001252 ret = device_create_file(&pdev->dev, &dev_attr_clock_source);
Ben Dooksb4975492008-07-03 12:32:51 +01001253 if (ret < 0)
Thomas Abrahamda121502011-11-02 19:23:25 +09001254 dev_err(&pdev->dev, "failed to add clock source attr.\n");
Ben Dooksb4975492008-07-03 12:32:51 +01001255
Ben Dooks305554762008-10-21 14:06:36 +01001256 ret = s3c24xx_serial_cpufreq_register(ourport);
1257 if (ret < 0)
Thomas Abrahamda121502011-11-02 19:23:25 +09001258 dev_err(&pdev->dev, "failed to add cpufreq notifier\n");
Ben Dooks305554762008-10-21 14:06:36 +01001259
Ben Dooksb4975492008-07-03 12:32:51 +01001260 return 0;
1261
1262 probe_err:
1263 return ret;
1264}
1265
Bill Pembertonae8d8a12012-11-19 13:26:18 -05001266static int s3c24xx_serial_remove(struct platform_device *dev)
Ben Dooksb4975492008-07-03 12:32:51 +01001267{
1268 struct uart_port *port = s3c24xx_dev_to_port(&dev->dev);
1269
1270 if (port) {
Ben Dooks305554762008-10-21 14:06:36 +01001271 s3c24xx_serial_cpufreq_deregister(to_ourport(port));
Ben Dooksb4975492008-07-03 12:32:51 +01001272 device_remove_file(&dev->dev, &dev_attr_clock_source);
1273 uart_remove_one_port(&s3c24xx_uart_drv, port);
1274 }
1275
1276 return 0;
1277}
1278
Ben Dooksb4975492008-07-03 12:32:51 +01001279/* UART power management code */
MyungJoo Hamaef7fe52011-06-29 15:28:24 +09001280#ifdef CONFIG_PM_SLEEP
1281static int s3c24xx_serial_suspend(struct device *dev)
Ben Dooksb4975492008-07-03 12:32:51 +01001282{
MyungJoo Hamaef7fe52011-06-29 15:28:24 +09001283 struct uart_port *port = s3c24xx_dev_to_port(dev);
Ben Dooksb4975492008-07-03 12:32:51 +01001284
1285 if (port)
1286 uart_suspend_port(&s3c24xx_uart_drv, port);
1287
1288 return 0;
1289}
1290
MyungJoo Hamaef7fe52011-06-29 15:28:24 +09001291static int s3c24xx_serial_resume(struct device *dev)
Ben Dooksb4975492008-07-03 12:32:51 +01001292{
MyungJoo Hamaef7fe52011-06-29 15:28:24 +09001293 struct uart_port *port = s3c24xx_dev_to_port(dev);
Ben Dooksb4975492008-07-03 12:32:51 +01001294 struct s3c24xx_uart_port *ourport = to_ourport(port);
1295
1296 if (port) {
Thomas Abraham9484b0092012-10-03 07:40:04 +09001297 clk_prepare_enable(ourport->clk);
Ben Dooksb4975492008-07-03 12:32:51 +01001298 s3c24xx_serial_resetport(port, s3c24xx_port_to_cfg(port));
Thomas Abraham9484b0092012-10-03 07:40:04 +09001299 clk_disable_unprepare(ourport->clk);
Ben Dooksb4975492008-07-03 12:32:51 +01001300
1301 uart_resume_port(&s3c24xx_uart_drv, port);
1302 }
1303
1304 return 0;
1305}
MyungJoo Hamaef7fe52011-06-29 15:28:24 +09001306
1307static const struct dev_pm_ops s3c24xx_serial_pm_ops = {
1308 .suspend = s3c24xx_serial_suspend,
1309 .resume = s3c24xx_serial_resume,
1310};
Kukjin Kimb882fc12011-07-28 08:50:38 +09001311#define SERIAL_SAMSUNG_PM_OPS (&s3c24xx_serial_pm_ops)
1312
MyungJoo Hamaef7fe52011-06-29 15:28:24 +09001313#else /* !CONFIG_PM_SLEEP */
Kukjin Kimb882fc12011-07-28 08:50:38 +09001314
1315#define SERIAL_SAMSUNG_PM_OPS NULL
MyungJoo Hamaef7fe52011-06-29 15:28:24 +09001316#endif /* CONFIG_PM_SLEEP */
Ben Dooksb4975492008-07-03 12:32:51 +01001317
Ben Dooksb4975492008-07-03 12:32:51 +01001318/* Console code */
1319
1320#ifdef CONFIG_SERIAL_SAMSUNG_CONSOLE
1321
1322static struct uart_port *cons_uart;
1323
1324static int
1325s3c24xx_serial_console_txrdy(struct uart_port *port, unsigned int ufcon)
1326{
1327 struct s3c24xx_uart_info *info = s3c24xx_port_to_info(port);
1328 unsigned long ufstat, utrstat;
1329
1330 if (ufcon & S3C2410_UFCON_FIFOMODE) {
Uwe Kleine-König9ddc5b62010-01-20 17:02:24 +01001331 /* fifo mode - check amount of data in fifo registers... */
Ben Dooksb4975492008-07-03 12:32:51 +01001332
1333 ufstat = rd_regl(port, S3C2410_UFSTAT);
1334 return (ufstat & info->tx_fifofull) ? 0 : 1;
1335 }
1336
1337 /* in non-fifo mode, we go and use the tx buffer empty */
1338
1339 utrstat = rd_regl(port, S3C2410_UTRSTAT);
1340 return (utrstat & S3C2410_UTRSTAT_TXE) ? 1 : 0;
1341}
1342
Julien Pichon93b5c032012-09-21 23:22:31 -07001343#ifdef CONFIG_CONSOLE_POLL
1344/*
1345 * Console polling routines for writing and reading from the uart while
1346 * in an interrupt or debug context.
1347 */
1348
1349static int s3c24xx_serial_get_poll_char(struct uart_port *port)
1350{
1351 struct s3c24xx_uart_port *ourport = to_ourport(port);
1352 unsigned int ufstat;
1353
1354 ufstat = rd_regl(port, S3C2410_UFSTAT);
1355 if (s3c24xx_serial_rx_fifocnt(ourport, ufstat) == 0)
1356 return NO_POLL_CHAR;
1357
1358 return rd_regb(port, S3C2410_URXH);
1359}
1360
1361static void s3c24xx_serial_put_poll_char(struct uart_port *port,
1362 unsigned char c)
1363{
1364 unsigned int ufcon = rd_regl(cons_uart, S3C2410_UFCON);
1365
1366 while (!s3c24xx_serial_console_txrdy(port, ufcon))
1367 cpu_relax();
1368 wr_regb(cons_uart, S3C2410_UTXH, c);
1369}
1370
1371#endif /* CONFIG_CONSOLE_POLL */
1372
Ben Dooksb4975492008-07-03 12:32:51 +01001373static void
1374s3c24xx_serial_console_putchar(struct uart_port *port, int ch)
1375{
1376 unsigned int ufcon = rd_regl(cons_uart, S3C2410_UFCON);
1377 while (!s3c24xx_serial_console_txrdy(port, ufcon))
1378 barrier();
1379 wr_regb(cons_uart, S3C2410_UTXH, ch);
1380}
1381
1382static void
1383s3c24xx_serial_console_write(struct console *co, const char *s,
1384 unsigned int count)
1385{
1386 uart_console_write(cons_uart, s, count, s3c24xx_serial_console_putchar);
1387}
1388
1389static void __init
1390s3c24xx_serial_get_options(struct uart_port *port, int *baud,
1391 int *parity, int *bits)
1392{
Ben Dooksb4975492008-07-03 12:32:51 +01001393 struct clk *clk;
1394 unsigned int ulcon;
1395 unsigned int ucon;
1396 unsigned int ubrdiv;
1397 unsigned long rate;
Thomas Abraham5f5a7a52011-10-24 11:47:46 +02001398 unsigned int clk_sel;
1399 char clk_name[MAX_CLK_NAME_LENGTH];
Ben Dooksb4975492008-07-03 12:32:51 +01001400
1401 ulcon = rd_regl(port, S3C2410_ULCON);
1402 ucon = rd_regl(port, S3C2410_UCON);
1403 ubrdiv = rd_regl(port, S3C2410_UBRDIV);
1404
1405 dbg("s3c24xx_serial_get_options: port=%p\n"
1406 "registers: ulcon=%08x, ucon=%08x, ubdriv=%08x\n",
1407 port, ulcon, ucon, ubrdiv);
1408
1409 if ((ucon & 0xf) != 0) {
1410 /* consider the serial port configured if the tx/rx mode set */
1411
1412 switch (ulcon & S3C2410_LCON_CSMASK) {
1413 case S3C2410_LCON_CS5:
1414 *bits = 5;
1415 break;
1416 case S3C2410_LCON_CS6:
1417 *bits = 6;
1418 break;
1419 case S3C2410_LCON_CS7:
1420 *bits = 7;
1421 break;
1422 default:
1423 case S3C2410_LCON_CS8:
1424 *bits = 8;
1425 break;
1426 }
1427
1428 switch (ulcon & S3C2410_LCON_PMASK) {
1429 case S3C2410_LCON_PEVEN:
1430 *parity = 'e';
1431 break;
1432
1433 case S3C2410_LCON_PODD:
1434 *parity = 'o';
1435 break;
1436
1437 case S3C2410_LCON_PNONE:
1438 default:
1439 *parity = 'n';
1440 }
1441
1442 /* now calculate the baud rate */
1443
Thomas Abraham5f5a7a52011-10-24 11:47:46 +02001444 clk_sel = s3c24xx_serial_getsource(port);
1445 sprintf(clk_name, "clk_uart_baud%d", clk_sel);
Ben Dooksb4975492008-07-03 12:32:51 +01001446
Thomas Abraham5f5a7a52011-10-24 11:47:46 +02001447 clk = clk_get(port->dev, clk_name);
Kyoungil Kim7cd88832012-05-20 17:45:54 +09001448 if (!IS_ERR(clk))
Thomas Abraham5f5a7a52011-10-24 11:47:46 +02001449 rate = clk_get_rate(clk);
Ben Dooksb4975492008-07-03 12:32:51 +01001450 else
1451 rate = 1;
1452
Ben Dooksb4975492008-07-03 12:32:51 +01001453 *baud = rate / (16 * (ubrdiv + 1));
1454 dbg("calculated baud %d\n", *baud);
1455 }
1456
1457}
1458
Ben Dooksb4975492008-07-03 12:32:51 +01001459static int __init
1460s3c24xx_serial_console_setup(struct console *co, char *options)
1461{
1462 struct uart_port *port;
1463 int baud = 9600;
1464 int bits = 8;
1465 int parity = 'n';
1466 int flow = 'n';
1467
1468 dbg("s3c24xx_serial_console_setup: co=%p (%d), %s\n",
1469 co, co->index, options);
1470
1471 /* is this a valid port */
1472
Ben Dooks03d5e772008-11-03 09:21:23 +00001473 if (co->index == -1 || co->index >= CONFIG_SERIAL_SAMSUNG_UARTS)
Ben Dooksb4975492008-07-03 12:32:51 +01001474 co->index = 0;
1475
1476 port = &s3c24xx_serial_ports[co->index].port;
1477
1478 /* is the port configured? */
1479
Thomas Abrahamee430f12011-06-14 19:12:26 +09001480 if (port->mapbase == 0x0)
1481 return -ENODEV;
Ben Dooksb4975492008-07-03 12:32:51 +01001482
1483 cons_uart = port;
1484
1485 dbg("s3c24xx_serial_console_setup: port=%p (%d)\n", port, co->index);
1486
1487 /*
1488 * Check whether an invalid uart number has been specified, and
1489 * if so, search for the first available port that does have
1490 * console support.
1491 */
1492 if (options)
1493 uart_parse_options(options, &baud, &parity, &bits, &flow);
1494 else
1495 s3c24xx_serial_get_options(port, &baud, &parity, &bits);
1496
1497 dbg("s3c24xx_serial_console_setup: baud %d\n", baud);
1498
1499 return uart_set_options(port, co, baud, parity, bits, flow);
1500}
1501
Ben Dooksb4975492008-07-03 12:32:51 +01001502static struct console s3c24xx_serial_console = {
1503 .name = S3C24XX_SERIAL_NAME,
1504 .device = uart_console_device,
1505 .flags = CON_PRINTBUFFER,
1506 .index = -1,
1507 .write = s3c24xx_serial_console_write,
Thomas Abraham5822a5d2011-06-14 19:12:26 +09001508 .setup = s3c24xx_serial_console_setup,
1509 .data = &s3c24xx_uart_drv,
Ben Dooksb4975492008-07-03 12:32:51 +01001510};
Ben Dooksb4975492008-07-03 12:32:51 +01001511#endif /* CONFIG_SERIAL_SAMSUNG_CONSOLE */
1512
Thomas Abrahamda121502011-11-02 19:23:25 +09001513#ifdef CONFIG_CPU_S3C2410
1514static struct s3c24xx_serial_drv_data s3c2410_serial_drv_data = {
1515 .info = &(struct s3c24xx_uart_info) {
1516 .name = "Samsung S3C2410 UART",
1517 .type = PORT_S3C2410,
1518 .fifosize = 16,
1519 .rx_fifomask = S3C2410_UFSTAT_RXMASK,
1520 .rx_fifoshift = S3C2410_UFSTAT_RXSHIFT,
1521 .rx_fifofull = S3C2410_UFSTAT_RXFULL,
1522 .tx_fifofull = S3C2410_UFSTAT_TXFULL,
1523 .tx_fifomask = S3C2410_UFSTAT_TXMASK,
1524 .tx_fifoshift = S3C2410_UFSTAT_TXSHIFT,
1525 .def_clk_sel = S3C2410_UCON_CLKSEL0,
1526 .num_clks = 2,
1527 .clksel_mask = S3C2410_UCON_CLKMASK,
1528 .clksel_shift = S3C2410_UCON_CLKSHIFT,
1529 },
1530 .def_cfg = &(struct s3c2410_uartcfg) {
1531 .ucon = S3C2410_UCON_DEFAULT,
1532 .ufcon = S3C2410_UFCON_DEFAULT,
1533 },
1534};
1535#define S3C2410_SERIAL_DRV_DATA ((kernel_ulong_t)&s3c2410_serial_drv_data)
1536#else
1537#define S3C2410_SERIAL_DRV_DATA (kernel_ulong_t)NULL
1538#endif
1539
1540#ifdef CONFIG_CPU_S3C2412
1541static struct s3c24xx_serial_drv_data s3c2412_serial_drv_data = {
1542 .info = &(struct s3c24xx_uart_info) {
1543 .name = "Samsung S3C2412 UART",
1544 .type = PORT_S3C2412,
1545 .fifosize = 64,
1546 .has_divslot = 1,
1547 .rx_fifomask = S3C2440_UFSTAT_RXMASK,
1548 .rx_fifoshift = S3C2440_UFSTAT_RXSHIFT,
1549 .rx_fifofull = S3C2440_UFSTAT_RXFULL,
1550 .tx_fifofull = S3C2440_UFSTAT_TXFULL,
1551 .tx_fifomask = S3C2440_UFSTAT_TXMASK,
1552 .tx_fifoshift = S3C2440_UFSTAT_TXSHIFT,
1553 .def_clk_sel = S3C2410_UCON_CLKSEL2,
1554 .num_clks = 4,
1555 .clksel_mask = S3C2412_UCON_CLKMASK,
1556 .clksel_shift = S3C2412_UCON_CLKSHIFT,
1557 },
1558 .def_cfg = &(struct s3c2410_uartcfg) {
1559 .ucon = S3C2410_UCON_DEFAULT,
1560 .ufcon = S3C2410_UFCON_DEFAULT,
1561 },
1562};
1563#define S3C2412_SERIAL_DRV_DATA ((kernel_ulong_t)&s3c2412_serial_drv_data)
1564#else
1565#define S3C2412_SERIAL_DRV_DATA (kernel_ulong_t)NULL
1566#endif
1567
1568#if defined(CONFIG_CPU_S3C2440) || defined(CONFIG_CPU_S3C2416) || \
Denis 'GNUtoo' Cariklib26469a2012-02-23 08:23:52 +01001569 defined(CONFIG_CPU_S3C2443) || defined(CONFIG_CPU_S3C2442)
Thomas Abrahamda121502011-11-02 19:23:25 +09001570static struct s3c24xx_serial_drv_data s3c2440_serial_drv_data = {
1571 .info = &(struct s3c24xx_uart_info) {
1572 .name = "Samsung S3C2440 UART",
1573 .type = PORT_S3C2440,
1574 .fifosize = 64,
1575 .has_divslot = 1,
1576 .rx_fifomask = S3C2440_UFSTAT_RXMASK,
1577 .rx_fifoshift = S3C2440_UFSTAT_RXSHIFT,
1578 .rx_fifofull = S3C2440_UFSTAT_RXFULL,
1579 .tx_fifofull = S3C2440_UFSTAT_TXFULL,
1580 .tx_fifomask = S3C2440_UFSTAT_TXMASK,
1581 .tx_fifoshift = S3C2440_UFSTAT_TXSHIFT,
1582 .def_clk_sel = S3C2410_UCON_CLKSEL2,
1583 .num_clks = 4,
1584 .clksel_mask = S3C2412_UCON_CLKMASK,
1585 .clksel_shift = S3C2412_UCON_CLKSHIFT,
1586 },
1587 .def_cfg = &(struct s3c2410_uartcfg) {
1588 .ucon = S3C2410_UCON_DEFAULT,
1589 .ufcon = S3C2410_UFCON_DEFAULT,
1590 },
1591};
1592#define S3C2440_SERIAL_DRV_DATA ((kernel_ulong_t)&s3c2440_serial_drv_data)
1593#else
1594#define S3C2440_SERIAL_DRV_DATA (kernel_ulong_t)NULL
1595#endif
1596
1597#if defined(CONFIG_CPU_S3C6400) || defined(CONFIG_CPU_S3C6410) || \
1598 defined(CONFIG_CPU_S5P6440) || defined(CONFIG_CPU_S5P6450) || \
1599 defined(CONFIG_CPU_S5PC100)
1600static struct s3c24xx_serial_drv_data s3c6400_serial_drv_data = {
1601 .info = &(struct s3c24xx_uart_info) {
1602 .name = "Samsung S3C6400 UART",
1603 .type = PORT_S3C6400,
1604 .fifosize = 64,
1605 .has_divslot = 1,
1606 .rx_fifomask = S3C2440_UFSTAT_RXMASK,
1607 .rx_fifoshift = S3C2440_UFSTAT_RXSHIFT,
1608 .rx_fifofull = S3C2440_UFSTAT_RXFULL,
1609 .tx_fifofull = S3C2440_UFSTAT_TXFULL,
1610 .tx_fifomask = S3C2440_UFSTAT_TXMASK,
1611 .tx_fifoshift = S3C2440_UFSTAT_TXSHIFT,
1612 .def_clk_sel = S3C2410_UCON_CLKSEL2,
1613 .num_clks = 4,
1614 .clksel_mask = S3C6400_UCON_CLKMASK,
1615 .clksel_shift = S3C6400_UCON_CLKSHIFT,
1616 },
1617 .def_cfg = &(struct s3c2410_uartcfg) {
1618 .ucon = S3C2410_UCON_DEFAULT,
1619 .ufcon = S3C2410_UFCON_DEFAULT,
1620 },
1621};
1622#define S3C6400_SERIAL_DRV_DATA ((kernel_ulong_t)&s3c6400_serial_drv_data)
1623#else
1624#define S3C6400_SERIAL_DRV_DATA (kernel_ulong_t)NULL
1625#endif
1626
1627#ifdef CONFIG_CPU_S5PV210
1628static struct s3c24xx_serial_drv_data s5pv210_serial_drv_data = {
1629 .info = &(struct s3c24xx_uart_info) {
1630 .name = "Samsung S5PV210 UART",
1631 .type = PORT_S3C6400,
1632 .has_divslot = 1,
1633 .rx_fifomask = S5PV210_UFSTAT_RXMASK,
1634 .rx_fifoshift = S5PV210_UFSTAT_RXSHIFT,
1635 .rx_fifofull = S5PV210_UFSTAT_RXFULL,
1636 .tx_fifofull = S5PV210_UFSTAT_TXFULL,
1637 .tx_fifomask = S5PV210_UFSTAT_TXMASK,
1638 .tx_fifoshift = S5PV210_UFSTAT_TXSHIFT,
1639 .def_clk_sel = S3C2410_UCON_CLKSEL0,
1640 .num_clks = 2,
1641 .clksel_mask = S5PV210_UCON_CLKMASK,
1642 .clksel_shift = S5PV210_UCON_CLKSHIFT,
1643 },
1644 .def_cfg = &(struct s3c2410_uartcfg) {
1645 .ucon = S5PV210_UCON_DEFAULT,
1646 .ufcon = S5PV210_UFCON_DEFAULT,
1647 },
1648 .fifosize = { 256, 64, 16, 16 },
1649};
1650#define S5PV210_SERIAL_DRV_DATA ((kernel_ulong_t)&s5pv210_serial_drv_data)
1651#else
1652#define S5PV210_SERIAL_DRV_DATA (kernel_ulong_t)NULL
1653#endif
1654
Kukjin Kim5f7b6d12012-02-01 00:11:23 +09001655#if defined(CONFIG_CPU_EXYNOS4210) || defined(CONFIG_SOC_EXYNOS4212) || \
Kukjin Kim2edb36c2012-11-15 15:48:56 +09001656 defined(CONFIG_SOC_EXYNOS4412) || defined(CONFIG_SOC_EXYNOS5250) || \
1657 defined(CONFIG_SOC_EXYNOS5440)
Thomas Abrahamda121502011-11-02 19:23:25 +09001658static struct s3c24xx_serial_drv_data exynos4210_serial_drv_data = {
1659 .info = &(struct s3c24xx_uart_info) {
1660 .name = "Samsung Exynos4 UART",
1661 .type = PORT_S3C6400,
1662 .has_divslot = 1,
1663 .rx_fifomask = S5PV210_UFSTAT_RXMASK,
1664 .rx_fifoshift = S5PV210_UFSTAT_RXSHIFT,
1665 .rx_fifofull = S5PV210_UFSTAT_RXFULL,
1666 .tx_fifofull = S5PV210_UFSTAT_TXFULL,
1667 .tx_fifomask = S5PV210_UFSTAT_TXMASK,
1668 .tx_fifoshift = S5PV210_UFSTAT_TXSHIFT,
1669 .def_clk_sel = S3C2410_UCON_CLKSEL0,
1670 .num_clks = 1,
1671 .clksel_mask = 0,
1672 .clksel_shift = 0,
1673 },
1674 .def_cfg = &(struct s3c2410_uartcfg) {
1675 .ucon = S5PV210_UCON_DEFAULT,
1676 .ufcon = S5PV210_UFCON_DEFAULT,
1677 .has_fracval = 1,
1678 },
1679 .fifosize = { 256, 64, 16, 16 },
1680};
1681#define EXYNOS4210_SERIAL_DRV_DATA ((kernel_ulong_t)&exynos4210_serial_drv_data)
1682#else
1683#define EXYNOS4210_SERIAL_DRV_DATA (kernel_ulong_t)NULL
1684#endif
1685
1686static struct platform_device_id s3c24xx_serial_driver_ids[] = {
1687 {
1688 .name = "s3c2410-uart",
1689 .driver_data = S3C2410_SERIAL_DRV_DATA,
1690 }, {
1691 .name = "s3c2412-uart",
1692 .driver_data = S3C2412_SERIAL_DRV_DATA,
1693 }, {
1694 .name = "s3c2440-uart",
1695 .driver_data = S3C2440_SERIAL_DRV_DATA,
1696 }, {
1697 .name = "s3c6400-uart",
1698 .driver_data = S3C6400_SERIAL_DRV_DATA,
1699 }, {
1700 .name = "s5pv210-uart",
1701 .driver_data = S5PV210_SERIAL_DRV_DATA,
1702 }, {
1703 .name = "exynos4210-uart",
1704 .driver_data = EXYNOS4210_SERIAL_DRV_DATA,
1705 },
1706 { },
1707};
1708MODULE_DEVICE_TABLE(platform, s3c24xx_serial_driver_ids);
1709
Thomas Abraham26c919e2011-11-06 22:10:44 +05301710#ifdef CONFIG_OF
1711static const struct of_device_id s3c24xx_uart_dt_match[] = {
Heiko Stübner666ca0b2012-11-22 11:37:44 +01001712 { .compatible = "samsung,s3c2410-uart",
1713 .data = (void *)S3C2410_SERIAL_DRV_DATA },
1714 { .compatible = "samsung,s3c2412-uart",
1715 .data = (void *)S3C2412_SERIAL_DRV_DATA },
1716 { .compatible = "samsung,s3c2440-uart",
1717 .data = (void *)S3C2440_SERIAL_DRV_DATA },
1718 { .compatible = "samsung,s3c6400-uart",
1719 .data = (void *)S3C6400_SERIAL_DRV_DATA },
1720 { .compatible = "samsung,s5pv210-uart",
1721 .data = (void *)S5PV210_SERIAL_DRV_DATA },
Thomas Abraham26c919e2011-11-06 22:10:44 +05301722 { .compatible = "samsung,exynos4210-uart",
Mark Browna169a882011-11-08 17:00:14 +09001723 .data = (void *)EXYNOS4210_SERIAL_DRV_DATA },
Thomas Abraham26c919e2011-11-06 22:10:44 +05301724 {},
1725};
1726MODULE_DEVICE_TABLE(of, s3c24xx_uart_dt_match);
1727#else
1728#define s3c24xx_uart_dt_match NULL
1729#endif
1730
Thomas Abrahamda121502011-11-02 19:23:25 +09001731static struct platform_driver samsung_serial_driver = {
1732 .probe = s3c24xx_serial_probe,
Bill Pemberton2d47b712012-11-19 13:21:34 -05001733 .remove = s3c24xx_serial_remove,
Thomas Abrahamda121502011-11-02 19:23:25 +09001734 .id_table = s3c24xx_serial_driver_ids,
1735 .driver = {
1736 .name = "samsung-uart",
1737 .owner = THIS_MODULE,
1738 .pm = SERIAL_SAMSUNG_PM_OPS,
Thomas Abraham26c919e2011-11-06 22:10:44 +05301739 .of_match_table = s3c24xx_uart_dt_match,
Thomas Abrahamda121502011-11-02 19:23:25 +09001740 },
1741};
1742
1743/* module initialisation code */
1744
1745static int __init s3c24xx_serial_modinit(void)
1746{
1747 int ret;
1748
1749 ret = uart_register_driver(&s3c24xx_uart_drv);
1750 if (ret < 0) {
Sachin Kamatd20925e2012-09-05 10:30:10 +05301751 pr_err("Failed to register Samsung UART driver\n");
Sachin Kamate740d8f2012-09-12 12:00:01 +05301752 return ret;
Thomas Abrahamda121502011-11-02 19:23:25 +09001753 }
1754
1755 return platform_driver_register(&samsung_serial_driver);
1756}
1757
1758static void __exit s3c24xx_serial_modexit(void)
1759{
1760 uart_unregister_driver(&s3c24xx_uart_drv);
1761}
1762
1763module_init(s3c24xx_serial_modinit);
1764module_exit(s3c24xx_serial_modexit);
1765
1766MODULE_ALIAS("platform:samsung-uart");
Ben Dooksb4975492008-07-03 12:32:51 +01001767MODULE_DESCRIPTION("Samsung SoC Serial port driver");
1768MODULE_AUTHOR("Ben Dooks <ben@simtec.co.uk>");
1769MODULE_LICENSE("GPL v2");