[ARM] 3639/1: S3C2412: serial port support
authorBen Dooks <ben-linux@fluff.org>
Sat, 24 Jun 2006 20:21:32 +0000 (21:21 +0100)
committerRussell King <rmk+kernel@arm.linux.org.uk>
Sat, 24 Jun 2006 20:21:32 +0000 (21:21 +0100)
Patch from Ben Dooks

Serial port support for the on-board UART blocks
on the Samsung S3C2412 and S3C2413 UARTs.

Signed-off-by: Ben Dooks <ben-linux@fluff.org>
Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
drivers/serial/Kconfig
drivers/serial/s3c2410.c
include/asm-arm/arch-s3c2410/regs-serial.h
include/linux/serial_core.h

index bef4a9622ed7850c16fb5f35ba84be8af1d36971..5b48ac22c9c5964b62b93500831f1656a1b84e1a 100644 (file)
@@ -354,21 +354,24 @@ config SERIAL_CLPS711X_CONSOLE
          kernel at boot time.)
 
 config SERIAL_S3C2410
-       tristate "Samsung S3C2410 Serial port support"
+       tristate "Samsung S3C2410/S3C2440/S3C2442/S3C2412 Serial port support"
        depends on ARM && ARCH_S3C2410
        select SERIAL_CORE
        help
-         Support for the on-chip UARTs on the Samsung S3C2410X CPU,
+         Support for the on-chip UARTs on the Samsung S3C24XX series CPUs,
          providing /dev/ttySAC0, 1 and 2 (note, some machines may not
          provide all of these ports, depending on how the serial port
          pins are configured.
 
+         Currently this driver supports the UARTS on the S3C2410, S3C2440,
+         S3C2442, S3C2412 and S3C2413 CPUs.
+
 config SERIAL_S3C2410_CONSOLE
        bool "Support for console on S3C2410 serial port"
        depends on SERIAL_S3C2410=y
        select SERIAL_CORE_CONSOLE
        help
-         Allow selection of the S3C2410 on-board serial ports for use as
+         Allow selection of the S3C24XX on-board serial ports for use as
          an virtual console.
 
          Even if you say Y here, the currently visible virtual console
index 53c2465bad2d37117e121ee78e5967f2c5cfc67a..837b6da520b3aeb0f9bbd0897ead81f123233b55 100644 (file)
@@ -872,6 +872,8 @@ static const char *s3c24xx_serial_type(struct uart_port *port)
                return "S3C2410";
        case PORT_S3C2440:
                return "S3C2440";
+       case PORT_S3C2412:
+               return "S3C2412";
        default:
                return NULL;
        }
@@ -1528,6 +1530,141 @@ static inline void s3c2440_serial_exit(void)
 #define s3c2440_uart_inf_at NULL
 #endif /* CONFIG_CPU_S3C2440 */
 
+#if defined(CONFIG_CPU_S3C2412) || defined(CONFIG_CPU_S3C2413)
+
+static int s3c2412_serial_setsource(struct uart_port *port,
+                                    struct s3c24xx_uart_clksrc *clk)
+{
+       unsigned long ucon = rd_regl(port, S3C2410_UCON);
+
+       ucon &= ~S3C2412_UCON_CLKMASK;
+
+       if (strcmp(clk->name, "uclk") == 0)
+               ucon |= S3C2440_UCON_UCLK;
+       else if (strcmp(clk->name, "pclk") == 0)
+               ucon |= S3C2440_UCON_PCLK;
+       else if (strcmp(clk->name, "usysclk") == 0)
+               ucon |= S3C2412_UCON_USYSCLK;
+       else {
+               printk(KERN_ERR "unknown clock source %s\n", clk->name);
+               return -EINVAL;
+       }
+
+       wr_regl(port, S3C2410_UCON, ucon);
+       return 0;
+}
+
+
+static int s3c2412_serial_getsource(struct uart_port *port,
+                                   struct s3c24xx_uart_clksrc *clk)
+{
+       unsigned long ucon = rd_regl(port, S3C2410_UCON);
+
+       switch (ucon & S3C2412_UCON_CLKMASK) {
+       case S3C2412_UCON_UCLK:
+               clk->divisor = 1;
+               clk->name = "uclk";
+               break;
+
+       case S3C2412_UCON_PCLK:
+       case S3C2412_UCON_PCLK2:
+               clk->divisor = 1;
+               clk->name = "pclk";
+               break;
+
+       case S3C2412_UCON_USYSCLK:
+               clk->divisor = 1;
+               clk->name = "usysclk";
+               break;
+       }
+
+       return 0;
+}
+
+static int s3c2412_serial_resetport(struct uart_port *port,
+                                   struct s3c2410_uartcfg *cfg)
+{
+       unsigned long ucon = rd_regl(port, S3C2410_UCON);
+
+       dbg("%s: port=%p (%08lx), cfg=%p\n",
+           __FUNCTION__, port, port->mapbase, cfg);
+
+       /* ensure we don't change the clock settings... */
+
+       ucon &= S3C2412_UCON_CLKMASK;
+
+       wr_regl(port, S3C2410_UCON,  ucon | cfg->ucon);
+       wr_regl(port, S3C2410_ULCON, cfg->ulcon);
+
+       /* reset both fifos */
+
+       wr_regl(port, S3C2410_UFCON, cfg->ufcon | S3C2410_UFCON_RESETBOTH);
+       wr_regl(port, S3C2410_UFCON, cfg->ufcon);
+
+       return 0;
+}
+
+static struct s3c24xx_uart_info s3c2412_uart_inf = {
+       .name           = "Samsung S3C2412 UART",
+       .type           = PORT_S3C2412,
+       .fifosize       = 64,
+       .rx_fifomask    = S3C2440_UFSTAT_RXMASK,
+       .rx_fifoshift   = S3C2440_UFSTAT_RXSHIFT,
+       .rx_fifofull    = S3C2440_UFSTAT_RXFULL,
+       .tx_fifofull    = S3C2440_UFSTAT_TXFULL,
+       .tx_fifomask    = S3C2440_UFSTAT_TXMASK,
+       .tx_fifoshift   = S3C2440_UFSTAT_TXSHIFT,
+       .get_clksrc     = s3c2412_serial_getsource,
+       .set_clksrc     = s3c2412_serial_setsource,
+       .reset_port     = s3c2412_serial_resetport,
+};
+
+/* device management */
+
+static int s3c2412_serial_probe(struct platform_device *dev)
+{
+       dbg("s3c2440_serial_probe: dev=%p\n", dev);
+       return s3c24xx_serial_probe(dev, &s3c2440_uart_inf);
+}
+
+static struct platform_driver s3c2412_serial_drv = {
+       .probe          = s3c2412_serial_probe,
+       .remove         = s3c24xx_serial_remove,
+       .suspend        = s3c24xx_serial_suspend,
+       .resume         = s3c24xx_serial_resume,
+       .driver         = {
+               .name   = "s3c2412-uart",
+               .owner  = THIS_MODULE,
+       },
+};
+
+
+static inline int s3c2412_serial_init(void)
+{
+       return s3c24xx_serial_init(&s3c2412_serial_drv, &s3c2412_uart_inf);
+}
+
+static inline void s3c2412_serial_exit(void)
+{
+       platform_driver_unregister(&s3c2412_serial_drv);
+}
+
+#define s3c2412_uart_inf_at &s3c2412_uart_inf
+#else
+
+static inline int s3c2412_serial_init(void)
+{
+       return 0;
+}
+
+static inline void s3c2412_serial_exit(void)
+{
+}
+
+#define s3c2412_uart_inf_at NULL
+#endif /* CONFIG_CPU_S3C2440 */
+
+
 /* module initialisation code */
 
 static int __init s3c24xx_serial_modinit(void)
