blob: 9368c3e81cc89fb8258c8229a06a44473fb891e6 [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 Dooks30555472008-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 Dooks30555472008-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 Abraham9484b002012-10-03 07:40:04 +0900541 clk_disable_unprepare(ourport->baudclk);
Ben Dooksb4975492008-07-03 12:32:51 +0100542
Thomas Abraham9484b002012-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 Abraham9484b002012-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 Abraham9484b002012-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 Dooks090f848d2008-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 Dooks090f848d2008-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 Abraham9484b002012-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 Abraham9484b002012-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 Dooks30555472008-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 Dooks090f848d2008-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 Dooks090f848d2008-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 Dooks090f848d2008-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 Dooks090f848d2008-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);
Kukjin Kim7b246a12012-04-03 18:14:24 -07001009 wr_regl(port, S3C2410_ULCON, cfg->ulcon);
Thomas Abraham0dfb3b42011-10-24 11:48:21 +02001010
1011 /* reset both fifos */
1012 wr_regl(port, S3C2410_UFCON, cfg->ufcon | S3C2410_UFCON_RESETBOTH);
1013 wr_regl(port, S3C2410_UFCON, cfg->ufcon);
1014
1015 /* some delay is required after fifo reset */
1016 udelay(1);
Ben Dooksb4975492008-07-03 12:32:51 +01001017}
1018
Ben Dooks30555472008-10-21 14:06:36 +01001019
1020#ifdef CONFIG_CPU_FREQ
1021
1022static int s3c24xx_serial_cpufreq_transition(struct notifier_block *nb,
1023 unsigned long val, void *data)
1024{
1025 struct s3c24xx_uart_port *port;
1026 struct uart_port *uport;
1027
1028 port = container_of(nb, struct s3c24xx_uart_port, freq_transition);
1029 uport = &port->port;
1030
1031 /* check to see if port is enabled */
1032
1033 if (port->pm_level != 0)
1034 return 0;
1035
1036 /* try and work out if the baudrate is changing, we can detect
1037 * a change in rate, but we do not have support for detecting
1038 * a disturbance in the clock-rate over the change.
1039 */
1040
Kyoungil Kim25f04ad2012-05-20 17:49:31 +09001041 if (IS_ERR(port->baudclk))
Ben Dooks30555472008-10-21 14:06:36 +01001042 goto exit;
1043
Kyoungil Kim25f04ad2012-05-20 17:49:31 +09001044 if (port->baudclk_rate == clk_get_rate(port->baudclk))
Ben Dooks30555472008-10-21 14:06:36 +01001045 goto exit;
1046
1047 if (val == CPUFREQ_PRECHANGE) {
1048 /* we should really shut the port down whilst the
1049 * frequency change is in progress. */
1050
1051 } else if (val == CPUFREQ_POSTCHANGE) {
1052 struct ktermios *termios;
1053 struct tty_struct *tty;
1054
Alan Coxebd2c8f2009-09-19 13:13:28 -07001055 if (uport->state == NULL)
Ben Dooks30555472008-10-21 14:06:36 +01001056 goto exit;
Ben Dooks30555472008-10-21 14:06:36 +01001057
Alan Coxebd2c8f2009-09-19 13:13:28 -07001058 tty = uport->state->port.tty;
Ben Dooks30555472008-10-21 14:06:36 +01001059
Ben Dooks7de40c22008-12-14 23:11:02 +00001060 if (tty == NULL)
Ben Dooks30555472008-10-21 14:06:36 +01001061 goto exit;
Ben Dooks30555472008-10-21 14:06:36 +01001062
Alan Coxadc8d742012-07-14 15:31:47 +01001063 termios = &tty->termios;
Ben Dooks30555472008-10-21 14:06:36 +01001064
1065 if (termios == NULL) {
Sachin Kamatd20925e2012-09-05 10:30:10 +05301066 dev_warn(uport->dev, "%s: no termios?\n", __func__);
Ben Dooks30555472008-10-21 14:06:36 +01001067 goto exit;
1068 }
1069
1070 s3c24xx_serial_set_termios(uport, termios, NULL);
1071 }
1072
1073 exit:
1074 return 0;
1075}
1076
1077static inline int s3c24xx_serial_cpufreq_register(struct s3c24xx_uart_port *port)
1078{
1079 port->freq_transition.notifier_call = s3c24xx_serial_cpufreq_transition;
1080
1081 return cpufreq_register_notifier(&port->freq_transition,
1082 CPUFREQ_TRANSITION_NOTIFIER);
1083}
1084
1085static inline void s3c24xx_serial_cpufreq_deregister(struct s3c24xx_uart_port *port)
1086{
1087 cpufreq_unregister_notifier(&port->freq_transition,
1088 CPUFREQ_TRANSITION_NOTIFIER);
1089}
1090
1091#else
1092static inline int s3c24xx_serial_cpufreq_register(struct s3c24xx_uart_port *port)
1093{
1094 return 0;
1095}
1096
1097static inline void s3c24xx_serial_cpufreq_deregister(struct s3c24xx_uart_port *port)
1098{
1099}
1100#endif
1101
Ben Dooksb4975492008-07-03 12:32:51 +01001102/* s3c24xx_serial_init_port
1103 *
1104 * initialise a single serial port from the platform device given
1105 */
1106
1107static int s3c24xx_serial_init_port(struct s3c24xx_uart_port *ourport,
Ben Dooksb4975492008-07-03 12:32:51 +01001108 struct platform_device *platdev)
1109{
1110 struct uart_port *port = &ourport->port;
Thomas Abrahamda121502011-11-02 19:23:25 +09001111 struct s3c2410_uartcfg *cfg = ourport->cfg;
Ben Dooksb4975492008-07-03 12:32:51 +01001112 struct resource *res;
1113 int ret;
1114
1115 dbg("s3c24xx_serial_init_port: port=%p, platdev=%p\n", port, platdev);
1116
1117 if (platdev == NULL)
1118 return -ENODEV;
1119
Ben Dooksb4975492008-07-03 12:32:51 +01001120 if (port->mapbase != 0)
1121 return 0;
1122
Ben Dooksb4975492008-07-03 12:32:51 +01001123 /* setup info for port */
1124 port->dev = &platdev->dev;
Ben Dooksb4975492008-07-03 12:32:51 +01001125
Thomas Abraham88bb4ea2011-08-10 15:51:19 +05301126 /* Startup sequence is different for s3c64xx and higher SoC's */
1127 if (s3c24xx_serial_has_interrupt_mask(port))
1128 s3c24xx_serial_ops.startup = s3c64xx_serial_startup;
1129
Ben Dooksb4975492008-07-03 12:32:51 +01001130 port->uartclk = 1;
1131
1132 if (cfg->uart_flags & UPF_CONS_FLOW) {
1133 dbg("s3c24xx_serial_init_port: enabling flow control\n");
1134 port->flags |= UPF_CONS_FLOW;
1135 }
1136
1137 /* sort our the physical and virtual addresses for each UART */
1138
1139 res = platform_get_resource(platdev, IORESOURCE_MEM, 0);
1140 if (res == NULL) {
Sachin Kamatd20925e2012-09-05 10:30:10 +05301141 dev_err(port->dev, "failed to find memory resource for uart\n");
Ben Dooksb4975492008-07-03 12:32:51 +01001142 return -EINVAL;
1143 }
1144
1145 dbg("resource %p (%lx..%lx)\n", res, res->start, res->end);
1146
Ben Dooksb690ace2008-10-21 14:07:03 +01001147 port->mapbase = res->start;
Kukjin Kim2555e662010-09-01 15:13:44 +09001148 port->membase = S3C_VA_UART + (res->start & 0xfffff);
Ben Dooksb4975492008-07-03 12:32:51 +01001149 ret = platform_get_irq(platdev, 0);
1150 if (ret < 0)
1151 port->irq = 0;
Ben Dooksb73c289c2008-10-21 14:07:04 +01001152 else {
Ben Dooksb4975492008-07-03 12:32:51 +01001153 port->irq = ret;
Ben Dooksb73c289c2008-10-21 14:07:04 +01001154 ourport->rx_irq = ret;
1155 ourport->tx_irq = ret + 1;
1156 }
Sachin Kamat9303ac12012-09-05 10:30:11 +05301157
Ben Dooksb73c289c2008-10-21 14:07:04 +01001158 ret = platform_get_irq(platdev, 1);
1159 if (ret > 0)
1160 ourport->tx_irq = ret;
Ben Dooksb4975492008-07-03 12:32:51 +01001161
1162 ourport->clk = clk_get(&platdev->dev, "uart");
1163
Thomas Abraham88bb4ea2011-08-10 15:51:19 +05301164 /* Keep all interrupts masked and cleared */
1165 if (s3c24xx_serial_has_interrupt_mask(port)) {
1166 wr_regl(port, S3C64XX_UINTM, 0xf);
1167 wr_regl(port, S3C64XX_UINTP, 0xf);
1168 wr_regl(port, S3C64XX_UINTSP, 0xf);
1169 }
1170
Ben Dooksb73c289c2008-10-21 14:07:04 +01001171 dbg("port: map=%08x, mem=%08x, irq=%d (%d,%d), clock=%ld\n",
1172 port->mapbase, port->membase, port->irq,
1173 ourport->rx_irq, ourport->tx_irq, port->uartclk);
Ben Dooksb4975492008-07-03 12:32:51 +01001174
1175 /* reset the fifos (and setup the uart) */
1176 s3c24xx_serial_resetport(port, cfg);
1177 return 0;
1178}
1179
1180static ssize_t s3c24xx_serial_show_clksrc(struct device *dev,
1181 struct device_attribute *attr,
1182 char *buf)
1183{
1184 struct uart_port *port = s3c24xx_dev_to_port(dev);
1185 struct s3c24xx_uart_port *ourport = to_ourport(port);
1186
Kyoungil Kim7cd88832012-05-20 17:45:54 +09001187 if (IS_ERR(ourport->baudclk))
1188 return -EINVAL;
1189
KeyYoung Park7b15e1d2012-05-30 17:29:55 +09001190 return snprintf(buf, PAGE_SIZE, "* %s\n",
1191 ourport->baudclk->name ?: "(null)");
Ben Dooksb4975492008-07-03 12:32:51 +01001192}
1193
1194static DEVICE_ATTR(clock_source, S_IRUGO, s3c24xx_serial_show_clksrc, NULL);
1195
Thomas Abraham26c919e2011-11-06 22:10:44 +05301196
Ben Dooksb4975492008-07-03 12:32:51 +01001197/* Device driver serial port probe */
1198
Thomas Abraham26c919e2011-11-06 22:10:44 +05301199static const struct of_device_id s3c24xx_uart_dt_match[];
Ben Dooksb4975492008-07-03 12:32:51 +01001200static int probe_index;
1201
Thomas Abraham26c919e2011-11-06 22:10:44 +05301202static inline struct s3c24xx_serial_drv_data *s3c24xx_get_driver_data(
1203 struct platform_device *pdev)
1204{
1205#ifdef CONFIG_OF
1206 if (pdev->dev.of_node) {
1207 const struct of_device_id *match;
1208 match = of_match_node(s3c24xx_uart_dt_match, pdev->dev.of_node);
1209 return (struct s3c24xx_serial_drv_data *)match->data;
1210 }
1211#endif
1212 return (struct s3c24xx_serial_drv_data *)
1213 platform_get_device_id(pdev)->driver_data;
1214}
1215
Thomas Abrahamda121502011-11-02 19:23:25 +09001216static int s3c24xx_serial_probe(struct platform_device *pdev)
Ben Dooksb4975492008-07-03 12:32:51 +01001217{
1218 struct s3c24xx_uart_port *ourport;
1219 int ret;
1220
Thomas Abrahamda121502011-11-02 19:23:25 +09001221 dbg("s3c24xx_serial_probe(%p) %d\n", pdev, probe_index);
Ben Dooksb4975492008-07-03 12:32:51 +01001222
1223 ourport = &s3c24xx_serial_ports[probe_index];
Thomas Abrahamda121502011-11-02 19:23:25 +09001224
Thomas Abraham26c919e2011-11-06 22:10:44 +05301225 ourport->drv_data = s3c24xx_get_driver_data(pdev);
1226 if (!ourport->drv_data) {
1227 dev_err(&pdev->dev, "could not find driver data\n");
1228 return -ENODEV;
1229 }
Thomas Abrahamda121502011-11-02 19:23:25 +09001230
Kyoungil Kim7cd88832012-05-20 17:45:54 +09001231 ourport->baudclk = ERR_PTR(-EINVAL);
Thomas Abrahamda121502011-11-02 19:23:25 +09001232 ourport->info = ourport->drv_data->info;
1233 ourport->cfg = (pdev->dev.platform_data) ?
1234 (struct s3c2410_uartcfg *)pdev->dev.platform_data :
1235 ourport->drv_data->def_cfg;
1236
1237 ourport->port.fifosize = (ourport->info->fifosize) ?
1238 ourport->info->fifosize :
1239 ourport->drv_data->fifosize[probe_index];
1240
Ben Dooksb4975492008-07-03 12:32:51 +01001241 probe_index++;
1242
1243 dbg("%s: initialising port %p...\n", __func__, ourport);
1244
Thomas Abrahamda121502011-11-02 19:23:25 +09001245 ret = s3c24xx_serial_init_port(ourport, pdev);
Ben Dooksb4975492008-07-03 12:32:51 +01001246 if (ret < 0)
1247 goto probe_err;
1248
1249 dbg("%s: adding port\n", __func__);
1250 uart_add_one_port(&s3c24xx_uart_drv, &ourport->port);
Thomas Abrahamda121502011-11-02 19:23:25 +09001251 platform_set_drvdata(pdev, &ourport->port);
Ben Dooksb4975492008-07-03 12:32:51 +01001252
Thomas Abrahamda121502011-11-02 19:23:25 +09001253 ret = device_create_file(&pdev->dev, &dev_attr_clock_source);
Ben Dooksb4975492008-07-03 12:32:51 +01001254 if (ret < 0)
Thomas Abrahamda121502011-11-02 19:23:25 +09001255 dev_err(&pdev->dev, "failed to add clock source attr.\n");
Ben Dooksb4975492008-07-03 12:32:51 +01001256
Ben Dooks30555472008-10-21 14:06:36 +01001257 ret = s3c24xx_serial_cpufreq_register(ourport);
1258 if (ret < 0)
Thomas Abrahamda121502011-11-02 19:23:25 +09001259 dev_err(&pdev->dev, "failed to add cpufreq notifier\n");
Ben Dooks30555472008-10-21 14:06:36 +01001260
Ben Dooksb4975492008-07-03 12:32:51 +01001261 return 0;
1262
1263 probe_err:
1264 return ret;
1265}
1266
Bill Pembertonae8d8a12012-11-19 13:26:18 -05001267static int s3c24xx_serial_remove(struct platform_device *dev)
Ben Dooksb4975492008-07-03 12:32:51 +01001268{
1269 struct uart_port *port = s3c24xx_dev_to_port(&dev->dev);
1270
1271 if (port) {
Ben Dooks30555472008-10-21 14:06:36 +01001272 s3c24xx_serial_cpufreq_deregister(to_ourport(port));
Ben Dooksb4975492008-07-03 12:32:51 +01001273 device_remove_file(&dev->dev, &dev_attr_clock_source);
1274 uart_remove_one_port(&s3c24xx_uart_drv, port);
1275 }
1276
1277 return 0;
1278}
1279
Ben Dooksb4975492008-07-03 12:32:51 +01001280/* UART power management code */
MyungJoo Hamaef7fe52011-06-29 15:28:24 +09001281#ifdef CONFIG_PM_SLEEP
1282static int s3c24xx_serial_suspend(struct device *dev)
Ben Dooksb4975492008-07-03 12:32:51 +01001283{
MyungJoo Hamaef7fe52011-06-29 15:28:24 +09001284 struct uart_port *port = s3c24xx_dev_to_port(dev);
Ben Dooksb4975492008-07-03 12:32:51 +01001285
1286 if (port)
1287 uart_suspend_port(&s3c24xx_uart_drv, port);
1288
1289 return 0;
1290}
1291
MyungJoo Hamaef7fe52011-06-29 15:28:24 +09001292static int s3c24xx_serial_resume(struct device *dev)
Ben Dooksb4975492008-07-03 12:32:51 +01001293{
MyungJoo Hamaef7fe52011-06-29 15:28:24 +09001294 struct uart_port *port = s3c24xx_dev_to_port(dev);
Ben Dooksb4975492008-07-03 12:32:51 +01001295 struct s3c24xx_uart_port *ourport = to_ourport(port);
1296
1297 if (port) {
Thomas Abraham9484b002012-10-03 07:40:04 +09001298 clk_prepare_enable(ourport->clk);
Ben Dooksb4975492008-07-03 12:32:51 +01001299 s3c24xx_serial_resetport(port, s3c24xx_port_to_cfg(port));
Thomas Abraham9484b002012-10-03 07:40:04 +09001300 clk_disable_unprepare(ourport->clk);
Ben Dooksb4975492008-07-03 12:32:51 +01001301
1302 uart_resume_port(&s3c24xx_uart_drv, port);
1303 }
1304
1305 return 0;
1306}
MyungJoo Hamaef7fe52011-06-29 15:28:24 +09001307
1308static const struct dev_pm_ops s3c24xx_serial_pm_ops = {
1309 .suspend = s3c24xx_serial_suspend,
1310 .resume = s3c24xx_serial_resume,
1311};
Kukjin Kimb882fc12011-07-28 08:50:38 +09001312#define SERIAL_SAMSUNG_PM_OPS (&s3c24xx_serial_pm_ops)
1313
MyungJoo Hamaef7fe52011-06-29 15:28:24 +09001314#else /* !CONFIG_PM_SLEEP */
Kukjin Kimb882fc12011-07-28 08:50:38 +09001315
1316#define SERIAL_SAMSUNG_PM_OPS NULL
MyungJoo Hamaef7fe52011-06-29 15:28:24 +09001317#endif /* CONFIG_PM_SLEEP */
Ben Dooksb4975492008-07-03 12:32:51 +01001318
Ben Dooksb4975492008-07-03 12:32:51 +01001319/* Console code */
1320
1321#ifdef CONFIG_SERIAL_SAMSUNG_CONSOLE
1322
1323static struct uart_port *cons_uart;
1324
1325static int
1326s3c24xx_serial_console_txrdy(struct uart_port *port, unsigned int ufcon)
1327{
1328 struct s3c24xx_uart_info *info = s3c24xx_port_to_info(port);
1329 unsigned long ufstat, utrstat;
1330
1331 if (ufcon & S3C2410_UFCON_FIFOMODE) {
Uwe Kleine-König9ddc5b62010-01-20 17:02:24 +01001332 /* fifo mode - check amount of data in fifo registers... */
Ben Dooksb4975492008-07-03 12:32:51 +01001333
1334 ufstat = rd_regl(port, S3C2410_UFSTAT);
1335 return (ufstat & info->tx_fifofull) ? 0 : 1;
1336 }
1337
1338 /* in non-fifo mode, we go and use the tx buffer empty */
1339
1340 utrstat = rd_regl(port, S3C2410_UTRSTAT);
1341 return (utrstat & S3C2410_UTRSTAT_TXE) ? 1 : 0;
1342}
1343
Julien Pichon93b5c032012-09-21 23:22:31 -07001344#ifdef CONFIG_CONSOLE_POLL
1345/*
1346 * Console polling routines for writing and reading from the uart while
1347 * in an interrupt or debug context.
1348 */
1349
1350static int s3c24xx_serial_get_poll_char(struct uart_port *port)
1351{
1352 struct s3c24xx_uart_port *ourport = to_ourport(port);
1353 unsigned int ufstat;
1354
1355 ufstat = rd_regl(port, S3C2410_UFSTAT);
1356 if (s3c24xx_serial_rx_fifocnt(ourport, ufstat) == 0)
1357 return NO_POLL_CHAR;
1358
1359 return rd_regb(port, S3C2410_URXH);
1360}
1361
1362static void s3c24xx_serial_put_poll_char(struct uart_port *port,
1363 unsigned char c)
1364{
1365 unsigned int ufcon = rd_regl(cons_uart, S3C2410_UFCON);
1366
1367 while (!s3c24xx_serial_console_txrdy(port, ufcon))
1368 cpu_relax();
1369 wr_regb(cons_uart, S3C2410_UTXH, c);
1370}
1371
1372#endif /* CONFIG_CONSOLE_POLL */
1373
Ben Dooksb4975492008-07-03 12:32:51 +01001374static void
1375s3c24xx_serial_console_putchar(struct uart_port *port, int ch)
1376{
1377 unsigned int ufcon = rd_regl(cons_uart, S3C2410_UFCON);
1378 while (!s3c24xx_serial_console_txrdy(port, ufcon))
1379 barrier();
1380 wr_regb(cons_uart, S3C2410_UTXH, ch);
1381}
1382
1383static void
1384s3c24xx_serial_console_write(struct console *co, const char *s,
1385 unsigned int count)
1386{
1387 uart_console_write(cons_uart, s, count, s3c24xx_serial_console_putchar);
1388}
1389
1390static void __init
1391s3c24xx_serial_get_options(struct uart_port *port, int *baud,
1392 int *parity, int *bits)
1393{
Ben Dooksb4975492008-07-03 12:32:51 +01001394 struct clk *clk;
1395 unsigned int ulcon;
1396 unsigned int ucon;
1397 unsigned int ubrdiv;
1398 unsigned long rate;
Thomas Abraham5f5a7a52011-10-24 11:47:46 +02001399 unsigned int clk_sel;
1400 char clk_name[MAX_CLK_NAME_LENGTH];
Ben Dooksb4975492008-07-03 12:32:51 +01001401
1402 ulcon = rd_regl(port, S3C2410_ULCON);
1403 ucon = rd_regl(port, S3C2410_UCON);
1404 ubrdiv = rd_regl(port, S3C2410_UBRDIV);
1405
1406 dbg("s3c24xx_serial_get_options: port=%p\n"
1407 "registers: ulcon=%08x, ucon=%08x, ubdriv=%08x\n",
1408 port, ulcon, ucon, ubrdiv);
1409
1410 if ((ucon & 0xf) != 0) {
1411 /* consider the serial port configured if the tx/rx mode set */
1412
1413 switch (ulcon & S3C2410_LCON_CSMASK) {
1414 case S3C2410_LCON_CS5:
1415 *bits = 5;
1416 break;
1417 case S3C2410_LCON_CS6:
1418 *bits = 6;
1419 break;
1420 case S3C2410_LCON_CS7:
1421 *bits = 7;
1422 break;
1423 default:
1424 case S3C2410_LCON_CS8:
1425 *bits = 8;
1426 break;
1427 }
1428
1429 switch (ulcon & S3C2410_LCON_PMASK) {
1430 case S3C2410_LCON_PEVEN:
1431 *parity = 'e';
1432 break;
1433
1434 case S3C2410_LCON_PODD:
1435 *parity = 'o';
1436 break;
1437
1438 case S3C2410_LCON_PNONE:
1439 default:
1440 *parity = 'n';
1441 }
1442
1443 /* now calculate the baud rate */
1444
Thomas Abraham5f5a7a52011-10-24 11:47:46 +02001445 clk_sel = s3c24xx_serial_getsource(port);
1446 sprintf(clk_name, "clk_uart_baud%d", clk_sel);
Ben Dooksb4975492008-07-03 12:32:51 +01001447
Thomas Abraham5f5a7a52011-10-24 11:47:46 +02001448 clk = clk_get(port->dev, clk_name);
Kyoungil Kim7cd88832012-05-20 17:45:54 +09001449 if (!IS_ERR(clk))
Thomas Abraham5f5a7a52011-10-24 11:47:46 +02001450 rate = clk_get_rate(clk);
Ben Dooksb4975492008-07-03 12:32:51 +01001451 else
1452 rate = 1;
1453
Ben Dooksb4975492008-07-03 12:32:51 +01001454 *baud = rate / (16 * (ubrdiv + 1));
1455 dbg("calculated baud %d\n", *baud);
1456 }
1457
1458}
1459
Ben Dooksb4975492008-07-03 12:32:51 +01001460static int __init
1461s3c24xx_serial_console_setup(struct console *co, char *options)
1462{
1463 struct uart_port *port;
1464 int baud = 9600;
1465 int bits = 8;
1466 int parity = 'n';
1467 int flow = 'n';
1468
1469 dbg("s3c24xx_serial_console_setup: co=%p (%d), %s\n",
1470 co, co->index, options);
1471
1472 /* is this a valid port */
1473
Ben Dooks03d5e772008-11-03 09:21:23 +00001474 if (co->index == -1 || co->index >= CONFIG_SERIAL_SAMSUNG_UARTS)
Ben Dooksb4975492008-07-03 12:32:51 +01001475 co->index = 0;
1476
1477 port = &s3c24xx_serial_ports[co->index].port;
1478
1479 /* is the port configured? */
1480
Thomas Abrahamee430f12011-06-14 19:12:26 +09001481 if (port->mapbase == 0x0)
1482 return -ENODEV;
Ben Dooksb4975492008-07-03 12:32:51 +01001483
1484 cons_uart = port;
1485
1486 dbg("s3c24xx_serial_console_setup: port=%p (%d)\n", port, co->index);
1487
1488 /*
1489 * Check whether an invalid uart number has been specified, and
1490 * if so, search for the first available port that does have
1491 * console support.
1492 */
1493 if (options)
1494 uart_parse_options(options, &baud, &parity, &bits, &flow);
1495 else
1496 s3c24xx_serial_get_options(port, &baud, &parity, &bits);
1497
1498 dbg("s3c24xx_serial_console_setup: baud %d\n", baud);
1499
1500 return uart_set_options(port, co, baud, parity, bits, flow);
1501}
1502
Ben Dooksb4975492008-07-03 12:32:51 +01001503static struct console s3c24xx_serial_console = {
1504 .name = S3C24XX_SERIAL_NAME,
1505 .device = uart_console_device,
1506 .flags = CON_PRINTBUFFER,
1507 .index = -1,
1508 .write = s3c24xx_serial_console_write,
Thomas Abraham5822a5d2011-06-14 19:12:26 +09001509 .setup = s3c24xx_serial_console_setup,
1510 .data = &s3c24xx_uart_drv,
Ben Dooksb4975492008-07-03 12:32:51 +01001511};
Ben Dooksb4975492008-07-03 12:32:51 +01001512#endif /* CONFIG_SERIAL_SAMSUNG_CONSOLE */
1513
Thomas Abrahamda121502011-11-02 19:23:25 +09001514#ifdef CONFIG_CPU_S3C2410
1515static struct s3c24xx_serial_drv_data s3c2410_serial_drv_data = {
1516 .info = &(struct s3c24xx_uart_info) {
1517 .name = "Samsung S3C2410 UART",
1518 .type = PORT_S3C2410,
1519 .fifosize = 16,
1520 .rx_fifomask = S3C2410_UFSTAT_RXMASK,
1521 .rx_fifoshift = S3C2410_UFSTAT_RXSHIFT,
1522 .rx_fifofull = S3C2410_UFSTAT_RXFULL,
1523 .tx_fifofull = S3C2410_UFSTAT_TXFULL,
1524 .tx_fifomask = S3C2410_UFSTAT_TXMASK,
1525 .tx_fifoshift = S3C2410_UFSTAT_TXSHIFT,
1526 .def_clk_sel = S3C2410_UCON_CLKSEL0,
1527 .num_clks = 2,
1528 .clksel_mask = S3C2410_UCON_CLKMASK,
1529 .clksel_shift = S3C2410_UCON_CLKSHIFT,
1530 },
1531 .def_cfg = &(struct s3c2410_uartcfg) {
1532 .ucon = S3C2410_UCON_DEFAULT,
1533 .ufcon = S3C2410_UFCON_DEFAULT,
1534 },
1535};
1536#define S3C2410_SERIAL_DRV_DATA ((kernel_ulong_t)&s3c2410_serial_drv_data)
1537#else
1538#define S3C2410_SERIAL_DRV_DATA (kernel_ulong_t)NULL
1539#endif
1540
1541#ifdef CONFIG_CPU_S3C2412
1542static struct s3c24xx_serial_drv_data s3c2412_serial_drv_data = {
1543 .info = &(struct s3c24xx_uart_info) {
1544 .name = "Samsung S3C2412 UART",
1545 .type = PORT_S3C2412,
1546 .fifosize = 64,
1547 .has_divslot = 1,
1548 .rx_fifomask = S3C2440_UFSTAT_RXMASK,
1549 .rx_fifoshift = S3C2440_UFSTAT_RXSHIFT,
1550 .rx_fifofull = S3C2440_UFSTAT_RXFULL,
1551 .tx_fifofull = S3C2440_UFSTAT_TXFULL,
1552 .tx_fifomask = S3C2440_UFSTAT_TXMASK,
1553 .tx_fifoshift = S3C2440_UFSTAT_TXSHIFT,
1554 .def_clk_sel = S3C2410_UCON_CLKSEL2,
1555 .num_clks = 4,
1556 .clksel_mask = S3C2412_UCON_CLKMASK,
1557 .clksel_shift = S3C2412_UCON_CLKSHIFT,
1558 },
1559 .def_cfg = &(struct s3c2410_uartcfg) {
1560 .ucon = S3C2410_UCON_DEFAULT,
1561 .ufcon = S3C2410_UFCON_DEFAULT,
1562 },
1563};
1564#define S3C2412_SERIAL_DRV_DATA ((kernel_ulong_t)&s3c2412_serial_drv_data)
1565#else
1566#define S3C2412_SERIAL_DRV_DATA (kernel_ulong_t)NULL
1567#endif
1568
1569#if defined(CONFIG_CPU_S3C2440) || defined(CONFIG_CPU_S3C2416) || \
Denis 'GNUtoo' Cariklib26469a2012-02-23 08:23:52 +01001570 defined(CONFIG_CPU_S3C2443) || defined(CONFIG_CPU_S3C2442)
Thomas Abrahamda121502011-11-02 19:23:25 +09001571static struct s3c24xx_serial_drv_data s3c2440_serial_drv_data = {
1572 .info = &(struct s3c24xx_uart_info) {
1573 .name = "Samsung S3C2440 UART",
1574 .type = PORT_S3C2440,
1575 .fifosize = 64,
1576 .has_divslot = 1,
1577 .rx_fifomask = S3C2440_UFSTAT_RXMASK,
1578 .rx_fifoshift = S3C2440_UFSTAT_RXSHIFT,
1579 .rx_fifofull = S3C2440_UFSTAT_RXFULL,
1580 .tx_fifofull = S3C2440_UFSTAT_TXFULL,
1581 .tx_fifomask = S3C2440_UFSTAT_TXMASK,
1582 .tx_fifoshift = S3C2440_UFSTAT_TXSHIFT,
1583 .def_clk_sel = S3C2410_UCON_CLKSEL2,
1584 .num_clks = 4,
1585 .clksel_mask = S3C2412_UCON_CLKMASK,
1586 .clksel_shift = S3C2412_UCON_CLKSHIFT,
1587 },
1588 .def_cfg = &(struct s3c2410_uartcfg) {
1589 .ucon = S3C2410_UCON_DEFAULT,
1590 .ufcon = S3C2410_UFCON_DEFAULT,
1591 },
1592};
1593#define S3C2440_SERIAL_DRV_DATA ((kernel_ulong_t)&s3c2440_serial_drv_data)
1594#else
1595#define S3C2440_SERIAL_DRV_DATA (kernel_ulong_t)NULL
1596#endif
1597
1598#if defined(CONFIG_CPU_S3C6400) || defined(CONFIG_CPU_S3C6410) || \
1599 defined(CONFIG_CPU_S5P6440) || defined(CONFIG_CPU_S5P6450) || \
1600 defined(CONFIG_CPU_S5PC100)
1601static struct s3c24xx_serial_drv_data s3c6400_serial_drv_data = {
1602 .info = &(struct s3c24xx_uart_info) {
1603 .name = "Samsung S3C6400 UART",
1604 .type = PORT_S3C6400,
1605 .fifosize = 64,
1606 .has_divslot = 1,
1607 .rx_fifomask = S3C2440_UFSTAT_RXMASK,
1608 .rx_fifoshift = S3C2440_UFSTAT_RXSHIFT,
1609 .rx_fifofull = S3C2440_UFSTAT_RXFULL,
1610 .tx_fifofull = S3C2440_UFSTAT_TXFULL,
1611 .tx_fifomask = S3C2440_UFSTAT_TXMASK,
1612 .tx_fifoshift = S3C2440_UFSTAT_TXSHIFT,
1613 .def_clk_sel = S3C2410_UCON_CLKSEL2,
1614 .num_clks = 4,
1615 .clksel_mask = S3C6400_UCON_CLKMASK,
1616 .clksel_shift = S3C6400_UCON_CLKSHIFT,
1617 },
1618 .def_cfg = &(struct s3c2410_uartcfg) {
1619 .ucon = S3C2410_UCON_DEFAULT,
1620 .ufcon = S3C2410_UFCON_DEFAULT,
1621 },
1622};
1623#define S3C6400_SERIAL_DRV_DATA ((kernel_ulong_t)&s3c6400_serial_drv_data)
1624#else
1625#define S3C6400_SERIAL_DRV_DATA (kernel_ulong_t)NULL
1626#endif
1627
1628#ifdef CONFIG_CPU_S5PV210
1629static struct s3c24xx_serial_drv_data s5pv210_serial_drv_data = {
1630 .info = &(struct s3c24xx_uart_info) {
1631 .name = "Samsung S5PV210 UART",
1632 .type = PORT_S3C6400,
1633 .has_divslot = 1,
1634 .rx_fifomask = S5PV210_UFSTAT_RXMASK,
1635 .rx_fifoshift = S5PV210_UFSTAT_RXSHIFT,
1636 .rx_fifofull = S5PV210_UFSTAT_RXFULL,
1637 .tx_fifofull = S5PV210_UFSTAT_TXFULL,
1638 .tx_fifomask = S5PV210_UFSTAT_TXMASK,
1639 .tx_fifoshift = S5PV210_UFSTAT_TXSHIFT,
1640 .def_clk_sel = S3C2410_UCON_CLKSEL0,
1641 .num_clks = 2,
1642 .clksel_mask = S5PV210_UCON_CLKMASK,
1643 .clksel_shift = S5PV210_UCON_CLKSHIFT,
1644 },
1645 .def_cfg = &(struct s3c2410_uartcfg) {
1646 .ucon = S5PV210_UCON_DEFAULT,
1647 .ufcon = S5PV210_UFCON_DEFAULT,
1648 },
1649 .fifosize = { 256, 64, 16, 16 },
1650};
1651#define S5PV210_SERIAL_DRV_DATA ((kernel_ulong_t)&s5pv210_serial_drv_data)
1652#else
1653#define S5PV210_SERIAL_DRV_DATA (kernel_ulong_t)NULL
1654#endif
1655
Kukjin Kim5f7b6d12012-02-01 00:11:23 +09001656#if defined(CONFIG_CPU_EXYNOS4210) || defined(CONFIG_SOC_EXYNOS4212) || \
Kukjin Kim7ccfe012012-02-01 00:39:11 +09001657 defined(CONFIG_SOC_EXYNOS4412) || defined(CONFIG_SOC_EXYNOS5250)
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[] = {
1712 { .compatible = "samsung,exynos4210-uart",
Mark Browna169a882011-11-08 17:00:14 +09001713 .data = (void *)EXYNOS4210_SERIAL_DRV_DATA },
Thomas Abraham26c919e2011-11-06 22:10:44 +05301714 {},
1715};
1716MODULE_DEVICE_TABLE(of, s3c24xx_uart_dt_match);
1717#else
1718#define s3c24xx_uart_dt_match NULL
1719#endif
1720
Thomas Abrahamda121502011-11-02 19:23:25 +09001721static struct platform_driver samsung_serial_driver = {
1722 .probe = s3c24xx_serial_probe,
Bill Pemberton2d47b712012-11-19 13:21:34 -05001723 .remove = s3c24xx_serial_remove,
Thomas Abrahamda121502011-11-02 19:23:25 +09001724 .id_table = s3c24xx_serial_driver_ids,
1725 .driver = {
1726 .name = "samsung-uart",
1727 .owner = THIS_MODULE,
1728 .pm = SERIAL_SAMSUNG_PM_OPS,
Thomas Abraham26c919e2011-11-06 22:10:44 +05301729 .of_match_table = s3c24xx_uart_dt_match,
Thomas Abrahamda121502011-11-02 19:23:25 +09001730 },
1731};
1732
1733/* module initialisation code */
1734
1735static int __init s3c24xx_serial_modinit(void)
1736{
1737 int ret;
1738
1739 ret = uart_register_driver(&s3c24xx_uart_drv);
1740 if (ret < 0) {
Sachin Kamatd20925e2012-09-05 10:30:10 +05301741 pr_err("Failed to register Samsung UART driver\n");
Sachin Kamate740d8f2012-09-12 12:00:01 +05301742 return ret;
Thomas Abrahamda121502011-11-02 19:23:25 +09001743 }
1744
1745 return platform_driver_register(&samsung_serial_driver);
1746}
1747
1748static void __exit s3c24xx_serial_modexit(void)
1749{
1750 uart_unregister_driver(&s3c24xx_uart_drv);
1751}
1752
1753module_init(s3c24xx_serial_modinit);
1754module_exit(s3c24xx_serial_modexit);
1755
1756MODULE_ALIAS("platform:samsung-uart");
Ben Dooksb4975492008-07-03 12:32:51 +01001757MODULE_DESCRIPTION("Samsung SoC Serial port driver");
1758MODULE_AUTHOR("Ben Dooks <ben@simtec.co.uk>");
1759MODULE_LICENSE("GPL v2");