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");