* [PATCH v6 01/10] usb: dwc3: core.h: add some register definitions
2016-04-11 11:34 [PATCH v6 00/10] usb: dwc3: add dual-role support Roger Quadros
@ 2016-04-11 11:34 ` Roger Quadros
2016-04-11 11:34 ` [PATCH v6 02/10] usb: dwc3: omap: Make the wrapper interrupt shared Roger Quadros
` (8 subsequent siblings)
9 siblings, 0 replies; 30+ messages in thread
From: Roger Quadros @ 2016-04-11 11:34 UTC (permalink / raw)
To: balbi
Cc: tony, Joao.Pinto, sergei.shtylyov, peter.chen, jun.li,
grygorii.strashko, yoshihiro.shimoda.uh, nsekhar, linux-usb,
linux-kernel, linux-omap, Roger Quadros
Add OTG and GHWPARAMS6 register definitions
Signed-off-by: Roger Quadros <rogerq@ti.com>
---
drivers/usb/dwc3/core.h | 84 ++++++++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 83 insertions(+), 1 deletion(-)
diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h
index 6254b2f..81039f7 100644
--- a/drivers/usb/dwc3/core.h
+++ b/drivers/usb/dwc3/core.h
@@ -176,6 +176,15 @@
#define DWC3_GCTL_GBLHIBERNATIONEN (1 << 1)
#define DWC3_GCTL_DSBLCLKGTNG (1 << 0)
+/* Global Status Register */
+#define DWC3_GSTS_OTG_IP BIT(10)
+#define DWC3_GSTS_BC_IP BIT(9)
+#define DWC3_GSTS_ADP_IP BIT(8)
+#define DWC3_GSTS_HOST_IP BIT(7)
+#define DWC3_GSTS_DEVICE_IP BIT(6)
+#define DWC3_GSTS_CSR_TIMEOUT BIT(5)
+#define DWC3_GSTS_BUS_ERR_ADDR_VLD BIT(4)
+
/* Global USB2 PHY Configuration Register */
#define DWC3_GUSB2PHYCFG_PHYSOFTRST (1 << 31)
#define DWC3_GUSB2PHYCFG_SUSPHY (1 << 6)
@@ -239,7 +248,12 @@
#define DWC3_MAX_HIBER_SCRATCHBUFS 15
/* Global HWPARAMS6 Register */
-#define DWC3_GHWPARAMS6_EN_FPGA (1 << 7)
+#define DWC3_GHWPARAMS6_BCSUPPORT BIT(14)
+#define DWC3_GHWPARAMS6_OTG3SUPPORT BIT(13)
+#define DWC3_GHWPARAMS6_ADPSUPPORT BIT(12)
+#define DWC3_GHWPARAMS6_HNPSUPPORT BIT(11)
+#define DWC3_GHWPARAMS6_SRPSUPPORT BIT(10)
+#define DWC3_GHWPARAMS6_EN_FPGA BIT(7)
/* Global Frame Length Adjustment Register */
#define DWC3_GFLADJ_30MHZ_SDBND_SEL (1 << 7)
@@ -404,6 +418,74 @@
#define DWC3_DEPCMD_TYPE_BULK 2
#define DWC3_DEPCMD_TYPE_INTR 3
+/* OTG Configuration Register */
+#define DWC3_OCFG_DISPWRCUTTOFF BIT(5)
+#define DWC3_OCFG_HIBDISMASK BIT(4)
+#define DWC3_OCFG_SFTRSTMASK BIT(3)
+#define DWC3_OCFG_OTGVERSION BIT(2)
+#define DWC3_OCFG_HNPCAP BIT(1)
+#define DWC3_OCFG_SRPCAP BIT(0)
+
+/* OTG CTL Register */
+#define DWC3_OCTL_OTG3GOERR BIT(7)
+#define DWC3_OCTL_PERIMODE BIT(6)
+#define DWC3_OCTL_PRTPWRCTL BIT(5)
+#define DWC3_OCTL_HNPREQ BIT(4)
+#define DWC3_OCTL_SESREQ BIT(3)
+#define DWC3_OCTL_TERMSELIDPULSE BIT(2)
+#define DWC3_OCTL_DEVSETHNPEN BIT(1)
+#define DWC3_OCTL_HSTSETHNPEN BIT(0)
+
+/* OTG Event Register */
+#define DWC3_OEVT_DEVICEMODE BIT(31)
+#define DWC3_OEVT_XHCIRUNSTPSET BIT(27)
+#define DWC3_OEVT_DEVRUNSTPSET BIT(26)
+#define DWC3_OEVT_HIBENTRY BIT(25)
+#define DWC3_OEVT_CONIDSTSCHNG BIT(24)
+#define DWC3_OEVT_HRRCONFNOTIF BIT(23)
+#define DWC3_OEVT_HRRINITNOTIF BIT(22)
+#define DWC3_OEVT_ADEVIDLE BIT(21)
+#define DWC3_OEVT_ADEVBHOSTEND BIT(20)
+#define DWC3_OEVT_ADEVHOST BIT(19)
+#define DWC3_OEVT_ADEVHNPCHNG BIT(18)
+#define DWC3_OEVT_ADEVSRPDET BIT(17)
+#define DWC3_OEVT_ADEVSESSENDDET BIT(16)
+#define DWC3_OEVT_BDEVBHOSTEND BIT(11)
+#define DWC3_OEVT_BDEVHNPCHNG BIT(10)
+#define DWC3_OEVT_BDEVSESSVLDDET BIT(9)
+#define DWC3_OEVT_BDEVVBUSCHNG BIT(8)
+#define DWC3_OEVT_BSESSVLD BIT(3)
+#define DWC3_OEVT_HSTNEGSTS BIT(2)
+#define DWC3_OEVT_SESREQSTS BIT(1)
+#define DWC3_OEVT_ERROR BIT(0)
+
+/* OTG Event Enable Register */
+#define DWC3_OEVTEN_XHCIRUNSTPSETEN BIT(27)
+#define DWC3_OEVTEN_DEVRUNSTPSETEN BIT(26)
+#define DWC3_OEVTEN_HIBENTRYEN BIT(25)
+#define DWC3_OEVTEN_CONIDSTSCHNGEN BIT(24)
+#define DWC3_OEVTEN_HRRCONFNOTIFEN BIT(23)
+#define DWC3_OEVTEN_HRRINITNOTIFEN BIT(22)
+#define DWC3_OEVTEN_ADEVIDLEEN BIT(21)
+#define DWC3_OEVTEN_ADEVBHOSTENDEN BIT(20)
+#define DWC3_OEVTEN_ADEVHOSTEN BIT(19)
+#define DWC3_OEVTEN_ADEVHNPCHNGEN BIT(18)
+#define DWC3_OEVTEN_ADEVSRPDETEN BIT(17)
+#define DWC3_OEVTEN_ADEVSESSENDDETEN BIT(16)
+#define DWC3_OEVTEN_BDEVHOSTENDEN BIT(11)
+#define DWC3_OEVTEN_BDEVHNPCHNGEN BIT(10)
+#define DWC3_OEVTEN_BDEVSESSVLDDETEN BIT(9)
+#define DWC3_OEVTEN_BDEVVBUSCHNGE BIT(8)
+
+/* OTG Status Register */
+#define DWC3_OSTS_DEVRUNSTP BIT(13)
+#define DWC3_OSTS_XHCIRUNSTP BIT(12)
+#define DWC3_OSTS_PERIPHERALSTATE BIT(4)
+#define DWC3_OSTS_XHCIPRTPOWER BIT(3)
+#define DWC3_OSTS_BSESVLD BIT(2)
+#define DWC3_OSTS_VBUSVLD BIT(1)
+#define DWC3_OSTS_CONIDSTS BIT(0)
+
/* Structures */
struct dwc3_trb;
--
2.5.0
^ permalink raw reply related [flat|nested] 30+ messages in thread* [PATCH v6 02/10] usb: dwc3: omap: Make the wrapper interrupt shared
2016-04-11 11:34 [PATCH v6 00/10] usb: dwc3: add dual-role support Roger Quadros
2016-04-11 11:34 ` [PATCH v6 01/10] usb: dwc3: core.h: add some register definitions Roger Quadros
@ 2016-04-11 11:34 ` Roger Quadros
[not found] ` <1460374506-9779-3-git-send-email-rogerq-l0cyMroinI0@public.gmane.org>
2016-04-11 11:34 ` [PATCH v6 03/10] usb: dwc3: omap: Pass VBUS and ID events transparently Roger Quadros
` (7 subsequent siblings)
9 siblings, 1 reply; 30+ messages in thread
From: Roger Quadros @ 2016-04-11 11:34 UTC (permalink / raw)
To: balbi
Cc: tony, Joao.Pinto, sergei.shtylyov, peter.chen, jun.li,
grygorii.strashko, yoshihiro.shimoda.uh, nsekhar, linux-usb,
linux-kernel, linux-omap, Roger Quadros
The wrapper interrupt is shared with OTG core so mark it IRQF_SHARED.
Use request_threaded_irq() to ensure that irqflags match for the
shared interrupt handlers. If we don't use request_treaded_irq() then
forced threaded irq will set IRQF_ONESHOT and this won't match with
the OTG irq handler.
Signed-off-by: Roger Quadros <rogerq@ti.com>
---
drivers/usb/dwc3/dwc3-omap.c | 12 +++++++++---
1 file changed, 9 insertions(+), 3 deletions(-)
diff --git a/drivers/usb/dwc3/dwc3-omap.c b/drivers/usb/dwc3/dwc3-omap.c
index 22e9606..51ca098 100644
--- a/drivers/usb/dwc3/dwc3-omap.c
+++ b/drivers/usb/dwc3/dwc3-omap.c
@@ -274,19 +274,25 @@ static irqreturn_t dwc3_omap_interrupt(int irq, void *_omap)
{
struct dwc3_omap *omap = _omap;
u32 reg;
+ int ret = IRQ_NONE;
reg = dwc3_omap_read_irqmisc_status(omap);
+ if (reg)
+ ret = IRQ_HANDLED;
+
if (reg & USBOTGSS_IRQMISC_DMADISABLECLR)
omap->dma_status = false;
dwc3_omap_write_irqmisc_status(omap, reg);
reg = dwc3_omap_read_irq0_status(omap);
+ if (reg)
+ ret = IRQ_HANDLED;
dwc3_omap_write_irq0_status(omap, reg);
- return IRQ_HANDLED;
+ return ret;
}
static void dwc3_omap_enable_irqs(struct dwc3_omap *omap)
@@ -506,8 +512,8 @@ static int dwc3_omap_probe(struct platform_device *pdev)
reg = dwc3_omap_readl(omap->base, USBOTGSS_SYSCONFIG);
omap->dma_status = !!(reg & USBOTGSS_SYSCONFIG_DMADISABLE);
- ret = devm_request_irq(dev, omap->irq, dwc3_omap_interrupt, 0,
- "dwc3-omap", omap);
+ ret = devm_request_threaded_irq(dev, omap->irq, dwc3_omap_interrupt,
+ NULL, IRQF_SHARED, "dwc3-omap", omap);
if (ret) {
dev_err(dev, "failed to request IRQ #%d --> %d\n",
omap->irq, ret);
--
2.5.0
^ permalink raw reply related [flat|nested] 30+ messages in thread* [PATCH v6 03/10] usb: dwc3: omap: Pass VBUS and ID events transparently
2016-04-11 11:34 [PATCH v6 00/10] usb: dwc3: add dual-role support Roger Quadros
2016-04-11 11:34 ` [PATCH v6 01/10] usb: dwc3: core.h: add some register definitions Roger Quadros
2016-04-11 11:34 ` [PATCH v6 02/10] usb: dwc3: omap: Make the wrapper interrupt shared Roger Quadros
@ 2016-04-11 11:34 ` Roger Quadros
[not found] ` <1460374506-9779-4-git-send-email-rogerq-l0cyMroinI0@public.gmane.org>
2016-04-11 11:35 ` [PATCH v6 04/10] usb: dwc3: omap: fix up error path on probe() Roger Quadros
` (6 subsequent siblings)
9 siblings, 1 reply; 30+ messages in thread
From: Roger Quadros @ 2016-04-11 11:34 UTC (permalink / raw)
To: balbi
Cc: tony, Joao.Pinto, sergei.shtylyov, peter.chen, jun.li,
grygorii.strashko, yoshihiro.shimoda.uh, nsekhar, linux-usb,
linux-kernel, linux-omap, Roger Quadros
Don't make any decisions regarding VBUS session based on ID
status. That is best left to the OTG core.
Pass ID and VBUS events independent of each other so that OTG
core knows exactly what to do.
This makes dual-role with extcon work with OTG irq on OMAP platforms.
Signed-off-by: Roger Quadros <rogerq@ti.com>
---
drivers/usb/dwc3/dwc3-omap.c | 15 ++++++---------
1 file changed, 6 insertions(+), 9 deletions(-)
diff --git a/drivers/usb/dwc3/dwc3-omap.c b/drivers/usb/dwc3/dwc3-omap.c
index 51ca098..c9b918d 100644
--- a/drivers/usb/dwc3/dwc3-omap.c
+++ b/drivers/usb/dwc3/dwc3-omap.c
@@ -233,19 +233,14 @@ static void dwc3_omap_set_mailbox(struct dwc3_omap *omap,
}
val = dwc3_omap_read_utmi_ctrl(omap);
- val &= ~(USBOTGSS_UTMI_OTG_CTRL_IDDIG
- | USBOTGSS_UTMI_OTG_CTRL_VBUSVALID
- | USBOTGSS_UTMI_OTG_CTRL_SESSEND);
- val |= USBOTGSS_UTMI_OTG_CTRL_SESSVALID
- | USBOTGSS_UTMI_OTG_CTRL_POWERPRESENT;
+ val &= ~USBOTGSS_UTMI_OTG_CTRL_IDDIG;
dwc3_omap_write_utmi_ctrl(omap, val);
break;
case OMAP_DWC3_VBUS_VALID:
val = dwc3_omap_read_utmi_ctrl(omap);
val &= ~USBOTGSS_UTMI_OTG_CTRL_SESSEND;
- val |= USBOTGSS_UTMI_OTG_CTRL_IDDIG
- | USBOTGSS_UTMI_OTG_CTRL_VBUSVALID
+ val |= USBOTGSS_UTMI_OTG_CTRL_VBUSVALID
| USBOTGSS_UTMI_OTG_CTRL_SESSVALID
| USBOTGSS_UTMI_OTG_CTRL_POWERPRESENT;
dwc3_omap_write_utmi_ctrl(omap, val);
@@ -254,14 +249,16 @@ static void dwc3_omap_set_mailbox(struct dwc3_omap *omap,
case OMAP_DWC3_ID_FLOAT:
if (omap->vbus_reg)
regulator_disable(omap->vbus_reg);
+ val = dwc3_omap_read_utmi_ctrl(omap);
+ val |= USBOTGSS_UTMI_OTG_CTRL_IDDIG;
+ dwc3_omap_write_utmi_ctrl(omap, val);
case OMAP_DWC3_VBUS_OFF:
val = dwc3_omap_read_utmi_ctrl(omap);
val &= ~(USBOTGSS_UTMI_OTG_CTRL_SESSVALID
| USBOTGSS_UTMI_OTG_CTRL_VBUSVALID
| USBOTGSS_UTMI_OTG_CTRL_POWERPRESENT);
- val |= USBOTGSS_UTMI_OTG_CTRL_SESSEND
- | USBOTGSS_UTMI_OTG_CTRL_IDDIG;
+ val |= USBOTGSS_UTMI_OTG_CTRL_SESSEND;
dwc3_omap_write_utmi_ctrl(omap, val);
break;
--
2.5.0
^ permalink raw reply related [flat|nested] 30+ messages in thread* [PATCH v6 04/10] usb: dwc3: omap: fix up error path on probe()
2016-04-11 11:34 [PATCH v6 00/10] usb: dwc3: add dual-role support Roger Quadros
` (2 preceding siblings ...)
2016-04-11 11:34 ` [PATCH v6 03/10] usb: dwc3: omap: Pass VBUS and ID events transparently Roger Quadros
@ 2016-04-11 11:35 ` Roger Quadros
[not found] ` <1460374506-9779-5-git-send-email-rogerq-l0cyMroinI0@public.gmane.org>
2016-04-11 11:35 ` [PATCH v6 05/10] usb: dwc3: core: cleanup IRQ resources Roger Quadros
` (5 subsequent siblings)
9 siblings, 1 reply; 30+ messages in thread
From: Roger Quadros @ 2016-04-11 11:35 UTC (permalink / raw)
To: balbi
Cc: tony, Joao.Pinto, sergei.shtylyov, peter.chen, jun.li,
grygorii.strashko, yoshihiro.shimoda.uh, nsekhar, linux-usb,
linux-kernel, linux-omap, Roger Quadros
From: Felipe Balbi <balbi@kernel.org>
Even if pm_runtime_get*() fails, we *MUST* call
pm_runtime_put_sync() before disabling PM.
While at it, remove superfluous dwc3_omap_disable_irqs()
in error path.
Signed-off-by: Felipe Balbi <balbi@kernel.org>
[nsekhar@ti.com: patch description updates]
Signed-off-by: Sekhar Nori <nsekhar@ti.com>
Signed-off-by: Roger Quadros <rogerq@ti.com>
---
drivers/usb/dwc3/dwc3-omap.c | 12 ++++--------
1 file changed, 4 insertions(+), 8 deletions(-)
diff --git a/drivers/usb/dwc3/dwc3-omap.c b/drivers/usb/dwc3/dwc3-omap.c
index c9b918d..3497b25 100644
--- a/drivers/usb/dwc3/dwc3-omap.c
+++ b/drivers/usb/dwc3/dwc3-omap.c
@@ -499,7 +499,7 @@ static int dwc3_omap_probe(struct platform_device *pdev)
ret = pm_runtime_get_sync(dev);
if (ret < 0) {
dev_err(dev, "get_sync failed with err %d\n", ret);
- goto err0;
+ goto err1;
}
dwc3_omap_map_offset(omap);
@@ -519,28 +519,24 @@ static int dwc3_omap_probe(struct platform_device *pdev)
ret = dwc3_omap_extcon_register(omap);
if (ret < 0)
- goto err2;
+ goto err1;
ret = of_platform_populate(node, NULL, NULL, dev);
if (ret) {
dev_err(&pdev->dev, "failed to create dwc3 core\n");
- goto err3;
+ goto err2;
}
dwc3_omap_enable_irqs(omap);
return 0;
-err3:
+err2:
extcon_unregister_notifier(omap->edev, EXTCON_USB, &omap->vbus_nb);
extcon_unregister_notifier(omap->edev, EXTCON_USB_HOST, &omap->id_nb);
-err2:
- dwc3_omap_disable_irqs(omap);
err1:
pm_runtime_put_sync(dev);
-
-err0:
pm_runtime_disable(dev);
return ret;
--
2.5.0
^ permalink raw reply related [flat|nested] 30+ messages in thread* [PATCH v6 05/10] usb: dwc3: core: cleanup IRQ resources
2016-04-11 11:34 [PATCH v6 00/10] usb: dwc3: add dual-role support Roger Quadros
` (3 preceding siblings ...)
2016-04-11 11:35 ` [PATCH v6 04/10] usb: dwc3: omap: fix up error path on probe() Roger Quadros
@ 2016-04-11 11:35 ` Roger Quadros
2016-04-11 11:35 ` [PATCH v6 06/10] usb: dwc3: add dual-role support Roger Quadros
` (4 subsequent siblings)
9 siblings, 0 replies; 30+ messages in thread
From: Roger Quadros @ 2016-04-11 11:35 UTC (permalink / raw)
To: balbi
Cc: tony, Joao.Pinto, sergei.shtylyov, peter.chen, jun.li,
grygorii.strashko, yoshihiro.shimoda.uh, nsekhar, linux-usb,
linux-kernel, linux-omap, Roger Quadros
Implementations might use different IRQs for
host, gadget and OTG so use named interrupt resources
to allow Device tree to specify the 3 interrupts.
Following are the interrupt names
Peripheral Interrupt - peripheral
HOST Interrupt - host
OTG Interrupt - otg
We still maintain backward compatibility for a single named
interrupt for all 3 interrupts (e.g. for dwc3-pci) and
single unnamed interrupt for all 3 interrupts (e.g. old DT).
Signed-off-by: Roger Quadros <rogerq@ti.com>
---
drivers/usb/dwc3/core.c | 26 ++++++++++++++++----------
drivers/usb/dwc3/core.h | 5 +++++
drivers/usb/dwc3/gadget.c | 19 ++++++++++++++++++-
drivers/usb/dwc3/host.c | 19 +++++++++++++++++++
4 files changed, 58 insertions(+), 11 deletions(-)
diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c
index 17fd8144..1c754749 100644
--- a/drivers/usb/dwc3/core.c
+++ b/drivers/usb/dwc3/core.c
@@ -746,6 +746,8 @@ static int dwc3_core_init_mode(struct dwc3 *dwc)
{
struct device *dev = dwc->dev;
int ret;
+ struct resource *res;
+ struct platform_device *dwc3_pdev = to_platform_device(dwc->dev);
switch (dwc->dr_mode) {
case USB_DR_MODE_PERIPHERAL:
@@ -765,6 +767,20 @@ static int dwc3_core_init_mode(struct dwc3 *dwc)
}
break;
case USB_DR_MODE_OTG:
+ dwc->otg_irq = platform_get_irq_byname(dwc3_pdev, "otg");
+ if (dwc->otg_irq <= 0) {
+ dwc->otg_irq = platform_get_irq_byname(dwc3_pdev,
+ "dwc_usb3");
+ if (dwc->otg_irq <= 0) {
+ res = platform_get_resource(dwc3_pdev,
+ IORESOURCE_IRQ, 0);
+ if (!res) {
+ dev_err(dwc->dev, "missing otg IRQ\n");
+ return -ENODEV;
+ }
+ dwc->otg_irq = res->start;
+ }
+ }
dwc3_set_mode(dwc, DWC3_GCTL_PRTCAP_OTG);
ret = dwc3_host_init(dwc);
if (ret) {
@@ -831,16 +847,6 @@ static int dwc3_probe(struct platform_device *pdev)
dwc->mem = mem;
dwc->dev = dev;
- res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
- if (!res) {
- dev_err(dev, "missing IRQ\n");
- return -ENODEV;
- }
- dwc->xhci_resources[1].start = res->start;
- dwc->xhci_resources[1].end = res->end;
- dwc->xhci_resources[1].flags = res->flags;
- dwc->xhci_resources[1].name = res->name;
-
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res) {
dev_err(dev, "missing memory resource\n");
diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h
index 81039f7..79422dd 100644
--- a/drivers/usb/dwc3/core.h
+++ b/drivers/usb/dwc3/core.h
@@ -754,6 +754,8 @@ struct dwc3_scratchpad_array {
* @maximum_speed: maximum speed requested (mainly for testing purposes)
* @revision: revision register contents
* @dr_mode: requested mode of operation
+ * @gadget_irq: IRQ number for Peripheral IRQs
+ * @otg_irq: IRQ number for OTG IRQs
* @usb2_phy: pointer to USB2 PHY
* @usb3_phy: pointer to USB3 PHY
* @usb2_generic_phy: pointer to USB2 PHY
@@ -857,6 +859,9 @@ struct dwc3 {
enum usb_dr_mode dr_mode;
+ int gadget_irq;
+ int otg_irq;
+
/* used for suspend/resume */
u32 dcfg;
u32 gctl;
diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c
index 3ac170f..90f514c 100644
--- a/drivers/usb/dwc3/gadget.c
+++ b/drivers/usb/dwc3/gadget.c
@@ -1617,7 +1617,7 @@ static int dwc3_gadget_start(struct usb_gadget *g,
int irq;
u32 reg;
- irq = platform_get_irq(to_platform_device(dwc->dev), 0);
+ irq = dwc->gadget_irq;
ret = request_threaded_irq(irq, dwc3_interrupt, dwc3_thread_interrupt,
IRQF_SHARED, "dwc3", dwc);
if (ret) {
@@ -2795,6 +2795,23 @@ static irqreturn_t dwc3_interrupt(int irq, void *_dwc)
int dwc3_gadget_init(struct dwc3 *dwc)
{
int ret;
+ struct resource *res;
+ struct platform_device *dwc3_pdev = to_platform_device(dwc->dev);
+
+ dwc->gadget_irq = platform_get_irq_byname(dwc3_pdev, "peripheral");
+ if (dwc->gadget_irq <= 0) {
+ dwc->gadget_irq = platform_get_irq_byname(dwc3_pdev,
+ "dwc_usb3");
+ if (dwc->gadget_irq <= 0) {
+ res = platform_get_resource(dwc3_pdev, IORESOURCE_IRQ,
+ 0);
+ if (!res) {
+ dev_err(dwc->dev, "missing peripheral IRQ\n");
+ return -ENODEV;
+ }
+ dwc->gadget_irq = res->start;
+ }
+ }
dwc->ctrl_req = dma_alloc_coherent(dwc->dev, sizeof(*dwc->ctrl_req),
&dwc->ctrl_req_addr, GFP_KERNEL);
diff --git a/drivers/usb/dwc3/host.c b/drivers/usb/dwc3/host.c
index c679f63..f2b60a4 100644
--- a/drivers/usb/dwc3/host.c
+++ b/drivers/usb/dwc3/host.c
@@ -25,6 +25,25 @@ int dwc3_host_init(struct dwc3 *dwc)
struct platform_device *xhci;
struct usb_xhci_pdata pdata;
int ret;
+ struct resource *res;
+ struct platform_device *dwc3_pdev = to_platform_device(dwc->dev);
+
+ res = platform_get_resource_byname(dwc3_pdev, IORESOURCE_IRQ, "host");
+ if (!res) {
+ res = platform_get_resource_byname(dwc3_pdev, IORESOURCE_IRQ,
+ "dwc_usb3");
+ if (!res) {
+ res = platform_get_resource(dwc3_pdev, IORESOURCE_IRQ,
+ 0);
+ if (!res)
+ return -ENOMEM;
+ }
+ }
+
+ dwc->xhci_resources[1].start = res->start;
+ dwc->xhci_resources[1].end = res->end;
+ dwc->xhci_resources[1].flags = res->flags;
+ dwc->xhci_resources[1].name = res->name;
xhci = platform_device_alloc("xhci-hcd", PLATFORM_DEVID_AUTO);
if (!xhci) {
--
2.5.0
^ permalink raw reply related [flat|nested] 30+ messages in thread* [PATCH v6 06/10] usb: dwc3: add dual-role support
2016-04-11 11:34 [PATCH v6 00/10] usb: dwc3: add dual-role support Roger Quadros
` (4 preceding siblings ...)
2016-04-11 11:35 ` [PATCH v6 05/10] usb: dwc3: core: cleanup IRQ resources Roger Quadros
@ 2016-04-11 11:35 ` Roger Quadros
2016-04-11 11:35 ` [PATCH v6 07/10] usb: dwc3: gadget: Fix suspend/resume during dual-role mode Roger Quadros
` (3 subsequent siblings)
9 siblings, 0 replies; 30+ messages in thread
From: Roger Quadros @ 2016-04-11 11:35 UTC (permalink / raw)
To: balbi
Cc: tony, Joao.Pinto, sergei.shtylyov, peter.chen, jun.li,
grygorii.strashko, yoshihiro.shimoda.uh, nsekhar, linux-usb,
linux-kernel, linux-omap, Roger Quadros
Register with the USB OTG core. Since we don't support
OTG yet we just work as a dual-role device even
if device tree says "otg".
Get ID and VBUS information from the OTG controller
and kick the OTG state machine.
Make sure dual-role functionality works across system
suspend/resume.
Signed-off-by: Roger Quadros <rogerq@ti.com>
---
drivers/usb/dwc3/core.c | 544 ++++++++++++++++++++++++++++++++++++++++++++--
drivers/usb/dwc3/core.h | 20 ++
drivers/usb/dwc3/gadget.c | 6 +-
drivers/usb/dwc3/host.c | 2 +
4 files changed, 550 insertions(+), 22 deletions(-)
diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c
index 1c754749..f24c091 100644
--- a/drivers/usb/dwc3/core.c
+++ b/drivers/usb/dwc3/core.c
@@ -57,6 +57,7 @@ void dwc3_set_mode(struct dwc3 *dwc, u32 mode)
reg = dwc3_readl(dwc->regs, DWC3_GCTL);
reg &= ~(DWC3_GCTL_PRTCAPDIR(DWC3_GCTL_PRTCAP_OTG));
reg |= DWC3_GCTL_PRTCAPDIR(mode);
+ dwc->current_mode = mode;
dwc3_writel(dwc->regs, DWC3_GCTL, reg);
}
@@ -742,13 +743,444 @@ static int dwc3_core_get_phy(struct dwc3 *dwc)
return 0;
}
-static int dwc3_core_init_mode(struct dwc3 *dwc)
+/* Get OTG events and sync it to OTG fsm */
+static void dwc3_otg_fsm_sync(struct dwc3 *dwc)
+{
+ u32 reg;
+ int id, vbus;
+
+ /*
+ * calling usb_otg_sync_inputs() during resume breaks host
+ * if adapter was removed during suspend as xhci driver
+ * is not prepared to see hcd removal before xhci_resume.
+ */
+ if (dwc->otg_prevent_sync)
+ return;
+
+ reg = dwc3_readl(dwc->regs, DWC3_OSTS);
+ dwc3_trace(trace_dwc3_core, "otgstatus 0x%x\n", reg);
+
+ id = !!(reg & DWC3_OSTS_CONIDSTS);
+ vbus = !!(reg & DWC3_OSTS_BSESVLD);
+
+ dwc3_trace(trace_dwc3_core, "id %d vbus %d\n", id, vbus);
+ dwc->otg->fsm.id = id;
+ dwc->otg->fsm.b_sess_vld = vbus;
+ usb_otg_sync_inputs(dwc->otg);
+}
+
+static void dwc3_otg_mask_irq(struct dwc3 *dwc)
+{
+ dwc->oevten = dwc3_readl(dwc->regs, DWC3_OEVTEN);
+ dwc3_writel(dwc->regs, DWC3_OEVTEN, 0);
+}
+
+static void dwc3_otg_unmask_irq(struct dwc3 *dwc)
+{
+ dwc3_writel(dwc->regs, DWC3_OEVTEN, dwc->oevten);
+}
+
+static void dwc3_otg_disable_events(struct dwc3 *dwc, u32 disable_mask)
+{
+ dwc->oevten &= ~(disable_mask);
+ dwc3_writel(dwc->regs, DWC3_OEVTEN, dwc->oevten);
+}
+
+static void dwc3_otg_enable_events(struct dwc3 *dwc, u32 enable_mask)
+{
+ dwc->oevten |= (enable_mask);
+ dwc3_writel(dwc->regs, DWC3_OEVTEN, dwc->oevten);
+}
+
+#define DWC3_OTG_ALL_EVENTS (DWC3_OEVTEN_XHCIRUNSTPSETEN | \
+ DWC3_OEVTEN_DEVRUNSTPSETEN | DWC3_OEVTEN_HIBENTRYEN | \
+ DWC3_OEVTEN_CONIDSTSCHNGEN | DWC3_OEVTEN_HRRCONFNOTIFEN | \
+ DWC3_OEVTEN_HRRINITNOTIFEN | DWC3_OEVTEN_ADEVIDLEEN | \
+ DWC3_OEVTEN_ADEVBHOSTENDEN | DWC3_OEVTEN_ADEVHOSTEN | \
+ DWC3_OEVTEN_ADEVHNPCHNGEN | DWC3_OEVTEN_ADEVSRPDETEN | \
+ DWC3_OEVTEN_ADEVSESSENDDETEN | DWC3_OEVTEN_BDEVHOSTENDEN | \
+ DWC3_OEVTEN_BDEVHNPCHNGEN | DWC3_OEVTEN_BDEVSESSVLDDETEN | \
+ DWC3_OEVTEN_BDEVVBUSCHNGE)
+
+static int dwc3_drd_start_host(struct usb_otg *otg, int on);
+static int dwc3_drd_start_gadget(struct usb_otg *otg, int on);
+static irqreturn_t dwc3_otg_thread_irq(int irq, void *_dwc)
+{
+ struct dwc3 *dwc = _dwc;
+ unsigned long flags;
+
+ spin_lock_irqsave(&dwc->lock, flags);
+
+ /*
+ * this bit is needed for otg-host to work after system suspend/resume
+ */
+ if ((dwc->otg->state == OTG_STATE_A_HOST) &&
+ !(dwc->oevt & DWC3_OEVT_DEVICEMODE)) {
+ spin_unlock_irqrestore(&dwc->lock, flags);
+ dwc3_drd_start_host(dwc->otg, true);
+ spin_lock_irqsave(&dwc->lock, flags);
+ }
+
+ dwc3_otg_fsm_sync(dwc);
+ dwc3_otg_unmask_irq(dwc);
+
+ dwc->oevt = 0;
+ spin_unlock_irqrestore(&dwc->lock, flags);
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t dwc3_otg_irq(int irq, void *_dwc)
+{
+ struct dwc3 *dwc = _dwc;
+ irqreturn_t ret = IRQ_NONE;
+ u32 reg;
+
+ reg = dwc3_readl(dwc->regs, DWC3_OEVT);
+ if (reg) {
+ dwc->oevt = reg;
+ dwc3_writel(dwc->regs, DWC3_OEVT, reg);
+ dwc3_otg_mask_irq(dwc);
+ ret = IRQ_WAKE_THREAD;
+ }
+
+ return ret;
+}
+
+/* --------------------- Dual-Role management ------------------------------- */
+static void dwc3_otgregs_init(struct dwc3 *dwc)
+{
+ u32 reg;
+
+ /*
+ * Prevent host/device reset from resetting OTG core.
+ * If we don't do this then xhci_reset (USBCMD.HCRST) will reset
+ * the signal outputs sent to the PHY, the OTG FSM logic of the
+ * core and also the resets to the VBUS filters inside the core.
+ */
+ reg = dwc3_readl(dwc->regs, DWC3_OCFG);
+ reg |= DWC3_OCFG_SFTRSTMASK;
+ dwc3_writel(dwc->regs, DWC3_OCFG, reg);
+
+ /* Disable hibernation for simplicity */
+ reg = dwc3_readl(dwc->regs, DWC3_GCTL);
+ reg &= ~DWC3_GCTL_GBLHIBERNATIONEN;
+ dwc3_writel(dwc->regs, DWC3_GCTL, reg);
+
+ /*
+ * Initialize OTG registers as per
+ * Figure 11-4 OTG Driver Overall Programming Flow
+ */
+ /* OCFG.SRPCap = 0, OCFG.HNPCap = 0 */
+ reg = dwc3_readl(dwc->regs, DWC3_OCFG);
+ reg &= ~(DWC3_OCFG_SRPCAP | DWC3_OCFG_HNPCAP);
+ dwc3_writel(dwc->regs, DWC3_OCFG, reg);
+ /* OEVT = FFFF */
+ dwc3_writel(dwc->regs, DWC3_OEVT, ~0);
+ /* OEVTEN = 0 */
+ dwc3_otg_disable_events(dwc, DWC3_OTG_ALL_EVENTS);
+ /* OEVTEN.ConIDStsChngEn = 1. Instead we enable all events */
+ dwc3_otg_enable_events(dwc, DWC3_OTG_ALL_EVENTS);
+ /*
+ * OCTL.PeriMode = 1, OCTL.DevSetHNPEn = 0, OCTL.HstSetHNPEn = 0,
+ * OCTL.HNPReq = 0
+ */
+ reg = dwc3_readl(dwc->regs, DWC3_OCTL);
+ reg |= DWC3_OCTL_PERIMODE;
+ reg &= ~(DWC3_OCTL_DEVSETHNPEN | DWC3_OCTL_HSTSETHNPEN |
+ DWC3_OCTL_HNPREQ);
+ dwc3_writel(dwc->regs, DWC3_OCTL, reg);
+}
+
+static int dwc3_drd_start_host(struct usb_otg *otg, int on)
+{
+ struct dwc3 *dwc = dev_get_drvdata(otg->dev);
+ u32 reg;
+ unsigned long flags;
+
+ dwc3_trace(trace_dwc3_core, "%s: %d\n", __func__, on);
+
+ /* switch OTG core */
+ if (on) {
+ /* As per Figure 11-10 A-Device Flow Diagram */
+
+ spin_lock_irqsave(&dwc->lock, flags);
+ /* OCFG.HNPCap = 0, OCFG.SRPCap = 0 */
+ reg = dwc3_readl(dwc->regs, DWC3_OCFG);
+ reg &= ~(DWC3_OCFG_SRPCAP | DWC3_OCFG_HNPCAP);
+ dwc3_writel(dwc->regs, DWC3_OCFG, reg);
+
+ /*
+ * OCTL.PeriMode=0, OCTL.TermSelDLPulse = 0,
+ * OCTL.DevSetHNPEn = 0, OCTL.HstSetHNPEn = 0
+ */
+ reg = dwc3_readl(dwc->regs, DWC3_OCTL);
+ reg &= ~(DWC3_OCTL_PERIMODE | DWC3_OCTL_TERMSELIDPULSE |
+ DWC3_OCTL_DEVSETHNPEN | DWC3_OCTL_HSTSETHNPEN);
+ dwc3_writel(dwc->regs, DWC3_OCTL, reg);
+
+ /*
+ * OCFG.DisPrtPwrCutoff = 0/1
+ */
+ reg = dwc3_readl(dwc->regs, DWC3_OCFG);
+ reg &= ~DWC3_OCFG_DISPWRCUTTOFF;
+ dwc3_writel(dwc->regs, DWC3_OCFG, reg);
+
+ /* start the xHCI host driver */
+ spin_unlock_irqrestore(&dwc->lock, flags);
+ usb_otg_start_host(otg, true);
+ spin_lock_irqsave(&dwc->lock, flags);
+
+ /*
+ * OCFG.SRPCap = 1, OCFG.HNPCap = GHWPARAMS6.HNP_CAP
+ * We don't want SRP/HNP for simple dual-role so leave
+ * these disabled.
+ */
+
+ /*
+ * OEVTEN.OTGADevHostEvntEn = 1
+ * OEVTEN.OTGADevSessEndDetEvntEn = 1
+ * We don't want HNP/role-swap so leave these disabled.
+ */
+
+ /* GUSB2PHYCFG.ULPIAutoRes = 1/0, GUSB2PHYCFG.SusPHY = 1 */
+ if (!dwc->dis_u2_susphy_quirk) {
+ reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0));
+ reg |= DWC3_GUSB2PHYCFG_SUSPHY;
+ dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg);
+ }
+
+ /* Set Port Power to enable VBUS: OCTL.PrtPwrCtl = 1 */
+ reg = dwc3_readl(dwc->regs, DWC3_OCTL);
+ reg |= DWC3_OCTL_PRTPWRCTL;
+ dwc3_writel(dwc->regs, DWC3_OCTL, reg);
+ spin_unlock_irqrestore(&dwc->lock, flags);
+ } else {
+ /*
+ * Exit from A-device flow as per
+ * Figure 11-4 OTG Driver Overall Programming Flow
+ */
+ /* stop the HCD */
+ usb_otg_start_host(otg, false);
+
+ spin_lock_irqsave(&dwc->lock, flags);
+ /*
+ * OEVTEN.OTGADevBHostEndEvntEn=0, OEVTEN.OTGADevHNPChngEvntEn=0
+ * OEVTEN.OTGADevSessEndDetEvntEn=0,
+ * OEVTEN.OTGADevHostEvntEn = 0
+ * But we don't disable any OTG events
+ */
+
+ /* OCTL.HstSetHNPEn = 0, OCTL.PrtPwrCtl=0 */
+ reg = dwc3_readl(dwc->regs, DWC3_OCTL);
+ reg &= ~(DWC3_OCTL_HSTSETHNPEN | DWC3_OCTL_PRTPWRCTL);
+ dwc3_writel(dwc->regs, DWC3_OCTL, reg);
+
+ /* Initialize OTG registers */
+ dwc3_otgregs_init(dwc);
+ spin_unlock_irqrestore(&dwc->lock, flags);
+ }
+
+ return 0;
+}
+
+static int dwc3_drd_start_gadget(struct usb_otg *otg, int on)
+{
+ struct dwc3 *dwc = dev_get_drvdata(otg->dev);
+ u32 reg;
+ unsigned long flags;
+
+ dwc3_trace(trace_dwc3_core, "%s: %d\n", __func__, on);
+ if (on)
+ dwc3_event_buffers_setup(dwc);
+
+ if (on) {
+ /* As per Figure 11-20 B-Device Flow Diagram */
+
+ spin_lock_irqsave(&dwc->lock, flags);
+ /*
+ * OCFG.HNPCap = GHWPARAMS6.HNP_CAP, OCFG.SRPCap = 1
+ * but we set them to 0 for simple dual-role operation.
+ */
+ reg = dwc3_readl(dwc->regs, DWC3_OCFG);
+ reg &= ~(DWC3_OCFG_SRPCAP | DWC3_OCFG_HNPCAP);
+ /* OCFG.OTGSftRstMsk = 0/1 */
+ reg |= DWC3_OCFG_SFTRSTMASK;
+ dwc3_writel(dwc->regs, DWC3_OCFG, reg);
+ /*
+ * OCTL.PeriMode = 1
+ * OCTL.TermSelDLPulse = 0/1, OCTL.HNPReq = 0
+ * OCTL.DevSetHNPEn = 0, OCTL.HstSetHNPEn = 0
+ */
+ reg = dwc3_readl(dwc->regs, DWC3_OCTL);
+ reg |= DWC3_OCTL_PERIMODE;
+ reg &= ~(DWC3_OCTL_TERMSELIDPULSE | DWC3_OCTL_HNPREQ |
+ DWC3_OCTL_DEVSETHNPEN | DWC3_OCTL_HSTSETHNPEN);
+ dwc3_writel(dwc->regs, DWC3_OCTL, reg);
+ /* OEVTEN.OTGBDevSesVldDetEvntEn = 1 */
+ dwc3_otg_enable_events(dwc, DWC3_OEVT_BDEVSESSVLDDET);
+ /* GUSB2PHYCFG.ULPIAutoRes = 0, GUSB2PHYCFG0.SusPHY = 1 */
+ if (!dwc->dis_u2_susphy_quirk) {
+ reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0));
+ reg |= DWC3_GUSB2PHYCFG_SUSPHY;
+ dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg);
+ }
+ /* GCTL.GblHibernationEn = 0 */
+ reg = dwc3_readl(dwc->regs, DWC3_GCTL);
+ reg &= ~DWC3_GCTL_GBLHIBERNATIONEN;
+ dwc3_writel(dwc->regs, DWC3_GCTL, reg);
+
+ spin_unlock_irqrestore(&dwc->lock, flags);
+
+ /* start the Peripheral driver */
+ usb_otg_start_gadget(otg, true);
+ } else {
+ /*
+ * Exit from B-device flow as per
+ * Figure 11-4 OTG Driver Overall Programming Flow
+ */
+ /* stop the Peripheral driver */
+ usb_otg_start_gadget(otg, false);
+
+ spin_lock_irqsave(&dwc->lock, flags);
+
+ /*
+ * OEVTEN.OTGBDevHNPChngEvntEn = 0
+ * OEVTEN.OTGBDevVBusChngEvntEn = 0
+ * OEVTEN.OTGBDevBHostEndEvntEn = 0
+ */
+ reg = dwc3_readl(dwc->regs, DWC3_OEVTEN);
+ reg &= ~(DWC3_OEVT_BDEVHNPCHNG | DWC3_OEVT_BDEVVBUSCHNG |
+ DWC3_OEVT_BDEVBHOSTEND);
+ dwc3_writel(dwc->regs, DWC3_OEVTEN, reg);
+
+ /* OCTL.DevSetHNPEn = 0, OCTL.HNPReq = 0, OCTL.PeriMode=1 */
+ reg = dwc3_readl(dwc->regs, DWC3_OCTL);
+ reg &= ~(DWC3_OCTL_DEVSETHNPEN | DWC3_OCTL_HNPREQ);
+ reg |= DWC3_OCTL_PERIMODE;
+ dwc3_writel(dwc->regs, DWC3_OCTL, reg);
+
+ /* Initialize OTG registers */
+ dwc3_otgregs_init(dwc);
+ spin_unlock_irqrestore(&dwc->lock, flags);
+ }
+
+ return 0;
+}
+
+static struct otg_fsm_ops dwc3_drd_ops = {
+ .start_host = dwc3_drd_start_host,
+ .start_gadget = dwc3_drd_start_gadget,
+};
+
+static int dwc3_drd_register(struct dwc3 *dwc)
+{
+ int ret;
+
+ /* register parent as DRD device with OTG core */
+ dwc->otg = usb_otg_register(dwc->dev, &dwc->otg_config);
+ if (IS_ERR(dwc->otg)) {
+ ret = PTR_ERR(dwc->otg);
+ if (ret == -ENOTSUPP)
+ dev_err(dwc->dev, "CONFIG_USB_OTG needed for dual-role\n");
+ else
+ dev_err(dwc->dev, "Failed to register with OTG core\n");
+
+ return ret;
+ }
+
+ return 0;
+}
+
+static int dwc3_drd_init(struct dwc3 *dwc)
{
- struct device *dev = dwc->dev;
int ret;
+ struct usb_otg_caps *otgcaps = &dwc->otg_caps;
+ u32 reg;
+ unsigned long flags;
struct resource *res;
struct platform_device *dwc3_pdev = to_platform_device(dwc->dev);
+ dwc->otg_irq = platform_get_irq_byname(dwc3_pdev, "otg");
+ if (dwc->otg_irq <= 0) {
+ dwc->otg_irq = platform_get_irq_byname(dwc3_pdev,
+ "dwc_usb3");
+ if (dwc->otg_irq <= 0) {
+ res = platform_get_resource(dwc3_pdev,
+ IORESOURCE_IRQ, 0);
+ if (!res) {
+ dev_err(dwc->dev, "missing otg IRQ\n");
+ return -ENODEV;
+ }
+ dwc->otg_irq = res->start;
+ }
+ }
+
+ otgcaps->otg_rev = 0;
+ otgcaps->hnp_support = false;
+ otgcaps->srp_support = false;
+ otgcaps->adp_support = false;
+ dwc->otg_config.fsm_ops = &dwc3_drd_ops;
+ dwc->otg_config.otg_caps = otgcaps;
+
+ ret = dwc3_drd_register(dwc);
+ if (ret)
+ return ret;
+
+ /* disable all otg irqs */
+ dwc3_otg_disable_events(dwc, DWC3_OTG_ALL_EVENTS);
+ /* clear all events */
+ dwc3_writel(dwc->regs, DWC3_OEVT, ~0);
+
+ ret = request_threaded_irq(dwc->otg_irq, dwc3_otg_irq,
+ dwc3_otg_thread_irq,
+ IRQF_SHARED, "dwc3-otg", dwc);
+ if (ret) {
+ dev_err(dwc->dev, "failed to request irq #%d --> %d\n",
+ dwc->otg_irq, ret);
+ ret = -ENODEV;
+ goto error;
+ }
+
+ spin_lock_irqsave(&dwc->lock, flags);
+
+ /*
+ * As per Figure 11-4 OTG Driver Overall Programming Flow,
+ * block "Initialize GCTL for OTG operation".
+ */
+ /* GCTL.PrtCapDir=2'b11 */
+ dwc3_set_mode(dwc, DWC3_GCTL_PRTCAP_OTG);
+ /* GUSB2PHYCFG0.SusPHY=0 */
+ reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0));
+ reg &= ~DWC3_GUSB2PHYCFG_SUSPHY;
+ dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg);
+
+ /* Initialize OTG registers */
+ dwc3_otgregs_init(dwc);
+ spin_unlock_irqrestore(&dwc->lock, flags);
+
+ dwc3_otg_fsm_sync(dwc);
+
+ return 0;
+
+error:
+ usb_otg_unregister(dwc->dev);
+
+ return ret;
+}
+
+static void dwc3_drd_exit(struct dwc3 *dwc)
+{
+ usb_otg_unregister(dwc->dev);
+}
+
+/* -------------------------------------------------------------------------- */
+
+static int dwc3_core_init_mode(struct dwc3 *dwc)
+{
+ struct device *dev = dwc->dev;
+ int ret;
+
switch (dwc->dr_mode) {
case USB_DR_MODE_PERIPHERAL:
dwc3_set_mode(dwc, DWC3_GCTL_PRTCAP_DEVICE);
@@ -767,30 +1199,32 @@ static int dwc3_core_init_mode(struct dwc3 *dwc)
}
break;
case USB_DR_MODE_OTG:
- dwc->otg_irq = platform_get_irq_byname(dwc3_pdev, "otg");
- if (dwc->otg_irq <= 0) {
- dwc->otg_irq = platform_get_irq_byname(dwc3_pdev,
- "dwc_usb3");
- if (dwc->otg_irq <= 0) {
- res = platform_get_resource(dwc3_pdev,
- IORESOURCE_IRQ, 0);
- if (!res) {
- dev_err(dwc->dev, "missing otg IRQ\n");
- return -ENODEV;
- }
- dwc->otg_irq = res->start;
+ ret = dwc3_drd_init(dwc);
+ if (ret) {
+ dev_err(dev,
+ "limiting to peripheral only as dual-role init failed: %d",
+ ret);
+ dwc->dr_mode = USB_DR_MODE_PERIPHERAL;
+ dwc3_set_mode(dwc, DWC3_GCTL_PRTCAP_DEVICE);
+ ret = dwc3_gadget_init(dwc);
+ if (ret) {
+ dev_err(dev, "failed to initialize gadget\n");
+ return ret;
}
}
- dwc3_set_mode(dwc, DWC3_GCTL_PRTCAP_OTG);
+
ret = dwc3_host_init(dwc);
if (ret) {
dev_err(dev, "failed to initialize host\n");
+ dwc3_drd_exit(dwc);
return ret;
}
ret = dwc3_gadget_init(dwc);
if (ret) {
dev_err(dev, "failed to initialize gadget\n");
+ dwc3_host_exit(dwc);
+ dwc3_drd_exit(dwc);
return ret;
}
break;
@@ -814,6 +1248,7 @@ static void dwc3_core_exit_mode(struct dwc3 *dwc)
case USB_DR_MODE_OTG:
dwc3_host_exit(dwc);
dwc3_gadget_exit(dwc);
+ dwc3_drd_exit(dwc);
break;
default:
/* do nothing */
@@ -1142,6 +1577,30 @@ static int dwc3_remove(struct platform_device *pdev)
}
#ifdef CONFIG_PM_SLEEP
+static int dwc3_prepare(struct device *dev)
+{
+ struct dwc3 *dwc = dev_get_drvdata(dev);
+ unsigned long flags;
+
+ spin_lock_irqsave(&dwc->lock, flags);
+ dwc->otg_prevent_sync = true;
+ spin_unlock_irqrestore(&dwc->lock, flags);
+
+ return 0;
+}
+
+static void dwc3_complete(struct device *dev)
+{
+ struct dwc3 *dwc = dev_get_drvdata(dev);
+ unsigned long flags;
+
+ spin_lock_irqsave(&dwc->lock, flags);
+ dwc->otg_prevent_sync = false;
+ spin_unlock_irqrestore(&dwc->lock, flags);
+ if (dwc->dr_mode == USB_DR_MODE_OTG)
+ dwc3_otg_fsm_sync(dwc);
+}
+
static int dwc3_suspend(struct device *dev)
{
struct dwc3 *dwc = dev_get_drvdata(dev);
@@ -1149,18 +1608,40 @@ static int dwc3_suspend(struct device *dev)
spin_lock_irqsave(&dwc->lock, flags);
+ /* Save OTG state only if we're really using it */
+ if (dwc->current_mode == DWC3_GCTL_PRTCAP_OTG) {
+ dwc->ocfg = dwc3_readl(dwc->regs, DWC3_OCFG);
+ dwc->octl = dwc3_readl(dwc->regs, DWC3_OCTL);
+ dwc3_otg_mask_irq(dwc);
+ }
+
+ dwc->gctl = dwc3_readl(dwc->regs, DWC3_GCTL);
+
switch (dwc->dr_mode) {
case USB_DR_MODE_PERIPHERAL:
- case USB_DR_MODE_OTG:
dwc3_gadget_suspend(dwc);
- /* FALLTHROUGH */
+ break;
+ case USB_DR_MODE_OTG:
+ dwc->otg_protocol = dwc->otg->fsm.protocol;
+
+ switch (dwc->otg->fsm.protocol) {
+ case PROTO_GADGET:
+ dwc3_gadget_suspend(dwc);
+ break;
+ case PROTO_HOST:
+ case PROTO_UNDEF:
+ default:
+ /* nothing */
+ break;
+ }
case USB_DR_MODE_HOST:
+ case USB_DR_MODE_UNKNOWN:
default:
- dwc3_event_buffers_cleanup(dwc);
+ /* nothing */
break;
}
- dwc->gctl = dwc3_readl(dwc->regs, DWC3_GCTL);
+ dwc3_event_buffers_cleanup(dwc);
spin_unlock_irqrestore(&dwc->lock, flags);
usb_phy_shutdown(dwc->usb3_phy);
@@ -1198,15 +1679,34 @@ static int dwc3_resume(struct device *dev)
switch (dwc->dr_mode) {
case USB_DR_MODE_PERIPHERAL:
- case USB_DR_MODE_OTG:
dwc3_gadget_resume(dwc);
- /* FALLTHROUGH */
+ break;
+ case USB_DR_MODE_OTG:
+ switch (dwc->otg_protocol) {
+ case PROTO_GADGET:
+ dwc3_gadget_resume(dwc);
+ break;
+ case PROTO_HOST:
+ break;
+ case PROTO_UNDEF:
+ default:
+ /* nothing */
+ break;
+ }
case USB_DR_MODE_HOST:
+ case USB_DR_MODE_UNKNOWN:
default:
/* do nothing */
break;
}
+ /* Restore OTG state only if we're really using it */
+ if (dwc->current_mode == DWC3_GCTL_PRTCAP_OTG) {
+ dwc3_writel(dwc->regs, DWC3_OCFG, dwc->ocfg);
+ dwc3_writel(dwc->regs, DWC3_OCTL, dwc->octl);
+ dwc3_otg_unmask_irq(dwc);
+ }
+
spin_unlock_irqrestore(&dwc->lock, flags);
pm_runtime_disable(dev);
@@ -1223,6 +1723,8 @@ err_usb2phy_init:
static const struct dev_pm_ops dwc3_dev_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS(dwc3_suspend, dwc3_resume)
+ .prepare = dwc3_prepare,
+ .complete = dwc3_complete,
};
#define DWC3_PM_OPS &(dwc3_dev_pm_ops)
diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h
index 79422dd..c9f83c5 100644
--- a/drivers/usb/dwc3/core.h
+++ b/drivers/usb/dwc3/core.h
@@ -31,6 +31,7 @@
#include <linux/usb/gadget.h>
#include <linux/usb/otg.h>
#include <linux/ulpi/interface.h>
+#include <linux/usb/otg-fsm.h>
#include <linux/phy/phy.h>
@@ -754,6 +755,12 @@ struct dwc3_scratchpad_array {
* @maximum_speed: maximum speed requested (mainly for testing purposes)
* @revision: revision register contents
* @dr_mode: requested mode of operation
+ * @otg: usb otg data structure
+ * @otg_config: otg controller configuration
+ * @otg_prevent_sync: flag to block events to otg fsm
+ * @otg_protocol: saved copy of otg state during suspend
+ * @current_mode: current mode of operation written to PRTCAPDIR
+ * @oevt: cached OEVT register during OTG irq
* @gadget_irq: IRQ number for Peripheral IRQs
* @otg_irq: IRQ number for OTG IRQs
* @usb2_phy: pointer to USB2 PHY
@@ -763,6 +770,9 @@ struct dwc3_scratchpad_array {
* @ulpi: pointer to ulpi interface
* @dcfg: saved contents of DCFG register
* @gctl: saved contents of GCTL register
+ * @ocfg: saved contents of OCFG register
+ * @octl: saved contents of OCTL register
+ * @oevten: saved contents of OEVTEN register
* @isoch_delay: wValue from Set Isochronous Delay request;
* @u2sel: parameter from Set SEL request.
* @u2pel: parameter from Set SEL request.
@@ -858,6 +868,13 @@ struct dwc3 {
size_t regs_size;
enum usb_dr_mode dr_mode;
+ struct usb_otg *otg;
+ struct usb_otg_caps otg_caps;
+ struct usb_otg_config otg_config;
+ bool otg_prevent_sync;
+ int otg_protocol;
+ u32 current_mode;
+ u32 oevt;
int gadget_irq;
int otg_irq;
@@ -865,6 +882,9 @@ struct dwc3 {
/* used for suspend/resume */
u32 dcfg;
u32 gctl;
+ u32 ocfg;
+ u32 octl;
+ u32 oevten;
u32 nr_scratch;
u32 num_event_buffers;
diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c
index 90f514c..83d5c57 100644
--- a/drivers/usb/dwc3/gadget.c
+++ b/drivers/usb/dwc3/gadget.c
@@ -2894,7 +2894,11 @@ int dwc3_gadget_init(struct dwc3 *dwc)
if (ret)
goto err5;
- ret = usb_add_gadget_udc(dwc->dev, &dwc->gadget);
+ if (dwc->dr_mode == USB_DR_MODE_OTG)
+ ret = usb_otg_add_gadget_udc(dwc->dev, &dwc->gadget, dwc->dev);
+ else
+ ret = usb_add_gadget_udc(dwc->dev, &dwc->gadget);
+
if (ret) {
dev_err(dwc->dev, "failed to register udc\n");
goto err5;
diff --git a/drivers/usb/dwc3/host.c b/drivers/usb/dwc3/host.c
index f2b60a4..89339d1 100644
--- a/drivers/usb/dwc3/host.c
+++ b/drivers/usb/dwc3/host.c
@@ -69,6 +69,8 @@ int dwc3_host_init(struct dwc3 *dwc)
memset(&pdata, 0, sizeof(pdata));
pdata.usb3_lpm_capable = dwc->usb3_lpm_capable;
+ if (dwc->dr_mode == USB_DR_MODE_OTG)
+ pdata.otg_dev = dwc->dev;
ret = platform_device_add_data(xhci, &pdata, sizeof(pdata));
if (ret) {
--
2.5.0
^ permalink raw reply related [flat|nested] 30+ messages in thread* [PATCH v6 07/10] usb: dwc3: gadget: Fix suspend/resume during dual-role mode
2016-04-11 11:34 [PATCH v6 00/10] usb: dwc3: add dual-role support Roger Quadros
` (5 preceding siblings ...)
2016-04-11 11:35 ` [PATCH v6 06/10] usb: dwc3: add dual-role support Roger Quadros
@ 2016-04-11 11:35 ` Roger Quadros
[not found] ` <1460374506-9779-8-git-send-email-rogerq-l0cyMroinI0@public.gmane.org>
2016-04-11 11:35 ` [PATCH v6 08/10] usb: dwc3: core: fix PHY handling during suspend Roger Quadros
` (2 subsequent siblings)
9 siblings, 1 reply; 30+ messages in thread
From: Roger Quadros @ 2016-04-11 11:35 UTC (permalink / raw)
To: balbi
Cc: tony, Joao.Pinto, sergei.shtylyov, peter.chen, jun.li,
grygorii.strashko, yoshihiro.shimoda.uh, nsekhar, linux-usb,
linux-kernel, linux-omap, Roger Quadros
Gadget controller might not be always active during suspend/
resume when we are operating in dual-role/otg mode.
Check if we're active and only if we are then perform
necessary actions during suspend/resume.
Signed-off-by: Roger Quadros <rogerq@ti.com>
---
drivers/usb/dwc3/gadget.c | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c
index 83d5c57..1ca5ac0 100644
--- a/drivers/usb/dwc3/gadget.c
+++ b/drivers/usb/dwc3/gadget.c
@@ -2952,6 +2952,9 @@ void dwc3_gadget_exit(struct dwc3 *dwc)
int dwc3_gadget_suspend(struct dwc3 *dwc)
{
+ if (!dwc->gadget_driver)
+ return 0;
+
if (dwc->pullups_connected) {
dwc3_gadget_disable_irq(dwc);
dwc3_gadget_run_stop(dwc, true, true);
@@ -2970,6 +2973,9 @@ int dwc3_gadget_resume(struct dwc3 *dwc)
struct dwc3_ep *dep;
int ret;
+ if (!dwc->gadget_driver)
+ return 0;
+
/* Start with SuperSpeed Default */
dwc3_gadget_ep0_desc.wMaxPacketSize = cpu_to_le16(512);
--
2.5.0
^ permalink raw reply related [flat|nested] 30+ messages in thread* [PATCH v6 08/10] usb: dwc3: core: fix PHY handling during suspend
2016-04-11 11:34 [PATCH v6 00/10] usb: dwc3: add dual-role support Roger Quadros
` (6 preceding siblings ...)
2016-04-11 11:35 ` [PATCH v6 07/10] usb: dwc3: gadget: Fix suspend/resume during dual-role mode Roger Quadros
@ 2016-04-11 11:35 ` Roger Quadros
2016-04-11 12:24 ` Felipe Balbi
2016-04-11 11:35 ` [PATCH v6 09/10] ARM: dts: dra7*-evm: Enable dual-role for usb1 Roger Quadros
2016-04-11 11:35 ` [PATCH v6 10/10] ARM: dts: am43xx: Enable dual-role on USB1 Roger Quadros
9 siblings, 1 reply; 30+ messages in thread
From: Roger Quadros @ 2016-04-11 11:35 UTC (permalink / raw)
To: balbi
Cc: tony, Joao.Pinto, sergei.shtylyov, peter.chen, jun.li,
grygorii.strashko, yoshihiro.shimoda.uh, nsekhar, linux-usb,
linux-kernel, linux-omap, Roger Quadros
From: Felipe Balbi <balbi@kernel.org>
we need to power off the PHY during suspend and
power it back on during resume.
Signed-off-by: Felipe Balbi <balbi@kernel.org>
[nsekhar@ti.com: fix call to usb_phy_set_suspend() in dwc3_suspend()]
Signed-off-by: Sekhar Nori <nsekhar@ti.com>
Signed-off-by: Roger Quadros <rogerq@ti.com>
---
drivers/usb/dwc3/core.c | 23 ++++++++++++++++++++++-
1 file changed, 22 insertions(+), 1 deletion(-)
diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c
index f24c091..60665dd 100644
--- a/drivers/usb/dwc3/core.c
+++ b/drivers/usb/dwc3/core.c
@@ -1649,6 +1649,11 @@ static int dwc3_suspend(struct device *dev)
phy_exit(dwc->usb2_generic_phy);
phy_exit(dwc->usb3_generic_phy);
+ usb_phy_set_suspend(dwc->usb2_phy, 1);
+ usb_phy_set_suspend(dwc->usb3_phy, 1);
+ WARN_ON(phy_power_off(dwc->usb2_generic_phy) < 0);
+ WARN_ON(phy_power_off(dwc->usb3_generic_phy) < 0);
+
pinctrl_pm_select_sleep_state(dev);
return 0;
@@ -1662,11 +1667,21 @@ static int dwc3_resume(struct device *dev)
pinctrl_pm_select_default_state(dev);
+ usb_phy_set_suspend(dwc->usb2_phy, 0);
+ usb_phy_set_suspend(dwc->usb3_phy, 0);
+ ret = phy_power_on(dwc->usb2_generic_phy);
+ if (ret < 0)
+ return ret;
+
+ ret = phy_power_on(dwc->usb3_generic_phy);
+ if (ret < 0)
+ goto err_usb2phy_power;
+
usb_phy_init(dwc->usb3_phy);
usb_phy_init(dwc->usb2_phy);
ret = phy_init(dwc->usb2_generic_phy);
if (ret < 0)
- return ret;
+ goto err_usb3phy_power;
ret = phy_init(dwc->usb3_generic_phy);
if (ret < 0)
@@ -1718,6 +1733,12 @@ static int dwc3_resume(struct device *dev)
err_usb2phy_init:
phy_exit(dwc->usb2_generic_phy);
+err_usb3phy_power:
+ phy_power_off(dwc->usb3_generic_phy);
+
+err_usb2phy_power:
+ phy_power_off(dwc->usb2_generic_phy);
+
return ret;
}
--
2.5.0
^ permalink raw reply related [flat|nested] 30+ messages in thread* Re: [PATCH v6 08/10] usb: dwc3: core: fix PHY handling during suspend
2016-04-11 11:35 ` [PATCH v6 08/10] usb: dwc3: core: fix PHY handling during suspend Roger Quadros
@ 2016-04-11 12:24 ` Felipe Balbi
[not found] ` <8760vonxin.fsf-ral2JQCrhuEAvxtiuMwx3w@public.gmane.org>
0 siblings, 1 reply; 30+ messages in thread
From: Felipe Balbi @ 2016-04-11 12:24 UTC (permalink / raw)
Cc: tony, Joao.Pinto, sergei.shtylyov, peter.chen, jun.li,
grygorii.strashko, yoshihiro.shimoda.uh, nsekhar, linux-usb,
linux-kernel, linux-omap, Roger Quadros
[-- Attachment #1: Type: text/plain, Size: 512 bytes --]
Hi,
Roger Quadros <rogerq@ti.com> writes:
> From: Felipe Balbi <balbi@kernel.org>
>
> we need to power off the PHY during suspend and
> power it back on during resume.
>
> Signed-off-by: Felipe Balbi <balbi@kernel.org>
> [nsekhar@ti.com: fix call to usb_phy_set_suspend() in dwc3_suspend()]
> Signed-off-by: Sekhar Nori <nsekhar@ti.com>
> Signed-off-by: Roger Quadros <rogerq@ti.com>
is this also a fix which needs to be rebased on v4.6-rc3 and merged
during current -rc cycle ?
--
balbi
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 818 bytes --]
^ permalink raw reply [flat|nested] 30+ messages in thread
* [PATCH v6 09/10] ARM: dts: dra7*-evm: Enable dual-role for usb1
2016-04-11 11:34 [PATCH v6 00/10] usb: dwc3: add dual-role support Roger Quadros
` (7 preceding siblings ...)
2016-04-11 11:35 ` [PATCH v6 08/10] usb: dwc3: core: fix PHY handling during suspend Roger Quadros
@ 2016-04-11 11:35 ` Roger Quadros
2016-04-11 11:35 ` [PATCH v6 10/10] ARM: dts: am43xx: Enable dual-role on USB1 Roger Quadros
9 siblings, 0 replies; 30+ messages in thread
From: Roger Quadros @ 2016-04-11 11:35 UTC (permalink / raw)
To: balbi
Cc: tony, Joao.Pinto, sergei.shtylyov, peter.chen, jun.li,
grygorii.strashko, yoshihiro.shimoda.uh, nsekhar, linux-usb,
linux-kernel, linux-omap, Roger Quadros
Now that we have dual-role support working at USB core,
enable dual-role support for usb1 controller.
Signed-off-by: Roger Quadros <rogerq@ti.com>
---
arch/arm/boot/dts/dra7-evm.dts | 2 +-
arch/arm/boot/dts/dra72-evm.dts | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/arch/arm/boot/dts/dra7-evm.dts b/arch/arm/boot/dts/dra7-evm.dts
index d9b8723..61c88f7 100644
--- a/arch/arm/boot/dts/dra7-evm.dts
+++ b/arch/arm/boot/dts/dra7-evm.dts
@@ -722,7 +722,7 @@
};
&usb1 {
- dr_mode = "peripheral";
+ dr_mode = "otg";
pinctrl-names = "default";
pinctrl-0 = <&usb1_pins>;
};
diff --git a/arch/arm/boot/dts/dra72-evm.dts b/arch/arm/boot/dts/dra72-evm.dts
index 6affe2d..f7d345e 100644
--- a/arch/arm/boot/dts/dra72-evm.dts
+++ b/arch/arm/boot/dts/dra72-evm.dts
@@ -592,7 +592,7 @@
};
&usb1 {
- dr_mode = "peripheral";
+ dr_mode = "otg";
pinctrl-names = "default";
pinctrl-0 = <&usb1_pins>;
};
--
2.5.0
^ permalink raw reply related [flat|nested] 30+ messages in thread* [PATCH v6 10/10] ARM: dts: am43xx: Enable dual-role on USB1
2016-04-11 11:34 [PATCH v6 00/10] usb: dwc3: add dual-role support Roger Quadros
` (8 preceding siblings ...)
2016-04-11 11:35 ` [PATCH v6 09/10] ARM: dts: dra7*-evm: Enable dual-role for usb1 Roger Quadros
@ 2016-04-11 11:35 ` Roger Quadros
9 siblings, 0 replies; 30+ messages in thread
From: Roger Quadros @ 2016-04-11 11:35 UTC (permalink / raw)
To: balbi
Cc: tony, Joao.Pinto, sergei.shtylyov, peter.chen, jun.li,
grygorii.strashko, yoshihiro.shimoda.uh, nsekhar, linux-usb,
linux-kernel, linux-omap, Roger Quadros
USB1 port is micro-AB type and can function as peripheral
as well as host. Enable dual-role mode for USB1.
Signed-off-by: Roger Quadros <rogerq@ti.com>
---
arch/arm/boot/dts/am437x-gp-evm.dts | 2 +-
arch/arm/boot/dts/am437x-sk-evm.dts | 2 +-
arch/arm/boot/dts/am43x-epos-evm.dts | 2 +-
3 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/arch/arm/boot/dts/am437x-gp-evm.dts b/arch/arm/boot/dts/am437x-gp-evm.dts
index 8889be1..5834448 100644
--- a/arch/arm/boot/dts/am437x-gp-evm.dts
+++ b/arch/arm/boot/dts/am437x-gp-evm.dts
@@ -765,7 +765,7 @@
};
&usb1 {
- dr_mode = "peripheral";
+ dr_mode = "otg";
status = "okay";
};
diff --git a/arch/arm/boot/dts/am437x-sk-evm.dts b/arch/arm/boot/dts/am437x-sk-evm.dts
index d82dd6e..ad772da 100644
--- a/arch/arm/boot/dts/am437x-sk-evm.dts
+++ b/arch/arm/boot/dts/am437x-sk-evm.dts
@@ -571,7 +571,7 @@
};
&usb1 {
- dr_mode = "peripheral";
+ dr_mode = "otg";
status = "okay";
pinctrl-names = "default";
pinctrl-0 = <&usb1_pins>;
diff --git a/arch/arm/boot/dts/am43x-epos-evm.dts b/arch/arm/boot/dts/am43x-epos-evm.dts
index 83dfafa..b2b596b 100644
--- a/arch/arm/boot/dts/am43x-epos-evm.dts
+++ b/arch/arm/boot/dts/am43x-epos-evm.dts
@@ -675,7 +675,7 @@
};
&usb1 {
- dr_mode = "peripheral";
+ dr_mode = "otg";
status = "okay";
};
--
2.5.0
^ permalink raw reply related [flat|nested] 30+ messages in thread