| From 8595748e58885439c7e18a11a94702378bff3b02 Mon Sep 17 00:00:00 2001 |
| From: Li Jun <jun.li@nxp.com> |
| Date: Sun, 27 Jan 2019 19:40:03 +0800 |
| Subject: [PATCH] usb: dwc3: drd: add usb role switch class support for dual |
| role swap |
| |
| Register a usb_role_switch for dual role swap if the property |
| "usb-role-switch" is present. |
| |
| Signed-off-by: Li Jun <jun.li@nxp.com> |
| --- |
| drivers/usb/dwc3/core.h | 2 ++ |
| drivers/usb/dwc3/drd.c | 47 ++++++++++++++++++++++++++++++++++++++++++++++- |
| 2 files changed, 48 insertions(+), 1 deletion(-) |
| |
| --- a/drivers/usb/dwc3/core.h |
| +++ b/drivers/usb/dwc3/core.h |
| @@ -25,6 +25,7 @@ |
| #include <linux/usb/ch9.h> |
| #include <linux/usb/gadget.h> |
| #include <linux/usb/otg.h> |
| +#include <linux/usb/role.h> |
| #include <linux/ulpi/interface.h> |
| |
| #include <linux/phy/phy.h> |
| @@ -1097,6 +1098,7 @@ struct dwc3 { |
| void __iomem *regs; |
| size_t regs_size; |
| |
| + struct usb_role_switch *role_switch; |
| enum usb_dr_mode dr_mode; |
| u32 current_dr_role; |
| u32 desired_dr_role; |
| --- a/drivers/usb/dwc3/drd.c |
| +++ b/drivers/usb/dwc3/drd.c |
| @@ -476,6 +476,43 @@ static struct extcon_dev *dwc3_get_extco |
| 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: |
| + return 0; |
| + }; |
| + |
| + 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 const 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 +521,12 @@ 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")) { |
| + 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 +573,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); |