diff options
Diffstat (limited to 'drivers/usb')
-rw-r--r-- | drivers/usb/serial/ch341.c | 37 |
1 files changed, 20 insertions, 17 deletions
diff --git a/drivers/usb/serial/ch341.c b/drivers/usb/serial/ch341.c index bdf525fe7807..096bc070635a 100644 --- a/drivers/usb/serial/ch341.c +++ b/drivers/usb/serial/ch341.c @@ -132,10 +132,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; @@ -155,11 +155,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; } @@ -218,10 +217,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) @@ -236,11 +231,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; @@ -355,16 +346,28 @@ 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; baud_rate = tty_get_baud_rate(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); |