diff options
author | Robin Peng <robinpeng@google.com> | 2022-11-10 11:30:08 +0000 |
---|---|---|
committer | Robin Peng <robinpeng@google.com> | 2022-11-10 11:30:08 +0000 |
commit | 2437e5ee64944692e67787ebe88c7a784c3536ef (patch) | |
tree | 11065270c6b08c9824c09b172ff37230ef4b64bb | |
parent | 017b15a47bd025e83510af236cb07a8a4490a789 (diff) | |
parent | 28c0ac0f85c1ffa9eaa032be17c02cd2afdce059 (diff) | |
download | bms-2437e5ee64944692e67787ebe88c7a784c3536ef.tar.gz |
Merge android13-gs-pixel-5.15 into android14-gs-pixel-5.15
Bug: 236259002
Change-Id: Id6c4888abbc79799379e51b360e0de47b2ad5c71
Signed-off-by: Robin Peng <robinpeng@google.com>
-rw-r--r-- | gbms_power_supply.h | 1 | ||||
-rw-r--r-- | gbms_storage.c | 2 | ||||
-rw-r--r-- | gbms_storage.h | 2 | ||||
-rw-r--r-- | google_battery.c | 732 | ||||
-rw-r--r-- | google_bms.c | 203 | ||||
-rw-r--r-- | google_bms.h | 51 | ||||
-rw-r--r-- | google_charger.c | 233 | ||||
-rw-r--r-- | google_cpm.c | 105 | ||||
-rw-r--r-- | google_dock.c | 215 | ||||
-rw-r--r-- | google_dual_batt_gauge.c | 51 | ||||
-rw-r--r-- | google_eeprom.c | 16 | ||||
-rw-r--r-- | max1720x_battery.c | 46 | ||||
-rw-r--r-- | p9221_charger.c | 441 | ||||
-rw-r--r-- | p9221_charger.h | 70 | ||||
-rw-r--r-- | p9221_chip.c | 149 | ||||
-rw-r--r-- | pca9468_charger.c | 54 | ||||
-rw-r--r-- | pca9468_charger.h | 1 | ||||
-rw-r--r-- | pca9468_gbms_pps.c | 4 | ||||
-rw-r--r-- | wc68_driver.c | 51 | ||||
-rw-r--r-- | wc68_regs.h | 1 |
20 files changed, 1750 insertions, 678 deletions
diff --git a/gbms_power_supply.h b/gbms_power_supply.h index 195a7ce..901e786 100644 --- a/gbms_power_supply.h +++ b/gbms_power_supply.h @@ -88,6 +88,7 @@ enum gbms_property { GBMS_PROP_BATTERY_AGE, /* GBMS time in field */ GBMS_PROP_CAPACITY_FADE_RATE, /* GBMS capaciy fade rate */ GBMS_PROP_CHARGE_FULL_ESTIMATE, /* GBMS google_capacity */ + GBMS_PROP_POGO_VOUT_ENABLED, /* GBMS gogo vout enabled */ }; union gbms_propval { diff --git a/gbms_storage.c b/gbms_storage.c index 940889f..e8a0cd6 100644 --- a/gbms_storage.c +++ b/gbms_storage.c @@ -49,7 +49,7 @@ struct gbms_cache_entry { #define GBMS_PROVIDER_NAME_MAX 32 -#define GBMS_PROVIDERS_MAX 4 +#define GBMS_PROVIDERS_MAX 5 static spinlock_t providers_lock; static bool gbms_storage_init_done; diff --git a/gbms_storage.h b/gbms_storage.h index a536d37..cff6114 100644 --- a/gbms_storage.h +++ b/gbms_storage.h @@ -59,6 +59,7 @@ typedef uint32_t gbms_tag_t; 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,6 +81,7 @@ enum gbms_tags { 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 */ diff --git a/google_battery.c b/google_battery.c index f9e8436..d5ce3d6 100644 --- a/google_battery.c +++ b/google_battery.c @@ -294,6 +294,15 @@ struct swelling_data { ktime_t last_update; }; + +struct bhi_weight bhi_w[] = { + [BHI_ALGO_ACHI] = {100, 0, 0}, + [BHI_ALGO_ACHI_B] = {100, 0, 0}, + [BHI_ALGO_ACHI_RAVG] = {95, 5, 0}, + [BHI_ALGO_ACHI_RAVG_B] = {95, 5, 0}, + [BHI_ALGO_MIX_N_MATCH] = {90, 10, 5}, +}; + struct bhi_data { /* context */ @@ -331,6 +340,11 @@ struct health_data int bhi_cap_index; int bhi_imp_index; int bhi_sd_index; + /* debug health metrics */ + int bhi_debug_cap_index; + int bhi_debug_imp_index; + int bhi_debug_sd_index; + int bhi_debug_health_index; /* current battery state */ struct bhi_data bhi_data; @@ -530,13 +544,13 @@ struct batt_drv { /* irdrop for DC */ bool dc_irdrop; + + /* shutdown flag */ + int boot_to_os_attempts; }; static int gbatt_get_temp(const struct batt_drv *batt_drv, int *temp); -static int batt_chg_tier_stats_cstr(char *buff, int size, - const struct gbms_ce_tier_stats *tier_stat, - bool verbose); static int gbatt_get_capacity(struct batt_drv *batt_drv); static inline void batt_update_cycle_count(struct batt_drv *batt_drv) @@ -1332,6 +1346,8 @@ static void batt_rl_update_status(struct batt_drv *batt_drv) ssoc_state->bd_trickle_full = false; ssoc_state->bd_trickle_eoc = false; } + + dump_ssoc_state(&batt_drv->ssoc_state, batt_drv->ssoc_log); } /* ------------------------------------------------------------------------- */ @@ -1432,14 +1448,6 @@ done: } /* ------------------------------------------------------------------------- */ - -static void cev_ts_init(struct gbms_ce_tier_stats *stats, int8_t idx) -{ - stats->vtier_idx = idx; - stats->temp_idx = -1; - stats->soc_in = -1; -} - /* CEV = Charging EVent */ static void cev_stats_init(struct gbms_charging_event *ce_data, const struct gbms_chg_profile *profile) @@ -1458,18 +1466,18 @@ static void cev_stats_init(struct gbms_charging_event *ce_data, ce_data->last_soc = -1; for (i = 0; i < GBMS_STATS_TIER_COUNT ; i++) - cev_ts_init(&ce_data->tier_stats[i], i); + gbms_tier_stats_init(&ce_data->tier_stats[i], i); /* batt_chg_health_stats_close() will fix this */ - cev_ts_init(&ce_data->health_stats, GBMS_STATS_AC_TI_INVALID); - cev_ts_init(&ce_data->health_pause_stats, GBMS_STATS_AC_TI_PAUSE); - cev_ts_init(&ce_data->health_dryrun_stats, GBMS_STATS_AC_TI_V2_PREDICT); + gbms_tier_stats_init(&ce_data->health_stats, GBMS_STATS_AC_TI_INVALID); + gbms_tier_stats_init(&ce_data->health_pause_stats, GBMS_STATS_AC_TI_PAUSE); + gbms_tier_stats_init(&ce_data->health_dryrun_stats, GBMS_STATS_AC_TI_V2_PREDICT); - cev_ts_init(&ce_data->full_charge_stats, GBMS_STATS_AC_TI_FULL_CHARGE); - cev_ts_init(&ce_data->high_soc_stats, GBMS_STATS_AC_TI_HIGH_SOC); - cev_ts_init(&ce_data->overheat_stats, GBMS_STATS_BD_TI_OVERHEAT_TEMP); - cev_ts_init(&ce_data->cc_lvl_stats, GBMS_STATS_BD_TI_CUSTOM_LEVELS); - cev_ts_init(&ce_data->trickle_stats, GBMS_STATS_BD_TI_TRICKLE_CLEARED); + gbms_tier_stats_init(&ce_data->full_charge_stats, GBMS_STATS_AC_TI_FULL_CHARGE); + gbms_tier_stats_init(&ce_data->high_soc_stats, GBMS_STATS_AC_TI_HIGH_SOC); + gbms_tier_stats_init(&ce_data->overheat_stats, GBMS_STATS_BD_TI_OVERHEAT_TEMP); + gbms_tier_stats_init(&ce_data->cc_lvl_stats, GBMS_STATS_BD_TI_CUSTOM_LEVELS); + gbms_tier_stats_init(&ce_data->trickle_stats, GBMS_STATS_BD_TI_TRICKLE_CLEARED); } static void batt_chg_stats_start(struct batt_drv *batt_drv) @@ -1514,18 +1522,6 @@ static bool batt_chg_stats_qual(const struct batt_drv *batt_drv) } /* call holding stats_lock */ -static void batt_chg_stats_tier(struct gbms_ce_tier_stats *tier, - int msc_state, - ktime_t elap) -{ - if (msc_state < 0 || msc_state >= MSC_STATES_COUNT) - return; - - tier->msc_cnt[msc_state] += 1; - tier->msc_elap[msc_state] += elap; -} - -/* call holding stats_lock */ static void batt_chg_stats_soc_update(struct gbms_charging_event *ce_data, qnum_t soc, ktime_t elap, int tier_index, int cc) @@ -1552,90 +1548,6 @@ static void batt_chg_stats_soc_update(struct gbms_charging_event *ce_data, ce_data->last_soc = index; } -static void batt_chg_stats_update_tier(const struct batt_drv *const batt_drv, - int temp_idx, int ibatt_ma, int temp, - ktime_t elap, int cc, - struct gbms_ce_tier_stats *tier) -{ - const uint16_t icl_settled = batt_drv->chg_state.f.icl; - - /* - * book time to previous msc_state for this tier, there is an - * interesting wrinkle here since some tiers (health, full, etc) - * might be entered and exited multiple times. - */ - batt_chg_stats_tier(tier, batt_drv->msc_state, elap); - - if (tier->soc_in == -1) { - int soc_in; - - soc_in = GPSY_GET_PROP(batt_drv->fg_psy, - GBMS_PROP_CAPACITY_RAW); - if (soc_in < 0) { - pr_info("MSC_STAT cannot read soc_in=%d\n", soc_in); - return; - } - - tier->temp_idx = temp_idx; - - tier->temp_in = temp; - tier->temp_min = temp; - tier->temp_max = temp; - - tier->ibatt_min = ibatt_ma; - tier->ibatt_max = ibatt_ma; - - tier->icl_min = icl_settled; - tier->icl_max = icl_settled; - - tier->soc_in = soc_in; - tier->cc_in = cc; - tier->cc_total = 0; - } else { - const u8 flags = batt_drv->chg_state.f.flags; - - /* crossed temperature tier */ - if (temp_idx != tier->temp_idx) - tier->temp_idx = -1; - - if (flags & GBMS_CS_FLAG_CC) { - tier->time_fast += elap; - } else if (flags & GBMS_CS_FLAG_CV) { - tier->time_taper += elap; - } else { - tier->time_other += elap; - } - - /* - * averages: temp < 100. icl_settled < 3000, sum(ibatt) - * is bound to battery capacity, elap in seconds, sums - * are stored in an s64. For icl_settled I need a tier - * to last for more than ~97M years. - */ - if (temp < tier->temp_min) - tier->temp_min = temp; - if (temp > tier->temp_max) - tier->temp_max = temp; - tier->temp_sum += temp * elap; - - if (icl_settled < tier->icl_min) - tier->icl_min = icl_settled; - if (icl_settled > tier->icl_max) - tier->icl_max = icl_settled; - tier->icl_sum += icl_settled * elap; - - if (ibatt_ma < tier->ibatt_min) - tier->ibatt_min = ibatt_ma; - if (ibatt_ma > tier->ibatt_max) - tier->ibatt_max = ibatt_ma; - tier->ibatt_sum += ibatt_ma * elap; - - tier->cc_total = cc - tier->cc_in; - } - - tier->sample_count += 1; -} - /* call holding stats_lock */ static void batt_chg_stats_update(struct batt_drv *batt_drv, int temp_idx, int tier_idx, int ibatt_ma, int temp, @@ -1645,7 +1557,7 @@ static void batt_chg_stats_update(struct batt_drv *batt_drv, int temp_idx, const int msc_state = batt_drv->msc_state; /* last msc_state */ struct gbms_charging_event *ce_data = &batt_drv->ce_data; struct gbms_ce_tier_stats *tier = NULL; - int cc; + int cc, soc_in; if (elap == 0) return; @@ -1658,26 +1570,32 @@ static void batt_chg_stats_update(struct batt_drv *batt_drv, int temp_idx, } cc = cc / 1000; + soc_in = GPSY_GET_PROP(batt_drv->fg_psy, GBMS_PROP_CAPACITY_RAW); + if (soc_in < 0) { + pr_info("MSC_STAT cannot read soc_in=%d\n", soc_in); + return; + } + /* Note: To log new voltage tiers, add to list in go/pixel-vtier-defs */ /* --- Log tiers in PARALLEL below --- */ if (soc_real >= SSOC_HIGH_SOC) - batt_chg_stats_update_tier(batt_drv, temp_idx, ibatt_ma, temp, - elap, cc, - &ce_data->high_soc_stats); + gbms_stats_update_tier(temp_idx, ibatt_ma, temp, elap, cc, + &batt_drv->chg_state, msc_state, soc_in, + &ce_data->high_soc_stats); if (batt_drv->chg_health.dry_run_deadline > 0) - batt_chg_stats_update_tier(batt_drv, temp_idx, ibatt_ma, temp, - elap, cc, - &ce_data->health_dryrun_stats); + gbms_stats_update_tier(temp_idx, ibatt_ma, temp, elap, cc, + &batt_drv->chg_state, msc_state, soc_in, + &ce_data->health_dryrun_stats); /* --- Log tiers in SERIES below --- */ if (batt_drv->batt_full) { /* Override regular charge tiers when fully charged */ - batt_chg_stats_update_tier(batt_drv, temp_idx, ibatt_ma, - temp, elap, cc, - &ce_data->full_charge_stats); + gbms_stats_update_tier(temp_idx, ibatt_ma, temp, elap, cc, + &batt_drv->chg_state, msc_state, soc_in, + &ce_data->full_charge_stats); } else if (msc_state == MSC_HEALTH_PAUSE) { @@ -1685,9 +1603,9 @@ static void batt_chg_stats_update(struct batt_drv *batt_drv, int temp_idx, * We log the pause tier in different AC tier groups so that we * can capture pause time separately. */ - batt_chg_stats_update_tier(batt_drv, temp_idx, ibatt_ma, temp, - elap, cc, - &ce_data->health_pause_stats); + gbms_stats_update_tier(temp_idx, ibatt_ma, temp, elap, cc, + &batt_drv->chg_state, msc_state, soc_in, + &ce_data->health_pause_stats); } else if (msc_state == MSC_HEALTH || msc_state == MSC_HEALTH_ALWAYS_ON) { /* @@ -1703,9 +1621,9 @@ static void batt_chg_stats_update(struct batt_drv *batt_drv, int temp_idx, */ /* tier used for TTF during HC, check msc_logic_health() */ - batt_chg_stats_update_tier(batt_drv, temp_idx, ibatt_ma, - temp, elap, cc, - &ce_data->health_stats); + gbms_stats_update_tier(temp_idx, ibatt_ma, temp, elap, cc, + &batt_drv->chg_state, msc_state, soc_in, + &ce_data->health_stats); } else { const qnum_t soc = ssoc_get_capacity_raw(&batt_drv->ssoc_state); @@ -1727,17 +1645,17 @@ static void batt_chg_stats_update(struct batt_drv *batt_drv, int temp_idx, /* batt_drv->batt_health is protected with chg_lock, */ if (batt_drv->batt_health == POWER_SUPPLY_HEALTH_OVERHEAT) { - batt_chg_stats_update_tier(batt_drv, temp_idx, ibatt_ma, temp, - elap, cc, - &ce_data->overheat_stats); + gbms_stats_update_tier(temp_idx, ibatt_ma, temp, elap, cc, + &batt_drv->chg_state, msc_state, soc_in, + &ce_data->overheat_stats); tier = NULL; } /* custom charge levels (DWELL-DEFEND or RETAIL) */ if (batt_drv->chg_state.f.flags & GBMS_CS_FLAG_CCLVL) { - batt_chg_stats_update_tier(batt_drv, temp_idx, ibatt_ma, temp, - elap, cc, - &ce_data->cc_lvl_stats); + gbms_stats_update_tier(temp_idx, ibatt_ma, temp, elap, cc, + &batt_drv->chg_state, msc_state, soc_in, + &ce_data->cc_lvl_stats); tier = NULL; } @@ -1748,8 +1666,8 @@ static void batt_chg_stats_update(struct batt_drv *batt_drv, int temp_idx, if (!tier) return; - batt_chg_stats_update_tier(batt_drv, temp_idx, ibatt_ma, temp, - elap, cc, tier); + gbms_stats_update_tier(temp_idx, ibatt_ma, temp, elap, cc, + &batt_drv->chg_state, msc_state, soc_in, tier); } static int batt_chg_health_vti(const struct batt_chg_health *chg_health) @@ -1919,31 +1837,6 @@ static int batt_chg_stats_soc_next(const struct gbms_charging_event *ce_data, return soc_next; } -static void bat_log_cstr_handler(struct logbuffer *log, char *buf, int len) -{ - int i, j = 0; - char tmp[LOG_BUFFER_ENTRY_SIZE]; - - buf[len] = '\n'; - for (i = 0; i <= len; i++) { - if (buf[i] == '\n') { - tmp[j] = '\0'; - /* skip first blank line */ - if (i != 0) - logbuffer_log(log, "%s", tmp); - j = 0; - } else if (j >= LOG_BUFFER_ENTRY_SIZE - 1) { - tmp[j] = '\0'; - logbuffer_log(log, "%s", tmp); - i--; - j = 0; - } else { - tmp[j] = buf[i]; - j++; - } - } -} - static void bat_log_chg_stats(struct logbuffer *log, const struct gbms_charging_event *ce_data) { @@ -1982,15 +1875,15 @@ static void bat_log_chg_stats(struct logbuffer *log, if (!elap) continue; - len = batt_chg_tier_stats_cstr(buff, sizeof(buff), + len = gbms_tier_stats_cstr(buff, sizeof(buff), &ce_data->tier_stats[i], true); - bat_log_cstr_handler(log, buff, len); + gbms_log_cstr_handler(log, buff, len); if (soc_next) { len = ttf_soc_cstr(buff, sizeof(buff), &ce_data->soc_stats, soc_in, soc_next); - bat_log_cstr_handler(log, buff, len); + gbms_log_cstr_handler(log, buff, len); } } } @@ -2015,75 +1908,6 @@ static void batt_chg_stats_pub(struct batt_drv *batt_drv, char *reason, mutex_unlock(&batt_drv->stats_lock); } -/* Log only when elap != 0 add a special meaning for health status */ -static int batt_chg_tier_stats_cstr(char *buff, int size, - const struct gbms_ce_tier_stats *tier_stat, - bool verbose) -{ - const int soc_in = tier_stat->soc_in >> 8; - const long elap = tier_stat->time_fast + tier_stat->time_taper + - tier_stat->time_other; - const static char *codes[] = {"n", "s", "d", "l", "v", "vo", "p", "f", - "t", "dl", "st", "tc", "r", "w", "rs", - "n", "ny", "h", "hp", "ha"}; - long temp_avg, ibatt_avg, icl_avg; - int j, len = 0; - - if (elap) { - temp_avg = div_u64(tier_stat->temp_sum, elap); - ibatt_avg = div_u64(tier_stat->ibatt_sum, elap); - icl_avg = div_u64(tier_stat->icl_sum, elap); - } else { - temp_avg = 0; - ibatt_avg = 0; - icl_avg = 0; - } - - len += scnprintf(&buff[len], size - len, "\n%d%c ", - tier_stat->vtier_idx, - (verbose) ? ':' : ','); - - len += scnprintf(&buff[len], size - len, - "%d.%d,%d,%d, %d,%d,%d, %d,%ld,%d, %d,%ld,%d, %d,%ld,%d", - soc_in, - tier_stat->soc_in & 0xff, - tier_stat->cc_in, - tier_stat->temp_in, - tier_stat->time_fast, - tier_stat->time_taper, - tier_stat->time_other, - tier_stat->temp_min, - temp_avg, - tier_stat->temp_max, - tier_stat->ibatt_min, - ibatt_avg, - tier_stat->ibatt_max, - tier_stat->icl_min, - icl_avg, - tier_stat->icl_max); - - if (!verbose || !elap) - return len; - - /* time spent in every multi step charging state */ - len += scnprintf(&buff[len], size - len, "\n%d:", - tier_stat->vtier_idx); - - for (j = 0; j < MSC_STATES_COUNT; j++) - len += scnprintf(&buff[len], size - len, " %s=%d", - codes[j], tier_stat->msc_elap[j]); - - /* count spent in each step charging state */ - len += scnprintf(&buff[len], size - len, "\n%d:", - tier_stat->vtier_idx); - - for (j = 0; j < MSC_STATES_COUNT; j++) - len += scnprintf(&buff[len], size - len, " %s=%d", - codes[j], tier_stat->msc_cnt[j]); - - return len; -} - /* health_stats->tier_index is set on stats_close() */ static int batt_health_stats_cstr(char *buff, int size, const struct gbms_charging_event *ce_data, @@ -2102,15 +1926,14 @@ static int batt_health_stats_cstr(char *buff, int size, if (vti == GBMS_STATS_AC_TI_INVALID) return len; - len += batt_chg_tier_stats_cstr(&buff[len], size - len, - health_stats, - verbose); + len += gbms_tier_stats_cstr(&buff[len], size - len, + health_stats, verbose); /* Only add pause tier logging if there is pause time */ if (ce_data->health_pause_stats.soc_in != -1) - len += batt_chg_tier_stats_cstr(&buff[len], size - len, - &ce_data->health_pause_stats, - verbose); + len += gbms_tier_stats_cstr(&buff[len], size - len, + &ce_data->health_pause_stats, + verbose); return len; } @@ -2165,9 +1988,9 @@ static int batt_chg_stats_cstr(char *buff, int size, if (!elap) continue; - len += batt_chg_tier_stats_cstr(&buff[len], size - len, - &ce_data->tier_stats[i], - verbose); + len += gbms_tier_stats_cstr(&buff[len], size - len, + &ce_data->tier_stats[i], + verbose); if (soc_next) len += ttf_soc_cstr(&buff[len], size - len, @@ -2177,37 +2000,37 @@ static int batt_chg_stats_cstr(char *buff, int size, /* Does not currently check MSC_HEALTH */ if (ce_data->health_dryrun_stats.soc_in != -1) - len += batt_chg_tier_stats_cstr(&buff[len], size - len, - &ce_data->health_dryrun_stats, - verbose); + len += gbms_tier_stats_cstr(&buff[len], size - len, + &ce_data->health_dryrun_stats, + verbose); if (ce_data->full_charge_stats.soc_in != -1) - len += batt_chg_tier_stats_cstr(&buff[len], size - len, - &ce_data->full_charge_stats, - verbose); + len += gbms_tier_stats_cstr(&buff[len], size - len, + &ce_data->full_charge_stats, + verbose); if (ce_data->high_soc_stats.soc_in != -1) - len += batt_chg_tier_stats_cstr(&buff[len], size - len, - &ce_data->high_soc_stats, - verbose); + len += gbms_tier_stats_cstr(&buff[len], size - len, + &ce_data->high_soc_stats, + verbose); if (ce_data->overheat_stats.soc_in != -1) - len += batt_chg_tier_stats_cstr(&buff[len], size - len, - &ce_data->overheat_stats, - verbose); + len += gbms_tier_stats_cstr(&buff[len], size - len, + &ce_data->overheat_stats, + verbose); if (ce_data->cc_lvl_stats.soc_in != -1) - len += batt_chg_tier_stats_cstr(&buff[len], size - len, - &ce_data->cc_lvl_stats, - verbose); + len += gbms_tier_stats_cstr(&buff[len], size - len, + &ce_data->cc_lvl_stats, + verbose); /* If bd_clear triggers, we need to know about it even if trickle hasn't * triggered */ if (ce_data->trickle_stats.soc_in != -1 || ce_data->bd_clear_trickle) - len += batt_chg_tier_stats_cstr(&buff[len], size - len, - &ce_data->trickle_stats, - verbose); + len += gbms_tier_stats_cstr(&buff[len], size - len, + &ce_data->trickle_stats, + verbose); return len; } @@ -2366,7 +2189,7 @@ static void batt_res_work(struct batt_drv *batt_drv) /* ------------------------------------------------------------------------- */ -static void batt_log_csi_info(struct batt_drv *batt_drv) +static void batt_log_csi_ttf_info(struct batt_drv *batt_drv) { struct csi_stats *csi_stats = &batt_drv->csi_stats; const bool same_type_and_status = @@ -2377,6 +2200,9 @@ static void batt_log_csi_info(struct batt_drv *batt_drv) const ktime_t right_now = get_boot_sec(); int ssoc = -1; + if (!batt_drv->init_complete) + return; + if (chg_state_is_disconnected(&batt_drv->chg_state)) goto log_and_done; @@ -2408,14 +2234,21 @@ log_and_done: const int csi_speed_avg = csi_stats->csi_time_sum == 0 ? csi_stats->speed_sum : (csi_stats->speed_sum / csi_stats->csi_time_sum); + const int cc = GPSY_GET_PROP(batt_drv->fg_psy, POWER_SUPPLY_PROP_CHARGE_COUNTER); + ktime_t res = 0; + const int max_ratio = batt_ttf_estimate(&res, batt_drv); + + if (max_ratio < 0) + res = 0; gbms_logbuffer_prlog(batt_drv->ttf_stats.ttf_log, LOGLEVEL_INFO, 0, LOGLEVEL_DEBUG, - "CSI ssoc=%d min=%d max=%d avg=%d type=%d status=%d", - csi_stats->ssoc, - csi_stats->csi_speed_min, csi_stats->csi_speed_max, - csi_speed_avg, - csi_stats->csi_current_type, - csi_stats->csi_current_status); + "ssoc=%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, csi_stats->csi_speed_min, + csi_stats->csi_speed_max, csi_speed_avg, + csi_stats->csi_current_type, csi_stats->csi_current_status, + cc / 1000, right_now, res / 3600, (res % 3600) / 60, (res % 3600) % 60, + res, max_ratio); } /* ssoc == -1 on disconnect */ @@ -2427,7 +2260,7 @@ log_and_done: csi_stats->csi_speed_max = current_speed; csi_stats->csi_time_sum = 0; - csi_stats->speed_sum = current_speed; + csi_stats->speed_sum = 0; csi_stats->last_update = right_now; } @@ -2441,7 +2274,7 @@ static int csi_status_cb(struct gvotable_election *el, const char *reason, return 0; batt_drv->csi_current_status = status; - batt_log_csi_info(batt_drv); + batt_log_csi_ttf_info(batt_drv); if (batt_drv->psy) power_supply_changed(batt_drv->psy); @@ -2459,7 +2292,7 @@ static int csi_type_cb(struct gvotable_election *el, const char *reason, return 0; batt_drv->csi_current_type = type; - batt_log_csi_info(batt_drv); + batt_log_csi_ttf_info(batt_drv); if (batt_drv->psy) power_supply_changed(batt_drv->psy); @@ -2538,7 +2371,7 @@ static bool batt_csi_check_ad_power(const union gbms_ce_adapter_details *ad) case CHG_EV_ADAPTER_TYPE_WLC: case CHG_EV_ADAPTER_TYPE_WLC_EPP: case CHG_EV_ADAPTER_TYPE_WLC_SPP: - limit_mw = 7500; + limit_mw = 7500000; break; default: break; @@ -2568,21 +2401,21 @@ static void batt_update_csi_status(struct batt_drv *batt_drv) return; } - gvotable_cast_long_vote(batt_drv->csi_status_votable, "CSI_STATUS_CHG", - CSI_STATUS_Charging, !is_disconnected); - /* * discharging when the battery current is negative. There will likely * be a more specific reason (e.g System_* or Adapter_* or one of * Defender_*). */ - gvotable_cast_long_vote(batt_drv->csi_status_votable, "CSI_STATUS_DSG", - CSI_STATUS_NotCharging, - !is_disconnected && batt_drv->msc_state == MSC_DSG); - gvotable_cast_long_vote(batt_drv->csi_status_votable, "CSI_STATUS_100", - CSI_STATUS_Charging, - !is_disconnected && batt_drv->chg_done); + /* Charging Status Health_Cold */ + gvotable_cast_long_vote(batt_drv->csi_status_votable, "CSI_STATUS_COLD", + CSI_STATUS_Health_Cold, + !is_disconnected && is_cold); + + /* Charging Status Health_Hot */ + gvotable_cast_long_vote(batt_drv->csi_status_votable, "CSI_STATUS_HOT", + CSI_STATUS_Health_Hot, + !is_disconnected && is_hot); /* looks at absolute power, it could look also look at golden adapter */ gvotable_cast_long_vote(batt_drv->csi_status_votable, "CSI_STATUS_ADA_POWR", @@ -2595,20 +2428,21 @@ static void batt_update_csi_status(struct batt_drv *batt_drv) CSI_STATUS_Adapter_Quality, !is_disconnected && batt_csi_check_ad_qual(batt_drv)); - /* Charging Status Health_Cold */ - gvotable_cast_long_vote(batt_drv->csi_status_votable, "CSI_STATUS_COLD", - CSI_STATUS_Health_Cold, - !is_disconnected && is_cold); - - /* Charging Status Health_Hot */ - gvotable_cast_long_vote(batt_drv->csi_status_votable, "CSI_STATUS_HOT", - CSI_STATUS_Health_Hot, - !is_disconnected && is_hot); - /* Charging Status Defender_Trickle */ gvotable_cast_long_vote(batt_drv->csi_status_votable, "CSI_STATUS_DEFEND_TRICKLE", CSI_STATUS_Defender_Trickle, !is_disconnected && is_trickle); + + gvotable_cast_long_vote(batt_drv->csi_status_votable, "CSI_STATUS_DSG", + CSI_STATUS_NotCharging, + !is_disconnected && batt_drv->msc_state == MSC_DSG); + + gvotable_cast_long_vote(batt_drv->csi_status_votable, "CSI_STATUS_100", + CSI_STATUS_Charging, + !is_disconnected && batt_drv->chg_done); + + gvotable_cast_long_vote(batt_drv->csi_status_votable, "CSI_STATUS_CHG", + CSI_STATUS_Charging, !is_disconnected); } #define CSI_CHG_SPEED_MAX 100 @@ -2683,7 +2517,7 @@ static void batt_update_csi_info(struct batt_drv *batt_drv) charging_speed = batt_calc_charging_speed(batt_drv); if (batt_drv->csi_current_speed != charging_speed) { batt_drv->csi_current_speed = charging_speed; - batt_log_csi_info(batt_drv); + batt_log_csi_ttf_info(batt_drv); } } @@ -3129,13 +2963,7 @@ static bool batt_health_set_chg_deadline(struct batt_chg_health *chg_health, return new_deadline || rest_state != chg_health->rest_state; } -/* cc_max in ua: capacity in mAh, rest_rate in deciPct */ -static int msc_logic_health_get_rate(const struct batt_chg_health *rest, - int capacity_ma) -{ - return capacity_ma * rest->rest_rate * 10; -} - +#define HEALTH_CHG_RATE_BEFORE_TRIGGER 80 /* health based charging trade charging speed for battery cycle life. */ static bool msc_logic_health(struct batt_drv *batt_drv) { @@ -3144,6 +2972,7 @@ static bool msc_logic_health(struct batt_drv *batt_drv) const ktime_t deadline = rest->rest_deadline; enum chg_health_state rest_state = rest->rest_state; const bool aon_enabled = rest->always_on_soc != -1; + const int capacity_ma = batt_drv->battery_capacity; const ktime_t now = get_boot_sec(); int fv_uv = -1, cc_max = -1; bool changed = false; @@ -3232,9 +3061,8 @@ static bool msc_logic_health(struct batt_drv *batt_drv) done_exit: if (rest_state == CHG_HEALTH_ACTIVE || rest_state == CHG_HEALTH_DONE) { - const int capacity_ma = batt_drv->battery_capacity; - - cc_max = msc_logic_health_get_rate(rest, capacity_ma); + /* cc_max in ua: capacity in mAh, rest_rate in deciPct */ + cc_max = capacity_ma * rest->rest_rate * 10; /* * default FV_UV to the last charge tier since fv_uv will be @@ -3245,6 +3073,8 @@ done_exit: fv_uv = profile->volt_limits[profile->volt_nb_limits - 1]; /* TODO: make sure that we wakeup when we are close to ttf */ + } else if (rest_state == CHG_HEALTH_ENABLED) { + cc_max = capacity_ma * rest->rest_rate_before_trigger * 10; } else if (rest_state == CHG_HEALTH_PAUSE) { /* * pause charging behavior when the the deadline is longer than @@ -3637,7 +3467,8 @@ static int bhi_calc_sd_index(int algo, const struct bhi_data *bhi_data) return bhi_data->ccbin_index; } -static int bhi_calc_health_index(int algo, int cap_index, int imp_index, int sd_index) +static int bhi_calc_health_index(int algo, const struct health_data *health_data, + int cap_index, int imp_index, int sd_index) { int ratio, index; int w_ci = 0; @@ -3652,21 +3483,17 @@ static int bhi_calc_health_index(int algo, int cap_index, int imp_index, int sd_ return BHI_ALGO_FULL_HEALTH; case BHI_ALGO_ACHI: case BHI_ALGO_ACHI_B: - w_ci = 100; - w_ii = 0; - w_sd = 0; - break; case BHI_ALGO_ACHI_RAVG: case BHI_ALGO_ACHI_RAVG_B: - w_ci = 95; - w_ii = 5; - w_sd = 0; - break; case BHI_ALGO_MIX_N_MATCH: - /* TODO: use the weights in health_data */ - w_ci = 90; - w_ii = 10; - w_sd = 5; + w_ci = bhi_w[algo].w_ci; + w_ii = bhi_w[algo].w_ii; + w_sd = bhi_w[algo].w_sd; + break; + case BHI_ALGO_DEBUG: + w_ci = health_data->bhi_w_ci; + w_ii = health_data->bhi_w_pi; + w_sd = health_data->bhi_w_sd; break; default: return -EINVAL; @@ -3769,7 +3596,7 @@ static int batt_bhi_stats_update(struct batt_drv *batt_drv) changed |= health_data->bhi_sd_index != index; health_data->bhi_sd_index = index; - index = bhi_calc_health_index(bhi_algo, + index = bhi_calc_health_index(bhi_algo, health_data, health_data->bhi_cap_index, health_data->bhi_imp_index, health_data->bhi_sd_index); @@ -3779,7 +3606,7 @@ static int batt_bhi_stats_update(struct batt_drv *batt_drv) changed |= health_data->bhi_index != index; health_data->bhi_index = index; - status = bhi_calc_health_status(bhi_algo, index, health_data); + status = bhi_calc_health_status(bhi_algo, BHI_ROUND_INDEX(index), health_data); changed |= health_data->bhi_status != status; health_data->bhi_status = status; @@ -4002,7 +3829,7 @@ static int msc_logic(struct batt_drv *batt_drv) } mutex_lock(&batt_drv->stats_lock); - batt_chg_stats_tier(&batt_drv->ce_data.tier_stats[tier_idx], + gbms_chg_stats_tier(&batt_drv->ce_data.tier_stats[tier_idx], batt_drv->msc_irdrop_state, elap); batt_drv->msc_irdrop_state = msc_state; mutex_unlock(&batt_drv->stats_lock); @@ -4181,9 +4008,9 @@ static void google_battery_dump_profile(const struct gbms_chg_profile *profile) { char *buff; - buff = kzalloc(GBMS_CHG_ALG_BUF, GFP_KERNEL); + buff = kzalloc(GBMS_CHG_ALG_BUF_SZ, GFP_KERNEL); if (buff) { - gbms_dump_chg_profile(buff, GBMS_CHG_ALG_BUF, profile); + gbms_dump_chg_profile(buff, GBMS_CHG_ALG_BUF_SZ, profile); pr_info("%s", buff); kfree(buff); } @@ -4322,7 +4149,7 @@ static int batt_chg_logic(struct batt_drv *batt_drv) mutex_unlock(&batt_drv->bpst_state.lock); /* here on: disconnect */ - batt_log_csi_info(batt_drv); + batt_log_csi_ttf_info(batt_drv); batt_chg_stats_pub(batt_drv, "disconnect", false, false); /* change curve before changing the state. */ @@ -4931,6 +4758,31 @@ static int debug_set_ssoc_rls(void *data, u64 val) DEFINE_SIMPLE_ATTRIBUTE(debug_ssoc_rls_fops, debug_get_ssoc_rls, debug_set_ssoc_rls, "%llu\n"); +static int debug_get_fv_dc_ratio(void *data, u64 *val) +{ + struct batt_drv *batt_drv = (struct batt_drv *)data; + + *val = batt_drv->chg_profile.fv_dc_ratio; + + return 0; +} + +static int debug_set_fv_dc_ratio(void *data, u64 val) +{ + struct batt_drv *batt_drv = (struct batt_drv *)data; + + if (val < 0) + return -EINVAL; + + mutex_lock(&batt_drv->chg_lock); + batt_drv->chg_profile.fv_dc_ratio = val; + mutex_unlock(&batt_drv->chg_lock); + + return 0; +} + +DEFINE_SIMPLE_ATTRIBUTE(debug_fv_dc_ratio_fops, + debug_get_fv_dc_ratio, debug_set_fv_dc_ratio, "%llu\n"); static ssize_t debug_get_ssoc_uicurve(struct file *filp, char __user *buf, @@ -4996,7 +4848,7 @@ DEFINE_SIMPLE_ATTRIBUTE(debug_force_psy_update_fops, /* Adaptive Charging */ static int debug_chg_health_rest_rate_read(void *data, u64 *val) { - struct batt_drv *batt_drv = (struct batt_drv *)data; + struct batt_drv *batt_drv = data; if (!batt_drv->psy) return -EINVAL; @@ -5008,7 +4860,7 @@ static int debug_chg_health_rest_rate_read(void *data, u64 *val) /* Adaptive Charging */ static int debug_chg_health_rest_rate_write(void *data, u64 val) { - struct batt_drv *batt_drv = (struct batt_drv *)data; + struct batt_drv *batt_drv = data; if (!batt_drv->psy) return -EINVAL; @@ -5022,6 +4874,36 @@ DEFINE_SIMPLE_ATTRIBUTE(debug_chg_health_rest_rate_fops, debug_chg_health_rest_rate_read, debug_chg_health_rest_rate_write, "%llu\n"); + +/* Adaptive Charging */ +static int debug_chg_health_rest_rate_before_trigger_read(void *data, u64 *val) +{ + struct batt_drv *batt_drv = data; + + if (!batt_drv->psy) + return -EINVAL; + + *val = batt_drv->chg_health.rest_rate_before_trigger; + return 0; +} + +/* Adaptive Charging */ +static int debug_chg_health_rest_rate_before_trigger_write(void *data, u64 val) +{ + struct batt_drv *batt_drv = data; + + if (!batt_drv->psy) + return -EINVAL; + + batt_drv->chg_health.rest_rate_before_trigger = val; + return 0; +} + +/* Adaptive Charging */ +DEFINE_SIMPLE_ATTRIBUTE(debug_chg_health_rest_rate_before_trigger_fops, + debug_chg_health_rest_rate_before_trigger_read, + debug_chg_health_rest_rate_before_trigger_write, "%llu\n"); + /* Adaptive Charging */ static int debug_chg_health_thr_soc_read(void *data, u64 *val) { @@ -5345,6 +5227,42 @@ static ssize_t debug_get_blf_state(struct file *filp, char __user *buf, } BATTERY_DEBUG_ATTRIBUTE(debug_blf_state_fops, debug_get_blf_state, 0); +static ssize_t debug_get_bhi_status(struct file *filp, char __user *buf, + size_t count, loff_t *ppos) +{ + struct batt_drv *batt_drv = (struct batt_drv *)filp->private_data; + struct health_data *health_data = &batt_drv->health_data; + const int cap_idx = health_data->bhi_debug_cap_index; + const int imp_idx = health_data->bhi_debug_imp_index; + const int sd_idx = health_data->bhi_debug_sd_index; + const int algo = BHI_ALGO_DEBUG; + int health_idx = health_data->bhi_debug_health_index; + int health_status, len; + char *tmp; + + tmp = kzalloc(PAGE_SIZE, GFP_KERNEL); + if (!tmp) + return -ENOMEM; + + if (health_idx == 0) + health_idx = bhi_calc_health_index(algo, health_data, cap_idx, imp_idx, sd_idx); + + health_status = bhi_calc_health_status(algo, BHI_ROUND_INDEX(health_idx), health_data); + + if (health_data->bhi_debug_health_index != 0) + scnprintf(tmp, PAGE_SIZE, "%d, %d\n", health_status, health_idx); + else + scnprintf(tmp, PAGE_SIZE, "%d, %d [%d/%d %d/%d %d/%d]\n", health_status, + health_idx, cap_idx, health_data->bhi_w_ci, imp_idx, + health_data->bhi_w_pi, sd_idx, health_data->bhi_w_sd); + + len = simple_read_from_buffer(buf, count, ppos, tmp, strlen(tmp)); + kfree(tmp); + + return len; +} +BATTERY_DEBUG_ATTRIBUTE(debug_bhi_status_fops, debug_get_bhi_status, 0); + /* TODO: add writes to restart pairing (i.e. provide key) */ static ssize_t batt_pairing_state_show(struct device *dev, struct device_attribute *attr, @@ -5502,11 +5420,11 @@ static ssize_t batt_show_chg_details(struct device *dev, /* NOTE: vtier_idx is -1, can also check elap */ if (h->soc_in != -1) - len += batt_chg_tier_stats_cstr(&buf[len], - PAGE_SIZE - len, h, !!elap_h); + len += gbms_tier_stats_cstr(&buf[len], + PAGE_SIZE - len, h, !!elap_h); if (p->soc_in != -1) - len += batt_chg_tier_stats_cstr(&buf[len], - PAGE_SIZE - len, p, !!elap_p); + len += gbms_tier_stats_cstr(&buf[len], + PAGE_SIZE - len, p, !!elap_p); } len += scnprintf(&buf[len], PAGE_SIZE - len, "\n"); @@ -6500,7 +6418,7 @@ static ssize_t health_index_stats_show(struct device *dev, 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; - struct health_data *health_data = &batt_drv->health_data; + struct health_data *health_data = &batt_drv->health_data; int len = 0, i; mutex_lock(&batt_drv->chg_lock); @@ -6511,7 +6429,7 @@ static ssize_t health_index_stats_show(struct device *dev, cap_index = bhi_calc_cap_index(i, batt_drv); imp_index = bhi_calc_imp_index(i, bhi_data); sd_index = bhi_calc_sd_index(i, bhi_data); - health_index = bhi_calc_health_index(i, cap_index, imp_index, sd_index); + health_index = bhi_calc_health_index(i, health_data, cap_index, imp_index, sd_index); health_status = bhi_calc_health_status(i, BHI_ROUND_INDEX(health_index), health_data); if (health_index < 0) continue; @@ -6830,41 +6748,6 @@ static ssize_t dev_sn_show(struct device *dev, static const DEVICE_ATTR_RW(dev_sn); -#define USER_SHUTDOWN_FLAG 0x01 -static int batt_set_charger_mode(bool enable) -{ - u8 data; - int ret; - - data = enable ? USER_SHUTDOWN_FLAG : 0; - ret = gbms_storage_write(GBMS_TAG_SUFG, &data, sizeof(data)); - if (ret < 0) - return -EIO; - - return 0; -} - -static ssize_t charger_mode_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t count) -{ - int ret = 0, val = 0; - - ret = kstrtoint(buf, 0, &val); - if (ret < 0) - return ret; - - if (val >= 0) { - ret = batt_set_charger_mode((val > 0)); - if (ret < 0) - pr_err("Cannot set charger mode, ret=%d\n", ret); - } - - return count; -} - -static DEVICE_ATTR_WO(charger_mode); - /* ------------------------------------------------------------------------- */ static int batt_init_fs(struct batt_drv *batt_drv) @@ -7043,9 +6926,6 @@ static int batt_init_fs(struct batt_drv *batt_drv) ret = device_create_file(&batt_drv->psy->dev, &dev_attr_dev_sn); if (ret) dev_err(&batt_drv->psy->dev, "Failed to create dev sn\n"); - ret = device_create_file(&batt_drv->psy->dev, &dev_attr_charger_mode); - if (ret) - dev_err(&batt_drv->psy->dev, "Failed to create charger_mode\n"); return 0; @@ -7065,6 +6945,7 @@ static int batt_init_debugfs(struct batt_drv *batt_drv) debugfs_create_file("ssoc_gdf", 0644, de, batt_drv, &debug_ssoc_gdf_fops); debugfs_create_file("ssoc_uic", 0644, de, batt_drv, &debug_ssoc_uic_fops); debugfs_create_file("ssoc_rls", 0444, de, batt_drv, &debug_ssoc_rls_fops); + debugfs_create_file("fv_dc_ratio", 0644, de, batt_drv, &debug_fv_dc_ratio_fops); debugfs_create_file("ssoc_uicurve", 0644, de, batt_drv, &debug_ssoc_uicurve_cstr_fops); debugfs_create_file("force_psy_update", 0400, de, batt_drv, @@ -7091,6 +6972,8 @@ static int batt_init_debugfs(struct batt_drv *batt_drv) &debug_chg_health_thr_soc_fops); debugfs_create_file("chg_health_rest_rate", 0600, de, batt_drv, &debug_chg_health_rest_rate_fops); + debugfs_create_file("chg_health_rest_rate_before_trigger", 0600, de, batt_drv, + &debug_chg_health_rest_rate_before_trigger_fops); debugfs_create_file("chg_health_stage", 0600, de, batt_drv, &debug_chg_health_stage_fops); @@ -7106,10 +6989,20 @@ static int batt_init_debugfs(struct batt_drv *batt_drv) /* bhi fullcapnom count */ debugfs_create_u32("bhi_w_ci", 0644, de, &batt_drv->health_data.bhi_w_ci); - debugfs_create_u32("bhi_w_pi", 0644, de, &batt_drv->health_data.bhi_w_ci); - debugfs_create_u32("bhi_w_sd", 0644, de, &batt_drv->health_data.bhi_w_ci); + debugfs_create_u32("bhi_w_pi", 0644, de, &batt_drv->health_data.bhi_w_pi); + debugfs_create_u32("bhi_w_sd", 0644, de, &batt_drv->health_data.bhi_w_sd); debugfs_create_u32("act_impedance", 0644, de, &batt_drv->health_data.bhi_data.act_impedance); + debugfs_create_u32("bhi_debug_cap_idx", 0644, de, + &batt_drv->health_data.bhi_debug_cap_index); + debugfs_create_u32("bhi_debug_imp_idx", 0644, de, + &batt_drv->health_data.bhi_debug_imp_index); + debugfs_create_u32("bhi_debug_sd_idx", 0644, de, + &batt_drv->health_data.bhi_debug_sd_index); + debugfs_create_u32("bhi_debug_health_idx", 0644, de, + &batt_drv->health_data.bhi_debug_health_index); + debugfs_create_file("bhi_debug_status", 0644, de, batt_drv, + &debug_bhi_status_fops); /* google_resistance, tuning */ debugfs_create_u32("ravg_temp_low", 0644, de, @@ -7122,6 +7015,9 @@ static int batt_init_debugfs(struct batt_drv *batt_drv) &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; } @@ -7230,32 +7126,6 @@ static int gbatt_get_temp(const struct batt_drv *batt_drv, int *temp) return err; } -static void bat_log_ttf_estimate(const char *label, int ssoc, - struct batt_drv *batt_drv) -{ - int cc, err; - ktime_t res = 0; - u64 hours; - int remaining_sec; - - err = batt_ttf_estimate(&res, batt_drv); - if (err < 0) { - logbuffer_log(batt_drv->ttf_stats.ttf_log, - "%s ssoc=%d time=%ld err=%d", - (label) ? label : "", ssoc, get_boot_sec(), err); - return; - } - - hours = div_u64_rem(res, 3600, &remaining_sec); - - cc = GPSY_GET_PROP(batt_drv->fg_psy, POWER_SUPPLY_PROP_CHARGE_COUNTER); - logbuffer_log(batt_drv->ttf_stats.ttf_log, - "%s ssoc=%d cc=%d time=%ld %d:%d:%d (est=%ld, max_ratio=%d)", - (label) ? label : "", ssoc, cc / 1000, get_boot_sec(), - hours, remaining_sec / 60, remaining_sec % 60, - res, err); -} - static int batt_do_md5(const u8 *data, unsigned int len, u8 *result) { struct crypto_shash *tfm; @@ -7534,6 +7404,38 @@ static int google_battery_init_hist_work(struct batt_drv *batt_drv ) 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. @@ -7606,11 +7508,8 @@ static void google_battery_work(struct work_struct *work) pr_debug("%s: change of ssoc %d->%d\n", __func__, prev_ssoc, ssoc); - if (ssoc > prev_ssoc) - bat_log_ttf_estimate("SSOC", ssoc, batt_drv); - dump_ssoc_state(ssoc_state, batt_drv->ssoc_log); - batt_log_csi_info(batt_drv); + batt_log_csi_ttf_info(batt_drv); notify_psy_changed = true; } @@ -7625,6 +7524,13 @@ static void google_battery_work(struct work_struct *work) __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; } @@ -7640,7 +7546,7 @@ static void google_battery_work(struct work_struct *work) /* fuel gauge triggered recharge logic. */ full = (ssoc == SSOC_FULL); if (full && !batt_drv->batt_full) { - bat_log_ttf_estimate("Full", ssoc, batt_drv); + batt_log_csi_ttf_info(batt_drv); batt_chg_stats_pub(batt_drv, "100%", false, true); } batt_drv->batt_full = full; @@ -7710,7 +7616,7 @@ static void google_battery_work(struct work_struct *work) } } else if (batt_drv->ttf_debounce) { batt_drv->ttf_debounce = 0; - bat_log_ttf_estimate("Start", prev_ssoc, batt_drv); + batt_log_csi_ttf_info(batt_drv); } /* acquired in msc_logic */ @@ -8414,6 +8320,12 @@ static int batt_bhi_init(struct batt_drv *batt_drv) /* design is the value used to build the charge table */ health_data->bhi_data.capacity_design = batt_drv->battery_capacity; + /* debug data initialization */ + health_data->bhi_debug_cap_index = 0; + health_data->bhi_debug_imp_index = 0; + health_data->bhi_debug_sd_index = 0; + health_data->bhi_debug_health_index = 0; + return 0; } @@ -8476,6 +8388,7 @@ static void google_battery_init_work(struct work_struct *work) 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); @@ -8561,6 +8474,11 @@ static void google_battery_init_work(struct work_struct *work) 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); @@ -8678,6 +8596,12 @@ static void google_battery_init_work(struct work_struct *work) if (ret < 0) batt_drv->chg_health.rest_rate = 0; + ret = of_property_read_u32(batt_drv->device->of_node, + "google,chg-rest-rate-before-trigger", + &batt_drv->chg_health.rest_rate_before_trigger); + if (ret < 0) + batt_drv->chg_health.rest_rate_before_trigger = HEALTH_CHG_RATE_BEFORE_TRIGGER; + /* override setting google,battery-roundtrip = 0 in device tree */ batt_drv->disable_votes = of_property_read_bool(node, "google,disable-votes"); diff --git a/google_bms.c b/google_bms.c index f4f7a83..673ffa2 100644 --- a/google_bms.c +++ b/google_bms.c @@ -33,6 +33,9 @@ #include "google_psy.h" #include "google_bms.h" +/* sync from google/logbuffer.c */ +#define LOG_BUFFER_ENTRY_SIZE 256 + #define GBMS_DEFAULT_FV_UV_RESOLUTION 25000 #define GBMS_DEFAULT_FV_UV_MARGIN_DPCT 1020 #define GBMS_DEFAULT_FV_DC_RATIO 20 @@ -89,6 +92,16 @@ const char *gbms_chg_ev_adapter_s(int adapter) } EXPORT_SYMBOL_GPL(gbms_chg_ev_adapter_s); +static const char *gbms_get_code(const int index) +{ + const static char *codes[] = {"n", "s", "d", "l", "v", "vo", "p", "f", + "t", "dl", "st", "tc", "r", "w", "rs", + "n", "ny", "h", "hp", "ha"}; + const int len = ARRAY_SIZE(codes); + + return (index >= 0 && index < len) ? codes[index] : "?"; +} + /* convert C rates to current. Caller can account for tolerances reducing * battery_capacity. fv_uv_resolution is used to create discrete steps. * NOTE: the call covert C rates to chanrge currents IN PLACE, ie you cannot @@ -729,3 +742,193 @@ bool chg_state_is_disconnected(const union gbms_charger_state *chg_state) chg_state->f.chg_status == POWER_SUPPLY_STATUS_UNKNOWN); } EXPORT_SYMBOL_GPL(chg_state_is_disconnected); + +/* Tier stats common routines */ +void gbms_tier_stats_init(struct gbms_ce_tier_stats *stats, int8_t idx) +{ + stats->vtier_idx = idx; + stats->temp_idx = -1; + stats->soc_in = -1; +} +EXPORT_SYMBOL_GPL(gbms_tier_stats_init); + +/* call holding stats_lock */ +void gbms_chg_stats_tier(struct gbms_ce_tier_stats *tier, + int msc_state, + ktime_t elap) +{ + if (msc_state < 0 || msc_state >= MSC_STATES_COUNT) + return; + + tier->msc_cnt[msc_state] += 1; + tier->msc_elap[msc_state] += elap; +} +EXPORT_SYMBOL_GPL(gbms_chg_stats_tier); + + void gbms_stats_update_tier(int temp_idx, int ibatt_ma, int temp, ktime_t elap, + int cc, union gbms_charger_state *chg_state, + enum gbms_msc_states_t msc_state, int soc_in, + struct gbms_ce_tier_stats *tier) +{ + const uint16_t icl_settled = chg_state->f.icl; + + /* + * book time to previous msc_state for this tier, there is an + * interesting wrinkle here since some tiers (health, full, etc) + * might be entered and exited multiple times. + */ + gbms_chg_stats_tier(tier, msc_state, elap); + tier->sample_count += 1; + + if (tier->soc_in == -1) { + tier->temp_idx = temp_idx; + + tier->temp_in = temp; + tier->temp_min = temp; + tier->temp_max = temp; + + tier->ibatt_min = ibatt_ma; + tier->ibatt_max = ibatt_ma; + + tier->icl_min = icl_settled; + tier->icl_max = icl_settled; + + tier->soc_in = soc_in; + tier->cc_in = cc; + tier->cc_total = 0; + return; + } + + /* crossed temperature tier */ + if (temp_idx != tier->temp_idx) + tier->temp_idx = -1; + + if (chg_state->f.chg_type == POWER_SUPPLY_CHARGE_TYPE_FAST) { + tier->time_fast += elap; + } else if (chg_state->f.chg_type == POWER_SUPPLY_CHARGE_TYPE_TAPER_EXT) { + tier->time_taper += elap; + } else { + tier->time_other += elap; + } + + /* + * averages: temp < 100. icl_settled < 3000, sum(ibatt) + * is bound to battery capacity, elap in seconds, sums + * are stored in an s64. For icl_settled I need a tier + * to last for more than ~97M years. + */ + if (temp < tier->temp_min) + tier->temp_min = temp; + if (temp > tier->temp_max) + tier->temp_max = temp; + tier->temp_sum += temp * elap; + + if (icl_settled < tier->icl_min) + tier->icl_min = icl_settled; + if (icl_settled > tier->icl_max) + tier->icl_max = icl_settled; + tier->icl_sum += icl_settled * elap; + + if (ibatt_ma < tier->ibatt_min) + tier->ibatt_min = ibatt_ma; + if (ibatt_ma > tier->ibatt_max) + tier->ibatt_max = ibatt_ma; + tier->ibatt_sum += ibatt_ma * elap; + + tier->cc_total = cc - tier->cc_in; +} +EXPORT_SYMBOL_GPL(gbms_stats_update_tier); + +/* Log only when elap != 0 */ +int gbms_tier_stats_cstr(char *buff, int size, + const struct gbms_ce_tier_stats *tier_stat, + bool verbose) +{ + const int soc_in = tier_stat->soc_in >> 8; + const long elap = tier_stat->time_fast + tier_stat->time_taper + + tier_stat->time_other; + + long temp_avg, ibatt_avg, icl_avg; + int j, len = 0; + + if (elap) { + temp_avg = tier_stat->temp_sum / elap; + ibatt_avg = tier_stat->ibatt_sum / elap; + icl_avg = tier_stat->icl_sum / elap; + } else { + temp_avg = 0; + ibatt_avg = 0; + icl_avg = 0; + } + + len += scnprintf(&buff[len], size - len, "\n%d%c ", + tier_stat->vtier_idx, + (verbose) ? ':' : ','); + + len += scnprintf(&buff[len], size - len, + "%d.%d,%d,%d, %d,%d,%d, %d,%ld,%d, %d,%ld,%d, %d,%ld,%d", + soc_in, + tier_stat->soc_in & 0xff, + tier_stat->cc_in, + tier_stat->temp_in, + tier_stat->time_fast, + tier_stat->time_taper, + tier_stat->time_other, + tier_stat->temp_min, + temp_avg, + tier_stat->temp_max, + tier_stat->ibatt_min, + ibatt_avg, + tier_stat->ibatt_max, + tier_stat->icl_min, + icl_avg, + tier_stat->icl_max); + + if (!verbose || !elap) + return len; + + /* time spent in every multi step charging state */ + len += scnprintf(&buff[len], size - len, "\n%d:", + tier_stat->vtier_idx); + + for (j = 0; j < MSC_STATES_COUNT; j++) + len += scnprintf(&buff[len], size - len, " %s=%d", + gbms_get_code(j), tier_stat->msc_elap[j]); + + /* count spent in each step charging state */ + len += scnprintf(&buff[len], size - len, "\n%d:", + tier_stat->vtier_idx); + + for (j = 0; j < MSC_STATES_COUNT; j++) + len += scnprintf(&buff[len], size - len, " %s=%d", + gbms_get_code(j), tier_stat->msc_cnt[j]); + + return len; +} +EXPORT_SYMBOL_GPL(gbms_tier_stats_cstr); + +void gbms_log_cstr_handler(struct logbuffer *log, char *buf, int len) +{ + int i, j = 0; + char tmp[LOG_BUFFER_ENTRY_SIZE]; + + buf[len] = '\n'; + for (i = 0; i <= len; i++) { + if (buf[i] == '\n') { + tmp[j] = '\0'; + /* skip first blank line */ + if (i != 0) + logbuffer_log(log, "%s", tmp); + j = 0; + } else if (j >= LOG_BUFFER_ENTRY_SIZE - 1) { + tmp[j] = '\0'; + logbuffer_log(log, "%s", tmp); + i--; + j = 0; + } else { + tmp[j] = buf[i]; + j++; + } + } +} +EXPORT_SYMBOL_GPL(gbms_log_cstr_handler); diff --git a/google_bms.h b/google_bms.h index c2f42d2..4183f4f 100644 --- a/google_bms.h +++ b/google_bms.h @@ -30,7 +30,7 @@ struct device_node; #define GBMS_CHG_TEMP_NB_LIMITS_MAX 10 #define GBMS_CHG_VOLT_NB_LIMITS_MAX 5 -#define GBMS_CHG_ALG_BUF 500 +#define GBMS_CHG_ALG_BUF_SZ 500 #define GBMS_CHG_TOPOFF_NB_LIMITS_MAX 6 #define GBMS_AACR_DATA_MAX 10 @@ -67,8 +67,8 @@ struct gbms_chg_profile { u32 aacr_nb_limits; }; -#define WLC_BPP_THRESHOLD_UV 700000 -#define WLC_EPP_THRESHOLD_UV 1100000 +#define WLC_BPP_THRESHOLD_UV 7000000 +#define WLC_EPP_THRESHOLD_UV 11000000 #define FOREACH_CHG_EV_ADAPTER(S) \ S(UNKNOWN), \ @@ -84,10 +84,23 @@ struct gbms_chg_profile { S(USB_BRICKID), \ S(USB_HVDCP), \ S(USB_HVDCP3), \ + S(FLOAT), \ S(WLC), \ S(WLC_EPP), \ S(WLC_SPP), \ - S(POGO), \ + S(GPP), \ + S(10W), \ + S(L7), \ + S(DL), \ + S(WPC_EPP), \ + S(WPC_GPP), \ + S(WPC_10W), \ + S(WPC_BPP), \ + S(WPC_L7), \ + S(EXT), \ + S(EXT1), \ + S(EXT2), \ + S(EXT_UNKNOWN), \ #define CHG_EV_ADAPTER_STRING(s) #s #define _CHG_EV_ADAPTER_PRIMITIVE_CAT(a, ...) a ## __VA_ARGS__ @@ -296,8 +309,10 @@ enum gbms_stats_tier_idx_t { GBMS_STATS_BD_TI_OVERHEAT_TEMP = 110, GBMS_STATS_BD_TI_CUSTOM_LEVELS = 111, GBMS_STATS_BD_TI_TRICKLE = 112, + GBMS_STATS_BD_TI_DOCK = 113, GBMS_STATS_BD_TI_TRICKLE_CLEARED = 122, + GBMS_STATS_BD_TI_DOCK_CLEARED = 123, }; /* health state */ @@ -309,6 +324,7 @@ struct batt_chg_health { ktime_t rest_deadline; /* full by this in seconds */ ktime_t dry_run_deadline; /* full by this in seconds (prediction) */ int rest_rate; /* centirate once enter */ + int rest_rate_before_trigger; enum chg_health_state rest_state; int rest_cc_max; @@ -522,6 +538,26 @@ int gbms_read_aacr_limits(struct gbms_chg_profile *profile, bool chg_state_is_disconnected(const union gbms_charger_state *chg_state); +/* Voltage tier stats */ +void gbms_tier_stats_init(struct gbms_ce_tier_stats *stats, int8_t idx); + +void gbms_chg_stats_tier(struct gbms_ce_tier_stats *tier, + int msc_state, ktime_t elap); + +void gbms_stats_update_tier(int temp_idx, int ibatt_ma, int temp, ktime_t elap, + int cc, union gbms_charger_state *chg_state, + enum gbms_msc_states_t msc_state, int soc_in, + struct gbms_ce_tier_stats *tier); + +int gbms_tier_stats_cstr(char *buff, int size, + const struct gbms_ce_tier_stats *tier_stat, + bool verbose); + +void gbms_log_cstr_handler(struct logbuffer *log, char *buf, int len); + + + + /* * Charger modes * @@ -559,6 +595,7 @@ enum bhi_algo { */ BHI_ALGO_MIX_N_MATCH = 6, + BHI_ALGO_DEBUG = 7, BHI_ALGO_MAX, }; @@ -570,6 +607,12 @@ enum bhi_status { BH_FAILED, }; +struct bhi_weight { + int w_ci; + int w_ii; + int w_sd; +}; + /* Charging Speed */ enum csi_type { CSI_TYPE_UNKNOWN = -1, diff --git a/google_charger.c b/google_charger.c index e9e67c7..9b29cd3 100644 --- a/google_charger.c +++ b/google_charger.c @@ -72,6 +72,7 @@ #define MSC_USER_VOTER "msc_user" #define MSC_USER_CHG_LEVEL_VOTER "msc_user_chg_level" #define MSC_CHG_TERM_VOTER "msc_chg_term" +#define MSC_PWR_VOTER "msc_pwr_disable" #define CHG_TERM_LONG_DELAY_MS 300000 /* 5 min */ #define CHG_TERM_SHORT_DELAY_MS 60000 /* 1 min */ @@ -103,6 +104,10 @@ #define PD_SNK_MAX_MA_9V 2200 #define OP_SNK_MW 7600 /* see b/159863291 */ +/* type detection */ +#define EXT1_DETECT_THRESHOLD_UV (10500000) +#define EXT2_DETECT_THRESHOLD_UV (5000000) + #define usb_pd_is_high_volt(ad) \ ((ad)->ad_type == CHG_EV_ADAPTER_TYPE_USB_PD && \ (ad)->ad_voltage * 100 > PD_SNK_MIN_MV) @@ -137,6 +142,7 @@ struct chg_thermal_device { int *thermal_budgets; int thermal_levels; int current_level; + int therm_fan_alarm_level; }; struct chg_termination { @@ -295,6 +301,7 @@ struct chg_drv { int charge_start_level; /* retail, userspace bd config */ /* pps charging */ + bool pps_enable; struct pd_pps_data pps_data; unsigned int pps_cc_tolerance_pct; union gbms_charger_state chg_state; @@ -314,6 +321,14 @@ struct chg_drv { /* debug */ struct dentry *debug_entry; + + /* dock_defend */ + struct delayed_work bd_dd_work; + bool ext_volt_complete; + + struct mutex stats_lock; + struct gbms_ce_tier_stats dd_stats; + ktime_t last_update; }; static void reschedule_chg_work(struct chg_drv *chg_drv) @@ -400,6 +415,60 @@ static char *psy_usbc_type_str[] = { "PD", "PD_DRP", "PD_PPS", "BrickID" }; +static int chg_work_read_soc(struct power_supply *bat_psy, int *soc); + +static void chg_stats_init(struct chg_drv *chg_drv, struct gbms_ce_tier_stats *tier, int8_t idx) +{ + ktime_t now = get_boot_sec(); + + mutex_lock(&chg_drv->stats_lock); + gbms_tier_stats_init(tier, idx); + chg_drv->last_update = now; + mutex_unlock(&chg_drv->stats_lock); +} + +static void chg_stats_update(struct chg_drv *chg_drv, struct gbms_ce_tier_stats *tier) +{ + int ibatt_ma, temp; + int cc, soc_in; + ktime_t elap, now = get_boot_sec(); + int ioerr; + + mutex_lock(&chg_drv->stats_lock); + if (tier->soc_in == -1) + elap = 0; + else + elap = now - chg_drv->last_update; + chg_drv->last_update = now; + + ibatt_ma = GPSY_GET_INT_PROP(chg_drv->bat_psy, POWER_SUPPLY_PROP_CURRENT_NOW, &ioerr); + if (ioerr < 0) { + pr_err("%s: read ibatt_ma=%d, ioerr=%d\n", __func__, ibatt_ma, ioerr); + goto stats_update_unlock; + } + ibatt_ma /= 1000; + + temp = GPSY_GET_INT_PROP(chg_drv->bat_psy, POWER_SUPPLY_PROP_TEMP, &ioerr); + if (ioerr < 0) + goto stats_update_unlock; + + cc = GPSY_GET_INT_PROP(chg_drv->bat_psy, POWER_SUPPLY_PROP_CHARGE_COUNTER, &ioerr); + if (ioerr < 0) + goto stats_update_unlock; + + cc /= 1000; + + /* Ignore error in soc_in read */ + ioerr = chg_work_read_soc(chg_drv->bat_psy, &soc_in); + if (ioerr != 0) + soc_in = -1; + + gbms_stats_update_tier(0, ibatt_ma, temp, elap, cc, &chg_drv->chg_state, -1, + soc_in << 8, tier); +stats_update_unlock: + mutex_unlock(&chg_drv->stats_lock); +} + /* called on google_charger_init_work() and on every disconnect */ static inline void chg_init_state(struct chg_drv *chg_drv) { @@ -420,7 +489,9 @@ static inline void chg_init_state(struct chg_drv *chg_drv) /* reset and re-enable PPS detection */ pps_init_state(&chg_drv->pps_data); - if (chg_drv->pps_data.nr_snk_pdo) + if (!chg_drv->pps_enable) + chg_drv->pps_data.stage = PPS_DISABLED; + else if (chg_drv->pps_data.nr_snk_pdo) chg_drv->pps_data.stage = PPS_NONE; } @@ -588,9 +659,9 @@ static int info_wlc_state(union gbms_ce_adapter_details *ad, return -EINVAL; } - if (amperage_max >= WLC_EPP_THRESHOLD_UV) { + if (voltage_max >= WLC_EPP_THRESHOLD_UV) { ad->ad_type = CHG_EV_ADAPTER_TYPE_WLC_EPP; - } else if (amperage_max >= WLC_BPP_THRESHOLD_UV) { + } else if (voltage_max >= WLC_BPP_THRESHOLD_UV) { ad->ad_type = CHG_EV_ADAPTER_TYPE_WLC_SPP; } @@ -617,13 +688,18 @@ static int info_ext_state(union gbms_ce_adapter_details *ad, return 0; if (voltage_max < 0 || amperage_max < 0) { - ad->ad_type = CHG_EV_ADAPTER_TYPE_UNKNOWN; + ad->ad_type = CHG_EV_ADAPTER_TYPE_EXT_UNKNOWN; ad->ad_voltage = voltage_max; ad->ad_amperage = amperage_max; return -EINVAL; + } else if (voltage_max > EXT1_DETECT_THRESHOLD_UV) { + ad->ad_type = CHG_EV_ADAPTER_TYPE_EXT1; + } else if (voltage_max > EXT2_DETECT_THRESHOLD_UV) { + ad->ad_type = CHG_EV_ADAPTER_TYPE_EXT2; + } else { + ad->ad_type = CHG_EV_ADAPTER_TYPE_EXT; } - ad->ad_type = CHG_EV_ADAPTER_TYPE_POGO; ad->ad_voltage = voltage_max / 100000; ad->ad_amperage = amperage_max / 100000; @@ -1395,7 +1471,6 @@ static void thermal_stats_init(struct thermal_stats_data *thermal_stats) { thermal_stats->ibatt_sum = 0; } -static int chg_work_read_soc(struct power_supply *bat_psy, int *soc); static void thermal_stats_work(struct chg_drv *chg_drv) { struct thermal_stats_data *thermal_stats = &chg_drv->thermal_stats; struct power_supply *bat_psy = chg_drv->bat_psy; @@ -1917,6 +1992,10 @@ static void bd_dd_run_defender(struct chg_drv *chg_drv, int soc, int *disable_ch bd_state->dd_triggered, (soc >= lowerbd)); + /* Start DD stats */ + if (bd_state->dd_state == DOCK_DEFEND_ACTIVE) + chg_stats_update(chg_drv, &chg_drv->dd_stats); + /* need icl_ramp_work when disable_pwrsrc 1 -> 0 */ if (!*disable_pwrsrc && chg_drv->disable_pwrsrc) { struct power_supply *dc_psy; @@ -1986,6 +2065,11 @@ static int chg_run_defender(struct chg_drv *chg_drv) /* force TEMP-DEFEND off */ chg_drv->bd_state.enabled = 0; + /* set dd_state to inactive state (DOCK_DEFEND_ENABLED) */ + if (chg_drv->bd_state.dd_enabled) + chg_drv->bd_state.dd_state = bd_dd_state_update(chg_drv->bd_state.dd_state, + false, false); + } else if (chg_drv->bd_state.enabled) { const bool was_triggered = bd_state->triggered; @@ -2027,6 +2111,11 @@ static int chg_run_defender(struct chg_drv *chg_drv) was_triggered, chg_drv->stop_charging, lock_soc); } + + /* set dd_state to inactive state (DOCK_DEFEND_ENABLED) */ + if (bd_state->dd_enabled) + bd_state->dd_state = bd_dd_state_update(bd_state->dd_state, + false, false); } /* run dock_defend */ if (!bd_state->triggered && bd_state->dd_enabled) @@ -2264,6 +2353,14 @@ static void chg_work(struct work_struct *work) const int upperbd = chg_drv->charge_stop_level; const int lowerbd = chg_drv->charge_start_level; + /* + * Update DD stats last time if DD is active. + * NOTE: *** Ensure this is done before disconnect indication to google_battery + */ + if (chg_drv->dd_stats.vtier_idx == GBMS_STATS_BD_TI_DOCK && + chg_drv->bd_state.dd_state == DOCK_DEFEND_ACTIVE) + chg_stats_update(chg_drv, &chg_drv->dd_stats); + /* reset dock_defend */ if (chg_drv->bd_state.dd_triggered) { chg_update_charging_state(chg_drv, false, false); @@ -2318,7 +2415,7 @@ static void chg_work(struct work_struct *work) goto exit_chg_work; } else { - // Run thermal stats when connected to power (preset || online) + /* Run thermal stats when connected to power (preset || online) */ thermal_stats_work(chg_drv); if (chg_drv->stop_charging != 0 && present) { @@ -2360,6 +2457,11 @@ static void chg_work(struct work_struct *work) rc = 0; } + /* Book dd stats to correct charging type if DD active */ + if (chg_drv->dd_stats.vtier_idx == GBMS_STATS_BD_TI_DOCK && + chg_drv->bd_state.dd_state == DOCK_DEFEND_ACTIVE) + chg_stats_update(chg_drv, &chg_drv->dd_stats); + /* * chg_drv->disable_pwrsrc -> chg_drv->disable_charging * update_interval = 0 will reschedule if TEMP-DEFEND is enabled @@ -3147,6 +3249,13 @@ static ssize_t set_dd_settings(struct device *dev, struct device_attribute *attr if (chg_drv->bd_state.dd_settings != val) { chg_drv->bd_state.dd_settings = val; + + /* Update DD stats tier. Book stats till now to DOCK tier */ + if (DOCK_DEFEND_USER_CLEARED == chg_drv->bd_state.dd_settings) { + chg_stats_update(chg_drv, &chg_drv->dd_stats); + chg_drv->dd_stats.vtier_idx = GBMS_STATS_BD_TI_DOCK_CLEARED; + } + if (chg_drv->bat_psy) power_supply_changed(chg_drv->bat_psy); } @@ -3659,6 +3768,68 @@ static ssize_t thermal_stats_store(struct device *dev, static DEVICE_ATTR_RW(thermal_stats); +static ssize_t +thermal_dc_fan_alarm_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct chg_drv *chg_drv = dev_get_drvdata(dev); + struct chg_thermal_device *ctdev_dcin = &chg_drv->thermal_devices[CHG_TERMAL_DEVICE_DC_IN]; + int value = ctdev_dcin->therm_fan_alarm_level; + + return scnprintf(buf, PAGE_SIZE, "%d\n", value);; +} + +static ssize_t thermal_dc_fan_alarm_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct chg_drv *chg_drv = dev_get_drvdata(dev); + struct chg_thermal_device *ctdev_dcin = &chg_drv->thermal_devices[CHG_TERMAL_DEVICE_DC_IN]; + int ret = 0; + u32 value; + + ret = kstrtou32(buf, 0, &value); + if (ret < 0) + return ret; + + if (value <= ctdev_dcin->thermal_levels) + ctdev_dcin->therm_fan_alarm_level = value; + + return count; +} + +static DEVICE_ATTR_RW(thermal_dc_fan_alarm); + +static ssize_t +charge_stats_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct chg_drv *chg_drv = dev_get_drvdata(dev); + struct gbms_ce_tier_stats *dd_stats = &chg_drv->dd_stats; + ssize_t len; + + mutex_lock(&chg_drv->stats_lock); + len = gbms_tier_stats_cstr(buf, PAGE_SIZE, dd_stats, false); + mutex_unlock(&chg_drv->stats_lock); + + return len; +} + +static ssize_t charge_stats_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct chg_drv *chg_drv = dev_get_drvdata(dev); + + if (count < 1) + return -ENODATA; + + if (buf[0] == '0') + chg_stats_init(chg_drv, &chg_drv->dd_stats, GBMS_STATS_BD_TI_DOCK); + + return count; +} + +static DEVICE_ATTR_RW(charge_stats); + static int chg_init_fs(struct chg_drv *chg_drv) { int ret; @@ -3780,6 +3951,18 @@ static int chg_init_fs(struct chg_drv *chg_drv) return ret; } + ret = device_create_file(chg_drv->device, &dev_attr_thermal_dc_fan_alarm); + if (ret != 0) { + pr_err("Failed to create thermal_dc_fan_alarm, ret=%d\n", ret); + return ret; + } + + ret = device_create_file(chg_drv->device, &dev_attr_charge_stats); + if (ret != 0) { + pr_err("Failed to create charge_stats files, ret=%d\n", ret); + return ret; + } + /* dock_defend */ if (chg_drv->ext_psy_name) { ret = device_create_file(chg_drv->device, &dev_attr_dd_state); @@ -4076,7 +4259,7 @@ static int msc_pwr_disable_cb(struct gvotable_election *el, if (!chg_drv->chg_psy) return 0; - chg_vote_input_suspend(chg_drv, MSC_CHG_VOTER, pwr_disable); + chg_vote_input_suspend(chg_drv, MSC_PWR_VOTER, pwr_disable); return 0; } @@ -4244,10 +4427,11 @@ static void chg_init_votables(struct chg_drv *chg_drv) static int fan_get_level(struct chg_thermal_device *tdev) { int level = FAN_LVL_UNKNOWN; + int alarm_level = tdev->therm_fan_alarm_level; if (tdev->current_level == 0) level = FAN_LVL_NOT_CARE; - else if (tdev->current_level == tdev->thermal_levels) + else if (tdev->current_level >= alarm_level) level = FAN_LVL_ALARM; else level = FAN_LVL_MED; @@ -4790,6 +4974,12 @@ static int chg_tdev_init(struct chg_thermal_device *tdev, const char *name, return -ENODATA; } + rc = of_property_read_u32(chg_drv->device->of_node, + "google,wlc-thermal-dc-fan-alarm", + &tdev->therm_fan_alarm_level); + if (rc < 0) + tdev->therm_fan_alarm_level = tdev->thermal_levels; + tdev->chg_drv = chg_drv; return 0; @@ -5060,7 +5250,6 @@ static void google_charger_init_work(struct work_struct *work) struct power_supply *chg_psy = NULL, *usb_psy = NULL; struct power_supply *wlc_psy = NULL, *bat_psy = NULL; struct power_supply *ext_psy = NULL, *tcpm_psy = NULL; - bool pps_enable; int ret = 0; chg_psy = psy_get_by_name(chg_drv, chg_drv->chg_psy_name); @@ -5110,22 +5299,25 @@ static void google_charger_init_work(struct work_struct *work) chg_drv->tcpm_psy = tcpm_psy; /* PPS negotiation handled in google_charger */ - pps_enable = of_property_read_bool(chg_drv->device->of_node, - "google,pps-enable"); if (!tcpm_psy) { pr_info("PPS not available\n"); - } else if (!pps_enable) { - pr_info("PPS not enabled\n"); } else { const char *name = tcpm_psy->desc->name; + const bool pps_enable = of_property_read_bool(chg_drv->device->of_node, + "google,pps-enable"); ret = pps_init(&chg_drv->pps_data, chg_drv->device, tcpm_psy); - if (ret == 0 && chg_drv->debug_entry) - pps_init_fs(&chg_drv->pps_data, chg_drv->debug_entry); - if (ret < 0) + if (ret < 0) { pr_err("PPS init failure for %s (%d)\n", name, ret); - else + } else if (pps_enable) { + if (chg_drv->debug_entry) + pps_init_fs(&chg_drv->pps_data, chg_drv->debug_entry); + chg_drv->pps_enable = true; pr_info("PPS available for %s\n", name); + } else { + chg_drv->pps_data.stage = PPS_DISABLED; + pr_info("PPS not enabled\n"); + } } ret = chg_thermal_device_init(chg_drv); @@ -5142,14 +5334,17 @@ static void google_charger_init_work(struct work_struct *work) chg_drv->charge_start_level = DEFAULT_CHARGE_START_LEVEL; mutex_init(&chg_drv->thermal_stats.lock); thermal_stats_init(&chg_drv->thermal_stats); + mutex_init(&chg_drv->stats_lock); /* reset override charging parameters */ chg_drv->user_fv_uv = -1; chg_drv->user_cc_max = -1; /* dock_defend */ - if (chg_drv->ext_psy) + if (chg_drv->ext_psy) { bd_dd_init(chg_drv); + chg_stats_init(chg_drv, &chg_drv->dd_stats, GBMS_STATS_BD_TI_DOCK); + } chg_drv->psy_nb.notifier_call = chg_psy_changed; ret = power_supply_reg_notifier(&chg_drv->psy_nb); diff --git a/google_cpm.c b/google_cpm.c index 9fc7644..3339efe 100644 --- a/google_cpm.c +++ b/google_cpm.c @@ -124,6 +124,7 @@ struct mdis_thermal_device u32 *thermal_mitigation; int thermal_levels; int current_level; + int therm_fan_alarm_level; }; struct gcpm_drv { @@ -153,6 +154,7 @@ struct gcpm_drv { /* MDIS: device and current budget */ struct mdis_thermal_device thermal_device; struct gvotable_election *mdis_votable; + struct gvotable_election *fan_level_votable; /* CSI */ struct gvotable_election *csi_status_votable; @@ -252,6 +254,8 @@ struct gcpm_drv { /* debug fs */ struct dentry *debug_entry; + + int wlc_dc_fcc; }; #define gcpm_psy_name(psy) \ @@ -696,6 +700,12 @@ static int gcpm_dc_start(struct gcpm_drv *gcpm, int index) if (ret < 0) pr_debug("PPS_DC: start cannot update votes (%d)\n", ret); + if (gcpm->pps_index == PPS_INDEX_WLC) { + gcpm_update_gcpm_fcc(gcpm, "WLC_FCC", gcpm->wlc_dc_fcc, gcpm->wlc_dc_fcc > 0); + pr_info("PPS_DC: index=%d vote gcpm_fcc to %d\n", + gcpm->pps_index, gcpm->wlc_dc_fcc); + } + /* this is the CP */ dc_psy = gcpm_chg_get_active_cp(gcpm); if (!dc_psy) { @@ -956,6 +966,7 @@ exit_done: static int gcpm_chg_select(struct gcpm_drv *gcpm) { int index = GCPM_DEFAULT_CHARGER; + int ret; if (!gcpm->dc_init_complete) goto exit_done; /* index == GCPM_DEFAULT_CHARGER; */ @@ -968,6 +979,11 @@ static int gcpm_chg_select(struct gcpm_drv *gcpm) if (gcpm->dc_ctl == GCPM_DC_CTL_DISABLE_BOTH) goto exit_done; /* index == GCPM_DEFAULT_CHARGER; */ + ret = gvotable_get_current_int_vote(gcpm->dc_chg_avail_votable); + dev_dbg(gcpm->device, "%s: dc_chg_avail vote: %d\n", __func__, ret); + if (ret <= 0) + goto exit_done; + /* * check demand first to react to thermal engine, then voltage to * make sure that we are over min and that we don't start over high @@ -1001,18 +1017,11 @@ exit_done: static bool gcpm_chg_dc_check_source(const struct gcpm_drv *gcpm, int index) { - int ret; - /* Will run detection only the first time */ if (gcpm->tcpm_pps_data.stage == PPS_NOTSUPP && gcpm->wlc_pps_data.stage == PPS_NOTSUPP ) return false; - ret = gvotable_get_current_int_vote(gcpm->dc_chg_avail_votable); - dev_dbg(gcpm->device, "%s: dc_chg_avail vote: %d\n", __func__, ret); - if (ret <= 0) - return false; - return gcpm_is_dc(gcpm, index); } @@ -1469,6 +1478,9 @@ static int gcpm_pps_wlc_dc_restart_default(struct gcpm_drv *gcpm) if (ret < 0) pr_err("PPS_DC: wlc_dc_rd cannot update votes (%d)\n", ret); + pr_debug("PPS_DC: gcpm_update_gcpm_fcc unvote\n"); + gcpm_update_gcpm_fcc(gcpm, "WLC_FCC", 0, false); + /* Clear taper count if not complete */ gcpm_taper_ctl(gcpm, 0); @@ -2315,6 +2327,34 @@ static ssize_t dc_ctl_store(struct device *dev, } static DEVICE_ATTR_RW(dc_ctl); +static ssize_t thermal_mdis_fan_alarm_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct gcpm_drv *gcpm = dev_get_drvdata(dev); + + return scnprintf(buf, PAGE_SIZE, "%d\n", gcpm->thermal_device.therm_fan_alarm_level); +} + +static ssize_t thermal_mdis_fan_alarm_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct gcpm_drv *gcpm = dev_get_drvdata(dev); + int ret = 0; + u32 value; + + ret = kstrtou32(buf, 0, &value); + if (ret < 0) + return ret; + + if (value <= gcpm->thermal_device.thermal_levels) + gcpm->thermal_device.therm_fan_alarm_level = value; + + return count; +} +static DEVICE_ATTR_RW(thermal_mdis_fan_alarm); + /* ------------------------------------------------------------------------ */ static int gcpm_get_max_charge_cntl_limit(struct thermal_cooling_device *tcd, @@ -2335,6 +2375,40 @@ static int gcpm_get_cur_charge_cntl_limit(struct thermal_cooling_device *tcd, return 0; } +#define FAN_MDIS_ALARM_DEFAULT 3 +static int fan_get_level(struct mdis_thermal_device *tdev) +{ + int fan_level = FAN_LVL_UNKNOWN; + + if (tdev->current_level <= 0) + fan_level = FAN_LVL_NOT_CARE; + else if (tdev->current_level >= tdev->therm_fan_alarm_level) + fan_level = FAN_LVL_ALARM; + else + fan_level = FAN_LVL_MED; + + return fan_level; +} + +static int gcpm_mdis_update_fan(struct gcpm_drv *gcpm) +{ + int ret = 0; + + if (!gcpm->fan_level_votable) + gcpm->fan_level_votable = gvotable_election_get_handle(VOTABLE_FAN_LEVEL); + + if (gcpm->fan_level_votable) { + const int level = fan_get_level(&gcpm->thermal_device); + + ret = gvotable_cast_int_vote(gcpm->fan_level_votable, "THERMAL_MDIS", + level, true); + if (ret < 0) + pr_err("%s: cannot update fan level (%d)", __func__, ret); + } + + return ret; +} + static inline int mdis_cast_vote(struct gvotable_election *el, int vote, bool enabled) { int ret = 0; @@ -2428,7 +2502,7 @@ static int gcpm_mdis_update_limits(struct gcpm_drv *gcpm, int msc_fcc, if (dc_icl != 0 && dc_icl_votable) { int wlc_state; - /* need to set online WLC if not onlne */ + /* need to set online WLC if not online */ wlc_state = mdis_set_wlc_online(gcpm); if (wlc_state == PPS_PSY_OFFLINE) dev_err(gcpm->device, "MDIS: WLC offine\n"); @@ -2903,6 +2977,8 @@ static int gcpm_mdis_callback(struct gvotable_election *el, const char *reason, } } + gcpm_mdis_update_fan(gcpm); + if (!gcpm->csi_status_votable) { gcpm->csi_status_votable = gvotable_election_get_handle(VOTABLE_CSI_STATUS); if (!gcpm->csi_status_votable) @@ -3022,6 +3098,11 @@ static int gcpm_init_mdis(struct gcpm_drv *gcpm) gcpm->mdis_out_limits[i] = limits; } + ret = of_property_read_u32(gcpm->device->of_node, "google,mdis-fan-alarm-level", + &tdev->therm_fan_alarm_level); + if (ret < 0) + tdev->therm_fan_alarm_level = FAN_MDIS_ALARM_DEFAULT; + /* mdis thermal engine uses this callback */ gcpm->mdis_votable = gvotable_create_int_election(NULL, gvotable_comparator_int_min, @@ -3894,6 +3975,10 @@ static int google_cpm_probe(struct platform_device *pdev) &gcpm->taper_step_current); if (ret < 0) gcpm->taper_step_current = GCPM_TAPER_STEP_CURRENT; + ret = of_property_read_u32(pdev->dev.of_node, "google,wlc-dc-fcc-ua", + &gcpm->wlc_dc_fcc); + if (ret < 0) + gcpm->wlc_dc_fcc = 0; dev_info(gcpm->device, "taper ts_m=%d ts_ccs=%d ts_i=%d ts_cnt=%d ts_g=%d ts_v=%d ts_c=%d\n", gcpm->taper_step_fv_margin, gcpm->taper_step_cc_step, @@ -4006,6 +4091,10 @@ static int google_cpm_probe(struct platform_device *pdev) if (ret) dev_err(gcpm->device, "Failed to create dc_crl\n"); + ret = device_create_file(gcpm->device, &dev_attr_thermal_mdis_fan_alarm); + if (ret) + dev_err(gcpm->device, "Failed to create thermal_mdis_fan_alarm\n"); + /* give time to fg driver to start */ schedule_delayed_work(&gcpm->init_work, msecs_to_jiffies(INIT_DELAY_MS)); diff --git a/google_dock.c b/google_dock.c index 05b1b41..3d6b387 100644 --- a/google_dock.c +++ b/google_dock.c @@ -42,6 +42,10 @@ #define DOCK_13_5W_VOUT_UV 9000000 #define DOCK_ICL_RAMP_DELAY_DEFAULT_MS (4 * 1000) /* 4 seconds */ +/* type detection */ +#define EXT_DETECT_DELAY_MS (1000) +#define EXT_DETECT_RETRIES (3) + struct dock_drv { struct device *device; struct power_supply *psy; @@ -51,6 +55,7 @@ struct dock_drv { struct delayed_work init_work; struct delayed_work notifier_work; struct delayed_work icl_ramp_work; + struct delayed_work detect_work; struct alarm icl_ramp_alarm; struct notifier_block nb; struct gvotable_election *dc_icl_votable; @@ -63,11 +68,39 @@ struct dock_drv { u32 icl_ramp_delay_ms; int online; int pogo_ovp_en; - int pogo_acc_gpio; - int pogo_acc_irq; + int voltage_max; /* > 10.5V mean Ext1 else > 5V mean Ext2. */ + int detect_retries; + struct wakeup_source *detect_ws; }; -static bool google_dock_find_mode_votable(struct dock_drv *dock); +/* ------------------------------------------------------------------------- */ + +static bool google_dock_find_mode_votable(struct dock_drv *dock) +{ + if (!dock->chg_mode_votable) { + dock->chg_mode_votable = gvotable_election_get_handle(GBMS_MODE_VOTABLE); + if (!dock->chg_mode_votable) { + dev_err(dock->device, "Could not get CHARGER_MODE votable\n"); + return false; + } + } + + return true; +} + +static int google_dock_set_pogo_vout(struct dock_drv *dock, + int enabled) +{ + if (!google_dock_find_mode_votable(dock)) + return -EINVAL; + + dev_dbg(dock->device, "pogo_vout_enabled=%d\n", enabled); + + return gvotable_cast_long_vote(dock->chg_mode_votable, + DOCK_VOUT_VOTER, + GBMS_CHGR_MODE_VOUT, + enabled != 0); +} /* ------------------------------------------------------------------------- */ static ssize_t is_dock_show(struct device *dev, @@ -87,15 +120,14 @@ static DEVICE_ATTR_RO(is_dock); static int debug_pogo_vout_write(void *data, u64 val) { struct dock_drv *dock = (struct dock_drv *)data; + int ret; if (val < 0 || val > 1) return -EINVAL; - if (google_dock_find_mode_votable(dock)) - gvotable_cast_long_vote(dock->chg_mode_votable, - DOCK_VOUT_VOTER, - GBMS_CHGR_MODE_VOUT, - val != 0); + ret = google_dock_set_pogo_vout(dock, val); + if (ret) + dev_err(dock->device, "Failed to set pogo vout: %d\n", ret); return 0; } @@ -265,9 +297,13 @@ static void google_dock_notifier_check_dc(struct dock_drv *dock) google_dock_set_icl(dock); google_dock_icl_ramp_reset(dock); google_dock_icl_ramp_start(dock); + dock->voltage_max = -1; /* Dock detection started, but not done */ + schedule_delayed_work(&dock->detect_work, + msecs_to_jiffies(EXT_DETECT_DELAY_MS)); } else { google_dock_vote_defaults(dock); google_dock_icl_ramp_reset(dock); + dock->voltage_max = 0; dev_info(dock->device, "%s: online: %d->0\n", __func__, dock->online); @@ -328,99 +364,6 @@ static int google_dock_parse_dt(struct device *dev, return 0; } -static bool google_dock_find_mode_votable(struct dock_drv *dock) -{ - if (!dock->chg_mode_votable) { - dock->chg_mode_votable = gvotable_election_get_handle(GBMS_MODE_VOTABLE); - if (!dock->chg_mode_votable) { - dev_err(dock->device, "Could not get CHARGER_MODE votable\n"); - return false; - } - } - - return true; -} - -static irqreturn_t pogo_acc_irq(int irq, void *irq_data) -{ - struct dock_drv *dock = irq_data; - - dev_info(dock->device, "POGO IRQ triggered\n"); - - /* - * TODO: b/237977206, rework board doesn't have accessory circuit. - * use /d/google_dock/pogo_vout as WA instead when accessory - * is attached/detected. - */ - if (google_dock_find_mode_votable(dock)) { - int gpio_en; - - gpio_en = gpio_get_value_cansleep(dock->pogo_acc_gpio); - gvotable_cast_long_vote(dock->chg_mode_votable, - DOCK_VOUT_VOTER, - GBMS_CHGR_MODE_VOUT, - gpio_en != 0); - } - - return IRQ_HANDLED; -} - -static int google_dock_power_out_init(struct device *dev, - struct dock_drv *dock) -{ - int ret; - struct device_node *node = dev->of_node; - - /* Accessory Detect IRQ */ - dock->pogo_acc_gpio = of_get_named_gpio(node, "google,pogo-accessory-detect", 0); - if (dock->pogo_acc_gpio < 0) { - dev_warn(dev, "pogo accessory detect gpio not found ret:%d\n", - dock->pogo_acc_gpio); - return dock->pogo_acc_gpio; - } - - ret = devm_gpio_request(dev, dock->pogo_acc_gpio, "google,pogo-accessory-detect"); - if (ret) { - dev_err(dev, "failed to request pogo-accessory-detect gpio, ret:%d\n", ret); - return ret; - } - - ret = gpio_direction_input(dock->pogo_acc_gpio); - if (ret) { - dev_err(dev, "failed set pogo-accessory-detect as input, ret:%d\n", ret); - return ret; - } - - dock->pogo_acc_irq = gpio_to_irq(dock->pogo_acc_gpio); - if (dock->pogo_acc_irq <= 0) { - dev_err(dev, "Pogo irq not found\n"); - return -ENODEV; - } - - ret = devm_request_threaded_irq(dev, dock->pogo_acc_irq, - NULL, pogo_acc_irq, - (IRQF_SHARED | IRQF_ONESHOT | - IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING), - dev_name(dev), dock); - if (ret < 0) { - dev_err(dev, "pogo-accessory-detect request irq failed ret:%d\n", ret); - return ret; - } - - ret = enable_irq_wake(dock->pogo_acc_irq); - if (ret) { - dev_err(dev, "Enable irq wake failed ret:%d\n", ret); - goto free_irq; - } - - return 0; - -free_irq: - devm_free_irq(dev, dock->pogo_acc_irq, dock); - - return ret; -} - static enum power_supply_property dock_props[] = { POWER_SUPPLY_PROP_PRESENT, POWER_SUPPLY_PROP_ONLINE, @@ -465,6 +408,10 @@ static int dock_get_property(struct power_supply *psy, ret = 0; break; + case POWER_SUPPLY_PROP_VOLTAGE_MAX: + val->intval = dock->voltage_max; + break; + default: ret = power_supply_get_property(dock->dc_psy, psp, val); break; @@ -505,6 +452,9 @@ static int dock_set_property(struct power_supply *psy, val->intval, true); changed = true; break; + case GBMS_PROP_POGO_VOUT_ENABLED: + ret = google_dock_set_pogo_vout(dock, val->intval); + break; default: return -EINVAL; } @@ -523,6 +473,7 @@ static int dock_property_is_writeable(struct power_supply *psy, { switch (psp) { case POWER_SUPPLY_PROP_CURRENT_MAX: + case GBMS_PROP_POGO_VOUT_ENABLED: return 1; default: break; @@ -582,6 +533,52 @@ retry_init_work: msecs_to_jiffies(DOCK_DELAY_INIT_MS)); } +static void google_dock_detect_work(struct work_struct *work) +{ + struct dock_drv *dock = container_of(work, struct dock_drv, + detect_work.work); + union power_supply_propval val; + int err = 0; + + if (!dock->dc_psy) + return; + + __pm_stay_awake(dock->detect_ws); + err = power_supply_get_property(dock->dc_psy, POWER_SUPPLY_PROP_PRESENT, &val); + if (err < 0 || val.intval == 0) { + if (err < 0) + dev_dbg(dock->device, "Error getting charging status: %d\n", err); + else + dev_dbg(dock->device, "dc_psy not present. Retrying detection\n"); + goto dock_detect_retry; + } + + err = power_supply_get_property(dock->dc_psy, POWER_SUPPLY_PROP_VOLTAGE_NOW, &val); + if (err) { + if (err != -EAGAIN) + dev_dbg(dock->device, "failed to read dc supply voltage err: %d\n", err); + + goto dock_detect_retry; + } + + dev_info(dock->device, "dc_psy v=%d, retries=%d\n", val.intval, dock->detect_retries); + + dock->voltage_max = val.intval; + dock->detect_retries = EXT_DETECT_RETRIES; + __pm_relax(dock->detect_ws); + power_supply_changed(dock->psy); + return; + +dock_detect_retry: + if (dock->detect_retries) { + dock->detect_retries--; + schedule_delayed_work(&dock->detect_work, msecs_to_jiffies(EXT_DETECT_DELAY_MS)); + } else { + dock->detect_retries = EXT_DETECT_RETRIES; + __pm_relax(dock->detect_ws); + } +} + static int google_dock_probe(struct platform_device *pdev) { const char *dc_psy_name; @@ -611,12 +608,22 @@ static int google_dock_probe(struct platform_device *pdev) mutex_init(&dock->dock_lock); INIT_DELAYED_WORK(&dock->init_work, google_dock_init_work); INIT_DELAYED_WORK(&dock->icl_ramp_work, google_dock_icl_ramp_work); + INIT_DELAYED_WORK(&dock->detect_work, google_dock_detect_work); alarm_init(&dock->icl_ramp_alarm, ALARM_BOOTTIME, google_dock_icl_ramp_alarm_cb); dock->icl_ramp_delay_ms = DOCK_ICL_RAMP_DELAY_DEFAULT_MS; dock->online = 0; + dock->voltage_max = 0; + dock->detect_retries = EXT_DETECT_RETRIES; + + dock->detect_ws = wakeup_source_register(NULL, "Detect"); + if (!dock->detect_ws) { + dev_err(dock->device, "Failed to register dock detect wakeup source\n"); + return -ENOMEM; + } + platform_set_drvdata(pdev, dock); psy_cfg.drv_data = dock; @@ -653,10 +660,6 @@ static int google_dock_probe(struct platform_device *pdev) return ret; } - ret = google_dock_power_out_init(dock->device, dock); - if (ret) - dev_warn(dock->device, "Fail to init pogo power out: %d\n", ret); - if (dock->pogo_ovp_en >= 0) gpio_direction_output(dock->pogo_ovp_en, 1); @@ -676,6 +679,8 @@ static int google_dock_remove(struct platform_device *pdev) cancel_delayed_work(&dock->notifier_work); cancel_delayed_work(&dock->icl_ramp_work); alarm_try_to_cancel(&dock->icl_ramp_alarm); + cancel_delayed_work(&dock->detect_work); + wakeup_source_unregister(dock->detect_ws); return 0; } diff --git a/google_dual_batt_gauge.c b/google_dual_batt_gauge.c index b7e4848..33297bf 100644 --- a/google_dual_batt_gauge.c +++ b/google_dual_batt_gauge.c @@ -52,6 +52,8 @@ struct dual_fg_drv { struct gbms_chg_profile chg_profile; + struct notifier_block fg_nb; + u32 battery_capacity; int cc_max; @@ -326,6 +328,17 @@ static int gdbatt_get_property(struct power_supply *psy, /* TODO: need hash SN */ val->strval = fg_1.strval; break; + /* support bhi */ + case GBMS_PROP_HEALTH_ACT_IMPEDANCE: + case GBMS_PROP_HEALTH_IMPEDANCE: + case GBMS_PROP_RESISTANCE: + case GBMS_PROP_RESISTANCE_RAW: + case GBMS_PROP_RESISTANCE_AVG: + case GBMS_PROP_BATTERY_AGE: + case GBMS_PROP_CHARGE_FULL_ESTIMATE: + case GBMS_PROP_CAPACITY_FADE_RATE: + val->intval = fg_1.intval; + break; default: pr_debug("getting unsupported property: %d\n", psp); break; @@ -375,6 +388,14 @@ static int gdbatt_set_property(struct power_supply *psy, } dual_fg_drv->cable_in = !!val->intval; break; + case GBMS_PROP_HEALTH_ACT_IMPEDANCE: + /* TODO: discuss with BattEng to decide save data */ + /* if (dual_fg_drv->first_fg_psy) { + ret = GPSY_SET_PROP(dual_fg_drv->first_fg_psy, psp, val->intval); + if (ret < 0) + pr_err("Cannot set the first HEALTH_ACT_IMPEDANCE, ret=%d\n", ret); + } */ + break; default: return -EINVAL; } @@ -435,6 +456,31 @@ static int gdbatt_init_chg_profile(struct dual_fg_drv *dual_fg_drv) return ret; } +static int psy_changed(struct notifier_block *nb, + unsigned long action, void *data) +{ + struct power_supply *psy = data; + struct dual_fg_drv *dual_fg_drv = container_of(nb, struct dual_fg_drv, fg_nb); + + if ((action != PSY_EVENT_PROP_CHANGED) || (psy == NULL) || (psy->desc == NULL) || + (psy->desc->name == NULL)) + return NOTIFY_OK; + + pr_debug("name=%s evt=%lu\n", psy->desc->name, action); + + if (action == PSY_EVENT_PROP_CHANGED) { + bool is_first_fg = (dual_fg_drv->first_fg_psy_name != NULL) && + !strcmp(psy->desc->name, dual_fg_drv->first_fg_psy_name); + bool is_second_fg = (dual_fg_drv->second_fg_psy_name != NULL) && + !strcmp(psy->desc->name, dual_fg_drv->second_fg_psy_name); + + if (is_first_fg || is_second_fg) + power_supply_changed(dual_fg_drv->psy); + } + + return NOTIFY_OK; +} + static void google_dual_batt_gauge_init_work(struct work_struct *work) { struct dual_fg_drv *dual_fg_drv = container_of(work, struct dual_fg_drv, @@ -494,6 +540,11 @@ static void google_dual_batt_gauge_init_work(struct work_struct *work) if (err < 0) dev_info(dual_fg_drv->device,"fail to init chg profile (%d)\n", err); + dual_fg_drv->fg_nb.notifier_call = psy_changed; + err = power_supply_reg_notifier(&dual_fg_drv->fg_nb); + if (err < 0) + pr_err("cannot register power supply notifer (%d)\n", err); + dual_fg_drv->init_complete = true; dual_fg_drv->resume_complete = true; mod_delayed_work(system_wq, &dual_fg_drv->gdbatt_work, 0); 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 @@ int gbee_storage_info(gbms_tag_t tag, size_t *addr, size_t *count, void *ptr) *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 @@ static int gbee_storage_iter(int index, gbms_tag_t *tag, void *ptr) 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 @@ static bool gbee_storage_is_writable(gbms_tag_t tag) 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 3a26a1d..784c8c0 100644 --- a/max1720x_battery.c +++ b/max1720x_battery.c @@ -259,6 +259,9 @@ struct max1720x_chip { int bhi_acim; struct max1720x_rc_switch rc_switch; + + /* support no_battery */ + bool no_battery; }; #define MAX1720_EMPTY_VOLTAGE(profile, temp, cycle) \ @@ -266,6 +269,7 @@ struct max1720x_chip { static irqreturn_t max1720x_fg_irq_thread_fn(int irq, void *obj); +static int max1720x_set_next_update(struct max1720x_chip *chip); static bool max17x0x_reglog_init(struct max1720x_chip *chip) { @@ -1633,6 +1637,13 @@ static int max1720x_get_cycle_count(struct max1720x_chip *chip) chip->cycle_count = cycle_count + chip->cycle_count_offset; + if (chip->model_ok && reg_cycle >= chip->model_next_update) { + err = max1720x_set_next_update(chip); + if (err < 0) + dev_err(chip->dev, "%s cannot set next update (%d)\n", + __func__, err); + } + return chip->cycle_count; } @@ -2830,13 +2841,17 @@ static irqreturn_t max1720x_fg_irq_thread_fn(int irq, void *obj) if (fg_status & MAX1720X_STATUS_POR) { 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 (chip->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); } @@ -3509,6 +3524,9 @@ static int max1720x_init_model(struct max1720x_chip *chip) if (chip->gauge_type != MAX_M5_GAUGE_TYPE) return 0; + if (chip->no_battery) + return 0; + /* ->batt_id negative for no lookup */ if (chip->batt_id >= 0) { chip->batt_node = max1720x_find_batt_node(chip); @@ -4281,8 +4299,6 @@ static int max1720x_model_load(struct max1720x_chip *chip) if (ret < 0) dev_err(chip->dev, "Load Model fixing drift data rc=%d\n", ret); - /* The caller need to call max1720x_set_next_update() */ - /* mark model state as "safe" */ chip->reg_prop_capacity_raw = MAX1720X_REPSOC; chip->model_state_valid = true; @@ -4335,14 +4351,6 @@ static void max1720x_model_work(struct work_struct *work) if (max1720x_check_drift_enabled(&chip->drift_data)) max1720x_fixup_capacity(chip, chip->cap_estimate.cable_in); - /* save state only when model is running */ - if (chip->model_ok) { - rc = max1720x_set_next_update(chip); - if (rc < 0) - dev_err(chip->dev, "%s cannot set next update (%d)\n", - __func__, rc); - } - if (chip->model_reload >= MAX_M5_LOAD_MODEL_REQUEST) { const unsigned long delay = msecs_to_jiffies(60 * 1000); @@ -4678,6 +4686,9 @@ static int max1720x_init_max_m5(struct max1720x_chip *chip) { 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); @@ -5846,6 +5857,7 @@ static int max1720x_probe(struct i2c_client *client, chip->dev = dev; chip->fake_battery = of_property_read_bool(dev->of_node, "maxim,no-battery") ? 0 : -1; + chip->no_battery = of_property_read_bool(dev->of_node, "maxim,no-battery"); chip->primary = client; chip->batt_id_defer_cnt = DEFAULT_BATTERY_ID_RETRIES; i2c_set_clientdata(client, chip); diff --git a/p9221_charger.c b/p9221_charger.c index 6937e75..0a76190 100644 --- a/p9221_charger.c +++ b/p9221_charger.c @@ -53,6 +53,13 @@ #define RTX_BEN_ENABLED 2 #define REENABLE_RTX_DELAY 3000 +#define P9XXX_CHK_RP_DELAY_MS 200 + +#define P9XXX_VOUT_5480MV 5480 +#define P9XXX_VOUT_5000MV 5000 +#define P9XXX_FOD_CHK_DELAY_MS 2000 + +#define P9XXX_VOUT_5480MV 5480 enum wlc_align_codes { WLC_ALIGN_CHECKING = 0, @@ -64,6 +71,7 @@ enum wlc_align_codes { enum wlc_chg_mode { WLC_BPP = 0, WLC_EPP, + WLC_EPP_COMP, WLC_HPP, WLC_HPP_HV, }; @@ -288,6 +296,30 @@ bool p9xxx_is_capdiv_en(struct p9221_charger_data *charger) return false; } +static int p9221_check_fod_by_fsw(struct p9221_charger_data *charger) +{ + const int freq_high_thres = charger->pdata->fod_fsw_high; + const int freq_low_thres = charger->pdata->fod_fsw_low; + u32 vout_mv = 0, freq_khz = 0; + int ret; + + ret = charger->chip_get_vout_max(charger, &vout_mv); + if (ret == 0 && vout_mv == P9XXX_VOUT_5000MV) + return WLC_BPP; + + if (!charger->is_mfg_google || freq_high_thres < 0 || freq_low_thres < 0) + return WLC_EPP; + + ret = charger->chip_get_op_freq(charger, &freq_khz); + if (ret != 0) + return WLC_EPP; + if (freq_khz < freq_low_thres || + (charger->fod_mode == WLC_EPP_COMP && freq_khz < freq_high_thres)) + return WLC_EPP_COMP; + + return WLC_EPP; +} + static void p9221_write_fod(struct p9221_charger_data *charger) { int mode = WLC_BPP; @@ -295,7 +327,7 @@ static void p9221_write_fod(struct p9221_charger_data *charger) int fod_count = charger->pdata->fod_num; int ret; int retries = 3; - static char *wlc_mode[] = { "BPP", "EPP", "HPP" , "HPP_HV" }; + static char *wlc_mode[] = { "BPP", "EPP", "EPP_COMP" , "HPP" , "HPP_HV" }; if (charger->no_fod) @@ -311,9 +343,16 @@ static void p9221_write_fod(struct p9221_charger_data *charger) fod = charger->pdata->fod; if (p9221_is_epp(charger) && charger->pdata->fod_epp_num) { - fod = charger->pdata->fod_epp; - fod_count = charger->pdata->fod_epp_num; mode = WLC_EPP; + if (charger->pdata->fod_fsw) + mode = p9221_check_fod_by_fsw(charger); + if (mode == WLC_EPP) { + fod = charger->pdata->fod_epp; + fod_count = charger->pdata->fod_epp_num; + } else if (mode == WLC_EPP_COMP) { + fod = charger->pdata->fod_epp_comp; + fod_count = charger->pdata->fod_epp_comp_num; + } } if (p9xxx_is_capdiv_en(charger) && charger->pdata->fod_hpp_num) { @@ -327,9 +366,8 @@ static void p9221_write_fod(struct p9221_charger_data *charger) } } - if (mode == charger->fod_mode && - (mode == WLC_HPP || mode == WLC_HPP_HV)) - return; + if (mode == charger->fod_mode) + goto done; charger->fod_mode = mode; @@ -361,7 +399,7 @@ static void p9221_write_fod(struct p9221_charger_data *charger) } if (memcmp(fod, fod_read, fod_count) == 0) - return; + goto done; p9221_hex_str(fod_read, fod_count, s, sizeof(s), 0); dev_err(&charger->client->dev, @@ -375,6 +413,10 @@ no_fod: dev_warn(&charger->client->dev, "FOD not set! bpp:%d epp:%d hpp:%d hpp_hv:%d r:%d\n", charger->pdata->fod_num, charger->pdata->fod_epp_num, charger->pdata->fod_hpp_num, charger->pdata->fod_hpp_hv_num, retries); +done: + if (charger->pdata->fod_fsw) + mod_delayed_work(system_wq, &charger->chk_fod_work, + msecs_to_jiffies(P9XXX_FOD_CHK_DELAY_MS)); } #define CC_DATA_LOCK_MS 250 @@ -457,7 +499,7 @@ static int p9221_send_csp(struct p9221_charger_data *charger, u8 stat) } if (charger->online) { - ret = p9221_reg_write_8(charger, P9221R5_CHARGE_STAT_REG, stat); + ret = p9221_reg_write_8(charger, charger->reg_csp_addr, stat); if (ret == 0) ret = charger->chip_set_cmd(charger, P9221R5_COM_SENDCSP); if (ret < 0) @@ -596,19 +638,56 @@ static int p9221_set_switch_reg(struct p9221_charger_data *charger, bool enable) #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); @@ -665,8 +744,12 @@ static int p9221_reset_wlc_dc(struct p9221_charger_data *charger) 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); @@ -696,6 +779,10 @@ static int p9221_reset_wlc_dc(struct p9221_charger_data *charger) p9221_write_fod(charger); } + ret = p9221_reg_write_8(charger, P9412_MOT_REG, P9412_MOT_65PCT); + if (ret < 0) + dev_warn(&charger->client->dev, "Fail to set MOT register(%d)\n", ret); + return ret; } @@ -872,6 +959,7 @@ static void p9221_set_offline(struct p9221_charger_data *charger) mutex_lock(&charger->stats_lock); charger->online = false; charger->online_at = 0; + charger->check_rp = RP_NOTSET; cancel_delayed_work(&charger->charge_stats_work); p9221_dump_charge_stats(charger); mutex_unlock(&charger->stats_lock); @@ -1062,7 +1150,7 @@ static void p9221_power_mitigation_work(struct work_struct *work) { struct p9221_charger_data *charger = container_of(work, struct p9221_charger_data, power_mitigation_work.work); - const u32 vout_5500mv = 5500; + int ret = 0; charger->wait_for_online = false; @@ -1077,17 +1165,19 @@ static void p9221_power_mitigation_work(struct work_struct *work) } if (!p9221_is_epp(charger)) { - if (charger->trigger_power_mitigation) { - dev_info(&charger->client->dev, "power_mitigate: change Vout to 5.5V\n"); - charger->chip_set_vout_max(charger, vout_5500mv); - dev_info(&charger->client->dev, "power_mitigate: write 0 to 0xF4\n"); - p9221_reg_write_8(charger, 0xF4, 0); - if (charger->ll_bpp_cep == 1) - p9221_ll_bpp_cep(charger, charger->last_capacity); + /* only for LL */ + if (charger->trigger_power_mitigation && charger->ll_bpp_cep == 1) { + dev_info(&charger->client->dev, + "power_mitigate: change Vout to %d mV and disable CMFET\n", + P9XXX_VOUT_5480MV); + ret = charger->chip_set_vout_max(charger, P9XXX_VOUT_5480MV); + ret |= p9221_reg_write_8(charger, P9412_CMFET_L_REG, 0); + if (ret < 0) + dev_err(&charger->client->dev, "Fail to configure LL\n"); + p9221_ll_bpp_cep(charger, charger->last_capacity); } charger->fod_cnt = 0; - dev_info(&charger->client->dev, - "power_mitigate: already BPP\n"); + dev_info(&charger->client->dev, "power_mitigate: already BPP\n"); return; } @@ -2152,6 +2242,7 @@ static int p9221_set_psy_online(struct p9221_charger_data *charger, int online) if (online == PPS_PSY_PROG_ONLINE) { const int extben_gpio = charger->pdata->ext_ben_gpio; bool feat_enable; + u8 val8; pr_info("%s: online=%d, enabled=%d wlc_dc_enabled=%d prop_mode_en=%d\n", __func__, online, enabled, wlc_dc_enabled, @@ -2177,7 +2268,8 @@ static int p9221_set_psy_online(struct p9221_charger_data *charger, int online) } /* not there, must return not supp */ - if (!charger->pdata->has_wlc_dc || !p9221_is_online(charger)) + if (!charger->pdata->has_wlc_dc || !p9221_is_online(charger) + || !p9221_is_epp(charger)) return -EOPNOTSUPP; if (charger->last_capacity > WLC_HPP_SOC_LIMIT) @@ -2189,6 +2281,15 @@ static int p9221_set_psy_online(struct p9221_charger_data *charger, int online) return -EAGAIN; } + ret = charger->reg_read_8(charger, P9221R5_EPP_TX_GUARANTEED_POWER_REG, &val8); + if (ret < 0) + return -EINVAL; + if (val8 < P9XXX_TX_GUAR_PWR_15W) { + dev_warn(&charger->client->dev, "Tx guar_pwr=%dW\n", val8 / 2); + p9221_set_auth_dc_icl(charger, false); + return -EOPNOTSUPP; + } + /* will return -EAGAIN until the feature is supported */ mutex_lock(&charger->auth_lock); @@ -2257,6 +2358,11 @@ static int p9221_set_psy_online(struct p9221_charger_data *charger, int online) p9221_set_switch_reg(charger, true); + /* Adjusting the minimum on time(MOT) for mitigate LC node voltage overshoot */ + ret = p9221_reg_write_8(charger, P9412_MOT_REG, P9412_MOT_40PCT); + if (ret < 0) + dev_warn(&charger->client->dev, "Fail to adjust MOT(%d)\n", ret); + return 1; } else if (wlc_dc_enabled) { /* TODO: thermals might come in and disable with 0 */ @@ -2329,15 +2435,22 @@ static void p9221_dream_defend(struct p9221_charger_data *charger) static void p9221_ll_check_chg_term(struct p9221_charger_data *charger, int capacity) { + int ll_icl_ua; + + ll_icl_ua = gvotable_get_int_vote(charger->dc_icl_votable, LL_BPP_CEP_VOTER); + if (capacity < 97) { + if (ll_icl_ua == P9221_LL_BPP_CHG_TERM_UA) + dev_info(&charger->client->dev, "power_mitigate: remove LL_BPP_CEP_VOTER(capacity=%d)\n", + capacity); gvotable_cast_int_vote(charger->dc_icl_votable, LL_BPP_CEP_VOTER, 0, false); - dev_dbg(&charger->client->dev, "power_mitigate: remove LL_BPP_CEP_VOTER\n"); } else if (capacity == 101) { /* when gdf==100, it's chg_term and vote 200mA */ + if (ll_icl_ua != P9221_LL_BPP_CHG_TERM_UA) + dev_info(&charger->client->dev, "power_mitigate: LL_BPP vote DC_ICL=%duA(capacity=%d)\n", + P9221_LL_BPP_CHG_TERM_UA, capacity); gvotable_cast_int_vote(charger->dc_icl_votable, LL_BPP_CEP_VOTER, P9221_LL_BPP_CHG_TERM_UA, true); - dev_dbg(&charger->client->dev, "power_mitigate: vote DC_ICL=%duA\n", - P9221_LL_BPP_CHG_TERM_UA); } } @@ -2359,9 +2472,9 @@ static void p9221_ll_bpp_cep(struct p9221_charger_data *charger, int capacity) p9221_ll_check_chg_term(charger, capacity); if (icl_ua > 0) - dev_info(&charger->client->dev, - "power_mitigate: set ICL to %duA\n", - gvotable_get_current_int_vote(charger->dc_icl_votable)); + dev_dbg(&charger->client->dev, + "power_mitigate: DD vote ICL = %duA\n", + gvotable_get_int_vote(charger->dc_icl_votable, DD_VOTER)); } /* @@ -2634,7 +2747,8 @@ static int p9221_enable_interrupts(struct p9221_charger_data *charger) mask = charger->ints.stat_rtx_mask; } else { mask = charger->ints.stat_limit_mask | charger->ints.stat_cc_mask | - charger->ints.vrecton_bit | charger->ints.prop_mode_mask; + charger->ints.vrecton_bit | charger->ints.prop_mode_mask | + charger->ints.extended_mode_bit; if (charger->pdata->needs_dcin_reset == P9221_WC_DC_RESET_VOUTCHANGED) @@ -2709,6 +2823,13 @@ static int p9221_set_dc_icl(struct p9221_charger_data *charger) } } + if (!p9221_is_epp(charger) && charger->last_capacity == 100 && charger->ll_bpp_cep < 0) { + dev_info(&charger->client->dev, "vote ICL as %d for BPP mode(capacity=%d)\n", + P9221_LL_BPP_CHG_TERM_UA, charger->last_capacity); + gvotable_cast_int_vote(charger->dc_icl_votable, LL_BPP_CEP_VOTER, + P9221_LL_BPP_CHG_TERM_UA, true); + } + /* Default to BPP ICL */ icl = P9221_DC_ICL_BPP_UA; @@ -2923,6 +3044,39 @@ static void p9221_icl_ramp_start(struct p9221_charger_data *charger) ms_to_ktime(charger->pdata->icl_ramp_delay_ms)); } +static void p9xxx_check_ll_bpp_cep(struct p9221_charger_data *charger) +{ + int ret, ll_icl_ua; + u32 vout_mv = 0; + u8 val8 = 0; + bool is_ll_bpp = true; + + ll_icl_ua = gvotable_get_int_vote(charger->dc_icl_votable, LL_BPP_CEP_VOTER); + + if (ll_icl_ua <= 0 || charger->ll_bpp_cep == 1) + return; + + if (p9221_is_epp(charger)) + is_ll_bpp = false; + + ret = charger->chip_get_vout_max(charger, &vout_mv); + if (ret < 0 || vout_mv != P9XXX_VOUT_5480MV) + is_ll_bpp = false; + ret = p9221_reg_read_8(charger, P9412_CMFET_L_REG, &val8); + if (ret < 0 || val8 != 0) + is_ll_bpp = false; + + if (is_ll_bpp) { + charger->ll_bpp_cep = 1; + dev_info(&charger->client->dev, "ll_bpp_cep = %d\n", charger->ll_bpp_cep); + } else { + charger->ll_bpp_cep = 0; + dev_info(&charger->client->dev, + "remove LL_BPP_CEP_VOTER(capacity=%d)\n", charger->last_capacity); + gvotable_cast_int_vote(charger->dc_icl_votable, LL_BPP_CEP_VOTER, 0, false); + } +} + static void p9221_set_online(struct p9221_charger_data *charger) { int ret; @@ -3156,6 +3310,9 @@ static void p9221_notifier_check_dc(struct p9221_charger_data *charger) if (!charger->dc_icl_bpp) p9221_icl_ramp_start(charger); + /* Check if Tx in LL BPP mode */ + p9xxx_check_ll_bpp_cep(charger); + ret = p9221_reg_read_16(charger, P9221_OTP_FW_MINOR_REV_REG, &charger->fw_rev); if (ret) @@ -3267,6 +3424,10 @@ static void p9221_notifier_work(struct work_struct *work) goto done_relax; } + /* Calibrate light load */ + if (charger->pdata->light_load) + p9xxx_chip_set_light_load_reg(charger, P9222_LIGHT_LOAD_VALUE); + p9xxx_write_q_factor(charger); if (charger->pdata->tx_4191q > 0) p9xxx_update_q_factor(charger); @@ -3827,6 +3988,10 @@ static ssize_t p9221_store_txlen(struct device *dev, if (!charger->online) return -ENODEV; + /* Don't send bidi data in cases of BPP and NOT dream-defend */ + if (!p9221_is_epp(charger) && !charger->trigger_power_mitigation) + return -EINVAL; + charger->tx_len = len; ret = set_renego_state(charger, P9XXX_SEND_DATA); @@ -4195,6 +4360,10 @@ static ssize_t wpc_ready_show(struct device *dev, struct i2c_client *client = to_i2c_client(dev); struct p9221_charger_data *charger = i2c_get_clientdata(client); + /* Skip it on BPP */ + if (!p9221_is_epp(charger) || !charger->pdata->has_wlc_dc) + return scnprintf(buf, PAGE_SIZE, "N\n"); + return scnprintf(buf, PAGE_SIZE, "%c\n", p9412_is_calibration_done(charger) ? 'Y' : 'N'); } @@ -4615,11 +4784,14 @@ static int p9382_set_rtx(struct p9221_charger_data *charger, bool enable) { int ret = 0, tx_icl = -1; + if ((enable && charger->ben_state) || (!enable && !charger->ben_state)) { + logbuffer_prlog(charger->rtx_log, "RTx is %s\n", enable ? "enabled" : "disabled"); + return 0; + } + mutex_lock(&charger->rtx_lock); if (enable == 0) { - logbuffer_log(charger->rtx_log, "disable rtx\n"); - if (charger->is_rtx_mode) { ret = charger->chip_tx_mode_en(charger, false); charger->is_rtx_mode = false; @@ -4632,6 +4804,9 @@ static int p9382_set_rtx(struct p9221_charger_data *charger, bool enable) if (ret) dev_err(&charger->client->dev, "fail to enable dcin, ret=%d\n", ret); + + logbuffer_log(charger->rtx_log, "disable rtx\n"); + goto done; } else { logbuffer_log(charger->rtx_log, "enable rtx"); @@ -4685,7 +4860,7 @@ static int p9382_set_rtx(struct p9221_charger_data *charger, bool enable) dev_err(&charger->client->dev, "cannot enter rTX mode (%d)\n", ret); logbuffer_log(charger->rtx_log, - "cannot enter rTX mode (%d)\n", ret); + "cannot enter rTX mode (%d)", ret); p9382_ben_cfg(charger, RTX_BEN_DISABLED); ret = p9382_disable_dcin_en(charger, false); @@ -4746,6 +4921,79 @@ done: return ret; } +#define P9412_RTX_OCP_THRES_MA 900 +static int p9412_check_rtx_ocp(struct p9221_charger_data *chgr) +{ + int ret, cnt, retry, ocp_count = 0, current_now, chk_ms, total_delay; + u16 status_reg; + + total_delay = chgr->rtx_total_delay > 0 ? chgr->rtx_total_delay : 3000; + chk_ms = chgr->rtx_ocp_chk_ms > 0 ? chgr->rtx_ocp_chk_ms : 20; + retry = (total_delay / chk_ms > 0) ? (total_delay / chk_ms) : 1; + + logbuffer_log(chgr->rtx_log, "use rtx_ocp_chk_ms=%d retry=%d", chk_ms, retry); + + for (cnt = 0; cnt < retry; cnt++) { + if (!chgr->ben_state) + return -EIO; + /* check if rx_is_connected: if yes, goto 7V */ + ret = chgr->reg_read_16(chgr, P9221_STATUS_REG, &status_reg); + if (ret < 0) { + logbuffer_log(chgr->rtx_log, "ioerr disable RTx(%d)", ret); + return -EIO; + } + if (status_reg & chgr->ints.rx_connected_bit) { + logbuffer_log(chgr->rtx_log, "rx is connected, goto 7V"); + return 0; + } + /* check if (ocp_cnt > 2): if yes, disable rtx */ + ret = chgr->chip_get_iout(chgr, ¤t_now); + if (ret == 0 && current_now > P9412_RTX_OCP_THRES_MA) { + ocp_count++; + logbuffer_log(chgr->rtx_log, "cnt=%d,current_now=%d,ocp_count=%d", + cnt, current_now, ocp_count); + } + if (ret < 0) { + logbuffer_log(chgr->rtx_log, "iout disable RTx(%d)", ret); + return -EINVAL; + } + if (ocp_count > 2 || current_now == 0) { + logbuffer_log(chgr->rtx_log, "ocp_count=%d current_now=%d disable RTx", + ocp_count, current_now); + return -EINVAL; + } + usleep_range(chk_ms * USEC_PER_MSEC, (chk_ms + 2) * USEC_PER_MSEC); + } + + return 0; +} + +static void p9412_chk_rtx_ocp_work(struct work_struct *work) +{ + struct p9221_charger_data *chgr = container_of(work, + struct p9221_charger_data, chk_rtx_ocp_work.work); + int ret; + + /* check TX OCP before enable 7V */ + ret = p9412_check_rtx_ocp(chgr); + if (!chgr->ben_state) + return; + if (ret < 0) { + p9382_set_rtx(chgr, false); + return; + } + + ret = chgr->reg_write_8(chgr, P9412_APBSTPING_REG, P9412_APBSTPING_7V); + ret |= chgr->reg_write_8(chgr, P9412_APBSTCONTROL_REG, P9412_APBSTPING_7V); + if (ret == 0) { + schedule_delayed_work(&chgr->rtx_work, msecs_to_jiffies(P9382_RTX_TIMEOUT_MS)); + } else { + logbuffer_log(chgr->rtx_log, "Failed to configure Ext-Boost Vout registers(%d)", + ret); + p9382_set_rtx(chgr, false); + } +} + static ssize_t rtx_show(struct device *dev, struct device_attribute *attr, char *buf) @@ -4768,15 +5016,19 @@ static ssize_t rtx_store(struct device *dev, int ret; if (buf[0] == '0') { - dev_info(&charger->client->dev, "battery share off\n"); - logbuffer_log(charger->rtx_log, "battery share off"); - cancel_delayed_work_sync(&charger->rtx_work); + logbuffer_prlog(charger->rtx_log, "battery share off"); + mutex_lock(&charger->rtx_lock); charger->rtx_reset_cnt = 0; + mutex_unlock(&charger->rtx_lock); ret = p9382_set_rtx(charger, false); + cancel_delayed_work_sync(&charger->rtx_work); + if (charger->pdata->apbst_en) + cancel_delayed_work_sync(&charger->chk_rtx_ocp_work); } else if (buf[0] == '1') { - dev_info(&charger->client->dev, "battery share on\n"); - logbuffer_log(charger->rtx_log, "battery share on"); + logbuffer_prlog(charger->rtx_log, "battery share on"); + mutex_lock(&charger->rtx_lock); charger->rtx_reset_cnt = 0; + mutex_unlock(&charger->rtx_lock); ret = p9382_set_rtx(charger, true); } else { return -EINVAL; @@ -5126,22 +5378,26 @@ static void p9xxx_reset_rtx_for_ocp(struct p9221_charger_data *charger) { int ext_bst_on = 0; - if (charger->pdata->ben_gpio > 0) - ext_bst_on = gpio_get_value_cansleep(charger->pdata->ben_gpio); - + mutex_lock(&charger->rtx_lock); charger->rtx_reset_cnt += 1; - if ((charger->rtx_reset_cnt >= RTX_RESET_COUNT_MAX) || ext_bst_on) { + if (charger->rtx_reset_cnt >= RTX_RESET_COUNT_MAX) { if (charger->rtx_reset_cnt == RTX_RESET_COUNT_MAX) charger->rtx_err = RTX_HARD_OCP; charger->rtx_reset_cnt = 0; } + mutex_unlock(&charger->rtx_lock); charger->is_rtx_mode = false; p9382_set_rtx(charger, false); msleep(REENABLE_RTX_DELAY); + if (charger->pdata->ben_gpio > 0) + ext_bst_on = gpio_get_value_cansleep(charger->pdata->ben_gpio); + if (ext_bst_on) + return; + if (charger->rtx_reset_cnt) { dev_info(&charger->client->dev, "re-enable RTx mode, cnt=%d\n", charger->rtx_reset_cnt); logbuffer_log(charger->rtx_log, "re-enable RTx mode, cnt=%d\n", charger->rtx_reset_cnt); @@ -5210,8 +5466,8 @@ static void rtx_irq_handler(struct p9221_charger_data *charger, u16 irq_src) if (mode_reg == P9XXX_SYS_OP_MODE_TX_MODE) { charger->is_rtx_mode = true; cancel_delayed_work_sync(&charger->rtx_work); - schedule_delayed_work(&charger->rtx_work, - msecs_to_jiffies(P9382_RTX_TIMEOUT_MS)); + if (!charger->pdata->apbst_en) + schedule_delayed_work(&charger->rtx_work, msecs_to_jiffies(P9382_RTX_TIMEOUT_MS)); } dev_info(&charger->client->dev, "P9221_SYSTEM_MODE_REG reg: %02x\n", @@ -5273,12 +5529,9 @@ static void rtx_irq_handler(struct p9221_charger_data *charger, u16 irq_src) } if (irq_src & csp_bit) { - ret = p9221_reg_read_8(charger, P9382A_CHARGE_STAT_REG, - &csp_reg); + ret = p9221_reg_read_8(charger, charger->reg_csp_addr, &csp_reg); if (ret) { - logbuffer_log(charger->rtx_log, - "failed to read CSP_REG reg: %d", - ret); + logbuffer_log(charger->rtx_log, "failed to read CSP_REG reg: %d", ret); } else { charger->rtx_csp = csp_reg; schedule_work(&charger->uevent_work); @@ -5523,6 +5776,18 @@ static void p9221_irq_handler(struct p9221_charger_data *charger, u16 irq_src) /* charger->prop_mode_en is reset on disconnect */ } + + /* This only necessary for P9222 */ + if (irq_src & charger->ints.extended_mode_bit) { + if (charger->check_rp == RP_NOTSET && + charger->pdata->epp_rp_value != -1) { + pm_stay_awake(charger->dev); + charger->check_rp = RP_CHECKING; + cancel_delayed_work_sync(&charger->chk_rp_work); + schedule_delayed_work(&charger->chk_rp_work, + msecs_to_jiffies(P9XXX_CHK_RP_DELAY_MS)); + } + } } #define IRQ_DEBOUNCE_TIME_MS 4 @@ -5639,6 +5904,27 @@ static irqreturn_t p9221_irq_det_thread(int irq, void *irq_data) return IRQ_HANDLED; } +static void p9xxx_chk_fod_work(struct work_struct *work) +{ + struct p9221_charger_data *charger = container_of(work, + struct p9221_charger_data, chk_fod_work.work); + + if (!charger->online) + return; + + p9221_write_fod(charger); +} + +static void p9xxx_chk_rp_work(struct work_struct *work) +{ + struct p9221_charger_data *charger = container_of(work, + struct p9221_charger_data, chk_rp_work.work); + + charger->chip_renegotiate_pwr(charger); + pm_relax(charger->dev); +} + + static void p9382_rtx_disable_work(struct work_struct *work) { struct p9221_charger_data *charger = container_of(work, @@ -5907,6 +6193,31 @@ static int p9221_parse_dt(struct device *dev, } } + + pdata->fod_epp_comp_num = + of_property_count_elems_of_size(node, "fod_epp_comp", sizeof(u8)); + if (pdata->fod_epp_comp_num <= 0) { + dev_err(dev, "No dt fod epp comp provided (%d)\n", + pdata->fod_epp_comp_num); + pdata->fod_epp_comp_num = 0; + } else { + if (pdata->fod_epp_comp_num > P9221R5_NUM_FOD) { + dev_err(dev, "Incorrect num of EPP COMP FOD %d, using first %d\n", + pdata->fod_epp_comp_num, P9221R5_NUM_FOD); + pdata->fod_epp_comp_num = P9221R5_NUM_FOD; + } + ret = of_property_read_u8_array(node, "fod_epp_comp", pdata->fod_epp_comp, + pdata->fod_epp_comp_num); + if (ret == 0) { + char buf[P9221R5_NUM_FOD * 3 + 1]; + + p9221_hex_str(pdata->fod_epp_comp, pdata->fod_epp_comp_num, buf, + pdata->fod_epp_comp_num * 3 + 1, false); + dev_info(dev, "dt fod_epp_comp: %s (%d)\n", buf, + pdata->fod_epp_comp_num); + } + } + pdata->fod_hpp_num = of_property_count_elems_of_size(node, "fod_hpp", sizeof(u8)); if (pdata->fod_hpp_num <= 0) { @@ -5951,6 +6262,26 @@ static int p9221_parse_dt(struct device *dev, } } + ret = of_property_read_bool(node, "google,fod_fsw_base"); + if (ret) + pdata->fod_fsw = true; + + ret = of_property_read_u32(node, "google,fod_fsw_high_thres", &data); + if (ret < 0) { + pdata->fod_fsw_high = -1; + } else { + pdata->fod_fsw_high = data; + dev_info(dev, "dt fod_fsw_high_thres:%d\n", pdata->fod_fsw_high); + } + + ret = of_property_read_u32(node, "google,fod_fsw_low_thres", &data); + if (ret < 0) { + pdata->fod_fsw_low = -1; + } else { + pdata->fod_fsw_low = data; + dev_info(dev, "dt fod_fsw_low_thres:%d\n", pdata->fod_fsw_low); + } + ret = of_property_read_u32(node, "google,q_value", &data); if (ret < 0) { pdata->q_value = -1; @@ -5975,6 +6306,14 @@ static int p9221_parse_dt(struct device *dev, dev_info(dev, "dt epp_rp_value: %d\n", pdata->epp_rp_value); } + ret = of_property_read_u32(node, "google,epp_rp_low_value", &data); + if (ret < 0) { + pdata->epp_rp_low_value = -1; + } else { + pdata->epp_rp_low_value = data; + dev_info(dev, "dt epp_rp_low_value: %d\n", pdata->epp_rp_low_value); + } + pdata->epp_vout_mv = P9221_MAX_VOUT_SET_MV_DEFAULT; ret = of_property_read_u32(node, "google,epp_vout_mv", &data); if (ret == 0) { @@ -6126,6 +6465,9 @@ static int p9221_parse_dt(struct device *dev, else pdata->epp_icl = data; + /* Calibrate light load */ + pdata->light_load = of_property_read_bool(node, "google,light_load"); + return 0; } @@ -6350,6 +6692,7 @@ static int p9221_charger_probe(struct i2c_client *client, charger->last_disable = -1; charger->irq_at = 0; charger->ll_bpp_cep = -EINVAL; + charger->check_rp = RP_NOTSET; mutex_init(&charger->io_lock); mutex_init(&charger->cmd_lock); mutex_init(&charger->stats_lock); @@ -6369,6 +6712,9 @@ static int p9221_charger_probe(struct i2c_client *client, INIT_DELAYED_WORK(&charger->rtx_work, p9382_rtx_work); INIT_DELAYED_WORK(&charger->auth_dc_icl_work, p9221_auth_dc_icl_work); INIT_DELAYED_WORK(&charger->fg_work, p9221_fg_work); + INIT_DELAYED_WORK(&charger->chk_rp_work, p9xxx_chk_rp_work); + INIT_DELAYED_WORK(&charger->chk_rtx_ocp_work, p9412_chk_rtx_ocp_work); + INIT_DELAYED_WORK(&charger->chk_fod_work, p9xxx_chk_fod_work); INIT_WORK(&charger->uevent_work, p9221_uevent_work); INIT_WORK(&charger->rtx_disable_work, p9382_rtx_disable_work); INIT_WORK(&charger->rtx_reset_work, p9xxx_rtx_reset_work); @@ -6594,6 +6940,8 @@ static int p9221_charger_probe(struct i2c_client *client, } else { debugfs_create_bool("no_fod", 0644, charger->debug_entry, &charger->no_fod); debugfs_create_u32("de_q_value", 0644, charger->debug_entry, &charger->de_q_value); + debugfs_create_u32("de_chk_ocp_ms", 0644, charger->debug_entry, &charger->rtx_ocp_chk_ms); + debugfs_create_u32("de_rtx_delay_ms", 0644, charger->debug_entry, &charger->rtx_total_delay); } /* can independently read battery capacity */ @@ -6673,6 +7021,9 @@ static int p9221_charger_remove(struct i2c_client *client) cancel_delayed_work_sync(&charger->align_work); cancel_delayed_work_sync(&charger->rtx_work); cancel_delayed_work_sync(&charger->auth_dc_icl_work); + cancel_delayed_work_sync(&charger->chk_rp_work); + cancel_delayed_work_sync(&charger->chk_rtx_ocp_work); + cancel_delayed_work_sync(&charger->chk_fod_work); cancel_work_sync(&charger->uevent_work); cancel_work_sync(&charger->rtx_disable_work); cancel_work_sync(&charger->rtx_reset_work); diff --git a/p9221_charger.h b/p9221_charger.h index b141764..a889aad 100644 --- a/p9221_charger.h +++ b/p9221_charger.h @@ -72,18 +72,22 @@ #define P9221R5_ILIM_MAX_UA (1600 * 1000) #define P9221_CHECK_NP_DELAY_MS 50 -#define P9221_NEG_POWER_5W (5 / 0.5) -#define P9221_NEG_POWER_10W (10 / 0.5) +#define P9221_NEG_POWER_5W (5 * 2) +#define P9221_NEG_POWER_10W (10 * 2) #define P9221_PTMC_EPP_TX_1912 0x32 #define P9221_PTMC_EPP_TX_4191 0x50 +#define P9222_RX_CALIBRATION_LIGHT_LOAD 0x5831 +#define P9222_LIGHT_LOAD_VALUE 0x0C + #define P9221_DCIN_RETRY_DELAY_MS 50 #define P9XXX_DC_ICL_EPP_1000 1000000 #define P9XXX_DC_ICL_EPP_750 750000 #define P9XXX_DC_ICL_EPP_100 100000 -#define P9XXX_NEG_POWER_10W (10 / 0.5) -#define P9XXX_NEG_POWER_11W (11 / 0.5) +#define P9XXX_NEG_POWER_10W (10 * 2) +#define P9XXX_NEG_POWER_11W (11 * 2) +#define P9XXX_TX_GUAR_PWR_15W (15 * 2) #define P9382_RTX_TIMEOUT_MS (2 * 1000) #define WLCDC_DEBOUNCE_TIME_S 400 #define WLCDC_AUTH_CHECK_S 15 @@ -287,21 +291,32 @@ */ #define P9222_CHIP_ID 0x9222 #define P9222RE_SYSTEM_MODE_REG 0x3F +#define P9222RE_CHARGE_STAT_REG 0x4E +#define P9222RE_EPT_REG 0x4F #define P9222RE_VOUT_REG 0x50 #define P9222RE_VOUT_SET_REG 0x52 #define P9222RE_VRECT_REG 0x54 #define P9222RE_IOUT_REG 0x58 #define P9222RE_DIE_TEMP_REG 0x5A #define P9222RE_OP_FREQ_REG 0x5C +#define P9222RE_TX_PINGFREQ_REG 0x5E #define P9222RE_ILIM_SET_REG 0x60 -#define P9222_COM_REG 0x62 +#define P9222RE_COM_REG 0x62 #define P9222RE_FOD_REG 0x84 +#define P9222RE_COM_CHAN_RECV_SIZE_REG 0x98 +#define P9222RE_EPP_TX_GUARANTEED_POWER_REG 0xB4 #define P9222RE_EPP_REQ_NEGOTIATED_POWER_REG 0xBD +#define P9222RE_EPP_REQ_MAXIMUM_POWER_REG 0xBE #define P9222RE_EPP_Q_FACTOR_REG 0xD2 #define P9222RE_TX_MFG_CODE_REG 0x106 #define P9222RE_PROP_TX_ID_REG 0x118 +#define P9222RE_DIE_TEMP_ADC_REG 0x12A +#define P9222RE_COM_PACKET_TYPE_ADDR 0x600 +#define P9222RE_COM_CHAN_SEND_SIZE_REG 0x601 +#define P9222RE_DATA_BUF_START 0x604 +#define P9222RE_DATA_BUF_SIZE 0x100 -#define P9222_EPT_REG 0x4F +#define P9222RE_COM_CCACTIVATE BIT(9) /* * P9222 SYSTEM_MODE_REG bits @@ -311,7 +326,7 @@ #define P9222_VOUT_SET_MIN_MV 3500 #define P9222_VOUT_SET_MAX_MV 12500 - +#define P9222_NEG_POWER_10W 10000 /* * Interrupt/Status flags for P9222 @@ -320,6 +335,7 @@ #define P9222_STAT_OVT BIT(2) #define P9222_STAT_OVC BIT(3) #define P9222_STAT_OVV BIT(4) +#define P9222_EXTENDED_MODE BIT(12) #define P9222_STAT_PPRCVD BIT(15) /* @@ -488,7 +504,18 @@ /* p9412 AP BOOST PING register */ #define P9412_APBSTPING_REG 0xF0 +#define P9412_APBSTCONTROL_REG 0xF1 #define P9412_APBSTPING_7V BIT(0) +#define P9412_TXOCP_REG 0xA0 +#define P9412_TXOCP_1400MA 1400 + +#define P9412_MOT_REG 0xD0 +#define P9412_MOT_40PCT 0x10 +#define P9412_MOT_65PCT 0x1A + +#define P9412_MOT_REG 0xD0 +#define P9412_MOT_40PCT 0x10 +#define P9412_MOT_65PCT 0x1A /* Features */ typedef enum { @@ -513,6 +540,12 @@ enum p9221_align_mfg_chk_state { ALIGN_MFG_PASSED, }; +enum p9xxx_chk_rp { + RP_NOTSET = -1, + RP_CHECKING, + RP_DONE, +}; + #define WLC_SOC_STATS_LEN 101 struct p9221_soc_data { @@ -587,15 +620,21 @@ struct p9221_charger_platform_data { int epp_vout_mv; u8 fod[P9221R5_NUM_FOD]; u8 fod_epp[P9221R5_NUM_FOD]; + u8 fod_epp_comp[P9221R5_NUM_FOD]; u8 fod_hpp[P9221R5_NUM_FOD]; u8 fod_hpp_hv[P9221R5_NUM_FOD]; int fod_num; int fod_epp_num; + int fod_epp_comp_num; int fod_hpp_num; int fod_hpp_hv_num; + bool fod_fsw; + int fod_fsw_high; + int fod_fsw_low; int q_value; int tx_4191q; int epp_rp_value; + int epp_rp_low_value; int needs_dcin_reset; int nb_alignment_freq; int *alignment_freq; @@ -617,6 +656,8 @@ struct p9221_charger_platform_data { /* phone type for tx_id*/ u8 phone_type; u32 epp_icl; + /* calibrate light load */ + bool light_load; }; struct p9221_charger_ints_bit { @@ -639,6 +680,7 @@ struct p9221_charger_ints_bit { u16 stat_limit_mask; u16 stat_cc_mask; u16 prop_mode_mask; + u16 extended_mode_bit; /* Tx mode */ u16 hard_ocp_bit; u16 tx_conflict_bit; @@ -680,6 +722,9 @@ struct p9221_charger_data { struct delayed_work power_mitigation_work; struct delayed_work auth_dc_icl_work; struct delayed_work fg_work; + struct delayed_work chk_rp_work; + struct delayed_work chk_rtx_ocp_work; + struct delayed_work chk_fod_work; struct work_struct uevent_work; struct work_struct rtx_disable_work; struct work_struct rtx_reset_work; @@ -753,6 +798,8 @@ struct p9221_charger_data { u32 rtx_csp; int rtx_err; int rtx_reset_cnt; + int rtx_ocp_chk_ms; + int rtx_total_delay; bool chg_on_rtx; bool is_rtx_mode; bool prop_mode_en; @@ -783,6 +830,7 @@ struct p9221_charger_data { bool sw_ramp_done; bool hpp_hv; int fod_mode; + enum p9xxx_chk_rp check_rp; #if IS_ENABLED(CONFIG_GPIOLIB) struct gpio_chip gpio; @@ -801,6 +849,8 @@ struct p9221_charger_data { u16 reg_get_pp_buf_addr; u16 reg_set_fod_addr; u16 reg_q_factor_addr; + u16 reg_csp_addr; + u16 reg_light_load_addr; int (*reg_read_n)(struct p9221_charger_data *chgr, u16 reg, void *buf, size_t n); @@ -857,7 +907,7 @@ bool p9xxx_is_capdiv_en(struct p9221_charger_data *charger); int p9221_wlc_disable(struct p9221_charger_data *charger, int disable, u8 reason); int p9221_set_auth_dc_icl(struct p9221_charger_data *charger, bool enable); int p9xxx_sw_ramp_icl(struct p9221_charger_data *charger, const int icl_target); -int p9xxx_gpio_set_value(struct p9221_charger_data *charger, unsigned gpio, int value); +int p9xxx_gpio_set_value(struct p9221_charger_data *charger, int gpio, int value); void p9xxx_gpio_init(struct p9221_charger_data *charger); extern int p9221_chip_init_funcs(struct p9221_charger_data *charger, @@ -916,4 +966,8 @@ enum p9xxx_renego_state { -ENOTSUPP : chgr->reg_read_n(chgr, chgr->reg_set_fod_addr, data, len)) #define p9xxx_chip_set_q_factor_reg(chgr, data) (chgr->reg_q_factor_addr == 0 ? \ -ENOTSUPP : chgr->reg_write_8(chgr, chgr->reg_q_factor_addr, data)) +#define p9xxx_chip_set_light_load_reg(chgr, data) (chgr->reg_light_load_addr == 0 ? \ + -ENOTSUPP : chgr->reg_write_8(chgr, chgr->reg_light_load_addr, data)) +#define logbuffer_prlog(p, fmt, ...) \ + gbms_logbuffer_prlog(p, LOGLEVEL_INFO, 0, LOGLEVEL_DEBUG, fmt, ##__VA_ARGS__) #endif /* __P9221_CHARGER_H__ */ diff --git a/p9221_chip.c b/p9221_chip.c index 2b76996..24f93e5 100644 --- a/p9221_chip.c +++ b/p9221_chip.c @@ -523,6 +523,15 @@ static int p9221_get_data_buf(struct p9221_charger_data *chgr, return chgr->reg_read_n(chgr, P9221R5_DATA_RECV_BUF_START, data, len); } +static int p9222_get_data_buf(struct p9221_charger_data *chgr, + u8 data[], size_t len) +{ + if (!len || len > P9222RE_DATA_BUF_SIZE) + return -EINVAL; + + return chgr->reg_read_n(chgr, P9222RE_DATA_BUF_START, data, len); +} + static int p9382_get_data_buf(struct p9221_charger_data *chgr, u8 data[], size_t len) { @@ -551,6 +560,15 @@ static int p9221_set_data_buf(struct p9221_charger_data *chgr, return chgr->reg_write_n(chgr, P9221R5_DATA_SEND_BUF_START, data, len); } +static int p9222_set_data_buf(struct p9221_charger_data *chgr, + const u8 data[], size_t len) +{ + if (!len || len > P9222RE_DATA_BUF_SIZE) + return -EINVAL; + + return chgr->reg_write_n(chgr, P9222RE_DATA_BUF_START, data, len); +} + static int p9382_set_data_buf(struct p9221_charger_data *chgr, const u8 data[], size_t len) { @@ -581,6 +599,17 @@ static int p9221_get_cc_recv_size(struct p9221_charger_data *chgr, size_t *len) return ret; } +static int p9222_get_cc_recv_size(struct p9221_charger_data *chgr, size_t *len) +{ + int ret; + u8 len8; + + ret = chgr->reg_read_8(chgr, P9222RE_COM_CHAN_RECV_SIZE_REG, &len8); + if (ret == 0) + *len = len8; + return ret; +} + static int p9412_get_cc_recv_size(struct p9221_charger_data *chgr, size_t *len) { int ret; @@ -598,6 +627,22 @@ static int p9221_set_cc_send_size(struct p9221_charger_data *chgr, size_t len) return chgr->reg_write_8(chgr, P9221R5_COM_CHAN_SEND_SIZE_REG, len); } +static int p9222_set_cc_send_size(struct p9221_charger_data *chgr, size_t len) +{ + int ret; + + /* set packet type(BiDi) */ + ret = chgr->reg_write_8(chgr, chgr->reg_packet_type_addr, + BIDI_COM_PACKET_TYPE); + if (ret) { + dev_err(&chgr->client->dev, + "Failed to write packet type %d\n", ret); + return ret; + } + + return chgr->reg_write_16(chgr, P9222RE_COM_CHAN_SEND_SIZE_REG, len); +} + static int p9382_set_cc_send_size(struct p9221_charger_data *chgr, size_t len) { int ret; @@ -719,35 +764,45 @@ static int p9412_chip_tx_mode(struct p9221_charger_data *chgr, bool enable) if (enable) { if (chgr->pdata->apbst_en) { - ret = chgr->reg_write_8(chgr, P9412_APBSTPING_REG, - P9412_APBSTPING_7V); + ret = chgr->reg_write_8(chgr, P9412_APBSTPING_REG, 0); + ret |= chgr->reg_write_8(chgr, P9412_APBSTCONTROL_REG, P9412_APBSTPING_7V); logbuffer_log(chgr->rtx_log, - "configure Ext-Boost Vout to 7V.(%d)\n", ret); + "configure Ext-Boost Vout to 5V.(%d)", ret); if (ret < 0) return ret; } - ret = chgr->reg_write_8(chgr, P9412_TX_CMD_REG, - P9412_TX_CMD_TX_MODE_EN); + ret = chgr->reg_write_8(chgr, P9412_TX_CMD_REG, P9412_TX_CMD_TX_MODE_EN); if (ret) { logbuffer_log(chgr->rtx_log, - "tx_cmd_reg write failed (%d)\n", ret); + "tx_cmd_reg write failed (%d)", ret); return ret; } ret = p9382_wait_for_mode(chgr, P9XXX_SYS_OP_MODE_TX_MODE); - if (ret) + if (ret) { logbuffer_log(chgr->rtx_log, "error waiting for tx_mode (%d)", ret); + return ret; + } + + ret = chgr->reg_write_16(chgr, P9412_TXOCP_REG, P9412_TXOCP_1400MA); + logbuffer_log(chgr->rtx_log, "configure TX OCP to %dMA", P9412_TXOCP_1400MA); + if (ret < 0) + return ret; + + if (!chgr->pdata->apbst_en) + return ret; + mod_delayed_work(system_wq, &chgr->chk_rtx_ocp_work, 0); } else { ret = chgr->chip_set_cmd(chgr, P9412_CMD_TXMODE_EXIT); if (ret == 0) { ret = p9382_wait_for_mode(chgr, 0); if (ret < 0) - pr_err("cannot exit rTX mode (%d)\n", ret); + pr_err("cannot exit rTX mode (%d)", ret); } if (chgr->pdata->apbst_en) { ret = chgr->reg_write_8(chgr, P9412_APBSTPING_REG, 0); logbuffer_log(chgr->rtx_log, - "configure Ext-Boost back to 5V.(%d)\n", ret); + "configure Ext-Boost back to 5V.(%d)", ret); } } @@ -761,7 +816,7 @@ static int p9222_chip_set_cmd_reg(struct p9221_charger_data *chgr, u16 cmd) int ret; for (retry = 0; retry < P9221_COM_CHAN_RETRIES; retry++) { - ret = chgr->reg_read_16(chgr, P9222_COM_REG, &cur_cmd); + ret = chgr->reg_read_16(chgr, P9222RE_COM_REG, &cur_cmd); if (ret == 0 && cur_cmd == 0) break; msleep(25); @@ -773,7 +828,7 @@ static int p9222_chip_set_cmd_reg(struct p9221_charger_data *chgr, u16 cmd) return -EBUSY; } - ret = chgr->reg_write_16(chgr, P9222_COM_REG, (u16)cmd); + ret = chgr->reg_write_16(chgr, P9222RE_COM_REG, (u16)cmd); if (ret) dev_err(&chgr->client->dev, "Failed to set cmd reg %02x: %d\n", (u16)cmd, ret); @@ -904,7 +959,7 @@ static int p9222_send_eop(struct p9221_charger_data *chgr, u8 reason) mutex_lock(&chgr->cmd_lock); - ret = chgr->reg_write_8(chgr, P9222_EPT_REG, reason); + ret = chgr->reg_write_8(chgr, P9222RE_EPT_REG, reason); if (ret == 0) ret = chgr->chip_set_cmd(chgr, P9221R5_COM_SENDEPT); @@ -1078,16 +1133,42 @@ static int p9221_chip_renegotiate_pwr(struct p9221_charger_data *chgr) static int p9222_chip_renegotiate_pwr(struct p9221_charger_data *chgr) { - int ret; - int val8 = P9412_MW_TO_HW(chgr->pdata->epp_rp_value); + int ret = 0, guar_pwr_mw, cnt; + u8 val8, rp8; + + if (chgr->check_rp != RP_CHECKING) + return ret; + + ret = chgr->reg_read_8(chgr, P9222RE_EPP_TX_GUARANTEED_POWER_REG, &val8); + if (ret) + return ret; + guar_pwr_mw = P9412_HW_TO_MW(val8); + + /* write renegotiated power to 11W(>10W) or 10W(<=10W) */ + if (chgr->pdata->epp_rp_low_value != -1 && guar_pwr_mw <= P9222_NEG_POWER_10W) + rp8 = P9412_MW_TO_HW(chgr->pdata->epp_rp_low_value); + else + rp8 = P9412_MW_TO_HW(chgr->pdata->epp_rp_value); + + /* + * The neg_pwr write window is 200ms to 340ms, write every 20ms to make + * sure it works + */ + for (cnt = 0; cnt < 7 ; cnt++) { + /* units 0.5 W */ + ret = chgr->reg_write_8(chgr, P9222RE_EPP_REQ_NEGOTIATED_POWER_REG, rp8); + ret |= chgr->reg_write_8(chgr, P9222RE_EPP_REQ_MAXIMUM_POWER_REG, rp8); + + usleep_range(20 * USEC_PER_MSEC, 22 * USEC_PER_MSEC); + if (!chgr->online) + return -ENODEV; + } + if (ret == 0) + logbuffer_log(chgr->log, "read neg_pwr=0x%x, write neg_pwr=0x%x(guar_pwr=%dW)", + val8, rp8, guar_pwr_mw/1000); + + chgr->check_rp = RP_DONE; - /* units 0.5 W*/ - ret = chgr->reg_write_8(chgr, - P9222RE_EPP_REQ_NEGOTIATED_POWER_REG, val8); - if (ret < 0) - dev_err(&chgr->client->dev, - "cannot write to EPP_NEG_POWER=%d (%d)\n", - val8, ret); return ret; } @@ -1563,6 +1644,7 @@ void p9221_chip_init_interrupt_bits(struct p9221_charger_data *chgr, u16 chip_id chgr->ints.propmode_stat_bit = P9412_PROP_MODE_STAT_INT; chgr->ints.cdmode_change_bit = P9412_CDMODE_CHANGE_INT; chgr->ints.cdmode_err_bit = P9412_CDMODE_ERROR_INT; + chgr->ints.extended_mode_bit = 0; chgr->ints.hard_ocp_bit = P9412_STAT_OVC; chgr->ints.tx_conflict_bit = P9412_STAT_TXCONFLICT; @@ -1586,6 +1668,7 @@ void p9221_chip_init_interrupt_bits(struct p9221_charger_data *chgr, u16 chip_id chgr->ints.propmode_stat_bit = 0; chgr->ints.cdmode_change_bit = 0; chgr->ints.cdmode_err_bit = 0; + chgr->ints.extended_mode_bit = 0; chgr->ints.hard_ocp_bit = P9382_STAT_HARD_OCP; chgr->ints.tx_conflict_bit = P9382_STAT_TXCONFLICT; @@ -1609,6 +1692,7 @@ void p9221_chip_init_interrupt_bits(struct p9221_charger_data *chgr, u16 chip_id chgr->ints.propmode_stat_bit = 0; chgr->ints.cdmode_change_bit = 0; chgr->ints.cdmode_err_bit = 0; + chgr->ints.extended_mode_bit = P9222_EXTENDED_MODE; chgr->ints.hard_ocp_bit = 0; chgr->ints.tx_conflict_bit = 0; @@ -1632,6 +1716,7 @@ void p9221_chip_init_interrupt_bits(struct p9221_charger_data *chgr, u16 chip_id chgr->ints.propmode_stat_bit = 0; chgr->ints.cdmode_change_bit = 0; chgr->ints.cdmode_err_bit = 0; + chgr->ints.extended_mode_bit = 0; chgr->ints.hard_ocp_bit = 0; chgr->ints.tx_conflict_bit = 0; @@ -1683,6 +1768,8 @@ void p9221_chip_init_params(struct p9221_charger_data *chgr, u16 chip_id) chgr->set_cmd_ccactivate_bit = P9412_COM_CCACTIVATE; chgr->reg_set_fod_addr = P9221R5_FOD_REG; chgr->reg_q_factor_addr = P9221R5_EPP_Q_FACTOR_REG; + chgr->reg_csp_addr = P9221R5_CHARGE_STAT_REG; + chgr->reg_light_load_addr = 0; break; case P9382A_CHIP_ID: chgr->reg_tx_id_addr = P9382_PROP_TX_ID_REG; @@ -1693,16 +1780,20 @@ void p9221_chip_init_params(struct p9221_charger_data *chgr, u16 chip_id) chgr->set_cmd_ccactivate_bit = P9221R5_COM_CCACTIVATE; chgr->reg_set_fod_addr = P9221R5_FOD_REG; chgr->reg_q_factor_addr = P9221R5_EPP_Q_FACTOR_REG; + chgr->reg_csp_addr = P9221R5_CHARGE_STAT_REG; + chgr->reg_light_load_addr = 0; break; case P9222_CHIP_ID: chgr->reg_tx_id_addr = P9222RE_PROP_TX_ID_REG; chgr->reg_tx_mfg_code_addr = P9222RE_TX_MFG_CODE_REG; - chgr->reg_packet_type_addr = 0; + chgr->reg_packet_type_addr = P9222RE_COM_PACKET_TYPE_ADDR; chgr->reg_set_pp_buf_addr = P9221R5_DATA_SEND_BUF_START; chgr->reg_get_pp_buf_addr = P9221R5_DATA_RECV_BUF_START; - chgr->set_cmd_ccactivate_bit = P9221R5_COM_CCACTIVATE; + chgr->set_cmd_ccactivate_bit = P9222RE_COM_CCACTIVATE; chgr->reg_set_fod_addr = P9222RE_FOD_REG; chgr->reg_q_factor_addr = P9222RE_EPP_Q_FACTOR_REG; + chgr->reg_csp_addr = P9222RE_CHARGE_STAT_REG; + chgr->reg_light_load_addr = P9222_RX_CALIBRATION_LIGHT_LOAD; break; default: chgr->reg_tx_id_addr = P9221R5_PROP_TX_ID_REG; @@ -1713,6 +1804,8 @@ void p9221_chip_init_params(struct p9221_charger_data *chgr, u16 chip_id) chgr->set_cmd_ccactivate_bit = P9221R5_COM_CCACTIVATE; chgr->reg_set_fod_addr = P9221R5_FOD_REG; chgr->reg_q_factor_addr = P9221R5_EPP_Q_FACTOR_REG; + chgr->reg_csp_addr = P9221R5_CHARGE_STAT_REG; + chgr->reg_light_load_addr = 0; break; } } @@ -1803,10 +1896,10 @@ int p9221_chip_init_funcs(struct p9221_charger_data *chgr, u16 chip_id) chgr->chip_get_vout_max = p9222_chip_get_vout_max; chgr->chip_set_vout_max = p9222_chip_set_vout_max; chgr->chip_tx_mode_en = p9221_chip_tx_mode; - chgr->chip_set_data_buf = p9221_set_data_buf; - chgr->chip_get_data_buf = p9221_get_data_buf; - chgr->chip_get_cc_recv_size = p9221_get_cc_recv_size; - chgr->chip_set_cc_send_size = p9221_set_cc_send_size; + chgr->chip_get_data_buf = p9222_get_data_buf; + chgr->chip_set_data_buf = p9222_set_data_buf; + chgr->chip_get_cc_recv_size = p9222_get_cc_recv_size; + chgr->chip_set_cc_send_size = p9222_set_cc_send_size; chgr->chip_get_align_x = p9221_get_align_x; chgr->chip_get_align_y = p9221_get_align_y; chgr->chip_send_ccreset = p9221_send_ccreset; @@ -1862,7 +1955,7 @@ int p9221_chip_init_funcs(struct p9221_charger_data *chgr, u16 chip_id) } #if IS_ENABLED(CONFIG_GPIOLIB) -int p9xxx_gpio_set_value(struct p9221_charger_data *chgr, unsigned gpio, int value) +int p9xxx_gpio_set_value(struct p9221_charger_data *chgr, int gpio, int value) { if (gpio <= 0) return -EINVAL; diff --git a/pca9468_charger.c b/pca9468_charger.c index 7a574b8..70011f5 100644 --- a/pca9468_charger.c +++ b/pca9468_charger.c @@ -2665,6 +2665,7 @@ static int pca9468_charge_adjust_ccmode(struct pca9468_charger *pca9468) switch(ccmode) { case STS_MODE_IIN_LOOP: + pca9468->chg_data.iin_loop_count++; case STS_MODE_CHG_LOOP: /* CHG_LOOP does't exist */ apply_ircomp = true; @@ -2881,6 +2882,7 @@ static int pca9468_charge_ccmode(struct pca9468_charger *pca9468) break; case STS_MODE_IIN_LOOP: + pca9468->chg_data.iin_loop_count++; case STS_MODE_CHG_LOOP: iin = pca9468_read_adc(pca9468, ADCCH_IIN); if (iin < 0) @@ -2980,8 +2982,9 @@ static int pca9468_charge_start_cvmode(struct pca9468_charger *pca9468) } switch(cvmode) { - case STS_MODE_CHG_LOOP: case STS_MODE_IIN_LOOP: + pca9468->chg_data.iin_loop_count++; + case STS_MODE_CHG_LOOP: if (pca9468->ta_type == TA_TYPE_WIRELESS) { /* Decrease RX voltage (100mV) */ @@ -3175,8 +3178,9 @@ static int pca9468_charge_cvmode(struct pca9468_charger *pca9468) pca9468->timer_period = PCA9468_CVMODE_CHECK_T; } break; - case STS_MODE_CHG_LOOP: case STS_MODE_IIN_LOOP: + pca9468->chg_data.iin_loop_count++; + case STS_MODE_CHG_LOOP: /* Check the TA type */ if (pca9468->ta_type == TA_TYPE_WIRELESS) { /* Decrease RX Voltage (100mV) */ @@ -3356,9 +3360,25 @@ static int pca9468_preset_dcmode(struct pca9468_charger *pca9468) PCA9468_TA_MAX_CUR); ret = pca9468_get_apdo_max_power(pca9468, ta_max_vol, 0); } + if (ret < 0) { + int ret1; + pr_err("%s: No APDO to support 2:1\n", __func__); pca9468->chg_mode = CHG_NO_DC_MODE; + + if (!pca9468->dc_avail) + pca9468->dc_avail = + gvotable_election_get_handle(VOTABLE_DC_CHG_AVAIL); + + if (pca9468->dc_avail) { + ret1 = gvotable_cast_int_vote(pca9468->dc_avail, + REASON_DC_DRV, 0, 1); + if (ret1 < 0) + dev_err(pca9468->dev, + "Unable to cast vote for DC Chg avail (%d)\n", + ret1); + } goto error; } @@ -3864,15 +3884,6 @@ error: __func__, timer_id, pca9468->timer_id, charging_state, pca9468->charging_state, pca9468->timer_period, ret); - if (!pca9468->dc_avail) - pca9468->dc_avail = gvotable_election_get_handle(VOTABLE_DC_CHG_AVAIL); - - if (pca9468->dc_avail) { - ret = gvotable_cast_int_vote(pca9468->dc_avail, REASON_DC_DRV, 0, 1); - if (ret < 0) - dev_err(pca9468->dev, "Unable to cast vote for DC Chg avail (%d)\n", ret); - } - pca9468_stop_charging(pca9468); } @@ -4319,6 +4330,20 @@ static int pca9468_mains_set_property(struct power_supply *psy, __func__, ret); pca9468->mains_online = false; + + /* Reset DC Chg un-avail on disconnect */ + if (!pca9468->dc_avail) + pca9468->dc_avail = + gvotable_election_get_handle(VOTABLE_DC_CHG_AVAIL); + + if (pca9468->dc_avail) { + ret = gvotable_cast_int_vote(pca9468->dc_avail, + REASON_DC_DRV, 1, 1); + if (ret < 0) + dev_err(pca9468->dev, + "Unable to cast vote for DC Chg avail (%d)\n", + ret); + } } else if (pca9468->mains_online == false) { pca9468->mains_online = true; } @@ -4843,10 +4868,11 @@ static ssize_t p9468_show_chg_stats(struct device *dev, struct device_attribute chg_data->receiver_state[3], chg_data->receiver_state[4]); len += scnprintf(&buff[len], max_size - len, - "N: ovc=%d,ovc_ibatt=%d,ovc_delta=%d rcp=%d,stby=%d\n", + "N: ovc=%d,ovc_ibatt=%d,ovc_delta=%d rcp=%d,stby=%d, iin_loop=%d\n", chg_data->ovc_count, chg_data->ovc_max_ibatt, chg_data->ovc_max_delta, chg_data->rcp_count, - chg_data->stby_count); + chg_data->stby_count, + chg_data->iin_loop_count); len += scnprintf(&buff[len], max_size - len, "C: nc=%d,pre=%d,ca=%d,cc=%d,cv=%d,adj=%d\n", chg_data->nc_count, @@ -4993,7 +5019,7 @@ static int pca9468_probe(struct i2c_client *client, 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 */ diff --git a/pca9468_charger.h b/pca9468_charger.h index d5f7a25..d1f3ee8 100644 --- a/pca9468_charger.h +++ b/pca9468_charger.h @@ -102,6 +102,7 @@ struct p9468_chg_stats { unsigned int cv_count; unsigned int adj_count; unsigned int stby_count; + unsigned int iin_loop_count; }; #define p9468_chg_stats_valid(chg_data) ((chg_data)->valid) diff --git a/pca9468_gbms_pps.c b/pca9468_gbms_pps.c index 37b1bfa..4cdd75e 100644 --- a/pca9468_gbms_pps.c +++ b/pca9468_gbms_pps.c @@ -672,10 +672,10 @@ void p9468_chg_stats_dump(const struct pca9468_charger *pca9468) const struct p9468_chg_stats *chg_data = &pca9468->chg_data; logbuffer_prlog(pca9468, LOGLEVEL_INFO, - "N: ovc=%d,ovc_ibatt=%d,ovc_delta=%d rcp=%d,stby=%d", + "N: ovc=%d,ovc_ibatt=%d,ovc_delta=%d rcp=%d,stby=%d,iin_loop=%d", chg_data->ovc_count, chg_data->ovc_max_ibatt, chg_data->ovc_max_delta, - chg_data->rcp_count, chg_data->stby_count); + chg_data->rcp_count, chg_data->stby_count, chg_data->iin_loop_count); logbuffer_prlog(pca9468, LOGLEVEL_INFO, "C: nc=%d,pre=%d,ca=%d,cc=%d,cv=%d,adj=%d\n", chg_data->nc_count, chg_data->pre_count, diff --git a/wc68_driver.c b/wc68_driver.c index 609da84..61463e4 100644 --- a/wc68_driver.c +++ b/wc68_driver.c @@ -41,8 +41,6 @@ #define WC68_IIN_CFG_DFT 2500000 /* uA*/ /* Charging Float Voltage default value */ #define WC68_VFLOAT_DFT 4350000 /* uV */ -/* Charging Sub Float Voltage default value */ -#define WC68_VFLOAT_SUB_DFT 5000000 /* 5000000uV */ /* Charging Float Voltage max voltage for comp */ #define WC68_COMP_VFLOAT_MAX 4450000 /* uV */ @@ -89,8 +87,6 @@ #define WC68_MAX_RETRY_CNT 3 /* retries */ /* TA IIN tolerance */ #define WC68_TA_IIN_OFFSET 100000 /* uA */ -/* IIN_CC upper protection offset in Power Limit Mode TA */ -#define WC68_IIN_CC_UPPER_OFFSET 50000 /* 50mA */ /* PD Message Voltage and Current Step */ #define PD_MSG_TA_VOL_STEP 20000 /* uV */ @@ -101,18 +97,12 @@ /* WCRX voltage Step */ #define WCRX_VOL_STEP 100000 /* uV */ -#define WC68_OTV_MARGIN 12000 /* uV */ - /* irdrop default limit */ #define WC68_IRDROP_LIMIT_CNT 3 /* tiers */ #define WC68_IRDROP_LIMIT_TIER1 -30000 /* uV */ #define WC68_IRDROP_LIMIT_TIER2 -19000 /* uV */ #define WC68_IRDROP_LIMIT_TIER3 0 /* uV */ -/* Spread Spectrum default settings */ -#define WC68_SC_CLK_DITHER_RATE_DEF 0 /* 25kHz */ -#define WC68_SC_CLK_DITHER_LIMIT_DEF 0xF /* 10% */ - #define CBUS_UCP_DFT 400000 /* 400 mV, actual value TBD */ /* Status */ @@ -646,8 +636,8 @@ static int wc68_check_state(u8 val[8], struct wc68_charger *wc68, int loglevel) return ret; logbuffer_prlog(wc68, loglevel, - "%s: INTR_FLG reg[1]=%#x,[2]=%#x,[3]=%#x,[4]=%#x,[5]=%#x,[6]=%#x,[7]=%#x", - __func__, val[1], val[2], val[3], val[4], val[5], val[6], val[7]); + "%s: INTR_FLG reg[0]=%#x,reg[1]=%#x,[2]=%#x,[3]=%#x,[4]=%#x,[5]=%#x,[6]=%#x,[7]=%#x", + __func__, val[0], val[1], val[2], val[3], val[4], val[5], val[6], val[7]); return 0; } @@ -3246,8 +3236,22 @@ static int wc68_preset_dcmode(struct wc68_charger *wc68) ret = wc68_get_apdo_max_power(wc68, ta_max_vol, 0); } if (ret < 0) { + int ret1; + dev_err(wc68->dev, "%s: No APDO to support 2:1\n", __func__); wc68->chg_mode = CHG_NO_DC_MODE; + + if (!wc68->dc_avail) + wc68->dc_avail = gvotable_election_get_handle(VOTABLE_DC_CHG_AVAIL); + + if (wc68->dc_avail) { + ret1 = gvotable_cast_int_vote(wc68->dc_avail, REASON_DC_DRV, 0, 1); + if (ret1 < 0) + dev_err(wc68->dev, + "Unable to cast vote for DC Chg avail (%d)\n", + ret1); + } + goto error; } @@ -3739,15 +3743,6 @@ error: __func__, timer_id, wc68->timer_id, charging_state, wc68->charging_state, wc68->timer_period, ret); - if (!wc68->dc_avail) - wc68->dc_avail = gvotable_election_get_handle(VOTABLE_DC_CHG_AVAIL); - - if (wc68->dc_avail) { - ret = gvotable_cast_int_vote(wc68->dc_avail, REASON_DC_DRV, 0, 1); - if (ret < 0) - dev_err(wc68->dev, "Unable to cast vote for DC Chg avail (%d)\n", ret); - } - wc68_stop_charging(wc68); } @@ -4169,6 +4164,20 @@ static int wc68_mains_set_property(struct power_supply *psy, __func__, ret); wc68->mains_online = false; + + /* Reset DC Chg un-avail on disconnect */ + if (!wc68->dc_avail) + wc68->dc_avail = gvotable_election_get_handle(VOTABLE_DC_CHG_AVAIL); + + if (wc68->dc_avail) { + ret = gvotable_cast_int_vote(wc68->dc_avail, + REASON_DC_DRV, 1, 1); + if (ret < 0) + dev_err(wc68->dev, + "Unable to cast vote for DC Chg avail (%d)\n", + ret); + } + } else if (wc68->mains_online == false) { wc68->mains_online = true; } diff --git a/wc68_regs.h b/wc68_regs.h index 38553b8..a3b6f5f 100644 --- a/wc68_regs.h +++ b/wc68_regs.h @@ -85,7 +85,6 @@ enum { }; /* ADC step */ -#define VIN_STEP 16000 /* 16mV(16000uV) LSB, Range(0V ~ 16.368V) */ #define VBAT_STEP 3076 /* (3076uV) LSB, Range(0V ~ 6.297V) */ #define IIN_STEP 3662 /* (3662uA) LSB, Range(-7.5A ~ 7.496A) */ #define DIETEMP_STEP -116 /* 0.116C LSB, Range(-40 ~ 150C) */ |