USB-serial fixes for 5.13-rc6

Here are two fixes for the cp210x driver. The first fixes a regression
 with early revisions of the CP2102N which specifically broke some ESP32
 development boards. The second makes sure that the pin configuration is
 detected properly also for the CP2102N QFN20 package.
 
 Both have been in linux-next over night and with no reported issues.
 -----BEGIN PGP SIGNATURE-----
 
 iHUEABYIAB0WIQQHbPq+cpGvN/peuzMLxc3C7H1lCAUCYMM1XQAKCRALxc3C7H1l
 CH1DAQDlY9HI8ujDidiFHqBw/UduU7jmncuiGR/sAVzEAstPigD/VOJH+cD/yn7Z
 35gSi1DvdrBRjs8qTlzZvoiSN81nTgM=
 =yLzP
 -----END PGP SIGNATURE-----

Merge tag 'usb-serial-5.13-rc6' of https://git.kernel.org/pub/scm/linux/kernel/git/johan/usb-serial into usb-linus

Johan writes:

USB-serial fixes for 5.13-rc6

Here are two fixes for the cp210x driver. The first fixes a regression
with early revisions of the CP2102N which specifically broke some ESP32
development boards. The second makes sure that the pin configuration is
detected properly also for the CP2102N QFN20 package.

Both have been in linux-next over night and with no reported issues.

* tag 'usb-serial-5.13-rc6' of https://git.kernel.org/pub/scm/linux/kernel/git/johan/usb-serial:
  USB: serial: cp210x: fix CP2102N-A01 modem control
  USB: serial: cp210x: fix alternate function for CP2102N QFN20
This commit is contained in:
Greg Kroah-Hartman 2021-06-11 12:32:49 +02:00
commit 7c4363d394
1 changed files with 78 additions and 6 deletions

View File

