Snap for 10302501 from ca21ebe25218a5e2e1b64e2125baa78f532f02d2 to android13-gs-pixel-5.10-release

Change-Id: I1661460e73edcc955078237141a573c71f81b517
diff --git a/gbms_storage.c b/gbms_storage.c
index 2c4f2e0..d1a44f4 100644
--- a/gbms_storage.c
+++ b/gbms_storage.c
@@ -1118,7 +1118,7 @@
 	GBEE_STATUS_OK,
 };
 
-#define GBEE_POLL_RETRIES	5
+#define GBEE_POLL_RETRIES	15
 #define GBEE_POLL_INTERVAL_MS	200
 
 /* only one battery eeprom for now */
diff --git a/google_battery.c b/google_battery.c
index 8e3d412..d6ce2b9 100644
--- a/google_battery.c
+++ b/google_battery.c
@@ -187,6 +187,9 @@
 	/* Save/Restore fake capacity */
 	bool save_soc_available;
 	u16 save_soc;
+
+	/* adjust SOC */
+	int point_full_ui_soc;
 };
 
 struct gbatt_ccbin_data {
@@ -322,7 +325,7 @@
 	int battery_age;		/* from the FG, time in field */
 
 	/* capacity metrics */
-	int capacity_design;		/* from the FG or from charge table */
+	int pack_capacity;		/* mAh, from the FG or from charge table */
 	int capacity_fade;		/* from the FG */
 
 	/* impedance */
@@ -430,6 +433,8 @@
 	uint16_t cc_in;
 	uint16_t cc_out;
 	ktime_t thermal_severity[CSI_THERMAL_SEVERITY_MAX];
+	int thermal_lvl_max;
+	int thermal_lvl_min;
 };
 
 #define TEMP_SAMPLE_SIZE 5
@@ -528,6 +533,7 @@
 	struct gvotable_election *fcc_votable;
 	struct gvotable_election *fv_votable;
 	struct gvotable_election *temp_dryrun_votable;
+	struct gvotable_election *point_full_ui_soc_votable;
 
 	/* FAN level */
 	struct gvotable_election *fan_level_votable;
@@ -624,6 +630,7 @@
 	/* irdrop for DC */
 	bool dc_irdrop;
 
+	int batt_id;
 };
 
 static int gbatt_get_temp(struct batt_drv *batt_drv, int *temp);
@@ -875,6 +882,9 @@
 	/* splice only when real is within the curve range */
 	curve[1].real = real;
 	curve[1].ui = ui;
+
+	if (curve[1].real > curve[UICURVE_MAX - 1].real)
+		curve[UICURVE_MAX - 1].real = ssoc_point_full;
 }
 
 static void ssoc_uicurve_dup(struct ssoc_uicurve *dst,
@@ -884,6 +894,22 @@
 		memcpy(dst, curve, sizeof(*dst)*UICURVE_MAX);
 }
 
+/* "optimized" to work on 3 element curves */
+static void ssoc_uicurve_splice_full(struct ssoc_uicurve *curve,
+				     qnum_t real,qnum_t ui)
+{
+	/*
+	 * for case: curve:[15.00 15.00][99.00 99.00][98.00 100.00]
+	 * the calculation in ssoc_uicurve_map causes minus value
+	 */
+	if (curve[1].real > real)
+		return;
+
+	curve[UICURVE_MAX - 1].real = real;
+	curve[UICURVE_MAX - 1].ui = ui;
+}
+
+
 
 /* ------------------------------------------------------------------------- */
 
@@ -1069,6 +1095,7 @@
  * QC could need:
  *	QG_CC_SOC, QG_Raw_SOC, QG_Bat_SOC, QG_Sys_SOC, QG_Mon_SOC
  */
