diff options
-rw-r--r-- | drivers/usb/serial/cp210x.c | 39 |
1 files changed, 34 insertions, 5 deletions
diff --git a/drivers/usb/serial/cp210x.c b/drivers/usb/serial/cp210x.c index 4281f2bfe0e1..3778685c7b99 100644 --- a/drivers/usb/serial/cp210x.c +++ b/drivers/usb/serial/cp210x.c @@ -355,6 +355,9 @@ static struct usb_serial_driver * const serial_drivers[] = { #define CP210X_PARTNUM_CP2104 0x04 #define CP210X_PARTNUM_CP2105 0x05 #define CP210X_PARTNUM_CP2108 0x08 +#define CP210X_PARTNUM_CP2102N_QFN28 0x20 +#define CP210X_PARTNUM_CP2102N_QFN24 0x21 +#define CP210X_PARTNUM_CP2102N_QFN20 0x22 #define CP210X_PARTNUM_UNKNOWN 0xFF /* CP210X_GET_COMM_STATUS returns these 0x13 bytes */ @@ -454,6 +457,15 @@ struct cp210x_gpio_write { u8 state; } __packed; +static bool cp210x_is_cp2102n(struct usb_serial *serial) +{ + struct cp210x_serial_private *priv = usb_get_serial_data(serial); + + return (priv->partnum == CP210X_PARTNUM_CP2102N_QFN28) || + (priv->partnum == CP210X_PARTNUM_CP2102N_QFN24) || + (priv->partnum == CP210X_PARTNUM_CP2102N_QFN20); +} + /* * Helper to get interface number when we only have struct usb_serial. */ @@ -1053,20 +1065,32 @@ static speed_t cp210x_get_an205_rate(speed_t baud) static void cp210x_change_speed(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 usb_serial *serial = port->serial; + struct cp210x_serial_private *priv = usb_get_serial_data(serial); u32 baud; baud = tty->termios.c_ospeed; - /* This maps the requested rate to a rate valid on cp2102 or cp2103, - * or to an arbitrary rate in [1M, max_speed] + /* + * This maps the requested rate to the actual rate on cp2102n, a valid + * rate on cp2102 or cp2103, or to an arbitrary rate in + * [1M, max_speed]. * * NOTE: B0 is not implemented. */ - if (baud < 1000000) + if (cp210x_is_cp2102n(serial)) { + int clk_div; + int prescaler; + + baud = clamp(baud, 300u, priv->max_speed); + prescaler = (baud <= 365) ? 4 : 1; + clk_div = DIV_ROUND_CLOSEST(48000000, 2 * prescaler * baud); + baud = 48000000 / (2 * prescaler * clk_div); + } else if (baud < 1000000) { baud = cp210x_get_an205_rate(baud); - else if (baud > priv->max_speed) + } else if (baud > priv->max_speed) { baud = priv->max_speed; + } dev_dbg(&port->dev, "%s - setting baud rate to %u\n", __func__, baud); if (cp210x_write_u32_reg(port, CP210X_SET_BAUDRATE, baud)) { @@ -1520,6 +1544,11 @@ static void cp210x_init_max_speed(struct usb_serial *serial) else max = 921600; /* SCI */ break; + case CP210X_PARTNUM_CP2102N_QFN28: + case CP210X_PARTNUM_CP2102N_QFN24: + case CP210X_PARTNUM_CP2102N_QFN20: + max = 3000000; + break; default: max = 2000000; break; |