serial: 8250: Encapsulate port i/o method init
[linux-2.6-block.git] / drivers / tty / serial / 8250 / 8250_core.c
index e3b9570a1eff8aa467ec7dabbe93369c47940e87..c5db1395c3487db3e22cba7d0531fb02d045fc79 100644 (file)
@@ -61,7 +61,7 @@ static struct uart_driver serial8250_reg;
 
 static int serial_index(struct uart_port *port)
 {
-       return (serial8250_reg.minor - 64) + port->line;
+       return port->minor - 64;
 }
 
 static unsigned int skip_txen_test; /* force skip of txen test at init time */
@@ -895,7 +895,7 @@ static int broken_efr(struct uart_8250_port *up)
        /*
         * Exar ST16C2550 "A2" devices incorrectly detect as
         * having an EFR, and report an ID of 0x0201.  See
-        * http://linux.derkeiler.com/Mailing-Lists/Kernel/2004-11/4812.html 
+        * http://linux.derkeiler.com/Mailing-Lists/Kernel/2004-11/4812.html
         */
        if (autoconfig_read_divisor_id(up) == 0x0201 && size_fifo(up) == 16)
                return 1;
@@ -903,23 +903,6 @@ static int broken_efr(struct uart_8250_port *up)
        return 0;
 }
 
-static inline int ns16550a_goto_highspeed(struct uart_8250_port *up)
-{
-       unsigned char status;
-
-       status = serial_in(up, 0x04); /* EXCR2 */
-#define PRESL(x) ((x) & 0x30)
-       if (PRESL(status) == 0x10) {
-               /* already in high speed mode */
-               return 0;
-       } else {
-               status &= ~0xB0; /* Disable LOCK, mask out PRESL[01] */
-               status |= 0x10;  /* 1.625 divisor for baud_base --> 921600 */
-               serial_out(up, 0x04, status);
-       }
-       return 1;
-}
-
 /*
  * We know that the chip has FIFOs.  Does it have an EFR?  The
  * EFR is located in the same register position as the IIR and
@@ -1260,7 +1243,7 @@ static void autoconfig(struct uart_8250_port *up, unsigned int probeflags)
        serial_out(up, UART_LCR, save_lcr);
 
        port->fifosize = uart_config[up->port.type].fifo_size;
-       old_capabilities = up->capabilities; 
+       old_capabilities = up->capabilities;
        up->capabilities = uart_config[port->type].flags;
        up->tx_loadsz = uart_config[port->type].tx_loadsz;
 
@@ -1907,6 +1890,48 @@ static void serial8250_backup_timeout(unsigned long data)
                jiffies + uart_poll_timeout(&up->port) + HZ / 5);
 }
 
+static int univ8250_setup_irq(struct uart_8250_port *up)
+{
+       struct uart_port *port = &up->port;
+       int retval = 0;
+
+       /*
+        * The above check will only give an accurate result the first time
+        * the port is opened so this value needs to be preserved.
+        */
+       if (up->bugs & UART_BUG_THRE) {
+               pr_debug("ttyS%d - using backup timer\n", serial_index(port));
+
+               up->timer.function = serial8250_backup_timeout;
+               up->timer.data = (unsigned long)up;
+               mod_timer(&up->timer, jiffies +
+                         uart_poll_timeout(port) + HZ / 5);
+       }
+
+       /*
+        * If the "interrupt" for this port doesn't correspond with any
+        * hardware interrupt, we use a timer-based system.  The original
+        * driver used to do this with IRQ0.
+        */
+       if (!port->irq) {
+               up->timer.data = (unsigned long)up;
+               mod_timer(&up->timer, jiffies + uart_poll_timeout(port));
+       } else
+               retval = serial_link_irq_chain(up);
+
+       return retval;
+}
+
+static void univ8250_release_irq(struct uart_8250_port *up)
+{
+       struct uart_port *port = &up->port;
+
+       del_timer_sync(&up->timer);
+       up->timer.function = serial8250_timeout;
+       if (port->irq)
+               serial_unlink_irq_chain(up);
+}
+
 static unsigned int serial8250_tx_empty(struct uart_port *port)
 {
        struct uart_8250_port *up = up_to_u8250p(port);
@@ -2138,8 +2163,8 @@ int serial8250_do_startup(struct uart_port *port)
        /*
         * Clear the interrupt registers.
         */
-       if (serial_port_in(port, UART_LSR) & UART_LSR_DR)
-               serial_port_in(port, UART_RX);
+       serial_port_in(port, UART_LSR);
+       serial_port_in(port, UART_RX);
        serial_port_in(port, UART_IIR);
        serial_port_in(port, UART_MSR);
 
@@ -2211,35 +2236,12 @@ int serial8250_do_startup(struct uart_port *port)
                if ((!(iir1 & UART_IIR_NO_INT) && (iir & UART_IIR_NO_INT)) ||
                    up->port.flags & UPF_BUG_THRE) {
                        up->bugs |= UART_BUG_THRE;
-                       pr_debug("ttyS%d - using backup timer\n",
-                                serial_index(port));
                }
        }
 