+#define DISABLE_POINT_FULL_UI_SOC (-1)
 static int ssoc_work(struct batt_ssoc_state *ssoc_state,
 		     struct power_supply *fg_psy)
 {
@@ -2575,10 +2602,12 @@
 			res = 0;
 
 		gbms_logbuffer_prlog(batt_drv->ttf_stats.ttf_log, LOGLEVEL_INFO, 0, LOGLEVEL_DEBUG,
-				     "ssoc=%d temp=%d CSI[min=%d max=%d avg=%d type=%d status=%d TTF[cc=%d time=%lld %lld:%lld:%lld (est=%lld max_ratio=%d)]",
-				     csi_stats->ssoc, batt_drv->batt_temp, csi_stats->csi_speed_min,
-				     csi_stats->csi_speed_max, csi_speed_avg,
+				     "ssoc=%d temp=%d CSI[speed=%d,%d,%d type=%d status=%d lvl=%d,%d"
+				     " TTF[cc=%d time=%lld %lld:%lld:%lld (est=%lld max_ratio=%d)]",
+				     csi_stats->ssoc, batt_drv->batt_temp, csi_speed_avg,
+				     csi_stats->csi_speed_min, csi_stats->csi_speed_max,
 				     csi_stats->csi_current_type, csi_stats->csi_current_status,
+				     csi_stats->thermal_lvl_min, csi_stats->thermal_lvl_max,
 				     cc / 1000, right_now, res / 3600, (res % 3600) / 60,
 				     (res % 3600) % 60, res, max_ratio);
 
@@ -2592,6 +2621,12 @@
 	csi_stats->csi_speed_min = current_speed;
 	csi_stats->csi_speed_max = current_speed;
 
+	/* ssoc == -1 on disconnect */
+	if (ssoc == -1) {
+		csi_stats->thermal_lvl_min = 0;
+		csi_stats->thermal_lvl_max = 0;
+	}
+
 	csi_stats->csi_time_sum = 0;
 	csi_stats->speed_sum = 0;
 	csi_stats->last_update = right_now;
@@ -2774,14 +2809,15 @@
 
 	gvotable_cast_long_vote(batt_drv->csi_status_votable, "CSI_STATUS_DSG",
 				CSI_STATUS_NotCharging,
-				!is_disconnected && batt_drv->msc_state == MSC_DSG);
+				!is_disconnected && batt_drv->msc_state == MSC_DSG &&
+				!batt_drv->chg_done);
 
 	gvotable_cast_long_vote(batt_drv->csi_status_votable, "CSI_STATUS_100",
 				CSI_STATUS_Charging,
-				!is_disconnected && batt_drv->chg_done);
+				!is_disconnected && batt_drv->batt_full && !batt_drv->chg_done);
 
 	gvotable_cast_long_vote(batt_drv->csi_status_votable, "CSI_STATUS_CHG",
-					CSI_STATUS_Charging, !is_disconnected);
+				CSI_STATUS_Charging, !is_disconnected);
 }
 
 #define CSI_CHG_SPEED_MAX 100
@@ -2849,12 +2885,37 @@
 	return chg_speed;
 }
 
