diff --git a/drivers/power/bq20z75.c b/drivers/power/bq20z75.c index a1c7ae2ec9f8..492da27e1a47 100644 --- a/drivers/power/bq20z75.c +++ b/drivers/power/bq20z75.c @@ -36,7 +36,11 @@ enum { REG_TIME_TO_FULL, REG_STATUS, REG_CYCLE_COUNT, - REG_SERIAL_NUMBER + REG_SERIAL_NUMBER, + REG_REMAINING_CAPACITY, + REG_FULL_CHARGE_CAPACITY, + REG_DESIGN_CAPACITY, + REG_DESIGN_VOLTAGE, }; /* manufacturer access defines */ @@ -44,7 +48,7 @@ enum { #define MANUFACTURER_ACCESS_SLEEP 0x0011 /* battery status value bits */ -#define BATTERY_CHARGING 0x40 +#define BATTERY_DISCHARGING 0x40 #define BATTERY_FULL_CHARGED 0x20 #define BATTERY_FULL_DISCHARGED 0x10 @@ -72,6 +76,10 @@ static const struct bq20z75_device_data { 32767), [REG_CAPACITY] = BQ20Z75_DATA(POWER_SUPPLY_PROP_CAPACITY, 0x0E, 0, 100), + [REG_REMAINING_CAPACITY] = + BQ20Z75_DATA(POWER_SUPPLY_PROP_ENERGY_NOW, 0x0F, 0, 65535), + [REG_FULL_CHARGE_CAPACITY] = + BQ20Z75_DATA(POWER_SUPPLY_PROP_ENERGY_FULL, 0x10, 0, 65535), [REG_TIME_TO_EMPTY] = BQ20Z75_DATA(POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG, 0x12, 0, 65535), @@ -82,6 +90,12 @@ static const struct bq20z75_device_data { BQ20Z75_DATA(POWER_SUPPLY_PROP_STATUS, 0x16, 0, 65535), [REG_CYCLE_COUNT] = BQ20Z75_DATA(POWER_SUPPLY_PROP_CYCLE_COUNT, 0x17, 0, 65535), + [REG_DESIGN_CAPACITY] = + BQ20Z75_DATA(POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN, 0x18, 0, + 65535), + [REG_DESIGN_VOLTAGE] = + BQ20Z75_DATA(POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN, 0x19, 0, + 65535), [REG_SERIAL_NUMBER] = BQ20Z75_DATA(POWER_SUPPLY_PROP_SERIAL_NUMBER, 0x1C, 0, 65535), }; @@ -99,6 +113,10 @@ static enum power_supply_property bq20z75_properties[] = { POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG, POWER_SUPPLY_PROP_TIME_TO_FULL_AVG, POWER_SUPPLY_PROP_SERIAL_NUMBER, + POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN, + POWER_SUPPLY_PROP_ENERGY_NOW, + POWER_SUPPLY_PROP_ENERGY_FULL, + POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN, }; struct bq20z75_info { @@ -106,6 +124,35 @@ struct bq20z75_info { struct power_supply power_supply; }; +static int bq20z75_read_word_data(struct i2c_client *client, u8 address) +{ + s32 ret; + + ret = i2c_smbus_read_word_data(client, address); + if (ret < 0) { + dev_err(&client->dev, + "%s: i2c read at address 0x%x failed\n", + __func__, address); + return ret; + } + return le16_to_cpu(ret); +} + +static int bq20z75_write_word_data(struct i2c_client *client, u8 address, + u16 value) +{ + s32 ret; + + ret = i2c_smbus_write_word_data(client, address, le16_to_cpu(value)); + if (ret < 0) { + dev_err(&client->dev, + "%s: i2c write to address 0x%x failed\n", + __func__, address); + return ret; + } + return 0; +} + static int bq20z75_get_battery_presence_and_health( struct i2c_client *client, enum power_supply_property psp, union power_supply_propval *val) @@ -115,24 +162,17 @@ static int bq20z75_get_battery_presence_and_health( /* Write to ManufacturerAccess with * ManufacturerAccess command and then * read the status */ - ret = i2c_smbus_write_word_data(client, + ret = bq20z75_write_word_data(client, bq20z75_data[REG_MANUFACTURER_DATA].addr, MANUFACTURER_ACCESS_STATUS); - if (ret < 0) { - dev_err(&client->dev, - "%s: i2c write for battery presence failed\n", - __func__); - return -ENODEV; - } + if (ret < 0) + return ret; - ret = i2c_smbus_read_word_data(client, + + ret = bq20z75_read_word_data(client, bq20z75_data[REG_MANUFACTURER_DATA].addr); - if (ret < 0) { - dev_err(&client->dev, - "%s: i2c read for battery presence failed\n", - __func__); - return -EIO; - } + if (ret < 0) + return ret; if (ret < bq20z75_data[REG_MANUFACTURER_DATA].min_value || ret > bq20z75_data[REG_MANUFACTURER_DATA].max_value) { @@ -171,31 +211,28 @@ static int bq20z75_get_battery_property(struct i2c_client *client, { s32 ret; - ret = i2c_smbus_read_word_data(client, + ret = bq20z75_read_word_data(client, bq20z75_data[reg_offset].addr); - if (ret < 0) { - dev_err(&client->dev, - "%s: i2c read for %d failed\n", __func__, reg_offset); - return -EIO; - } + if (ret < 0) + return ret; + + /* returned values are 16 bit */ + if (bq20z75_data[reg_offset].min_value < 0) + ret = (s16)ret; if (ret >= bq20z75_data[reg_offset].min_value && ret <= bq20z75_data[reg_offset].max_value) { val->intval = ret; if (psp == POWER_SUPPLY_PROP_STATUS) { - if (ret & BATTERY_CHARGING) - val->intval = POWER_SUPPLY_STATUS_CHARGING; - else if (ret & BATTERY_FULL_CHARGED) + if (ret & BATTERY_FULL_CHARGED) val->intval = POWER_SUPPLY_STATUS_FULL; else if (ret & BATTERY_FULL_DISCHARGED) val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING; - else + else if (ret & BATTERY_DISCHARGING) val->intval = POWER_SUPPLY_STATUS_DISCHARGING; + else + val->intval = POWER_SUPPLY_STATUS_CHARGING; } - /* bq20z75 provides battery tempreture in 0.1°K - * so convert it to °C */ - else if (psp == POWER_SUPPLY_PROP_TEMP) - val->intval = ret - 2731; } else { if (psp == POWER_SUPPLY_PROP_STATUS) val->intval = POWER_SUPPLY_STATUS_UNKNOWN; @@ -206,21 +243,77 @@ static int bq20z75_get_battery_property(struct i2c_client *client, return 0; } +static void bq20z75_unit_adjustment(struct i2c_client *client, + enum power_supply_property psp, union power_supply_propval *val) +{ +#define BASE_UNIT_CONVERSION 1000 +#define BATTERY_MODE_CAP_MULT_WATT (10 * BASE_UNIT_CONVERSION) +#define TIME_UNIT_CONVERSION 600 +#define TEMP_KELVIN_TO_CELCIUS 2731 + switch (psp) { + case POWER_SUPPLY_PROP_ENERGY_NOW: + case POWER_SUPPLY_PROP_ENERGY_FULL: + case POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN: + val->intval *= BATTERY_MODE_CAP_MULT_WATT; + break; + + case POWER_SUPPLY_PROP_VOLTAGE_NOW: + case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN: + case POWER_SUPPLY_PROP_CURRENT_NOW: + val->intval *= BASE_UNIT_CONVERSION; + break; + + case POWER_SUPPLY_PROP_TEMP: + /* bq20z75 provides battery tempreture in 0.1°K + * so convert it to 0.1°C */ + val->intval -= TEMP_KELVIN_TO_CELCIUS; + val->intval *= 10; + break; + + case POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG: + case POWER_SUPPLY_PROP_TIME_TO_FULL_AVG: + val->intval *= TIME_UNIT_CONVERSION; + break; + + default: + dev_dbg(&client->dev, + "%s: no need for unit conversion %d\n", __func__, psp); + } +} + static int bq20z75_get_battery_capacity(struct i2c_client *client, + int reg_offset, enum power_supply_property psp, union power_supply_propval *val) { s32 ret; - ret = i2c_smbus_read_byte_data(client, bq20z75_data[REG_CAPACITY].addr); - if (ret < 0) { - dev_err(&client->dev, - "%s: i2c read for %d failed\n", __func__, REG_CAPACITY); - return -EIO; - } + ret = bq20z75_read_word_data(client, bq20z75_data[reg_offset].addr); + if (ret < 0) + return ret; - /* bq20z75 spec says that this can be >100 % - * even if max value is 100 % */ - val->intval = min(ret, 100); + if (psp == POWER_SUPPLY_PROP_CAPACITY) { + /* bq20z75 spec says that this can be >100 % + * even if max value is 100 % */ + val->intval = min(ret, 100); + } else + val->intval = ret; + + return 0; +} + +static char bq20z75_serial[5]; +static int bq20z75_get_battery_serial_number(struct i2c_client *client, + union power_supply_propval *val) +{ + int ret; + + ret = bq20z75_read_word_data(client, + bq20z75_data[REG_SERIAL_NUMBER].addr); + if (ret < 0) + return ret; + + ret = sprintf(bq20z75_serial, "%04x", ret); + val->strval = bq20z75_serial; return 0; } @@ -247,8 +340,23 @@ static int bq20z75_get_property(struct power_supply *psy, val->intval = POWER_SUPPLY_TECHNOLOGY_LION; break; + case POWER_SUPPLY_PROP_ENERGY_NOW: + case POWER_SUPPLY_PROP_ENERGY_FULL: + case POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN: case POWER_SUPPLY_PROP_CAPACITY: - ret = bq20z75_get_battery_capacity(client, val); + for (count = 0; count < ARRAY_SIZE(bq20z75_data); count++) { + if (psp == bq20z75_data[count].psp) + break; + } + + ret = bq20z75_get_battery_capacity(client, count, psp, val); + if (ret) + return ret; + + break; + + case POWER_SUPPLY_PROP_SERIAL_NUMBER: + ret = bq20z75_get_battery_serial_number(client, val); if (ret) return ret; break; @@ -260,7 +368,7 @@ static int bq20z75_get_property(struct power_supply *psy, case POWER_SUPPLY_PROP_TEMP: case POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG: case POWER_SUPPLY_PROP_TIME_TO_FULL_AVG: - case POWER_SUPPLY_PROP_SERIAL_NUMBER: + case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN: for (count = 0; count < ARRAY_SIZE(bq20z75_data); count++) { if (psp == bq20z75_data[count].psp) break; @@ -269,6 +377,7 @@ static int bq20z75_get_property(struct power_supply *psy, ret = bq20z75_get_battery_property(client, count, psp, val); if (ret) return ret; + break; default: @@ -277,6 +386,9 @@ static int bq20z75_get_property(struct power_supply *psy, return -EINVAL; } + /* Convert units to match requirements for power supply class */ + bq20z75_unit_adjustment(client, psp, val); + dev_dbg(&client->dev, "%s: property = %d, value = %d\n", __func__, psp, val->intval); @@ -335,15 +447,11 @@ static int bq20z75_suspend(struct i2c_client *client, s32 ret; /* write to manufacturer access with sleep command */ - ret = i2c_smbus_write_word_data(client, + ret = bq20z75_write_word_data(client, bq20z75_data[REG_MANUFACTURER_DATA].addr, MANUFACTURER_ACCESS_SLEEP); - if (ret < 0) { - dev_err(&client->dev, - "%s: i2c write for %d failed\n", - __func__, MANUFACTURER_ACCESS_SLEEP); - return -EIO; - } + if (ret < 0) + return ret; return 0; }