aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/serial/s3c2410.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/s3c2410.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/s3c2410.c')
-rw-r--r--drivers/serial/s3c2410.c1834
1 files changed, 11 insertions, 1823 deletions
diff --git a/drivers/serial/s3c2410.c b/drivers/serial/s3c2410.c
index b87c0b55aa22..c5f03f41686f 100644
--- a/drivers/serial/s3c2410.c
+++ b/drivers/serial/s3c2410.c
@@ -1,8 +1,8 @@
1/* linux/drivers/serial/s3c2410.c 1/* linux/drivers/serial/s3c2410.c
2 * 2 *
3 * Driver for Samsung SoC onboard UARTs. 3 * Driver for Samsung S3C2410 SoC onboard UARTs.
4 * 4 *
5 * Ben Dooks, Copyright (c) 2003-2005 Simtec Electronics 5 * Ben Dooks, Copyright (c) 2003-2005,2008 Simtec Electronics
6 * http://armlinux.simtec.co.uk/ 6 * http://armlinux.simtec.co.uk/
7 * 7 *
8 * This program is free software; you can redistribute it and/or modify 8 * This program is free software; you can redistribute it and/or modify
@@ -10,1247 +10,21 @@
10 * published by the Free Software Foundation. 10 * published by the Free Software Foundation.
11*/ 11*/
12 12
13/* Note on 2440 fclk clock source handling
14 *
15 * Whilst it is possible to use the fclk as clock source, the method
16 * of properly switching too/from this is currently un-implemented, so
17 * whichever way is configured at startup is the one that will be used.
18*/
19
20/* Hote on 2410 error handling
21 *
22 * The s3c2410 manual has a love/hate affair with the contents of the
23 * UERSTAT register in the UART blocks, and keeps marking some of the
24 * error bits as reserved. Having checked with the s3c2410x01,
25 * it copes with BREAKs properly, so I am happy to ignore the RESERVED
26 * feature from the latter versions of the manual.
27 *
28 * If it becomes aparrent that latter versions of the 2410 remove these
29 * bits, then action will have to be taken to differentiate the versions
30 * and change the policy on BREAK
31 *
32 * BJD, 04-Nov-2004
33*/
34
35
36#if defined(CONFIG_SERIAL_S3C2410_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)
37#define SUPPORT_SYSRQ
38#endif
39
40#include <linux/module.h> 13#include <linux/module.h>
41#include <linux/ioport.h> 14#include <linux/ioport.h>
15#include <linux/io.h>
42#include <linux/platform_device.h> 16#include <linux/platform_device.h>
43#include <linux/init.h> 17#include <linux/init.h>
44#include <linux/sysrq.h>
45#include <linux/console.h>
46#include <linux/tty.h>
47#include <linux/tty_flip.h>
48#include <linux/serial_core.h> 18#include <linux/serial_core.h>
49#include <linux/serial.h> 19#include <linux/serial.h>
50#include <linux/delay.h>
51#include <linux/clk.h>
52 20
53#include <asm/io.h>
54#include <asm/irq.h> 21#include <asm/irq.h>
55
56#include <asm/hardware.h> 22#include <asm/hardware.h>
57 23
58#include <asm/plat-s3c/regs-serial.h> 24#include <asm/plat-s3c/regs-serial.h>
59#include <asm/arch/regs-gpio.h> 25#include <asm/arch/regs-gpio.h>
60 26
61/* structures */ 27#include "samsung.h"
62
63struct s3c24xx_uart_info {
64 char *name;
65 unsigned int type;
66 unsigned int fifosize;
67 unsigned long rx_fifomask;
68 unsigned long rx_fifoshift;
69 unsigned long rx_fifofull;
70 unsigned long tx_fifomask;
71 unsigned long tx_fifoshift;
72 unsigned long tx_fifofull;
73
74 /* clock source control */
75
76 int (*get_clksrc)(struct uart_port *, struct s3c24xx_uart_clksrc *clk);
77 int (*set_clksrc)(struct uart_port *, struct s3c24xx_uart_clksrc *clk);
78
79 /* uart controls */
80 int (*reset_port)(struct uart_port *, struct s3c2410_uartcfg *);
81};
82
83struct s3c24xx_uart_port {
84 unsigned char rx_claimed;
85 unsigned char tx_claimed;
86
87 struct s3c24xx_uart_info *info;
88 struct s3c24xx_uart_clksrc *clksrc;
89 struct clk *clk;
90 struct clk *baudclk;
91 struct uart_port port;
92};
93
94
95/* configuration defines */
96
97#if 0
98#if 1
99/* send debug to the low-level output routines */
100
101extern void printascii(const char *);
102
103static void
104s3c24xx_serial_dbg(const char *fmt, ...)
105{
106 va_list va;
107 char buff[256];
108
109 va_start(va, fmt);
110 vsprintf(buff, fmt, va);
111 va_end(va);
112
113 printascii(buff);
114}
115
116#define dbg(x...) s3c24xx_serial_dbg(x)
117
118#else
119#define dbg(x...) printk(KERN_DEBUG "s3c24xx: ");
120#endif
121#else /* no debug */
122#define dbg(x...) do {} while(0)
123#endif
124
125/* UART name and device definitions */
126
127#define S3C24XX_SERIAL_NAME "ttySAC"
128#define S3C24XX_SERIAL_MAJOR 204
129#define S3C24XX_SERIAL_MINOR 64
130
131
132/* conversion functions */
133
134#define s3c24xx_dev_to_port(__dev) (struct uart_port *)dev_get_drvdata(__dev)
135#define s3c24xx_dev_to_cfg(__dev) (struct s3c2410_uartcfg *)((__dev)->platform_data)
136
137/* we can support 3 uarts, but not always use them */
138
139#ifdef CONFIG_CPU_S3C2400
140#define NR_PORTS (2)
141#else
142#define NR_PORTS (3)
143#endif
144
145/* port irq numbers */
146
147#define TX_IRQ(port) ((port)->irq + 1)
148#define RX_IRQ(port) ((port)->irq)
149
150/* register access controls */
151
152#define portaddr(port, reg) ((port)->membase + (reg))
153
154#define rd_regb(port, reg) (__raw_readb(portaddr(port, reg)))
155#define rd_regl(port, reg) (__raw_readl(portaddr(port, reg)))
156
157#define wr_regb(port, reg, val) \
158 do { __raw_writeb(val, portaddr(port, reg)); } while(0)
159
160#define wr_regl(port, reg, val) \
161 do { __raw_writel(val, portaddr(port, reg)); } while(0)
162
163/* macros to change one thing to another */
164
165#define tx_enabled(port) ((port)->unused[0])
166#define rx_enabled(port) ((port)->unused[1])
167
168/* flag to ignore all characters comming in */
169#define RXSTAT_DUMMY_READ (0x10000000)
170
171static inline struct s3c24xx_uart_port *to_ourport(struct uart_port *port)
172{
173 return container_of(port, struct s3c24xx_uart_port, port);
174}
175
176/* translate a port to the device name */
177
178static inline const char *s3c24xx_serial_portname(struct uart_port *port)
179{
180 return to_platform_device(port->dev)->name;
181}
182
183static int s3c24xx_serial_txempty_nofifo(struct uart_port *port)
184{
185 return (rd_regl(port, S3C2410_UTRSTAT) & S3C2410_UTRSTAT_TXE);
186}
187
188static void s3c24xx_serial_rx_enable(struct uart_port *port)
189{
190 unsigned long flags;
191 unsigned int ucon, ufcon;
192 int count = 10000;
193
194 spin_lock_irqsave(&port->lock, flags);
195
196 while (--count && !s3c24xx_serial_txempty_nofifo(port))
197 udelay(100);
198
199 ufcon = rd_regl(port, S3C2410_UFCON);
200 ufcon |= S3C2410_UFCON_RESETRX;
201 wr_regl(port, S3C2410_UFCON, ufcon);
202
203 ucon = rd_regl(port, S3C2410_UCON);
204 ucon |= S3C2410_UCON_RXIRQMODE;
205 wr_regl(port, S3C2410_UCON, ucon);
206
207 rx_enabled(port) = 1;
208 spin_unlock_irqrestore(&port->lock, flags);
209}
210
211static void s3c24xx_serial_rx_disable(struct uart_port *port)
212{
213 unsigned long flags;
214 unsigned int ucon;
215
216 spin_lock_irqsave(&port->lock, flags);
217
218 ucon = rd_regl(port, S3C2410_UCON);
219 ucon &= ~S3C2410_UCON_RXIRQMODE;
220 wr_regl(port, S3C2410_UCON, ucon);
221
222 rx_enabled(port) = 0;
223 spin_unlock_irqrestore(&port->lock, flags);
224}
225
226static void s3c24xx_serial_stop_tx(struct uart_port *port)
227{
228 if (tx_enabled(port)) {
229 disable_irq(TX_IRQ(port));
230 tx_enabled(port) = 0;
231 if (port->flags & UPF_CONS_FLOW)
232 s3c24xx_serial_rx_enable(port);
233 }
234}
235
236static void s3c24xx_serial_start_tx(struct uart_port *port)
237{
238 if (!tx_enabled(port)) {
239 if (port->flags & UPF_CONS_FLOW)
240 s3c24xx_serial_rx_disable(port);
241
242 enable_irq(TX_IRQ(port));
243 tx_enabled(port) = 1;
244 }
245}
246
247
248static void s3c24xx_serial_stop_rx(struct uart_port *port)
249{
250 if (rx_enabled(port)) {
251 dbg("s3c24xx_serial_stop_rx: port=%p\n", port);
252 disable_irq(RX_IRQ(port));
253 rx_enabled(port) = 0;
254 }
255}
256
257static void s3c24xx_serial_enable_ms(struct uart_port *port)
258{
259}
260
261static inline struct s3c24xx_uart_info *s3c24xx_port_to_info(struct uart_port *port)
262{
263 return to_ourport(port)->info;
264}
265
266static inline struct s3c2410_uartcfg *s3c24xx_port_to_cfg(struct uart_port *port)
267{
268 if (port->dev == NULL)
269 return NULL;
270
271 return (struct s3c2410_uartcfg *)port->dev->platform_data;
272}
273
274static int s3c24xx_serial_rx_fifocnt(struct s3c24xx_uart_port *ourport,
275 unsigned long ufstat)
276{
277 struct s3c24xx_uart_info *info = ourport->info;
278
279 if (ufstat & info->rx_fifofull)
280 return info->fifosize;
281
282 return (ufstat & info->rx_fifomask) >> info->rx_fifoshift;
283}
284
285
286/* ? - where has parity gone?? */
287#define S3C2410_UERSTAT_PARITY (0x1000)
288
289static irqreturn_t
290s3c24xx_serial_rx_chars(int irq, void *dev_id)
291{
292 struct s3c24xx_uart_port *ourport = dev_id;
293 struct uart_port *port = &ourport->port;
294 struct tty_struct *tty = port->info->tty;
295 unsigned int ufcon, ch, flag, ufstat, uerstat;
296 int max_count = 64;
297
298 while (max_count-- > 0) {
299 ufcon = rd_regl(port, S3C2410_UFCON);
300 ufstat = rd_regl(port, S3C2410_UFSTAT);
301
302 if (s3c24xx_serial_rx_fifocnt(ourport, ufstat) == 0)
303 break;
304
305 uerstat = rd_regl(port, S3C2410_UERSTAT);
306 ch = rd_regb(port, S3C2410_URXH);
307
308 if (port->flags & UPF_CONS_FLOW) {
309 int txe = s3c24xx_serial_txempty_nofifo(port);
310
311 if (rx_enabled(port)) {
312 if (!txe) {
313 rx_enabled(port) = 0;
314 continue;
315 }
316 } else {
317 if (txe) {
318 ufcon |= S3C2410_UFCON_RESETRX;
319 wr_regl(port, S3C2410_UFCON, ufcon);
320 rx_enabled(port) = 1;
321 goto out;
322 }
323 continue;
324 }
325 }
326
327 /* insert the character into the buffer */
328
329 flag = TTY_NORMAL;
330 port->icount.rx++;
331
332 if (unlikely(uerstat & S3C2410_UERSTAT_ANY)) {
333 dbg("rxerr: port ch=0x%02x, rxs=0x%08x\n",
334 ch, uerstat);
335
336 /* check for break */
337 if (uerstat & S3C2410_UERSTAT_BREAK) {
338 dbg("break!\n");
339 port->icount.brk++;
340 if (uart_handle_break(port))
341 goto ignore_char;
342 }
343
344 if (uerstat & S3C2410_UERSTAT_FRAME)
345 port->icount.frame++;
346 if (uerstat & S3C2410_UERSTAT_OVERRUN)
347 port->icount.overrun++;
348
349 uerstat &= port->read_status_mask;
350
351 if (uerstat & S3C2410_UERSTAT_BREAK)
352 flag = TTY_BREAK;
353 else if (uerstat & S3C2410_UERSTAT_PARITY)
354 flag = TTY_PARITY;
355 else if (uerstat & ( S3C2410_UERSTAT_FRAME | S3C2410_UERSTAT_OVERRUN))
356 flag = TTY_FRAME;
357 }
358
359 if (uart_handle_sysrq_char(port, ch))
360 goto ignore_char;
361
362 uart_insert_char(port, uerstat, S3C2410_UERSTAT_OVERRUN, ch, flag);
363
364 ignore_char:
365 continue;
366 }
367 tty_flip_buffer_push(tty);
368
369 out:
370 return IRQ_HANDLED;
371}
372
373static irqreturn_t s3c24xx_serial_tx_chars(int irq, void *id)
374{
375 struct s3c24xx_uart_port *ourport = id;
376 struct uart_port *port = &ourport->port;
377 struct circ_buf *xmit = &port->info->xmit;
378 int count = 256;
379
380 if (port->x_char) {
381 wr_regb(port, S3C2410_UTXH, port->x_char);
382 port->icount.tx++;
383 port->x_char = 0;
384 goto out;
385 }
386
387 /* if there isnt anything more to transmit, or the uart is now
388 * stopped, disable the uart and exit
389 */
390
391 if (uart_circ_empty(xmit) || uart_tx_stopped(port)) {
392 s3c24xx_serial_stop_tx(port);
393 goto out;
394 }
395
396 /* try and drain the buffer... */
397
398 while (!uart_circ_empty(xmit) && count-- > 0) {
399 if (rd_regl(port, S3C2410_UFSTAT) & ourport->info->tx_fifofull)
400 break;
401
402 wr_regb(port, S3C2410_UTXH, xmit->buf[xmit->tail]);
403 xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
404 port->icount.tx++;
405 }
406
407 if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
408 uart_write_wakeup(port);
409
410 if (uart_circ_empty(xmit))
411 s3c24xx_serial_stop_tx(port);
412
413 out:
414 return IRQ_HANDLED;
415}
416
417static unsigned int s3c24xx_serial_tx_empty(struct uart_port *port)
418{
419 struct s3c24xx_uart_info *info = s3c24xx_port_to_info(port);
420 unsigned long ufstat = rd_regl(port, S3C2410_UFSTAT);
421 unsigned long ufcon = rd_regl(port, S3C2410_UFCON);
422
423 if (ufcon & S3C2410_UFCON_FIFOMODE) {
424 if ((ufstat & info->tx_fifomask) != 0 ||
425 (ufstat & info->tx_fifofull))
426 return 0;
427
428 return 1;
429 }
430
431 return s3c24xx_serial_txempty_nofifo(port);
432}
433
434/* no modem control lines */
435static unsigned int s3c24xx_serial_get_mctrl(struct uart_port *port)
436{
437 unsigned int umstat = rd_regb(port,S3C2410_UMSTAT);
438
439 if (umstat & S3C2410_UMSTAT_CTS)
440 return TIOCM_CAR | TIOCM_DSR | TIOCM_CTS;
441 else
442 return TIOCM_CAR | TIOCM_DSR;
443}
444
445static void s3c24xx_serial_set_mctrl(struct uart_port *port, unsigned int mctrl)
446{
447 /* todo - possibly remove AFC and do manual CTS */
448}
449
450static void s3c24xx_serial_break_ctl(struct uart_port *port, int break_state)
451{
452 unsigned long flags;
453 unsigned int ucon;
454
455 spin_lock_irqsave(&port->lock, flags);
456
457 ucon = rd_regl(port, S3C2410_UCON);
458
459 if (break_state)
460 ucon |= S3C2410_UCON_SBREAK;
461 else
462 ucon &= ~S3C2410_UCON_SBREAK;
463
464 wr_regl(port, S3C2410_UCON, ucon);
465
466 spin_unlock_irqrestore(&port->lock, flags);
467}
468
469static void s3c24xx_serial_shutdown(struct uart_port *port)
470{
471 struct s3c24xx_uart_port *ourport = to_ourport(port);
472
473 if (ourport->tx_claimed) {
474 free_irq(TX_IRQ(port), ourport);
475 tx_enabled(port) = 0;
476 ourport->tx_claimed = 0;
477 }
478
479 if (ourport->rx_claimed) {
480 free_irq(RX_IRQ(port), ourport);
481 ourport->rx_claimed = 0;
482 rx_enabled(port) = 0;
483 }
484}
485
486
487static int s3c24xx_serial_startup(struct uart_port *port)
488{
489 struct s3c24xx_uart_port *ourport = to_ourport(port);
490 int ret;
491
492 dbg("s3c24xx_serial_startup: port=%p (%08lx,%p)\n",
493 port->mapbase, port->membase);
494
495 rx_enabled(port) = 1;
496
497 ret = request_irq(RX_IRQ(port),
498 s3c24xx_serial_rx_chars, 0,
499 s3c24xx_serial_portname(port), ourport);
500
501 if (ret != 0) {
502 printk(KERN_ERR "cannot get irq %d\n", RX_IRQ(port));
503 return ret;
504 }
505
506 ourport->rx_claimed = 1;
507
508 dbg("requesting tx irq...\n");
509
510 tx_enabled(port) = 1;
511
512 ret = request_irq(TX_IRQ(port),
513 s3c24xx_serial_tx_chars, 0,
514 s3c24xx_serial_portname(port), ourport);
515
516 if (ret) {
517 printk(KERN_ERR "cannot get irq %d\n", TX_IRQ(port));
518 goto err;
519 }
520
521 ourport->tx_claimed = 1;
522
523 dbg("s3c24xx_serial_startup ok\n");
524
525 /* the port reset code should have done the correct
526 * register setup for the port controls */
527
528 return ret;
529
530 err:
531 s3c24xx_serial_shutdown(port);
532 return ret;
533}
534
535/* power power management control */
536
537static void s3c24xx_serial_pm(struct uart_port *port, unsigned int level,
538 unsigned int old)
539{
540 struct s3c24xx_uart_port *ourport = to_ourport(port);
541
542 switch (level) {
543 case 3:
544 if (!IS_ERR(ourport->baudclk) && ourport->baudclk != NULL)
545 clk_disable(ourport->baudclk);
546
547 clk_disable(ourport->clk);
548 break;
549
550 case 0:
551 clk_enable(ourport->clk);
552
553 if (!IS_ERR(ourport->baudclk) && ourport->baudclk != NULL)
554 clk_enable(ourport->baudclk);
555
556 break;
557 default:
558 printk(KERN_ERR "s3c24xx_serial: unknown pm %d\n", level);
559 }
560}
561
562/* baud rate calculation
563 *
564 * The UARTs on the S3C2410/S3C2440 can take their clocks from a number
565 * of different sources, including the peripheral clock ("pclk") and an
566 * external clock ("uclk"). The S3C2440 also adds the core clock ("fclk")
567 * with a programmable extra divisor.
568 *
569 * The following code goes through the clock sources, and calculates the
570 * baud clocks (and the resultant actual baud rates) and then tries to
571 * pick the closest one and select that.
572 *
573*/
574
575
576#define MAX_CLKS (8)
577
578static struct s3c24xx_uart_clksrc tmp_clksrc = {
579 .name = "pclk",
580 .min_baud = 0,
581 .max_baud = 0,
582 .divisor = 1,
583};
584
585static inline int
586s3c24xx_serial_getsource(struct uart_port *port, struct s3c24xx_uart_clksrc *c)
587{
588 struct s3c24xx_uart_info *info = s3c24xx_port_to_info(port);
589
590 return (info->get_clksrc)(port, c);
591}
592
593static inline int
594s3c24xx_serial_setsource(struct uart_port *port, struct s3c24xx_uart_clksrc *c)
595{
596 struct s3c24xx_uart_info *info = s3c24xx_port_to_info(port);
597
598 return (info->set_clksrc)(port, c);
599}
600
601struct baud_calc {
602 struct s3c24xx_uart_clksrc *clksrc;
603 unsigned int calc;
604 unsigned int quot;
605 struct clk *src;
606};
607
608static int s3c24xx_serial_calcbaud(struct baud_calc *calc,
609 struct uart_port *port,
610 struct s3c24xx_uart_clksrc *clksrc,
611 unsigned int baud)
612{
613 unsigned long rate;
614
615 calc->src = clk_get(port->dev, clksrc->name);
616 if (calc->src == NULL || IS_ERR(calc->src))
617 return 0;
618
619 rate = clk_get_rate(calc->src);
620 rate /= clksrc->divisor;
621
622 calc->clksrc = clksrc;
623 calc->quot = (rate + (8 * baud)) / (16 * baud);
624 calc->calc = (rate / (calc->quot * 16));
625
626 calc->quot--;
627 return 1;
628}
629
630static unsigned int s3c24xx_serial_getclk(struct uart_port *port,
631 struct s3c24xx_uart_clksrc **clksrc,
632 struct clk **clk,
633 unsigned int baud)
634{
635 struct s3c2410_uartcfg *cfg = s3c24xx_port_to_cfg(port);
636 struct s3c24xx_uart_clksrc *clkp;
637 struct baud_calc res[MAX_CLKS];
638 struct baud_calc *resptr, *best, *sptr;
639 int i;
640
641 clkp = cfg->clocks;
642 best = NULL;
643
644 if (cfg->clocks_size < 2) {
645 if (cfg->clocks_size == 0)
646 clkp = &tmp_clksrc;
647
648 /* check to see if we're sourcing fclk, and if so we're
649 * going to have to update the clock source
650 */
651
652 if (strcmp(clkp->name, "fclk") == 0) {
653 struct s3c24xx_uart_clksrc src;
654
655 s3c24xx_serial_getsource(port, &src);
656
657 /* check that the port already using fclk, and if
658 * not, then re-select fclk
659 */
660
661 if (strcmp(src.name, clkp->name) == 0) {
662 s3c24xx_serial_setsource(port, clkp);
663 s3c24xx_serial_getsource(port, &src);
664 }
665
666 clkp->divisor = src.divisor;
667 }
668
669 s3c24xx_serial_calcbaud(res, port, clkp, baud);
670 best = res;
671 resptr = best + 1;
672 } else {
673 resptr = res;
674
675 for (i = 0; i < cfg->clocks_size; i++, clkp++) {
676 if (s3c24xx_serial_calcbaud(resptr, port, clkp, baud))
677 resptr++;
678 }
679 }
680
681 /* ok, we now need to select the best clock we found */
682
683 if (!best) {
684 unsigned int deviation = (1<<30)|((1<<30)-1);
685 int calc_deviation;
686
687 for (sptr = res; sptr < resptr; sptr++) {
688 calc_deviation = baud - sptr->calc;
689 if (calc_deviation < 0)
690 calc_deviation = -calc_deviation;
691
692 if (calc_deviation < deviation) {
693 best = sptr;
694 deviation = calc_deviation;
695 }
696 }
697 }
698
699 /* store results to pass back */
700
701 *clksrc = best->clksrc;
702 *clk = best->src;
703
704 return best->quot;
705}
706
707static void s3c24xx_serial_set_termios(struct uart_port *port,
708 struct ktermios *termios,
709 struct ktermios *old)
710{
711 struct s3c2410_uartcfg *cfg = s3c24xx_port_to_cfg(port);
712 struct s3c24xx_uart_port *ourport = to_ourport(port);
713 struct s3c24xx_uart_clksrc *clksrc = NULL;
714 struct clk *clk = NULL;
715 unsigned long flags;
716 unsigned int baud, quot;
717 unsigned int ulcon;
718 unsigned int umcon;
719
720 /*
721 * We don't support modem control lines.
722 */
723 termios->c_cflag &= ~(HUPCL | CMSPAR);
724 termios->c_cflag |= CLOCAL;
725
726 /*
727 * Ask the core to calculate the divisor for us.
728 */
729
730 baud = uart_get_baud_rate(port, termios, old, 0, 115200*8);
731
732 if (baud == 38400 && (port->flags & UPF_SPD_MASK) == UPF_SPD_CUST)
733 quot = port->custom_divisor;
734 else
735 quot = s3c24xx_serial_getclk(port, &clksrc, &clk, baud);
736
737 /* check to see if we need to change clock source */
738
739 if (ourport->clksrc != clksrc || ourport->baudclk != clk) {
740 s3c24xx_serial_setsource(port, clksrc);
741
742 if (ourport->baudclk != NULL && !IS_ERR(ourport->baudclk)) {
743 clk_disable(ourport->baudclk);
744 ourport->baudclk = NULL;
745 }
746
747 clk_enable(clk);
748
749 ourport->clksrc = clksrc;
750 ourport->baudclk = clk;
751 }
752
753 switch (termios->c_cflag & CSIZE) {
754 case CS5:
755 dbg("config: 5bits/char\n");
756 ulcon = S3C2410_LCON_CS5;
757 break;
758 case CS6:
759 dbg("config: 6bits/char\n");
760 ulcon = S3C2410_LCON_CS6;
761 break;
762 case CS7:
763 dbg("config: 7bits/char\n");
764 ulcon = S3C2410_LCON_CS7;
765 break;
766 case CS8:
767 default:
768 dbg("config: 8bits/char\n");
769 ulcon = S3C2410_LCON_CS8;
770 break;
771 }
772
773 /* preserve original lcon IR settings */
774 ulcon |= (cfg->ulcon & S3C2410_LCON_IRM);
775
776 if (termios->c_cflag & CSTOPB)
777 ulcon |= S3C2410_LCON_STOPB;
778
779 umcon = (termios->c_cflag & CRTSCTS) ? S3C2410_UMCOM_AFC : 0;
780
781 if (termios->c_cflag & PARENB) {
782 if (termios->c_cflag & PARODD)
783 ulcon |= S3C2410_LCON_PODD;
784 else
785 ulcon |= S3C2410_LCON_PEVEN;
786 } else {
787 ulcon |= S3C2410_LCON_PNONE;
788 }
789
790 spin_lock_irqsave(&port->lock, flags);
791
792 dbg("setting ulcon to %08x, brddiv to %d\n", ulcon, quot);
793
794 wr_regl(port, S3C2410_ULCON, ulcon);
795 wr_regl(port, S3C2410_UBRDIV, quot);
796 wr_regl(port, S3C2410_UMCON, umcon);
797
798 dbg("uart: ulcon = 0x%08x, ucon = 0x%08x, ufcon = 0x%08x\n",
799 rd_regl(port, S3C2410_ULCON),
800 rd_regl(port, S3C2410_UCON),
801 rd_regl(port, S3C2410_UFCON));
802
803 /*
804 * Update the per-port timeout.
805 */
806 uart_update_timeout(port, termios->c_cflag, baud);
807
808 /*
809 * Which character status flags are we interested in?
810 */
811 port->read_status_mask = S3C2410_UERSTAT_OVERRUN;
812 if (termios->c_iflag & INPCK)
813 port->read_status_mask |= S3C2410_UERSTAT_FRAME | S3C2410_UERSTAT_PARITY;
814
815 /*
816 * Which character status flags should we ignore?
817 */
818 port->ignore_status_mask = 0;
819 if (termios->c_iflag & IGNPAR)
820 port->ignore_status_mask |= S3C2410_UERSTAT_OVERRUN;
821 if (termios->c_iflag & IGNBRK && termios->c_iflag & IGNPAR)
822 port->ignore_status_mask |= S3C2410_UERSTAT_FRAME;
823
824 /*
825 * Ignore all characters if CREAD is not set.
826 */
827 if ((termios->c_cflag & CREAD) == 0)
828 port->ignore_status_mask |= RXSTAT_DUMMY_READ;
829
830 spin_unlock_irqrestore(&port->lock, flags);
831}
832
833static const char *s3c24xx_serial_type(struct uart_port *port)
834{
835 switch (port->type) {
836 case PORT_S3C2410:
837 return "S3C2410";
838 case PORT_S3C2440:
839 return "S3C2440";
840 case PORT_S3C2412:
841 return "S3C2412";
842 default:
843 return NULL;
844 }
845}
846
847#define MAP_SIZE (0x100)
848
849static void s3c24xx_serial_release_port(struct uart_port *port)
850{
851 release_mem_region(port->mapbase, MAP_SIZE);
852}
853
854static int s3c24xx_serial_request_port(struct uart_port *port)
855{
856 const char *name = s3c24xx_serial_portname(port);
857 return request_mem_region(port->mapbase, MAP_SIZE, name) ? 0 : -EBUSY;
858}
859
860static void s3c24xx_serial_config_port(struct uart_port *port, int flags)
861{
862 struct s3c24xx_uart_info *info = s3c24xx_port_to_info(port);
863
864 if (flags & UART_CONFIG_TYPE &&
865 s3c24xx_serial_request_port(port) == 0)
866 port->type = info->type;
867}
868
869/*
870 * verify the new serial_struct (for TIOCSSERIAL).
871 */
872static int
873s3c24xx_serial_verify_port(struct uart_port *port, struct serial_struct *ser)
874{
875 struct s3c24xx_uart_info *info = s3c24xx_port_to_info(port);
876
877 if (ser->type != PORT_UNKNOWN && ser->type != info->type)
878 return -EINVAL;
879
880 return 0;
881}
882
883
884#ifdef CONFIG_SERIAL_S3C2410_CONSOLE
885
886static struct console s3c24xx_serial_console;
887
888#define S3C24XX_SERIAL_CONSOLE &s3c24xx_serial_console
889#else
890#define S3C24XX_SERIAL_CONSOLE NULL
891#endif
892
893static struct uart_ops s3c24xx_serial_ops = {
894 .pm = s3c24xx_serial_pm,
895 .tx_empty = s3c24xx_serial_tx_empty,
896 .get_mctrl = s3c24xx_serial_get_mctrl,
897 .set_mctrl = s3c24xx_serial_set_mctrl,
898 .stop_tx = s3c24xx_serial_stop_tx,
899 .start_tx = s3c24xx_serial_start_tx,
900 .stop_rx = s3c24xx_serial_stop_rx,
901 .enable_ms = s3c24xx_serial_enable_ms,
902 .break_ctl = s3c24xx_serial_break_ctl,
903 .startup = s3c24xx_serial_startup,
904 .shutdown = s3c24xx_serial_shutdown,
905 .set_termios = s3c24xx_serial_set_termios,
906 .type = s3c24xx_serial_type,
907 .release_port = s3c24xx_serial_release_port,
908 .request_port = s3c24xx_serial_request_port,
909 .config_port = s3c24xx_serial_config_port,
910 .verify_port = s3c24xx_serial_verify_port,
911};
912
913
914static struct uart_driver s3c24xx_uart_drv = {
915 .owner = THIS_MODULE,
916 .dev_name = "s3c2410_serial",
917 .nr = 3,
918 .cons = S3C24XX_SERIAL_CONSOLE,
919 .driver_name = S3C24XX_SERIAL_NAME,
920 .major = S3C24XX_SERIAL_MAJOR,
921 .minor = S3C24XX_SERIAL_MINOR,
922};
923
924static struct s3c24xx_uart_port s3c24xx_serial_ports[NR_PORTS] = {
925 [0] = {
926 .port = {
927 .lock = __SPIN_LOCK_UNLOCKED(s3c24xx_serial_ports[0].port.lock),
928 .iotype = UPIO_MEM,
929 .irq = IRQ_S3CUART_RX0,
930 .uartclk = 0,
931 .fifosize = 16,
932 .ops = &s3c24xx_serial_ops,
933 .flags = UPF_BOOT_AUTOCONF,
934 .line = 0,
935 }
936 },
937 [1] = {
938 .port = {
939 .lock = __SPIN_LOCK_UNLOCKED(s3c24xx_serial_ports[1].port.lock),
940 .iotype = UPIO_MEM,
941 .irq = IRQ_S3CUART_RX1,
942 .uartclk = 0,
943 .fifosize = 16,
944 .ops = &s3c24xx_serial_ops,
945 .flags = UPF_BOOT_AUTOCONF,
946 .line = 1,
947 }
948 },
949#if NR_PORTS > 2
950
951 [2] = {
952 .port = {
953 .lock = __SPIN_LOCK_UNLOCKED(s3c24xx_serial_ports[2].port.lock),
954 .iotype = UPIO_MEM,
955 .irq = IRQ_S3CUART_RX2,
956 .uartclk = 0,
957 .fifosize = 16,
958 .ops = &s3c24xx_serial_ops,
959 .flags = UPF_BOOT_AUTOCONF,
960 .line = 2,
961 }
962 }
963#endif
964};
965
966/* s3c24xx_serial_resetport
967 *
968 * wrapper to call the specific reset for this port (reset the fifos
969 * and the settings)
970*/
971
972static inline int s3c24xx_serial_resetport(struct uart_port * port,
973 struct s3c2410_uartcfg *cfg)
974{
975 struct s3c24xx_uart_info *info = s3c24xx_port_to_info(port);
976
977 return (info->reset_port)(port, cfg);
978}
979
980/* s3c24xx_serial_init_port
981 *
982 * initialise a single serial port from the platform device given
983 */
984
985static int s3c24xx_serial_init_port(struct s3c24xx_uart_port *ourport,
986 struct s3c24xx_uart_info *info,
987 struct platform_device *platdev)
988{
989 struct uart_port *port = &ourport->port;
990 struct s3c2410_uartcfg *cfg;
991 struct resource *res;
992 int ret;
993
994 dbg("s3c24xx_serial_init_port: port=%p, platdev=%p\n", port, platdev);
995
996 if (platdev == NULL)
997 return -ENODEV;
998
999 cfg = s3c24xx_dev_to_cfg(&platdev->dev);
1000
1001 if (port->mapbase != 0)
1002 return 0;
1003
1004 if (cfg->hwport > 3)
1005 return -EINVAL;
1006
1007 /* setup info for port */
1008 port->dev = &platdev->dev;
1009 ourport->info = info;
1010
1011 /* copy the info in from provided structure */
1012 ourport->port.fifosize = info->fifosize;
1013
1014 dbg("s3c24xx_serial_init_port: %p (hw %d)...\n", port, cfg->hwport);
1015
1016 port->uartclk = 1;
1017
1018 if (cfg->uart_flags & UPF_CONS_FLOW) {
1019 dbg("s3c24xx_serial_init_port: enabling flow control\n");
1020 port->flags |= UPF_CONS_FLOW;
1021 }
1022
1023 /* sort our the physical and virtual addresses for each UART */
1024
1025 res = platform_get_resource(platdev, IORESOURCE_MEM, 0);
1026 if (res == NULL) {
1027 printk(KERN_ERR "failed to find memory resource for uart\n");
1028 return -EINVAL;
1029 }
1030
1031 dbg("resource %p (%lx..%lx)\n", res, res->start, res->end);
1032
1033 port->mapbase = res->start;
1034 port->membase = S3C24XX_VA_UART + (res->start - S3C24XX_PA_UART);
1035 ret = platform_get_irq(platdev, 0);
1036 if (ret < 0)
1037 port->irq = 0;
1038 else
1039 port->irq = ret;
1040
1041 ourport->clk = clk_get(&platdev->dev, "uart");
1042
1043 dbg("port: map=%08x, mem=%08x, irq=%d, clock=%ld\n",
1044 port->mapbase, port->membase, port->irq, port->uartclk);
1045
1046 /* reset the fifos (and setup the uart) */
1047 s3c24xx_serial_resetport(port, cfg);
1048 return 0;
1049}
1050
1051static ssize_t s3c24xx_serial_show_clksrc(struct device *dev,
1052 struct device_attribute *attr,
1053 char *buf)
1054{
1055 struct uart_port *port = s3c24xx_dev_to_port(dev);
1056 struct s3c24xx_uart_port *ourport = to_ourport(port);
1057
1058 return snprintf(buf, PAGE_SIZE, "* %s\n", ourport->clksrc->name);
1059}
1060
1061static DEVICE_ATTR(clock_source, S_IRUGO, s3c24xx_serial_show_clksrc, NULL);
1062
1063/* Device driver serial port probe */
1064
1065static int probe_index = 0;
1066
1067static int s3c24xx_serial_probe(struct platform_device *dev,
1068 struct s3c24xx_uart_info *info)
1069{
1070 struct s3c24xx_uart_port *ourport;
1071 int ret;
1072
1073 dbg("s3c24xx_serial_probe(%p, %p) %d\n", dev, info, probe_index);
1074
1075 ourport = &s3c24xx_serial_ports[probe_index];
1076 probe_index++;
1077
1078 dbg("%s: initialising port %p...\n", __func__, ourport);
1079
1080 ret = s3c24xx_serial_init_port(ourport, info, dev);
1081 if (ret < 0)
1082 goto probe_err;
1083
1084 dbg("%s: adding port\n", __func__);
1085 uart_add_one_port(&s3c24xx_uart_drv, &ourport->port);
1086 platform_set_drvdata(dev, &ourport->port);
1087
1088 ret = device_create_file(&dev->dev, &dev_attr_clock_source);
1089 if (ret < 0) {
1090 printk(KERN_ERR "%s: failed to add clksrc attr.\n", __func__);
1091 }
1092
1093 return 0;
1094
1095 probe_err:
1096 return ret;
1097}
1098
1099static int s3c24xx_serial_remove(struct platform_device *dev)
1100{
1101 struct uart_port *port = s3c24xx_dev_to_port(&dev->dev);
1102
1103 if (port) {
1104 device_remove_file(&dev->dev, &dev_attr_clock_source);
1105 uart_remove_one_port(&s3c24xx_uart_drv, port);
1106 }
1107
1108 return 0;
1109}
1110
1111/* UART power management code */
1112
1113#ifdef CONFIG_PM
1114
1115static int s3c24xx_serial_suspend(struct platform_device *dev, pm_message_t state)
1116{
1117 struct uart_port *port = s3c24xx_dev_to_port(&dev->dev);
1118
1119 if (port)
1120 uart_suspend_port(&s3c24xx_uart_drv, port);
1121
1122 return 0;
1123}
1124
1125static int s3c24xx_serial_resume(struct platform_device *dev)
1126{
1127 struct uart_port *port = s3c24xx_dev_to_port(&dev->dev);
1128 struct s3c24xx_uart_port *ourport = to_ourport(port);
1129
1130 if (port) {
1131 clk_enable(ourport->clk);
1132 s3c24xx_serial_resetport(port, s3c24xx_port_to_cfg(port));
1133 clk_disable(ourport->clk);
1134
1135 uart_resume_port(&s3c24xx_uart_drv, port);
1136 }
1137
1138 return 0;
1139}
1140
1141#else
1142#define s3c24xx_serial_suspend NULL
1143#define s3c24xx_serial_resume NULL
1144#endif
1145
1146static int s3c24xx_serial_init(struct platform_driver *drv,
1147 struct s3c24xx_uart_info *info)
1148{
1149 dbg("s3c24xx_serial_init(%p,%p)\n", drv, info);
1150 return platform_driver_register(drv);
1151}
1152
1153
1154/* now comes the code to initialise either the s3c2410 or s3c2440 serial
1155 * port information
1156*/
1157
1158/* cpu specific variations on the serial port support */
1159
1160#ifdef CONFIG_CPU_S3C2400
1161
1162static int s3c2400_serial_getsource(struct uart_port *port,
1163 struct s3c24xx_uart_clksrc *clk)
1164{
1165 clk->divisor = 1;
1166 clk->name = "pclk";
1167
1168 return 0;
1169}
1170
1171static int s3c2400_serial_setsource(struct uart_port *port,
1172 struct s3c24xx_uart_clksrc *clk)
1173{
1174 return 0;
1175}
1176
1177static int s3c2400_serial_resetport(struct uart_port *port,
1178 struct s3c2410_uartcfg *cfg)
1179{
1180 dbg("s3c2400_serial_resetport: port=%p (%08lx), cfg=%p\n",
1181 port, port->mapbase, cfg);
1182
1183 wr_regl(port, S3C2410_UCON, cfg->ucon);
1184 wr_regl(port, S3C2410_ULCON, cfg->ulcon);
1185
1186 /* reset both fifos */
1187
1188 wr_regl(port, S3C2410_UFCON, cfg->ufcon | S3C2410_UFCON_RESETBOTH);
1189 wr_regl(port, S3C2410_UFCON, cfg->ufcon);
1190
1191 return 0;
1192}
1193
1194static struct s3c24xx_uart_info s3c2400_uart_inf = {
1195 .name = "Samsung S3C2400 UART",
1196 .type = PORT_S3C2400,
1197 .fifosize = 16,
1198 .rx_fifomask = S3C2410_UFSTAT_RXMASK,
1199 .rx_fifoshift = S3C2410_UFSTAT_RXSHIFT,
1200 .rx_fifofull = S3C2410_UFSTAT_RXFULL,
1201 .tx_fifofull = S3C2410_UFSTAT_TXFULL,
1202 .tx_fifomask = S3C2410_UFSTAT_TXMASK,
1203 .tx_fifoshift = S3C2410_UFSTAT_TXSHIFT,
1204 .get_clksrc = s3c2400_serial_getsource,
1205 .set_clksrc = s3c2400_serial_setsource,
1206 .reset_port = s3c2400_serial_resetport,
1207};
1208
1209static int s3c2400_serial_probe(struct platform_device *dev)
1210{
1211 return s3c24xx_serial_probe(dev, &s3c2400_uart_inf);
1212}
1213
1214static struct platform_driver s3c2400_serial_drv = {
1215 .probe = s3c2400_serial_probe,
1216 .remove = s3c24xx_serial_remove,
1217 .suspend = s3c24xx_serial_suspend,
1218 .resume = s3c24xx_serial_resume,
1219 .driver = {
1220 .name = "s3c2400-uart",
1221 .owner = THIS_MODULE,
1222 },
1223};
1224
1225static inline int s3c2400_serial_init(void)
1226{
1227 return s3c24xx_serial_init(&s3c2400_serial_drv, &s3c2400_uart_inf);
1228}
1229
1230static inline void s3c2400_serial_exit(void)
1231{
1232 platform_driver_unregister(&s3c2400_serial_drv);
1233}
1234
1235#define s3c2400_uart_inf_at &s3c2400_uart_inf
1236#else
1237
1238static inline int s3c2400_serial_init(void)
1239{
1240 return 0;
1241}
1242
1243static inline void s3c2400_serial_exit(void)
1244{
1245}
1246
1247#define s3c2400_uart_inf_at NULL
1248
1249#endif /* CONFIG_CPU_S3C2400 */
1250
1251/* S3C2410 support */
1252
1253#ifdef CONFIG_CPU_S3C2410
1254 28
1255static int s3c2410_serial_setsource(struct uart_port *port, 29static int s3c2410_serial_setsource(struct uart_port *port,
1256 struct s3c24xx_uart_clksrc *clk) 30 struct s3c24xx_uart_clksrc *clk)
@@ -1309,8 +83,6 @@ static struct s3c24xx_uart_info s3c2410_uart_inf = {
1309 .reset_port = s3c2410_serial_resetport, 83 .reset_port = s3c2410_serial_resetport,
1310}; 84};
1311 85
1312/* device management */
1313
1314static int s3c2410_serial_probe(struct platform_device *dev) 86static int s3c2410_serial_probe(struct platform_device *dev)
1315{ 87{
1316 return s3c24xx_serial_probe(dev, &s3c2410_uart_inf); 88 return s3c24xx_serial_probe(dev, &s3c2410_uart_inf);
@@ -1319,612 +91,28 @@ static int s3c2410_serial_probe(struct platform_device *dev)
1319static struct platform_driver s3c2410_serial_drv = { 91static struct platform_driver s3c2410_serial_drv = {
1320 .probe = s3c2410_serial_probe, 92 .probe = s3c2410_serial_probe,
1321 .remove = s3c24xx_serial_remove, 93 .remove = s3c24xx_serial_remove,
1322 .suspend = s3c24xx_serial_suspend,
1323 .resume = s3c24xx_serial_resume,
1324 .driver = { 94 .driver = {
1325 .name = "s3c2410-uart", 95 .name = "s3c2410-uart",
1326 .owner = THIS_MODULE, 96 .owner = THIS_MODULE,
1327 }, 97 },
1328}; 98};
1329 99
1330static inline int s3c2410_serial_init(void) 100s3c24xx_console_init(&s3c2410_serial_drv, &s3c2410_uart_inf);
101
102static int __init s3c2410_serial_init(void)
1331{ 103{
1332 return s3c24xx_serial_init(&s3c2410_serial_drv, &s3c2410_uart_inf); 104 return s3c24xx_serial_init(&s3c2410_serial_drv, &s3c2410_uart_inf);
1333} 105}
1334 106
1335static inline void s3c2410_serial_exit(void) 107static void __exit s3c2410_serial_exit(void)
1336{ 108{
1337 platform_driver_unregister(&s3c2410_serial_drv); 109 platform_driver_unregister(&s3c2410_serial_drv);
1338} 110}
1339 111
1340#define s3c2410_uart_inf_at &s3c2410_uart_inf 112module_init(s3c2410_serial_init);
1341#else 113module_exit(s3c2410_serial_exit);
1342
1343static inline int s3c2410_serial_init(void)
1344{
1345 return 0;
1346}
1347
1348static inline void s3c2410_serial_exit(void)
1349{
1350}
1351
1352#define s3c2410_uart_inf_at NULL
1353
1354#endif /* CONFIG_CPU_S3C2410 */
1355
1356#if defined(CONFIG_CPU_S3C2440) || defined(CONFIG_CPU_S3C2442)
1357
1358static int s3c2440_serial_setsource(struct uart_port *port,
1359 struct s3c24xx_uart_clksrc *clk)
1360{
1361 unsigned long ucon = rd_regl(port, S3C2410_UCON);
1362
1363 // todo - proper fclk<>nonfclk switch //
1364
1365 ucon &= ~S3C2440_UCON_CLKMASK;
1366
1367 if (strcmp(clk->name, "uclk") == 0)
1368 ucon |= S3C2440_UCON_UCLK;
1369 else if (strcmp(clk->name, "pclk") == 0)
1370 ucon |= S3C2440_UCON_PCLK;
1371 else if (strcmp(clk->name, "fclk") == 0)
1372 ucon |= S3C2440_UCON_FCLK;
1373 else {
1374 printk(KERN_ERR "unknown clock source %s\n", clk->name);
1375 return -EINVAL;
1376 }
1377
1378 wr_regl(port, S3C2410_UCON, ucon);
1379 return 0;
1380}
1381
1382
1383static int s3c2440_serial_getsource(struct uart_port *port,
1384 struct s3c24xx_uart_clksrc *clk)
1385{
1386 unsigned long ucon = rd_regl(port, S3C2410_UCON);
1387 unsigned long ucon0, ucon1, ucon2;
1388
1389 switch (ucon & S3C2440_UCON_CLKMASK) {
1390 case S3C2440_UCON_UCLK:
1391 clk->divisor = 1;
1392 clk->name = "uclk";
1393 break;
1394
1395 case S3C2440_UCON_PCLK:
1396 case S3C2440_UCON_PCLK2:
1397 clk->divisor = 1;
1398 clk->name = "pclk";
1399 break;
1400
1401 case S3C2440_UCON_FCLK:
1402 /* the fun of calculating the uart divisors on
1403 * the s3c2440 */
1404
1405 ucon0 = __raw_readl(S3C24XX_VA_UART0 + S3C2410_UCON);
1406 ucon1 = __raw_readl(S3C24XX_VA_UART1 + S3C2410_UCON);
1407 ucon2 = __raw_readl(S3C24XX_VA_UART2 + S3C2410_UCON);
1408
1409 printk("ucons: %08lx, %08lx, %08lx\n", ucon0, ucon1, ucon2);
1410
1411 ucon0 &= S3C2440_UCON0_DIVMASK;
1412 ucon1 &= S3C2440_UCON1_DIVMASK;
1413 ucon2 &= S3C2440_UCON2_DIVMASK;
1414
1415 if (ucon0 != 0) {
1416 clk->divisor = ucon0 >> S3C2440_UCON_DIVSHIFT;
1417 clk->divisor += 6;
1418 } else if (ucon1 != 0) {
1419 clk->divisor = ucon1 >> S3C2440_UCON_DIVSHIFT;
1420 clk->divisor += 21;
1421 } else if (ucon2 != 0) {
1422 clk->divisor = ucon2 >> S3C2440_UCON_DIVSHIFT;
1423 clk->divisor += 36;
1424 } else {
1425 /* manual calims 44, seems to be 9 */
1426 clk->divisor = 9;
1427 }
1428
1429 clk->name = "fclk";
1430 break;
1431 }
1432
1433 return 0;
1434}
1435
1436static int s3c2440_serial_resetport(struct uart_port *port,
1437 struct s3c2410_uartcfg *cfg)
1438{
1439 unsigned long ucon = rd_regl(port, S3C2410_UCON);
1440
1441 dbg("s3c2440_serial_resetport: port=%p (%08lx), cfg=%p\n",
1442 port, port->mapbase, cfg);
1443
1444 /* ensure we don't change the clock settings... */
1445
1446 ucon &= (S3C2440_UCON0_DIVMASK | (3<<10));
1447
1448 wr_regl(port, S3C2410_UCON, ucon | cfg->ucon);
1449 wr_regl(port, S3C2410_ULCON, cfg->ulcon);
1450
1451 /* reset both fifos */
1452
1453 wr_regl(port, S3C2410_UFCON, cfg->ufcon | S3C2410_UFCON_RESETBOTH);
1454 wr_regl(port, S3C2410_UFCON, cfg->ufcon);
1455
1456 return 0;
1457}
1458
1459static struct s3c24xx_uart_info s3c2440_uart_inf = {
1460 .name = "Samsung S3C2440 UART",
1461 .type = PORT_S3C2440,
1462 .fifosize = 64,
1463 .rx_fifomask = S3C2440_UFSTAT_RXMASK,
1464 .rx_fifoshift = S3C2440_UFSTAT_RXSHIFT,
1465 .rx_fifofull = S3C2440_UFSTAT_RXFULL,
1466 .tx_fifofull = S3C2440_UFSTAT_TXFULL,
1467 .tx_fifomask = S3C2440_UFSTAT_TXMASK,
1468 .tx_fifoshift = S3C2440_UFSTAT_TXSHIFT,
1469 .get_clksrc = s3c2440_serial_getsource,
1470 .set_clksrc = s3c2440_serial_setsource,
1471 .reset_port = s3c2440_serial_resetport,
1472};
1473
1474/* device management */
1475
1476static int s3c2440_serial_probe(struct platform_device *dev)
1477{
1478 dbg("s3c2440_serial_probe: dev=%p\n", dev);
1479 return s3c24xx_serial_probe(dev, &s3c2440_uart_inf);
1480}
1481
1482static struct platform_driver s3c2440_serial_drv = {
1483 .probe = s3c2440_serial_probe,
1484 .remove = s3c24xx_serial_remove,
1485 .suspend = s3c24xx_serial_suspend,
1486 .resume = s3c24xx_serial_resume,
1487 .driver = {
1488 .name = "s3c2440-uart",
1489 .owner = THIS_MODULE,
1490 },
1491};
1492
1493
1494static inline int s3c2440_serial_init(void)
1495{
1496 return s3c24xx_serial_init(&s3c2440_serial_drv, &s3c2440_uart_inf);
1497}
1498
1499static inline void s3c2440_serial_exit(void)
1500{
1501 platform_driver_unregister(&s3c2440_serial_drv);
1502}
1503
1504#define s3c2440_uart_inf_at &s3c2440_uart_inf
1505#else
1506
1507static inline int s3c2440_serial_init(void)
1508{
1509 return 0;
1510}
1511
1512static inline void s3c2440_serial_exit(void)
1513{
1514}
1515
1516#define s3c2440_uart_inf_at NULL
1517#endif /* CONFIG_CPU_S3C2440 */
1518
1519#if defined(CONFIG_CPU_S3C2412)
1520
1521static int s3c2412_serial_setsource(struct uart_port *port,
1522 struct s3c24xx_uart_clksrc *clk)
1523{
1524 unsigned long ucon = rd_regl(port, S3C2410_UCON);
1525
1526 ucon &= ~S3C2412_UCON_CLKMASK;
1527
1528 if (strcmp(clk->name, "uclk") == 0)
1529 ucon |= S3C2440_UCON_UCLK;
1530 else if (strcmp(clk->name, "pclk") == 0)
1531 ucon |= S3C2440_UCON_PCLK;
1532 else if (strcmp(clk->name, "usysclk") == 0)
1533 ucon |= S3C2412_UCON_USYSCLK;
1534 else {
1535 printk(KERN_ERR "unknown clock source %s\n", clk->name);
1536 return -EINVAL;
1537 }
1538
1539 wr_regl(port, S3C2410_UCON, ucon);
1540 return 0;
1541}
1542
1543
1544static int s3c2412_serial_getsource(struct uart_port *port,
1545 struct s3c24xx_uart_clksrc *clk)
1546{
1547 unsigned long ucon = rd_regl(port, S3C2410_UCON);
1548
1549 switch (ucon & S3C2412_UCON_CLKMASK) {
1550 case S3C2412_UCON_UCLK:
1551 clk->divisor = 1;
1552 clk->name = "uclk";
1553 break;
1554
1555 case S3C2412_UCON_PCLK:
1556 case S3C2412_UCON_PCLK2:
1557 clk->divisor = 1;
1558 clk->name = "pclk";
1559 break;
1560
1561 case S3C2412_UCON_USYSCLK:
1562 clk->divisor = 1;
1563 clk->name = "usysclk";
1564 break;
1565 }
1566
1567 return 0;
1568}
1569
1570static int s3c2412_serial_resetport(struct uart_port *port,
1571 struct s3c2410_uartcfg *cfg)
1572{
1573 unsigned long ucon = rd_regl(port, S3C2410_UCON);
1574
1575 dbg("%s: port=%p (%08lx), cfg=%p\n",
1576 __func__, port, port->mapbase, cfg);
1577
1578 /* ensure we don't change the clock settings... */
1579
1580 ucon &= S3C2412_UCON_CLKMASK;
1581
1582 wr_regl(port, S3C2410_UCON, ucon | cfg->ucon);
1583 wr_regl(port, S3C2410_ULCON, cfg->ulcon);
1584
1585 /* reset both fifos */
1586
1587 wr_regl(port, S3C2410_UFCON, cfg->ufcon | S3C2410_UFCON_RESETBOTH);
1588 wr_regl(port, S3C2410_UFCON, cfg->ufcon);
1589
1590 return 0;
1591}
1592
1593static struct s3c24xx_uart_info s3c2412_uart_inf = {
1594 .name = "Samsung S3C2412 UART",
1595 .type = PORT_S3C2412,
1596 .fifosize = 64,
1597 .rx_fifomask = S3C2440_UFSTAT_RXMASK,
1598 .rx_fifoshift = S3C2440_UFSTAT_RXSHIFT,
1599 .rx_fifofull = S3C2440_UFSTAT_RXFULL,
1600 .tx_fifofull = S3C2440_UFSTAT_TXFULL,
1601 .tx_fifomask = S3C2440_UFSTAT_TXMASK,
1602 .tx_fifoshift = S3C2440_UFSTAT_TXSHIFT,
1603 .get_clksrc = s3c2412_serial_getsource,
1604 .set_clksrc = s3c2412_serial_setsource,
1605 .reset_port = s3c2412_serial_resetport,
1606};
1607
1608/* device management */
1609
1610static int s3c2412_serial_probe(struct platform_device *dev)
1611{
1612 dbg("s3c2440_serial_probe: dev=%p\n", dev);
1613 return s3c24xx_serial_probe(dev, &s3c2412_uart_inf);
1614}
1615
1616static struct platform_driver s3c2412_serial_drv = {
1617 .probe = s3c2412_serial_probe,
1618 .remove = s3c24xx_serial_remove,
1619 .suspend = s3c24xx_serial_suspend,
1620 .resume = s3c24xx_serial_resume,
1621 .driver = {
1622 .name = "s3c2412-uart",
1623 .owner = THIS_MODULE,
1624 },
1625};
1626
1627
1628static inline int s3c2412_serial_init(void)
1629{
1630 return s3c24xx_serial_init(&s3c2412_serial_drv, &s3c2412_uart_inf);
1631}
1632
1633static inline void s3c2412_serial_exit(void)
1634{
1635 platform_driver_unregister(&s3c2412_serial_drv);
1636}
1637
1638#define s3c2412_uart_inf_at &s3c2412_uart_inf
1639#else
1640
1641static inline int s3c2412_serial_init(void)
1642{
1643 return 0;
1644}
1645
1646static inline void s3c2412_serial_exit(void)
1647{
1648}
1649
1650#define s3c2412_uart_inf_at NULL
1651#endif /* CONFIG_CPU_S3C2440 */
1652
1653
1654/* module initialisation code */
1655
1656static int __init s3c24xx_serial_modinit(void)
1657{
1658 int ret;
1659
1660 ret = uart_register_driver(&s3c24xx_uart_drv);
1661 if (ret < 0) {
1662 printk(KERN_ERR "failed to register UART driver\n");
1663 return -1;
1664 }
1665
1666 s3c2400_serial_init();
1667 s3c2410_serial_init();
1668 s3c2412_serial_init();
1669 s3c2440_serial_init();
1670
1671 return 0;
1672}
1673
1674static void __exit s3c24xx_serial_modexit(void)
1675{
1676 s3c2400_serial_exit();
1677 s3c2410_serial_exit();
1678 s3c2412_serial_exit();
1679 s3c2440_serial_exit();
1680
1681 uart_unregister_driver(&s3c24xx_uart_drv);
1682}
1683
1684
1685module_init(s3c24xx_serial_modinit);
1686module_exit(s3c24xx_serial_modexit);
1687
1688/* Console code */
1689
1690#ifdef CONFIG_SERIAL_S3C2410_CONSOLE
1691
1692static struct uart_port *cons_uart;
1693
1694static int
1695s3c24xx_serial_console_txrdy(struct uart_port *port, unsigned int ufcon)
1696{
1697 struct s3c24xx_uart_info *info = s3c24xx_port_to_info(port);
1698 unsigned long ufstat, utrstat;
1699
1700 if (ufcon & S3C2410_UFCON_FIFOMODE) {
1701 /* fifo mode - check ammount of data in fifo registers... */
1702
1703 ufstat = rd_regl(port, S3C2410_UFSTAT);
1704 return (ufstat & info->tx_fifofull) ? 0 : 1;
1705 }
1706
1707 /* in non-fifo mode, we go and use the tx buffer empty */
1708
1709 utrstat = rd_regl(port, S3C2410_UTRSTAT);
1710 return (utrstat & S3C2410_UTRSTAT_TXE) ? 1 : 0;
1711}
1712
1713static void
1714s3c24xx_serial_console_putchar(struct uart_port *port, int ch)
1715{
1716 unsigned int ufcon = rd_regl(cons_uart, S3C2410_UFCON);
1717 while (!s3c24xx_serial_console_txrdy(port, ufcon))
1718 barrier();
1719 wr_regb(cons_uart, S3C2410_UTXH, ch);
1720}
1721
1722static void
1723s3c24xx_serial_console_write(struct console *co, const char *s,
1724 unsigned int count)
1725{
1726 uart_console_write(cons_uart, s, count, s3c24xx_serial_console_putchar);
1727}
1728
1729static void __init
1730s3c24xx_serial_get_options(struct uart_port *port, int *baud,
1731 int *parity, int *bits)
1732{
1733 struct s3c24xx_uart_clksrc clksrc;
1734 struct clk *clk;
1735 unsigned int ulcon;
1736 unsigned int ucon;
1737 unsigned int ubrdiv;
1738 unsigned long rate;
1739
1740 ulcon = rd_regl(port, S3C2410_ULCON);
1741 ucon = rd_regl(port, S3C2410_UCON);
1742 ubrdiv = rd_regl(port, S3C2410_UBRDIV);
1743
1744 dbg("s3c24xx_serial_get_options: port=%p\n"
1745 "registers: ulcon=%08x, ucon=%08x, ubdriv=%08x\n",
1746 port, ulcon, ucon, ubrdiv);
1747
1748 if ((ucon & 0xf) != 0) {
1749 /* consider the serial port configured if the tx/rx mode set */
1750
1751 switch (ulcon & S3C2410_LCON_CSMASK) {
1752 case S3C2410_LCON_CS5:
1753 *bits = 5;
1754 break;
1755 case S3C2410_LCON_CS6:
1756 *bits = 6;
1757 break;
1758 case S3C2410_LCON_CS7:
1759 *bits = 7;
1760 break;
1761 default:
1762 case S3C2410_LCON_CS8:
1763 *bits = 8;
1764 break;
1765 }
1766
1767 switch (ulcon & S3C2410_LCON_PMASK) {
1768 case S3C2410_LCON_PEVEN:
1769 *parity = 'e';
1770 break;
1771
1772 case S3C2410_LCON_PODD:
1773 *parity = 'o';
1774 break;
1775
1776 case S3C2410_LCON_PNONE:
1777 default:
1778 *parity = 'n';
1779 }
1780
1781 /* now calculate the baud rate */
1782
1783 s3c24xx_serial_getsource(port, &clksrc);
1784
1785 clk = clk_get(port->dev, clksrc.name);
1786 if (!IS_ERR(clk) && clk != NULL)
1787 rate = clk_get_rate(clk) / clksrc.divisor;
1788 else
1789 rate = 1;
1790
1791
1792 *baud = rate / ( 16 * (ubrdiv + 1));
1793 dbg("calculated baud %d\n", *baud);
1794 }
1795
1796}
1797
1798/* s3c24xx_serial_init_ports
1799 *
1800 * initialise the serial ports from the machine provided initialisation
1801 * data.
1802*/
1803
1804static int s3c24xx_serial_init_ports(struct s3c24xx_uart_info *info)
1805{
1806 struct s3c24xx_uart_port *ptr = s3c24xx_serial_ports;
1807 struct platform_device **platdev_ptr;
1808 int i;
1809
1810 dbg("s3c24xx_serial_init_ports: initialising ports...\n");
1811
1812 platdev_ptr = s3c24xx_uart_devs;
1813
1814 for (i = 0; i < NR_PORTS; i++, ptr++, platdev_ptr++) {
1815 s3c24xx_serial_init_port(ptr, info, *platdev_ptr);
1816 }
1817
1818 return 0;
1819}
1820
1821static int __init
1822s3c24xx_serial_console_setup(struct console *co, char *options)
1823{
1824 struct uart_port *port;
1825 int baud = 9600;
1826 int bits = 8;
1827 int parity = 'n';
1828 int flow = 'n';
1829
1830 dbg("s3c24xx_serial_console_setup: co=%p (%d), %s\n",
1831 co, co->index, options);
1832
1833 /* is this a valid port */
1834
1835 if (co->index == -1 || co->index >= NR_PORTS)
1836 co->index = 0;
1837
1838 port = &s3c24xx_serial_ports[co->index].port;
1839
1840 /* is the port configured? */
1841
1842 if (port->mapbase == 0x0) {
1843 co->index = 0;
1844 port = &s3c24xx_serial_ports[co->index].port;
1845 }
1846
1847 cons_uart = port;
1848
1849 dbg("s3c24xx_serial_console_setup: port=%p (%d)\n", port, co->index);
1850
1851 /*
1852 * Check whether an invalid uart number has been specified, and
1853 * if so, search for the first available port that does have
1854 * console support.
1855 */
1856 if (options)
1857 uart_parse_options(options, &baud, &parity, &bits, &flow);
1858 else
1859 s3c24xx_serial_get_options(port, &baud, &parity, &bits);
1860
1861 dbg("s3c24xx_serial_console_setup: baud %d\n", baud);
1862
1863 return uart_set_options(port, co, baud, parity, bits, flow);
1864}
1865
1866/* s3c24xx_serial_initconsole
1867 *
1868 * initialise the console from one of the uart drivers
1869*/
1870
1871static struct console s3c24xx_serial_console =
1872{
1873 .name = S3C24XX_SERIAL_NAME,
1874 .device = uart_console_device,
1875 .flags = CON_PRINTBUFFER,
1876 .index = -1,
1877 .write = s3c24xx_serial_console_write,
1878 .setup = s3c24xx_serial_console_setup
1879};
1880
1881static int s3c24xx_serial_initconsole(void)
1882{
1883 struct s3c24xx_uart_info *info;
1884 struct platform_device *dev = s3c24xx_uart_devs[0];
1885
1886 dbg("s3c24xx_serial_initconsole\n");
1887
1888 /* select driver based on the cpu */
1889
1890 if (dev == NULL) {
1891 printk(KERN_ERR "s3c24xx: no devices for console init\n");
1892 return 0;
1893 }
1894
1895 if (strcmp(dev->name, "s3c2400-uart") == 0) {
1896 info = s3c2400_uart_inf_at;
1897 } else if (strcmp(dev->name, "s3c2410-uart") == 0) {
1898 info = s3c2410_uart_inf_at;
1899 } else if (strcmp(dev->name, "s3c2440-uart") == 0) {
1900 info = s3c2440_uart_inf_at;
1901 } else if (strcmp(dev->name, "s3c2412-uart") == 0) {
1902 info = s3c2412_uart_inf_at;
1903 } else {
1904 printk(KERN_ERR "s3c24xx: no driver for %s\n", dev->name);
1905 return 0;
1906 }
1907
1908 if (info == NULL) {
1909 printk(KERN_ERR "s3c24xx: no driver for console\n");
1910 return 0;
1911 }
1912
1913 s3c24xx_serial_console.data = &s3c24xx_uart_drv;
1914 s3c24xx_serial_init_ports(info);
1915
1916 register_console(&s3c24xx_serial_console);
1917 return 0;
1918}
1919
1920console_initcall(s3c24xx_serial_initconsole);
1921
1922#endif /* CONFIG_SERIAL_S3C2410_CONSOLE */
1923 114
1924MODULE_LICENSE("GPL v2"); 115MODULE_LICENSE("GPL v2");
1925MODULE_AUTHOR("Ben Dooks <ben@simtec.co.uk>"); 116MODULE_AUTHOR("Ben Dooks <ben@simtec.co.uk>");
1926MODULE_DESCRIPTION("Samsung S3C2410/S3C2440/S3C2412 Serial port driver"); 117MODULE_DESCRIPTION("Samsung S3C2410 SoC Serial port driver");
1927MODULE_ALIAS("platform:s3c2400-uart");
1928MODULE_ALIAS("platform:s3c2410-uart"); 118MODULE_ALIAS("platform:s3c2410-uart");
1929MODULE_ALIAS("platform:s3c2412-uart");
1930MODULE_ALIAS("platform:s3c2440-uart");