Merge cherrypicks of ['partner-android-review.googlesource.com/3020167', 'partner-android-review.googlesource.com/3036702', 'partner-android-review.googlesource.com/3036782', 'partner-android-review.googlesource.com/3038036', 'partner-android-review.googlesource.com/3043516'] into sparse-12859539-L51200030008761432.
SPARSE_CHANGE: Ic3c98a1728fb68c1a8da8ab44646b426b9e67062
SPARSE_CHANGE: Ief29811aba1d50eb21fc3de584cd6c38f0f9e133
SPARSE_CHANGE: I6e0238f0a4bc666c0143c69c7d3b19add7cef85c
SPARSE_CHANGE: Iaa51de3c538d21d5eb1b7f5ae139ccc57e2871cb
SPARSE_CHANGE: I6ee215c6bbc602c3aebb496f966fb1845b7da6bf


Change-Id: I84e307d9decbe9768c41b954604391044378268f
Signed-off-by: Coastguard Worker <[email protected]>
diff --git a/gbms_power_supply.h b/gbms_power_supply.h
index 05ca729..6ef82e2 100644
--- a/gbms_power_supply.h
+++ b/gbms_power_supply.h
@@ -96,6 +96,7 @@
 	GBMS_PROP_BATT_ID,			/* GBMS battery id */
 	GBMS_PROP_RECAL_FG,			/* GBMS FG reset */
 	GBMS_PROP_LOGBUFFER_BD,			/* GBMS pass logbuffer_bd address */
+	GBMS_PROP_AAFV,				/* GBMS pass aafv to FG */
 };
 
 union gbms_propval {
diff --git a/google_battery.c b/google_battery.c
index 05a41bd..7529071 100644
--- a/google_battery.c
+++ b/google_battery.c
@@ -287,6 +287,13 @@
 	BATT_AAFV_MAX,
 };
 
+enum batt_aact_state {
+	BATT_AACT_UNKNOWN = -1,
+	BATT_AACT_DISABLED = 0,
+	BATT_AACT_ENABLED = 1,
+	BATT_AACT_MAX,
+};
+
 #define BATT_TEMP_RECORD_THR 3
 /* discharge saved after charge */
 #define SD_CHG_START 0
@@ -526,6 +533,7 @@
 	/* for testing */
 	int fake_aacr_cc;
 	int fake_aafv_cc;
+	int fake_aact_cc;
 
 	/* props */
 	int soh;
@@ -650,6 +658,16 @@
 	struct logbuffer *bd_log;
 	struct mutex aafv_state_lock;
 
+	/* AACT: Aged Adjusted Charge Table */
+	enum batt_aact_state aact_state;
+	struct mutex aact_state_lock;
+
+	/* AACP: Configuration Version for AAFV, AACR and AACT for the device */
+	int aacp_version;
+
+	/* AACC: Age adjusted cycle count */
+	int aacc;
+
 	/* BHI: updated on disconnect, EOC */
 	struct health_data health_data;
 	struct swelling_data sd;
@@ -1959,6 +1977,10 @@
 	ce_data->csi_aggregate_status = batt_drv->csi_stats.aggregate_status;
 	ce_data->csi_aggregate_type = batt_drv->csi_stats.aggregate_type;
 
+	/* Log aacp_version and aacc into chg_stats */
+	ce_data->aacp_version = batt_drv->aacp_version;
+	ce_data->aacc = batt_drv->aacc;
+
 	/* Note: To log new voltage tiers, add to list in go/pixel-vtier-defs */
 	/* ---  Log tiers in PARALLEL below ---  */
 
@@ -2362,7 +2384,7 @@
 				ce_data->adapter_details.ad_voltage * 100,
 				ce_data->adapter_details.ad_amperage * 100);
 
