CHROMIUM: power: sbs-battery: Prevent CAPACITY_MODE races

A subset of smart battery commands return charge or energy depending on
the CAPACITY_MODE bit setting of BatteryMode(). In order to
unambiguously read a charge or energy value, it is necessary to ensure
that CAPACITY_MODE is set as desired, and not changed for the duration
of the attribute read.

BUG=chromium:686942
TEST=On kevin, spam reads to charge_full battery sysfs attribute while
performing suspend_stress_test, verify that the same value is always
returned.

Signed-off-by: Shawn Nematbakhsh <shawnn@chromium.org>
Change-Id: Ic39e8d7f982d7045a2da6bf6fe835c2c3434815c
Reviewed-on: https://chromium-review.googlesource.com/531760
Commit-Ready: Shawn N <shawnn@chromium.org>
Tested-by: Shawn N <shawnn@chromium.org>
Reviewed-by: Guenter Roeck <groeck@chromium.org>
diff --git a/drivers/power/sbs-battery.c b/drivers/power/sbs-battery.c
index 837f460..b9d1285 100644
--- a/drivers/power/sbs-battery.c
+++ b/drivers/power/sbs-battery.c
@@ -167,6 +167,7 @@
 	int				poll_time;
 	struct delayed_work		work;
 	int				ignore_changes;
+	struct mutex			mode_lock;
 };
 
 static char model_name[I2C_SMBUS_BLOCK_MAX + 1];
@@ -580,7 +581,13 @@
 		if (ret < 0)
 			break;
 
+		/* sbs_get_battery_capacity() will change the battery mode
+		 * temporarily to read the requested attribute. Ensure we stay
+		 * in the desired mode for the duration of the attribute read.
+		 */
+		mutex_lock(&chip->mode_lock);
 		ret = sbs_get_battery_capacity(client, ret, psp, val);
+		mutex_unlock(&chip->mode_lock);
 		break;
 
 	case POWER_SUPPLY_PROP_SERIAL_NUMBER:
@@ -831,6 +838,7 @@
 	 */
 	chip->ignore_changes = 1;
 	chip->last_state = POWER_SUPPLY_STATUS_UNKNOWN;
+	mutex_init(&chip->mode_lock);
 
 	pdata = sbs_of_populate_pdata(client);