-       /*
-        * The above check will only give an accurate result the first time
-        * the port is opened so this value needs to be preserved.
-        */
-       if (up->bugs & UART_BUG_THRE) {
-               up->timer.function = serial8250_backup_timeout;
-               up->timer.data = (unsigned long)up;
-               mod_timer(&up->timer, jiffies +
-                       uart_poll_timeout(port) + HZ / 5);
-       }
-
-       /*
-        * If the "interrupt" for this port doesn't correspond with any
-        * hardware interrupt, we use a timer-based system.  The original
-        * driver used to do this with IRQ0.
-        */
-       if (!port->irq) {
-               up->timer.data = (unsigned long)up;
-               mod_timer(&up->timer, jiffies + uart_poll_timeout(port));
-       } else {
-               retval = serial_link_irq_chain(up);
-               if (retval)
-                       goto out;
-       }
+       retval = up->ops->setup_irq(up);
+       if (retval)
+               goto out;
 
        /*
         * Now, initialize the UART
@@ -2270,7 +2272,7 @@ int serial8250_do_startup(struct uart_port *port)
           is variable. So, let's just don't test if we receive
           TX irq. This way, we'll never enable UART_BUG_TXEN.
         */
-       if (skip_txen_test || up->port.flags & UPF_NO_TXEN_TEST)
+       if (up->port.flags & UPF_NO_TXEN_TEST)
                goto dont_test_tx_en;
 
        /*
@@ -2300,8 +2302,8 @@ dont_test_tx_en:
         * saved flags to avoid getting false values from polling
         * routines or the previous session.
         */
-       if (serial_port_in(port, UART_LSR) & UART_LSR_DR)
-               serial_port_in(port, UART_RX);
+       serial_port_in(port, UART_LSR);
+       serial_port_in(port, UART_RX);
        serial_port_in(port, UART_IIR);
        serial_port_in(port, UART_MSR);
        up->lsr_saved_flags = 0;
@@ -2394,14 +2396,10 @@ void serial8250_do_shutdown(struct uart_port *port)
         * Read data port to reset things, and then unlink from
         * the IRQ chain.
         */
-       if (serial_port_in(port, UART_LSR) & UART_LSR_DR)
-               serial_port_in(port, UART_RX);
+       serial_port_in(port, UART_RX);
        serial8250_rpm_put(up);
 
-       del_timer_sync(&up->timer);
-       up->timer.function = serial8250_timeout;
-       if (port->irq)
-               serial_unlink_irq_chain(up);
+       up->ops->release_irq(up);
 }
 EXPORT_SYMBOL_GPL(serial8250_do_shutdown);
 
@@ -3101,6 +3099,11 @@ static struct uart_ops serial8250_pops = {
 #endif
 };
 
+static const struct uart_8250_ops univ8250_driver_ops = {
+       .setup_irq      = univ8250_setup_irq,
+       .release_irq    = univ8250_release_irq,
+};
+
 static struct uart_8250_port serial8250_ports[UART_NR];
 
 /**
@@ -3131,6 +3134,23 @@ void serial8250_set_isa_configurator(
 }
 EXPORT_SYMBOL(serial8250_set_isa_configurator);
 
+static void serial8250_init_port(struct uart_8250_port *up)
+{
+       struct uart_port *port = &up->port;
+
+       spin_lock_init(&port->lock);
+       port->ops = &serial8250_pops;
+
+       up->cur_iotype = 0xFF;
+}
+
+static void serial8250_set_defaults(struct uart_8250_port *up)
+{
+       struct uart_port *port = &up->port;
+
+       set_io_from_upio(port);
+}
+
 static void __init serial8250_isa_init_ports(void)
 {
        struct uart_8250_port *up;
@@ -3149,19 +3169,18 @@ static void __init serial8250_isa_init_ports(void)
                struct uart_port *port = &up->port;
 
                port->line = i;
-               spin_lock_init(&port->lock);
+               serial8250_init_port(up);
 
                init_timer(&up->timer);
                up->timer.function = serial8250_timeout;
-               up->cur_iotype = 0xFF;
+
+               up->ops = &univ8250_driver_ops;
 
                /*
                 * ALPHA_KLUDGE_MCR needs to be killed.
                 */
                up->mcr_mask = ~ALPHA_KLUDGE_MCR;
                up->mcr_force = ALPHA_KLUDGE_MCR;
