From 0b8148c1f2b20cb3883ec7ed2058a53b2f063ff6 Mon Sep 17 00:00:00 2001 From: wen zhang Date: Wed, 11 Nov 2020 22:32:27 +0800 Subject: Kakadu: Add new battery support for gauge bq27542 Configure battery parameter and driver for gauge bq27542 on kakadu. BUG=b:172197008, b:171456201 BRANCH=master TEST=1.make -j BOARD=kakadu 2.verified the battery parameter and the result is OK. Change-Id: I4c2cc11df1e13cb7313280afca717a7f1e2461ff Signed-off-by: wen zhang Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/ec/+/2531765 Reviewed-by: Ting Shen --- baseboard/kukui/baseboard.h | 3 + baseboard/kukui/battery_bq27541.c | 225 ++++++++++++++++++++++++++++++++++++++ baseboard/kukui/build.mk | 1 + board/kakadu/board.h | 3 +- driver/battery/bq27541.c | 79 ++++++++++++- 5 files changed, 306 insertions(+), 5 deletions(-) create mode 100644 baseboard/kukui/battery_bq27541.c diff --git a/baseboard/kukui/baseboard.h b/baseboard/kukui/baseboard.h index f3c0678386..d2ac8cbf99 100644 --- a/baseboard/kukui/baseboard.h +++ b/baseboard/kukui/baseboard.h @@ -12,6 +12,7 @@ * Variant battery defines, pick one: * VARIANT_KUKUI_BATTERY_MAX17055 * VARIANT_KUKUI_BATTERY_MM8013 + * VARIANT_KUKUI_BATTERY_BQ27541 * VARIANT_KUKUI_BATTERY_SMART */ #if defined(VARIANT_KUKUI_BATTERY_MAX17055) @@ -20,6 +21,8 @@ #define BATTERY_MAX17055_RSENSE 5 /* m-ohm */ #elif defined(VARIANT_KUKUI_BATTERY_MM8013) #define CONFIG_BATTERY_MM8013 +#elif defined(VARIANT_KUKUI_BATTERY_BQ27541) +#define CONFIG_BATTERY_BQ27541 #elif defined(VARIANT_KUKUI_BATTERY_SMART) #define CONFIG_BATTERY_SMART #define CONFIG_BATTERY_FUEL_GAUGE diff --git a/baseboard/kukui/battery_bq27541.c b/baseboard/kukui/battery_bq27541.c new file mode 100644 index 0000000000..94f46b3326 --- /dev/null +++ b/baseboard/kukui/battery_bq27541.c @@ -0,0 +1,225 @@ +/* Copyright 2020 The Chromium OS Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + * + * Battery pack vendor provided charging profile + */ + +#include "battery.h" +#include "charge_state.h" +#include "charger_mt6370.h" +#include "console.h" +#include "driver/tcpm/mt6370.h" +#include "ec_commands.h" +#include "util.h" + +#define TEMP_OUT_OF_RANGE TEMP_ZONE_COUNT + +#define BATT_ID 0 + +#define BATTERY_CPT_CHARGE_MIN_TEMP 0 +#define BATTERY_CPT_CHARGE_MAX_TEMP 50 + +#define CHARGER_LIMIT_TIMEOUT_HOURS 48 +#define CHARGER_LIMIT_TIMEOUT_HOURS_TEMP 2 + +#define BAT_LEVEL_PD_LIMIT 85 + +#define CPRINTS(format, args...) cprints(CC_CHARGER, format, ## args) + +enum battery_type { + BATTERY_CPT = 0, + BATTERY_COUNT +}; + +static const struct battery_info info[] = { + [BATTERY_CPT] = { + .voltage_max = 4400, + .voltage_normal = 3850, + .voltage_min = 3000, + .precharge_voltage = 3400, + .precharge_current = 256, + .start_charging_min_c = 0, + .start_charging_max_c = 45, + .charging_min_c = 0, + .charging_max_c = 50, + .discharging_min_c = -20, + .discharging_max_c = 60, + }, +}; + +const struct battery_info *battery_get_info(void) +{ + return &info[BATT_ID]; +} + +int charger_profile_override(struct charge_state_data *curr) +{ + static timestamp_t deadline_48; + static timestamp_t deadline_2; + int cycle_count = 0, rv, val; + unsigned char rcv = 0, rcv_cycle = 0, rcv_soh = 0; + /* (FullCharge Capacity / Design Capacity) * 100 = SOH */ + int full_cap = 0, design_cap = 0, soh = 0; + /* battery temp in 0.1 deg C */ + int bat_temp_c = curr->batt.temperature - 2731; + /* + * Keep track of battery temperature range: + * + * ZONE_0 ZONE_1 ZONE_2 + * -----+--------+--------+------------+----- Temperature (C) + * t0 t1 t2 t3 + */ + enum { + TEMP_ZONE_0, /* t0 < bat_temp_c <= t1 */ + TEMP_ZONE_1, /* t1 < bat_temp_c <= t2 */ + TEMP_ZONE_2, /* t2 < bat_temp_c <= t3 */ + TEMP_ZONE_3, /* t3 < bat_temp_c <= t4 */ + TEMP_ZONE_COUNT + } temp_zone; + + static struct { + int temp_min; /* 0.1 deg C */ + int temp_max; /* 0.1 deg C */ + int desired_current; /* mA */ + int desired_voltage; /* mV */ + } temp_zones[BATTERY_COUNT][TEMP_ZONE_COUNT] = { + [BATTERY_CPT] = { + /* TEMP_ZONE_0 */ + {BATTERY_CPT_CHARGE_MIN_TEMP * 10, 150, 1408, 4370}, + /* TEMP_ZONE_1 */ + {150, 430, 3520, 4370}, + /* TEMP_ZONE_2 */ + {430, 450, 2112, 4320}, + /* TEMP_ZONE_3 */ + {450, BATTERY_CPT_CHARGE_MAX_TEMP * 10, 1760, 4170}, + }, + }; + BUILD_ASSERT(ARRAY_SIZE(temp_zones[0]) == TEMP_ZONE_COUNT); + BUILD_ASSERT(ARRAY_SIZE(temp_zones) == BATTERY_COUNT); + + if ((curr->batt.flags & BATT_FLAG_BAD_TEMPERATURE) || + (bat_temp_c < temp_zones[BATT_ID][0].temp_min) || + (bat_temp_c >= temp_zones[BATT_ID][TEMP_ZONE_COUNT - 1].temp_max)) + temp_zone = TEMP_OUT_OF_RANGE; + else { + for (temp_zone = 0; temp_zone < TEMP_ZONE_COUNT; temp_zone++) { + if (bat_temp_c < + temp_zones[BATT_ID][temp_zone].temp_max) + break; + } + } + + switch (temp_zone) { + case TEMP_ZONE_0: + case TEMP_ZONE_1: + case TEMP_ZONE_2: + case TEMP_ZONE_3: + curr->requested_current = + temp_zones[BATT_ID][temp_zone].desired_current; + curr->requested_voltage = + temp_zones[BATT_ID][temp_zone].desired_voltage; + break; + case TEMP_OUT_OF_RANGE: + curr->requested_current = curr->requested_voltage = 0; + curr->batt.flags &= ~BATT_FLAG_WANT_CHARGE; + curr->state = ST_IDLE; + break; + } + + /* Check cycle count to decrease charging voltage. */ + rv = battery_cycle_count(&val); + if (!rv) + cycle_count = val; + if (cycle_count > 20 && cycle_count <= 50) + rcv_cycle = 50; + else if (cycle_count > 50 && cycle_count <= 300) + rcv_cycle = 65; + else if (cycle_count > 300 && cycle_count <= 600) + rcv_cycle = 80; + else if (cycle_count > 600 && cycle_count <= 1000) + rcv_cycle = 100; + else if (cycle_count > 1000) + rcv_cycle = 150; + /* Check SOH to decrease charging voltage. */ + if (!battery_full_charge_capacity(&full_cap) && + !battery_design_capacity(&design_cap)) + soh = ((full_cap * 100) / design_cap); + if (soh > 70 && soh <= 75) + rcv_soh = 50; + else if (soh > 60 && soh <= 70) + rcv_soh = 65; + else if (soh > 55 && soh <= 60) + rcv_soh = 80; + else if (soh > 50 && soh <= 55) + rcv_soh = 100; + else if (soh <= 50) + rcv_soh = 150; + rcv = MAX(rcv_cycle, rcv_soh); + curr->requested_voltage -= rcv; + + /* Should not keep charging voltage > 4250mV for 48hrs. */ + if ((curr->state == ST_DISCHARGE) || + curr->chg.voltage < 4250) { + deadline_48.val = 0; + /* Starting count 48hours */ + } else if (curr->state == ST_CHARGE || + curr->state == ST_PRECHARGE) { + if (deadline_48.val == 0) + deadline_48.val = get_time().val + + CHARGER_LIMIT_TIMEOUT_HOURS * HOUR; + /* If charging voltage keep > 4250 for 48hrs, + * set charging voltage = 4250 + */ + else if (timestamp_expired(deadline_48, NULL)) + curr->requested_voltage = 4250; + } + /* Should not keeep battery voltage > 4100mV and + * battery temperature > 45C for two hour + */ + if (curr->state == ST_DISCHARGE || + curr->batt.voltage < 4100 || + bat_temp_c < 450) { + deadline_2.val = 0; + } else if (curr->state == ST_CHARGE || + curr->state == ST_PRECHARGE) { + if (deadline_2.val == 0) + deadline_2.val = get_time().val + + CHARGER_LIMIT_TIMEOUT_HOURS_TEMP * HOUR; + else if (timestamp_expired(deadline_2, NULL)) { + /* Set discharge and charging voltage = 4100mV */ + if (curr->batt.voltage >= 4100) { + curr->requested_current = 0; + curr->requested_voltage = 4100; + } + } + } + +#ifdef VARIANT_KUKUI_CHARGER_MT6370 + mt6370_charger_profile_override(curr); +#endif /* CONFIG_CHARGER_MT6370 */ + + return 0; +} + +enum ec_status charger_profile_override_get_param(uint32_t param, + uint32_t *value) +{ + return EC_RES_INVALID_PARAM; +} + +enum ec_status charger_profile_override_set_param(uint32_t param, + uint32_t value) +{ + return EC_RES_INVALID_PARAM; +} + +int get_battery_manufacturer_name(char *dest, int size) +{ + static const char * const name[] = { + [BATTERY_CPT] = "AS1XXXD3Ka", + }; + ASSERT(dest); + strzcpy(dest, name[BATT_ID], size); + return EC_SUCCESS; +} diff --git a/baseboard/kukui/build.mk b/baseboard/kukui/build.mk index ab62483bc7..ebd0882707 100644 --- a/baseboard/kukui/build.mk +++ b/baseboard/kukui/build.mk @@ -12,6 +12,7 @@ baseboard-$(CONFIG_BOOTBLOCK)+=emmc.o baseboard-$(VARIANT_KUKUI_BATTERY_MAX17055)+=battery_max17055.o baseboard-$(VARIANT_KUKUI_BATTERY_MM8013)+=battery_mm8013.o +baseboard-$(VARIANT_KUKUI_BATTERY_BQ27541)+=battery_bq27541.o baseboard-$(VARIANT_KUKUI_BATTERY_SMART)+=battery_smart.o baseboard-$(VARIANT_KUKUI_CHARGER_MT6370)+=charger_mt6370.o diff --git a/board/kakadu/board.h b/board/kakadu/board.h index cc3b757433..8374894722 100644 --- a/board/kakadu/board.h +++ b/board/kakadu/board.h @@ -8,7 +8,8 @@ #ifndef __CROS_EC_BOARD_H #define __CROS_EC_BOARD_H -#define VARIANT_KUKUI_BATTERY_MM8013 +#define BQ27541_ADDR 0x55 +#define VARIANT_KUKUI_BATTERY_BQ27541 #define VARIANT_KUKUI_POGO_KEYBOARD #define VARIANT_KUKUI_CHARGER_MT6370 diff --git a/driver/battery/bq27541.c b/driver/battery/bq27541.c index 868b0215e8..0fa91db904 100644 --- a/driver/battery/bq27541.c +++ b/driver/battery/bq27541.c @@ -2,10 +2,11 @@ * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. * - * Battery driver for BQ27541/BQ27741/BQ27742. + * Battery driver for BQ27541/BQ27542/BQ27741/BQ27742. */ #include "battery.h" +#include "battery_smart.h" #include "console.h" #include "extpower.h" #include "hooks.h" @@ -14,6 +15,7 @@ #define BQ27541_ADDR_FLAGS 0x55 #define BQ27541_TYPE_ID 0x0541 +#define BQ27542_TYPE_ID 0x0542 #define BQ27741_TYPE_ID 0x0741 #define BQ27742_TYPE_ID 0x0742 @@ -39,12 +41,25 @@ #define REG_TT_EAT_CONSTANT_POWER 0x26 #define REG_CYCLE_COUNT 0x2a #define REG_STATE_OF_CHARGE 0x2c +#define REG_DATA_FLASH_BLOCK 0x3f #define REG_DESIGN_CAPACITY 0x3c +#define REG_MANUFACTURER_INFO 0x52 #define REG_DEVICE_NAME_LENGTH 0x62 #define MAX_DEVICE_NAME_LENGTH 7 #define REG_DEVICE_NAME 0x63 #define REG_PROTECTOR 0x6d +/* Over-charge */ +#define BQ27542_FLAG_BATHI BIT(13) +/* Over Temperature in discharge */ +#define BQ27542_FLAG_OTD BIT(11) +/* Over Temperature in charge */ +#define BQ27542_FLAG_OTC BIT(7) +/* Charge allowed */ +#define BQ27542_FLAG_CHG BIT(3) +/* Discharge */ +#define BQ27542_FLAG_DSG BIT(0) + static int battery_type_id; static int fake_state_of_charge = -1; @@ -69,10 +84,12 @@ int bq27541_probe(void) rv = bq27541_write(REG_CTRL, 0x1); rv |= bq27541_read(REG_CTRL, &battery_type_id); - + /* Read twice to get the right value */ + rv |= bq27541_read(REG_CTRL, &battery_type_id); if (rv) return rv; if (battery_type_id == BQ27541_TYPE_ID || + battery_type_id == BQ27542_TYPE_ID || battery_type_id == BQ27741_TYPE_ID || battery_type_id == BQ27742_TYPE_ID) return EC_SUCCESS; @@ -95,6 +112,16 @@ int battery_device_name(char *device_name, int buf_size) strzcpy(device_name, "", len); return 0; } + /* Battery pack vendor specific */ + if (battery_type_id == BQ27542_TYPE_ID) { + rv = bq27541_write(REG_DATA_FLASH_BLOCK, 0x1); + for (i = 0; i < len; ++i) { + rv |= bq27541_read8(REG_MANUFACTURER_INFO + i, &val); + device_name[i] = val; + } + device_name[i] = '\0'; + return rv; + } rv = bq27541_read8(REG_DEVICE_NAME_LENGTH, &val); if (rv) @@ -112,7 +139,7 @@ int battery_device_name(char *device_name, int buf_size) int battery_state_of_charge_abs(int *percent) { - return EC_ERROR_UNIMPLEMENTED; + return bq27541_read(REG_STATE_OF_CHARGE, percent); } int battery_remaining_capacity(int *capacity) @@ -149,6 +176,9 @@ int battery_time_at_rate(int rate, int *minutes) { int rv; + if (battery_type_id == BQ27542_TYPE_ID) + return EC_ERROR_UNIMPLEMENTED; + rv = bq27541_write(REG_AT_RATE, rate); if (rv) return rv; @@ -197,7 +227,7 @@ static int battery_charging_allowed(int *allowed) if (battery_type_id == BQ27541_TYPE_ID || battery_type_id == BQ27741_TYPE_ID) *allowed = (val & 0x100); - else /* BQ27742_TYPE_ID */ + else /* BQ27742_TYPE_ID, BQ27542_TYPE_ID */ *allowed = (val & 0x8); return EC_SUCCESS; @@ -210,6 +240,25 @@ int battery_get_mode(int *mode) int battery_status(int *status) { + int rv; + int flag = 0; + + *status = 0; + if (battery_type_id == BQ27542_TYPE_ID) { + rv = bq27541_read(REG_FLAGS, &flag); + if (rv) + return rv; + + if (flag & (BQ27542_FLAG_OTC | BQ27542_FLAG_OTD)) + *status |= STATUS_OVERTEMP_ALARM; + if (flag & BQ27542_FLAG_DSG) + *status |= STATUS_DISCHARGING; + if (flag & BQ27542_FLAG_BATHI) + *status |= STATUS_OVERCHARGED_ALARM; + + return EC_SUCCESS; + } + return EC_ERROR_UNIMPLEMENTED; } @@ -249,6 +298,12 @@ void battery_get_params(struct batt_params *batt) batt->flags |= BATT_FLAG_BAD_CURRENT; batt->current = (int16_t)v; + if (battery_remaining_capacity(&batt->remaining_capacity)) + batt->flags |= BATT_FLAG_BAD_REMAINING_CAPACITY; + + if (battery_full_charge_capacity(&batt->full_capacity)) + batt->flags |= BATT_FLAG_BAD_FULL_CAPACITY; + /* Default to not desiring voltage and current */ batt->desired_voltage = batt->desired_current = 0; @@ -353,3 +408,19 @@ DECLARE_CONSOLE_COMMAND(battfake, command_battfake, "percent (-1 = use real level)", "Set fake battery level"); +#ifdef CONFIG_CMD_PWR_AVG +int battery_get_avg_current(void) +{ + int current = -EC_ERROR_UNKNOWN; + + bq27541_read(REG_AVERAGE_CURRENT, ¤t); + return current; +} + +int battery_get_avg_voltage(void) +{ + /* BQ27541 does not have this parameter */ + return -EC_ERROR_UNIMPLEMENTED; +} +#endif /* CONFIG_CMD_PWR_AVG */ + -- cgit v1.2.1