Skip to content
/ linux Public

Commit 76c1123

Browse files
Prashanth KSasha Levin
authored andcommitted
usb: dwc3: gadget: Move vbus draw to workqueue context
[ Upstream commit 54aaa3b ] Currently dwc3_gadget_vbus_draw() can be called from atomic context, which in turn invokes power-supply-core APIs. And some these PMIC APIs have operations that may sleep, leading to kernel panic. Fix this by moving the vbus_draw into a workqueue context. Fixes: 99288de ("usb: dwc3: add an alternate path in vbus_draw callback") Cc: stable <stable@kernel.org> Tested-by: Samuel Wu <wusamuel@google.com> Acked-by: Thinh Nguyen <Thinh.Nguyen@synopsys.com> Signed-off-by: Prashanth K <prashanth.k@oss.qualcomm.com> Link: https://patch.msgid.link/20260204054155.3063825-1-prashanth.k@oss.qualcomm.com Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> Signed-off-by: Sasha Levin <sashal@kernel.org>
1 parent aa8d68d commit 76c1123

File tree

3 files changed

+25
-6
lines changed

3 files changed

+25
-6
lines changed

drivers/usb/dwc3/core.c

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1892,6 +1892,20 @@ static int dwc3_get_clocks(struct dwc3 *dwc)
18921892
return 0;
18931893
}
18941894

1895+
static void dwc3_vbus_draw_work(struct work_struct *work)
1896+
{
1897+
struct dwc3 *dwc = container_of(work, struct dwc3, vbus_draw_work);
1898+
union power_supply_propval val = {0};
1899+
int ret;
1900+
1901+
val.intval = 1000 * (dwc->current_limit);
1902+
ret = power_supply_set_property(dwc->usb_psy, POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT, &val);
1903+
1904+
if (ret < 0)
1905+
dev_dbg(dwc->dev, "Error (%d) setting vbus draw (%d mA)\n",
1906+
ret, dwc->current_limit);
1907+
}
1908+
18951909
static struct power_supply *dwc3_get_usb_power_supply(struct dwc3 *dwc)
18961910
{
18971911
struct power_supply *usb_psy;
@@ -1906,6 +1920,7 @@ static struct power_supply *dwc3_get_usb_power_supply(struct dwc3 *dwc)
19061920
if (!usb_psy)
19071921
return ERR_PTR(-EPROBE_DEFER);
19081922

1923+
INIT_WORK(&dwc->vbus_draw_work, dwc3_vbus_draw_work);
19091924
return usb_psy;
19101925
}
19111926

@@ -2097,8 +2112,10 @@ static void dwc3_remove(struct platform_device *pdev)
20972112

20982113
dwc3_free_event_buffers(dwc);
20992114

2100-
if (dwc->usb_psy)
2115+
if (dwc->usb_psy) {
2116+
cancel_work_sync(&dwc->vbus_draw_work);
21012117
power_supply_put(dwc->usb_psy);
2118+
}
21022119
}
21032120

21042121
#ifdef CONFIG_PM

drivers/usb/dwc3/core.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1034,6 +1034,8 @@ struct dwc3_scratchpad_array {
10341034
* @role_switch_default_mode: default operation mode of controller while
10351035
* usb role is USB_ROLE_NONE.
10361036
* @usb_psy: pointer to power supply interface.
1037+
* @vbus_draw_work: Work to set the vbus drawing limit
1038+
* @current_limit: How much current to draw from vbus, in milliAmperes.
10371039
* @usb2_phy: pointer to USB2 PHY
10381040
* @usb3_phy: pointer to USB3 PHY
10391041
* @usb2_generic_phy: pointer to USB2 PHY
@@ -1202,6 +1204,8 @@ struct dwc3 {
12021204
enum usb_dr_mode role_switch_default_mode;
12031205

12041206
struct power_supply *usb_psy;
1207+
struct work_struct vbus_draw_work;
1208+
unsigned int current_limit;
12051209

12061210
u32 fladj;
12071211
u32 ref_clk_per;

drivers/usb/dwc3/gadget.c

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3139,19 +3139,17 @@ static void dwc3_gadget_set_ssp_rate(struct usb_gadget *g,
31393139
static int dwc3_gadget_vbus_draw(struct usb_gadget *g, unsigned int mA)
31403140
{
31413141
struct dwc3 *dwc = gadget_to_dwc(g);
3142-
union power_supply_propval val = {0};
3143-
int ret;
31443142

31453143
if (dwc->usb2_phy)
31463144
return usb_phy_set_power(dwc->usb2_phy, mA);
31473145

31483146
if (!dwc->usb_psy)
31493147
return -EOPNOTSUPP;
31503148

3151-
val.intval = 1000 * mA;
3152-
ret = power_supply_set_property(dwc->usb_psy, POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT, &val);
3149+
dwc->current_limit = mA;
3150+
schedule_work(&dwc->vbus_draw_work);
31533151

3154-
return ret;
3152+
return 0;
31553153
}
31563154

31573155
/**

0 commit comments

Comments
 (0)