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.

BQ4050: read_block will provide wrong data after hundreds of times read

Part Number: BQ4050

Hi,

my customer is reading 13.31 0x21 DeviceName() every one second, and the clock frequency of SMBUS is 100kHz.

Normally the data is 20 73 65 83 48 48 53 71 45 90 77 45 52 48 53 48 45 50 83 49 80 0 0 0

At 310th time the data is wrong,

And then it go back to normal, at 744th time,the data is wrong, and it can't go back to normal, even reading other IC's register on the same SMBUS will give wrong data.

[  318.340000] {lcb}fg_read_sn times=308

[  318.340000] 20 73 65 83 48 48 53 71 45 90 77 45 52 48 53 48 45 50 83 49 80 0 0 0

[  319.360000] {lcb}fg_read_sn times=309

[  319.360000] 20 73 65 83 48 48 53 71 45 90 77 45 52 48 53 48 45 50 83 49 80 0 0 0

[  320.380000] {lcb}fg_read_sn times=310

[  320.380000] 20 73 65 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0

[  321.400000] {lcb}fg_read_sn times=311

[  321.400000] 20 73 65 83 48 48 53 71 45 90 77 45 52 48 53 48 45 50 83 49 80 0 0 0

[  322.420000] {lcb}fg_read_sn times=312

[  322.420000] 20 73 65 83 48 48 53 71 45 90 77 45 52 48 53 48 45 50 83 49 80 0 0 0

 

。。。

 

[  761.050000] 20 73 65 83 48 48 53 71 45 90 77 45 52 48 53 48 45 50 83 49 80 0 0 0

[  762.070000] {lcb}fg_read_sn times=742

[  762.070000] 20 73 65 83 48 48 53 71 45 90 77 45 52 48 53 48 45 50 83 49 80 0 0 0

[  763.090000] {lcb}fg_read_sn times=743

[  763.090000] 20 73 65 83 48 48 53 71 45 90 77 45 52 48 53 48 45 50 83 49 80 0 0 0

[  764.130000] {lcb}fg_read_sn times=744

[  764.130000] 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0

[  765.420000] {lcb}fg_read_sn times=745

[  765.420000] 192 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0

[  766.680000] {lcb}fg_read_sn times=746

[  766.680000] 30 0 0 99 0 198 11 255 255 127 6 64 6 2 0 110 6 100 0 192 0 0 0 0

[  767.950000] {lcb}fg_read_sn times=747

[  767.950000] 30 0 0 99 0 198 11 255 255 127 6 64 6 2 0 110 6 100 0 192 0 0 0 0

The code is as below: what's may cause such problem?

#include <linux/module.h>
#include <linux/param.h>
#include <linux/jiffies.h>
#include <linux/workqueue.h>
#include <linux/delay.h>
#include <linux/platform_device.h>
#include <linux/power_supply.h>
#include <linux/i2c.h>
#include <linux/slab.h>
#include <linux/uaccess.h>
#include <linux/interrupt.h>

extern int battery_get_volt(void);
extern struct kobject *paxinfo_sysfs_kobj;

enum {
	battery_chgIC_ok = 0,
	battery_err = (1 << 0),
	chgIC_err= (1 << 1),
	overDischarge_err = (1 << 2),
};

static int pm_status = battery_chgIC_ok;

#define bq_info	pr_info
#define bq_dbg	pr_debug
#define bq_err	pr_err
#define bq_log	pr_err

#define	INVALID_REG_ADDR	0xFF

#define FG_FLAGS_FD				BIT(4)
#define	FG_FLAGS_FC				BIT(5)
#define	FG_FLAGS_DSG			BIT(6)
#define FG_FLAGS_RCA			BIT(9)

#define	BQ_FG_REG_TEMP			0x08	/* Battery Temperature */
#define	BQ_FG_REG_VOLT			0x09	/* Battery Voltage */
#define	BQ_FG_REG_AI			0x0B	/* Average Current */
#define	BQ_FG_REG_BATT_STATUS	0x16	/* BatteryStatus */
#define	BQ_FG_REG_TTE			0x12	/* Time to Empty */
#define	BQ_FG_REG_TTF			0x13	/* Time to Full */
#define	BQ_FG_REG_FCC			0x10	/* Full Charge Capacity */
#define	BQ_FG_REG_RM			0x0F	/* Remaining Capacity */
#define	BQ_FG_REG_CC			0x17	/* Cycle Count */
#define	BQ_FG_REG_SOC			0x0D	/* Relative State of Charge */
#define	BQ_FG_REG_SOH			0x4F	/* State of Health */
#define	BQ_FG_REG_DC			0x18	/* Design Capacity */
#define	BQ_FG_REG_MBA			0x44	/* ManufacturerBlockAccess*/
#define	BQ_FG_REG_CELLVOl4		0x3C	/* CellVoltage4()*/
#define	BQ_FG_REG_CELLVOl3		0x3D	/* CellVoltage3()*/
#define	BQ_FG_REG_CELLVOl2		0x3E	/* CellVoltage2()*/
#define	BQ_FG_REG_CELLVOl1		0x3F	/* CellVoltage1()*/
#define	BQ_FG_REG_MDDATA		0x1B	/* ManufacturerDate()*/
#define	BQ_FG_REG_SNUM			0x1C	/* SerialNumber()*/
#define	BQ_FG_REG_DEVICE_NAME	0x21	/* DeviceName()*/


