Skip to content

Commit 79bcd5a

Browse files
committed
power: supply: sbs-battery: add PEC support
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 <[email protected]> Signed-off-by: Sebastian Reichel <[email protected]>
1 parent c4b12a2 commit 79bcd5a

File tree

1 file changed

+69
-3
lines changed

1 file changed

+69
-3
lines changed

drivers/power/supply/sbs-battery.c

Lines changed: 69 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,14 @@ enum {
4646
REG_MODEL_NAME,
4747
};
4848

49+
#define REG_ADDR_SPEC_INFO 0x1A
50+
#define SPEC_INFO_VERSION_MASK GENMASK(7, 4)
51+
#define SPEC_INFO_VERSION_SHIFT 4
52+
53+
#define SBS_VERSION_1_0 1
54+
#define SBS_VERSION_1_1 2
55+
#define SBS_VERSION_1_1_WITH_PEC 3
56+
4957
/* Battery Mode defines */
5058
#define BATTERY_MODE_OFFSET 0x03
5159
#define BATTERY_MODE_CAPACITY_MASK BIT(15)
@@ -175,6 +183,64 @@ static char model_name[I2C_SMBUS_BLOCK_MAX + 1];
175183
static char manufacturer[I2C_SMBUS_BLOCK_MAX + 1];
176184
static bool force_load;
177185

186+
static int sbs_update_presence(struct sbs_info *chip, bool is_present)
187+
{
188+
struct i2c_client *client = chip->client;
189+
int retries = chip->i2c_retry_count;
190+
s32 ret = 0;
191+
u8 version;
192+
193+
if (chip->is_present == is_present)
194+
return 0;
195+
196+
if (!is_present) {
197+
chip->is_present = false;
198+
/* Disable PEC when no device is present */
199+
client->flags &= ~I2C_CLIENT_PEC;
200+
return 0;
201+
}
202+
203+
/* Check if device supports packet error checking and use it */
204+
while (retries > 0) {
205+
ret = i2c_smbus_read_word_data(client, REG_ADDR_SPEC_INFO);
206+
if (ret >= 0)
207+
break;
208+
209+
/*
210+
* Some batteries trigger the detection pin before the
211+
* I2C bus is properly connected. This works around the
212+
* issue.
213+
*/
214+
msleep(100);
215+
216+
retries--;
217+
}
218+
219+
if (ret < 0) {
220+
dev_dbg(&client->dev, "failed to read spec info: %d\n", ret);
221+
222+
/* fallback to old behaviour */
223+
client->flags &= ~I2C_CLIENT_PEC;
224+
chip->is_present = true;
225+
226+
return ret;
227+
}
228+
229+
version = (ret & SPEC_INFO_VERSION_MASK) >> SPEC_INFO_VERSION_SHIFT;
230+
231+
if (version == SBS_VERSION_1_1_WITH_PEC)
232+
client->flags |= I2C_CLIENT_PEC;
233+
else
234+
client->flags &= ~I2C_CLIENT_PEC;
235+
236+
dev_dbg(&client->dev, "PEC: %s\n", (client->flags & I2C_CLIENT_PEC) ?
237+
"enabled" : "disabled");
238+
239+
chip->is_present = true;
240+
241+
return 0;
242+
}
243+
178244
static int sbs_read_word_data(struct i2c_client *client, u8 address)
179245
{
180246
struct sbs_info *chip = i2c_get_clientdata(client);
@@ -579,7 +645,7 @@ static int sbs_get_property(struct power_supply *psy,
579645
return ret;
580646
if (psp == POWER_SUPPLY_PROP_PRESENT) {
581647
val->intval = ret;
582-
chip->is_present = val->intval;
648+
sbs_update_presence(chip, ret);
583649
return 0;
584650
}
585651
if (ret == 0)
@@ -678,7 +744,7 @@ static int sbs_get_property(struct power_supply *psy,
678744

679745
if (!chip->gpio_detect &&
680746
chip->is_present != (ret >= 0)) {
681-
chip->is_present = (ret >= 0);
747+
sbs_update_presence(chip, (ret >= 0));
682748
power_supply_changed(chip->power_supply);
683749
}
684750

@@ -709,7 +775,7 @@ static void sbs_supply_changed(struct sbs_info *chip)
709775
ret = gpiod_get_value_cansleep(chip->gpio_detect);
710776
if (ret < 0)
711777
return;
712-
chip->is_present = ret;
778+
sbs_update_presence(chip, ret);
713779
power_supply_changed(battery);
714780
}
715781

0 commit comments

Comments
 (0)