BQ25798: bq25798 with imx8mp

Part Number: BQ25798
Other Parts Discussed in Thread: BQ25790, , BQ25792

Tool/software:

Hi i have an issue with battery charging.

root@imx8mp-lpddr4-evk:~# i2cdump -y -f 0 0x6b
No size specified (using byte-data access)
0 1 2 3 4 5 6 7 8 9 a b c d e f 0123456789abcdef
00: XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XXXXXXXXXXXXXXXX
10: XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XXXXXXXXXXXXXXXX
20: XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XXXXXXXXXXXXXXXX
30: XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XXXXXXXXXXXXXXXX
40: XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XXXXXXXXXXXXXXXX
50: XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XXXXXXXXXXXXXXXX
60: XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XXXXXXXXXXXXXXXX
70: XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XXXXXXXXXXXXXXXX
80: XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XXXXXXXXXXXXXXXX
90: XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XXXXXXXXXXXXXXXX
a0: XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XXXXXXXXXXXXXXXX
b0: XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XXXXXXXXXXXXXXXX
c0: XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XXXXXXXXXXXXXXXX
d0: XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XXXXXXXXXXXXXXXX
e0: XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XXXXXXXXXXXXXXXX
f0: XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XXXXXXXXXXXXXXXX
root@imx8mp-lpddr4-evk:~# i2cset -y -f 0 0x6b 0x18 0x00
root@imx8mp-lpddr4-evk:~# i2cdump -y -f 0 0x6b
No size specified (using byte-data access)
0 1 2 3 4 5 6 7 8 9 a b c d e f 0123456789abcdef
00: 04 01 a4 00 c8 2a 00 88 c4 04 23 00 dc 4b 3d a2 ???.?*.???#.?K=?
10: 80 40 00 01 1e aa c0 7a 00 00 88 0f 0a 01 00 08 ?@.????z..????.?
20: 00 00 80 80 00 08 00 00 00 00 00 00 00 00 b0 00 ..??.?........?.
30: 00 02 f6 00 00 13 26 13 38 13 1c 0e ed 0f ec 03 .??..?&?8???????
40: 5f 00 3e 00 00 00 00 00 19 ff ff ff ff ff ff ff _.>.....?.......
50: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ................
60: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ................
70: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ................
80: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ................
90: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ................
a0: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ................
b0: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ................
c0: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ................
d0: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ................
e0: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ................
f0: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ................
root@imx8mp-lpddr4-evk:~# cat /sys/devices/system/cpu/*/cpufreq/cpuinfo_cur_freq
1800000
1800000
1800000
1800000
root@imx8mp-lpddr4-evk:~#

when i set i2cset -y -f 0 0x6b 0x18 0xD5
it doesn't give the i2c dump as bellow 
root@imx8mp-lpddr4-evk:~# i2cdump -y -f 0 0x6b
No size specified (using byte-data access)
0 1 2 3 4 5 6 7 8 9 a b c d e f 0123456789abcdef
00: XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XXXXXXXXXXXXXXXX
10: XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XXXXXXXXXXXXXXXX
20: XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XXXXXXXXXXXXXXXX
30: XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XXXXXXXXXXXXXXXX
40: XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XXXXXXXXXXXXXXXX
50: XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XXXXXXXXXXXXXXXX
60: XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XXXXXXXXXXXXXXXX
70: XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XXXXXXXXXXXXXXXX
80: XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XXXXXXXXXXXXXXXX
90: XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XXXXXXXXXXXXXXXX
a0: XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XXXXXXXXXXXXXXXX
b0: XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XXXXXXXXXXXXXXXX
c0: XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XXXXXXXXXXXXXXXX
d0: XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XXXXXXXXXXXXXXXX
e0: XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XXXXXXXXXXXXXXXX
f0: XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XXXXXXXXXXXXXXXX
root@imx8mp-lpddr4-evk:~#

my dts on imx8mp 

bat: battery {
compatible = "simple-battery";
constant-charge-current-max-microamp = <1000000>;
constant-charge-voltage-max-microvolt = <4200000>;
precharge-current-microamp = <180000>;
charge-term-current-microamp = <200000>;
};

bq25790: charger@6b {
compatible = "ti,bq25790";
reg = <0x6b>;
interrupt-parent = <&gpio1>;
interrupts = <8 IRQ_TYPE_EDGE_FALLING>;
ti,watchdog-timeout-ms = <2000>;
input-current-limit-microamp = <3200000>;
input-voltage-limit-microvolt = <5000000>;
monitored-battery = <&bat>;
};



driver code and circuit diagram attached for your reference   ( in circuit there is bq25792 changed it to bq25798)

Please guide us to fix this issue.


// SPDX-License-Identifier: GPL-2.0
// BQ25790 driver
// Copyright (C) 2020 Texas Instruments Incorporated - http://www.ti.com/

#include <linux/module.h>
#include <linux/i2c.h>
#include <linux/power_supply.h>
#include <linux/regmap.h>
#include <linux/types.h>
#include <linux/gpio/consumer.h>
#include <linux/interrupt.h>
#include <linux/delay.h>
#include <linux/usb/phy.h>

#include <linux/acpi.h>
#include <linux/of.h>

#include "bq25790_charger.h"

#define BQ25790_NUM_WD_VAL	8

struct bq25790_init_data {
	u32 ichg;
	u32 ilim;
	u32 vreg;
	u32 iterm;
	u32 iprechg;
	u32 vlim;
	u32 ichg_max;
	u32 vreg_max;
};

struct bq25790_state {
	bool online;
	u8 chrg_status;
	u8 chrg_type;
	u8 health;
	u8 chrg_fault;
	u8 vsys_status;
	u8 vbus_status;
	u8 fault_0;
	u8 fault_1;
	u32 vbat_adc;
	u32 vbus_adc;
	u32 ibat_adc;
};

struct bq25790_device {
	struct i2c_client *client;
	struct device *dev;
	struct power_supply *charger;
	struct power_supply *battery;
	struct mutex lock;

	struct usb_phy *usb2_phy;
	struct usb_phy *usb3_phy;
	struct notifier_block usb_nb;
	struct work_struct usb_work;
	unsigned long usb_event;
	struct regmap *regmap;

	char model_name[I2C_NAME_SIZE];
	int device_id;

	struct bq25790_init_data init_data;
	struct bq25790_state state;
	int watchdog_timer;
};