#define	FG_MAC_CMD_OP_STATUS 	0x0000
#define	FG_MAC_CMD_DEV_TYPE	 	0x0001
#define	FG_MAC_CMD_FW_VER	 	0x0002
#define	FG_MAC_CMD_HW_VER	 	0x0003
#define	FG_MAC_CMD_IF_SIG	 	0x0004
#define	FG_MAC_CMD_CHEM_ID	 	0x0006
#define	FG_MAC_CMD_GAUGING		0x0021
#define	FG_MAC_CMD_SEAL		 	0x0030
#define	FG_MAC_CMD_DEV_RESET 	0x0041
#define	FG_MAC_CMD_MANUFACTURER_INFO 	0x0070 // ManufacturerInfo

enum {
	SEAL_STATE_RSVED,
	SEAL_STATE_UNSEALED,
	SEAL_STATE_SEALED,
	SEAL_STATE_FA,
};

enum bq_fg_device {
	BQ40Z50,
};

struct bq_fg_chip {
	struct device *dev;
	struct i2c_client *client;
	struct mutex i2c_rw_lock;
	u8 chip;
	char sn[64];

	int batt_volt;
	int batt_curr;
	int batt_soc;
	int batt_temp;
	int batt_tte;
	int batt_fcc;		/* Full charge capacity */
	int batt_dc;		/* Design Capacity */	
	int batt_cyclecnt;	/* cycle count */
	int batt_rm;		/* Remaining capacity */
	int soh;
	int batt_status;
	int batt_capacity_level;
	
	bool batt_fc;
	bool batt_fd;	/* full depleted */
	bool batt_dsg;
	bool batt_rca;	/* remaining capacity alarm */
	
	struct workqueue_struct *bq4050_workqueue;
	struct delayed_work monitor_work;

	struct power_supply fg_psy;
	struct power_supply *adapter_psy;

	int battery_present;
};

static int __fg_read_word(struct i2c_client *client, u8 reg, u16 *val)
{
	s32 ret;

	ret = i2c_smbus_read_word_data(client, reg);
	if (ret < 0) {
		bq_err("i2c read word fail: can't read from reg 0x%02X\n", reg);
		return ret;
	}

	*val = (u16)ret;

	return 0;
}

static int __fg_write_word(struct i2c_client *client, u8 reg, u16 val)
{
	s32 ret;

	ret = i2c_smbus_write_word_data(client, reg, val);
	if (ret < 0) {
		bq_err("i2c write word fail: can't write 0x%02X to reg 0x%02X\n",
				val, reg);
		return ret;
	}

	return 0;
}

static int __fg_read_block(struct i2c_client *client, u8 reg, u8 *buf, u8 len)
{

	int ret;

	/* len is ignored due to smbus block reading contains Num of bytes to be returned */
	ret = i2c_smbus_read_block_data(client, reg, buf);

	return ret;
}

static int __fg_write_block(struct i2c_client *client, u8 reg, u8 *buf, u8 len)
{
	int ret;

	ret = i2c_smbus_write_block_data(client, reg, len, buf);

	return ret;
}

static int fg_read_word(struct bq_fg_chip *bq, u8 reg, u16 *val)
{
	int ret;

	mutex_lock(&bq->i2c_rw_lock);
	ret = __fg_read_word(bq->client, reg, val);
	mutex_unlock(&bq->i2c_rw_lock);

	return ret;
}

static int fg_write_word(struct bq_fg_chip *bq, u8 reg, u16 val)
{
	int ret;

	mutex_lock(&bq->i2c_rw_lock);
	ret = __fg_write_word(bq->client, reg, val);
	mutex_unlock(&bq->i2c_rw_lock);

	return ret;
}

static int fg_read_block(struct bq_fg_chip *bq, u8 reg, u8 *buf, u8 len)
{
	int ret;

	mutex_lock(&bq->i2c_rw_lock);
	ret = __fg_read_block(bq->client, reg, buf, len);
	mutex_unlock(&bq->i2c_rw_lock);

	return ret;
}

