forked from mirrors/linux
		
	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:
		
							parent
							
								
									32c6fefb29
								
							
						
					
					
						commit
						e1b5d2bed6
					
				
					 1 changed files with 24 additions and 39 deletions
				
			
		| 
						 | 
					@ -608,49 +608,32 @@ static int ci_usb_role_switch_set(struct usb_role_switch *sw,
 | 
				
			||||||
				  enum usb_role role)
 | 
									  enum usb_role role)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct ci_hdrc *ci = usb_role_switch_get_drvdata(sw);
 | 
						struct ci_hdrc *ci = usb_role_switch_get_drvdata(sw);
 | 
				
			||||||
	struct ci_hdrc_cable *cable = NULL;
 | 
						struct ci_hdrc_cable *cable;
 | 
				
			||||||
	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]) ||
 | 
						if (role == USB_ROLE_HOST) {
 | 
				
			||||||
	    (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)
 | 
					 | 
				
			||||||
		cable = &ci->platdata->id_extcon;
 | 
							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->changed = true;
 | 
				
			||||||
		cable->connected = 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;
 | 
						return 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1305,11 +1288,13 @@ static void ci_extcon_wakeup_int(struct ci_hdrc *ci)
 | 
				
			||||||
	cable_id = &ci->platdata->id_extcon;
 | 
						cable_id = &ci->platdata->id_extcon;
 | 
				
			||||||
	cable_vbus = &ci->platdata->vbus_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))
 | 
							(otgsc & OTGSC_IDIE) && (otgsc & OTGSC_IDIS))
 | 
				
			||||||
		ci_irq(ci);
 | 
							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))
 | 
							(otgsc & OTGSC_BSVIE) && (otgsc & OTGSC_BSVIS))
 | 
				
			||||||
		ci_irq(ci);
 | 
							ci_irq(ci);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in a new issue