static struct reg_default bq25790_reg_defs[] = {
	{BQ25790_INPUT_V_LIM, 0x24},
	{BQ25790_INPUT_I_LIM_MSB, 0x01},
	{BQ25790_INPUT_I_LIM_LSB, 0x2c},
	{BQ25790_PRECHRG_CTRL, 0xc3},
	{BQ25790_TERM_CTRL, 0x5},
	{BQ25790_VOTG_REG, 0xdc},
	{BQ25790_IOTG_REG, 0x4b},
	{BQ25790_TIMER_CTRL, 0x3d},
	{BQ25790_CHRG_CTRL_0, 0xa2},
	{BQ25790_CHRG_CTRL_1, 0x85},
	{BQ25790_CHRG_CTRL_2, 0x40},
	{BQ25790_CHRG_CTRL_3, 0x12},
	{BQ25790_CHRG_CTRL_5, 0x16},
	{BQ25790_MPPT_CTRL, 0xaa},
	{BQ25790_TEMP_CTRL, 0xc0},
	{BQ25790_NTC_CTRL_0, 0x7a},
	{BQ25790_NTC_CTRL_1, 0x54},
	{BQ25790_ICO_I_LIM, 0x0},
	{BQ25790_CHRG_STAT_0, 0x0},
	{BQ25790_CHRG_STAT_1, 0x0},
	{BQ25790_CHRG_STAT_2, 0x0},
	{BQ25790_CHRG_STAT_3, 0x0},
	{BQ25790_CHRG_STAT_4, 0x0},
	{BQ25790_FAULT_STAT_0, 0x0},
	{BQ25790_FAULT_STAT_1, 0x0},
	{BQ25790_CHRG_FLAG_0, 0x0},
	{BQ25790_CHRG_FLAG_1, 0x0},
	{BQ25790_CHRG_FLAG_2, 0x0},
	{BQ25790_CHRG_FLAG_3, 0x0},
	{BQ25790_FAULT_FLAG_0, 0x0},
	{BQ25790_FAULT_FLAG_1, 0x0},
	{BQ25790_CHRG_MSK_0, 0x0},
	{BQ25790_CHRG_MSK_1, 0x0},
	{BQ25790_CHRG_MSK_2, 0x0},
	{BQ25790_CHRG_MSK_3, 0x0},
	{BQ25790_FAULT_MSK_0, 0x0},
	{BQ25790_FAULT_MSK_1, 0x0},
	{BQ25790_ADC_CTRL, 0x30},
	{BQ25790_FN_DISABE_0, 0x0},
	{BQ25790_FN_DISABE_1, 0x0},
	{BQ25790_ADC_IBUS, 0x0},
	{BQ25790_ADC_IBAT_MSB, 0x0},
	{BQ25790_ADC_IBAT_LSB, 0x0},
	{BQ25790_ADC_VBUS_MSB, 0x0},
	{BQ25790_ADC_VBUS_LSB, 0x0},
	{BQ25790_ADC_VAC1, 0x0},
	{BQ25790_ADC_VAC2, 0x0},
	{BQ25790_ADC_VBAT_MSB, 0x0},
	{BQ25790_ADC_VBAT_LSB, 0x0},
	{BQ25790_ADC_VBUS_MSB, 0x0},
	{BQ25790_ADC_VBUS_LSB, 0x0},
	{BQ25790_ADC_TS, 0x0},
	{BQ25790_ADC_TDIE, 0x0},
	{BQ25790_ADC_DP, 0x0},
	{BQ25790_ADC_DM, 0x0},
	{BQ25790_DPDM_DRV, 0x0},
	{BQ25790_PART_INFO, 0x0},
};

static int bq25790_watchdog_time[BQ25790_NUM_WD_VAL] = {0, 500, 1000, 2000,
							20000, 40000, 80000,
							160000};

static enum power_supply_usb_type bq25790_usb_type[] = {
	POWER_SUPPLY_USB_TYPE_PD_DRP,
	POWER_SUPPLY_USB_TYPE_SDP,
	POWER_SUPPLY_USB_TYPE_CDP,
	POWER_SUPPLY_USB_TYPE_DCP,
	POWER_SUPPLY_USB_TYPE_UNKNOWN,
};

static int bq25790_usb_notifier(struct notifier_block *nb, unsigned long val,
				void *priv)
{
	struct bq25790_device *bq =
			container_of(nb, struct bq25790_device, usb_nb);

	bq->usb_event = val;
	queue_work(system_power_efficient_wq, &bq->usb_work);

	return NOTIFY_OK;
}

static void bq25790_usb_work(struct work_struct *data)
{
	struct bq25790_device *bq =
			container_of(data, struct bq25790_device, usb_work);

	switch (bq->usb_event) {
	case USB_EVENT_ID:
		break;

	case USB_EVENT_NONE:
		power_supply_changed(bq->charger);
		break;
	}

	return;
}

static int bq25790_get_vbat_adc(struct bq25790_device *bq)
{
	int ret;
	int vbat_adc_lsb, vbat_adc_msb;
	u16 vbat_adc;

	ret = regmap_read(bq->regmap, BQ25790_ADC_VBAT_MSB, &vbat_adc_msb);
	if (ret)
		return ret;

	ret = regmap_read(bq->regmap, BQ25790_ADC_VBAT_LSB, &vbat_adc_lsb);
	if (ret)
		return ret;

	vbat_adc = (vbat_adc_msb << 8) | vbat_adc_lsb;

	return vbat_adc * BQ25790_ADC_VOLT_STEP_uV;
}

static int bq25790_get_vbus_adc(struct bq25790_device *bq)
{
	int ret;
	int vbus_adc_lsb, vbus_adc_msb;
	u16 vbus_adc;

	ret = regmap_update_bits(bq->regmap, BQ25790_ADC_CTRL,
				 BQ25790_ADC_EN, BQ25790_ADC_EN);
	if (ret)
		return ret;

	ret = regmap_read(bq->regmap, BQ25790_ADC_VBUS_MSB, &vbus_adc_msb);
	if (ret)
		return ret;

	ret = regmap_read(bq->regmap, BQ25790_ADC_VBUS_LSB, &vbus_adc_lsb);
	if (ret)
		return ret;

	vbus_adc = (vbus_adc_msb << 8) | vbus_adc_lsb;

	return vbus_adc * BQ25790_ADC_VOLT_STEP_uV;
}

static int bq25790_get_ibat_adc(struct bq25790_device *bq)
{
	int ret;
	int ibat_adc_lsb, ibat_adc_msb;
	u16 ibat_adc;

	ret = regmap_read(bq->regmap, BQ25790_ADC_IBAT_MSB, &ibat_adc_msb);
	if (ret)
		return ret;

	ret = regmap_read(bq->regmap, BQ25790_ADC_IBAT_LSB, &ibat_adc_lsb);
	if (ret)
		return ret;

	ibat_adc = (ibat_adc_msb << 8) | ibat_adc_lsb;

	return ibat_adc * BQ25790_ADC_CURR_STEP_uA;
}

static int bq25790_get_term_curr(struct bq25790_device *bq)
{
	int ret;
	int reg_val;

	ret = regmap_read(bq->regmap, BQ25790_TERM_CTRL, &reg_val);
	if (ret)
		return ret;

	reg_val &= BQ25790_TERMCHRG_CUR_MASK;

	return reg_val * BQ25790_TERMCHRG_CURRENT_STEP_uA;
}

static int bq25790_get_prechrg_curr(struct bq25790_device *bq)
{
	int ret;
	int reg_val;

	ret = regmap_read(bq->regmap, BQ25790_PRECHRG_CTRL, &reg_val);
	if (ret)
		return ret;

	reg_val &= BQ25790_PRECHRG_CUR_MASK;

	return reg_val * BQ25790_PRECHRG_CURRENT_STEP_uA;
}

static int bq25790_get_ichg_curr(struct bq25790_device *bq)
{
	int ret;
	int ichg, ichg_lsb, ichg_msb;

	ret = regmap_read(bq->regmap, BQ25790_CHRG_I_LIM_LSB, &ichg_lsb);
	if (ret)
		return ret;

	ret = regmap_read(bq->regmap, BQ25790_CHRG_I_LIM_MSB, &ichg_msb);
	if (ret)
		return ret;

	ichg = (ichg_msb << 8) | ichg_lsb;

	return ichg * BQ25790_ICHRG_CURRENT_STEP_uA;
}

static int bq25790_set_term_curr(struct bq25790_device *bq, int term_current)
{
	int reg_val;

	if (term_current < BQ25790_TERMCHRG_I_MIN_uA ||
	    term_current > BQ25790_TERMCHRG_I_MAX_uA)
		return -EINVAL;

	reg_val = term_current / BQ25790_TERMCHRG_CURRENT_STEP_uA;

	return regmap_update_bits(bq->regmap, BQ25790_TERM_CTRL,
				  BQ25790_TERMCHRG_CUR_MASK, reg_val);
}

static int bq25790_set_prechrg_curr(struct bq25790_device *bq, int pre_current)
{
	int reg_val;

	if (pre_current < BQ25790_PRECHRG_I_MIN_uA ||
	    pre_current > BQ25790_PRECHRG_I_MAX_uA)
		return -EINVAL;

	reg_val = pre_current / BQ25790_PRECHRG_CURRENT_STEP_uA;

	return regmap_update_bits(bq->regmap, BQ25790_PRECHRG_CTRL,
				  BQ25790_PRECHRG_CUR_MASK, reg_val);
}