static int fg_write_block(struct bq_fg_chip *bq, u8 reg, u8 *data, u8 len)
{
	int ret;

	mutex_lock(&bq->i2c_rw_lock);
	ret = __fg_write_block(bq->client, reg, data, len);
	mutex_unlock(&bq->i2c_rw_lock);

	return ret;
}

#if 0
static u8 checksum(u8 *data, u8 len)
{
	u8 i;
	u16 sum = 0;

	for (i = 0; i < len; i++)
		sum += data[i];

	sum &= 0xFF;

	return 0xFF - sum;
}

static void fg_print_buf(const char *msg, u8 *buf, u8 len)
{
	int i;
	int idx = 0;
	int num;
	u8 strbuf[128];

	bq_err("%s buf: ", msg);
	for (i = 0; i < len; i++) {
		num = sprintf(&strbuf[idx], "%02X ", buf[i]);
		idx += num;
	}
	bq_err("%s\n", strbuf);
}
#else
static void fg_print_buf(const char *msg, u8 *buf, u8 len)
{}
#endif

static int fg_mac_read_block(struct bq_fg_chip *bq, u16 cmd, u8 *buf, u8 len)
{
	int ret;
	u8 t_buf[40];
	u8 t_len;
	int i;

	t_buf[0] = (u8)(cmd >> 8);
	t_buf[1] = (u8)cmd;
	ret = fg_write_block(bq, BQ_FG_REG_MBA, t_buf, 2);
	if (ret < 0)
		return ret;

	msleep(100);

	ret = fg_read_block(bq, BQ_FG_REG_MBA, t_buf, 36);
	if (ret < 0)
		return ret;
	t_len = ret;
	/* ret contains number of data bytes in gauge's response*/
	fg_print_buf("mac_read_block", t_buf, t_len);

	for (i = 0; i < t_len - 2; i++)
		buf[i] = t_buf[i+2];

	return 0;
}

#if 0
static int fg_mac_write_block(struct bq_fg_chip *bq, u16 cmd, u8 *data, u8 len)
{
	int ret;
	u8 cksum;
	u8 t_buf[40];
	int i;

	if (len > 32)
		return -1;

	t_buf[0] = (u8)(cmd >> 8);
	t_buf[1] = (u8)cmd;
	for (i = 0; i < len; i++)
		t_buf[i+2] = data[i];

	/*write command/addr, data*/
	ret = fg_write_block(bq, bq->regs[BQ_FG_REG_MBA], t_buf, len + 2);
	if (ret < 0)
		return ret;

	return ret;
}
#endif

static int fg_read_fw_version(struct bq_fg_chip *bq)
{
	int ret;
	u8 buf[36];
	int i;
	static int times = 0;

	ret = fg_write_word(bq, BQ_FG_REG_MBA, FG_MAC_CMD_FW_VER);
	if (ret < 0) {
		bq_err("Failed to send firmware version subcommand:%d\n", ret);
		return ret;
	}

	mdelay(2);

	ret = fg_mac_read_block(bq, BQ_FG_REG_MBA, buf, 11);
	if (ret < 0) {
		bq_err("Failed to read firmware version:%d\n", ret);
		return ret;
	}

	printk("{lcb}fg_read_sn times=%d\n", times++);
	for(i = 0;i < 24;i++) {
		printk("%d ", buf[i]);
	}
	printk("\n");
	
	return 0;
}

static char* fg_read_sn(struct bq_fg_chip *bq)
{
	static int times = 0;
	int ret, len = 0;	
	u8 buf[64] = {0};
	int i;

#if 0 // 0x44 0x0002
	fg_read_fw_version(bq);
#else // 0x21
	ret = fg_read_block(bq, BQ_FG_REG_DEVICE_NAME, buf, 24);
	if (ret < 0) {
		printk("{lcb}fg_read_block BQ_FG_REG_DEVICE_NAME err ret:%d\n", ret);
		return ret;
	}

	printk("{lcb}fg_read_sn times=%d\n", times++);
	for(i = 0;i < 24;i++) {
		printk("%d ", buf[i]);
	}
	printk("\n");	
#endif

	return bq->sn;
}

static int fg_read_status(struct bq_fg_chip *bq)
{
	int ret;
	u16 flags;

	ret = fg_read_word(bq, BQ_FG_REG_BATT_STATUS, &flags);
	if (ret < 0)
		return ret;

	bq->batt_fc		= !!(flags & FG_FLAGS_FC);// 1:Fully Charged
	bq->batt_fd		= !!(flags & FG_FLAGS_FD);// 1:Battery fully depleted
	bq->batt_rca	= !!(flags & FG_FLAGS_RCA);// 1:RemainingCapacity() < RemainingCapacityAlarm()
	bq->batt_dsg	= !!(flags & FG_FLAGS_DSG);// 0:Battery is in CHARGE mode 1:Battery is in DISCHARGE or RELAX mode.

	return 0;
}

