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