@@ -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-
215210int 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+
245244static const char * const state_list [] = { "idle" , "discharge" , "charge" ,
246245 "precharge" };
247246BUILD_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
9671023static 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}
21152185DECLARE_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
21182188static enum ec_status
21192189charge_command_current_limit (struct host_cmd_handler_args * args )
0 commit comments