Received: by 2002:ad5:474a:0:0:0:0:0 with SMTP id i10csp3108049imu; Sun, 27 Jan 2019 21:58:58 -0800 (PST) X-Google-Smtp-Source: ALg8bN4inFsug5SCEyrKR9e5HiJz9cqVo2YnVTYJj8ynw9dyAiQCvkAZfcjAPAluPfLkFe+ILI/6 X-Received: by 2002:a63:ec13:: with SMTP id j19mr18555210pgh.6.1548655138671; Sun, 27 Jan 2019 21:58:58 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1548655138; cv=none; d=google.com; s=arc-20160816; b=X0qmL9Nm8AQv68pZRXeAgFMJiy3TKxVY9dwqujAZZGQUAsUnTui3Q9AZrTgi1J3fFw t4yvCXgwEX6u+I8oRm2X6yA8kCXBMUWVtodpD/saSp7X1+DDGra1fObkh07sZI8oRRFF lfhDpwtWY48iSmjAzCVHxB0zZk96Z+54W39sFvbVIDD4qjKoJ9A/lC8asasnqvdwbwDs bOLHw0TbgH1eZkTHWRAMlsq6YTb5wrtuOq8NXgeyWfwVyVUrD/hOmUGVINQhN6KQWZj/ 9thYGZ6RXulq2RBDO3ykFg4GxN/YByna20mH5bbHbVg9a4ArTqSFA3lUAPfGNB2LZH75 f76g== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:sender:message-id:date:subject:cc:to:from; bh=CA4hkh2IPqeUafx3vuCle8gLKl+RFvO92fb3hdLr6r8=; b=amr7RluGehminrO4j8h0Aqrr/u9/4D+7O5ajMd00B8nRz596IJE+fzNTo0YcgKEImf +0c1P9CIY/+eCsfVBAtTInKG1hrp71njGvpu1fryYBTOB/TMyn2OGjsf3DjPhrrrvj35 JaYFP1QjCuL3vcYQIrTAfcnoBKOGn1ARKFVaIgvvUdpJ3pi8nOzrMXvdZlHbbQgeg3kN xNvPvpwglacmVuZ8h/JK6z0tRIoOHgugW1RXEUoEq8FI3nWzt9D24KrqjgkCdNrYhWya U12zhYQEpSB0nR+ozMLRHbm0gw3CONXWmYU3tyzkU7Y8iKxURPsVtoucY3k7iWx2XmAJ +PEQ== ARC-Authentication-Results: i=1; mx.google.com; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org Return-Path: Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id l96si32821518plb.292.2019.01.27.21.58.43; Sun, 27 Jan 2019 21:58:58 -0800 (PST) Received-SPF: pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) client-ip=209.132.180.67; Authentication-Results: mx.google.com; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726661AbfA1F6A (ORCPT + 99 others); Mon, 28 Jan 2019 00:58:00 -0500 Received: from hermes.aosc.io ([199.195.250.187]:41100 "EHLO hermes.aosc.io" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726149AbfA1F6A (ORCPT ); Mon, 28 Jan 2019 00:58:00 -0500 Received: from localhost (localhost [127.0.0.1]) (Authenticated sender: icenowy@aosc.io) by hermes.aosc.io (Postfix) with ESMTPSA id 6345919D11F; Mon, 28 Jan 2019 05:57:56 +0000 (UTC) From: Icenowy Zheng To: Johan Hovold , Greg Kroah-Hartman Cc: linux-usb@vger.kernel.org, linux-kernel@vger.kernel.org, Icenowy Zheng Subject: [PATCH] USB: serial: cp210x: add GPIO support for CP2104 Date: Mon, 28 Jan 2019 13:57:17 +0800 Message-Id: <20190128055717.31162-1-icenowy@aosc.io> Sender: linux-kernel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org The CP2104 chips feature 4 controllable GPIO pins, which are similar to the ones on CP2102N chip (output-only when push-pull, output or simulated input mode when open-drain). Add support for the GPIO pins for cp210x driver. The pin get/set routine is shared with CP2102N, but the pinconf initialization code is not shared because the acquisition of GPIO configuration in OTP ROM is similar to CP2105, not CP2102N. Signed-off-by: Icenowy Zheng --- drivers/usb/serial/cp210x.c | 82 +++++++++++++++++++++++++++++++++++-- 1 file changed, 78 insertions(+), 4 deletions(-) diff --git a/drivers/usb/serial/cp210x.c b/drivers/usb/serial/cp210x.c index c0777a374a88..b27cf9d00aec 100644 --- a/drivers/usb/serial/cp210x.c +++ b/drivers/usb/serial/cp210x.c @@ -443,10 +443,10 @@ struct cp210x_pin_mode { #define CP210X_PIN_MODE_GPIO BIT(0) /* - * CP210X_VENDOR_SPECIFIC, CP210X_GET_PORTCONFIG call reads these 0xf bytes. - * Structure needs padding due to unused/unspecified bytes. + * CP210X_VENDOR_SPECIFIC, CP210X_GET_PORTCONFIG call reads these 0xf bytes + * on a CP2105 chip. Structure needs padding due to unused/unspecified bytes. */ -struct cp210x_config { +struct cp210x_dual_port_config { __le16 gpio_mode; u8 __pad0[2]; __le16 reset_state; @@ -457,6 +457,19 @@ struct cp210x_config { u8 device_cfg; } __packed; +/* + * CP210X_VENDOR_SPECIFIC, CP210X_GET_PORTCONFIG call reads these 0xd bytes + * on a CP2104 chip. Structure needs padding due to unused/unspecified bytes. + */ +struct cp210x_single_port_config { + __le16 gpio_mode; + u8 __pad0[2]; + __le16 reset_state; + u8 __pad1[4]; + __le16 suspend_state; + u8 device_cfg; +} __packed; + /* GPIO modes */ #define CP210X_SCI_GPIO_MODE_OFFSET 9 #define CP210X_SCI_GPIO_MODE_MASK GENMASK(11, 9) @@ -464,11 +477,19 @@ struct cp210x_config { #define CP210X_ECI_GPIO_MODE_OFFSET 2 #define CP210X_ECI_GPIO_MODE_MASK GENMASK(3, 2) +#define CP210X_GPIO_MODE_OFFSET 8 +#define CP210X_GPIO_MODE_MASK GENMASK(11, 8) + /* CP2105 port configuration values */ #define CP2105_GPIO0_TXLED_MODE BIT(0) #define CP2105_GPIO1_RXLED_MODE BIT(1) #define CP2105_GPIO1_RS485_MODE BIT(2) +/* CP2104 port configuration values */ +#define CP2104_GPIO0_TXLED_MODE BIT(0) +#define CP2104_GPIO1_RXLED_MODE BIT(1) +#define CP2104_GPIO2_RS485_MODE BIT(2) + /* CP2102N configuration array indices */ #define CP210X_2NCONFIG_CONFIG_VERSION_IDX 2 #define CP210X_2NCONFIG_GPIO_MODE_IDX 581 @@ -1470,7 +1491,7 @@ 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; + struct cp210x_dual_port_config config; u8 intf_num = cp210x_interface_num(serial); u8 iface_config; int result; @@ -1529,6 +1550,56 @@ static int cp2105_gpioconf_init(struct usb_serial *serial) return 0; } +static int cp2104_gpioconf_init(struct usb_serial *serial) +{ + struct cp210x_serial_private *priv = usb_get_serial_data(serial); + struct cp210x_single_port_config config; + u8 iface_config; + u8 gpio_latch; + 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 = 4; + + iface_config = config.device_cfg; + priv->gpio_pushpull = (u8)((le16_to_cpu(config.gpio_mode) & + CP210X_GPIO_MODE_MASK) >> + CP210X_GPIO_MODE_OFFSET); + gpio_latch = (u8)((le16_to_cpu(config.reset_state) & + CP210X_GPIO_MODE_MASK) >> + CP210X_GPIO_MODE_OFFSET); + + /* mark all pins which are not in GPIO mode */ + if (iface_config & CP2104_GPIO0_TXLED_MODE) /* GPIO 0 */ + priv->gpio_altfunc |= BIT(0); + if (iface_config & CP2104_GPIO1_RXLED_MODE) /* GPIO 1 */ + priv->gpio_altfunc |= BIT(1); + if (iface_config & CP2104_GPIO2_RS485_MODE) /* GPIO 2 */ + priv->gpio_altfunc |= BIT(2); + + /* + * Like CP2102N, CP2104 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; +} + static int cp2102n_gpioconf_init(struct usb_serial *serial) { struct cp210x_serial_private *priv = usb_get_serial_data(serial); @@ -1620,6 +1691,9 @@ static int cp210x_gpio_init(struct usb_serial *serial) int result; switch (priv->partnum) { + case CP210X_PARTNUM_CP2104: + result = cp2104_gpioconf_init(serial); + break; case CP210X_PARTNUM_CP2105: result = cp2105_gpioconf_init(serial); break; -- 2.18.1