summaryrefslogtreecommitdiff
path: root/drivers/iio/adc/qcom-rradc.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/iio/adc/qcom-rradc.c')
-rw-r--r--drivers/iio/adc/qcom-rradc.c136
1 files changed, 131 insertions, 5 deletions
diff --git a/drivers/iio/adc/qcom-rradc.c b/drivers/iio/adc/qcom-rradc.c
index b3aa73f1a5a1..b9e12f795c79 100644
--- a/drivers/iio/adc/qcom-rradc.c
+++ b/drivers/iio/adc/qcom-rradc.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2016-2017, 2020, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -193,7 +193,8 @@
#define FG_RR_ADC_STS_CHANNEL_READING_MASK 0x3
#define FG_RR_ADC_STS_CHANNEL_STS 0x2
-#define FG_RR_CONV_CONTINUOUS_TIME_MIN_MS 50
+#define FG_RR_CONV_CONTINUOUS_TIME_MIN_MS 50
+#define FG_RR_CONV_CONT_CBK_TIME_MIN_MS 10
#define FG_RR_CONV_MAX_RETRY_CNT 50
#define FG_RR_TP_REV_VERSION1 21
#define FG_RR_TP_REV_VERSION2 29
@@ -236,6 +237,11 @@ struct rradc_chip {
struct pmic_revid_data *pmic_fab_id;
int volt;
struct power_supply *usb_trig;
+ struct power_supply *batt_psy;
+ struct power_supply *bms_psy;
+ struct notifier_block nb;
+ bool conv_cbk;
+ struct work_struct psy_notify_work;
};
struct rradc_channels {
@@ -680,6 +686,28 @@ static const struct rradc_channels rradc_chans[] = {
FG_ADC_RR_AUX_THERM_STS)
};
+static bool rradc_is_batt_psy_available(struct rradc_chip *chip)
+{
+ if (!chip->batt_psy)
+ chip->batt_psy = power_supply_get_by_name("battery");
+
+ if (!chip->batt_psy)
+ return false;
+
+ return true;
+}
+
+static bool rradc_is_bms_psy_available(struct rradc_chip *chip)
+{
+ if (!chip->bms_psy)
+ chip->bms_psy = power_supply_get_by_name("bms");
+
+ if (!chip->bms_psy)
+ return false;
+
+ return true;
+}
+
static int rradc_enable_continuous_mode(struct rradc_chip *chip)
{
int rc = 0;
@@ -749,6 +777,7 @@ static int rradc_check_status_ready_with_retry(struct rradc_chip *chip,
struct rradc_chan_prop *prop, u8 *buf, u16 status)
{
int rc = 0, retry_cnt = 0, mask = 0;
+ union power_supply_propval pval = {0, };
switch (prop->channel) {
case RR_ADC_BATT_ID:
@@ -775,7 +804,11 @@ static int rradc_check_status_ready_with_retry(struct rradc_chip *chip,
break;
}
- msleep(FG_RR_CONV_CONTINUOUS_TIME_MIN_MS);
+ if ((chip->conv_cbk) && (prop->channel == RR_ADC_USBIN_V))
+ msleep(FG_RR_CONV_CONT_CBK_TIME_MIN_MS);
+ else
+ msleep(FG_RR_CONV_CONTINUOUS_TIME_MIN_MS);
+
retry_cnt++;
rc = rradc_read(chip, status, buf, 1);
if (rc < 0) {
@@ -784,8 +817,26 @@ static int rradc_check_status_ready_with_retry(struct rradc_chip *chip,
}
}
- if (retry_cnt >= FG_RR_CONV_MAX_RETRY_CNT)
- rc = -ENODATA;
+ if ((retry_cnt >= FG_RR_CONV_MAX_RETRY_CNT) &&
+ ((prop->channel != RR_ADC_DCIN_V) ||
+ (prop->channel != RR_ADC_DCIN_I))) {
+ pr_err("rradc is hung, Proceed to recovery\n");
+ if (rradc_is_bms_psy_available(chip)) {
+ rc = power_supply_set_property(chip->bms_psy,
+ POWER_SUPPLY_PROP_FG_RESET_CLOCK,
+ &pval);
+ if (rc < 0) {
+ pr_err("Couldn't reset FG clock rc=%d\n", rc);
+ return rc;
+ }
+ } else {
+ pr_err("Error obtaining bms power supply\n");
+ rc = -EINVAL;
+ }
+ } else {
+ if (retry_cnt >= FG_RR_CONV_MAX_RETRY_CNT)
+ rc = -ENODATA;
+ }
return rc;
}
@@ -1073,6 +1124,67 @@ static int rradc_read_raw(struct iio_dev *indio_dev,
return rc;
}
+static void psy_notify_work(struct work_struct *work)
+{
+ struct rradc_chip *chip = container_of(work,
+ struct rradc_chip, psy_notify_work);
+
+ struct rradc_chan_prop *prop;
+ union power_supply_propval pval = {0, };
+ u16 adc_code;
+ int rc = 0;
+
+ if (rradc_is_batt_psy_available(chip)) {
+ rc = power_supply_get_property(chip->batt_psy,
+ POWER_SUPPLY_PROP_STATUS, &pval);
+ if (rc < 0)
+ pr_err("Error obtaining battery status, rc=%d\n", rc);
+
+ if (pval.intval == POWER_SUPPLY_STATUS_CHARGING) {
+ chip->conv_cbk = true;
+ prop = &chip->chan_props[RR_ADC_USBIN_V];
+ rc = rradc_do_conversion(chip, prop, &adc_code);
+ if (rc == -ENODATA) {
+ pr_err("rradc is hung, Proceed to recovery\n");
+ if (rradc_is_bms_psy_available(chip)) {
+ rc = power_supply_set_property
+ (chip->bms_psy,
+ POWER_SUPPLY_PROP_FG_RESET_CLOCK,
+ &pval);
+ if (rc < 0)
+ pr_err("Couldn't reset FG clock rc=%d\n",
+ rc);
+ prop = &chip->chan_props[RR_ADC_BATT_ID];
+ rc = rradc_do_conversion(chip, prop,
+ &adc_code);
+ if (rc == -ENODATA)
+ pr_err("RRADC read failed after reset");
+ } else {
+ pr_err("Error obtaining bms power supply");
+ }
+ }
+ }
+ } else {
+ pr_err("Error obtaining battery power supply");
+ }
+ chip->conv_cbk = false;
+ pm_relax(chip->dev);
+}
+
+static int rradc_psy_notifier_cb(struct notifier_block *nb,
+ unsigned long event, void *data)
+{
+ struct power_supply *psy = data;
+ struct rradc_chip *chip = container_of(nb, struct rradc_chip, nb);
+
+ if (strcmp(psy->desc->name, "battery") == 0) {
+ pm_stay_awake(chip->dev);
+ schedule_work(&chip->psy_notify_work);
+ }
+
+ return NOTIFY_OK;
+}
+
static const struct iio_info rradc_info = {
.read_raw = &rradc_read_raw,
.driver_module = THIS_MODULE,
@@ -1184,6 +1296,20 @@ static int rradc_probe(struct platform_device *pdev)
if (!chip->usb_trig)
pr_debug("Error obtaining usb power supply\n");
+ chip->batt_psy = power_supply_get_by_name("battery");
+ if (!chip->batt_psy)
+ pr_debug("Error obtaining battery power supply\n");
+
+ chip->bms_psy = power_supply_get_by_name("bms");
+ if (!chip->bms_psy)
+ pr_debug("Error obtaining bms power supply\n");
+
+ chip->nb.notifier_call = rradc_psy_notifier_cb;
+ rc = power_supply_reg_notifier(&chip->nb);
+ if (rc < 0)
+ pr_err("Error registering psy notifier rc = %d\n", rc);
+ INIT_WORK(&chip->psy_notify_work, psy_notify_work);
+
return devm_iio_device_register(dev, indio_dev);
}