static int bq25790_set_ichrg_curr(struct bq25790_device *bq, int chrg_curr)
{
	int ret;
	int ichg, ichg_msb, ichg_lsb;

	if (chrg_curr < BQ25790_ICHRG_I_MIN_uA ||
	    chrg_curr > BQ25790_ICHRG_I_MAX_uA)
		return -EINVAL;

	ichg = chrg_curr / BQ25790_ICHRG_CURRENT_STEP_uA;
	ichg_msb = (ichg >> 8) & 0xff;
	ret = regmap_write(bq->regmap, BQ25790_CHRG_I_LIM_MSB, ichg_msb);
	if (ret)
		return ret;

	ichg_lsb = ichg & 0xff;

	return regmap_write(bq->regmap, BQ25790_CHRG_I_LIM_LSB, ichg_lsb);
}

static int bq25790_set_chrg_volt(struct bq25790_device *bq, int chrg_volt)
{
	int vlim_lsb, vlim_msb, vlim;
	int ret;

	if (chrg_volt < BQ25790_VREG_V_MIN_uV ||
	    chrg_volt > BQ25790_VREG_V_MAX_uV)
		return -EINVAL;

	vlim = chrg_volt / BQ25790_VREG_V_STEP_uV;
	vlim_msb = (vlim >> 8) & 0xff;
	ret = regmap_write(bq->regmap, BQ25790_CHRG_V_LIM_MSB, vlim_msb);
	if (ret)
		return ret;

	vlim_lsb = vlim & 0xff;

	return regmap_write(bq->regmap, BQ25790_CHRG_V_LIM_LSB, vlim_lsb);
}

static int bq25790_get_chrg_volt(struct bq25790_device *bq)
{
	int ret;
	int vlim_lsb, vlim_msb, chrg_volt;

	ret = regmap_read(bq->regmap, BQ25790_CHRG_V_LIM_MSB, &vlim_msb);
	if (ret)
		return ret;

	ret = regmap_read(bq->regmap, BQ25790_CHRG_V_LIM_LSB, &vlim_lsb);
	if (ret)
		return ret;

	chrg_volt = (vlim_msb << 8) | vlim_lsb;

	return chrg_volt * BQ25790_VREG_V_STEP_uV;
}

static int bq25790_set_input_volt_lim(struct bq25790_device *bq, int vindpm)
{
	int ret;
	int vlim_lsb, vlim_msb;
	int vlim;

	if (vindpm < BQ25790_VINDPM_V_MIN_uV ||
	    vindpm > BQ25790_VINDPM_V_MAX_uV)
		return -EINVAL;

	vlim = vindpm / BQ25790_VINDPM_STEP_uV;

	vlim_msb = (vlim >> 8) & 0xff;

	ret = regmap_write(bq->regmap, BQ25790_CHRG_V_LIM_MSB, vlim_msb);
	if (ret)
		return ret;

	vlim_lsb = vlim & 0xff;

	return regmap_write(bq->regmap, BQ25790_CHRG_V_LIM_LSB, vlim_lsb);
}

static int bq25790_get_input_volt_lim(struct bq25790_device *bq)
{
	int ret;
	int vlim;

	ret = regmap_read(bq->regmap, BQ25790_INPUT_V_LIM, &vlim);
	if (ret)
		return ret;

	return vlim * BQ25790_VINDPM_STEP_uV;
}

static int bq25790_set_input_curr_lim(struct bq25790_device *bq, int iindpm)
{
	int ret;
	int ilim, ilim_lsb, ilim_msb;

	if (iindpm < BQ25790_IINDPM_I_MIN_uA ||
	    iindpm > BQ25790_IINDPM_I_MAX_uA)
		return -EINVAL;

	ilim = iindpm / BQ25790_IINDPM_STEP_uA;
	ilim_msb = (ilim >> 8) & 0xff;

	ret = regmap_write(bq->regmap, BQ25790_INPUT_I_LIM_MSB, ilim_msb);
	if (ret)
		return ret;

	ilim_lsb = ilim & 0xff;

	return regmap_write(bq->regmap, BQ25790_INPUT_I_LIM_LSB, ilim_lsb);
}

static int bq25790_get_input_curr_lim(struct bq25790_device *bq)
{
	int ret;
	int ilim_msb, ilim_lsb;
	u16 ilim;

	ret = regmap_read(bq->regmap, BQ25790_INPUT_I_LIM_MSB, &ilim_msb);
	if (ret)
		return ret;

	ret = regmap_read(bq->regmap, BQ25790_INPUT_I_LIM_LSB, &ilim_lsb);
	if (ret)
		return ret;

	ilim = (ilim_msb << 8) | ilim_lsb;

	return ilim * BQ25790_IINDPM_STEP_uA;
}

static int bq25790_get_state(struct bq25790_device *bq,
			     struct bq25790_state *state)
{
	int chrg_stat_0, chrg_stat_1, chrg_stat_3, chrg_stat_4;
	int chrg_ctrl_0, fault_0, fault_1;
	int ret;

	ret = regmap_read(bq->regmap, BQ25790_CHRG_STAT_0, &chrg_stat_0);
	if (ret)
		return ret;

	state->vbus_status = chrg_stat_0 & BQ25790_VBUS_PRESENT;

	ret = regmap_read(bq->regmap, BQ25790_CHRG_STAT_1, &chrg_stat_1);
	if (ret)
		return ret;

	ret = regmap_read(bq->regmap, BQ25790_CHRG_CTRL_0, &chrg_ctrl_0);
	if (ret)
		return ret;

	if (chrg_ctrl_0 & BQ25790_CHRG_EN)
		state->chrg_status = chrg_stat_1 & BQ25790_CHG_STAT_MSK;
	else
		state->chrg_status = BQ25790_NOT_CHRGING;

	state->chrg_type = chrg_stat_1 & BQ25790_VBUS_STAT_MSK;

	ret = regmap_read(bq->regmap, BQ25790_CHRG_STAT_4, &chrg_stat_4);
	if (ret)
		return ret;

	state->health = chrg_stat_4 & BQ25790_TEMP_MASK;

	ret = regmap_read(bq->regmap, BQ25790_FAULT_STAT_0, &fault_0);
	if (ret)
		return ret;

	state->fault_0 = fault_0;

	ret = regmap_read(bq->regmap, BQ25790_FAULT_STAT_1, &fault_1);
	if (ret)
		return ret;

	state->fault_1 = fault_1;

	ret = regmap_read(bq->regmap, BQ25790_CHRG_STAT_3, &chrg_stat_3);
	if (ret)
		return ret;

	state->online = chrg_stat_3 & BQ25790_VSYS_STAT;

	state->vbat_adc = bq25790_get_vbat_adc(bq);

	state->vbus_adc = bq25790_get_vbus_adc(bq);

	state->ibat_adc = bq25790_get_ibat_adc(bq);

	return 0;
}

static int bq25790_set_property(struct power_supply *psy,
		enum power_supply_property prop,
		const union power_supply_propval *val)
{
	struct bq25790_device *bq = power_supply_get_drvdata(psy);
	int ret = -EINVAL;

	switch (prop) {
	case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:
		ret = bq25790_set_input_curr_lim(bq, val->intval);
		break;
	case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE:
		ret = bq25790_set_chrg_volt(bq, val->intval);
		break;
	case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT:
		ret = bq25790_set_ichrg_curr(bq, val->intval);
		break;
	case POWER_SUPPLY_PROP_PRECHARGE_CURRENT:
		ret = bq25790_set_prechrg_curr(bq, val->intval);
		break;
	case POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT:
		ret = bq25790_set_term_curr(bq, val->intval);
		break;
	case POWER_SUPPLY_PROP_STATUS:
		break;
	case POWER_SUPPLY_PROP_INPUT_VOLTAGE_LIMIT:
		ret = bq25790_set_input_volt_lim(bq, val->intval);
		break;
	default:
		return -EINVAL;
	}

	return ret;
}

static int bq25790_get_property(struct power_supply *psy,
				enum power_supply_property psp,
				union power_supply_propval *val)
{
	struct bq25790_device *bq = power_supply_get_drvdata(psy);
	struct bq25790_state state;
	int ret = 0;

