nouveau: gm20b: support t210b01 variant
The gm20b in Tegra210B01 has a different ID and has different pllg programming requirements.
This commit is contained in:
1 parent
03303eaa6c
commit
1c46175557
1 file changed
+177
-17
@@ -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);
|
||||
/*
|
||||
|
||||
Reference in new issue
Block a user