I've written a register dump code for the BQ24297 power management chip. I wanted to see why the charging fault is flashing and to gain general understanding of the chip. The fault state is reported at the end of the output.
Note that there is a similar program for the BQ27421 fuel guage chip in /usr/share/pyra/bin
This belongs in https://dev.pyra-handheld.com/packages/pyra-misc but I'll need approval to push to the dev site.
EDIT:
Updated to read registers from/sys/class/power_supply/bq24297/device/registers
or /dev/i2c-1 if the -i argument is given
EDIT2: Updated example output for good fast charging state
Note that there is a similar program for the BQ27421 fuel guage chip in /usr/share/pyra/bin
This belongs in https://dev.pyra-handheld.com/packages/pyra-misc but I'll need approval to push to the dev site.
/sys/class/power_supply/bq24297/device/registers
INPUT SOURCE CONTROL, REG00: 0x37
EN_HIZ= no
Input voltage limit, VINDPM=4.12V
Input current limit, INLIM =3000mA
POWER-ON CONFIGURATION, REG01: 0x17
Register Reset= keep
I2C Watchdog Timer Reset= normal
OTG_CONFIG= OTG_DISABLE
CHG_CONFIG= Charge enabled
Minimum system voltage= 3.3V
Boost mode current limit, BOOST_LIM= 1.5A
CHARGE CURRENT CONTROL, REG02: 0x60
Charge current= 2048mA
Boost mode temperature monitor threshold voltage=
V bcold0 (Typ. 76% of REGN or -10°C w/103AT thermistor)
FORCE_20PCT=
ICHG as Fast Charge Current (REG02[7:2]) and
IPRECH as Pre-Charge Current (REG03[7:4]) programmed
PRE-CHARGE & TERMINATION CURRENT, REG03: 0x00
Pre-charge current= 128mA
Termination current= 128mA
CHARGE VOLTAGE CONTROL REGISTER, REG04: 0x8a
Charge voltage, VREG =4.05V
Battery recharge threshold, VRECHG =3.95V
Battery low voltage, BATLOWV=3.00V
CHARGE TERMINATION / TIMER CONTROL REGISTER, REG05: 0x8c
Termination = Enabled
Watchdog timeout = 0s (disabled)
Charging safety timer = Enabled (12hr)
BOOST VOLTAGE / THERMAL REGULATION CONTROL REGISTER, REG06: 0x73
Boost voltage = 4.998
Boost mode temperature threshold = 33% of REGN or 55°C w/103AT thermistor
Thermal regulation threshold = 120°C
MISC OPERATION CONTROL REGISTER, REG07: 0x4b
Force DPDM detection = Not in input current limit detection
Safety timer setting during Input DPM and Thermal Regulation:
Safety timer slowed by 2X during input DPM or thermal regulation
Batt FET disable = Allow BATFET (Q4) turn on
Charge fault INT mask = INT on CHRG_FAULT
Batt fault INT mask = INT on BAT_FAULT
SYSTEM STATUS REGISTER, REG08: 0xa4
VBUS status = Adapter port
Charge status = Fast Charging
DPM status = Not DPM
Power good status= Power Good
Thermal status = Normal
VSYS status = Not in VSYSMIN regulation (BAT > VSYSMIN)
NEW FAULT REGISTER, REG09: 0x00
Watchdog fault = Normal
OTG fault = Normal
Charge fault = Normal
Battery fault = Normal
NTC fault (cold) = Normal
NTC fault (hot) = Normal
VENDOR / PART / REVISION STATUS REGISTER, REG0A: 0x60
Chip code = BQ24297
Revision = 0
INPUT SOURCE CONTROL, REG00: 0x37
EN_HIZ= no
Input voltage limit, VINDPM=4.12V
Input current limit, INLIM =3000mA
POWER-ON CONFIGURATION, REG01: 0x17
Register Reset= keep
I2C Watchdog Timer Reset= normal
OTG_CONFIG= OTG_DISABLE
CHG_CONFIG= Charge enabled
Minimum system voltage= 3.3V
Boost mode current limit, BOOST_LIM= 1.5A
CHARGE CURRENT CONTROL, REG02: 0x60
Charge current= 2048mA
Boost mode temperature monitor threshold voltage=
V bcold0 (Typ. 76% of REGN or -10°C w/103AT thermistor)
FORCE_20PCT=
ICHG as Fast Charge Current (REG02[7:2]) and
IPRECH as Pre-Charge Current (REG03[7:4]) programmed
PRE-CHARGE & TERMINATION CURRENT, REG03: 0x00
Pre-charge current= 128mA
Termination current= 128mA
CHARGE VOLTAGE CONTROL REGISTER, REG04: 0x8a
Charge voltage, VREG =4.05V
Battery recharge threshold, VRECHG =3.95V
Battery low voltage, BATLOWV=3.00V
CHARGE TERMINATION / TIMER CONTROL REGISTER, REG05: 0x8c
Termination = Enabled
Watchdog timeout = 0s (disabled)
Charging safety timer = Enabled (12hr)
BOOST VOLTAGE / THERMAL REGULATION CONTROL REGISTER, REG06: 0x73
Boost voltage = 4.998
Boost mode temperature threshold = 33% of REGN or 55°C w/103AT thermistor
Thermal regulation threshold = 120°C
MISC OPERATION CONTROL REGISTER, REG07: 0x4b
Force DPDM detection = Not in input current limit detection
Safety timer setting during Input DPM and Thermal Regulation:
Safety timer slowed by 2X during input DPM or thermal regulation
Batt FET disable = Allow BATFET (Q4) turn on
Charge fault INT mask = INT on CHRG_FAULT
Batt fault INT mask = INT on BAT_FAULT
SYSTEM STATUS REGISTER, REG08: 0xa4
VBUS status = Adapter port
Charge status = Fast Charging
DPM status = Not DPM
Power good status= Power Good
Thermal status = Normal
VSYS status = Not in VSYSMIN regulation (BAT > VSYSMIN)
NEW FAULT REGISTER, REG09: 0x00
Watchdog fault = Normal
OTG fault = Normal
Charge fault = Normal
Battery fault = Normal
NTC fault (cold) = Normal
NTC fault (hot) = Normal
VENDOR / PART / REVISION STATUS REGISTER, REG0A: 0x60
Chip code = BQ24297
Revision = 0
C:
/*
* bqdump - bq24297 register dumper
*
* Copyright (c) 2021, Ian Williams
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the organization nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <errno.h>
#include <linux/i2c.h>
#include <linux/i2c-dev.h>
#define SYS_REGISTERS "/sys/class/power_supply/bq24297/device/registers"
#define I2C_ADDRESS 0x6b
#define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0]))
static int bq_read_i2c(int fd, __u8 reg, int len)
{
struct i2c_rdwr_ioctl_data rdwr;
struct i2c_msg msg[2];
unsigned char data[2];
int ret;
assert(len == 1 || len == 2);
msg[0].addr = I2C_ADDRESS;
msg[0].flags = 0;
msg[0].buf = ®
msg[0].len = sizeof(reg);
msg[1].addr = I2C_ADDRESS;
msg[1].flags = I2C_M_RD;
msg[1].buf = data;
msg[1].len = len;
rdwr.msgs = msg;
rdwr.nmsgs = ARRAY_SIZE(msg);
// I2C_SMBUS also works, but reads sometimes return 0xff?
ret = ioctl(fd, I2C_RDWR, &rdwr);
if (ret != ARRAY_SIZE(msg)) {
fprintf(stderr, "read at %02x failed: ", reg);
perror(NULL);
exit(1);
}
ret = data[0];
if (len > 1)
ret |= data[1] << 8;
return ret;
}
const int inlim_val[] = { 100, 150, 500, 900, 1000, 1500, 2000, 3000 };
const char *vbus_status_val[] = {
"Unknown (no input or DPDM inspection incomplete)",
"USB host",
"Adapter port",
"OTG"
};
const char *charge_status_val[] = {
"Not Charging",
"Pre-charge (<V_BATLOWV)",
"Fast Charging",
"Charge Termination Done"
};
const char *charge_fault_val[] = {
"Normal",
"Input fault (OVP or bad source)",
"Thermal shutdown",
"Charge timer expiration"
};
const int charge_timer_duration_val[] = { 5, 8, 12, 20 };
const char *boosthot_val[] = {
"33\% of REGN or 55°C w/103AT thermistor",
"36\% of REGN or 60°C w/103AT thermistor",
"30\% of REGN or 65°C w/103AT thermistor",
"Disable boost mode thermal protection."
};
int main(int argc, char *argv[])
{
/* If false, read registers using sysfs, otherwise use i2c */
int use_i2c = 0;
/* Parse command line arguments to determine whether to read from sysfs or direct from i2c */
if (argc > 1)
{
int opt;
while ((opt = getopt(argc, argv, "ih")) != -1) {
switch (opt) {
case 'i': use_i2c = 1; break;
default:
printf("Usage: %s [-i]\n", argv[0]);
printf(" Dumps registers from BQ24297 power management chip at i2c:1 addr %02x\n\n", I2C_ADDRESS);
printf(" Default is to read from \n %s\n\n", SYS_REGISTERS);
printf(" -i\tRegisters are read directly from /dev/i2c-1 (not recommended)\n");
exit(EXIT_FAILURE);
}
}
}
const int nreg = 0x0A + 1;
int reg[nreg];
if (use_i2c)
{
printf("Bus i2c-1, address 0x%02x\n\n", I2C_ADDRESS);
int fd = open("/dev/i2c-1", O_RDWR);
if (fd < 0) {
perror("i2c open");
return 1;
}
for (int i=0; i<nreg; i++)
reg[i] = bq_read_i2c(fd, i, 1);
}
else
{
printf("%s\n\n", SYS_REGISTERS);
FILE* fp = fopen( SYS_REGISTERS, "r");
if (fp == NULL)
exit(EXIT_FAILURE);
char * line = NULL;
size_t len = 0;
ssize_t read;
while ((read = getline(&line, &len, fp)) != -1) {
char * pch;
pch = strtok (line," \t"); /* "reg" */
if (pch == NULL) break;
pch = strtok (NULL," \t");
if (pch == NULL) break;
int regaddr = (int)strtol(pch, NULL, 16);
/*printf ("REG%02x = ",regaddr);*/
pch = strtok (NULL," \t"); /* "value" */
if (pch == NULL) break;
pch = strtok (NULL," \t");
if (pch == NULL) break;
if (regaddr >= 0 && regaddr < nreg)
{
reg[regaddr] = (int)strtol(pch, NULL, 16);
/*printf ("%02x\n",reg[regaddr]);*/
}
}
fclose(fp);
if (line)
free(line);
}
/* Input source control register, 0x00 */
float vindpm = 3.88 + 0.08*(float)((reg[0] & 0x70)>>4);
int inlim = inlim_val[reg[0] & 0x0F];
printf("INPUT SOURCE CONTROL, REG00: 0x%02x\n", reg[0]);
printf(" EN_HIZ= %s\n", reg[0] & (1 << 7) ? "yes" : "no");
printf(" Input voltage limit, VINDPM=%.2fV\n", vindpm);
printf(" Input current limit, INLIM =%dmA\n", inlim);
printf("\n");
/* Power-On Configuration Register, 0x01 */
float sysminv = 3.0 + 0.1*(float)((reg[1] & 0x0E) >> 1);
float boostlim = reg[1] & 1 ? 1.5 : 1.0;
printf("POWER-ON CONFIGURATION, REG01: 0x%02x\n", reg[1]);
printf(" Register Reset= %s\n", reg[1] & (1 << 7) ? "reset to default" : "keep");
printf(" I2C Watchdog Timer Reset= %s\n", reg[1] & (1 << 6) ? "reset" : "normal");
printf(" OTG_CONFIG= %s\n", reg[1] & (1 << 5) ? "OTG_ENABLE" : "OTG_DISABLE");
printf(" CHG_CONFIG= %s\n", reg[1] & (1 << 4) ? "Charge enabled" : "Charge disable");
printf(" Minimum system voltage= %gV\n", sysminv);
printf(" Boost mode current limit, BOOST_LIM= %.1fA\n", boostlim);
printf("\n");
/* Charge Current Control Register, 0x02 */
int chargecur_mA = 512 + 64*(reg[2] >> 2);
printf("CHARGE CURRENT CONTROL, REG02: 0x%02x\n", reg[2]);
printf(" Charge current= %dmA\n", chargecur_mA);
printf(" Boost mode temperature monitor threshold voltage=\n%s\n",
reg[2] & (1 << 1) ?
" V_bcold1 (Typ. 79% of REGN or -20°C w/103AT thermistor)" :
" V bcold0 (Typ. 76% of REGN or -10°C w/103AT thermistor)");
printf(" FORCE_20PCT=\n%s\n",
reg[2] & (1 << 0) ?
" ICHG as 20% Fast Charge Current (REG02[7:2]) and \n IPRECH as 50% Pre-Charge Current(REG03[7:4]) programmed\n" :
" ICHG as Fast Charge Current (REG02[7:2]) and \n IPRECH as Pre-Charge Current (REG03[7:4]) programmed");
printf("\n");
/* Pre-Charge/Termination Current Control Register, 0x03 */
int iprechg_mA = 128 + 128*((reg[3] & 0xF0) >> 4);
int iterm_mA = 128 + 128* (reg[3] & 0x0F);
printf("PRE-CHARGE & TERMINATION CURRENT, REG03: 0x%02x\n", reg[3]);
printf(" Pre-charge current= %dmA\n", iprechg_mA);
printf(" Termination current= %dmA\n", iterm_mA);
printf("\n");
/* Charge Voltage Control Register, 0x04 */
float vreg = 3.504 + 0.016*(float)(reg[4] >> 2);
printf("CHARGE VOLTAGE CONTROL REGISTER, REG04: 0x%02x\n", reg[4]);
printf(" Charge voltage, VREG =%.2fV\n", vreg);
printf(" Battery recharge threshold, VRECHG =%.2fV\n", reg[4] & 1 ? vreg - 0.3 : vreg - 0.1);
printf(" Battery low voltage, BATLOWV=%.2fV\n", reg[4] & 2 ? 3.0 : 2.8 );
printf("\n");
/* Charge Termination/Timer Control Register */
int watchdog_timeout = (reg[5] & 0x30) >> 4;
int charge_safety_duration = charge_timer_duration_val[(reg[5] & 0x06) >> 1];
printf("CHARGE TERMINATION / TIMER CONTROL REGISTER, REG05: 0x%02x\n", reg[5]);
printf(" Termination = %s\n", reg[5] & (1<<7) ? "Enabled" : "Disabled");
printf(" Watchdog timeout = %ds", watchdog_timeout );
if (watchdog_timeout == 0) printf(" (disabled)");
printf("\n");
printf(" Charging safety timer = %s", reg[5] & (1<<3) ? "Enabled" : "Disabled");
printf(" (%dhr)\n", charge_safety_duration);
printf("\n");
/* Boost Voltage/Thermal Regulation Control Register, 0x06 */
float boostv = 4.55 + 0.064*(float)(reg[6] >> 4);
const char* boosthot = boosthot_val[(reg[6] & 0x0C) >> 2];
int thermreg = 60 + 20*(reg[6] & 3);
printf("BOOST VOLTAGE / THERMAL REGULATION CONTROL REGISTER, REG06: 0x%02x\n", reg[6]);
printf(" Boost voltage = %g\n", boostv);
printf(" Boost mode temperature threshold = %s\n", boosthot );
printf(" Thermal regulation threshold = %d°C\n", thermreg );
printf("\n");
/* Misc Operation Control Register, 0x07 */
printf("MISC OPERATION CONTROL REGISTER, REG07: 0x%02x\n", reg[7]);
printf(" Force DPDM detection = %s\n", reg[7] & (1<<7) ? "Force input current limit detection when VBUS power is present"
: "Not in input current limit detection");
printf(" Safety timer setting during Input DPM and Thermal Regulation:\n");
printf(" %s\n", reg[7] & (1<<6) ? "Safety timer slowed by 2X during input DPM or thermal regulation"
: "Safety timer not slowed by 2X during input DPM or thermal regulation");
printf(" Batt FET disable = %s\n", reg[7] & (1<<5) ? "Turn off BATFET (Q4)" : "Allow BATFET (Q4) turn on");
printf(" Charge fault INT mask = %s\n", reg[7] & (1<<1) ? "INT on CHRG_FAULT" : "No INT on CHRG_FAULT");
printf(" Batt fault INT mask = %s\n", reg[7] & (1<<0) ? "INT on BAT_FAULT" : "No INT on BAT_FAULT");
printf("\n");
/* System Status Register, 0x08 */
printf("SYSTEM STATUS REGISTER, REG08: 0x%02x\n", reg[8]);
printf(" VBUS status = %s\n", vbus_status_val[reg[8] >> 6]);
printf(" Charge status = %s\n", charge_status_val[(reg[8] & 0x30) >> 4]);
printf(" DPM status = %s\n", reg[8] & (1<<3) ? "VINDPM or IINDPM" : "Not DPM");
printf(" Power good status= %s\n", reg[8] & (1<<2) ? "Power Good" : "Not Power Good");
printf(" Thermal status = %s\n", reg[8] & (1<<1) ? "In Thermal Regulation" : "Normal" );
printf(" VSYS status = %s\n", reg[8] & (1<<0) ? "In VSYSMIN regulation (BAT < VSYSMIN)" : "Not in VSYSMIN regulation (BAT > VSYSMIN)");
printf("\n");
/* New Fault Register, 0x09 */
printf("NEW FAULT REGISTER, REG09: 0x%02x\n", reg[9]);
printf(" Watchdog fault = %s\n", reg[9] & (1<<7) ? "Watchdog timer expiration" : "Normal");
printf(" OTG fault = %s\n", reg[9] & (1<<6) ? "VBUS overloaded in OTG, or VBUS OVP, or battery is too low (any conditions that cannot start boost function)" : "Normal");
printf(" Charge fault = %s\n", charge_fault_val[(reg[9] & 0x30) >> 4]);
printf(" Battery fault = %s\n", reg[9] & (1<<3) ? "Battery OVP" : "Normal");
printf(" NTC fault (cold) = %s\n", reg[9] & (1<<1) ? "Cold" : "Normal");
printf(" NTC fault (hot) = %s\n", reg[9] & (1<<0) ? "Hot" : "Normal");
printf("\n");
/* Vender / Part / Revision Status Register, 0x0A */
printf("VENDOR / PART / REVISION STATUS REGISTER, REG0A: 0x%02x\n", reg[10]);
int code = (reg[10] & 0xE0) >> 5;
printf(" Chip code = %s\n", code==1 ? "BQ24296" : code==3 ? "BQ24297" : "Unrecognised");
printf(" Revision = %d\n", reg[10] & 0x07);
return 0;
}
Makefile:
CC = $(CROSS_COMPILE)gcc
CFLAGS = -Wall -O2 -ggdb
TARGETS = bqdump274 bqdump24297 neon_loop2 pyra_test_inputs
all: $(TARGETS)
clean:
$(RM) $(TARGETS)
pyra_test_inputs: LDLIBS += -lpthread
neon_loop2: ASFLAGS += -mcpu=cortex-a15 -mfpu=neon
.PHONY: all clean
EDIT:
Updated to read registers from/sys/class/power_supply/bq24297/device/registers
or /dev/i2c-1 if the -i argument is given
EDIT2: Updated example output for good fast charging state
Last edited: