Skip to content

Commit

Permalink
power: supply: sbs-battery: add PEC support
Browse files Browse the repository at this point in the history
SBS batteries optionally have support for PEC. This enables
PEC handling based on the implemented SBS version as suggested
by the standard. The support for PEC is re-evaluated when the
battery is hotplugged into the system, since there might be
systems supporting batteries from different SBS generations.

Reviewed-by: Emil Velikov <emil.velikov@collabora.com>
Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com>
  • Loading branch information
sre committed May 28, 2020
1 parent c4b12a2 commit 79bcd5a
Showing 1 changed file with 69 additions and 3 deletions.
72 changes: 69 additions & 3 deletions drivers/power/supply/sbs-battery.c
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,14 @@ enum {
REG_MODEL_NAME,
};

#define REG_ADDR_SPEC_INFO 0x1A
#define SPEC_INFO_VERSION_MASK GENMASK(7, 4)
#define SPEC_INFO_VERSION_SHIFT 4

#define SBS_VERSION_1_0 1
#define SBS_VERSION_1_1 2
#define SBS_VERSION_1_1_WITH_PEC 3

/* Battery Mode defines */
#define BATTERY_MODE_OFFSET 0x03
#define BATTERY_MODE_CAPACITY_MASK BIT(15)
Expand Down Expand Up @@ -175,6 +183,64 @@ static char model_name[I2C_SMBUS_BLOCK_MAX + 1];
static char manufacturer[I2C_SMBUS_BLOCK_MAX + 1];
static bool force_load;

static int sbs_update_presence(struct sbs_info *chip, bool is_present)
{
struct i2c_client *client = chip->client;
int retries = chip->i2c_retry_count;
s32 ret = 0;
u8 version;

if (chip->is_present == is_present)
return 0;

if (!is_present) {
chip->is_present = false;
/* Disable PEC when no device is present */
client->flags &= ~I2C_CLIENT_PEC;
return 0;
}

/* Check if device supports packet error checking and use it */
while (retries > 0) {
ret = i2c_smbus_read_word_data(client, REG_ADDR_SPEC_INFO);
if (ret >= 0)
break;

/*
* Some batteries trigger the detection pin before the
* I2C bus is properly connected. This works around the
* issue.
*/
msleep(100);

retries--;
}

if (ret < 0) {
dev_dbg(&client->dev, "failed to read spec info: %d\n", ret);

/* fallback to old behaviour */
client->flags &= ~I2C_CLIENT_PEC;
chip->is_present = true;

return ret;
}

version = (ret & SPEC_INFO_VERSION_MASK) >> SPEC_INFO_VERSION_SHIFT;

if (version == SBS_VERSION_1_1_WITH_PEC)
client->flags |= I2C_CLIENT_PEC;
else
client->flags &= ~I2C_CLIENT_PEC;

dev_dbg(&client->dev, "PEC: %s\n", (client->flags & I2C_CLIENT_PEC) ?
"enabled" : "disabled");

chip->is_present = true;

return 0;
}

static int sbs_read_word_data(struct i2c_client *client, u8 address)
{
struct sbs_info *chip = i2c_get_clientdata(client);
Expand Down Expand Up @@ -579,7 +645,7 @@ static int sbs_get_property(struct power_supply *psy,
return ret;
if (psp == POWER_SUPPLY_PROP_PRESENT) {
val->intval = ret;
chip->is_present = val->intval;
sbs_update_presence(chip, ret);
return 0;
}
if (ret == 0)
Expand Down Expand Up @@ -678,7 +744,7 @@ static int sbs_get_property(struct power_supply *psy,

if (!chip->gpio_detect &&
chip->is_present != (ret >= 0)) {
chip->is_present = (ret >= 0);
sbs_update_presence(chip, (ret >= 0));
power_supply_changed(chip->power_supply);
}

Expand Down Expand Up @@ -709,7 +775,7 @@ static void sbs_supply_changed(struct sbs_info *chip)
ret = gpiod_get_value_cansleep(chip->gpio_detect);
if (ret < 0)
return;
chip->is_present = ret;
sbs_update_presence(chip, ret);
power_supply_changed(battery);
}

Expand Down

0 comments on commit 79bcd5a

Please sign in to comment.