Merge branch 'omap-serial' of git://git.linaro.org/people/rmk/linux-arm
authorLinus Torvalds <torvalds@linux-foundation.org>
Wed, 12 Dec 2012 15:45:16 +0000 (07:45 -0800)
committerLinus Torvalds <torvalds@linux-foundation.org>
Wed, 12 Dec 2012 15:45:16 +0000 (07:45 -0800)
Pull ARM OMAP serial updates from Russell King:
 "This series is a major reworking of the OMAP serial driver code fixing
  various bugs in the hardware-assisted flow control, extending up into
  serial_core for a couple of issues.  These fixes have been done as a
  set of progressive changes and transformations in the hope that no new
  bugs will be introduced by this series.

  The problems are many-fold, from the driver not being informed about
  updated settings, to the driver not knowing what the intentions of the
  upper layers are.

  The first four patches tackle the serial_core layer, allowing it to
  provide the necessary information to drivers, and the remaining
  patches allow the OMAP serial driver to take advantage of this.

  This brings hardware assisted RTS/CTS and XON/OFF flow control into a
  useful state.

  These patches have been in linux-next for most of the last cycle;
  indeed they predate the previous merge window.  They've also been
  posted to the OMAP people."

* 'omap-serial' of git://git.linaro.org/people/rmk/linux-arm: (21 commits)
  SERIAL: omap: fix hardware assisted flow control
  SERIAL: omap: simplify (2)
  SERIAL: omap: move xon/xoff setting earlier
  SERIAL: omap: always set TCR
  SERIAL: omap: simplify
  SERIAL: omap: don't read back LCR/MCR/EFR
  SERIAL: omap: serial_omap_configure_xonxoff() contents into set_termios
  SERIAL: omap: configure xon/xoff before setting modem control lines
  SERIAL: omap: remove OMAP_UART_SYSC_RESET and OMAP_UART_FIFO_CLR
  SERIAL: omap: move driver private definitions and structures to driver
  SERIAL: omap: remove 'irq_pending' bitfield
  SERIAL: omap: fix MCR TCRTLR bit handling
  SERIAL: omap: fix set_mctrl() breakage
  SERIAL: omap: no need to re-read EFR
  SERIAL: omap: remove setting of EFR SCD bit
  SERIAL: omap: allow hardware assisted IXANY mode to be disabled
  SERIAL: omap: allow hardware assisted rts/cts modes to be disabled
  SERIAL: core: add throttle/unthrottle callbacks for hardware assisted flow control
  SERIAL: core: add hardware assisted h/w flow control support
  SERIAL: core: add hardware assisted s/w flow control support
  ...

Conflicts:
drivers/tty/serial/omap-serial.c

1  2 
drivers/tty/serial/omap-serial.c
drivers/tty/serial/serial_core.c

@@@ -44,6 -44,8 +44,8 @@@
  
  #include <plat/omap-serial.h>
  
+ #define OMAP_MAX_HSUART_PORTS 6
  #define UART_BUILD_REVISION(x, y)     (((x) << 8) | (y))
  
  #define OMAP_UART_REV_42 0x0402
  #define OMAP_UART_REV_52 0x0502
  #define OMAP_UART_REV_63 0x0603
  
+ #define UART_ERRATA_i202_MDR1_ACCESS  BIT(0)
+ #define UART_ERRATA_i291_DMA_FORCEIDLE        BIT(1)
  #define DEFAULT_CLK_SPEED 48000000 /* 48Mhz*/
  
  /* SCR register bitmasks */
  #define OMAP_UART_SCR_RX_TRIG_GRANU1_MASK             (1 << 7)
+ #define OMAP_UART_SCR_TX_EMPTY                        (1 << 3)
  
  /* FCR register bitmasks */
  #define OMAP_UART_FCR_RX_FIFO_TRIG_MASK                       (0x3 << 6)
  #define OMAP_UART_MVR_MAJ_SHIFT               8
  #define OMAP_UART_MVR_MIN_MASK                0x3f
  