static int fg_read_rsoh(struct bq_fg_chip *bq)
{
	int ret;
	u16 soh = 0;

	ret = fg_read_word(bq, BQ_FG_REG_SOH, &soh);
	if (ret < 0) {
		bq_err("could not read soh, ret = %d\n", ret);
		return ret;
	}
	
	return soh;
}

static int fg_read_rsoc(struct bq_fg_chip *bq)
{
	int ret;
	u16 soc = 0;

	ret = fg_read_word(bq, BQ_FG_REG_SOC, &soc);
	if (ret < 0) {
		bq_err("could not read RSOC, ret = %d\n", ret);
		return ret;
	}
	return soc;
}

static int fg_read_temperature(struct bq_fg_chip *bq)
{
	int ret;
	u16 temp = 0;

	ret = fg_read_word(bq, BQ_FG_REG_TEMP, &temp);
	if (ret < 0) {
		bq_err("could not read temperature, ret = %d\n", ret);
		return ret;
	}
	return (temp - 2730) / 10;
}

static int fg_read_volt(struct bq_fg_chip *bq)
{
	int ret;
	u16 volt = 0;

	ret = fg_read_word(bq, BQ_FG_REG_VOLT, &volt);
	if (ret < 0) {
		bq_err("could not read voltage, ret = %d\n", ret);
		return ret;
	}

	return volt * 1000;
}

static int fg_read_current(struct bq_fg_chip *bq)
{
	int ret;
	u16 avg_curr = 0;
	int curr;

	ret = fg_read_word(bq, BQ_FG_REG_AI, &avg_curr);
	if (ret < 0) {
		bq_err("could not read current, ret = %d\n", ret);
		return ret;
	}
	
	curr = (int)((s16)avg_curr);

	return curr;
}

static int fg_read_fcc(struct bq_fg_chip *bq)
{
	int ret;
	u16 fcc;

	ret = fg_read_word(bq, BQ_FG_REG_FCC, &fcc);
	if (ret < 0) {
		bq_err("could not read Full Charge Capacity, ret=%d\n", ret);
		return ret;
	}
		
	return fcc;
}

static int fg_read_dc(struct bq_fg_chip *bq)
{
	int ret;
	u16 dc;

	ret = fg_read_word(bq, BQ_FG_REG_DC, &dc);
	if (ret < 0) {
		bq_err("could not read Design Capacity, ret=%d\n", ret);
		return ret;
	}

	return dc;
}

static int fg_read_rm(struct bq_fg_chip *bq)
{
	int ret;
	u16 rm;

	ret = fg_read_word(bq, BQ_FG_REG_RM, &rm);

	if (ret < 0) {
		bq_err("could not read Remaining Capacity, ret=%d\n", ret);
		return ret;
	}

	return rm;
}

static int fg_read_cyclecount(struct bq_fg_chip *bq)
{
	int ret;
	u16 cc;

	ret = fg_read_word(bq, BQ_FG_REG_CC, &cc);

	if (ret < 0) {
		bq_err("could not read Cycle Count, ret=%d\n", ret);
		return ret;
	}

	return cc;
}

static int fg_read_tte(struct bq_fg_chip *bq)
{
	int ret;
	u16 tte;

	ret = fg_read_word(bq, BQ_FG_REG_TTE, &tte);

	if (ret < 0) {
		bq_err("could not read Time To Empty, ret=%d\n", ret);
		return ret;
	}

	if (ret == 0xFFFF)
		return -ENODATA;

	return tte;
}

static int fg_get_batt_status(struct bq_fg_chip *bq)
{
	int retval;
	union power_supply_propval ret = {0,};
	
	fg_read_status(bq);

    if (!bq->adapter_psy) 
        bq->adapter_psy = power_supply_get_by_name("adapter");

    if (bq->adapter_psy) {
       bq->adapter_psy->get_property(bq->adapter_psy, POWER_SUPPLY_PROP_ONLINE, &ret);
    } 
	else {
        bq_err("adapter_psy not found error\n");
    }

	if(ret.intval) {
		if(bq->battery_present == 0) 
			retval = POWER_SUPPLY_STATUS_DISCHARGING;
		else {
			if(bq->batt_curr > 0)
				retval = POWER_SUPPLY_STATUS_CHARGING;
			else if(bq->batt_fc)
				retval = POWER_SUPPLY_STATUS_FULL;
			else//else if(bq->batt_dsg)
				retval = POWER_SUPPLY_STATUS_NOT_CHARGING;		
		}
	}
	else {
		retval = POWER_SUPPLY_STATUS_NOT_CHARGING;
	}

	return retval;
}

