Merge android13-gs-pixel-5.10-tm-qpr3 into android13-gs-pixel-5.10-udc

Bug: 255246572
Change-Id: I7e8e2ce9e0ff9682e80ed09afbb255a288e80aaa
Signed-off-by: Joey Lin <[email protected]>
diff --git a/gbms_storage.h b/gbms_storage.h
index b3753b2..7eb82c7 100644
--- a/gbms_storage.h
+++ b/gbms_storage.h
@@ -59,6 +59,7 @@
 
 enum gbms_tags {
 	GBMS_TAG_ACIM = 0x4143494d, /* Activation Impedance */
+	GBMS_TAG_AYMD = 0x41594d44,
 	GBMS_TAG_BCNT = 0x42434e54,
 	GBMS_TAG_BGCE = 0x42474345,
 	GBMS_TAG_BGPN = 0x4247504e,
@@ -80,12 +81,14 @@
 	GBMS_TAG_MINF = 0x4d494e46,
 	GBMS_TAG_MXSN = 0x4d58534e,
 	GBMS_TAG_MXCN = 0x4d58434e,
+	GBMS_TAG_MYMD = 0x4d594d44,
 	GBMS_TAG_THAS = 0x54484153,
 
 	/* User Space Read/Write scratch */
 	GBMS_TAG_RS32 = 0x52533332,
 	GBMS_TAG_RSBM = 0x5253424d,
 	GBMS_TAG_RSBR = 0x52534252,
+	GBMS_TAG_SUFG = 0x53554647, /* shutdown by user_request flag */
 
 	/* Reboot scratch */
 	GBMS_TAG_RRS0 = 0x52525330,
diff --git a/google_battery.c b/google_battery.c
index 06e24b4..dd3e5a8 100644
--- a/google_battery.c
+++ b/google_battery.c
@@ -352,6 +352,8 @@
 	int bhi_debug_imp_index;
 	int bhi_debug_sd_index;
 	int bhi_debug_health_index;
+	/* algo BHI_ALGO_INDI capacity threshold */
+	int bhi_indi_cap;
 
 	/* current battery state */
 	struct bhi_data bhi_data;
@@ -548,6 +550,12 @@
 
 	/* battery pack status */
 	struct batt_bpst bpst_state;
+
+	/* shutdown flag */
+	int boot_to_os_attempts;
+
+	/* battery critical level */
+	int batt_critical_voltage;
 };
 
 static int gbatt_get_temp(const struct batt_drv *batt_drv, int *temp);
