|
@@ -23,6 +23,7 @@
|
|
|
#include <linux/kernel.h>
|
|
|
#include <linux/module.h>
|
|
|
#include <linux/of.h>
|
|
|
+#include <linux/of_device.h>
|
|
|
#include <linux/power/sbs-battery.h>
|
|
|
#include <linux/power_supply.h>
|
|
|
#include <linux/slab.h>
|
|
@@ -156,6 +157,9 @@ static enum power_supply_property sbs_properties[] = {
|
|
|
POWER_SUPPLY_PROP_MODEL_NAME
|
|
|
};
|
|
|
|
|
|
+/* Supports special manufacturer commands from TI BQ20Z75 IC. */
|
|
|
+#define SBS_FLAGS_TI_BQ20Z75 BIT(0)
|
|
|
+
|
|
|
struct sbs_info {
|
|
|
struct i2c_client *client;
|
|
|
struct power_supply *power_supply;
|
|
@@ -168,6 +172,7 @@ struct sbs_info {
|
|
|
u32 poll_retry_count;
|
|
|
struct delayed_work work;
|
|
|
struct mutex mode_lock;
|
|
|
+ u32 flags;
|
|
|
};
|
|
|
|
|
|
static char model_name[I2C_SMBUS_BLOCK_MAX + 1];
|
|
@@ -315,17 +320,41 @@ static int sbs_status_correct(struct i2c_client *client, int *intval)
|
|
|
static int sbs_get_battery_presence_and_health(
|
|
|
struct i2c_client *client, enum power_supply_property psp,
|
|
|
union power_supply_propval *val)
|
|
|
+{
|
|
|
+ int ret;
|
|
|
+
|
|
|
+ if (psp == POWER_SUPPLY_PROP_PRESENT) {
|
|
|
+ /* Dummy command; if it succeeds, battery is present. */
|
|
|
+ ret = sbs_read_word_data(client, sbs_data[REG_STATUS].addr);
|
|
|
+ if (ret < 0)
|
|
|
+ val->intval = 0; /* battery disconnected */
|
|
|
+ else
|
|
|
+ val->intval = 1; /* battery present */
|
|
|
+ } else { /* POWER_SUPPLY_PROP_HEALTH */
|
|
|
+ /* SBS spec doesn't have a general health command. */
|
|
|
+ val->intval = POWER_SUPPLY_HEALTH_UNKNOWN;
|
|
|
+ }
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static int sbs_get_ti_battery_presence_and_health(
|
|
|
+ struct i2c_client *client, enum power_supply_property psp,
|
|
|
+ union power_supply_propval *val)
|
|
|
{
|
|
|
s32 ret;
|
|
|
|
|
|
/*
|
|
|
* Write to ManufacturerAccess with ManufacturerAccess command
|
|
|
- * and then read the status. Do not check for error on the write
|
|
|
- * since not all batteries implement write access to this command,
|
|
|
- * while others mandate it.
|
|
|
+ * and then read the status.
|
|
|
*/
|
|
|
- sbs_write_word_data(client, sbs_data[REG_MANUFACTURER_DATA].addr,
|
|
|
- MANUFACTURER_ACCESS_STATUS);
|
|
|
+ ret = sbs_write_word_data(client, sbs_data[REG_MANUFACTURER_DATA].addr,
|
|
|
+ MANUFACTURER_ACCESS_STATUS);
|
|
|
+ if (ret < 0) {
|
|
|
+ if (psp == POWER_SUPPLY_PROP_PRESENT)
|
|
|
+ val->intval = 0; /* battery removed */
|
|
|
+ return ret;
|
|
|
+ }
|
|
|
|
|
|
ret = sbs_read_word_data(client, sbs_data[REG_MANUFACTURER_DATA].addr);
|
|
|
if (ret < 0) {
|
|
@@ -600,7 +629,12 @@ static int sbs_get_property(struct power_supply *psy,
|
|
|
switch (psp) {
|
|
|
case POWER_SUPPLY_PROP_PRESENT:
|
|
|
case POWER_SUPPLY_PROP_HEALTH:
|
|
|
- ret = sbs_get_battery_presence_and_health(client, psp, val);
|
|
|
+ if (client->flags & SBS_FLAGS_TI_BQ20Z75)
|
|
|
+ ret = sbs_get_ti_battery_presence_and_health(client,
|
|
|
+ psp, val);
|
|
|
+ else
|
|
|
+ ret = sbs_get_battery_presence_and_health(client, psp,
|
|
|
+ val);
|
|
|
if (psp == POWER_SUPPLY_PROP_PRESENT)
|
|
|
return 0;
|
|
|
break;
|
|
@@ -806,6 +840,7 @@ static int sbs_probe(struct i2c_client *client,
|
|
|
if (!chip)
|
|
|
return -ENOMEM;
|
|
|
|
|
|
+ chip->flags = (u32)(uintptr_t)of_device_get_match_data(&client->dev);
|
|
|
chip->client = client;
|
|
|
chip->enable_detection = false;
|
|
|
psy_cfg.of_node = client->dev.of_node;
|
|
@@ -911,16 +946,19 @@ static int sbs_suspend(struct device *dev)
|
|
|
{
|
|
|
struct i2c_client *client = to_i2c_client(dev);
|
|
|
struct sbs_info *chip = i2c_get_clientdata(client);
|
|
|
+ int ret;
|
|
|
|
|
|
if (chip->poll_time > 0)
|
|
|
cancel_delayed_work_sync(&chip->work);
|
|
|
|
|
|
- /*
|
|
|
- * Write to manufacturer access with sleep command.
|
|
|
- * Support is manufacturer dependend, so ignore errors.
|
|
|
- */
|
|
|
- sbs_write_word_data(client, sbs_data[REG_MANUFACTURER_DATA].addr,
|
|
|
- MANUFACTURER_ACCESS_SLEEP);
|
|
|
+ if (chip->flags & SBS_FLAGS_TI_BQ20Z75) {
|
|
|
+ /* Write to manufacturer access with sleep command. */
|
|
|
+ ret = sbs_write_word_data(client,
|
|
|
+ sbs_data[REG_MANUFACTURER_DATA].addr,
|
|
|
+ MANUFACTURER_ACCESS_SLEEP);
|
|
|
+ if (chip->is_present && ret < 0)
|
|
|
+ return ret;
|
|
|
+ }
|
|
|
|
|
|
return 0;
|
|
|
}
|
|
@@ -941,7 +979,10 @@ MODULE_DEVICE_TABLE(i2c, sbs_id);
|
|
|
|
|
|
static const struct of_device_id sbs_dt_ids[] = {
|
|
|
{ .compatible = "sbs,sbs-battery" },
|
|
|
- { .compatible = "ti,bq20z75" },
|
|
|
+ {
|
|
|
+ .compatible = "ti,bq20z75",
|
|
|
+ .data = (void *)SBS_FLAGS_TI_BQ20Z75,
|
|
|
+ },
|
|
|
{ }
|
|
|
};
|
|
|
MODULE_DEVICE_TABLE(of, sbs_dt_ids);
|