From 99c95c2c53f4ffb036b63469face0e54e14cfc49 Mon Sep 17 00:00:00 2001 From: Rakesh Kota Date: Thu, 19 May 2022 12:17:48 +0530 Subject: [PATCH 1/3] 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 Signed-off-by: Monish Chunara --- drivers/power/supply/bq256xx_charger.c | 77 ++++++++++++++++++++++++++ 1 file changed, 77 insertions(+) diff --git a/drivers/power/supply/bq256xx_charger.c b/drivers/power/supply/bq256xx_charger.c index 967beb3142ff..fdf3defc1880 100644 --- a/drivers/power/supply/bq256xx_charger.c +++ b/drivers/power/supply/bq256xx_charger.c @@ -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, From c8ae80ba3369b6d99e5dfaf050a6693feab01a8a Mon Sep 17 00:00:00 2001 From: Ashish Chavan Date: Thu, 21 Apr 2022 16:48:43 +0530 Subject: [PATCH 2/3] power: BQ256xxx: Add support for debug board detection Disable charging when a debug board is detected. Debug board is reported based on a GPIO state, add support to sample this GPIO and disable charging by setting the VINDP to 5.4V. Change-Id: I8b8a0a833b4f50e24e0223cf9f2510c7e4ec8056 Signed-off-by: Ashish Chavan Signed-off-by: Rakesh Kota Signed-off-by: Monish Chunara --- drivers/power/supply/bq256xx_charger.c | 34 ++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/drivers/power/supply/bq256xx_charger.c b/drivers/power/supply/bq256xx_charger.c index fdf3defc1880..bd1852b4ecf5 100644 --- a/drivers/power/supply/bq256xx_charger.c +++ b/drivers/power/supply/bq256xx_charger.c @@ -18,6 +18,8 @@ #include #include #include +#include +#include #define BQ256XX_MANUFACTURER "Texas Instruments" @@ -145,6 +147,8 @@ #define BQ256XX_REG_RST BIT(7) +#define BQ256XX_MAX_INPUT_VOLTAGE_UV 5400000 + /** * struct bq256xx_init_data - * @ichg: fast charge current @@ -261,6 +265,8 @@ struct bq256xx_device { bool irq_waiting; bool resume_completed; + /* debug_board_gpio to deteect the debug board*/ + int debug_board_gpio; }; /** @@ -1544,6 +1550,30 @@ static int bq256xx_power_supply_init(struct bq256xx_device *bq, return 0; } +static int bq256xx_debug_board_detect(struct bq256xx_device *bq) +{ + int ret = 0; + + if (!of_find_property(bq->dev->of_node, "debugboard-detect-gpio", NULL)) + return ret; + + bq->debug_board_gpio = of_get_named_gpio(bq->dev->of_node, + "debugboard-detect-gpio", 0); + if (IS_ERR(&bq->debug_board_gpio)) { + ret = PTR_ERR(&bq->debug_board_gpio); + dev_err(bq->dev, "Failed to initialize debugboard_detecte gpio\n"); + return ret; + } + gpio_direction_input(bq->debug_board_gpio); + if (gpio_get_value(bq->debug_board_gpio)) { + bq->init_data.vindpm = BQ256XX_MAX_INPUT_VOLTAGE_UV; + dev_info(bq->dev, + "debug_board detected, setting vindpm to %d\n", bq->init_data.vindpm); + } + + return ret; +} + static int bq256xx_hw_init(struct bq256xx_device *bq) { struct power_supply_battery_info *bat_info; @@ -1599,6 +1629,10 @@ static int bq256xx_hw_init(struct bq256xx_device *bq) bat_info->constant_charge_voltage_max_uv; } + ret = bq256xx_debug_board_detect(bq); + if (ret) + return ret; + ret = bq->chip_info->bq256xx_set_vindpm(bq, bq->init_data.vindpm); if (ret) return ret; From 157d08801ebf71740574761e661256758427d378 Mon Sep 17 00:00:00 2001 From: Rakesh Kota Date: Mon, 27 Jun 2022 19:05:41 +0530 Subject: [PATCH 3/3] power: supply: bq256xx: Register interrupt after extcon Interrupt handler send extcon notofication based on POPI. In probe there is possiblity that POPI extcon notification before extcon registation, so moving interrupt registation after extcon registation. Change-Id: If4c3582418d7a487384622c6d0c25384f47e2e64 Signed-off-by: Rakesh Kota Signed-off-by: Monish Chunara --- drivers/power/supply/bq256xx_charger.c | 28 +++++++++++++------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/drivers/power/supply/bq256xx_charger.c b/drivers/power/supply/bq256xx_charger.c index bd1852b4ecf5..753e8e718bd7 100644 --- a/drivers/power/supply/bq256xx_charger.c +++ b/drivers/power/supply/bq256xx_charger.c @@ -1757,20 +1757,6 @@ static int bq256xx_probe(struct i2c_client *client, usb_register_notifier(bq->usb3_phy, &bq->usb_nb); } - if (client->irq) { - ret = devm_request_threaded_irq(dev, client->irq, NULL, - bq256xx_irq_handler_thread, - IRQF_TRIGGER_FALLING | - IRQF_ONESHOT, - dev_name(&client->dev), bq); - if (ret < 0) { - 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); if (ret) { dev_err(dev, "Failed to register power supply\n"); @@ -1805,6 +1791,20 @@ static int bq256xx_probe(struct i2c_client *client, extcon_set_state_sync(bq->extcon, EXTCON_USB, !!state.vbus_gd); + if (client->irq) { + ret = devm_request_threaded_irq(dev, client->irq, NULL, + bq256xx_irq_handler_thread, + IRQF_TRIGGER_FALLING | + IRQF_ONESHOT, + dev_name(&client->dev), bq); + if (ret < 0) { + dev_err(dev, "get irq fail: %d\n", ret); + return ret; + } + + enable_irq_wake(client->irq); + } + dev_dbg(dev, "bq256xx successfully probed. charger=0x%x\n", state.vbus_gd);