Skip to content

Commit cc509b6

Browse files
Xu Yanggregkh
authored andcommitted
usb: chipidea: core: handle power lost in workqueue
When power is recycled in usb controller during system power management, the controller will recognize it and switch role if role has been changed during power lost. In current design, it will be completed in resume() function. However, this may bring issues since usb class devices have their pm operations too and these device's resume() functions are still not being called at this point. When usb controller recognized host role should be stopped, these usb class devices will be removed at this point. But these usb class devices can't be removed in some cases, such as scsi devices. Since scsi driver may sync data to U-disk, however it will block there because scsi drvier can only handle pm request when is in suspended state. Therefore, there may exist a dependency between ci_resume() and usb class device's resume(). To break this potential dependency, we need to handle power lost work in a workqueue. Fixes: 74494b3 ("usb: chipidea: core: add controller resume support when controller is powered off") cc: stable@vger.kernel.org Signed-off-by: Xu Yang <xu.yang_2@nxp.com> Link: https://lore.kernel.org/r/20240119123537.3614838-1-xu.yang_2@nxp.com Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
1 parent 61a3488 commit cc509b6

File tree

2 files changed

+26
-20
lines changed

2 files changed

+26
-20
lines changed

drivers/usb/chipidea/ci.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,7 @@ struct hw_bank {
176176
* @enabled_otg_timer_bits: bits of enabled otg timers
177177
* @next_otg_timer: next nearest enabled timer to be expired
178178
* @work: work for role changing
179+
* @power_lost_work: work for power lost handling
179180
* @wq: workqueue thread
180181
* @qh_pool: allocation pool for queue heads
181182
* @td_pool: allocation pool for transfer descriptors
@@ -226,6 +227,7 @@ struct ci_hdrc {
226227
enum otg_fsm_timer next_otg_timer;
227228
struct usb_role_switch *role_switch;
228229
struct work_struct work;
230+
struct work_struct power_lost_work;
229231
struct workqueue_struct *wq;
230232

231233
struct dma_pool *qh_pool;

drivers/usb/chipidea/core.c

Lines changed: 24 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -856,6 +856,27 @@ static int ci_extcon_register(struct ci_hdrc *ci)
856856
return 0;
857857
}
858858

859+
static void ci_power_lost_work(struct work_struct *work)
860+
{
861+
struct ci_hdrc *ci = container_of(work, struct ci_hdrc, power_lost_work);
862+
enum ci_role role;
863+
864+
disable_irq_nosync(ci->irq);
865+
pm_runtime_get_sync(ci->dev);
866+
if (!ci_otg_is_fsm_mode(ci)) {
867+
role = ci_get_role(ci);
868+
869+
if (ci->role != role) {
870+
ci_handle_id_switch(ci);
871+
} else if (role == CI_ROLE_GADGET) {
872+
if (ci->is_otg && hw_read_otgsc(ci, OTGSC_BSV))
873+
usb_gadget_vbus_connect(&ci->gadget);
874+
}
875+
}
876+
pm_runtime_put_sync(ci->dev);
877+
enable_irq(ci->irq);
878+
}
879+
859880
static DEFINE_IDA(ci_ida);
860881

861882
struct platform_device *ci_hdrc_add_device(struct device *dev,
@@ -1045,6 +1066,8 @@ static int ci_hdrc_probe(struct platform_device *pdev)
10451066

10461067
spin_lock_init(&ci->lock);
10471068
mutex_init(&ci->mutex);
1069+
INIT_WORK(&ci->power_lost_work, ci_power_lost_work);
1070+
10481071
ci->dev = dev;
10491072
ci->platdata = dev_get_platdata(dev);
10501073
ci->imx28_write_fix = !!(ci->platdata->flags &
@@ -1396,25 +1419,6 @@ static int ci_suspend(struct device *dev)
13961419
return 0;
13971420
}
13981421

1399-
static void ci_handle_power_lost(struct ci_hdrc *ci)
1400-
{
1401-
enum ci_role role;
1402-
1403-
disable_irq_nosync(ci->irq);
1404-
if (!ci_otg_is_fsm_mode(ci)) {
1405-
role = ci_get_role(ci);
1406-
1407-
if (ci->role != role) {
1408-
ci_handle_id_switch(ci);
1409-
} else if (role == CI_ROLE_GADGET) {
1410-
if (ci->is_otg && hw_read_otgsc(ci, OTGSC_BSV))
1411-
usb_gadget_vbus_connect(&ci->gadget);
1412-
}
1413-
}
1414-
1415-
enable_irq(ci->irq);
1416-
}
1417-
14181422
static int ci_resume(struct device *dev)
14191423
{
14201424
struct ci_hdrc *ci = dev_get_drvdata(dev);
@@ -1446,7 +1450,7 @@ static int ci_resume(struct device *dev)
14461450
ci_role(ci)->resume(ci, power_lost);
14471451

14481452
if (power_lost)
1449-
ci_handle_power_lost(ci);
1453+
queue_work(system_freezable_wq, &ci->power_lost_work);
14501454

14511455
if (ci->supports_runtime_pm) {
14521456
pm_runtime_disable(dev);

0 commit comments

Comments
 (0)