summaryrefslogtreecommitdiff
path: root/drivers/usb/chipidea/core.c
diff options
context:
space:
mode:
authorPeter Chen <peter.chen@nxp.com>2018-11-16 16:40:48 +0800
committerJason Liu <jason.hui.liu@nxp.com>2019-02-12 10:35:21 +0800
commit6ee745601e2cff0170d0be089881e584b54d661b (patch)
tree0448bf879b5950287e7e8bcb7e1813849ce5a1a4 /drivers/usb/chipidea/core.c
parente3e2c5353ae41b9718756b1da84c8e17581663a9 (diff)
MLK-20346 usb: chipidea: change power_lost workqueue as freezable
Like commit d144dfea8af7 ("usb: chipidea: otg: change workqueue ci_otg as freezable"), the power_lost work item may try to remove hcd if controller is powered off during the system suspend, and the similar deadlock happens, see below dumps. Meanwhile, with this change, we need to disable USB interrupt during the work item runs (after driver resume has finished), otherwise, USB transfer will be timeout (5s) due to USB interrupt is disabled and IAA watchdog is still not ready at that time. Workqueue: events ci_power_lost_work Call trace: [<ffff000008085c44>] __switch_to+0x8c/0xd0 [<ffff000008d7bbf4>] __schedule+0x19c/0x5d8 [<ffff000008d7c068>] schedule+0x38/0xa0 [<ffff000008d7f3b4>] schedule_timeout+0x19c/0x338 [<ffff000008d7cc10>] wait_for_common+0xa0/0x148 [<ffff000008d7cccc>] wait_for_completion+0x14/0x20 [<ffff0000080e6040>] flush_work+0xd8/0x1f0 [<ffff0000080e61f4>] flush_delayed_work+0x3c/0x48 [<ffff0000081ae1c8>] wb_shutdown+0x90/0xd0 [<ffff0000081ae688>] bdi_unregister+0x58/0x1c0 [<ffff000008413a60>] del_gendisk+0x218/0x228 [<ffff00000871683c>] sd_remove+0x64/0xc0 [<ffff0000086b6eec>] device_release_driver_internal+0x154/0x1f0 [<ffff0000086b6f9c>] device_release_driver+0x14/0x20 [<ffff0000086b5d40>] bus_remove_device+0xc8/0x108 [<ffff0000086b2a08>] device_del+0x1f8/0x300 [<ffff0000087049ec>] __scsi_remove_device+0xec/0x128 [<ffff000008702c70>] scsi_forget_host+0x70/0x78 [<ffff0000086f7ee8>] scsi_remove_host+0xa0/0x140 [<ffff0000088e0588>] usb_stor_disconnect+0x50/0xc0 [<ffff00000887eab8>] usb_unbind_interface+0x78/0x280 [<ffff0000086b6eec>] device_release_driver_internal+0x154/0x1f0 [<ffff0000086b6f9c>] device_release_driver+0x14/0x20 [<ffff0000086b5d40>] bus_remove_device+0xc8/0x108 [<ffff0000086b2a08>] device_del+0x1f8/0x300 [<ffff00000887c364>] usb_disable_device+0xa4/0x210 [<ffff000008872cfc>] usb_disconnect+0x7c/0x240 [<ffff000008872e40>] usb_disconnect+0x1c0/0x240 [<ffff000008878e10>] usb_remove_hcd+0xc0/0x1d8 [<ffff0000088e7bac>] host_stop+0x34/0x90 [<ffff0000088e4088>] ci_handle_id_switch+0x70/0x1d0 [<ffff0000088e3038>] ci_power_lost_work+0x90/0xa8 [<ffff0000080e7100>] process_one_work+0x1e0/0x340 [<ffff0000080e72b0>] worker_thread+0x50/0x458 [<ffff0000080ed32c>] kthread+0xfc/0x128 [<ffff000008084eb8>] ret_from_fork+0x10/0x18 Reviewed-by: Jun Li <jun.li@nxp.com> Signed-off-by: Peter Chen <peter.chen@nxp.com>
Diffstat (limited to 'drivers/usb/chipidea/core.c')
-rw-r--r--drivers/usb/chipidea/core.c23
1 files changed, 17 insertions, 6 deletions
diff --git a/drivers/usb/chipidea/core.c b/drivers/usb/chipidea/core.c
index 67f6e3b44669..4a20245ce709 100644
--- a/drivers/usb/chipidea/core.c
+++ b/drivers/usb/chipidea/core.c
@@ -963,6 +963,7 @@ static void ci_power_lost_work(struct work_struct *work)
struct ci_hdrc *ci = container_of(work, struct ci_hdrc,
power_lost_work);
+ disable_irq_nosync(ci->irq);
pm_runtime_get_sync(ci->dev);
if (!ci_otg_is_fsm_mode(ci))
ci_start_new_role(ci);
@@ -1132,8 +1133,6 @@ static int ci_hdrc_probe(struct platform_device *pdev)
device_set_wakeup_capable(&pdev->dev, true);
- /* Init workqueue for controller power lost handling */
- INIT_WORK(&ci->power_lost_work, ci_power_lost_work);
mutex_init(&ci->mutex);
ret = dbg_create_files(ci);
@@ -1144,8 +1143,19 @@ static int ci_hdrc_probe(struct platform_device *pdev)
if (ret)
goto remove_debug;
+ /* Init workqueue for controller power lost handling */
+ ci->power_lost_wq = create_freezable_workqueue("ci_power_lost");
+ if (!ci->power_lost_wq) {
+ dev_err(ci->dev, "can't create power_lost workqueue\n");
+ goto remove_sys_group;
+ }
+
+ INIT_WORK(&ci->power_lost_work, ci_power_lost_work);
+
return 0;
+remove_sys_group:
+ sysfs_remove_group(&ci->dev->kobj, &ci_attr_group);
remove_debug:
dbg_remove_files(ci);
stop:
@@ -1173,6 +1183,8 @@ static int ci_hdrc_remove(struct platform_device *pdev)
pm_runtime_put_noidle(&pdev->dev);
}
+ flush_workqueue(ci->power_lost_wq);
+ destroy_workqueue(ci->power_lost_wq);
dbg_remove_files(ci);
sysfs_remove_group(&ci->dev->kobj, &ci_attr_group);
ci_role_destroy(ci);
@@ -1284,6 +1296,7 @@ static int ci_suspend(struct device *dev)
{
struct ci_hdrc *ci = dev_get_drvdata(dev);
+ flush_workqueue(ci->power_lost_wq);
if (ci->wq)
flush_workqueue(ci->wq);
/*
@@ -1349,10 +1362,8 @@ static int ci_resume(struct device *dev)
if (ci->role != CI_ROLE_END && ci_role(ci)->resume)
ci_role(ci)->resume(ci, power_lost);
- if (power_lost) {
- disable_irq_nosync(ci->irq);
- schedule_work(&ci->power_lost_work);
- }
+ if (power_lost)
+ queue_work(ci->power_lost_wq, &ci->power_lost_work);
if (ci->supports_runtime_pm) {
pm_runtime_disable(dev);