mirror of
https://github.com/BobTheBlinker/android_kernel_motorola_sm6375.git
synced 2026-10-07 12:25:00 -04:00
power: supply: qg: Add support to make QG driver GKI compliant
Add support for "bms" power supply which is not present in kernel 5.4 through IIO device and properties under it through individual IIO channels under the IIO device. Change-Id: I55c44e5db2dc4549021cb429444c7c5afa679c3a Signed-off-by: Shyam Kumar Thella <sthella@codeaurora.org>
This commit is contained in:
parent
1074012e87
commit
7bfcd4bd53
11 changed files with 641 additions and 273 deletions
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
121
drivers/power/supply/qcom/qg-iio.h
Normal file
121
drivers/power/supply/qcom/qg-iio.h
Normal 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
|
||||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 = {
|
||||
|
|
|
|||
Loading…
Reference in a new issue