google_battery: add debug sysfs to fake cell fault detection status

  -- add a debug sysfs to fake single battery disconnect
     - /d/bpst/bpst_sbd_status
  -- TODO: to finish the implementation when the detection
           algorithm in b/203019566 lockdown

Bug: 203019566
Signed-off-by: Jack Wu <[email protected]>
Change-Id: I137b279ac5d73588b71f3772828df3498e3f60eb
diff --git a/google_battery.c b/google_battery.c
index 44557f2..9665985 100644
--- a/google_battery.c
+++ b/google_battery.c
@@ -208,6 +208,14 @@
 	int res_temp_high;
 };
 
+/* battery pack status */
+struct batt_bpst {
+	struct mutex lock;
+	bool bpst_enable;
+	/* single battery disconnect status */
+	int bpst_sbd_status;
+};
+
 enum batt_paired_state {
 	BATT_PAIRING_WRITE_ERROR = -4,
 	BATT_PAIRING_READ_ERROR = -3,
@@ -440,6 +448,9 @@
 
 	/* battery power metrics */
 	struct power_metrics power_metrics;
+
+	/* battery pack status */
+	struct batt_bpst bpst_state;
 };
 
 static int gbatt_get_temp(const struct batt_drv *batt_drv, int *temp);
@@ -3451,6 +3462,35 @@
 	}
 }
 
+/* cell fault: disconnect of one of the battery cells */
+static bool batt_cell_fault_detect(struct batt_bpst *bpst_state)
+{
+	int bpst_sbd_status;
+
+	/*
+	 * fake bpst_sbd_status by "echo 1 > /d/bpst/bpst_sbd_status"
+	 * TODO: will implement the code from the algo in b/203019566
+	 */
+	bpst_sbd_status = bpst_state->bpst_sbd_status;
+
+	/* report detection result*/
+	bpst_state->bpst_sbd_status = 0;
+
+	return !!bpst_sbd_status;
+}
+
+static int batt_init_bpst_profile(struct batt_drv *batt_drv)
+{
+	struct batt_bpst *bpst_state = &batt_drv->bpst_state;
+	struct device_node *node = batt_drv->device->of_node;
+
+	bpst_state->bpst_enable = of_property_read_bool(node, "google,bpst-enable");
+	if (!bpst_state->bpst_enable)
+		return -EINVAL;
+
+	return 0;
+}
+
 /* called holding chg_lock */
 static int batt_chg_logic(struct batt_drv *batt_drv)
 {
@@ -3476,6 +3516,16 @@
 	if (chg_state_is_disconnected(chg_state)) {
 		const qnum_t ssoc_delta = ssoc_get_delta(batt_drv);
 
+		/* update bpst */
+		mutex_lock(&batt_drv->bpst_state.lock);
+		if (batt_drv->bpst_state.bpst_enable) {
+			bool cell_fault_detect = batt_cell_fault_detect(&batt_drv->bpst_state);
+
+			if (cell_fault_detect)
+				pr_info("MSC_BPST: cell_fault_detect in disconnected\n");
+		}
+		mutex_unlock(&batt_drv->bpst_state.lock);
+
 		if (batt_drv->ssoc_state.buck_enabled == 0)
 			goto msc_logic_exit;
 
@@ -4303,6 +4353,32 @@
 }
 
 BATTERY_DEBUG_ATTRIBUTE(debug_power_metrics_fops, debug_get_power_metrics, NULL);
+
+static int debug_bpst_sbd_status_read(void *data, u64 *val)
+{
+	struct batt_drv *batt_drv = (struct batt_drv *)data;
+
+	*val = batt_drv->bpst_state.bpst_sbd_status;
+	return 0;
+}
+
+static int debug_bpst_sbd_status_write(void *data, u64 val)
+{
+	struct batt_drv *batt_drv = (struct batt_drv *)data;
+
+	if (val < 0 || val > 1)
+		return -EINVAL;
+
+	mutex_lock(&batt_drv->bpst_state.lock);
+	batt_drv->bpst_state.bpst_sbd_status = val;
+	mutex_unlock(&batt_drv->bpst_state.lock);
+
+	return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(debug_bpst_sbd_status_fops,
+			debug_bpst_sbd_status_read,
+			debug_bpst_sbd_status_write, "%llu\n");
 #endif
 
 /* ------------------------------------------------------------------------- */
@@ -5926,6 +6002,22 @@
 	return 0;
 }
 
+/* bpst detection */
+static int batt_bpst_init_fs(struct batt_drv *batt_drv)
+{
+	if (batt_drv->bpst_state.bpst_enable) {
+		struct dentry *de = NULL;
+
+		de = debugfs_create_dir("bpst", 0);
+		if (IS_ERR_OR_NULL(de))
+			return 0;
+
+		debugfs_create_file("bpst_sbd_status", 0600, de, batt_drv,
+				    &debug_bpst_sbd_status_fops);
+	}
+	return 0;
+}
+
 /* ------------------------------------------------------------------------- */
 
 /* could also use battery temperature, age */
@@ -7115,6 +7207,7 @@
 	mutex_init(&batt_drv->batt_lock);
 	mutex_init(&batt_drv->stats_lock);
 	mutex_init(&batt_drv->cc_data.lock);
+	mutex_init(&batt_drv->bpst_state.lock);
 
 	if (!batt_drv->fg_psy) {
 
@@ -7188,6 +7281,11 @@
 		batt_drv->sd.is_enable = false;
 	}
 
+	/* init bpst setting */
+	ret = batt_init_bpst_profile(batt_drv);
+	if (ret < 0)
+		pr_err("bpst profile disabled, ret=%d\n", ret);
+
 	/* cycle count is cached: read here bc SSOC, chg_profile might use it */
 	batt_update_cycle_count(batt_drv);
 
@@ -7358,6 +7456,9 @@
 	/* debugfs */
 	(void)batt_init_fs(batt_drv);
 
+	/* bpst */
+	(void)batt_bpst_init_fs(batt_drv);
+
 	/* power metrics */
 	schedule_delayed_work(&batt_drv->power_metrics.work,
 			      msecs_to_jiffies(batt_drv->power_metrics.polling_rate * 1000));