	mutex_lock(&bq->lock);
	ret = bq25790_get_state(bq, &state);
	mutex_unlock(&bq->lock);
	if (ret)
		return ret;

	switch (psp) {
	case POWER_SUPPLY_PROP_STATUS:
		if (!state.chrg_type || (state.chrg_type == BQ25790_OTG_MODE))
			val->intval = POWER_SUPPLY_STATUS_DISCHARGING;
		else if (!state.chrg_status)
			val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING;
		else if (state.chrg_status == BQ25790_TERM_CHRG)
			val->intval = POWER_SUPPLY_STATUS_FULL;
		else
			val->intval = POWER_SUPPLY_STATUS_CHARGING;
		break;
	case POWER_SUPPLY_PROP_CHARGE_TYPE:
		switch (state.chrg_status) {
		case BQ25790_TRICKLE_CHRG:
			val->intval = POWER_SUPPLY_CHARGE_TYPE_TRICKLE;
			break;
		case BQ25790_PRECHRG:
			val->intval = POWER_SUPPLY_CHARGE_TYPE_TRICKLE;
			break;
		case BQ25790_FAST_CHRG:
			val->intval = POWER_SUPPLY_CHARGE_TYPE_FAST;
			break;
		case BQ25790_TAPER_CHRG:
			val->intval = POWER_SUPPLY_CHARGE_TYPE_STANDARD;
			break;
		case BQ25790_TOP_OFF_CHRG:
			val->intval = POWER_SUPPLY_CHARGE_TYPE_TRICKLE;
			break;
		case BQ25790_NOT_CHRGING:
			val->intval = POWER_SUPPLY_CHARGE_TYPE_NONE;
			break;
		default:
			val->intval = POWER_SUPPLY_CHARGE_TYPE_UNKNOWN;
		}
		break;
	case POWER_SUPPLY_PROP_MANUFACTURER:
		val->strval = BQ25790_MANUFACTURER;
		break;

	case POWER_SUPPLY_PROP_MODEL_NAME:
		val->strval = BQ25790_NAME;
		break;

	case POWER_SUPPLY_PROP_ONLINE:
		val->intval = state.online;
		break;
	case POWER_SUPPLY_PROP_USB_TYPE:
		if (!state.chrg_type) {
			val->intval = POWER_SUPPLY_USB_TYPE_UNKNOWN;
			break;
		}
		switch (state.chrg_type) {
		case BQ25790_USB_SDP:
			val->intval = POWER_SUPPLY_USB_TYPE_SDP;
			break;
		case BQ25790_USB_CDP:
			val->intval = POWER_SUPPLY_USB_TYPE_CDP;
			break;
		case BQ25790_USB_DCP:
			val->intval = POWER_SUPPLY_USB_TYPE_DCP;
			break;
		case BQ25790_OTG_MODE:
			val->intval = POWER_SUPPLY_USB_TYPE_PD_DRP;
			break;

		default:
			val->intval = POWER_SUPPLY_USB_TYPE_UNKNOWN;
			break;
		}
		break;

	case POWER_SUPPLY_PROP_HEALTH:
		if (state.fault_1 & (BQ25790_OTG_OVP | BQ25790_VSYS_OVP))
			val->intval = POWER_SUPPLY_HEALTH_OVERVOLTAGE;
		else
			val->intval = POWER_SUPPLY_HEALTH_GOOD;

		switch (state.health) {
		case BQ25790_TEMP_HOT:
			val->intval = POWER_SUPPLY_HEALTH_HOT;
			break;
		case BQ25790_TEMP_WARM:
			val->intval = POWER_SUPPLY_HEALTH_WARM;
			break;
		case BQ25790_TEMP_COOL:
			val->intval = POWER_SUPPLY_HEALTH_COOL;
			break;
		case BQ25790_TEMP_COLD:
			val->intval = POWER_SUPPLY_HEALTH_COLD;
			break;
		}
		break;
	case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT:
		ret = bq25790_get_ichg_curr(bq);
		if (ret < 0)
			return ret;

		val->intval = ret;
		break;

	case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE:
		ret = bq25790_get_chrg_volt(bq);
		if (ret < 0)
			return ret;

		val->intval = ret;
		break;

	case POWER_SUPPLY_PROP_PRECHARGE_CURRENT:
		ret = bq25790_get_prechrg_curr(bq);
		if (ret < 0)
			return ret;

		val->intval = ret;
		break;
	case POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT:
		ret = bq25790_get_term_curr(bq);
		if (ret < 0)
			return ret;

		val->intval = ret;
		break;
	case POWER_SUPPLY_PROP_VOLTAGE_NOW:
		val->intval = state.vbus_adc;
		break;
	case POWER_SUPPLY_PROP_INPUT_VOLTAGE_LIMIT:
		ret = bq25790_get_input_volt_lim(bq);
		if (ret < 0)
			return ret;

		val->intval = ret;
		break;
	case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:
		ret = bq25790_get_input_curr_lim(bq);
		if (ret < 0)
			return ret;

		val->intval = ret;
		break;
	default:
		return -EINVAL;
	}

	return ret;
}

static int bq25790_battery_get_property(struct power_supply *psy,
				       enum power_supply_property psp,
				       union power_supply_propval *val)
{
	struct bq25790_device *bq = power_supply_get_drvdata(psy);
	struct bq25790_state state;
	int ret = 0;

	mutex_lock(&bq->lock);
	ret = bq25790_get_state(bq, &state);
	mutex_unlock(&bq->lock);
	if (ret)
		return ret;

	ret = regmap_update_bits(bq->regmap, BQ25790_ADC_CTRL,
				 BQ25790_ADC_EN, BQ25790_ADC_EN);
	if (ret)
		return ret;

	switch (psp) {
	case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX:
		val->intval = bq->init_data.ichg_max;
		break;
	case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX:
		val->intval = bq->init_data.vreg_max;
		break;

	case POWER_SUPPLY_PROP_VOLTAGE_NOW:
		val->intval = state.vbat_adc;
		break;
	case POWER_SUPPLY_PROP_CURRENT_NOW:
		val->intval = state.ibat_adc;
		break;
	default:
		return -EINVAL;
	}

	return ret;
}

static bool bq25790_state_changed(struct bq25790_device *bq,
				  struct bq25790_state *new_state)
{
	struct bq25790_state old_state;

	mutex_lock(&bq->lock);
	old_state = bq->state;
	mutex_unlock(&bq->lock);

	return (old_state.chrg_status != new_state->chrg_status ||
		old_state.chrg_fault != new_state->chrg_fault	||
		old_state.online != new_state->online		||
		old_state.health != new_state->health	||
		old_state.fault_0 != new_state->fault_0 ||
		old_state.fault_1 != new_state->fault_1 ||
		old_state.chrg_type != new_state->chrg_type ||
		old_state.vsys_status != new_state->vsys_status ||
		old_state.vbat_adc != new_state->vbat_adc ||
		old_state.vbus_adc != new_state->vbus_adc ||
		old_state.ibat_adc != new_state->ibat_adc);
}

static irqreturn_t bq25790_irq_handler_thread(int irq, void *private)
{
	struct bq25790_device *bq = private;
	struct bq25790_state state;
	int ret;

	ret = bq25790_get_state(bq, &state);
	if (ret < 0)
		goto irq_out;

	if (!bq25790_state_changed(bq, &state))
		goto irq_out;

	mutex_lock(&bq->lock);
	bq->state = state;
	mutex_unlock(&bq->lock);

	power_supply_changed(bq->charger);

irq_out:
	return IRQ_HANDLED;
}

static enum power_supply_property bq25790_power_supply_props[] = {
	POWER_SUPPLY_PROP_MANUFACTURER,
	POWER_SUPPLY_PROP_MODEL_NAME,
	POWER_SUPPLY_PROP_STATUS,
	POWER_SUPPLY_PROP_ONLINE,
	POWER_SUPPLY_PROP_HEALTH,
	POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT,
	POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE,
	POWER_SUPPLY_PROP_PRECHARGE_CURRENT,
	POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT,
	POWER_SUPPLY_PROP_VOLTAGE_NOW,
	POWER_SUPPLY_PROP_INPUT_VOLTAGE_LIMIT,
	POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT,
	POWER_SUPPLY_PROP_CHARGE_TYPE,
	POWER_SUPPLY_PROP_USB_TYPE,
};

