V4L/DVB: s2255drv: return if vdev not found
[safe/jmp/linux-2.6] / drivers / serial / samsung.c
index e06686a..a9d6c56 100644 (file)
@@ -2,7 +2,7 @@
  *
  * Driver core for Samsung SoC onboard UARTs.
  *
- * Ben Dooks, Copyright (c) 2003-2005,2008 Simtec Electronics
+ * Ben Dooks, Copyright (c) 2003-2008 Simtec Electronics
  *     http://armlinux.simtec.co.uk/
  *
  * This program is free software; you can redistribute it and/or modify
@@ -196,7 +196,7 @@ s3c24xx_serial_rx_chars(int irq, void *dev_id)
 {
        struct s3c24xx_uart_port *ourport = dev_id;
        struct uart_port *port = &ourport->port;
-       struct tty_struct *tty = port->info->port.tty;
+       struct tty_struct *tty = port->state->port.tty;
        unsigned int ufcon, ch, flag, ufstat, uerstat;
        int max_count = 64;
 
@@ -281,7 +281,7 @@ static irqreturn_t s3c24xx_serial_tx_chars(int irq, void *id)
 {
        struct s3c24xx_uart_port *ourport = id;
        struct uart_port *port = &ourport->port;
-       struct circ_buf *xmit = &port->info->xmit;
+       struct circ_buf *xmit = &port->state->xmit;
        int count = 256;
 
        if (port->x_char) {
@@ -508,6 +508,7 @@ s3c24xx_serial_setsource(struct uart_port *port, struct s3c24xx_uart_clksrc *c)
 struct baud_calc {
        struct s3c24xx_uart_clksrc      *clksrc;
        unsigned int                     calc;
+       unsigned int                     divslot;
        unsigned int                     quot;
        struct clk                      *src;
 };
@@ -517,6 +518,7 @@ static int s3c24xx_serial_calcbaud(struct baud_calc *calc,
                                   struct s3c24xx_uart_clksrc *clksrc,
                                   unsigned int baud)
 {
+       struct s3c24xx_uart_port *ourport = to_ourport(port);
        unsigned long rate;
 
        calc->src = clk_get(port->dev, clksrc->name);
@@ -527,8 +529,24 @@ static int s3c24xx_serial_calcbaud(struct baud_calc *calc,
        rate /= clksrc->divisor;
 
        calc->clksrc = clksrc;
-       calc->quot = (rate + (8 * baud)) / (16 * baud);
-       calc->calc = (rate / (calc->quot * 16));
+
+       if (ourport->info->has_divslot) {
+               unsigned long div = rate / baud;
+
+               /* The UDIVSLOT register on the newer UARTs allows us to
+                * get a divisor adjustment of 1/16th on the baud clock.
+                *
+                * We don't keep the UDIVSLOT value (the 16ths we calculated
+                * by not multiplying the baud by 16) as it is easy enough
+                * to recalculate.
+                */
+
+               calc->quot = div / 16;
+               calc->calc = rate / div;
+       } else {
+               calc->quot = (rate + (8 * baud)) / (16 * baud);
+               calc->calc = (rate / (calc->quot * 16));
+       }
 
        calc->quot--;
        return 1;
@@ -611,6 +629,30 @@ static unsigned int s3c24xx_serial_getclk(struct uart_port *port,
        return best->quot;
 }
 
+/* udivslot_table[]
+ *
+ * This table takes the fractional value of the baud divisor and gives
+ * the recommended setting for the UDIVSLOT register.
+ */
+static u16 udivslot_table[16] = {
+       [0] = 0x0000,
+       [1] = 0x0080,
+       [2] = 0x0808,
+       [3] = 0x0888,
+       [4] = 0x2222,
+       [5] = 0x4924,
+       [6] = 0x4A52,
+       [7] = 0x54AA,
+       [8] = 0x5555,
+       [9] = 0xD555,
+       [10] = 0xD5D5,
+       [11] = 0xDDD5,
+       [12] = 0xDDDD,
+       [13] = 0xDFDD,
+       [14] = 0xDFDF,
+       [15] = 0xFFDF,
+};
+
 static void s3c24xx_serial_set_termios(struct uart_port *port,
                                       struct ktermios *termios,
                                       struct ktermios *old)
@@ -623,6 +665,7 @@ static void s3c24xx_serial_set_termios(struct uart_port *port,
        unsigned int baud, quot;
        unsigned int ulcon;
        unsigned int umcon;
+       unsigned int udivslot = 0;
 
        /*
         * We don't support modem control lines.
@@ -644,6 +687,7 @@ static void s3c24xx_serial_set_termios(struct uart_port *port,
        /* check to see if we need  to change clock source */
 
        if (ourport->clksrc != clksrc || ourport->baudclk != clk) {
+               dbg("selecting clock %p\n", clk);
                s3c24xx_serial_setsource(port, clksrc);
 
                if (ourport->baudclk != NULL && !IS_ERR(ourport->baudclk)) {
@@ -658,6 +702,13 @@ static void s3c24xx_serial_set_termios(struct uart_port *port,
                ourport->baudclk_rate = clk ? clk_get_rate(clk) : 0;
        }
 
+       if (ourport->info->has_divslot) {
+               unsigned int div = ourport->baudclk_rate / baud;
+
+               udivslot = udivslot_table[div & 15];
+               dbg("udivslot = %04x (div %d)\n", udivslot, div & 15);
+       }
+
        switch (termios->c_cflag & CSIZE) {
        case CS5:
                dbg("config: 5bits/char\n");
@@ -697,12 +748,16 @@ static void s3c24xx_serial_set_termios(struct uart_port *port,
 
        spin_lock_irqsave(&port->lock, flags);
 
-       dbg("setting ulcon to %08x, brddiv to %d\n", ulcon, quot);
+       dbg("setting ulcon to %08x, brddiv to %d, udivslot %08x\n",
+           ulcon, quot, udivslot);
 
        wr_regl(port, S3C2410_ULCON, ulcon);
        wr_regl(port, S3C2410_UBRDIV, quot);
        wr_regl(port, S3C2410_UMCON, umcon);
 
+       if (ourport->info->has_divslot)
+               wr_regl(port, S3C2443_DIVSLOT, udivslot);
+
        dbg("uart: ulcon = 0x%08x, ucon = 0x%08x, ufcon = 0x%08x\n",
            rd_regl(port, S3C2410_ULCON),
            rd_regl(port, S3C2410_UCON),
@@ -937,10 +992,10 @@ static int s3c24xx_serial_cpufreq_transition(struct notifier_block *nb,
                struct ktermios *termios;
                struct tty_struct *tty;
 
-               if (uport->info == NULL)
+               if (uport->state == NULL)
                        goto exit;
 
-               tty = uport->info->port.tty;
+               tty = uport->state->port.tty;
 
                if (tty == NULL)
                        goto exit;
@@ -1119,7 +1174,7 @@ int s3c24xx_serial_probe(struct platform_device *dev,
 
 EXPORT_SYMBOL_GPL(s3c24xx_serial_probe);
 
-int s3c24xx_serial_remove(struct platform_device *dev)
+int __devexit s3c24xx_serial_remove(struct platform_device *dev)
 {
        struct uart_port *port = s3c24xx_dev_to_port(&dev->dev);
 
@@ -1216,7 +1271,7 @@ s3c24xx_serial_console_txrdy(struct uart_port *port, unsigned int ufcon)
        unsigned long ufstat, utrstat;
 
        if (ufcon & S3C2410_UFCON_FIFOMODE) {
-               /* fifo mode - check ammount of data in fifo registers... */
+               /* fifo mode - check amount of data in fifo registers... */
 
                ufstat = rd_regl(port, S3C2410_UFSTAT);
                return (ufstat & info->tx_fifofull) ? 0 : 1;
@@ -1319,7 +1374,7 @@ s3c24xx_serial_get_options(struct uart_port *port, int *baud,
  * data.
 */
 
-static int s3c24xx_serial_init_ports(struct s3c24xx_uart_info *info)
+static int s3c24xx_serial_init_ports(struct s3c24xx_uart_info **info)
 {
        struct s3c24xx_uart_port *ptr = s3c24xx_serial_ports;
        struct platform_device **platdev_ptr;
@@ -1330,7 +1385,7 @@ static int s3c24xx_serial_init_ports(struct s3c24xx_uart_info *info)
        platdev_ptr = s3c24xx_uart_devs;
 
        for (i = 0; i < CONFIG_SERIAL_SAMSUNG_UARTS; i++, ptr++, platdev_ptr++) {
-               s3c24xx_serial_init_port(ptr, info, *platdev_ptr);
+               s3c24xx_serial_init_port(ptr, info[i], *platdev_ptr);
        }
 
        return 0;
@@ -1396,7 +1451,7 @@ static struct console s3c24xx_serial_console = {
 };
 
 int s3c24xx_serial_initconsole(struct platform_driver *drv,
-                              struct s3c24xx_uart_info *info)
+                              struct s3c24xx_uart_info **info)
 
 {
        struct platform_device *dev = s3c24xx_uart_devs[0];