+static void batt_update_thermal_lvl(struct batt_drv *batt_drv)
+{
+	struct csi_stats *csi_stats = &batt_drv->csi_stats;
+	int thermal_level = 0;
+
+	if (chg_state_is_disconnected(&batt_drv->chg_state))
+		return;
+
+	if (!batt_drv->thermal_level_votable)
+		batt_drv->thermal_level_votable = gvotable_election_get_handle(VOTABLE_THERMAL_LVL);
+	if (batt_drv->thermal_level_votable)
+		thermal_level = gvotable_get_current_int_vote(batt_drv->thermal_level_votable);
+
+	if (thermal_level < 0)
+		return;
+
+	if (csi_stats->thermal_lvl_max == 0 && csi_stats->thermal_lvl_min == 0)
+		csi_stats->thermal_lvl_max = csi_stats->thermal_lvl_min = thermal_level;
+	else if (thermal_level > csi_stats->thermal_lvl_max)
+		csi_stats->thermal_lvl_max = thermal_level;
+	else if (thermal_level < csi_stats->thermal_lvl_min)
+		csi_stats->thermal_lvl_min = thermal_level;
+}
+
 static void batt_update_csi_info(struct batt_drv *batt_drv)
 {
 	int charging_speed;
 
 	batt_update_csi_type(batt_drv);
 	batt_update_csi_status(batt_drv);
+	batt_update_thermal_lvl(batt_drv);
 
 	charging_speed = batt_calc_charging_speed(batt_drv);
 	if (batt_drv->csi_current_speed != charging_speed) {
@@ -3694,10 +3755,17 @@
 static int bhi_cap_data_update(struct bhi_data *bhi_data, struct batt_drv *batt_drv)
 {
 	struct power_supply *fg_psy = batt_drv->fg_psy;
+	const int fade_rate = GPSY_GET_PROP(fg_psy, GBMS_PROP_CAPACITY_FADE_RATE);
+	const int designcap = GPSY_GET_PROP(fg_psy, POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN);
 	int cap_fade;
 
 	/* GBMS_PROP_CAPACITY_FADE_RATE is in percent */
-	cap_fade = GPSY_GET_PROP(fg_psy, GBMS_PROP_CAPACITY_FADE_RATE);
+	if (fade_rate < 0 || designcap < 0)
+		return -ENODATA;
+	if (bhi_data->pack_capacity <= 0)
+		return -EINVAL;
+
+	cap_fade = fade_rate * designcap / (bhi_data->pack_capacity * 1000);
 	if (cap_fade < 0)
 		return -ENODATA;
 	if (cap_fade > 100)
@@ -3718,7 +3786,7 @@
  */
 static int bhi_health_get_capacity(int algo, const struct bhi_data *bhi_data)
 {
-	return bhi_data->capacity_design * (100 - bhi_data->capacity_fade) / 100;
+	return bhi_data->pack_capacity * (100 - bhi_data->capacity_fade) / 100;
 }
 
 /* The limit for capacity is 80% of design */
@@ -3734,7 +3802,7 @@
 	if (health_data->bhi_debug_cap_index)
 		return health_data->bhi_debug_cap_index;
 
-	if (!bhi_data->capacity_design)
+	if (!bhi_data->pack_capacity)
 		return -ENODATA;
 
 	capacity_health = bhi_health_get_capacity(algo, bhi_data);
@@ -3752,12 +3820,12 @@
 	 * ret = gbms_storage_read(GBMS_TAG_GCFE, &gcap sizeof(gcap));
 	 */
 
-	index = (capacity_health * BHI_ALGO_FULL_HEALTH) / bhi_data->capacity_design;
+	index = (capacity_health * BHI_ALGO_FULL_HEALTH) / bhi_data->pack_capacity;
 	if (index > BHI_ALGO_FULL_HEALTH)
 		index = BHI_ALGO_FULL_HEALTH;
 
-	pr_debug("%s: algo=%d index=%d ch=%d, ca=%d, cd=%d, fr=%d\n", __func__,
-		algo, index, capacity_health, capacity_aacr, bhi_data->capacity_design,
+	pr_debug("%s: algo=%d index=%d ch=%d, ca=%d, pc=%d, fr=%d\n", __func__,
+		algo, index, capacity_health, capacity_aacr, bhi_data->pack_capacity,
 		bhi_data->capacity_fade);
 
 	return index;
@@ -7360,13 +7428,27 @@
 	struct power_supply *psy = container_of(dev, struct power_supply, dev);
 	struct batt_drv *batt_drv = power_supply_get_drvdata(psy);
 	struct bhi_data *bhi_data = &batt_drv->health_data.bhi_data;
-	int cnt = sscanf(buf, "%hu,%hu,%hu,%hu,%hu,%hu,%hu,%hu",
-			 &bhi_data->trend[0], &bhi_data->trend[1], &bhi_data->trend[2],
-			 &bhi_data->trend[3], &bhi_data->trend[4], &bhi_data->trend[5],
-			 &bhi_data->trend[6], &bhi_data->trend[7]);
+	u16 trend[BHI_TREND_POINTS_SIZE];
+	const int buf_len = strlen(buf);
+	int cnt = 0, len = 0;
+	u16 batt_id;
 
-	if (cnt != BHI_TREND_POINTS_SIZE)
-		return -ERANGE;
+	do {
+		cnt = sscanf(&buf[len], "%hu,%hu,%hu,%hu,%hu,%hu,%hu,%hu,%hu", &batt_id,
+			     &trend[0], &trend[1], &trend[2], &trend[3],
+			     &trend[4], &trend[5], &trend[6], &trend[7]);
+
+		if (cnt != BHI_TREND_POINTS_SIZE + 1)
+			return -ERANGE;
+
+		if ((int)batt_id == batt_drv->batt_id) {
+			memcpy(&bhi_data->trend, trend, sizeof(trend));
+			break;
+		}
+
+		while (buf[len] != '\n' && len < buf_len)
+			len++;
+	} while (len++ < buf_len);
 
 	return count;
 }
@@ -7394,13 +7476,27 @@
 	struct power_supply *psy = container_of(dev, struct power_supply, dev);
 	struct batt_drv *batt_drv = power_supply_get_drvdata(psy);
 	struct bhi_data *bhi_data = &batt_drv->health_data.bhi_data;
-	int cnt = sscanf(buf, "%hu,%hu,%hu,%hu,%hu,%hu,%hu,%hu",
-			 &bhi_data->l_bound[0], &bhi_data->l_bound[1], &bhi_data->l_bound[2],
-			 &bhi_data->l_bound[3], &bhi_data->l_bound[4], &bhi_data->l_bound[5],
-			 &bhi_data->l_bound[6], &bhi_data->l_bound[7]);
+	u16 l_bound[BHI_TREND_POINTS_SIZE];
+	const int buf_len = strlen(buf);
+	int cnt = 0, len = 0;
+	u16 batt_id;
 
-	if (cnt != BHI_TREND_POINTS_SIZE)
-		return -ERANGE;
+	do {
+		cnt = sscanf(&buf[len], "%hu,%hu,%hu,%hu,%hu,%hu,%hu,%hu,%hu", &batt_id,
+			     &l_bound[0], &l_bound[1], &l_bound[2], &l_bound[3],
+			     &l_bound[4], &l_bound[5], &l_bound[6], &l_bound[7]);
+
+		if (cnt != BHI_TREND_POINTS_SIZE + 1)
+			return -ERANGE;
+
+		if ((int)batt_id == batt_drv->batt_id) {
+			memcpy(&bhi_data->l_bound, l_bound, sizeof(l_bound));
+			break;
+		}
+
+		while (buf[len] != '\n' && len < buf_len)
+			len++;
+	} while (len++ < buf_len);
 
 	return count;
 }
@@ -8605,6 +8701,35 @@
 	return (ret < 0) ? -EIO : 0;
 }
 
+static int point_full_ui_soc_cb(struct gvotable_election *el,
+			      const char *reason, void *vote)
+{
+	struct batt_drv *batt_drv = gvotable_get_data(el);
+	struct batt_ssoc_state *ssoc_state = &batt_drv->ssoc_state;
+	int ssoc = ssoc_get_capacity(ssoc_state);
+	int soc = GVOTABLE_PTR_TO_INT(vote);
+
+	if (ssoc_state->point_full_ui_soc == soc)
+		return 0;
+
+	dev_info(batt_drv->device, "update point_full_ui_soc: %d -> %d\n",
+		 ssoc_state->point_full_ui_soc, soc);
+
+	ssoc_state->point_full_ui_soc = soc;
+
+	if (ssoc_state->point_full_ui_soc != DISABLE_POINT_FULL_UI_SOC &&
+	    ssoc < SSOC_FULL && ssoc_state->buck_enabled == 1) {
+		struct ssoc_uicurve *curve = ssoc_state->ssoc_curve;
+		const qnum_t full = qnum_fromint(ssoc_state->point_full_ui_soc);
+
+		ssoc_uicurve_splice_full(curve, full, ssoc_point_full);
+		dump_ssoc_state(&batt_drv->ssoc_state, batt_drv->ssoc_log);
+		ssoc_state->point_full_ui_soc = DISABLE_POINT_FULL_UI_SOC;
+	}
+
+	return 0;
+}
+
 /*
  * poll the battery, run SOC%, dead battery, critical.
  * scheduled from psy_changed and from timer
@@ -9528,7 +9653,10 @@
 		health_data->bhi_cycle_grace = 0;
 
 	/* design is the value used to build the charge table */
-	health_data->bhi_data.capacity_design = batt_drv->battery_capacity;
+	health_data->bhi_data.pack_capacity = batt_drv->battery_capacity;
+
+	/* need battery id to get right trend points */
+	batt_drv->batt_id = GPSY_GET_PROP(batt_drv->fg_psy, GBMS_PROP_BATT_ID);
 
 	/* debug data initialization */
 	health_data->bhi_debug_cycle_count = 0;
@@ -10117,6 +10245,19 @@
 	gvotable_set_vote2str(batt_drv->csi_type_votable, gvotable_v2s_int);
 	gvotable_election_set_name(batt_drv->csi_type_votable, VOTABLE_CSI_TYPE);
 
+	batt_drv->point_full_ui_soc_votable =
+		gvotable_create_int_election(NULL, gvotable_comparator_int_min,
+					     point_full_ui_soc_cb, batt_drv);
+	if (IS_ERR_OR_NULL(batt_drv->point_full_ui_soc_votable)) {
+		ret = PTR_ERR(batt_drv->point_full_ui_soc_votable);
+		dev_err(batt_drv->device, "Fail to create point_full_ui_soc_votable\n");
+		batt_drv->point_full_ui_soc_votable = NULL;
+	} else {
+		gvotable_set_vote2str(batt_drv->point_full_ui_soc_votable, gvotable_v2s_int);
+		gvotable_set_default(batt_drv->point_full_ui_soc_votable, (void *)DISABLE_POINT_FULL_UI_SOC);
+		gvotable_election_set_name(batt_drv->point_full_ui_soc_votable, VOTABLE_CHARGING_UISOC);
+	}
+
 	/* AACR server side */
 	batt_drv->aacr_cycle_grace = AACR_START_CYCLE_DEFAULT;
 	batt_drv->aacr_cycle_max = AACR_MAX_CYCLE_DEFAULT;
@@ -10184,11 +10325,13 @@
 	gvotable_destroy_election(batt_drv->fan_level_votable);
 	gvotable_destroy_election(batt_drv->csi_status_votable);
 	gvotable_destroy_election(batt_drv->csi_type_votable);
+	gvotable_destroy_election(batt_drv->point_full_ui_soc_votable);
 
 	batt_drv->fan_level_votable = NULL;
 	batt_drv->csi_status_votable = NULL;
 	batt_drv->csi_type_votable = NULL;
 	batt_drv->charging_policy_votable = NULL;
+	batt_drv->point_full_ui_soc_votable = NULL;
 
 	return 0;
 }