static int fg_get_batt_capacity_level(struct bq_fg_chip *bq)
{
	if (bq->batt_fc)
		return POWER_SUPPLY_CAPACITY_LEVEL_FULL;
	else if (bq->batt_rca)
		return POWER_SUPPLY_CAPACITY_LEVEL_LOW;
	else if (bq->batt_fd)
		return POWER_SUPPLY_CAPACITY_LEVEL_CRITICAL;
	else
		return POWER_SUPPLY_CAPACITY_LEVEL_NORMAL;
}

static int fg_get_status_charge_enable(struct bq_fg_chip *bq)
{    
    union power_supply_propval ret = {0,};

	if (!bq->adapter_psy) 
		bq->adapter_psy = power_supply_get_by_name("adapter");	

    if (bq->adapter_psy) {
        bq->adapter_psy->get_property(bq->adapter_psy, POWER_SUPPLY_PROP_CHARGE_ENABLE, &ret);
        return ret.intval;
    } 
	else {
        printk("adapter_psy not found error\n");
        return -ENODEV;
    }
}

static int fg_set_status_charge_enable(struct bq_fg_chip *bq, const union power_supply_propval *val)
{  
	if (!bq->adapter_psy) 
		bq->adapter_psy = power_supply_get_by_name("adapter");	

    if (bq->adapter_psy) {
        return bq->adapter_psy->set_property(
            bq->adapter_psy, POWER_SUPPLY_PROP_CHARGE_ENABLE, val);
    } 
	else {
        printk("adapter_psy not found error\n");
        return -ENODEV;
    }
}

static int battery_present_check(struct bq_fg_chip *bq)
{    
	int ret, i, bat_num;
	u16 cell_vol;
	static char cell_vol_reg[4] = {BQ_FG_REG_CELLVOl1, BQ_FG_REG_CELLVOl2, BQ_FG_REG_CELLVOl3, BQ_FG_REG_CELLVOl4};
	
	for(i = 0, bat_num = 0;i < ARRAY_SIZE(cell_vol_reg);i++) {
		ret = fg_read_word(bq, cell_vol_reg[i], &cell_vol);
		if (ret < 0) {
			msleep(10);
		}
		else {
			if(cell_vol > 2500)
				bat_num++;
			
			if(bat_num >= 2)
				return 1;
		}
	}
	
	return 0;
}

static enum power_supply_property fg_props[] = {
	POWER_SUPPLY_PROP_STATUS,
	POWER_SUPPLY_PROP_CHARGE_ENABLE,
	POWER_SUPPLY_PROP_VOLTAGE_NOW,
	POWER_SUPPLY_PROP_ONLINE,
	POWER_SUPPLY_PROP_PRESENT,
	POWER_SUPPLY_PROP_CURRENT_NOW,
	POWER_SUPPLY_PROP_CAPACITY,
	POWER_SUPPLY_PROP_CAPACITY_LEVEL,
	POWER_SUPPLY_PROP_TEMP,
	POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW,
	POWER_SUPPLY_PROP_CHARGE_FULL,
	POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN,
	POWER_SUPPLY_PROP_CYCLE_COUNT,
	POWER_SUPPLY_PROP_CHARGE_NOW,
	POWER_SUPPLY_PROP_MODEL_NAME,
};

static int fg_get_property(struct power_supply *psy, enum power_supply_property psp,
					union power_supply_propval *val)
{
    struct bq_fg_chip *bq;
    bq = container_of(psy, struct bq_fg_chip, fg_psy); 

	switch (psp) {
	case POWER_SUPPLY_PROP_STATUS:
		val->intval = bq->batt_status;
		break;
	case POWER_SUPPLY_PROP_CHARGE_ENABLE:
        val->intval = fg_get_status_charge_enable(bq);
        break;
	case POWER_SUPPLY_PROP_VOLTAGE_NOW:
		val->intval = bq->batt_volt;
		break;
	case POWER_SUPPLY_PROP_ONLINE:
        val->intval = bq->battery_present;
        break; 
	case POWER_SUPPLY_PROP_PRESENT:
		val->intval = bq->battery_present;
		break;
	case POWER_SUPPLY_PROP_CURRENT_NOW:
		val->intval = bq->batt_curr;
		break;
	case POWER_SUPPLY_PROP_CAPACITY:
		val->intval = bq->batt_soc;
		break;
	case POWER_SUPPLY_PROP_CAPACITY_LEVEL:
		val->intval = bq->batt_capacity_level;
		break;
	case POWER_SUPPLY_PROP_TEMP:
		val->intval = bq->batt_temp;
		break;
	case POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW:
		val->intval = bq->batt_tte;
		break;
	case POWER_SUPPLY_PROP_CHARGE_FULL:
		val->intval = bq->batt_fcc;
		break;
	case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN:
		val->intval = bq->batt_dc;
		break;
	case POWER_SUPPLY_PROP_CYCLE_COUNT:
		val->intval = bq->batt_cyclecnt;
		break;
	case POWER_SUPPLY_PROP_CHARGE_NOW:
		val->intval = bq->batt_rm;
		break;
	case POWER_SUPPLY_PROP_MODEL_NAME:             
		//val->strval = fg_read_sn(bq); 
		val->strval = NULL;
        break;
	default:
		return -EINVAL;
	}
	