+ #define OMAP_UART_DMA_CH_FREE -1
+ #define MSR_SAVE_FLAGS                UART_MSR_ANY_DELTA
+ #define OMAP_MODE13X_SPEED    230400
+ /* WER = 0x7F
+  * Enable module level wakeup in WER reg
+  */
+ #define OMAP_UART_WER_MOD_WKUP        0X7F
+ /* Enable XON/XOFF flow control on output */
+ #define OMAP_UART_SW_TX               0x08
+ /* Enable XON/XOFF flow control on input */
+ #define OMAP_UART_SW_RX               0x02
+ #define OMAP_UART_SW_CLR      0xF0
+ #define OMAP_UART_TCR_TRIG    0x0F
+ struct uart_omap_dma {
+       u8                      uart_dma_tx;
+       u8                      uart_dma_rx;
+       int                     rx_dma_channel;
+       int                     tx_dma_channel;
+       dma_addr_t              rx_buf_dma_phys;
+       dma_addr_t              tx_buf_dma_phys;
+       unsigned int            uart_base;
+       /*
+        * Buffer for rx dma.It is not required for tx because the buffer
+        * comes from port structure.
+        */
+       unsigned char           *rx_buf;
+       unsigned int            prev_rx_dma_pos;
+       int                     tx_buf_size;
+       int                     tx_dma_used;
+       int                     rx_dma_used;
+       spinlock_t              tx_lock;
+       spinlock_t              rx_lock;
+       /* timer to poll activity on rx dma */
+       struct timer_list       rx_timer;
+       unsigned int            rx_buf_size;
+       unsigned int            rx_poll_rate;
+       unsigned int            rx_timeout;
+ };
  struct uart_omap_port {
        struct uart_port        port;
        struct uart_omap_dma    uart_dma;
        unsigned char           msr_saved_flags;
        char                    name[20];
        unsigned long           port_activity;
 -      u32                     context_loss_cnt;
 +      int                     context_loss_cnt;
        u32                     errata;
        u8                      wakeups_enabled;
-       unsigned int            irq_pending:1;
  
        int                     DTR_gpio;
        int                     DTR_inverted;
@@@ -303,6 -354,34 +354,34 @@@ static void serial_omap_start_tx(struc
        pm_runtime_put_autosuspend(up->dev);
  }
  
+ static void serial_omap_throttle(struct uart_port *port)
+ {
+       struct uart_omap_port *up = to_uart_omap_port(port);
+       unsigned long flags;
+       pm_runtime_get_sync(up->dev);
+       spin_lock_irqsave(&up->port.lock, flags);
+       up->ier &= ~(UART_IER_RLSI | UART_IER_RDI);
+       serial_out(up, UART_IER, up->ier);
+       spin_unlock_irqrestore(&up->port.lock, flags);
+       pm_runtime_mark_last_busy(up->dev);
+       pm_runtime_put_autosuspend(up->dev);
+ }
+ static void serial_omap_unthrottle(struct uart_port *port)
+ {
+       struct uart_omap_port *up = to_uart_omap_port(port);
+       unsigned long flags;
+       pm_runtime_get_sync(up->dev);
+       spin_lock_irqsave(&up->port.lock, flags);
+       up->ier |= UART_IER_RLSI | UART_IER_RDI;
+       serial_out(up, UART_IER, up->ier);
+       spin_unlock_irqrestore(&up->port.lock, flags);
+       pm_runtime_mark_last_busy(up->dev);
+       pm_runtime_put_autosuspend(up->dev);
+ }
  static unsigned int check_modem_status(struct uart_omap_port *up)
  {
        unsigned int status;
@@@ -504,7 -583,7 +583,7 @@@ static unsigned int serial_omap_get_mct
  static void serial_omap_set_mctrl(struct uart_port *port, unsigned int mctrl)
  {
        struct uart_omap_port *up = to_uart_omap_port(port);
-       unsigned char mcr = 0;
+       unsigned char mcr = 0, old_mcr;
  
        dev_dbg(up->port.dev, "serial_omap_set_mctrl+%d\n", up->port.line);
        if (mctrl & TIOCM_RTS)
                mcr |= UART_MCR_LOOP;
  
        pm_runtime_get_sync(up->dev);
-       up->mcr = serial_in(up, UART_MCR);
-       up->mcr |= mcr;
+       old_mcr = serial_in(up, UART_MCR);
+       old_mcr &= ~(UART_MCR_LOOP | UART_MCR_OUT2 | UART_MCR_OUT1 |
+                    UART_MCR_DTR | UART_MCR_RTS);
+       up->mcr = old_mcr | mcr;
        serial_out(up, UART_MCR, up->mcr);
        pm_runtime_mark_last_busy(up->dev);
        pm_runtime_put_autosuspend(up->dev);
@@@ -654,61 -735,6 +735,6 @@@ static void serial_omap_shutdown(struc
        free_irq(up->port.irq, up);
  }
  
- static inline void
- serial_omap_configure_xonxoff
-               (struct uart_omap_port *up, struct ktermios *termios)
- {
-       up->lcr = serial_in(up, UART_LCR);
-       serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B);
-       up->efr = serial_in(up, UART_EFR);
-       serial_out(up, UART_EFR, up->efr & ~UART_EFR_ECB);
-       serial_out(up, UART_XON1, termios->c_cc[VSTART]);
-       serial_out(up, UART_XOFF1, termios->c_cc[VSTOP]);
-       /* clear SW control mode bits */
-       up->efr &= OMAP_UART_SW_CLR;
-       /*
-        * IXON Flag:
-        * Enable XON/XOFF flow control on output.
-        * Transmit XON1, XOFF1
-        */
-       if (termios->c_iflag & IXON)
-               up->efr |= OMAP_UART_SW_TX;
-       /*
-        * IXOFF Flag:
-        * Enable XON/XOFF flow control on input.
-        * Receiver compares XON1, XOFF1.
-        */
-       if (termios->c_iflag & IXOFF)
-               up->efr |= OMAP_UART_SW_RX;
-       serial_out(up, UART_EFR, up->efr | UART_EFR_ECB);
-       serial_out(up, UART_LCR, UART_LCR_CONF_MODE_A);
-       up->mcr = serial_in(up, UART_MCR);
-       /*
-        * IXANY Flag:
-        * Enable any character to restart output.
-        * Operation resumes after receiving any
-        * character after recognition of the XOFF character
-        */
-       if (termios->c_iflag & IXANY)
-               up->mcr |= UART_MCR_XONANY;
-       serial_out(up, UART_MCR, up->mcr | UART_MCR_TCRTLR);
-       serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B);
-       serial_out(up, UART_TI752_TCR, OMAP_UART_TCR_TRIG);
-       serial_out(up, UART_LCR, UART_LCR_CONF_MODE_A);
-       serial_out(up, UART_MCR, up->mcr & ~UART_MCR_TCRTLR);
-       serial_out(up, UART_LCR, up->lcr);
- }
  static void serial_omap_uart_qos_work(struct work_struct *work)
  {
        struct uart_omap_port *up = container_of(work, struct uart_omap_port,
@@@ -726,7 -752,6 +752,6 @@@ serial_omap_set_termios(struct uart_por
  {
        struct uart_omap_port *up = to_uart_omap_port(port);
        unsigned char cval = 0;
-       unsigned char efr = 0;
        unsigned long flags = 0;
        unsigned int baud, quot;
  
  
        serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B);
  
-       up->efr = serial_in(up, UART_EFR);
+       up->efr = serial_in(up, UART_EFR) & ~UART_EFR_ECB;
+       up->efr &= ~UART_EFR_SCD;
        serial_out(up, UART_EFR, up->efr | UART_EFR_ECB);
  
        serial_out(up, UART_LCR, UART_LCR_CONF_MODE_A);
-       up->mcr = serial_in(up, UART_MCR);
+       up->mcr = serial_in(up, UART_MCR) & ~UART_MCR_TCRTLR;
        serial_out(up, UART_MCR, up->mcr | UART_MCR_TCRTLR);
        /* FIFO ENABLE, DMA MODE */
  
  
        serial_out(up, UART_OMAP_SCR, up->scr);
  
-       serial_out(up, UART_EFR, up->efr);
+       /* Reset UART_MCR_TCRTLR: this must be done with the EFR_ECB bit set */
        serial_out(up, UART_LCR, UART_LCR_CONF_MODE_A);
        serial_out(up, UART_MCR, up->mcr);
+       serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B);
+       serial_out(up, UART_EFR, up->efr);
+       serial_out(up, UART_LCR, UART_LCR_CONF_MODE_A);
  
        /* Protocol, Baud Rate, and Interrupt Settings */
  
                serial_out(up, UART_OMAP_MDR1, up->mdr1);
  
        serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B);
-       up->efr = serial_in(up, UART_EFR);
        serial_out(up, UART_EFR, up->efr | UART_EFR_ECB);
  
        serial_out(up, UART_LCR, 0);
        else
                serial_out(up, UART_OMAP_MDR1, up->mdr1);
  
-       /* Hardware Flow Control Configuration */
+       /* Configure flow control */
+       serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B);
+       /* XON1/XOFF1 accessible mode B, TCRTLR=0, ECB=0 */
+       serial_out(up, UART_XON1, termios->c_cc[VSTART]);
+       serial_out(up, UART_XOFF1, termios->c_cc[VSTOP]);
+       /* Enable access to TCR/TLR */
+       serial_out(up, UART_EFR, up->efr | UART_EFR_ECB);
+       serial_out(up, UART_LCR, UART_LCR_CONF_MODE_A);
+       serial_out(up, UART_MCR, up->mcr | UART_MCR_TCRTLR);
+       serial_out(up, UART_TI752_TCR, OMAP_UART_TCR_TRIG);
+       if (termios->c_cflag & CRTSCTS && up->port.flags & UPF_HARD_FLOW) {
+               /* Enable AUTORTS and AUTOCTS */
+               up->efr |= UART_EFR_CTS | UART_EFR_RTS;
+               /* Ensure MCR RTS is asserted */
+               up->mcr |= UART_MCR_RTS;
+       } else {
+               /* Disable AUTORTS and AUTOCTS */
+               up->efr &= ~(UART_EFR_CTS | UART_EFR_RTS);
+       }
  
-       if (termios->c_cflag & CRTSCTS) {
-               efr |= (UART_EFR_CTS | UART_EFR_RTS);
-               serial_out(up, UART_LCR, UART_LCR_CONF_MODE_A);
+       if (up->port.flags & UPF_SOFT_FLOW) {
+               /* clear SW control mode bits */
+               up->efr &= OMAP_UART_SW_CLR;
  
-               up->mcr = serial_in(up, UART_MCR);
-               serial_out(up, UART_MCR, up->mcr | UART_MCR_TCRTLR);
+               /*
+                * IXON Flag:
+                * Enable XON/XOFF flow control on input.
+                * Receiver compares XON1, XOFF1.
+                */
+               if (termios->c_iflag & IXON)
+                       up->efr |= OMAP_UART_SW_RX;
  
-               serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B);
-               up->efr = serial_in(up, UART_EFR);
-               serial_out(up, UART_EFR, up->efr | UART_EFR_ECB);
+               /*
+                * IXOFF Flag:
+                * Enable XON/XOFF flow control on output.
+                * Transmit XON1, XOFF1
+                */
+               if (termios->c_iflag & IXOFF)
+                       up->efr |= OMAP_UART_SW_TX;
  
-               serial_out(up, UART_TI752_TCR, OMAP_UART_TCR_TRIG);
-               serial_out(up, UART_EFR, efr); /* Enable AUTORTS and AUTOCTS */
-               serial_out(up, UART_LCR, UART_LCR_CONF_MODE_A);
-               serial_out(up, UART_MCR, up->mcr | UART_MCR_RTS);
-               serial_out(up, UART_LCR, cval);
+               /*
+                * IXANY Flag:
+                * Enable any character to restart output.
+                * Operation resumes after receiving any
+                * character after recognition of the XOFF character
+                */
+               if (termios->c_iflag & IXANY)
+                       up->mcr |= UART_MCR_XONANY;
+               else
+                       up->mcr &= ~UART_MCR_XONANY;
        }
+       serial_out(up, UART_MCR, up->mcr);
+       serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B);
+       serial_out(up, UART_EFR, up->efr);
+       serial_out(up, UART_LCR, up->lcr);
  
        serial_omap_set_mctrl(&up->port, up->port.mctrl);
