summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDaisuke Nojiri <dnojiri@chromium.org>2022-06-21 21:52:03 +0000
committerChromeos LUCI <chromeos-scoped@luci-project-accounts.iam.gserviceaccount.com>2022-06-29 23:39:58 +0000
commit122ce784c72a2c623dd9615eda5d2ff6a0fe2c94 (patch)
treee6ecf93296b0e8d000b129b9e9093ae75fe85a53
parent8d9a5c873a739513fe72c4e3b6ad28ce4e39279a (diff)
downloadchrome-ec-122ce784c72a2c623dd9615eda5d2ff6a0fe2c94.tar.gz
GPU: Add Nvidia GPU D-Notify driver
(Based on the driver written by Tim Wawrzynczak.) This patch adds Nvidia GPU D-Notify driver. It asserts a GPIO, sends a host event, and sets D-Notify level in shared memory when power availability changes (i.e. AC plug/unplug, battery charge/discharge). BUG=b:216485035 BRANCH=None TEST=make run-nvidia_gpu && make buildall Signed-off-by: Daisuke Nojiri <dnojiri@chromium.org> Change-Id: I231619157fe03fb357882540ffa34b4d48fba253 Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/ec/+/3716794 Reviewed-by: Tim Wawrzynczak <twawrzynczak@chromium.org>
-rw-r--r--driver/build.mk5
-rw-r--r--driver/nvidia_gpu.c154
-rw-r--r--driver/nvidia_gpu.h59
-rw-r--r--include/charge_manager.h2
-rw-r--r--include/config.h12
-rw-r--r--include/console_channel.inc3
-rw-r--r--include/ec_commands.h6
-rw-r--r--include/extpower.h2
-rw-r--r--test/build.mk2
-rw-r--r--test/nvidia_gpu.c204
-rw-r--r--test/nvidia_gpu.tasklist10
-rw-r--r--test/test_config.h5
-rw-r--r--zephyr/Kconfig.throttle_ap10
13 files changed, 468 insertions, 6 deletions
diff --git a/driver/build.mk b/driver/build.mk
index f8abe50742..81955294eb 100644
--- a/driver/build.mk
+++ b/driver/build.mk
@@ -118,6 +118,9 @@ driver-$(CONFIG_LED_DRIVER_TLC59116F)+=led/tlc59116f.o
# 7-segment display
driver-$(CONFIG_MAX695X_SEVEN_SEGMENT_DISPLAY)+=led/max695x.o
+# Nvidia GPU D-Notify driver
+driver-$(CONFIG_GPU_NVIDIA)+=nvidia_gpu.o
+
# Voltage regulators
driver-$(CONFIG_REGULATOR_IR357X)+=regulator_ir357x.o
@@ -241,4 +244,4 @@ driver-$(CONFIG_MP2964)+=mp2964.o
# SOC Interface
driver-$(CONFIG_AMD_SB_RMI)+=sb_rmi.o
-driver-$(CONFIG_AMD_STT)+=amd_stt.o
+driver-$(CONFIG_AMD_STT)+=amd_stt.o \ No newline at end of file
diff --git a/driver/nvidia_gpu.c b/driver/nvidia_gpu.c
new file mode 100644
index 0000000000..3737b4ecd0
--- /dev/null
+++ b/driver/nvidia_gpu.c
@@ -0,0 +1,154 @@
+/* Copyright 2022 The ChromiumOS Authors.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ *
+ * Nvidia GPU D-Notify driver
+ */
+
+#include <stddef.h>
+
+#include "charge_manager.h"
+#include "charge_state.h"
+#include "compile_time_macros.h"
+#include "console.h"
+#include "extpower.h"
+#include "gpio.h"
+#include "hooks.h"
+#include "host_command.h"
+#include "nvidia_gpu.h"
+#include "throttle_ap.h"
+#include "timer.h"
+
+#define CPRINTS(fmt, args...) cprints(CC_GPU, "GPU: " fmt, ## args)
+#define CPRINTF(fmt, args...) cprintf(CC_GPU, "GPU: " fmt, ## args)
+
+/*
+ * BIT0~2: D-Notify level (0:D1, ... 4:D5)
+ * note: may need a bit for disabling dynamic boost.
+ */
+#define MEMMAP_D_NOTIFY_MASK GENMASK(2, 0)
+
+test_export_static enum d_notify_level d_notify_level = D_NOTIFY_1;
+test_export_static bool policy_initialized = false;
+test_export_static const struct d_notify_policy *d_notify_policy = NULL;
+
+void nvidia_gpu_init_policy(const struct d_notify_policy *policy)
+{
+ if (policy) {
+ d_notify_policy = policy;
+ policy_initialized = true;
+ }
+}
+
+static void set_d_notify_level(enum d_notify_level level)
+{
+ uint8_t *memmap_gpu = (uint8_t *)host_get_memmap(EC_MEMMAP_GPU);
+
+ if (level == d_notify_level)
+ return;
+
+ d_notify_level = level;
+ *memmap_gpu = (*memmap_gpu & ~MEMMAP_D_NOTIFY_MASK) | d_notify_level;
+ host_set_single_event(EC_HOST_EVENT_GPU);
+ CPRINTS("Set D-notify level to D%c", ('1' + (int)d_notify_level));
+}
+
+static void evaluate_d_notify_level(void)
+{
+ enum d_notify_level lvl;
+ const struct d_notify_policy *policy = d_notify_policy;
+
+ /*
+ * We don't need to care about 'transitioning to S0' because throttling
+ * is unlikely required when the system is about to start.
+ */
+ if (!chipset_in_state(CHIPSET_STATE_ON))
+ return;
+
+ if (!policy_initialized) {
+ CPRINTS("WARN: %s called before policies are set.", __func__);
+ return;
+ }
+
+ if (extpower_is_present()) {
+ const int watts = charge_manager_get_power_limit_uw() / 1000000;
+
+ for (lvl = D_NOTIFY_1; lvl <= D_NOTIFY_5; lvl++) {
+ if (policy[lvl].power_source != D_NOTIFY_AC &&
+ policy[lvl].power_source != D_NOTIFY_AC_DC)
+ continue;
+
+ if (policy[lvl].power_source == D_NOTIFY_AC) {
+ if (watts >= policy[lvl].ac.min_charger_watts) {
+ set_d_notify_level(lvl);
+ break;
+ }
+ } else {
+ set_d_notify_level(lvl);
+ break;
+ }
+ }
+ } else {
+ const int soc = charge_get_percent();
+
+ for (lvl = D_NOTIFY_5; lvl >= D_NOTIFY_1; lvl--) {
+ if (policy[lvl].power_source == D_NOTIFY_DC) {
+ if (soc <= policy[lvl].dc.min_battery_soc) {
+ set_d_notify_level(lvl);
+ break;
+ }
+ } else if (policy[lvl].power_source == D_NOTIFY_AC_DC) {
+ set_d_notify_level(lvl);
+ break;
+ }
+ }
+ }
+}
+
+static void disable_gpu_acoff(void)
+{
+ gpio_set_level(GPIO_NVIDIA_GPU_ACOFF_ODL, 1);
+ evaluate_d_notify_level();
+}
+DECLARE_DEFERRED(disable_gpu_acoff);
+
+static void handle_battery_soc_change(void)
+{
+ evaluate_d_notify_level();
+}
+DECLARE_HOOK(HOOK_BATTERY_SOC_CHANGE, handle_battery_soc_change,
+ HOOK_PRIO_DEFAULT);
+
+/*
+ * This function enables and disables both hard and soft throttles. (Thus,
+ * <type> has no meaning.).
+ *
+ * When throttling, it hard-throttles the GPU and sets the D-level to D5. It
+ * also schedules a deferred call to disable the hard throttle. So, it's not
+ * necessary to call it for unthrottling.
+ *
+ * Currently, it's upto each board when this is called. For example, it can be
+ * called from board_set_active_charge_port since board_set_active_charge_port
+ * is called whenever (and prior to) active port or active supplier or both
+ * changes.
+ */
+void throttle_gpu(enum throttle_level level,
+ enum throttle_type type, /* not used */
+ enum throttle_sources source)
+{
+ if (level == THROTTLE_ON) {
+ /* Cancel pending deferred call. */
+ hook_call_deferred(&disable_gpu_acoff_data, -1);
+ /* Toggle hardware throttle immediately. */
+ gpio_set_level(GPIO_NVIDIA_GPU_ACOFF_ODL, 0);
+ /*
+ * Switch to the lowest (D5) first then move up as the situation
+ * improves.
+ */
+ set_d_notify_level(D_NOTIFY_5);
+ hook_call_deferred(&disable_gpu_acoff_data,
+ NVIDIA_GPU_ACOFF_DURATION);
+ } else {
+ disable_gpu_acoff();
+ }
+}
diff --git a/driver/nvidia_gpu.h b/driver/nvidia_gpu.h
new file mode 100644
index 0000000000..267f6643f5
--- /dev/null
+++ b/driver/nvidia_gpu.h
@@ -0,0 +1,59 @@
+/* Copyright 2022 The ChromiumOS Authors.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ *
+ * Nvidia GPU D-Notify driver header file
+ */
+
+#ifndef DRIVER_NVIDIA_GPU_H
+#define DRIVER_NVIDIA_GPU_H
+
+#define NVIDIA_GPU_ACOFF_DURATION (100 * MSEC)
+
+enum d_notify_level {
+ D_NOTIFY_1 = 0,
+ D_NOTIFY_2,
+ D_NOTIFY_3,
+ D_NOTIFY_4,
+ D_NOTIFY_5,
+ D_NOTIFY_COUNT,
+};
+
+enum d_notify_policy_type {
+ /* High- or low-power A/C */
+ D_NOTIFY_AC,
+ /* Too low of A/C to still charge or DC with high battery SOC */
+ D_NOTIFY_AC_DC,
+ /* DC with medium or low battery SOC */
+ D_NOTIFY_DC,
+};
+
+struct d_notify_policy {
+ enum d_notify_policy_type power_source;
+ union {
+ struct {
+ unsigned int min_charger_watts;
+ } ac;
+ struct {
+ unsigned int min_battery_soc;
+ } dc;
+ };
+};
+
+#define AC_ATLEAST_W(W) { \
+ .power_source = D_NOTIFY_AC, \
+ .ac.min_charger_watts = (W), \
+ }
+
+#define AC_DC { \
+ .power_source = D_NOTIFY_AC_DC, \
+ }
+
+#define DC_ATLEAST_SOC(S) { \
+ .power_source = D_NOTIFY_DC, \
+ .dc.min_battery_soc = (S), \
+ }
+
+void nvidia_gpu_init_policy(const struct d_notify_policy *policies);
+
+#endif /* DRIVER_NVIDIA_GPU_H */
diff --git a/include/charge_manager.h b/include/charge_manager.h
index cca8c9ce25..0545be594c 100644
--- a/include/charge_manager.h
+++ b/include/charge_manager.h
@@ -235,7 +235,7 @@ int charge_manager_get_selected_charge_port(void);
*
* @return Power limit (uW).
*/
-int charge_manager_get_power_limit_uw(void);
+test_mockable int charge_manager_get_power_limit_uw(void);
/**
* Get the charger current (mA) value.
diff --git a/include/config.h b/include/config.h
index 87c4582642..b116c5d347 100644
--- a/include/config.h
+++ b/include/config.h
@@ -2338,6 +2338,11 @@
/* Support getting gpio flags. */
#undef CONFIG_GPIO_GET_EXTENDED
+/*
+ * GPU Drivers
+ */
+#undef CONFIG_GPU_NVIDIA
+
/* Do we want to detect the lid angle? */
#undef CONFIG_LID_ANGLE
@@ -6862,4 +6867,9 @@
#define CONFIG_S5_EXIT_WAIT 4
#endif
-#endif /* __CROS_EC_CONFIG_H */
+/* HAS_GPU_DRIVER enables D-Notify and throttling. */
+#if defined(CONFIG_GPU_NVIDIA)
+#define HAS_GPU_DRIVER
+#endif
+
+#endif /* __CROS_EC_CONFIG_H */
diff --git a/include/console_channel.inc b/include/console_channel.inc
index 96691c21c3..705a8209df 100644
--- a/include/console_channel.inc
+++ b/include/console_channel.inc
@@ -110,3 +110,6 @@ CONSOLE_CHANNEL(CC_USBPD, "usbpd")
#endif
CONSOLE_CHANNEL(CC_VBOOT, "vboot")
CONSOLE_CHANNEL(CC_HOOK, "hook")
+#ifdef HAS_GPU_DRIVER
+CONSOLE_CHANNEL(CC_GPU, "gpu")
+#endif
diff --git a/include/ec_commands.h b/include/ec_commands.h
index 24f53e449b..bddf95ca32 100644
--- a/include/ec_commands.h
+++ b/include/ec_commands.h
@@ -169,7 +169,8 @@ extern "C" {
/* 0x94 - 0x99: 1st Accelerometer */
/* 0x9a - 0x9f: 2nd Accelerometer */
#define EC_MEMMAP_GYRO_DATA 0xa0 /* Gyroscope data 0xa0 - 0xa5 */
-/* Unused 0xa6 - 0xdf */
+#define EC_MEMMAP_GPU 0xa6 /* GPU-specific, 8 bits */
+/* Unused 0xa7 - 0xdf */
/*
* ACPI is unable to access memory mapped data at or above this offset due to
@@ -671,7 +672,8 @@ enum host_event_code {
/* Event generated by a device attached to the EC */
EC_HOST_EVENT_DEVICE = 10,
EC_HOST_EVENT_THERMAL = 11,
- EC_HOST_EVENT_USB_CHARGER = 12,
+ /* GPU related event. Formerly named EC_HOST_EVENT_USB_CHARGER. */
+ EC_HOST_EVENT_GPU = 12,
EC_HOST_EVENT_KEY_PRESSED = 13,
/*
* EC has finished initializing the host interface. The host can check
diff --git a/include/extpower.h b/include/extpower.h
index edc4bb105e..aa15d1f605 100644
--- a/include/extpower.h
+++ b/include/extpower.h
@@ -21,7 +21,7 @@ __override_proto void board_check_extpower(void);
/**
* Return non-zero if external power is present.
*/
-int extpower_is_present(void);
+test_mockable int extpower_is_present(void);
/**
* Interrupt handler for external power GPIOs.
diff --git a/test/build.mk b/test/build.mk
index fac9544ca4..8c1b4b0d53 100644
--- a/test/build.mk
+++ b/test/build.mk
@@ -66,6 +66,7 @@ test-list-host += motion_lid
test-list-host += motion_sense_fifo
test-list-host += mutex
test-list-host += newton_fit
+test-list-host += nvidia_gpu
test-list-host += online_calibration
test-list-host += online_calibration_spoof
test-list-host += pingpong
@@ -191,6 +192,7 @@ motion_angle-y=motion_angle.o motion_angle_data_literals.o motion_common.o
motion_angle_tablet-y=motion_angle_tablet.o motion_angle_data_literals_tablet.o motion_common.o
motion_lid-y=motion_lid.o
motion_sense_fifo-y=motion_sense_fifo.o
+nvidia_gpu-y=nvidia_gpu.o
online_calibration-y=online_calibration.o
online_calibration_spoof-y=online_calibration_spoof.o gyro_cal_init_for_test.o
rgb_keyboard-y=rgb_keyboard.o
diff --git a/test/nvidia_gpu.c b/test/nvidia_gpu.c
new file mode 100644
index 0000000000..c408fa429e
--- /dev/null
+++ b/test/nvidia_gpu.c
@@ -0,0 +1,204 @@
+/* Copyright 2022 The ChromiumOS Authors.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ *
+ * Tests for Nvidia GPU.
+ */
+#include <stdio.h>
+
+#include "charge_manager.h"
+#include "charge_state.h"
+#include "common.h"
+#include "console.h"
+#include "hooks.h"
+#include "host_command.h"
+#include "task.h"
+#include "test_util.h"
+#include "throttle_ap.h"
+#include "timer.h"
+#include "util.h"
+
+#include "driver/nvidia_gpu.h"
+
+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),
+};
+
+extern enum d_notify_level d_notify_level;
+extern bool policy_initialized;
+extern const struct d_notify_policy *d_notify_policy;
+static int extpower_presence = 1;
+static int nvidia_gpu_acoff_odl = 1;
+static int charge_percent = 100;
+static int charge_power = 100;
+static uint8_t *memmap_gpu;
+
+__override int charge_get_percent(void)
+{
+ return charge_percent;
+}
+
+__override int charge_manager_get_power_limit_uw(void)
+{
+ return charge_power * 1000000;
+}
+
+__override int extpower_is_present(void)
+{
+ return extpower_presence;
+}
+
+__override int gpio_get_level(enum gpio_signal signal)
+{
+ if (signal == GPIO_NVIDIA_GPU_ACOFF_ODL)
+ return nvidia_gpu_acoff_odl;
+ return 0;
+}
+
+__override void gpio_set_level(enum gpio_signal signal, int value)
+{
+ if (signal == GPIO_NVIDIA_GPU_ACOFF_ODL)
+ nvidia_gpu_acoff_odl = value;
+}
+
+static void setup(int extpower, int gpio_acoff, int percent, int power,
+ enum d_notify_level level)
+{
+ extpower_presence = extpower;
+ nvidia_gpu_acoff_odl = gpio_acoff;
+ charge_percent = percent;
+ charge_power = power;
+ d_notify_level = level;
+ *memmap_gpu = level;
+}
+
+static void plug_ac(int plug)
+{
+ extpower_presence = plug;
+ hook_notify(HOOK_AC_CHANGE);
+}
+
+static int check_d_notify_level(enum d_notify_level expected_level)
+{
+ TEST_EQ(d_notify_level, expected_level, "%d");
+ TEST_EQ(*memmap_gpu, expected_level, "%d");
+
+ return EC_SUCCESS;
+}
+
+static int test_startup(void)
+{
+ /* Test initial values after HOOK_INIT. Don't call setup(). */
+
+ TEST_ASSERT(IS_ENABLED(HAS_GPU_DRIVER));
+ TEST_ASSERT(policy_initialized);
+ TEST_NE(d_notify_policy, NULL, "%p");
+ TEST_EQ(check_d_notify_level(D_NOTIFY_1), EC_SUCCESS, "%d");
+
+ return EC_SUCCESS;
+}
+
+static int test_ac_unplug(void)
+{
+ setup(1, 1, 100, 100, D_NOTIFY_1);
+
+ /* Unplug AC. D1 -> D5 */
+ plug_ac(0);
+ throttle_gpu(THROTTLE_ON, THROTTLE_HARD, THROTTLE_SRC_AC);
+ TEST_EQ(nvidia_gpu_acoff_odl, 0, "%d");
+ TEST_EQ(check_d_notify_level(D_NOTIFY_5), EC_SUCCESS, "%d");
+ TEST_ASSERT(host_is_event_set(EC_HOST_EVENT_GPU));
+ host_clear_events(EC_HOST_EVENT_MASK(EC_HOST_EVENT_GPU));
+
+ /* Wait half of NVIDIA_GPU_ACOFF_DURATION. D5 -> D5. */
+ usleep(NVIDIA_GPU_ACOFF_DURATION / 2);
+ TEST_EQ(nvidia_gpu_acoff_odl, 0, "%d");
+ TEST_EQ(check_d_notify_level(D_NOTIFY_5), EC_SUCCESS, "%d");
+ TEST_ASSERT(!host_is_event_set(EC_HOST_EVENT_GPU));
+
+ /* Wait another half of NVIDIA_GPU_ACOFF_DURATION. D5 -> D3. */
+ usleep(NVIDIA_GPU_ACOFF_DURATION / 2);
+ TEST_EQ(nvidia_gpu_acoff_odl, 1, "%d");
+ TEST_EQ(check_d_notify_level(D_NOTIFY_3), EC_SUCCESS, "%d");
+ TEST_ASSERT(host_is_event_set(EC_HOST_EVENT_GPU));
+ host_clear_events(EC_HOST_EVENT_MASK(EC_HOST_EVENT_GPU));
+
+ /* Discharge to 60%. D3 -> D3. */
+ charge_percent = 60;
+ hook_notify(HOOK_BATTERY_SOC_CHANGE);
+ TEST_EQ(nvidia_gpu_acoff_odl, 1, "%d");
+ TEST_EQ(check_d_notify_level(D_NOTIFY_3), EC_SUCCESS, "%d");
+ TEST_ASSERT(!host_is_event_set(EC_HOST_EVENT_GPU));
+
+ /* Discharge to 20%. D3 -> D4 */
+ charge_percent = 20;
+ hook_notify(HOOK_BATTERY_SOC_CHANGE);
+ TEST_EQ(nvidia_gpu_acoff_odl, 1, "%d");
+ TEST_EQ(check_d_notify_level(D_NOTIFY_4), EC_SUCCESS, "%d");
+ TEST_ASSERT(host_is_event_set(EC_HOST_EVENT_GPU));
+ host_clear_events(EC_HOST_EVENT_MASK(EC_HOST_EVENT_GPU));
+
+ /* Discharge to 5%. D4 -> D5 */
+ charge_percent = 5;
+ hook_notify(HOOK_BATTERY_SOC_CHANGE);
+ TEST_EQ(nvidia_gpu_acoff_odl, 1, "%d");
+ TEST_EQ(check_d_notify_level(D_NOTIFY_5), EC_SUCCESS, "%d");
+ TEST_ASSERT(host_is_event_set(EC_HOST_EVENT_GPU));
+ host_clear_events(EC_HOST_EVENT_MASK(EC_HOST_EVENT_GPU));
+
+ return EC_SUCCESS;
+}
+
+static int test_ac_plug(void)
+{
+ /* Plug 100W AC. D5 -> D1. */
+ setup(0, 1, 5, 100, D_NOTIFY_5);
+ plug_ac(1);
+ throttle_gpu(THROTTLE_OFF, THROTTLE_HARD, THROTTLE_SRC_AC);
+ TEST_EQ(nvidia_gpu_acoff_odl, 1, "%d");
+ TEST_EQ(check_d_notify_level(D_NOTIFY_1), EC_SUCCESS, "%d");
+ TEST_ASSERT(host_is_event_set(EC_HOST_EVENT_GPU));
+ host_clear_events(EC_HOST_EVENT_MASK(EC_HOST_EVENT_GPU));
+
+ /* Plug 65W AC. D5 -> D2. */
+ setup(0, 1, 5, 65, D_NOTIFY_5);
+ plug_ac(1);
+ throttle_gpu(THROTTLE_OFF, THROTTLE_HARD, THROTTLE_SRC_AC);
+ TEST_EQ(nvidia_gpu_acoff_odl, 1, "%d");
+ TEST_EQ(check_d_notify_level(D_NOTIFY_2), EC_SUCCESS, "%d");
+ TEST_ASSERT(host_is_event_set(EC_HOST_EVENT_GPU));
+ host_clear_events(EC_HOST_EVENT_MASK(EC_HOST_EVENT_GPU));
+
+ /* Plug 35W AC. D5 -> D3. */
+ setup(0, 1, 5, 35, D_NOTIFY_5);
+ plug_ac(1);
+ throttle_gpu(THROTTLE_OFF, THROTTLE_HARD, THROTTLE_SRC_AC);
+ TEST_EQ(nvidia_gpu_acoff_odl, 1, "%d");
+ TEST_EQ(check_d_notify_level(D_NOTIFY_3), EC_SUCCESS, "%d");
+ TEST_ASSERT(host_is_event_set(EC_HOST_EVENT_GPU));
+ host_clear_events(EC_HOST_EVENT_MASK(EC_HOST_EVENT_GPU));
+
+ return EC_SUCCESS;
+}
+
+static void board_gpu_init(void)
+{
+ nvidia_gpu_init_policy(d_notify_policies);
+}
+DECLARE_HOOK(HOOK_INIT, board_gpu_init, HOOK_PRIO_DEFAULT);
+
+void run_test(int argc, char **argv)
+{
+ memmap_gpu = (uint8_t *)host_get_memmap(EC_MEMMAP_GPU);
+
+ test_chipset_on();
+
+ RUN_TEST(test_startup);
+ RUN_TEST(test_ac_unplug);
+ RUN_TEST(test_ac_plug);
+ test_print_result();
+}
diff --git a/test/nvidia_gpu.tasklist b/test/nvidia_gpu.tasklist
new file mode 100644
index 0000000000..b16ca2ffc0
--- /dev/null
+++ b/test/nvidia_gpu.tasklist
@@ -0,0 +1,10 @@
+/* Copyright 2022 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+/**
+ * See CONFIG_TASK_LIST in config.h for details.
+ */
+#define CONFIG_TEST_TASK_LIST \
+ TASK_TEST(CHIPSET, chipset_task, NULL, TASK_STACK_SIZE)
diff --git a/test/test_config.h b/test/test_config.h
index 67615182b3..f4370cd80e 100644
--- a/test/test_config.h
+++ b/test/test_config.h
@@ -142,6 +142,11 @@
#define CONFIG_RGBKBD_DEMO_DOT
#endif
+#ifdef TEST_NVIDIA_GPU
+#define CONFIG_GPU_NVIDIA
+#define GPIO_NVIDIA_GPU_ACOFF_ODL 123
+#endif
+
#ifdef TEST_STILLNESS_DETECTOR
#define CONFIG_MKBP_EVENT
#define CONFIG_MKBP_USE_GPIO
diff --git a/zephyr/Kconfig.throttle_ap b/zephyr/Kconfig.throttle_ap
index ad5bfe9c77..74498a4e9f 100644
--- a/zephyr/Kconfig.throttle_ap
+++ b/zephyr/Kconfig.throttle_ap
@@ -58,4 +58,14 @@ config PLATFORM_EC_THROTTLE_AP_ON_BAT_LOW_VOLTAGE_THRESH
default 0
endif
+config PLATFORM_EC_GPU_NVIDIA
+ bool "Nvidia GPU supports throttling"
+ default n
+ help
+ Enable GPU throttling. When the GPU is throttled, a software (D-Notify)
+ and a hardware throttle (GPIO_NVIDIA_GPU_ACOFF_ODL) are enabled. A
+ hardware throttle will be automatically disabled after a fixed period
+ of time but a software throttle may remain and keep changing as the
+ situation changes.
+
endif # PLATFORM_EC_THROTTLE_AP