Skip to content

Commit aa609ec

Browse files
committed
driver: isl9241: clear BGATE_OFF when bypass entry aborts
isl9241_nvdc_to_bypass() sets CONTROL1.BGATE_OFF in step 7 and clears it in step 15. If the VSYS ADC read fails or VSYS does not reach the target within ISL9241_BYPASS_VSYS_TIMEOUT_MS, the function returns early and skips step 15. The caller isl9241_enable_bypass_mode() then calls isl9241_bypass_to_nvdc(), which does not touch BGATE and returns success. The battery FET stays off and the battery is isolated. Nothing else clears the bit. Dropping to 100 W or less does not call the driver again, because the charger is not in bypass. Only EC re-init or a later successful bypass entry clears it. A Framework 16 (Ryzen 7040, EC lotus-4.0.5-2246d58) was in this state with CONTROL0=0x0060 and CONTROL1=0x0343. It did not charge on 65 W and 100 W USB-C supplies and sat at 8-9 % with 0-4 mA battery current. Clearing only BGATE_OFF through CHARGE_STATE SET_PARAM made it charge at 2.43 A within 25 seconds, without a reboot. Clear BGATE_OFF before returning when the VSYS wait fails. The rest of the rollback stays in isl9241_bypass_to_nvdc(). Add two tests, run in a new testcase drivers.isl9241.bypass with CONFIG_PLATFORM_EC_CHARGER_BYPASS_MODE. Both check that BGATE_OFF is clear after charger_enable_bypass_mode(true) aborts. test_bypass_abort_releases_bgate injects a read failure on the VSYS ADC register. test_bypass_timeout_releases_bgate gives the charge manager a 20 V supply on port 0 while the emulated VSYS stays at 0, so the wait times out. Add isl9241_emul_get_i2c_common_data() to the ISL9241 emulator for the first one, like the ISL923x emulator has. Include <stdbool.h> in isl9241_public.h. It declares bool isl9241_is_in_bypass_mode(), and the ISL9241 emulator includes it first; with GCC 14 the emulator did not compile without it. BRANCH=fwk-tulip-29169 BUG=lotus battery stops charging after an aborted bypass mode entry TEST=twister drivers.isl9241.bypass passes; both new tests fail without the fix
1 parent c6575ef commit aa609ec

6 files changed

Lines changed: 111 additions & 2 deletions

File tree

‎driver/charger/isl9241.c‎

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -911,16 +911,28 @@ static enum ec_error_list isl9241_nvdc_to_bypass(int chgnum)
911911
if (_get_vsys_voltage(chgnum, 0, &vsys)) {
912912
CPRINTS("Aborting bypass mode. Vsys is unknown.");
913913
rv = EC_ERROR_UNKNOWN;
914-
goto unlock_control3;
914+
break;
915915
}
916916
if (timestamp_expired(deadline, NULL)) {
917917
CPRINTS("Aborting bypass mode. Vsys too low (%d < %d)",
918918
vsys, vsys_target);
919919
rv = EC_ERROR_TIMEOUT;
920-
goto unlock_control3;
920+
break;
921921
}
922922
} while (vsys < vsys_target - 256);
923923

924+
if (rv) {
925+
/*
926+
* Undo step 7. The caller's rollback to NVDC does not touch
927+
* BGATE, so the battery would stay disconnected and never
928+
* charge until the charger is re-initialized or a later
929+
* bypass entry succeeds.
930+
*/
931+
isl9241_update(chgnum, ISL9241_REG_CONTROL1,
932+
ISL9241_CONTROL1_BGATE_OFF, MASK_CLR);
933+
goto unlock_control3;
934+
}
935+
924936
/* 10*: Turn on Bypass gate */
925937
isl9241_update(chgnum, ISL9241_REG_CONTROL0,
926938
ISL9241_CONTROL0_EN_BYPASS_GATE, MASK_SET);

‎include/driver/charger/isl9241_public.h‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88
#ifndef __CROS_EC_DRIVER_CHARGER_ISL9241_PUBLIC_H
99
#define __CROS_EC_DRIVER_CHARGER_ISL9241_PUBLIC_H
1010

11+
#include <stdbool.h>
12+
1113
#define ISL9241_ADDR_FLAGS 0x09
1214

1315
/* Default minimum VIN voltage controlled by ISL9241_REG_VIN_VOLTAGE */

‎zephyr/emul/emul_isl9241.c‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,12 @@ uint16_t isl9241_emul_peek(const struct emul *emul, int reg)
7373
return data->regs[reg];
7474
}
7575

76+
struct i2c_common_emul_data *
77+
isl9241_emul_get_i2c_common_data(const struct emul *emul)
78+
{
79+
return emul->data;
80+
}
81+
7682
void isl9241_emul_set_vbus(const struct emul *emul, int vbus_mv)
7783
{
7884
struct isl9241_emul_data *data = (struct isl9241_emul_data *)emul->data;

‎zephyr/include/emul/emul_isl9241.h‎

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,19 @@
66
#ifndef EMUL_ISL9241_H
77
#define EMUL_ISL9241_H
88

9+
#include "emul/emul_common_i2c.h"
10+
911
#include <zephyr/drivers/emul.h>
1012

13+
/**
14+
* Get the I2C common data, e.g. to inject register read failures
15+
*
16+
* @param emul - ISL9241 emulator data
17+
* @return I2C common emulator data
18+
*/
19+
struct i2c_common_emul_data *
20+
isl9241_emul_get_i2c_common_data(const struct emul *emul);
21+
1122
/**
1223
* Peek an internal register value
1324
*

‎zephyr/test/drivers/isl9241/src/isl9241.c‎

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
*/
55

66
#include "battery_smart.h"
7+
#include "charge_manager.h"
78
#include "charger.h"
89
#include "console.h"
910
#include "driver/charger/isl9241.h"
@@ -252,6 +253,73 @@ ZTEST_F(isl9241_driver, test_dump_registers)
252253
}
253254
}
254255

