static void mn10300_serial_dis_tx_intr(struct mn10300_serial_port *port)
 {
-       unsigned long flags;
+       int retries = 100;
        u16 x;
 
-       flags = arch_local_cli_save();
-       *port->tx_icr = NUM2GxICR_LEVEL(CONFIG_MN10300_SERIAL_IRQ_LEVEL);
-       x = *port->tx_icr;
-       arch_local_irq_restore(flags);
+       /* nothing to do if irq isn't set up */
+       if (!mn10300_serial_int_tbl[port->tx_irq].port)
+               return;
+
+       port->tx_flags |= MNSCx_TX_STOP;
+       mb();
+
+       /*
+        * Here we wait for the irq to be disabled. Either it already is
+        * disabled or we wait some number of retries for the VDMA handler
+        * to disable it. The retries give the VDMA handler enough time to
+        * run to completion if it was already in progress. If the VDMA IRQ
+        * is enabled but the handler is not yet running when arrive here,
+        * the STOP flag will prevent the handler from conflicting with the
+        * driver code following this loop.
+        */
+       while ((*port->tx_icr & GxICR_ENABLE) && retries-- > 0)
+               ;
+       if (retries <= 0) {
+               *port->tx_icr =
+                       NUM2GxICR_LEVEL(CONFIG_MN10300_SERIAL_IRQ_LEVEL);
+               x = *port->tx_icr;
+       }
 }
 
 static void mn10300_serial_en_tx_intr(struct mn10300_serial_port *port)
 {
-       unsigned long flags;
        u16 x;
 
-       flags = arch_local_cli_save();
+       /* nothing to do if irq isn't set up */
+       if (!mn10300_serial_int_tbl[port->tx_irq].port)
+               return;
+
+       /* stop vdma irq if not already stopped */
+       if (!(port->tx_flags & MNSCx_TX_STOP))
+               mn10300_serial_dis_tx_intr(port);
+
+       port->tx_flags &= ~MNSCx_TX_STOP;
+       mb();
+
        *port->tx_icr =
-               NUM2GxICR_LEVEL(CONFIG_MN10300_SERIAL_IRQ_LEVEL) | GxICR_ENABLE;
+               NUM2GxICR_LEVEL(CONFIG_MN10300_SERIAL_IRQ_LEVEL) |
+               GxICR_ENABLE | GxICR_REQUEST | GxICR_DETECT;
        x = *port->tx_icr;
-       arch_local_irq_restore(flags);
 }
 
 static void mn10300_serial_dis_rx_intr(struct mn10300_serial_port *port)
        struct mn10300_serial_port *port =
                container_of(_port, struct mn10300_serial_port, uart);
 
-       u16 x;
-
        _enter("%s{%lu}",
               port->name,
               CIRC_CNT(&port->uart.state->xmit.head,
                        UART_XMIT_SIZE));
 
        /* kick the virtual DMA controller */
-       arch_local_cli();
-       x = *port->tx_icr;
-       x |= GxICR_ENABLE;
-
-       if (*port->_status & SC01STR_TBF)
-               x &= ~(GxICR_REQUEST | GxICR_DETECT);
-       else
-               x |= GxICR_REQUEST | GxICR_DETECT;
+       mn10300_serial_en_tx_intr(port);
 
        _debug("CTR=%04hx ICR=%02hx STR=%04x TMD=%02hx TBR=%04hx ICR=%04hx",
               *port->_control, *port->_intr, *port->_status,
               (port->div_timer == MNSCx_DIV_TIMER_8BIT) ?
                   *(volatile u8 *)port->_tmxbr : *port->_tmxbr,
               *port->tx_icr);
-
-       *port->tx_icr = x;
-       x = *port->tx_icr;
-       arch_local_sti();
 }
 
 /*
 {
        struct mn10300_serial_port *port =
                container_of(_port, struct mn10300_serial_port, uart);
+       unsigned long flags;
 
        _enter("%s,%02x", port->name, ch);
 
        if (likely(port->gdbstub)) {
                port->tx_xchar = ch;
-               if (ch)
+               if (ch) {
+                       spin_lock_irqsave(&port->uart.lock, flags);
                        mn10300_serial_en_tx_intr(port);
+                       spin_unlock_irqrestore(&port->uart.lock, flags);
+               }
        }
 }
 
 {
        struct mn10300_serial_port *port =
                container_of(_port, struct mn10300_serial_port, uart);
+       unsigned long flags;
 
        _enter("%s,%d", port->name, ctl);
 
+       spin_lock_irqsave(&port->uart.lock, flags);
        if (ctl) {
                /* tell the virtual DMA handler to assert BREAK */
