From bd8cdbe80c41895a39591a3cb79d5a7f7f9d19fb Mon Sep 17 00:00:00 2001 From: Daisuke Nojiri Date: Thu, 9 Jun 2022 10:01:41 -0600 Subject: Agah: Enable Nvidia GPU D-Notify driver This patch enables Nvidia GPU D-Notify driver for Agah. BUG=b:216485035, b:236674641 BRANCH=None TEST=On Agah, plug & unplug BJ & USB-C and verify expected modes (nvdc, nvdc_chrg, bypass, bypass_chrg, bat) are selected. TEST=On Agah, plug & unplug BJ & USB-C and verify expected D-levels are selected. Signed-off-by: Daisuke Nojiri Change-Id: I17b60e6054c91c60a7c1931e10a738607951055c Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/ec/+/3704268 Reviewed-by: Tim Wawrzynczak --- board/agah/board.c | 13 +++ board/agah/board.h | 5 + board/agah/charger_isl9241.c | 218 ++++++++++++++++++++++++------------------- board/agah/gpio.inc | 8 +- 4 files changed, 146 insertions(+), 98 deletions(-) diff --git a/board/agah/board.c b/board/agah/board.c index 979c9a38aa..47f2628fbb 100644 --- a/board/agah/board.c +++ b/board/agah/board.c @@ -26,6 +26,8 @@ #include "usbc_config.h" #include "util.h" +#include "driver/nvidia_gpu.h" + #include "gpio_list.h" /* Must come after other header files. */ /* Console output macros */ @@ -34,6 +36,15 @@ static int block_sequence; +struct d_notify_policy d_notify_policies[] = { + AC_ATLEAST_W(100), + AC_ATLEAST_W(65), + AC_DC, + DC_ATLEAST_SOC(20), + DC_ATLEAST_SOC(5), +}; +BUILD_ASSERT(ARRAY_SIZE(d_notify_policies) == D_NOTIFY_COUNT); + __override void board_cbi_init(void) { } @@ -63,6 +74,8 @@ static void board_init(void) } gpio_enable_interrupt(GPIO_PG_PP3300_S5_OD); gpio_enable_interrupt(GPIO_BJ_ADP_PRESENT_ODL); + + nvidia_gpu_init_policy(d_notify_policies); } DECLARE_HOOK(HOOK_INIT, board_init, HOOK_PRIO_DEFAULT); diff --git a/board/agah/board.h b/board/agah/board.h index d4ce9f23d1..d03aba2580 100644 --- a/board/agah/board.h +++ b/board/agah/board.h @@ -13,6 +13,11 @@ /* Baseboard features */ #include "baseboard.h" +/* + * Nvidia GPU + */ +#define CONFIG_GPU_NVIDIA + /* * This will happen automatically on NPCX9 ES2 and later. Do not remove * until we can confirm all earlier chips are out of service. diff --git a/board/agah/charger_isl9241.c b/board/agah/charger_isl9241.c index 138c3841f7..1bc77c0ee4 100644 --- a/board/agah/charger_isl9241.c +++ b/board/agah/charger_isl9241.c @@ -3,6 +3,36 @@ * found in the LICENSE file. */ +/* + * + * We need to deal with plug / unplug of AC chargers: + * + * +---------+ +USB +---------+ + * | BATTERY |------------>| BATTERY | + * | |<------------| +USB | + * +---------+ -USB +---------+ + * | ^ | ^ + * +BJ | | -BJ +BJ | | -BJ + * v | v | + * +---------+ +USB +---------+ + * | BATTERY |------------>| BATTERY | + * | +BJ |<------------| +BJ+USB | + * +---------+ -USB +---------+ + * + * Depending on available battery charge, power rating of the new charger, and + * the system power state, transition/throttling may or may not occur but + * switching chargers is handled as follows: + * + * 1. Detects a new charger or removal of an existing charger. + * 2. charge_manager_update_charge is called with new charger's info. + * 3. board_set_active_charge_port is called. + * 3.1 It triggers hard & soft throttling for AP & GPU. + * 3.2 It disable active port then enables the new port. + * 4. HOOK_POWER_SUPPLY_CHANGE is called. We disables hard throttling. + * 5. charger task wakes up on HOOK_POWER_SUPPLY_CHANGE, enables (or disables) + * bypass mode. + */ + #include "common.h" #include "charge_manager.h" @@ -15,6 +45,7 @@ #include "gpio.h" #include "hooks.h" #include "stdbool.h" +#include "throttle_ap.h" #include "usbc_ppc.h" #include "usb_pd.h" #include "util.h" @@ -32,71 +63,42 @@ const struct charger_config_t chg_chips[] = { }; BUILD_ASSERT(ARRAY_SIZE(chg_chips) == CHARGER_NUM); -static int board_disable_bj_port(void) +static int board_enable_bj_port(bool enable) { - gpio_set_level(GPIO_EN_PPVAR_BJ_ADP_L, 1); - /* If the current port is BJ, disable bypass mode. */ - if (charge_manager_get_supplier() == CHARGE_SUPPLIER_DEDICATED) - return charger_enable_bypass_mode(0, 0); + if (enable) { + if (gpio_get_level(GPIO_BJ_ADP_PRESENT_ODL)) + return EC_ERROR_INVAL; + gpio_set_level(GPIO_EN_PPVAR_BJ_ADP_L, 0); + } else { + gpio_set_level(GPIO_EN_PPVAR_BJ_ADP_L, 1); + } - CPRINTS("BJ power is disabled"); + CPRINTS("BJ power is %sabled", enable ? "en" : "dis"); return EC_SUCCESS; } -static int board_enable_bj_port(void) -{ - if (gpio_get_level(GPIO_BJ_ADP_PRESENT_ODL)) - return EC_ERROR_INVAL; - gpio_set_level(GPIO_EN_PPVAR_BJ_ADP_L, 0); - - CPRINTS("BJ power is enabled"); - - return charger_enable_bypass_mode(0, 1); -} - -/* - * TODO: - * - * When AC is being plugged in (including switching source port), - * 1. Deassert NVIDIA_GPU_ACOFF_ODL. - * 2. Call evaluate_d_notify. - * - * When AC is being lost, - * 1. Assert NVIDIA_GPU_ACOFF_ODL. - * 2. Set D-Notify to D5. - * 3. Differ-call - * a. Deassert NVIDIA_GPU_ACOFF_ODL. - * b. evaluate_d_notify - */ -static int board_throttle_ap_gpu(bool enable) +static void board_throttle_ap_gpu(void) { - int rv = EC_SUCCESS; - - if (!chipset_in_state(CHIPSET_STATE_ON)) - return EC_SUCCESS; - - CPRINTS("TODO: %s to %s AP & GPU (%d)", rv ? "Failed" : "Succeeded", - enable ? "throttle" : "unthrottle", rv); - - return rv; + throttle_ap(THROTTLE_ON, THROTTLE_HARD, THROTTLE_SRC_AC); + throttle_gpu(THROTTLE_ON, THROTTLE_HARD, THROTTLE_SRC_AC); } /* Disable all VBUS sink ports except . = -1 disables all ports. */ -static int board_disable_vbus_sink(int port) +static int board_disable_other_vbus_sink(int except_port) { int i, r, rv = EC_SUCCESS; for (i = 0; i < ppc_cnt; i++) { - if (i == port) + if (i == except_port) continue; /* * Do not return early if one fails otherwise we can get into a * boot loop assertion failure. */ r = ppc_vbus_sink_enable(i, 0); - CPRINTS("%s to disable sink path C%d (%d).", - r ? "Failed" : "Succeeded", i, r); + if (r) + CPRINTS("Failed to disable sink path C%d (%d)", i, r); rv |= r; } @@ -107,74 +109,71 @@ static int board_disable_vbus_sink(int port) #define MIN_BATT_FOR_SWITCHING_SOURCE_PORT 1 /* - * It should also work on POR with/without a battery: - * - * 1. EC gathers power info of all ports. - * 2. Identify the highest power port. - * 3. If - * 1. battery soc = 0% --> Exit - * 2. BJ_ADP_PRESENT_ODL = 1 --> Exit - * 3. highest power port == active port --> Exit - * 4. If - * 1. in S0, throttle AP & GPU to the DC rating. - * 5. Turn off the current active port. - * 6. Turn on the highest power port. - * 7. If - * 1. in S0, throttle AP & GPU back. - * - * TODO: Are the following cases covered? - * 1. Two AC adapters are plugged. Then, the active adapter is removed. - * * TODO: Recover from incomplete execution: - * 1. Failed to turn on/off PPC. */ int board_set_active_charge_port(int port) { - enum charge_supplier supplier = charge_manager_get_supplier(); + enum charge_supplier active_supplier = charge_manager_get_supplier(); int active_port = charge_manager_get_active_charge_port(); - CPRINTS("Changing charge port to %d (current port=%d supplier=%d)", - port, active_port, supplier); + CPRINTS("Switching charger from P%d (supplier=%d) to P%d", + active_port, active_supplier, port); if (port == CHARGE_PORT_NONE) { CPRINTS("Disabling all charger ports"); - board_throttle_ap_gpu(1); - - board_disable_bj_port(); - board_disable_vbus_sink(-1); + board_enable_bj_port(false); + board_disable_other_vbus_sink(-1); return EC_SUCCESS; } + /* Return on invalid or no-op call. */ if (port < 0 || CHARGE_PORT_COUNT <= port) { return EC_ERROR_INVAL; } else if (port == active_port) { return EC_SUCCESS; } else if (board_vbus_source_enabled(port)) { /* Don't charge from a USBC source port */ - CPRINTS("Don't enable C%d. It's sourcing.", port); + CPRINTS("Don't enable P%d. It's sourcing.", port); return EC_ERROR_INVAL; } + /* + * If we're in S0, throttle AP and GPU. They'll be unthrottled when + * a port/supply switch completes (via HOOK_POWER_SUPPLY_CHANGE). + * + * If we're running currently on a battery (active_supplier == NONE), we + * don't need to throttle because we're not disabling any port. + */ + if (chipset_in_state(CHIPSET_STATE_ON) + && active_supplier != CHARGE_SUPPLIER_NONE) + board_throttle_ap_gpu(); + + /* + * We're here for the two cases: + * 1. A new charger was connected. + * 2. One charger was disconnected and we're switching to another. + */ + /* * We need to check the battery if we're switching a source port. If * we're just starting up or no AC was previously plugged, we shouldn't * check the battery. Both cases can be caught by supplier == NONE. */ - if (supplier != CHARGE_SUPPLIER_NONE) { + if (active_supplier != CHARGE_SUPPLIER_NONE) { if (charge_get_percent() < MIN_BATT_FOR_SWITCHING_SOURCE_PORT) return EC_ERROR_NOT_POWERED; } /* Turn off other ports' sink paths before enabling requested port. */ - if (port == CHARGE_PORT_TYPEC0 || port == CHARGE_PORT_TYPEC1) { + if (is_pd_port(port)) { /* - * BJ port is on POR. So, we need to turn it off even if we're - * not previously on BJ. + * BJ port is enabled on start-up. So, we need to turn it off + * even if we were not previously on BJ. */ - board_disable_bj_port(); - if (board_disable_vbus_sink(port)) + board_enable_bj_port(false); + if (board_disable_other_vbus_sink(port)) return EC_ERROR_UNCHANGED; /* Enable requested USBC charge port. */ @@ -187,16 +186,12 @@ int board_set_active_charge_port(int port) * We can't proceed unless both ports are successfully * disconnected as sources. */ - if (board_disable_vbus_sink(-1)) + if (board_disable_other_vbus_sink(-1)) return EC_ERROR_UNKNOWN; - board_enable_bj_port(); + board_enable_bj_port(true); } - /* Switching port is complete. Turn off throttling. */ - if (supplier != CHARGE_SUPPLIER_NONE) - board_throttle_ap_gpu(0); - - CPRINTS("New charger p%d", port); + CPRINTS("New charger P%d", port); return EC_SUCCESS; } @@ -215,31 +210,62 @@ static const struct charge_port_info bj_power = { }; /* Debounce time for BJ plug/unplug */ -#define BJ_DEBOUNCE_MS 1000 +#define BJ_DEBOUNCE_MS CONFIG_EXTPOWER_DEBOUNCE_MS + +int board_should_charger_bypass(void) +{ + return charge_manager_get_active_charge_port() == DEDICATED_CHARGE_PORT; +} -static void bj_connect_deferred(void) +static void bj_connect(void) { static int8_t bj_connected = -1; - const struct charge_port_info *pi = NULL; int connected = !gpio_get_level(GPIO_BJ_ADP_PRESENT_ODL); + /* Debounce */ if (connected == bj_connected) return; - if (connected) - pi = &bj_power; + bj_connected = connected; + CPRINTS("BJ %sconnected", connected ? "" : "dis"); charge_manager_update_charge(CHARGE_SUPPLIER_DEDICATED, - DEDICATED_CHARGE_PORT, pi); - bj_connected = connected; - CPRINTS("BJ %s", connected ? "connected" : "disconnected"); + DEDICATED_CHARGE_PORT, + connected ? &bj_power : NULL); } -DECLARE_DEFERRED(bj_connect_deferred); +DECLARE_DEFERRED(bj_connect); +/* This handler shouldn't be needed if ACOK from isl9241 is working. */ void bj_present_interrupt(enum gpio_signal signal) { - hook_call_deferred(&bj_connect_deferred_data, BJ_DEBOUNCE_MS * MSEC); + hook_call_deferred(&bj_connect_data, BJ_DEBOUNCE_MS * MSEC); +} + +void ac_change(void) +{ + /* + * Serialize. We don't handle USB-C here because we'll get a + * notification from TCPC. + */ + hook_call_deferred(&bj_connect_data, 0); +} +DECLARE_HOOK(HOOK_AC_CHANGE, ac_change, HOOK_PRIO_DEFAULT); + + +static void power_supply_changed(void) +{ + /* + * We've switched to a new charge port (or no port). Hardware throttles + * can be removed now. Software throttles may stay enabled and change + * as the situation changes. + */ + throttle_ap(THROTTLE_OFF, THROTTLE_HARD, THROTTLE_SRC_AC); + /* + * Unthrottling GPU is done through a deferred call scheduled when it + * was throttled. + */ } +DECLARE_HOOK(HOOK_POWER_SUPPLY_CHANGE, power_supply_changed, HOOK_PRIO_DEFAULT); static void bj_state_init(void) { @@ -252,6 +278,6 @@ static void bj_state_init(void) charge_manager_update_charge(j, i, NULL); } - bj_connect_deferred(); + bj_connect(); } DECLARE_HOOK(HOOK_INIT, bj_state_init, HOOK_PRIO_INIT_CHARGE_MANAGER + 1); diff --git a/board/agah/gpio.inc b/board/agah/gpio.inc index b708231cc4..7f38c87057 100644 --- a/board/agah/gpio.inc +++ b/board/agah/gpio.inc @@ -67,8 +67,13 @@ GPIO(VCCST_PWRGD_OD, PIN(A, 4), GPIO_ODR_LOW) GPIO(EN_USB_A_LOW_POWER, PIN(9, 3), GPIO_OUT_LOW) GPIO(PG_PP3300_S5_EC_SEQ_OD, PIN(B, 5), GPIO_OUT_LOW) GPIO(USB_C2_FRS_EN, PIN(D, 4), GPIO_OUT_LOW) +GPIO(NVIDIA_GPU_ACOFF_ODL, PIN(9, 5), GPIO_ODR_HIGH) -/* Barreljack */ +/* + * Barrel-jack adapter enable switch. When starting up on a depleted battery, + * we'll be powered by either BJ or USB-C but not both. The EC will detect BJ + * or USBC and disable the other ports. + */ GPIO(EN_PPVAR_BJ_ADP_L, PIN(A, 2), GPIO_OUT_LOW) /* @@ -122,7 +127,6 @@ UNUSED(PIN(0, 2)) /* GPIO02 */ UNUSED(PIN(6, 6)) /* GPIO66 */ UNUSED(PIN(5, 7)) /* GPIO57/SER_IRQ/ESPI_ALERT_L */ UNUSED(PIN(8, 1)) /* GPIO81 */ -UNUSED(PIN(9, 5)) /* GPIO95 */ UNUSED(PIN(7, 3)) /* GPIO73 */ UNUSED(PIN(5, 0)) /* GPIO50 */ UNUSED(PIN(6, 0)) /* GPIO60 */ -- cgit v1.2.1