aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/serial/samsung.c
diff options
context:
space:
mode:
authorBen Dooks <ben-linux@fluff.org>2008-07-03 07:32:51 -0400
committerBen Dooks <ben-linux@fluff.org>2008-07-03 11:51:31 -0400
commitb497549a035c2a81b71c7a27f2b00c8a16c09423 (patch)
treebfbbe3485674d1a87db563a4e298fbf13846b9f5 /drivers/serial/samsung.c
parent6fc601e37bbb4045ee0afefc76b64284ea800c89 (diff)
[ARM] S3C24XX: Split serial driver into core and per-cpu drivers
The S3C2410 serial driver in drivers/serial/s3c2410.c has been growing bigger with the addition of more variants of this hardware with the growing Samsung SoCs range. As such, it would be easier to split this code up into a core and per-cpu drivers to make driver addition easier, and the core smaller. Signed-off-by: Ben Dooks <ben-linux@fluff.org>
Diffstat (limited to 'drivers/serial/samsung.c')
-rw-r--r--drivers/serial/samsung.c1317
1 files changed, 1317 insertions, 0 deletions
diff --git a/drivers/serial/samsung.c b/drivers/serial/samsung.c
new file mode 100644
index 000000000000..4a3ecaa629e6
--- /dev/null
+++ b/drivers/serial/samsung.c
@@ -0,0 +1,1317 @@
1/* linux/drivers/serial/samsuing.c
2 *
3 * Driver core for Samsung SoC onboard UARTs.
4 *
5 * Ben Dooks, Copyright (c) 2003-2005,2008 Simtec Electronics
6 * http://armlinux.simtec.co.uk/
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License version 2 as
10 * published by the Free Software Foundation.
11*/
12
13/* Hote on 2410 error handling
14 *
15 * The s3c2410 manual has a love/hate affair with the contents of the
16 * UERSTAT register in the UART blocks, and keeps marking some of the
17 * error bits as reserved. Having checked with the s3c2410x01,
18 * it copes with BREAKs properly, so I am happy to ignore the RESERVED
19 * feature from the latter versions of the manual.
20 *
21 * If it becomes aparrent that latter versions of the 2410 remove these
22 * bits, then action will have to be taken to differentiate the versions
23 * and change the policy on BREAK
24 *
25 * BJD, 04-Nov-2004
26*/
27
28#if defined(CONFIG_SERIAL_SAMSUNG_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)
29#define SUPPORT_SYSRQ
30#endif
31
32#include <linux/module.h>
33#include <linux/ioport.h>
34#include <linux/io.h>
35#include <linux/platform_device.h>
36#include <linux/init.h>
37#include <linux/sysrq.h>
38#include <linux/console.h>
39#include <linux/tty.h>
40#include <linux/tty_flip.h>
41#include <linux/serial_core.h>
42#include <linux/serial.h>
43#include <linux/delay.h>
44#include <linux/clk.h>
45
46#include <asm/irq.h>
47
48#include <asm/hardware.h>
49
50#include <asm/plat-s3c/regs-serial.h>
51#include <asm/arch/regs-gpio.h>
52
53#include "samsung.h"
54
55/* UART name and device definitions */
56
57#define S3C24XX_SERIAL_NAME "ttySAC"
58#define S3C24XX_SERIAL_MAJOR 204
59#define S3C24XX_SERIAL_MINOR 64
60
61/* we can support 3 uarts, but not always use them */
62
63#ifdef CONFIG_CPU_S3C2400
64#define NR_PORTS (2)
65#else
66#define NR_PORTS (3)
67#endif
68
69/* port irq numbers */
70
71#define TX_IRQ(port) ((port)->irq + 1)
72#define RX_IRQ(port) ((port)->irq)
73
74/* macros to change one thing to another */
75
76#define tx_enabled(port) ((port)->unused[0])
77#define rx_enabled(port) ((port)->unused[1])
78
79/* flag to ignore all characters comming in */
80#define RXSTAT_DUMMY_READ (0x10000000)
81
82static inline struct s3c24xx_uart_port *to_ourport(struct uart_port *port)
83{
84 return container_of(port, struct s3c24xx_uart_port, port);
85}
86
87/* translate a port to the device name */
88
89static inline const char *s3c24xx_serial_portname(struct uart_port *port)
90{
91 return to_platform_device(port->dev)->name;
92}
93
94static int s3c24xx_serial_txempty_nofifo(struct uart_port *port)
95{
96 return (rd_regl(port, S3C2410_UTRSTAT) & S3C2410_UTRSTAT_TXE);
97}
98
99static void s3c24xx_serial_rx_enable(struct uart_port *port)
100{
101 unsigned long flags;
102 unsigned int ucon, ufcon;
103 int count = 10000;
104
105 spin_lock_irqsave(&port->lock, flags);
106
107 while (--count && !s3c24xx_serial_txempty_nofifo(port))
108 udelay(100);
109
110 ufcon = rd_regl(port, S3C2410_UFCON);
111 ufcon |= S3C2410_UFCON_RESETRX;
112 wr_regl(port, S3C2410_UFCON, ufcon);
113
114 ucon = rd_regl(port, S3C2410_UCON);
115 ucon |= S3C2410_UCON_RXIRQMODE;
116 wr_regl(port, S3C2410_UCON, ucon);
117
118 rx_enabled(port) = 1;
119 spin_unlock_irqrestore(&port->lock, flags);
120}
121
122static void s3c24xx_serial_rx_disable(struct uart_port *port)
123{
124 unsigned long flags;
125 unsigned int ucon;
126
127 spin_lock_irqsave(&port->lock, flags);
128
129 ucon = rd_regl(port, S3C2410_UCON);
130 ucon &= ~S3C2410_UCON_RXIRQMODE;
131 wr_regl(port, S3C2410_UCON, ucon);
132
133 rx_enabled(port) = 0;
134 spin_unlock_irqrestore(&port->lock, flags);
135}
136
137static void s3c24xx_serial_stop_tx(struct uart_port *port)
138{
139 if (tx_enabled(port)) {
140 disable_irq(TX_IRQ(port));
141 tx_enabled(port) = 0;
142 if (port->flags & UPF_CONS_FLOW)
143 s3c24xx_serial_rx_enable(port);
144 }
145}
146
147static void s3c24xx_serial_start_tx(struct uart_port *port)
148{
149 if (!tx_enabled(port)) {
150 if (port->flags & UPF_CONS_FLOW)
151 s3c24xx_serial_rx_disable(port);
152
153 enable_irq(TX_IRQ(port));
154 tx_enabled(port) = 1;
155 }
156}
157
158
159static void s3c24xx_serial_stop_rx(struct uart_port *port)
160{
161 if (rx_enabled(port)) {
162 dbg("s3c24xx_serial_stop_rx: port=%p\n", port);
163 disable_irq(RX_IRQ(port));
164 rx_enabled(port) = 0;
165 }
166}
167
168static void s3c24xx_serial_enable_ms(struct uart_port *port)
169{
170}
171
172static inline struct s3c24xx_uart_info *s3c24xx_port_to_info(struct uart_port *port)
173{
174 return to_ourport(port)->info;
175}
176
177static inline struct s3c2410_uartcfg *s3c24xx_port_to_cfg(struct uart_port *port)
178{
179 if (port->dev == NULL)
180 return NULL;
181
182 return (struct s3c2410_uartcfg *)port->dev->platform_data;
183}
184
185static int s3c24xx_serial_rx_fifocnt(struct s3c24xx_uart_port *ourport,
186 unsigned long ufstat)
187{
188 struct s3c24xx_uart_info *info = ourport->info;
189
190 if (ufstat & info->rx_fifofull)
191 return info->fifosize;
192
193 return (ufstat & info->rx_fifomask) >> info->rx_fifoshift;
194}
195
196
197/* ? - where has parity gone?? */
198#define S3C2410_UERSTAT_PARITY (0x1000)
199
200static irqreturn_t
201s3c24xx_serial_rx_chars(int irq, void *dev_id)
202{
203 struct s3c24xx_uart_port *ourport = dev_id;
204 struct uart_port *port = &ourport->port;
205 struct tty_struct *tty = port->info->tty;
206 unsigned int ufcon, ch, flag, ufstat, uerstat;
207 int max_count = 64;
208
209 while (max_count-- > 0) {
210 ufcon = rd_regl(port, S3C2410_UFCON);
211 ufstat = rd_regl(port, S3C2410_UFSTAT);
212
213 if (s3c24xx_serial_rx_fifocnt(ourport, ufstat) == 0)
214 break;
215
216 uerstat = rd_regl(port, S3C2410_UERSTAT);
217 ch = rd_regb(port, S3C2410_URXH);
218
219 if (port->flags & UPF_CONS_FLOW) {
220 int txe = s3c24xx_serial_txempty_nofifo(port);
221
222 if (rx_enabled(port)) {
223 if (!txe) {
224 rx_enabled(port) = 0;
225 continue;
226 }
227 } else {
228 if (txe) {
229 ufcon |= S3C2410_UFCON_RESETRX;
230 wr_regl(port, S3C2410_UFCON, ufcon);
231 rx_enabled(port) = 1;
232 goto out;
233 }
234 continue;
235 }
236 }
237
238 /* insert the character into the buffer */
239
240 flag = TTY_NORMAL;
241 port->icount.rx++;
242
243 if (unlikely(uerstat & S3C2410_UERSTAT_ANY)) {
244 dbg("rxerr: port ch=0x%02x, rxs=0x%08x\n",
245 ch, uerstat);
246
247 /* check for break */
248 if (uerstat & S3C2410_UERSTAT_BREAK) {
249 dbg("break!\n");
250 port->icount.brk++;
251 if (uart_handle_break(port))
252 goto ignore_char;
253 }
254
255 if (uerstat & S3C2410_UERSTAT_FRAME)
256 port->icount.frame++;
257 if (uerstat & S3C2410_UERSTAT_OVERRUN)
258 port->icount.overrun++;
259
260 uerstat &= port->read_status_mask;
261
262 if (uerstat & S3C2410_UERSTAT_BREAK)
263 flag = TTY_BREAK;
264 else if (uerstat & S3C2410_UERSTAT_PARITY)
265 flag = TTY_PARITY;
266 else if (uerstat & (S3C2410_UERSTAT_FRAME |
267 S3C2410_UERSTAT_OVERRUN))
268 flag = TTY_FRAME;
269 }
270
271 if (uart_handle_sysrq_char(port, ch))
272 goto ignore_char;
273
274 uart_insert_char(port, uerstat, S3C2410_UERSTAT_OVERRUN,
275 ch, flag);
276
277 ignore_char:
278 continue;
279 }
280 tty_flip_buffer_push(tty);
281
282 out:
283 return IRQ_HANDLED;
284}
285
286static irqreturn_t s3c24xx_serial_tx_chars(int irq, void *id)
287{
288 struct s3c24xx_uart_port *ourport = id;
289 struct uart_port *port = &ourport->port;
290 struct circ_buf *xmit = &port->info->xmit;
291 int count = 256;
292
293 if (port->x_char) {
294 wr_regb(port, S3C2410_UTXH, port->x_char);
295 port->icount.tx++;
296 port->x_char = 0;
297 goto out;
298 }
299
300 /* if there isnt anything more to transmit, or the uart is now
301 * stopped, disable the uart and exit
302 */
303
304 if (uart_circ_empty(xmit) || uart_tx_stopped(port)) {
305 s3c24xx_serial_stop_tx(port);
306 goto out;
307 }
308
309 /* try and drain the buffer... */
310
311 while (!uart_circ_empty(xmit) && count-- > 0) {
312 if (rd_regl(port, S3C2410_UFSTAT) & ourport->info->tx_fifofull)
313 break;
314
315 wr_regb(port, S3C2410_UTXH, xmit->buf[xmit->tail]);
316 xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
317 port->icount.tx++;
318 }
319
320 if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
321 uart_write_wakeup(port);
322
323 if (uart_circ_empty(xmit))
324 s3c24xx_serial_stop_tx(port);
325
326 out:
327 return IRQ_HANDLED;
328}
329
330static unsigned int s3c24xx_serial_tx_empty(struct uart_port *port)
331{
332 struct s3c24xx_uart_info *info = s3c24xx_port_to_info(port);
333 unsigned long ufstat = rd_regl(port, S3C2410_UFSTAT);
334 unsigned long ufcon = rd_regl(port, S3C2410_UFCON);
335
336 if (ufcon & S3C2410_UFCON_FIFOMODE) {
337 if ((ufstat & info->tx_fifomask) != 0 ||
338 (ufstat & info->tx_fifofull))
339 return 0;
340
341 return 1;
342 }
343
344 return s3c24xx_serial_txempty_nofifo(port);
345}
346
347/* no modem control lines */
348static unsigned int s3c24xx_serial_get_mctrl(struct uart_port *port)
349{
350 unsigned int umstat = rd_regb(port, S3C2410_UMSTAT);
351
352 if (umstat & S3C2410_UMSTAT_CTS)
353 return TIOCM_CAR | TIOCM_DSR | TIOCM_CTS;
354 else
355 return TIOCM_CAR | TIOCM_DSR;
356}
357
358static void s3c24xx_serial_set_mctrl(struct uart_port *port, unsigned int mctrl)
359{
360 /* todo - possibly remove AFC and do manual CTS */
361}
362
363static void s3c24xx_serial_break_ctl(struct uart_port *port, int break_state)
364{
365 unsigned long flags;
366 unsigned int ucon;
367
368 spin_lock_irqsave(&port->lock, flags);
369
370 ucon = rd_regl(port, S3C2410_UCON);
371
372 if (break_state)
373 ucon |= S3C2410_UCON_SBREAK;
374 else
375 ucon &= ~S3C2410_UCON_SBREAK;
376
377 wr_regl(port, S3C2410_UCON, ucon);
378
379 spin_unlock_irqrestore(&port->lock, flags);
380}
381
382static void s3c24xx_serial_shutdown(struct uart_port *port)
383{
384 struct s3c24xx_uart_port *ourport = to_ourport(port);
385
386 if (ourport->tx_claimed) {
387 free_irq(TX_IRQ(port), ourport);
388 tx_enabled(port) = 0;
389 ourport->tx_claimed = 0;
390 }
391
392 if (ourport->rx_claimed) {
393 free_irq(RX_IRQ(port), ourport);
394 ourport->rx_claimed = 0;
395 rx_enabled(port) = 0;
396 }
397}
398
399
400static int s3c24xx_serial_startup(struct uart_port *port)
401{
402 struct s3c24xx_uart_port *ourport = to_ourport(port);
403 int ret;
404
405 dbg("s3c24xx_serial_startup: port=%p (%08lx,%p)\n",
406 port->mapbase, port->membase);
407
408 rx_enabled(port) = 1;
409
410 ret = request_irq(RX_IRQ(port),
411 s3c24xx_serial_rx_chars, 0,
412 s3c24xx_serial_portname(port), ourport);
413
414 if (ret != 0) {
415 printk(KERN_ERR "cannot get irq %d\n", RX_IRQ(port));
416 return ret;
417 }
418
419 ourport->rx_claimed = 1;
420
421 dbg("requesting tx irq...\n");
422
423 tx_enabled(port) = 1;
424
425 ret = request_irq(TX_IRQ(port),
426 s3c24xx_serial_tx_chars, 0,
427 s3c24xx_serial_portname(port), ourport);
428
429 if (ret) {
430 printk(KERN_ERR "cannot get irq %d\n", TX_IRQ(port));
431 goto err;
432 }
433
434 ourport->tx_claimed = 1;
435
436 dbg("s3c24xx_serial_startup ok\n");
437
438 /* the port reset code should have done the correct
439 * register setup for the port controls */
440
441 return ret;
442
443 err:
444 s3c24xx_serial_shutdown(port);
445 return ret;
446}
447
448/* power power management control */
449
450static void s3c24xx_serial_pm(struct uart_port *port, unsigned int level,
451 unsigned int old)
452{
453 struct s3c24xx_uart_port *ourport = to_ourport(port);
454
455 switch (level) {
456 case 3:
457 if (!IS_ERR(ourport->baudclk) && ourport->baudclk != NULL)
458 clk_disable(ourport->baudclk);
459
460 clk_disable(ourport->clk);
461 break;
462
463 case 0:
464 clk_enable(ourport->clk);
465
466 if (!IS_ERR(ourport->baudclk) && ourport->baudclk != NULL)
467 clk_enable(ourport->baudclk);
468
469 break;
470 default:
471 printk(KERN_ERR "s3c24xx_serial: unknown pm %d\n", level);
472 }
473}
474
475/* baud rate calculation
476 *
477 * The UARTs on the S3C2410/S3C2440 can take their clocks from a number
478 * of different sources, including the peripheral clock ("pclk") and an
479 * external clock ("uclk"). The S3C2440 also adds the core clock ("fclk")
480 * with a programmable extra divisor.
481 *
482 * The following code goes through the clock sources, and calculates the
483 * baud clocks (and the resultant actual baud rates) and then tries to
484 * pick the closest one and select that.
485 *
486*/
487
488
489#define MAX_CLKS (8)
490
491static struct s3c24xx_uart_clksrc tmp_clksrc = {
492 .name = "pclk",
493 .min_baud = 0,
494 .max_baud = 0,
495 .divisor = 1,
496};
497
498static inline int
499s3c24xx_serial_getsource(struct uart_port *port, struct s3c24xx_uart_clksrc *c)
500{
501 struct s3c24xx_uart_info *info = s3c24xx_port_to_info(port);
502
503 return (info->get_clksrc)(port, c);
504}
505
506static inline int
507s3c24xx_serial_setsource(struct uart_port *port, struct s3c24xx_uart_clksrc *c)
508{
509 struct s3c24xx_uart_info *info = s3c24xx_port_to_info(port);
510
511 return (info->set_clksrc)(port, c);
512}
513
514struct baud_calc {
515 struct s3c24xx_uart_clksrc *clksrc;
516 unsigned int calc;
517 unsigned int quot;
518 struct clk *src;
519};
520
521static int s3c24xx_serial_calcbaud(struct baud_calc *calc,
522 struct uart_port *port,
523 struct s3c24xx_uart_clksrc *clksrc,
524 unsigned int baud)
525{
526 unsigned long rate;
527
528 calc->src = clk_get(port->dev, clksrc->name);
529 if (calc->src == NULL || IS_ERR(calc->src))
530 return 0;
531
532 rate = clk_get_rate(calc->src);
533 rate /= clksrc->divisor;
534
535 calc->clksrc = clksrc;
536 calc->quot = (rate + (8 * baud)) / (16 * baud);
537 calc->calc = (rate / (calc->quot * 16));
538
539 calc->quot--;
540 return 1;
541}
542
543static unsigned int s3c24xx_serial_getclk(struct uart_port *port,
544 struct s3c24xx_uart_clksrc **clksrc,
545 struct clk **clk,
546 unsigned int baud)
547{
548 struct s3c2410_uartcfg *cfg = s3c24xx_port_to_cfg(port);
549 struct s3c24xx_uart_clksrc *clkp;
550 struct baud_calc res[MAX_CLKS];
551 struct baud_calc *resptr, *best, *sptr;
552 int i;
553
554 clkp = cfg->clocks;
555 best = NULL;
556
557 if (cfg->clocks_size < 2) {
558 if (cfg->clocks_size == 0)
559 clkp = &tmp_clksrc;
560
561 /* check to see if we're sourcing fclk, and if so we're
562 * going to have to update the clock source
563 */
564
565 if (strcmp(clkp->name, "fclk") == 0) {
566 struct s3c24xx_uart_clksrc src;
567
568 s3c24xx_serial_getsource(port, &src);
569
570 /* check that the port already using fclk, and if
571 * not, then re-select fclk
572 */
573
574 if (strcmp(src.name, clkp->name) == 0) {
575 s3c24xx_serial_setsource(port, clkp);
576 s3c24xx_serial_getsource(port, &src);
577 }
578
579 clkp->divisor = src.divisor;
580 }
581
582 s3c24xx_serial_calcbaud(res, port, clkp, baud);
583 best = res;
584 resptr = best + 1;
585 } else {
586 resptr = res;
587
588 for (i = 0; i < cfg->clocks_size; i++, clkp++) {
589 if (s3c24xx_serial_calcbaud(resptr, port, clkp, baud))
590 resptr++;
591 }
592 }
593
594 /* ok, we now need to select the best clock we found */
595
596 if (!best) {
597 unsigned int deviation = (1<<30)|((1<<30)-1);
598 int calc_deviation;
599
600 for (sptr = res; sptr < resptr; sptr++) {
601 calc_deviation = baud - sptr->calc;
602 if (calc_deviation < 0)
603 calc_deviation = -calc_deviation;
604
605 if (calc_deviation < deviation) {
606 best = sptr;
607 deviation = calc_deviation;
608 }
609 }
610 }
611
612 /* store results to pass back */
613
614 *clksrc = best->clksrc;
615 *clk = best->src;
616
617 return best->quot;
618}
619
620static void s3c24xx_serial_set_termios(struct uart_port *port,
621 struct ktermios *termios,
622 struct ktermios *old)
623{
624 struct s3c2410_uartcfg *cfg = s3c24xx_port_to_cfg(port);
625 struct s3c24xx_uart_port *ourport = to_ourport(port);
626 struct s3c24xx_uart_clksrc *clksrc = NULL;
627 struct clk *clk = NULL;
628 unsigned long flags;
629 unsigned int baud, quot;
630 unsigned int ulcon;
631 unsigned int umcon;
632
633 /*
634 * We don't support modem control lines.
635 */
636 termios->c_cflag &= ~(HUPCL | CMSPAR);
637 termios->c_cflag |= CLOCAL;
638
639 /*
640 * Ask the core to calculate the divisor for us.
641 */
642
643 baud = uart_get_baud_rate(port, termios, old, 0, 115200*8);
644
645 if (baud == 38400 && (port->flags & UPF_SPD_MASK) == UPF_SPD_CUST)
646 quot = port->custom_divisor;
647 else
648 quot = s3c24xx_serial_getclk(port, &clksrc, &clk, baud);
649
650 /* check to see if we need to change clock source */
651
652 if (ourport->clksrc != clksrc || ourport->baudclk != clk) {
653 s3c24xx_serial_setsource(port, clksrc);
654
655 if (ourport->baudclk != NULL && !IS_ERR(ourport->baudclk)) {
656 clk_disable(ourport->baudclk);
657 ourport->baudclk = NULL;
658 }
659
660 clk_enable(clk);
661
662 ourport->clksrc = clksrc;
663 ourport->baudclk = clk;
664 }
665
666 switch (termios->c_cflag & CSIZE) {
667 case CS5:
668 dbg("config: 5bits/char\n");
669 ulcon = S3C2410_LCON_CS5;
670 break;
671 case CS6:
672 dbg("config: 6bits/char\n");
673 ulcon = S3C2410_LCON_CS6;
674 break;
675 case CS7:
676 dbg("config: 7bits/char\n");
677 ulcon = S3C2410_LCON_CS7;
678 break;
679 case CS8:
680 default:
681 dbg("config: 8bits/char\n");
682 ulcon = S3C2410_LCON_CS8;
683 break;
684 }
685
686 /* preserve original lcon IR settings */
687 ulcon |= (cfg->ulcon & S3C2410_LCON_IRM);
688
689 if (termios->c_cflag & CSTOPB)
690 ulcon |= S3C2410_LCON_STOPB;
691
692 umcon = (termios->c_cflag & CRTSCTS) ? S3C2410_UMCOM_AFC : 0;
693
694 if (termios->c_cflag & PARENB) {
695 if (termios->c_cflag & PARODD)
696 ulcon |= S3C2410_LCON_PODD;
697 else
698 ulcon |= S3C2410_LCON_PEVEN;
699 } else {
700 ulcon |= S3C2410_LCON_PNONE;
701 }
702
703 spin_lock_irqsave(&port->lock, flags);
704
705 dbg("setting ulcon to %08x, brddiv to %d\n", ulcon, quot);
706
707 wr_regl(port, S3C2410_ULCON, ulcon);
708 wr_regl(port, S3C2410_UBRDIV, quot);
709 wr_regl(port, S3C2410_UMCON, umcon);
710
711 dbg("uart: ulcon = 0x%08x, ucon = 0x%08x, ufcon = 0x%08x\n",
712 rd_regl(port, S3C2410_ULCON),
713 rd_regl(port, S3C2410_UCON),
714 rd_regl(port, S3C2410_UFCON));
715
716 /*
717 * Update the per-port timeout.
718 */
719 uart_update_timeout(port, termios->c_cflag, baud);
720
721 /*
722 * Which character status flags are we interested in?
723 */
724 port->read_status_mask = S3C2410_UERSTAT_OVERRUN;
725 if (termios->c_iflag & INPCK)
726 port->read_status_mask |= S3C2410_UERSTAT_FRAME | S3C2410_UERSTAT_PARITY;
727
728 /*
729 * Which character status flags should we ignore?
730 */
731 port->ignore_status_mask = 0;
732 if (termios->c_iflag & IGNPAR)
733 port->ignore_status_mask |= S3C2410_UERSTAT_OVERRUN;
734 if (termios->c_iflag & IGNBRK && termios->c_iflag & IGNPAR)
735 port->ignore_status_mask |= S3C2410_UERSTAT_FRAME;
736
737 /*
738 * Ignore all characters if CREAD is not set.
739 */
740 if ((termios->c_cflag & CREAD) == 0)
741 port->ignore_status_mask |= RXSTAT_DUMMY_READ;
742
743 spin_unlock_irqrestore(&port->lock, flags);
744}
745
746static const char *s3c24xx_serial_type(struct uart_port *port)
747{
748 switch (port->type) {
749 case PORT_S3C2410:
750 return "S3C2410";
751 case PORT_S3C2440:
752 return "S3C2440";
753 case PORT_S3C2412:
754 return "S3C2412";
755 default:
756 return NULL;
757 }
758}
759
760#define MAP_SIZE (0x100)
761
762static void s3c24xx_serial_release_port(struct uart_port *port)
763{
764 release_mem_region(port->mapbase, MAP_SIZE);
765}
766
767static int s3c24xx_serial_request_port(struct uart_port *port)
768{
769 const char *name = s3c24xx_serial_portname(port);
770 return request_mem_region(port->mapbase, MAP_SIZE, name) ? 0 : -EBUSY;
771}
772
773static void s3c24xx_serial_config_port(struct uart_port *port, int flags)
774{
775 struct s3c24xx_uart_info *info = s3c24xx_port_to_info(port);
776
777 if (flags & UART_CONFIG_TYPE &&
778 s3c24xx_serial_request_port(port) == 0)
779 port->type = info->type;
780}
781
782/*
783 * verify the new serial_struct (for TIOCSSERIAL).
784 */
785static int
786s3c24xx_serial_verify_port(struct uart_port *port, struct serial_struct *ser)
787{
788 struct s3c24xx_uart_info *info = s3c24xx_port_to_info(port);
789
790 if (ser->type != PORT_UNKNOWN && ser->type != info->type)
791 return -EINVAL;
792
793 return 0;
794}
795
796
797#ifdef CONFIG_SERIAL_SAMSUNG_CONSOLE
798
799static struct console s3c24xx_serial_console;
800
801#define S3C24XX_SERIAL_CONSOLE &s3c24xx_serial_console
802#else
803#define S3C24XX_SERIAL_CONSOLE NULL
804#endif
805
806static struct uart_ops s3c24xx_serial_ops = {
807 .pm = s3c24xx_serial_pm,
808 .tx_empty = s3c24xx_serial_tx_empty,
809 .get_mctrl = s3c24xx_serial_get_mctrl,
810 .set_mctrl = s3c24xx_serial_set_mctrl,
811 .stop_tx = s3c24xx_serial_stop_tx,
812 .start_tx = s3c24xx_serial_start_tx,
813 .stop_rx = s3c24xx_serial_stop_rx,
814 .enable_ms = s3c24xx_serial_enable_ms,
815 .break_ctl = s3c24xx_serial_break_ctl,
816 .startup = s3c24xx_serial_startup,
817 .shutdown = s3c24xx_serial_shutdown,
818 .set_termios = s3c24xx_serial_set_termios,
819 .type = s3c24xx_serial_type,
820 .release_port = s3c24xx_serial_release_port,
821 .request_port = s3c24xx_serial_request_port,
822 .config_port = s3c24xx_serial_config_port,
823 .verify_port = s3c24xx_serial_verify_port,
824};
825
826
827static struct uart_driver s3c24xx_uart_drv = {
828 .owner = THIS_MODULE,
829 .dev_name = "s3c2410_serial",
830 .nr = 3,
831 .cons = S3C24XX_SERIAL_CONSOLE,
832 .driver_name = S3C24XX_SERIAL_NAME,
833 .major = S3C24XX_SERIAL_MAJOR,
834 .minor = S3C24XX_SERIAL_MINOR,
835};
836
837static struct s3c24xx_uart_port s3c24xx_serial_ports[NR_PORTS] = {
838 [0] = {
839 .port = {
840 .lock = __SPIN_LOCK_UNLOCKED(s3c24xx_serial_ports[0].port.lock),
841 .iotype = UPIO_MEM,
842 .irq = IRQ_S3CUART_RX0,
843 .uartclk = 0,
844 .fifosize = 16,
845 .ops = &s3c24xx_serial_ops,
846 .flags = UPF_BOOT_AUTOCONF,
847 .line = 0,
848 }
849 },
850 [1] = {
851 .port = {
852 .lock = __SPIN_LOCK_UNLOCKED(s3c24xx_serial_ports[1].port.lock),
853 .iotype = UPIO_MEM,
854 .irq = IRQ_S3CUART_RX1,
855 .uartclk = 0,
856 .fifosize = 16,
857 .ops = &s3c24xx_serial_ops,
858 .flags = UPF_BOOT_AUTOCONF,
859 .line = 1,
860 }
861 },
862#if NR_PORTS > 2
863
864 [2] = {
865 .port = {
866 .lock = __SPIN_LOCK_UNLOCKED(s3c24xx_serial_ports[2].port.lock),
867 .iotype = UPIO_MEM,
868 .irq = IRQ_S3CUART_RX2,
869 .uartclk = 0,
870 .fifosize = 16,
871 .ops = &s3c24xx_serial_ops,
872 .flags = UPF_BOOT_AUTOCONF,
873 .line = 2,
874 }
875 }
876#endif
877};
878
879/* s3c24xx_serial_resetport
880 *
881 * wrapper to call the specific reset for this port (reset the fifos
882 * and the settings)
883*/
884
885static inline int s3c24xx_serial_resetport(struct uart_port *port,
886 struct s3c2410_uartcfg *cfg)
887{
888 struct s3c24xx_uart_info *info = s3c24xx_port_to_info(port);
889
890 return (info->reset_port)(port, cfg);
891}
892
893/* s3c24xx_serial_init_port
894 *
895 * initialise a single serial port from the platform device given
896 */
897
898static int s3c24xx_serial_init_port(struct s3c24xx_uart_port *ourport,
899 struct s3c24xx_uart_info *info,
900 struct platform_device *platdev)
901{
902 struct uart_port *port = &ourport->port;
903 struct s3c2410_uartcfg *cfg;
904 struct resource *res;
905 int ret;
906
907 dbg("s3c24xx_serial_init_port: port=%p, platdev=%p\n", port, platdev);
908
909 if (platdev == NULL)
910 return -ENODEV;
911
912 cfg = s3c24xx_dev_to_cfg(&platdev->dev);
913
914 if (port->mapbase != 0)
915 return 0;
916
917 if (cfg->hwport > 3)
918 return -EINVAL;
919
920 /* setup info for port */
921 port->dev = &platdev->dev;
922 ourport->info = info;
923
924 /* copy the info in from provided structure */
925 ourport->port.fifosize = info->fifosize;
926
927 dbg("s3c24xx_serial_init_port: %p (hw %d)...\n", port, cfg->hwport);
928
929 port->uartclk = 1;
930
931 if (cfg->uart_flags & UPF_CONS_FLOW) {
932 dbg("s3c24xx_serial_init_port: enabling flow control\n");
933 port->flags |= UPF_CONS_FLOW;
934 }
935
936 /* sort our the physical and virtual addresses for each UART */
937
938 res = platform_get_resource(platdev, IORESOURCE_MEM, 0);
939 if (res == NULL) {
940 printk(KERN_ERR "failed to find memory resource for uart\n");
941 return -EINVAL;
942 }
943
944 dbg("resource %p (%lx..%lx)\n", res, res->start, res->end);
945
946 port->mapbase = res->start;
947 port->membase = S3C24XX_VA_UART + (res->start - S3C24XX_PA_UART);
948 ret = platform_get_irq(platdev, 0);
949 if (ret < 0)
950 port->irq = 0;
951 else
952 port->irq = ret;
953
954 ourport->clk = clk_get(&platdev->dev, "uart");
955
956 dbg("port: map=%08x, mem=%08x, irq=%d, clock=%ld\n",
957 port->mapbase, port->membase, port->irq, port->uartclk);
958
959 /* reset the fifos (and setup the uart) */
960 s3c24xx_serial_resetport(port, cfg);
961 return 0;
962}
963
964static ssize_t s3c24xx_serial_show_clksrc(struct device *dev,
965 struct device_attribute *attr,
966 char *buf)
967{
968 struct uart_port *port = s3c24xx_dev_to_port(dev);
969 struct s3c24xx_uart_port *ourport = to_ourport(port);
970
971 return snprintf(buf, PAGE_SIZE, "* %s\n", ourport->clksrc->name);
972}
973
974static DEVICE_ATTR(clock_source, S_IRUGO, s3c24xx_serial_show_clksrc, NULL);
975
976/* Device driver serial port probe */
977
978static int probe_index;
979
980int s3c24xx_serial_probe(struct platform_device *dev,
981 struct s3c24xx_uart_info *info)
982{
983 struct s3c24xx_uart_port *ourport;
984 int ret;
985
986 dbg("s3c24xx_serial_probe(%p, %p) %d\n", dev, info, probe_index);
987
988 ourport = &s3c24xx_serial_ports[probe_index];
989 probe_index++;
990
991 dbg("%s: initialising port %p...\n", __func__, ourport);
992
993 ret = s3c24xx_serial_init_port(ourport, info, dev);
994 if (ret < 0)
995 goto probe_err;
996
997 dbg("%s: adding port\n", __func__);
998 uart_add_one_port(&s3c24xx_uart_drv, &ourport->port);
999 platform_set_drvdata(dev, &ourport->port);
1000
1001 ret = device_create_file(&dev->dev, &dev_attr_clock_source);
1002 if (ret < 0)
1003 printk(KERN_ERR "%s: failed to add clksrc attr.\n", __func__);
1004
1005 return 0;
1006
1007 probe_err:
1008 return ret;
1009}
1010
1011EXPORT_SYMBOL_GPL(s3c24xx_serial_probe);
1012
1013int s3c24xx_serial_remove(struct platform_device *dev)
1014{
1015 struct uart_port *port = s3c24xx_dev_to_port(&dev->dev);
1016
1017 if (port) {
1018 device_remove_file(&dev->dev, &dev_attr_clock_source);
1019 uart_remove_one_port(&s3c24xx_uart_drv, port);
1020 }
1021
1022 return 0;
1023}
1024
1025EXPORT_SYMBOL_GPL(s3c24xx_serial_remove);
1026
1027/* UART power management code */
1028
1029#ifdef CONFIG_PM
1030
1031static int s3c24xx_serial_suspend(struct platform_device *dev, pm_message_t state)
1032{
1033 struct uart_port *port = s3c24xx_dev_to_port(&dev->dev);
1034
1035 if (port)
1036 uart_suspend_port(&s3c24xx_uart_drv, port);
1037
1038 return 0;
1039}
1040
1041static int s3c24xx_serial_resume(struct platform_device *dev)
1042{
1043 struct uart_port *port = s3c24xx_dev_to_port(&dev->dev);
1044 struct s3c24xx_uart_port *ourport = to_ourport(port);
1045
1046 if (port) {
1047 clk_enable(ourport->clk);
1048 s3c24xx_serial_resetport(port, s3c24xx_port_to_cfg(port));
1049 clk_disable(ourport->clk);
1050
1051 uart_resume_port(&s3c24xx_uart_drv, port);
1052 }
1053
1054 return 0;
1055}
1056#endif
1057
1058int s3c24xx_serial_init(struct platform_driver *drv,
1059 struct s3c24xx_uart_info *info)
1060{
1061 dbg("s3c24xx_serial_init(%p,%p)\n", drv, info);
1062
1063#ifdef CONFIG_PM
1064 drv->suspend = s3c24xx_serial_suspend;
1065 drv->resume = s3c24xx_serial_resume;
1066#endif
1067
1068 return platform_driver_register(drv);
1069}
1070
1071EXPORT_SYMBOL_GPL(s3c24xx_serial_init);
1072
1073/* module initialisation code */
1074
1075static int __init s3c24xx_serial_modinit(void)
1076{
1077 int ret;
1078
1079 ret = uart_register_driver(&s3c24xx_uart_drv);
1080 if (ret < 0) {
1081 printk(KERN_ERR "failed to register UART driver\n");
1082 return -1;
1083 }
1084
1085 return 0;
1086}
1087
1088static void __exit s3c24xx_serial_modexit(void)
1089{
1090 uart_unregister_driver(&s3c24xx_uart_drv);
1091}
1092
1093module_init(s3c24xx_serial_modinit);
1094module_exit(s3c24xx_serial_modexit);
1095
1096/* Console code */
1097
1098#ifdef CONFIG_SERIAL_SAMSUNG_CONSOLE
1099
1100static struct uart_port *cons_uart;
1101
1102static int
1103s3c24xx_serial_console_txrdy(struct uart_port *port, unsigned int ufcon)
1104{
1105 struct s3c24xx_uart_info *info = s3c24xx_port_to_info(port);
1106 unsigned long ufstat, utrstat;
1107
1108 if (ufcon & S3C2410_UFCON_FIFOMODE) {
1109 /* fifo mode - check ammount of data in fifo registers... */
1110
1111 ufstat = rd_regl(port, S3C2410_UFSTAT);
1112 return (ufstat & info->tx_fifofull) ? 0 : 1;
1113 }
1114
1115 /* in non-fifo mode, we go and use the tx buffer empty */
1116
1117 utrstat = rd_regl(port, S3C2410_UTRSTAT);
1118 return (utrstat & S3C2410_UTRSTAT_TXE) ? 1 : 0;
1119}
1120
1121static void
1122s3c24xx_serial_console_putchar(struct uart_port *port, int ch)
1123{
1124 unsigned int ufcon = rd_regl(cons_uart, S3C2410_UFCON);
1125 while (!s3c24xx_serial_console_txrdy(port, ufcon))
1126 barrier();
1127 wr_regb(cons_uart, S3C2410_UTXH, ch);
1128}
1129
1130static void
1131s3c24xx_serial_console_write(struct console *co, const char *s,
1132 unsigned int count)
1133{
1134 uart_console_write(cons_uart, s, count, s3c24xx_serial_console_putchar);
1135}
1136
1137static void __init
1138s3c24xx_serial_get_options(struct uart_port *port, int *baud,
1139 int *parity, int *bits)
1140{
1141 struct s3c24xx_uart_clksrc clksrc;
1142 struct clk *clk;
1143 unsigned int ulcon;
1144 unsigned int ucon;
1145 unsigned int ubrdiv;
1146 unsigned long rate;
1147
1148 ulcon = rd_regl(port, S3C2410_ULCON);
1149 ucon = rd_regl(port, S3C2410_UCON);
1150 ubrdiv = rd_regl(port, S3C2410_UBRDIV);
1151
1152 dbg("s3c24xx_serial_get_options: port=%p\n"
1153 "registers: ulcon=%08x, ucon=%08x, ubdriv=%08x\n",
1154 port, ulcon, ucon, ubrdiv);
1155
1156 if ((ucon & 0xf) != 0) {
1157 /* consider the serial port configured if the tx/rx mode set */
1158
1159 switch (ulcon & S3C2410_LCON_CSMASK) {
1160 case S3C2410_LCON_CS5:
1161 *bits = 5;
1162 break;
1163 case S3C2410_LCON_CS6:
1164 *bits = 6;
1165 break;
1166 case S3C2410_LCON_CS7:
1167 *bits = 7;
1168 break;
1169 default:
1170 case S3C2410_LCON_CS8:
1171 *bits = 8;
1172 break;
1173 }
1174
1175 switch (ulcon & S3C2410_LCON_PMASK) {
1176 case S3C2410_LCON_PEVEN:
1177 *parity = 'e';
1178 break;
1179
1180 case S3C2410_LCON_PODD:
1181 *parity = 'o';
1182 break;
1183
1184 case S3C2410_LCON_PNONE:
1185 default:
1186 *parity = 'n';
1187 }
1188
1189 /* now calculate the baud rate */
1190
1191 s3c24xx_serial_getsource(port, &clksrc);
1192
1193 clk = clk_get(port->dev, clksrc.name);
1194 if (!IS_ERR(clk) && clk != NULL)
1195 rate = clk_get_rate(clk) / clksrc.divisor;
1196 else
1197 rate = 1;
1198
1199
1200 *baud = rate / (16 * (ubrdiv + 1));
1201 dbg("calculated baud %d\n", *baud);
1202 }
1203
1204}
1205
1206/* s3c24xx_serial_init_ports
1207 *
1208 * initialise the serial ports from the machine provided initialisation
1209 * data.
1210*/
1211
1212static int s3c24xx_serial_init_ports(struct s3c24xx_uart_info *info)
1213{
1214 struct s3c24xx_uart_port *ptr = s3c24xx_serial_ports;
1215 struct platform_device **platdev_ptr;
1216 int i;
1217
1218 dbg("s3c24xx_serial_init_ports: initialising ports...\n");
1219
1220 platdev_ptr = s3c24xx_uart_devs;
1221
1222 for (i = 0; i < NR_PORTS; i++, ptr++, platdev_ptr++) {
1223 s3c24xx_serial_init_port(ptr, info, *platdev_ptr);
1224 }
1225
1226 return 0;
1227}
1228
1229static int __init
1230s3c24xx_serial_console_setup(struct console *co, char *options)
1231{
1232 struct uart_port *port;
1233 int baud = 9600;
1234 int bits = 8;
1235 int parity = 'n';
1236 int flow = 'n';
1237
1238 dbg("s3c24xx_serial_console_setup: co=%p (%d), %s\n",
1239 co, co->index, options);
1240
1241 /* is this a valid port */
1242
1243 if (co->index == -1 || co->index >= NR_PORTS)
1244 co->index = 0;
1245
1246 port = &s3c24xx_serial_ports[co->index].port;
1247
1248 /* is the port configured? */
1249
1250 if (port->mapbase == 0x0) {
1251 co->index = 0;
1252 port = &s3c24xx_serial_ports[co->index].port;
1253 }
1254
1255 cons_uart = port;
1256
1257 dbg("s3c24xx_serial_console_setup: port=%p (%d)\n", port, co->index);
1258
1259 /*
1260 * Check whether an invalid uart number has been specified, and
1261 * if so, search for the first available port that does have
1262 * console support.
1263 */
1264 if (options)
1265 uart_parse_options(options, &baud, &parity, &bits, &flow);
1266 else
1267 s3c24xx_serial_get_options(port, &baud, &parity, &bits);
1268
1269 dbg("s3c24xx_serial_console_setup: baud %d\n", baud);
1270
1271 return uart_set_options(port, co, baud, parity, bits, flow);
1272}
1273
1274/* s3c24xx_serial_initconsole
1275 *
1276 * initialise the console from one of the uart drivers
1277*/
1278
1279static struct console s3c24xx_serial_console = {
1280 .name = S3C24XX_SERIAL_NAME,
1281 .device = uart_console_device,
1282 .flags = CON_PRINTBUFFER,
1283 .index = -1,
1284 .write = s3c24xx_serial_console_write,
1285 .setup = s3c24xx_serial_console_setup
1286};
1287
1288int s3c24xx_serial_initconsole(struct platform_driver *drv,
1289 struct s3c24xx_uart_info *info)
1290
1291{
1292 struct platform_device *dev = s3c24xx_uart_devs[0];
1293
1294 dbg("s3c24xx_serial_initconsole\n");
1295
1296 /* select driver based on the cpu */
1297
1298 if (dev == NULL) {
1299 printk(KERN_ERR "s3c24xx: no devices for console init\n");
1300 return 0;
1301 }
1302
1303 if (strcmp(dev->name, drv->driver.name) != 0)
1304 return 0;
1305
1306 s3c24xx_serial_console.data = &s3c24xx_uart_drv;
1307 s3c24xx_serial_init_ports(info);
1308
1309 register_console(&s3c24xx_serial_console);
1310 return 0;
1311}
1312
1313#endif /* CONFIG_SERIAL_SAMSUNG_CONSOLE */
1314
1315MODULE_DESCRIPTION("Samsung SoC Serial port driver");
1316MODULE_AUTHOR("Ben Dooks <ben@simtec.co.uk>");
1317MODULE_LICENSE("GPL v2");