-	len += scnprintf(&buff[len], size - len, "%s%hu,%hu, %hu,%hu %d %hu,%hu",
+	len += scnprintf(&buff[len], size - len, "%s%hu,%hu, %hu,%hu %d %hu,%hu, %d,%d",
 				(verbose) ?  "\nS: " : ", ",
 				ce_data->charging_stats.ssoc_in,
 				ce_data->charging_stats.voltage_in,
@@ -2370,7 +2392,9 @@
 				ce_data->charging_stats.voltage_out,
 				state_capacity,
 				ce_data->csi_aggregate_status,
-				ce_data->csi_aggregate_type);
+				ce_data->csi_aggregate_type,
+				ce_data->aacp_version,
+				ce_data->aacc);
 
 
 	if (verbose) {
@@ -3950,7 +3974,7 @@
 	if (batt_drv->fake_aacr_cc)
 		cycle_count = batt_drv->fake_aacr_cc;
 	else
-		cycle_count = batt_drv->cycle_count;
+		cycle_count = batt_drv->aacc;
 
 	if (batt_drv->aacr_state == BATT_AACR_DISABLED)
 		goto exit_done;
@@ -4259,7 +4283,7 @@
 
 	if (bhi_algo_has_bounds(algo)) {
 		const int cycle_count = batt_drv->fake_aacr_cc ?
-					batt_drv->fake_aacr_cc : batt_drv->cycle_count;
+					batt_drv->fake_aacr_cc : batt_drv->aacc;
 
 		capacity_health = bhi_algo_apply_bounds(algo, capacity_health, cycle_count,
 							bhi_data);
@@ -4855,7 +4879,7 @@
 
 static u32 aafv_update_state(struct batt_drv *batt_drv)
 {
-	int cycle_count = batt_drv->cycle_count;
+	int cycle_count = batt_drv->aacc;
 	int offset, aafv_offset = 0;
 
 	mutex_lock(&batt_drv->aafv_state_lock);
@@ -4902,6 +4926,16 @@
 		*fv_uv = (int)aafv_fv;
 }
 
+/* AACC ------------------------------------------------------------------- */
+
+static void aacc_update_cycle_count(struct batt_drv *batt_drv)
+{
+	int cycle_count = batt_drv->cycle_count;
+
+	/* TODO: AACC is TBD */
+	batt_drv->aacc = cycle_count;
+}
+
 /* ------------------------------------------------------------------------ */
 
 /* TODO: factor msc_logic_irdop from the logic about tier switch */
@@ -5194,8 +5228,14 @@
 	if (vbatt_idx != batt_drv->vbatt_idx || temp_idx != batt_drv->temp_idx) {
 		const int last_vbatt_idx = gbms_msc_get_last_voltage_idx(profile, temp_idx);
 
-		if (vbatt_idx == last_vbatt_idx)
+		if (vbatt_idx == last_vbatt_idx) {
+			int ret;
+
 			aafv_update_voltage(batt_drv, &fv_uv, last_vbatt_idx);
+			ret = GPSY_SET_PROP(fg_psy, GBMS_PROP_AAFV, fv_uv);
+			if (ret < 0)
+				pr_err("pass aafv to FG failed %d", ret);
+		}
 	}
 
 	batt_prlog(batt_prlog_level(changed),
@@ -5287,16 +5327,18 @@
 {
 	u32 capacity = aacr_get_capacity(batt_drv);
 
-	if (capacity != batt_drv->chg_profile.capacity_ma) {
-		gbms_logbuffer_devlog(batt_drv->ttf_stats.ttf_log, batt_drv->device,
-		LOGLEVEL_INFO, 0, LOGLEVEL_INFO, "AACR: capacity:%d->%d, state:%d, "
-		"algo:%d, cycle_grace:%d, cycle_max:%d, min_cap_rate:%d, cliff_cap_rate:%d",
-		batt_drv->chg_profile.capacity_ma, capacity, batt_drv->aacr_state,
-		batt_drv->aacr_algo, batt_drv->aacr_cycle_grace, batt_drv->aacr_cycle_max,
-		batt_drv->aacr_min_capacity_rate, batt_drv->aacr_cliff_capacity_rate);
-		gbms_init_chg_table(&batt_drv->chg_profile, batt_drv->device->of_node, capacity);
-		google_battery_dump_profile(&batt_drv->chg_profile);
-	}
+	if (capacity == batt_drv->chg_profile.capacity_ma)
+		return;
+
+	gbms_logbuffer_devlog(batt_drv->ttf_stats.ttf_log, batt_drv->device,
+			      LOGLEVEL_INFO, 0, LOGLEVEL_INFO,
+			      "AACR: capacity:%d->%d, state:%d, algo:%d, cycle_grace:%d, cycle_max:%d, min_cap_rate:%d, cliff_cap_rate:%d",
+			      batt_drv->chg_profile.capacity_ma, capacity, batt_drv->aacr_state,
+			      batt_drv->aacr_algo, batt_drv->aacr_cycle_grace,
+			      batt_drv->aacr_cycle_max, batt_drv->aacr_min_capacity_rate,
+			      batt_drv->aacr_cliff_capacity_rate);
+	gbms_init_chg_table(&batt_drv->chg_profile, batt_drv->device->of_node, capacity);
+	google_battery_dump_profile(&batt_drv->chg_profile);
 }
 
 /* cell fault: disconnect of one of the battery cells */
@@ -5397,6 +5439,60 @@
 	return 0;
 }
 
+/* AACT ------------------------------------------------------------------- */
+
+/* call holding mutex_lock(&batt_drv->aact_state_lock); */
+static void aact_reset(struct gbms_chg_profile *profile)
+{
+	profile->aact_nb_limits = 0;
+	profile->aact_idx = 0;
+}
+
+/* call holding mutex_lock(&batt_drv->aact_state_lock); */
+static int aact_get_index(const struct batt_drv *batt_drv)
+{
+	int cycle_count = batt_drv->aacc;
+
+	if (batt_drv->fake_aact_cc)
+		cycle_count = batt_drv->fake_aact_cc;
+
+	return gbms_aact_get_index(&batt_drv->chg_profile, cycle_count);
+}
+
+/* call holding mutex_lock(&batt_drv->aact_state_lock); */
+static int aact_update_chg_table(struct batt_drv *batt_drv)
+{
+	struct gbms_chg_profile *profile = &batt_drv->chg_profile;
+	struct device_node *node = batt_drv->device->of_node;
+	int ret;
+
+	if (!profile->aact_init_profile && batt_drv->aact_state == BATT_AACT_ENABLED) {
+		/* init AACT charge table */
+		ret = gbms_init_aact_profile(profile, node);
+		if (ret < 0)
+			return ret;
+
+		gbms_init_chg_table(profile, node, batt_drv->battery_capacity);
+	} else if (profile->aact_init_profile && batt_drv->aact_state == BATT_AACT_DISABLED) {
+		/* reset AACT */
+		aact_reset(profile);
+
+		/* init default charge table */
+		ret = gbms_init_chg_profile(profile, node);
+		if (ret < 0)
+			return ret;
+
+		gbms_init_chg_table(profile, node, batt_drv->battery_capacity);
+	}
+
+	if (batt_drv->aact_state == BATT_AACT_ENABLED)
+		profile->aact_idx = aact_get_index(batt_drv);
+
+	return 0;
+}
+
+/* ------------------------------------------------------------------------- */
+
 /* call holding mutex_lock(&batt_drv->chg_lock); */
 static int batt_chg_logic(struct batt_drv *batt_drv)
 {
@@ -5447,6 +5543,9 @@
 		batt_update_cycle_count(batt_drv);
 		batt_rl_reset(batt_drv);
 
+		/* aacc: cycle count */
+		aacc_update_cycle_count(batt_drv);
+
 		/* charging_policy: vote AC false when disconnected */
 		gvotable_cast_long_vote(batt_drv->charging_policy_votable, "MSC_AC",
 					CHARGING_POLICY_VOTE_ADAPTIVE_AC, false);
@@ -5492,6 +5591,26 @@
 		if (bhi_data->res_state.estimate_filter)
 			batt_res_state_set(&bhi_data->res_state, true);
 
+		mutex_lock(&batt_drv->aact_state_lock);
+		err = aact_update_chg_table(batt_drv);
+		if (err < 0) {
+			struct gbms_chg_profile *profile = &batt_drv->chg_profile;
+			struct device_node *node = batt_drv->device->of_node;
+			int rc;
+
+			/* reset AACT */
+			aact_reset(profile);
+
+			/* set state to unknown and init default charge table */
+			batt_drv->aact_state = BATT_AACT_UNKNOWN;
+			rc = gbms_init_chg_profile(profile, node);
+			if (rc == 0)
+				gbms_init_chg_table(profile, node, aacr_get_capacity(batt_drv));
+
+			pr_err("Cannot update aact charge table (%d)\n", err);
+		}
+		mutex_unlock(&batt_drv->aact_state_lock);
+
 		aacr_update_chg_table(batt_drv);
 
 		batt_chg_stats_start(batt_drv);
@@ -6264,6 +6383,7 @@
 	if (!tmp)
 		return -ENOMEM;
 
+	mutex_lock(&batt_drv->aact_state_lock);
 	if (raw_profile_cycles) {
 		struct gbms_chg_profile profile;
 		int count;
@@ -6272,6 +6392,14 @@
 		if (len < 0)
 			goto exit_done;
 
+		if (batt_drv->aact_state == BATT_AACT_ENABLED) {
+			len = gbms_init_aact_profile(&profile, batt_drv->device->of_node);
+			if (len < 0)
+				goto exit_done;
+
+			profile.aact_idx = aact_get_index(batt_drv);
+		}
+
 		/* len is the capacity */
 		len = aacr_get_capacity_at_cycle(batt_drv, raw_profile_cycles);
 		if (len <= 0) {
@@ -6291,6 +6419,7 @@
 	len = simple_read_from_buffer(buf, count, ppos, tmp, strlen(tmp));
 
 exit_done:
+	mutex_unlock(&batt_drv->aact_state_lock);
 	kfree(tmp);
 	return len;
 }
@@ -8201,6 +8330,112 @@
 
 static const DEVICE_ATTR_RO(aafv_offset);
 
+/* AACT ------------------------------------------------------------------- */
+
+static ssize_t aact_state_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 val, ret = 0;
+
+	ret = kstrtoint(buf, 0, &val);
+	if (ret < 0)
+		return ret;
+
+	mutex_lock(&batt_drv->aact_state_lock);
+
+	if (batt_drv->aact_state == val)
+		goto done;
+
+	if (val == BATT_AACT_ENABLED && batt_drv->aact_state != BATT_AACT_DISABLED) {
+		mutex_unlock(&batt_drv->aact_state_lock);
+		return -EINVAL;
+	}
+
+	dev_info(batt_drv->device, "AACT: aact_state: %d -> %d\n", batt_drv->aact_state, val);
+	batt_drv->aact_state = val;
+
+	ret = aact_update_chg_table(batt_drv);
+	if (ret < 0) {
+		struct gbms_chg_profile *profile = &batt_drv->chg_profile;
+		struct device_node *node = batt_drv->device->of_node;
+		int err;
+
+		/* reset AACT */
+		aact_reset(profile);
+
+		/* set state to unknown and init default charge table */
+		batt_drv->aact_state = BATT_AACT_UNKNOWN;
+		err = gbms_init_chg_profile(profile, node);
+		if (err == 0)
+			gbms_init_chg_table(profile, node, aacr_get_capacity(batt_drv));
+
+		mutex_unlock(&batt_drv->aact_state_lock);
+		return ret;
+	}
+
+done:
+	mutex_unlock(&batt_drv->aact_state_lock);
+	return count;
+}
+
+static ssize_t aact_state_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->aact_state);
+}
+
+static const DEVICE_ATTR_RW(aact_state);
+
+/* AACP ------------------------------------------------------------------- */
+
+static ssize_t aacp_version_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 = 0;
+
+	ret = kstrtoint(buf, 0, &value);
+	if (ret < 0)
+		return ret;
+
+	if (value >= 0)
+		batt_drv->aacp_version = value;
+
+	return count;
+}
+
+static ssize_t aacp_version_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->aacp_version);
+}
+
+static const DEVICE_ATTR_RW(aacp_version);
+
+/* AACC ------------------------------------------------------------------- */
+
+static ssize_t aacc_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->aacc);
+}
+
+static const DEVICE_ATTR_RO(aacc);
+
 /* Swelling  --------------------------------------------------------------- */
 
 static ssize_t swelling_data_show(struct device *dev,
@@ -9358,6 +9593,18 @@
 	ret = device_create_file(&batt_drv->psy->dev, &dev_attr_aafv_offset);
 	if (ret)
 		dev_err(&batt_drv->psy->dev, "Failed to create aafv offset\n");
+	/* aact */
+	ret = device_create_file(&batt_drv->psy->dev, &dev_attr_aact_state);
+	if (ret)
+		dev_err(&batt_drv->psy->dev, "Failed to create aact state\n");
+	/* aacp */
+	ret = device_create_file(&batt_drv->psy->dev, &dev_attr_aacp_version);
+	if (ret)
+		dev_err(&batt_drv->psy->dev, "Failed to create aacp version\n");
+	/* aacc */
+	ret = device_create_file(&batt_drv->psy->dev, &dev_attr_aacc);
+	if (ret)
+		dev_err(&batt_drv->psy->dev, "Failed to create aacc\n");
 
 	/* health and health index */
 	ret = device_create_file(&batt_drv->psy->dev, &dev_attr_swelling_data);
@@ -9478,16 +9725,16 @@
 	debugfs_create_u32("blf_collect_now", 0600, de, &batt_drv->blf_collect_now);
 
 	/* defender */
-	debugfs_create_u32("fake_capacity", 0600, de,
-			    &batt_drv->fake_capacity);
+	debugfs_create_u32("fake_capacity", 0600, de, &batt_drv->fake_capacity);
 
 	/* aacr test */
-	debugfs_create_u32("fake_aacr_cc", 0600, de,
-			    &batt_drv->fake_aacr_cc);
+	debugfs_create_u32("fake_aacr_cc", 0600, de, &batt_drv->fake_aacr_cc);
 
 	/* aafv test */
-	debugfs_create_u32("fake_aafv_cc", 0600, de,
-			    &batt_drv->fake_aafv_cc);
+	debugfs_create_u32("fake_aafv_cc", 0600, de, &batt_drv->fake_aafv_cc);
+
+	/* aact test */
+	debugfs_create_u32("fake_aact_cc", 0600, de, &batt_drv->fake_aact_cc);
 
 	/* health charging (adaptive charging) */
 	debugfs_create_file("chg_health_thr_soc", 0600, de, batt_drv,
@@ -11427,6 +11674,7 @@
 	mutex_init(&batt_drv->hda_tz_lock);
 	mutex_init(&batt_drv->aacr_state_lock);
 	mutex_init(&batt_drv->aafv_state_lock);
+	mutex_init(&batt_drv->aact_state_lock);
 
 	ret = of_property_read_u32(node, "google,batt-init-delay", &init_delay_ms);
 	if (ret < 0)
@@ -11975,6 +12223,12 @@
 	batt_drv->aafv_cliff_cycle = AAFV_CLIFF_CYCLE_DEFAULT;
 	batt_drv->aafv_cliff_offset = AAFV_CLIFF_OFFSET_DEFAULT;
 
+	/* AACT server side */
+	batt_drv->aact_state = BATT_AACT_DISABLED;
+
+	/* AACP default version */
+	batt_drv->aacp_version = 1;
+
 	/* create the sysfs node */
 	batt_init_fs(batt_drv);
 	batt_bpst_init_fs(batt_drv);
diff --git a/google_bms.c b/google_bms.c
index a2e13d0..328ea64 100644
--- a/google_bms.c
+++ b/google_bms.c
@@ -151,21 +151,33 @@
 	u32 ccm;
 	int vi, ti, ret;
 	const int cc_ua_step = profile->cc_ua_resolution;
+	int temp_nb_count = profile->temp_nb_limits - 1;
 	u32 cccm_array_size = (profile->temp_nb_limits - 1)
 			       * profile->volt_nb_limits;
 
 	profile->capacity_ma = capacity_ma;
 
-	ret = of_property_read_u32_array(node, "google,chg-cc-limits",
-					 profile->cccm_limits,
-					 cccm_array_size);
+	if (!profile->aact_init_profile) {
+		ret = of_property_read_u32_array(node, "google,chg-cc-limits",
+						 profile->cccm_limits,
+						 cccm_array_size);
+	} else {
+		temp_nb_count = (profile->temp_nb_limits - 1) * profile->aact_nb_limits;
+		cccm_array_size = (profile->temp_nb_limits - 1)
+				   * profile->volt_nb_limits
+				   * profile->aact_nb_limits;
+		ret = of_property_read_u32_array(node, "google,aact-cc-limits",
+						 profile->cccm_limits,
+						 cccm_array_size);
+	}
+
 	if (ret < 0)
 		pr_warn("unable to get default cccm_limits.\n");
 
 	/* chg-battery-capacity is in mAh, chg-cc-limits relative to 100 */
-	for (ti = 0; ti < profile->temp_nb_limits - 1; ti++) {
+	for (ti = 0; ti < temp_nb_count; ti++) {
 		for (vi = 0; vi < profile->volt_nb_limits; vi++) {
-			ccm = GBMS_CCCM_LIMITS(profile, ti, vi);
+			ccm = GBMS_CCCM_LIMITS_GET(profile, ti, vi);
 			ccm *= capacity_ma * 10;
 
 			/* round to the nearest resolution */
@@ -388,7 +400,6 @@
 }
 EXPORT_SYMBOL_GPL(gbms_read_aafv_limits);
 
-/* return the pct amount of capacity fade at cycles or negative if not enabled */
 int gbms_aafv_get_offset(const struct gbms_chg_profile *profile, const int cycles)
 {
 	int idx;
@@ -520,6 +531,8 @@
 		profile->volt_limits[vi] = profile->volt_limits[vi] /
 		    profile->fv_uv_resolution * profile->fv_uv_resolution;
 
+	/* reset AACT */
+	profile->aact_init_profile = false;
 	return 0;
 }
 EXPORT_SYMBOL_GPL(gbms_init_chg_profile_internal);
@@ -531,6 +544,126 @@
 }
 EXPORT_SYMBOL_GPL(gbms_free_chg_profile);
 
+static int gbms_read_aact_cccm_limits(struct gbms_chg_profile *profile,
+				      struct device_node *node)
+{
+	int ret;
+
+	profile->temp_nb_limits =
+	    of_property_count_elems_of_size(node, "google,aact-temp-limits",
+					    sizeof(u32));
+	if (profile->temp_nb_limits <= 0) {
+		ret = profile->temp_nb_limits;
+		gbms_err(profile, "cannot read aact-temp-limits, ret=%d\n", ret);
+		return -EINVAL;
+	}
+	if (profile->temp_nb_limits > GBMS_CHG_TEMP_NB_LIMITS_MAX) {
+		gbms_err(profile, "aact-temp-nb-limits exceeds driver max: %d\n",
+			 GBMS_CHG_TEMP_NB_LIMITS_MAX);
+		return -EINVAL;
+	}
+	ret = of_property_read_u32_array(node, "google,aact-temp-limits",
+					 (u32 *)profile->temp_limits,
+					 profile->temp_nb_limits);
+	if (ret < 0) {
+		gbms_err(profile, "cannot read aact-temp-limits table, ret=%d\n", ret);
+		return ret;
+	}
+
+	profile->volt_nb_limits =
+	    of_property_count_elems_of_size(node, "google,aact-cv-limits",
+					    sizeof(u32));
+	if (profile->volt_nb_limits <= 0) {
+		ret = profile->volt_nb_limits;
+		gbms_err(profile, "cannot read aact-cv-limits, ret=%d\n", ret);
+		return -EINVAL;
+	}
+	if (profile->volt_nb_limits > GBMS_CHG_VOLT_NB_LIMITS_MAX) {
+		gbms_err(profile, "aact-cv-nb-limits exceeds driver max: %d\n",
+			 GBMS_CHG_VOLT_NB_LIMITS_MAX);
+		return -EINVAL;
+	}
+	ret = of_property_read_u32_array(node, "google,aact-cv-limits",
+					 (u32 *)profile->volt_limits,
+					 profile->volt_nb_limits);
+	if (ret < 0) {
+		gbms_err(profile, "cannot read aact-cv-limits table, ret=%d\n", ret);
+		return ret;
+	}
+
+	profile->aact_nb_limits =
+	    of_property_count_elems_of_size(node, "google,chg-aact-ecc",
+					    sizeof(u32));
+	if (profile->aact_nb_limits <= 0) {
+		ret = profile->aact_nb_limits;
+		gbms_err(profile, "cannot read chg-aact-ecc, ret=%d\n", ret);
+		return -EINVAL;
+	}
+	if (profile->aact_nb_limits > GBMS_AACT_NB_LIMITS_MAX) {
+		gbms_err(profile, "chg-aact-ecc exceeds driver max: %d\n",
+			 GBMS_AACT_NB_LIMITS_MAX);
+		return -EINVAL;
+	}
+	ret = of_property_read_u32_array(node, "google,chg-aact-ecc",
+					 (u32 *)profile->aact_limits,
+					 profile->aact_nb_limits);
+	if (ret < 0) {
+		gbms_err(profile, "cannot read aact-cv-limits table, ret=%d\n", ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+int gbms_init_aact_profile_internal(struct gbms_chg_profile *profile,
+				    struct device_node *node,
+				    const char *owner_name)
+{
+	int ret;
+	u32 cccm_array_size, mem_size;
+
+	profile->owner_name = owner_name;
+
+	ret = gbms_read_aact_cccm_limits(profile, node);
+	if (ret < 0)
+		return ret;
+
+	cccm_array_size = (profile->temp_nb_limits - 1)
+			  * profile->volt_nb_limits
+			  * profile->aact_nb_limits;
+	mem_size = sizeof(s32) * cccm_array_size;
+
+	profile->cccm_limits = kzalloc(mem_size, GFP_KERNEL);
+	if (!profile->cccm_limits)
+		return -ENOMEM;
+
+	/* load C rates into profile->cccm_limits */
+	ret = of_property_read_u32_array(node, "google,aact-cc-limits",
+					 profile->cccm_limits,
+					 cccm_array_size);
+	if (ret < 0) {
+		gbms_err(profile, "cannot read aact-cc-limits table, ret=%d\n", ret);
+		kfree(profile->cccm_limits);
+		profile->cccm_limits = 0;
+		return -EINVAL;
+	}
+
+	profile->aact_init_profile = true;
+	return 0;
+}
+EXPORT_SYMBOL_GPL(gbms_init_aact_profile_internal);
+
+int gbms_aact_get_index(const struct gbms_chg_profile *profile, const int cycles)
+{
+	int idx = 0;
+
+	while (idx <= profile->aact_nb_limits - 1 && cycles >= profile->aact_limits[idx])
+		idx++;
+
+	return idx - 1;
+}
+EXPORT_SYMBOL_GPL(gbms_aact_get_index);
+
 /* NOTE: I should really pass the scale */
 void gbms_dump_raw_profile(char *buff, size_t len, const struct gbms_chg_profile *profile, int scale)
 {
@@ -614,16 +747,15 @@
 {
 	int cc_max;
 	int vbatt_idx = 0;
-	const int vbatt_last_idx = gbms_msc_get_last_voltage_idx(profile, temp_idx);
 
 	if (!profile)
 		return 0;
 
-	while (vbatt_idx < vbatt_last_idx &&
+	while (vbatt_idx < profile->volt_nb_limits - 1 &&
 	       vbatt > profile->volt_limits[vbatt_idx])
 		vbatt_idx++;
 
-	if (vbatt_idx != vbatt_last_idx) {
+	if (vbatt_idx != profile->volt_nb_limits - 1) {
 		const int vt = profile->volt_limits[vbatt_idx];
 		const int headr = profile->fv_uv_resolution * 3;
 
@@ -635,7 +767,7 @@
 		return vbatt_idx;
 
 	cc_max = GBMS_CCCM_LIMITS(profile, temp_idx, vbatt_idx);
-	while (vbatt_idx < vbatt_last_idx - 1 &&
+	while (vbatt_idx < profile->volt_nb_limits - 2 &&
 		cc_max == GBMS_CCCM_LIMITS(profile, temp_idx, vbatt_idx + 1))
 			vbatt_idx++;
 
diff --git a/google_bms.h b/google_bms.h
index 58b71ad..8947fff 100644
--- a/google_bms.h
+++ b/google_bms.h
@@ -37,6 +37,7 @@
 #define GBMS_CHG_TOPOFF_NB_LIMITS_MAX 6
 #define GBMS_AACR_DATA_MAX 10
 #define GBMS_AAFV_DATA_MAX 16
+#define GBMS_AACT_NB_LIMITS_MAX 10
 
 struct gbms_chg_profile {
 	const char *owner_name;
@@ -78,6 +79,12 @@
 	u32 aafv_nb_limits;
 	u32 aafv_offset;
 
+	/* AACT feature */
+	int aact_nb_limits;
+	s32 aact_limits[GBMS_AACT_NB_LIMITS_MAX];
+	int aact_idx;
+	bool aact_init_profile;
+
 	bool debug_chg_profile;
 	bool enable_switch_chg_profile;
 };
@@ -402,6 +409,9 @@
 	uint16_t csi_aggregate_status;
 	uint16_t csi_aggregate_type;
 
+	int aacp_version;
+	int aacc;
+
 	/* health based charging */
 	struct batt_chg_health		ce_health;	/* updated on close */
 	struct gbms_ce_tier_stats	health_stats;	/* updated in HC */
@@ -422,9 +432,16 @@
 #define GBMS_CCCM_LIMITS_SET(profile, ti, vi) \
 	profile->cccm_limits[((ti) * profile->volt_nb_limits) + (vi)]
 
-#define GBMS_CCCM_LIMITS(profile, ti, vi) \
+#define GBMS_CCCM_LIMITS_GET(profile, ti, vi) \
 	(((ti) >= 0 && (vi) >= 0) ? profile->cccm_limits[((ti) * profile->volt_nb_limits) + (vi)] : 0)
 
+#define GBMS_AACT_IDX(profile) \
+	(profile->aact_idx * (profile->temp_nb_limits - 1))
+
+#define GBMS_CCCM_LIMITS(profile, ti, vi) \
+	(((ti) >= 0 && (vi) >= 0) ? \
+	profile->cccm_limits[((ti + GBMS_AACT_IDX(profile)) * profile->volt_nb_limits) + (vi)] : 0)
+
 /* newgen charging */
 #define GBMS_CS_FLAG_BUCK_EN		BIT(0)
 #define GBMS_CS_FLAG_DONE		BIT(1)
@@ -452,6 +469,11 @@
 			  struct device_node *node, const char *owner_name);
 #define gbms_init_chg_profile(p, n) \
 	gbms_init_chg_profile_internal(p, n, KBUILD_MODNAME)
+int gbms_init_aact_profile_internal(struct gbms_chg_profile *profile,
+			  struct device_node *node, const char *owner_name);
+#define gbms_init_aact_profile(p, n) \
+	gbms_init_aact_profile_internal(p, n, KBUILD_MODNAME)
+int gbms_aact_get_index(const struct gbms_chg_profile *profile, const int cycles);
 
 void gbms_init_chg_table(struct gbms_chg_profile *profile,
 			 struct device_node *node, u32 capacity);
diff --git a/google_dual_batt_gauge.c b/google_dual_batt_gauge.c
index 73eb7b0..ce01e3f 100644
--- a/google_dual_batt_gauge.c
+++ b/google_dual_batt_gauge.c
@@ -828,6 +828,7 @@
 	case GBMS_PROP_CAPACITY_FADE_RATE:
 	case GBMS_PROP_CAPACITY_FADE_RATE_FCR:
 	case GBMS_PROP_BATT_ID:
+	case GBMS_PROP_AAFV:
 		val->prop.intval = fg_1.prop.intval;
 		break;
 	case GBMS_PROP_RECAL_FG:
@@ -894,6 +895,18 @@
 	case GBMS_PROP_RECAL_FG:
 		/* TODO: under porting */
 		break;
+	case GBMS_PROP_AAFV:
+		if (dual_fg_drv->first_fg_psy) {
+			ret = GPSY_SET_PROP(dual_fg_drv->first_fg_psy, psp, val->prop.intval);
+			if (ret < 0)
+				pr_err("Cannot set aafv to the first FG, ret=%d\n", ret);
+		}
+		if (dual_fg_drv->second_fg_psy) {
+			ret = GPSY_SET_PROP(dual_fg_drv->second_fg_psy, psp, val->prop.intval);
+			if (ret < 0)
+				pr_err("Cannot set aafv to the second FG, ret=%d\n", ret);
+		}
+		break;
 	default:
 		pr_debug("%s: route to gdbatt_set_property, psp:%d\n", __func__, psp);
 		return -ENODATA;
@@ -912,6 +925,7 @@
 {
 	switch (psp) {
 	case GBMS_PROP_BATT_CE_CTRL:
+	case GBMS_PROP_AAFV:
 		return 1;
 	default:
 		break;
@@ -966,7 +980,7 @@
 	/* chg-battery-capacity is in mAh, chg-cc-limits relative to 100 */
 	for (ti = 0; ti < pack_profile->temp_nb_limits - 1; ti++) {
 		for (vi = 0; vi < pack_profile->volt_nb_limits; vi++) {
-			ccm = GBMS_CCCM_LIMITS(pack_profile, ti, vi);
+			ccm = GBMS_CCCM_LIMITS_GET(pack_profile, ti, vi);
 			ccm *= capacity_ma * 10;
 
 			GBMS_CCCM_LIMITS_SET(pack_profile, ti, vi) = ccm;
diff --git a/max1720x_battery.c b/max1720x_battery.c
index 4833bad..8aef260 100644
--- a/max1720x_battery.c
+++ b/max1720x_battery.c
@@ -243,6 +243,9 @@
 
 	/* buffer for recording learning history */
 	struct maxfg_capture_buf cb_lh;
+
+	/* AAFV: Aged Adjusted Float Voltage */
+	int aafv;
 };
 
 #define MAX1720_EMPTY_VOLTAGE(profile, temp, cycle) \
@@ -2699,6 +2702,9 @@
 		if (chip->gauge_type == MAX_M5_GAUGE_TYPE)
 			val->prop.intval = max_m5_recal_state(chip->model_data);
 		break;
+	case GBMS_PROP_AAFV:
+		val->prop.intval = chip->aafv;
+		break;
 	default:
 		pr_debug("%s: route to max1720x_get_property, psp:%d\n", __func__, psp);
 		err = -ENODATA;
@@ -2781,6 +2787,9 @@
 	case GBMS_PROP_RECAL_FG:
 		max1720x_set_recalibration(chip, val->prop.intval);
 		break;
+	case GBMS_PROP_AAFV:
+		chip->aafv = val->prop.intval;
+		break;
 	default:
 		pr_debug("%s: route to max1720x_set_property, psp:%d\n", __func__, psp);
 		return -ENODATA;
@@ -2798,6 +2807,7 @@
 	switch (psp) {
 	case GBMS_PROP_BATT_CE_CTRL:
 	case GBMS_PROP_HEALTH_ACT_IMPEDANCE:
+	case GBMS_PROP_AAFV:
 		return 1;
 	default:
 		break;
diff --git a/max77779_fg.c b/max77779_fg.c
index e3f26b0..a05aecf 100644
--- a/max77779_fg.c
+++ b/max77779_fg.c
@@ -1673,6 +1673,9 @@
 	case GBMS_PROP_RECAL_FG:
 		/* TODO: under porting */
 		break;
+	case GBMS_PROP_AAFV:
+		val->prop.intval = chip->aafv;
+		break;
 	default:
 		pr_debug("%s: route to max77779_fg_get_property, psp:%d\n", __func__, psp);
 		err = -ENODATA;
@@ -1745,6 +1748,9 @@
 	case GBMS_PROP_RECAL_FG:
 		/* TODO: under porting */
 		break;
+	case GBMS_PROP_AAFV:
+		chip->aafv = val->prop.intval;
+		break;
 	default:
 		pr_debug("%s: route to max77779_fg_set_property, psp:%d\n", __func__, psp);
 		return -ENODATA;
@@ -1762,6 +1768,7 @@
 	switch (psp) {
 	case GBMS_PROP_BATT_CE_CTRL:
 	case GBMS_PROP_HEALTH_ACT_IMPEDANCE:
+	case GBMS_PROP_AAFV:
 		return 1;
 	default:
 		break;
diff --git a/max77779_fg.h b/max77779_fg.h
index 5e3d4e2..bc79522 100644
--- a/max77779_fg.h
+++ b/max77779_fg.h
@@ -194,6 +194,9 @@
 
 	/* mutex lock to access FG USR reg */
 	struct mutex usr_lock;
+
+	/* AAFV: Aged Adjusted Float Voltage */
+	int aafv;
 };
 
 /** ------------------------------------------------------------------------ */