From patchwork Mon Apr 3 12:20:29 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Roger Quadros X-Patchwork-Id: 96624 Delivered-To: patch@linaro.org Received: by 10.140.89.233 with SMTP id v96csp72323qgd; Mon, 3 Apr 2017 05:20:44 -0700 (PDT) X-Received: by 10.99.140.70 with SMTP id q6mr17822892pgn.222.1491222044806; Mon, 03 Apr 2017 05:20:44 -0700 (PDT) Return-Path: Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id d1si14147694pln.333.2017.04.03.05.20.44; Mon, 03 Apr 2017 05:20:44 -0700 (PDT) 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; dkim=pass header.i=@ti.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; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=ti.com Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1752970AbdDCMUn (ORCPT + 25 others); Mon, 3 Apr 2017 08:20:43 -0400 Received: from fllnx210.ext.ti.com ([198.47.19.17]:16440 "EHLO fllnx210.ext.ti.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1752523AbdDCMUl (ORCPT ); Mon, 3 Apr 2017 08:20:41 -0400 Received: from dflxv15.itg.ti.com ([128.247.5.124]) by fllnx210.ext.ti.com (8.15.1/8.15.1) with ESMTP id v33CKbPS032322; Mon, 3 Apr 2017 07:20:37 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ti.com; s=ti-com-17Q1; t=1491222038; bh=o9ESdBus2LqZKmRJRetIS8RMnmOwiGHE1hCk2ogJBkw=; h=From:To:CC:Subject:Date:In-Reply-To:References; b=OHmhg+dON0tyS2lfX00Tc1GKgZfCGwZVZwYBG2NMrRcd1vfV/lIHpRqL/fMSncLKR R5Jm6ItHqlmH5lIbf6F5FQAtlLoOK1DeDYaqzZg6LJ4HBOWZX/Ei1R2f3IjZRtRFUd QTeHFndrjjRyogDOJMFaSQ7lB5jTB4n2DBSEDQn8= Received: from DFLE73.ent.ti.com (dfle73.ent.ti.com [128.247.5.110]) by dflxv15.itg.ti.com (8.14.3/8.13.8) with ESMTP id v33CKbx8030640; Mon, 3 Apr 2017 07:20:37 -0500 Received: from dflp33.itg.ti.com (10.64.6.16) by DFLE73.ent.ti.com (128.247.5.110) with Microsoft SMTP Server id 14.3.294.0; Mon, 3 Apr 2017 07:20:37 -0500 Received: from lta0400828d.ti.com (ileax41-snat.itg.ti.com [10.172.224.153]) by dflp33.itg.ti.com (8.14.3/8.13.8) with ESMTP id v33CKXuq010968; Mon, 3 Apr 2017 07:20:36 -0500 From: Roger Quadros To: CC: , , , Roger Quadros Subject: [PATCH v3 1/3] usb: udc: allow adding and removing the same gadget device Date: Mon, 3 Apr 2017 15:20:29 +0300 Message-ID: <1491222031-18120-2-git-send-email-rogerq@ti.com> X-Mailer: git-send-email 2.7.4 In-Reply-To: <1491222031-18120-1-git-send-email-rogerq@ti.com> References: <1491222031-18120-1-git-send-email-rogerq@ti.com> MIME-Version: 1.0 Sender: linux-kernel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org allow usb_del_gadget_udc() and usb add_gadget_udc() to be called repeatedly on the same gadget->dev structure. We need to clear the gadget->dev structure so that kobject_init() doesn't complain about already initialized object. Signed-off-by: Roger Quadros --- drivers/usb/gadget/udc/core.c | 1 + 1 file changed, 1 insertion(+) -- 2.7.4 diff --git a/drivers/usb/gadget/udc/core.c b/drivers/usb/gadget/udc/core.c index d685d82..efce68e 100644 --- a/drivers/usb/gadget/udc/core.c +++ b/drivers/usb/gadget/udc/core.c @@ -1273,6 +1273,7 @@ void usb_del_gadget_udc(struct usb_gadget *gadget) flush_work(&gadget->work); device_unregister(&udc->dev); device_unregister(&gadget->dev); + memset(&gadget->dev, 0x00, sizeof(gadget->dev)); } EXPORT_SYMBOL_GPL(usb_del_gadget_udc); From patchwork Mon Apr 3 12:20:30 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Roger Quadros X-Patchwork-Id: 96627 Delivered-To: patch@linaro.org Received: by 10.140.89.233 with SMTP id v96csp72696qgd; Mon, 3 Apr 2017 05:21:43 -0700 (PDT) X-Received: by 10.99.101.132 with SMTP id z126mr4347588pgb.218.1491222102904; Mon, 03 Apr 2017 05:21:42 -0700 (PDT) Return-Path: Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id a124si7883683pgc.127.2017.04.03.05.21.42; Mon, 03 Apr 2017 05:21:42 -0700 (PDT) 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; dkim=pass header.i=@ti.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; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=ti.com Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1753161AbdDCMVa (ORCPT + 25 others); Mon, 3 Apr 2017 08:21:30 -0400 Received: from fllnx209.ext.ti.com ([198.47.19.16]:13243 "EHLO fllnx209.ext.ti.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1752483AbdDCMV1 (ORCPT ); Mon, 3 Apr 2017 08:21:27 -0400 Received: from dlelxv90.itg.ti.com ([172.17.2.17]) by fllnx209.ext.ti.com (8.15.1/8.15.1) with ESMTP id v33CKiC7017167; Mon, 3 Apr 2017 07:20:44 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ti.com; s=ti-com-17Q1; t=1491222044; bh=4a5/yNgcuxeh2d/obCt1cjLyqIHtw/+kc4MosvH5yh0=; h=From:To:CC:Subject:Date:In-Reply-To:References; b=tHbZP0YZDZMZ61oHyTLCEnMOPIJpOpZzZJaLnqcadV5LRVN0vLbqdbxkTLOpmNsJu y4dBKXDPFQb8ee4D36WGTNhJ4k605pDCj3uIr/kFrRZOKAJ0Ez5J9EbzWkZAz97mK3 mGQTc65hQZ6mNPO4nmSEpstUqmA2G+FqP025TGdw= Received: from DFLE73.ent.ti.com (dfle73.ent.ti.com [128.247.5.110]) by dlelxv90.itg.ti.com (8.14.3/8.13.8) with ESMTP id v33CKd14013376; Mon, 3 Apr 2017 07:20:39 -0500 Received: from dflp33.itg.ti.com (10.64.6.16) by DFLE73.ent.ti.com (128.247.5.110) with Microsoft SMTP Server id 14.3.294.0; Mon, 3 Apr 2017 07:20:39 -0500 Received: from lta0400828d.ti.com (ileax41-snat.itg.ti.com [10.172.224.153]) by dflp33.itg.ti.com (8.14.3/8.13.8) with ESMTP id v33CKXur010968; Mon, 3 Apr 2017 07:20:38 -0500 From: Roger Quadros To: CC: , , , Roger Quadros Subject: [PATCH v3 2/3] usb: dwc3: make role-switching work with debugfs/mode Date: Mon, 3 Apr 2017 15:20:30 +0300 Message-ID: <1491222031-18120-3-git-send-email-rogerq@ti.com> X-Mailer: git-send-email 2.7.4 In-Reply-To: <1491222031-18120-1-git-send-email-rogerq@ti.com> References: <1491222031-18120-1-git-send-email-rogerq@ti.com> MIME-Version: 1.0 Sender: linux-kernel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org If dr_mode == "otg", we start by default in PERIPHERAL mode. Keep track of current role in "current_dr_role" whenever dwc3_set_mode() is called. When debugfs/mode is changed AND we're in dual-role mode, handle the switch by stopping and starting the respective host/gadget controllers. Signed-off-by: Roger Quadros --- drivers/usb/dwc3/core.c | 21 +++++++++++--------- drivers/usb/dwc3/core.h | 3 +++ drivers/usb/dwc3/debugfs.c | 49 +++++++++++++++++++++++++++++++++++++++------- 3 files changed, 57 insertions(+), 16 deletions(-) -- 2.7.4 diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c index 369bab1..bf917c2 100644 --- a/drivers/usb/dwc3/core.c +++ b/drivers/usb/dwc3/core.c @@ -108,6 +108,8 @@ void dwc3_set_mode(struct dwc3 *dwc, u32 mode) reg &= ~(DWC3_GCTL_PRTCAPDIR(DWC3_GCTL_PRTCAP_OTG)); reg |= DWC3_GCTL_PRTCAPDIR(mode); dwc3_writel(dwc->regs, DWC3_GCTL, reg); + + dwc->current_dr_role = mode; } u32 dwc3_core_fifo_space(struct dwc3_ep *dep, u8 type) @@ -277,7 +279,7 @@ static int dwc3_alloc_event_buffers(struct dwc3 *dwc, unsigned length) * * Returns 0 on success otherwise negative errno. */ -static int dwc3_event_buffers_setup(struct dwc3 *dwc) +int dwc3_event_buffers_setup(struct dwc3 *dwc) { struct dwc3_event_buffer *evt; @@ -862,13 +864,8 @@ static int dwc3_core_init_mode(struct dwc3 *dwc) } break; case USB_DR_MODE_OTG: - ret = dwc3_host_init(dwc); - if (ret) { - if (ret != -EPROBE_DEFER) - dev_err(dev, "failed to initialize host\n"); - return ret; - } - + /* start in peripheral role by default */ + dwc3_set_mode(dwc, DWC3_GCTL_PRTCAP_DEVICE); ret = dwc3_gadget_init(dwc); if (ret) { if (ret != -EPROBE_DEFER) @@ -894,7 +891,13 @@ static void dwc3_core_exit_mode(struct dwc3 *dwc) dwc3_host_exit(dwc); break; case USB_DR_MODE_OTG: - dwc3_host_exit(dwc); + /* role might have changed since start */ + if (dwc->current_dr_role == DWC3_GCTL_PRTCAP_HOST) { + dwc3_host_exit(dwc); + /* Add back UDC to match dwc3_gadget_exit() */ + usb_add_gadget_udc(dwc->dev, &dwc->gadget); + } + dwc3_gadget_exit(dwc); break; default: diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h index 7ffdee5..adb04af 100644 --- a/drivers/usb/dwc3/core.h +++ b/drivers/usb/dwc3/core.h @@ -785,6 +785,7 @@ struct dwc3_scratchpad_array { * @maximum_speed: maximum speed requested (mainly for testing purposes) * @revision: revision register contents * @dr_mode: requested mode of operation + * @current_dr_role: current role of operation when in dual-role mode * @hsphy_mode: UTMI phy mode, one of following: * - USBPHY_INTERFACE_MODE_UTMI * - USBPHY_INTERFACE_MODE_UTMIW @@ -901,6 +902,7 @@ struct dwc3 { size_t regs_size; enum usb_dr_mode dr_mode; + u32 current_dr_role; enum usb_phy_interface hsphy_mode; u32 fladj; @@ -1167,6 +1169,7 @@ struct dwc3_gadget_ep_cmd_params { /* prototypes */ void dwc3_set_mode(struct dwc3 *dwc, u32 mode); u32 dwc3_core_fifo_space(struct dwc3_ep *dep, u8 type); +int dwc3_event_buffers_setup(struct dwc3 *dwc); /* check whether we are on the DWC_usb3 core */ static inline bool dwc3_is_usb3(struct dwc3 *dwc) diff --git a/drivers/usb/dwc3/debugfs.c b/drivers/usb/dwc3/debugfs.c index 31926dd..a74a83b 100644 --- a/drivers/usb/dwc3/debugfs.c +++ b/drivers/usb/dwc3/debugfs.c @@ -327,19 +327,54 @@ static ssize_t dwc3_mode_write(struct file *file, return -EFAULT; if (!strncmp(buf, "host", 4)) - mode |= DWC3_GCTL_PRTCAP_HOST; + mode = DWC3_GCTL_PRTCAP_HOST; if (!strncmp(buf, "device", 6)) - mode |= DWC3_GCTL_PRTCAP_DEVICE; + mode = DWC3_GCTL_PRTCAP_DEVICE; if (!strncmp(buf, "otg", 3)) - mode |= DWC3_GCTL_PRTCAP_OTG; + mode = DWC3_GCTL_PRTCAP_OTG; - if (mode) { - spin_lock_irqsave(&dwc->lock, flags); - dwc3_set_mode(dwc, mode); - spin_unlock_irqrestore(&dwc->lock, flags); + if (!mode) + return -EINVAL; + + if (mode == dwc->current_dr_role) + goto exit; + + /* prevent role switching if we're not dual-role */ + if (dwc->dr_mode != USB_DR_MODE_OTG) + return -EINVAL; + + /* stop old role */ + switch (dwc->current_dr_role) { + case DWC3_GCTL_PRTCAP_HOST: + dwc3_host_exit(dwc); + break; + case DWC3_GCTL_PRTCAP_DEVICE: + usb_del_gadget_udc(&dwc->gadget); + break; + default: + break; + } + + /* switch PRTCAP mode. updates current_dr_role */ + spin_lock_irqsave(&dwc->lock, flags); + dwc3_set_mode(dwc, mode); + spin_unlock_irqrestore(&dwc->lock, flags); + + /* start new role */ + switch (dwc->current_dr_role) { + case DWC3_GCTL_PRTCAP_HOST: + dwc3_host_init(dwc); + break; + case DWC3_GCTL_PRTCAP_DEVICE: + dwc3_event_buffers_setup(dwc); + usb_add_gadget_udc(dwc->dev, &dwc->gadget); + break; + default: + break; } +exit: return count; } From patchwork Mon Apr 3 12:20:31 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Roger Quadros X-Patchwork-Id: 96625 Delivered-To: patch@linaro.org Received: by 10.140.89.233 with SMTP id v96csp72501qgd; Mon, 3 Apr 2017 05:21:14 -0700 (PDT) X-Received: by 10.84.195.1 with SMTP id i1mr21438161pld.175.1491222074811; Mon, 03 Apr 2017 05:21:14 -0700 (PDT) Return-Path: Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id 80si14130231pga.172.2017.04.03.05.21.14; Mon, 03 Apr 2017 05:21:14 -0700 (PDT) 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; dkim=pass header.i=@ti.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; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=ti.com Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1753125AbdDCMUx (ORCPT + 25 others); Mon, 3 Apr 2017 08:20:53 -0400 Received: from lelnx193.ext.ti.com ([198.47.27.77]:29050 "EHLO lelnx193.ext.ti.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1752984AbdDCMUu (ORCPT ); Mon, 3 Apr 2017 08:20:50 -0400 Received: from dlelxv90.itg.ti.com ([172.17.2.17]) by lelnx193.ext.ti.com (8.15.1/8.15.1) with ESMTP id v33CKfS2009744; Mon, 3 Apr 2017 07:20:41 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ti.com; s=ti-com-17Q1; t=1491222041; bh=HnBqMlEVEV8bE/Per3iR0MmrpzOCwp32RKWTCLKQjcs=; h=From:To:CC:Subject:Date:In-Reply-To:References; b=B+T6bVtwb+dkGozthK0ipIVHc7avYzy4NO+eEBmTaKREWzsIFErBQfOaGQ0tRS7Qb xv5P9nwQc3hZ8NfiqtnsDJdBda78pzNnyqGUyjU9iaLX0qfPQemuoRS5TTNuGGovmo Hd86nqEm1mTzBRJ8zSDZSOG7J4BznKbl9qnmLUPY= Received: from DLEE71.ent.ti.com (dlee71.ent.ti.com [157.170.170.114]) by dlelxv90.itg.ti.com (8.14.3/8.13.8) with ESMTP id v33CKfla013405; Mon, 3 Apr 2017 07:20:41 -0500 Received: from dflp33.itg.ti.com (10.64.6.16) by DLEE71.ent.ti.com (157.170.170.114) with Microsoft SMTP Server id 14.3.294.0; Mon, 3 Apr 2017 07:20:40 -0500 Received: from lta0400828d.ti.com (ileax41-snat.itg.ti.com [10.172.224.153]) by dflp33.itg.ti.com (8.14.3/8.13.8) with ESMTP id v33CKXus010968; Mon, 3 Apr 2017 07:20:39 -0500 From: Roger Quadros To: CC: , , , Roger Quadros Subject: [PATCH v3 3/3] usb: dwc3: Add dual-role support Date: Mon, 3 Apr 2017 15:20:31 +0300 Message-ID: <1491222031-18120-4-git-send-email-rogerq@ti.com> X-Mailer: git-send-email 2.7.4 In-Reply-To: <1491222031-18120-1-git-send-email-rogerq@ti.com> References: <1491222031-18120-1-git-send-email-rogerq@ti.com> MIME-Version: 1.0 Sender: linux-kernel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org If dr_mode is "otg" then support dual role mode of operation. Currently this mode is only supported when an extcon handle is present in the dwc3 device tree node. This is needed to get the ID status events of the port. We're using a workqueue to manage the dual-role state transitions as the extcon notifier (dwc3_drd_notifier) is called in an atomic context by extcon_sync() and this doesn't go well with usb_del_gadget_udc() causing a lockdep and softirq warning. Signed-off-by: Roger Quadros --- drivers/usb/dwc3/Makefile | 4 ++ drivers/usb/dwc3/core.c | 15 +---- drivers/usb/dwc3/core.h | 19 ++++++ drivers/usb/dwc3/drd.c | 167 ++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 193 insertions(+), 12 deletions(-) create mode 100644 drivers/usb/dwc3/drd.c -- 2.7.4 diff --git a/drivers/usb/dwc3/Makefile b/drivers/usb/dwc3/Makefile index ffca340..f15fabb 100644 --- a/drivers/usb/dwc3/Makefile +++ b/drivers/usb/dwc3/Makefile @@ -17,6 +17,10 @@ ifneq ($(filter y,$(CONFIG_USB_DWC3_GADGET) $(CONFIG_USB_DWC3_DUAL_ROLE)),) dwc3-y += gadget.o ep0.o endif +ifneq ($(CONFIG_USB_DWC3_DUAL_ROLE),) + dwc3-y += drd.o +endif + ifneq ($(CONFIG_USB_DWC3_ULPI),) dwc3-y += ulpi.o endif diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c index bf917c2..15c05a1 100644 --- a/drivers/usb/dwc3/core.c +++ b/drivers/usb/dwc3/core.c @@ -864,12 +864,10 @@ static int dwc3_core_init_mode(struct dwc3 *dwc) } break; case USB_DR_MODE_OTG: - /* start in peripheral role by default */ - dwc3_set_mode(dwc, DWC3_GCTL_PRTCAP_DEVICE); - ret = dwc3_gadget_init(dwc); + ret = dwc3_drd_init(dwc); if (ret) { if (ret != -EPROBE_DEFER) - dev_err(dev, "failed to initialize gadget\n"); + dev_err(dev, "failed to initialize dual-role\n"); return ret; } break; @@ -891,14 +889,7 @@ static void dwc3_core_exit_mode(struct dwc3 *dwc) dwc3_host_exit(dwc); break; case USB_DR_MODE_OTG: - /* role might have changed since start */ - if (dwc->current_dr_role == DWC3_GCTL_PRTCAP_HOST) { - dwc3_host_exit(dwc); - /* Add back UDC to match dwc3_gadget_exit() */ - usb_add_gadget_udc(dwc->dev, &dwc->gadget); - } - - dwc3_gadget_exit(dwc); + dwc3_drd_exit(dwc); break; default: /* do nothing */ diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h index adb04af..8402d8f 100644 --- a/drivers/usb/dwc3/core.h +++ b/drivers/usb/dwc3/core.h @@ -786,6 +786,10 @@ struct dwc3_scratchpad_array { * @revision: revision register contents * @dr_mode: requested mode of operation * @current_dr_role: current role of operation when in dual-role mode + * @edev: extcon handle + * @edev_nb: extcon notifier + * @drd_work: dual-role work + * @drd_prevent_change: flag to prevent dual-role state change * @hsphy_mode: UTMI phy mode, one of following: * - USBPHY_INTERFACE_MODE_UTMI * - USBPHY_INTERFACE_MODE_UTMIW @@ -903,6 +907,11 @@ struct dwc3 { enum usb_dr_mode dr_mode; u32 current_dr_role; + struct extcon_dev *edev; + struct notifier_block edev_nb; + bool drd_prevent_change; + struct work_struct drd_work; + enum usb_phy_interface hsphy_mode; u32 fladj; @@ -1225,6 +1234,16 @@ static inline int dwc3_send_gadget_generic_command(struct dwc3 *dwc, { return 0; } #endif +#if IS_ENABLED(CONFIG_USB_DWC3_DUAL_ROLE) +int dwc3_drd_init(struct dwc3 *dwc); +void dwc3_drd_exit(struct dwc3 *dwc); +#else +static inline int dwc3_drd_init(struct dwc3 *dwc) +{ return 0; } +static void dwc3_drd_exit(struct dwc3 *dwc); +{ } +#endif + /* power management interface */ #if !IS_ENABLED(CONFIG_USB_DWC3_HOST) int dwc3_gadget_suspend(struct dwc3 *dwc); diff --git a/drivers/usb/dwc3/drd.c b/drivers/usb/dwc3/drd.c new file mode 100644 index 0000000..c9b02a3 --- /dev/null +++ b/drivers/usb/dwc3/drd.c @@ -0,0 +1,167 @@ +/** + * drd.c - DesignWare USB3 DRD Controller Dual-role support + * + * Copyright (C) 2017 Texas Instruments Incorporated - http://www.ti.com + * + * Authors: Roger Quadros + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 of + * the License as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include + +#include "debug.h" +#include "core.h" +#include "gadget.h" + +static void dwc3_drd_update(struct dwc3 *dwc) +{ + int id; + int new_role; + unsigned long flags; + + if (dwc->drd_prevent_change) + return; + + id = extcon_get_state(dwc->edev, EXTCON_USB_HOST); + /* Host means ID is 0 */ + id = !id; + + if (!id) + new_role = DWC3_GCTL_PRTCAP_HOST; + else + new_role = DWC3_GCTL_PRTCAP_DEVICE; + + if (dwc->current_dr_role == new_role) + return; + + /* stop old role */ + switch (dwc->current_dr_role) { + case DWC3_GCTL_PRTCAP_HOST: + dwc3_host_exit(dwc); + break; + case DWC3_GCTL_PRTCAP_DEVICE: + usb_del_gadget_udc(&dwc->gadget); + break; + default: + break; + } + + /* switch PRTCAP mode. updates current_dr_role */ + spin_lock_irqsave(&dwc->lock, flags); + dwc3_set_mode(dwc, new_role); + spin_unlock_irqrestore(&dwc->lock, flags); + + /* start new role */ + switch (dwc->current_dr_role) { + case DWC3_GCTL_PRTCAP_HOST: + dwc3_host_init(dwc); + break; + case DWC3_GCTL_PRTCAP_DEVICE: + dwc3_event_buffers_setup(dwc); + usb_add_gadget_udc(dwc->dev, &dwc->gadget); + break; + default: + break; + } +} + +static void dwc3_drd_work(struct work_struct *work) +{ + struct dwc3 *dwc = container_of(work, struct dwc3, + drd_work); + dwc3_drd_update(dwc); +} + +static int dwc3_drd_notifier(struct notifier_block *nb, + unsigned long event, void *ptr) +{ + struct dwc3 *dwc = container_of(nb, struct dwc3, edev_nb); + + queue_work(system_power_efficient_wq, &dwc->drd_work); + + return NOTIFY_DONE; +} + +int dwc3_drd_init(struct dwc3 *dwc) +{ + int ret; + int id; + struct device *dev = dwc->dev; + + INIT_WORK(&dwc->drd_work, dwc3_drd_work); + + if (dev->of_node) { + if (of_property_read_bool(dev->of_node, "extcon")) + dwc->edev = extcon_get_edev_by_phandle(dev, 0); + + if (IS_ERR(dwc->edev)) + return PTR_ERR(dwc->edev); + } else { + return -ENODEV; + } + + dwc->edev_nb.notifier_call = dwc3_drd_notifier; + ret = extcon_register_notifier(dwc->edev, EXTCON_USB_HOST, + &dwc->edev_nb); + if (ret < 0) { + dev_err(dwc->dev, "Couldn't register USB-HOST cable notifier\n"); + return -ENODEV; + } + + /* sanity check id & vbus states */ + id = extcon_get_state(dwc->edev, EXTCON_USB_HOST); + if (id < 0) { + dev_err(dwc->dev, "Invalid USB cable state. ID %d", id); + ret = -ENODEV; + goto fail; + } + + /* start in peripheral role by default */ + dwc3_set_mode(dwc, DWC3_GCTL_PRTCAP_DEVICE); + ret = dwc3_gadget_init(dwc); + if (ret) + goto fail; + + /* check & update drd state */ + dwc3_drd_update(dwc); + + return 0; + +fail: + extcon_unregister_notifier(dwc->edev, EXTCON_USB_HOST, + &dwc->edev_nb); + + return ret; +} + +void dwc3_drd_exit(struct dwc3 *dwc) +{ + unsigned long flags; + + spin_lock_irqsave(&dwc->lock, flags); + dwc->drd_prevent_change = true; + spin_unlock_irqrestore(&dwc->lock, flags); + + extcon_unregister_notifier(dwc->edev, EXTCON_USB_HOST, + &dwc->edev_nb); + + /* role might have changed since start, stop active controller */ + if (dwc->current_dr_role == DWC3_GCTL_PRTCAP_HOST) { + dwc3_host_exit(dwc); + /* Add back UDC to match dwc3_gadget_exit() */ + usb_add_gadget_udc(dwc->dev, &dwc->gadget); + } + + dwc3_gadget_exit(dwc); +}