power: bq256xx: Handle interrupt while suspended

The i2c controller device once suspended fails all the
i2c transactions. There is a possibility that an slave i2c
interrupt occurs while the device is suspended and the
interrupt callback does the i2c transactions before the controller
has resumed.

Handle this case by maintaining a suspend/resume flag and handle
the interrupt (i2c transactions) after the device has resumed.

Change-Id: I4a505d17cfe594f70fbeac8020d3116697c0806f
Signed-off-by: Rakesh Kota <quic_kotarake@quicinc.com>
Signed-off-by: Monish Chunara <quic_mchunara@quicinc.com>
This commit is contained in:
Rakesh Kota 2022-05-19 12:17:48 +05:30 committed by Monish Chunara
parent 6f70057701
commit 99c95c2c53

View file

@ -216,6 +216,7 @@ enum bq256xx_id {
* @charger: power supply registered for the charger
* @battery: power supply registered for the battery
* @lock: mutex lock structure
* @irq_lock: mutex lock structure for irq
*
* @usb2_phy: usb_phy identifier
* @usb3_phy: usb_phy identifier
@ -229,6 +230,9 @@ enum bq256xx_id {
* @chip_info: device variant information
* @state: device status and faults
* @watchdog_timer: watchdog timer value in milliseconds
*
* @irq_waiting: flag for status of irq waiting
* @resume_completed: suspend/resume flag
*/
struct bq256xx_device {
struct i2c_client *client;
@ -236,6 +240,8 @@ struct bq256xx_device {
struct power_supply *charger;
struct power_supply *battery;
struct mutex lock;
struct mutex irq_lock;
struct regmap *regmap;
struct usb_phy *usb2_phy;
@ -252,6 +258,9 @@ struct bq256xx_device {
int watchdog_timer;
/* extcon for VBUS / ID notification to USB*/
struct extcon_dev *extcon;
bool irq_waiting;
bool resume_completed;
};
/**
@ -1170,6 +1179,15 @@ static irqreturn_t bq256xx_irq_handler_thread(int irq, void *private)
struct bq256xx_state state;
int ret;
mutex_lock(&bq->irq_lock);
bq->irq_waiting = true;
if (!bq->resume_completed) {
pr_debug("IRQ triggered before device-resume\n");
disable_irq_nosync(irq);
mutex_unlock(&bq->irq_lock);
return IRQ_HANDLED;
}
ret = bq256xx_get_state(bq, &state);
if (ret < 0)
goto irq_out;
@ -1184,6 +1202,8 @@ static irqreturn_t bq256xx_irq_handler_thread(int irq, void *private)
power_supply_changed(bq->charger);
irq_out:
bq->irq_waiting = false;
mutex_unlock(&bq->irq_lock);
return IRQ_HANDLED;
}
@ -1661,8 +1681,10 @@ static int bq256xx_probe(struct i2c_client *client,
bq->client = client;
bq->dev = dev;
bq->chip_info = &bq256xx_chip_info_tbl[id->driver_data];
bq->resume_completed = true;
mutex_init(&bq->lock);
mutex_init(&bq->irq_lock);
strncpy(bq->model_name, id->name, I2C_NAME_SIZE);
@ -1711,6 +1733,8 @@ static int bq256xx_probe(struct i2c_client *client,
dev_err(dev, "get irq fail: %d\n", ret);
return ret;
}
enable_irq_wake(client->irq);
}
ret = bq256xx_power_supply_init(bq, &psy_cfg, dev);
@ -1747,6 +1771,9 @@ static int bq256xx_probe(struct i2c_client *client,
extcon_set_state_sync(bq->extcon, EXTCON_USB, !!state.vbus_gd);
dev_dbg(dev, "bq256xx successfully probed. charger=0x%x\n",
state.vbus_gd);
return ret;
}
@ -1786,11 +1813,61 @@ static const struct acpi_device_id bq256xx_acpi_match[] = {
};
MODULE_DEVICE_TABLE(acpi, bq256xx_acpi_match);
static int bq256xx_suspend(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
struct bq256xx_device *bq = i2c_get_clientdata(client);
mutex_lock(&bq->irq_lock);
bq->resume_completed = false;
mutex_unlock(&bq->irq_lock);
return 0;
}
static int bq256xx_suspend_noirq(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
struct bq256xx_device *bq = i2c_get_clientdata(client);
if (bq->irq_waiting) {
dev_err_ratelimited(dev, "Aborting suspend, an interrupt was detected while suspending\n");
return -EBUSY;
}
return 0;
}
static int bq256xx_resume(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
struct bq256xx_device *bq = i2c_get_clientdata(client);
mutex_lock(&bq->irq_lock);
bq->resume_completed = true;
mutex_unlock(&bq->irq_lock);
if (bq->irq_waiting) {
/* irq was pending, call the handler */
bq256xx_irq_handler_thread(client->irq, bq);
enable_irq(client->irq);
}
return 0;
}
static const struct dev_pm_ops bq256xx_pm_ops = {
.suspend = bq256xx_suspend,
.suspend_noirq = bq256xx_suspend_noirq,
.resume = bq256xx_resume,
};
static struct i2c_driver bq256xx_driver = {
.driver = {
.name = "bq256xx-charger",
.of_match_table = bq256xx_of_match,
.acpi_match_table = bq256xx_acpi_match,
.pm = &bq256xx_pm_ops,
},
.probe = bq256xx_probe,
.id_table = bq256xx_i2c_ids,