mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2026-09-22 09:34:56 +02:00
usb: dwc3: gadget: Move vbus draw to workqueue context
[ Upstream commit54aaa3b387] 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:99288de360("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>
This commit is contained in:
+18
-1
@@ -1892,6 +1892,20 @@ static int dwc3_get_clocks(struct dwc3 *dwc)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void dwc3_vbus_draw_work(struct work_struct *work)
|
||||
{
|
||||
struct dwc3 *dwc = container_of(work, struct dwc3, vbus_draw_work);
|
||||
union power_supply_propval val = {0};
|
||||
int ret;
|
||||
|
||||
val.intval = 1000 * (dwc->current_limit);
|
||||
ret = power_supply_set_property(dwc->usb_psy, POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT, &val);
|
||||
|
||||
if (ret < 0)
|
||||
dev_dbg(dwc->dev, "Error (%d) setting vbus draw (%d mA)\n",
|
||||
ret, dwc->current_limit);
|
||||
}
|
||||
|
||||
static struct power_supply *dwc3_get_usb_power_supply(struct dwc3 *dwc)
|
||||
{
|
||||
struct power_supply *usb_psy;
|
||||
@@ -1906,6 +1920,7 @@ static struct power_supply *dwc3_get_usb_power_supply(struct dwc3 *dwc)
|
||||
if (!usb_psy)
|
||||
return ERR_PTR(-EPROBE_DEFER);
|
||||
|
||||
INIT_WORK(&dwc->vbus_draw_work, dwc3_vbus_draw_work);
|
||||
return usb_psy;
|
||||
}
|
||||
|
||||
@@ -2097,8 +2112,10 @@ static void dwc3_remove(struct platform_device *pdev)
|
||||
|
||||
dwc3_free_event_buffers(dwc);
|
||||
|
||||
if (dwc->usb_psy)
|
||||
if (dwc->usb_psy) {
|
||||
cancel_work_sync(&dwc->vbus_draw_work);
|
||||
power_supply_put(dwc->usb_psy);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
|
||||
@@ -1034,6 +1034,8 @@ struct dwc3_scratchpad_array {
|
||||
* @role_switch_default_mode: default operation mode of controller while
|
||||
* usb role is USB_ROLE_NONE.
|
||||
* @usb_psy: pointer to power supply interface.
|
||||
* @vbus_draw_work: Work to set the vbus drawing limit
|
||||
* @current_limit: How much current to draw from vbus, in milliAmperes.
|
||||
* @usb2_phy: pointer to USB2 PHY
|
||||
* @usb3_phy: pointer to USB3 PHY
|
||||
* @usb2_generic_phy: pointer to USB2 PHY
|
||||
@@ -1202,6 +1204,8 @@ struct dwc3 {
|
||||
enum usb_dr_mode role_switch_default_mode;
|
||||
|
||||
struct power_supply *usb_psy;
|
||||
struct work_struct vbus_draw_work;
|
||||
unsigned int current_limit;
|
||||
|
||||
u32 fladj;
|
||||
u32 ref_clk_per;
|
||||
|
||||
@@ -3139,8 +3139,6 @@ static void dwc3_gadget_set_ssp_rate(struct usb_gadget *g,
|
||||
static int dwc3_gadget_vbus_draw(struct usb_gadget *g, unsigned int mA)
|
||||
{
|
||||
struct dwc3 *dwc = gadget_to_dwc(g);
|
||||
union power_supply_propval val = {0};
|
||||
int ret;
|
||||
|
||||
if (dwc->usb2_phy)
|
||||
return usb_phy_set_power(dwc->usb2_phy, mA);
|
||||
@@ -3148,10 +3146,10 @@ static int dwc3_gadget_vbus_draw(struct usb_gadget *g, unsigned int mA)
|
||||
if (!dwc->usb_psy)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
val.intval = 1000 * mA;
|
||||
ret = power_supply_set_property(dwc->usb_psy, POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT, &val);
|
||||
dwc->current_limit = mA;
|
||||
schedule_work(&dwc->vbus_draw_work);
|
||||
|
||||
return ret;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user