diff --git a/drivers/gpu/nvgpu/Makefile.linux.configs b/drivers/gpu/nvgpu/Makefile.linux.configs index d22f2606c..431e947f1 100644 --- a/drivers/gpu/nvgpu/Makefile.linux.configs +++ b/drivers/gpu/nvgpu/Makefile.linux.configs @@ -71,8 +71,8 @@ ifeq ($(CONFIG_PM_DEVFREQ),y) # Select this entry to enable gk20a scaling CONFIG_GK20A_DEVFREQ := y -# Disable support to pass PM_QOS constraints to devfreq based scaling. -CONFIG_GK20A_PM_QOS := n +# Enable support to pass PM_QOS constraints to devfreq based scaling. +CONFIG_GK20A_PM_QOS := y endif endif @@ -184,21 +184,9 @@ else CONFIG_NVGPU_SYNCFD_NONE := y endif -# Below check indicates the build is invoked from Nvidia's -# internal build system. -ifneq ($(NV_BUILD_KERNEL_OPTIONS),) - -ifneq ($(filter 4.9 4.14,$(patsubst -,$(space),$(NV_BUILD_KERNEL_OPTIONS))),) -# Enable support to pass PM_QOS constraints to devfreq based scaling. -CONFIG_GK20A_PM_QOS := y -endif - -endif - # Set config for OOT module build ifeq ($(CONFIG_TEGRA_OOT_MODULE),m) CONFIG_GK20A_DEVFREQ := y -CONFIG_GK20A_PM_QOS := n endif ifeq ($(CONFIG_GK20A_PMU),y) diff --git a/drivers/gpu/nvgpu/common/clk_arb/clk_arb_gp10b.c b/drivers/gpu/nvgpu/common/clk_arb/clk_arb_gp10b.c index 81bda15a7..2f99d8728 100644 --- a/drivers/gpu/nvgpu/common/clk_arb/clk_arb_gp10b.c +++ b/drivers/gpu/nvgpu/common/clk_arb/clk_arb_gp10b.c @@ -24,6 +24,13 @@ #include #include +#ifdef __KERNEL__ +#include +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 10, 0) +#include "os/linux/scale.h" +#endif +#endif + #include "clk_arb_gp10b.h" bool gp10b_check_clk_arb_support(struct gk20a *g) @@ -303,6 +310,12 @@ void gp10b_clk_arb_run_arbiter_cb(struct nvgpu_clk_arb *arb) gpc2clk_target = arb->gpc2clk_max; } +#ifdef __KERNEL__ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 10, 0) + gpc2clk_target = gk20a_scale_clamp_clk_target(g, gpc2clk_target); +#endif +#endif + gpc2clk_session_target = gpc2clk_target; nvgpu_mutex_acquire(&arb->pstate_lock); diff --git a/drivers/gpu/nvgpu/common/clk_arb/clk_arb_gv100.c b/drivers/gpu/nvgpu/common/clk_arb/clk_arb_gv100.c index d030d3ae4..fd318dfd3 100644 --- a/drivers/gpu/nvgpu/common/clk_arb/clk_arb_gv100.c +++ b/drivers/gpu/nvgpu/common/clk_arb/clk_arb_gv100.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016-2021, NVIDIA CORPORATION. All rights reserved. + * Copyright (c) 2016-2022, NVIDIA CORPORATION. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), @@ -27,6 +27,13 @@ #include #include +#ifdef __KERNEL__ +#include +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 10, 0) +#include "os/linux/scale.h" +#endif +#endif + #include "clk_arb_gv100.h" bool gv100_check_clk_arb_support(struct gk20a *g) @@ -427,6 +434,12 @@ void gv100_clk_arb_run_arbiter_cb(struct nvgpu_clk_arb *arb) gpc2clk_target = arb->gpc_cap_clkmhz; } +#ifdef __KERNEL__ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 10, 0) + gpc2clk_target = gk20a_scale_clamp_clk_target(g, gpc2clk_target); +#endif +#endif + vf_point.gpc_mhz = gpc2clk_target; (void)nvgpu_clk_arb_find_slave_points(arb, &vf_point); if (status != 0) { diff --git a/drivers/gpu/nvgpu/os/linux/driver_common.c b/drivers/gpu/nvgpu/os/linux/driver_common.c index db3ede607..089d95a81 100644 --- a/drivers/gpu/nvgpu/os/linux/driver_common.c +++ b/drivers/gpu/nvgpu/os/linux/driver_common.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -200,7 +201,12 @@ static void nvgpu_init_pm_vars(struct gk20a *g) /* disable devfreq for pre-silicon */ if (!nvgpu_platform_is_silicon(g)) { platform->devfreq_governor = NULL; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 10, 0) + platform->qos_min_notify = NULL; + platform->qos_max_notify = NULL; +#else platform->qos_notify = NULL; +#endif } nvgpu_set_enabled(g, NVGPU_GPU_CAN_ELCG, @@ -221,7 +227,12 @@ static void nvgpu_init_pm_vars(struct gk20a *g) platform->can_railgate_init = false; /* Disable frequency scaling for hypervisor platforms */ platform->devfreq_governor = NULL; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 10, 0) + platform->qos_min_notify = NULL; + platform->qos_max_notify = NULL; +#else platform->qos_notify = NULL; +#endif } else { /* Always enable railgating on simulation platform */ platform->can_railgate_init = nvgpu_platform_is_simulation(g) ? diff --git a/drivers/gpu/nvgpu/os/linux/platform_ga10b_tegra.c b/drivers/gpu/nvgpu/os/linux/platform_ga10b_tegra.c index 3ef1b601b..264584808 100644 --- a/drivers/gpu/nvgpu/os/linux/platform_ga10b_tegra.c +++ b/drivers/gpu/nvgpu/os/linux/platform_ga10b_tegra.c @@ -775,7 +775,12 @@ struct gk20a_platform ga10b_tegra_platform = { .postscale = ga10b_tegra_postscale, .devfreq_governor = "nvhost_podgov", +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 10, 0) + .qos_min_notify = gk20a_scale_qos_min_notify, + .qos_max_notify = gk20a_scale_qos_max_notify, +#else .qos_notify = gk20a_scale_qos_notify, +#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(5, 10, 0) */ .dump_platform_dependencies = gk20a_tegra_debug_dump, diff --git a/drivers/gpu/nvgpu/os/linux/platform_gk20a.h b/drivers/gpu/nvgpu/os/linux/platform_gk20a.h index 55525c0e8..8e4a8b106 100644 --- a/drivers/gpu/nvgpu/os/linux/platform_gk20a.h +++ b/drivers/gpu/nvgpu/os/linux/platform_gk20a.h @@ -17,6 +17,7 @@ #define _GK20A_PLATFORM_H_ #include +#include #include #include @@ -264,11 +265,21 @@ struct gk20a_platform { * this governor to be used in scaling */ const char *devfreq_governor; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 10, 0) + /* Quality of service notifier callback for min frequency limit. */ + int (*qos_min_notify)(struct notifier_block *nb, + unsigned long n, void *p); + + /* Quality of service notifier callback for max frequency limit. */ + int (*qos_max_notify)(struct notifier_block *nb, + unsigned long n, void *p); +#else /* Quality of service notifier callback. If this is set, the scaling * routines will register a callback to Qos. Each time we receive * a new value, this callback gets called. */ int (*qos_notify)(struct notifier_block *nb, unsigned long n, void *p); +#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(5, 10, 0) */ /* Called as part of debug dump. If the gpu gets hung, this function * is responsible for delivering all necessary debug data of other diff --git a/drivers/gpu/nvgpu/os/linux/platform_gk20a_tegra.c b/drivers/gpu/nvgpu/os/linux/platform_gk20a_tegra.c index de1b4e825..de6ac5b78 100644 --- a/drivers/gpu/nvgpu/os/linux/platform_gk20a_tegra.c +++ b/drivers/gpu/nvgpu/os/linux/platform_gk20a_tegra.c @@ -1065,7 +1065,12 @@ struct gk20a_platform gm20b_tegra_platform = { .postscale = gm20b_tegra_postscale, #endif .devfreq_governor = "nvhost_podgov", +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 10, 0) + .qos_min_notify = gk20a_scale_qos_min_notify, + .qos_max_notify = gk20a_scale_qos_max_notify, +#else .qos_notify = gk20a_scale_qos_notify, +#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(5, 10, 0) */ .dump_platform_dependencies = gk20a_tegra_debug_dump, diff --git a/drivers/gpu/nvgpu/os/linux/platform_gv11b_tegra.c b/drivers/gpu/nvgpu/os/linux/platform_gv11b_tegra.c index 62e4f132a..94f5fab56 100644 --- a/drivers/gpu/nvgpu/os/linux/platform_gv11b_tegra.c +++ b/drivers/gpu/nvgpu/os/linux/platform_gv11b_tegra.c @@ -301,7 +301,12 @@ struct gk20a_platform gv11b_tegra_platform = { .postscale = gp10b_tegra_postscale, .devfreq_governor = "nvhost_podgov", +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 10, 0) + .qos_min_notify = gk20a_scale_qos_min_notify, + .qos_max_notify = gk20a_scale_qos_max_notify, +#else .qos_notify = gk20a_scale_qos_notify, +#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(5, 10, 0) */ .dump_platform_dependencies = gk20a_tegra_debug_dump, diff --git a/drivers/gpu/nvgpu/os/linux/scale.c b/drivers/gpu/nvgpu/os/linux/scale.c index 7e669120a..9942f242f 100644 --- a/drivers/gpu/nvgpu/os/linux/scale.c +++ b/drivers/gpu/nvgpu/os/linux/scale.c @@ -49,6 +49,82 @@ */ #if defined(CONFIG_GK20A_PM_QOS) && defined(CONFIG_COMMON_CLK) + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 10, 0) +int gk20a_scale_qos_min_notify(struct notifier_block *nb, + unsigned long n, void *p) +{ + struct gk20a_scale_profile *profile = + container_of(nb, struct gk20a_scale_profile, + qos_min_notify_block); + struct gk20a *g = get_gk20a(profile->dev); + struct nvgpu_os_linux *l = nvgpu_os_linux_from_gk20a(g); + struct devfreq *devfreq = l->devfreq; + + if (!devfreq) + return NOTIFY_OK; + + nvgpu_mutex_acquire(&profile->lock); + + profile->qos_min_freq = (unsigned long)n * 1000UL; + + nvgpu_mutex_release(&profile->lock); + + return NOTIFY_OK; +} + +int gk20a_scale_qos_max_notify(struct notifier_block *nb, + unsigned long n, void *p) +{ + struct gk20a_scale_profile *profile = + container_of(nb, struct gk20a_scale_profile, + qos_max_notify_block); + struct gk20a *g = get_gk20a(profile->dev); + struct nvgpu_os_linux *l = nvgpu_os_linux_from_gk20a(g); + struct devfreq *devfreq = l->devfreq; + + if (!devfreq) + return NOTIFY_OK; + + nvgpu_mutex_acquire(&profile->lock); + + profile->qos_max_freq = (unsigned long)n * 1000UL; + + nvgpu_mutex_release(&profile->lock); + + return NOTIFY_OK; +} + +u16 gk20a_scale_clamp_clk_target(struct gk20a *g, + u16 gpc2clk_target) +{ + struct gk20a_scale_profile *profile = g->scale_profile; + u16 min_freq_mhz, max_freq_mhz; + + if (!profile) + return gpc2clk_target; + + nvgpu_mutex_acquire(&profile->lock); + + min_freq_mhz = (u16) (profile->qos_min_freq / 1000000UL); + max_freq_mhz = (u16) (profile->qos_max_freq / 1000000UL); + + nvgpu_log_info(g, "target %u qos_min %u qos_max %u", gpc2clk_target, + min_freq_mhz, max_freq_mhz); + + if (gpc2clk_target < min_freq_mhz) { + gpc2clk_target = min_freq_mhz; + } + + if (gpc2clk_target > max_freq_mhz) { + gpc2clk_target = max_freq_mhz; + } + + nvgpu_mutex_release(&profile->lock); + + return gpc2clk_target; +} +#else int gk20a_scale_qos_notify(struct notifier_block *nb, unsigned long n, void *p) { @@ -81,6 +157,7 @@ int gk20a_scale_qos_notify(struct notifier_block *nb, return NOTIFY_OK; } +#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(5, 10, 0) */ #elif defined(CONFIG_GK20A_PM_QOS) int gk20a_scale_qos_notify(struct notifier_block *nb, unsigned long n, void *p) @@ -397,21 +474,34 @@ void gk20a_scale_init(struct device *dev) #ifdef CONFIG_DEVFREQ_THERMAL struct thermal_cooling_device *cooling; #endif + struct devfreq *devfreq; int err; if (g->scale_profile) return; - if (!platform->devfreq_governor && !platform->qos_notify) + if (!platform->devfreq_governor) return; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 10, 0) + if (!platform->qos_min_notify && !platform->qos_max_notify) { + return; + } +#else + if (!platform->qos_notify) { + return; + } +#endif + profile = nvgpu_kzalloc(g, sizeof(*profile)); if (!profile) return; profile->dev = dev; #ifdef CONFIG_GK20A_PM_QOS +#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 10, 0) profile->dev_stat.busy = false; +#endif #endif /* Create frequency table */ @@ -427,7 +517,6 @@ void gk20a_scale_init(struct device *dev) g->scale_profile = profile; if (platform->devfreq_governor) { - struct devfreq *devfreq; int error = 0; register_gpu_opp(dev); @@ -474,6 +563,35 @@ void gk20a_scale_init(struct device *dev) } #ifdef CONFIG_GK20A_PM_QOS +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 10, 0) + nvgpu_mutex_init(&profile->lock); + + /* Should we register min frequency QoS callback for this device? */ + if (devfreq && platform->qos_min_notify) { + profile->qos_min_notify_block.notifier_call = + platform->qos_min_notify; + + err = dev_pm_qos_add_notifier(devfreq->dev.parent, + &profile->qos_min_notify_block, + DEV_PM_QOS_MIN_FREQUENCY); + if (err) { + nvgpu_err(g, "failed to add min freq notifier %d", err); + } + } + + /* Should we register max frequency QoS callback for this device? */ + if (devfreq && platform->qos_max_notify) { + profile->qos_max_notify_block.notifier_call = + platform->qos_max_notify; + + err = dev_pm_qos_add_notifier(devfreq->dev.parent, + &profile->qos_max_notify_block, + DEV_PM_QOS_MAX_FREQUENCY); + if (err) { + nvgpu_err(g, "failed to add max freq notifier %d", err); + } + } +#else /* Should we register QoS callback for this device? */ if (platform->qos_notify) { profile->qos_notify_block.notifier_call = @@ -484,6 +602,7 @@ void gk20a_scale_init(struct device *dev) pm_qos_add_max_notifier(PM_QOS_GPU_FREQ_BOUNDS, &profile->qos_notify_block); } +#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(5, 10, 0) */ #endif return; @@ -497,18 +616,54 @@ void gk20a_scale_exit(struct device *dev) struct gk20a_platform *platform = dev_get_drvdata(dev); struct gk20a *g = platform->g; struct nvgpu_os_linux *l = nvgpu_os_linux_from_gk20a(g); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 10, 0) + struct devfreq *devfreq = l->devfreq; + struct gk20a_scale_profile *profile; +#endif int err; - if (!platform->devfreq_governor && !platform->qos_notify) + if (!platform->devfreq_governor) return; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 10, 0) + if (!platform->qos_min_notify && !platform->qos_max_notify) { + return; + } +#else + if (!platform->qos_notify) { + return; + } +#endif + #ifdef CONFIG_GK20A_PM_QOS +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 10, 0) + if (devfreq) { + profile = g->scale_profile; + + err = dev_pm_qos_remove_notifier(devfreq->dev.parent, + &profile->qos_min_notify_block, + DEV_PM_QOS_MIN_FREQUENCY); + if (err) { + nvgpu_err(g, "failed to remove min freq notifier %d", err); + } + + err = dev_pm_qos_remove_notifier(devfreq->dev.parent, + &profile->qos_max_notify_block, + DEV_PM_QOS_MAX_FREQUENCY); + if (err) { + nvgpu_err(g, "failed to remove max freq notifier %d", err); + } + } + + nvgpu_mutex_destroy(&profile->lock); +#else if (platform->qos_notify) { pm_qos_remove_min_notifier(PM_QOS_GPU_FREQ_BOUNDS, &g->scale_profile->qos_notify_block); pm_qos_remove_max_notifier(PM_QOS_GPU_FREQ_BOUNDS, &g->scale_profile->qos_notify_block); } +#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(5, 10, 0) */ #endif #ifdef CONFIG_DEVFREQ_THERMAL diff --git a/drivers/gpu/nvgpu/os/linux/scale.h b/drivers/gpu/nvgpu/os/linux/scale.h index c1e6fe866..ff5b5e77c 100644 --- a/drivers/gpu/nvgpu/os/linux/scale.h +++ b/drivers/gpu/nvgpu/os/linux/scale.h @@ -1,7 +1,7 @@ /* * gk20a clock scaling profile * - * Copyright (c) 2013-2016, NVIDIA Corporation. All rights reserved. + * Copyright (c) 2013-2022, NVIDIA Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, @@ -20,6 +20,7 @@ #define GK20A_SCALE_H #include +#include struct clk; @@ -29,9 +30,12 @@ struct gk20a_scale_profile { struct devfreq_dev_profile devfreq_profile; struct devfreq_dev_status dev_stat; struct notifier_block qos_notify_block; + struct notifier_block qos_min_notify_block; + struct notifier_block qos_max_notify_block; unsigned long qos_min_freq; unsigned long qos_max_freq; void *private_data; + struct nvgpu_mutex lock; }; /* Initialization and de-initialization for module */ @@ -49,18 +53,49 @@ void gk20a_scale_notify_idle(struct device *); void gk20a_scale_suspend(struct device *); void gk20a_scale_resume(struct device *); + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 10, 0) +int gk20a_scale_qos_min_notify(struct notifier_block *nb, + unsigned long n, void *p); +int gk20a_scale_qos_max_notify(struct notifier_block *nb, + unsigned long n, void *p); +u16 gk20a_scale_clamp_clk_target(struct gk20a *g, + u16 gpc2clk_target); +#else int gk20a_scale_qos_notify(struct notifier_block *nb, unsigned long n, void *p); +#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(5, 10, 0) */ + #else static inline void gk20a_scale_notify_busy(struct device *dev) {} static inline void gk20a_scale_notify_idle(struct device *dev) {} static inline void gk20a_scale_suspend(struct device *dev) {} static inline void gk20a_scale_resume(struct device *dev) {} + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 10, 0) +static inline int gk20a_scale_qos_min_notify(struct notifier_block *nb, + unsigned long n, void *p) +{ + return -ENOSYS; +} +static inline int gk20a_scale_qos_max_notify(struct notifier_block *nb, + unsigned long n, void *p) +{ + return -ENOSYS; +} +static inline u16 gk20a_scale_clamp_clk_target(struct gk20a *g, + u16 gpc2clk_target) +{ + return gpc2clk_target; +} +#else static inline int gk20a_scale_qos_notify(struct notifier_block *nb, unsigned long n, void *p) { return -ENOSYS; } +#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(5, 10, 0) */ + #endif #endif diff --git a/drivers/gpu/nvgpu/os/linux/vgpu/vgpu_linux.c b/drivers/gpu/nvgpu/os/linux/vgpu/vgpu_linux.c index d343be5ef..047fd5230 100644 --- a/drivers/gpu/nvgpu/os/linux/vgpu/vgpu_linux.c +++ b/drivers/gpu/nvgpu/os/linux/vgpu/vgpu_linux.c @@ -245,6 +245,8 @@ done: #ifdef CONFIG_GK20A_PM_QOS +#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 10, 0) + static int vgpu_qos_notify(struct notifier_block *nb, unsigned long n, void *data) { @@ -297,6 +299,7 @@ static void vgpu_pm_qos_remove(struct device *dev) g->scale_profile = NULL; } +#endif #endif static int vgpu_pm_init(struct device *dev) @@ -315,9 +318,11 @@ static int vgpu_pm_init(struct device *dev) gk20a_scale_init(dev); #ifdef CONFIG_GK20A_PM_QOS +#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 10, 0) err = vgpu_pm_qos_init(dev); if (err) return err; +#endif #endif return err; @@ -517,7 +522,9 @@ int vgpu_remove(struct platform_device *pdev) nvgpu_mutex_destroy(&l->dmabuf_priv_list_lock); #ifdef CONFIG_GK20A_PM_QOS +#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 10, 0) vgpu_pm_qos_remove(dev); +#endif #endif if (g->remove_support) g->remove_support(g);