@@ -1112,28 +1120,32 @@
 			const char *reason, void *vote)
 {
 	struct batt_drv *batt_drv = gvotable_get_data(el);
+	const int last_lvl = batt_drv->fan_last_level;
 	int lvl = GVOTABLE_PTR_TO_INT(vote);
 
 	if (!batt_drv)
 		return 0;
 
-	if (batt_drv->fan_last_level != lvl) {
-		pr_debug("FAN_LEVEL %d->%d reason=%s\n",
-			 batt_drv->fan_last_level, lvl, reason ? reason : "<>");
+	if (batt_drv->fan_last_level == lvl)
+		return 0;
 
-		if (!chg_state_is_disconnected(&batt_drv->chg_state)) {
-			logbuffer_log(batt_drv->ttf_stats.ttf_log,
-				      "FAN_LEVEL %d->%d reason=%s",
-				      batt_drv->fan_last_level, lvl,
-				      reason ? reason : "<>");
+	pr_debug("FAN_LEVEL %d->%d reason=%s\n",
+		batt_drv->fan_last_level, lvl, reason ? reason : "<>");
 
-			batt_drv->fan_last_level = lvl;
-			if (batt_drv->psy)
-				power_supply_changed(batt_drv->psy);
-		} else {
-			/* Disconnected */
-			batt_drv->fan_last_level = lvl;
-		}
+	batt_drv->fan_last_level = lvl;
+
+	if (!chg_state_is_disconnected(&batt_drv->chg_state)) {
+
+		logbuffer_log(batt_drv->ttf_stats.ttf_log,
+			"FAN_LEVEL %d->%d reason=%s",
+			last_lvl, lvl,
+			reason ? reason : "<>");
+
+		/*
+		 * Send the uevent by kobject API to distinguish the uevent sent by
+                 * power_supply_changed() since fan_level is not a standard power_supply_property
+		 */
+		kobject_uevent(&batt_drv->device->kobj, KOBJ_CHANGE);
 	}
 
 	return 0;
@@ -3231,17 +3243,18 @@
 
 /* BHI -------------------------------------------------------------------- */
 
-#define ONE_YEAR_HRS	(24 * 365)
-#define BHI_INDI_CAP	85
+#define ONE_YEAR_HRS		(24 * 365)
+#define BHI_INDI_CAP_DEFAULT	85
 static int bhi_individual_conditions_index(const struct health_data *health_data)
 {
 	const struct bhi_data *bhi_data = &health_data->bhi_data;
 	const int cur_impedance = batt_ravg_value(&bhi_data->res_state);
 	const int age_impedance_max = bhi_data->act_impedance * 2;
 	const int cur_capacity_pct = 100 - bhi_data->capacity_fade;
+	const int bhi_indi_cap = health_data->bhi_indi_cap;
 
 	if (health_data->bhi_data.battery_age >= ONE_YEAR_HRS ||
-	    cur_impedance >= age_impedance_max || cur_capacity_pct <= BHI_INDI_CAP)
+	    cur_impedance >= age_impedance_max || cur_capacity_pct <= bhi_indi_cap)
 		return health_data->need_rep_threshold * 100;
 
 	return BHI_ALGO_FULL_HEALTH;
@@ -6504,11 +6517,42 @@
 	struct power_supply *psy = container_of(dev, struct power_supply, dev);
 	struct batt_drv *batt_drv = power_supply_get_drvdata(psy);
 
-	return scnprintf(buf, PAGE_SIZE, "%d\n", batt_drv->health_data.bhi_algo);
+	return scnprintf(buf, PAGE_SIZE, "%d\n", batt_drv->health_data.bhi_indi_cap);
 }
 
 static const DEVICE_ATTR_RW(health_algo);
 
+static ssize_t health_indi_cap_store(struct device *dev,
+				 struct device_attribute *attr,
+				 const char *buf, size_t count)
+{
+	struct power_supply *psy = container_of(dev, struct power_supply, dev);
+	struct batt_drv *batt_drv = power_supply_get_drvdata(psy);
+	int value, ret;
+
+	ret = kstrtoint(buf, 0, &value);
+	if (ret < 0)
+		return ret;
+
+	if (value > 100 || value < 0)
+		return count;
+
+	batt_drv->health_data.bhi_indi_cap = value;
+
+	return count;
+}
+
+static ssize_t health_indi_cap_show(struct device *dev,
+				struct device_attribute *attr, char *buf)
+{
+	struct power_supply *psy = container_of(dev, struct power_supply, dev);
+	struct batt_drv *batt_drv = power_supply_get_drvdata(psy);
+
+	return scnprintf(buf, PAGE_SIZE, "%d\n", batt_drv->health_data.bhi_indi_cap);
+}
+
+static const DEVICE_ATTR_RW(health_indi_cap);
+
 /* CSI --------------------------------------------------------------------- */
 
 static ssize_t charging_speed_store(struct device *dev,
@@ -6917,6 +6961,9 @@
 	ret = device_create_file(&batt_drv->psy->dev, &dev_attr_health_algo);
 	if (ret)
 		dev_err(&batt_drv->psy->dev, "Failed to create health algo\n");
+	ret = device_create_file(&batt_drv->psy->dev, &dev_attr_health_indi_cap);
+	if (ret)
+		dev_err(&batt_drv->psy->dev, "Failed to create health individual capacity\n");
 
 	/* csi */
 	ret = device_create_file(&batt_drv->psy->dev, &dev_attr_charging_speed);
@@ -7027,6 +7074,9 @@
 			   &batt_drv->health_data.bhi_data.res_state.ravg_soc_high);
 	debugfs_create_file("ravg", 0400, de,  batt_drv, &debug_ravg_fops);
 
+	/* shutdown flag */
+	debugfs_create_u32("boot_to_os_attempts", 0660, de, &batt_drv->boot_to_os_attempts);
+
 	return 0;
 }
 
@@ -7075,6 +7125,37 @@
 	return ssoc_get_capacity(&batt_drv->ssoc_state) == 0;
 }
 
