diff --git a/drivers/gpu/nvgpu/include/nvgpu/fuse.h b/drivers/gpu/nvgpu/include/nvgpu/fuse.h index dc01dba1c..9f6a338e3 100644 --- a/drivers/gpu/nvgpu/include/nvgpu/fuse.h +++ b/drivers/gpu/nvgpu/include/nvgpu/fuse.h @@ -94,6 +94,19 @@ int nvgpu_tegra_fuse_read_reserved_calib(struct gk20a *g, u32 *val); */ int nvgpu_tegra_fuse_read_gcplex_config_fuse(struct gk20a *g, u32 *val); +/** + * @brief - Reads FUSE_OPT_GPC_DISABLE_0 fuse. + * + * @param g [in] - GPU super structure. + * @param val [out] - Populated with register FUSE_OPT_GPC_DISABLE_0 value. + * + * - Provide information about the GPU GPC floor-sweep info + * + * @return 0 on success or negative value on failure. + * + */ +int nvgpu_tegra_fuse_read_opt_gpc_disable(struct gk20a *g, u32 *val); + /** * @brief - Reads the per-device identifier fuses. * diff --git a/drivers/gpu/nvgpu/include/nvgpu/linux/soc_fuse.h b/drivers/gpu/nvgpu/include/nvgpu/linux/soc_fuse.h index 01c480c4c..ed8d73863 100644 --- a/drivers/gpu/nvgpu/include/nvgpu/linux/soc_fuse.h +++ b/drivers/gpu/nvgpu/include/nvgpu/linux/soc_fuse.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, NVIDIA CORPORATION. All rights reserved. + * Copyright (c) 2020-2021, 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, @@ -33,6 +33,10 @@ #define FUSE_RESERVED_CALIB0_0 0x204 #endif +#ifndef FUSE_OPT_GPC_DISABLE_0 +#define FUSE_OPT_GPC_DISABLE_0 0x188 +#endif + /* T186+ */ #if !defined(FUSE_PDI0) && !defined(FUSE_PDI1) #define FUSE_PDI0 0x300 diff --git a/drivers/gpu/nvgpu/os/linux/fuse.c b/drivers/gpu/nvgpu/os/linux/fuse.c index ada735ee5..ee0710b8a 100644 --- a/drivers/gpu/nvgpu/os/linux/fuse.c +++ b/drivers/gpu/nvgpu/os/linux/fuse.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2020, NVIDIA CORPORATION. All rights reserved. + * Copyright (c) 2017-2021, 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, @@ -34,6 +34,11 @@ int nvgpu_tegra_fuse_read_gcplex_config_fuse(struct gk20a *g, u32 *val) return tegra_fuse_readl(FUSE_GCPLEX_CONFIG_FUSE_0, val); } +int nvgpu_tegra_fuse_read_opt_gpc_disable(struct gk20a *g, u32 *val) +{ + return tegra_fuse_readl(FUSE_OPT_GPC_DISABLE_0, val); +} + int nvgpu_tegra_fuse_read_per_device_identifier(struct gk20a *g, u64 *pdi) { u32 lo = 0U; diff --git a/drivers/gpu/nvgpu/os/linux/platform_ga10b_tegra.c b/drivers/gpu/nvgpu/os/linux/platform_ga10b_tegra.c index 6624bfadd..e708d259d 100644 --- a/drivers/gpu/nvgpu/os/linux/platform_ga10b_tegra.c +++ b/drivers/gpu/nvgpu/os/linux/platform_ga10b_tegra.c @@ -27,10 +27,13 @@ #include #endif #include +#include #include #include #include +#include +#include #ifdef CONFIG_NV_TEGRA_BPMP #include @@ -76,6 +79,34 @@ struct gk20a_platform_clk tegra_ga10b_clocks[] = { {"fuse", UINT_MAX} }; +#define NVGPU_GPC0_DISABLE BIT(0) +#define NVGPU_GPC1_DISABLE BIT(1) + +static bool ga10b_tegra_is_clock_available(struct gk20a *g, char *clk_name) +{ + u32 gpc_disable = 0U; + + /* Check for gpc floor-sweeping and report availability of gpcclks */ + if (nvgpu_tegra_fuse_read_opt_gpc_disable(g, &gpc_disable)) { + nvgpu_err(g, "unable to read opt_gpc_disable fuse"); + return false; + } + + if ((strcmp(clk_name, "gpc0clk") == 0) && + (gpc_disable & NVGPU_GPC0_DISABLE)) { + nvgpu_info(g, "GPC0 is floor-swept"); + return false; + } + + if ((strcmp(clk_name, "gpc1clk") == 0) && + (gpc_disable & NVGPU_GPC1_DISABLE)) { + nvgpu_info(g, "GPC1 is floor-swept"); + return false; + } + + return true; +} + /* * This function finds clocks in tegra platform and populates * the clock information to platform data. @@ -89,6 +120,7 @@ static int ga10b_tegra_acquire_platform_clocks(struct device *dev, struct gk20a *g = platform->g; struct device_node *np = nvgpu_get_node(g); unsigned int i, num_clks_dt; + int err; /* Get clocks only on supported platforms(silicon and fpga) */ if (!nvgpu_platform_is_silicon(g) && !nvgpu_platform_is_fpga(g)) { @@ -107,15 +139,21 @@ static int ga10b_tegra_acquire_platform_clocks(struct device *dev, platform->num_clks = 0; - /* TODO add floorsweep check before requesting clocks */ for (i = 0; i < num_clks_dt; i++) { long rate; - struct clk *c = of_clk_get_by_name(np, clk_entries[i].name); + struct clk *c; + + if (ga10b_tegra_is_clock_available(g, clk_entries[i].name)) { + c = of_clk_get_by_name(np, clk_entries[i].name); + } else { + continue; + } if (IS_ERR(c)) { nvgpu_info(g, "cannot get clock %s", clk_entries[i].name); - /* Continue with other clocks enable */ + err = PTR_ERR(c); + goto err_get_clock; } else { rate = clk_entries[i].default_rate; clk_set_rate(c, rate); @@ -135,6 +173,14 @@ static int ga10b_tegra_acquire_platform_clocks(struct device *dev, #endif return 0; + +err_get_clock: + while (i > 0 && i--) { + clk_put(platform->clk[i]); + platform->clk[i] = NULL; + } + + return err; } static int ga10b_tegra_get_clocks(struct device *dev)