nouveau: gm20b: support t210b01 variant

The gm20b in Tegra210B01 has a different ID and has different pllg
programming requirements.
This commit is contained in:
tmakin committed 2026-07-29 00:37:55 -05:00
1 parent 03303eaa6c
commit 1c46175557
1 file changed
+177 -17
+177 -17
View File
@@ -96,12 +96,18 @@ struct gm20b_clk_dvfs_params {
u32 vco_ctrl;
};
static const struct gm20b_clk_dvfs_params gm20b_dvfs_params = {
static const struct gm20b_clk_dvfs_params gm20b_b1_dvfs_params = {
.coeff_slope = -165230,
.coeff_offs = 214007,
.vco_ctrl = 0x7 << 3,
};
static const struct gm20b_clk_dvfs_params gm20b_c1_dvfs_params = {
.coeff_slope = 0,
.coeff_offs = 0,
.vco_ctrl = 0x7 << 3,
};
/*
* base.n is now the *integer* part of the N factor.
* sdm_din contains n's decimal part.
@@ -358,7 +364,7 @@ gm20b_pllg_disable(struct gm20b_clk *clk)
}
static int
gm20b_pllg_program_mnp(struct gm20b_clk *clk, const struct gk20a_pll *pll)
gm20b_pllg_program_mnp(struct gm20b_clk *clk, const struct gk20a_pll *pll, bool pll_c1)
{
struct nvkm_subdev *subdev = &clk->base.base.subdev;
struct nvkm_device *device = subdev->device;
@@ -396,7 +402,7 @@ gm20b_pllg_program_mnp(struct gm20b_clk *clk, const struct gk20a_pll *pll)
* the case, calculate and program an interim PL that will allow
* us to respect that rule.
*/
if ((old & new) == 0) {
if ((old & new) == 0 && !pll_c1) {
cur_pll.base.pl = min(old | BIT(ffs(new) - 1),
new | BIT(ffs(old) - 1));
gk20a_pllg_write_mnp(&clk->base, &cur_pll.base);
@@ -431,7 +437,7 @@ gm20b_pllg_program_mnp(struct gm20b_clk *clk, const struct gk20a_pll *pll)
}
static int
gm20b_pllg_program_mnp_slide(struct gm20b_clk *clk, const struct gk20a_pll *pll)
gm20b_pllg_program_mnp_slide(struct gm20b_clk *clk, const struct gk20a_pll *pll, bool pll_c1)
{
struct gk20a_pll cur_pll;
int ret;
@@ -453,7 +459,7 @@ gm20b_pllg_program_mnp_slide(struct gm20b_clk *clk, const struct gk20a_pll *pll)
/* program MNP with the new clock parameters and new NDIV_LO */
cur_pll = *pll;
cur_pll.n = gk20a_pllg_n_lo(&clk->base, &cur_pll);
ret = gm20b_pllg_program_mnp(clk, &cur_pll);
ret = gm20b_pllg_program_mnp(clk, &cur_pll, pll_c1);
if (ret)
return ret;
@@ -611,7 +617,7 @@ gm20b_clk_prog(struct nvkm_clk *base)
pll_safe = clk->new_pll;
gm20b_dvfs_calc_safe_pll(clk, &pll_safe);
ret = gm20b_pllg_program_mnp_slide(clk, &pll_safe);
ret = gm20b_pllg_program_mnp_slide(clk, &pll_safe, false);
if (ret)
return ret;
}
@@ -632,7 +638,73 @@ prog:
clk->dvfs = clk->new_dvfs;
clk->base.pll = clk->new_pll;
return gm20b_pllg_program_mnp_slide(clk, &clk->base.pll);
return gm20b_pllg_program_mnp_slide(clk, &clk->base.pll, false);
}
static int
gm20b_b01_clk_prog(struct nvkm_clk *base)
{
struct gm20b_clk *clk = gm20b_clk(base);
u32 cur_freq;
int ret;
/* No change in DVFS settings? */
if (clk->uv == clk->new_uv)
goto prog;
/*
* Interim step for changing DVFS detection settings: low enough
* frequency to be safe at DVFS coeff = 0.
*
* 1. If voltage is increasing:
* - safe frequency target matches the lowest - old - frequency
* - DVFS settings are still old
* - Voltage already increased to new level by volt, but maximum
* detection limit assures PLL output remains under F/V curve
*
* 2. If voltage is decreasing:
* - safe frequency target matches the lowest - new - frequency
* - DVFS settings are still old
* - Voltage is also old, it will be lowered by volt afterwards
*
* Interim step can be skipped if old frequency is below safe minimum,
* i.e., it is low enough to be safe at any voltage in operating range
* with zero DVFS coefficient.
*/
cur_freq = nvkm_clk_read(&clk->base.base, nv_clk_src_gpc);
if (cur_freq > clk->safe_fmax_vmin) {
struct gk20a_pll pll_safe;
if (clk->uv < clk->new_uv)
/* voltage will raise: safe frequency is current one */
pll_safe = clk->base.pll;
else
/* voltage will drop: safe frequency is new one */
pll_safe = clk->new_pll;
gm20b_dvfs_calc_safe_pll(clk, &pll_safe);
ret = gm20b_pllg_program_mnp_slide(clk, &pll_safe, true);
if (ret)
return ret;
}
/*
* DVFS detection settings transition:
* - Set DVFS coefficient zero
* - Set calibration level to new voltage
* - Set DVFS coefficient to match new voltage
*/
gm20b_dvfs_program_coeff(clk, 0);
gm20b_dvfs_program_ext_cal(clk, clk->new_dvfs.dfs_ext_cal);
gm20b_dvfs_program_coeff(clk, clk->new_dvfs.dfs_coeff);
gm20b_dvfs_program_dfs_detection(clk, &clk->new_dvfs);
prog:
clk->uv = clk->new_uv;
clk->dvfs = clk->new_dvfs;
clk->base.pll = clk->new_pll;
return gm20b_pllg_program_mnp_slide(clk, &clk->base.pll, true);
}
static struct nvkm_pstate
@@ -911,8 +983,85 @@ calibrated:
return 0;
}
static int
gm20b_b01_clk_init_dvfs(struct gm20b_clk *clk)
{
struct nvkm_subdev *subdev = &clk->base.base.subdev;
struct nvkm_device *device = subdev->device;
bool fused = clk->uvdet_offs && clk->uvdet_slope;
static const s32 ADC_SLOPE_UV = 10000; /* default ADC detection slope */
u32 data;
int ret;
/* Enable NA DVFS */
nvkm_mask(device, GPCPLL_DVFS1, GPCPLL_DVFS1_EN_DFS_BIT,
GPCPLL_DVFS1_EN_DFS_BIT);
/* Set VCO_CTRL */
if (clk->dvfs_params->vco_ctrl)
nvkm_mask(device, GPCPLL_CFG3, GPCPLL_CFG3_VCO_CTRL_MASK,
clk->dvfs_params->vco_ctrl << GPCPLL_CFG3_VCO_CTRL_SHIFT);
if (fused) {
/* Start internal calibration, but ignore results */
nvkm_mask(device, GPCPLL_DVFS1, GPCPLL_DVFS1_EN_DFS_CAL_BIT,
GPCPLL_DVFS1_EN_DFS_CAL_BIT);
/* got uvdev parameters from fuse, skip calibration */
goto calibrated;
}
/*
* If calibration parameters are not fused, start internal calibration,
* wait for completion, and use results along with default slope to
* calculate ADC offset during boot.
*/
nvkm_mask(device, GPCPLL_DVFS1, GPCPLL_DVFS1_EN_DFS_CAL_BIT,
GPCPLL_DVFS1_EN_DFS_CAL_BIT);
/*
* C1 PLL revision must be enabled to read internal calibration results.
*/
nvkm_mask(device, GPCPLL_CFG, GPCPLL_CFG_ENABLE, 1);
/* Wait for internal calibration done (spec < 2us). */
ret = nvkm_wait_usec(device, 10, GPCPLL_DVFS1,
GPCPLL_DVFS1_DFS_CAL_DONE_BIT,
GPCPLL_DVFS1_DFS_CAL_DONE_BIT);
if (ret < 0) {
nvkm_error(subdev, "GPCPLL calibration timeout\n");
return -ETIMEDOUT;
}
data = nvkm_rd32(device, GPCPLL_CFG3) >>
GPCPLL_CFG3_PLL_DFS_TESTOUT_SHIFT;
data &= MASK(GPCPLL_CFG3_PLL_DFS_TESTOUT_WIDTH);
/*
* Disable C1 PLL revision after reading calibration results.
*/
nvkm_mask(device, GPCPLL_CFG, GPCPLL_CFG_ENABLE, 0);
clk->uvdet_slope = ADC_SLOPE_UV;
clk->uvdet_offs = ((s32)clk->uv) - data * ADC_SLOPE_UV;
nvkm_debug(subdev, "calibrated DVFS parameters: offs %d, slope %d\n",
clk->uvdet_offs, clk->uvdet_slope);
calibrated:
/* Compute and apply initial DVFS parameters */
gm20b_dvfs_calc_det_coeff(clk, clk->uv, &clk->dvfs);
gm20b_dvfs_program_coeff(clk, 0);
gm20b_dvfs_program_ext_cal(clk, clk->dvfs.dfs_ext_cal);
gm20b_dvfs_program_coeff(clk, clk->dvfs.dfs_coeff);
gm20b_dvfs_program_dfs_detection(clk, &clk->new_dvfs);
return 0;
}
/* Forward declaration to detect speedo >=1 in gm20b_clk_init() */
static const struct nvkm_clk_func gm20b_clk;
static const struct nvkm_clk_func gm20b_b01_clk;
static int
gm20b_clk_init(struct nvkm_clk *base)
@@ -966,6 +1115,17 @@ gm20b_clk_init(struct nvkm_clk *base)
ret = gm20b_clk_init_dvfs(_clk);
if (ret)
return ret;
} else if (clk->base.func == &gm20b_b01_clk) {
struct gm20b_clk *_clk = gm20b_clk(base);
struct nvkm_volt *volt = device->volt;
/* Get current voltage */
_clk->uv = nvkm_volt_get(volt);
/* Initialize DVFS */
ret = gm20b_b01_clk_init_dvfs(_clk);
if (ret)
return ret;
}
/* Start with lowest frequency */
@@ -1021,10 +1181,10 @@ gm20b_clk = {
static const struct nvkm_clk_func
gm20b_b01_clk = {
.init = gm20b_clk_init,
.fini = gk20a_clk_fini,
.fini = gm20b_clk_fini,
.read = gk20a_clk_read,
.calc = gk20a_clk_calc,
.prog = gk20a_clk_prog,
.calc = gm20b_clk_calc,
.prog = gm20b_b01_clk_prog,
.tidy = gk20a_clk_tidy,
.pstates = gm20b_b01_pstates,
.nr_pstates = ARRAY_SIZE(gm20b_b01_pstates),
@@ -1038,14 +1198,14 @@ gm20b_b01_clk = {
static const struct nvkm_clk_func
gm20b_b01_hiopt_clk_speedo = {
.init = gm20b_clk_init,
.fini = gk20a_clk_fini,
.fini = gm20b_clk_fini,
.read = gk20a_clk_read,
.calc = gk20a_clk_calc,
.prog = gk20a_clk_prog,
.calc = gm20b_clk_calc,
.prog = gm20b_b01_clk_prog,
.tidy = gk20a_clk_tidy,
.pstates = gm20b_pstates,
.pstates = gm20b_b01_pstates,
/* HIOPT speedo only supports 16 voltages */
.nr_pstates = ARRAY_SIZE(gm20b_pstates) - 1,
.nr_pstates = ARRAY_SIZE(gm20b_b01_pstates) - 1,
.domains = {
{ nv_clk_src_crystal, 0xff },
{ nv_clk_src_gpc, 0xff, 0, "core", GK20A_CLK_GPC_MDIV },
@@ -1217,7 +1377,7 @@ gm20b_clk_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst,
clk->base.pl_to_div = pl_to_div;
clk->base.div_to_pl = div_to_pl;
clk->dvfs_params = &gm20b_dvfs_params;
clk->dvfs_params = &gm20b_b1_dvfs_params;
ret = gm20b_clk_init_fused_params(clk);
/*
@@ -1277,7 +1437,7 @@ gm20b_b01_clk_new(struct nvkm_device *device, enum nvkm_subdev_type type, int in
clk->base.pl_to_div = pl_to_div;
clk->base.div_to_pl = div_to_pl;
clk->dvfs_params = &gm20b_dvfs_params;
clk->dvfs_params = &gm20b_c1_dvfs_params;
ret = gm20b_clk_init_fused_params(clk);
/*