Skip to content

Commit

Permalink
usb: dwc2: reinit for host mode during resume if lost power
Browse files Browse the repository at this point in the history
If the dwc2 controller lost power in suspend, we need to
reinit the core when the dwc2 in host mode. There are two
methods to set the dwc2 in host mode, and we need to reinit
the dwc2 to host mode separately during resume.

1. Set the dr_mode to otg, and plug in OTG cable with
   the ID pin conneted to GND.

   In this case, we can reinit the core to device mode
   firstly, and later after do dwc2_hsotg_resume, it can
   trigger the ID status change interrupt if the OTG cable
   is still connected, then we can init for Host mode in
   the ID status change interrupt handler.

2. Set the dr_mode to host, and force the dwc2 to Host
   mode irrespective of ID input pin.

   In this case, we can't depend on the ID status change,
   so we do force host mode and init the core during resume.

With this patch, it can fix the kernel panic if the dr_mode
is set to host mode during resume.

[  134.975352] Unable to handle kernel write to read-only memory at virtual address 0000000000000068
...
[  134.981681] CPU: 0 PID: 1646 Comm: Binder:157_3 Not tainted 4.19.193 #37
[  134.982290] Hardware name: Rockchip rk3326 S1002 avb board (DT)
[  134.982836] pstate: 60400085 (nZCv daIf +PAN -UAO)
[  134.983286] pc : kill_all_requests+0x20/0xe8
[  134.983685] lr : dwc2_hsotg_core_init_disconnected+0x24/0x770
...
[  135.098090]
[  135.098248] Call trace:
[  135.098503]  kill_all_requests+0x20/0xe8
[  135.098876]  dwc2_hsotg_core_init_disconnected+0x24/0x770
[  135.099377]  dwc2_resume+0x108/0x110
[  135.099722]  dpm_run_callback+0x48/0x230
[  135.100099]  device_resume+0xb4/0x250
[  135.100448]  dpm_resume+0x104/0x398
[  135.100784]  dpm_resume_end+0x14/0x28
[  135.101142]  suspend_devices_and_enter+0x15c/0xa68
[  135.101586]  pm_suspend+0x458/0x6d8
[  135.101927]  state_store+0x84/0x108
[  135.102271]  kobj_attr_store+0x14/0x28
[  135.102637]  sysfs_kf_write+0x48/0x58
[  135.102988]  kernfs_fop_write+0xf4/0x220
[  135.103367]  __vfs_write+0x34/0x158
[  135.103708]  vfs_write+0xb0/0x1d0
[  135.104027]  ksys_write+0x64/0xe0
[  135.104346]  __arm64_sys_write+0x14/0x20
[  135.104726]  el0_svc_common.constprop.0+0x64/0x178
[  135.105172]  el0_svc_compat_handler+0x18/0x20
[  135.105580]  el0_svc_compat+0x8/0x34
[  135.105929] Code: a90153f3 aa0003f6 f9001bf7 2a0203f7 (f900343f)
[  135.106485] ---[ end trace ddc1f4a0765afacd ]---
[  135.253494] Kernel panic - not syncing: Fatal exception

Change-Id: Ibfcd1c9176d8b4abb2fc8b3b5c8b0b6a866db4e7
Signed-off-by: William Wu <william.wu@rock-chips.com>
  • Loading branch information
wuliangfeng committed May 25, 2022
1 parent f4d9021 commit b604650
Showing 1 changed file with 38 additions and 12 deletions.
50 changes: 38 additions & 12 deletions drivers/usb/dwc2/platform.c
Original file line number Diff line number Diff line change
Expand Up @@ -628,19 +628,45 @@ static int __maybe_unused dwc2_resume(struct device *dev)
return ret;
}

/* Stop hcd if dr_mode is host and PD is power off when suspend */
if (dwc2->op_state == OTG_STATE_A_HOST && dwc2_is_device_mode(dwc2)) {
spin_lock_irqsave(&dwc2->lock, flags);
dwc2_hcd_disconnect(dwc2, true);
dwc2->op_state = OTG_STATE_B_PERIPHERAL;
dwc2->lx_state = DWC2_L3;
if (!dwc2->driver)
dwc2_hsotg_core_init_disconnected(dwc2, false);
spin_unlock_irqrestore(&dwc2->lock, flags);
}
if (dwc2_is_device_mode(dwc2)) {
if (dwc2->dr_mode == USB_DR_MODE_HOST) {
/* Reinit for Host mode if lost power */
dwc2_force_mode(dwc2, true);

spin_lock_irqsave(&dwc2->lock, flags);
dwc2_hsotg_disconnect(dwc2);
spin_unlock_irqrestore(&dwc2->lock, flags);

dwc2->op_state = OTG_STATE_A_HOST;
/* Initialize the Core for Host mode */
dwc2_core_init(dwc2, false);
dwc2_enable_global_interrupts(dwc2);
dwc2_hcd_start(dwc2);
} else {
/* Reinit for OTG in Host mode if lost power */
if (dwc2->dr_mode == USB_DR_MODE_OTG &&
dwc2->op_state == OTG_STATE_A_HOST) {
/*
* Reinit the core to device mode, and later
* after do dwc2_hsotg_resume, it can trigger
* the ID status change interrupt if the OTG
* cable is still connected, then we can init
* for Host mode in the ID status change
* interrupt handler.
*/
spin_lock_irqsave(&dwc2->lock, flags);
dwc2_hcd_disconnect(dwc2, true);
dwc2->op_state = OTG_STATE_B_PERIPHERAL;
dwc2->lx_state = DWC2_L3;
if (!dwc2->driver)
dwc2_hsotg_core_init_disconnected(dwc2, false);
spin_unlock_irqrestore(&dwc2->lock, flags);

if (dwc2_is_device_mode(dwc2))
ret = dwc2_hsotg_resume(dwc2);
}

ret = dwc2_hsotg_resume(dwc2);
}
}

return ret;
}
Expand Down

0 comments on commit b604650

Please sign in to comment.