[PATCH v4] Input: drv260x: Fix suspend and resume sequencing

From: Maurizio Casciano

Date: Sun Aug 30 2026 - 10:18:09 EST


Force-feedback playback is queued asynchronously, but system suspend
can cut power while the worker is pending. Disable and drain the work
item before entering standby, and keep it disabled until resume has
restored communication.

The enable GPIO gates I2C access without resetting the device. Raise it
and observe the startup delay before leaving standby after resume or a
failed regulator shutdown.

Use explicit mutex locking with goto-based error unwinding, and balance
the work disable count on every resume error path so a later successful
resume can restore playback.

Reported-by: Sashiko AI review <sashiko-bot@xxxxxxxxxx>
Link: https://lore.kernel.org/linux-input/20260829230740.126461F000E9@xxxxxxxxxxxxxxx/
Suggested-by: Dmitry Torokhov <dmitry.torokhov@xxxxxxxxx>
Link: https://lore.kernel.org/linux-input/apLD91vzHIrLOPWC@xxxxxxxxxx/
Assisted-by: Codex:gpt-5.6-sol [sparse]
Signed-off-by: Maurizio Casciano <mauriziocasciano7@xxxxxxxxx>
---
drivers/input/misc/drv260x.c | 50 ++++++++++++++++++++++++++----------
1 file changed, 37 insertions(+), 13 deletions(-)

diff --git a/drivers/input/misc/drv260x.c b/drivers/input/misc/drv260x.c
index 6c5c4c53753b..b6499b2fcd8d 100644
--- a/drivers/input/misc/drv260x.c
+++ b/drivers/input/misc/drv260x.c
@@ -569,18 +569,20 @@ static int drv260x_probe(struct i2c_client *client)
static int drv260x_suspend(struct device *dev)
{
struct drv260x_data *haptics = dev_get_drvdata(dev);
- int error;
+ int error, restore_error;

- guard(mutex)(&haptics->input_dev->mutex);
+ mutex_lock(&haptics->input_dev->mutex);

if (input_device_enabled(haptics->input_dev)) {
+ disable_work_sync(&haptics->work);
+
error = regmap_update_bits(haptics->regmap,
DRV260X_MODE,
DRV260X_STANDBY_MASK,
DRV260X_STANDBY);
if (error) {
dev_err(dev, "Failed to set standby mode\n");
- return error;
+ goto err_enable_work;
}

gpiod_set_value(haptics->enable_gpio, 0);
@@ -588,14 +590,25 @@ static int drv260x_suspend(struct device *dev)
error = regulator_disable(haptics->regulator);
if (error) {
dev_err(dev, "Failed to disable regulator\n");
- regmap_update_bits(haptics->regmap,
- DRV260X_MODE,
- DRV260X_STANDBY_MASK, 0);
- return error;
+ goto err_leave_standby;
}
}

+ mutex_unlock(&haptics->input_dev->mutex);
return 0;
+
+err_leave_standby:
+ gpiod_set_value(haptics->enable_gpio, 1);
+ fsleep(250);
+ restore_error = regmap_update_bits(haptics->regmap,
+ DRV260X_MODE,
+ DRV260X_STANDBY_MASK, 0);
+ if (restore_error)
+ dev_err(dev, "Failed to leave standby mode: %d\n", restore_error);
+err_enable_work:
+ enable_work(&haptics->work);
+ mutex_unlock(&haptics->input_dev->mutex);
+ return error;
}

static int drv260x_resume(struct device *dev)
@@ -603,28 +616,39 @@ static int drv260x_resume(struct device *dev)
struct drv260x_data *haptics = dev_get_drvdata(dev);
int error;

- guard(mutex)(&haptics->input_dev->mutex);
+ mutex_lock(&haptics->input_dev->mutex);

if (input_device_enabled(haptics->input_dev)) {
error = regulator_enable(haptics->regulator);
if (error) {
dev_err(dev, "Failed to enable regulator\n");
- return error;
+ goto err_enable_work;
}

+ gpiod_set_value(haptics->enable_gpio, 1);
+ fsleep(250);
+
error = regmap_update_bits(haptics->regmap,
DRV260X_MODE,
DRV260X_STANDBY_MASK, 0);
if (error) {
- dev_err(dev, "Failed to unset standby mode\n");
- regulator_disable(haptics->regulator);
- return error;
+ dev_err(dev, "Failed to leave standby mode: %d\n", error);
+ goto err_disable_regulator;
}

- gpiod_set_value(haptics->enable_gpio, 1);
+ enable_work(&haptics->work);
}

+ mutex_unlock(&haptics->input_dev->mutex);
return 0;
+
+err_disable_regulator:
+ gpiod_set_value(haptics->enable_gpio, 0);
+ regulator_disable(haptics->regulator);
+err_enable_work:
+ enable_work(&haptics->work);
+ mutex_unlock(&haptics->input_dev->mutex);
+ return error;
}

static DEFINE_SIMPLE_DEV_PM_OPS(drv260x_pm_ops, drv260x_suspend, drv260x_resume);
--
2.53.0