diff --git a/google_bms.h b/google_bms.h
index 2a943ba..bb41cc8 100644
--- a/google_bms.h
+++ b/google_bms.h
@@ -470,6 +470,7 @@
 #define VOTABLE_CSI_TYPE	"CSI_TYPE"
 
 #define VOTABLE_CHARGING_POLICY	"CHARGING_POLICY"
+#define VOTABLE_CHARGING_UISOC	"CHARGING_UISOC"
 
 #define VOTABLE_DC_CHG_AVAIL	"DC_AVAIL"
 #define REASON_DC_DRV		"DC_DRV"
diff --git a/max1720x_battery.c b/max1720x_battery.c
index 75f6608..7d94796 100644
--- a/max1720x_battery.c
+++ b/max1720x_battery.c
@@ -5719,8 +5719,6 @@
 	chip->init_complete = true;
 	chip->bhi_acim = 0;
 
-	max17x0x_init_sysfs(chip);
-
 	/*
 	 * Handle any IRQ that might have been set before init
 	 * NOTE: will clear the POR bit and trigger model load if needed
@@ -6108,6 +6106,8 @@
 	reg = max17x0x_find_by_tag(&chip->regmap, MAX17X0X_TAG_vfsoc);
 	chip->reg_prop_capacity_raw = (reg) ? reg->reg : MAX1720X_REPSOC;
 
+	max17x0x_init_sysfs(chip);
+
 	INIT_DELAYED_WORK(&chip->cap_estimate.settle_timer,
 			  batt_ce_capacityfiltered_work);
 	INIT_DELAYED_WORK(&chip->init_work, max1720x_init_work);
diff --git a/max77759_charger.c b/max77759_charger.c
index e3bcf66..e3998d5 100644
--- a/max77759_charger.c
+++ b/max77759_charger.c
@@ -55,6 +55,9 @@
 #define CHGR_DTLS_OFF_JEITA				0x0c
 #define CHGR_DTLS_OFF_TEMP				0x0d
 
+#define CHGR_CHG_CNFG_12_VREG_4P6V			0x1
+#define CHGR_CHG_CNFG_12_VREG_4P7V			0x2
+
 static inline int max77759_reg_read(struct regmap *regmap, uint8_t reg,
 				    uint8_t *val)
 {
@@ -1856,6 +1859,37 @@
 	return 0;
 }
 
+static int max77759_higher_headroom_enable(struct max77759_chgr_data *data, bool flag)
+{
+	int ret = 0;
+	u8 reg, reg_rd, val = flag ? CHGR_CHG_CNFG_12_VREG_4P7V : CHGR_CHG_CNFG_12_VREG_4P6V;
+
+	ret = max77759_reg_read(data->regmap, MAX77759_CHG_CNFG_12, &reg);
+	if (ret < 0)
+		return ret;
+
+	reg_rd = reg;
+	ret = max77759_chg_prot(data->regmap, false);
+	if (ret < 0)
+		return ret;
+
+	reg = _chg_cnfg_12_vchgin_reg_set(reg, val);
+	ret = max77759_reg_write(data->regmap, MAX77759_CHG_CNFG_12, reg);
+	if (ret)
+		goto done;;
+
+	dev_dbg(data->dev, "%s: val: %#02x, reg: %#02x -> %#02x\n", __func__, val, reg_rd, reg);
+
+	ret = max77759_reg_read(data->regmap, MAX77759_CHG_CNFG_12, &reg);
+	if (ret)
+		goto done;
+
+done:
+	ret = max77759_chg_prot(data->regmap, true);
+	if (ret < 0)
+		dev_err(data->dev, "%s: error enabling prot (%d)\n", __func__, ret);
+	return ret < 0 ? ret : 0;
+}
 
 static int max77759_chg_is_valid(struct max77759_chgr_data *data)
 {
@@ -2180,6 +2214,10 @@
 		ret = max77759_set_regulation_voltage(data, pval->intval);
 		pr_debug("%s: charge_voltage=%d (%d)\n",
 			__func__, pval->intval, ret);
+		if (ret)
+			break;
+		if (max77759_is_online(data) && pval->intval >= data->chg_term_voltage * 1000)
+			ret = max77759_higher_headroom_enable(data, true);
 		break;
 	/* called from google_cpm when switching chargers */
 	case GBMS_PROP_CHARGING_ENABLED:
@@ -2791,6 +2829,10 @@
 		pr_debug("%s: INSEL insel_auto_clear=%d (%d)\n", __func__,
 			 data->insel_clear, data->insel_clear ? ret : 0);
 		atomic_inc(&data->insel_cnt);
+
+		ret = max77759_higher_headroom_enable(data, false); /* reset on plug/unplug */
+		if (ret)
+			return IRQ_NONE;
 	}
 
 #if IS_ENABLED(CONFIG_GOOGLE_BCL)
diff --git a/max_m5.c b/max_m5.c
index 4532e8c..062bd6c 100644
--- a/max_m5.c
+++ b/max_m5.c
@@ -687,6 +687,14 @@
 	cp->qresidual10 = m5_data->model_save.qresidual10;
 	cp->qresidual20 = m5_data->model_save.qresidual20;
 	cp->qresidual30 = m5_data->model_save.qresidual30;
+	/* b/278492168 restore dpacc with fullcapnom for taskperiod=351ms */
+	if (cp->taskperiod == 0x2d00 && cp->dpacc == 0x3200)
+		cp->dqacc = cp->fullcapnom >> 2;
+	else if (cp->taskperiod == 0x2d00 && cp->dpacc == 0x0c80)
+		cp->dqacc = cp->fullcapnom >> 4;
+	else
+		dev_warn(m5_data->dev, "taskperiod:%#x, dpacc:%#x, dqacc:%#x\n",
+			 cp->taskperiod, cp->dpacc, cp->dqacc);
 
 	m5_data->cycles = m5_data->model_save.cycles;
 	m5_data->cv_mixcap = m5_data->model_save.cv_mixcap;
