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>
This commit is contained in:
Xu Yang 2022-10-09 23:53:36 +08:00 committed by Greg Kroah-Hartman
parent 32c6fefb29
commit e1b5d2bed6
1 changed files with 24 additions and 39 deletions

View File

@ -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;
struct ci_hdrc_cable *cable;
if ((ci_role != CI_ROLE_END && !ci->roles[ci_role]) ||
(current_role == role))
return 0;
pm_runtime_get_sync(ci->dev);
/* Stop current role */
spin_lock_irqsave(&ci->lock, flags);
if (current_role == USB_ROLE_DEVICE)
cable = &ci->platdata->vbus_extcon;
else if (current_role == USB_ROLE_HOST)
if (role == USB_ROLE_HOST) {
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);
cable = &ci->platdata->vbus_extcon;
cable->changed = true;
cable->connected = false;
} else if (role == USB_ROLE_DEVICE) {
cable = &ci->platdata->id_extcon;
cable->changed = true;
cable->connected = false;
cable = &ci->platdata->vbus_extcon;
cable->changed = true;
cable->connected = true;
} 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);
}