* [PATCH v2 0/2] usb: dwc3: Add dual-role support using OTG core
@ 2018-02-27 11:29 Roger Quadros
0 siblings, 0 replies; 11+ messages in thread
From: Roger Quadros @ 2018-02-27 11:29 UTC (permalink / raw)
To: balbi; +Cc: linux-usb, linux-kernel, Roger Quadros
Hi Felipe,
Some platforms (e.g. TI's AM437x) don't have USB ID pin state available
over GPIO/extcon but need to rely on the DWC3 core's OTG block to
get the ID pin state instead.
This series implements simple dual-role functionality using DWC3's OTG block.
Debugfs 'mode' override is also functional so user can switch
between "otg", "host" or "device" modes for debug.
Although system suspend/resume isn't working yet in mainline for AM437x,
I've tested this series for system suspend/resume using a local tree.
Roger Quadros (2):
usb: dwc3: core.h: add some register definitions
usb: dwc3: add dual role support using OTG block
drivers/usb/dwc3/core.c | 67 ++++++-
drivers/usb/dwc3/core.h | 111 +++++++++++
drivers/usb/dwc3/drd.c | 489 ++++++++++++++++++++++++++++++++++++++++++++++--
3 files changed, 639 insertions(+), 28 deletions(-)
--
cheers,
-roger
Texas Instruments Finland Oy, Porkkalankatu 22, 00180 Helsinki. Y-tunnus/Business ID: 0615521-4. Kotipaikka/Domicile: Helsinki
^ permalink raw reply [flat|nested] 11+ messages in thread
* [v2,1/2] usb: dwc3: core.h: add some register definitions
2018-02-27 11:29 [PATCH v2 0/2] usb: dwc3: Add dual-role support using OTG core Roger Quadros
@ 2018-02-27 11:29 ` Roger Quadros
-1 siblings, 0 replies; 11+ messages in thread
From: Roger Quadros @ 2018-02-27 11:29 UTC (permalink / raw)
To: balbi; +Cc: linux-usb, linux-kernel, Roger Quadros
Add OTG and GHWPARAMS6 register definitions
Signed-off-by: Roger Quadros <rogerq@ti.com>
---
drivers/usb/dwc3/core.h | 82 +++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 82 insertions(+)
diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h
index 860d2bc..0d4c698 100644
--- a/drivers/usb/dwc3/core.h
+++ b/drivers/usb/dwc3/core.h
@@ -201,6 +201,15 @@
#define DWC3_GUCTL1_TX_IPGAP_LINECHECK_DIS BIT(28)
#define DWC3_GUCTL1_DEV_L1_EXIT_BY_HW BIT(24)
+/* 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 BIT(31)
#define DWC3_GUSB2PHYCFG_U2_FREECLK_EXISTS BIT(30)
@@ -286,6 +295,11 @@
#define DWC3_MAX_HIBER_SCRATCHBUFS 15
/* Global HWPARAMS6 Register */
+#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 HWPARAMS7 Register */
@@ -467,6 +481,74 @@
#define DWC3_DEV_IMOD_INTERVAL_SHIFT 0
#define DWC3_DEV_IMOD_INTERVAL_MASK (0xffff << 0)
+/* 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_BDEVBHOSTENDEN BIT(11)
+#define DWC3_OEVTEN_BDEVHNPCHNGEN BIT(10)
+#define DWC3_OEVTEN_BDEVSESSVLDDETEN BIT(9)
+#define DWC3_OEVTEN_BDEVVBUSCHNGEN 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;
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [PATCH v2 1/2] usb: dwc3: core.h: add some register definitions
@ 2018-02-27 11:29 ` Roger Quadros
0 siblings, 0 replies; 11+ messages in thread
From: Roger Quadros @ 2018-02-27 11:29 UTC (permalink / raw)
To: balbi; +Cc: linux-usb, linux-kernel, Roger Quadros
Add OTG and GHWPARAMS6 register definitions
Signed-off-by: Roger Quadros <rogerq@ti.com>
---
drivers/usb/dwc3/core.h | 82 +++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 82 insertions(+)
diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h
index 860d2bc..0d4c698 100644
--- a/drivers/usb/dwc3/core.h
+++ b/drivers/usb/dwc3/core.h
@@ -201,6 +201,15 @@
#define DWC3_GUCTL1_TX_IPGAP_LINECHECK_DIS BIT(28)
#define DWC3_GUCTL1_DEV_L1_EXIT_BY_HW BIT(24)
+/* 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 BIT(31)
#define DWC3_GUSB2PHYCFG_U2_FREECLK_EXISTS BIT(30)
@@ -286,6 +295,11 @@
#define DWC3_MAX_HIBER_SCRATCHBUFS 15
/* Global HWPARAMS6 Register */
+#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 HWPARAMS7 Register */
@@ -467,6 +481,74 @@
#define DWC3_DEV_IMOD_INTERVAL_SHIFT 0
#define DWC3_DEV_IMOD_INTERVAL_MASK (0xffff << 0)
+/* 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_BDEVBHOSTENDEN BIT(11)
+#define DWC3_OEVTEN_BDEVHNPCHNGEN BIT(10)
+#define DWC3_OEVTEN_BDEVSESSVLDDETEN BIT(9)
+#define DWC3_OEVTEN_BDEVVBUSCHNGEN 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;
--
cheers,
-roger
Texas Instruments Finland Oy, Porkkalankatu 22, 00180 Helsinki. Y-tunnus/Business ID: 0615521-4. Kotipaikka/Domicile: Helsinki
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [v2,2/2] usb: dwc3: add dual role support using OTG block
2018-02-27 11:29 [PATCH v2 0/2] usb: dwc3: Add dual-role support using OTG core Roger Quadros
@ 2018-02-27 11:30 ` Roger Quadros
-1 siblings, 0 replies; 11+ messages in thread
From: Roger Quadros @ 2018-02-27 11:30 UTC (permalink / raw)
To: balbi; +Cc: linux-usb, linux-kernel, Roger Quadros
This is useful on platforms (e.g. TI AM437x) that don't
have ID available on a GPIO but do have the OTG block.
We can obtain the ID state via the OTG block and use it
for dual-role switching.
Signed-off-by: Roger Quadros <rogerq@ti.com>
---
drivers/usb/dwc3/core.c | 67 ++++++-
drivers/usb/dwc3/core.h | 29 +++
drivers/usb/dwc3/drd.c | 489 ++++++++++++++++++++++++++++++++++++++++++++++--
3 files changed, 557 insertions(+), 28 deletions(-)
diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c
index df4569d..397a4d4 100644
--- a/drivers/usb/dwc3/core.c
+++ b/drivers/usb/dwc3/core.c
@@ -89,10 +89,7 @@ static int dwc3_get_dr_mode(struct dwc3 *dwc)
return 0;
}
-static void dwc3_event_buffers_cleanup(struct dwc3 *dwc);
-static int dwc3_event_buffers_setup(struct dwc3 *dwc);
-
-static void dwc3_set_prtcap(struct dwc3 *dwc, u32 mode)
+void dwc3_set_prtcap(struct dwc3 *dwc, u32 mode)
{
u32 reg;
@@ -110,16 +107,19 @@ static void __dwc3_set_mode(struct work_struct *work)
unsigned long flags;
int ret;
- if (!dwc->desired_dr_role)
+ if (dwc->dr_mode != USB_DR_MODE_OTG)
return;
- if (dwc->desired_dr_role == dwc->current_dr_role)
+ if (dwc->current_dr_role == DWC3_GCTL_PRTCAP_OTG)
+ dwc3_otg_update(dwc, 0);
+
+ if (!dwc->desired_dr_role)
return;
- if (dwc->dr_mode != USB_DR_MODE_OTG)
+ if (dwc->desired_dr_role == dwc->current_dr_role)
return;
- if (dwc->desired_dr_role == DWC3_GCTL_PRTCAP_OTG)
+ if (dwc->desired_dr_role == DWC3_GCTL_PRTCAP_OTG && dwc->edev)
return;
switch (dwc->current_dr_role) {
@@ -130,6 +130,13 @@ static void __dwc3_set_mode(struct work_struct *work)
dwc3_gadget_exit(dwc);
dwc3_event_buffers_cleanup(dwc);
break;
+ case DWC3_GCTL_PRTCAP_OTG:
+ dwc3_otg_exit(dwc);
+ spin_lock_irqsave(&dwc->lock, flags);
+ dwc->desired_otg_role = DWC3_OTG_ROLE_IDLE;
+ spin_unlock_irqrestore(&dwc->lock, flags);
+ dwc3_otg_update(dwc, 1);
+ break;
default:
break;
}
@@ -165,9 +172,14 @@ static void __dwc3_set_mode(struct work_struct *work)
if (ret)
dev_err(dwc->dev, "failed to initialize peripheral\n");
break;
+ case DWC3_GCTL_PRTCAP_OTG:
+ dwc3_otg_init(dwc);
+ dwc3_otg_update(dwc, 0);
+ break;
default:
break;
}
+
}
void dwc3_set_mode(struct dwc3 *dwc, u32 mode)
@@ -351,7 +363,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;
@@ -368,7 +380,7 @@ static int dwc3_event_buffers_setup(struct dwc3 *dwc)
return 0;
}
-static void dwc3_event_buffers_cleanup(struct dwc3 *dwc)
+void dwc3_event_buffers_cleanup(struct dwc3 *dwc)
{
struct dwc3_event_buffer *evt;
@@ -1329,6 +1341,20 @@ static int dwc3_suspend_common(struct dwc3 *dwc, pm_message_t msg)
if (!PMSG_IS_AUTO(msg))
dwc3_core_exit(dwc);
break;
+ case DWC3_GCTL_PRTCAP_OTG:
+ /* do nothing during runtime_suspend */
+ if (PMSG_IS_AUTO(msg))
+ break;
+
+ if (dwc->current_otg_role == DWC3_OTG_ROLE_DEVICE) {
+ spin_lock_irqsave(&dwc->lock, flags);
+ dwc3_gadget_suspend(dwc);
+ spin_unlock_irqrestore(&dwc->lock, flags);
+ }
+
+ dwc3_otg_exit(dwc);
+ dwc3_core_exit(dwc);
+ break;
default:
/* do nothing */
break;
@@ -1360,6 +1386,27 @@ static int dwc3_resume_common(struct dwc3 *dwc, pm_message_t msg)
return ret;
}
break;
+ case DWC3_GCTL_PRTCAP_OTG:
+ /* nothing to do on runtime_resume */
+ if (PMSG_IS_AUTO(msg))
+ break;
+
+ ret = dwc3_core_init(dwc);
+ if (ret)
+ return ret;
+
+ dwc3_set_prtcap(dwc, dwc->current_dr_role);
+
+ dwc3_otg_init(dwc);
+ if (dwc->current_otg_role == DWC3_OTG_ROLE_HOST) {
+ dwc3_otg_host_init(dwc);
+ } else if (dwc->current_otg_role == DWC3_OTG_ROLE_DEVICE) {
+ spin_lock_irqsave(&dwc->lock, flags);
+ dwc3_gadget_resume(dwc);
+ spin_unlock_irqrestore(&dwc->lock, flags);
+ }
+
+ break;
default:
/* do nothing */
break;
diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h
index 0d4c698..09243a6 100644
--- a/drivers/usb/dwc3/core.h
+++ b/drivers/usb/dwc3/core.h
@@ -58,6 +58,11 @@
#define DWC3_DEVICE_EVENT_CMD_CMPL 10
#define DWC3_DEVICE_EVENT_OVERFLOW 11
+/* Controller's role while using the OTG block */
+#define DWC3_OTG_ROLE_IDLE 0
+#define DWC3_OTG_ROLE_HOST 1
+#define DWC3_OTG_ROLE_DEVICE 2
+
#define DWC3_GEVNTCOUNT_MASK 0xfffc
#define DWC3_GEVNTCOUNT_EHB BIT(31)
#define DWC3_GSNPSID_MASK 0xffff0000
@@ -863,6 +868,10 @@ struct dwc3_scratchpad_array {
* @regs_size: address space size
* @fladj: frame length adjustment
* @irq_gadget: peripheral controller's IRQ number
+ * @otg_irq: IRQ number for OTG IRQs
+ * @current_otg_role: current role of operation while using the OTG block
+ * @desired_otg_role: desired role of operation while using the OTG block
+ * @otg_restart_host: flag that OTG controller needs to restart host
* @nr_scratch: number of scratch buffers
* @u1u2: only used on revisions <1.83a for workaround
* @maximum_speed: maximum speed requested (mainly for testing purposes)
@@ -996,6 +1005,10 @@ struct dwc3 {
u32 fladj;
u32 irq_gadget;
+ u32 otg_irq;
+ u32 current_otg_role;
+ u32 desired_otg_role;
+ bool otg_restart_host;
u32 nr_scratch;
u32 u1u2;
u32 maximum_speed;
@@ -1257,6 +1270,7 @@ struct dwc3_gadget_ep_cmd_params {
#define DWC3_HAS_OTG BIT(3)
/* prototypes */
+void dwc3_set_prtcap(struct dwc3 *dwc, u32 mode);
void dwc3_set_mode(struct dwc3 *dwc, u32 mode);
u32 dwc3_core_fifo_space(struct dwc3_ep *dep, u8 type);
@@ -1274,6 +1288,9 @@ static inline bool dwc3_is_usb31(struct dwc3 *dwc)
bool dwc3_has_imod(struct dwc3 *dwc);
+int dwc3_event_buffers_setup(struct dwc3 *dwc);
+void dwc3_event_buffers_cleanup(struct dwc3 *dwc);
+
#if IS_ENABLED(CONFIG_USB_DWC3_HOST) || IS_ENABLED(CONFIG_USB_DWC3_DUAL_ROLE)
int dwc3_host_init(struct dwc3 *dwc);
void dwc3_host_exit(struct dwc3 *dwc);
@@ -1317,11 +1334,23 @@ static inline int dwc3_send_gadget_generic_command(struct dwc3 *dwc,
#if IS_ENABLED(CONFIG_USB_DWC3_DUAL_ROLE)
int dwc3_drd_init(struct dwc3 *dwc);
void dwc3_drd_exit(struct dwc3 *dwc);
+void dwc3_otg_init(struct dwc3 *dwc);
+void dwc3_otg_exit(struct dwc3 *dwc);
+void dwc3_otg_update(struct dwc3 *dwc, bool ignore_idstatus);
+void dwc3_otg_host_init(struct dwc3 *dwc);
#else
static inline int dwc3_drd_init(struct dwc3 *dwc)
{ return 0; }
static inline void dwc3_drd_exit(struct dwc3 *dwc)
{ }
+static inline void dwc3_otg_init(struct dwc3 *dwc)
+{ }
+static inline void dwc3_otg_exit(struct dwc3 *dwc)
+{ }
+static inline void dwc3_otg_update(struct dwc3 *dwc, bool ignore_idstatus)
+{ }
+static inline void dwc3_otg_host_init(struct dwc3 *dwc)
+{ }
#endif
/* power management interface */
diff --git a/drivers/usb/dwc3/drd.c b/drivers/usb/dwc3/drd.c
index cc8ab9a..1d8c557 100644
--- a/drivers/usb/dwc3/drd.c
+++ b/drivers/usb/dwc3/drd.c
@@ -8,22 +8,423 @@
*/
#include <linux/extcon.h>
+#include <linux/platform_device.h>
#include "debug.h"
#include "core.h"
#include "gadget.h"
-static void dwc3_drd_update(struct dwc3 *dwc)
+static void dwc3_otg_disable_events(struct dwc3 *dwc, u32 disable_mask)
+{
+ u32 reg = dwc3_readl(dwc->regs, DWC3_OEVTEN);
+
+ reg &= ~(disable_mask);
+ dwc3_writel(dwc->regs, DWC3_OEVTEN, reg);
+}
+
+static void dwc3_otg_enable_events(struct dwc3 *dwc, u32 enable_mask)
+{
+ u32 reg = dwc3_readl(dwc->regs, DWC3_OEVTEN);
+
+ reg |= (enable_mask);
+ dwc3_writel(dwc->regs, DWC3_OEVTEN, reg);
+}
+
+static void dwc3_otg_clear_events(struct dwc3 *dwc)
+{
+ u32 reg = dwc3_readl(dwc->regs, DWC3_OEVT);
+
+ dwc3_writel(dwc->regs, DWC3_OEVTEN, reg);
+}
+
+#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_BDEVBHOSTENDEN | \
+ DWC3_OEVTEN_BDEVHNPCHNGEN | DWC3_OEVTEN_BDEVSESSVLDDETEN | \
+ DWC3_OEVTEN_BDEVVBUSCHNGEN)
+
+static irqreturn_t dwc3_otg_thread_irq(int irq, void *_dwc)
{
+ struct dwc3 *dwc = _dwc;
+
+ spin_lock(&dwc->lock);
+ if (dwc->otg_restart_host) {
+ dwc3_otg_host_init(dwc);
+ dwc->otg_restart_host = 0;
+ }
+
+ spin_unlock(&dwc->lock);
+
+ dwc3_set_mode(dwc, DWC3_GCTL_PRTCAP_OTG);
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t dwc3_otg_irq(int irq, void *_dwc)
+{
+ u32 reg;
+ struct dwc3 *dwc = _dwc;
+ irqreturn_t ret = IRQ_NONE;
+
+ reg = dwc3_readl(dwc->regs, DWC3_OEVT);
+ if (reg) {
+ /* ignore non OTG events, we can't disable them in OEVTEN */
+ if (!(reg & DWC3_OTG_ALL_EVENTS)) {
+ dwc3_writel(dwc->regs, DWC3_OEVT, reg);
+ return IRQ_NONE;
+ }
+
+ if (dwc->current_otg_role == DWC3_OTG_ROLE_HOST &&
+ !(reg & DWC3_OEVT_DEVICEMODE))
+ dwc->otg_restart_host = 1;
+ dwc3_writel(dwc->regs, DWC3_OEVT, reg);
+ ret = IRQ_WAKE_THREAD;
+ }
+
+ return ret;
+}
+
+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_otg_clear_events(dwc);
+ /* 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_otg_get_irq(struct dwc3 *dwc)
+{
+ struct platform_device *dwc3_pdev = to_platform_device(dwc->dev);
+ int irq;
+
+ irq = platform_get_irq_byname(dwc3_pdev, "otg");
+ if (irq > 0)
+ goto out;
+
+ if (irq == -EPROBE_DEFER)
+ goto out;
+
+ irq = platform_get_irq_byname(dwc3_pdev, "dwc_usb3");
+ if (irq > 0)
+ goto out;
+
+ if (irq == -EPROBE_DEFER)
+ goto out;
+
+ irq = platform_get_irq(dwc3_pdev, 0);
+ if (irq > 0)
+ goto out;
+
+ if (irq != -EPROBE_DEFER)
+ dev_err(dwc->dev, "missing OTG IRQ\n");
+
+ if (!irq)
+ irq = -EINVAL;
+
+out:
+ return irq;
+}
+
+void dwc3_otg_init(struct dwc3 *dwc)
+{
+ u32 reg;
+
+ /*
+ * As per Figure 11-4 OTG Driver Overall Programming Flow,
+ * block "Initialize GCTL for OTG operation".
+ */
+ /* GCTL.PrtCapDir=2'b11 */
+ dwc3_set_prtcap(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);
+}
+
+void dwc3_otg_exit(struct dwc3 *dwc)
+{
+ /* disable all OTG IRQs */
+ dwc3_otg_disable_events(dwc, DWC3_OTG_ALL_EVENTS);
+ /* clear all events */
+ dwc3_otg_clear_events(dwc);
+}
+
+/* should be called before Host controller driver is started */
+void dwc3_otg_host_init(struct dwc3 *dwc)
+{
+ u32 reg;
+
+ /* As per Figure 11-10 A-Device Flow Diagram */
+ /* OCFG.HNPCap = 0, OCFG.SRPCap = 0. Already 0 */
+
+ /*
+ * 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);
+
+ /*
+ * 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);
+}
+
+/* should be called after Host controller driver is stopped */
+static void dwc3_otg_host_exit(struct dwc3 *dwc)
+{
+ u32 reg;
+
+ /*
+ * Exit from A-device flow as per
+ * Figure 11-4 OTG Driver Overall Programming Flow
+ */
+
+ /*
+ * 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);
+}
+
+/* should be called before the gadget controller driver is started */
+static void dwc3_otg_device_init(struct dwc3 *dwc)
+{
+ u32 reg;
+
+ /* As per Figure 11-20 B-Device Flow Diagram */
+
+ /*
+ * OCFG.HNPCap = GHWPARAMS6.HNP_CAP, OCFG.SRPCap = 1
+ * but we keep them 0 for simple dual-role operation.
+ */
+ reg = dwc3_readl(dwc->regs, DWC3_OCFG);
+ /* 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_OEVTEN_BDEVSESSVLDDETEN);
+ /* 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. Already 0. */
+}
+
+/* should be called after the gadget controller driver is stopped */
+static void dwc3_otg_device_exit(struct dwc3 *dwc)
+{
+ u32 reg;
+
+ /*
+ * Exit from B-device flow as per
+ * Figure 11-4 OTG Driver Overall Programming Flow
+ */
+
+ /*
+ * OEVTEN.OTGBDevHNPChngEvntEn = 0
+ * OEVTEN.OTGBDevVBusChngEvntEn = 0
+ * OEVTEN.OTGBDevBHostEndEvntEn = 0
+ */
+ dwc3_otg_disable_events(dwc, DWC3_OEVTEN_BDEVHNPCHNGEN |
+ DWC3_OEVTEN_BDEVVBUSCHNGEN |
+ DWC3_OEVTEN_BDEVBHOSTENDEN);
+
+ /* 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);
+}
+
+void dwc3_otg_update(struct dwc3 *dwc, bool ignore_idstatus)
+{
+ int ret;
+ u32 reg;
int id;
+ unsigned long flags;
- id = extcon_get_state(dwc->edev, EXTCON_USB_HOST);
- if (id < 0)
- id = 0;
+ if (dwc->dr_mode != USB_DR_MODE_OTG)
+ return;
- dwc3_set_mode(dwc, id ?
- DWC3_GCTL_PRTCAP_HOST :
- DWC3_GCTL_PRTCAP_DEVICE);
+ /* don't do anything if debug user changed role to not OTG */
+ if (dwc->current_dr_role != DWC3_GCTL_PRTCAP_OTG)
+ return;
+
+ if (!ignore_idstatus) {
+ reg = dwc3_readl(dwc->regs, DWC3_OSTS);
+ id = !!(reg & DWC3_OSTS_CONIDSTS);
+
+ dwc->desired_otg_role = id ? DWC3_OTG_ROLE_DEVICE :
+ DWC3_OTG_ROLE_HOST;
+ }
+
+ if (dwc->desired_otg_role == dwc->current_otg_role)
+ return;
+
+ switch (dwc->current_otg_role) {
+ case DWC3_OTG_ROLE_HOST:
+ dwc3_host_exit(dwc);
+ spin_lock_irqsave(&dwc->lock, flags);
+ dwc3_otg_host_exit(dwc);
+ spin_unlock_irqrestore(&dwc->lock, flags);
+ break;
+ case DWC3_OTG_ROLE_DEVICE:
+ dwc3_gadget_exit(dwc);
+ spin_lock_irqsave(&dwc->lock, flags);
+ dwc3_event_buffers_cleanup(dwc);
+ dwc3_otg_device_exit(dwc);
+ spin_unlock_irqrestore(&dwc->lock, flags);
+ break;
+ default:
+ break;
+ }
+
+ spin_lock_irqsave(&dwc->lock, flags);
+
+ dwc->current_otg_role = dwc->desired_otg_role;
+
+ spin_unlock_irqrestore(&dwc->lock, flags);
+
+ switch (dwc->desired_otg_role) {
+ case DWC3_OTG_ROLE_HOST:
+ spin_lock_irqsave(&dwc->lock, flags);
+ dwc3_otgregs_init(dwc);
+ dwc3_otg_host_init(dwc);
+ spin_unlock_irqrestore(&dwc->lock, flags);
+ ret = dwc3_host_init(dwc);
+ if (ret) {
+ dev_err(dwc->dev, "failed to initialize host\n");
+ } else {
+ if (dwc->usb2_phy)
+ otg_set_vbus(dwc->usb2_phy->otg, true);
+ if (dwc->usb2_generic_phy)
+ phy_set_mode(dwc->usb2_generic_phy,
+ PHY_MODE_USB_HOST);
+ }
+ break;
+ case DWC3_OTG_ROLE_DEVICE:
+ spin_lock_irqsave(&dwc->lock, flags);
+ dwc3_otgregs_init(dwc);
+ dwc3_otg_device_init(dwc);
+ dwc3_event_buffers_setup(dwc);
+ spin_unlock_irqrestore(&dwc->lock, flags);
+
+ if (dwc->usb2_phy)
+ otg_set_vbus(dwc->usb2_phy->otg, false);
+ if (dwc->usb2_generic_phy)
+ phy_set_mode(dwc->usb2_generic_phy,
+ PHY_MODE_USB_DEVICE);
+ ret = dwc3_gadget_init(dwc);
+ if (ret)
+ dev_err(dwc->dev, "failed to initialize peripheral\n");
+ break;
+ default:
+ break;
+ }
+}
+
+static void dwc3_drd_update(struct dwc3 *dwc)
+{
+ int id;
+
+ if (dwc->edev) {
+ id = extcon_get_state(dwc->edev, EXTCON_USB_HOST);
+ if (id < 0)
+ id = 0;
+ dwc3_set_mode(dwc, id ?
+ DWC3_GCTL_PRTCAP_HOST :
+ DWC3_GCTL_PRTCAP_DEVICE);
+ }
}
static int dwc3_drd_notifier(struct notifier_block *nb,
@@ -40,11 +441,11 @@ static int dwc3_drd_notifier(struct notifier_block *nb,
int dwc3_drd_init(struct dwc3 *dwc)
{
- int ret;
+ int ret, irq;
- if (dwc->dev->of_node) {
- if (of_property_read_bool(dwc->dev->of_node, "extcon"))
- dwc->edev = extcon_get_edev_by_phandle(dwc->dev, 0);
+ if (dwc->dev->of_node &&
+ of_property_read_bool(dwc->dev->of_node, "extcon")) {
+ dwc->edev = extcon_get_edev_by_phandle(dwc->dev, 0);
if (IS_ERR(dwc->edev))
return PTR_ERR(dwc->edev);
@@ -56,19 +457,71 @@ int dwc3_drd_init(struct dwc3 *dwc)
dev_err(dwc->dev, "couldn't register cable notifier\n");
return ret;
}
- }
- dwc3_drd_update(dwc);
+ dwc3_drd_update(dwc);
+ } else {
+ dwc3_set_prtcap(dwc, DWC3_GCTL_PRTCAP_OTG);
+ dwc->current_dr_role = DWC3_GCTL_PRTCAP_OTG;
+
+ /* use OTG block to get ID event */
+ irq = dwc3_otg_get_irq(dwc);
+ if (irq < 0)
+ return irq;
+
+ dwc->otg_irq = irq;
+
+ /* disable all OTG IRQs */
+ dwc3_otg_disable_events(dwc, DWC3_OTG_ALL_EVENTS);
+ /* clear all events */
+ dwc3_otg_clear_events(dwc);
+
+ 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;
+ return ret;
+ }
+
+ dwc3_otg_init(dwc);
+ dwc3_set_mode(dwc, DWC3_GCTL_PRTCAP_OTG);
+ }
return 0;
}
void dwc3_drd_exit(struct dwc3 *dwc)
{
- extcon_unregister_notifier(dwc->edev, EXTCON_USB_HOST,
- &dwc->edev_nb);
+ unsigned long flags;
+
+ if (dwc->edev)
+ extcon_unregister_notifier(dwc->edev, EXTCON_USB_HOST,
+ &dwc->edev_nb);
+
+ cancel_work_sync(&dwc->drd_work);
+
+ /* debug user might have changed role, clean based on current role */
+ switch (dwc->current_dr_role) {
+ case DWC3_GCTL_PRTCAP_HOST:
+ dwc3_host_exit(dwc);
+ break;
+ case DWC3_GCTL_PRTCAP_DEVICE:
+ dwc3_gadget_exit(dwc);
+ dwc3_event_buffers_cleanup(dwc);
+ break;
+ case DWC3_GCTL_PRTCAP_OTG:
+ dwc3_otg_exit(dwc);
+ spin_lock_irqsave(&dwc->lock, flags);
+ dwc->desired_otg_role = DWC3_OTG_ROLE_IDLE;
+ spin_unlock_irqrestore(&dwc->lock, flags);
+ dwc3_otg_update(dwc, 1);
+ break;
+ default:
+ break;
+ }
- dwc3_set_mode(dwc, DWC3_GCTL_PRTCAP_DEVICE);
- flush_work(&dwc->drd_work);
- dwc3_gadget_exit(dwc);
+ if (!dwc->edev)
+ free_irq(dwc->otg_irq, dwc);
}
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [PATCH v2 2/2] usb: dwc3: add dual role support using OTG block
@ 2018-02-27 11:30 ` Roger Quadros
0 siblings, 0 replies; 11+ messages in thread
From: Roger Quadros @ 2018-02-27 11:30 UTC (permalink / raw)
To: balbi; +Cc: linux-usb, linux-kernel, Roger Quadros
This is useful on platforms (e.g. TI AM437x) that don't
have ID available on a GPIO but do have the OTG block.
We can obtain the ID state via the OTG block and use it
for dual-role switching.
Signed-off-by: Roger Quadros <rogerq@ti.com>
---
drivers/usb/dwc3/core.c | 67 ++++++-
drivers/usb/dwc3/core.h | 29 +++
drivers/usb/dwc3/drd.c | 489 ++++++++++++++++++++++++++++++++++++++++++++++--
3 files changed, 557 insertions(+), 28 deletions(-)
diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c
index df4569d..397a4d4 100644
--- a/drivers/usb/dwc3/core.c
+++ b/drivers/usb/dwc3/core.c
@@ -89,10 +89,7 @@ static int dwc3_get_dr_mode(struct dwc3 *dwc)
return 0;
}
-static void dwc3_event_buffers_cleanup(struct dwc3 *dwc);
-static int dwc3_event_buffers_setup(struct dwc3 *dwc);
-
-static void dwc3_set_prtcap(struct dwc3 *dwc, u32 mode)
+void dwc3_set_prtcap(struct dwc3 *dwc, u32 mode)
{
u32 reg;
@@ -110,16 +107,19 @@ static void __dwc3_set_mode(struct work_struct *work)
unsigned long flags;
int ret;
- if (!dwc->desired_dr_role)
+ if (dwc->dr_mode != USB_DR_MODE_OTG)
return;
- if (dwc->desired_dr_role == dwc->current_dr_role)
+ if (dwc->current_dr_role == DWC3_GCTL_PRTCAP_OTG)
+ dwc3_otg_update(dwc, 0);
+
+ if (!dwc->desired_dr_role)
return;
- if (dwc->dr_mode != USB_DR_MODE_OTG)
+ if (dwc->desired_dr_role == dwc->current_dr_role)
return;
- if (dwc->desired_dr_role == DWC3_GCTL_PRTCAP_OTG)
+ if (dwc->desired_dr_role == DWC3_GCTL_PRTCAP_OTG && dwc->edev)
return;
switch (dwc->current_dr_role) {
@@ -130,6 +130,13 @@ static void __dwc3_set_mode(struct work_struct *work)
dwc3_gadget_exit(dwc);
dwc3_event_buffers_cleanup(dwc);
break;
+ case DWC3_GCTL_PRTCAP_OTG:
+ dwc3_otg_exit(dwc);
+ spin_lock_irqsave(&dwc->lock, flags);
+ dwc->desired_otg_role = DWC3_OTG_ROLE_IDLE;
+ spin_unlock_irqrestore(&dwc->lock, flags);
+ dwc3_otg_update(dwc, 1);
+ break;
default:
break;
}
@@ -165,9 +172,14 @@ static void __dwc3_set_mode(struct work_struct *work)
if (ret)
dev_err(dwc->dev, "failed to initialize peripheral\n");
break;
+ case DWC3_GCTL_PRTCAP_OTG:
+ dwc3_otg_init(dwc);
+ dwc3_otg_update(dwc, 0);
+ break;
default:
break;
}
+
}
void dwc3_set_mode(struct dwc3 *dwc, u32 mode)
@@ -351,7 +363,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;
@@ -368,7 +380,7 @@ static int dwc3_event_buffers_setup(struct dwc3 *dwc)
return 0;
}
-static void dwc3_event_buffers_cleanup(struct dwc3 *dwc)
+void dwc3_event_buffers_cleanup(struct dwc3 *dwc)
{
struct dwc3_event_buffer *evt;
@@ -1329,6 +1341,20 @@ static int dwc3_suspend_common(struct dwc3 *dwc, pm_message_t msg)
if (!PMSG_IS_AUTO(msg))
dwc3_core_exit(dwc);
break;
+ case DWC3_GCTL_PRTCAP_OTG:
+ /* do nothing during runtime_suspend */
+ if (PMSG_IS_AUTO(msg))
+ break;
+
+ if (dwc->current_otg_role == DWC3_OTG_ROLE_DEVICE) {
+ spin_lock_irqsave(&dwc->lock, flags);
+ dwc3_gadget_suspend(dwc);
+ spin_unlock_irqrestore(&dwc->lock, flags);
+ }
+
+ dwc3_otg_exit(dwc);
+ dwc3_core_exit(dwc);
+ break;
default:
/* do nothing */
break;
@@ -1360,6 +1386,27 @@ static int dwc3_resume_common(struct dwc3 *dwc, pm_message_t msg)
return ret;
}
break;
+ case DWC3_GCTL_PRTCAP_OTG:
+ /* nothing to do on runtime_resume */
+ if (PMSG_IS_AUTO(msg))
+ break;
+
+ ret = dwc3_core_init(dwc);
+ if (ret)
+ return ret;
+
+ dwc3_set_prtcap(dwc, dwc->current_dr_role);
+
+ dwc3_otg_init(dwc);
+ if (dwc->current_otg_role == DWC3_OTG_ROLE_HOST) {
+ dwc3_otg_host_init(dwc);
+ } else if (dwc->current_otg_role == DWC3_OTG_ROLE_DEVICE) {
+ spin_lock_irqsave(&dwc->lock, flags);
+ dwc3_gadget_resume(dwc);
+ spin_unlock_irqrestore(&dwc->lock, flags);
+ }
+
+ break;
default:
/* do nothing */
break;
diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h
index 0d4c698..09243a6 100644
--- a/drivers/usb/dwc3/core.h
+++ b/drivers/usb/dwc3/core.h
@@ -58,6 +58,11 @@
#define DWC3_DEVICE_EVENT_CMD_CMPL 10
#define DWC3_DEVICE_EVENT_OVERFLOW 11
+/* Controller's role while using the OTG block */
+#define DWC3_OTG_ROLE_IDLE 0
+#define DWC3_OTG_ROLE_HOST 1
+#define DWC3_OTG_ROLE_DEVICE 2
+
#define DWC3_GEVNTCOUNT_MASK 0xfffc
#define DWC3_GEVNTCOUNT_EHB BIT(31)
#define DWC3_GSNPSID_MASK 0xffff0000
@@ -863,6 +868,10 @@ struct dwc3_scratchpad_array {
* @regs_size: address space size
* @fladj: frame length adjustment
* @irq_gadget: peripheral controller's IRQ number
+ * @otg_irq: IRQ number for OTG IRQs
+ * @current_otg_role: current role of operation while using the OTG block
+ * @desired_otg_role: desired role of operation while using the OTG block
+ * @otg_restart_host: flag that OTG controller needs to restart host
* @nr_scratch: number of scratch buffers
* @u1u2: only used on revisions <1.83a for workaround
* @maximum_speed: maximum speed requested (mainly for testing purposes)
@@ -996,6 +1005,10 @@ struct dwc3 {
u32 fladj;
u32 irq_gadget;
+ u32 otg_irq;
+ u32 current_otg_role;
+ u32 desired_otg_role;
+ bool otg_restart_host;
u32 nr_scratch;
u32 u1u2;
u32 maximum_speed;
@@ -1257,6 +1270,7 @@ struct dwc3_gadget_ep_cmd_params {
#define DWC3_HAS_OTG BIT(3)
/* prototypes */
+void dwc3_set_prtcap(struct dwc3 *dwc, u32 mode);
void dwc3_set_mode(struct dwc3 *dwc, u32 mode);
u32 dwc3_core_fifo_space(struct dwc3_ep *dep, u8 type);
@@ -1274,6 +1288,9 @@ static inline bool dwc3_is_usb31(struct dwc3 *dwc)
bool dwc3_has_imod(struct dwc3 *dwc);
+int dwc3_event_buffers_setup(struct dwc3 *dwc);
+void dwc3_event_buffers_cleanup(struct dwc3 *dwc);
+
#if IS_ENABLED(CONFIG_USB_DWC3_HOST) || IS_ENABLED(CONFIG_USB_DWC3_DUAL_ROLE)
int dwc3_host_init(struct dwc3 *dwc);
void dwc3_host_exit(struct dwc3 *dwc);
@@ -1317,11 +1334,23 @@ static inline int dwc3_send_gadget_generic_command(struct dwc3 *dwc,
#if IS_ENABLED(CONFIG_USB_DWC3_DUAL_ROLE)
int dwc3_drd_init(struct dwc3 *dwc);
void dwc3_drd_exit(struct dwc3 *dwc);
+void dwc3_otg_init(struct dwc3 *dwc);
+void dwc3_otg_exit(struct dwc3 *dwc);
+void dwc3_otg_update(struct dwc3 *dwc, bool ignore_idstatus);
+void dwc3_otg_host_init(struct dwc3 *dwc);
#else
static inline int dwc3_drd_init(struct dwc3 *dwc)
{ return 0; }
static inline void dwc3_drd_exit(struct dwc3 *dwc)
{ }
+static inline void dwc3_otg_init(struct dwc3 *dwc)
+{ }
+static inline void dwc3_otg_exit(struct dwc3 *dwc)
+{ }
+static inline void dwc3_otg_update(struct dwc3 *dwc, bool ignore_idstatus)
+{ }
+static inline void dwc3_otg_host_init(struct dwc3 *dwc)
+{ }
#endif
/* power management interface */
diff --git a/drivers/usb/dwc3/drd.c b/drivers/usb/dwc3/drd.c
index cc8ab9a..1d8c557 100644
--- a/drivers/usb/dwc3/drd.c
+++ b/drivers/usb/dwc3/drd.c
@@ -8,22 +8,423 @@
*/
#include <linux/extcon.h>
+#include <linux/platform_device.h>
#include "debug.h"
#include "core.h"
#include "gadget.h"
-static void dwc3_drd_update(struct dwc3 *dwc)
+static void dwc3_otg_disable_events(struct dwc3 *dwc, u32 disable_mask)
+{
+ u32 reg = dwc3_readl(dwc->regs, DWC3_OEVTEN);
+
+ reg &= ~(disable_mask);
+ dwc3_writel(dwc->regs, DWC3_OEVTEN, reg);
+}
+
+static void dwc3_otg_enable_events(struct dwc3 *dwc, u32 enable_mask)
+{
+ u32 reg = dwc3_readl(dwc->regs, DWC3_OEVTEN);
+
+ reg |= (enable_mask);
+ dwc3_writel(dwc->regs, DWC3_OEVTEN, reg);
+}
+
+static void dwc3_otg_clear_events(struct dwc3 *dwc)
+{
+ u32 reg = dwc3_readl(dwc->regs, DWC3_OEVT);
+
+ dwc3_writel(dwc->regs, DWC3_OEVTEN, reg);
+}
+
+#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_BDEVBHOSTENDEN | \
+ DWC3_OEVTEN_BDEVHNPCHNGEN | DWC3_OEVTEN_BDEVSESSVLDDETEN | \
+ DWC3_OEVTEN_BDEVVBUSCHNGEN)
+
+static irqreturn_t dwc3_otg_thread_irq(int irq, void *_dwc)
{
+ struct dwc3 *dwc = _dwc;
+
+ spin_lock(&dwc->lock);
+ if (dwc->otg_restart_host) {
+ dwc3_otg_host_init(dwc);
+ dwc->otg_restart_host = 0;
+ }
+
+ spin_unlock(&dwc->lock);
+
+ dwc3_set_mode(dwc, DWC3_GCTL_PRTCAP_OTG);
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t dwc3_otg_irq(int irq, void *_dwc)
+{
+ u32 reg;
+ struct dwc3 *dwc = _dwc;
+ irqreturn_t ret = IRQ_NONE;
+
+ reg = dwc3_readl(dwc->regs, DWC3_OEVT);
+ if (reg) {
+ /* ignore non OTG events, we can't disable them in OEVTEN */
+ if (!(reg & DWC3_OTG_ALL_EVENTS)) {
+ dwc3_writel(dwc->regs, DWC3_OEVT, reg);
+ return IRQ_NONE;
+ }
+
+ if (dwc->current_otg_role == DWC3_OTG_ROLE_HOST &&
+ !(reg & DWC3_OEVT_DEVICEMODE))
+ dwc->otg_restart_host = 1;
+ dwc3_writel(dwc->regs, DWC3_OEVT, reg);
+ ret = IRQ_WAKE_THREAD;
+ }
+
+ return ret;
+}
+
+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_otg_clear_events(dwc);
+ /* 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_otg_get_irq(struct dwc3 *dwc)
+{
+ struct platform_device *dwc3_pdev = to_platform_device(dwc->dev);
+ int irq;
+
+ irq = platform_get_irq_byname(dwc3_pdev, "otg");
+ if (irq > 0)
+ goto out;
+
+ if (irq == -EPROBE_DEFER)
+ goto out;
+
+ irq = platform_get_irq_byname(dwc3_pdev, "dwc_usb3");
+ if (irq > 0)
+ goto out;
+
+ if (irq == -EPROBE_DEFER)
+ goto out;
+
+ irq = platform_get_irq(dwc3_pdev, 0);
+ if (irq > 0)
+ goto out;
+
+ if (irq != -EPROBE_DEFER)
+ dev_err(dwc->dev, "missing OTG IRQ\n");
+
+ if (!irq)
+ irq = -EINVAL;
+
+out:
+ return irq;
+}
+
+void dwc3_otg_init(struct dwc3 *dwc)
+{
+ u32 reg;
+
+ /*
+ * As per Figure 11-4 OTG Driver Overall Programming Flow,
+ * block "Initialize GCTL for OTG operation".
+ */
+ /* GCTL.PrtCapDir=2'b11 */
+ dwc3_set_prtcap(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);
+}
+
+void dwc3_otg_exit(struct dwc3 *dwc)
+{
+ /* disable all OTG IRQs */
+ dwc3_otg_disable_events(dwc, DWC3_OTG_ALL_EVENTS);
+ /* clear all events */
+ dwc3_otg_clear_events(dwc);
+}
+
+/* should be called before Host controller driver is started */
+void dwc3_otg_host_init(struct dwc3 *dwc)
+{
+ u32 reg;
+
+ /* As per Figure 11-10 A-Device Flow Diagram */
+ /* OCFG.HNPCap = 0, OCFG.SRPCap = 0. Already 0 */
+
+ /*
+ * 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);
+
+ /*
+ * 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);
+}
+
+/* should be called after Host controller driver is stopped */
+static void dwc3_otg_host_exit(struct dwc3 *dwc)
+{
+ u32 reg;
+
+ /*
+ * Exit from A-device flow as per
+ * Figure 11-4 OTG Driver Overall Programming Flow
+ */
+
+ /*
+ * 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);
+}
+
+/* should be called before the gadget controller driver is started */
+static void dwc3_otg_device_init(struct dwc3 *dwc)
+{
+ u32 reg;
+
+ /* As per Figure 11-20 B-Device Flow Diagram */
+
+ /*
+ * OCFG.HNPCap = GHWPARAMS6.HNP_CAP, OCFG.SRPCap = 1
+ * but we keep them 0 for simple dual-role operation.
+ */
+ reg = dwc3_readl(dwc->regs, DWC3_OCFG);
+ /* 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_OEVTEN_BDEVSESSVLDDETEN);
+ /* 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. Already 0. */
+}
+
+/* should be called after the gadget controller driver is stopped */
+static void dwc3_otg_device_exit(struct dwc3 *dwc)
+{
+ u32 reg;
+
+ /*
+ * Exit from B-device flow as per
+ * Figure 11-4 OTG Driver Overall Programming Flow
+ */
+
+ /*
+ * OEVTEN.OTGBDevHNPChngEvntEn = 0
+ * OEVTEN.OTGBDevVBusChngEvntEn = 0
+ * OEVTEN.OTGBDevBHostEndEvntEn = 0
+ */
+ dwc3_otg_disable_events(dwc, DWC3_OEVTEN_BDEVHNPCHNGEN |
+ DWC3_OEVTEN_BDEVVBUSCHNGEN |
+ DWC3_OEVTEN_BDEVBHOSTENDEN);
+
+ /* 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);
+}
+
+void dwc3_otg_update(struct dwc3 *dwc, bool ignore_idstatus)
+{
+ int ret;
+ u32 reg;
int id;
+ unsigned long flags;
- id = extcon_get_state(dwc->edev, EXTCON_USB_HOST);
- if (id < 0)
- id = 0;
+ if (dwc->dr_mode != USB_DR_MODE_OTG)
+ return;
- dwc3_set_mode(dwc, id ?
- DWC3_GCTL_PRTCAP_HOST :
- DWC3_GCTL_PRTCAP_DEVICE);
+ /* don't do anything if debug user changed role to not OTG */
+ if (dwc->current_dr_role != DWC3_GCTL_PRTCAP_OTG)
+ return;
+
+ if (!ignore_idstatus) {
+ reg = dwc3_readl(dwc->regs, DWC3_OSTS);
+ id = !!(reg & DWC3_OSTS_CONIDSTS);
+
+ dwc->desired_otg_role = id ? DWC3_OTG_ROLE_DEVICE :
+ DWC3_OTG_ROLE_HOST;
+ }
+
+ if (dwc->desired_otg_role == dwc->current_otg_role)
+ return;
+
+ switch (dwc->current_otg_role) {
+ case DWC3_OTG_ROLE_HOST:
+ dwc3_host_exit(dwc);
+ spin_lock_irqsave(&dwc->lock, flags);
+ dwc3_otg_host_exit(dwc);
+ spin_unlock_irqrestore(&dwc->lock, flags);
+ break;
+ case DWC3_OTG_ROLE_DEVICE:
+ dwc3_gadget_exit(dwc);
+ spin_lock_irqsave(&dwc->lock, flags);
+ dwc3_event_buffers_cleanup(dwc);
+ dwc3_otg_device_exit(dwc);
+ spin_unlock_irqrestore(&dwc->lock, flags);
+ break;
+ default:
+ break;
+ }
+
+ spin_lock_irqsave(&dwc->lock, flags);
+
+ dwc->current_otg_role = dwc->desired_otg_role;
+
+ spin_unlock_irqrestore(&dwc->lock, flags);
+
+ switch (dwc->desired_otg_role) {
+ case DWC3_OTG_ROLE_HOST:
+ spin_lock_irqsave(&dwc->lock, flags);
+ dwc3_otgregs_init(dwc);
+ dwc3_otg_host_init(dwc);
+ spin_unlock_irqrestore(&dwc->lock, flags);
+ ret = dwc3_host_init(dwc);
+ if (ret) {
+ dev_err(dwc->dev, "failed to initialize host\n");
+ } else {
+ if (dwc->usb2_phy)
+ otg_set_vbus(dwc->usb2_phy->otg, true);
+ if (dwc->usb2_generic_phy)
+ phy_set_mode(dwc->usb2_generic_phy,
+ PHY_MODE_USB_HOST);
+ }
+ break;
+ case DWC3_OTG_ROLE_DEVICE:
+ spin_lock_irqsave(&dwc->lock, flags);
+ dwc3_otgregs_init(dwc);
+ dwc3_otg_device_init(dwc);
+ dwc3_event_buffers_setup(dwc);
+ spin_unlock_irqrestore(&dwc->lock, flags);
+
+ if (dwc->usb2_phy)
+ otg_set_vbus(dwc->usb2_phy->otg, false);
+ if (dwc->usb2_generic_phy)
+ phy_set_mode(dwc->usb2_generic_phy,
+ PHY_MODE_USB_DEVICE);
+ ret = dwc3_gadget_init(dwc);
+ if (ret)
+ dev_err(dwc->dev, "failed to initialize peripheral\n");
+ break;
+ default:
+ break;
+ }
+}
+
+static void dwc3_drd_update(struct dwc3 *dwc)
+{
+ int id;
+
+ if (dwc->edev) {
+ id = extcon_get_state(dwc->edev, EXTCON_USB_HOST);
+ if (id < 0)
+ id = 0;
+ dwc3_set_mode(dwc, id ?
+ DWC3_GCTL_PRTCAP_HOST :
+ DWC3_GCTL_PRTCAP_DEVICE);
+ }
}
static int dwc3_drd_notifier(struct notifier_block *nb,
@@ -40,11 +441,11 @@ static int dwc3_drd_notifier(struct notifier_block *nb,
int dwc3_drd_init(struct dwc3 *dwc)
{
- int ret;
+ int ret, irq;
- if (dwc->dev->of_node) {
- if (of_property_read_bool(dwc->dev->of_node, "extcon"))
- dwc->edev = extcon_get_edev_by_phandle(dwc->dev, 0);
+ if (dwc->dev->of_node &&
+ of_property_read_bool(dwc->dev->of_node, "extcon")) {
+ dwc->edev = extcon_get_edev_by_phandle(dwc->dev, 0);
if (IS_ERR(dwc->edev))
return PTR_ERR(dwc->edev);
@@ -56,19 +457,71 @@ int dwc3_drd_init(struct dwc3 *dwc)
dev_err(dwc->dev, "couldn't register cable notifier\n");
return ret;
}
- }
- dwc3_drd_update(dwc);
+ dwc3_drd_update(dwc);
+ } else {
+ dwc3_set_prtcap(dwc, DWC3_GCTL_PRTCAP_OTG);
+ dwc->current_dr_role = DWC3_GCTL_PRTCAP_OTG;
+
+ /* use OTG block to get ID event */
+ irq = dwc3_otg_get_irq(dwc);
+ if (irq < 0)
+ return irq;
+
+ dwc->otg_irq = irq;
+
+ /* disable all OTG IRQs */
+ dwc3_otg_disable_events(dwc, DWC3_OTG_ALL_EVENTS);
+ /* clear all events */
+ dwc3_otg_clear_events(dwc);
+
+ 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;
+ return ret;
+ }
+
+ dwc3_otg_init(dwc);
+ dwc3_set_mode(dwc, DWC3_GCTL_PRTCAP_OTG);
+ }
return 0;
}
void dwc3_drd_exit(struct dwc3 *dwc)
{
- extcon_unregister_notifier(dwc->edev, EXTCON_USB_HOST,
- &dwc->edev_nb);
+ unsigned long flags;
+
+ if (dwc->edev)
+ extcon_unregister_notifier(dwc->edev, EXTCON_USB_HOST,
+ &dwc->edev_nb);
+
+ cancel_work_sync(&dwc->drd_work);
+
+ /* debug user might have changed role, clean based on current role */
+ switch (dwc->current_dr_role) {
+ case DWC3_GCTL_PRTCAP_HOST:
+ dwc3_host_exit(dwc);
+ break;
+ case DWC3_GCTL_PRTCAP_DEVICE:
+ dwc3_gadget_exit(dwc);
+ dwc3_event_buffers_cleanup(dwc);
+ break;
+ case DWC3_GCTL_PRTCAP_OTG:
+ dwc3_otg_exit(dwc);
+ spin_lock_irqsave(&dwc->lock, flags);
+ dwc->desired_otg_role = DWC3_OTG_ROLE_IDLE;
+ spin_unlock_irqrestore(&dwc->lock, flags);
+ dwc3_otg_update(dwc, 1);
+ break;
+ default:
+ break;
+ }
- dwc3_set_mode(dwc, DWC3_GCTL_PRTCAP_DEVICE);
- flush_work(&dwc->drd_work);
- dwc3_gadget_exit(dwc);
+ if (!dwc->edev)
+ free_irq(dwc->otg_irq, dwc);
}
--
cheers,
-roger
Texas Instruments Finland Oy, Porkkalankatu 22, 00180 Helsinki. Y-tunnus/Business ID: 0615521-4. Kotipaikka/Domicile: Helsinki
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [v2,2/2] usb: dwc3: add dual role support using OTG block
2018-02-27 11:30 ` [PATCH v2 2/2] " Roger Quadros
@ 2018-03-08 10:39 ` Felipe Balbi
-1 siblings, 0 replies; 11+ messages in thread
From: Felipe Balbi @ 2018-03-08 10:39 UTC (permalink / raw)
To: Roger Quadros; +Cc: linux-usb, linux-kernel
Roger Quadros <rogerq@ti.com> writes:
> This is useful on platforms (e.g. TI AM437x) that don't
> have ID available on a GPIO but do have the OTG block.
>
> We can obtain the ID state via the OTG block and use it
> for dual-role switching.
>
> Signed-off-by: Roger Quadros <rogerq@ti.com>
patch one applied fine to testing/next. But not this one:
checking file drivers/usb/dwc3/core.c
Hunk #2 FAILED at 107.
Hunk #3 succeeded at 122 (offset -5 lines).
Hunk #4 succeeded at 166 (offset -3 lines).
Hunk #5 succeeded at 365 (offset 5 lines).
Hunk #6 succeeded at 382 (offset 5 lines).
Hunk #7 succeeded at 1193 with fuzz 2 (offset -145 lines).
Hunk #8 FAILED at 1383.
2 out of 8 hunks FAILED
checking file drivers/usb/dwc3/core.h
Hunk #2 succeeded at 901 (offset 33 lines).
Hunk #3 succeeded at 1040 (offset 35 lines).
Hunk #4 succeeded at 1316 (offset 46 lines).
Hunk #5 succeeded at 1334 (offset 46 lines).
Hunk #6 succeeded at 1380 (offset 46 lines).
checking file drivers/usb/dwc3/drd.c
care to rebase?
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH v2 2/2] usb: dwc3: add dual role support using OTG block
@ 2018-03-08 10:39 ` Felipe Balbi
0 siblings, 0 replies; 11+ messages in thread
From: Felipe Balbi @ 2018-03-08 10:39 UTC (permalink / raw)
To: Roger Quadros; +Cc: linux-usb, linux-kernel, Roger Quadros
[-- Attachment #1: Type: text/plain, Size: 1052 bytes --]
Roger Quadros <rogerq@ti.com> writes:
> This is useful on platforms (e.g. TI AM437x) that don't
> have ID available on a GPIO but do have the OTG block.
>
> We can obtain the ID state via the OTG block and use it
> for dual-role switching.
>
> Signed-off-by: Roger Quadros <rogerq@ti.com>
patch one applied fine to testing/next. But not this one:
checking file drivers/usb/dwc3/core.c
Hunk #2 FAILED at 107.
Hunk #3 succeeded at 122 (offset -5 lines).
Hunk #4 succeeded at 166 (offset -3 lines).
Hunk #5 succeeded at 365 (offset 5 lines).
Hunk #6 succeeded at 382 (offset 5 lines).
Hunk #7 succeeded at 1193 with fuzz 2 (offset -145 lines).
Hunk #8 FAILED at 1383.
2 out of 8 hunks FAILED
checking file drivers/usb/dwc3/core.h
Hunk #2 succeeded at 901 (offset 33 lines).
Hunk #3 succeeded at 1040 (offset 35 lines).
Hunk #4 succeeded at 1316 (offset 46 lines).
Hunk #5 succeeded at 1334 (offset 46 lines).
Hunk #6 succeeded at 1380 (offset 46 lines).
checking file drivers/usb/dwc3/drd.c
care to rebase?
--
balbi
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 832 bytes --]
^ permalink raw reply [flat|nested] 11+ messages in thread
* [v2,2/2] usb: dwc3: add dual role support using OTG block
2018-03-08 10:39 ` [PATCH v2 2/2] " Felipe Balbi
@ 2018-03-08 12:31 ` Roger Quadros
-1 siblings, 0 replies; 11+ messages in thread
From: Roger Quadros @ 2018-03-08 12:31 UTC (permalink / raw)
To: Felipe Balbi; +Cc: linux-usb, linux-kernel
Felipe,
On 08/03/18 12:39, Felipe Balbi wrote:
> Roger Quadros <rogerq@ti.com> writes:
>
>> This is useful on platforms (e.g. TI AM437x) that don't
>> have ID available on a GPIO but do have the OTG block.
>>
>> We can obtain the ID state via the OTG block and use it
>> for dual-role switching.
>>
>> Signed-off-by: Roger Quadros <rogerq@ti.com>
>
> patch one applied fine to testing/next. But not this one:
That's because you will need this commit from v4.16-rc3
c4a5153e87fd ("usb: dwc3: core: Power-off core/PHYs on system_suspend in host mode")
Could you please apply that and let me know if it works?
>
> checking file drivers/usb/dwc3/core.c
> Hunk #2 FAILED at 107.
> Hunk #3 succeeded at 122 (offset -5 lines).
> Hunk #4 succeeded at 166 (offset -3 lines).
> Hunk #5 succeeded at 365 (offset 5 lines).
> Hunk #6 succeeded at 382 (offset 5 lines).
> Hunk #7 succeeded at 1193 with fuzz 2 (offset -145 lines).
> Hunk #8 FAILED at 1383.
> 2 out of 8 hunks FAILED
> checking file drivers/usb/dwc3/core.h
> Hunk #2 succeeded at 901 (offset 33 lines).
> Hunk #3 succeeded at 1040 (offset 35 lines).
> Hunk #4 succeeded at 1316 (offset 46 lines).
> Hunk #5 succeeded at 1334 (offset 46 lines).
> Hunk #6 succeeded at 1380 (offset 46 lines).
> checking file drivers/usb/dwc3/drd.c
>
> care to rebase?
>
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH v2 2/2] usb: dwc3: add dual role support using OTG block
@ 2018-03-08 12:31 ` Roger Quadros
0 siblings, 0 replies; 11+ messages in thread
From: Roger Quadros @ 2018-03-08 12:31 UTC (permalink / raw)
To: Felipe Balbi; +Cc: linux-usb, linux-kernel
Felipe,
On 08/03/18 12:39, Felipe Balbi wrote:
> Roger Quadros <rogerq@ti.com> writes:
>
>> This is useful on platforms (e.g. TI AM437x) that don't
>> have ID available on a GPIO but do have the OTG block.
>>
>> We can obtain the ID state via the OTG block and use it
>> for dual-role switching.
>>
>> Signed-off-by: Roger Quadros <rogerq@ti.com>
>
> patch one applied fine to testing/next. But not this one:
That's because you will need this commit from v4.16-rc3
c4a5153e87fd ("usb: dwc3: core: Power-off core/PHYs on system_suspend in host mode")
Could you please apply that and let me know if it works?
>
> checking file drivers/usb/dwc3/core.c
> Hunk #2 FAILED at 107.
> Hunk #3 succeeded at 122 (offset -5 lines).
> Hunk #4 succeeded at 166 (offset -3 lines).
> Hunk #5 succeeded at 365 (offset 5 lines).
> Hunk #6 succeeded at 382 (offset 5 lines).
> Hunk #7 succeeded at 1193 with fuzz 2 (offset -145 lines).
> Hunk #8 FAILED at 1383.
> 2 out of 8 hunks FAILED
> checking file drivers/usb/dwc3/core.h
> Hunk #2 succeeded at 901 (offset 33 lines).
> Hunk #3 succeeded at 1040 (offset 35 lines).
> Hunk #4 succeeded at 1316 (offset 46 lines).
> Hunk #5 succeeded at 1334 (offset 46 lines).
> Hunk #6 succeeded at 1380 (offset 46 lines).
> checking file drivers/usb/dwc3/drd.c
>
> care to rebase?
>
--
cheers,
-roger
Texas Instruments Finland Oy, Porkkalankatu 22, 00180 Helsinki. Y-tunnus/Business ID: 0615521-4. Kotipaikka/Domicile: Helsinki
^ permalink raw reply [flat|nested] 11+ messages in thread
* [v2,2/2] usb: dwc3: add dual role support using OTG block
2018-03-08 12:31 ` [PATCH v2 2/2] " Roger Quadros
@ 2018-03-08 13:12 ` Felipe Balbi
-1 siblings, 0 replies; 11+ messages in thread
From: Felipe Balbi @ 2018-03-08 13:12 UTC (permalink / raw)
To: Roger Quadros; +Cc: linux-usb, linux-kernel
Hi,
Roger Quadros <rogerq@ti.com> writes:
> Felipe,
>
> On 08/03/18 12:39, Felipe Balbi wrote:
>> Roger Quadros <rogerq@ti.com> writes:
>>
>>> This is useful on platforms (e.g. TI AM437x) that don't
>>> have ID available on a GPIO but do have the OTG block.
>>>
>>> We can obtain the ID state via the OTG block and use it
>>> for dual-role switching.
>>>
>>> Signed-off-by: Roger Quadros <rogerq@ti.com>
>>
>> patch one applied fine to testing/next. But not this one:
>
> That's because you will need this commit from v4.16-rc3
>
> c4a5153e87fd ("usb: dwc3: core: Power-off core/PHYs on system_suspend in host mode")
>
> Could you please apply that and let me know if it works?
indeed, applied fine. thanks
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH v2 2/2] usb: dwc3: add dual role support using OTG block
@ 2018-03-08 13:12 ` Felipe Balbi
0 siblings, 0 replies; 11+ messages in thread
From: Felipe Balbi @ 2018-03-08 13:12 UTC (permalink / raw)
To: Roger Quadros; +Cc: linux-usb, linux-kernel
[-- Attachment #1: Type: text/plain, Size: 752 bytes --]
Hi,
Roger Quadros <rogerq@ti.com> writes:
> Felipe,
>
> On 08/03/18 12:39, Felipe Balbi wrote:
>> Roger Quadros <rogerq@ti.com> writes:
>>
>>> This is useful on platforms (e.g. TI AM437x) that don't
>>> have ID available on a GPIO but do have the OTG block.
>>>
>>> We can obtain the ID state via the OTG block and use it
>>> for dual-role switching.
>>>
>>> Signed-off-by: Roger Quadros <rogerq@ti.com>
>>
>> patch one applied fine to testing/next. But not this one:
>
> That's because you will need this commit from v4.16-rc3
>
> c4a5153e87fd ("usb: dwc3: core: Power-off core/PHYs on system_suspend in host mode")
>
> Could you please apply that and let me know if it works?
indeed, applied fine. thanks
--
balbi
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 832 bytes --]
^ permalink raw reply [flat|nested] 11+ messages in thread
end of thread, other threads:[~2018-03-08 13:13 UTC | newest]
Thread overview: 11+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2018-03-08 13:12 [v2,2/2] usb: dwc3: add dual role support using OTG block Felipe Balbi
2018-03-08 13:12 ` [PATCH v2 2/2] " Felipe Balbi
-- strict thread matches above, loose matches on Subject: below --
2018-03-08 12:31 [v2,2/2] " Roger Quadros
2018-03-08 12:31 ` [PATCH v2 2/2] " Roger Quadros
2018-03-08 10:39 [v2,2/2] " Felipe Balbi
2018-03-08 10:39 ` [PATCH v2 2/2] " Felipe Balbi
2018-02-27 11:30 [v2,2/2] " Roger Quadros
2018-02-27 11:30 ` [PATCH v2 2/2] " Roger Quadros
2018-02-27 11:29 [v2,1/2] usb: dwc3: core.h: add some register definitions Roger Quadros
2018-02-27 11:29 ` [PATCH v2 1/2] " Roger Quadros
2018-02-27 11:29 [PATCH v2 0/2] usb: dwc3: Add dual-role support using OTG core Roger Quadros
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.