aboutsummaryrefslogtreecommitdiffstats
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
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>
-rw-r--r--arch/arm/configs/s3c2410_defconfig15
-rw-r--r--drivers/serial/Kconfig54
-rw-r--r--drivers/serial/Makefile4
-rw-r--r--drivers/serial/s3c2400.c106
-rw-r--r--drivers/serial/s3c2410.c1834
-rw-r--r--drivers/serial/s3c2412.c151
-rw-r--r--drivers/serial/s3c2440.c181
-rw-r--r--drivers/serial/samsung.c1317
-rw-r--r--drivers/serial/samsung.h102
9 files changed, 1929 insertions, 1835 deletions
diff --git a/arch/arm/configs/s3c2410_defconfig b/arch/arm/configs/s3c2410_defconfig
index 53d599cf86b..1a0434b0079 100644
--- a/arch/arm/configs/s3c2410_defconfig
+++ b/arch/arm/configs/s3c2410_defconfig
@@ -1,7 +1,7 @@
1# 1#
2# Automatically generated make config: don't edit 2# Automatically generated make config: don't edit
3# Linux kernel version: 2.6.26-rc8 3# Linux kernel version: 2.6.26-rc8
4# Wed Jun 25 13:39:12 2008 4# Thu Jul 3 11:48:41 2008
5# 5#
6CONFIG_ARM=y 6CONFIG_ARM=y
7CONFIG_SYS_SUPPORTS_APM_EMULATION=y 7CONFIG_SYS_SUPPORTS_APM_EMULATION=y
@@ -12,6 +12,7 @@ CONFIG_MMU=y
12CONFIG_NO_IOPORT=y 12CONFIG_NO_IOPORT=y
13CONFIG_GENERIC_HARDIRQS=y 13CONFIG_GENERIC_HARDIRQS=y
14CONFIG_STACKTRACE_SUPPORT=y 14CONFIG_STACKTRACE_SUPPORT=y
15CONFIG_HAVE_LATENCYTOP_SUPPORT=y
15CONFIG_LOCKDEP_SUPPORT=y 16CONFIG_LOCKDEP_SUPPORT=y
16CONFIG_TRACE_IRQFLAGS_SUPPORT=y 17CONFIG_TRACE_IRQFLAGS_SUPPORT=y
17CONFIG_HARDIRQS_SW_RESEND=y 18CONFIG_HARDIRQS_SW_RESEND=y
@@ -23,6 +24,7 @@ CONFIG_GENERIC_HWEIGHT=y
23CONFIG_GENERIC_CALIBRATE_DELAY=y 24CONFIG_GENERIC_CALIBRATE_DELAY=y
24CONFIG_ARCH_SUPPORTS_AOUT=y 25CONFIG_ARCH_SUPPORTS_AOUT=y
25CONFIG_ZONE_DMA=y 26CONFIG_ZONE_DMA=y
27CONFIG_GENERIC_HARDIRQS_NO__DO_IRQ=y
26CONFIG_VECTORS_BASE=0xffff0000 28CONFIG_VECTORS_BASE=0xffff0000
27CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config" 29CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config"
28 30
@@ -131,7 +133,6 @@ CONFIG_CLASSIC_RCU=y
131# CONFIG_ARCH_AT91 is not set 133# CONFIG_ARCH_AT91 is not set
132# CONFIG_ARCH_CLPS7500 is not set 134# CONFIG_ARCH_CLPS7500 is not set
133# CONFIG_ARCH_CLPS711X is not set 135# CONFIG_ARCH_CLPS711X is not set
134# CONFIG_ARCH_CO285 is not set
135# CONFIG_ARCH_EBSA110 is not set 136# CONFIG_ARCH_EBSA110 is not set
136# CONFIG_ARCH_EP93XX is not set 137# CONFIG_ARCH_EP93XX is not set
137# CONFIG_ARCH_FOOTBRIDGE is not set 138# CONFIG_ARCH_FOOTBRIDGE is not set
@@ -145,8 +146,11 @@ CONFIG_CLASSIC_RCU=y
145# CONFIG_ARCH_IXP2000 is not set 146# CONFIG_ARCH_IXP2000 is not set
146# CONFIG_ARCH_IXP4XX is not set 147# CONFIG_ARCH_IXP4XX is not set
147# CONFIG_ARCH_L7200 is not set 148# CONFIG_ARCH_L7200 is not set
149# CONFIG_ARCH_KIRKWOOD is not set
148# CONFIG_ARCH_KS8695 is not set 150# CONFIG_ARCH_KS8695 is not set
149# CONFIG_ARCH_NS9XXX is not set 151# CONFIG_ARCH_NS9XXX is not set
152# CONFIG_ARCH_LOKI is not set
153# CONFIG_ARCH_MV78XX0 is not set
150# CONFIG_ARCH_MXC is not set 154# CONFIG_ARCH_MXC is not set
151# CONFIG_ARCH_ORION5X is not set 155# CONFIG_ARCH_ORION5X is not set
152# CONFIG_ARCH_PNX4008 is not set 156# CONFIG_ARCH_PNX4008 is not set
@@ -871,8 +875,12 @@ CONFIG_SERIAL_8250_SHARE_IRQ=y
871# 875#
872# Non-8250 serial port support 876# Non-8250 serial port support
873# 877#
878CONFIG_SERIAL_SAMSUNG=y
879# CONFIG_SERIAL_SAMSUNG_DEBUG is not set
880CONFIG_SERIAL_SAMSUNG_CONSOLE=y
874CONFIG_SERIAL_S3C2410=y 881CONFIG_SERIAL_S3C2410=y
875CONFIG_SERIAL_S3C2410_CONSOLE=y 882CONFIG_SERIAL_S3C2412=y
883CONFIG_SERIAL_S3C2440=y
876CONFIG_SERIAL_CORE=y 884CONFIG_SERIAL_CORE=y
877CONFIG_SERIAL_CORE_CONSOLE=y 885CONFIG_SERIAL_CORE_CONSOLE=y
878CONFIG_UNIX98_PTYS=y 886CONFIG_UNIX98_PTYS=y
@@ -1544,6 +1552,7 @@ CONFIG_FRAME_POINTER=y
1544# CONFIG_RCU_TORTURE_TEST is not set 1552# CONFIG_RCU_TORTURE_TEST is not set
1545# CONFIG_BACKTRACE_SELF_TEST is not set 1553# CONFIG_BACKTRACE_SELF_TEST is not set
1546# CONFIG_FAULT_INJECTION is not set 1554# CONFIG_FAULT_INJECTION is not set
1555# CONFIG_LATENCYTOP is not set
1547# CONFIG_SAMPLES is not set 1556# CONFIG_SAMPLES is not set
1548CONFIG_DEBUG_USER=y 1557CONFIG_DEBUG_USER=y
1549CONFIG_DEBUG_ERRORS=y 1558CONFIG_DEBUG_ERRORS=y
diff --git a/drivers/serial/Kconfig b/drivers/serial/Kconfig
index 9bc42763623..5a9754455ee 100644
--- a/drivers/serial/Kconfig
+++ b/drivers/serial/Kconfig
@@ -448,22 +448,27 @@ config SERIAL_CLPS711X_CONSOLE
448 your boot loader (lilo or loadlin) about how to pass options to the 448 your boot loader (lilo or loadlin) about how to pass options to the
449 kernel at boot time.) 449 kernel at boot time.)
450 450
451config SERIAL_S3C2410 451config SERIAL_SAMSUNG
452 tristate "Samsung S3C2410/S3C2440/S3C2442/S3C2412 Serial port support" 452 tristate "Samsung SoC serial support"
453 depends on ARM && ARCH_S3C2410 453 depends on ARM && PLAT_S3C24XX
454 select SERIAL_CORE
455 help 454 help
456 Support for the on-chip UARTs on the Samsung S3C24XX series CPUs, 455 Support for the on-chip UARTs on the Samsung S3C24XX series CPUs,
457 providing /dev/ttySAC0, 1 and 2 (note, some machines may not 456 providing /dev/ttySAC0, 1 and 2 (note, some machines may not
458 provide all of these ports, depending on how the serial port 457 provide all of these ports, depending on how the serial port
459 pins are configured. 458 pins are configured.
460 459
461 Currently this driver supports the UARTS on the S3C2410, S3C2440, 460config SERIAL_SAMSUNG_DEBUG
462 S3C2442, S3C2412 and S3C2413 CPUs. 461 bool "Samsung SoC serial debug"
462 depends on SERIAL_SAMSUNG
463 help
464 Add support for debugging the serial driver. Since this is
465 generally being used as a console, we use our own output
466 routines that go via the low-level debug printascii()
467 function.
463 468
464config SERIAL_S3C2410_CONSOLE 469config SERIAL_SAMSUNG_CONSOLE
465 bool "Support for console on S3C2410 serial port" 470 bool "Support for console on Samsung SoC serial port"
466 depends on SERIAL_S3C2410=y 471 depends on SERIAL_SAMSUNG=y
467 select SERIAL_CORE_CONSOLE 472 select SERIAL_CORE_CONSOLE
468 help 473 help
469 Allow selection of the S3C24XX on-board serial ports for use as 474 Allow selection of the S3C24XX on-board serial ports for use as
@@ -476,6 +481,37 @@ config SERIAL_S3C2410_CONSOLE
476 your boot loader about how to pass options to the kernel at 481 your boot loader about how to pass options to the kernel at
477 boot time.) 482 boot time.)
478 483
484config SERIAL_S3C2400
485 tristate "Samsung S3C2410 Serial port support"
486 depends on ARM && SERIAL_SAMSUNG && CPU_S3C2400
487 default y if CPU_S3C2400
488 help
489 Serial port support for the Samsung S3C2400 SoC
490
491config SERIAL_S3C2410
492 tristate "Samsung S3C2410 Serial port support"
493 depends on SERIAL_SAMSUNG && CPU_S3C2410
494 default y if CPU_S3C2410
495 help
496 Serial port support for the Samsung S3C2410 SoC
497
498config SERIAL_S3C2412
499 tristate "Samsung S3C2412/S3C2413 Serial port support"
500 depends on SERIAL_SAMSUNG && CPU_S3C2412
501 default y if CPU_S3C2412
502 help
503 Serial port support for the Samsung S3C2412 and S3C2413 SoC
504
505config SERIAL_S3C2440
506 tristate "Samsung S3C2440/S3C2442 Serial port support"
507 depends on SERIAL_SAMSUNG && (CPU_S3C2440 || CPU_S3C2442)
508 default y if CPU_S3C2440
509 default y if CPU_S3C2442
510 help
511 Serial port support for the Samsung S3C2440 and S3C2442 SoC
512
513
514
479config SERIAL_DZ 515config SERIAL_DZ
480 bool "DECstation DZ serial driver" 516 bool "DECstation DZ serial driver"
481 depends on MACH_DECSTATION && 32BIT 517 depends on MACH_DECSTATION && 32BIT
diff --git a/drivers/serial/Makefile b/drivers/serial/Makefile
index 0d9c09b1e83..7d85c1fbe7e 100644
--- a/drivers/serial/Makefile
+++ b/drivers/serial/Makefile
@@ -28,7 +28,11 @@ obj-$(CONFIG_SERIAL_PNX8XXX) += pnx8xxx_uart.o
28obj-$(CONFIG_SERIAL_SA1100) += sa1100.o 28obj-$(CONFIG_SERIAL_SA1100) += sa1100.o
29obj-$(CONFIG_SERIAL_BFIN) += bfin_5xx.o 29obj-$(CONFIG_SERIAL_BFIN) += bfin_5xx.o
30obj-$(CONFIG_SERIAL_BFIN_SPORT) += bfin_sport_uart.o 30obj-$(CONFIG_SERIAL_BFIN_SPORT) += bfin_sport_uart.o
31obj-$(CONFIG_SERIAL_SAMSUNG) += samsung.o
32obj-$(CONFIG_SERIAL_S3C2400) += s3c2400.o
31obj-$(CONFIG_SERIAL_S3C2410) += s3c2410.o 33obj-$(CONFIG_SERIAL_S3C2410) += s3c2410.o
34obj-$(CONFIG_SERIAL_S3C2412) += s3c2412.o
35obj-$(CONFIG_SERIAL_S3C2440) += s3c2440.o
32obj-$(CONFIG_SERIAL_SUNCORE) += suncore.o 36obj-$(CONFIG_SERIAL_SUNCORE) += suncore.o
33obj-$(CONFIG_SERIAL_SUNHV) += sunhv.o 37obj-$(CONFIG_SERIAL_SUNHV) += sunhv.o
34obj-$(CONFIG_SERIAL_SUNZILOG) += sunzilog.o 38obj-$(CONFIG_SERIAL_SUNZILOG) += sunzilog.o
diff --git a/drivers/serial/s3c2400.c b/drivers/serial/s3c2400.c
new file mode 100644
index 00000000000..a1102053e55
--- /dev/null
+++ b/drivers/serial/s3c2400.c
@@ -0,0 +1,106 @@
1/* linux/drivers/serial/s3c240.c
2 *
3 * Driver for Samsung SoC onboard UARTs.
4 *
5 * Ben Dooks, Copyright (c) 2003-2005 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#include <linux/module.h>
14#include <linux/ioport.h>
15#include <linux/io.h>
16#include <linux/platform_device.h>
17
18#include <asm/irq.h>
19
20#include <asm/hardware.h>
21
22#include <asm/plat-s3c/regs-serial.h>
23#include <asm/arch/regs-gpio.h>
24
25#include "samsung.h"
26
27static int s3c2400_serial_getsource(struct uart_port *port,
28 struct s3c24xx_uart_clksrc *clk)
29{
30 clk->divisor = 1;
31 clk->name = "pclk";
32
33 return 0;
34}
35
36static int s3c2400_serial_setsource(struct uart_port *port,
37 struct s3c24xx_uart_clksrc *clk)
38{
39 return 0;
40}
41
42static int s3c2400_serial_resetport(struct uart_port *port,
43 struct s3c2410_uartcfg *cfg)
44{
45 dbg("s3c2400_serial_resetport: port=%p (%08lx), cfg=%p\n",
46 port, port->mapbase, cfg);
47
48 wr_regl(port, S3C2410_UCON, cfg->ucon);
49 wr_regl(port, S3C2410_ULCON, cfg->ulcon);
50
51 /* reset both fifos */
52
53 wr_regl(port, S3C2410_UFCON, cfg->ufcon | S3C2410_UFCON_RESETBOTH);
54 wr_regl(port, S3C2410_UFCON, cfg->ufcon);
55
56 return 0;
57}
58
59static struct s3c24xx_uart_info s3c2400_uart_inf = {
60 .name = "Samsung S3C2400 UART",
61 .type = PORT_S3C2400,
62 .fifosize = 16,
63 .rx_fifomask = S3C2410_UFSTAT_RXMASK,
64 .rx_fifoshift = S3C2410_UFSTAT_RXSHIFT,
65 .rx_fifofull = S3C2410_UFSTAT_RXFULL,
66 .tx_fifofull = S3C2410_UFSTAT_TXFULL,
67 .tx_fifomask = S3C2410_UFSTAT_TXMASK,
68 .tx_fifoshift = S3C2410_UFSTAT_TXSHIFT,
69 .get_clksrc = s3c2400_serial_getsource,
70 .set_clksrc = s3c2400_serial_setsource,
71 .reset_port = s3c2400_serial_resetport,
72};
73
74static int s3c2400_serial_probe(struct platform_device *dev)
75{
76 return s3c24xx_serial_probe(dev, &s3c2400_uart_inf);
77}
78
79static struct platform_driver s3c2400_serial_drv = {
80 .probe = s3c2400_serial_probe,
81 .remove = s3c24xx_serial_remove,
82 .driver = {
83 .name = "s3c2400-uart",
84 .owner = THIS_MODULE,
85 },
86};
87
88s3c24xx_console_init(&s3c2400_serial_drv, &s3c2400_uart_inf);
89
90static inline int s3c2400_serial_init(void)
91{
92 return s3c24xx_serial_init(&s3c2400_serial_drv, &s3c2400_uart_inf);
93}
94
95static inline void s3c2400_serial_exit(void)
96{
97 platform_driver_unregister(&s3c2400_serial_drv);
98}
99
100module_init(s3c2400_serial_init);
101module_exit(s3c2400_serial_exit);
102
103MODULE_LICENSE("GPL v2");
104MODULE_AUTHOR("Ben Dooks <ben@simtec.co.uk>");
105MODULE_DESCRIPTION("Samsung S3C2400 SoC Serial port driver");
106MODULE_ALIAS("platform:s3c2400-uart");
diff --git a/drivers/serial/s3c2410.c b/drivers/serial/s3c2410.c
index b87c0b55aa2..c5f03f41686 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");
diff --git a/drivers/serial/s3c2412.c b/drivers/serial/s3c2412.c
new file mode 100644
index 00000000000..ce0c220e3e9
--- /dev/null
+++ b/drivers/serial/s3c2412.c
@@ -0,0 +1,151 @@
1/* linux/drivers/serial/s3c2412.c
2 *
3 * Driver for Samsung S3C2412 and S3C2413 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#include <linux/module.h>
14#include <linux/ioport.h>
15#include <linux/io.h>
16#include <linux/platform_device.h>
17#include <linux/init.h>
18#include <linux/serial_core.h>
19#include <linux/serial.h>
20
21#include <asm/irq.h>
22#include <asm/hardware.h>
23
24#include <asm/plat-s3c/regs-serial.h>
25#include <asm/arch/regs-gpio.h>
26
27#include "samsung.h"
28
29static int s3c2412_serial_setsource(struct uart_port *port,
30 struct s3c24xx_uart_clksrc *clk)
31{
32 unsigned long ucon = rd_regl(port, S3C2410_UCON);
33
34 ucon &= ~S3C2412_UCON_CLKMASK;
35
36 if (strcmp(clk->name, "uclk") == 0)
37 ucon |= S3C2440_UCON_UCLK;
38 else if (strcmp(clk->name, "pclk") == 0)
39 ucon |= S3C2440_UCON_PCLK;
40 else if (strcmp(clk->name, "usysclk") == 0)
41 ucon |= S3C2412_UCON_USYSCLK;
42 else {
43 printk(KERN_ERR "unknown clock source %s\n", clk->name);
44 return -EINVAL;
45 }
46
47 wr_regl(port, S3C2410_UCON, ucon);
48 return 0;
49}
50
51
52static int s3c2412_serial_getsource(struct uart_port *port,
53 struct s3c24xx_uart_clksrc *clk)
54{
55 unsigned long ucon = rd_regl(port, S3C2410_UCON);
56
57 switch (ucon & S3C2412_UCON_CLKMASK) {
58 case S3C2412_UCON_UCLK:
59 clk->divisor = 1;
60 clk->name = "uclk";
61 break;
62
63 case S3C2412_UCON_PCLK:
64 case S3C2412_UCON_PCLK2:
65 clk->divisor = 1;
66 clk->name = "pclk";
67 break;
68
69 case S3C2412_UCON_USYSCLK:
70 clk->divisor = 1;
71 clk->name = "usysclk";
72 break;
73 }
74
75 return 0;
76}
77
78static int s3c2412_serial_resetport(struct uart_port *port,
79 struct s3c2410_uartcfg *cfg)
80{
81 unsigned long ucon = rd_regl(port, S3C2410_UCON);
82
83 dbg("%s: port=%p (%08lx), cfg=%p\n",
84 __func__, port, port->mapbase, cfg);
85
86 /* ensure we don't change the clock settings... */
87
88 ucon &= S3C2412_UCON_CLKMASK;
89
90 wr_regl(port, S3C2410_UCON, ucon | cfg->ucon);
91 wr_regl(port, S3C2410_ULCON, cfg->ulcon);
92
93 /* reset both fifos */
94
95 wr_regl(port, S3C2410_UFCON, cfg->ufcon | S3C2410_UFCON_RESETBOTH);
96 wr_regl(port, S3C2410_UFCON, cfg->ufcon);
97
98 return 0;
99}
100
101static struct s3c24xx_uart_info s3c2412_uart_inf = {
102 .name = "Samsung S3C2412 UART",
103 .type = PORT_S3C2412,
104 .fifosize = 64,
105 .rx_fifomask = S3C2440_UFSTAT_RXMASK,
106 .rx_fifoshift = S3C2440_UFSTAT_RXSHIFT,
107 .rx_fifofull = S3C2440_UFSTAT_RXFULL,
108 .tx_fifofull = S3C2440_UFSTAT_TXFULL,
109 .tx_fifomask = S3C2440_UFSTAT_TXMASK,
110 .tx_fifoshift = S3C2440_UFSTAT_TXSHIFT,
111 .get_clksrc = s3c2412_serial_getsource,
112 .set_clksrc = s3c2412_serial_setsource,
113 .reset_port = s3c2412_serial_resetport,
114};
115
116/* device management */
117
118static int s3c2412_serial_probe(struct platform_device *dev)
119{
120 dbg("s3c2440_serial_probe: dev=%p\n", dev);
121 return s3c24xx_serial_probe(dev, &s3c2412_uart_inf);
122}
123
124static struct platform_driver s3c2412_serial_drv = {
125 .probe = s3c2412_serial_probe,
126 .remove = s3c24xx_serial_remove,
127 .driver = {
128 .name = "s3c2412-uart",
129 .owner = THIS_MODULE,
130 },
131};
132
133s3c24xx_console_init(&s3c2412_serial_drv, &s3c2412_uart_inf);
134
135static inline int s3c2412_serial_init(void)
136{
137 return s3c24xx_serial_init(&s3c2412_serial_drv, &s3c2412_uart_inf);
138}
139
140static inline void s3c2412_serial_exit(void)
141{
142 platform_driver_unregister(&s3c2412_serial_drv);
143}
144
145module_init(s3c2412_serial_init);
146module_exit(s3c2412_serial_exit);
147
148MODULE_DESCRIPTION("Samsung S3C2412,S3C2413 SoC Serial port driver");
149MODULE_AUTHOR("Ben Dooks <ben@simtec.co.uk>");
150MODULE_LICENSE("GPL v2");
151MODULE_ALIAS("platform:s3c2412-uart");
diff --git a/drivers/serial/s3c2440.c b/drivers/serial/s3c2440.c
new file mode 100644
index 00000000000..38f954bd39c
--- /dev/null
+++ b/drivers/serial/s3c2440.c
@@ -0,0 +1,181 @@
1/* linux/drivers/serial/s3c2440.c
2 *
3 * Driver for Samsung S3C2440 and S3C2442 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#include <linux/module.h>
14#include <linux/ioport.h>
15#include <linux/io.h>
16#include <linux/platform_device.h>
17#include <linux/init.h>
18#include <linux/serial_core.h>
19#include <linux/serial.h>
20
21#include <asm/irq.h>
22#include <asm/hardware.h>
23
24#include <asm/plat-s3c/regs-serial.h>
25#include <asm/arch/regs-gpio.h>
26
27#include "samsung.h"
28
29
30static int s3c2440_serial_setsource(struct uart_port *port,
31 struct s3c24xx_uart_clksrc *clk)
32{
33 unsigned long ucon = rd_regl(port, S3C2410_UCON);
34
35 /* todo - proper fclk<>nonfclk switch. */
36
37 ucon &= ~S3C2440_UCON_CLKMASK;
38
39 if (strcmp(clk->name, "uclk") == 0)
40 ucon |= S3C2440_UCON_UCLK;
41 else if (strcmp(clk->name, "pclk") == 0)
42 ucon |= S3C2440_UCON_PCLK;
43 else if (strcmp(clk->name, "fclk") == 0)
44 ucon |= S3C2440_UCON_FCLK;
45 else {
46 printk(KERN_ERR "unknown clock source %s\n", clk->name);
47 return -EINVAL;
48 }
49
50 wr_regl(port, S3C2410_UCON, ucon);
51 return 0;
52}
53
54
55static int s3c2440_serial_getsource(struct uart_port *port,
56 struct s3c24xx_uart_clksrc *clk)
57{
58 unsigned long ucon = rd_regl(port, S3C2410_UCON);
59 unsigned long ucon0, ucon1, ucon2;
60
61 switch (ucon & S3C2440_UCON_CLKMASK) {
62 case S3C2440_UCON_UCLK:
63 clk->divisor = 1;
64 clk->name = "uclk";
65 break;
66
67 case S3C2440_UCON_PCLK:
68 case S3C2440_UCON_PCLK2:
69 clk->divisor = 1;
70 clk->name = "pclk";
71 break;
72
73 case S3C2440_UCON_FCLK:
74 /* the fun of calculating the uart divisors on
75 * the s3c2440 */
76
77 ucon0 = __raw_readl(S3C24XX_VA_UART0 + S3C2410_UCON);
78 ucon1 = __raw_readl(S3C24XX_VA_UART1 + S3C2410_UCON);
79 ucon2 = __raw_readl(S3C24XX_VA_UART2 + S3C2410_UCON);
80
81 printk("ucons: %08lx, %08lx, %08lx\n", ucon0, ucon1, ucon2);
82
83 ucon0 &= S3C2440_UCON0_DIVMASK;
84 ucon1 &= S3C2440_UCON1_DIVMASK;
85 ucon2 &= S3C2440_UCON2_DIVMASK;
86
87 if (ucon0 != 0) {
88 clk->divisor = ucon0 >> S3C2440_UCON_DIVSHIFT;
89 clk->divisor += 6;
90 } else if (ucon1 != 0) {
91 clk->divisor = ucon1 >> S3C2440_UCON_DIVSHIFT;
92 clk->divisor += 21;
93 } else if (ucon2 != 0) {
94 clk->divisor = ucon2 >> S3C2440_UCON_DIVSHIFT;
95 clk->divisor += 36;
96 } else {
97 /* manual calims 44, seems to be 9 */
98 clk->divisor = 9;
99 }
100
101 clk->name = "fclk";
102 break;
103 }
104
105 return 0;
106}
107
108static int s3c2440_serial_resetport(struct uart_port *port,
109 struct s3c2410_uartcfg *cfg)
110{
111 unsigned long ucon = rd_regl(port, S3C2410_UCON);
112
113 dbg("s3c2440_serial_resetport: port=%p (%08lx), cfg=%p\n",
114 port, port->mapbase, cfg);
115
116 /* ensure we don't change the clock settings... */
117
118 ucon &= (S3C2440_UCON0_DIVMASK | (3<<10));
119
120 wr_regl(port, S3C2410_UCON, ucon | cfg->ucon);
121 wr_regl(port, S3C2410_ULCON, cfg->ulcon);
122
123 /* reset both fifos */
124
125 wr_regl(port, S3C2410_UFCON, cfg->ufcon | S3C2410_UFCON_RESETBOTH);
126 wr_regl(port, S3C2410_UFCON, cfg->ufcon);
127
128 return 0;
129}
130
131static struct s3c24xx_uart_info s3c2440_uart_inf = {
132 .name = "Samsung S3C2440 UART",
133 .type = PORT_S3C2440,
134 .fifosize = 64,
135 .rx_fifomask = S3C2440_UFSTAT_RXMASK,
136 .rx_fifoshift = S3C2440_UFSTAT_RXSHIFT,
137 .rx_fifofull = S3C2440_UFSTAT_RXFULL,
138 .tx_fifofull = S3C2440_UFSTAT_TXFULL,
139 .tx_fifomask = S3C2440_UFSTAT_TXMASK,
140 .tx_fifoshift = S3C2440_UFSTAT_TXSHIFT,
141 .get_clksrc = s3c2440_serial_getsource,
142 .set_clksrc = s3c2440_serial_setsource,
143 .reset_port = s3c2440_serial_resetport,
144};
145
146/* device management */
147
148static int s3c2440_serial_probe(struct platform_device *dev)
149{
150 dbg("s3c2440_serial_probe: dev=%p\n", dev);
151 return s3c24xx_serial_probe(dev, &s3c2440_uart_inf);
152}
153
154static struct platform_driver s3c2440_serial_drv = {
155 .probe = s3c2440_serial_probe,
156 .remove = s3c24xx_serial_remove,
157 .driver = {
158 .name = "s3c2440-uart",
159 .owner = THIS_MODULE,
160 },
161};
162
163s3c24xx_console_init(&s3c2440_serial_drv, &s3c2440_uart_inf);
164
165static int __init s3c2440_serial_init(void)
166{
167 return s3c24xx_serial_init(&s3c2440_serial_drv, &s3c2440_uart_inf);
168}
169
170static void __exit s3c2440_serial_exit(void)
171{
172 platform_driver_unregister(&s3c2440_serial_drv);
173}
174
175module_init(s3c2440_serial_init);
176module_exit(s3c2440_serial_exit);
177
178MODULE_DESCRIPTION("Samsung S3C2440,S3C2442 SoC Serial port driver");
179MODULE_AUTHOR("Ben Dooks <ben@simtec.co.uk>");
180MODULE_LICENSE("GPLi v2");
181MODULE_ALIAS("platform:s3c2440-uart");
diff --git a/drivers/serial/samsung.c b/drivers/serial/samsung.c
new file mode 100644
index 00000000000..4a3ecaa629e
--- /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");
diff --git a/drivers/serial/samsung.h b/drivers/serial/samsung.h
new file mode 100644
index 00000000000..5c92ebbe7d9
--- /dev/null
+++ b/drivers/serial/samsung.h
@@ -0,0 +1,102 @@
1/* linux/drivers/serial/samsung.h
2 *
3 * Driver 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
13struct s3c24xx_uart_info {
14 char *name;
15 unsigned int type;
16 unsigned int fifosize;
17 unsigned long rx_fifomask;
18 unsigned long rx_fifoshift;
19 unsigned long rx_fifofull;
20 unsigned long tx_fifomask;
21 unsigned long tx_fifoshift;
22 unsigned long tx_fifofull;
23
24 /* clock source control */
25
26 int (*get_clksrc)(struct uart_port *, struct s3c24xx_uart_clksrc *clk);
27 int (*set_clksrc)(struct uart_port *, struct s3c24xx_uart_clksrc *clk);
28
29 /* uart controls */
30 int (*reset_port)(struct uart_port *, struct s3c2410_uartcfg *);
31};
32
33struct s3c24xx_uart_port {
34 unsigned char rx_claimed;
35 unsigned char tx_claimed;
36
37 struct s3c24xx_uart_info *info;
38 struct s3c24xx_uart_clksrc *clksrc;
39 struct clk *clk;
40 struct clk *baudclk;
41 struct uart_port port;
42};
43
44/* conversion functions */
45
46#define s3c24xx_dev_to_port(__dev) (struct uart_port *)dev_get_drvdata(__dev)
47#define s3c24xx_dev_to_cfg(__dev) (struct s3c2410_uartcfg *)((__dev)->platform_data)
48
49/* register access controls */
50
51#define portaddr(port, reg) ((port)->membase + (reg))
52
53#define rd_regb(port, reg) (__raw_readb(portaddr(port, reg)))
54#define rd_regl(port, reg) (__raw_readl(portaddr(port, reg)))
55
56#define wr_regb(port, reg, val) __raw_writeb(val, portaddr(port, reg))
57#define wr_regl(port, reg, val) __raw_writel(val, portaddr(port, reg))
58
59extern int s3c24xx_serial_probe(struct platform_device *dev,
60 struct s3c24xx_uart_info *uart);
61
62extern int s3c24xx_serial_remove(struct platform_device *dev);
63
64extern int s3c24xx_serial_initconsole(struct platform_driver *drv,
65 struct s3c24xx_uart_info *uart);
66
67extern int s3c24xx_serial_init(struct platform_driver *drv,
68 struct s3c24xx_uart_info *info);
69
70#ifdef CONFIG_SERIAL_SAMSUNG_CONSOLE
71
72#define s3c24xx_console_init(__drv, __inf) \
73static int __init s3c_serial_console_init(void) \
74{ \
75 return s3c24xx_serial_initconsole(__drv, __inf); \
76} \
77 \
78console_initcall(s3c_serial_console_init)
79
80#else
81#define s3c24xx_console_init(drv, inf) extern void no_console(void)
82#endif
83
84#ifdef CONFIG_SERIAL_SAMSUNG_DEBUG
85
86extern void printascii(const char *);
87
88static void dbg(const char *fmt, ...)
89{
90 va_list va;
91 char buff[256];
92
93 va_start(va, fmt);
94 vsprintf(buff, fmt, va);
95 va_end(va);
96
97 printascii(buff);
98}
99
100#else
101#define dbg(x...) do { } while (0)
102#endif