|
@@ -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;
|
|
|
}
|