From patchwork Thu Feb 16 13:06:17 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Roger Quadros X-Patchwork-Id: 94068 Delivered-To: patch@linaro.org Received: by 10.140.20.99 with SMTP id 90csp2495842qgi; Thu, 16 Feb 2017 05:07:07 -0800 (PST) X-Received: by 10.98.18.217 with SMTP id 86mr2548563pfs.90.1487250427418; Thu, 16 Feb 2017 05:07:07 -0800 (PST) Return-Path: Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id y71si6938081plh.62.2017.02.16.05.07.07; Thu, 16 Feb 2017 05:07:07 -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; dmarc=fail (p=NONE sp=NONE dis=NONE) header.from=ti.com Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1754879AbdBPNHF (ORCPT + 25 others); Thu, 16 Feb 2017 08:07:05 -0500 Received: from fllnx210.ext.ti.com ([198.47.19.17]:17363 "EHLO fllnx210.ext.ti.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1754750AbdBPNGf (ORCPT ); Thu, 16 Feb 2017 08:06:35 -0500 Received: from dlelxv90.itg.ti.com ([172.17.2.17]) by fllnx210.ext.ti.com (8.15.1/8.15.1) with ESMTP id v1GD6Tq7020237; Thu, 16 Feb 2017 07:06:29 -0600 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 v1GD6Tvq019660; Thu, 16 Feb 2017 07:06:29 -0600 Received: from dlep32.itg.ti.com (157.170.170.100) by DFLE73.ent.ti.com (128.247.5.110) with Microsoft SMTP Server id 14.3.294.0; Thu, 16 Feb 2017 07:06:28 -0600 Received: from lta0400828d.ti.com (ileax41-snat.itg.ti.com [10.172.224.153]) by dlep32.itg.ti.com (8.14.3/8.13.8) with ESMTP id v1GD6JN9006214; Thu, 16 Feb 2017 07:06:27 -0600 From: Roger Quadros To: CC: , , , Roger Quadros Subject: [PATCH v2 4/4] usb: dwc3: Workaround for super-speed host on dra7 in dual-role mode Date: Thu, 16 Feb 2017 15:06:17 +0200 Message-ID: <1487250377-13653-5-git-send-email-rogerq@ti.com> X-Mailer: git-send-email 2.7.4 In-Reply-To: <1487250377-13653-1-git-send-email-rogerq@ti.com> References: <1487250377-13653-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 dra7 OTG core limits the host controller to USB2.0 (high-speed) mode when we're operating in dual-role. We work around that by bypassing the OTG core and reading the extcon framework directly for ID/VBUS events. Signed-off-by: Roger Quadros --- Documentation/devicetree/bindings/usb/dwc3.txt | 2 + drivers/usb/dwc3/core.c | 169 ++++++++++++++++++++++++- drivers/usb/dwc3/core.h | 5 + 3 files changed, 170 insertions(+), 6 deletions(-) -- 2.7.4 Signed-off-by: Roger Quadros diff --git a/Documentation/devicetree/bindings/usb/dwc3.txt b/Documentation/devicetree/bindings/usb/dwc3.txt index e3e6983..9955c0d 100644 --- a/Documentation/devicetree/bindings/usb/dwc3.txt +++ b/Documentation/devicetree/bindings/usb/dwc3.txt @@ -53,6 +53,8 @@ Optional properties: - snps,quirk-frame-length-adjustment: Value for GFLADJ_30MHZ field of GFLADJ register for post-silicon frame length adjustment when the fladj_30mhz_sdbnd signal is invalid or incorrect. + - extcon: phandle to the USB connector extcon device. If present, extcon + device will be used to get USB cable events instead of OTG controller. - tx-fifo-resize: determines if the FIFO *has* to be reallocated. diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c index 619fa7c..b02d911 100644 --- a/drivers/usb/dwc3/core.c +++ b/drivers/usb/dwc3/core.c @@ -918,6 +918,19 @@ static void dwc3_otg_fsm_sync(struct dwc3 *dwc) if (dwc->otg_prevent_sync) return; + if (dwc->edev) { + /* get ID */ + id = extcon_get_state(dwc->edev, EXTCON_USB_HOST); + /* Host means ID == 0 */ + id = !id; + + /* get VBUS */ + vbus = extcon_get_state(dwc->edev, EXTCON_USB); + dwc3_drd_statemachine(dwc, id, vbus); + + return; + } + reg = dwc3_readl(dwc->regs, DWC3_OSTS); id = !!(reg & DWC3_OSTS_CONIDSTS); vbus = !!(reg & DWC3_OSTS_BSESVLD); @@ -934,6 +947,17 @@ static void dwc3_drd_work(struct work_struct *work) spin_unlock(&dwc->lock); } +static int dwc3_drd_notifier(struct notifier_block *nb, + unsigned long event, void *ptr) +{ + struct dwc3 *dwc = container_of(nb, struct dwc3, edev_nb); + + if (!dwc->otg_prevent_sync) + queue_work(system_power_efficient_wq, &dwc->otg_work); + + return NOTIFY_DONE; +} + static void dwc3_otg_disable_events(struct dwc3 *dwc, u32 disable_mask) { dwc->oevten &= ~(disable_mask); @@ -1040,6 +1064,27 @@ static int dwc3_drd_start_host(struct dwc3 *dwc, int on, bool skip) { u32 reg; + if (!dwc->edev) + goto otg; + + if (on) + dwc3_set_mode(dwc, DWC3_GCTL_PRTCAP_HOST); + + if (!skip) { + spin_unlock(&dwc->lock); + + /* start or stop the HCD */ + if (on) + dwc3_host_init(dwc); + else + dwc3_host_exit(dwc); + + spin_lock(&dwc->lock); + } + + return 0; + +otg: /* switch OTG core */ if (on) { /* As per Figure 11-10 A-Device Flow Diagram */ @@ -1133,6 +1178,33 @@ static int dwc3_drd_start_gadget(struct dwc3 *dwc, int on) if (on) dwc3_event_buffers_setup(dwc); + if (!dwc->edev) + goto otg; + + if (on) { + dwc3_set_mode(dwc, DWC3_GCTL_PRTCAP_DEVICE); + /* start the Peripheral driver */ + if (dwc->gadget_driver) { + __dwc3_gadget_start(dwc); + if (dwc->gadget_pullup) + dwc3_gadget_run_stop(dwc, true, false); + } + } else { + /* stop the Peripheral driver */ + if (dwc->gadget_driver) { + if (dwc->gadget_pullup) + dwc3_gadget_run_stop(dwc, false, false); + spin_unlock(&dwc->lock); + if (dwc->gadget_driver->disconnect) + dwc->gadget_driver->disconnect(&dwc->gadget); + spin_lock(&dwc->lock); + __dwc3_gadget_stop(dwc); + } + } + + return 0; + +otg: if (on) { /* As per Figure 11-20 B-Device Flow Diagram */ @@ -1251,6 +1323,13 @@ static void dwc3_otg_core_init(struct dwc3 *dwc) { u32 reg; + /* force drd state machine update the first time */ + dwc->otg_fsm.b_sess_vld = -1; + dwc->otg_fsm.id = -1; + + if (dwc->edev) + return; + /* * As per Figure 11-4 OTG Driver Overall Programming Flow, * block "Initialize GCTL for OTG operation". @@ -1264,15 +1343,14 @@ static void dwc3_otg_core_init(struct dwc3 *dwc) /* Initialize OTG registers */ dwc3_otgregs_init(dwc); - - /* force drd state machine update the first time */ - dwc->otg_fsm.b_sess_vld = -1; - dwc->otg_fsm.id = -1; } /* dwc->lock must be held */ static void dwc3_otg_core_exit(struct dwc3 *dwc) { + if (dwc->edev) + return; + /* disable all otg irqs */ dwc3_otg_disable_events(dwc, DWC3_OTG_ALL_EVENTS); /* clear all events */ @@ -1286,6 +1364,57 @@ static int dwc3_drd_init(struct dwc3 *dwc) INIT_WORK(&dwc->otg_work, dwc3_drd_work); + /* If extcon device is present we don't rely on OTG core for ID event */ + if (dwc->edev) { + int id, vbus; + + dwc->edev_nb.notifier_call = dwc3_drd_notifier; + ret = extcon_register_notifier(dwc->edev, EXTCON_USB, + &dwc->edev_nb); + if (ret < 0) { + dev_err(dwc->dev, "Couldn't register USB cable notifier\n"); + return -ENODEV; + } + + 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"); + ret = -ENODEV; + goto extcon_fail; + } + + /* sanity check id & vbus states */ + id = extcon_get_state(dwc->edev, EXTCON_USB_HOST); + vbus = extcon_get_state(dwc->edev, EXTCON_USB); + if (id < 0 || vbus < 0) { + dev_err(dwc->dev, "Invalid USB cable state. id %d, vbus %d\n", + id, vbus); + ret = -ENODEV; + goto fail; + } + + ret = dwc3_gadget_init(dwc); + if (ret) + goto fail; + + spin_lock_irqsave(&dwc->lock, flags); + dwc3_otg_core_init(dwc); + spin_unlock_irqrestore(&dwc->lock, flags); + queue_work(system_power_efficient_wq, &dwc->otg_work); + + return 0; + +fail: + extcon_unregister_notifier(dwc->edev, EXTCON_USB_HOST, + &dwc->edev_nb); +extcon_fail: + extcon_unregister_notifier(dwc->edev, EXTCON_USB, + &dwc->edev_nb); + + return ret; + } + irq = dwc3_otg_get_irq(dwc); if (irq < 0) return irq; @@ -1326,13 +1455,24 @@ static void dwc3_drd_exit(struct dwc3 *dwc) { unsigned long flags; + spin_lock(&dwc->lock); + dwc->otg_prevent_sync = true; + spin_unlock(&dwc->lock); cancel_work_sync(&dwc->otg_work); + spin_lock_irqsave(&dwc->lock, flags); dwc3_otg_core_exit(dwc); if (dwc->otg_fsm.protocol == PROTO_HOST) dwc3_drd_start_host(dwc, 0, 0); dwc->otg_fsm.protocol = PROTO_UNDEF; - free_irq(dwc->otg_irq, dwc); + if (dwc->edev) { + extcon_unregister_notifier(dwc->edev, EXTCON_USB_HOST, + &dwc->edev_nb); + extcon_unregister_notifier(dwc->edev, EXTCON_USB, + &dwc->edev_nb); + } else { + free_irq(dwc->otg_irq, dwc); + } spin_unlock_irqrestore(&dwc->lock, flags); dwc3_gadget_exit(dwc); @@ -1587,6 +1727,14 @@ static int dwc3_probe(struct platform_device *pdev) dwc3_get_properties(dwc); + 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); + } + platform_set_drvdata(pdev, dwc); dwc3_cache_hwparams(dwc); @@ -1743,6 +1891,13 @@ static int dwc3_resume_common(struct dwc3 *dwc) if (ret) return ret; + if (dwc->dr_mode == USB_DR_MODE_OTG && + dwc->edev) { + if (dwc->otg_fsm.protocol == PROTO_HOST) + dwc3_set_mode(dwc, DWC3_GCTL_PRTCAP_HOST); + else if (dwc->otg_fsm.protocol == PROTO_GADGET) + dwc3_set_mode(dwc, DWC3_GCTL_PRTCAP_DEVICE); + } spin_lock_irqsave(&dwc->lock, flags); switch (dwc->dr_mode) { @@ -1771,8 +1926,10 @@ static int dwc3_resume_common(struct dwc3 *dwc) if (dwc->dr_mode == USB_DR_MODE_OTG) { dwc3_otg_core_init(dwc); - if (dwc->otg_fsm.protocol == PROTO_HOST) + if ((dwc->otg_fsm.protocol == PROTO_HOST) && + !dwc->edev) { dwc3_drd_start_host(dwc, true, 1); + } } spin_unlock_irqrestore(&dwc->lock, flags); diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h index bf8951d..fc060ae 100644 --- a/drivers/usb/dwc3/core.h +++ b/drivers/usb/dwc3/core.h @@ -38,6 +38,7 @@ #include #include +#include #include #define DWC3_MSG_MAX 500 @@ -869,6 +870,8 @@ struct dwc3_scratchpad_array { * @current_mode: current mode of operation written to PRTCAPDIR * @otg_work: work struct for otg/dual-role * @otg_needs_host_start: flag that OTG controller needs to start host + * @edev: extcon handle + * @edev_nb: extcon notifier * @fladj: frame length adjustment * @irq_gadget: peripheral controller's IRQ number * @otg_irq: IRQ number for OTG IRQs @@ -1007,6 +1010,8 @@ struct dwc3 { u32 current_mode; struct work_struct otg_work; bool otg_needs_host_start; + struct extcon_dev *edev; + struct notifier_block edev_nb; enum usb_phy_interface hsphy_mode;