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

Bug: 255246572
Change-Id: I9ff696ac5ec7f5eb3a4a8289ad84d53e576171f4
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 6489ce3..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;
@@ -549,6 +551,9 @@
 	/* battery pack status */
 	struct batt_bpst bpst_state;
 
+	/* shutdown flag */
+	int boot_to_os_attempts;
+
 	/* battery critical level */
 	int batt_critical_voltage;
 };
@@ -1115,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;
@@ -3234,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;
@@ -6507,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,
@@ -6920,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);
@@ -7030,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;
 }
 
@@ -7393,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)
@@ -7447,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.
@@ -7534,6 +7613,13 @@
 				 __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;
 		}
@@ -8330,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;
 
@@ -8402,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);
@@ -8487,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);
 
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..bd691c7 100644
--- a/p9221_charger.c
+++ b/p9221_charger.c
@@ -27,7 +27,7 @@
 #include <linux/debugfs.h>
 
 #define P9221R5_OVER_CHECK_NUM		3
-
+#define MFG_CHK_COUNT_MAX		30
 #define OVC_LIMIT			1
 #define OVC_THRESHOLD			1400000
 #define OVC_BACKOFF_LIMIT		900000
@@ -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);
 
@@ -1397,7 +1438,7 @@
 	 *  Check 10 times if alignment_capble is still 0.
 	 */
 
-	if ((charger->mfg_check_count < 10) ||
+	if ((charger->mfg_check_count < MFG_CHK_COUNT_MAX) ||
 	    (charger->alignment_capable == ALIGN_MFG_PASSED)) {
 
 		/* release the align_ws before return*/
@@ -1414,7 +1455,8 @@
 	/* release the align_ws */
 	__pm_relax(charger->align_ws);
 
-	dev_info(&charger->client->dev, "align_work ended\n");
+	dev_info(&charger->client->dev, "align_work ended(mfg_check_count=%d)\n",
+		 charger->mfg_check_count);
 }
 
 static const char *p9221_get_tx_id_str(struct p9221_charger_data *charger)
@@ -2227,6 +2269,23 @@
 	return ret;
 }
 
+static int p9xxx_check_alignment(struct p9221_charger_data *charger)
+{
+	int ret = 0;
+
+	if (charger->alignment == 100) {
+		dev_dbg(&charger->client->dev, "Alignment check OK\n");
+	} else if (charger->alignment == -1 && charger->mfg_check_count < MFG_CHK_COUNT_MAX) {
+		ret = -EAGAIN;
+		dev_dbg(&charger->client->dev, "Alignment checking\n");
+	} else {
+		ret = -EOPNOTSUPP;
+		dev_err(&charger->client->dev, "Misalignment!\n");
+	}
+
+	return ret;
+}
+
 /* < 0 error, 0 = no changes, > 1 changed */
 static int p9221_set_psy_online(struct p9221_charger_data *charger, int online)
 {
@@ -2305,15 +2364,20 @@
 			return -EOPNOTSUPP;
 		}
 
+		/* AUTH is passed remove the DC_ICL limit */
+		p9221_set_auth_dc_icl(charger, false);
+		mutex_unlock(&charger->auth_lock);
+
+		/* Check alignment before enabling proprietary mode */
+		ret = p9xxx_check_alignment(charger);
+		if (ret < 0)
+			return ret;
+
 		ret = p9221_set_hpp_dc_icl(charger, true);
 		if (ret < 0)
 			dev_warn(&charger->client->dev, "Cannot enable HPP_ICL (%d)\n", ret);
 		mdelay(10);
 
-		/* AUTH is passed remove the DC_ICL limit */
-		p9221_set_auth_dc_icl(charger, false);
-		mutex_unlock(&charger->auth_lock);
-
 		/*
 		 * run ->chip_prop_mode_en() if proprietary mode or cap divider
 		 * mode isn't enabled (i.e. with p9412_prop_mode_enable())
@@ -2410,8 +2474,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 72a564a..a3fc6f6 100644
--- a/pca9468_charger.c
+++ b/pca9468_charger.c
@@ -5014,7 +5014,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 */