Merge "power: pm8921-bms: lock/unlock BMS output registers" into msm-3.0
diff --git a/drivers/power/pm8921-bms.c b/drivers/power/pm8921-bms.c
index d0c1392..e666bfc 100644
--- a/drivers/power/pm8921-bms.c
+++ b/drivers/power/pm8921-bms.c
@@ -87,6 +87,7 @@
unsigned int batt_id_channel;
unsigned int pmic_bms_irq[PM_BMS_MAX_INTS];
DECLARE_BITMAP(enabled_irqs, PM_BMS_MAX_INTS);
+ spinlock_t bms_output_lock;
};
static struct pm8921_bms_chip *the_chip;
@@ -156,6 +157,32 @@
return 0;
}
+#define HOLD_OREG_DATA BIT(1)
+static int pm_bms_lock_output_data(struct pm8921_bms_chip *chip)
+{
+ int rc;
+
+ rc = pm_bms_masked_write(chip, BMS_CONTROL, HOLD_OREG_DATA,
+ HOLD_OREG_DATA);
+ if (rc) {
+ pr_err("couldnt lock bms output rc = %d\n", rc);
+ return rc;
+ }
+ return 0;
+}
+
+static int pm_bms_unlock_output_data(struct pm8921_bms_chip *chip)
+{
+ int rc;
+
+ rc = pm_bms_masked_write(chip, BMS_CONTROL, HOLD_OREG_DATA, 0);
+ if (rc) {
+ pr_err("fail to unlock BMS_CONTROL rc = %d\n", rc);
+ return rc;
+ }
+ return 0;
+}
+
#define SELECT_OUTPUT_DATA 0x1C
#define SELECT_OUTPUT_TYPE_SHIFT 2
#define OCV_FOR_RBATT 0x0
@@ -182,6 +209,18 @@
pr_err("invalid type %d asked to read\n", type);
return -EINVAL;
}
+
+ /* make sure the bms registers are locked */
+ rc = pm8xxx_readb(chip->dev->parent, BMS_CONTROL, ®);
+ if (rc) {
+ pr_err("fail to read BMS_OUTPUT0 for type %d rc = %d\n",
+ type, rc);
+ return rc;
+ }
+
+ /* Output register data must be held (locked) while reading output */
+ WARN_ON(!(reg && HOLD_OREG_DATA));
+
rc = pm_bms_masked_write(chip, BMS_CONTROL, SELECT_OUTPUT_DATA,
type << SELECT_OUTPUT_TYPE_SHIFT);
if (rc) {
@@ -779,11 +818,16 @@
int64_t *cc_mah)
{
int coulumb_counter;
+ unsigned long flags;
*fcc = calculate_fcc(chip, batt_temp, chargecycles);
pr_debug("FCC = %umAh batt_temp = %d, cycles = %d",
*fcc, batt_temp, chargecycles);
+ /* fcc doesnt need to be read from hardware, lock the bms now */
+ spin_lock_irqsave(&chip->bms_output_lock, flags);
+ pm_bms_lock_output_data(chip);
+
*unusable_charge = calculate_unusable_charge_mah(chip, *fcc,
batt_temp, chargecycles);
@@ -796,6 +840,9 @@
/* calculate cc milli_volt_hour */
calculate_cc_mah(chip, cc_mah, &coulumb_counter);
+
+ pm_bms_unlock_output_data(chip);
+ spin_unlock_irqrestore(&chip->bms_output_lock, flags);
pr_debug("cc_mah = %lldmAh cc = %d\n", *cc_mah, coulumb_counter);
}
@@ -862,8 +909,7 @@
* shutdown for low battery
*/
soc = BATTERY_POWER_SUPPLY_SOC;
- pr_debug("Adjusting SOC to %d\n",
- BATTERY_POWER_SUPPLY_SOC);
+ pr_debug("Adjusting SOC to %d\n", soc);
}
}
@@ -947,16 +993,26 @@
int pm8921_bms_get_vsense_avg(int *result)
{
- if (the_chip)
- return read_vsense_avg(the_chip, result);
+ int rc = -EINVAL;
+ unsigned long flags;
+
+ if (the_chip) {
+ spin_lock_irqsave(&the_chip->bms_output_lock, flags);
+ pm_bms_lock_output_data(the_chip);
+ rc = read_vsense_avg(the_chip, result);
+ pm_bms_unlock_output_data(the_chip);
+ spin_unlock_irqrestore(&the_chip->bms_output_lock, flags);
+ }
pr_err("called before initialization\n");
- return -EINVAL;
+ return rc;
}
EXPORT_SYMBOL(pm8921_bms_get_vsense_avg);
int pm8921_bms_get_battery_current(int *result)
{
+ unsigned long flags;
+
if (!the_chip) {
pr_err("called before initialization\n");
return -EINVAL;
@@ -966,7 +1022,11 @@
return -EINVAL;
}
+ spin_lock_irqsave(&the_chip->bms_output_lock, flags);
+ pm_bms_lock_output_data(the_chip);
read_vsense_avg(the_chip, result);
+ pm_bms_unlock_output_data(the_chip);
+ spin_unlock_irqrestore(&the_chip->bms_output_lock, flags);
pr_debug("vsense=%d\n", *result);
/* cast for signed division */
*result = *result / (int)the_chip->r_sense;
@@ -1528,7 +1588,7 @@
pr_err("Cannot allocate pm_bms_chip\n");
return -ENOMEM;
}
-
+ spin_lock_init(&chip->bms_output_lock);
chip->dev = &pdev->dev;
chip->r_sense = pdata->r_sense;
chip->i_test = pdata->i_test;