Skip to content

Commit 63a8eef

Browse files
committed
USB: serial: cp210x: fix CP2102N-A01 modem control
CP2102N revision A01 (firmware version <= 1.0.4) has a buggy flow-control implementation that uses the ulXonLimit instead of ulFlowReplace field of the flow-control settings structure (erratum CP2102N_E104). A recent change that set the input software flow-control limits incidentally broke RTS control for these devices when CRTSCTS is not set as the new limits would always enable hardware flow control. Fix this by explicitly disabling flow control for the buggy firmware versions and only updating the input software flow-control limits when IXOFF is requested. This makes sure that the terminal settings matches the default zero ulXonLimit (ulFlowReplace) for these devices. Link: https://lore.kernel.org/r/[email protected] Reported-by: David Frey <[email protected]> Reported-by: Alex Villacís Lasso <[email protected]> Tested-by: Alex Villacís Lasso <[email protected]> Fixes: f61309d ("USB: serial: cp210x: set IXOFF thresholds") Cc: [email protected] # 5.12 Signed-off-by: Johan Hovold <[email protected]>
1 parent 6f7ec77 commit 63a8eef

File tree

1 file changed

+59
-5
lines changed

1 file changed

+59
-5
lines changed

drivers/usb/serial/cp210x.c

Lines changed: 59 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -252,9 +252,11 @@ struct cp210x_serial_private {
252252
u8 gpio_input;
253253
#endif
254254
u8 partnum;
255+
u32 fw_version;
255256
speed_t min_speed;
256257
speed_t max_speed;
257258
bool use_actual_rate;
259+
bool no_flow_control;
258260
};
259261

260262
enum cp210x_event_state {
@@ -398,6 +400,7 @@ struct cp210x_special_chars {
398400

399401
/* CP210X_VENDOR_SPECIFIC values */
400402
#define CP210X_READ_2NCONFIG 0x000E
403+
#define CP210X_GET_FW_VER_2N 0x0010
401404
#define CP210X_READ_LATCH 0x00C2
402405
#define CP210X_GET_PARTNUM 0x370B
403406
#define CP210X_GET_PORTCONFIG 0x370C
@@ -1128,13 +1131,23 @@ static bool cp210x_termios_change(const struct ktermios *a, const struct ktermio
11281131
static void cp210x_set_flow_control(struct tty_struct *tty,
11291132
struct usb_serial_port *port, struct ktermios *old_termios)
11301133
{
1134+
struct cp210x_serial_private *priv = usb_get_serial_data(port->serial);
11311135
struct cp210x_port_private *port_priv = usb_get_serial_port_data(port);
11321136
struct cp210x_special_chars chars;
11331137
struct cp210x_flow_ctl flow_ctl;
11341138
u32 flow_repl;
11351139
u32 ctl_hs;
11361140
int ret;
11371141

1142+
/*
1143+
* Some CP2102N interpret ulXonLimit as ulFlowReplace (erratum
1144+
* CP2102N_E104). Report back that flow control is not supported.
1145+
*/
1146+
if (priv->no_flow_control) {
1147+
tty->termios.c_cflag &= ~CRTSCTS;
1148+
tty->termios.c_iflag &= ~(IXON | IXOFF);
1149+
}
1150+
11381151
if (old_termios &&
11391152
C_CRTSCTS(tty) == (old_termios->c_cflag & CRTSCTS) &&
11401153
I_IXON(tty) == (old_termios->c_iflag & IXON) &&
@@ -1191,19 +1204,20 @@ static void cp210x_set_flow_control(struct tty_struct *tty,
11911204
port_priv->crtscts = false;
11921205
}
11931206

1194-
if (I_IXOFF(tty))
1207+
if (I_IXOFF(tty)) {
11951208
flow_repl |= CP210X_SERIAL_AUTO_RECEIVE;
1196-
else
1209+
1210+
flow_ctl.ulXonLimit = cpu_to_le32(128);
1211+
flow_ctl.ulXoffLimit = cpu_to_le32(128);
1212+
} else {
11971213
flow_repl &= ~CP210X_SERIAL_AUTO_RECEIVE;
1214+
}
11981215

11991216
if (I_IXON(tty))
12001217
flow_repl |= CP210X_SERIAL_AUTO_TRANSMIT;
12011218
else
12021219
flow_repl &= ~CP210X_SERIAL_AUTO_TRANSMIT;
12031220

1204-
flow_ctl.ulXonLimit = cpu_to_le32(128);
1205-
flow_ctl.ulXoffLimit = cpu_to_le32(128);
1206-
12071221
dev_dbg(&port->dev, "%s - ctrl = 0x%02x, flow = 0x%02x\n", __func__,
12081222
ctl_hs, flow_repl);
12091223

@@ -1926,6 +1940,45 @@ static void cp210x_init_max_speed(struct usb_serial *serial)
19261940
priv->use_actual_rate = use_actual_rate;
19271941
}
19281942

1943+
static int cp210x_get_fw_version(struct usb_serial *serial, u16 value)
1944+
{
1945+
struct cp210x_serial_private *priv = usb_get_serial_data(serial);
1946+
u8 ver[3];
1947+
int ret;
1948+
1949+
ret = cp210x_read_vendor_block(serial, REQTYPE_DEVICE_TO_HOST, value,
1950+
ver, sizeof(ver));
1951+
if (ret)
1952+
return ret;
1953+
1954+
dev_dbg(&serial->interface->dev, "%s - %d.%d.%d\n", __func__,
1955+
ver[0], ver[1], ver[2]);
1956+
1957+
priv->fw_version = ver[0] << 16 | ver[1] << 8 | ver[2];
1958+
1959+
return 0;
1960+
}
1961+
1962+
static void cp210x_determine_quirks(struct usb_serial *serial)
1963+
{
1964+
struct cp210x_serial_private *priv = usb_get_serial_data(serial);
1965+
int ret;
1966+
1967+
switch (priv->partnum) {
1968+
case CP210X_PARTNUM_CP2102N_QFN28:
1969+
case CP210X_PARTNUM_CP2102N_QFN24:
1970+
case CP210X_PARTNUM_CP2102N_QFN20:
1971+
ret = cp210x_get_fw_version(serial, CP210X_GET_FW_VER_2N);
1972+
if (ret)
1973+
break;
1974+
if (priv->fw_version <= 0x10004)
1975+
priv->no_flow_control = true;
1976+
break;
1977+
default:
1978+
break;
1979+
}
1980+
}
1981+
19291982
static int cp210x_attach(struct usb_serial *serial)
19301983
{
19311984
int result;
@@ -1946,6 +1999,7 @@ static int cp210x_attach(struct usb_serial *serial)
19461999

19472000
usb_set_serial_data(serial, priv);
19482001

2002+
cp210x_determine_quirks(serial);
19492003
cp210x_init_max_speed(serial);
19502004

19512005
result = cp210x_gpio_init(serial);

0 commit comments

Comments
 (0)