From patchwork Wed May 15 12:09:09 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Biju Das X-Patchwork-Id: 10944643 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id CA8AF17EE for ; Wed, 15 May 2019 12:16:52 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id BBBF8289E9 for ; Wed, 15 May 2019 12:16:52 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id B0380289F5; Wed, 15 May 2019 12:16:52 +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=-7.9 required=2.0 tests=BAYES_00,MAILING_LIST_MULTI, RCVD_IN_DNSWL_HI autolearn=unavailable version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 3301C289FA for ; Wed, 15 May 2019 12:16:52 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1728414AbfEOMQu (ORCPT ); Wed, 15 May 2019 08:16:50 -0400 Received: from relmlor2.renesas.com ([210.160.252.172]:22171 "EHLO relmlie6.idc.renesas.com" rhost-flags-OK-OK-OK-FAIL) by vger.kernel.org with ESMTP id S1728561AbfEOMQt (ORCPT ); Wed, 15 May 2019 08:16:49 -0400 X-IronPort-AV: E=Sophos;i="5.60,472,1549897200"; d="scan'208";a="15816038" Received: from unknown (HELO relmlir5.idc.renesas.com) ([10.200.68.151]) by relmlie6.idc.renesas.com with ESMTP; 15 May 2019 21:16:47 +0900 Received: from be1yocto.ree.adwin.renesas.com (unknown [172.29.43.62]) by relmlir5.idc.renesas.com (Postfix) with ESMTP id 9537A4003864; Wed, 15 May 2019 21:16:44 +0900 (JST) From: Biju Das To: Felipe Balbi , Greg Kroah-Hartman , Yoshihiro Shimoda Cc: Biju Das , Heikki Krogerus , Simon Horman , Fabrizio Castro , Kees Cook , linux-usb@vger.kernel.org, Simon Horman , Geert Uytterhoeven , Chris Paterson , linux-renesas-soc@vger.kernel.org Subject: [PATCH v6 4/7] usb: gadget: udc: renesas_usb3: Add dual role switch support Date: Wed, 15 May 2019 13:09:09 +0100 Message-Id: <1557922152-16449-5-git-send-email-biju.das@bp.renesas.com> X-Mailer: git-send-email 2.7.4 In-Reply-To: <1557922152-16449-1-git-send-email-biju.das@bp.renesas.com> References: <1557922152-16449-1-git-send-email-biju.das@bp.renesas.com> Sender: linux-usb-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-usb@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP The RZ/G2E cat874 board has a type-c connector connected to hd3ss3220 usb type-c drp port controller. This patch adds dual role switch support for the type-c connector using the usb role switch class framework. Signed-off-by: Biju Das --- V5-->V6 * Added graph api's to find the role supported by the connector. V4-->V5 * Incorporated Shimoda-san's review comment (https://patchwork.kernel.org/patch/10902537/) V3-->V4 * No Change V2-->V3 * Incorporated Shimoda-san's review comment (https://patchwork.kernel.org/patch/10852507/) * Used renesas,usb-role-switch property for differentiating USB role switch associated with Type-C port controller driver. V1-->V2 * Driver uses usb role clas for handling dual role switch and handling connect/disconnect events instead of extcon. --- drivers/usb/gadget/udc/renesas_usb3.c | 121 ++++++++++++++++++++++++++++++++-- 1 file changed, 114 insertions(+), 7 deletions(-) diff --git a/drivers/usb/gadget/udc/renesas_usb3.c b/drivers/usb/gadget/udc/renesas_usb3.c index 7dc2485..1d41998 100644 --- a/drivers/usb/gadget/udc/renesas_usb3.c +++ b/drivers/usb/gadget/udc/renesas_usb3.c @@ -24,6 +24,7 @@ #include #include #include +#include #include /* register definitions */ @@ -351,6 +352,8 @@ struct renesas_usb3 { int disabled_count; struct usb_request *ep0_req; + + enum usb_role connection_state; u16 test_mode; u8 ep0_buf[USB3_EP0_BUF_SIZE]; bool softconnect; @@ -359,6 +362,7 @@ struct renesas_usb3 { bool extcon_usb; /* check vbus and set EXTCON_USB */ bool forced_b_device; bool start_to_connect; + bool dual_role_sw; }; #define gadget_to_renesas_usb3(_gadget) \ @@ -699,8 +703,10 @@ static void usb3_mode_config(struct renesas_usb3 *usb3, bool host, bool a_dev) unsigned long flags; spin_lock_irqsave(&usb3->lock, flags); - usb3_set_mode_by_role_sw(usb3, host); - usb3_vbus_out(usb3, a_dev); + if (!usb3->dual_role_sw || usb3->connection_state != USB_ROLE_NONE) { + usb3_set_mode_by_role_sw(usb3, host); + usb3_vbus_out(usb3, a_dev); + } /* for A-Peripheral or forced B-device mode */ if ((!host && a_dev) || usb3->start_to_connect) usb3_connect(usb3); @@ -716,7 +722,8 @@ static void usb3_check_id(struct renesas_usb3 *usb3) { usb3->extcon_host = usb3_is_a_device(usb3); - if (usb3->extcon_host && !usb3->forced_b_device) + if ((!usb3->dual_role_sw && usb3->extcon_host && !usb3->forced_b_device) + || usb3->connection_state == USB_ROLE_HOST) usb3_mode_config(usb3, true, true); else usb3_mode_config(usb3, false, false); @@ -2343,14 +2350,65 @@ static enum usb_role renesas_usb3_role_switch_get(struct device *dev) return cur_role; } -static int renesas_usb3_role_switch_set(struct device *dev, - enum usb_role role) +static void handle_ext_role_switch_states(struct device *dev, + enum usb_role role) +{ + struct renesas_usb3 *usb3 = dev_get_drvdata(dev); + struct device *host = usb3->host_dev; + enum usb_role cur_role = renesas_usb3_role_switch_get(dev); + + switch (role) { + case USB_ROLE_NONE: + usb3->connection_state = USB_ROLE_NONE; + if (usb3->driver) + usb3_disconnect(usb3); + usb3_vbus_out(usb3, false); + break; + case USB_ROLE_DEVICE: + if (usb3->connection_state == USB_ROLE_NONE) { + usb3->connection_state = USB_ROLE_DEVICE; + usb3_set_mode(usb3, false); + if (usb3->driver) + usb3_connect(usb3); + } else if (cur_role == USB_ROLE_HOST) { + device_release_driver(host); + usb3_set_mode(usb3, false); + if (usb3->driver) + usb3_connect(usb3); + } + usb3_vbus_out(usb3, false); + break; + case USB_ROLE_HOST: + if (usb3->connection_state == USB_ROLE_NONE) { + if (usb3->driver) + usb3_disconnect(usb3); + + usb3->connection_state = USB_ROLE_HOST; + usb3_set_mode(usb3, true); + usb3_vbus_out(usb3, true); + if (device_attach(host) < 0) + dev_err(dev, "device_attach(host) failed\n"); + } else if (cur_role == USB_ROLE_DEVICE) { + usb3_disconnect(usb3); + /* Must set the mode before device_attach of the host */ + usb3_set_mode(usb3, true); + /* This device_attach() might sleep */ + if (device_attach(host) < 0) + dev_err(dev, "device_attach(host) failed\n"); + } + break; + default: + break; + } +} + +static void handle_role_switch_states(struct device *dev, + enum usb_role role) { struct renesas_usb3 *usb3 = dev_get_drvdata(dev); struct device *host = usb3->host_dev; enum usb_role cur_role = renesas_usb3_role_switch_get(dev); - pm_runtime_get_sync(dev); if (cur_role == USB_ROLE_HOST && role == USB_ROLE_DEVICE) { device_release_driver(host); usb3_set_mode(usb3, false); @@ -2361,6 +2419,20 @@ static int renesas_usb3_role_switch_set(struct device *dev, if (device_attach(host) < 0) dev_err(dev, "device_attach(host) failed\n"); } +} + +static int renesas_usb3_role_switch_set(struct device *dev, + enum usb_role role) +{ + struct renesas_usb3 *usb3 = dev_get_drvdata(dev); + + pm_runtime_get_sync(dev); + + if (usb3->dual_role_sw) + handle_ext_role_switch_states(dev, role); + else + handle_role_switch_states(dev, role); + pm_runtime_put(dev); return 0; @@ -2650,12 +2722,41 @@ static const unsigned int renesas_usb3_cable[] = { EXTCON_NONE, }; -static const struct usb_role_switch_desc renesas_usb3_role_switch_desc = { +static struct usb_role_switch_desc renesas_usb3_role_switch_desc = { .set = renesas_usb3_role_switch_set, .get = renesas_usb3_role_switch_get, .allow_userspace_control = true, }; +static bool is_usb_dual_role_switch(struct device *dev) +{ + struct device_node *np = dev->of_node; + struct device_node *parent; + struct device_node *child; + bool ret = false; + const char *role_type = NULL; + + child = of_graph_get_endpoint_by_regs(np, -1, -1); + if (!child) + return ret; + + parent = of_graph_get_remote_port_parent(child); + of_node_put(child); + child = of_get_child_by_name(parent, "connector"); + of_node_put(parent); + if (!child) + return ret; + + if (of_device_is_compatible(child, "usb-c-connector")) { + of_property_read_string(child, "data-role", &role_type); + if (role_type && (!strncmp(role_type, "dual", strlen("dual")))) + ret = true; + } + + of_node_put(child); + return ret; +} + static int renesas_usb3_probe(struct platform_device *pdev) { struct renesas_usb3 *usb3; @@ -2741,6 +2842,12 @@ static int renesas_usb3_probe(struct platform_device *pdev) if (ret < 0) goto err_dev_create; + if (device_property_read_bool(&pdev->dev, "usb-role-switch") && + is_usb_dual_role_switch(&pdev->dev)) { + usb3->dual_role_sw = true; + renesas_usb3_role_switch_desc.fwnode = dev_fwnode(&pdev->dev); + } + INIT_WORK(&usb3->role_work, renesas_usb3_role_work); usb3->role_sw = usb_role_switch_register(&pdev->dev, &renesas_usb3_role_switch_desc);