summaryrefslogtreecommitdiff
path: root/drivers/usb/dwc3/drd.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/usb/dwc3/drd.c')
-rw-r--r--drivers/usb/dwc3/drd.c51
1 files changed, 49 insertions, 2 deletions
diff --git a/drivers/usb/dwc3/drd.c b/drivers/usb/dwc3/drd.c
index c946d64142ad..9b522396746b 100644
--- a/drivers/usb/dwc3/drd.c
+++ b/drivers/usb/dwc3/drd.c
@@ -476,6 +476,44 @@ static struct extcon_dev *dwc3_get_extcon(struct dwc3 *dwc)
return edev;
}
+static int dwc3_usb_role_switch_set(struct device *dev, enum usb_role role)
+{
+ u32 mode;
+
+ switch (role) {
+ case USB_ROLE_HOST:
+ mode = DWC3_GCTL_PRTCAP_HOST;
+ break;
+ case USB_ROLE_DEVICE:
+ mode = DWC3_GCTL_PRTCAP_DEVICE;
+ break;
+ default:
+ mode = DWC3_GCTL_PRTCAP_NONE;
+ break;
+ };
+
+ dwc3_set_mode(dev_get_drvdata(dev), mode);
+ return 0;
+}
+
+static enum usb_role dwc3_usb_role_switch_get(struct device *dev)
+{
+ struct dwc3 *dwc = dev_get_drvdata(dev);
+ unsigned long flags;
+ enum usb_role role;
+
+ spin_lock_irqsave(&dwc->lock, flags);
+ role = dwc->current_dr_role;
+ spin_unlock_irqrestore(&dwc->lock, flags);
+
+ return role;
+}
+
+static struct usb_role_switch_desc dwc3_role_switch = {
+ .set = dwc3_usb_role_switch_set,
+ .get = dwc3_usb_role_switch_get,
+};
+
int dwc3_drd_init(struct dwc3 *dwc)
{
int ret, irq;
@@ -484,7 +522,13 @@ int dwc3_drd_init(struct dwc3 *dwc)
if (IS_ERR(dwc->edev))
return PTR_ERR(dwc->edev);
- if (dwc->edev) {
+ if (device_property_read_bool(dwc->dev, "usb-role-switch")) {
+ dwc3_role_switch.fwnode = dev_fwnode(dwc->dev);
+ dwc->role_switch = usb_role_switch_register(dwc->dev,
+ &dwc3_role_switch);
+ if (IS_ERR(dwc->role_switch))
+ return PTR_ERR(dwc->role_switch);
+ } else if (dwc->edev) {
dwc->edev_nb.notifier_call = dwc3_drd_notifier;
ret = extcon_register_notifier(dwc->edev, EXTCON_USB_HOST,
&dwc->edev_nb);
@@ -531,6 +575,9 @@ void dwc3_drd_exit(struct dwc3 *dwc)
{
unsigned long flags;
+ if (dwc->role_switch)
+ usb_role_switch_unregister(dwc->role_switch);
+
if (dwc->edev)
extcon_unregister_notifier(dwc->edev, EXTCON_USB_HOST,
&dwc->edev_nb);
@@ -557,6 +604,6 @@ void dwc3_drd_exit(struct dwc3 *dwc)
break;
}
- if (!dwc->edev)
+ if (!dwc->edev && !dwc->role_switch)
free_irq(dwc->otg_irq, dwc);
}