+#define VBATT_CRITICAL_LEVEL		3300000
+#define VBATT_CRITICAL_DEADLINE_SEC	40
+
+static bool gbatt_check_critical_level(const struct batt_drv *batt_drv,
+				       int fg_status)
+{
+	const struct batt_ssoc_state *ssoc_state = &batt_drv->ssoc_state;
+	const int soc = ssoc_get_real(ssoc_state);
+
+	if (fg_status == POWER_SUPPLY_STATUS_UNKNOWN)
+		return true;
+
+	if (soc == 0 && ssoc_state->buck_enabled == 1 &&
+	    fg_status == POWER_SUPPLY_STATUS_DISCHARGING) {
+		const ktime_t now = get_boot_sec();
+		int vbatt;
+
+		/* disable the check */
+		if (now > VBATT_CRITICAL_DEADLINE_SEC || batt_drv->batt_critical_voltage == 0)
+			return true;
+
+		vbatt = GPSY_GET_PROP(batt_drv->fg_psy, POWER_SUPPLY_PROP_VOLTAGE_NOW);
+		if (vbatt == -EAGAIN)
+			return false;
+
+		return (vbatt < 0) ? : vbatt < batt_drv->batt_critical_voltage;
+	}
+
+	return false;
+}
+
 #define SSOC_LEVEL_FULL		SSOC_SPOOF
 #define SSOC_LEVEL_HIGH		80
 #define SSOC_LEVEL_NORMAL	30
@@ -7087,9 +7168,10 @@
  * NOTE: CRITICAL_LEVEL implies BATTERY_DEAD but BATTERY_DEAD doesn't imply
  * CRITICAL.
  */