-               port->tx_break = 1;
+               port->tx_flags |= MNSCx_TX_BREAK;
                mn10300_serial_en_tx_intr(port);
        } else {
-               port->tx_break = 0;
+               port->tx_flags &= ~MNSCx_TX_BREAK;
                *port->_control &= ~SC01CTR_BKE;
                mn10300_serial_en_tx_intr(port);
        }
+       spin_unlock_irqrestore(&port->uart.lock, flags);
 }
 
 /*
                return -ENOMEM;
 
        port->rx_inp = port->rx_outp = 0;
+       port->tx_flags = 0;
 
        /* finally, enable the device */
        *port->_intr = SC01ICR_TI;
  */
 static void mn10300_serial_shutdown(struct uart_port *_port)
 {
+       unsigned long flags;
        u16 x;
        struct mn10300_serial_port *port =
                container_of(_port, struct mn10300_serial_port, uart);
 
        _enter("%s", port->name);
 
+       spin_lock_irqsave(&_port->lock, flags);
+       mn10300_serial_dis_tx_intr(port);
+
+       *port->rx_icr = NUM2GxICR_LEVEL(CONFIG_MN10300_SERIAL_IRQ_LEVEL);
+       x = *port->rx_icr;
+       port->tx_flags = 0;
+       spin_unlock_irqrestore(&_port->lock, flags);
+
        /* disable the serial port and its baud rate timer */
-       port->tx_break = 0;
        *port->_control &= ~(SC01CTR_TXE | SC01CTR_RXE | SC01CTR_BKE);
        *port->_tmxmd = 0;
 
        free_irq(port->rx_irq, port);
        free_irq(port->tx_irq, port);
 
-       arch_local_cli();
-       *port->rx_icr = NUM2GxICR_LEVEL(CONFIG_MN10300_SERIAL_IRQ_LEVEL);
-       x = *port->rx_icr;
-       *port->tx_icr = NUM2GxICR_LEVEL(CONFIG_MN10300_SERIAL_IRQ_LEVEL);
-       x = *port->tx_icr;
-       arch_local_sti();
+       mn10300_serial_int_tbl[port->tx_irq].port = NULL;
+       mn10300_serial_int_tbl[port->rx_irq].port = NULL;
 }
 
 /*
 {
        struct mn10300_serial_port *port;
        unsigned i;
-       u16 scxctr, txicr, tmp;
+       u16 scxctr;
        u8 tmxmd;
+       unsigned long flags;
+       int locked = 1;
 
        port = mn10300_serial_ports[co->index];
 
+       local_irq_save(flags);
+       if (port->uart.sysrq) {
+               /* mn10300_serial_interrupt() already took the lock */
+               locked = 0;
+       } else if (oops_in_progress) {
+               locked = spin_trylock(&port->uart.lock);
+       } else
+               spin_lock(&port->uart.lock);
+
        /* firstly hijack the serial port from the "virtual DMA" controller */
-       arch_local_cli();
-       txicr = *port->tx_icr;
-       *port->tx_icr = NUM2GxICR_LEVEL(CONFIG_MN10300_SERIAL_IRQ_LEVEL);
-       tmp = *port->tx_icr;
-       arch_local_sti();
+       mn10300_serial_dis_tx_intr(port);
 
        /* the transmitter may be disabled */
        scxctr = *port->_control;
        if (!(scxctr & SC01CTR_TXE))
                *port->_control = scxctr;
 
-       arch_local_cli();
-       *port->tx_icr = txicr;
-       tmp = *port->tx_icr;
-       arch_local_sti();
+       mn10300_serial_en_tx_intr(port);
+
+       if (locked)
+               spin_unlock(&port->uart.lock);
+       local_irq_restore(flags);
 }
 
 /*