* [PATCH v6] USB: serial: cp210x: Add support for GPIOs on CP2108
@ 2021-04-05 6:18 Pho Tran
2021-04-05 9:23 ` kernel test robot
0 siblings, 1 reply; 3+ messages in thread
From: Pho Tran @ 2021-04-05 6:18 UTC (permalink / raw)
To: johan, gregkh; +Cc: linux-usb, linux-kernel, Hung.Nguyen, Tung.Pham, Pho Tran
From: Pho Tran <pho.tran@silabs.com>
Similar to other CP210x devices, GPIO interfaces (gpiochip) should be
supported for CP2108.
CP2108 has 4 serial interfaces but only 1 set of GPIO pins are shared
to all of those interfaces. So, just need to initialize GPIOs of CP2108
with only one interface (I use interface 0). It means just only 1 gpiochip
device file will be created for CP2108.
CP2108 has 16 GPIOs, So data types of several variables need to be is u16
instead of u8(in struct cp210x_serial_private). This doesn't affect other
CP210x devices.
Because CP2108 has 16 GPIO pins, the parameter passed by cp210x functions
will be different from other CP210x devices. So need to check part number
of the device to use correct data format before sending commands to
devices.
Like CP2104, CP2108 have GPIO pins with configurable options. Therefore,
should be mask all pins which are not in GPIO mode in cp2108_gpio_init()
function. Alternate functions of GPIO0 to GPIO3 is determine by
enhancedfxn_IFC[0] and similar for enhancedfxn_IFC[1] and
enhancedfxn_IFC[2]. Refer to CP2108 datasheet at sector
"9: GPIO and UART pins" for more detail:
https://www.silabs.com/documents/public/data-sheets/cp2108-datasheet.pdf
Signed-off-by: Pho Tran <pho.tran@silabs.com>
---
03/15/2021: Patch v5 Modifed code according to comment of Johan:
1. Unified the handling of CP2108 and other types and
take care about endianness.
2. Used suitable types data for variable.
3. Fixed cp2108_gpio_init and add more detail on
commit message and comment.
4. Dropped some of the ones that don't add any value.
03/12/2021: Patch v4 used git send-mail instead of send patch by manual
follow the instructions of Johan Hovold <johan@kernel.org>.
03/05/2021: Patch v3 modified format and contents of changelog follow feedback
from Johan Hovold <johan@kernel.org>.
03/04/2021: Patch v2 modified format patch as comment from
Johan Hovold <johan@kernel.org>:
1. Break commit message lines at 80 cols
2. Use kernel u8 and u16 instead of the c99 ones.
03/01/2021: Initialed submission of patch "Make the CP210x driver work with
GPIOs of CP2108.".
drivers/usb/serial/cp210x.c | 253 +++++++++++++++++++++++++++++++-----
1 file changed, 219 insertions(+), 34 deletions(-)
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;
}
--
2.17.1
^ permalink raw reply related [flat|nested] 3+ messages in thread
* Re: [PATCH v6] USB: serial: cp210x: Add support for GPIOs on CP2108
2021-04-05 6:18 [PATCH v6] USB: serial: cp210x: Add support for GPIOs on CP2108 Pho Tran
@ 2021-04-05 9:23 ` kernel test robot
0 siblings, 0 replies; 3+ messages in thread
From: kernel test robot @ 2021-04-05 9:23 UTC (permalink / raw)
To: Pho Tran, johan, gregkh
Cc: kbuild-all, linux-usb, linux-kernel, Hung.Nguyen, Tung.Pham, Pho Tran
[-- Attachment #1: Type: text/plain, Size: 12644 bytes --]
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
[-- Attachment #2: .config.gz --]
[-- Type: application/gzip, Size: 34377 bytes --]
^ permalink raw reply [flat|nested] 3+ messages in thread
* Re: [PATCH v6] USB: serial: cp210x: Add support for GPIOs on CP2108
@ 2021-04-05 9:23 ` kernel test robot
0 siblings, 0 replies; 3+ messages in thread
From: kernel test robot @ 2021-04-05 9:23 UTC (permalink / raw)
To: kbuild-all
[-- Attachment #1: Type: text/plain, Size: 12936 bytes --]
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(a)lists.01.org
[-- Attachment #2: config.gz --]
[-- Type: application/gzip, Size: 34377 bytes --]
^ permalink raw reply [flat|nested] 3+ messages in thread
end of thread, other threads:[~2021-04-05 9:24 UTC | newest]
Thread overview: 3+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-04-05 6:18 [PATCH v6] USB: serial: cp210x: Add support for GPIOs on CP2108 Pho Tran
2021-04-05 9:23 ` kernel test robot
2021-04-05 9:23 ` kernel test robot
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.