-static int gbatt_get_capacity_level(struct batt_ssoc_state *ssoc_state,
+static int gbatt_get_capacity_level(const struct batt_drv *batt_drv,
 				    int fg_status)
 {
+	const struct batt_ssoc_state *ssoc_state = &batt_drv->ssoc_state;
 	const int soc = ssoc_get_real(ssoc_state);
 	int capacity_level;
 
@@ -7106,8 +7188,7 @@
 	} else if (ssoc_state->buck_enabled == -1) {
 		/* only at startup, this should not happen */
 		capacity_level = POWER_SUPPLY_CAPACITY_LEVEL_UNKNOWN;
-	} else if (fg_status == POWER_SUPPLY_STATUS_DISCHARGING ||
-		   fg_status == POWER_SUPPLY_STATUS_UNKNOWN) {
+	} else if (gbatt_check_critical_level(batt_drv, fg_status)) {
 		capacity_level = POWER_SUPPLY_CAPACITY_LEVEL_CRITICAL;
 	} else {
 		capacity_level = POWER_SUPPLY_CAPACITY_LEVEL_LOW;
@@ -7359,9 +7440,9 @@
 
 	idx = cycle_cnt / batt_drv->hist_delta_cycle_cnt;
 
-	/* check if the cycle_cnt is valid */
+	/* save in last when over max cycles */
 	if (idx >= batt_drv->hist_data_max_cnt)
-		return -ENOENT;
+		idx = batt_drv->hist_data_max_cnt - 1;
 
 	ret = batt_hist_data_collect(batt_drv->hist_data, idx);
 	if (ret < 0)
@@ -7413,6 +7494,38 @@
 	return 0;
 }
 
+#define BOOT_TO_OS_ATTEMPTS 3
+
+static int batt_init_shutdown_flag(struct batt_drv *batt_drv)
+{
+	u8 data;
+	int ret;
+
+	ret = gbms_storage_read(GBMS_TAG_SUFG, &data, sizeof(data));
+	if (ret < 0)
+		return -EIO;
+
+	batt_drv->boot_to_os_attempts = data;
+
+	/* reset battery shutdown flag */
+	data = 0;
+	ret = gbms_storage_write(GBMS_TAG_SUFG, &data, sizeof(data));
+
+	return (ret < 0) ? -EIO : 0;
+}
+
+static int batt_set_shutdown_flag(struct batt_drv *batt_drv)
+{
+	u8 data = batt_drv->boot_to_os_attempts;
+	int ret;
+
+	if (data == 0)
+		data = BOOT_TO_OS_ATTEMPTS;
+
+	ret = gbms_storage_write(GBMS_TAG_SUFG, &data, sizeof(data));
+
+	return (ret < 0) ? -EIO : 0;
+}
 
 /*
  * poll the battery, run SOC%, dead battery, critical.
@@ -7494,13 +7607,19 @@
 		 * same behavior during the transition 99 -> 100 -> Full
 		 */
 
-		level = gbatt_get_capacity_level(&batt_drv->ssoc_state,
-						 fg_status);
+		level = gbatt_get_capacity_level(batt_drv, fg_status);
 		if (level != batt_drv->capacity_level) {
 			pr_debug("%s: change of capacity level %d->%d\n",
 				 __func__, batt_drv->capacity_level,
 				 level);
 
+			/* set battery critical shutdown */
+			if (level == POWER_SUPPLY_CAPACITY_LEVEL_CRITICAL) {
+				ret = batt_set_shutdown_flag(batt_drv);
+				if (ret < 0)
+					pr_warn("failed to write shutdown flag, ret=%d\n", ret);
+			}
+
 			batt_drv->capacity_level = level;
 			notify_psy_changed = true;
 		}
@@ -8297,6 +8416,12 @@
 	if (ret < 0)
 		health_data->cycle_count_need_rep_threshold = BHI_CC_NEED_REP_THRESHOLD_DEFAULT;
 
+	/* algorithm BHI_ALGO_INDI capacity threshold */
+	ret = of_property_read_u32(batt_drv->device->of_node, "google,bhi-indi-cap",
+				   &health_data->bhi_indi_cap);
+	if (ret < 0)
+		health_data->bhi_indi_cap = BHI_INDI_CAP_DEFAULT;
+
 	/* design is the value used to build the charge table */
 	health_data->bhi_data.capacity_design = batt_drv->battery_capacity;
 
@@ -8369,6 +8494,7 @@
 	batt_drv->hold_taper_ws = false;
 	batt_drv->fake_temp = 0;
 	batt_drv->fake_battery_present = -1;
+	batt_drv->boot_to_os_attempts = 0;
 	batt_reset_chg_drv_state(batt_drv);
 
 	mutex_init(&batt_drv->chg_lock);
@@ -8454,6 +8580,11 @@
 	if (ret < 0)
 		pr_err("bpst profile disabled, ret=%d\n", ret);
 
+	/* init shutdown flag */
+	ret = batt_init_shutdown_flag(batt_drv);
+	if (ret < 0)
+		pr_err("failed to init shutdown flag, ret=%d\n", ret);
+
 	/* cycle count is cached: read here bc SSOC, chg_profile might use it */
 	batt_update_cycle_count(batt_drv);
 
@@ -8596,6 +8727,11 @@
 	if (ret < 0)
 		batt_drv->hist_delta_cycle_cnt = HCC_DEFAULT_DELTA_CYCLE_CNT;
 
+	ret = of_property_read_u32(batt_drv->device->of_node, "google,batt-voltage-critical",
+				   &batt_drv->batt_critical_voltage);
+	if (ret < 0)
+		batt_drv->batt_critical_voltage = VBATT_CRITICAL_LEVEL;
+
 	/* battery virtual sensor */
 	ret = of_property_read_string(batt_drv->device->of_node,
 				      "google,batt-vs-tz-name",
diff --git a/google_eeprom.c b/google_eeprom.c
index 75b2755..6260ba1 100644
--- a/google_eeprom.c
+++ b/google_eeprom.c
@@ -22,6 +22,8 @@
 #define BATT_EEPROM_TAG_BGPN_LEN	GBMS_BGPN_LEN
 #define BATT_EEPROM_TAG_BRID_OFFSET	0x17
 #define BATT_EEPROM_TAG_BRID_LEN	1
+#define BATT_EEPROM_TAG_MYMD_OFFSET	0x1A
+#define BATT_EEPROM_TAG_MYMD_LEN	4
 #define BATT_EEPROM_TAG_STRD_OFFSET	0x1E
 #define BATT_EEPROM_TAG_STRD_LEN	12
 #define BATT_EEPROM_TAG_RSOC_OFFSET	0x2A
@@ -48,6 +50,8 @@
 #define BATT_EEPROM_TAG_EXTRA_START	(BATT_EEPROM_TAG_HIST_OFFSET + BATT_TOTAL_HIST_LEN)
 
 // 0x3E2 is the first free with 75 history entries
+#define BATT_EEPROM_TAG_AYMD_OFFSET	0x3E4
+#define BATT_EEPROM_TAG_AYMD_LEN	4
 #define BATT_EEPROM_TAG_GCFE_OFFSET	0x3E8
 #define BATT_EEPROM_TAG_GCFE_LEN	2
 #define BATT_EEPROM_TAG_RAVG_OFFSET	0x3EA
@@ -147,6 +151,14 @@
 		*addr = BATT_EEPROM_TAG_THAS_OFFSET;
 		*count = BATT_EEPROM_TAG_THAS_LEN;
 		break;
+	case GBMS_TAG_AYMD:
+		*addr = BATT_EEPROM_TAG_AYMD_OFFSET;
+		*count = BATT_EEPROM_TAG_AYMD_LEN;
+		break;
+	case GBMS_TAG_MYMD:
+		*addr = BATT_EEPROM_TAG_MYMD_OFFSET;
+		*count = BATT_EEPROM_TAG_MYMD_LEN;
+		break;
 	default:
 		ret = -ENOENT;
 		break;
@@ -166,7 +178,8 @@
 					   GBMS_TAG_STRD, GBMS_TAG_RSOC,
 					   GBMS_TAG_ACIM, GBMS_TAG_GCFE,
 					   GBMS_TAG_RAVG, GBMS_TAG_RFCN,
-					   GBMS_TAG_THAS};
+					   GBMS_TAG_THAS, GBMS_TAG_AYMD,
+					   GBMS_TAG_MYMD};
 	const int count = ARRAY_SIZE(keys);
 
 	if (index < 0 || index >= count)