	return 0;
}

static int fg_set_property(struct power_supply *psy,
			       enum power_supply_property prop,
			       const union power_supply_propval *val)
{
	int ret = 0;
    struct bq_fg_chip *bq;
    bq = container_of(psy, struct bq_fg_chip, fg_psy); 

	switch(prop) {
		case POWER_SUPPLY_PROP_CHARGE_ENABLE:
			ret = fg_set_status_charge_enable(bq, val);		
            break;
		default:
			break;
	}	
	return ret;
}

static int fg_prop_is_writeable(struct power_supply *psy,
				       enum power_supply_property prop)
{
	int ret = 0;
	switch (prop) {
		case POWER_SUPPLY_PROP_CHARGE_ENABLE:
			ret = 1;
			break;
		default:
			ret = 0;
			break;
	}
	return ret;
}

static int fg_psy_register(struct bq_fg_chip *bq)
{
	int ret;
	struct i2c_client *client = bq->client;

	bq->fg_psy.name = "battery";
	bq->fg_psy.type = POWER_SUPPLY_TYPE_BATTERY;
	bq->fg_psy.properties = fg_props;
	bq->fg_psy.num_properties = ARRAY_SIZE(fg_props);
	bq->fg_psy.get_property = fg_get_property;
	bq->fg_psy.set_property = fg_set_property;
	bq->fg_psy.property_is_writeable = fg_prop_is_writeable;

	ret = power_supply_register(&client->dev, &bq->fg_psy);
	if(ret < 0) {
	    return ret;
	}
	return 0;
}

static void fg_psy_unregister(struct bq_fg_chip *bq)
{
	power_supply_unregister(&bq->fg_psy);
}

static ssize_t fg_attr_show_Ra_table(struct device *dev,
				struct device_attribute *attr, char *buf)
{
	struct i2c_client *client = to_i2c_client(dev);
	struct bq_fg_chip *bq = i2c_get_clientdata(client);
	u8 t_buf[40];
	u8 temp_buf[40];
	int ret;
	int i, idx, len;

	ret = fg_mac_read_block(bq, 0x4102, t_buf, 32);
	if (ret < 0)
		return 0;

	idx = 0;
	len = sprintf(temp_buf, "RaTable:\n");
	memcpy(&buf[idx], temp_buf, len);
	idx += len;
	for (i = 0; i < 15; i++) {
		len = sprintf(temp_buf, "%d ", t_buf[i*2] << 8 | t_buf[i*2 + 1]);
		memcpy(&buf[idx], temp_buf, len);
		idx += len;
	}

	return idx;
}

static ssize_t fg_attr_show_Qmax(struct device *dev,
				struct device_attribute *attr, char *buf)
{
	struct i2c_client *client = to_i2c_client(dev);
	struct bq_fg_chip *bq = i2c_get_clientdata(client);
	int ret;
	u8 t_buf[32];
	u8 temp_buf[32];
	int i, idx, len;

	memset(t_buf, 0, 64);
	/* GaugingStatus3 register contains Qmax value */
	ret = fg_mac_read_block(bq, 0x0075, t_buf, 24);
	if (ret < 0)
		return 0;
	idx = 0;
	for (i = 0; i < 4; i++) {
		len = sprintf(temp_buf, "Qmax Cell %d = %d\n", i, (t_buf[i*2] << 8) | t_buf[i*2+1]);
		memcpy(&buf[idx], temp_buf, len);
		idx += len;
	}

	return idx;
}

static const u8 fg_dump_regs[] = {
	0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a,
	0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14,
	0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 
	0x3c, 0x3d, 0x3e, 0x3f, 0x4a, 0x4b, 0x4f,
};

static struct bq_fg_chip *g_bq = NULL;

static ssize_t bq4050_reg_show(struct device *dev,
				struct device_attribute *attr, char *buf)
{
	int i, ret, len = 0;
	u16 val;

	if(g_bq == NULL) {
		return -1;
	}
		
