From ae2d561c4865620649c84b6b7e4cb48eb1a65b29 Mon Sep 17 00:00:00 2001 From: Divya Date: Thu, 9 Sep 2021 07:22:51 +0000 Subject: [PATCH] gpu: nvgpu: add platform support for Static PG - Add support for taking static PG config values from DT nodes - Check those values against valid set of values for GPC, TPC and FBP - Store valid values in g->gpc_pg_mask, g->fbp_pg_mask and g->tpc_pg_mask[] array. Bug 200768322 JIRA NVGPU-6433 Change-Id: Ifc87e7d369034b1daa13866bc16a970602514bf6 Signed-off-by: Divya Reviewed-on: https://git-master.nvidia.com/r/c/linux-nvgpu/+/2594802 Reviewed-by: Vijayakumar Subbu Reviewed-by: svc-mobile-coverity Reviewed-by: svc-mobile-misra Reviewed-by: svc-mobile-cert Reviewed-by: mobile promotions GVS: Gerrit_Virtual_Submit Tested-by: mobile promotions --- drivers/gpu/nvgpu/common/init/nvgpu_init.c | 9 +- drivers/gpu/nvgpu/hal/init/hal_ga10b.c | 6 +- drivers/gpu/nvgpu/hal/tpc/tpc_gv11b.c | 81 +++++--- drivers/gpu/nvgpu/hal/tpc/tpc_gv11b.h | 4 +- drivers/gpu/nvgpu/include/nvgpu/gk20a.h | 45 +++-- .../gpu/nvgpu/include/nvgpu/gops/floorsweep.h | 6 +- drivers/gpu/nvgpu/os/linux/driver_common.c | 13 +- drivers/gpu/nvgpu/os/linux/module.c | 35 +++- .../gpu/nvgpu/os/linux/platform_ga10b_tegra.c | 183 +++++++++++++++++- drivers/gpu/nvgpu/os/linux/platform_gk20a.h | 26 +-- .../gpu/nvgpu/os/linux/platform_gv11b_tegra.c | 42 ++-- drivers/gpu/nvgpu/os/linux/sysfs.c | 27 +-- 12 files changed, 364 insertions(+), 113 deletions(-) diff --git a/drivers/gpu/nvgpu/common/init/nvgpu_init.c b/drivers/gpu/nvgpu/common/init/nvgpu_init.c index da3217522..bfc70e800 100644 --- a/drivers/gpu/nvgpu/common/init/nvgpu_init.c +++ b/drivers/gpu/nvgpu/common/init/nvgpu_init.c @@ -436,7 +436,6 @@ static int nvgpu_init_fbpa_ecc(struct gk20a *g) static int nvgpu_init_power_gate(struct gk20a *g) { int err; - u32 fuse_status = 0x0; /* * Pre-Silicon - Static pg feature related settings @@ -478,12 +477,8 @@ static int nvgpu_init_power_gate(struct gk20a *g) * halt the GPU poweron. */ g->can_tpc_pg = false; - if (g->ops.fuse.fuse_status_opt_tpc_gpc != NULL) { - fuse_status = g->ops.fuse.fuse_status_opt_tpc_gpc(g, 0); - } - if (g->ops.tpc_pg.init_tpc_pg != NULL) { - err = g->ops.tpc_pg.init_tpc_pg(g, fuse_status); + err = g->ops.tpc_pg.init_tpc_pg(g, &g->can_tpc_pg); if (err != 0) { return err; } @@ -518,7 +513,7 @@ static int nvgpu_init_power_gate_gr(struct gk20a *g) } return 0; } -#endif +#endif /* CONFIG_NVGPU_STATIC_POWERGATE */ static int nvgpu_init_boot_clk_or_clk_arb(struct gk20a *g) { diff --git a/drivers/gpu/nvgpu/hal/init/hal_ga10b.c b/drivers/gpu/nvgpu/hal/init/hal_ga10b.c index 5f4b038a8..02e5cbda4 100644 --- a/drivers/gpu/nvgpu/hal/init/hal_ga10b.c +++ b/drivers/gpu/nvgpu/hal/init/hal_ga10b.c @@ -1637,7 +1637,7 @@ static const struct gops_tpc_pg ga10b_ops_tpc_pg = { * HALs for static-pg will be updated * for pre-silicon platform during HAL init. * For silicon, static-pg feature related settings - * will be taken care of by BPMP. + * will be taken care by BPMP. * Silicon: assigining the HALs to NULL. * Pre-Silicon: To-do JIRA-NVGPU-7112 * to add these HALs @@ -1757,8 +1757,8 @@ int ga10b_init_hal(struct gk20a *g) gops->priv_ring = ga10b_ops_priv_ring; gops->fuse = ga10b_ops_fuse; gops->top = ga10b_ops_top; -#ifdef CONFIG_NVGPU_TPC_POWERGATE - gops->tpc = ga10b_ops_tpc; +#ifdef CONFIG_NVGPU_STATIC_POWERGATE + gops->tpc_pg = ga10b_ops_tpc_pg; #endif gops->grmgr = ga10b_ops_grmgr; gops->chip_init_gpu_characteristics = ga10b_init_gpu_characteristics; diff --git a/drivers/gpu/nvgpu/hal/tpc/tpc_gv11b.c b/drivers/gpu/nvgpu/hal/tpc/tpc_gv11b.c index 3dcc65d3d..2e074d13e 100644 --- a/drivers/gpu/nvgpu/hal/tpc/tpc_gv11b.c +++ b/drivers/gpu/nvgpu/hal/tpc/tpc_gv11b.c @@ -24,32 +24,52 @@ #include #include "tpc_gv11b.h" -int gv11b_tpc_pg(struct gk20a *g, u32 fuse_status) +#ifdef CONFIG_NVGPU_STATIC_POWERGATE +/* + * validate the requested tpc pg mask + * with respect to the current hw value. + */ +int gv11b_tpc_pg(struct gk20a *g, bool *can_tpc_pg) { int err = 0; + u32 fuse_status = 0x0; + + if (g->ops.fuse.fuse_status_opt_tpc_gpc != NULL) { + fuse_status = g->ops.fuse.fuse_status_opt_tpc_gpc(g, 0); + } if (fuse_status == 0x0) { - g->can_tpc_pg = true; - + /* + * fuse_status = 0x0 means all TPCs are active + * thus, tpc_pg_mask passed by user or DT + * can be applied to powergate the TPC(s). + */ + *can_tpc_pg = true; } else { /* if hardware has already floorswept any TPC * (fuse_status != 0x0) and if TPC PG mask - * sent from userspace is 0x0 GPU will be powered on - * with the default fuse_status setting. It cannot - * un-floorsweep any TPC + * sent from userspace/DT node is 0x0 (it is trying to + * set all TPCs active), GPU will be powered on + * with the default HW fuse_status setting. It cannot + * un-floorsweep any TPC. * thus, set g->tpc_pg_mask to fuse_status value + * and boot with default HW fuse settings. */ - if (g->tpc_pg_mask == 0x0) { - g->can_tpc_pg = true; - g->tpc_pg_mask = fuse_status; + if (g->tpc_pg_mask[PG_GPC0] == 0x0U) { + *can_tpc_pg = true; + g->tpc_pg_mask[PG_GPC0] = fuse_status; - } else if (fuse_status == g->tpc_pg_mask) { - g->can_tpc_pg = true; + } else if (fuse_status == g->tpc_pg_mask[PG_GPC0]) { + *can_tpc_pg = true; - } else if ((fuse_status & g->tpc_pg_mask) == + } else if ((fuse_status & g->tpc_pg_mask[PG_GPC0]) == fuse_status) { - g->can_tpc_pg = true; - + /* + * if HW has already floorswept any TPC + * check if the tpc_pg_mask sent by user + * is floorsweeping only additional TPCs + */ + *can_tpc_pg = true; } else { /* If userspace sends a TPC PG mask such that * it tries to un-floorsweep any TPC which is @@ -59,9 +79,9 @@ int gv11b_tpc_pg(struct gk20a *g, u32 fuse_status) * Return -EINVAL here and halt GPU poweron. */ nvgpu_err(g, "Invalid TPC_PG mask: 0x%x", - g->tpc_pg_mask); - g->can_tpc_pg = false; - g->tpc_pg_mask = 0x0; + g->tpc_pg_mask[PG_GPC0]); + *can_tpc_pg = false; + g->tpc_pg_mask[PG_GPC0] = 0x0; err = -EINVAL; } } @@ -69,19 +89,36 @@ int gv11b_tpc_pg(struct gk20a *g, u32 fuse_status) return err; } +/* To-do: check to rename this function */ void gv11b_gr_pg_tpc(struct gk20a *g) { - u32 tpc_pg_status = g->ops.fuse.fuse_status_opt_tpc_gpc(g, 0); + u32 tpc_pg_status = g->ops.fuse.fuse_status_opt_tpc_gpc(g, PG_GPC0); - if (tpc_pg_status == g->tpc_pg_mask) { + /* + * if the fuse status for tpc is same as tpc_pg_mask + * passed, then do nothing and return + */ + if (tpc_pg_status == g->tpc_pg_mask[PG_GPC0]) { return; } - g->ops.fuse.fuse_ctrl_opt_tpc_gpc(g, 0, g->tpc_pg_mask); + /* + * write to fuse_ctrl register to update the fuse settings + * as per the tpc_pg_mask + */ + g->ops.fuse.fuse_ctrl_opt_tpc_gpc(g, PG_GPC0, + g->tpc_pg_mask[PG_GPC0]); + /* + * To confirm that the write was successful + * read the fuse_status register. + * The write done is previous step may take some time to + * get update in fuse_status register + */ do { - tpc_pg_status = g->ops.fuse.fuse_status_opt_tpc_gpc(g, 0); - } while (tpc_pg_status != g->tpc_pg_mask); + tpc_pg_status = g->ops.fuse.fuse_status_opt_tpc_gpc(g, PG_GPC0); + } while (tpc_pg_status != g->tpc_pg_mask[PG_GPC0]); return; } +#endif diff --git a/drivers/gpu/nvgpu/hal/tpc/tpc_gv11b.h b/drivers/gpu/nvgpu/hal/tpc/tpc_gv11b.h index b58a2b8c0..596429eae 100644 --- a/drivers/gpu/nvgpu/hal/tpc/tpc_gv11b.h +++ b/drivers/gpu/nvgpu/hal/tpc/tpc_gv11b.h @@ -25,10 +25,12 @@ #ifndef NVGPU_TPC_GV11B_H #define NVGPU_TPC_GV11B_H +#ifdef CONFIG_NVGPU_STATIC_POWERGATE struct gk20a; -int gv11b_tpc_pg(struct gk20a *g, u32 fuse_status); +int gv11b_tpc_pg(struct gk20a *g, bool *can_tpc_pg); void gv11b_gr_pg_tpc(struct gk20a *g); +#endif #endif /* NVGPU_TPC_GV11B_H */ diff --git a/drivers/gpu/nvgpu/include/nvgpu/gk20a.h b/drivers/gpu/nvgpu/include/nvgpu/gk20a.h index 5d909440c..21bccdd2e 100644 --- a/drivers/gpu/nvgpu/include/nvgpu/gk20a.h +++ b/drivers/gpu/nvgpu/include/nvgpu/gk20a.h @@ -287,21 +287,24 @@ struct railgate_stats { /** * @} */ - /** @cond DOXYGEN_SHOULD_SKIP_THIS */ -/* MAX_TPC_PG_CONFIGS describes the maximum number of - * valid confiurations we can have for the TPC mask. The valid - * mask is used by SW to write to NV_FUSE_OPT_CTRL_GPC_TPC - * register to powergate the TPC in each GPC - * - * MAX_GPC_FBP_FS_CONFIGS describes the maximum number of - * valid confiurations we can have for the GPC and FBP mask. - * The valid mask is used by SW to write to NV_FUSE_OPT_CTRL_GPC - * NV_FUSE_OPT_CTRL_FBP registers to powergate the GPC and - * floorsweep FBP +#ifdef CONFIG_NVGPU_STATIC_POWERGATE +#define MAX_PG_GPC 2 +#define MAX_TPC_PER_GPC 4 +#define PG_GPC0 0 +#define PG_GPC1 1 +/* + * MAX_PG_TPC_CONFIGS describes the maximum number of + * valid configurations we can have for the TPC mask. */ -#define MAX_TPC_PG_CONFIGS 9 -#define MAX_GPC_FBP_FS_CONFIGS 3 +#define MAX_PG_TPC_CONFIGS (0x1 << MAX_TPC_PER_GPC) +/* + * MAX_PG_GPC_FBP_CONFIGS describes the maximum number of + * valid configurations we can have for the GPC and FBP mask. + */ +#define MAX_PG_GPC_FBP_CONFIGS ((0x1 << MAX_PG_GPC) - 1) + +#endif struct nvgpu_gpfifo_userdata { struct nvgpu_gpfifo_entry nvgpu_user *entries; @@ -724,17 +727,25 @@ struct gk20a { u32 fecs_feature_override_ecc_val; #endif +#ifdef CONFIG_NVGPU_STATIC_POWERGATE /** @cond DOXYGEN_SHOULD_SKIP_THIS */ - u32 tpc_pg_mask; + /* tpc pg mask array for available GPCs */ + u32 tpc_pg_mask[MAX_PG_GPC]; u32 fbp_pg_mask; u32 gpc_pg_mask; bool can_tpc_pg; bool can_fbp_pg; bool can_gpc_pg; - u32 valid_tpc_mask[MAX_TPC_PG_CONFIGS]; - u32 valid_gpc_fbp_fs_mask[MAX_GPC_FBP_FS_CONFIGS]; - + /* + * Valid config array for tpc pg mask + * and gpc/fbp mask. These valid values + * are chip specific and calculated based + * on available number of GPC, FBP and TPC + */ + u32 valid_tpc_pg_mask[MAX_PG_TPC_CONFIGS]; + u32 valid_gpc_fbp_pg_mask[MAX_PG_GPC_FBP_CONFIGS]; +#endif struct nvgpu_bios *bios; bool bios_is_init; diff --git a/drivers/gpu/nvgpu/include/nvgpu/gops/floorsweep.h b/drivers/gpu/nvgpu/include/nvgpu/gops/floorsweep.h index f2091e7ca..67c0918bd 100644 --- a/drivers/gpu/nvgpu/include/nvgpu/gops/floorsweep.h +++ b/drivers/gpu/nvgpu/include/nvgpu/gops/floorsweep.h @@ -24,17 +24,17 @@ #ifdef CONFIG_NVGPU_STATIC_POWERGATE struct gops_tpc_pg { - int (*init_tpc_pg)(struct gk20a *g, u32 fuse_status); + int (*init_tpc_pg)(struct gk20a *g, bool *can_tpc_pg); void (*tpc_pg)(struct gk20a *g); }; struct gops_fbp_pg { - int (*init_fbp_pg)(struct gk20a *g, bool *can_fbp_fs); + int (*init_fbp_pg)(struct gk20a *g, bool *can_fbp_pg); void (*fbp_pg)(struct gk20a *g); }; struct gops_gpc_pg { - int (*init_gpc_pg)(struct gk20a *g, bool *can_gpc_fs); + int (*init_gpc_pg)(struct gk20a *g, bool *can_gpc_pg); void (*gpc_pg)(struct gk20a *g); }; diff --git a/drivers/gpu/nvgpu/os/linux/driver_common.c b/drivers/gpu/nvgpu/os/linux/driver_common.c index c6c0b7f97..f66f5935e 100644 --- a/drivers/gpu/nvgpu/os/linux/driver_common.c +++ b/drivers/gpu/nvgpu/os/linux/driver_common.c @@ -184,14 +184,17 @@ static void nvgpu_init_pm_vars(struct gk20a *g) nvgpu_set_enabled(g, NVGPU_CAN_RAILGATE, nvgpu_platform_is_simulation(g)? true : platform->can_railgate_init); +#ifdef CONFIG_NVGPU_STATIC_POWERGATE g->can_tpc_pg = platform->can_tpc_pg; + g->can_gpc_pg = platform->can_gpc_pg; + g->can_fbp_pg = platform->can_fbp_pg; - for (i = 0; i < MAX_TPC_PG_CONFIGS; i++) - g->valid_tpc_mask[i] = platform->valid_tpc_mask[i]; - - for (i = 0; i < MAX_GPC_FBP_FS_CONFIGS; i++) - g->valid_gpc_fbp_fs_mask[i] = platform->valid_gpc_fbp_fs_mask[i]; + for (i = 0; i < MAX_PG_TPC_CONFIGS; i++) + g->valid_tpc_pg_mask[i] = platform->valid_tpc_pg_mask[i]; + for (i = 0; i < MAX_PG_GPC_FBP_CONFIGS; i++) + g->valid_gpc_fbp_pg_mask[i] = platform->valid_gpc_fbp_pg_mask[i]; +#endif g->ldiv_slowdown_factor = platform->ldiv_slowdown_factor_init; /* if default delay is not set, set default delay to 500msec */ if (platform->railgate_delay_init) diff --git a/drivers/gpu/nvgpu/os/linux/module.c b/drivers/gpu/nvgpu/os/linux/module.c index 9b5547707..861f021cd 100644 --- a/drivers/gpu/nvgpu/os/linux/module.c +++ b/drivers/gpu/nvgpu/os/linux/module.c @@ -1588,6 +1588,7 @@ static int nvgpu_read_fuse_overrides(struct gk20a *g) struct gk20a_platform *platform = dev_get_drvdata(dev_from_gk20a(g)); u32 *fuses; int count, i; + int ret = 0; if (!np) /* may be pcie device */ return 0; @@ -1612,24 +1613,44 @@ static int nvgpu_read_fuse_overrides(struct gk20a *g) case GP10B_FUSE_OPT_ECC_EN: g->fecs_feature_override_ecc_val = value; break; +#ifdef CONFIG_NVGPU_STATIC_POWERGATE case GV11B_FUSE_OPT_TPC_DISABLE: - if (platform->set_tpc_pg_mask != NULL) - platform->set_tpc_pg_mask(dev_from_gk20a(g), + if (platform->set_tpc_pg_mask != NULL) { + ret = platform->set_tpc_pg_mask(dev_from_gk20a(g), value); + if (ret != 0) { + return -EINVAL; + } + } break; case GA10B_FUSE_OPT_TPC_DISABLE: - /* TBD- JIRA NVGPU-6433 */ + if (platform->set_tpc_pg_mask != NULL) { + ret = platform->set_tpc_pg_mask(dev_from_gk20a(g), + value); + if (ret != 0) { + return -EINVAL; + } + } break; case GA10B_FUSE_OPT_GPC_DISABLE: - if (platform->set_gpc_mask != NULL) - platform->set_gpc_mask(dev_from_gk20a(g), + if (platform->set_gpc_pg_mask != NULL) { + ret = platform->set_gpc_pg_mask(dev_from_gk20a(g), value); + if (ret != 0) { + return -EINVAL; + } + } break; case GA10B_FUSE_OPT_FBP_DISABLE: - if (platform->set_fbp_mask != NULL) - platform->set_fbp_mask(dev_from_gk20a(g), + if (platform->set_fbp_pg_mask != NULL) { + ret = platform->set_fbp_pg_mask(dev_from_gk20a(g), value); + if (ret != 0) { + return -EINVAL; + } + } break; +#endif default: nvgpu_err(g, "ignore unknown fuse override %08x", fuse); break; diff --git a/drivers/gpu/nvgpu/os/linux/platform_ga10b_tegra.c b/drivers/gpu/nvgpu/os/linux/platform_ga10b_tegra.c index b4293577b..3e43fa782 100644 --- a/drivers/gpu/nvgpu/os/linux/platform_ga10b_tegra.c +++ b/drivers/gpu/nvgpu/os/linux/platform_ga10b_tegra.c @@ -281,13 +281,19 @@ static int ga10b_tegra_suspend(struct device *dev) return 0; } -static bool is_tpc_mask_valid(struct gk20a_platform *platform, u32 tpc_pg_mask) +#ifdef CONFIG_NVGPU_STATIC_POWERGATE +static bool ga10b_tegra_is_gpc_fbp_pg_mask_valid(struct gk20a_platform *platform, + u32 dt_gpc_fbp_pg_mask) { u32 i; bool valid = false; - for (i = 0; i < MAX_TPC_PG_CONFIGS; i++) { - if (tpc_pg_mask == platform->valid_tpc_mask[i]) { + for (i = 0U; i < MAX_PG_GPC_FBP_CONFIGS; i++) { + /* + * check if gpc/fbp pg mask passed by DT node + * is valid gpc/fbp pg mask + */ + if (dt_gpc_fbp_pg_mask == platform->valid_gpc_fbp_pg_mask[i]) { valid = true; break; } @@ -295,16 +301,162 @@ static bool is_tpc_mask_valid(struct gk20a_platform *platform, u32 tpc_pg_mask) return valid; } -static void ga10b_tegra_set_tpc_pg_mask(struct device *dev, u32 tpc_pg_mask) +static int ga10b_tegra_set_gpc_pg_mask(struct device *dev, u32 dt_gpc_pg_mask) { struct gk20a_platform *platform = gk20a_get_platform(dev); struct gk20a *g = get_gk20a(dev); - if (is_tpc_mask_valid(platform, tpc_pg_mask)) { - g->tpc_pg_mask = tpc_pg_mask; + if (ga10b_tegra_is_gpc_fbp_pg_mask_valid(platform, dt_gpc_pg_mask)) { + g->gpc_pg_mask = dt_gpc_pg_mask; + /* + * update FBP PG mask same as GPC PG mask + * as there is 1:1 mapping for GPC and FBP + */ + g->fbp_pg_mask = dt_gpc_pg_mask; + return 0; + } + + nvgpu_err(g, "Invalid GPC-PG mask"); + return -EINVAL; +} + +static int ga10b_tegra_set_fbp_pg_mask(struct device *dev, u32 dt_fbp_pg_mask) +{ + struct gk20a_platform *platform = gk20a_get_platform(dev); + struct gk20a *g = get_gk20a(dev); + + if (ga10b_tegra_is_gpc_fbp_pg_mask_valid(platform, dt_fbp_pg_mask)) { + g->fbp_pg_mask = dt_fbp_pg_mask; + /* + * update GPC PG mask same as FBP PG mask + * as there is 1:1 mapping for GPC and FBP + */ + g->gpc_pg_mask = dt_fbp_pg_mask; + return 0; + } + + nvgpu_err(g, "Invalid FBP-PG mask"); + return -EINVAL; +} + +static void ga10b_tegra_set_valid_tpc_pg_mask(struct gk20a_platform *platform) +{ + u32 i; + + for (i = 0U; i < MAX_PG_TPC_CONFIGS; i++) { + /* + * There are 4 TPCs in each GPC in ga10b + * thus, valid tpc pg mask ranges from + * 0x0 to 0xF + * 0XF will powergate all TPCs, but this + * value will be re-checked again as per the + * the GPC-PG mask + */ + platform->valid_tpc_pg_mask[i] = i; } } +static int ga10b_tegra_set_tpc_pg_mask(struct device *dev, u32 dt_tpc_pg_mask) +{ + struct gk20a *g = get_gk20a(dev); + struct gk20a_platform *platform = gk20a_get_platform(dev); + u32 i, j; + bool pg_status, valid = false; + /* Hold tpc pg mask value for validation */ + u32 tmp_tpc_pg_mask[MAX_PG_GPC]; + /* Hold user requested combined tpc pg mask value */ + u32 combined_tpc_pg_mask; + + /* first set the valid masks ranges for tpc pg */ + ga10b_tegra_set_valid_tpc_pg_mask(platform); + + /* + * if tpc pg mask sent from DT node tries + * to powergate all the TPCs in both GPC0 and GPC1 + * then it is inavlid pg config + */ + if (dt_tpc_pg_mask == 0xFF) { + nvgpu_err(g, "Invalid TPC_PG_MASK:0x%x", dt_tpc_pg_mask); + return -EINVAL; + } else { + /* store dt_tpc_pg_mask in a temp variable */ + combined_tpc_pg_mask = dt_tpc_pg_mask; + + /* separately store tpc pg mask in a temp array */ + for (i = 0U; i < MAX_PG_GPC; i++) { + tmp_tpc_pg_mask[i] = (combined_tpc_pg_mask >> (4*i)) & 0xFU; + } + + /* check if the tpc pg mask sent from DT is valid or not */ + for (i = 0U ; i < MAX_PG_GPC; i++) { + for (j = 0U; j < MAX_PG_TPC_CONFIGS; j++) { + if (tmp_tpc_pg_mask[i] == platform->valid_tpc_pg_mask[j]) { + /* store the valid config */ + g->tpc_pg_mask[i] = tmp_tpc_pg_mask[i]; + valid = true; + break; + } + } + if (valid == false) { + nvgpu_err(g, "Invalid TPC PG mask: 0x%x", + tmp_tpc_pg_mask[i]); + return -EINVAL; + } + } + /* + * check if all TPCs of a GPC are powergated + * then powergate the corresponding GPC + */ + for (i = 0U; i < MAX_PG_GPC; i++) { + if (g->tpc_pg_mask[i] == 0xFU) { + g->gpc_pg_mask = (0x1U << i); + g->fbp_pg_mask = (0x1U << i); + } + } + + /* + * If any one GPC is already floorswept + * then all the TPCs in that GPC are floorswept + * based on gpc_mask update the tpc_pg_mask + */ + switch (g->gpc_pg_mask) { + case 0x0: /* do nothing as all GPCs are active */ + break; + case 0x1: + g->tpc_pg_mask[PG_GPC0] = 0xFU; + break; + case 0x2: + g->tpc_pg_mask[PG_GPC1] = 0xFU; + break; + default: + nvgpu_err(g, "Invalid GPC PG mask: 0x%x", + g->gpc_pg_mask); + return -EINVAL; + } + } + + /* + * If both GPC0_TPC and GPC1_TPC mask are 0xF + * then it is invalid as we cannot powergate + * all the TPCs on the chip. This is invalid + * configuration + */ + for (i = 0U; i < MAX_PG_GPC; i++) { + if (g->tpc_pg_mask[i] == 0xF) { + pg_status = true; + } else { + pg_status = false; + break; + } + } + if (pg_status == true) { + nvgpu_err(g, "Disabling all TPCs isn't allowed!"); + return -EINVAL; + } + return 0; +} +#endif + struct gk20a_platform ga10b_tegra_platform = { #ifdef CONFIG_TEGRA_GK20A_NVHOST .has_syncpoints = true, @@ -321,11 +473,28 @@ struct gk20a_platform ga10b_tegra_platform = { .railgate_delay_init = 500, .can_railgate_init = false, +#ifdef CONFIG_NVGPU_STATIC_POWERGATE /* add tpc powergate JIRA NVGPU-4683 */ .can_tpc_pg = false, + .can_gpc_pg = false, + .can_fbp_pg = false, - .set_tpc_pg_mask = ga10b_tegra_set_tpc_pg_mask, + /* + * there are 2 GPCs and 2 FBPs + * so the valid config to powergate + * is 0x0 (all active) + * 0x1 (GPC1/FBP1 active) + * 0x2 (GPC0/FBP0 active) + * 0x3 (both powergated) becomes invalid + */ + .valid_gpc_fbp_pg_mask[0] = 0x0, + .valid_gpc_fbp_pg_mask[1] = 0x1, + .valid_gpc_fbp_pg_mask[2] = 0x2, + .set_tpc_pg_mask = ga10b_tegra_set_tpc_pg_mask, + .set_gpc_pg_mask = ga10b_tegra_set_gpc_pg_mask, + .set_fbp_pg_mask = ga10b_tegra_set_fbp_pg_mask, +#endif .can_slcg = true, .can_blcg = true, .can_elcg = true, diff --git a/drivers/gpu/nvgpu/os/linux/platform_gk20a.h b/drivers/gpu/nvgpu/os/linux/platform_gk20a.h index 16da95373..86f9b4911 100644 --- a/drivers/gpu/nvgpu/os/linux/platform_gk20a.h +++ b/drivers/gpu/nvgpu/os/linux/platform_gk20a.h @@ -81,6 +81,7 @@ struct gk20a_platform { /* controls gc off feature for pci gpu */ bool can_pci_gc_off; +#ifdef CONFIG_NVGPU_STATIC_POWERGATE /* Should be populated at probe. */ bool can_tpc_pg; @@ -89,7 +90,7 @@ struct gk20a_platform { /* Should be populated at probe. */ bool can_gpc_pg; - +#endif /* Should be populated at probe. */ bool can_elpg_init; @@ -118,12 +119,14 @@ struct gk20a_platform { /* Reset control for device */ struct reset_control *reset_control; #endif - /* valid TPC-MASK */ - u32 valid_tpc_mask[MAX_TPC_PG_CONFIGS]; - /* Valid GPC and FBP mask */ - u32 valid_gpc_fbp_fs_mask[MAX_GPC_FBP_FS_CONFIGS]; +#ifdef CONFIG_NVGPU_STATIC_POWERGATE + /* valid TPC-PG MASK */ + u32 valid_tpc_pg_mask[MAX_PG_TPC_CONFIGS]; + /* Valid GPC-PG and FBP-PG mask */ + u32 valid_gpc_fbp_pg_mask[MAX_PG_GPC_FBP_CONFIGS]; +#endif /* Delay before rail gated */ int railgate_delay_init; @@ -244,15 +247,16 @@ struct gk20a_platform { /* Pre callback is called before frequency change */ void (*prescale)(struct device *dev); +#ifdef CONFIG_NVGPU_STATIC_POWERGATE /* Set TPC_PG_MASK during probe */ - void (*set_tpc_pg_mask)(struct device *dev, u32 tpc_pg_mask); + int (*set_tpc_pg_mask)(struct device *dev, u32 dt_tpc_pg_mask); - /* Set GPC_MASK during probe */ - void (*set_gpc_mask)(struct device *dev, u32 gpc_mask); - - /* Set FBP_MASK during probe */ - void (*set_fbp_mask)(struct device *dev, u32 fbp_mask); + /* Set GPC_PG_MASK during probe */ + int (*set_gpc_pg_mask)(struct device *dev, u32 dt_gpc_pg_mask); + /* Set FBP_PG_MASK during probe */ + int (*set_fbp_pg_mask)(struct device *dev, u32 dt_fbp_pg_mask); +#endif /* Devfreq governor name. If scaling is enabled, we request * this governor to be used in scaling */ const char *devfreq_governor; diff --git a/drivers/gpu/nvgpu/os/linux/platform_gv11b_tegra.c b/drivers/gpu/nvgpu/os/linux/platform_gv11b_tegra.c index f864c6c6f..88d751374 100644 --- a/drivers/gpu/nvgpu/os/linux/platform_gv11b_tegra.c +++ b/drivers/gpu/nvgpu/os/linux/platform_gv11b_tegra.c @@ -207,13 +207,14 @@ static int gv11b_tegra_suspend(struct device *dev) return 0; } -static bool is_tpc_mask_valid(struct gk20a_platform *platform, u32 tpc_pg_mask) +static bool gv11b_tegra_is_tpc_mask_valid(struct gk20a_platform *platform, + u32 dt_tpc_pg_mask) { u32 i; bool valid = false; - for (i = 0; i < MAX_TPC_PG_CONFIGS; i++) { - if (tpc_pg_mask == platform->valid_tpc_mask[i]) { + for (i = 0U; i < MAX_PG_TPC_CONFIGS; i++) { + if (dt_tpc_pg_mask == platform->valid_tpc_pg_mask[i]) { valid = true; break; } @@ -221,13 +222,17 @@ static bool is_tpc_mask_valid(struct gk20a_platform *platform, u32 tpc_pg_mask) return valid; } -static void gv11b_tegra_set_tpc_pg_mask(struct device *dev, u32 tpc_pg_mask) +static int gv11b_tegra_set_tpc_pg_mask(struct device *dev, u32 dt_tpc_pg_mask) { struct gk20a_platform *platform = gk20a_get_platform(dev); struct gk20a *g = get_gk20a(dev); - if (is_tpc_mask_valid(platform, tpc_pg_mask)) { - g->tpc_pg_mask = tpc_pg_mask; + if (gv11b_tegra_is_tpc_mask_valid(platform, dt_tpc_pg_mask)) { + g->tpc_pg_mask[PG_GPC0] = dt_tpc_pg_mask; + return 0; + } else { + nvgpu_err(g, "Invalid TPC-PG mask"); + return -EINVAL; } } @@ -245,21 +250,22 @@ struct gk20a_platform gv11b_tegra_platform = { .late_probe = gv11b_tegra_late_probe, .remove = gv11b_tegra_remove, .railgate_delay_init = 500, - .can_railgate_init = true, - .can_tpc_pg = true, - .valid_tpc_mask[0] = 0x0, - .valid_tpc_mask[1] = 0x1, - .valid_tpc_mask[2] = 0x2, - .valid_tpc_mask[3] = 0x4, - .valid_tpc_mask[4] = 0x8, - .valid_tpc_mask[5] = 0x5, - .valid_tpc_mask[6] = 0x6, - .valid_tpc_mask[7] = 0x9, - .valid_tpc_mask[8] = 0xa, + .can_railgate_init = true, +#ifdef CONFIG_NVGPU_STATIC_POWERGATE + .can_tpc_pg = true, + .valid_tpc_pg_mask[0] = 0x0, + .valid_tpc_pg_mask[1] = 0x1, + .valid_tpc_pg_mask[2] = 0x2, + .valid_tpc_pg_mask[3] = 0x4, + .valid_tpc_pg_mask[4] = 0x8, + .valid_tpc_pg_mask[5] = 0x5, + .valid_tpc_pg_mask[6] = 0x6, + .valid_tpc_pg_mask[7] = 0x9, + .valid_tpc_pg_mask[8] = 0xa, .set_tpc_pg_mask = gv11b_tegra_set_tpc_pg_mask, - +#endif .can_slcg = true, .can_blcg = true, .can_elcg = true, diff --git a/drivers/gpu/nvgpu/os/linux/sysfs.c b/drivers/gpu/nvgpu/os/linux/sysfs.c index c95f0c124..d1dc2a1eb 100644 --- a/drivers/gpu/nvgpu/os/linux/sysfs.c +++ b/drivers/gpu/nvgpu/os/linux/sysfs.c @@ -836,14 +836,6 @@ static ssize_t force_idle_read(struct device *dev, static DEVICE_ATTR(force_idle, ROOTRW, force_idle_read, force_idle_store); #endif -static ssize_t tpc_pg_mask_read(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct gk20a *g = get_gk20a(dev); - - return snprintf(buf, NVGPU_CPU_PAGE_SIZE, "%d\n", g->tpc_pg_mask); -} - static ssize_t gpc_fs_mask_show(struct device *dev, struct device_attribute *attr, char *buf) { @@ -886,13 +878,14 @@ static ssize_t fbp_fs_mask_show(struct device *dev, static DEVICE_ATTR_RO(fbp_fs_mask); +#ifdef CONFIG_NVGPU_STATIC_POWERGATE static bool is_tpc_mask_valid(struct gk20a *g, u32 tpc_mask) { u32 i; bool valid = false; - for (i = 0; i < MAX_TPC_PG_CONFIGS; i++) { - if (tpc_mask == g->valid_tpc_mask[i]) { + for (i = 0U; i < MAX_PG_TPC_CONFIGS; i++) { + if (tpc_mask == g->valid_tpc_pg_mask[i]) { valid = true; break; } @@ -900,6 +893,15 @@ static bool is_tpc_mask_valid(struct gk20a *g, u32 tpc_mask) return valid; } +static ssize_t tpc_pg_mask_read(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct gk20a *g = get_gk20a(dev); + + return snprintf(buf, NVGPU_CPU_PAGE_SIZE, "%d\n", + g->tpc_pg_mask[PG_GPC0]); +} + static ssize_t tpc_pg_mask_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { @@ -915,7 +917,7 @@ static ssize_t tpc_pg_mask_store(struct device *dev, return -EINVAL; } - if (val == g->tpc_pg_mask) { + if (val == g->tpc_pg_mask[PG_GPC0]) { nvgpu_info(g, "no value change, same mask already set"); goto exit; } @@ -935,7 +937,7 @@ static ssize_t tpc_pg_mask_store(struct device *dev, * the possible valid TPC configurations. */ if (is_tpc_mask_valid(g, (u32)val)) { - g->tpc_pg_mask = val; + g->tpc_pg_mask[PG_GPC0] = val; } else { nvgpu_err(g, "TPC-PG mask is invalid"); nvgpu_mutex_release(&g->static_pg_lock); @@ -946,6 +948,7 @@ exit: return count; } +#endif static DEVICE_ATTR(tpc_pg_mask, ROOTRW, tpc_pg_mask_read, tpc_pg_mask_store);