-
-               port->ops = &serial8250_pops;
        }
 
        if (share_irqs)
@@ -3181,11 +3200,11 @@ static void __init serial8250_isa_init_ports(void)
                port->membase  = old_serial_port[i].iomem_base;
                port->iotype   = old_serial_port[i].io_type;
                port->regshift = old_serial_port[i].iomem_reg_shift;
-               set_io_from_upio(port);
+               serial8250_set_defaults(up);
+
                port->irqflags |= irqflag;
                if (serial8250_isa_config != NULL)
                        serial8250_isa_config(i, &up->port, &up->capabilities);
-
        }
 }
 
@@ -3214,6 +3233,9 @@ serial8250_register_ports(struct uart_driver *drv, struct device *dev)
 
                up->port.dev = dev;
 
+               if (skip_txen_test)
+                       up->port.flags |= UPF_NO_TXEN_TEST;
+
                if (up->port.flags & UPF_FIXED_TYPE)
                        serial8250_init_fixed_type_port(up, up->port.type);
 
@@ -3237,10 +3259,9 @@ static void serial8250_console_putchar(struct uart_port *port, int ch)
  *
  *     The console_lock must be held when we get here.
  */
-static void
-serial8250_console_write(struct console *co, const char *s, unsigned int count)
+static void serial8250_console_write(struct uart_8250_port *up, const char *s,
+                                    unsigned int count)
 {
-       struct uart_8250_port *up = &serial8250_ports[co->index];
        struct uart_port *port = &up->port;
        unsigned long flags;
        unsigned int ier;
@@ -3312,14 +3333,35 @@ serial8250_console_write(struct console *co, const char *s, unsigned int count)
        serial8250_rpm_put(up);
 }
 
-static int serial8250_console_setup(struct console *co, char *options)
+static void univ8250_console_write(struct console *co, const char *s,
+                                  unsigned int count)
 {
-       struct uart_port *port;
+       struct uart_8250_port *up = &serial8250_ports[co->index];
+
+       serial8250_console_write(up, s, count);
+}
+
+static int serial8250_console_setup(struct uart_8250_port *up, char *options)
+{
+       struct uart_port *port = &up->port;
        int baud = 9600;
        int bits = 8;
        int parity = 'n';
        int flow = 'n';
 
+       if (!port->iobase && !port->membase)
+               return -ENODEV;
+
+       if (options)
+               uart_parse_options(options, &baud, &parity, &bits, &flow);
+
+       return uart_set_options(port, port->cons, baud, parity, bits, flow);
+}
+
+static int univ8250_console_setup(struct console *co, char *options)
+{
+       struct uart_8250_port *up;
+
        /*
         * Check whether an invalid uart number has been specified, and
         * if so, search for the first available port that does have
@@ -3327,54 +3369,83 @@ static int serial8250_console_setup(struct console *co, char *options)
         */
        if (co->index >= nr_uarts)
                co->index = 0;
-       port = &serial8250_ports[co->index].port;
-       if (!port->iobase && !port->membase)
-               return -ENODEV;
+       up = &serial8250_ports[co->index];
+       /* link port to console */
+       up->port.cons = co;
 
-       if (options)
-               uart_parse_options(options, &baud, &parity, &bits, &flow);
-
-       return uart_set_options(port, co, baud, parity, bits, flow);
+       return serial8250_console_setup(up, options);
 }
 