diff --git a/p9221_charger.c b/p9221_charger.c
index c6744b8..ead5797 100644
--- a/p9221_charger.c
+++ b/p9221_charger.c
@@ -596,6 +596,22 @@
 	sysfs_notify(&charger->dev->kobj, NULL, "rxdone");
 }
 
+#define FORCE_FULL_SOC 98
+static void p9xxx_ll_adjust_soc(struct p9221_charger_data *charger, int soc)
+{
+	if (!charger->point_full_ui_soc_votable) {
+		charger->point_full_ui_soc_votable =
+			gvotable_election_get_handle(VOTABLE_CHARGING_UISOC);
+		if (!charger->point_full_ui_soc_votable) {
+			dev_err(&charger->client->dev, "Could not get votable: CHARGING_UISOC\n");
+			return;
+		}
+	}
+
+	gvotable_cast_long_vote(charger->point_full_ui_soc_votable, LL_BPP_CEP_VOTER,
+				FORCE_FULL_SOC, charger->ll_bpp_cep == 1 && soc >= FORCE_FULL_SOC);
+}
+
 /*
  * Put the default ICL back to BPP, reset OCP voter
  * @pre charger && charger->dc_icl_votable && charger->client->dev
@@ -604,6 +620,8 @@
 {
 	int ret, ocp_icl;
 
+	p9xxx_ll_adjust_soc(charger, FORCE_FULL_SOC);
+
 	if (!charger->dc_icl_votable) {
 		dev_err(&charger->client->dev,
 			"Could not vote DC_ICL - no votable\n");
@@ -2013,9 +2031,8 @@
 		val->intval = p9221_get_psy_online(charger);
 		break;
 	case POWER_SUPPLY_PROP_SERIAL_NUMBER:
+		/* val->strval == NULL means NODATA */
 		val->strval = p9221_get_tx_id_str(charger);