@@ -1542,6 +1679,7 @@ static int __init s3c24xx_serial_modinit(void)
 
        s3c2400_serial_init();
        s3c2410_serial_init();
+       s3c2412_serial_init();
        s3c2440_serial_init();
 
        return 0;
@@ -1551,6 +1689,7 @@ static void __exit s3c24xx_serial_modexit(void)
 {
        s3c2400_serial_exit();
        s3c2410_serial_exit();
+       s3c2412_serial_exit();
        s3c2440_serial_exit();
 
        uart_unregister_driver(&s3c24xx_uart_drv);
@@ -1773,6 +1912,8 @@ static int s3c24xx_serial_initconsole(void)
                info = s3c2410_uart_inf_at;
        } else if (strcmp(dev->name, "s3c2440-uart") == 0) {
                info = s3c2440_uart_inf_at;
+       } else if (strcmp(dev->name, "s3c2412-uart") == 0) {
+               info = s3c2412_uart_inf_at;
        } else {
                printk(KERN_ERR "s3c24xx: no driver for %s\n", dev->name);
                return 0;
@@ -1796,4 +1937,4 @@ console_initcall(s3c24xx_serial_initconsole);
 
 MODULE_LICENSE("GPL");
 MODULE_AUTHOR("Ben Dooks <ben@simtec.co.uk>");
-MODULE_DESCRIPTION("Samsung S3C2410/S3C2440 Serial port driver");
+MODULE_DESCRIPTION("Samsung S3C2410/S3C2440/S3C2412 Serial port driver");
index 83b01254c4ac7374421cbad670debe9424ef42cc..93f651ae29677908a996a9c9524824d9508a0c8d 100644 (file)
 #define S3C2440_UCON2_DIVMASK    (7 << 12)
 #define S3C2440_UCON_DIVSHIFT    (12)
 
+#define S3C2412_UCON_CLKMASK   (3<<10)
+#define S3C2412_UCON_UCLK      (1<<10)
+#define S3C2412_UCON_USYSCLK   (3<<10)
+#define S3C2412_UCON_PCLK      (0<<10)
+#define S3C2412_UCON_PCLK2     (2<<10)
+
 #define S3C2410_UCON_UCLK        (1<<10)
 #define S3C2410_UCON_SBREAK      (1<<4)
 
 #define        S3C2410_UMCOM_AFC         (1<<4)
 #define        S3C2410_UMCOM_RTS_LOW     (1<<0)
 
+#define S3C2412_UMCON_AFC_63   (0<<5)
+#define S3C2412_UMCON_AFC_56   (1<<5)
+#define S3C2412_UMCON_AFC_48   (2<<5)
+#define S3C2412_UMCON_AFC_40   (3<<5)
+#define S3C2412_UMCON_AFC_32   (4<<5)
+#define S3C2412_UMCON_AFC_24   (5<<5)
+#define S3C2412_UMCON_AFC_16   (6<<5)
+#define S3C2412_UMCON_AFC_8    (7<<5)
+
 #define S3C2410_UFSTAT_TXFULL    (1<<9)
 #define S3C2410_UFSTAT_RXFULL    (1<<8)
 #define S3C2410_UFSTAT_TXMASK    (15<<4)
index 0ef50baa7da6d09b782e662ddbde7b218899a81d..951c4e858274618ba3295dbce522954088ea9379 100644 (file)
 /* SUN4V Hypervisor Console */
 #define PORT_SUNHV     72
 
+#define PORT_S3C2412   73
+
+
 #ifdef __KERNEL__
 
 #include <linux/compiler.h>