@@ -229,6 +242,7 @@
 	case GBMS_TAG_RAVG:
 	case GBMS_TAG_RFCN:
 	case GBMS_TAG_THAS:
+	case GBMS_TAG_AYMD:
 		return true;
 	default:
 		return false;
diff --git a/max1720x_battery.c b/max1720x_battery.c
index 5d17030..d29dddc 100644
--- a/max1720x_battery.c
+++ b/max1720x_battery.c
@@ -129,6 +129,8 @@
 
 #define BHI_CAP_FCN_COUNT	3
 
+#define DEFAULT_STATUS_CHARGE_MA	100
+
 #pragma pack(1)
 struct max17x0x_eeprom_history {
 	u16 tempco;
@@ -259,6 +261,9 @@
 	int bhi_acim;
 
 	struct max1720x_rc_switch rc_switch;
+
+	/* battery current criteria for report status charge */
+	u32 status_charge_threshold_ma;
 };
 
 #define MAX1720_EMPTY_VOLTAGE(profile, temp, cycle) \
@@ -1245,10 +1250,14 @@
 		return -EIO;
 	current_avg = -reg_to_micro_amp(data, chip->RSense);
 
-	err = REGMAP_READ(&chip->regmap, MAX1720X_ICHGTERM, &data);
-	if (err)
-		return -EIO;
-	ichgterm = reg_to_micro_amp(data, chip->RSense);
+	if (chip->status_charge_threshold_ma) {
+		ichgterm = chip->status_charge_threshold_ma * 1000;
+	} else {
+		err = REGMAP_READ(&chip->regmap, MAX1720X_ICHGTERM, &data);
+		if (err)
+			return -EIO;
+		ichgterm = reg_to_micro_amp(data, chip->RSense);
+	}
 
 	err = REGMAP_READ(&chip->regmap, MAX1720X_FULLSOCTHR, &data);
 	if (err)