-		if (val->strval == NULL)
-			return -ENODATA;
 		break;
 	case POWER_SUPPLY_PROP_CAPACITY:
 		/* Zero may be returned on transition to wireless "online", as
@@ -2037,8 +2054,10 @@
 
 			val->intval = rc ? : charger->wlc_dc_current_now;
 		} else {
-			if (!charger->dc_icl_votable)
-				return -EAGAIN;
+			if (!charger->dc_icl_votable) {
+				val->intval = -EAGAIN;
+				break;
+			}
 			val->intval = gvotable_get_current_int_vote(
 						charger->dc_icl_votable);
 		}
@@ -2575,6 +2594,9 @@
 	if (capacity > 98)
 		icl_ua = 300000;
 
+	if (capacity >= FORCE_FULL_SOC)
+		p9xxx_ll_adjust_soc(charger, capacity);
+
 	gvotable_cast_int_vote(charger->dc_icl_votable,
 			       DD_VOTER, icl_ua, icl_ua > 0);
 
@@ -6591,6 +6613,8 @@
 
 	pdata->ll_vout_not_set = of_property_read_bool(node, "google,ll-bpp-vout-not-set");
 
+	pdata->disable_repeat_eop = of_property_read_bool(node, "google,disable-repeat-eop");
+
 	return 0;
 }
 
diff --git a/p9221_charger.h b/p9221_charger.h
index 34b110c..030af07 100644
--- a/p9221_charger.h
+++ b/p9221_charger.h
@@ -665,6 +665,7 @@
 	bool				light_load;
 	bool				disable_align;
 	bool				ll_vout_not_set;
+	bool				disable_repeat_eop;
 };
 
 struct p9221_charger_ints_bit {
@@ -715,6 +716,7 @@
 	struct gvotable_election	*wlc_disable_votable;
 	struct gvotable_election	*csi_status_votable;
 	struct gvotable_election	*csi_type_votable;
+	struct gvotable_election	*point_full_ui_soc_votable;
 	struct notifier_block		nb;
 	struct mutex			io_lock;
 	struct mutex			cmd_lock;
diff --git a/p9221_chip.c b/p9221_chip.c
index 58105c6..e1d876c 100644
--- a/p9221_chip.c
+++ b/p9221_chip.c
@@ -958,6 +958,41 @@
 }
 
 /* send eop */
