power: qpnp-smb5: Add lock for USBIN VADC reads
The USBIN_V ADC voltage can be read by multiple clients
which could lead to a race while storing/re-storing the
ADC_EN register and end up incorrectly configuring it.
This can lead to the charger ADC channels being completely
disabled and mis-triggering the thermal regulation impacting
ICL.
Fix it by adding a mutex while accessing it.
Change-Id: I6ca2f48dec2ff57e9395bfefc8c843c2e0650442
Signed-off-by: Anirudh Ghayal <aghayal@codeaurora.org>
diff --git a/drivers/power/supply/qcom/qpnp-smb5.c b/drivers/power/supply/qcom/qpnp-smb5.c
index 2a997dd..4239cb7 100644
--- a/drivers/power/supply/qcom/qpnp-smb5.c
+++ b/drivers/power/supply/qcom/qpnp-smb5.c
@@ -490,7 +490,7 @@
static int smb5_get_adc_data(struct smb_charger *chg, int channel,
union power_supply_propval *val)
{
- int rc, ret = 0;
+ int rc = 0;
struct qpnp_vadc_result result;
u8 reg;
@@ -516,6 +516,8 @@
if (IS_ERR(chg->vadc_dev))
return PTR_ERR(chg->vadc_dev);
+ mutex_lock(&chg->vadc_lock);
+
switch (channel) {
case USBIN_VOLTAGE:
/* Store ADC channel config */
@@ -523,7 +525,7 @@
if (rc < 0) {
dev_err(chg->dev,
"Couldn't read ADC config rc=%d\n", rc);
- return rc;
+ goto done;
}
/* Disable all ADC channels except IBAT channel */
@@ -532,44 +534,40 @@
if (rc < 0) {
dev_err(chg->dev,
"Couldn't write ADC config rc=%d\n", rc);
- return rc;
+ goto done;
}
rc = qpnp_vadc_read(chg->vadc_dev, VADC_USB_IN_V_DIV_16_PM5,
&result);
- if (rc < 0) {
+ if (rc < 0)
pr_err("Failed to read USBIN_V over vadc, rc=%d\n", rc);
- ret = rc;
- goto restore;
- }
- val->intval = result.physical;
+ else
+ val->intval = result.physical;
-restore:
/* Restore ADC channel config */
- rc = smblib_write(chg, BATIF_ADC_CHANNEL_EN_REG, reg);
- if (rc < 0) {
+ rc |= smblib_write(chg, BATIF_ADC_CHANNEL_EN_REG, reg);
+ if (rc < 0)
dev_err(chg->dev,
"Couldn't write ADC config rc=%d\n", rc);
- return rc;
- }
- /* If ADC read failed return ADC error */
- if (ret < 0)
- rc = ret;
+
break;
case USBIN_CURRENT:
rc = qpnp_vadc_read(chg->vadc_dev, VADC_USB_IN_I_PM5, &result);
if (rc < 0) {
pr_err("Failed to read USBIN_I over vadc, rc=%d\n", rc);
- return rc;
+ goto done;
}
val->intval = result.physical;
break;
default:
pr_debug("Invalid channel\n");
- return -EINVAL;
+ rc = -EINVAL;
+ break;
}
- return 0;
+done:
+ mutex_unlock(&chg->vadc_lock);
+ return rc;
}
@@ -2602,6 +2600,7 @@
chg->irq_info = smb5_irqs;
chg->die_health = -EINVAL;
chg->otg_present = false;
+ mutex_init(&chg->vadc_lock);
chg->regmap = dev_get_regmap(chg->dev->parent, NULL);
if (!chg->regmap) {