summaryrefslogtreecommitdiff
path: root/drivers/power/supply/sbs-battery.c
diff options
context:
space:
mode:
authorJean-Francois Dagenais <jeff.dagenais@gmail.com>2020-05-13 21:56:11 +0300
committerSebastian Reichel <sre@kernel.org>2020-05-29 01:39:34 +0300
commit182fc88268f3e33fb81cbb373b30d656daf0cb48 (patch)
treefbb02955e33f33475c314eea236d4905612e229d /drivers/power/supply/sbs-battery.c
parentf0318bc99c8107acddde3aa99a7f696c0999f37c (diff)
downloadlinux-182fc88268f3e33fb81cbb373b30d656daf0cb48.tar.xz
power: supply: sbs-battery: add ability to disable charger broadcasts
In certain designs, it is possible to add a battery on a populated i2c bus without an sbs compliant charger. In that case, the battery will unnecessarily and sometimes undesirably master the bus trying to write info in the charger. It is observed in many occasion that these battery "broadcasts" are even corrupting other ongoing master to slave communication. I.e. the multi-master support in the battery is inadequate. Thankfully, the CHARGER_MODE bit allows designers to disable that SBS battery behaviour. This needs to be done once when the battery is first seen on the bus. Signed-off-by: Jean-Francois Dagenais <jeff.dagenais@gmail.com> [rebased code] Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com>
Diffstat (limited to 'drivers/power/supply/sbs-battery.c')
-rw-r--r--drivers/power/supply/sbs-battery.c29
1 files changed, 29 insertions, 0 deletions
diff --git a/drivers/power/supply/sbs-battery.c b/drivers/power/supply/sbs-battery.c
index e3c685b2c247..7c6905a486da 100644
--- a/drivers/power/supply/sbs-battery.c
+++ b/drivers/power/supply/sbs-battery.c
@@ -68,6 +68,7 @@ enum sbs_capacity_mode {
CAPACITY_MODE_AMPS = 0,
CAPACITY_MODE_WATTS = BATTERY_MODE_CAPACITY_MASK
};
+#define BATTERY_MODE_CHARGER_MASK (1<<14)
/* manufacturer access defines */
#define MANUFACTURER_ACCESS_STATUS 0x0006
@@ -193,6 +194,7 @@ struct sbs_info {
bool is_present;
struct gpio_desc *gpio_detect;
bool enable_detection;
+ bool charger_broadcasts;
int last_state;
int poll_time;
u32 i2c_retry_count;
@@ -207,6 +209,27 @@ static char manufacturer[I2C_SMBUS_BLOCK_MAX + 1];
static char chemistry[I2C_SMBUS_BLOCK_MAX + 1];
static bool force_load;
+static int sbs_read_word_data(struct i2c_client *client, u8 address);
+static int sbs_write_word_data(struct i2c_client *client, u8 address, u16 value);
+
+static void sbs_disable_charger_broadcasts(struct sbs_info *chip)
+{
+ int val = sbs_read_word_data(chip->client, BATTERY_MODE_OFFSET);
+ if (val < 0)
+ goto exit;
+
+ val |= BATTERY_MODE_CHARGER_MASK;
+
+ val = sbs_write_word_data(chip->client, BATTERY_MODE_OFFSET, val);
+
+exit:
+ if (val < 0)
+ dev_err(&chip->client->dev,
+ "Failed to disable charger broadcasting: %d\n", val);
+ else
+ dev_dbg(&chip->client->dev, "%s\n", __func__);
+}
+
static int sbs_update_presence(struct sbs_info *chip, bool is_present)
{
struct i2c_client *client = chip->client;
@@ -260,6 +283,9 @@ static int sbs_update_presence(struct sbs_info *chip, bool is_present)
dev_dbg(&client->dev, "PEC: %s\n", (client->flags & I2C_CLIENT_PEC) ?
"enabled" : "disabled");
+ if (!chip->is_present && is_present && !chip->charger_broadcasts)
+ sbs_disable_charger_broadcasts(chip);
+
chip->is_present = true;
return 0;
@@ -1017,6 +1043,9 @@ static int sbs_probe(struct i2c_client *client,
}
chip->i2c_retry_count = chip->i2c_retry_count + 1;
+ chip->charger_broadcasts = !of_property_read_bool(client->dev.of_node,
+ "sbs,disable-charger-broadcasts");
+
chip->gpio_detect = devm_gpiod_get_optional(&client->dev,
"sbs,battery-detect", GPIOD_IN);
if (IS_ERR(chip->gpio_detect)) {