+/*   send multiple times to make sure it works */
+#define EOP_RESTART_COUNT	2
+#define P9222_EOP_REPEAT_COUNT	4
+static int p9222_send_repeat_eop(struct p9221_charger_data *chgr, u8 reason)
+{
+	int count, ret = 0;
+	u8 val, ept_reason;
+
+	for (count = 0; count < P9222_EOP_REPEAT_COUNT; count++) {
+		ept_reason = (count < EOP_RESTART_COUNT) ? P9221_EOP_RESTART_POWER : reason;
+
+		ret = chgr->reg_write_8(chgr, P9222RE_EPT_REG, ept_reason);
+		if (ret == 0)
+			ret = chgr->chip_set_cmd(chgr, P9221R5_COM_SENDEPT);
+		if (ret < 0) {
+			dev_err(&chgr->client->dev, "fail send eop_%d (%d)\n", count, ret);
+			return ret;
+		}
+		mdelay(500);
+
+		/* Check Tx is offline due to EPT command works */
+		ret = chgr->reg_read_8(chgr, P9221_STATUS_REG, &val);
+		if (ret < 0) {
+			dev_info(&chgr->client->dev,
+				 "WLC chip offline, count=%d, ret=%d\n", count, ret);
+			break;
+		}
+	}
+
+	dev_info(&chgr->client->dev,
+		 "send 3xEOP command success(reason=%02x)\n", reason);
+
+	return 0;
+}
+
 static int p9221_send_eop(struct p9221_charger_data *chgr, u8 reason)
 {
 	int ret;
@@ -982,11 +1017,16 @@
 
 	mutex_lock(&chgr->cmd_lock);
 
-	ret = chgr->reg_write_8(chgr, P9222RE_EPT_REG, reason);
-	if (ret == 0)
-		ret = chgr->chip_set_cmd(chgr, P9221R5_COM_SENDEPT);
+	if (chgr->pdata->disable_repeat_eop) {
+		ret = chgr->reg_write_8(chgr, P9222RE_EPT_REG, reason);
+		if (ret == 0)
+			ret = chgr->chip_set_cmd(chgr, P9221R5_COM_SENDEPT);
+	} else {
+		ret = p9222_send_repeat_eop(chgr, reason);
+	}
 
 	mutex_unlock(&chgr->cmd_lock);
+
 	return ret;
 }