static enum power_supply_property bq25790_battery_props[] = {
	POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX,
	POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX,
	POWER_SUPPLY_PROP_VOLTAGE_NOW,
	POWER_SUPPLY_PROP_CURRENT_NOW,
};

static char *bq25790_charger_supplied_to[] = {
	"main-battery",
};

static int bq25790_property_is_writeable(struct power_supply *psy,
					 enum power_supply_property prop)
{
	switch (prop) {
	case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:
	case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE:
	case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT:
	case POWER_SUPPLY_PROP_PRECHARGE_CURRENT:
	case POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT:
	case POWER_SUPPLY_PROP_STATUS:
	case POWER_SUPPLY_PROP_INPUT_VOLTAGE_LIMIT:
		return true;
	default:
		return false;
	}
}

static const struct power_supply_desc bq25790_power_supply_desc = {
	.name = "bq25790-charger",
	.type = POWER_SUPPLY_TYPE_USB,
	.usb_types = bq25790_usb_type,
	.num_usb_types = ARRAY_SIZE(bq25790_usb_type),
	.properties = bq25790_power_supply_props,
	.num_properties = ARRAY_SIZE(bq25790_power_supply_props),
	.get_property = bq25790_get_property,
	.set_property = bq25790_set_property,
	.property_is_writeable = bq25790_property_is_writeable,
};

static const struct power_supply_desc bq25790_battery_desc = {
	.name = "bq25790-battery",
	.type = POWER_SUPPLY_TYPE_BATTERY,
	.get_property = bq25790_battery_get_property,
	.properties = bq25790_battery_props,
	.num_properties = ARRAY_SIZE(bq25790_battery_props),
};

static bool bq25790_is_volatile_reg(struct device *dev, unsigned int reg)
{
	switch (reg) {
	case BQ25790_ICO_I_LIM...BQ25790_FAULT_FLAG_1:
	case BQ25790_ADC_IBUS...BQ25790_ADC_DM:
	case BQ25790_CHRG_CTRL_0:
		return true;
	default:
		return false;
	}
}

static const struct regmap_config bq25790_regmap_config = {
	.reg_bits = 8,
	.val_bits = 8,

	.max_register = BQ25790_PART_INFO,
	.reg_defaults	= bq25790_reg_defs,
	.num_reg_defaults = ARRAY_SIZE(bq25790_reg_defs),
	.cache_type = REGCACHE_RBTREE,
	.volatile_reg = bq25790_is_volatile_reg,
};

static int bq25790_power_supply_init(struct bq25790_device *bq,
							struct device *dev)
{
	struct power_supply_config psy_cfg = { .drv_data = bq,
						.of_node = dev->of_node, };

	psy_cfg.supplied_to = bq25790_charger_supplied_to;
	psy_cfg.num_supplicants = ARRAY_SIZE(bq25790_charger_supplied_to);

	bq->charger = devm_power_supply_register(bq->dev,
						 &bq25790_power_supply_desc,
						 &psy_cfg);
	if (IS_ERR(bq->charger))
		return -EINVAL;

	bq->battery = devm_power_supply_register(bq->dev,
						      &bq25790_battery_desc,
						      &psy_cfg);
	if (IS_ERR(bq->battery))
		return -EINVAL;
	return 0;
}

static int bq25790_hw_init(struct bq25790_device *bq)
{
	struct power_supply_battery_info *bat_info;
	int wd_reg_val = BQ25790_WATCHDOG_DIS;
	int wd_max_val = BQ25790_NUM_WD_VAL - 1;
	int ret = 0;
	int i;

	if (bq->watchdog_timer) {
		if (bq->watchdog_timer >= bq25790_watchdog_time[wd_max_val])
			wd_reg_val = wd_max_val;
		else {
			for (i = 0; i < wd_max_val; i++) {
				if (bq->watchdog_timer > bq25790_watchdog_time[i] &&
				    bq->watchdog_timer < bq25790_watchdog_time[i + 1]) {
					wd_reg_val = i;
					break;
				}
			}
		}
	}

	ret = regmap_update_bits(bq->regmap, BQ25790_CHRG_CTRL_1,
				 BQ25790_WATCHDOG_MASK, wd_reg_val);
	if (ret)
		return ret;

	ret = power_supply_get_battery_info(bq->charger, &bat_info);
	if (ret) {
		dev_warn(bq->dev, "battery info missing, default values will be applied\n");

		bat_info->constant_charge_current_max_ua = BQ25790_ICHRG_I_DEF_uA;
		bat_info->constant_charge_voltage_max_uv = BQ25790_VREG_V_DEF_uV;
		bat_info->precharge_current_ua = BQ25790_PRECHRG_I_DEF_uA;
		bat_info->charge_term_current_ua = BQ25790_TERMCHRG_I_DEF_uA;
		bq->init_data.ichg_max = BQ25790_ICHRG_I_MAX_uA;
		bq->init_data.vreg_max = BQ25790_VREG_V_MAX_uV;
	} else {
		bq->init_data.ichg_max = bat_info->constant_charge_current_max_ua;

		bq->init_data.vreg_max = bat_info->constant_charge_voltage_max_uv;
	}

	ret = bq25790_set_ichrg_curr(bq,
				bat_info->constant_charge_current_max_ua);
	if (ret)
		goto err_out;

	ret = bq25790_set_prechrg_curr(bq, bat_info->precharge_current_ua);
	if (ret)
		goto err_out;

	ret = bq25790_set_chrg_volt(bq,
				bat_info->constant_charge_voltage_max_uv);
	if (ret)
		goto err_out;

	ret = bq25790_set_term_curr(bq, bat_info->charge_term_current_ua);
	if (ret)
		goto err_out;

	ret = bq25790_set_input_volt_lim(bq, bq->init_data.vlim);
	if (ret)
		goto err_out;

	ret = bq25790_set_input_curr_lim(bq, bq->init_data.ilim);
	if (ret)
		goto err_out;

err_out:
	return ret;
}

static int bq25790_parse_dt(struct bq25790_device *bq)
{
	int ret;

	ret = device_property_read_u32(bq->dev, "ti,watchdog-timeout-ms",
				       &bq->watchdog_timer);
	if (ret)
		bq->watchdog_timer = BQ25790_WATCHDOG_DIS;

	if (bq->watchdog_timer > BQ25790_WATCHDOG_MAX ||
	    bq->watchdog_timer < BQ25790_WATCHDOG_DIS)
		return -EINVAL;

	ret = device_property_read_u32(bq->dev,
				       "input-voltage-limit-microvolt",
				       &bq->init_data.vlim);
	if (ret)
		bq->init_data.vlim = BQ25790_VINDPM_DEF_uV;

	if (bq->init_data.vlim > BQ25790_VINDPM_V_MAX_uV ||
	    bq->init_data.vlim < BQ25790_VINDPM_V_MIN_uV)
		return -EINVAL;

	ret = device_property_read_u32(bq->dev,
				       "input-current-limit-microamp",
				       &bq->init_data.ilim);
	if (ret)
		bq->init_data.ilim = BQ25790_IINDPM_DEF_uA;

	if (bq->init_data.ilim > BQ25790_IINDPM_I_MAX_uA ||
	    bq->init_data.ilim < BQ25790_IINDPM_I_MIN_uA)
		return -EINVAL;

	return 0;
}

