Return-Path: Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1752521AbcDOVR3 (ORCPT ); Fri, 15 Apr 2016 17:17:29 -0400 Received: from pygmy.kinoho.net ([134.0.27.24]:50293 "EHLO pygmy.kinoho.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1751708AbcDOVOe (ORCPT ); Fri, 15 Apr 2016 17:14:34 -0400 From: Grigori Goronzy To: Johan Hovold Cc: Greg Kroah-Hartman , linux-usb@vger.kernel.org, linux-kernel@vger.kernel.org, Grigori Goronzy Subject: [PATCH v4 05/13] USB: ch341: reinitialize chip on reconfiguration Date: Fri, 15 Apr 2016 23:14:08 +0200 Message-Id: <1460754856-27908-6-git-send-email-greg@chown.ath.cx> X-Mailer: git-send-email 1.9.1 In-Reply-To: <1460754856-27908-1-git-send-email-greg@chown.ath.cx> References: <1460754856-27908-1-git-send-email-greg@chown.ath.cx> Sender: linux-kernel-owner@vger.kernel.org List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Length: 4277 Lines: 137 Changing the LCR register after initialization does not seem to be reliable on all chips (particularly not on CH341A). Restructure initialization and configuration to always reinit the chip on configuration changes instead and pass the LCR register value directly to the initialization command. v2: get rid of unused variable, improve error handling. Tested-by: Ryan Barber Signed-off-by: Grigori Goronzy --- drivers/usb/serial/ch341.c | 47 ++++++++++++++++++++++++---------------------- 1 file changed, 25 insertions(+), 22 deletions(-) diff --git a/drivers/usb/serial/ch341.c b/drivers/usb/serial/ch341.c index 95c8a40..6181616 100644 --- a/drivers/usb/serial/ch341.c +++ b/drivers/usb/serial/ch341.c @@ -61,6 +61,8 @@ * the Net/FreeBSD uchcom.c driver by Takanori Watanabe. Domo arigato. */ +#define CH341_SERIAL_INIT 0xA1 +#define CH341_VERSION 0x5F #define CH341_MODEM_CTRL 0xA4 #define CH341_REQ_WRITE_REG 0x9A #define CH341_REQ_READ_REG 0x95 @@ -129,10 +131,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; @@ -152,11 +154,8 @@ static int ch341_set_baudrate(struct usb_device *dev, factor = 0x10000 - factor; a = (factor & 0xff00) | divisor; - b = factor & 0xff; - r = ch341_control_out(dev, 0x9a, 0x1312, a); - if (!r) - r = ch341_control_out(dev, 0x9a, 0x0f2c, b); + r = ch341_control_out(dev, CH341_SERIAL_INIT, 0x9c | (ctrl << 8), a | 0x80); return r; } @@ -177,7 +176,7 @@ static int ch341_get_status(struct usb_device *dev, struct ch341_private *priv) if (!buffer) return -ENOMEM; - r = ch341_control_in(dev, 0x95, 0x0706, 0, buffer, size); + r = ch341_control_in(dev, CH341_REQ_READ_REG, 0x0706, 0, buffer, size); if (r < 0) goto out; @@ -207,24 +206,20 @@ static int ch341_configure(struct usb_device *dev, struct ch341_private *priv) return -ENOMEM; /* expect two bytes 0x27 0x00 */ - r = ch341_control_in(dev, 0x5f, 0, 0, buffer, size); + r = ch341_control_in(dev, CH341_VERSION, 0, 0, buffer, size); if (r < 0) goto out; - r = ch341_control_out(dev, 0xa1, 0, 0); - if (r < 0) - goto out; - - r = ch341_set_baudrate(dev, priv); + r = ch341_control_out(dev, CH341_SERIAL_INIT, 0, 0); if (r < 0) goto out; /* expect two bytes 0x56 0x00 */ - r = ch341_control_in(dev, 0x95, 0x2518, 0, buffer, size); + r = ch341_control_in(dev, CH341_REQ_READ_REG, 0x2518, 0, buffer, size); if (r < 0) goto out; - r = ch341_control_out(dev, 0x9a, 0x2518, 0x0050); + r = ch341_control_out(dev, CH341_REQ_WRITE_REG, 0x2518, 0x00c3); if (r < 0) goto out; @@ -233,11 +228,7 @@ static int ch341_configure(struct usb_device *dev, struct ch341_private *priv) if (r < 0) goto out; - r = ch341_control_out(dev, 0xa1, 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; @@ -352,16 +343,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); -- 1.9.1