Tool/software:
HI team,
Do we have Linux code base on kernel 6.1.55 for BQ25180?
Thanks,
Leo
This thread has been locked.
If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.
Hi TI,
I paste code of bq2518x_set_precharge_current :
------------------------------------------------------
#define BQ2518X_PERCENTAGE 100
static int bq2518x_set_precharge_current(struct bq2518x_device *bq2518x,
int val)
{
int ret;
unsigned int icharge_value;
unsigned int times_of_icharge;
icharge_value = bq2518x_get_const_charge_current(bq2518x);
times_of_icharge = (icharge_value / val) * BQ2518X_PERCENTAGE;
switch (times_of_icharge) {
case BQ2518X_TIME_OF_ICHARGE01:
ret = regmap_update_bits(bq2518x->regmap, BQ2518X_CHARGERCTRL0,
BQ2518X_IPRECHG_MASK, (BQ2518X_TIME_OF_1X_TERM |
BQ2518X_TERMINATION_CURRENT_PERCENT05));
break;
case BQ2518X_TIME_OF_ICHARGE02:
ret = regmap_update_bits(bq2518x->regmap, BQ2518X_CHARGERCTRL0,
BQ2518X_IPRECHG_MASK, (BQ2518X_TIME_OF_1X_TERM |
BQ2518X_TERMINATION_CURRENT_PERCENT10));
break;
case BQ2518X_TIME_OF_ICHARGE03:
ret = regmap_update_bits(bq2518x->regmap, BQ2518X_CHARGERCTRL0,
BQ2518X_IPRECHG_MASK, (BQ2518X_TIME_OF_1X_TERM |
BQ2518X_TERMINATION_CURRENT_PERCENT20));
break;
case BQ2518X_TIME_OF_ICHARGE04:
ret = regmap_update_bits(bq2518x->regmap, BQ2518X_CHARGERCTRL0,
BQ2518X_IPRECHG_MASK, (BQ2518X_TIME_OF_2X_TERM |
BQ2518X_TERMINATION_CURRENT_PERCENT20));
break;
default:
ret = -EINVAL;
break;
}
return ret;
}
------------------------------------------------------
I don't understand why times_of_icharge = (icharge_value / val) * 100 in 2518x driver ? Because of this, the value of times_of_icharge can’t match BQ2518X_TIME_OF_ICHARGE01 ~ BQ2518X_TIME_OF_ICHARGE04 (5/10/20/40), so ret = -EINVAL and loading driver failed.
Could you help to confirm it and explain the cause of this issue ? Becasue of this, I'm not sure what value to configure in DTS to ensure that the driver can run properly .
WNC/Jason
Hello Leo,
The BQ25180 driver we have was developed for Linux kernel 5.15. It may work without modifications on 6.1.55, but there could be compatibility issues due to kernel API changes.
BQ2518x driver: https://e2e.ti.com/cfs-file/__key/communityserver-discussions-components-files/196/bq2518x_5F00_driver.tar.gz2
I recommend trying to compile it to check for compatibility. Let me know if you encounter any issues.
Best regards,
Alec
Hello Jason,
It appears the issue is related to the times_of_icharge calculation.
If val is configured to represent the precharge current in your DTS, then the correct calculation should be:
times_of_icharge = (val / icharge_value) * BQ2518X_PERCENTAGE;
For example, if you want the precharge current to be 5% of a 1A fast charge current (where icharge_value = 1,000,000uA), the calculation would be:
times_of_icharge = (50,000uA / 1,000,000uA) * 100 = 5%
This would match BQ2518X_TIME_OF_ICHARGE01, and properly set the precharge current.
I recommend modifying the calculation as shown above to ensure the correct configuration of the precharge current.
Without this adjustment, it doesn't seem that times_of_icharge will ever produce a valid value (5, 10, 20, or 40).
Let me know if this works and if you still encounter any issues.
Best regards,
Alec
Hi Alec,
1. BQ2518x driver you provided can not compile under kernel 6.1.55, because power_supply_get_battery_info is defined differently between kernel5.15 and kernel6.1.55. And I'm not sure if there are other underlying problems.
2. #define BQ2518X_PERCENTAGE 100
static int bq2518x_set_precharge_current(struct bq2518x_device *bq2518x, int val)
{
int ret;
unsigned int icharge_value;
unsigned int times_of_icharge;
times_of_icharge = (icharge_value / val) * BQ2518X_PERCENTAGE
case BQ2518X_TIME_OF_ICHARGE01 //5
case BQ2518X_TIME_OF_ICHARGE02 //10
case BQ2518X_TIME_OF_ICHARGE03 //20
case BQ2518X_TIME_OF_ICHARGE04 //40
default:
ret = -EINVAL;
.........
No matter what values of icharge_value and val, times_of_icharge is an integer greater than 100 or 0 rather than 5/10/20/40 . So ret = -EINVAL and loading driver failed .
Could you please tell me your email and discuss with teams online ?
Thanks!
WNC/Jason
Hello Jason,
Thank you for the update and for pointing out that the definition of power_supply_get_battery_info has changed between kernel versions. It's possible that this change could be affecting the driver compilation, but I'll need more details to confirm if it's a breaking change or if there's another underlying issue.
Could you provide the error logs or or compilation messages that specifically mention the issue with power_supply_get_battery_info?
Just to clarify, the expression to calculate times_of_icharge could result in values greater than 5, 10, 20, or 40. To avoid this, I recommend flipping the expression inside the parentheses, like this:
times_of_icharge = (val / icharge_value) * BQ2518X_PERCENTAGE;
I'll send my email to you through private messaging so we can continue the discussion offline.
Best regards,
Alec
Hi Alec,
I have fixed the compilation issue caused by power_supply_get_battery_info with reference to 2515x driver in kernel 6.1.55.
------------------------------
static int bq2518x_set_precharge_current(struct bq2518x_device *bq2518x,
int val)
{
int ret;
unsigned int icharge_value;
unsigned int times_of_icharge;
icharge_value = bq2518x_get_const_charge_current(bq2518x);
times_of_icharge = (val / icharge_value) * BQ2518X_PERCENTAGE;
printk("icharge_value=%d,val=%d,times_of_icharge=%d,%s,%d\n", icharge_value, val, times_of_icharge, __func__,__LINE__);
------------------------------
debug info : .icharge_value=770, val=50000, times_of_icharge=6400
My dts configuration is as follow :
------------------------------
bat: battery {
compatible = "simple-battery";
constant-charge-current-max-microamp = <1000000>;
precharge-current-microamp = <50000>;
constant-charge-voltage-max-microvolt = <4650000>;
};
&lpi2c1 {
#address-cells = <1>;
#size-cells = <0>;
clock-frequency = <400000>;
pinctrl-names = "default", "sleep";
pinctrl-0 = <&pinctrl_lpi2c1>;
pinctrl-1 = <&pinctrl_lpi2c1>;
status = "okay";
.........
bq25180: charger@6a {
compatible = "ti,bq25180";
reg = <0x6a>;
monitored-battery = <&bat>;
input-current-limit-microamp = <1000000>;
};
};
------------------------------
Is the value of times_of_icharge invalid because my DTS configuration is incorrect ?
BTW, Is the unit of current and voltage in DTS and driver micro or milli ? It seems the units aren't unified. such as
#define BQ2518X_ICHG_MAX_UA 1000
#define BQ2518X_VBAT_REG_MAX 4650000
And now I have a serious problem: the battery cannot be charged. Is it related to the issue above? Could you help with some suggestions to solve the problem .
current property :
root@imx93-14x14-lpddr4x-evk:~# cat /sys/devices/platform/soc@0/44000000.bus/44340000.i2c/i2c-0/0-006a/power_supply/bq2518x-mains/uevent
POWER_SUPPLY_NAME=bq2518x-mains
POWER_SUPPLY_TYPE=Mains
POWER_SUPPLY_ONLINE=1
POWER_SUPPLY_STATUS=Not charging
POWER_SUPPLY_HEALTH=Good
POWER_SUPPLY_INPUT_CURRENT_LIMIT=1100000
POWER_SUPPLY_MODEL_NAME=bq25180
POWER_SUPPLY_MANUFACTURER=Texas Instruments
POWER_SUPPLY_CONSTANT_CHARGE_VOLTAGE=3500000
POWER_SUPPLY_CONSTANT_CHARGE_CURRENT=370
POWER_SUPPLY_PRECHARGE_CURRENT=18
Hello Jason,
Thank you for the update. I'm glad to hear that the compilation issue has been resolved.
Is the value of times_of_icharge invalid because my DTS configuration is incorrect ?
From your DTS configuration, val = 50,000 is correctly set for 50mA precharge current. However, it seems that icharge_value = 770 is being set in mA, while your DTS defines the charge current as 1,000,000 uA.
The issue is likely that icharge_value is not being set correctly in the driver, or there's some inconsistency between the DTS and the driver code. Although your DTS specifies 1,000,000 for the charge current, it looks like the driver is reading 770mA (or 770,000uA). This discrepancy is causing the times_of_icharge calculation to be incorrect.
I would suggest double-checking the icharge_value being passed from the DTS to the driver. It seems like the driver might not be correctly interpreting the constant charge current value from the DTS. Once this value is correctly set, the times_of_icharge calculation should work as expected.
BTW, Is the unit of current and voltage in DTS and driver micro or milli ? It seems the units aren't unified. such as
In the Linux kernel power supply class, the units for current and voltage should always be micro. Your DTS appears to be using microamps for current, which is correct.
And now I have a serious problem: the battery cannot be charged. Is it related to the issue above? Could you help with some suggestions to solve the problem .
This issue is likely related to the DTS configuration or the driver issue. Once we've resolved the driver issue, we can determine whether the battery charging problem is related to that or if it's something else entirely. It would be helpful if you could provide a register dump of the device when the battery is not charging. This will help provide more insight into what's happening at the hardware level. If the battery still isn't charging after resolving the driver issue, we can take further steps to investigate the issue.
Best regards,
Alec
Hi Alec,
Thanks for your support !
The dts configured value is different from the displayed value because driver will convert it and then display it (voltage is similar to current ). The code is as follows :
------------------------------
static int bq2518x_get_const_charge_current(struct bq2518x_device *bq2518x)
{.........
ret = regmap_read(bq2518x->regmap, BQ2518X_ICHG_CTRL, &ichgctrl);
if (ret)
return ret;
ichg_code = ichgctrl & BQ2518X_ICHG_MASK;
if ((ichg_code + BQ2518X_ICHG_MIN_STEP) <= BQ2518X_ICHG_LIMIT)
ichg = ichg_code + BQ2518X_ICHG_MIN_STEP;
else
ichg = BQ2518X_ICHG_MAX_START + ((ichg_code - BQ2518X_ICHG_MAX_COMPARE)
* BQ2518X_ICHG_MAX_STEP);
return ichg;
}
static int bq2518x_set_const_charge_current(struct bq2518x_device *bq2518x,
int val)
{.........
if (val > BQ2518X_ICHG_MAX_UA || val < BQ2518X_ICHG_MIN_UA) {
return -EINVAL;
}
bq2518x_set_charge_disable(bq2518x, 1);
ret = regmap_update_bits(bq2518x->regmap, BQ2518X_ICHG_CTRL,
BQ2518X_ICHG_MASK, val);
if (ret)
return ret;
return bq2518x_set_charge_disable(bq2518x, 0);
}
------------------------------
My test :
echo 1000000 > /sys/devices/platform/soc@0/44000000.bus/44340000.i2c/i2c-0/0-006a/power_supply/bq2518x-mains/constant_charge_current
cat /sys/devices/platform/soc@0/44000000.bus/44340000.i2c/i2c-0/0-006a/power_supply/bq2518x-mains/constant_charge_current
POWER_SUPPLY_CONSTANT_CHARGE_CURRENT=370
echo 1000 > /sys/devices/platform/soc@0/44000000.bus/44340000.i2c/i2c-0/0-006a/power_supply/bq2518x-mains/constant_charge_current
cat /sys/devices/platform/soc@0/44000000.bus/44340000.i2c/i2c-0/0-006a/power_supply/bq2518x-mains/constant_charge_current
POWER_SUPPLY_CONSTANT_CHARGE_CURRENT=770
i2cdump -y -f 0 0x6a
root@imx93-14x14-lpddr4x-evk:~# i2cdump -y -f 0 0x6a
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: 61 00 00 00 7f 20 56 87 07 11 60 00 c0 ff ff ff !@B.@ V???`.?...
10: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ................
20: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ................
Hello Jason,
I see what's happening here.
The driver isn't converting a physical current value (in uA) into the proper register code. Instead, it writes the value directly into the register. Later, when reading back, the "get" function converts that 7-bit register code (which is in the range 0-127) back into a physical current using a specific formula.
The driver's range check in the "set" function uses:
This means the driver is not intrepreting the written value as a physical current in uA; it treats the value as an ICHG code (a register value).
When you read back the current, the process is as follows:
For example:
The register field uses a 7-bit mask (BQ2518X_ICHG_MASK = 0x7f), so only the lowest 7 bits of any written value are stored. In other words, any number larger than 127 is reduced to its remainder when divided by 128, which is mathematically equivalent to taking the number modulo 128.
For example:
That's why you see the values 64 and 104 in the register, which the "get" function converts to 370 and 770, respectively. Essentially, the value echoed isn't being converted from uA into a proper register code; it's just being reduced to a number between 0 and 127.
Best regards,
Alec
Hi Alec,
Could you help to update the driver for the issues above ? Now, I'm writing to the register by command of i2cset, otherwise if it is configured by DTS, the register value will not match the datasheet after driver running.such as :
static int bq2518x_set_batt_reg(struct bq2518x_device *bq2518x, int val)
{
int vbat_reg_code;
int ret;
if (val > BQ2518X_VBAT_REG_MAX || val < BQ2518X_VBAT_REG_MIN)
return -EINVAL;
ret = regmap_read(bq2518x->regmap, BQ2518X_VBAT_CTRL, &vbat_reg_code);
if (ret)
return ret;
vbat_reg_code = ((val - BQ2518X_VBAT_BASE_VOLT) / BQ2518X_VBAT_STEP_UV) &
(vbat_reg_code & BQ2518X_PG_MODE);
return regmap_write(bq2518x->regmap, BQ2518X_VBAT_CTRL, vbat_reg_code);
}
Hello Jason,
I have updated the set function to properly convert the physical current value (in mA) into the register code (ichg_code) expected by the hardware. The updated naming now reflects that the values are in mA. Below is the new set function code:
#define BQ2518X_ICHG_MIN_MA 5 #define BQ2518X_ICHG_MAX_MA 1000 #define BQ2518X_ICHG_MIN_STEP 5 #define BQ2518X_ICHG_LIMIT 35 #define BQ2518X_ICHG_MAX_START 40 #define BQ2518X_ICHG_MAX_COMPARE 31 #define BQ2518X_ICHG_MAX_STEP 10 static int bq2518x_set_const_charge_current(struct bq2518x_device *bq2518x, int val) { int ret; int ichg_code; if (val < BQ2518X_ICHG_MIN_MA || val > BQ2518X_ICHG_MAX_MA) return -EINVAL; if (val <= BQ2518X_ICHG_LIMIT) { ichg_code = val - BQ2518X_ICHG_MIN_STEP; } else { ichg_code = ((val - BQ2518X_ICHG_MAX_START) / BQ2518X_ICHG_MAX_STEP) + BQ2518X_ICHG_MAX_COMPARE; } ichg_code &= BQ2518X_ICHG_MASK; ret = bq2518x_set_charge_disable(bq2518x, 1); if (ret) return ret; ret = regmap_update_bits(bq2518x->regmap, BQ2518X_ICHG_CTRL, BQ2518X_ICHG_MASK, ichg_code); if (ret) return ret; return bq2518x_set_charge_disable(bq2518x, 0); }
Please test this updated code and let me know if it resolves the issue.
Best regards,
Alec
Hello Alec,
1. I have updated the function of bq2518x_set_const_charge_current, but the final register value is not what I want. Could you tell me how to config dts to ensure that the register values are as follows:
The current configuration of my DTS is as follows :
bat: battery {
compatible = "simple-battery";
constant-charge-current-max-microamp = <1000>;
precharge-current-microamp = <5000>;
constant-charge-voltage-max-microvolt = <4200000>;
};
...............
bq25180: charger@6a {
compatible = "ti,bq25180";
reg = <0x6a>;
monitored-battery = <&bat>;
input-current-limit-microamp = <1000000>;
};
2.There seems to be something wrong with the function of "bq2518x_set_charge_disable" which is also used in the code you provided above.
static int bq2518x_set_charge_disable(struct bq2518x_device *bq2518x, int val)
{
if (val)
val = 0;
else
val = BQ2518X_CHG_DIS;
return regmap_update_bits(bq2518x->regmap, BQ2518X_ICHG_CTRL,
BQ2518X_CHG_DIS_MASK, val);
}
----------------------------------------------
ret = bq2518x_set_charge_disable(bq2518x, 1);
ret = regmap_update_bits(bq2518x->regmap, BQ2518X_ICHG_CTRL, BQ2518X_ICHG_MASK, ichg_code);
return bq2518x_set_charge_disable(bq2518x, 0);
----------------------------------------------
bq2518x_set_charge_disable(bq2518x, 1) sets 0 to bit(7) of register BQ2518X_ICHG_CTRL. This means Battery Charging Enabled.
bq2518x_set_charge_disable(bq2518x, 0) means Battery Charging Disabled. Doesn't the logic seem right ?
3. I updated default value, and comment "bq2518x_hw_init", but the register value was still modifed, such as BQ2518X_ICHG_CTRL, BQ2518X_CHARGERCTRL1, BQ2518X_IC_CTRL and BQ2518X_IC_CTRL. Could you tell me where else changed the value of register except "bq2518x_hw_init" ?
Hello Jason,
Even if you comment out bq2518x_hw_init(), the regmap default cache still applies. The driver has an array of "default" register values (bq25180_reg_defaults[]), and regmap writes these to the hardware on probe — regardless of whether bq2518x_hw_init() is called. That explains why you're still seeing the registers change from their true silicon defaults.
With the changes below, you should see the correct register values for your configuration:
1. Charge disable
By default, bit 7 (CHG_DIS) = 1 means "disable," but the driver's logic was inverted.
static int bq2518x_set_charge_disable(struct bq2518x_device *bq2518x, int val) { if (val) val = BQ2518X_CHG_DIS; else val = 0; return regmap_update_bits(bq2518x->regmap, BQ2518X_ICHG_CTRL, BQ2518X_CHG_DIS_MASK, val); }
This inverts the logic so passing 1 truly disables charging.
2. Constant charge current
Before, the code wrote val directly, assuming it was the register code. Now, we divide by 1000 to convert the DTS value (uA) into mA, then compute the register code.
static int bq2518x_set_const_charge_current(struct bq2518x_device *bq2518x, int val) { int ret; val /= 1000; ... return ret; }
3. Precharge current
Before, the function didn't convert uA to mA, and the fraction formula was reversed.
static int bq2518x_set_precharge_current(struct bq2518x_device *bq2518x, int val) { int ret; unsigned int icharge_value; unsigned int times_of_icharge; val /= 1000; icharge_value = bq2518x_get_const_charge_current(bq2518x); times_of_icharge = (val / icharge_value) * BQ2518X_PERCENTAGE; ... return ret; }
DTS Example
bat: battery { compatible = "simple-battery"; constant-charge-voltage-max-microvolt = <4200000>; constant-charge-current-max-microamp = <1000000>; precharge-current-microamp = <100000>; }; bq25180: charger@6b { compatible = "ti,bq25180"; reg = <0x6a>; monitored-battery = <&battery>; input-current-limit-microamp = <1000000>; };
Let me know if this resolves your issue or if you have any further questions.
Best regards,
Alec
Hello Alec,
With the changes above, register values are shown below
However, the values I expect are shown below:
Please help to check whether the dts configuration is correct. How should I configure to get the register values I want ?
bat: battery {
compatible = "simple-battery";
constant-charge-voltage-max-microvolt = <4200000>;
constant-charge-current-max-microamp = <1000000>;
precharge-current-microamp = <100000>;
};
bq25180: charger@6b {
compatible = "ti,bq25180";
reg = <0x6a>;
monitored-battery = <&battery>;
input-current-limit-microamp = <1000000>;
};
Hello Jason,
Thanks for your patience as we work through this.
It seems that Reg0x4, Reg0x6, Reg0x7, and Reg0x8 are not being set correctly, meaning that ICHG, BATOCP, Watchdog, and ILIM aren't being configured as expected.
Fix Reg0x8 (ILIM bits):
static int bq2518x_set_ilim_lvl(struct bq2518x_device *bq2518x, int val) { int i = 0; unsigned int array_size = ARRAY_SIZE(bq2518x_ilim_lvl_values); for (i = array_size - 1; i > 0; i--) { if (val >= bq2518x_ilim_lvl_values[i]) break; } return regmap_update_bits(bq2518x->regmap, BQ2518X_TMR_ILIM, BQ2518X_ILIM_MASK, i); }
Fix Reg0x4 (ICHG bits):
ret = power_supply_get_battery_info(bq2518x->battery, &bat_info);
I believe this will cause the driver to read the battery properties (like constant-charge-current-max-microamp) from the battery node (bq2518x->battery), where they're actually defined. Let me know if this fixes Reg0x4.
Fix Reg0x7 (Watchdog bits):
Fix Reg0x6 (BATOCP bits):
There are a few options to configure the BATOCP bits in Reg0x6. However, the easiest option is likely to add the following function to the driver:
#define BQ2518X_BATOCP_MASK 0xc0 #define BQ2518X_BATOCP_1500MA BIT(7) static int bq2518x_set_batocp_1500ma(struct bq2518x_device *bq2518x) { return regmap_update_bits(bq2518x->regmap, BQ2518X_CHARGERCTRL1, BQ2518X_BATOCP_MASK, BQ2518X_BATOCP_1500MA); }
Then, we can call this function in "bq2518x_hw_init()":
static int bq2518x_hw_init(struct bq2518x_device *bq2518x) { int ret; /* Disable watchdog timers */ /* Set ILIM */ /* ... */ ret = bq2518x_set_batocp_1500ma(bq2518x); if (ret) return ret; /* ... */ }
Let me know if these changes configure the registers as expected, and we can work from there.
Best regards,
Alec
Hi Alec,
Thanks for your support !
With the changes you suggested , register values are shown below:
only battery power
It seems that Reg0x4, Reg0x6, Reg0x7, and Reg0x8 are being set correctly, but it seems that Reg0x3 is not correct .
My current dts configuration:
bat: battery {
compatible = "simple-battery";
constant-charge-current-max-microamp = <1000000>;
precharge-current-microamp = <100000>;
constant-charge-voltage-max-microvolt = <4200000>;
};
bq25180: charger@6a {
compatible = "ti,bq25180";
reg = <0x6a>;
monitored-battery = <&bat>;
input-current-limit-microamp = <1100000>;
};
cat /sys/devices/platform/soc@0/44000000.bus/44340000.i2c/i2c-0/0-006a/power_supply/bq2518x-mains/uevent
POWER_SUPPLY_NAME=bq2518x-mains
POWER_SUPPLY_TYPE=Mains
POWER_SUPPLY_ONLINE=0
POWER_SUPPLY_STATUS=Discharging
POWER_SUPPLY_HEALTH=Good
POWER_SUPPLY_INPUT_CURRENT_LIMIT=1100000
POWER_SUPPLY_MODEL_NAME=bq25180
POWER_SUPPLY_MANUFACTURER=Texas Instruments
POWER_SUPPLY_CONSTANT_CHARGE_VOLTAGE=3500000
POWER_SUPPLY_CONSTANT_CHARGE_CURRENT=370
POWER_SUPPLY_PRECHARGE_CURRENT=18
cat /sys/devices/platform/soc@0/44000000.bus/44340000.i2c/i2c-0/0-006a/power_supply/bq2518x-battery/uevent
POWER_SUPPLY_NAME=bq2518x-battery
POWER_SUPPLY_TYPE=Battery
POWER_SUPPLY_CONSTANT_CHARGE_CURRENT_MAX=1000000
POWER_SUPPLY_CONSTANT_CHARGE_VOLTAGE_MAX=4200000
external power + battery
cat /sys/devices/platform/soc@0/44000000.bus/44340000.i2c/i2c-0/0-006a/power_supply/bq2518x-mains/uevent
POWER_SUPPLY_NAME=bq2518x-mains
POWER_SUPPLY_TYPE=Mains
POWER_SUPPLY_ONLINE=1
POWER_SUPPLY_STATUS=Not charging
POWER_SUPPLY_HEALTH=Good
POWER_SUPPLY_INPUT_CURRENT_LIMIT=1100000
POWER_SUPPLY_MODEL_NAME=bq25180
POWER_SUPPLY_MANUFACTURER=Texas Instruments
POWER_SUPPLY_CONSTANT_CHARGE_VOLTAGE=3500000
POWER_SUPPLY_CONSTANT_CHARGE_CURRENT=370
POWER_SUPPLY_PRECHARGE_CURRENT=18
only external power
cat /sys/devices/platform/soc@0/44000000.bus/44340000.i2c/i2c-0/0-006a/power_supply/bq2518x-mains/uevent
POWER_SUPPLY_NAME=bq2518x-mains
POWER_SUPPLY_TYPE=Mains
POWER_SUPPLY_ONLINE=1
POWER_SUPPLY_STATUS=Charging
POWER_SUPPLY_HEALTH=Good
POWER_SUPPLY_INPUT_CURRENT_LIMIT=1100000
POWER_SUPPLY_MODEL_NAME=bq25180
POWER_SUPPLY_MANUFACTURER=Texas Instruments
POWER_SUPPLY_CONSTANT_CHARGE_VOLTAGE=3500000
POWER_SUPPLY_CONSTANT_CHARGE_CURRENT=370
POWER_SUPPLY_PRECHARGE_CURRENT=18
Finally as described above,is POWER_SUPPLY_STATUS incorrect ? By the way, how do I check the battery's remaining power and the conditions that trigger charging ?
Hello Jason,
I'm happy to hear that those registers are now being set correctly with the recent fixes.
Regarding Reg0x3, the current logic incorrectly clears all the bits, which is why we are seeing 0x00. Here's an updated function that preserves bit 7 and sets the lower bits correctly:
static int bq2518x_set_batt_reg(struct bq2518x_device *bq2518x, int val) { int vbat_reg_code; int ret; if (val > BQ2518X_VBAT_REG_MAX || val < BQ2518X_VBAT_REG_MIN) return -EINVAL; ret = regmap_read(bq2518x->, BQ2518X_VBAT_CTRL, &vbat_reg_code); if (ret) return ret; vbat_reg_code = (vbat_reg_code & BQ2518X_PG_MODE) | (((val - BQ2518X_VBAT_BASE_VOLT) / BQ2518X_VBAT_STEP_UV) & ~BQ2518X_PG_MODE); return regmap_write(bq2518x->regmap, BQ2518X_VBAT_CTRL, vbat_reg_code); }
With this change, Reg0x3 should reflect the intended battery regulation voltage rather than 0x00.
As for POWER_SUPPLY_STATUS, it appears correct in each case. When no battery is present, the charger might report "constant voltage" or "charge done," which explains why POWER_SUPPLY_STATUS=Charging with only the input source connected. The BQ25180 can indicate charging states and faults, but it doesn't measure the battery's state of charge. For exact battery percentage or remaining capacity, you'd need a separate fuel gauge IC.
Let me know if this update resolves Reg0x3.
Best regards,
Alec
Hi Alec,
Thanks for your support !
I'm very sorry to tell you that I forgot to delete the script which automatically configured the registers, so the result last time was the role of the script rather than the role of modifying the code. I deleted the automatic script and reconfirmed the register values:
With the changes, Reg0x7 has been configured as expected. The values of other registers have changed but have not yet reached the expected value.
Hello Jason,
No problem — thanks for the update. It looks like Reg0x4, Reg0x5, Reg0x6, and Reg0x8 still aren't at the desired values when you disable your script.
Reg0x5:
Try changing the precharge current mask to:
#define BQ2518X_IPRECHG_MASK 0x70
I believe this mask should only cause the IPRECHG and ITERM bits to be updated.
Reg0x4:
Ensure that you are dividing by 1000 in bq2518x_set_const_charge_current() (i.e., val /= 1000;), so 1000000uA in DTS becomes 1000mA.
Reg0x6:
Ensure that you are using regmap_update_bits() with mask 0xC0 to set the BATOCP bits.
Reg0x8:
Ensure that your DTS sets input-current-limit-microamp = <1100000> for ILIM = 1.1A. Also, ensure that bq2518x_set_ilim_lvl() uses regmap_update_bits() to only change bits [2:0].
If you can share your updated functions (e.g., bq2518x_set_const_charge_current(), bq2518x_set_precharge_current(), etc.), I can quickly confirm the logic. Let me know if you have any questions — I'm here to help.
Best regards,
Alec
Hello Alec,
Thanks for your patient help during this time ! I'm sorry to tell you that I need to deal with an urgent matter temporarily, so I have to suspend BQ25180 driver debugging and I'll be back as soon as I've taken care of the urgent matter.
According to the modifications you provided above, driver initialized failed. The debug info is as follows :
wnc_debug100......icharge_value=1000,val=100,times_of_icharge=0,bq2518x_set_precharge_current,409
bq2518x-charger 0-006a: Cannot initialize the chip
bq2518x-charger: probe of 0-006a failed with error -22
I think that it may be interrupted at bq2518x_set_precharge_current. I attach the file of bq2518x_charger.c below, please help to check it.
Thanks.
// SPDX-License-Identifier: GPL-2.0 // bq2518x Battery Charger Driver // Copyright (C) 2020 Texas Instruments Incorporated - https://www.ti.com/ #include <linux/err.h> #include <linux/i2c.h> #include <linux/init.h> #include <linux/kernel.h> #include <linux/module.h> #include <linux/gpio/consumer.h> #include <linux/power_supply.h> #include <linux/regmap.h> #include <linux/types.h> #define BQ2518X_MANUFACTURER "Texas Instruments" #define BQ2518X_STAT0 0x00 #define BQ2518X_STAT1 0x01 #define BQ2518X_FLAG0 0x02 #define BQ2518X_VBAT_CTRL 0x03 #define BQ2518X_ICHG_CTRL 0x04 #define BQ2518X_CHARGERCTRL0 0x05 #define BQ2518X_CHARGERCTRL1 0x06 #define BQ2518X_IC_CTRL 0x07 #define BQ2518X_TMR_ILIM 0x08 #define BQ2518X_SHIP_RST 0x09 #define BQ2518X_SYS_REG 0x0A #define BQ2518X_TS_CONTROL 0x0B #define BQ2518X_MASK_ID 0x0C #define BQ2518X_DEFAULT_ICHG_UA 10000 #define BQ25180_DEFAULT_ILIM_UA 100000 #define BQ2518X_DEFAULT_VBAT_REG_UV 4200000 #define BQ2518X_DEFAULT_IPRECHARGE_UA 2500 #define BQ2518X_DIVISOR 65536 #define BQ2518X_VBAT_BASE_VOLT 3500000 #define BQ2518X_VBAT_REG_MAX 4650000 #define BQ2518X_VBAT_REG_MIN 3600000 #define BQ2518X_VBAT_STEP_UV 10000 #define BQ2518X_UV_FACTOR 1000000 #define BQ2518X_VBAT_MULTIPLIER 6 #define BQ2518X_ICHG_DIVISOR 52429 #define BQ2518X_ICHG_CURR_STEP_THRESH_UA 318750 #define BQ2518X_ICHG_MIN_UA 5 #define BQ2518X_ICHG_MAX_UA 1000 //jason #define BQ2518X_ICHG_MIN_MA 5 #define BQ2518X_ICHG_MAX_MA 1000 #define BQ2518X_ICHG_RNG_1B0_UA 1250 #define BQ2518X_ICHG_RNG_1B1_UA 2500 #define BQ2518X_VLOWV_SEL_1B0_UV 3000000 #define BQ2518X_VLOWV_SEL_1B1_UV 2800000 #define BQ2518X_PRECHRG_ICHRG_RNGE_1875_UA 18750 #define BQ2518X_PRECHRG_ICHRG_RNGE_3750_UA 37500 #define BQ2518X_TWAKE2_MIN_US 1700000 #define BQ2518X_TWAKE2_MAX_US 2300000 #define BQ2518X_ILIM_150MA 0x2 #define BQ2518X_ILIM_MASK 0x7 #define BQ2518X_ILIM_MIN 50000 #define BQ2518X_ILIM_MAX 600000 #define BQ2518X_HEALTH_MASK 0xff #define BQ2518X_ICHGRNG_MASK 0x80 #define BQ2518X_STAT0_MASK 0xf0 #define BQ2518X_STAT1_MASK 0x1f #define BQ2518X_PRECHARGE_MASK 0x1f #define BQ2518X_CHG_STAT_STEP 4 #define BQ2518X_CHG_STAT (BIT(5) | BIT(6)) #define BQ2518X_CHG_STAT_NOT_CHARGING 0x00 #define BQ2518X_CHG_STAT_CONSTANT_CURRENT_CHARGING 0x01 #define BQ2518X_CHG_STAT_CONSTANT_VOLTAGE_CHARGING 0x02 #define BQ2518X_CHG_STAT_CHARGE_DONE 0x03 #define BQ2518X_PG_MODE BIT(7) #define BQ2518X_TS_COOL_STAT BIT(2) #define BQ2518X_TS_WARM_STAT BIT(3) #define BQ2518X_TS_COLD_STAT (BIT(4) | BIT(5)) #define BQ2518X_TS_HOT_STAT (BIT(6) | BIT(7)) #define BQ2518X_SAFETY_TIMER_EXP BIT(2) #define BQ2518X_EN_VBAT_READ BIT(3) #define BQ2518X_EN_ICHG_READ BIT(5) #define BQ2518X_VIN_GOOD BIT(0) #define BQ2518X_CHRG_DONE BIT(5) #define BQ2518X_CV_CHRG_MODE BIT(6) #define BQ2518X_VIN_OVP_STAT BIT(7) #define BQ2518X_WATCHDOG_DISABLE (BIT(0) | BIT(1)) #define BQ2518X_ICHARGE_RANGE BIT(7) #define BQ2518X_VLOWV_SEL BIT(5) #define BQ2518X_CHG_DIS BIT(7) #define BQ2518X_WATCHDOG_15S_ENABLE BIT(1) #define BQ2518X_CHG_DIS BIT(7) #define BQ2518X_ICHG_MASK 0x7f #define BQ2518X_TIME_OF_ICHARGE01 5 #define BQ2518X_TIME_OF_ICHARGE02 10 #define BQ2518X_TIME_OF_ICHARGE03 20 #define BQ2518X_TIME_OF_ICHARGE04 40 #define BQ2518X_TIME_OF_TERM_MASK 0x40 #define BQ2518X_TIME_OF_2X_TERM 0 #define BQ2518X_TIME_OF_1X_TERM BIT(6) #define BQ2518X_TIME_OF_2X_TERM_VALUE 2 #define BQ2518X_TIME_OF_1X_TERM_VALUE 1 #define BQ2518X_TERMINATION_CURRENT_MASK 0x30 #define BQ2518X_TERMINATION_CURRENT_DISABLE 0 #define BQ2518X_TERMINATION_CURRENT_PERCENT05 BIT(4) #define BQ2518X_TERMINATION_CURRENT_PERCENT10 BIT(5) #define BQ2518X_TERMINATION_CURRENT_PERCENT20 (BIT(4) | BIT(5)) //#define BQ2518X_IPRECHG_MASK 0x8f #define BQ2518X_IPRECHG_MASK 0x70 #define BQ2518X_ICHG_LIMIT 35 #define BQ2518X_ICHG_MIN_STEP 5 #define BQ2518X_ICHG_MAX_STEP 10 #define BQ2518X_ICHG_MAX_START 40 #define BQ2518X_ICHG_MAX_COMPARE 31 #define BQ2518X_CHG_DIS_MASK 0x80 static const int bq2518x_ilim_lvl_values[] = { 50000, 100000, 200000, 300000, 400000, 500000, 700000, 1100000 }; /** * struct bq2518x_init_data - * @ilim: input current limit * @ichg: fast charge current * @vbatreg: battery regulation voltage * @iprechg: precharge current */ struct bq2518x_init_data { int ilim; int ichg; int vbatreg; int iprechg; }; enum bq2518x_id { BQ25180, }; /** * struct bq2518x_device - * @mains: mains properties * @battery: battery properties * @regmap: register map structure * @dev: device structure * * * @model_name: string value describing device model * @device_id: value of device_id * @mains_online: boolean value indicating power supply online * * @init_data: charger initialization data structure */ struct bq2518x_device { struct power_supply *mains; struct power_supply *battery; struct regmap *regmap; struct device *dev; char model_name[I2C_NAME_SIZE]; int device_id; bool mains_online; struct bq2518x_init_data init_data; }; static const struct reg_default bq25180_reg_defaults[] = { {BQ2518X_STAT0, 0x0}, {BQ2518X_STAT1, 0x0}, {BQ2518X_VBAT_CTRL, 0x46}, {BQ2518X_ICHG_CTRL, 0x7F}, {BQ2518X_CHARGERCTRL0, 0x2C}, {BQ2518X_CHARGERCTRL1, 0x96}, {BQ2518X_IC_CTRL, 0x84}, {BQ2518X_TMR_ILIM, 0x4F}, {BQ2518X_SHIP_RST, 0x11}, {BQ2518X_SYS_REG, 0x40}, {BQ2518X_TS_CONTROL, 0x0}, {BQ2518X_MASK_ID, 0xC0}, }; static int bq2518x_wake_up(struct bq2518x_device *bq2518x) { int ret; int val; /* Read the STAT register if we can read it then the device is out * of ship mode */ ret = regmap_read(bq2518x->regmap, BQ2518X_STAT0, &val); if (ret) return ret; return 0; } static int bq2518x_update_ps_status(struct bq2518x_device *bq2518x) { bool dc = false; unsigned int val; int ret; ret = regmap_read(bq2518x->regmap, BQ2518X_STAT0, &val); if (ret) return ret; dc = val & BQ2518X_VIN_GOOD; ret = bq2518x->mains_online != dc; bq2518x->mains_online = dc; return ret; } static int bq2518x_disable_watchdog_timers(struct bq2518x_device *bq2518x) { int ret; ret = regmap_update_bits(bq2518x->regmap, BQ2518X_IC_CTRL, BQ2518X_WATCHDOG_DISABLE, BQ2518X_WATCHDOG_DISABLE); if (ret) return ret; return regmap_update_bits(bq2518x->regmap, BQ2518X_SYS_REG, BQ2518X_WATCHDOG_15S_ENABLE, 0); } static bool bq2518x_get_charge_disable(struct bq2518x_device *bq2518x) { int ret; int ichgctrl; ret = regmap_read(bq2518x->regmap, BQ2518X_ICHG_CTRL, &ichgctrl); if (ret) return ret; if (ichgctrl & BQ2518X_CHG_DIS) return false; return true; } static int bq2518x_set_charge_disable(struct bq2518x_device *bq2518x, int val) { if (val) // val = 0; //jason val = BQ2518X_CHG_DIS; else // val = BQ2518X_CHG_DIS; //jason val = 0; return regmap_update_bits(bq2518x->regmap, BQ2518X_ICHG_CTRL, BQ2518X_CHG_DIS_MASK, val); } static int bq2518x_get_const_charge_current(struct bq2518x_device *bq2518x) { int ret; unsigned int ichgctrl; unsigned int ichg_code; unsigned int ichg; ret = regmap_read(bq2518x->regmap, BQ2518X_ICHG_CTRL, &ichgctrl); if (ret) return ret; ichg_code = ichgctrl & BQ2518X_ICHG_MASK; if ((ichg_code + BQ2518X_ICHG_MIN_STEP) <= BQ2518X_ICHG_LIMIT) ichg = ichg_code + BQ2518X_ICHG_MIN_STEP; else ichg = BQ2518X_ICHG_MAX_START + ((ichg_code - BQ2518X_ICHG_MAX_COMPARE) * BQ2518X_ICHG_MAX_STEP); return ichg; } //jason #if 0 static int bq2518x_set_const_charge_current(struct bq2518x_device *bq2518x, int val) { int ret; if (val > BQ2518X_ICHG_MAX_UA || val < BQ2518X_ICHG_MIN_UA) return -EINVAL; bq2518x_set_charge_disable(bq2518x, 1); ret = regmap_update_bits(bq2518x->regmap, BQ2518X_ICHG_CTRL, BQ2518X_ICHG_MASK, val); if (ret) return ret; return bq2518x_set_charge_disable(bq2518x, 0); } #else static int bq2518x_set_const_charge_current(struct bq2518x_device *bq2518x, int val) { int ret; int ichg_code; val /= 1000; if (val < BQ2518X_ICHG_MIN_MA || val > BQ2518X_ICHG_MAX_MA) return -EINVAL; if (val <= BQ2518X_ICHG_LIMIT) { ichg_code = val - BQ2518X_ICHG_MIN_STEP; } else { ichg_code = ((val - BQ2518X_ICHG_MAX_START) / BQ2518X_ICHG_MAX_STEP) + BQ2518X_ICHG_MAX_COMPARE; } ichg_code &= BQ2518X_ICHG_MASK; ret = bq2518x_set_charge_disable(bq2518x, 1); if (ret) return ret; ret = regmap_update_bits(bq2518x->regmap, BQ2518X_ICHG_CTRL, BQ2518X_ICHG_MASK, ichg_code); if (ret) return ret; return bq2518x_set_charge_disable(bq2518x, 0); } #endif static int bq2518x_get_precharge_current(struct bq2518x_device *bq2518x) { int ret; int icharge_value; unsigned int chargectrl0; unsigned int persent; ret = regmap_read(bq2518x->regmap, BQ2518X_CHARGERCTRL0, &chargectrl0); if (ret) return ret; icharge_value = bq2518x_get_const_charge_current(bq2518x); if (chargectrl0 & BQ2518X_TIME_OF_TERM_MASK) persent = BQ2518X_TIME_OF_1X_TERM_VALUE; else persent = BQ2518X_TIME_OF_2X_TERM_VALUE; switch (chargectrl0 & BQ2518X_TERMINATION_CURRENT_MASK) { case BQ2518X_TERMINATION_CURRENT_DISABLE: icharge_value = 0; break; case BQ2518X_TERMINATION_CURRENT_PERCENT05: persent *= BQ2518X_TIME_OF_ICHARGE03; break; case BQ2518X_TERMINATION_CURRENT_PERCENT10: persent *= BQ2518X_TIME_OF_ICHARGE02; break; case BQ2518X_TERMINATION_CURRENT_PERCENT20: persent *= BQ2518X_TIME_OF_ICHARGE01; break; default: icharge_value = 0; break; } return (icharge_value / persent); } #define BQ2518X_PERCENTAGE 100 static int bq2518x_set_precharge_current(struct bq2518x_device *bq2518x, int val) { int ret; unsigned int icharge_value; unsigned int times_of_icharge; val /= 1000; icharge_value = bq2518x_get_const_charge_current(bq2518x); // times_of_icharge = (icharge_value / val) * BQ2518X_PERCENTAGE; times_of_icharge = (val / icharge_value) * BQ2518X_PERCENTAGE;//jason printk("wnc_debug100......icharge_value=%d,val=%d,times_of_icharge=%d,%s,%d\n", icharge_value, val, times_of_icharge, __func__,__LINE__); switch (times_of_icharge) { case BQ2518X_TIME_OF_ICHARGE01: ret = regmap_update_bits(bq2518x->regmap, BQ2518X_CHARGERCTRL0, BQ2518X_IPRECHG_MASK, (BQ2518X_TIME_OF_1X_TERM | BQ2518X_TERMINATION_CURRENT_PERCENT05)); break; case BQ2518X_TIME_OF_ICHARGE02: ret = regmap_update_bits(bq2518x->regmap, BQ2518X_CHARGERCTRL0, BQ2518X_IPRECHG_MASK, (BQ2518X_TIME_OF_1X_TERM | BQ2518X_TERMINATION_CURRENT_PERCENT10)); break; case BQ2518X_TIME_OF_ICHARGE03: ret = regmap_update_bits(bq2518x->regmap, BQ2518X_CHARGERCTRL0, BQ2518X_IPRECHG_MASK, (BQ2518X_TIME_OF_1X_TERM | BQ2518X_TERMINATION_CURRENT_PERCENT20)); break; case BQ2518X_TIME_OF_ICHARGE04: ret = regmap_update_bits(bq2518x->regmap, BQ2518X_CHARGERCTRL0, BQ2518X_IPRECHG_MASK, (BQ2518X_TIME_OF_2X_TERM | BQ2518X_TERMINATION_CURRENT_PERCENT20)); break; default: ret = -EINVAL; break; } return ret; } static int bq2518x_charging_status(struct bq2518x_device *bq2518x, union power_supply_propval *val) { unsigned int status; int ret; if (!bq2518x->mains_online) { val->intval = POWER_SUPPLY_STATUS_DISCHARGING; return 0; } ret = regmap_read(bq2518x->regmap, BQ2518X_STAT0, &status); if (ret) return ret; if (status & BQ2518X_STAT0_MASK) { switch ((status & BQ2518X_CHG_STAT) >> BQ2518X_CHG_STAT_STEP) { case BQ2518X_CHG_STAT_NOT_CHARGING: val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING; break; case BQ2518X_CHG_STAT_CONSTANT_CURRENT_CHARGING: val->intval = POWER_SUPPLY_STATUS_CHARGING; break; case BQ2518X_CHG_STAT_CONSTANT_VOLTAGE_CHARGING: val->intval = POWER_SUPPLY_STATUS_CHARGING; break; case BQ2518X_CHG_STAT_CHARGE_DONE: val->intval = POWER_SUPPLY_STATUS_FULL; break; default: val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING; break; } } return 0; } static int bq2518x_get_batt_reg(struct bq2518x_device *bq2518x) { int vbat_reg_code; int ret; ret = regmap_read(bq2518x->regmap, BQ2518X_VBAT_CTRL, &vbat_reg_code); if (ret) return ret; return (BQ2518X_VBAT_BASE_VOLT + ((vbat_reg_code&(~BQ2518X_PG_MODE)) * BQ2518X_VBAT_STEP_UV)); } static int bq2518x_set_batt_reg(struct bq2518x_device *bq2518x, int val) { int vbat_reg_code; int ret; if (val > BQ2518X_VBAT_REG_MAX || val < BQ2518X_VBAT_REG_MIN) return -EINVAL; ret = regmap_read(bq2518x->regmap, BQ2518X_VBAT_CTRL, &vbat_reg_code); if (ret) return ret; // vbat_reg_code = ((val - BQ2518X_VBAT_BASE_VOLT) / BQ2518X_VBAT_STEP_UV) & // (vbat_reg_code & BQ2518X_PG_MODE); vbat_reg_code = (vbat_reg_code & BQ2518X_PG_MODE) | (((val - BQ2518X_VBAT_BASE_VOLT) / BQ2518X_VBAT_STEP_UV) & ~BQ2518X_PG_MODE);//jason return regmap_write(bq2518x->regmap, BQ2518X_VBAT_CTRL, vbat_reg_code); } static int bq2518x_get_ilim_lvl(struct bq2518x_device *bq2518x) { int ret; int ilimctrl; ret = regmap_read(bq2518x->regmap, BQ2518X_TMR_ILIM, &ilimctrl); if (ret) return ret; return bq2518x_ilim_lvl_values[ilimctrl & BQ2518X_ILIM_MASK]; } static int bq2518x_set_ilim_lvl(struct bq2518x_device *bq2518x, int val) { int i = 0; unsigned int array_size = ARRAY_SIZE(bq2518x_ilim_lvl_values); for (i = array_size - 1; i > 0; i--) { if (val >= bq2518x_ilim_lvl_values[i]) break; } //return regmap_write(bq2518x->regmap, BQ2518X_TMR_ILIM, i); return regmap_update_bits(bq2518x->regmap, BQ2518X_TMR_ILIM, BQ2518X_ILIM_MASK, i);//jason } static int bq2518x_power_supply_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: return true; default: return false; } } static int bq2518x_charger_get_health(struct bq2518x_device *bq2518x, union power_supply_propval *val) { int health = POWER_SUPPLY_HEALTH_GOOD; int ret; unsigned int stat1; unsigned int ts_control; if (!bq2518x->mains_online) bq2518x_wake_up(bq2518x); ret = regmap_read(bq2518x->regmap, BQ2518X_TS_CONTROL, &ts_control); if (ret) return ret; ret = regmap_read(bq2518x->regmap, BQ2518X_STAT1, &stat1); if (ret) return ret; if (ts_control & BQ2518X_HEALTH_MASK) { switch (ts_control & BQ2518X_HEALTH_MASK) { case BQ2518X_TS_COOL_STAT: health = POWER_SUPPLY_HEALTH_COOL; break; case BQ2518X_TS_WARM_STAT: health = POWER_SUPPLY_HEALTH_WARM; break; default: if (ts_control & BQ2518X_TS_COLD_STAT) { health = POWER_SUPPLY_HEALTH_COLD; break; } if (ts_control & BQ2518X_TS_HOT_STAT) { health = POWER_SUPPLY_HEALTH_HOT; break; } health = POWER_SUPPLY_HEALTH_UNKNOWN; break; } } if (stat1 & BQ2518X_VIN_OVP_STAT) health = POWER_SUPPLY_HEALTH_OVERVOLTAGE; if (stat1 & BQ2518X_SAFETY_TIMER_EXP) health = POWER_SUPPLY_HEALTH_SAFETY_TIMER_EXPIRE; val->intval = health; return 0; } static int bq2518x_mains_set_property(struct power_supply *psy, enum power_supply_property prop, const union power_supply_propval *val) { struct bq2518x_device *bq2518x = power_supply_get_drvdata(psy); int ret; switch (prop) { case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE: ret = bq2518x_set_batt_reg(bq2518x, val->intval); break; case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT: ret = bq2518x_set_const_charge_current(bq2518x, val->intval); break; case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT: ret = bq2518x_set_ilim_lvl(bq2518x, val->intval); break; case POWER_SUPPLY_PROP_PRECHARGE_CURRENT: ret = bq2518x_set_precharge_current(bq2518x, val->intval); break; default: return -EINVAL; } return ret; } static int bq2518x_mains_get_property(struct power_supply *psy, enum power_supply_property prop, union power_supply_propval *val) { struct bq2518x_device *bq2518x = power_supply_get_drvdata(psy); int ret = 0; switch (prop) { case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT: ret = bq2518x_get_const_charge_current(bq2518x); if (ret < 0) return ret; val->intval = ret; break; case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE: ret = bq2518x_get_batt_reg(bq2518x); if (ret < 0) return ret; val->intval = ret; break; case POWER_SUPPLY_PROP_PRECHARGE_CURRENT: ret = bq2518x_get_precharge_current(bq2518x); if (ret < 0) return ret; val->intval = ret; break; case POWER_SUPPLY_PROP_ONLINE: val->intval = bq2518x->mains_online; break; case POWER_SUPPLY_PROP_HEALTH: ret = bq2518x_charger_get_health(bq2518x, val); if (ret) val->intval = POWER_SUPPLY_HEALTH_UNKNOWN; break; case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT: ret = bq2518x_get_ilim_lvl(bq2518x); if (ret < 0) return ret; val->intval = ret; break; case POWER_SUPPLY_PROP_MODEL_NAME: val->strval = bq2518x->model_name; break; case POWER_SUPPLY_PROP_MANUFACTURER: val->strval = BQ2518X_MANUFACTURER; break; case POWER_SUPPLY_PROP_STATUS: ret = bq2518x_charging_status(bq2518x, val); if (ret) return ret; break; default: return -EINVAL; } return ret; } static int bq2518x_battery_get_property(struct power_supply *psy, enum power_supply_property prop, union power_supply_propval *val) { struct bq2518x_device *bq2518x = power_supply_get_drvdata(psy); int ret; ret = bq2518x_update_ps_status(bq2518x); if (ret) return ret; switch (prop) { case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX: ret = bq2518x->init_data.vbatreg; if (ret < 0) return ret; val->intval = ret; break; case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX: ret = bq2518x->init_data.ichg; if (ret < 0) return ret; val->intval = ret; break; default: return -EINVAL; } return 0; } static const enum power_supply_property bq2518x_battery_properties[] = { POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX, POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX, }; static const enum power_supply_property bq2518x_mains_properties[] = { POWER_SUPPLY_PROP_ONLINE, POWER_SUPPLY_PROP_STATUS, POWER_SUPPLY_PROP_HEALTH, POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT, POWER_SUPPLY_PROP_MODEL_NAME, POWER_SUPPLY_PROP_MANUFACTURER, POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE, POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT, POWER_SUPPLY_PROP_PRECHARGE_CURRENT, }; static const struct power_supply_desc bq2518x_mains_desc = { .name = "bq2518x-mains", .type = POWER_SUPPLY_TYPE_MAINS, .get_property = bq2518x_mains_get_property, .set_property = bq2518x_mains_set_property, .properties = bq2518x_mains_properties, .num_properties = ARRAY_SIZE(bq2518x_mains_properties), .property_is_writeable = bq2518x_power_supply_property_is_writeable, }; static const struct power_supply_desc bq2518x_battery_desc = { .name = "bq2518x-battery", .type = POWER_SUPPLY_TYPE_BATTERY, .get_property = bq2518x_battery_get_property, .properties = bq2518x_battery_properties, .num_properties = ARRAY_SIZE(bq2518x_battery_properties), .property_is_writeable = bq2518x_power_supply_property_is_writeable, }; static int bq2518x_power_supply_register(struct bq2518x_device *bq2518x, struct device *dev, struct power_supply_config psy_cfg) { bq2518x->mains = devm_power_supply_register(bq2518x->dev, &bq2518x_mains_desc, &psy_cfg); if (IS_ERR(bq2518x->mains)) return -EINVAL; bq2518x->battery = devm_power_supply_register(bq2518x->dev, &bq2518x_battery_desc, &psy_cfg); if (IS_ERR(bq2518x->battery)) return -EINVAL; return 0; } #define BQ2518X_BATOCP_MASK 0xc0 #define BQ2518X_BATOCP_1500MA BIT(7) static int bq2518x_set_batocp_1500ma(struct bq2518x_device *bq2518x) { return regmap_update_bits(bq2518x->regmap, BQ2518X_CHARGERCTRL1, BQ2518X_BATOCP_MASK, BQ2518X_BATOCP_1500MA); } static int bq2518x_hw_init(struct bq2518x_device *bq2518x) { int ret; // struct power_supply_battery_info bat_info = { }; struct power_supply_battery_info *bat_info;//jason ret = bq2518x_disable_watchdog_timers(bq2518x); if (ret) return ret; if (bq2518x->init_data.ilim) { ret = bq2518x_set_ilim_lvl(bq2518x, bq2518x->init_data.ilim); if (ret) return ret; } ret = bq2518x_set_batocp_1500ma(bq2518x); if (ret) return ret; // ret = power_supply_get_battery_info(bq2518x->mains, &bat_info); ret = power_supply_get_battery_info(bq2518x->battery, &bat_info);//jason if (ret) { dev_warn(bq2518x->dev, "battery info missing, default values will be applied\n"); bq2518x->init_data.ichg = BQ2518X_DEFAULT_ICHG_UA; bq2518x->init_data.vbatreg = BQ2518X_DEFAULT_VBAT_REG_UV; bq2518x->init_data.iprechg = BQ2518X_DEFAULT_IPRECHARGE_UA; } else { bq2518x->init_data.ichg = bat_info->constant_charge_current_max_ua; bq2518x->init_data.vbatreg = bat_info->constant_charge_voltage_max_uv; bq2518x->init_data.iprechg = bat_info->precharge_current_ua; } ret = bq2518x_set_const_charge_current(bq2518x, bq2518x->init_data.ichg); if (ret) return ret; ret = bq2518x_set_batt_reg(bq2518x, bq2518x->init_data.vbatreg); if (ret) return ret; return bq2518x_set_precharge_current(bq2518x, bq2518x->init_data.iprechg); } static int bq2518x_read_properties(struct bq2518x_device *bq2518x) { int ret; ret = device_property_read_u32(bq2518x->dev, "input-current-limit-microamp", &bq2518x->init_data.ilim); if (ret) { switch (bq2518x->device_id) { case BQ25180: bq2518x->init_data.ilim = BQ25180_DEFAULT_ILIM_UA; break; } } return 0; } static bool bq2518x_volatile_register(struct device *dev, unsigned int reg) { switch (reg) { case BQ2518X_STAT0 ... BQ2518X_FLAG0: return true; default: return false; } } static const struct regmap_config bq25180_regmap_config = { .reg_bits = 8, .val_bits = 8, .max_register = BQ2518X_MASK_ID, .reg_defaults = bq25180_reg_defaults, .num_reg_defaults = ARRAY_SIZE(bq25180_reg_defaults), .cache_type = REGCACHE_RBTREE, .volatile_reg = bq2518x_volatile_register, }; static int bq2518x_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct device *dev = &client->dev; struct bq2518x_device *bq2518x; struct power_supply_config charger_cfg = {}; int ret; bq2518x = devm_kzalloc(dev, sizeof(*bq2518x), GFP_KERNEL); if (!bq2518x) return -ENOMEM; bq2518x->dev = dev; strncpy(bq2518x->model_name, id->name, I2C_NAME_SIZE); bq2518x->device_id = id->driver_data; switch (bq2518x->device_id) { case BQ25180: bq2518x->regmap = devm_regmap_init_i2c(client, &bq25180_regmap_config); break; } if (IS_ERR(bq2518x->regmap)) { dev_err(dev, "failed to allocate register map\n"); return PTR_ERR(bq2518x->regmap); } i2c_set_clientdata(client, bq2518x); charger_cfg.drv_data = bq2518x; charger_cfg.of_node = dev->of_node; ret = bq2518x_read_properties(bq2518x); if (ret) { dev_err(dev, "Failed to read device tree properties %d\n", ret); return ret; } ret = bq2518x_power_supply_register(bq2518x, dev, charger_cfg); if (ret) { dev_err(dev, "failed to register power supply\n"); return ret; } ret = bq2518x_hw_init(bq2518x); if (ret) { dev_err(dev, "Cannot initialize the chip\n"); return ret; } return 0; } static const struct i2c_device_id bq2518x_i2c_ids[] = { { "bq25180", BQ25180, }, {}, }; MODULE_DEVICE_TABLE(i2c, bq2518x_i2c_ids); static const struct of_device_id bq2518x_of_match[] = { { .compatible = "ti,bq25180", }, { }, }; MODULE_DEVICE_TABLE(of, bq2518x_of_match); static struct i2c_driver bq2518x_driver = { .driver = { .name = "bq2518x-charger", .of_match_table = bq2518x_of_match, }, .probe = bq2518x_probe, .id_table = bq2518x_i2c_ids, }; module_i2c_driver(bq2518x_driver); MODULE_AUTHOR("Texas Instruments"); MODULE_DESCRIPTION("bq2518x charger driver"); MODULE_LICENSE("GPL v2");
Hello Alec,
Append my current dts configuration :
bat: battery {
compatible = "simple-battery";
constant-charge-current-max-microamp = <1000000>;
precharge-current-microamp = <100000>;
constant-charge-voltage-max-microvolt = <4200000>;
};
bq25180: charger@6a {
compatible = "ti,bq25180";
reg = <0x6a>;
monitored-battery = <&bat>;
input-current-limit-microamp = <1100000>;
};
Thanks.
Hello Jason,
Not a problem! Thanks so much for all your patience and collaboration throughout this process!
You're right—there's an integer division issue with bq2518x_set_precharge_current().
Right now, bq2518x_set_precharge_current() has this:
times_of_icharge = (val / icharge_value) * 100;
Since val and icharge_value are both integers, (100 / 1000) becomes 0 rather than 0.1. That leads to times_of_icharge being zero and the function returning an error. To fix this, simply multiply first, then divide:
times_of_icharge = (val * BQ2518X_PERCENTAGE) / icharge_value;
This should result in (100 * 100) / 1000 = 10, correctly giving you 10% precharge. The switch statement should then pick the proper fraction (5%, 10%, 20%, 40%) instead of returning -EINVAL.
Let me know if this resolves the precharge current issue—always happy to help if anything comes up.
Best regards,
Alec
Hello Alec,
Thanks for your patience and professional support !
As expected, the precharge current issue has been resolved with the changes you suggested.
However, it seems that Reg0x6 and Reg0x8 are not being set correctly.
The code has been attached last time, please help to check this issue.
Thanks.
Hello Jason,
No problem—I'm glad to hear that Reg0x04 is now being set correctly.
I will look into this and follow up with you tomorrow (3/11).
Best regards,
Alec
Hello Alec,
By the way, could you provide the method to read the current temperature ?
Thanks.
Hello Jason,
Thanks for your patience.
It would be helpful if you could add debug print statements in bq2518x_hw_init() after each function call that updates Reg0x06 or Reg0x08. For example:
ret = bq2518x_set_batocp_1500ma(bq2518x); dev_info(bq2518x->dev, "bq2518x_set_batocp_1500ma ret=%d\n", ret); if (!ret) { unsigned int reg_val; regmap_read(bq2518x->regmap, BQ2518X_CHARGERCTRL1, ®_val); dev_info(bq2518x->dev, "CHARGERCTRL1 (Reg0x06) after BATOCP=0x%x\n", reg_val); } ... ret = bq2518x_set_ilim_lvl(bq2518x, bq2518x->init_data.ilim); dev_info(bq2518x->dev, "bq2518x_set_ilim_lvl ret=%d\n", ret); if (!ret) { unsigned int reg_val; regmap_read(bq2518x->regmap, BQ2518X_TMR_ILIM, ®_val); dev_info(bq2518x->dev, "TMR_ILIM (Reg0x08) after ILIM=0x%x\n", reg_val); }
If the registers show the correct values right after each call but revert later, it means something else is overwriting them. If they never show the updated bits at all, we know the calls didn't succeed or were skipped.
Regarding the battery temperature, the BQ25180 does not have an integrated ADC to report the temperature in degrees. Instead, it monitors the TS pin (typically connected to the battery pack's thermistor) and reports discrete temperature states in STAT1 (Reg0x01, bits [4:3]). These status bits report whether the battery is HOT, COLD, WARM, COOL, or NORMAL—but the charger can't provide an exact temperature value. If you need a more precise temperature measurement, you'd need a dedicated fuel gauge or external ADC to read the TS pin voltage.
Let me know what you find in the logs.
Best regards,
Alec
Hello Alec,
Added the debugging code above and the following information was printed :
It seems that something else is overwriting them. Where modified the two registers after bq2518x_hw_init() ?
Thanks.
Hello Jason,
After reviewing the driver, there's no code that writes to Reg0x06 (CHARGERCTRL1) or Reg0x08 (TMR_ILIM) after bq2518x_hw_init() completes. This means that something else is changing the values in these registers.
Check if you have any custom scripts or i2cset commands that could be writing to these registers. If you compile your kernel with CONFIG_REGMAP_DEBUG, you can see each register read/write in the kernel log (dmesg). This should help us pinpoint what is writing those values.
Let me know if you find any suspicious writes in the logs.
Best regards,
Alec
Hello Alec,
I'm sure the script related to register settings has been closed. I wonder if the problem is caused by watchdog, because I have encountered before, once watchdog is turned on( IC_CTRL REG=0x84), the register value of Reg0x06 and Reg0x08 will be changed. I hope this inspires you and helps us solve the problem, and I'm analyzing it in that direction.
Thanks.
Hello Jason,
Thanks for pointing that out! Yes, if the watchdog timer is enabled and there's no I2C transaction to "refresh" it before the 160s timeout, the BQ25180 will reset its registers to their default values. This is expected behavior. I initially assumed the watchdog was disabled, so I didn't consider this earlier.
Best regards,
Alec
Hello Alec,
Thanks so much for your professional explanation. It seems that root cause has been found and I'll look forward to your updating code.
Thanks.
Hello Jason,
Is the driver now working as expected? Do you have any remaining issues or questions, or has everything been resolved?
Best regards,
Alec
Hello Alec,
I tried to remove bq2518x_disable_watchdog_timers() from bq2518x_hw_init(), but Reg0x6 and Reg0x8 are still incorrect.
Could you help to give me some suggestion ?
Thanks.
Hello Alec,
Could you help to update driver code for the issue of Reg0x6 and reg0x8 incorrcet value ?
Thanks.
Hello Jason,
I notice from the STAT registers that the device is in TS HOT and DPPM. Could you try removing the input source and then reading the registers again?
Also, could you add bq2518x_disable_watchdog_timers() back into hw_init() to see if that prevents the registers from resetting?
Finally, I'm curious about your reg_defaults array—could you share those values as well?
Best regards,
Alec
Hello Alec,
I added bq2518x_disable_watchdog_timers() back into hw_init() and removed the input source, then read the registers :
my reg_defaults array :
static const struct reg_default bq25180_reg_defaults[] = {
{BQ2518X_STAT0, 0x0},
{BQ2518X_STAT1, 0x0},
{BQ2518X_VBAT_CTRL, 0x46},
{BQ2518X_ICHG_CTRL, 0x7F},
{BQ2518X_CHARGERCTRL0, 0x2C},
{BQ2518X_CHARGERCTRL1, 0x96},
{BQ2518X_IC_CTRL, 0x84},
{BQ2518X_TMR_ILIM, 0x4F},
{BQ2518X_SHIP_RST, 0x11},
{BQ2518X_SYS_REG, 0x40},
{BQ2518X_TS_CONTROL, 0x0},
{BQ2518X_MASK_ID, 0xC0},
};
Thanks.
Hello Jason,
Thanks for sharing your registers. It appears that Reg0x06 and Reg0x08 are still being overwritten by something.
There's nothing that should be overwriting Reg0x06 and Reg0x08 in the driver. I recommend searching your codebase and scripts for any references to these registers or the charger's I2C address (0x6A).
If you find anything referencing CHARGERCTRL1 (Reg0x06) or TMR_ILIM (Reg0x08), let me know. I suspect that's what's causing the unexpected revert.
Best regards,
Alec
Hello Alec,
Thanks for your support.
My script has been disabled, and I have not found anything referencing CHARGERCTRL1 (Reg0x06) or TMR_ILIM (Reg0x08). But I find that Reg0x06 and Reg0x08 were obviously reset. I think that if the two registers have been modified somewhere else, they wouldn't have been so coincidentally set to 0x56, 0x4d. Could you tell me what mechanism might reset them ? or any other suggestion ?
Thanks.
Hello Jason,
The watchdog timer will reset the registers if it expires, which could explain why Reg0x06 and Reg0x08 are reverting to their default values. Could you try writing ILIM and BATOCP while the adapter is connected (so VIN is valid) and then read them back to confirm whether they stay at your configured values?
Best regards,
Alec
Hello Alec,
I tried writing ILIM and BATOCP while the adapter was connected and then read them back, they stayed at the configured values instead of being reset.
I think the registers will be reset to "reg_defaults" when watchdog timer expires. However, the default values of Reg0x06 and Reg0x08 in "reg_defaults" are 0x96 and 0x4F. On the other side, the reset values of Reg0x06 and Reg0x08 are defined as 0x56 and 0x4D in datasheet. Does this mean Reg0x06 and Reg0x08 were reset by hardware? Could you think about what would trigger a hardware reset ?
I have not found anything referencing CHARGERCTRL1 (Reg0x06) or TMR_ILIM (Reg0x08) in my codebase, could you please give me some suggestion to solve this issue ?
Thanks.
Hello Alec,
In addition, Reg0x06 and Reg0x08 were overwrited after insmoding driver. After that, if reverting to 0x96 and 0x4D with "i2cset", they will not been overwrited again.
Thanks.
Hello Jason,
Thanks for the information.
On the other side, the reset values of Reg0x06 and Reg0x08 are defined as 0x56 and 0x4D in datasheet. Does this mean Reg0x06 and Reg0x08 were reset by hardware? Could you think about what would trigger a hardware reset ?
Yes, given that Reg0x06 and Reg0x08 are reset to their hardware default values (0x56 and 0x4D) instead of reg_defaults (0x96 and 0x4F), it seems that they are being reset by hardware. There are a few ways that the BQ25180 can reset its registers to their default values:
Best regards,
Alec
Hello Jason,
I wanted to check in and see if you found what was overwriting those registers.
Let me know if you have any questions.
Best regards,
Alec
Hi Alec,
I'm sorry for the late update because of an urgent project. But now I'm back to debug the BQ25180 driver.
I have not found anything referencing CHARGERCTRL1 (Reg0x06) or TMR_ILIM (Reg0x08) in my codebase, and I have not find anything overwriting those registers. I wonder that if the reset is caused by watchdog, why are the other registers not reset ?
I tried configuring Reg0x09[bit0 set to 0, bit7 set to 0, bit4-3 set to 00, bit6-5 set to 00] to avoid resetting, but the issue still hasn't been solved .
Could you please give me some suggestion to solve this issue ?
I tried the following test:
i2cdump -y -f 0 0x6a
cat /sys/devices/platform/soc@0/44000000.bus/44340000.i2c/i2c-0/0-006a/power_supply/bq2518x-mains/uevent
POWER_SUPPLY_INPUT_CURRENT_LIMIT=1100000,
It appear that the instruction does not dynamically refresh, so the printed information does not match the register value.
echo 1100000 > /sys/devices/platform/soc@0/44000000.bus/44340000.i2c/i2c-0/0-006a/power_supply/bq2518x-mains/input_current_limit
i2cdump -y -f 0 0x6a
echo 700000 > /sys/devices/platform/soc@0/44000000.bus/44340000.i2c/i2c-0/0-006a/power_supply/bq2518x-mains/input_current_limit
echo 1100000 > /sys/devices/platform/soc@0/44000000.bus/44340000.i2c/i2c-0/0-006a/power_supply/bq2518x-mains/input_current_limit
Hi Jason,
Thanks for all of the information.
Could you try modifying bq2518x_set_ilim_lvl() with the following? It performs a read-modify-write on the TMR_ILIM register, ensuring that only the ILIM bits are updated while preserving the other bits.
static int bq2518x_set_ilim_lvl(struct bq2518x_device *bq2518x, int val) { int ret, i = 0, tmr_val; unsigned int array_size = ARRAY_SIZE(bq2518x_ilim_lvl_values); for (i = array_size - 1; i > 0; i--) { if (val >= bq2518x_ilim_lvl_values[i]) break; } ret = regmap_read(bq2518x->regmap, BQ2518X_TMR_ILIM, &tmr_val); if (ret) return ret; tmr_val = (tmr_val & ~BQ2518X_ILIM_MASK) | (i & BQ2518X_ILIM_MASK); return regmap_write(bq2518x->regmap, BQ2518X_TMR_ILIM, tmr_val); }
Let me know if this allows ILIM to be set correctly.
Best regards,
Alec
Hi Alec,
Thanks for your support.
The ILIM has been set correctly with the modification you provided.
Now only issue of Reg06 remains, please help to solve it.
Thanks.
Hi Jason,
Great! I'm glad to hear that ILIM is now being set correctly.
To resolve the Reg0x06 issue, please try modifying bq2518x_set_batocp_1500ma() with the following change.
static int bq2518x_set_batocp_1500mA(struct bq2518x_device *bq2518x) { int ret, reg_val; ret = regmap_read(bq2518x->regmap, BQ2518X_CHARGERCTRL1, ®_val); if (ret) return ret; reg_val = (reg_val & ~BQ2518X_BATOCP_MASK) | (BQ2518X_BATOCP_1500MA & BQ2518X_BATOCP_MASK); return regmap_write(bq2518x->regmap, BQ2518X_CHARGERCTRL1, reg_val); }
Similar to the ILIM fix, this update performs a read-modify-write so that only the BATOCP bits are updated while preserving the other bits in the register.
Let me know if this resolves the issue.
Best regards,
Alec
Hi Alec,
Thanks for your professional support. I'm glad to tell you that the Reg06 issue has been resolved with your last change.
I still have some questions that I need your help.
1. I set register value with the command of i2cset, then executed "cat /sys/devices/platform/soc@0/44000000.bus/44340000.i2c/i2c-0/0-006a/power_supply/bq2518x-mains/uevent". I found that "cat" result was not updated in time.
Conversely, I excuted "echo 4200000 > /sys/devices/platform/soc@0/44000000.bus/44340000.i2c/i2c-0/0-006a/power_supply/bq2518x-mains/constant_charge_voltage", then register value was updated in time.
I'm worried that the "cat" result is not updated in time, like now I never see the "charging" state of POWER_SUPPLY_STATUS when the battery has worked for a long time and connected input source. So I can't judge whether POWER_SUPPLY_STATUS is right now.
2. As you said last time, I need to read the battery temperature from the register of STAT(Reg0x01, bits [4:3]). But I found that Reg01=00 after removing the input source. Whether the input source must be connected to read Reg01 ? and could you help to explain the meaning of these values ? How can I check whether the battery is HOT, COLD, WARM, COOL, or NORMAL ?
Thanks.
Hi Jason,
I'm glad that all of the registers are now being set correctly! Thanks for your patience throughout this debug.
1. Thanks for pointing this out. The CHG_STAT field is represented by bits 6-5 in the STAT0 register (Reg0x00). The current implementation shifts the value by 4 bits, which mis-aligns these bits. To correctly extract the two-bit value into the least-significant bits (positions 0 and 1), we need to shift right by 5 bits instead. This change ensures that bit 6 moves to position 1 and bit 5 moves to position 0, yielding the proper numeric value for the charging state.
Please change the following line in the driver:
#define BQ2518X_CHG_STAT_STEP 4
to
#define BQ2518X_CHG_STAT_STEP 5
This adjustment should allow the driver to correctly interpret the charging state from the STAT0 register.
Let me know if this resolves the issue.
2. This behavior is expected. The BQ25180 monitors the TS pin voltage, which is influenced by the NTC and any external resistor compensation networks, to determine whether the battery is COLD, COOL, WARM, HOT, or NORMAL. Battery temperature is only actively monitored when an input source is connected (i.e., during charging). To test whether the firmware correctly identifies the battery temperature state, you can bias the TS pin above or below the voltage thresholds specified in the datasheet (see section 8.3.13.1 of the BQ25180 datasheet for more details).
Best regards,
Alec