mirror of
git://nv-tegra.nvidia.com/linux-nvgpu.git
synced 2025-12-22 17:36:20 +03:00
gpu: nvgpu: add support for powering off gpu
Add support for powering off IGPU for switching between legacy to SMC mode/vice-versa or changing SMC configuration. The power off can be issued as follows echo 0 > /dev/nvgpu/igpu0/power The following steps are done during a poweroff. 1) Deterministic channel idle 2) Acquire write_lock on l->busy semaphore. 3) Wait till power_usage decrements to indicate 0 active jobs. 4) Invoke pm_runtime_put_sync_suspend() 5) Invoke nvgpu_gr_remove_support() to clear existing GR memory. 6) Release write_lock on l->busy 7) Deterministic channel unidle. Part of the sequence matches that of the gk20a_do_idle code. The common parts are extracted into new functions gk20a_block_new_jobs_and_idle() and gk20a_unblock_jobs() For joint-rail case, the current implementation, does a railgate and then sets pm_runtime_set_autosuspend_delay(-1) to disable regular runtime resume/suspend. Remove clearing of NVGPU_SUPPORT_MIG status during state change ias it leads to inconsistencies. Jira NVGPU-6920 Change-Id: I0b3eb3278176122ac061c1e8a94ebfb3c17c3925 Signed-off-by: Debarshi Dutta <ddutta@nvidia.com> Reviewed-on: https://git-master.nvidia.com/r/c/linux-nvgpu/+/2578501 Reviewed-by: svc_kernel_abi <svc_kernel_abi@nvidia.com> Reviewed-by: Vijayakumar Subbu <vsubbu@nvidia.com> Reviewed-by: mobile promotions <svcmobile_promotions@nvidia.com> Tested-by: Antony Clince Alex <aalex@nvidia.com> Tested-by: mobile promotions <svcmobile_promotions@nvidia.com> GVS: Gerrit_Virtual_Submit
This commit is contained in:
committed by
mobile promotions
parent
2e3c3aada6
commit
608decf1e6
@@ -1173,6 +1173,19 @@ const char *nvgpu_get_power_state(struct gk20a *g)
|
|||||||
return str;
|
return str;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool nvgpu_poweron_started(struct gk20a *g)
|
||||||
|
{
|
||||||
|
unsigned long flags = 0U;
|
||||||
|
u32 power_on;
|
||||||
|
|
||||||
|
nvgpu_spinlock_irqsave(&g->power_spinlock, flags);
|
||||||
|
power_on = g->power_on_state;
|
||||||
|
nvgpu_spinunlock_irqrestore(&g->power_spinlock, flags);
|
||||||
|
|
||||||
|
return (power_on == NVGPU_STATE_POWERED_ON ||
|
||||||
|
power_on == NVGPU_STATE_POWERING_ON);
|
||||||
|
}
|
||||||
|
|
||||||
bool nvgpu_is_powered_on(struct gk20a *g)
|
bool nvgpu_is_powered_on(struct gk20a *g)
|
||||||
{
|
{
|
||||||
unsigned long flags = 0U;
|
unsigned long flags = 0U;
|
||||||
|
|||||||
@@ -653,7 +653,6 @@ static void ga10b_grmgr_set_smc_state(struct gk20a *g, bool enable)
|
|||||||
smcarb_sys_pipe_info_mode_legacy_v());
|
smcarb_sys_pipe_info_mode_legacy_v());
|
||||||
}
|
}
|
||||||
nvgpu_writel(g, smcarb_sys_pipe_info_r(), smc_state);
|
nvgpu_writel(g, smcarb_sys_pipe_info_r(), smc_state);
|
||||||
nvgpu_set_enabled(g, NVGPU_SUPPORT_MIG, enable);
|
|
||||||
nvgpu_log(g, gpu_dbg_mig, "MIG boot reg_val[%x] enable[%d]",
|
nvgpu_log(g, gpu_dbg_mig, "MIG boot reg_val[%x] enable[%d]",
|
||||||
smc_state, enable);
|
smc_state, enable);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -272,6 +272,14 @@ void nvgpu_set_power_state(struct gk20a *g, u32 state);
|
|||||||
*/
|
*/
|
||||||
const char *nvgpu_get_power_state(struct gk20a *g);
|
const char *nvgpu_get_power_state(struct gk20a *g);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get whether power state is
|
||||||
|
* NVGPU_STATE_POWERING_ON or NVGPU_STATE_POWERED_ON
|
||||||
|
*
|
||||||
|
* @param g [in] The GPU
|
||||||
|
*/
|
||||||
|
bool nvgpu_poweron_started(struct gk20a *g);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Return the nvgpu power-on state
|
* @brief Return the nvgpu power-on state
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -185,7 +185,8 @@ static void nvgpu_init_pm_vars(struct gk20a *g)
|
|||||||
#endif
|
#endif
|
||||||
g->ptimer_src_freq = platform->ptimer_src_freq;
|
g->ptimer_src_freq = platform->ptimer_src_freq;
|
||||||
|
|
||||||
nvgpu_set_enabled(g, NVGPU_CAN_RAILGATE, platform->can_railgate_init);
|
nvgpu_set_enabled(g, NVGPU_CAN_RAILGATE,
|
||||||
|
nvgpu_platform_is_simulation(g)? true : platform->can_railgate_init);
|
||||||
g->can_tpc_powergate = platform->can_tpc_powergate;
|
g->can_tpc_powergate = platform->can_tpc_powergate;
|
||||||
|
|
||||||
for (i = 0; i < MAX_TPC_PG_CONFIGS; i++)
|
for (i = 0; i < MAX_TPC_PG_CONFIGS; i++)
|
||||||
|
|||||||
@@ -688,18 +688,18 @@ static struct of_device_id tegra_gk20a_of_match[] = {
|
|||||||
MODULE_DEVICE_TABLE(of, tegra_gk20a_of_match);
|
MODULE_DEVICE_TABLE(of, tegra_gk20a_of_match);
|
||||||
|
|
||||||
#ifdef CONFIG_PM
|
#ifdef CONFIG_PM
|
||||||
/**
|
/* Caller of this API can assume the following return values
|
||||||
* gk20a_do_idle() - force the GPU to idle and railgate
|
* 1) -EBUSY indicates failure of the API, no locks are held. (Failure)
|
||||||
*
|
* 2) 1 indicates pm_runtime_status_suspended without any locks held
|
||||||
* In success, this call MUST be balanced by caller with gk20a_do_unidle()
|
* and g->probe_done = false. (Success)
|
||||||
*
|
* 3) 0 indicates function successfully idles the driver and prevents
|
||||||
* Acquires two locks : &l->busy_lock and &platform->railgate_lock
|
* further jobs. Following steps are executed,
|
||||||
* In success, we hold these locks and return
|
* a) Hold back Deterministic Submits
|
||||||
* In failure, we release these locks and return
|
* b) Down-Write Busy lock
|
||||||
|
* c) Acquire platform->railgate lock.
|
||||||
*/
|
*/
|
||||||
int gk20a_do_idle(void *_g)
|
int gk20a_block_new_jobs_and_idle(struct gk20a *g)
|
||||||
{
|
{
|
||||||
struct gk20a *g = (struct gk20a *)_g;
|
|
||||||
struct nvgpu_os_linux *l = nvgpu_os_linux_from_gk20a(g);
|
struct nvgpu_os_linux *l = nvgpu_os_linux_from_gk20a(g);
|
||||||
struct device *dev = dev_from_gk20a(g);
|
struct device *dev = dev_from_gk20a(g);
|
||||||
struct gk20a_platform *platform = dev_get_drvdata(dev);
|
struct gk20a_platform *platform = dev_get_drvdata(dev);
|
||||||
@@ -716,7 +716,7 @@ int gk20a_do_idle(void *_g)
|
|||||||
*/
|
*/
|
||||||
pm_runtime_put_sync_autosuspend(dev);
|
pm_runtime_put_sync_autosuspend(dev);
|
||||||
if (pm_runtime_status_suspended(dev)) {
|
if (pm_runtime_status_suspended(dev)) {
|
||||||
return 0;
|
return 1;
|
||||||
} else {
|
} else {
|
||||||
nvgpu_err(g, "failed to idle");
|
nvgpu_err(g, "failed to idle");
|
||||||
return -EBUSY;
|
return -EBUSY;
|
||||||
@@ -779,6 +779,92 @@ int gk20a_do_idle(void *_g)
|
|||||||
return -EBUSY;
|
return -EBUSY;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int gk20a_block_new_jobs_and_poweroff(struct gk20a *g)
|
||||||
|
{
|
||||||
|
struct device *dev = dev_from_gk20a(g);
|
||||||
|
struct gk20a_platform *platform = dev_get_drvdata(dev);
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = gk20a_block_new_jobs_and_idle(g);
|
||||||
|
if (ret == -EBUSY) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ret == 1) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* check if it is already railgated ? */
|
||||||
|
if (platform->is_railgated(dev)) {
|
||||||
|
nvgpu_mutex_release(&platform->railgate_lock);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
nvgpu_mutex_release(&platform->railgate_lock);
|
||||||
|
|
||||||
|
/* For joint_xpu_rail platforms, This will decrement the
|
||||||
|
* extra refcount taken by us.
|
||||||
|
*/
|
||||||
|
if (!nvgpu_is_enabled(g, NVGPU_CAN_RAILGATE)) {
|
||||||
|
pm_runtime_dont_use_autosuspend(dev);
|
||||||
|
}
|
||||||
|
|
||||||
|
pm_runtime_put_sync_suspend(dev);
|
||||||
|
|
||||||
|
nvgpu_log_info(g, "power usage_count = %d", atomic_read(&dev->power.usage_count));
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void gk20a_unblock_jobs(struct gk20a *g)
|
||||||
|
{
|
||||||
|
struct nvgpu_os_linux *l = nvgpu_os_linux_from_gk20a(g);
|
||||||
|
struct device *dev = dev_from_gk20a(g);
|
||||||
|
|
||||||
|
/* For joint_xpu_rail, its unsafe to keep the rail gated. */
|
||||||
|
if (!nvgpu_is_enabled(g, NVGPU_CAN_RAILGATE)) {
|
||||||
|
pm_runtime_set_autosuspend_delay(dev, -1);
|
||||||
|
pm_runtime_use_autosuspend(dev);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* release the lock and open up all other busy() calls */
|
||||||
|
up_write(&l->busy_lock);
|
||||||
|
|
||||||
|
nvgpu_channel_deterministic_unidle(g);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* gk20a_do_idle() - force the GPU to idle and railgate
|
||||||
|
*
|
||||||
|
* In success, this call MUST be balanced by caller with gk20a_do_unidle()
|
||||||
|
*
|
||||||
|
* Acquires two locks : &l->busy_lock and &platform->railgate_lock
|
||||||
|
* In success, we hold these locks and return
|
||||||
|
* In failure, we release these locks and return
|
||||||
|
*/
|
||||||
|
int gk20a_do_idle(void *_g)
|
||||||
|
{
|
||||||
|
struct gk20a *g = (struct gk20a *)_g;
|
||||||
|
struct device *dev = dev_from_gk20a(g);
|
||||||
|
struct gk20a_platform *platform = dev_get_drvdata(dev);
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = gk20a_block_new_jobs_and_idle(g);
|
||||||
|
if (ret == -EBUSY) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ret == 1) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* check if it is already railgated ? */
|
||||||
|
if (platform->is_railgated(dev)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
/*
|
/*
|
||||||
* If railgating is enabled, autosuspend delay will be > 0. Set it to
|
* If railgating is enabled, autosuspend delay will be > 0. Set it to
|
||||||
* 0 to suspend immediately. If railgating is disabled setting it to
|
* 0 to suspend immediately. If railgating is disabled setting it to
|
||||||
@@ -800,6 +886,8 @@ int gk20a_do_idle(void *_g)
|
|||||||
(void) gk20a_do_unidle(g);
|
(void) gk20a_do_unidle(g);
|
||||||
return -EBUSY;
|
return -EBUSY;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -829,8 +917,7 @@ int gk20a_do_unidle(void *_g)
|
|||||||
nvgpu_mutex_release(&platform->railgate_lock);
|
nvgpu_mutex_release(&platform->railgate_lock);
|
||||||
|
|
||||||
if (g->railgate_delay && nvgpu_is_enabled(g, NVGPU_CAN_RAILGATE))
|
if (g->railgate_delay && nvgpu_is_enabled(g, NVGPU_CAN_RAILGATE))
|
||||||
pm_runtime_set_autosuspend_delay(dev,
|
pm_runtime_set_autosuspend_delay(dev, g->railgate_delay);
|
||||||
g->railgate_delay);
|
|
||||||
else
|
else
|
||||||
pm_runtime_set_autosuspend_delay(dev, -1);
|
pm_runtime_set_autosuspend_delay(dev, -1);
|
||||||
|
|
||||||
@@ -1720,6 +1807,44 @@ return_err_platform:
|
|||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int gk20a_driver_force_power_off(struct gk20a *g)
|
||||||
|
{
|
||||||
|
struct device *dev = dev_from_gk20a(g);
|
||||||
|
struct gk20a_platform *platform = gk20a_get_platform(dev);
|
||||||
|
int err = 0;
|
||||||
|
|
||||||
|
#ifdef CONFIG_NVGPU_DGPU
|
||||||
|
if (g->pci_class) {
|
||||||
|
nvgpu_err(g, "Poweroff is not supported for device yet.");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
err = gk20a_block_new_jobs_and_poweroff(g);
|
||||||
|
if (err != 0)
|
||||||
|
goto done;
|
||||||
|
|
||||||
|
nvgpu_gr_remove_support(g);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This is a WAR.
|
||||||
|
* For T210, powernode must not allow device nodes to be powered off
|
||||||
|
* even during a force poweroff. Once the WAR for T210 is removed,
|
||||||
|
* this will hold true for all chips.
|
||||||
|
*/
|
||||||
|
if (platform->platform_chip_id != TEGRA_210)
|
||||||
|
gk20a_user_nodes_deinit(dev);
|
||||||
|
|
||||||
|
gk20a_unblock_jobs(g);
|
||||||
|
|
||||||
|
done:
|
||||||
|
if (err != 0) {
|
||||||
|
nvgpu_err(g, "failed to poweroff");
|
||||||
|
}
|
||||||
|
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
int nvgpu_remove(struct device *dev)
|
int nvgpu_remove(struct device *dev)
|
||||||
{
|
{
|
||||||
struct gk20a *g = get_gk20a(dev);
|
struct gk20a *g = get_gk20a(dev);
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2011-2018, NVIDIA CORPORATION. All rights reserved.
|
* Copyright (c) 2011-2021, NVIDIA CORPORATION. All rights reserved.
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify it
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
* under the terms and conditions of the GNU General Public License,
|
* under the terms and conditions of the GNU General Public License,
|
||||||
@@ -21,6 +21,13 @@ struct nvgpu_os_linux;
|
|||||||
int gk20a_pm_finalize_poweron(struct device *dev);
|
int gk20a_pm_finalize_poweron(struct device *dev);
|
||||||
int nvgpu_finalize_poweron_linux(struct nvgpu_os_linux *l);
|
int nvgpu_finalize_poweron_linux(struct nvgpu_os_linux *l);
|
||||||
void gk20a_remove_support(struct gk20a *g);
|
void gk20a_remove_support(struct gk20a *g);
|
||||||
|
/*
|
||||||
|
* This method is currently only supported to allow changing
|
||||||
|
* MIG configurations. As such only GR state and device nodes
|
||||||
|
* are freed as part of this. Any future functionality update
|
||||||
|
* can be made by adding more to this.
|
||||||
|
*/
|
||||||
|
int gk20a_driver_force_power_off(struct gk20a *g);
|
||||||
void gk20a_driver_start_unload(struct gk20a *g);
|
void gk20a_driver_start_unload(struct gk20a *g);
|
||||||
int nvgpu_quiesce(struct gk20a *g);
|
int nvgpu_quiesce(struct gk20a *g);
|
||||||
int nvgpu_remove(struct device *dev);
|
int nvgpu_remove(struct device *dev);
|
||||||
|
|||||||
@@ -696,6 +696,8 @@ static int nvgpu_pci_probe(struct pci_dev *pdev,
|
|||||||
nvgpu_mutex_init(&l->dmabuf_priv_list_lock);
|
nvgpu_mutex_init(&l->dmabuf_priv_list_lock);
|
||||||
nvgpu_init_list_node(&l->dmabuf_priv_list);
|
nvgpu_init_list_node(&l->dmabuf_priv_list);
|
||||||
|
|
||||||
|
g->probe_done = true;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
err_free_irq:
|
err_free_irq:
|
||||||
@@ -760,10 +762,6 @@ static void nvgpu_pci_remove(struct pci_dev *pdev)
|
|||||||
nvgpu_thermal_deinit(g);
|
nvgpu_thermal_deinit(g);
|
||||||
}
|
}
|
||||||
|
|
||||||
err = nvgpu_quiesce(g);
|
|
||||||
/* TODO: handle failure to idle */
|
|
||||||
WARN(err, "gpu failed to idle during driver removal");
|
|
||||||
|
|
||||||
nvgpu_free_irq(g);
|
nvgpu_free_irq(g);
|
||||||
|
|
||||||
nvgpu_remove(dev);
|
nvgpu_remove(dev);
|
||||||
|
|||||||
@@ -34,8 +34,10 @@
|
|||||||
|
|
||||||
#include "platform_gk20a.h"
|
#include "platform_gk20a.h"
|
||||||
#include "os_linux.h"
|
#include "os_linux.h"
|
||||||
|
#include "module.h"
|
||||||
|
|
||||||
#define NVGPU_DRIVER_POWER_ON_NEEDED 1
|
#define NVGPU_DRIVER_POWER_ON_NEEDED 1
|
||||||
|
#define NVGPU_DRIVER_POWER_OFF_NEEDED 0
|
||||||
|
|
||||||
int gk20a_power_open(struct inode *inode, struct file *filp)
|
int gk20a_power_open(struct inode *inode, struct file *filp)
|
||||||
{
|
{
|
||||||
@@ -117,8 +119,7 @@ int gk20a_power_write(struct file *filp, const char __user *buf,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (power_status == NVGPU_DRIVER_POWER_ON_NEEDED) {
|
if (power_status == NVGPU_DRIVER_POWER_ON_NEEDED) {
|
||||||
if ((g->power_on_state == NVGPU_STATE_POWERING_ON) ||
|
if (nvgpu_poweron_started(g)) {
|
||||||
(g->power_on_state == NVGPU_STATE_POWERED_ON)) {
|
|
||||||
goto free_input;
|
goto free_input;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -130,8 +131,15 @@ int gk20a_power_write(struct file *filp, const char __user *buf,
|
|||||||
}
|
}
|
||||||
|
|
||||||
gk20a_idle(g);
|
gk20a_idle(g);
|
||||||
|
} else if (power_status == NVGPU_DRIVER_POWER_OFF_NEEDED) {
|
||||||
|
err = gk20a_driver_force_power_off(g);
|
||||||
|
if (err) {
|
||||||
|
nvgpu_err(g, "power_node_write failed at busy");
|
||||||
|
kfree(userinput);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
nvgpu_err(g, "1 is the valid value to power-on the GPU");
|
nvgpu_err(g, "1/0 are the valid values to power-on the GPU");
|
||||||
kfree(userinput);
|
kfree(userinput);
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -306,6 +306,8 @@ static ssize_t railgate_enable_store(struct device *dev,
|
|||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
nvgpu_log_info(g, "railgating is enabled %ld", railgate_enable);
|
||||||
|
|
||||||
if (railgate_enable) {
|
if (railgate_enable) {
|
||||||
nvgpu_set_enabled(g, NVGPU_CAN_RAILGATE, true);
|
nvgpu_set_enabled(g, NVGPU_CAN_RAILGATE, true);
|
||||||
pm_runtime_set_autosuspend_delay(dev, g->railgate_delay);
|
pm_runtime_set_autosuspend_delay(dev, g->railgate_delay);
|
||||||
|
|||||||
Reference in New Issue
Block a user