	for (i = 0; i < ARRAY_SIZE(fg_dump_regs); i++) {
		msleep(5);
		ret = fg_read_word(g_bq, fg_dump_regs[i], &val);
		if (ret < 0) {
			len += sprintf(&buf[len], "[0x%02x]=[err]\n", fg_dump_regs[i]);
		}			
		else {
			len += sprintf(&buf[len], "[0x%02x]=[0x%04x]\n", fg_dump_regs[i], val);		
		}		
	}
	
	return len;
}

static ssize_t bq4050_show_coulomb_counter(struct device *dev,
                struct device_attribute *attr, char *buf)
{
    return scnprintf(buf, PAGE_SIZE, "coulomb_counter=07\n");           
}

static DEVICE_ATTR(RaTable, S_IRUGO, fg_attr_show_Ra_table, NULL);
static DEVICE_ATTR(Qmax, S_IRUGO, fg_attr_show_Qmax, NULL);
static DEVICE_ATTR(bq4050_reg, S_IRUGO, bq4050_reg_show, NULL);
static DEVICE_ATTR(coulomb_counter, S_IRUGO, bq4050_show_coulomb_counter, NULL);

static struct attribute *bq4050_attributes[] = {
	&dev_attr_RaTable.attr,
	&dev_attr_Qmax.attr,
	&dev_attr_bq4050_reg.attr,
	&dev_attr_coulomb_counter.attr,
	NULL,
};

static const struct attribute_group bq4050_attr_group = {
	.attrs = bq4050_attributes,
};

static ssize_t bq4050_show_soh(struct device *dev,
				struct device_attribute *attr, char *buf)
{
	int len = 0;

	if(g_bq == NULL) {
		return -ENODATA;
	}
		
	len += sprintf(buf, "%d\n", g_bq->soh);

	return len;
}

static DEVICE_ATTR(soh, S_IRUGO, bq4050_show_soh, NULL);

static struct attribute *fg_attributes[] = {
	&dev_attr_soh.attr,
	NULL,
};

static const struct attribute_group fg_attr_group = {
	.attrs = fg_attributes,
};

static void fg_update_status(struct bq_fg_chip *bq)
{
	bq->batt_volt = fg_read_volt(bq);// uV
	if(pm_status == battery_chgIC_ok) {
		if((bq->batt_volt < 5000000) && (bq->batt_volt >= 0))
			pm_status |= overDischarge_err;
	}
	bq->batt_curr = fg_read_current(bq);// mA
	bq->batt_soc = fg_read_rsoc(bq);// %
	bq->batt_temp = fg_read_temperature(bq);//
	bq->batt_tte = fg_read_tte(bq);// min
	bq->batt_fcc = fg_read_fcc(bq);// mAh
	bq->batt_dc = fg_read_dc(bq);// mAh
	bq->batt_cyclecnt = fg_read_cyclecount(bq);// cycles
	bq->batt_rm = fg_read_rm(bq);// mAh
	bq->soh = fg_read_rsoh(bq);// %
	bq->batt_status = fg_get_batt_status(bq);
	bq->batt_capacity_level = fg_get_batt_capacity_level(bq);
	fg_read_sn(bq);
}

static void fg_clear_status(struct bq_fg_chip *bq)
{
	bq->batt_volt = -ENODATA;// mV
	bq->batt_curr = -ENODATA;// mA
	bq->batt_soc = -ENODATA;// %
	bq->batt_temp = -ENODATA;//
	bq->batt_tte = -ENODATA;// min
	bq->batt_fcc = -ENODATA;// mAh
	bq->batt_dc = -ENODATA;// mAh
	bq->batt_cyclecnt = -ENODATA;// cycles
	bq->batt_rm = -ENODATA;// mAh
	bq->soh = -ENODATA;// %
	bq->batt_status = fg_get_batt_status(bq);
	bq->batt_capacity_level = -ENODATA;
}

static void fg_monitor_workfunc(struct work_struct *work)
{
	struct delayed_work *monitor_work;
	struct bq_fg_chip *bq;
	monitor_work = container_of(work, struct delayed_work, work);
	bq = container_of(monitor_work, struct bq_fg_chip, monitor_work);

	bq->battery_present = battery_present_check(bq);

	if(bq->battery_present)
		fg_update_status(bq);
	else 
		fg_clear_status(bq);
	
	queue_delayed_work(bq->bq4050_workqueue, &bq->monitor_work, msecs_to_jiffies(1000));
}

static ssize_t charger_status_show(struct kobject *kobj, 
    struct kobj_attribute *attr, char *buf)
{
    int len = 0;
    len = sprintf(buf, "%d\n", (int)pm_status);
    return len;
}

static struct kobj_attribute charger_status_attribute = \
        __ATTR(charger_status, 0444, charger_status_show, NULL);