@ -252,9 +252,11 @@ struct cp210x_serial_private {
u8 gpio_input;
#endif
u8 partnum;
u32 fw_version;
speed_t min_speed;
speed_t max_speed;
bool use_actual_rate;
bool no_flow_control;
};
enum cp210x_event_state {
@ -398,6 +400,7 @@ struct cp210x_special_chars {
/* CP210X_VENDOR_SPECIFIC values */
#define CP210X_READ_2NCONFIG 0x000E
#define CP210X_GET_FW_VER_2N 0x0010
#define CP210X_READ_LATCH 0x00C2
#define CP210X_GET_PARTNUM 0x370B
#define CP210X_GET_PORTCONFIG 0x370C
@ -537,6 +540,12 @@ struct cp210x_single_port_config {
#define CP210X_2NCONFIG_GPIO_RSTLATCH_IDX 587
#define CP210X_2NCONFIG_GPIO_CONTROL_IDX 600
/* CP2102N QFN20 port configuration values */
#define CP2102N_QFN20_GPIO2_TXLED_MODE BIT(2)
#define CP2102N_QFN20_GPIO3_RXLED_MODE BIT(3)
#define CP2102N_QFN20_GPIO1_RS485_MODE BIT(4)
#define CP2102N_QFN20_GPIO0_CLK_MODE BIT(6)
/* CP210X_VENDOR_SPECIFIC, CP210X_WRITE_LATCH call writes these 0x2 bytes. */
struct cp210x_gpio_write {
u8 mask;
@ -1122,6 +1131,7 @@ static bool cp210x_termios_change(const struct ktermios *a, const struct ktermio
static void cp210x_set_flow_control(struct tty_struct *tty,
struct usb_serial_port *port, struct ktermios *old_termios)
{
struct cp210x_serial_private *priv = usb_get_serial_data(port->serial);
struct cp210x_port_private *port_priv = usb_get_serial_port_data(port);
struct cp210x_special_chars chars;
struct cp210x_flow_ctl flow_ctl;
@ -1129,6 +1139,15 @@ static void cp210x_set_flow_control(struct tty_struct *tty,
u32 ctl_hs;
int ret;
/*
* Some CP2102N interpret ulXonLimit as ulFlowReplace (erratum
* CP2102N_E104). Report back that flow control is not supported.
*/
if (priv->no_flow_control) {
tty->termios.c_cflag &= ~CRTSCTS;
tty->termios.c_iflag &= ~(IXON | IXOFF);
}
if (old_termios &&
C_CRTSCTS(tty) == (old_termios->c_cflag & CRTSCTS) &&
I_IXON(tty) == (old_termios->c_iflag & IXON) &&
@ -1185,19 +1204,20 @@ static void cp210x_set_flow_control(struct tty_struct *tty,
port_priv->crtscts = false;
}
if (I_IXOFF(tty))
if (I_IXOFF(tty)) {
flow_repl |= CP210X_SERIAL_AUTO_RECEIVE;
else
flow_ctl.ulXonLimit = cpu_to_le32(128);
flow_ctl.ulXoffLimit = cpu_to_le32(128);
} else {
flow_repl &= ~CP210X_SERIAL_AUTO_RECEIVE;
}
if (I_IXON(tty))
flow_repl |= CP210X_SERIAL_AUTO_TRANSMIT;
else
flow_repl &= ~CP210X_SERIAL_AUTO_TRANSMIT;
flow_ctl.ulXonLimit = cpu_to_le32(128);
flow_ctl.ulXoffLimit = cpu_to_le32(128);
dev_dbg(&port->dev, "%s - ctrl = 0x%02x, flow = 0x%02x\n", __func__,
ctl_hs, flow_repl);
@ -1733,7 +1753,19 @@ static int cp2102n_gpioconf_init(struct usb_serial *serial)
priv->gpio_pushpull = (gpio_pushpull >> 3) & 0x0f;
/* 0 indicates GPIO mode, 1 is alternate function */
priv->gpio_altfunc = (gpio_ctrl >> 2) & 0x0f;
if (priv->partnum == CP210X_PARTNUM_CP2102N_QFN20) {
/* QFN20 is special... */
if (gpio_ctrl & CP2102N_QFN20_GPIO0_CLK_MODE) /* GPIO 0 */
priv->gpio_altfunc |= BIT(0);
if (gpio_ctrl & CP2102N_QFN20_GPIO1_RS485_MODE) /* GPIO 1 */
priv->gpio_altfunc |= BIT(1);
if (gpio_ctrl & CP2102N_QFN20_GPIO2_TXLED_MODE) /* GPIO 2 */
priv->gpio_altfunc |= BIT(2);
if (gpio_ctrl & CP2102N_QFN20_GPIO3_RXLED_MODE) /* GPIO 3 */
priv->gpio_altfunc |= BIT(3);
} else {
priv->gpio_altfunc = (gpio_ctrl >> 2) & 0x0f;
}
if (priv->partnum == CP210X_PARTNUM_CP2102N_QFN28) {
/*
@ -1908,6 +1940,45 @@ static void cp210x_init_max_speed(struct usb_serial *serial)
priv->use_actual_rate = use_actual_rate;
}
static int cp210x_get_fw_version(struct usb_serial *serial, u16 value)
{
struct cp210x_serial_private *priv = usb_get_serial_data(serial);
u8 ver[3];
int ret;
ret = cp210x_read_vendor_block(serial, REQTYPE_DEVICE_TO_HOST, value,
ver, sizeof(ver));
if (ret)
return ret;
dev_dbg(&serial->interface->dev, "%s - %d.%d.%d\n", __func__,
ver[0], ver[1], ver[2]);
priv->fw_version = ver[0] << 16 | ver[1] << 8 | ver[2];
return 0;
}
static void cp210x_determine_quirks(struct usb_serial *serial)
{
struct cp210x_serial_private *priv = usb_get_serial_data(serial);
int ret;
switch (priv->partnum) {
case CP210X_PARTNUM_CP2102N_QFN28:
case CP210X_PARTNUM_CP2102N_QFN24:
case CP210X_PARTNUM_CP2102N_QFN20:
ret = cp210x_get_fw_version(serial, CP210X_GET_FW_VER_2N);
if (ret)
break;
if (priv->fw_version <= 0x10004)
priv->no_flow_control = true;
break;
default:
break;
}
}
static int cp210x_attach(struct usb_serial *serial)
{
int result;
@ -1928,6 +1999,7 @@ static int cp210x_attach(struct usb_serial *serial)
usb_set_serial_data(serial, priv);
cp210x_determine_quirks(serial);
cp210x_init_max_speed(serial);
result = cp210x_gpio_init(serial);