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

From: Maurizio Casciano

Date: Sat Aug 29 2026 - 18:57:19 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 goto-based error unwinding to balance the
work state.

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 | 38 ++++++++++++++++++++++++++----------
1 file changed, 28 insertions(+), 10 deletions(-)

diff --git a/drivers/input/misc/drv260x.c b/drivers/input/misc/drv260x.c
index 6c5c4c53753b..d8208b3f7645 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);

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,23 @@ 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;
}
}

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);
+ return error;
}

static int drv260x_resume(struct device *dev)
@@ -612,19 +623,26 @@ static int drv260x_resume(struct device *dev)
return error;
}

+ 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);
}

return 0;
+
+err_disable_regulator:
+ gpiod_set_value(haptics->enable_gpio, 0);
+ regulator_disable(haptics->regulator);
+ return error;
}

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