Message ID | 20210405061823.3855-1-photranvan0712@gmail.com (mailing list archive) |
---|---|
State | Superseded |
Headers | show |
Series | [v6] USB: serial: cp210x: Add support for GPIOs on CP2108 | expand |
Hi Pho, Thank you for the patch! Perhaps something to improve: [auto build test WARNING on usb-serial/usb-next] [also build test WARNING on usb/usb-testing tty/tty-testing v5.12-rc6 next-20210401] [If your patch is applied to the wrong git tree, kindly drop us a note. And when submitting patch, we suggest to use '--base' as documented in https://git-scm.com/docs/git-format-patch] url: https://github.com/0day-ci/linux/commits/Pho-Tran/USB-serial-cp210x-Add-support-for-GPIOs-on-CP2108/20210405-141927 base: https://git.kernel.org/pub/scm/linux/kernel/git/johan/usb-serial.git usb-next config: x86_64-randconfig-s022-20210405 (attached as .config) compiler: gcc-9 (Debian 9.3.0-22) 9.3.0 reproduce: # apt-get install sparse # sparse version: v0.6.3-279-g6d5d9b42-dirty # https://github.com/0day-ci/linux/commit/a4658078c55d4396be894b30d3b94fd86b770f45 git remote add linux-review https://github.com/0day-ci/linux git fetch --no-tags linux-review Pho-Tran/USB-serial-cp210x-Add-support-for-GPIOs-on-CP2108/20210405-141927 git checkout a4658078c55d4396be894b30d3b94fd86b770f45 # save the attached .config to linux build tree make W=1 C=1 CF='-fdiagnostic-prefix -D__CHECK_ENDIAN__' ARCH=x86_64 If you fix the issue, kindly add following tag as appropriate Reported-by: kernel test robot <lkp@intel.com> sparse warnings: (new ones prefixed by >>) >> drivers/usb/serial/cp210x.c:1540:13: sparse: sparse: incorrect type in assignment (different base types) @@ expected restricted __le16 [addressable] [usertype] buf @@ got unsigned short [usertype] @@ drivers/usb/serial/cp210x.c:1540:13: sparse: expected restricted __le16 [addressable] [usertype] buf drivers/usb/serial/cp210x.c:1540:13: sparse: got unsigned short [usertype] >> drivers/usb/serial/cp210x.c:1542:19: sparse: sparse: restricted __le16 degrades to integer >> drivers/usb/serial/cp210x.c:1581:24: sparse: sparse: incorrect type in assignment (different base types) @@ expected restricted __le16 [usertype] wIndex @@ got int @@ drivers/usb/serial/cp210x.c:1581:24: sparse: expected restricted __le16 [usertype] wIndex drivers/usb/serial/cp210x.c:1581:24: sparse: got int >> drivers/usb/serial/cp210x.c:1587:57: sparse: sparse: incorrect type in argument 6 (different base types) @@ expected unsigned short [usertype] index @@ got restricted __le16 [usertype] wIndex @@ drivers/usb/serial/cp210x.c:1587:57: sparse: expected unsigned short [usertype] index drivers/usb/serial/cp210x.c:1587:57: sparse: got restricted __le16 [usertype] wIndex >> drivers/usb/serial/cp210x.c:1676:14: sparse: sparse: incorrect type in assignment (different base types) @@ expected restricted __le16 [usertype] temp @@ got unsigned short [usertype] @@ drivers/usb/serial/cp210x.c:1676:14: sparse: expected restricted __le16 [usertype] temp drivers/usb/serial/cp210x.c:1676:14: sparse: got unsigned short [usertype] drivers/usb/serial/cp210x.c:1677:32: sparse: sparse: restricted __le16 degrades to integer drivers/usb/serial/cp210x.c:1678:14: sparse: sparse: incorrect type in assignment (different base types) @@ expected restricted __le16 [usertype] temp @@ got unsigned short [usertype] @@ drivers/usb/serial/cp210x.c:1678:14: sparse: expected restricted __le16 [usertype] temp drivers/usb/serial/cp210x.c:1678:14: sparse: got unsigned short [usertype] drivers/usb/serial/cp210x.c:1679:23: sparse: sparse: restricted __le16 degrades to integer >> drivers/usb/serial/cp210x.c:1679:20: sparse: sparse: incorrect type in assignment (different base types) @@ expected restricted __le16 [usertype] gpio_latch @@ got unsigned long @@ drivers/usb/serial/cp210x.c:1679:20: sparse: expected restricted __le16 [usertype] gpio_latch drivers/usb/serial/cp210x.c:1679:20: sparse: got unsigned long drivers/usb/serial/cp210x.c:1721:57: sparse: sparse: restricted __le16 degrades to integer vim +1540 drivers/usb/serial/cp210x.c 1496 1497 static int cp210x_gpio_get(struct gpio_chip *gc, unsigned int gpio) 1498 { 1499 struct usb_serial *serial = gpiochip_get_data(gc); 1500 struct cp210x_serial_private *priv = usb_get_serial_data(serial); 1501 u8 req_type = REQTYPE_DEVICE_TO_HOST; 1502 int result; 1503 __le16 buf; 1504 1505 result = usb_autopm_get_interface(serial->interface); 1506 if (result) 1507 return result; 1508 /* 1509 * This function will be read latch value of gpio and storage to buf(16bit) 1510 * where bit 0 is GPIO0, bit 1 is GPIO1, etc. Up to GPIOn where n is 1511 * total number of GPIO pins the interface supports. 1512 * Interfaces on CP2102N supports 7 GPIOs 1513 * Interfaces on CP2103 amd CP2104 supports 4 GPIOs 1514 * Enhanced interfaces on CP2105 support 3 GPIOs 1515 * Standard interfaces on CP2105 support 4 GPIOs 1516 * Interfaces on CP2108 supports 16 GPIOs 1517 */ 1518 switch (priv->partnum) { 1519 /* 1520 * Request type to Read_Latch of CP2105 and Cp2108 1521 * is 0xc1 <REQTYPE_INTERFACE_TO_HOST> 1522 */ 1523 case CP210X_PARTNUM_CP2108: 1524 req_type = REQTYPE_INTERFACE_TO_HOST; 1525 result = cp210x_read_vendor_block(serial, req_type, 1526 CP210X_READ_LATCH, &buf, sizeof(__le16)); 1527 break; 1528 case CP210X_PARTNUM_CP2105: 1529 req_type = REQTYPE_INTERFACE_TO_HOST; 1530 result = cp210x_read_vendor_block(serial, req_type, 1531 CP210X_READ_LATCH, &buf, sizeof(u8)); 1532 break; 1533 default: 1534 result = cp210x_read_vendor_block(serial, req_type, 1535 CP210X_READ_LATCH, &buf, sizeof(u8)); 1536 break; 1537 } 1538 if (result < 0) 1539 return result; > 1540 buf = le16_to_cpu(buf); 1541 usb_autopm_put_interface(serial->interface); > 1542 return !!(buf & BIT(gpio)); 1543 } 1544 1545 static void cp210x_gpio_set(struct gpio_chip *gc, unsigned int gpio, int value) 1546 { 1547 struct usb_serial *serial = gpiochip_get_data(gc); 1548 struct cp210x_serial_private *priv = usb_get_serial_data(serial); 1549 struct cp210x_gpio_write buf; 1550 __le16 wIndex; 1551 int result; 1552 1553 if (value == 1) { 1554 buf.cp210x_8gpios.state = BIT(gpio); 1555 buf.cp210x_16gpios.state = cpu_to_le16(BIT(gpio)); 1556 } else { 1557 buf.cp210x_8gpios.state = 0; 1558 buf.cp210x_16gpios.state = 0; 1559 } 1560 buf.cp210x_8gpios.mask = BIT(gpio); 1561 buf.cp210x_16gpios.mask = cpu_to_le16(BIT(gpio)); 1562 1563 result = usb_autopm_get_interface(serial->interface); 1564 if (result) 1565 goto out; 1566 1567 switch (priv->partnum) { 1568 case CP210X_PARTNUM_CP2108: 1569 result = cp210x_write_vendor_block(serial, 1570 REQTYPE_HOST_TO_INTERFACE, 1571 CP210X_WRITE_LATCH, &buf.cp210x_16gpios, 1572 sizeof(buf.cp210x_16gpios)); 1573 break; 1574 case CP210X_PARTNUM_CP2105: 1575 result = cp210x_write_vendor_block(serial, 1576 REQTYPE_HOST_TO_INTERFACE, 1577 CP210X_WRITE_LATCH, &buf.cp210x_8gpios, 1578 sizeof(buf.cp210x_8gpios)); 1579 break; 1580 default: > 1581 wIndex = buf.cp210x_8gpios.state << 8 | buf.cp210x_8gpios.mask; 1582 result = usb_control_msg(serial->dev, 1583 usb_sndctrlpipe(serial->dev, 0), 1584 CP210X_VENDOR_SPECIFIC, 1585 REQTYPE_HOST_TO_DEVICE, 1586 CP210X_WRITE_LATCH, > 1587 wIndex, 1588 NULL, 0, USB_CTRL_SET_TIMEOUT); 1589 break; 1590 } 1591 usb_autopm_put_interface(serial->interface); 1592 1593 out: 1594 if (result < 0) { 1595 dev_err(&serial->interface->dev, "failed to set GPIO value: %d\n", 1596 result); 1597 } 1598 } 1599 1600 static int cp210x_gpio_direction_get(struct gpio_chip *gc, unsigned int gpio) 1601 { 1602 struct usb_serial *serial = gpiochip_get_data(gc); 1603 struct cp210x_serial_private *priv = usb_get_serial_data(serial); 1604 1605 return priv->gpio_input & BIT(gpio); 1606 } 1607 1608 static int cp210x_gpio_direction_input(struct gpio_chip *gc, unsigned int gpio) 1609 { 1610 struct usb_serial *serial = gpiochip_get_data(gc); 1611 struct cp210x_serial_private *priv = usb_get_serial_data(serial); 1612 1613 if (priv->partnum == CP210X_PARTNUM_CP2105) { 1614 /* hardware does not support an input mode */ 1615 return -ENOTSUPP; 1616 } 1617 1618 /* push-pull pins cannot be changed to be inputs */ 1619 if (priv->gpio_pushpull & BIT(gpio)) 1620 return -EINVAL; 1621 1622 /* make sure to release pin if it is being driven low */ 1623 cp210x_gpio_set(gc, gpio, 1); 1624 1625 priv->gpio_input |= BIT(gpio); 1626 1627 return 0; 1628 } 1629 1630 static int cp210x_gpio_direction_output(struct gpio_chip *gc, unsigned int gpio, 1631 int value) 1632 { 1633 struct usb_serial *serial = gpiochip_get_data(gc); 1634 struct cp210x_serial_private *priv = usb_get_serial_data(serial); 1635 1636 priv->gpio_input &= ~BIT(gpio); 1637 cp210x_gpio_set(gc, gpio, value); 1638 1639 return 0; 1640 } 1641 1642 static int cp210x_gpio_set_config(struct gpio_chip *gc, unsigned int gpio, 1643 unsigned long config) 1644 { 1645 struct usb_serial *serial = gpiochip_get_data(gc); 1646 struct cp210x_serial_private *priv = usb_get_serial_data(serial); 1647 enum pin_config_param param = pinconf_to_config_param(config); 1648 1649 /* Succeed only if in correct mode (this can't be set at runtime) */ 1650 if ((param == PIN_CONFIG_DRIVE_PUSH_PULL) && 1651 (priv->gpio_pushpull & BIT(gpio))) 1652 return 0; 1653 1654 if ((param == PIN_CONFIG_DRIVE_OPEN_DRAIN) && 1655 !(priv->gpio_pushpull & BIT(gpio))) 1656 return 0; 1657 1658 return -ENOTSUPP; 1659 } 1660 1661 static int cp2108_gpio_init(struct usb_serial *serial) 1662 { 1663 struct cp210x_serial_private *priv = usb_get_serial_data(serial); 1664 struct cp210x_quad_port_config config; 1665 __le16 gpio_latch; 1666 __le16 temp; 1667 int result; 1668 u8 i; 1669 1670 result = cp210x_read_vendor_block(serial, REQTYPE_DEVICE_TO_HOST, 1671 CP210X_GET_PORTCONFIG, &config, 1672 sizeof(config)); 1673 if (result < 0) 1674 return result; 1675 priv->gc.ngpio = 16; > 1676 temp = le16_to_cpu(config.reset_state.gpio_mode_PB1); 1677 priv->gpio_pushpull = (temp & CP2108_GPIO_MODE_MASK) >> CP2108_GPIO_MODE_OFFSET; 1678 temp = le16_to_cpu(config.reset_state.gpio_latch_PB1); > 1679 gpio_latch = (temp & CP2108_GPIO_MODE_MASK) >> CP2108_GPIO_MODE_OFFSET; 1680 /* 1681 * Mark all pins which are not in GPIO mode 1682 * Refer to table 9.1: GPIO Mode alternate Functions on CP2108 datasheet: 1683 * https://www.silabs.com/documents/public/data-sheets/cp2108-datasheet.pdf 1684 * Alternate Functions of GPIO0 to GPIO3 is determine by enhancedfxn_IFC[0] 1685 * and the same for other pins, enhancedfxn_IFC[1]: GPIO4 to GPIO7, 1686 * enhancedfxn_IFC[2]: GPIO8 to GPIO11, enhancedfxn_IFC[3]: GPIO12 to GPIO15. 1687 */ 1688 for (i = 0; i < 4; i++) { 1689 switch (config.enhancedfxn_IFC[i]) { 1690 case EF_IFC_GPIO_TXLED: 1691 priv->gpio_altfunc |= BIT(i * 4); 1692 break; 1693 case EF_IFC_GPIO_RXLED: 1694 priv->gpio_altfunc |= BIT((i * 4) + 1); 1695 break; 1696 case EF_IFC_GPIO_RS485_LOGIC: 1697 case EF_IFC_GPIO_RS485: 1698 priv->gpio_altfunc |= BIT((i * 4) + 2); 1699 break; 1700 case EF_IFC_GPIO_CLOCK: 1701 priv->gpio_altfunc |= BIT((i * 4) + 3); 1702 break; 1703 case EF_IFC_DYNAMIC_SUSPEND: 1704 priv->gpio_altfunc |= BIT(i * 4); 1705 priv->gpio_altfunc |= BIT((i * 4) + 1); 1706 priv->gpio_altfunc |= BIT((i * 4) + 2); 1707 priv->gpio_altfunc |= BIT((i * 4) + 3); 1708 break; 1709 } 1710 } 1711 /* 1712 * Like CP2102N, CP2108 has also no strict input and output pin 1713 * modes. 1714 * Do the same input mode emulation as CP2102N. 1715 */ 1716 for (i = 0; i < priv->gc.ngpio; ++i) { 1717 /* 1718 * Set direction to "input" iff pin is open-drain and reset 1719 * value is 1. 1720 */ 1721 if (!(priv->gpio_pushpull & BIT(i)) && (gpio_latch & BIT(i))) 1722 priv->gpio_input |= BIT(i); 1723 } 1724 1725 return 0; 1726 } 1727 --- 0-DAY CI Kernel Test Service, Intel Corporation https://lists.01.org/hyperkitty/list/kbuild-all@lists.01.org
diff --git a/drivers/usb/serial/cp210x.c b/drivers/usb/serial/cp210x.c index 7bec1e730b20..f0ecb4315056 100644 --- a/drivers/usb/serial/cp210x.c +++ b/drivers/usb/serial/cp210x.c @@ -245,9 +245,9 @@ struct cp210x_serial_private { #ifdef CONFIG_GPIOLIB struct gpio_chip gc; bool gpio_registered; - u8 gpio_pushpull; - u8 gpio_altfunc; - u8 gpio_input; + u16 gpio_pushpull; + u16 gpio_altfunc; + u16 gpio_input; #endif u8 partnum; speed_t min_speed; @@ -399,6 +399,18 @@ static struct usb_serial_driver * const serial_drivers[] = { #define CP210X_PARTNUM_CP2102N_QFN20 0x22 #define CP210X_PARTNUM_UNKNOWN 0xFF +/* + * CP2108 Define bit locations for EnhancedFxn_IFCx + * Refer to https://www.silabs.com/documents/public/application-notes/an978-cp210x-usb-to-uart-api-specification.pdf + * for more information. + */ +#define EF_IFC_GPIO_TXLED 0x01 +#define EF_IFC_GPIO_RXLED 0x02 +#define EF_IFC_GPIO_RS485 0x04 +#define EF_IFC_GPIO_RS485_LOGIC 0x08 +#define EF_IFC_GPIO_CLOCK 0x10 +#define EF_IFC_DYNAMIC_SUSPEND 0x40 + /* CP210X_GET_COMM_STATUS returns these 0x13 bytes */ struct cp210x_comm_status { __le32 ulErrors; @@ -500,6 +512,45 @@ struct cp210x_single_port_config { u8 device_cfg; } __packed; +/* + * Quad Port Config definitions + * Refer to https://www.silabs.com/documents/public/application-notes/an978-cp210x-usb-to-uart-api-specification.pdf + * for more information. + * CP210X_VENDOR_SPECIFIC, CP210X_GET_PORTCONFIG call reads these 0x49 bytes + * on a CP2108 chip. + * CP2108 Quad Port State structure(used in Quad Port Config structure) + */ +struct cp210x_quad_port_state { + __le16 gpio_mode_PB0; + __le16 gpio_mode_PB1; + __le16 gpio_mode_PB2; + __le16 gpio_mode_PB3; + __le16 gpio_mode_PB4; + + + __le16 gpio_lowpower_PB0; + __le16 gpio_lowpower_PB1; + __le16 gpio_lowpower_PB2; + __le16 gpio_lowpower_PB3; + __le16 gpio_lowpower_PB4; + + __le16 gpio_latch_PB0; + __le16 gpio_latch_PB1; + __le16 gpio_latch_PB2; + __le16 gpio_latch_PB3; + __le16 gpio_latch_PB4; +}; + +// Cp2108 Quad Port Config structure +struct cp210x_quad_port_config { + struct cp210x_quad_port_state reset_state; + struct cp210x_quad_port_state suspend_state; + u8 ipdelay_IFC[4]; + u8 enhancedfxn_IFC[4]; + u8 enhancedfxn_device; + u8 extclkfreq[4]; +} __packed; + /* GPIO modes */ #define CP210X_SCI_GPIO_MODE_OFFSET 9 #define CP210X_SCI_GPIO_MODE_MASK GENMASK(11, 9) @@ -510,6 +561,9 @@ struct cp210x_single_port_config { #define CP210X_GPIO_MODE_OFFSET 8 #define CP210X_GPIO_MODE_MASK GENMASK(11, 8) +#define CP2108_GPIO_MODE_OFFSET 0 +#define CP2108_GPIO_MODE_MASK GENMASK(15, 0) + /* CP2105 port configuration values */ #define CP2105_GPIO0_TXLED_MODE BIT(0) #define CP2105_GPIO1_RXLED_MODE BIT(1) @@ -526,12 +580,31 @@ struct cp210x_single_port_config { #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 { +/* + * CP210X_VENDOR_SPECIFIC, CP210X_WRITE_LATCH call writes these + * 0x04 bytes on CP2108. + */ +struct cp210x_16gpios_write { + __le16 mask; + __le16 state; +}; + +/* + * CP210X_VENDOR_SPECIFIC, CP210X_WRITE_LATCH call writes these + * 0x02 bytes on CP2102N, Cp2103, Cp2104 and CP2105. + */ +struct cp210x_8gpios_write { u8 mask; u8 state; }; +//Struct cp210x_gpio_write include devices have both of 8 gpios and 16 gpios. +struct cp210x_gpio_write { + struct cp210x_8gpios_write cp210x_8gpios; + struct cp210x_16gpios_write cp210x_16gpios; +}; + + /* * Helper to get interface number when we only have struct usb_serial. */ @@ -1298,21 +1371,45 @@ static int cp210x_gpio_get(struct gpio_chip *gc, unsigned int gpio) struct cp210x_serial_private *priv = usb_get_serial_data(serial); u8 req_type = REQTYPE_DEVICE_TO_HOST; int result; - u8 buf; - - if (priv->partnum == CP210X_PARTNUM_CP2105) - req_type = REQTYPE_INTERFACE_TO_HOST; + __le16 buf; result = usb_autopm_get_interface(serial->interface); if (result) return result; - - result = cp210x_read_vendor_block(serial, req_type, - CP210X_READ_LATCH, &buf, sizeof(buf)); - usb_autopm_put_interface(serial->interface); +/* + * This function will be read latch value of gpio and storage to buf(16bit) + * where bit 0 is GPIO0, bit 1 is GPIO1, etc. Up to GPIOn where n is + * total number of GPIO pins the interface supports. + * Interfaces on CP2102N supports 7 GPIOs + * Interfaces on CP2103 amd CP2104 supports 4 GPIOs + * Enhanced interfaces on CP2105 support 3 GPIOs + * Standard interfaces on CP2105 support 4 GPIOs + * Interfaces on CP2108 supports 16 GPIOs + */ + switch (priv->partnum) { + /* + * Request type to Read_Latch of CP2105 and Cp2108 + * is 0xc1 <REQTYPE_INTERFACE_TO_HOST> + */ + case CP210X_PARTNUM_CP2108: + req_type = REQTYPE_INTERFACE_TO_HOST; + result = cp210x_read_vendor_block(serial, req_type, + CP210X_READ_LATCH, &buf, sizeof(__le16)); + break; + case CP210X_PARTNUM_CP2105: + req_type = REQTYPE_INTERFACE_TO_HOST; + result = cp210x_read_vendor_block(serial, req_type, + CP210X_READ_LATCH, &buf, sizeof(u8)); + break; + default: + result = cp210x_read_vendor_block(serial, req_type, + CP210X_READ_LATCH, &buf, sizeof(u8)); + break; + } if (result < 0) return result; - + buf = le16_to_cpu(buf); + usb_autopm_put_interface(serial->interface); return !!(buf & BIT(gpio)); } @@ -1321,37 +1418,49 @@ 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; + __le16 wIndex; int result; - if (value == 1) - buf.state = BIT(gpio); - else - buf.state = 0; - - buf.mask = BIT(gpio); + if (value == 1) { + buf.cp210x_8gpios.state = BIT(gpio); + buf.cp210x_16gpios.state = cpu_to_le16(BIT(gpio)); + } else { + buf.cp210x_8gpios.state = 0; + buf.cp210x_16gpios.state = 0; + } + buf.cp210x_8gpios.mask = BIT(gpio); + buf.cp210x_16gpios.mask = cpu_to_le16(BIT(gpio)); result = usb_autopm_get_interface(serial->interface); if (result) goto out; - if (priv->partnum == CP210X_PARTNUM_CP2105) { + switch (priv->partnum) { + case CP210X_PARTNUM_CP2108: result = cp210x_write_vendor_block(serial, - REQTYPE_HOST_TO_INTERFACE, - CP210X_WRITE_LATCH, &buf, - sizeof(buf)); - } else { - u16 wIndex = buf.state << 8 | buf.mask; - + REQTYPE_HOST_TO_INTERFACE, + CP210X_WRITE_LATCH, &buf.cp210x_16gpios, + sizeof(buf.cp210x_16gpios)); + break; + case CP210X_PARTNUM_CP2105: + result = cp210x_write_vendor_block(serial, + REQTYPE_HOST_TO_INTERFACE, + CP210X_WRITE_LATCH, &buf.cp210x_8gpios, + sizeof(buf.cp210x_8gpios)); + break; + default: + wIndex = buf.cp210x_8gpios.state << 8 | buf.cp210x_8gpios.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); + usb_sndctrlpipe(serial->dev, 0), + CP210X_VENDOR_SPECIFIC, + REQTYPE_HOST_TO_DEVICE, + CP210X_WRITE_LATCH, + wIndex, + NULL, 0, USB_CTRL_SET_TIMEOUT); + break; } - usb_autopm_put_interface(serial->interface); + out: if (result < 0) { dev_err(&serial->interface->dev, "failed to set GPIO value: %d\n", @@ -1420,6 +1529,73 @@ static int cp210x_gpio_set_config(struct gpio_chip *gc, unsigned int gpio, return -ENOTSUPP; } +static int cp2108_gpio_init(struct usb_serial *serial) +{ + struct cp210x_serial_private *priv = usb_get_serial_data(serial); + struct cp210x_quad_port_config config; + __le16 gpio_latch; + __le16 temp; + int result; + u8 i; + + result = cp210x_read_vendor_block(serial, REQTYPE_DEVICE_TO_HOST, + CP210X_GET_PORTCONFIG, &config, + sizeof(config)); + if (result < 0) + return result; + priv->gc.ngpio = 16; + temp = le16_to_cpu(config.reset_state.gpio_mode_PB1); + priv->gpio_pushpull = (temp & CP2108_GPIO_MODE_MASK) >> CP2108_GPIO_MODE_OFFSET; + temp = le16_to_cpu(config.reset_state.gpio_latch_PB1); + gpio_latch = (temp & CP2108_GPIO_MODE_MASK) >> CP2108_GPIO_MODE_OFFSET; + /* + * Mark all pins which are not in GPIO mode + * Refer to table 9.1: GPIO Mode alternate Functions on CP2108 datasheet: + * https://www.silabs.com/documents/public/data-sheets/cp2108-datasheet.pdf + * Alternate Functions of GPIO0 to GPIO3 is determine by enhancedfxn_IFC[0] + * and the same for other pins, enhancedfxn_IFC[1]: GPIO4 to GPIO7, + * enhancedfxn_IFC[2]: GPIO8 to GPIO11, enhancedfxn_IFC[3]: GPIO12 to GPIO15. + */ + for (i = 0; i < 4; i++) { + switch (config.enhancedfxn_IFC[i]) { + case EF_IFC_GPIO_TXLED: + priv->gpio_altfunc |= BIT(i * 4); + break; + case EF_IFC_GPIO_RXLED: + priv->gpio_altfunc |= BIT((i * 4) + 1); + break; + case EF_IFC_GPIO_RS485_LOGIC: + case EF_IFC_GPIO_RS485: + priv->gpio_altfunc |= BIT((i * 4) + 2); + break; + case EF_IFC_GPIO_CLOCK: + priv->gpio_altfunc |= BIT((i * 4) + 3); + break; + case EF_IFC_DYNAMIC_SUSPEND: + priv->gpio_altfunc |= BIT(i * 4); + priv->gpio_altfunc |= BIT((i * 4) + 1); + priv->gpio_altfunc |= BIT((i * 4) + 2); + priv->gpio_altfunc |= BIT((i * 4) + 3); + break; + } + } + /* + * Like CP2102N, CP2108 has also no strict input and output pin + * modes. + * Do the same input mode emulation as CP2102N. + */ + 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; +} + /* * This function is for configuring GPIO using shared pins, where other signals * are made unavailable by configuring the use of GPIO. This is believed to be @@ -1649,6 +1825,15 @@ static int cp210x_gpio_init(struct usb_serial *serial) case CP210X_PARTNUM_CP2102N_QFN20: result = cp2102n_gpioconf_init(serial); break; + case CP210X_PARTNUM_CP2108: + /* + * The GPIOs are not tied to any specific port so onlu register + * once for interface 0. + */ + if (cp210x_interface_num(serial) != 0) + return 0; + result = cp2108_gpio_init(serial); + break; default: return 0; }