@@ -2109,6 +2118,7 @@
 static int max1720x_get_fade_rate(struct max1720x_chip *chip)
 {
 	struct max17x0x_eeprom_history hist = { 0 };
+	int bhi_fcn_count = chip->bhi_fcn_count;
 	int ret, ratio, i, fcn_sum = 0;
 	u16 hist_idx;
 
@@ -2120,10 +2130,20 @@
 
 	dev_info(chip->dev, "%s: hist_idx=%d\n", __func__, hist_idx);
 
-	if (hist_idx < chip->bhi_fcn_count)
-		return -ENODATA;
+	/* no fade for new battery (less than 30 cycles) */
+	if (hist_idx < bhi_fcn_count)
+		return 0;
 
-	for (i = chip->bhi_fcn_count; i ; i--, hist_idx--) {
+	while (hist_idx >= BATT_MAX_HIST_CNT && bhi_fcn_count > 1) {
+		hist_idx--;
+		bhi_fcn_count--;
+		if (bhi_fcn_count == 1) {
+			hist_idx = BATT_MAX_HIST_CNT - 1;
+			break;
+		}
+	}
+
+	for (i = bhi_fcn_count; i ; i--, hist_idx--) {
 		ret = gbms_storage_read_data(GBMS_TAG_HIST, &hist,
 					     sizeof(hist), hist_idx);
 
@@ -2138,7 +2158,7 @@
 	}
 
 	/* convert from max17x0x_eeprom_history to percent */
-	ratio = fcn_sum / (chip->bhi_fcn_count * 8);
+	ratio = fcn_sum / (bhi_fcn_count * 8);
 	if (ratio > 100)
 		ratio = 100;
 
@@ -2831,15 +2851,21 @@
 	fg_status_clr = fg_status;
 
 	if (fg_status & MAX1720X_STATUS_POR) {
+		const bool no_battery = chip->fake_battery == 0;
+
 		mutex_lock(&chip->model_lock);
 		chip->por = true;
-		dev_warn(chip->dev, "POR is set(%04x), model reload:%d\n",
-			 fg_status, chip->model_reload);
-		/* trigger model load if not on-going */
-		if (chip->model_reload != MAX_M5_LOAD_MODEL_REQUEST) {
-			err = max1720x_model_reload(chip, true);
-			if (err < 0)
-				fg_status_clr &= ~MAX1720X_STATUS_POR;
+		if (no_battery) {
+			fg_status_clr &= ~MAX1720X_STATUS_POR;
+		} else {
+			dev_warn(chip->dev, "POR is set(%04x), model reload:%d\n",
+				 fg_status, chip->model_reload);
+			/* trigger model load if not on-going */
+			if (chip->model_reload != MAX_M5_LOAD_MODEL_REQUEST) {
+				err = max1720x_model_reload(chip, true);
+				if (err < 0)
+					fg_status_clr &= ~MAX1720X_STATUS_POR;
+			}
 		}
 		mutex_unlock(&chip->model_lock);
 	}
@@ -3507,11 +3533,15 @@
 /* Model reload will be disabled if the node is not found */
 static int max1720x_init_model(struct max1720x_chip *chip)
 {
+	const bool no_battery = chip->fake_battery == 0;
 	void *model_data;
 
 	if (chip->gauge_type != MAX_M5_GAUGE_TYPE)
 		return 0;
 
+	if (no_battery)
+		return 0;
+
 	/* ->batt_id negative for no lookup */
 	if (chip->batt_id >= 0) {
 		chip->batt_node = max1720x_find_batt_node(chip);
@@ -4671,6 +4701,9 @@
 {
 	int ret;
 
+	if (!chip->model_data)
+		return 0;
+
 	if (!max_m5_fg_model_check_version(chip->model_data)) {
 		if (max_m5_needs_reset_model_data(chip->model_data)) {
 			ret = max_m5_reset_state_data(chip->model_data);
@@ -5832,6 +5865,7 @@
 	const char *psy_name = NULL;
 	char monitor_name[32];
 	int ret = 0;
+	u32 data32;
 
 	chip = devm_kzalloc(dev, sizeof(*chip), GFP_KERNEL);
 	if (!chip)
@@ -5848,6 +5882,15 @@
 	if (chip->gauge_type < 0)
 		chip->gauge_type = -1;
 
+	ret = of_property_read_u32(dev->of_node, "maxim,status-charge-threshold-ma",
+				   &data32);
+	if (ret == 0)
+		chip->status_charge_threshold_ma = data32;
+	else if (chip->gauge_type == MAX_M5_GAUGE_TYPE)
+		chip->status_charge_threshold_ma = DEFAULT_STATUS_CHARGE_MA;
+	else
+		chip->status_charge_threshold_ma = 0;
+
 	/* needs chip->primary and (optional) chip->secondary */
 	ret = max17x0x_regmap_init(chip);
 	if (ret < 0) {
diff --git a/max77759_maxq.c b/max77759_maxq.c
index a613ac1..583ac4e 100644
--- a/max77759_maxq.c
+++ b/max77759_maxq.c
@@ -66,7 +66,9 @@
 
 #define RSBM_ADDR				0
 #define RSBR_ADDR				4
+#define SUFG_ADDR				8
 #define RS_TAG_LENGTH				4
+#define SU_TAG_LENGTH				1
 #define RS_TAG_OFFSET_ADDR			0
 #define RS_TAG_OFFSET_LENGTH			1
 #define RS_TAG_OFFSET_DATA			2
@@ -314,42 +316,54 @@
 
 static int maxq_rs_read(struct max77759_maxq *maxq, gbms_tag_t tag, u8 *data)
 {
-	int ret;
+	int ret, len;
 	u8 buff[OPCODE_USER_SPACE_R_RES_LEN];
 
-	if (tag == GBMS_TAG_RSBM)
+	if (tag == GBMS_TAG_RSBM) {
 		buff[RS_TAG_OFFSET_ADDR] = RSBM_ADDR;
-	else if (tag == GBMS_TAG_RSBR)
+		len = RS_TAG_LENGTH;
+	} else if (tag == GBMS_TAG_RSBR) {
 		buff[RS_TAG_OFFSET_ADDR] = RSBM_ADDR;
-	else
+		len = RS_TAG_LENGTH;
+	} else if (tag == GBMS_TAG_SUFG) {
+		buff[RS_TAG_OFFSET_ADDR] = SUFG_ADDR;
+		len = SU_TAG_LENGTH;
+	} else {
 		return -EINVAL;
+	}
 
-	buff[RS_TAG_OFFSET_LENGTH] = RS_TAG_LENGTH;
+	buff[RS_TAG_OFFSET_LENGTH] = len;
 
 	ret = maxq_user_space_read(maxq, buff);
 	if (ret < 0)
 		return ret;
 
-	memcpy(data, buff, RS_TAG_LENGTH);
+	memcpy(data, buff, len);
 
 	return ret;
 }
 
 static int maxq_rs_write(struct max77759_maxq *maxq, gbms_tag_t tag, u8 *data)
 {
-	int ret;
+	int ret, len;
 	u8 buff[OPCODE_USER_SPACE_W_REQ_LEN];
 
-	if (tag == GBMS_TAG_RSBM)
+	if (tag == GBMS_TAG_RSBM) {
 		buff[RS_TAG_OFFSET_ADDR] = RSBM_ADDR;
-	else if (tag == GBMS_TAG_RSBR)
+		len = RS_TAG_LENGTH;
+	} else if (tag == GBMS_TAG_RSBR) {
 		buff[RS_TAG_OFFSET_ADDR] = RSBR_ADDR;
-	else
+		len = RS_TAG_LENGTH;
+	} else if (tag == GBMS_TAG_SUFG) {
+		buff[RS_TAG_OFFSET_ADDR] = SUFG_ADDR;
+		len = SU_TAG_LENGTH;
+	} else {
 		return -EINVAL;
+	}
 
-	buff[RS_TAG_OFFSET_LENGTH] = RS_TAG_LENGTH;
+	buff[RS_TAG_OFFSET_LENGTH] = len;
 
-	memcpy(&buff[RS_TAG_OFFSET_DATA], data, RS_TAG_LENGTH);
+	memcpy(&buff[RS_TAG_OFFSET_DATA], data, len);
 
 	ret = maxq_user_space_write(maxq, buff);
 
@@ -374,6 +388,11 @@
 			return -EINVAL;
 		ret = maxq_rs_read(maxq, tag, buff);
 		break;
+	case GBMS_TAG_SUFG:
+		if (size && size > SU_TAG_LENGTH)
+			return -EINVAL;
+		ret = maxq_rs_read(maxq, tag, buff);
+		break;
 	default:
 		ret = -ENOENT;
 		break;
@@ -400,6 +419,11 @@
 			return -EINVAL;
 		ret = maxq_rs_write(maxq, tag, (void *)buff);
 		break;
+	case GBMS_TAG_SUFG:
+		if (size && size > SU_TAG_LENGTH)
+			return -EINVAL;
+		ret = maxq_rs_write(maxq, tag, (void *)buff);
+		break;
 	default:
 		ret = -ENOENT;
 		break;
diff --git a/p9221_charger.c b/p9221_charger.c
index e81a05d..b0af8d1 100644
--- a/p9221_charger.c
+++ b/p9221_charger.c
@@ -658,19 +658,56 @@
 
 #define EPP_MODE_REQ_PWR		15
 #define EPP_MODE_REQ_VOUT		12000
+#define WLC_VOUT_RAMP_DOWN_MV		15300
+#define WLC_VOUT_CFG_STEP		40		/* b/194346461 ramp down VOUT */
 static int p9xxx_set_bypass_mode(struct p9221_charger_data *charger)
 {
 	const int req_pwr = EPP_MODE_REQ_PWR;
+	const int vout_target = WLC_VOUT_RAMP_DOWN_MV;
 	int i, count, ret;
 	u8 cdmode, currpwr;
 	u32 vout_mv;
 
+	if (!charger->online)
+		return 0;
+
 	/* Check it's in Cap Div mode */
 	ret = charger->reg_read_8(charger, P9412_CDMODE_STS_REG, &cdmode);
 	if (ret || (cdmode & CDMODE_BYPASS_MODE))
 		return ret;
 	dev_info(&charger->client->dev, "cdmode_reg=%02x\n", cdmode);
 
+	usleep_range(500 * USEC_PER_MSEC, 510 * USEC_PER_MSEC);
+	/* Ramp down WLC Vout to 15.3V */
+	while (true) {
+		ret = charger->chip_get_vout(charger, &vout_mv);
+		if (ret < 0 || vout_mv == 0) {
+			dev_err(&charger->client->dev, "%s: invalid vout %d\n", __func__, ret);
+			return ret;
+		}
+
+		if (vout_mv < vout_target) {
+			dev_info(&charger->client->dev, "%s: underflow vout=%d, (target=%d)\n",
+				 __func__, vout_mv, vout_target);
+			break;
+		}
+
+		vout_mv -= WLC_VOUT_CFG_STEP;
+
+		ret = charger->chip_set_vout_max(charger, vout_mv);
+		if (ret < 0) {
+			dev_err(&charger->client->dev, "%s: cannot set vout %d\n", __func__, ret);
+			return ret;
+		} else {
+			dev_info(&charger->client->dev, "%s: vout set to %d\n", __func__, vout_mv);
+			usleep_range(250 * USEC_PER_MSEC, 260 * USEC_PER_MSEC);
+		}
+	}
+
+	if (!charger->online)
+		return 0;
+
+	usleep_range(500 * USEC_PER_MSEC, 510 * USEC_PER_MSEC);
 	for (count = 0; count < 3; count++) {
 		/* Change the Requested Power to 15W */
 		ret = charger->reg_write_8(charger, P9412_PROP_REQ_PWR_REG, req_pwr * 2);
@@ -727,8 +764,12 @@
 	const int extben_gpio = charger->pdata->ext_ben_gpio;
 	int ret;
 
+	if (!charger->pdata->has_wlc_dc)
+		return -EOPNOTSUPP;
+
 	charger->wlc_dc_enabled = false;
 
+	usleep_range(500 * USEC_PER_MSEC, 510 * USEC_PER_MSEC);
 	p9xxx_gpio_set_value(charger, dc_sw_gpio, 0);
 	p9xxx_gpio_set_value(charger, extben_gpio, 0);
 
@@ -2410,8 +2451,20 @@
 		return;
 	}
 
-	threshold = charger->mitigate_threshold > 0 ? charger->mitigate_threshold :
-		    charger->pdata->power_mitigate_threshold;
+	if (!charger->csi_type_votable)
+		charger->csi_type_votable = gvotable_election_get_handle(VOTABLE_CSI_TYPE);
+
+	if (charger->mitigate_threshold > 0)
+		threshold = charger->mitigate_threshold;
+	else if (charger->csi_type_votable &&
+		 charger->pdata->power_mitigate_threshold > 0 &&
+		 gvotable_get_current_int_vote(charger->csi_type_votable) == CSI_TYPE_Adaptive)
+		threshold = charger->last_capacity - 1; /* Run dream defend when AC trigger */
+	else
+		threshold = charger->pdata->power_mitigate_threshold;
+
+	pr_debug("dream_defend soc:%d threshold:%d\n", charger->last_capacity, threshold);
+
 	if (!threshold)
 		return;
 
diff --git a/p9221_charger.h b/p9221_charger.h
index 4a15eb2..dee1be6 100644
--- a/p9221_charger.h
+++ b/p9221_charger.h
@@ -710,6 +710,7 @@
 	struct gvotable_election	*chg_mode_votable;
 	struct gvotable_election	*wlc_disable_votable;
 	struct gvotable_election	*csi_status_votable;
+	struct gvotable_election	*csi_type_votable;
 	struct notifier_block		nb;
 	struct mutex			io_lock;
 	struct mutex			cmd_lock;
diff --git a/pca9468_charger.c b/pca9468_charger.c
index c8632c9..f94ccbd 100644
--- a/pca9468_charger.c
+++ b/pca9468_charger.c
@@ -4982,7 +4982,7 @@
 	pca9468_chg->pdata = pdata;
 	pca9468_chg->charging_state = DC_STATE_NO_CHARGING;
 	pca9468_chg->wlc_ramp_out_iin = true;
-	pca9468_chg->wlc_ramp_out_vout_target = 15300000; /* 15.3V as default */
+	pca9468_chg->wlc_ramp_out_vout_target = 0; /* use Vbatt*4 as default */
 	pca9468_chg->wlc_ramp_out_delay = 250; /* 250 ms default */
 
 	/* Create a work queue for the direct charger */