Skip to content

Commit 5055a41

Browse files
dnojiriTerrails
authored andcommitted
charge_state: Add EC_CMD_CHARGE_CONTROL V3
Currently, only if sustain_soc.lower == sustain_soc.upper, the battery sustainer uses IDLE for discharging. This forces a host to set lower < upper to avoid IDLE (and use DISCHARGE only). This is suboptimal not only because the behavior is obscure but also because a host can't specify lower < upper for boards which support IDLE. (That means there is no hysteresis.) This CL adds flags field in EC_CMD_CHARGE_CONTROL so that a host can explicitly disable IDLE. This CL also adds dedicated tests for EC_CMD_CHARGE_CONTROL to increase the coverage. There is no functionality change for the hosts speaking V2. BUG=b:222619859,b:188457962 TEST=make run-sbs_charging TEST=ectool chargecontrol normal 79 80 0x1 LOW_COVERAGE_REASON=All false positives except one trigger which is deliberately not tested. Comments are added for each trigger. Change-Id: I9ca2d5f68f9c1d84baff9feb5eefd7b60e98e474 Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/ec/+/4824703 Reviewed-by: Derek Basehore <[email protected]> Commit-Queue: Daisuke Nojiri <[email protected]> Auto-Submit: Daisuke Nojiri <[email protected]> Code-Coverage: Zoss <[email protected]> Tested-by: Daisuke Nojiri <[email protected]>
1 parent d7ae97e commit 5055a41

File tree

5 files changed

+303
-88
lines changed

5 files changed

+303
-88
lines changed

common/charge_state.c

+111-41
Original file line numberDiff line numberDiff line change
@@ -207,19 +207,13 @@ void reset_prev_disp_charge(void)
207207
prev_disp_charge = -1;
208208
}
209209

210-
static bool battery_sustainer_enabled(void)
211-
{
212-
return sustain_soc.lower != -1 && sustain_soc.upper != -1;
213-
}
214-
215210
int battery_sustainer_set(int8_t lower, int8_t upper)
216211
{
217212
if (lower == -1 || upper == -1) {
218-
if (battery_sustainer_enabled()) {
219-
CPRINTS("Sustainer disabled");
220-
sustain_soc.lower = -1;
221-
sustain_soc.upper = -1;
222-
}
213+
CPRINTS("Sustainer disabled");
214+
sustain_soc.lower = -1;
215+
sustain_soc.upper = -1;
216+
sustain_soc.flags = 0;
223217
return EC_SUCCESS;
224218
}
225219

@@ -242,6 +236,11 @@ static void battery_sustainer_disable(void)
242236
battery_sustainer_set(-1, -1);
243237
}
244238

239+
test_export_static bool battery_sustainer_enabled(void)
240+
{
241+
return sustain_soc.lower != -1 && sustain_soc.upper != -1;
242+
}
243+
245244
static const char *const state_list[] = { "idle", "discharge", "charge",
246245
"precharge" };
247246
BUILD_ASSERT(ARRAY_SIZE(state_list) == NUM_STATES_V2);
@@ -910,58 +909,115 @@ int battery_outside_charging_temperature(void)
910909
return 0;
911910
}
912911

