@@ -207,19 +207,13 @@ void reset_prev_disp_charge(void)
207
207
prev_disp_charge = -1 ;
208
208
}
209
209
210
- static bool battery_sustainer_enabled (void )
211
- {
212
- return sustain_soc .lower != -1 && sustain_soc .upper != -1 ;
213
- }
214
-
215
210
int battery_sustainer_set (int8_t lower , int8_t upper )
216
211
{
217
212
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 ;
223
217
return EC_SUCCESS ;
224
218
}
225
219
@@ -242,6 +236,11 @@ static void battery_sustainer_disable(void)
242
236
battery_sustainer_set (-1 , -1 );
243
237
}
244
238
239
+ test_export_static bool battery_sustainer_enabled (void )
240
+ {
241
+ return sustain_soc .lower != -1 && sustain_soc .upper != -1 ;
242
+ }
243
+
245
244
static const char * const state_list [] = { "idle" , "discharge" , "charge" ,
246
245
"precharge" };
247
246
BUILD_ASSERT (ARRAY_SIZE (state_list ) == NUM_STATES_V2 );
@@ -910,58 +909,115 @@ int battery_outside_charging_temperature(void)
910
909
return 0 ;
911
910
}
912
911
913
- static void sustain_battery_soc (void )
912
+ static enum ec_charge_control_mode
913
+ sustain_switch_mode (enum ec_charge_control_mode mode )
914
914
{
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 ;
925
917
926
918
/*
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.
931
940
*/
932
941
switch (mode ) {
933
942
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 ;
939
959
}
940
960
break ;
941
961
case CHARGE_CONTROL_IDLE :
942
962
/* Discharging naturally */
943
963
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 ;
945
976
break ;
946
977
case CHARGE_CONTROL_DISCHARGE :
947
978
/* 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 ;
953
993
break ;
954
994
default :
955
- return ;
995
+ break ;
956
996
}
957
997
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 ())
959
1010
return ;
960
1011
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 );
962
1018
CPRINTS ("%s: %s control mode to %s" , __func__ ,
963
1019
rv == EC_SUCCESS ? "Switched" : "Failed to switch" ,
964
- mode_text [mode ]);
1020
+ mode_text [new_mode ]);
965
1021
}
966
1022
967
1023
static void current_limit_battery_soc (void )
@@ -2093,13 +2149,27 @@ charge_command_charge_control(struct host_cmd_handler_args *args)
2093
2149
return EC_RES_UNAVAILABLE ;
2094
2150
if (rv )
2095
2151
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
+ }
2096
2164
} else {
2097
2165
battery_sustainer_disable ();
2098
2166
}
2099
2167
} else if (p -> cmd == EC_CHARGE_CONTROL_CMD_GET ) {
2100
2168
r -> mode = get_chg_ctrl_mode ();
2101
2169
r -> sustain_soc .lower = sustain_soc .lower ;
2102
2170
r -> sustain_soc .upper = sustain_soc .upper ;
2171
+ if (args -> version > 2 )
2172
+ r -> flags = sustain_soc .flags ;
2103
2173
args -> response_size = sizeof (* r );
2104
2174
return EC_RES_SUCCESS ;
2105
2175
} else {
@@ -2113,7 +2183,7 @@ charge_command_charge_control(struct host_cmd_handler_args *args)
2113
2183
return EC_RES_SUCCESS ;
2114
2184
}
2115
2185
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 ) );
2117
2187
2118
2188
static enum ec_status
2119
2189
charge_command_current_limit (struct host_cmd_handler_args * args )
0 commit comments