diff options
Diffstat (limited to 'drivers/iio/adc/qcom-rradc.c')
-rw-r--r-- | drivers/iio/adc/qcom-rradc.c | 136 |
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); } |