-static int serial8250_console_early_setup(void)
+/**
+ *     univ8250_console_match - non-standard console matching
+ *     @co:      registering console
+ *     @name:    name from console command line
+ *     @idx:     index from console command line
+ *     @options: ptr to option string from console command line
+ *
+ *     Only attempts to match console command lines of the form:
+ *         console=uart<>,io|mmio|mmio32,<addr>,<options>
+ *         console=uart<>,<addr>,options
+ *     This form is used to register an initial earlycon boot console and
+ *     replace it with the serial8250_console at 8250 driver init.
+ *
+ *     Performs console setup for a match (as required by interface)
+ *
+ *     Returns 0 if console matches; otherwise non-zero to use default matching
+ */
+static int univ8250_console_match(struct console *co, char *name, int idx,
+                                 char *options)
 {
-       return serial8250_find_port_for_earlycon();
+       char match[] = "uart";  /* 8250-specific earlycon name */
+       unsigned char iotype;
+       unsigned long addr;
+       int i;
+
+       if (strncmp(name, match, 4) != 0)
+               return -ENODEV;
+
+       if (uart_parse_earlycon(options, &iotype, &addr, &options))
+               return -ENODEV;
+
+       /* try to match the port specified on the command line */
+       for (i = 0; i < nr_uarts; i++) {
+               struct uart_port *port = &serial8250_ports[i].port;
+
+               if (port->iotype != iotype)
+                       continue;
+               if ((iotype == UPIO_MEM || iotype == UPIO_MEM32) &&
+                   (port->mapbase != addr))
+                       continue;
+               if (iotype == UPIO_PORT && port->iobase != addr)
+                       continue;
+
+               co->index = i;
+               return univ8250_console_setup(co, options);
+       }
+
+       return -ENODEV;
 }
 
-static struct console serial8250_console = {
+static struct console univ8250_console = {
        .name           = "ttyS",
-       .write          = serial8250_console_write,
+       .write          = univ8250_console_write,
        .device         = uart_console_device,
-       .setup          = serial8250_console_setup,
-       .early_setup    = serial8250_console_early_setup,
+       .setup          = univ8250_console_setup,
+       .match          = univ8250_console_match,
        .flags          = CON_PRINTBUFFER | CON_ANYTIME,
        .index          = -1,
        .data           = &serial8250_reg,
 };
 
-static int __init serial8250_console_init(void)
+static int __init univ8250_console_init(void)
 {
        serial8250_isa_init_ports();
-       register_console(&serial8250_console);
+       register_console(&univ8250_console);
        return 0;
 }
-console_initcall(serial8250_console_init);
-
-int serial8250_find_port(struct uart_port *p)
-{
-       int line;
-       struct uart_port *port;
-
-       for (line = 0; line < nr_uarts; line++) {
-               port = &serial8250_ports[line].port;
-               if (uart_match_port(p, port))
-                       return line;
-       }
-       return -ENODEV;
-}
+console_initcall(univ8250_console_init);
 
-#define SERIAL8250_CONSOLE     &serial8250_console
+#define SERIAL8250_CONSOLE     &univ8250_console
 #else
 #define SERIAL8250_CONSOLE     NULL
 #endif
@@ -3417,15 +3488,14 @@ int __init early_serial_setup(struct uart_port *port)
        p->type         = port->type;
        p->line         = port->line;
 
-       set_io_from_upio(p);
+       serial8250_set_defaults(up_to_u8250p(p));
+
        if (port->serial_in)
                p->serial_in = port->serial_in;
        if (port->serial_out)
                p->serial_out = port->serial_out;
        if (port->handle_irq)
                p->handle_irq = port->handle_irq;
-       else
-               p->handle_irq = serial8250_default_handle_irq;
 
        return 0;
 }
@@ -3683,10 +3753,14 @@ int serial8250_register_8250_port(struct uart_8250_port *up)
                if (up->port.dev)
                        uart->port.dev = up->port.dev;
 
+               if (skip_txen_test)
+                       uart->port.flags |= UPF_NO_TXEN_TEST;
+
                if (up->port.flags & UPF_FIXED_TYPE)
                        serial8250_init_fixed_type_port(uart, up->port.type);
 
-               set_io_from_upio(&uart->port);
+               serial8250_set_defaults(uart);
+
                /* Possibly override default I/O functions.  */
                if (up->port.serial_in)
                        uart->port.serial_in = up->port.serial_in;
@@ -3748,9 +3822,11 @@ void serial8250_unregister_port(int line)
        uart_remove_one_port(&serial8250_reg, &uart->port);
        if (serial8250_isa_devs) {
                uart->port.flags &= ~UPF_BOOT_AUTOCONF;
+               if (skip_txen_test)
+                       uart->port.flags |= UPF_NO_TXEN_TEST;
                uart->port.type = PORT_UNKNOWN;
                uart->port.dev = &serial8250_isa_devs->dev;
-               uart->capabilities = uart_config[uart->port.type].flags;
+               uart->capabilities = 0;
                uart_add_one_port(&serial8250_reg, &uart->port);
        } else {
                uart->port.dev = NULL;