static int bq_fg_probe(struct i2c_client *client,
				const struct i2c_device_id *id)
{
	int ret;
	struct bq_fg_chip *bq;

	bq_log("{BQ4050}**PROBE START**\n");
	
	bq = kzalloc(sizeof(struct bq_fg_chip), GFP_KERNEL);
	if (!bq)
		return -ENOMEM;

	bq->dev = &client->dev;
	bq->client = client;
	bq->chip = id->driver_data;
	
	i2c_set_clientdata(client, bq);
	
	mutex_init(&bq->i2c_rw_lock);

	fg_clear_status(bq);

	//ret = fg_read_fw_version(bq);
	
	ret = fg_psy_register(bq);	
	if (ret) {
		bq_err("{BQ4050}%s : fg_psy_register fail!\n", __func__);
		ret = -EINVAL;
		goto err_1;
    }

	g_bq = bq;

	ret = sysfs_create_group(&bq->fg_psy.dev->kobj, &fg_attr_group);
	if (ret) {
		bq_err("{BQ4050}Failed to register fg_attr_group, err:%d\n", ret);
		ret = -EINVAL;
		goto err_2;
	}

	if (paxinfo_sysfs_kobj) {
		ret = sysfs_create_file(paxinfo_sysfs_kobj, &charger_status_attribute.attr);
		if (ret)
			bq_err("{BQ4050}Failed to register charger_status_attr_group, err:%d\n", ret);
	}
	
	ret = sysfs_create_group(&bq->dev->kobj, &bq4050_attr_group);
	if (ret)
		bq_err("{BQ4050}Failed to register bq4050_attr_group, err:%d\n", ret);

	bq->bq4050_workqueue = create_singlethread_workqueue("fg_bq4050");
	INIT_DELAYED_WORK(&bq->monitor_work, fg_monitor_workfunc);
	queue_delayed_work(bq->bq4050_workqueue, &bq->monitor_work , msecs_to_jiffies(1000));
	
	bq_log("{BQ4050}**PROBE END**\n");

	return 0;

err_2:
	fg_psy_unregister(bq);
err_1:
	kfree(bq);
	
	return ret;
}

static int bq_fg_remove(struct i2c_client *client)
{
	struct bq_fg_chip *bq = i2c_get_clientdata(client);

	cancel_delayed_work(&bq->monitor_work);
	flush_workqueue(bq->bq4050_workqueue);
	destroy_workqueue(bq->bq4050_workqueue);

	sysfs_remove_group(&bq->dev->kobj, &bq4050_attr_group);
	sysfs_remove_file(paxinfo_sysfs_kobj, &charger_status_attribute.attr);
	sysfs_remove_group(&bq->fg_psy.dev->kobj, &fg_attr_group);
	
	fg_psy_unregister(bq);

	mutex_destroy(&bq->i2c_rw_lock);

	kfree(bq);

	return 0;
}

#ifdef CONFIG_PM
static int bq4050_bat_suspend(struct device *dev)
{
    struct i2c_client *client = to_i2c_client(dev);
    struct bq_fg_chip *bq = i2c_get_clientdata(client);
    cancel_delayed_work(&bq->monitor_work);
    return 0;
}

static int bq4050_bat_resume(struct device *dev)
{	
    struct i2c_client *client = to_i2c_client(dev);
    struct bq_fg_chip *bq = i2c_get_clientdata(client);
    queue_delayed_work(bq->bq4050_workqueue, &bq->monitor_work, msecs_to_jiffies(1000));
    return 0;
}

static const struct dev_pm_ops bq4050_bat_pm_ops = {
        .suspend  = bq4050_bat_suspend,
        .resume   = bq4050_bat_resume,
};
#endif

static const struct i2c_device_id bq_fg_id[] = {
	{ "bq40z50", BQ40Z50 },
	{},
};
MODULE_DEVICE_TABLE(i2c, bq_fg_id);

static struct i2c_driver bq_fg_driver = {
	.driver	= {
		.name   = "bq_fg",
		.owner  = THIS_MODULE,
#ifdef CONFIG_PM
        .pm     = &bq4050_bat_pm_ops,
#endif
	},
	.id_table       = bq_fg_id,
	.probe          = bq_fg_probe,
	.remove		= bq_fg_remove,
};

module_i2c_driver(bq_fg_driver);

MODULE_DESCRIPTION("TI BQ40Z50 Driver");
MODULE_LICENSE("GPL v2");
MODULE_AUTHOR("PAX");


  • The problem happens only with registers that should be read with block protocol, if they read registers with word protocol like 13.28 0x1B ManufacturerDate(), the problem will never happen.

  • Hi Howard,

    Please check if there is bus contention or other devices on the bus. You can lock up reads especially when the gauge is doing some interrupt processing. A block read requires the data to be fetched to the manufacturer access registers before it can be read back. If there's a chance that there's some comms between the data command request and reading back, the data can change.