-       /* Software Flow Control Configuration */
-       serial_omap_configure_xonxoff(up, termios);
  
        spin_unlock_irqrestore(&up->port.lock, flags);
        pm_runtime_mark_last_busy(up->dev);
@@@ -987,6 -1053,7 +1053,7 @@@ static void serial_omap_config_port(str
        dev_dbg(up->port.dev, "serial_omap_config_port+%d\n",
                                                        up->port.line);
        up->port.type = PORT_OMAP;
+       up->port.flags |= UPF_SOFT_FLOW | UPF_HARD_FLOW;
  }
  
  static int
@@@ -1077,7 -1144,7 +1144,7 @@@ out
  
  #ifdef CONFIG_SERIAL_OMAP_CONSOLE
  
 -static struct uart_omap_port *serial_omap_console_ports[4];
 +static struct uart_omap_port *serial_omap_console_ports[OMAP_MAX_HSUART_PORTS];
  
  static struct uart_driver serial_omap_reg;
  
@@@ -1190,6 -1257,8 +1257,8 @@@ static struct uart_ops serial_omap_pop
        .get_mctrl      = serial_omap_get_mctrl,
        .stop_tx        = serial_omap_stop_tx,
        .start_tx       = serial_omap_start_tx,
+       .throttle       = serial_omap_throttle,
+       .unthrottle     = serial_omap_unthrottle,
        .stop_rx        = serial_omap_stop_rx,
        .enable_ms      = serial_omap_enable_ms,
        .break_ctl      = serial_omap_break_ctl,
