diff options
-rw-r--r-- | drivers/power/bq27x00_battery.c | 198 |
1 files changed, 126 insertions, 72 deletions
diff --git a/drivers/power/bq27x00_battery.c b/drivers/power/bq27x00_battery.c index 3fede6541e6f..355fb2b673dc 100644 --- a/drivers/power/bq27x00_battery.c +++ b/drivers/power/bq27x00_battery.c @@ -5,6 +5,7 @@ * Copyright (C) 2008 Eurotech S.p.A. <info@eurotech.it> * Copyright (C) 2010-2011 Lars-Peter Clausen <lars@metafoo.de> * Copyright (C) 2011 Pali Rohár <pali.rohar@gmail.com> + * Copyright (C) 2011 NVIDIA Corporation. * * Based on a previous work by Copyright (C) 2008 Texas Instruments, Inc. * @@ -72,6 +73,7 @@ #define BQ27510_ENERGY_AVAIL 0x22 #define BQ27510_POWER_AVG 0x24 #define BQ27510_CYCLE_COUNT 0x2a + /* bq27510-g2 control register sub-commands*/ #define BQ27510_CNTL_DEVICE_TYPE 0x0001 #define BQ27510_CNTL_SET_SLEEP 0x0013 @@ -81,6 +83,10 @@ struct bq27x00_device_info; struct bq27x00_access_methods { int (*read)(struct bq27x00_device_info *di, u8 reg, bool single); + int (*ctrl_read)(struct bq27x00_device_info *di, u8 ctrl_reg, + u16 ctrl_func_reg); + int (*write)(struct bq27x00_device_info *di, u8 reg, u16 val, + bool single); }; enum bq27x00_chip { BQ27000, BQ27500, BQ27510 }; @@ -153,26 +159,38 @@ static inline int bq27x00_read(struct bq27x00_device_info *di, u8 reg, return di->bus.read(di, reg, single); } +static inline int bq27x00_ctrl_read(struct bq27x00_device_info *di, + u8 ctrl_reg, u16 ctrl_func_reg) +{ + return di->bus.ctrl_read(di, ctrl_reg, ctrl_func_reg); +} + +static inline int bq27x00_write(struct bq27x00_device_info *di, u8 reg, + u16 val, bool single) +{ + return di->bus.write(di, reg, val, single); +} + static int bq27510_battery_health(struct bq27x00_device_info *di, - int reg_offset) + union power_supply_propval *val) { int ret; - int status; - if (di->chip == BQ27500 || di->chip == BQ27510) { - ret = i2c_smbus_read_word_data(di->client, reg_offset); - if (ret < 0) { + if ((di->chip == BQ27500) || (di->chip == BQ27510)) { + ret = bq27x00_read(di, BQ27x00_REG_FLAGS, false); + if (ret < 0) { dev_err(di->dev, "read failure\n"); return ret; } if (ret & BQ27500_FLAG_SOCF) - status = POWER_SUPPLY_HEALTH_DEAD; + val->intval = POWER_SUPPLY_HEALTH_DEAD; else if (ret & BQ27500_FLAG_OTC) - status = POWER_SUPPLY_HEALTH_OVERHEAT; + val->intval = POWER_SUPPLY_HEALTH_OVERHEAT; else - status = POWER_SUPPLY_HEALTH_GOOD; - return status; + val->intval = POWER_SUPPLY_HEALTH_GOOD; + + return 0; } return -1; @@ -186,7 +204,7 @@ static int bq27x00_battery_read_rsoc(struct bq27x00_device_info *di) { int rsoc; - if (di->chip == BQ27500) + if ((di->chip == BQ27500) || (di->chip == BQ27510)) rsoc = bq27x00_read(di, BQ27500_REG_SOC, false); else rsoc = bq27x00_read(di, BQ27000_REG_RSOC, true); @@ -245,7 +263,7 @@ static int bq27x00_battery_read_ilmd(struct bq27x00_device_info *di) { int ilmd; - if (di->chip == BQ27500) + if ((di->chip == BQ27500) || (di->chip == BQ27510)) ilmd = bq27x00_read(di, BQ27500_REG_DCAP, false); else ilmd = bq27x00_read(di, BQ27000_REG_ILMD, true); @@ -255,7 +273,7 @@ static int bq27x00_battery_read_ilmd(struct bq27x00_device_info *di) return ilmd; } - if (di->chip == BQ27500) + if ((di->chip == BQ27500) || (di->chip == BQ27510)) ilmd *= 1000; else ilmd = ilmd * 256 * 3570 / BQ27000_RS; @@ -374,15 +392,15 @@ static int bq27x00_battery_current(struct bq27x00_device_info *di, { int curr; - if (di->chip == BQ27500) - curr = bq27x00_read(di, BQ27x00_REG_AI, false); + if ((di->chip == BQ27500) || (di->chip == BQ27510)) + curr = bq27x00_read(di, BQ27x00_REG_AI, false); else - curr = di->cache.current_now; + curr = di->cache.current_now; if (curr < 0) return curr; - if (di->chip == BQ27500) { + if ((di->chip == BQ27500) || (di->chip == BQ27510)) { /* bq27500 returns signed value */ val->intval = (int)((s16)curr) * 1000; } else { @@ -458,7 +476,7 @@ static int bq27x00_battery_energy(struct bq27x00_device_info *di, return ae; } - if (di->chip == BQ27500) + if ((di->chip == BQ27500) || (di->chip == BQ27510)) ae *= 1000; else ae = ae * 29200 / BQ27000_RS; @@ -485,12 +503,18 @@ static int bq27510_battery_present(struct bq27x00_device_info *di, { int ret; - ret = i2c_smbus_read_word_data(di->client, BQ27x00_REG_FLAGS); - if (!(ret & BQ27500_FLAG_BAT_DET)) - val->intval = 0; - else + ret = bq27x00_read(di, BQ27x00_REG_FLAGS, false); + if (ret < 0) { + dev_err(di->dev, "error reading flags\n"); + return ret; + } + + if (ret & BQ27500_FLAG_BAT_DET) val->intval = 1; - return val->intval; + else + val->intval = 0; + + return 0; } static char bq27510_serial[5]; @@ -500,18 +524,8 @@ static int bq27510_get_battery_serial_number(struct bq27x00_device_info *di, int ret; if (di->chip == BQ27510) { - ret = i2c_smbus_write_word_data(di->client, BQ27510_CNTL, + ret = bq27x00_ctrl_read(di, BQ27510_CNTL, BQ27510_CNTL_DEVICE_TYPE); - if (ret < 0) { - dev_err(di->dev, "write failure\n"); - return ret; - } - ret = i2c_smbus_read_word_data(di->client, 0x00); - if (ret < 0) { - dev_err(di->dev, "read failure\n"); - return ret; - } - ret = sprintf(bq27510_serial, "%04x", ret); val->strval = bq27510_serial; return 0; @@ -521,12 +535,19 @@ static int bq27510_get_battery_serial_number(struct bq27x00_device_info *di, } static int bq27510_battery_power_avg(struct bq27x00_device_info *di, - int reg_offset) + union power_supply_propval *val) { - if (di->chip == BQ27510) - return i2c_smbus_read_word_data(di->client, reg_offset); - else + int ret; + if (di->chip == BQ27510) { + ret = bq27x00_read(di, BQ27510_POWER_AVG, false); + if (ret < 0) { + dev_err(di->dev, "read failure\n"); + return ret; + } + val->intval = ret; return 0; + } + return -1; } static int bq27510_battery_cycle_count(struct bq27x00_device_info *di, @@ -535,8 +556,8 @@ static int bq27510_battery_cycle_count(struct bq27x00_device_info *di, int ret; if (di->chip == BQ27510) { - ret = i2c_smbus_read_word_data(di->client, reg_offset); - if (ret < 0) + ret = bq27x00_read(di, reg_offset, false); + if (ret < 0) dev_err(di->dev, "read failure\n"); return ret; } else { @@ -572,7 +593,7 @@ static int bq27x00_battery_get_property(struct power_supply *psy, ret = bq27x00_battery_voltage(di, val); break; case POWER_SUPPLY_PROP_PRESENT: - val->intval = bq27510_battery_present(di, val); + ret = bq27510_battery_present(di, val); break; case POWER_SUPPLY_PROP_CURRENT_NOW: ret = bq27x00_battery_current(di, val); @@ -611,8 +632,7 @@ static int bq27x00_battery_get_property(struct power_supply *psy, ret = bq27x00_battery_energy(di, val); break; case POWER_SUPPLY_PROP_POWER_AVG: - val->intval = bq27510_battery_power_avg(di, - BQ27510_POWER_AVG); + ret = bq27510_battery_power_avg(di, val); break; case POWER_SUPPLY_PROP_CYCLE_COUNT: val->intval = bq27510_battery_cycle_count(di, @@ -623,7 +643,7 @@ static int bq27x00_battery_get_property(struct power_supply *psy, return -EINVAL; break; case POWER_SUPPLY_PROP_HEALTH: - val->intval = bq27510_battery_health(di, BQ27x00_REG_FLAGS); + ret = bq27510_battery_health(di, val); break; default: return -EINVAL; @@ -719,6 +739,48 @@ static int bq27x00_read_i2c(struct bq27x00_device_info *di, u8 reg, bool single) return ret; } +static int bq27x00_write_i2c(struct bq27x00_device_info *di, u8 reg, + u16 val, bool single) +{ + struct i2c_client *client = to_i2c_client(di->dev); + unsigned char i2c_data[3]; + int ret, len; + + i2c_data[0] = reg; + i2c_data[1] = val & 0xff; + + if (single) { + len = 2; + } else { + i2c_data[2] = (val >> 8) & 0xff; + len = 3; + } + + ret = i2c_master_send(client, i2c_data, len); + if (ret == len) + return 0; + + return (ret < 0) ? ret : -EIO; +} + +static int bq27x00_ctrl_read_i2c(struct bq27x00_device_info *di, + u8 ctrl_reg, u16 ctrl_func_reg) +{ + int ret = bq27x00_write(di, ctrl_reg, ctrl_func_reg, false); + if (ret < 0) { + dev_err(di->dev, "write failure\n"); + return ret; + } + + ret = bq27x00_read(di, ctrl_reg, false); + if (ret < 0) { + dev_err(di->dev, "read failure\n"); + return ret; + } + + return ret; +} + static int bq27x00_battery_probe(struct i2c_client *client, const struct i2c_device_id *id) { @@ -757,6 +819,8 @@ static int bq27x00_battery_probe(struct i2c_client *client, di->chip = id->driver_data; di->bat.name = name; di->bus.read = &bq27x00_read_i2c; + di->bus.ctrl_read = &bq27x00_ctrl_read_i2c; + di->bus.write = &bq27x00_write_i2c; i2c_set_clientdata(client, di); @@ -767,7 +831,7 @@ static int bq27x00_battery_probe(struct i2c_client *client, goto batt_failed_3; } - read_data = i2c_smbus_read_word_data(di->client, BQ27x00_REG_FLAGS); + read_data = bq27x00_read(di, BQ27x00_REG_FLAGS, false); if (!(read_data & BQ27500_FLAG_BAT_DET)) { dev_err(&client->dev, "no battery present\n"); @@ -814,24 +878,19 @@ static int bq27x00_battery_remove(struct i2c_client *client) static int bq27x00_battery_suspend(struct i2c_client *client, pm_message_t state) { - u8 ret; - struct bq27x00_device_info *bq27500_device; - - bq27500_device = i2c_get_clientdata(client); + int ret; + struct bq27x00_device_info *di = i2c_get_clientdata(client); - if (bq27500_device->chip == BQ27510) { - ret = i2c_smbus_write_word_data(bq27500_device->client, - BQ27510_CNTL, BQ27510_CNTL_SET_SLEEP); + if (di->chip == BQ27510) { + ret = bq27x00_write(di, BQ27510_CNTL, + BQ27510_CNTL_SET_SLEEP, false); if (ret < 0) { - dev_err(&bq27500_device->client->dev, - "write failure\n"); + dev_err(di->dev, "write failure\n"); return ret; } - ret = i2c_smbus_write_word_data(bq27500_device->client, - BQ27510_CNTL, 0x01); - if (ret < 0) { - dev_err(&bq27500_device->client->dev, - "write failure\n"); + ret = bq27x00_write(di, BQ27510_CNTL, 0x01, false); + if (ret < 0) { + dev_err(di->dev, "write failure\n"); return ret; } } @@ -840,24 +899,19 @@ static int bq27x00_battery_suspend(struct i2c_client *client, static int bq27x00_battery_resume(struct i2c_client *client) { - u8 ret; - struct bq27x00_device_info *bq27500_device; - - bq27500_device = i2c_get_clientdata(client); + int ret; + struct bq27x00_device_info *di = i2c_get_clientdata(client); - if (bq27500_device->chip == BQ27510) { - ret = i2c_smbus_write_word_data(bq27500_device->client, - BQ27510_CNTL, BQ27510_CNTL_CLEAR_SLEEP); + if (di->chip == BQ27510) { + ret = bq27x00_write(di, BQ27510_CNTL, + BQ27510_CNTL_CLEAR_SLEEP, false); if (ret < 0) { - dev_err(&bq27500_device->client->dev, - "write failure\n"); + dev_err(di->dev, "write failure\n"); return ret; } - ret = i2c_smbus_write_word_data(bq27500_device->client, - BQ27510_CNTL, 0x01); - if (ret < 0) { - dev_err(&bq27500_device->client->dev, - "write failure\n"); + ret = bq27x00_write(di, BQ27510_CNTL, 0x01, false); + if (ret < 0) { + dev_err(di->dev, "write failure\n"); return ret; } } |