From patchwork Wed Dec 13 10:32:15 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Enric Balletbo i Serra X-Patchwork-Id: 10109669 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork.web.codeaurora.org (Postfix) with ESMTP id 1D05760327 for ; Wed, 13 Dec 2017 10:44:16 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 0D0F128A3C for ; Wed, 13 Dec 2017 10:44:16 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 0196328B8F; Wed, 13 Dec 2017 10:44:15 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-4.2 required=2.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID, RCVD_IN_DNSWL_MED, UNPARSEABLE_RELAY autolearn=unavailable version=3.3.1 Received: from bombadil.infradead.org (bombadil.infradead.org [65.50.211.133]) (using TLSv1.2 with cipher AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.wl.linuxfoundation.org (Postfix) with ESMTPS id 0C60C28A3C for ; Wed, 13 Dec 2017 10:44:14 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.infradead.org; s=bombadil.20170209; h=Sender: Content-Transfer-Encoding:Content-Type:MIME-Version:Cc:List-Subscribe: List-Help:List-Post:List-Archive:List-Unsubscribe:List-Id:Message-Id:Date: Subject:To:From:Reply-To:Content-ID:Content-Description:Resent-Date: Resent-From:Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID:In-Reply-To: References:List-Owner; bh=IzhxhhgsLwfwsMwQ38OKgtoxDnC9Q29E4f8/Q223vJM=; b=jSB gJza/EpQxnjR9WDN+bvDETwo4vQdwsIDEIp1nsLJ4F7+ETQjrN6wo7VNbu7TxO0tvJ5A542SgaE9p QNsLlQEcOXKktUcJgfjWnvfE0KqFTlpFtlPqXkz6271e7Bziw2CIOzxFH2V0+jIlmnFnVd9NPoNGY zrnlDTwHllz0/a0HXmyzsBVCrkO/WrKrmUD3OzrAZXUGNFqWfs/PFh2AX5xxfEOYMICJ5qeJbr70Z td10rH60MtH/MP70pYbnjoBudNntJsY0b84XBP5wIfTe72ZBFUfIAqQk6utxv0BJZ/n5YFnWbPun+ 2Lkk9Fmar1NOtx/XkKVbbgxdcOYBJ9w==; Received: from localhost ([127.0.0.1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.87 #1 (Red Hat Linux)) id 1eP4WL-0007aV-Nt; Wed, 13 Dec 2017 10:44:13 +0000 Received: from bhuna.collabora.co.uk ([46.235.227.227]) by bombadil.infradead.org with esmtps (Exim 4.87 #1 (Red Hat Linux)) id 1eP4LU-0008MR-Tp; Wed, 13 Dec 2017 10:33:05 +0000 Received: from [127.0.0.1] (localhost [127.0.0.1]) (Authenticated sender: eballetbo) with ESMTPSA id 165F426DFB6 From: Enric Balletbo i Serra To: MyungJoo Ham , Chanwoo Choi , Lee Jones , Rob Herring , Heiko Stuebner Subject: [PATCH v2 1/5] extcon: usbc-cros-ec: add support to notify USB type cables. Date: Wed, 13 Dec 2017 11:32:15 +0100 Message-Id: <20171213103219.1464-1-enric.balletbo@collabora.com> X-Mailer: git-send-email 2.9.3 X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20171213_023301_496033_6BADC0BB X-CRM114-Status: GOOD ( 13.42 ) X-BeenThere: linux-rockchip@lists.infradead.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: Upstream kernel work for Rockchip platforms List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: devicetree@vger.kernel.org, linux-kernel@vger.kernel.org, dianders@google.com, linux-rockchip@lists.infradead.org, groeck@chromium.org, briannorris@google.com, Benson Leung , linux-arm-kernel@lists.infradead.org MIME-Version: 1.0 Sender: "Linux-rockchip" Errors-To: linux-rockchip-bounces+patchwork-linux-rockchip=patchwork.kernel.org@lists.infradead.org X-Virus-Scanned: ClamAV using ClamSMTP From: Benson Leung Extend the driver to notify host and device type cables and the presence of power. Signed-off-by: Benson Leung Signed-off-by: Enric Balletbo i Serra Reviewed-by: Chanwoo Choi Acked-by: Lee Jones --- Changes since v1: - Use the BIT macro. Requested by Lee Jones. - Add the Reviewed-by: Chanwoo Choi. drivers/extcon/extcon-usbc-cros-ec.c | 142 ++++++++++++++++++++++++++++++++++- include/linux/mfd/cros_ec_commands.h | 17 +++++ 2 files changed, 155 insertions(+), 4 deletions(-) diff --git a/drivers/extcon/extcon-usbc-cros-ec.c b/drivers/extcon/extcon-usbc-cros-ec.c index 6187f73..6721ab0 100644 --- a/drivers/extcon/extcon-usbc-cros-ec.c +++ b/drivers/extcon/extcon-usbc-cros-ec.c @@ -34,16 +34,26 @@ struct cros_ec_extcon_info { struct notifier_block notifier; + unsigned int dr; /* data role */ + bool pr; /* power role (true if VBUS enabled) */ bool dp; /* DisplayPort enabled */ bool mux; /* SuperSpeed (usb3) enabled */ unsigned int power_type; }; static const unsigned int usb_type_c_cable[] = { + EXTCON_USB, + EXTCON_USB_HOST, EXTCON_DISP_DP, EXTCON_NONE, }; +enum usb_data_roles { + DR_NONE, + DR_HOST, + DR_DEVICE, +}; + /** * cros_ec_pd_command() - Send a command to the EC. * @info: pointer to struct cros_ec_extcon_info @@ -150,6 +160,7 @@ static int cros_ec_usb_get_role(struct cros_ec_extcon_info *info, pd_control.port = info->port_id; pd_control.role = USB_PD_CTRL_ROLE_NO_CHANGE; pd_control.mux = USB_PD_CTRL_MUX_NO_CHANGE; + pd_control.swap = USB_PD_CTRL_SWAP_NONE; ret = cros_ec_pd_command(info, EC_CMD_USB_PD_CONTROL, 1, &pd_control, sizeof(pd_control), &resp, sizeof(resp)); @@ -183,11 +194,72 @@ static int cros_ec_pd_get_num_ports(struct cros_ec_extcon_info *info) return resp.num_ports; } +static const char *cros_ec_usb_role_string(unsigned int role) +{ + return role == DR_NONE ? "DISCONNECTED" : + (role == DR_HOST ? "DFP" : "UFP"); +} + +static const char *cros_ec_usb_power_type_string(unsigned int type) +{ + switch (type) { + case USB_CHG_TYPE_NONE: + return "USB_CHG_TYPE_NONE"; + case USB_CHG_TYPE_PD: + return "USB_CHG_TYPE_PD"; + case USB_CHG_TYPE_PROPRIETARY: + return "USB_CHG_TYPE_PROPRIETARY"; + case USB_CHG_TYPE_C: + return "USB_CHG_TYPE_C"; + case USB_CHG_TYPE_BC12_DCP: + return "USB_CHG_TYPE_BC12_DCP"; + case USB_CHG_TYPE_BC12_CDP: + return "USB_CHG_TYPE_BC12_CDP"; + case USB_CHG_TYPE_BC12_SDP: + return "USB_CHG_TYPE_BC12_SDP"; + case USB_CHG_TYPE_OTHER: + return "USB_CHG_TYPE_OTHER"; + case USB_CHG_TYPE_VBUS: + return "USB_CHG_TYPE_VBUS"; + case USB_CHG_TYPE_UNKNOWN: + return "USB_CHG_TYPE_UNKNOWN"; + default: + return "USB_CHG_TYPE_UNKNOWN"; + } +} + +static bool cros_ec_usb_power_type_is_wall_wart(unsigned int type, + unsigned int role) +{ + switch (type) { + /* FIXME : Guppy, Donnettes, and other chargers will be miscategorized + * because they identify with USB_CHG_TYPE_C, but we can't return true + * here from that code because that breaks Suzy-Q and other kinds of + * USB Type-C cables and peripherals. + */ + case USB_CHG_TYPE_PROPRIETARY: + case USB_CHG_TYPE_BC12_DCP: + return true; + case USB_CHG_TYPE_PD: + case USB_CHG_TYPE_C: + case USB_CHG_TYPE_BC12_CDP: + case USB_CHG_TYPE_BC12_SDP: + case USB_CHG_TYPE_OTHER: + case USB_CHG_TYPE_VBUS: + case USB_CHG_TYPE_UNKNOWN: + case USB_CHG_TYPE_NONE: + default: + return false; + } +} + static int extcon_cros_ec_detect_cable(struct cros_ec_extcon_info *info, bool force) { struct device *dev = info->dev; int role, power_type; + unsigned int dr = DR_NONE; + bool pr = false; bool polarity = false; bool dp = false; bool mux = false; @@ -206,9 +278,12 @@ static int extcon_cros_ec_detect_cable(struct cros_ec_extcon_info *info, dev_err(dev, "failed getting role err = %d\n", role); return role; } + dev_dbg(dev, "disconnected\n"); } else { int pd_mux_state; + dr = (role & PD_CTRL_RESP_ROLE_DATA) ? DR_HOST : DR_DEVICE; + pr = (role & PD_CTRL_RESP_ROLE_POWER); pd_mux_state = cros_ec_usb_get_pd_mux_state(info); if (pd_mux_state < 0) pd_mux_state = USB_PD_MUX_USB_ENABLED; @@ -216,20 +291,62 @@ static int extcon_cros_ec_detect_cable(struct cros_ec_extcon_info *info, dp = pd_mux_state & USB_PD_MUX_DP_ENABLED; mux = pd_mux_state & USB_PD_MUX_USB_ENABLED; hpd = pd_mux_state & USB_PD_MUX_HPD_IRQ; - } - if (force || info->dp != dp || info->mux != mux || - info->power_type != power_type) { + dev_dbg(dev, + "connected role 0x%x pwr type %d dr %d pr %d pol %d mux %d dp %d hpd %d\n", + role, power_type, dr, pr, polarity, mux, dp, hpd); + } + /* + * When there is no USB host (e.g. USB PD charger), + * we are not really a UFP for the AP. + */ + if (dr == DR_DEVICE && + cros_ec_usb_power_type_is_wall_wart(power_type, role)) + dr = DR_NONE; + + if (force || info->dr != dr || info->pr != pr || info->dp != dp || + info->mux != mux || info->power_type != power_type) { + bool host_connected = false, device_connected = false; + + dev_dbg(dev, "Type/Role switch! type = %s role = %s\n", + cros_ec_usb_power_type_string(power_type), + cros_ec_usb_role_string(dr)); + info->dr = dr; + info->pr = pr; info->dp = dp; info->mux = mux; info->power_type = power_type; - extcon_set_state(info->edev, EXTCON_DISP_DP, dp); + if (dr == DR_DEVICE) + device_connected = true; + else if (dr == DR_HOST) + host_connected = true; + extcon_set_state(info->edev, EXTCON_USB, device_connected); + extcon_set_state(info->edev, EXTCON_USB_HOST, host_connected); + extcon_set_state(info->edev, EXTCON_DISP_DP, dp); + extcon_set_property(info->edev, EXTCON_USB, + EXTCON_PROP_USB_VBUS, + (union extcon_property_value)(int)pr); + extcon_set_property(info->edev, EXTCON_USB_HOST, + EXTCON_PROP_USB_VBUS, + (union extcon_property_value)(int)pr); + extcon_set_property(info->edev, EXTCON_USB, + EXTCON_PROP_USB_TYPEC_POLARITY, + (union extcon_property_value)(int)polarity); + extcon_set_property(info->edev, EXTCON_USB_HOST, + EXTCON_PROP_USB_TYPEC_POLARITY, + (union extcon_property_value)(int)polarity); extcon_set_property(info->edev, EXTCON_DISP_DP, EXTCON_PROP_USB_TYPEC_POLARITY, (union extcon_property_value)(int)polarity); + extcon_set_property(info->edev, EXTCON_USB, + EXTCON_PROP_USB_SS, + (union extcon_property_value)(int)mux); + extcon_set_property(info->edev, EXTCON_USB_HOST, + EXTCON_PROP_USB_SS, + (union extcon_property_value)(int)mux); extcon_set_property(info->edev, EXTCON_DISP_DP, EXTCON_PROP_USB_SS, (union extcon_property_value)(int)mux); @@ -237,6 +354,8 @@ static int extcon_cros_ec_detect_cable(struct cros_ec_extcon_info *info, EXTCON_PROP_DISP_HPD, (union extcon_property_value)(int)hpd); + extcon_sync(info->edev, EXTCON_USB); + extcon_sync(info->edev, EXTCON_USB_HOST); extcon_sync(info->edev, EXTCON_DISP_DP); } else if (hpd) { @@ -322,13 +441,28 @@ static int extcon_cros_ec_probe(struct platform_device *pdev) return ret; } + extcon_set_property_capability(info->edev, EXTCON_USB, + EXTCON_PROP_USB_VBUS); + extcon_set_property_capability(info->edev, EXTCON_USB_HOST, + EXTCON_PROP_USB_VBUS); + extcon_set_property_capability(info->edev, EXTCON_USB, + EXTCON_PROP_USB_TYPEC_POLARITY); + extcon_set_property_capability(info->edev, EXTCON_USB_HOST, + EXTCON_PROP_USB_TYPEC_POLARITY); extcon_set_property_capability(info->edev, EXTCON_DISP_DP, EXTCON_PROP_USB_TYPEC_POLARITY); + extcon_set_property_capability(info->edev, EXTCON_USB, + EXTCON_PROP_USB_SS); + extcon_set_property_capability(info->edev, EXTCON_USB_HOST, + EXTCON_PROP_USB_SS); extcon_set_property_capability(info->edev, EXTCON_DISP_DP, EXTCON_PROP_USB_SS); extcon_set_property_capability(info->edev, EXTCON_DISP_DP, EXTCON_PROP_DISP_HPD); + info->dr = DR_NONE; + info->pr = false; + platform_set_drvdata(pdev, info); /* Get PD events from the EC */ diff --git a/include/linux/mfd/cros_ec_commands.h b/include/linux/mfd/cros_ec_commands.h index 2b16e95..a83f649 100644 --- a/include/linux/mfd/cros_ec_commands.h +++ b/include/linux/mfd/cros_ec_commands.h @@ -2904,16 +2904,33 @@ enum usb_pd_control_mux { USB_PD_CTRL_MUX_AUTO = 5, }; +enum usb_pd_control_swap { + USB_PD_CTRL_SWAP_NONE = 0, + USB_PD_CTRL_SWAP_DATA = 1, + USB_PD_CTRL_SWAP_POWER = 2, + USB_PD_CTRL_SWAP_VCONN = 3, + USB_PD_CTRL_SWAP_COUNT +}; + struct ec_params_usb_pd_control { uint8_t port; uint8_t role; uint8_t mux; + uint8_t swap; } __packed; #define PD_CTRL_RESP_ENABLED_COMMS (1 << 0) /* Communication enabled */ #define PD_CTRL_RESP_ENABLED_CONNECTED (1 << 1) /* Device connected */ #define PD_CTRL_RESP_ENABLED_PD_CAPABLE (1 << 2) /* Partner is PD capable */ +#define PD_CTRL_RESP_ROLE_POWER BIT(0) /* 0=SNK/1=SRC */ +#define PD_CTRL_RESP_ROLE_DATA BIT(1) /* 0=UFP/1=DFP */ +#define PD_CTRL_RESP_ROLE_VCONN BIT(2) /* Vconn status */ +#define PD_CTRL_RESP_ROLE_DR_POWER BIT(3) /* Partner is dualrole power */ +#define PD_CTRL_RESP_ROLE_DR_DATA BIT(4) /* Partner is dualrole data */ +#define PD_CTRL_RESP_ROLE_USB_COMM BIT(5) /* Partner USB comm capable */ +#define PD_CTRL_RESP_ROLE_EXT_POWERED BIT(6) /* Partner externally powerd */ + struct ec_response_usb_pd_control_v1 { uint8_t enabled; uint8_t role;