913-
static void sustain_battery_soc(void)
912+
static enum ec_charge_control_mode
913+
sustain_switch_mode(enum ec_charge_control_mode mode)
914914
{
915-
enum ec_charge_control_mode mode = get_chg_ctrl_mode();
916-
int soc;
917-
int rv;
918-
919-
/* If either AC or battery is not present, nothing to do. */
920-
if (!curr.ac || curr.batt.is_present != BP_YES ||
921-
!battery_sustainer_enabled())
922-
return;
923-
924-
soc = charge_get_display_charge() / 10;
915+
enum ec_charge_control_mode new_mode = mode;
916+
int soc = charge_get_display_charge() / 10;
925917

926918
/*
927-
* When lower < upper, the sustainer discharges using DISCHARGE. When
928-
* lower == upper, the sustainer discharges using IDLE. The following
929-
* switch statement handle both cases but in reality either DISCHARGE
930-
* or IDLE is used but not both.
919+
* The sustain range is defined by 'lower' and 'upper' where the equal
920+
* values are inclusive:
921+
*
922+
* |------------NORMAL------------+--IDLE--+---DISCHARGE---|
923+
* 0% ^ ^ 100%
924+
* lower upper
925+
*
926+
* The switch statement below allows the sustainer to start with any soc
927+
* (0% ~ 100%) and any previous lower & upper limits. It sets mode to
928+
* NORMAL to charge till the soc hits the upper limit or sets mode to
929+
* DISCHARGE to discharge till the soc hits the upper limit.
930+
*
931+
* Once the soc enters in the sustain range, it'll switch to IDLE. In
932+
* IDLE mode, the system power is supplied from the AC. Thus, the soc
933+
* normally should stay in the sustain range unless there is high load
934+
* on the system or the charger is too weak.
935+
*
936+
* Some boards have a sing capacitor problem with mode == IDLE. For such
937+
* boards, a host can specify EC_CHARGE_CONTROL_FLAG_NO_IDLE, which
938+
* makes the sustainer use DISCHARGE instead of IDLE. This is done by
939+
* setting lower != upper in V2, which doesn't support the flag.
931940
*/
932941
switch (mode) {
933942
case CHARGE_CONTROL_NORMAL:
934-
/* Going up. Always DISCHARGE if the soc is above upper. */
935-
if (sustain_soc.lower == soc && soc == sustain_soc.upper) {
936-
mode = CHARGE_CONTROL_IDLE;
937-
} else if (sustain_soc.upper < soc) {
938-
mode = CHARGE_CONTROL_DISCHARGE;
943+
/* Currently charging */
944+
if (sustain_soc.upper < soc) {
945+
/*
946+
* We come here only if the soc is already above the
947+
* upper limit at the time the sustainer started.
948+
*/
949+
new_mode = CHARGE_CONTROL_DISCHARGE;
950+
} else if (sustain_soc.upper == soc) {
951+
/*
952+
* We've been charging and finally reached the upper.
953+
* Let's switch to IDLE to stay.
954+
*/
955+
if (sustain_soc.flags & EC_CHARGE_CONTROL_FLAG_NO_IDLE)
956+
new_mode = CHARGE_CONTROL_DISCHARGE;
957+
else
958+
new_mode = CHARGE_CONTROL_IDLE;
939959
}
940960
break;
941961
case CHARGE_CONTROL_IDLE:
942962
/* Discharging naturally */
943963
if (soc < sustain_soc.lower)
944-
mode = CHARGE_CONTROL_NORMAL;
964+
/*
965+
* Presumably, we stayed in the sustain range for a
966+
* while but finally fell off the range. Let's charge to
967+
* the upper.
968+
*/
969+
new_mode = CHARGE_CONTROL_NORMAL;
970+
else if (sustain_soc.upper < soc)
971+
/*
972+
* This can happen only if sustainer is restarted with
973+
* decreased upper limit. Let's discharge to the upper.
974+
*/
975+
new_mode = CHARGE_CONTROL_DISCHARGE;
945976
break;
946977
case CHARGE_CONTROL_DISCHARGE:
947978
/* Discharging actively. */
948-
if (sustain_soc.lower == soc && soc == sustain_soc.upper) {
949-
mode = CHARGE_CONTROL_IDLE;
950-
} else if (soc < sustain_soc.lower) {
951-
mode = CHARGE_CONTROL_NORMAL;
952-
}
979+
if (soc <= sustain_soc.upper &&
980+
!(sustain_soc.flags & EC_CHARGE_CONTROL_FLAG_NO_IDLE))
981+
/*
982+
* Normal case. We've been discharging and finally
983+
* reached the upper. Let's switch to IDLE to stay.
984+
*/
985+
new_mode = CHARGE_CONTROL_IDLE;
986+
else if (soc < sustain_soc.lower)
987+
/*
988+
* This can happen only if sustainer is restarted with
989+
* increase lower limit. Let's charge to the upper (then
990+
* switch to IDLE).
991+
*/
992+
new_mode = CHARGE_CONTROL_NORMAL;
953993
break;
954994
default:
955-
return;
995+
break;
956996
}
957997

958-
if (mode == get_chg_ctrl_mode())
998+
return new_mode;
999+
}
1000+
1001+
static void sustain_battery_soc(void)
1002+
{
1003+
enum ec_charge_control_mode mode = get_chg_ctrl_mode();
1004+
enum ec_charge_control_mode new_mode;
1005+
int rv;
1006+
1007+
/* If either AC or battery is not present, nothing to do. */
1008+
if (!curr.ac || curr.batt.is_present != BP_YES ||
1009+
!battery_sustainer_enabled())
9591010
return;
9601011

961-
rv = set_chg_ctrl_mode(mode);
1012+
new_mode = sustain_switch_mode(mode);
1013+
1014+
if (new_mode == mode)
1015+
return;
1016+
1017+
rv = set_chg_ctrl_mode(new_mode);
9621018
CPRINTS("%s: %s control mode to %s", __func__,
9631019
rv == EC_SUCCESS ? "Switched" : "Failed to switch",
964-
mode_text[mode]);
1020+
mode_text[new_mode]);
9651021
}
9661022

