summaryrefslogtreecommitdiff
path: root/drivers/usb/chipidea
diff options
context:
space:
mode:
authorXu Yang <xu.yang_2@nxp.com>2022-10-09 18:53:36 +0300
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2022-10-23 15:34:52 +0300
commite1b5d2bed67c60c30d01a89df32152d74cfc8e63 (patch)
treed84d80fbaf13f52b69c19ae318c3aebc278696d3 /drivers/usb/chipidea
parent32c6fefb291bf84c5a4dbc7d52b56a1605ed9aae (diff)
downloadlinux-e1b5d2bed67c60c30d01a89df32152d74cfc8e63.tar.xz
usb: chipidea: core: handle usb role switch in a common way
Currently, ci_usb_role_switch_set() may be called before system resume stage when suspended. Worse yet, ci_hdrc device may stay at RPM_ACTIVE state which will cause pm_runtime_get_sync() fail to resume the device. In this case, role-switch may unable to complete transition process due to not exit from lpm state or due to lack some means after system resume. Same as ci_cable_notifier(), usb_role_switch could handle its events based on ci_hdrc_cable mechanism. Signed-off-by: Xu Yang <xu.yang_2@nxp.com> Link: https://lore.kernel.org/r/20221009155336.766960-1-xu.yang_2@nxp.com Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/usb/chipidea')
-rw-r--r--drivers/usb/chipidea/core.c55
1 files changed, 20 insertions, 35 deletions
diff --git a/drivers/usb/chipidea/core.c b/drivers/usb/chipidea/core.c
index 6330fa911792..ae90fee75a32 100644
--- a/drivers/usb/chipidea/core.c
+++ b/drivers/usb/chipidea/core.c
@@ -608,49 +608,32 @@ static int ci_usb_role_switch_set(struct usb_role_switch *sw,
enum usb_role role)
{
struct ci_hdrc *ci = usb_role_switch_get_drvdata(sw);
- struct ci_hdrc_cable *cable = NULL;
- enum usb_role current_role = ci_role_to_usb_role(ci);
- enum ci_role ci_role = usb_role_to_ci_role(role);
- unsigned long flags;
-
- if ((ci_role != CI_ROLE_END && !ci->roles[ci_role]) ||
- (current_role == role))
- return 0;
+ struct ci_hdrc_cable *cable;
- pm_runtime_get_sync(ci->dev);
- /* Stop current role */
- spin_lock_irqsave(&ci->lock, flags);
- if (current_role == USB_ROLE_DEVICE)
+ if (role == USB_ROLE_HOST) {
+ cable = &ci->platdata->id_extcon;
+ cable->changed = true;
+ cable->connected = true;
cable = &ci->platdata->vbus_extcon;
- else if (current_role == USB_ROLE_HOST)
+ cable->changed = true;
+ cable->connected = false;
+ } else if (role == USB_ROLE_DEVICE) {
cable = &ci->platdata->id_extcon;
-
- if (cable) {
cable->changed = true;
cable->connected = false;
- ci_irq(ci);
- spin_unlock_irqrestore(&ci->lock, flags);
- if (ci->wq && role != USB_ROLE_NONE)
- flush_workqueue(ci->wq);
- spin_lock_irqsave(&ci->lock, flags);
- }
-
- cable = NULL;
-
- /* Start target role */
- if (role == USB_ROLE_DEVICE)
cable = &ci->platdata->vbus_extcon;
- else if (role == USB_ROLE_HOST)
- cable = &ci->platdata->id_extcon;
-
- if (cable) {
cable->changed = true;
cable->connected = true;
- ci_irq(ci);
+ } else {
+ cable = &ci->platdata->id_extcon;
+ cable->changed = true;
+ cable->connected = false;
+ cable = &ci->platdata->vbus_extcon;
+ cable->changed = true;
+ cable->connected = false;
}
- spin_unlock_irqrestore(&ci->lock, flags);
- pm_runtime_put_sync(ci->dev);
+ ci_irq(ci);
return 0;
}
@@ -1305,11 +1288,13 @@ static void ci_extcon_wakeup_int(struct ci_hdrc *ci)
cable_id = &ci->platdata->id_extcon;
cable_vbus = &ci->platdata->vbus_extcon;
- if (!IS_ERR(cable_id->edev) && ci->is_otg &&
+ if ((!IS_ERR(cable_id->edev) || !IS_ERR(ci->role_switch))
+ && ci->is_otg &&
(otgsc & OTGSC_IDIE) && (otgsc & OTGSC_IDIS))
ci_irq(ci);
- if (!IS_ERR(cable_vbus->edev) && ci->is_otg &&
+ if ((!IS_ERR(cable_vbus->edev) || !IS_ERR(ci->role_switch))
+ && ci->is_otg &&
(otgsc & OTGSC_BSVIE) && (otgsc & OTGSC_BSVIS))
ci_irq(ci);
}