Merge "defconfig: Enable SMB5 and QG drivers for holi"

This commit is contained in:
qctecmdr 2020-08-26 04:59:27 -07:00 committed by Gerrit - the friendly Code Review server
commit 2251e53c39
12 changed files with 646 additions and 273 deletions

View File

@ -195,3 +195,8 @@ CONFIG_SLIMBUS_MSM_NGD=m
# CONFIG_I2C_CHARDEV is not set
# CONFIG_SPI_SPIDEV is not set
CONFIG_QCOM_MICRODUMP=m
CONFIG_QCOM_POWER_SUPPLY=y
CONFIG_QPNP_QG=m
CONFIG_QPNP_SMB5=m
# CONFIG_SMB1398_CHARGER is not set
# CONFIG_SMB1355_SLAVE_CHARGER is not set

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -6,11 +6,14 @@
#define pr_fmt(fmt) "ALG: %s: " fmt, __func__
#include <linux/err.h>
#include <linux/iio/iio.h>
#include <linux/kernel.h>
#include <linux/mutex.h>
#include <linux/power_supply.h>
#include <linux/qti_power_supply.h>
#include <linux/slab.h>
#include <linux/sort.h>
#include <dt-bindings/iio/qti_power_supply_iio.h>
#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);

View File

@ -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

View File

@ -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,

View File

@ -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 <linux/iio/iio.h>
#include <dt-bindings/iio/qti_power_supply_iio.h>
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

View File

@ -6,6 +6,7 @@
#define pr_fmt(fmt) "QG-K: %s: " fmt, __func__
#include <linux/alarmtimer.h>
#include <linux/interrupt.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/power_supply.h>
@ -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);

View File

@ -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;
}

View File

@ -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

View File

@ -10,6 +10,7 @@
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/interrupt.h>
#include <linux/iio/iio.h>
#include <linux/kernel.h>
#include <linux/ktime.h>
#include <linux/module.h>
@ -23,13 +24,15 @@
#include <linux/timekeeping.h>
#include <linux/uaccess.h>
#include <linux/pmic-voter.h>
#include <linux/poll.h>
#include <linux/iio/consumer.h>
#include <linux/qpnp/qpnp-revid.h>
#include <dt-bindings/iio/qti_power_supply_iio.h>
#include <uapi/linux/qg.h>
#include <uapi/linux/qg-profile.h>
#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 = {