summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--drivers/usb/serial/cp210x.c39
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;