static int bq25790_probe(struct i2c_client *client,
			 const struct i2c_device_id *id)
{
	struct device *dev = &client->dev;
	struct bq25790_device *bq;
	int ret;

	bq = devm_kzalloc(dev, sizeof(*bq), GFP_KERNEL);
	if (!bq)
		return -ENOMEM;

	bq->client = client;
	bq->dev = dev;

	mutex_init(&bq->lock);

	strncpy(bq->model_name, id->name, I2C_NAME_SIZE);

	bq->regmap = devm_regmap_init_i2c(client, &bq25790_regmap_config);
	if (IS_ERR(bq->regmap)) {
		dev_err(dev, "Failed to allocate register map\n");
		return PTR_ERR(bq->regmap);
	}

	i2c_set_clientdata(client, bq);

	ret = bq25790_parse_dt(bq);
	if (ret) {
		dev_err(dev, "Failed to read device tree properties%d\n", ret);
		return ret;
	}

	/* OTG reporting */
	bq->usb2_phy = devm_usb_get_phy(dev, USB_PHY_TYPE_USB2);
	if (!IS_ERR_OR_NULL(bq->usb2_phy)) {
		INIT_WORK(&bq->usb_work, bq25790_usb_work);
		bq->usb_nb.notifier_call = bq25790_usb_notifier;
		usb_register_notifier(bq->usb2_phy, &bq->usb_nb);
	}

	bq->usb3_phy = devm_usb_get_phy(dev, USB_PHY_TYPE_USB3);
	if (!IS_ERR_OR_NULL(bq->usb3_phy)) {
		INIT_WORK(&bq->usb_work, bq25790_usb_work);
		bq->usb_nb.notifier_call = bq25790_usb_notifier;
		usb_register_notifier(bq->usb3_phy, &bq->usb_nb);
	}

	if (client->irq) {
		ret = devm_request_threaded_irq(dev, client->irq, NULL,
						bq25790_irq_handler_thread,
						IRQF_TRIGGER_FALLING |
						IRQF_ONESHOT,
						dev_name(&client->dev), bq);
		if (ret)
			goto error_out;
	}

	ret = bq25790_power_supply_init(bq, dev);
	if (ret) {
		dev_err(dev, "Failed to register power supply\n");
		goto error_out;
	}

	ret = bq25790_hw_init(bq);
	if (ret) {
		dev_err(dev, "Cannot initialize the chip.\n");
		goto error_out;
	}

	return ret;
error_out:
	if (!IS_ERR_OR_NULL(bq->usb2_phy))
		usb_unregister_notifier(bq->usb2_phy, &bq->usb_nb);

	if (!IS_ERR_OR_NULL(bq->usb3_phy))
		usb_unregister_notifier(bq->usb3_phy, &bq->usb_nb);
	return ret;
}

static const struct i2c_device_id bq25790_i2c_ids[] = {
	{ BQ25790_NAME, 0 },
	{ BQ25792_NAME, 1 },
	{},
};
MODULE_DEVICE_TABLE(i2c, bq25790_i2c_ids);

static const struct of_device_id bq25790_of_match[] = {
	{ .compatible = "ti,bq25790", },
	{ .compatible = "ti,bq25792", },
	{ },
};
MODULE_DEVICE_TABLE(of, bq25790_of_match);

static const struct acpi_device_id bq25790_acpi_match[] = {
	{BQ25790_NAME, 0},
	{BQ25792_NAME, 0},
	{},
};
MODULE_DEVICE_TABLE(acpi, bq25790_acpi_match);

static struct i2c_driver bq25790_driver = {
	.driver = {
		.name = "bq25790-charger",
		.of_match_table = bq25790_of_match,
		.acpi_match_table = ACPI_PTR(bq25790_acpi_match),
	},
	.probe = bq25790_probe,
	.id_table = bq25790_i2c_ids,
};
module_i2c_driver(bq25790_driver);

MODULE_AUTHOR("Dan Murphy <dmurphy@ti.com>");
MODULE_AUTHOR("Ricardo Rivera-Matos <r-rivera-matos@ti.com>");
MODULE_DESCRIPTION("bq25790 charger driver");
MODULE_LICENSE("GPL v2");


/* SPDX-License-Identifier: GPL-2.0-only */
// BQ25790 Charger Driver
// Copyright (C) 2020 Texas Instruments Incorporated - http://www.ti.com/

#ifndef _BQ25790_CHARGER_H
#define _BQ25790_CHARGER_H

#define BQ25790_MANUFACTURER	"Texas Instruments"
#define BQ25790_NAME		"bq25790"
#define BQ25792_NAME		"bq25792"

#define BQ25790_MIN_SYS_V	0x00
#define BQ25790_CHRG_V_LIM_MSB	0x01
#define BQ25790_CHRG_V_LIM_LSB	0x02
#define BQ25790_CHRG_I_LIM_MSB	0x03
#define BQ25790_CHRG_I_LIM_LSB	0x04
#define BQ25790_INPUT_V_LIM	0x05
#define BQ25790_INPUT_I_LIM_MSB	0x06
#define BQ25790_INPUT_I_LIM_LSB	0x07
#define BQ25790_PRECHRG_CTRL	0x08
#define BQ25790_TERM_CTRL	0x09
#define BQ25790_RECHRG_CTRL	0x0a
#define BQ25790_VOTG_REG	0x0b
#define BQ25790_IOTG_REG	0x0d
#define BQ25790_TIMER_CTRL	0x0e
#define BQ25790_CHRG_CTRL_0	0x0f
#define BQ25790_CHRG_CTRL_1	0x10
#define BQ25790_CHRG_CTRL_2	0x11
#define BQ25790_CHRG_CTRL_3	0x12
#define BQ25790_CHRG_CTRL_4	0x13
#define BQ25790_CHRG_CTRL_5	0x14
#define BQ25790_MPPT_CTRL	0x15
#define BQ25790_TEMP_CTRL	0x16
#define BQ25790_NTC_CTRL_0	0x17
#define BQ25790_NTC_CTRL_1	0x18
#define BQ25790_ICO_I_LIM	0x19
#define BQ25790_CHRG_STAT_0	0x1b
#define BQ25790_CHRG_STAT_1	0x1c
#define BQ25790_CHRG_STAT_2	0x1d
#define BQ25790_CHRG_STAT_3	0x1e
#define BQ25790_CHRG_STAT_4	0x1f
#define BQ25790_FAULT_STAT_0	0x20
#define BQ25790_FAULT_STAT_1	0x21
#define BQ25790_CHRG_FLAG_0	0x22
#define BQ25790_CHRG_FLAG_1	0x23
#define BQ25790_CHRG_FLAG_2	0x24
#define BQ25790_CHRG_FLAG_3	0x25
#define BQ25790_FAULT_FLAG_0	0x26
#define BQ25790_FAULT_FLAG_1	0x27
#define BQ25790_CHRG_MSK_0	0x28
#define BQ25790_CHRG_MSK_1	0x29
#define BQ25790_CHRG_MSK_2	0x2a
#define BQ25790_CHRG_MSK_3	0x2b
#define BQ25790_FAULT_MSK_0	0x2c
#define BQ25790_FAULT_MSK_1	0x2d
#define BQ25790_ADC_CTRL	0x2e
#define BQ25790_FN_DISABE_0	0x2f
#define BQ25790_FN_DISABE_1	0x30
#define BQ25790_ADC_IBUS	0x31
#define BQ25790_ADC_IBAT_MSB	0x33
#define BQ25790_ADC_IBAT_LSB	0x34
#define BQ25790_ADC_VBUS_MSB	0x35
#define BQ25790_ADC_VBUS_LSB	0x36
#define BQ25790_ADC_VAC1	0x37
#define BQ25790_ADC_VAC2	0x39
#define BQ25790_ADC_VBAT_MSB	0x3b
#define BQ25790_ADC_VBAT_LSB	0x3c
#define BQ25790_ADC_VSYS_MSB	0x3d
#define BQ25790_ADC_VSYS_LSB	0x3e
#define BQ25790_ADC_TS		0x3f
#define BQ25790_ADC_TDIE	0x41
#define BQ25790_ADC_DP		0x43
#define BQ25790_ADC_DM		0x45
#define BQ25790_DPDM_DRV	0x47
#define BQ25790_PART_INFO	0x48