256+
/*
257+
* An NVDC -> bypass entry that aborted while waiting for VSYS left
258+
* CONTROL1.BGATE_OFF set, so the battery stayed disconnected on AC.
259+
*/
260+
ZTEST_F(isl9241_driver, test_bypass_abort_releases_bgate)
261+
{
262+
struct i2c_common_emul_data *common_data =
263+
isl9241_emul_get_i2c_common_data(fixture->isl9241_emul);
264+
uint16_t control1;
265+
266+
if (!IS_ENABLED(CONFIG_PLATFORM_EC_CHARGER_BYPASS_MODE))
267+
ztest_test_skip();
268+
269+
/* Make NVDC -> bypass abort while it waits for VSYS. */
270+
i2c_common_emul_set_read_fail_reg(common_data,
271+
ISL9241_REG_VSYS_ADC_RESULTS);
272+
charger_enable_bypass_mode(CHARGER_SOLO, true);
273+
i2c_common_emul_set_read_fail_reg(common_data,
274+
I2C_COMMON_EMUL_NO_FAIL_REG);
275+
276+
control1 = isl9241_emul_peek(fixture->isl9241_emul,
277+
ISL9241_REG_CONTROL1);
278+
zassert_false(control1 & ISL9241_CONTROL1_BGATE_OFF,
279+
"BGATE still forced off after aborted entry: 0x%04x",
280+
control1);
281+
}
282+
283+
/* Same as above, but the entry aborted because VSYS never reached target. */
284+
ZTEST_F(isl9241_driver, test_bypass_timeout_releases_bgate)
285+
{
286+
struct charge_port_info charge = { .current = 3000, .voltage = 20000 };
287+
struct charge_port_info none = { 0 };
288+
uint16_t control0, control1;
289+
290+
if (!IS_ENABLED(CONFIG_PLATFORM_EC_CHARGER_BYPASS_MODE))
291+
ztest_test_skip();
292+
293+
/* VSYS stays at 0 while the target follows the 20 V supply. */
294+
k_msleep(1000);
295+
charge_manager_update_charge(CHARGE_SUPPLIER_TYPEC, USBC_PORT_C0,
296+
&charge);
297+
/* Port 0 is dual-role, so it charges only as the override port. */
298+
zassert_ok(charge_manager_set_override(USBC_PORT_C0));
299+
k_msleep(500);
300+
zassert_equal(charge_manager_get_charger_voltage(), charge.voltage,
301+
"port %d supplier %d",
302+
charge_manager_get_active_charge_port(),
303+
charge_manager_get_supplier());
304+
isl9241_emul_set_vsys(fixture->isl9241_emul, 0);
305+
306+
charger_enable_bypass_mode(CHARGER_SOLO, true);
307+
charge_manager_set_override(OVERRIDE_OFF);
308+
charge_manager_update_charge(CHARGE_SUPPLIER_TYPEC, USBC_PORT_C0,
309+
&none);
310+
k_msleep(500);
311+
312+
control0 = isl9241_emul_peek(fixture->isl9241_emul,
313+
ISL9241_REG_CONTROL0);
314+
control1 = isl9241_emul_peek(fixture->isl9241_emul,
315+
ISL9241_REG_CONTROL1);
316+
zassert_false(control0 & ISL9241_CONTROL0_EN_BYPASS_GATE,
317+
"Bypass entry did not abort: 0x%04x", control0);
318+
zassert_false(control1 & ISL9241_CONTROL1_BGATE_OFF,
319+
"BGATE still forced off after aborted entry: 0x%04x",
320+
control1);
321+
}
322+
255323
ZTEST(isl9241_driver, test_prochot_dump)
256324
{
257325
/*

‎zephyr/test/drivers/testcase.yaml‎

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -741,6 +741,16 @@ tests:
741741
extra_dtc_overlay_files:
742742
- ./boards/native_sim.overlay
743743
- ./isl9241/usbc.dts
744+
drivers.isl9241.bypass:
745+
extra_configs:
746+
- CONFIG_LINK_TEST_SUITE_ISL9241=y
747+
- CONFIG_PLATFORM_EC_CHARGER_DUMP_PROCHOT=y
748+
- CONFIG_PLATFORM_EC_CONSOLE_CMD_CHARGER_DUMP=y
749+
- CONFIG_PLATFORM_EC_CHARGER_ISL9241=y
750+
- CONFIG_PLATFORM_EC_CHARGER_BYPASS_MODE=y
751+
extra_dtc_overlay_files:
752+
- ./boards/native_sim.overlay
753+
- ./isl9241/usbc.dts
744754
drivers.isl9241.ec_host_cmd:
745755
extra_configs:
746756
- CONFIG_LINK_TEST_SUITE_ISL9241=y

0 commit comments

Comments
 (0)