@@@ -1238,7 -1307,7 +1307,7 @@@ static int serial_omap_resume(struct de
  }
  #endif
  
 -static void __devinit omap_serial_fill_features_erratas(struct uart_omap_port *up)
 +static void omap_serial_fill_features_erratas(struct uart_omap_port *up)
  {
        u32 mvr, scheme;
        u16 revision, major, minor;
        }
  }
  
 -static __devinit struct omap_uart_port_info *of_get_uart_port_info(struct device *dev)
 +static struct omap_uart_port_info *of_get_uart_port_info(struct device *dev)
  {
        struct omap_uart_port_info *omap_up_info;
  
        return omap_up_info;
  }
  
 -static int __devinit serial_omap_probe(struct platform_device *pdev)
 +static int serial_omap_probe(struct platform_device *pdev)
  {
        struct uart_omap_port   *up;
        struct resource         *mem, *irq;
@@@ -1441,7 -1510,7 +1510,7 @@@ err_port_line
        return ret;
  }
  
 -static int __devexit serial_omap_remove(struct platform_device *dev)
 +static int serial_omap_remove(struct platform_device *dev)
  {
        struct uart_omap_port *up = platform_get_drvdata(dev);
  
@@@ -1552,15 -1621,11 +1621,15 @@@ static int serial_omap_runtime_resume(s
  {
        struct uart_omap_port *up = dev_get_drvdata(dev);
  
 -      u32 loss_cnt = serial_omap_get_context_loss_count(up);
 +      int loss_cnt = serial_omap_get_context_loss_count(up);
  
 -      if (up->context_loss_cnt != loss_cnt)
 +      if (loss_cnt < 0) {
 +              dev_err(dev, "serial_omap_get_context_loss_count failed : %d\n",
 +                      loss_cnt);
                serial_omap_restore_context(up);
 -
 +      } else if (up->context_loss_cnt != loss_cnt) {
 +              serial_omap_restore_context(up);
 +      }
        up->latency = up->calc_latency;
        schedule_work(&up->qos_work);
  
@@@ -1586,7 -1651,7 +1655,7 @@@ MODULE_DEVICE_TABLE(of, omap_serial_of_
  
  static struct platform_driver serial_omap_driver = {
        .probe          = serial_omap_probe,
 -      .remove         = __devexit_p(serial_omap_remove),
 +      .remove         = serial_omap_remove,
        .driver         = {
                .name   = DRIVER_NAME,
                .pm     = &serial_omap_dev_pm_ops,
@@@ -610,34 -610,57 +610,57 @@@ static void uart_send_xchar(struct tty_
  static void uart_throttle(struct tty_struct *tty)
  {
        struct uart_state *state = tty->driver_data;
+       struct uart_port *port = state->uart_port;
+       uint32_t mask = 0;
  
        if (I_IXOFF(tty))
+               mask |= UPF_SOFT_FLOW;
+       if (tty->termios.c_cflag & CRTSCTS)
+               mask |= UPF_HARD_FLOW;
+       if (port->flags & mask) {
+               port->ops->throttle(port);
+               mask &= ~port->flags;
+       }
+       if (mask & UPF_SOFT_FLOW)
                uart_send_xchar(tty, STOP_CHAR(tty));
  
-       if (tty->termios.c_cflag & CRTSCTS)
-               uart_clear_mctrl(state->uart_port, TIOCM_RTS);
+       if (mask & UPF_HARD_FLOW)
+               uart_clear_mctrl(port, TIOCM_RTS);
  }
  
  static void uart_unthrottle(struct tty_struct *tty)
  {
        struct uart_state *state = tty->driver_data;
        struct uart_port *port = state->uart_port;
+       uint32_t mask = 0;
+       if (I_IXOFF(tty))
+               mask |= UPF_SOFT_FLOW;
+       if (tty->termios.c_cflag & CRTSCTS)
+               mask |= UPF_HARD_FLOW;
+       if (port->flags & mask) {
+               port->ops->unthrottle(port);
+               mask &= ~port->flags;
+       }
  
-       if (I_IXOFF(tty)) {
+       if (mask & UPF_SOFT_FLOW) {
                if (port->x_char)
                        port->x_char = 0;
                else
                        uart_send_xchar(tty, START_CHAR(tty));
        }
  
-       if (tty->termios.c_cflag & CRTSCTS)
+       if (mask & UPF_HARD_FLOW)
                uart_set_mctrl(port, TIOCM_RTS);
  }
  
 -static void uart_get_info(struct tty_port *port,
 -                        struct uart_state *state,
 +static void do_uart_get_info(struct tty_port *port,
                        struct serial_struct *retinfo)
  {
 +      struct uart_state *state = container_of(port, struct uart_state, port);
        struct uart_port *uport = state->uart_port;
  
        memset(retinfo, 0, sizeof(*retinfo));
        retinfo->iomem_base      = (void *)(unsigned long)uport->mapbase;
  }
  
 -static int uart_get_info_user(struct uart_state *state,
 -                       struct serial_struct __user *retinfo)
 +static void uart_get_info(struct tty_port *port,
 +                      struct serial_struct *retinfo)
  {
 -      struct tty_port *port = &state->port;
 -      struct serial_struct tmp;
 -
        /* Ensure the state we copy is consistent and no hardware changes
           occur as we go */
        mutex_lock(&port->mutex);
 -      uart_get_info(port, state, &tmp);
 +      do_uart_get_info(port, retinfo);
        mutex_unlock(&port->mutex);
 +}
 +
 +static int uart_get_info_user(struct tty_port *port,
 +                       struct serial_struct __user *retinfo)
 +{
 +      struct serial_struct tmp;
 +      uart_get_info(port, &tmp);
  
        if (copy_to_user(retinfo, &tmp, sizeof(*retinfo)))
                return -EFAULT;
@@@ -1135,7 -1154,7 +1158,7 @@@ uart_ioctl(struct tty_struct *tty, unsi
         */
        switch (cmd) {
        case TIOCGSERIAL:
 -              ret = uart_get_info_user(state, uarg);
 +              ret = uart_get_info_user(port, uarg);
                break;
  
        case TIOCSSERIAL:
@@@ -1214,9 -1233,22 +1237,22 @@@ static void uart_set_termios(struct tty
                                                struct ktermios *old_termios)
  {
        struct uart_state *state = tty->driver_data;
+       struct uart_port *uport = state->uart_port;
        unsigned long flags;
        unsigned int cflag = tty->termios.c_cflag;
+       unsigned int iflag_mask = IGNBRK|BRKINT|IGNPAR|PARMRK|INPCK;
+       bool sw_changed = false;
  
+       /*
+        * Drivers doing software flow control also need to know
+        * about changes to these input settings.
+        */
+       if (uport->flags & UPF_SOFT_FLOW) {
+               iflag_mask |= IXANY|IXON|IXOFF;
+               sw_changed =
+                  tty->termios.c_cc[VSTART] != old_termios->c_cc[VSTART] ||
+                  tty->termios.c_cc[VSTOP] != old_termios->c_cc[VSTOP];
+       }
  
        /*
         * These are the bits that are used to setup various
         * bits in c_cflag; c_[io]speed will always be set
         * appropriately by set_termios() in tty_ioctl.c
         */
- #define RELEVANT_IFLAG(iflag) ((iflag) & (IGNBRK|BRKINT|IGNPAR|PARMRK|INPCK))
        if ((cflag ^ old_termios->c_cflag) == 0 &&
            tty->termios.c_ospeed == old_termios->c_ospeed &&
            tty->termios.c_ispeed == old_termios->c_ispeed &&
-           RELEVANT_IFLAG(tty->termios.c_iflag ^ old_termios->c_iflag) == 0) {
+           ((tty->termios.c_iflag ^ old_termios->c_iflag) & iflag_mask) == 0 &&
+           !sw_changed) {
                return;
        }
  
  
        /* Handle transition to B0 status */
        if ((old_termios->c_cflag & CBAUD) && !(cflag & CBAUD))
-               uart_clear_mctrl(state->uart_port, TIOCM_RTS | TIOCM_DTR);
+               uart_clear_mctrl(uport, TIOCM_RTS | TIOCM_DTR);
        /* Handle transition away from B0 status */
        else if (!(old_termios->c_cflag & CBAUD) && (cflag & CBAUD)) {
                unsigned int mask = TIOCM_DTR;
                if (!(cflag & CRTSCTS) ||
                    !test_bit(TTY_THROTTLED, &tty->flags))
                        mask |= TIOCM_RTS;
-               uart_set_mctrl(state->uart_port, mask);
+               uart_set_mctrl(uport, mask);
        }
  
+       /*
+        * If the port is doing h/w assisted flow control, do nothing.
+        * We assume that tty->hw_stopped has never been set.
+        */
+       if (uport->flags & UPF_HARD_FLOW)
+               return;
        /* Handle turning off CRTSCTS */
        if ((old_termios->c_cflag & CRTSCTS) && !(cflag & CRTSCTS)) {
-               spin_lock_irqsave(&state->uart_port->lock, flags);
+               spin_lock_irqsave(&uport->lock, flags);
                tty->hw_stopped = 0;
                __uart_start(tty);
-               spin_unlock_irqrestore(&state->uart_port->lock, flags);
+               spin_unlock_irqrestore(&uport->lock, flags);
        }
        /* Handle turning on CRTSCTS */
        else if (!(old_termios->c_cflag & CRTSCTS) && (cflag & CRTSCTS)) {
-               spin_lock_irqsave(&state->uart_port->lock, flags);
-               if (!(state->uart_port->ops->get_mctrl(state->uart_port) & TIOCM_CTS)) {
+               spin_lock_irqsave(&uport->lock, flags);
+               if (!(uport->ops->get_mctrl(uport) & TIOCM_CTS)) {
                        tty->hw_stopped = 1;
-                       state->uart_port->ops->stop_tx(state->uart_port);
+                       uport->ops->stop_tx(uport);
                }
-               spin_unlock_irqrestore(&state->uart_port->lock, flags);
+               spin_unlock_irqrestore(&uport->lock, flags);
        }
  }
  
@@@ -2297,8 -2336,6 +2340,8 @@@ int uart_register_driver(struct uart_dr
        if (retval >= 0)
                return retval;
  
 +      for (i = 0; i < drv->nr; i++)
 +              tty_port_destroy(&drv->state[i].port);
        put_tty_driver(normal);
  out_kfree:
        kfree(drv->state);
  void uart_unregister_driver(struct uart_driver *drv)
  {
        struct tty_driver *p = drv->tty_driver;
 +      unsigned int i;
 +
        tty_unregister_driver(p);
        put_tty_driver(p);
 +      for (i = 0; i < drv->nr; i++)
 +              tty_port_destroy(&drv->state[i].port);
        kfree(drv->state);
        drv->state = NULL;
        drv->tty_driver = NULL;
@@@ -2339,166 -2372,21 +2382,166 @@@ struct tty_driver *uart_console_device(
  static ssize_t uart_get_attr_uartclk(struct device *dev,
        struct device_attribute *attr, char *buf)
  {
 -      int ret;
 +      struct serial_struct tmp;
        struct tty_port *port = dev_get_drvdata(dev);
 -      struct uart_state *state = container_of(port, struct uart_state, port);
  
 -      mutex_lock(&state->port.mutex);
 -      ret = snprintf(buf, PAGE_SIZE, "%d\n", state->uart_port->uartclk);
 -      mutex_unlock(&state->port.mutex);
 +      uart_get_info(port, &tmp);
 +      return snprintf(buf, PAGE_SIZE, "%d\n", tmp.baud_base * 16);
 +}
  
 -      return ret;
 +static ssize_t uart_get_attr_type(struct device *dev,
 +      struct device_attribute *attr, char *buf)
 +{
 +      struct serial_struct tmp;
 +      struct tty_port *port = dev_get_drvdata(dev);
 +
 +      uart_get_info(port, &tmp);
 +      return snprintf(buf, PAGE_SIZE, "%d\n", tmp.type);
 +}
 +static ssize_t uart_get_attr_line(struct device *dev,
 +      struct device_attribute *attr, char *buf)
 +{
 +      struct serial_struct tmp;
 +      struct tty_port *port = dev_get_drvdata(dev);
 +
 +      uart_get_info(port, &tmp);
 +      return snprintf(buf, PAGE_SIZE, "%d\n", tmp.line);
 +}
 +
 +static ssize_t uart_get_attr_port(struct device *dev,
 +      struct device_attribute *attr, char *buf)
 +{
 +      struct serial_struct tmp;
 +      struct tty_port *port = dev_get_drvdata(dev);
 +      unsigned long ioaddr;
 +
 +      uart_get_info(port, &tmp);
 +      ioaddr = tmp.port;
 +      if (HIGH_BITS_OFFSET)
 +              ioaddr |= (unsigned long)tmp.port_high << HIGH_BITS_OFFSET;
 +      return snprintf(buf, PAGE_SIZE, "0x%lX\n", ioaddr);
  }
  
 +static ssize_t uart_get_attr_irq(struct device *dev,
 +      struct device_attribute *attr, char *buf)
 +{
 +      struct serial_struct tmp;
 +      struct tty_port *port = dev_get_drvdata(dev);
 +
 +      uart_get_info(port, &tmp);
 +      return snprintf(buf, PAGE_SIZE, "%d\n", tmp.irq);
 +}
 +
 +static ssize_t uart_get_attr_flags(struct device *dev,
 +      struct device_attribute *attr, char *buf)
 +{
 +      struct serial_struct tmp;
 +      struct tty_port *port = dev_get_drvdata(dev);
 +
 +      uart_get_info(port, &tmp);
 +      return snprintf(buf, PAGE_SIZE, "0x%X\n", tmp.flags);
 +}
 +
 +static ssize_t uart_get_attr_xmit_fifo_size(struct device *dev,
 +      struct device_attribute *attr, char *buf)
 +{
 +      struct serial_struct tmp;
 +      struct tty_port *port = dev_get_drvdata(dev);
 +
 +      uart_get_info(port, &tmp);
 +      return snprintf(buf, PAGE_SIZE, "%d\n", tmp.xmit_fifo_size);
 +}
 +
 +
 +static ssize_t uart_get_attr_close_delay(struct device *dev,
 +      struct device_attribute *attr, char *buf)
 +{
 +      struct serial_struct tmp;
 +      struct tty_port *port = dev_get_drvdata(dev);
 +
 +      uart_get_info(port, &tmp);
 +      return snprintf(buf, PAGE_SIZE, "%d\n", tmp.close_delay);
 +}
 +
 +
 +static ssize_t uart_get_attr_closing_wait(struct device *dev,
 +      struct device_attribute *attr, char *buf)
 +{
 +      struct serial_struct tmp;
 +      struct tty_port *port = dev_get_drvdata(dev);
 +
 +      uart_get_info(port, &tmp);
 +      return snprintf(buf, PAGE_SIZE, "%d\n", tmp.closing_wait);
 +}
 +
 +static ssize_t uart_get_attr_custom_divisor(struct device *dev,
 +      struct device_attribute *attr, char *buf)
 +{
 +      struct serial_struct tmp;
 +      struct tty_port *port = dev_get_drvdata(dev);
 +
 +      uart_get_info(port, &tmp);
 +      return snprintf(buf, PAGE_SIZE, "%d\n", tmp.custom_divisor);
 +}
 +
 +static ssize_t uart_get_attr_io_type(struct device *dev,
 +      struct device_attribute *attr, char *buf)
 +{
 +      struct serial_struct tmp;
 +      struct tty_port *port = dev_get_drvdata(dev);
 +
 +      uart_get_info(port, &tmp);
 +      return snprintf(buf, PAGE_SIZE, "%d\n", tmp.io_type);
 +}
 +
 +static ssize_t uart_get_attr_iomem_base(struct device *dev,
 +      struct device_attribute *attr, char *buf)
 +{
 +      struct serial_struct tmp;
 +      struct tty_port *port = dev_get_drvdata(dev);
 +
 +      uart_get_info(port, &tmp);
 +      return snprintf(buf, PAGE_SIZE, "0x%lX\n", (unsigned long)tmp.iomem_base);
 +}
 +
 +static ssize_t uart_get_attr_iomem_reg_shift(struct device *dev,
 +      struct device_attribute *attr, char *buf)
 +{
 +      struct serial_struct tmp;
 +      struct tty_port *port = dev_get_drvdata(dev);
 +
 +      uart_get_info(port, &tmp);
 +      return snprintf(buf, PAGE_SIZE, "%d\n", tmp.iomem_reg_shift);
 +}
 +
 +static DEVICE_ATTR(type, S_IRUSR | S_IRGRP, uart_get_attr_type, NULL);
 +static DEVICE_ATTR(line, S_IRUSR | S_IRGRP, uart_get_attr_line, NULL);
 +static DEVICE_ATTR(port, S_IRUSR | S_IRGRP, uart_get_attr_port, NULL);
 +static DEVICE_ATTR(irq, S_IRUSR | S_IRGRP, uart_get_attr_irq, NULL);
 +static DEVICE_ATTR(flags, S_IRUSR | S_IRGRP, uart_get_attr_flags, NULL);
 +static DEVICE_ATTR(xmit_fifo_size, S_IRUSR | S_IRGRP, uart_get_attr_xmit_fifo_size, NULL);
  static DEVICE_ATTR(uartclk, S_IRUSR | S_IRGRP, uart_get_attr_uartclk, NULL);
 +static DEVICE_ATTR(close_delay, S_IRUSR | S_IRGRP, uart_get_attr_close_delay, NULL);
 +static DEVICE_ATTR(closing_wait, S_IRUSR | S_IRGRP, uart_get_attr_closing_wait, NULL);
 +static DEVICE_ATTR(custom_divisor, S_IRUSR | S_IRGRP, uart_get_attr_custom_divisor, NULL);
 +static DEVICE_ATTR(io_type, S_IRUSR | S_IRGRP, uart_get_attr_io_type, NULL);
 +static DEVICE_ATTR(iomem_base, S_IRUSR | S_IRGRP, uart_get_attr_iomem_base, NULL);
 +static DEVICE_ATTR(iomem_reg_shift, S_IRUSR | S_IRGRP, uart_get_attr_iomem_reg_shift, NULL);
  
  static struct attribute *tty_dev_attrs[] = {
 +      &dev_attr_type.attr,
 +      &dev_attr_line.attr,
 +      &dev_attr_port.attr,
 +      &dev_attr_irq.attr,
 +      &dev_attr_flags.attr,
 +      &dev_attr_xmit_fifo_size.attr,
        &dev_attr_uartclk.attr,
 +      &dev_attr_close_delay.attr,
 +      &dev_attr_closing_wait.attr,
 +      &dev_attr_custom_divisor.attr,
 +      &dev_attr_io_type.attr,
 +      &dev_attr_iomem_base.attr,
 +      &dev_attr_iomem_reg_shift.attr,
        NULL,
        };
  
@@@ -2511,7 -2399,6 +2554,7 @@@ static const struct attribute_group *tt
        NULL
        };
  
 +
  /**
   *    uart_add_one_port - attach a driver-defined port structure
   *    @drv: pointer to the uart low level driver structure for this port