#define BQ25790_CHRG_EN		BIT(5)
#define BQ25790_ADC_EN		BIT(7)

/* Charger Status 1 */
#define BQ25790_CHG_STAT_MSK	GENMASK(7, 5)
#define BQ25790_NOT_CHRGING	0
#define BQ25790_TRICKLE_CHRG	BIT(5)
#define BQ25790_PRECHRG		BIT(6)
#define BQ25790_FAST_CHRG	(BIT(5) | BIT(6))
#define BQ25790_TAPER_CHRG	BIT(7)
#define BQ25790_TOP_OFF_CHRG	(BIT(6) | BIT(7))
#define BQ25790_TERM_CHRG	(BIT(5) | BIT(6) | BIT(7))
#define BQ25790_VBUS_PRESENT	BIT(0)

#define BQ25790_VBUS_STAT_MSK	GENMASK(4, 1)
#define BQ25790_USB_SDP		BIT(1)
#define BQ25790_USB_CDP		BIT(2)
#define BQ25790_USB_DCP		(BIT(1) | BIT(2))
#define BQ25790_HVDCP		BIT(3)
#define BQ25790_UNKNOWN_3A	(BIT(3) | BIT(1))
#define BQ25790_NON_STANDARD	(BIT(3) | BIT(2))
#define BQ25790_OTG_MODE	(BIT(3) | BIT(2) | BIT(1))
#define BQ25790_UNQUAL_ADAPT	BIT(4)
#define BQ25790_DIRECT_PWR	(BIT(4) | BIT(2) | BIT(1))

/* Charger Status 4 */
#define BQ25790_TEMP_HOT	BIT(0)
#define BQ25790_TEMP_WARM	BIT(1)
#define BQ25790_TEMP_COOL	BIT(2)
#define BQ25790_TEMP_COLD	BIT(3)
#define BQ25790_TEMP_MASK	GENMASK(3, 0)

#define BQ25790_OTG_OVP		BIT(5)
#define BQ25790_VSYS_OVP	BIT(6)
#define BQ25790_VSYS_STAT	BIT(4)

#define BQ25790_PRECHRG_CUR_MASK		GENMASK(5, 0)
#define BQ25790_PRECHRG_CURRENT_STEP_uA		40000
#define BQ25790_PRECHRG_I_MIN_uA		40000
#define BQ25790_PRECHRG_I_MAX_uA		2000000
#define BQ25790_PRECHRG_I_DEF_uA		120000
#define BQ25790_TERMCHRG_CUR_MASK		GENMASK(4, 0)
#define BQ25790_TERMCHRG_CURRENT_STEP_uA	40000
#define BQ25790_TERMCHRG_I_MIN_uA		40000
#define BQ25790_TERMCHRG_I_MAX_uA		1000000
#define BQ25790_TERMCHRG_I_DEF_uA		200000
#define BQ25790_ICHRG_CURRENT_STEP_uA		10000
#define BQ25790_ICHRG_I_MIN_uA			50000
#define BQ25790_ICHRG_I_MAX_uA			5000000
#define BQ25790_ICHRG_I_DEF_uA			1000000

#define BQ25790_VREG_V_MAX_uV	18800000
#define BQ25790_VREG_V_MIN_uV	3000000
#define BQ25790_VREG_V_DEF_uV	3600000
#define BQ25790_VREG_V_STEP_uV	10000

#define BQ25790_IINDPM_I_MIN_uA	100000
#define BQ25790_IINDPM_I_MAX_uA	3300000
#define BQ25790_IINDPM_STEP_uA	10000
#define BQ25790_IINDPM_DEF_uA	1000000

#define BQ25790_VINDPM_V_MIN_uV 3600000
#define BQ25790_VINDPM_V_MAX_uV 22000000
#define BQ25790_VINDPM_STEP_uV	100000
#define BQ25790_VINDPM_DEF_uV	3600000

#define BQ25790_ADC_VOLT_STEP_uV	1000
#define BQ25790_ADC_CURR_STEP_uA	1000

#define BQ25790_WATCHDOG_MASK	GENMASK(2, 0)
#define BQ25790_WATCHDOG_DIS	0
#define BQ25790_WATCHDOG_MAX	160000

