|
@@ -239,11 +239,14 @@ MODULE_DEVICE_TABLE(usb, id_table);
|
|
|
struct cp210x_serial_private {
|
|
|
#ifdef CONFIG_GPIOLIB
|
|
|
struct gpio_chip gc;
|
|
|
- u8 config;
|
|
|
- u8 gpio_mode;
|
|
|
bool gpio_registered;
|
|
|
+ u8 gpio_pushpull;
|
|
|
+ u8 gpio_altfunc;
|
|
|
+ u8 gpio_input;
|
|
|
#endif
|
|
|
u8 partnum;
|
|
|
+ speed_t max_speed;
|
|
|
+ bool use_actual_rate;
|
|
|
};
|
|
|
|
|
|
struct cp210x_port_private {
|
|
@@ -356,6 +359,7 @@ static struct usb_serial_driver * const serial_drivers[] = {
|
|
|
#define CONTROL_WRITE_RTS 0x0200
|
|
|
|
|
|
/* CP210X_VENDOR_SPECIFIC values */
|
|
|
+#define CP210X_READ_2NCONFIG 0x000E
|
|
|
#define CP210X_READ_LATCH 0x00C2
|
|
|
#define CP210X_GET_PARTNUM 0x370B
|
|
|
#define CP210X_GET_PORTCONFIG 0x370C
|
|
@@ -369,6 +373,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 */
|
|
@@ -462,6 +469,12 @@ struct cp210x_config {
|
|
|
#define CP2105_GPIO1_RXLED_MODE BIT(1)
|
|
|
#define CP2105_GPIO1_RS485_MODE BIT(2)
|
|
|
|
|
|
+/* CP2102N configuration array indices */
|
|
|
+#define CP210X_2NCONFIG_CONFIG_VERSION_IDX 2
|
|
|
+#define CP210X_2NCONFIG_GPIO_MODE_IDX 581
|
|
|
+#define CP210X_2NCONFIG_GPIO_RSTLATCH_IDX 587
|
|
|
+#define CP210X_2NCONFIG_GPIO_CONTROL_IDX 600
|
|
|
+
|
|
|
/* CP210X_VENDOR_SPECIFIC, CP210X_WRITE_LATCH call writes these 0x2 bytes. */
|
|
|
struct cp210x_gpio_write {
|
|
|
u8 mask;
|
|
@@ -767,48 +780,6 @@ static int cp210x_get_line_ctl(struct usb_serial_port *port, u16 *ctl)
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
-/*
|
|
|
- * cp210x_quantise_baudrate
|
|
|
- * Quantises the baud rate as per AN205 Table 1
|
|
|
- */
|
|
|
-static unsigned int cp210x_quantise_baudrate(unsigned int baud)
|
|
|
-{
|
|
|
- if (baud <= 300)
|
|
|
- baud = 300;
|
|
|
- else if (baud <= 600) baud = 600;
|
|
|
- else if (baud <= 1200) baud = 1200;
|
|
|
- else if (baud <= 1800) baud = 1800;
|
|
|
- else if (baud <= 2400) baud = 2400;
|
|
|
- else if (baud <= 4000) baud = 4000;
|
|
|
- else if (baud <= 4803) baud = 4800;
|
|
|
- else if (baud <= 7207) baud = 7200;
|
|
|
- else if (baud <= 9612) baud = 9600;
|
|
|
- else if (baud <= 14428) baud = 14400;
|
|
|
- else if (baud <= 16062) baud = 16000;
|
|
|
- else if (baud <= 19250) baud = 19200;
|
|
|
- else if (baud <= 28912) baud = 28800;
|
|
|
- else if (baud <= 38601) baud = 38400;
|
|
|
- else if (baud <= 51558) baud = 51200;
|
|
|
- else if (baud <= 56280) baud = 56000;
|
|
|
- else if (baud <= 58053) baud = 57600;
|
|
|
- else if (baud <= 64111) baud = 64000;
|
|
|
- else if (baud <= 77608) baud = 76800;
|
|
|
- else if (baud <= 117028) baud = 115200;
|
|
|
- else if (baud <= 129347) baud = 128000;
|
|
|
- else if (baud <= 156868) baud = 153600;
|
|
|
- else if (baud <= 237832) baud = 230400;
|
|
|
- else if (baud <= 254234) baud = 250000;
|
|
|
- else if (baud <= 273066) baud = 256000;
|
|
|
- else if (baud <= 491520) baud = 460800;
|
|
|
- else if (baud <= 567138) baud = 500000;
|
|
|
- else if (baud <= 670254) baud = 576000;
|
|
|
- else if (baud < 1000000)
|
|
|
- baud = 921600;
|
|
|
- else if (baud > 2000000)
|
|
|
- baud = 2000000;
|
|
|
- return baud;
|
|
|
-}
|
|
|
-
|
|
|
static int cp210x_open(struct tty_struct *tty, struct usb_serial_port *port)
|
|
|
{
|
|
|
int result;
|
|
@@ -1028,6 +999,75 @@ static void cp210x_get_termios_port(struct usb_serial_port *port,
|
|
|
*cflagp = cflag;
|
|
|
}
|
|
|
|
|
|
+struct cp210x_rate {
|
|
|
+ speed_t rate;
|
|
|
+ speed_t high;
|
|
|
+};
|
|
|
+
|
|
|
+static const struct cp210x_rate cp210x_an205_table1[] = {
|
|
|
+ { 300, 300 },
|
|
|
+ { 600, 600 },
|
|
|
+ { 1200, 1200 },
|
|
|
+ { 1800, 1800 },
|
|
|
+ { 2400, 2400 },
|
|
|
+ { 4000, 4000 },
|
|
|
+ { 4800, 4803 },
|
|
|
+ { 7200, 7207 },
|
|
|
+ { 9600, 9612 },
|
|
|
+ { 14400, 14428 },
|
|
|
+ { 16000, 16062 },
|
|
|
+ { 19200, 19250 },
|
|
|
+ { 28800, 28912 },
|
|
|
+ { 38400, 38601 },
|
|
|
+ { 51200, 51558 },
|
|
|
+ { 56000, 56280 },
|
|
|
+ { 57600, 58053 },
|
|
|
+ { 64000, 64111 },
|
|
|
+ { 76800, 77608 },
|
|
|
+ { 115200, 117028 },
|
|
|
+ { 128000, 129347 },
|
|
|
+ { 153600, 156868 },
|
|
|
+ { 230400, 237832 },
|
|
|
+ { 250000, 254234 },
|
|
|
+ { 256000, 273066 },
|
|
|
+ { 460800, 491520 },
|
|
|
+ { 500000, 567138 },
|
|
|
+ { 576000, 670254 },
|
|
|
+ { 921600, UINT_MAX }
|
|
|
+};
|
|
|
+
|
|
|
+/*
|
|
|
+ * Quantises the baud rate as per AN205 Table 1
|
|
|
+ */
|
|
|
+static speed_t cp210x_get_an205_rate(speed_t baud)
|
|
|
+{
|
|
|
+ int i;
|
|
|
+
|
|
|
+ for (i = 0; i < ARRAY_SIZE(cp210x_an205_table1); ++i) {
|
|
|
+ if (baud <= cp210x_an205_table1[i].high)
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ return cp210x_an205_table1[i].rate;
|
|
|
+}
|
|
|
+
|
|
|
+static speed_t cp210x_get_actual_rate(struct usb_serial *serial, speed_t baud)
|
|
|
+{
|
|
|
+ struct cp210x_serial_private *priv = usb_get_serial_data(serial);
|
|
|
+ unsigned int prescale = 1;
|
|
|
+ unsigned int div;
|
|
|
+
|
|
|
+ baud = clamp(baud, 300u, priv->max_speed);
|
|
|
+
|
|
|
+ if (baud <= 365)
|
|
|
+ prescale = 4;
|
|
|
+
|
|
|
+ div = DIV_ROUND_CLOSEST(48000000, 2 * prescale * baud);
|
|
|
+ baud = 48000000 / (2 * prescale * div);
|
|
|
+
|
|
|
+ return baud;
|
|
|
+}
|
|
|
+
|
|
|
/*
|
|
|
* CP2101 supports the following baud rates:
|
|
|
*
|
|
@@ -1057,16 +1097,24 @@ static void cp210x_get_termios_port(struct usb_serial_port *port,
|
|
|
static void cp210x_change_speed(struct tty_struct *tty,
|
|
|
struct usb_serial_port *port, struct ktermios *old_termios)
|
|
|
{
|
|
|
+ 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,2M].
|
|
|
+ /*
|
|
|
+ * This maps the requested rate to the actual rate, a valid rate on
|
|
|
+ * cp2102 or cp2103, or to an arbitrary rate in [1M, max_speed].
|
|
|
*
|
|
|
* NOTE: B0 is not implemented.
|
|
|
*/
|
|
|
- baud = cp210x_quantise_baudrate(baud);
|
|
|
+ if (priv->use_actual_rate)
|
|
|
+ baud = cp210x_get_actual_rate(serial, baud);
|
|
|
+ else if (baud < 1000000)
|
|
|
+ baud = cp210x_get_an205_rate(baud);
|
|
|
+ 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)) {
|
|
@@ -1288,17 +1336,8 @@ static int cp210x_gpio_request(struct gpio_chip *gc, unsigned int offset)
|
|
|
struct usb_serial *serial = gpiochip_get_data(gc);
|
|
|
struct cp210x_serial_private *priv = usb_get_serial_data(serial);
|
|
|
|
|
|
- switch (offset) {
|
|
|
- case 0:
|
|
|
- if (priv->config & CP2105_GPIO0_TXLED_MODE)
|
|
|
- return -ENODEV;
|
|
|
- break;
|
|
|
- case 1:
|
|
|
- if (priv->config & (CP2105_GPIO1_RXLED_MODE |
|
|
|
- CP2105_GPIO1_RS485_MODE))
|
|
|
- return -ENODEV;
|
|
|
- break;
|
|
|
- }
|
|
|
+ if (priv->gpio_altfunc & BIT(offset))
|
|
|
+ return -ENODEV;
|
|
|
|
|
|
return 0;
|
|
|
}
|
|
@@ -1306,10 +1345,15 @@ static int cp210x_gpio_request(struct gpio_chip *gc, unsigned int offset)
|
|
|
static int cp210x_gpio_get(struct gpio_chip *gc, unsigned int gpio)
|
|
|
{
|
|
|
struct usb_serial *serial = gpiochip_get_data(gc);
|
|
|
+ struct cp210x_serial_private *priv = usb_get_serial_data(serial);
|
|
|
+ u8 req_type = REQTYPE_DEVICE_TO_HOST;
|
|
|
int result;
|
|
|
u8 buf;
|
|
|
|
|
|
- result = cp210x_read_vendor_block(serial, REQTYPE_INTERFACE_TO_HOST,
|
|
|
+ if (priv->partnum == CP210X_PARTNUM_CP2105)
|
|
|
+ req_type = REQTYPE_INTERFACE_TO_HOST;
|
|
|
+
|
|
|
+ result = cp210x_read_vendor_block(serial, req_type,
|
|
|
CP210X_READ_LATCH, &buf, sizeof(buf));
|
|
|
if (result < 0)
|
|
|
return result;
|
|
@@ -1320,7 +1364,9 @@ static int cp210x_gpio_get(struct gpio_chip *gc, unsigned int gpio)
|
|
|
static void cp210x_gpio_set(struct gpio_chip *gc, unsigned int gpio, int value)
|
|
|
{
|
|
|
struct usb_serial *serial = gpiochip_get_data(gc);
|
|
|
+ struct cp210x_serial_private *priv = usb_get_serial_data(serial);
|
|
|
struct cp210x_gpio_write buf;
|
|
|
+ int result;
|
|
|
|
|
|
if (value == 1)
|
|
|
buf.state = BIT(gpio);
|
|
@@ -1329,25 +1375,68 @@ static void cp210x_gpio_set(struct gpio_chip *gc, unsigned int gpio, int value)
|
|
|
|
|
|
buf.mask = BIT(gpio);
|
|
|
|
|
|
- cp210x_write_vendor_block(serial, REQTYPE_HOST_TO_INTERFACE,
|
|
|
- CP210X_WRITE_LATCH, &buf, sizeof(buf));
|
|
|
+ if (priv->partnum == CP210X_PARTNUM_CP2105) {
|
|
|
+ result = cp210x_write_vendor_block(serial,
|
|
|
+ REQTYPE_HOST_TO_INTERFACE,
|
|
|
+ CP210X_WRITE_LATCH, &buf,
|
|
|
+ sizeof(buf));
|
|
|
+ } else {
|
|
|
+ u16 wIndex = buf.state << 8 | buf.mask;
|
|
|
+
|
|
|
+ result = usb_control_msg(serial->dev,
|
|
|
+ usb_sndctrlpipe(serial->dev, 0),
|
|
|
+ CP210X_VENDOR_SPECIFIC,
|
|
|
+ REQTYPE_HOST_TO_DEVICE,
|
|
|
+ CP210X_WRITE_LATCH,
|
|
|
+ wIndex,
|
|
|
+ NULL, 0, USB_CTRL_SET_TIMEOUT);
|
|
|
+ }
|
|
|
+
|
|
|
+ if (result < 0) {
|
|
|
+ dev_err(&serial->interface->dev, "failed to set GPIO value: %d\n",
|
|
|
+ result);
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
static int cp210x_gpio_direction_get(struct gpio_chip *gc, unsigned int gpio)
|
|
|
{
|
|
|
- /* Hardware does not support an input mode */
|
|
|
- return 0;
|
|
|
+ struct usb_serial *serial = gpiochip_get_data(gc);
|
|
|
+ struct cp210x_serial_private *priv = usb_get_serial_data(serial);
|
|
|
+
|
|
|
+ return priv->gpio_input & BIT(gpio);
|
|
|
}
|
|
|
|
|
|
static int cp210x_gpio_direction_input(struct gpio_chip *gc, unsigned int gpio)
|
|
|
{
|
|
|
- /* Hardware does not support an input mode */
|
|
|
- return -ENOTSUPP;
|
|
|
+ struct usb_serial *serial = gpiochip_get_data(gc);
|
|
|
+ struct cp210x_serial_private *priv = usb_get_serial_data(serial);
|
|
|
+
|
|
|
+ if (priv->partnum == CP210X_PARTNUM_CP2105) {
|
|
|
+ /* hardware does not support an input mode */
|
|
|
+ return -ENOTSUPP;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* push-pull pins cannot be changed to be inputs */
|
|
|
+ if (priv->gpio_pushpull & BIT(gpio))
|
|
|
+ return -EINVAL;
|
|
|
+
|
|
|
+ /* make sure to release pin if it is being driven low */
|
|
|
+ cp210x_gpio_set(gc, gpio, 1);
|
|
|
+
|
|
|
+ priv->gpio_input |= BIT(gpio);
|
|
|
+
|
|
|
+ return 0;
|
|
|
}
|
|
|
|
|
|
static int cp210x_gpio_direction_output(struct gpio_chip *gc, unsigned int gpio,
|
|
|
int value)
|
|
|
{
|
|
|
+ struct usb_serial *serial = gpiochip_get_data(gc);
|
|
|
+ struct cp210x_serial_private *priv = usb_get_serial_data(serial);
|
|
|
+
|
|
|
+ priv->gpio_input &= ~BIT(gpio);
|
|
|
+ cp210x_gpio_set(gc, gpio, value);
|
|
|
+
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
@@ -1360,11 +1449,11 @@ static int cp210x_gpio_set_config(struct gpio_chip *gc, unsigned int gpio,
|
|
|
|
|
|
/* Succeed only if in correct mode (this can't be set at runtime) */
|
|
|
if ((param == PIN_CONFIG_DRIVE_PUSH_PULL) &&
|
|
|
- (priv->gpio_mode & BIT(gpio)))
|
|
|
+ (priv->gpio_pushpull & BIT(gpio)))
|
|
|
return 0;
|
|
|
|
|
|
if ((param == PIN_CONFIG_DRIVE_OPEN_DRAIN) &&
|
|
|
- !(priv->gpio_mode & BIT(gpio)))
|
|
|
+ !(priv->gpio_pushpull & BIT(gpio)))
|
|
|
return 0;
|
|
|
|
|
|
return -ENOTSUPP;
|
|
@@ -1377,12 +1466,13 @@ static int cp210x_gpio_set_config(struct gpio_chip *gc, unsigned int gpio,
|
|
|
* this driver that provide GPIO do so in a way that does not impact other
|
|
|
* signals and are thus expected to have very different initialisation.
|
|
|
*/
|
|
|
-static int cp2105_shared_gpio_init(struct usb_serial *serial)
|
|
|
+static int cp2105_gpioconf_init(struct usb_serial *serial)
|
|
|
{
|
|
|
struct cp210x_serial_private *priv = usb_get_serial_data(serial);
|
|
|
struct cp210x_pin_mode mode;
|
|
|
struct cp210x_config config;
|
|
|
u8 intf_num = cp210x_interface_num(serial);
|
|
|
+ u8 iface_config;
|
|
|
int result;
|
|
|
|
|
|
result = cp210x_read_vendor_block(serial, REQTYPE_DEVICE_TO_HOST,
|
|
@@ -1399,20 +1489,26 @@ static int cp2105_shared_gpio_init(struct usb_serial *serial)
|
|
|
|
|
|
/* 2 banks of GPIO - One for the pins taken from each serial port */
|
|
|
if (intf_num == 0) {
|
|
|
- if (mode.eci == CP210X_PIN_MODE_MODEM)
|
|
|
+ if (mode.eci == CP210X_PIN_MODE_MODEM) {
|
|
|
+ /* mark all GPIOs of this interface as reserved */
|
|
|
+ priv->gpio_altfunc = 0xff;
|
|
|
return 0;
|
|
|
+ }
|
|
|
|
|
|
- priv->config = config.eci_cfg;
|
|
|
- priv->gpio_mode = (u8)((le16_to_cpu(config.gpio_mode) &
|
|
|
+ iface_config = config.eci_cfg;
|
|
|
+ priv->gpio_pushpull = (u8)((le16_to_cpu(config.gpio_mode) &
|
|
|
CP210X_ECI_GPIO_MODE_MASK) >>
|
|
|
CP210X_ECI_GPIO_MODE_OFFSET);
|
|
|
priv->gc.ngpio = 2;
|
|
|
} else if (intf_num == 1) {
|
|
|
- if (mode.sci == CP210X_PIN_MODE_MODEM)
|
|
|
+ if (mode.sci == CP210X_PIN_MODE_MODEM) {
|
|
|
+ /* mark all GPIOs of this interface as reserved */
|
|
|
+ priv->gpio_altfunc = 0xff;
|
|
|
return 0;
|
|
|
+ }
|
|
|
|
|
|
- priv->config = config.sci_cfg;
|
|
|
- priv->gpio_mode = (u8)((le16_to_cpu(config.gpio_mode) &
|
|
|
+ iface_config = config.sci_cfg;
|
|
|
+ priv->gpio_pushpull = (u8)((le16_to_cpu(config.gpio_mode) &
|
|
|
CP210X_SCI_GPIO_MODE_MASK) >>
|
|
|
CP210X_SCI_GPIO_MODE_OFFSET);
|
|
|
priv->gc.ngpio = 3;
|
|
@@ -1420,6 +1516,125 @@ static int cp2105_shared_gpio_init(struct usb_serial *serial)
|
|
|
return -ENODEV;
|
|
|
}
|
|
|
|
|
|
+ /* mark all pins which are not in GPIO mode */
|
|
|
+ if (iface_config & CP2105_GPIO0_TXLED_MODE) /* GPIO 0 */
|
|
|
+ priv->gpio_altfunc |= BIT(0);
|
|
|
+ if (iface_config & (CP2105_GPIO1_RXLED_MODE | /* GPIO 1 */
|
|
|
+ CP2105_GPIO1_RS485_MODE))
|
|
|
+ priv->gpio_altfunc |= BIT(1);
|
|
|
+
|
|
|
+ /* driver implementation for CP2105 only supports outputs */
|
|
|
+ priv->gpio_input = 0;
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static int cp2102n_gpioconf_init(struct usb_serial *serial)
|
|
|
+{
|
|
|
+ struct cp210x_serial_private *priv = usb_get_serial_data(serial);
|
|
|
+ const u16 config_size = 0x02a6;
|
|
|
+ u8 gpio_rst_latch;
|
|
|
+ u8 config_version;
|
|
|
+ u8 gpio_pushpull;
|
|
|
+ u8 *config_buf;
|
|
|
+ u8 gpio_latch;
|
|
|
+ u8 gpio_ctrl;
|
|
|
+ int result;
|
|
|
+ u8 i;
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Retrieve device configuration from the device.
|
|
|
+ * The array received contains all customization settings done at the
|
|
|
+ * factory/manufacturer. Format of the array is documented at the
|
|
|
+ * time of writing at:
|
|
|
+ * https://www.silabs.com/community/interface/knowledge-base.entry.html/2017/03/31/cp2102n_setconfig-xsfa
|
|
|
+ */
|
|
|
+ config_buf = kmalloc(config_size, GFP_KERNEL);
|
|
|
+ if (!config_buf)
|
|
|
+ return -ENOMEM;
|
|
|
+
|
|
|
+ result = cp210x_read_vendor_block(serial,
|
|
|
+ REQTYPE_DEVICE_TO_HOST,
|
|
|
+ CP210X_READ_2NCONFIG,
|
|
|
+ config_buf,
|
|
|
+ config_size);
|
|
|
+ if (result < 0) {
|
|
|
+ kfree(config_buf);
|
|
|
+ return result;
|
|
|
+ }
|
|
|
+
|
|
|
+ config_version = config_buf[CP210X_2NCONFIG_CONFIG_VERSION_IDX];
|
|
|
+ gpio_pushpull = config_buf[CP210X_2NCONFIG_GPIO_MODE_IDX];
|
|
|
+ gpio_ctrl = config_buf[CP210X_2NCONFIG_GPIO_CONTROL_IDX];
|
|
|
+ gpio_rst_latch = config_buf[CP210X_2NCONFIG_GPIO_RSTLATCH_IDX];
|
|
|
+
|
|
|
+ kfree(config_buf);
|
|
|
+
|
|
|
+ /* Make sure this is a config format we understand. */
|
|
|
+ if (config_version != 0x01)
|
|
|
+ return -ENOTSUPP;
|
|
|
+
|
|
|
+ /*
|
|
|
+ * We only support 4 GPIOs even on the QFN28 package, because
|
|
|
+ * config locations of GPIOs 4-6 determined using reverse
|
|
|
+ * engineering revealed conflicting offsets with other
|
|
|
+ * documented functions. So we'll just play it safe for now.
|
|
|
+ */
|
|
|
+ priv->gc.ngpio = 4;
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Get default pin states after reset. Needed so we can determine
|
|
|
+ * the direction of an open-drain pin.
|
|
|
+ */
|
|
|
+ gpio_latch = (gpio_rst_latch >> 3) & 0x0f;
|
|
|
+
|
|
|
+ /* 0 indicates open-drain mode, 1 is push-pull */
|
|
|
+ priv->gpio_pushpull = (gpio_pushpull >> 3) & 0x0f;
|
|
|
+
|
|
|
+ /* 0 indicates GPIO mode, 1 is alternate function */
|
|
|
+ priv->gpio_altfunc = (gpio_ctrl >> 2) & 0x0f;
|
|
|
+
|
|
|
+ /*
|
|
|
+ * The CP2102N does not strictly has input and output pin modes,
|
|
|
+ * it only knows open-drain and push-pull modes which is set at
|
|
|
+ * factory. An open-drain pin can function both as an
|
|
|
+ * input or an output. We emulate input mode for open-drain pins
|
|
|
+ * by making sure they are not driven low, and we do not allow
|
|
|
+ * push-pull pins to be set as an input.
|
|
|
+ */
|
|
|
+ for (i = 0; i < priv->gc.ngpio; ++i) {
|
|
|
+ /*
|
|
|
+ * Set direction to "input" iff pin is open-drain and reset
|
|
|
+ * value is 1.
|
|
|
+ */
|
|
|
+ if (!(priv->gpio_pushpull & BIT(i)) && (gpio_latch & BIT(i)))
|
|
|
+ priv->gpio_input |= BIT(i);
|
|
|
+ }
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static int cp210x_gpio_init(struct usb_serial *serial)
|
|
|
+{
|
|
|
+ struct cp210x_serial_private *priv = usb_get_serial_data(serial);
|
|
|
+ int result;
|
|
|
+
|
|
|
+ switch (priv->partnum) {
|
|
|
+ case CP210X_PARTNUM_CP2105:
|
|
|
+ result = cp2105_gpioconf_init(serial);
|
|
|
+ break;
|
|
|
+ case CP210X_PARTNUM_CP2102N_QFN28:
|
|
|
+ case CP210X_PARTNUM_CP2102N_QFN24:
|
|
|
+ case CP210X_PARTNUM_CP2102N_QFN20:
|
|
|
+ result = cp2102n_gpioconf_init(serial);
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (result < 0)
|
|
|
+ return result;
|
|
|
+
|
|
|
priv->gc.label = "cp210x";
|
|
|
priv->gc.request = cp210x_gpio_request;
|
|
|
priv->gc.get_direction = cp210x_gpio_direction_get;
|
|
@@ -1452,7 +1667,7 @@ static void cp210x_gpio_remove(struct usb_serial *serial)
|
|
|
|
|
|
#else
|
|
|
|
|
|
-static int cp2105_shared_gpio_init(struct usb_serial *serial)
|
|
|
+static int cp210x_gpio_init(struct usb_serial *serial)
|
|
|
{
|
|
|
return 0;
|
|
|
}
|
|
@@ -1497,6 +1712,50 @@ static int cp210x_port_remove(struct usb_serial_port *port)
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
+static void cp210x_init_max_speed(struct usb_serial *serial)
|
|
|
+{
|
|
|
+ struct cp210x_serial_private *priv = usb_get_serial_data(serial);
|
|
|
+ bool use_actual_rate = false;
|
|
|
+ speed_t max;
|
|
|
+
|
|
|
+ switch (priv->partnum) {
|
|
|
+ case CP210X_PARTNUM_CP2101:
|
|
|
+ max = 921600;
|
|
|
+ break;
|
|
|
+ case CP210X_PARTNUM_CP2102:
|
|
|
+ case CP210X_PARTNUM_CP2103:
|
|
|
+ max = 1000000;
|
|
|
+ break;
|
|
|
+ case CP210X_PARTNUM_CP2104:
|
|
|
+ use_actual_rate = true;
|
|
|
+ max = 2000000;
|
|
|
+ break;
|
|
|
+ case CP210X_PARTNUM_CP2108:
|
|
|
+ max = 2000000;
|
|
|
+ break;
|
|
|
+ case CP210X_PARTNUM_CP2105:
|
|
|
+ if (cp210x_interface_num(serial) == 0) {
|
|
|
+ use_actual_rate = true;
|
|
|
+ max = 2000000; /* ECI */
|
|
|
+ } else {
|
|
|
+ max = 921600; /* SCI */
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ case CP210X_PARTNUM_CP2102N_QFN28:
|
|
|
+ case CP210X_PARTNUM_CP2102N_QFN24:
|
|
|
+ case CP210X_PARTNUM_CP2102N_QFN20:
|
|
|
+ use_actual_rate = true;
|
|
|
+ max = 3000000;
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ max = 2000000;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ priv->max_speed = max;
|
|
|
+ priv->use_actual_rate = use_actual_rate;
|
|
|
+}
|
|
|
+
|
|
|
static int cp210x_attach(struct usb_serial *serial)
|
|
|
{
|
|
|
int result;
|
|
@@ -1517,12 +1776,12 @@ static int cp210x_attach(struct usb_serial *serial)
|
|
|
|
|
|
usb_set_serial_data(serial, priv);
|
|
|
|
|
|
- if (priv->partnum == CP210X_PARTNUM_CP2105) {
|
|
|
- result = cp2105_shared_gpio_init(serial);
|
|
|
- if (result < 0) {
|
|
|
- dev_err(&serial->interface->dev,
|
|
|
- "GPIO initialisation failed, continuing without GPIO support\n");
|
|
|
- }
|
|
|
+ cp210x_init_max_speed(serial);
|
|
|
+
|
|
|
+ result = cp210x_gpio_init(serial);
|
|
|
+ if (result < 0) {
|
|
|
+ dev_err(&serial->interface->dev, "GPIO initialisation failed: %d\n",
|
|
|
+ result);
|
|
|
}
|
|
|
|
|
|
return 0;
|