USB: serial: ch341: reinitialize chip on reconfiguration
[pandora-kernel.git] / drivers / usb / serial / ch341.c
index 2d9d337..9538181 100644 (file)
@@ -133,10 +133,10 @@ static int ch341_control_in(struct usb_device *dev,
        return r;
 }
 
-static int ch341_set_baudrate(struct usb_device *dev,
-                             struct ch341_private *priv)
+static int ch341_init_set_baudrate(struct usb_device *dev,
+                                  struct ch341_private *priv, unsigned ctrl)
 {
-       short a, b;
+       short a;
        int r;
        unsigned long factor;
        short divisor;
@@ -158,11 +158,10 @@ static int ch341_set_baudrate(struct usb_device *dev,
 
        factor = 0x10000 - factor;
        a = (factor & 0xff00) | divisor;
-       b = factor & 0xff;
 
-       r = ch341_control_out(dev, CH341_REQ_WRITE_REG, 0x1312, a);
-       if (!r)
-               r = ch341_control_out(dev, CH341_REQ_WRITE_REG, 0x0f2c, b);
+       /* 0x9c is "enable SFR_UART Control register and timer" */
+       r = ch341_control_out(dev, CH341_REQ_SERIAL_INIT,
+                             0x9c | (ctrl << 8), a | 0x80);
 
        return r;
 }
@@ -227,10 +226,6 @@ static int ch341_configure(struct usb_device *dev, struct ch341_private *priv)
        if (r < 0)
                goto out;
 
-       r = ch341_set_baudrate(dev, priv);
-       if (r < 0)
-               goto out;
-
        /* expect two bytes 0x56 0x00 */
        r = ch341_control_in(dev, CH341_REQ_READ_REG, 0x2518, 0, buffer, size);
        if (r < 0)
@@ -245,11 +240,7 @@ static int ch341_configure(struct usb_device *dev, struct ch341_private *priv)
        if (r < 0)
                goto out;
 
-       r = ch341_control_out(dev, CH341_REQ_SERIAL_INIT, 0x501f, 0xd90a);
-       if (r < 0)
-               goto out;
-
-       r = ch341_set_baudrate(dev, priv);
+       r = ch341_init_set_baudrate(dev, priv, 0);
        if (r < 0)
                goto out;
 
@@ -365,6 +356,12 @@ static void ch341_set_termios(struct tty_struct *tty,
        struct ch341_private *priv = usb_get_serial_port_data(port);
        unsigned baud_rate;
        unsigned long flags;
+       unsigned char ctrl;
+       int r;
+
+       /* redundant changes may cause the chip to lose bytes */
+       if (old_termios && !tty_termios_hw_change(tty->termios, old_termios))
+               return;
 
        dbg("ch341_set_termios()");
 
@@ -372,11 +369,17 @@ static void ch341_set_termios(struct tty_struct *tty,
 
        priv->baud_rate = baud_rate;
 
+       ctrl = CH341_LCR_ENABLE_RX | CH341_LCR_ENABLE_TX | CH341_LCR_CS8;
+
        if (baud_rate) {
                spin_lock_irqsave(&priv->lock, flags);
                priv->line_control |= (CH341_BIT_DTR | CH341_BIT_RTS);
                spin_unlock_irqrestore(&priv->lock, flags);
-               ch341_set_baudrate(port->serial->dev, priv);
+               r = ch341_init_set_baudrate(port->serial->dev, priv, ctrl);
+               if (r < 0 && old_termios) {
+                       priv->baud_rate = tty_termios_baud_rate(old_termios);
+                       tty_termios_copy_hw(tty->termios, old_termios);
+               }
        } else {
                spin_lock_irqsave(&priv->lock, flags);
                priv->line_control &= ~(CH341_BIT_DTR | CH341_BIT_RTS);