#endif /* _BQ25790_CHARGER_H */

  • Hi,

    The TS pin voltage is in the TS COLD range:

    Regards,

    Jeff

  • Hi Jeff, 
    I have attached the latest register settings, still i am not able to charge the battery.
    Is there any setting or configuration am i missing ?

    * Created: Tue Oct 22 11:56:17 IDT 2024
    *
    * Format: Register Name  tab Character,\t  Register Address  tab Character,\t  Hexadecimal register value.
    * BQZ Container: Charger_2_00-bq25798.bqz
    *
    REG00	00	04
    REG01	01	00
    REG03	03	00
    REG05	05	2B
    REG06	06	00
    REG08	08	C3
    REG09	09	05
    REG0A	0A	23
    REG0B	0B	00
    REG0D	0D	BB
    REG0E	0E	39
    REG0F	0F	22
    REG10	10	80
    REG11	11	40
    REG12	12	B1
    REG13	13	01
    REG14	14	1E
    REG15	15	AA
    REG16	16	C0
    REG17	17	7A
    REG18	18	D5
    REG19	19	00
    REG1B	1B	8F
    REG1C	1C	6A
    REG1D	1D	01
    REG1E	1E	00
    REG1F	1F	00
    REG20	20	00
    REG21	21	00
    REG22	22	00
    REG23	23	00
    REG24	24	00
    REG25	25	00
    REG26	26	00
    REG27	27	00
    REG28	28	00
    REG29	29	00
    REG2A	2A	00
    REG2B	2B	00
    REG2C	2C	00
    REG2D	2D	00
    REG2E	2E	B0
    REG2F	2F	00
    REG30	30	00
    REG31	31	00
    REG33	33	00
    REG35	35	00
    REG37	37	00
    REG37	39	00
    REG3B	3B	00
    REG3D	3D	00
    REG3F	3F	00
    REG41	41	00
    REG43	43	00
    REG45	45	00
    REG47	47	00
    REG48	48	19

  • Hi,

    Now the charger is in IINDPM, meaning the system load plus charge current is requiring more power than the input current limit is allowing into the converter.  You also have the ILIM_HIZ pin resistors enabled, meaning they control the input current limit not the IINDPM register.  Your IINDPM is set to 1950mA.

    Regards,

    Jeff

  • Hi Jeff,

    What is maximum output load can be given by BQ25798/2 ? in Amps? 

  • Hi Master j,

    The max DC output current to SYS is 5A.  That includes the current going to the battery through the internal BATFET.  In most VBAT to VSYS configurations, the IC package gets too hot, forcing the converter into thermal regulation before reach the full 5A.  Using fSW= 750kHz and 2.2uH inductor gives the max efficiency and therefore lowest package heating and highest possible output current.

    Regards,

    Jeff

  • What is the maximum input current for BQ25798 if we are using USB DCP adapter?

  • Hi Master j,

    The maximum input current limit I2C settings is 3.3A.  The max DCP adapter setting that I have seen detected is 3A.

    Regards,

    Jeff

  • HI Jeff,

    Here is the scenario when we turn on the custom board and by setting 
    i2cset -y -f 0 0x6b 0x18 0xD5
    i2cset -y -f 0 0x6b 0x12 0xb4
    i2cset -y -f 0 0x6b 0x1b 0x0f

    i see charging is happening 

    But when i connect HDMI display, i am not able to read the register values and even battery not charging. as its starts showing XX values. why?

    how do i fix this?

  • H Master j,

    Regarding not being able to read the register values, I have no idea. As long as either VBUS or VBAT are above their respective UVLO voltages, the charger's I2C engine is up and running. Do you have an I2C sniffer or an oscope to independently read the I2C pulses being sent?  Is it possible the HDMI display is pulling too much current and crashing the battery?  Is the display connected to the charger's SYS output or BAT output?  With REG0x12=0xB4, the charger's minimum system regulation at SYS when V(BAT) < MINSYS is no longer enabled.  So, SYS = BAT all the time.  Also, I noticed that the charger input current = 1430mA so input power is 5V*1.4A = 7W.  Is that enough to power the HDMI display and charge the battery?  Seems low.  You can disabled the ILIM_HIZ pin resistors that clamp input current limit by setting REG0x14[1]=0.

    Regarding the I2C bits shown above, the status register bits REG0x1C are reporting taper charge, which is consistent with the ADC VBAT value.  This means the battery is almost fully charged.   

    Regards,

    Jeff

  • HI Jeff,

    With the 5V supply we are able to get the i2cdump with HDMI connection. but charging not happening bellow is the i2c dump.

    and attached BMS

    * Created: Tue Oct 22 11:56:17 IDT 2024
    *
    * Format: Register Name  tab Character,\t  Register Address  tab Character,\t  Hexadecimal register value.
    * BQZ Container: Charger_2_00-bq25798.bqz
    *
    REG00	00	04
    REG01	01	01
    REG03	03	00
    REG05	05	2a
    REG06	06	00
    REG08	08	f2
    REG09	09	05
    REG0A	0A	23
    REG0B	0B	00
    REG0D	0D	4B
    REG0E	0E	3d
    REG0F	0F	a2
    REG10	10	80
    REG11	11	40
    REG12	12	00
    REG13	13	01
    REG14	14	1E
    REG15	15	AA
    REG16	16	C0
    REG17	17	7A
    REG18	18	D5
    REG19	19	00
    REG1B	1B	8F
    REG1C	1C	6a
    REG1D	1D	01
    REG1E	1E	00
    REG1F	1F	00
    REG20	20	00
    REG22	22	00
    REG23	23	00
    REG24	24	00
    REG25	25	00
    REG26	26	00
    REG21	21	00
    REG27	27	00
    REG28	28	00
    REG29	29	00
    REG2A	2A	00
    REG2B	2B	00
    REG2C	2C	00
    REG2D	2D	00
    REG2E	2E	B0
    REG2F	2F	00
    REG30	30	00
    REG31	31	05
    REG33	33	00
    REG35	35	11
    REG37	37	11
    REG37	39	11
    REG3B	3B	0f
    REG3D	3D	10
    REG3F	3F	03
    REG41	41	00
    REG43	43	00
    REG45	45	00
    REG47	47	00
    REG48	48	19
      
     

  • Hi J,

    The charger is reporting Input current limit (IINDPM). With VBUS=5V and input current limit = 1.43A, the charger only has 5V*1.43A = 7.15W input power.  If the SYS load power is higher than 7.15W, the charger's converter is providing all of its available power to the SYS load so none remains for charging.  You can increase the input current limit by disabling the ILIM_HIZ resistors by setting REG0x14[1]=0 and then writing a higher value to REG0x06-07, assuming your input source can provide that much current. Or, if USB-C PD, you can negotiate a higher VBUS voltage. 

    Regards,

    Jeff

  • HI Jeff,

    Can you tell me how to get the battery voltage and current by reading resistors ? how do we calculate the battery voltage ? and current ?  

  • Hi J,

    Set EN_ADC REG0x2E[7] = 1 and then read registers 0x31-0x41.  The current  and die temp ADC registers are in 2's complement so your software has to account for that.

    Regards,

    Jeff

  • Hi Jeff,
    By disabling the ILIM_HIZ resistors by setting REG0x14[1]=0  i am able to see charging is happening with full load. Thank You.

    1. Now i have En_ADC  REG0x2E[7] = 1 bellow are the registers, Can you please tell me how do we get Battery Voltage.?

    2. When i hot pulg the usb power cable, Battery doesn't charge with out restart. how do i fix this?

    REG30 0x00
    REG31 0x07
    REG32 0xc0
    REG33 0x01
    REG34 0xde
    REG35 0x12
    REG36 0x32
    REG37 0x12
    REG38 0x32
    REG39 0x12
    REG3a 0xd7
    REG3b 0x10
    REG3c 0x63
    REG3d 0x10
    REG3e 0xb0
    REG3f 0x03
    REG40 0x60
    REG41 0x00

  • Hi J,

    Regarding 1, battery voltage is 0x3B = 0x10 and 0x3C=0x63.  0x1063 is 4195 decimal.  Calculation below:

    From the register map, each bit is 1mV:

    So 4.195V.

    Regarding 2, is it a legacy USB or USB-C cable?  What do the status and fault registers in 0x1B to 0x27 report after plug in and before restart?

    Regards,

    Jeff

  • Hi Jeff

    Thankyou, regarding calculation.

    Regarding  When i hot pulg the usb power cable, Battery doesn't charge with out restart. how do i fix this

    It is USB-C cable (standard adaptor-- 3.25A)

    Before restart
    REG1B 0x00
    REG1C 0x00
    REG1D 0x01
    REG1E 0x00
    REG1F 0x00
    REG20 0x00
    REG21 0x00
    REG22 0x00
    REG23 0x00
    REG24 0x00
    REG25 0x00
    REG26 0x00
    REG27 0x00

    After restart (charging is happening)
    REG1B 0x0F
    REG1C 0x6A
    REG1D 0x01
    REG1E 0x10
    REG1F 0x00
    REG20 0x00
    REG21 0x00
    REG22 0x00
    REG23 0x00
    REG24 0x00
    REG25 0x00
    REG26 0x00
    REG27 0x00

  • Hi J,

    The charger is reporting power not good.  So it appears the USB port is not providing a voltage > UVLO within the time frame the charger needs to determine power good.  Can you use an oscope to monitor the USB output = VBUS pin at plug in?  Below are the power up steps:

    9.3.4 Device Power Up from Input Source

    When an input source is present at VBUS, the device checks the input source voltage to turn on REGN LDO and all the bias circuits. It detects and sets the input current limit before the buck-boost converter is started. The power up sequence from input source is as listed below:

    1. The device begins the POR sequence when VVBUS > VVBUS_UVLOZ if there is a direct path from input to VBUS, or otherwise when VAC1 or VAC2 > VAC_PRESENT.

    2. 5 ms (typical) after a valid voltage is first present at either VBUS or VAC1/VAC2 pins, the charger starts the ACFET-RBFET detection, reads the resistance at PROG pin, and then configures the charger power on reset (POR) default register settings accordingly.

    3. If ACFET-RBFET are detected on the input source pathway, the corresponding ACDRV turns on the pair.

    4. 20 ms (typical) after valid voltage voltage presence, the I2C registers become accessible to the host. 5. As soon as VVBUS > VVBUS_UVLOZ (in step 1 or after step 3), VBUS_PRESENT_STAT is set to

    1. 150 ms (typical) later, REGN is turned on.

    6. Once REGN is on, the charger starts the poor source detection. After a good input source is detected (typically 30 ms if there is no retry), PG_STAT is set to 1, then the ADC reads the ILIM_HIZ pin voltage and VBUS voltage and updates the IINDPM and VINDPM registers accordingly.

    7. When the poor source detection completes, the charger performs D+/D- detection and updates the VBUS_STAT and IINDPM registers accordingly.

    8. 30 ms (typical) after the D+/D- detection completes, the converter starts switching to power SYS and charge the battery.

    Regards,

    Jeff

  • Hi jeff, 
    Now when there is no battery connected the board continuously reboots,  why?

  • Hi MJ,

    The input power is not high enough to provide the output power at SYS.  Increase the charger's input current limit to max by connecting ILIM_HIZ pin to REGN and leaving D+/D- pins floating.

    Regards,

    Jeff

  • HI Jeff,

    Thankyou,

    Bellow is the circuit 

    We have connected D+/D- pins to USB for switching to power SYS and charge the battery. (not updated in the bellow circuit )

    (and chip we are using is bq25798.)

  • HI MJ,

    Is the USB port a 500mA max SDP?  If so then the D+/D- lines have limited the input current to 500mA so max input power is 5V*500mA = 2.5W.

    Regards,

    Jeff