9671023
static void current_limit_battery_soc(void)
@@ -2093,13 +2149,27 @@ charge_command_charge_control(struct host_cmd_handler_args *args)
20932149
return EC_RES_UNAVAILABLE;
20942150
if (rv)
20952151
return EC_RES_INVALID_PARAM;
2152+
if (args->version == 2) {
2153+
/*
2154+
* V2 uses lower == upper to indicate NO_IDLE.
2155+
* TODO: Remove this if-branch once all OS-side
2156+
* components are updated to v3.
2157+
*/
2158+
if (sustain_soc.lower < sustain_soc.upper)
2159+
sustain_soc.flags =
2160+
EC_CHARGE_CONTROL_FLAG_NO_IDLE;
2161+
} else {
2162+
sustain_soc.flags = p->flags;
2163+
}
20962164
} else {
20972165
battery_sustainer_disable();
20982166
}
20992167
} else if (p->cmd == EC_CHARGE_CONTROL_CMD_GET) {
21002168
r->mode = get_chg_ctrl_mode();
21012169
r->sustain_soc.lower = sustain_soc.lower;
21022170
r->sustain_soc.upper = sustain_soc.upper;
2171+
if (args->version > 2)
2172+
r->flags = sustain_soc.flags;
21032173
args->response_size = sizeof(*r);
21042174
return EC_RES_SUCCESS;
21052175
} else {
@@ -2113,7 +2183,7 @@ charge_command_charge_control(struct host_cmd_handler_args *args)
21132183
return EC_RES_SUCCESS;
21142184
}
21152185
DECLARE_HOST_COMMAND(EC_CMD_CHARGE_CONTROL, charge_command_charge_control,
2116-
EC_VER_MASK(2));
2186+
EC_VER_MASK(2) | EC_VER_MASK(3));
21172187

21182188
static enum ec_status
21192189
charge_command_current_limit(struct host_cmd_handler_args *args)

include/charge_state.h

+1
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,7 @@ struct charge_state_data {
116116
struct sustain_soc {
117117
int8_t lower;
118118
int8_t upper;
119+
uint8_t flags; /* enum ec_charge_control_flag */
119120
};
120121

121122
#define BAT_MAX_DISCHG_CURRENT 5000 /* mA */

include/ec_commands.h

+8-3
Original file line numberDiff line numberDiff line change
@@ -4428,7 +4428,7 @@ struct ec_params_i2c_write {
44284428
* discharge the battery.
44294429
*/
44304430
#define EC_CMD_CHARGE_CONTROL 0x0096
4431-
#define EC_VER_CHARGE_CONTROL 2
4431+
#define EC_VER_CHARGE_CONTROL 3
44324432

44334433
enum ec_charge_control_mode {
44344434
CHARGE_CONTROL_NORMAL = 0,
@@ -4450,12 +4450,16 @@ enum ec_charge_control_cmd {
44504450
EC_CHARGE_CONTROL_CMD_GET,
44514451
};
44524452

4453+
enum ec_charge_control_flag {
4454+
EC_CHARGE_CONTROL_FLAG_NO_IDLE = BIT(0),
4455+
};
4456+
44534457
struct ec_params_charge_control {
44544458
uint32_t mode; /* enum charge_control_mode */
44554459

44564460
/* Below are the fields added in V2. */
44574461
uint8_t cmd; /* enum ec_charge_control_cmd. */
4458-
uint8_t reserved;
4462+
uint8_t flags; /* enum ec_charge_control_flag (v3+) */
44594463
/*
44604464
* Lower and upper thresholds for battery sustainer. This struct isn't
44614465
* named to avoid tainting foreign projects' name spaces.
@@ -4477,7 +4481,8 @@ struct ec_response_charge_control {
44774481
int8_t lower;
44784482
int8_t upper;
44794483
} sustain_soc;
4480-
uint16_t reserved;
4484+
uint8_t flags; /* enum ec_charge_control_flag (v3+) */
4485+
uint8_t reserved;
44814486
} __ec_align4;
44824487

44834488
/*****************************************************************************/

0 commit comments

Comments
 (0)