diff options
author | Johan Hovold <johan@kernel.org> | 2017-01-06 13:15:16 -0500 |
---|---|---|
committer | Johan Hovold <johan@kernel.org> | 2017-01-09 08:55:40 -0500 |
commit | 55fa15b5987db22b4f35d3f0798928c126be5f1c (patch) | |
tree | b547877c6572db2adf53e95a85e05a3a8eff3d23 | |
parent | 3cca8624b6624e7ffb87dcd8a0a05bef9b50e97b (diff) |
USB: serial: ch341: fix baud rate and line-control handling
Revert to using direct register writes to set the divisor and
line-control registers.
A recent change switched to using the init vendor command to update
these registers, something which also enabled support for CH341A
devices. It turns out that simply setting bit 7 in the divisor register
is sufficient to support CH341A and specifically prevent data from being
buffered until a full endpoint-size packet (32 bytes) has been received.
Using the init command also had the side-effect of temporarily
deasserting the DTR/RTS signals on every termios change (including
initialisation on open) something which for example could cause problems
in setups where DTR is used to trigger a reset.
Fixes: 4e46c410e050 ("USB: serial: ch341: reinitialize chip on
reconfiguration")
Signed-off-by: Johan Hovold <johan@kernel.org>
-rw-r--r-- | drivers/usb/serial/ch341.c | 24 |
1 files changed, 17 insertions, 7 deletions
diff --git a/drivers/usb/serial/ch341.c b/drivers/usb/serial/ch341.c index eabdd05a2147..8d7b0847109b 100644 --- a/drivers/usb/serial/ch341.c +++ b/drivers/usb/serial/ch341.c | |||
@@ -133,8 +133,8 @@ static int ch341_control_in(struct usb_device *dev, | |||
133 | return r; | 133 | return r; |
134 | } | 134 | } |
135 | 135 | ||
136 | static int ch341_init_set_baudrate(struct usb_device *dev, | 136 | static int ch341_set_baudrate_lcr(struct usb_device *dev, |
137 | struct ch341_private *priv, unsigned ctrl) | 137 | struct ch341_private *priv, u8 lcr) |
138 | { | 138 | { |
139 | short a; | 139 | short a; |
140 | int r; | 140 | int r; |
@@ -157,9 +157,19 @@ static int ch341_init_set_baudrate(struct usb_device *dev, | |||
157 | factor = 0x10000 - factor; | 157 | factor = 0x10000 - factor; |
158 | a = (factor & 0xff00) | divisor; | 158 | a = (factor & 0xff00) | divisor; |
159 | 159 | ||
160 | /* 0x9c is "enable SFR_UART Control register and timer" */ | 160 | /* |
161 | r = ch341_control_out(dev, CH341_REQ_SERIAL_INIT, | 161 | * CH341A buffers data until a full endpoint-size packet (32 bytes) |
162 | 0x9c | (ctrl << 8), a | 0x80); | 162 | * has been received unless bit 7 is set. |
163 | */ | ||
164 | a |= BIT(7); | ||
165 | |||
166 | r = ch341_control_out(dev, CH341_REQ_WRITE_REG, 0x1312, a); | ||
167 | if (r) | ||
168 | return r; | ||
169 | |||
170 | r = ch341_control_out(dev, CH341_REQ_WRITE_REG, 0x2518, lcr); | ||
171 | if (r) | ||
172 | return r; | ||
163 | 173 | ||
164 | return r; | 174 | return r; |
165 | } | 175 | } |
@@ -233,7 +243,7 @@ static int ch341_configure(struct usb_device *dev, struct ch341_private *priv) | |||
233 | if (r < 0) | 243 | if (r < 0) |
234 | goto out; | 244 | goto out; |
235 | 245 | ||
236 | r = ch341_init_set_baudrate(dev, priv, priv->lcr); | 246 | r = ch341_set_baudrate_lcr(dev, priv, priv->lcr); |
237 | if (r < 0) | 247 | if (r < 0) |
238 | goto out; | 248 | goto out; |
239 | 249 | ||
@@ -394,7 +404,7 @@ static void ch341_set_termios(struct tty_struct *tty, | |||
394 | if (baud_rate) { | 404 | if (baud_rate) { |
395 | priv->baud_rate = baud_rate; | 405 | priv->baud_rate = baud_rate; |
396 | 406 | ||
397 | r = ch341_init_set_baudrate(port->serial->dev, priv, ctrl); | 407 | r = ch341_set_baudrate_lcr(port->serial->dev, priv, ctrl); |
398 | if (r < 0 && old_termios) { | 408 | if (r < 0 && old_termios) { |
399 | priv->baud_rate = tty_termios_baud_rate(old_termios); | 409 | priv->baud_rate = tty_termios_baud_rate(old_termios); |
400 | tty_termios_copy_hw(&tty->termios, old_termios); | 410 | tty_termios_copy_hw(&tty->termios, old_termios); |