diff --git a/drivers/power/supply/Kconfig b/drivers/power/supply/Kconfig index 2bc09d92a310..45ff11afd8c4 100644 --- a/drivers/power/supply/Kconfig +++ b/drivers/power/supply/Kconfig @@ -723,4 +723,6 @@ config CHARGER_WILCO information can be found in Documentation/ABI/testing/sysfs-class-power-wilco +source "drivers/power/supply/qcom/Kconfig" + endif # POWER_SUPPLY diff --git a/drivers/power/supply/qcom/Kconfig b/drivers/power/supply/qcom/Kconfig index 4a52ec19c287..b35b496918e3 100644 --- a/drivers/power/supply/qcom/Kconfig +++ b/drivers/power/supply/qcom/Kconfig @@ -1,5 +1,4 @@ # SPDX-License-Identifier: GPL-2.0-only - menuconfig QCOM_POWER_SUPPLY bool "Support for Qualcomm Technologies, Inc. power supply" depends on ARCH_QCOM @@ -7,14 +6,18 @@ menuconfig QCOM_POWER_SUPPLY if QCOM_POWER_SUPPLY config QPNP_QG - bool "QPNP Qgauge driver" + tristate "QPNP Qgauge driver" depends on MFD_SPMI_PMIC + depends on IIO help Say Y here to enable the Qualcomm Technologies, Inc. QGauge driver which uses the periodic sampling of the battery voltage and current to determine the battery state-of-charge (SOC) and supports other battery management features. + To compile this driver as a module, choose M here: the + module will be called qcom-qpnp-qg. + config QPNP_SMB5 tristate "SMB5 Battery Charger" depends on MFD_SPMI_PMIC && IIO diff --git a/drivers/power/supply/qcom/Makefile b/drivers/power/supply/qcom/Makefile index f9feade452fd..d6cc8c45bc4b 100644 --- a/drivers/power/supply/qcom/Makefile +++ b/drivers/power/supply/qcom/Makefile @@ -2,6 +2,7 @@ obj-$(CONFIG_QPNP_SMB5) += qpnp-smb5-main.o qpnp-smb5-main-y += step-chg-jeita.o battery.o qpnp-smb5.o smb5-lib.o pmic-voter.o storm-watch.o schgm-flash.o battery-profile-loader.o smb5-iio.o -obj-$(CONFIG_QPNP_QG) += qpnp-qg.o battery-profile-loader.o pmic-voter.o qg-util.o qg-soc.o qg-sdam.o qg-battery-profile.o qg-profile-lib.o fg-alg.o +obj-$(CONFIG_QPNP_QG) += qcom-qpnp-qg.o +qcom-qpnp-qg-y += qpnp-qg.o battery-profile-loader.o pmic-voter.o qg-util.o qg-soc.o qg-sdam.o qg-battery-profile.o qg-profile-lib.o fg-alg.o obj-$(CONFIG_SMB1398_CHARGER) += smb1398-charger.o pmic-voter.o obj-$(CONFIG_SMB1355_SLAVE_CHARGER) += smb1355-charger.o pmic-voter.o diff --git a/drivers/power/supply/qcom/fg-alg.c b/drivers/power/supply/qcom/fg-alg.c index 24a009d4796f..6f8916d55f8e 100644 --- a/drivers/power/supply/qcom/fg-alg.c +++ b/drivers/power/supply/qcom/fg-alg.c @@ -6,11 +6,14 @@ #define pr_fmt(fmt) "ALG: %s: " fmt, __func__ #include +#include #include #include #include +#include #include #include +#include #include "fg-alg.h" #define FULL_SOC_RAW 255 @@ -801,6 +804,18 @@ int cap_learning_init(struct cap_learning *cl) /* SOH based profile loading */ +static int write_int_iio_chan(struct iio_channel *iio_chan_list, + int chan_id, int val) +{ + do { + if (iio_chan_list->channel->channel == chan_id) + return iio_write_channel_raw(iio_chan_list, + val); + } while (iio_chan_list++); + + return -ENOENT; +} + /** * soh_get_batt_age_level - * @sp: SOH profile object @@ -834,10 +849,9 @@ static int soh_get_batt_age_level(struct soh_profile *sp, int soh, */ int soh_profile_update(struct soh_profile *sp, int new_soh) { - union power_supply_propval pval = {0, }; int rc, batt_age_level = 0; - if (!sp || !sp->bms_psy) + if (!sp || !sp->bms_psy || !sp->iio_chan_list) return -ENODEV; if (new_soh <= 0) @@ -855,9 +869,8 @@ int soh_profile_update(struct soh_profile *sp, int new_soh) return rc; if (batt_age_level != sp->last_batt_age_level) { - pval.intval = batt_age_level; - rc = power_supply_set_property(sp->bms_psy, - POWER_SUPPLY_PROP_BATT_AGE_LEVEL, &pval); + rc = write_int_iio_chan(sp->iio_chan_list, + PSY_IIO_BATT_AGE_LEVEL, batt_age_level); if (rc < 0) { pr_err("Couldn't set batt_age_level rc=%d\n", rc); return rc; @@ -886,7 +899,8 @@ int soh_profile_init(struct device *dev, struct soh_profile *sp) { int rc, profile_count = 0; - if (!dev || !sp || !sp->bp_node || !sp->bms_psy) + if (!dev || !sp || !sp->bp_node || !sp->bms_psy || + !sp->iio_chan_list) return -ENODEV; rc = of_batterydata_get_aged_profile_count(sp->bp_node, @@ -1199,7 +1213,7 @@ static int get_time_to_full_locked(struct ttf *ttf, int *val) pr_debug("TTF: i_cc2cv=%d\n", i_cc2cv); /* if we are already in CV state then we can skip estimating CC */ - if (charge_type == POWER_SUPPLY_CHARGE_TYPE_TAPER) + if (charge_type == QTI_POWER_SUPPLY_CHARGE_TYPE_TAPER) goto cv_estimate; /* estimated SOC at the CC to CV transition */ @@ -1329,14 +1343,14 @@ static int get_time_to_full_locked(struct ttf *ttf, int *val) cv_estimate: pr_debug("TTF: t_predicted_cc=%d\n", t_predicted); - if (charge_type == POWER_SUPPLY_CHARGE_TYPE_TAPER) + if (charge_type == QTI_POWER_SUPPLY_CHARGE_TYPE_TAPER) iterm = max(100, abs(iterm)); else iterm = max(100, abs(iterm) + ttf->iterm_delta); pr_debug("TTF: iterm=%d\n", iterm); - if (charge_type == POWER_SUPPLY_CHARGE_TYPE_TAPER) + if (charge_type == QTI_POWER_SUPPLY_CHARGE_TYPE_TAPER) tau = max(MILLI_UNIT, ibatt_avg * MILLI_UNIT / iterm); else tau = max(MILLI_UNIT, i_cc2cv * MILLI_UNIT / iterm); diff --git a/drivers/power/supply/qcom/fg-alg.h b/drivers/power/supply/qcom/fg-alg.h index 99626f671152..8e20d77fa5ed 100644 --- a/drivers/power/supply/qcom/fg-alg.h +++ b/drivers/power/supply/qcom/fg-alg.h @@ -138,6 +138,7 @@ struct ttf { struct soh_profile { struct device_node *bp_node; struct power_supply *bms_psy; + struct iio_channel *iio_chan_list; struct soh_range *soh_data; int batt_id_kohms; int profile_count; @@ -166,5 +167,4 @@ int ttf_get_time_to_full(struct ttf *ttf, int *val); int ttf_tte_init(struct ttf *ttf); int soh_profile_init(struct device *dev, struct soh_profile *sp); int soh_profile_update(struct soh_profile *sp, int soh); - #endif diff --git a/drivers/power/supply/qcom/qg-core.h b/drivers/power/supply/qcom/qg-core.h index 858bd196b1fe..0a1ecfb754bd 100644 --- a/drivers/power/supply/qcom/qg-core.h +++ b/drivers/power/supply/qcom/qg-core.h @@ -10,6 +10,11 @@ #include "fg-alg.h" #include "qg-defs.h" +struct qg_config { + u32 qg_version; + u32 pmic_version; +}; + struct qg_batt_props { const char *batt_type_str; int float_volt_uv; @@ -86,11 +91,14 @@ struct qg_esr_data { struct qpnp_qg { struct device *dev; - struct pmic_revid_data *pmic_rev_id; struct regmap *regmap; struct qpnp_vadc_chip *vadc_dev; struct soh_profile *sp; struct power_supply *qg_psy; + struct iio_dev *indio_dev; + struct iio_chan_spec *iio_chan; + struct iio_channel *int_iio_chans; + struct iio_channel **ext_iio_chans; struct class *qg_class; struct device *qg_device; struct cdev qg_cdev; @@ -127,6 +135,7 @@ struct qpnp_qg { /* status variable */ u32 *debug_mask; u32 qg_version; + u32 pmic_version; bool qg_device_open; bool profile_loaded; bool battery_missing; @@ -263,6 +272,13 @@ enum qg_version { QG_LITE, }; +enum pmic_version { + PM2250, + PM6150, + PMI632, + PM7250B, +}; + enum qg_mode { QG_V_I_MODE, QG_V_MODE, diff --git a/drivers/power/supply/qcom/qg-iio.h b/drivers/power/supply/qcom/qg-iio.h new file mode 100644 index 000000000000..6cf4f8ac6194 --- /dev/null +++ b/drivers/power/supply/qcom/qg-iio.h @@ -0,0 +1,121 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2020 The Linux Foundation. All rights reserved. + */ + +#ifndef __QG_IIO_H +#define __QG_IIO_H + +#include +#include + +struct qg_iio_channels { + const char *datasheet_name; + int channel_num; + enum iio_chan_type type; + long info_mask; +}; + +#define QG_IIO_CHAN(_name, _num, _type, _mask) \ + { \ + .datasheet_name = _name, \ + .channel_num = _num, \ + .type = _type, \ + .info_mask = _mask, \ + }, + +#define QG_CHAN_VOLT(_name, _num) \ + QG_IIO_CHAN(_name, _num, IIO_VOLTAGE, \ + BIT(IIO_CHAN_INFO_PROCESSED)) + +#define QG_CHAN_CUR(_name, _num) \ + QG_IIO_CHAN(_name, _num, IIO_CURRENT, \ + BIT(IIO_CHAN_INFO_PROCESSED)) + +#define QG_CHAN_RES(_name, _num) \ + QG_IIO_CHAN(_name, _num, IIO_RESISTANCE, \ + BIT(IIO_CHAN_INFO_PROCESSED)) + +#define QG_CHAN_TEMP(_name, _num) \ + QG_IIO_CHAN(_name, _num, IIO_TEMP, \ + BIT(IIO_CHAN_INFO_PROCESSED)) + +#define QG_CHAN_POW(_name, _num) \ + QG_IIO_CHAN(_name, _num, IIO_POWER, \ + BIT(IIO_CHAN_INFO_PROCESSED)) + +#define QG_CHAN_ENERGY(_name, _num) \ + QG_IIO_CHAN(_name, _num, IIO_ENERGY, \ + BIT(IIO_CHAN_INFO_PROCESSED)) + +#define QG_CHAN_INDEX(_name, _num) \ + QG_IIO_CHAN(_name, _num, IIO_INDEX, \ + BIT(IIO_CHAN_INFO_PROCESSED)) + +#define QG_CHAN_ACT(_name, _num) \ + QG_IIO_CHAN(_name, _num, IIO_ACTIVITY, \ + BIT(IIO_CHAN_INFO_PROCESSED)) + +#define QG_CHAN_TSTAMP(_name, _num) \ + QG_IIO_CHAN(_name, _num, IIO_TIMESTAMP, \ + BIT(IIO_CHAN_INFO_PROCESSED)) + +#define QG_CHAN_COUNT(_name, _num) \ + QG_IIO_CHAN(_name, _num, IIO_COUNT, \ + BIT(IIO_CHAN_INFO_PROCESSED)) + +static const struct qg_iio_channels qg_iio_psy_channels[] = { + QG_CHAN_ENERGY("capacity", PSY_IIO_CAPACITY) + QG_CHAN_ENERGY("capacity_raw", PSY_IIO_CAPACITY_RAW) + QG_CHAN_ENERGY("real_capacity", PSY_IIO_REAL_CAPACITY) + QG_CHAN_TEMP("temp", PSY_IIO_TEMP) + QG_CHAN_VOLT("voltage_now", PSY_IIO_VOLTAGE_NOW) + QG_CHAN_VOLT("voltage_ocv", PSY_IIO_VOLTAGE_OCV) + QG_CHAN_CUR("current_now", PSY_IIO_CURRENT_NOW) + QG_CHAN_ENERGY("charge_counter", PSY_IIO_CHARGE_COUNTER) + QG_CHAN_RES("resistance", PSY_IIO_RESISTANCE) + QG_CHAN_RES("resistance_id", PSY_IIO_RESISTANCE_ID) + QG_CHAN_ACT("soc_reporting_ready", PSY_IIO_SOC_REPORTING_READY) + QG_CHAN_RES("resistance_capacitive", PSY_IIO_RESISTANCE_CAPACITIVE) + QG_CHAN_INDEX("debug_battery", PSY_IIO_DEBUG_BATTERY) + QG_CHAN_VOLT("voltage_min", PSY_IIO_VOLTAGE_MIN) + QG_CHAN_VOLT("voltage_max", PSY_IIO_VOLTAGE_MAX) + QG_CHAN_CUR("batt_full_current", PSY_IIO_BATT_FULL_CURRENT) + QG_CHAN_INDEX("batt_profile_version", PSY_IIO_BATT_PROFILE_VERSION) + QG_CHAN_COUNT("cycle_count", PSY_IIO_CYCLE_COUNT) + QG_CHAN_ENERGY("charge_full", PSY_IIO_CHARGE_FULL) + QG_CHAN_ENERGY("charge_full_design", PSY_IIO_CHARGE_FULL_DESIGN) + QG_CHAN_TSTAMP("time_to_full_avg", PSY_IIO_TIME_TO_FULL_AVG) + QG_CHAN_TSTAMP("time_to_full_now", PSY_IIO_TIME_TO_FULL_NOW) + QG_CHAN_TSTAMP("time_to_empty_avg", PSY_IIO_TIME_TO_EMPTY_AVG) + QG_CHAN_RES("esr_actual", PSY_IIO_ESR_ACTUAL) + QG_CHAN_RES("esr_nominal", PSY_IIO_ESR_NOMINAL) + QG_CHAN_INDEX("soh", PSY_IIO_SOH) + QG_CHAN_ENERGY("cc_soc", PSY_IIO_CC_SOC) + QG_CHAN_ACT("fg_reset", PSY_IIO_FG_RESET) + QG_CHAN_VOLT("voltage_avg", PSY_IIO_VOLTAGE_AVG) + QG_CHAN_CUR("current_avg", PSY_IIO_CURRENT_AVG) + QG_CHAN_POW("power_avg", PSY_IIO_POWER_AVG) + QG_CHAN_POW("power_now", PSY_IIO_POWER_NOW) + QG_CHAN_ACT("scale_mode_en", PSY_IIO_SCALE_MODE_EN) + QG_CHAN_INDEX("batt_age_level", PSY_IIO_BATT_AGE_LEVEL) + QG_CHAN_ACT("fg_type", PSY_IIO_FG_TYPE) +}; + +enum qg_ext_iio_channels { + INPUT_CURRENT_LIMITED = 0, + RECHARGE_SOC, + FORCE_RECHARGE, + CHARGE_DONE, + PARALLEL_CHARGING_ENABLED, +}; + +static const char * const qg_ext_iio_chan_name[] = { + [INPUT_CURRENT_LIMITED] = "input_current_limited", + [RECHARGE_SOC] = "recharge_soc", + [FORCE_RECHARGE] = "force_recharge", + [CHARGE_DONE] = "charge_done", + [PARALLEL_CHARGING_ENABLED] = "parallel_charging_enabled", +}; + +#endif diff --git a/drivers/power/supply/qcom/qg-soc.c b/drivers/power/supply/qcom/qg-soc.c index ad57f2dfb694..4682cc0bec24 100644 --- a/drivers/power/supply/qcom/qg-soc.c +++ b/drivers/power/supply/qcom/qg-soc.c @@ -6,6 +6,7 @@ #define pr_fmt(fmt) "QG-K: %s: " fmt, __func__ #include +#include #include #include #include @@ -14,6 +15,7 @@ #include "fg-alg.h" #include "qg-sdam.h" #include "qg-core.h" +#include "qg-iio.h" #include "qg-reg.h" #include "qg-util.h" #include "qg-defs.h" @@ -236,7 +238,7 @@ static int qg_process_tcss_soc(struct qpnp_qg *chip, int sys_soc) { int rc, ibatt_diff = 0, ibat_inc_hyst = 0; int qg_iterm_ua = (-1 * chip->dt.iterm_ma * 1000); - int soc_ibat, wt_ibat, wt_sys; + int soc_ibat, wt_ibat, wt_sys, val; union power_supply_propval prop = {0, }; if (!chip->dt.tcss_enable && !(qg_ss_feature & QG_TCSS)) @@ -267,9 +269,8 @@ static int qg_process_tcss_soc(struct qpnp_qg *chip, int sys_soc) chip->tcss_active = true; } - rc = power_supply_get_property(chip->batt_psy, - POWER_SUPPLY_PROP_INPUT_CURRENT_LIMITED, &prop); - if (!rc && prop.intval) { + rc = qg_read_iio_chan(chip, INPUT_CURRENT_LIMITED, &val); + if (!rc && val) { qg_dbg(chip, QG_DEBUG_SOC, "Input limited sys_soc=%d soc_tcss=%d\n", sys_soc, chip->soc_tcss); diff --git a/drivers/power/supply/qcom/qg-util.c b/drivers/power/supply/qcom/qg-util.c index 170ca876d9b7..447d623947a5 100644 --- a/drivers/power/supply/qcom/qg-util.c +++ b/drivers/power/supply/qcom/qg-util.c @@ -16,6 +16,7 @@ #include "qg-core.h" #include "qg-reg.h" #include "qg-defs.h" +#include "qg-iio.h" #include "qg-util.h" static inline bool is_sticky_register(u32 addr) @@ -309,26 +310,20 @@ bool is_input_present(struct qpnp_qg *chip) static bool is_parallel_available(struct qpnp_qg *chip) { - if (chip->parallel_psy) + if (is_chan_valid(chip, PARALLEL_CHARGING_ENABLED)) return true; - chip->parallel_psy = power_supply_get_by_name("parallel"); - if (!chip->parallel_psy) - return false; - - return true; + return false; } bool is_parallel_enabled(struct qpnp_qg *chip) { - union power_supply_propval pval = {0, }; + int val = 0; - if (is_parallel_available(chip)) { - power_supply_get_property(chip->parallel_psy, - POWER_SUPPLY_PROP_CHARGING_ENABLED, &pval); - } + if (is_parallel_available(chip)) + qg_read_iio_chan(chip, PARALLEL_CHARGING_ENABLED, &val); - return pval.intval ? true : false; + return val ? true : false; } int qg_write_monotonic_soc(struct qpnp_qg *chip, int msoc) @@ -469,3 +464,140 @@ int qg_get_ibat_avg(struct qpnp_qg *chip, int *ibat_ua) return 0; } + +bool is_chan_valid(struct qpnp_qg *chip, + enum qg_ext_iio_channels chan) +{ + int rc; + + if (IS_ERR(chip->ext_iio_chans[chan])) + return false; + + if (!chip->ext_iio_chans[chan]) { + chip->ext_iio_chans[chan] = iio_channel_get(chip->dev, + qg_ext_iio_chan_name[chan]); + if (IS_ERR(chip->ext_iio_chans[chan])) { + rc = PTR_ERR(chip->ext_iio_chans[chan]); + if (rc == -EPROBE_DEFER) + chip->ext_iio_chans[chan] = NULL; + + pr_err("Failed to get IIO channel %s, rc=%d\n", + qg_ext_iio_chan_name[chan], rc); + return false; + } + } + + return true; +} + +int qg_read_iio_chan(struct qpnp_qg *chip, + enum qg_ext_iio_channels chan, int *val) +{ + int rc; + + if (is_chan_valid(chip, chan)) { + rc = iio_read_channel_processed( + chip->ext_iio_chans[chan], val); + return (rc < 0) ? rc : 0; + } + + return -EINVAL; +} + +int qg_write_iio_chan(struct qpnp_qg *chip, + enum qg_ext_iio_channels chan, int val) +{ + if (is_chan_valid(chip, chan)) + return iio_write_channel_raw(chip->ext_iio_chans[chan], + val); + + return -EINVAL; +} + +int qg_read_int_iio_chan(struct iio_channel *iio_chan_list, int chan_id, + int *val) +{ + int rc; + + do { + if (iio_chan_list->channel->channel == chan_id) { + rc = iio_read_channel_processed(iio_chan_list, + val); + return (rc < 0) ? rc : 0; + } + } while (iio_chan_list++); + + return -ENOENT; +} + +int qg_read_range_data_from_node(struct device_node *node, + const char *prop_str, struct range_data *ranges, + int max_threshold, u32 max_value) +{ + int rc = 0, i, length, per_tuple_length, tuples; + + if (!node || !prop_str || !ranges) { + pr_err("Invalid parameters passed\n"); + return -EINVAL; + } + + rc = of_property_count_elems_of_size(node, prop_str, sizeof(u32)); + if (rc < 0) { + pr_err("Count %s failed, rc=%d\n", prop_str, rc); + return rc; + } + + length = rc; + per_tuple_length = sizeof(struct range_data) / sizeof(u32); + if (length % per_tuple_length) { + pr_err("%s length (%d) should be multiple of %d\n", + prop_str, length, per_tuple_length); + return -EINVAL; + } + tuples = length / per_tuple_length; + + if (tuples > MAX_STEP_CHG_ENTRIES) { + pr_err("too many entries(%d), only %d allowed\n", + tuples, MAX_STEP_CHG_ENTRIES); + return -EINVAL; + } + + rc = of_property_read_u32_array(node, prop_str, + (u32 *)ranges, length); + if (rc) { + pr_err("Read %s failed, rc=%d\n", prop_str, rc); + return rc; + } + + for (i = 0; i < tuples; i++) { + if (ranges[i].low_threshold > + ranges[i].high_threshold) { + pr_err("%s thresholds should be in ascendant ranges\n", + prop_str); + rc = -EINVAL; + goto clean; + } + + if (i != 0) { + if (ranges[i - 1].high_threshold > + ranges[i].low_threshold) { + pr_err("%s thresholds should be in ascendant ranges\n", + prop_str); + rc = -EINVAL; + goto clean; + } + } + + if (ranges[i].low_threshold > max_threshold) + ranges[i].low_threshold = max_threshold; + if (ranges[i].high_threshold > max_threshold) + ranges[i].high_threshold = max_threshold; + if (ranges[i].value > max_value) + ranges[i].value = max_value; + } + + return rc; +clean: + memset(ranges, 0, tuples * sizeof(struct range_data)); + return rc; +} diff --git a/drivers/power/supply/qcom/qg-util.h b/drivers/power/supply/qcom/qg-util.h index c72f0e5c503b..5f4a71dcebe8 100644 --- a/drivers/power/supply/qcom/qg-util.h +++ b/drivers/power/supply/qcom/qg-util.h @@ -6,6 +6,8 @@ #ifndef __QG_UTIL_H__ #define __QG_UTIL_H__ +#define MAX_STEP_CHG_ENTRIES 8 + int qg_read(struct qpnp_qg *chip, u32 addr, u8 *val, int len); int qg_write(struct qpnp_qg *chip, u32 addr, u8 *val, int len); int qg_masked_write(struct qpnp_qg *chip, int addr, u32 mask, u32 val); @@ -26,5 +28,14 @@ int qg_get_battery_voltage(struct qpnp_qg *chip, int *vbat_uv); int qg_get_vbat_avg(struct qpnp_qg *chip, int *vbat_uv); s64 qg_iraw_to_ua(struct qpnp_qg *chip, int iraw); int qg_get_ibat_avg(struct qpnp_qg *chip, int *ibat_ua); - +bool is_chan_valid(struct qpnp_qg *chip, enum qg_ext_iio_channels chan); +int qg_read_iio_chan(struct qpnp_qg *chip, + enum qg_ext_iio_channels chan, int *val); +int qg_write_iio_chan(struct qpnp_qg *chip, + enum qg_ext_iio_channels chan, int val); +int qg_read_int_iio_chan(struct iio_channel *iio_chan_list, int chan_id, + int *val); +int qg_read_range_data_from_node(struct device_node *node, + const char *prop_str, struct range_data *ranges, + int max_threshold, u32 max_value); #endif diff --git a/drivers/power/supply/qcom/qpnp-qg.c b/drivers/power/supply/qcom/qpnp-qg.c index e4aa5f0c7763..2f6ab4aa4c15 100644 --- a/drivers/power/supply/qcom/qpnp-qg.c +++ b/drivers/power/supply/qcom/qpnp-qg.c @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -23,13 +24,15 @@ #include #include #include +#include #include -#include +#include #include #include #include "fg-alg.h" #include "qg-sdam.h" #include "qg-core.h" +#include "qg-iio.h" #include "qg-reg.h" #include "qg-util.h" #include "qg-soc.h" @@ -37,6 +40,17 @@ #include "qg-defs.h" #include "battery-profile-loader.h" +static const struct qg_config config[] = { + [PM2250] = {QG_LITE, PM2250}, + [PM6150] = {QG_PMIC5, PM6150}, + [PMI632] = {QG_PMIC5, PMI632}, + [PM7250B] = {QG_PMIC5, PM7250B}, +}; + +static const char *qg_get_battery_type(struct qpnp_qg *chip); +static int qg_process_rt_fifo(struct qpnp_qg *chip); +static int qg_load_battery_profile(struct qpnp_qg *chip); + static int qg_debug_mask; static int qg_esr_mod_count = 30; @@ -81,6 +95,17 @@ static ssize_t esr_count_store(struct device *dev, struct device_attribute } static DEVICE_ATTR_RW(esr_count); +static ssize_t battery_type_show(struct device *dev, + struct device_attribute + *attr, char *buf) +{ + struct qpnp_qg *chip = dev_get_drvdata(dev); + + return scnprintf(buf, PAGE_SIZE, "%s\n", + qg_get_battery_type(chip)); +} +static DEVICE_ATTR_RO(battery_type); + static struct attribute *qg_attrs[] = { &dev_attr_esr_mod_count.attr, &dev_attr_esr_count.attr, @@ -90,13 +115,11 @@ static struct attribute *qg_attrs[] = { &dev_attr_fvss_delta_soc_interval_ms.attr, &dev_attr_fvss_vbat_scaling.attr, &dev_attr_qg_ss_feature.attr, + &dev_attr_battery_type.attr, NULL, }; ATTRIBUTE_GROUPS(qg); -static int qg_process_rt_fifo(struct qpnp_qg *chip); -static int qg_load_battery_profile(struct qpnp_qg *chip); - static bool is_battery_present(struct qpnp_qg *chip) { bool present = true; @@ -247,13 +270,6 @@ static void qg_notify_charger(struct qpnp_qg *chip) if (!chip->batt_psy) return; - if (is_debug_batt_id(chip)) { - prop.intval = 1; - power_supply_set_property(chip->batt_psy, - POWER_SUPPLY_PROP_DEBUG_BATTERY, &prop); - return; - } - if (!chip->profile_loaded) return; @@ -1850,7 +1866,6 @@ static int qg_get_power(struct qpnp_qg *chip, int *val, bool average) static int qg_get_ttf_param(void *data, enum ttf_param param, int *val) { - union power_supply_propval prop = {0, }; struct qpnp_qg *chip = data; int rc = 0; int64_t temp = 0; @@ -1874,14 +1889,15 @@ static int qg_get_ttf_param(void *data, enum ttf_param param, int *val) rc = qg_get_battery_current(chip, val); break; case TTF_FCC: - if (chip->qg_psy) { - rc = power_supply_get_property(chip->qg_psy, - POWER_SUPPLY_PROP_CHARGE_FULL, &prop); - if (rc >= 0) { - temp = div64_u64(prop.intval, 1000); - *val = div64_u64(chip->full_soc * temp, - QG_SOC_FULL); - } + if (!chip->dt.cl_disable && chip->dt.cl_feedback_on) + rc = qg_get_learned_capacity(chip, &temp); + else + rc = qg_get_nominal_capacity((int *)&temp, 250, + true); + if (!rc) { + temp = div64_u64(temp, 1000); + *val = div64_u64(chip->full_soc * temp, + QG_SOC_FULL); } break; case TTF_MODE: @@ -2067,15 +2083,15 @@ static int qg_setprop_batt_age_level(struct qpnp_qg *chip, int batt_age_level) return rc; } -static int qg_psy_set_property(struct power_supply *psy, - enum power_supply_property psp, - const union power_supply_propval *pval) +static int qg_iio_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int val1, + int val2, long mask) { - struct qpnp_qg *chip = power_supply_get_drvdata(psy); + struct qpnp_qg *chip = iio_priv(indio_dev); int rc = 0; - switch (psp) { - case POWER_SUPPLY_PROP_CHARGE_FULL: + switch (chan->channel) { + case PSY_IIO_CHARGE_FULL: if (chip->dt.cl_disable) { pr_warn("Capacity learning disabled!\n"); return 0; @@ -2084,246 +2100,209 @@ static int qg_psy_set_property(struct power_supply *psy, pr_warn("Capacity learning active!\n"); return 0; } - if (pval->intval <= 0 || pval->intval > chip->cl->nom_cap_uah) { + if (val1 <= 0 || val1 > chip->cl->nom_cap_uah) { pr_err("charge_full is out of bounds\n"); return -EINVAL; } mutex_lock(&chip->cl->lock); - rc = qg_store_learned_capacity(chip, pval->intval); + rc = qg_store_learned_capacity(chip, val1); if (!rc) - chip->cl->learned_cap_uah = pval->intval; + chip->cl->learned_cap_uah = val1; mutex_unlock(&chip->cl->lock); break; - case POWER_SUPPLY_PROP_SOH: - chip->soh = pval->intval; + case PSY_IIO_SOH: + chip->soh = val1; qg_dbg(chip, QG_DEBUG_STATUS, "SOH update: SOH=%d esr_actual=%d esr_nominal=%d\n", chip->soh, chip->esr_actual, chip->esr_nominal); if (chip->sp) soh_profile_update(chip->sp, chip->soh); break; - case POWER_SUPPLY_PROP_ESR_ACTUAL: - chip->esr_actual = pval->intval; + case PSY_IIO_ESR_ACTUAL: + chip->esr_actual = val1; break; - case POWER_SUPPLY_PROP_ESR_NOMINAL: - chip->esr_nominal = pval->intval; + case PSY_IIO_ESR_NOMINAL: + chip->esr_nominal = val1; break; - case POWER_SUPPLY_PROP_FG_RESET: + case PSY_IIO_FG_RESET: qg_reset(chip); break; - case POWER_SUPPLY_PROP_BATT_AGE_LEVEL: - rc = qg_setprop_batt_age_level(chip, pval->intval); + case PSY_IIO_BATT_AGE_LEVEL: + rc = qg_setprop_batt_age_level(chip, val1); break; default: + pr_err("Unsupported QG IIO chan %d\n", chan->channel); + rc = -EINVAL; break; } - return 0; + + if (rc < 0) + pr_err("Couldn't write IIO channel %d, rc = %d\n", + chan->channel, rc); + + return rc; } -static int qg_psy_get_property(struct power_supply *psy, - enum power_supply_property psp, - union power_supply_propval *pval) +static int qg_iio_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int *val1, + int *val2, long mask) { - struct qpnp_qg *chip = power_supply_get_drvdata(psy); - int rc = 0; + struct qpnp_qg *chip = iio_priv(indio_dev); int64_t temp = 0; + int rc = 0; - pval->intval = 0; + *val1 = 0; - switch (psp) { - case POWER_SUPPLY_PROP_CAPACITY: - rc = qg_get_battery_capacity(chip, &pval->intval); + switch (chan->channel) { + case PSY_IIO_CAPACITY: + rc = qg_get_battery_capacity(chip, val1); break; - case POWER_SUPPLY_PROP_CAPACITY_RAW: - pval->intval = chip->sys_soc; + case PSY_IIO_CAPACITY_RAW: + *val1 = chip->sys_soc; break; - case POWER_SUPPLY_PROP_REAL_CAPACITY: - rc = qg_get_battery_capacity_real(chip, &pval->intval); + case PSY_IIO_REAL_CAPACITY: + rc = qg_get_battery_capacity_real(chip, val1); break; - case POWER_SUPPLY_PROP_VOLTAGE_NOW: - rc = qg_get_battery_voltage(chip, &pval->intval); + case PSY_IIO_VOLTAGE_NOW: + rc = qg_get_battery_voltage(chip, val1); break; - case POWER_SUPPLY_PROP_CURRENT_NOW: - rc = qg_get_battery_current(chip, &pval->intval); + case PSY_IIO_CURRENT_NOW: + rc = qg_get_battery_current(chip, val1); break; - case POWER_SUPPLY_PROP_VOLTAGE_OCV: - rc = qg_sdam_read(SDAM_OCV_UV, &pval->intval); + case PSY_IIO_VOLTAGE_OCV: + rc = qg_sdam_read(SDAM_OCV_UV, val1); break; - case POWER_SUPPLY_PROP_TEMP: - rc = qg_get_battery_temp(chip, &pval->intval); + case PSY_IIO_TEMP: + rc = qg_get_battery_temp(chip, val1); break; - case POWER_SUPPLY_PROP_RESISTANCE_ID: - pval->intval = chip->batt_id_ohm; + case PSY_IIO_RESISTANCE_ID: + *val1 = chip->batt_id_ohm; break; - case POWER_SUPPLY_PROP_DEBUG_BATTERY: - pval->intval = is_debug_batt_id(chip); + case PSY_IIO_DEBUG_BATTERY: + *val1 = is_debug_batt_id(chip); break; - case POWER_SUPPLY_PROP_RESISTANCE: - rc = qg_sdam_read(SDAM_RBAT_MOHM, &pval->intval); + case PSY_IIO_RESISTANCE: + rc = qg_sdam_read(SDAM_RBAT_MOHM, val1); if (!rc) - pval->intval *= 1000; + *val1 *= 1000; break; - case POWER_SUPPLY_PROP_RESISTANCE_NOW: - pval->intval = chip->esr_last; + case PSY_IIO_SOC_REPORTING_READY: + *val1 = chip->soc_reporting_ready; break; - case POWER_SUPPLY_PROP_SOC_REPORTING_READY: - pval->intval = chip->soc_reporting_ready; + case PSY_IIO_RESISTANCE_CAPACITIVE: + *val1 = chip->dt.rbat_conn_mohm; break; - case POWER_SUPPLY_PROP_RESISTANCE_CAPACITIVE: - pval->intval = chip->dt.rbat_conn_mohm; + case PSY_IIO_VOLTAGE_MIN: + *val1 = chip->dt.vbatt_cutoff_mv * 1000; break; - case POWER_SUPPLY_PROP_BATTERY_TYPE: - pval->strval = qg_get_battery_type(chip); + case PSY_IIO_VOLTAGE_MAX: + *val1 = chip->bp.float_volt_uv; break; - case POWER_SUPPLY_PROP_VOLTAGE_MIN: - pval->intval = chip->dt.vbatt_cutoff_mv * 1000; + case PSY_IIO_BATT_FULL_CURRENT: + *val1 = chip->dt.iterm_ma * 1000; break; - case POWER_SUPPLY_PROP_VOLTAGE_MAX: - pval->intval = chip->bp.float_volt_uv; + case PSY_IIO_BATT_PROFILE_VERSION: + *val1 = chip->bp.qg_profile_version; break; - case POWER_SUPPLY_PROP_BATT_FULL_CURRENT: - pval->intval = chip->dt.iterm_ma * 1000; + case PSY_IIO_CHARGE_COUNTER: + rc = qg_get_charge_counter(chip, val1); break; - case POWER_SUPPLY_PROP_BATT_PROFILE_VERSION: - pval->intval = chip->bp.qg_profile_version; - break; - case POWER_SUPPLY_PROP_CHARGE_COUNTER: - rc = qg_get_charge_counter(chip, &pval->intval); - break; - case POWER_SUPPLY_PROP_CHARGE_FULL: + case PSY_IIO_CHARGE_FULL: if (!chip->dt.cl_disable && chip->dt.cl_feedback_on) rc = qg_get_learned_capacity(chip, &temp); else rc = qg_get_nominal_capacity((int *)&temp, 250, true); if (!rc) - pval->intval = (int)temp; + *val1 = (int)temp; break; - case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN: + case PSY_IIO_CHARGE_FULL_DESIGN: rc = qg_get_nominal_capacity((int *)&temp, 250, true); if (!rc) - pval->intval = (int)temp; + *val1 = (int)temp; break; - case POWER_SUPPLY_PROP_CYCLE_COUNTS: - rc = get_cycle_counts(chip->counter, &pval->strval); - if (rc < 0) - pval->strval = NULL; + case PSY_IIO_CYCLE_COUNT: + rc = get_cycle_count(chip->counter, val1); break; - case POWER_SUPPLY_PROP_CYCLE_COUNT: - rc = get_cycle_count(chip->counter, &pval->intval); + case PSY_IIO_TIME_TO_FULL_AVG: + rc = ttf_get_time_to_full(chip->ttf, val1); break; - case POWER_SUPPLY_PROP_TIME_TO_FULL_AVG: - rc = ttf_get_time_to_full(chip->ttf, &pval->intval); + case PSY_IIO_TIME_TO_FULL_NOW: + rc = ttf_get_time_to_full(chip->ttf, val1); break; - case POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG: - rc = ttf_get_time_to_empty(chip->ttf, &pval->intval); + case PSY_IIO_TIME_TO_EMPTY_AVG: + rc = ttf_get_time_to_empty(chip->ttf, val1); break; - case POWER_SUPPLY_PROP_ESR_ACTUAL: - pval->intval = (chip->esr_actual == -EINVAL) ? -EINVAL : + case PSY_IIO_ESR_ACTUAL: + *val1 = (chip->esr_actual == -EINVAL) ? -EINVAL : (chip->esr_actual * 1000); break; - case POWER_SUPPLY_PROP_ESR_NOMINAL: - pval->intval = (chip->esr_nominal == -EINVAL) ? -EINVAL : + case PSY_IIO_ESR_NOMINAL: + *val1 = (chip->esr_nominal == -EINVAL) ? -EINVAL : (chip->esr_nominal * 1000); break; - case POWER_SUPPLY_PROP_SOH: - pval->intval = chip->soh; + case PSY_IIO_SOH: + *val1 = chip->soh; break; - case POWER_SUPPLY_PROP_CC_SOC: - rc = qg_get_cc_soc(chip, &pval->intval); + case PSY_IIO_CC_SOC: + rc = qg_get_cc_soc(chip, val1); break; - case POWER_SUPPLY_PROP_VOLTAGE_AVG: - rc = qg_get_vbat_avg(chip, &pval->intval); + case PSY_IIO_FG_RESET: + *val1 = 0; break; - case POWER_SUPPLY_PROP_CURRENT_AVG: - rc = qg_get_ibat_avg(chip, &pval->intval); + case PSY_IIO_VOLTAGE_AVG: + rc = qg_get_vbat_avg(chip, val1); break; - case POWER_SUPPLY_PROP_POWER_NOW: - rc = qg_get_power(chip, &pval->intval, false); + case PSY_IIO_CURRENT_AVG: + rc = qg_get_ibat_avg(chip, val1); break; - case POWER_SUPPLY_PROP_POWER_AVG: - rc = qg_get_power(chip, &pval->intval, true); + case PSY_IIO_POWER_NOW: + rc = qg_get_power(chip, val1, false); break; - case POWER_SUPPLY_PROP_SCALE_MODE_EN: - pval->intval = chip->fvss_active; + case PSY_IIO_POWER_AVG: + rc = qg_get_power(chip, val1, true); break; - case POWER_SUPPLY_PROP_BATT_AGE_LEVEL: - pval->intval = chip->batt_age_level; + case PSY_IIO_SCALE_MODE_EN: + *val1 = chip->fvss_active; break; - case POWER_SUPPLY_PROP_FG_TYPE: - pval->intval = chip->qg_mode; + case PSY_IIO_BATT_AGE_LEVEL: + *val1 = chip->batt_age_level; + break; + case PSY_IIO_FG_TYPE: + *val1 = chip->qg_mode; break; default: - pr_debug("Unsupported property %d\n", psp); + pr_debug("Unsupported property %d\n", chan->channel); + rc = -EINVAL; break; } - return rc; + if (rc < 0) { + pr_err("Couldn't read IIO channel %d, rc = %d\n", + chan->channel, rc); + return rc; + } + + return IIO_VAL_INT; } -static int qg_property_is_writeable(struct power_supply *psy, - enum power_supply_property psp) +static int qg_iio_of_xlate(struct iio_dev *indio_dev, + const struct of_phandle_args *iiospec) { - switch (psp) { - case POWER_SUPPLY_PROP_CHARGE_FULL: - case POWER_SUPPLY_PROP_ESR_ACTUAL: - case POWER_SUPPLY_PROP_ESR_NOMINAL: - case POWER_SUPPLY_PROP_SOH: - case POWER_SUPPLY_PROP_FG_RESET: - case POWER_SUPPLY_PROP_BATT_AGE_LEVEL: - return 1; - default: - break; - } - return 0; + struct qpnp_qg *chip = iio_priv(indio_dev); + struct iio_chan_spec *iio_chan = chip->iio_chan; + int i; + + for (i = 0; i < ARRAY_SIZE(qg_iio_psy_channels); + i++, iio_chan++) + if (iio_chan->channel == iiospec->args[0]) + return i; + + return -EINVAL; } -static enum power_supply_property qg_psy_props[] = { - POWER_SUPPLY_PROP_CAPACITY, - POWER_SUPPLY_PROP_CAPACITY_RAW, - POWER_SUPPLY_PROP_REAL_CAPACITY, - POWER_SUPPLY_PROP_TEMP, - POWER_SUPPLY_PROP_VOLTAGE_NOW, - POWER_SUPPLY_PROP_VOLTAGE_OCV, - POWER_SUPPLY_PROP_CURRENT_NOW, - POWER_SUPPLY_PROP_CHARGE_COUNTER, - POWER_SUPPLY_PROP_RESISTANCE, - POWER_SUPPLY_PROP_RESISTANCE_ID, - POWER_SUPPLY_PROP_RESISTANCE_NOW, - POWER_SUPPLY_PROP_SOC_REPORTING_READY, - POWER_SUPPLY_PROP_RESISTANCE_CAPACITIVE, - POWER_SUPPLY_PROP_DEBUG_BATTERY, - POWER_SUPPLY_PROP_BATTERY_TYPE, - POWER_SUPPLY_PROP_VOLTAGE_MIN, - POWER_SUPPLY_PROP_VOLTAGE_MAX, - POWER_SUPPLY_PROP_BATT_FULL_CURRENT, - POWER_SUPPLY_PROP_BATT_PROFILE_VERSION, - POWER_SUPPLY_PROP_CYCLE_COUNT, - POWER_SUPPLY_PROP_CYCLE_COUNTS, - POWER_SUPPLY_PROP_CHARGE_FULL, - POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, - POWER_SUPPLY_PROP_TIME_TO_FULL_AVG, - POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG, - POWER_SUPPLY_PROP_ESR_ACTUAL, - POWER_SUPPLY_PROP_ESR_NOMINAL, - POWER_SUPPLY_PROP_SOH, - POWER_SUPPLY_PROP_CC_SOC, - POWER_SUPPLY_PROP_FG_RESET, - POWER_SUPPLY_PROP_VOLTAGE_AVG, - POWER_SUPPLY_PROP_CURRENT_AVG, - POWER_SUPPLY_PROP_POWER_AVG, - POWER_SUPPLY_PROP_POWER_NOW, - POWER_SUPPLY_PROP_SCALE_MODE_EN, - POWER_SUPPLY_PROP_BATT_AGE_LEVEL, - POWER_SUPPLY_PROP_FG_TYPE, -}; - -static const struct power_supply_desc qg_psy_desc = { - .name = "bms", - .type = POWER_SUPPLY_TYPE_BMS, - .properties = qg_psy_props, - .num_properties = ARRAY_SIZE(qg_psy_props), - .get_property = qg_psy_get_property, - .set_property = qg_psy_set_property, - .property_is_writeable = qg_property_is_writeable, +static const struct iio_info qg_iio_info = { + .read_raw = qg_iio_read_raw, + .write_raw = qg_iio_write_raw, + .of_xlate = qg_iio_of_xlate, }; #define DEFAULT_CL_BEGIN_IBAT_UA (-100000) @@ -2341,7 +2320,7 @@ static bool qg_cl_ok_to_begin(void *data) static int qg_charge_full_update(struct qpnp_qg *chip) { union power_supply_propval prop = {0, }; - int rc, recharge_soc, health; + int rc, recharge_soc, health, val; if (!chip->dt.hold_soc_while_full) goto out; @@ -2354,13 +2333,12 @@ static int qg_charge_full_update(struct qpnp_qg *chip) } health = prop.intval; - rc = power_supply_get_property(chip->batt_psy, - POWER_SUPPLY_PROP_RECHARGE_SOC, &prop); - if (rc < 0 || prop.intval < 0) { + rc = qg_read_iio_chan(chip, RECHARGE_SOC, &val); + if (rc < 0 || val < 0) { pr_debug("Failed to get recharge-soc\n"); recharge_soc = DEFAULT_RECHARGE_SOC; } else { - recharge_soc = prop.intval; + recharge_soc = val; } chip->recharge_soc = recharge_soc; @@ -2390,9 +2368,7 @@ static int qg_charge_full_update(struct qpnp_qg *chip) input_present && chip->msoc <= recharge_soc && chip->charge_status != POWER_SUPPLY_STATUS_CHARGING) { /* Force recharge */ - prop.intval = 0; - rc = power_supply_set_property(chip->batt_psy, - POWER_SUPPLY_PROP_FORCE_RECHARGE, &prop); + rc = qg_write_iio_chan(chip, FORCE_RECHARGE, 0); if (rc < 0) pr_err("Failed to force recharge rc=%d\n", rc); else @@ -2613,7 +2589,7 @@ static void qg_status_change_work(struct work_struct *work) struct qpnp_qg *chip = container_of(work, struct qpnp_qg, qg_status_change_work); union power_supply_propval prop = {0, }; - int rc = 0, batt_temp = 0; + int rc = 0, batt_temp = 0, val; bool input_present = false; if (!is_batt_available(chip)) { @@ -2639,12 +2615,11 @@ static void qg_status_change_work(struct work_struct *work) else chip->charge_status = prop.intval; - rc = power_supply_get_property(chip->batt_psy, - POWER_SUPPLY_PROP_CHARGE_DONE, &prop); + rc = qg_read_iio_chan(chip, CHARGE_DONE, &val); if (rc < 0) pr_err("Failed to get charge done status, rc=%d\n", rc); else - chip->charge_done = prop.intval; + chip->charge_done = val; qg_dbg(chip, QG_DEBUG_STATUS, "charge_status=%d charge_done=%d\n", chip->charge_status, chip->charge_done); @@ -2711,17 +2686,38 @@ static int qg_notifier_cb(struct notifier_block *nb, return NOTIFY_OK; } +static int qg_psy_get_property(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *pval) +{ + if (psp == POWER_SUPPLY_PROP_TYPE) + pval->intval = POWER_SUPPLY_TYPE_MAINS; + + return 0; +} + +static enum power_supply_property qg_psy_props[] = { + POWER_SUPPLY_PROP_TYPE, +}; + +static const struct power_supply_desc qg_psy_desc = { + .name = "bms", + .type = POWER_SUPPLY_TYPE_MAINS, + .properties = qg_psy_props, + .num_properties = ARRAY_SIZE(qg_psy_props), + .get_property = qg_psy_get_property, +}; + static int qg_init_psy(struct qpnp_qg *chip) { struct power_supply_config qg_psy_cfg = {}; int rc; qg_psy_cfg.drv_data = chip; - qg_psy_cfg.of_node = chip->dev->of_node; chip->qg_psy = devm_power_supply_register(chip->dev, &qg_psy_desc, &qg_psy_cfg); if (IS_ERR_OR_NULL(chip->qg_psy)) { - pr_err("Failed to register qg_psy rc = %ld\n", + pr_err("Failed to register qg_psy, rc = %d\n", PTR_ERR(chip->qg_psy)); return -ENODEV; } @@ -2729,7 +2725,64 @@ static int qg_init_psy(struct qpnp_qg *chip) chip->nb.notifier_call = qg_notifier_cb; rc = power_supply_reg_notifier(&chip->nb); if (rc < 0) - pr_err("Failed register psy notifier rc = %d\n", rc); + pr_err("Failed to register psy notifier rc = %d\n", rc); + + return rc; +} + +static int qg_init_iio_psy(struct qpnp_qg *chip, + struct platform_device *pdev) +{ + struct iio_dev *indio_dev = chip->indio_dev; + struct iio_chan_spec *chan; + int qg_num_iio_channels = ARRAY_SIZE(qg_iio_psy_channels); + int rc, i; + + chip->iio_chan = devm_kcalloc(chip->dev, qg_num_iio_channels, + sizeof(*chip->iio_chan), GFP_KERNEL); + if (!chip->iio_chan) + return -ENOMEM; + + chip->int_iio_chans = devm_kcalloc(chip->dev, + qg_num_iio_channels, + sizeof(*chip->int_iio_chans), + GFP_KERNEL); + if (!chip->int_iio_chans) + return -ENOMEM; + + chip->ext_iio_chans = devm_kcalloc(chip->dev, + ARRAY_SIZE(qg_ext_iio_chan_name), + sizeof(*chip->ext_iio_chans), + GFP_KERNEL); + if (!chip->ext_iio_chans) + return -ENOMEM; + + indio_dev->info = &qg_iio_info; + indio_dev->dev.parent = chip->dev; + indio_dev->dev.of_node = chip->dev->of_node; + indio_dev->name = pdev->name; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = chip->iio_chan; + indio_dev->num_channels = qg_num_iio_channels; + + for (i = 0; i < qg_num_iio_channels; i++) { + chip->int_iio_chans[i].indio_dev = indio_dev; + chan = &chip->iio_chan[i]; + chip->int_iio_chans[i].channel = chan; + chan->address = i; + chan->channel = qg_iio_psy_channels[i].channel_num; + chan->type = qg_iio_psy_channels[i].type; + chan->datasheet_name = + qg_iio_psy_channels[i].datasheet_name; + chan->extend_name = + qg_iio_psy_channels[i].datasheet_name; + chan->info_mask_separate = + qg_iio_psy_channels[i].info_mask; + } + + rc = devm_iio_device_register(chip->dev, indio_dev); + if (rc) + pr_err("Failed to register QG IIO device, rc=%d\n", rc); return rc; } @@ -3070,7 +3123,7 @@ static int qg_load_battery_profile(struct qpnp_qg *chip) return -ENOMEM; } - rc = read_range_data_from_node(profile_node, + rc = qg_read_range_data_from_node(profile_node, "qcom,step-chg-ranges", chip->ttf->step_chg_cfg, chip->bp.float_volt_uv, @@ -3348,30 +3401,28 @@ done: static int qg_set_wa_flags(struct qpnp_qg *chip) { - switch (chip->pmic_rev_id->pmic_subtype) { - case PMI632_SUBTYPE: + switch (chip->pmic_version) { + case PMI632: chip->wa_flags |= QG_RECHARGE_SOC_WA; if (!chip->dt.use_s7_ocv) chip->wa_flags |= QG_PON_OCV_WA; - if (chip->pmic_rev_id->rev4 == PMI632_V1P0_REV4) - chip->wa_flags |= QG_VBAT_LOW_WA; break; - case PM6150_SUBTYPE: + case PM6150: chip->wa_flags |= QG_CLK_ADJUST_WA | QG_RECHARGE_SOC_WA; qg_esr_mod_count = 10; break; - case PM7250B_SUBTYPE: + case PM7250B: qg_esr_mod_count = 10; break; - case PM2250_SUBTYPE: + case PM2250: chip->wa_flags |= QG_CLK_ADJUST_WA | QG_RECHARGE_SOC_WA | QG_VBAT_LOW_WA; break; default: - pr_err("Unsupported PMIC subtype %d\n", - chip->pmic_rev_id->pmic_subtype); + pr_err("Unsupported PMIC version %d\n", + chip->pmic_version); return -EINVAL; } @@ -3677,6 +3728,7 @@ static int qg_soh_batt_profile_init(struct qpnp_qg *chip) chip->sp->bp_node = chip->batt_node; chip->sp->last_batt_age_level = chip->batt_age_level; chip->sp->bms_psy = chip->qg_psy; + chip->sp->iio_chan_list = chip->int_iio_chans; rc = soh_profile_init(chip->dev, chip->sp); if (rc < 0) chip->sp = NULL; @@ -4112,7 +4164,7 @@ static int qg_parse_cl_dt(struct qpnp_qg *chip) static int qg_parse_dt(struct qpnp_qg *chip) { int rc = 0; - struct device_node *revid_node, *child, *node = chip->dev->of_node; + struct device_node *child, *node = chip->dev->of_node; u32 base, temp; u8 type; @@ -4121,28 +4173,6 @@ static int qg_parse_dt(struct qpnp_qg *chip) return -ENXIO; } - revid_node = of_parse_phandle(node, "qcom,pmic-revid", 0); - if (!revid_node) { - pr_err("Missing qcom,pmic-revid property - driver failed\n"); - return -EINVAL; - } - - chip->pmic_rev_id = get_revid_data(revid_node); - of_node_put(revid_node); - if (IS_ERR_OR_NULL(chip->pmic_rev_id)) { - pr_err("Failed to get pmic_revid, rc=%ld\n", - PTR_ERR(chip->pmic_rev_id)); - /* - * the revid peripheral must be registered, any failure - * here only indicates that the rev-id module has not - * probed yet. - */ - return -EPROBE_DEFER; - } - - qg_dbg(chip, QG_DEBUG_PON, "PMIC subtype %d Digital major %d\n", - chip->pmic_rev_id->pmic_subtype, chip->pmic_rev_id->rev4); - for_each_available_child_of_node(node, child) { rc = of_property_read_u32(child, "reg", &base); if (rc < 0) { @@ -4624,11 +4654,16 @@ static int qpnp_qg_probe(struct platform_device *pdev) { int rc = 0, soc = 0, nom_cap_uah; struct qpnp_qg *chip; + struct iio_dev *indio_dev; + struct qg_config *config; - chip = devm_kzalloc(&pdev->dev, sizeof(*chip), GFP_KERNEL); - if (!chip) + indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*chip)); + if (!indio_dev) return -ENOMEM; + chip = iio_priv(indio_dev); + chip->indio_dev = indio_dev; + chip->regmap = dev_get_regmap(pdev->dev.parent, NULL); if (!chip->regmap) { pr_err("Parent regmap is unavailable\n"); @@ -4675,7 +4710,18 @@ static int qpnp_qg_probe(struct platform_device *pdev) chip->esr_nominal = -EINVAL; chip->batt_age_level = -EINVAL; - chip->qg_version = (u8)of_device_get_match_data(&pdev->dev); + config = (struct qg_config *)of_device_get_match_data( + &pdev->dev); + if (!config) { + pr_err("Failed to get QG config data\n"); + return -EINVAL; + } + + chip->qg_version = config->qg_version; + chip->pmic_version = config->pmic_version; + + qg_dbg(chip, QG_DEBUG_PON, "QG version:%d PMIC version:%d", + chip->qg_version, chip->pmic_version); switch (chip->qg_version) { case QG_LITE: @@ -4800,9 +4846,15 @@ static int qpnp_qg_probe(struct platform_device *pdev) goto fail_device; } + rc = qg_init_iio_psy(chip, pdev); + if (rc < 0) { + pr_err("Failed to initialize QG IIO PSY, rc=%d\n", rc); + goto fail_votable; + } + rc = qg_init_psy(chip); if (rc < 0) { - pr_err("Failed to initialize QG psy, rc=%d\n", rc); + pr_err("Failed to initialize QG PSY, rc=%d\n", rc); goto fail_votable; } @@ -4884,9 +4936,24 @@ static void qpnp_qg_shutdown(struct platform_device *pdev) } static const struct of_device_id match_table[] = { - { .compatible = "qcom,qpnp-qg", .data = (void *)QG_PMIC5, }, - { .compatible = "qcom,qpnp-qg-lite", .data = (void *)QG_LITE, }, - { }, + { + .compatible = "qcom,qpnp-qg-lite", + .data = (void *)&config[PM2250], + }, + { + .compatible = "qcom,pm6150-qg", + .data = (void *)&config[PM6150], + }, + { + .compatible = "qcom,pmi632-qg", + .data = (void *)&config[PMI632], + }, + { + .compatible = "qcom,pm7250b-qg", + .data = (void *)&config[PM7250B], + }, + { + }, }; static struct platform_driver qpnp_qg_driver = {