123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074 |
- /*
- * Copyright (c) 2016, 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"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
- * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- * DEALINGS IN THE SOFTWARE.
- */
- #include <subdev/clk.h>
- #include <subdev/volt.h>
- #include <subdev/timer.h>
- #include <core/device.h>
- #include <core/tegra.h>
- #include "priv.h"
- #include "gk20a.h"
- #define GPCPLL_CFG_SYNC_MODE BIT(2)
- #define BYPASSCTRL_SYS (SYS_GPCPLL_CFG_BASE + 0x340)
- #define BYPASSCTRL_SYS_GPCPLL_SHIFT 0
- #define BYPASSCTRL_SYS_GPCPLL_WIDTH 1
- #define GPCPLL_CFG2_SDM_DIN_SHIFT 0
- #define GPCPLL_CFG2_SDM_DIN_WIDTH 8
- #define GPCPLL_CFG2_SDM_DIN_MASK \
- (MASK(GPCPLL_CFG2_SDM_DIN_WIDTH) << GPCPLL_CFG2_SDM_DIN_SHIFT)
- #define GPCPLL_CFG2_SDM_DIN_NEW_SHIFT 8
- #define GPCPLL_CFG2_SDM_DIN_NEW_WIDTH 15
- #define GPCPLL_CFG2_SDM_DIN_NEW_MASK \
- (MASK(GPCPLL_CFG2_SDM_DIN_NEW_WIDTH) << GPCPLL_CFG2_SDM_DIN_NEW_SHIFT)
- #define GPCPLL_CFG2_SETUP2_SHIFT 16
- #define GPCPLL_CFG2_PLL_STEPA_SHIFT 24
- #define GPCPLL_DVFS0 (SYS_GPCPLL_CFG_BASE + 0x10)
- #define GPCPLL_DVFS0_DFS_COEFF_SHIFT 0
- #define GPCPLL_DVFS0_DFS_COEFF_WIDTH 7
- #define GPCPLL_DVFS0_DFS_COEFF_MASK \
- (MASK(GPCPLL_DVFS0_DFS_COEFF_WIDTH) << GPCPLL_DVFS0_DFS_COEFF_SHIFT)
- #define GPCPLL_DVFS0_DFS_DET_MAX_SHIFT 8
- #define GPCPLL_DVFS0_DFS_DET_MAX_WIDTH 7
- #define GPCPLL_DVFS0_DFS_DET_MAX_MASK \
- (MASK(GPCPLL_DVFS0_DFS_DET_MAX_WIDTH) << GPCPLL_DVFS0_DFS_DET_MAX_SHIFT)
- #define GPCPLL_DVFS1 (SYS_GPCPLL_CFG_BASE + 0x14)
- #define GPCPLL_DVFS1_DFS_EXT_DET_SHIFT 0
- #define GPCPLL_DVFS1_DFS_EXT_DET_WIDTH 7
- #define GPCPLL_DVFS1_DFS_EXT_STRB_SHIFT 7
- #define GPCPLL_DVFS1_DFS_EXT_STRB_WIDTH 1
- #define GPCPLL_DVFS1_DFS_EXT_CAL_SHIFT 8
- #define GPCPLL_DVFS1_DFS_EXT_CAL_WIDTH 7
- #define GPCPLL_DVFS1_DFS_EXT_SEL_SHIFT 15
- #define GPCPLL_DVFS1_DFS_EXT_SEL_WIDTH 1
- #define GPCPLL_DVFS1_DFS_CTRL_SHIFT 16
- #define GPCPLL_DVFS1_DFS_CTRL_WIDTH 12
- #define GPCPLL_DVFS1_EN_SDM_SHIFT 28
- #define GPCPLL_DVFS1_EN_SDM_WIDTH 1
- #define GPCPLL_DVFS1_EN_SDM_BIT BIT(28)
- #define GPCPLL_DVFS1_EN_DFS_SHIFT 29
- #define GPCPLL_DVFS1_EN_DFS_WIDTH 1
- #define GPCPLL_DVFS1_EN_DFS_BIT BIT(29)
- #define GPCPLL_DVFS1_EN_DFS_CAL_SHIFT 30
- #define GPCPLL_DVFS1_EN_DFS_CAL_WIDTH 1
- #define GPCPLL_DVFS1_EN_DFS_CAL_BIT BIT(30)
- #define GPCPLL_DVFS1_DFS_CAL_DONE_SHIFT 31
- #define GPCPLL_DVFS1_DFS_CAL_DONE_WIDTH 1
- #define GPCPLL_DVFS1_DFS_CAL_DONE_BIT BIT(31)
- #define GPC_BCAST_GPCPLL_DVFS2 (GPC_BCAST_GPCPLL_CFG_BASE + 0x20)
- #define GPC_BCAST_GPCPLL_DVFS2_DFS_EXT_STROBE_BIT BIT(16)
- #define GPCPLL_CFG3_PLL_DFS_TESTOUT_SHIFT 24
- #define GPCPLL_CFG3_PLL_DFS_TESTOUT_WIDTH 7
- #define DFS_DET_RANGE 6 /* -2^6 ... 2^6-1 */
- #define SDM_DIN_RANGE 12 /* -2^12 ... 2^12-1 */
- struct gm20b_clk_dvfs_params {
- s32 coeff_slope;
- s32 coeff_offs;
- u32 vco_ctrl;
- };
- static const struct gm20b_clk_dvfs_params gm20b_dvfs_params = {
- .coeff_slope = -165230,
- .coeff_offs = 214007,
- .vco_ctrl = 0x7 << 3,
- };
- /*
- * base.n is now the *integer* part of the N factor.
- * sdm_din contains n's decimal part.
- */
- struct gm20b_pll {
- struct gk20a_pll base;
- u32 sdm_din;
- };
- struct gm20b_clk_dvfs {
- u32 dfs_coeff;
- s32 dfs_det_max;
- s32 dfs_ext_cal;
- };
- struct gm20b_clk {
- /* currently applied parameters */
- struct gk20a_clk base;
- struct gm20b_clk_dvfs dvfs;
- u32 uv;
- /* new parameters to apply */
- struct gk20a_pll new_pll;
- struct gm20b_clk_dvfs new_dvfs;
- u32 new_uv;
- const struct gm20b_clk_dvfs_params *dvfs_params;
- /* fused parameters */
- s32 uvdet_slope;
- s32 uvdet_offs;
- /* safe frequency we can use at minimum voltage */
- u32 safe_fmax_vmin;
- };
- #define gm20b_clk(p) container_of((gk20a_clk(p)), struct gm20b_clk, base)
- static u32 pl_to_div(u32 pl)
- {
- return pl;
- }
- static u32 div_to_pl(u32 div)
- {
- return div;
- }
- static const struct gk20a_clk_pllg_params gm20b_pllg_params = {
- .min_vco = 1300000, .max_vco = 2600000,
- .min_u = 12000, .max_u = 38400,
- .min_m = 1, .max_m = 255,
- .min_n = 8, .max_n = 255,
- .min_pl = 1, .max_pl = 31,
- };
- static void
- gm20b_pllg_read_mnp(struct gm20b_clk *clk, struct gm20b_pll *pll)
- {
- struct nvkm_subdev *subdev = &clk->base.base.subdev;
- struct nvkm_device *device = subdev->device;
- u32 val;
- gk20a_pllg_read_mnp(&clk->base, &pll->base);
- val = nvkm_rd32(device, GPCPLL_CFG2);
- pll->sdm_din = (val >> GPCPLL_CFG2_SDM_DIN_SHIFT) &
- MASK(GPCPLL_CFG2_SDM_DIN_WIDTH);
- }
- static void
- gm20b_pllg_write_mnp(struct gm20b_clk *clk, const struct gm20b_pll *pll)
- {
- struct nvkm_device *device = clk->base.base.subdev.device;
- nvkm_mask(device, GPCPLL_CFG2, GPCPLL_CFG2_SDM_DIN_MASK,
- pll->sdm_din << GPCPLL_CFG2_SDM_DIN_SHIFT);
- gk20a_pllg_write_mnp(&clk->base, &pll->base);
- }
- /*
- * Determine DFS_COEFF for the requested voltage. Always select external
- * calibration override equal to the voltage, and set maximum detection
- * limit "0" (to make sure that PLL output remains under F/V curve when
- * voltage increases).
- */
- static void
- gm20b_dvfs_calc_det_coeff(struct gm20b_clk *clk, s32 uv,
- struct gm20b_clk_dvfs *dvfs)
- {
- struct nvkm_subdev *subdev = &clk->base.base.subdev;
- const struct gm20b_clk_dvfs_params *p = clk->dvfs_params;
- u32 coeff;
- /* Work with mv as uv would likely trigger an overflow */
- s32 mv = DIV_ROUND_CLOSEST(uv, 1000);
- /* coeff = slope * voltage + offset */
- coeff = DIV_ROUND_CLOSEST(mv * p->coeff_slope, 1000) + p->coeff_offs;
- coeff = DIV_ROUND_CLOSEST(coeff, 1000);
- dvfs->dfs_coeff = min_t(u32, coeff, MASK(GPCPLL_DVFS0_DFS_COEFF_WIDTH));
- dvfs->dfs_ext_cal = DIV_ROUND_CLOSEST(uv - clk->uvdet_offs,
- clk->uvdet_slope);
- /* should never happen */
- if (abs(dvfs->dfs_ext_cal) >= BIT(DFS_DET_RANGE))
- nvkm_error(subdev, "dfs_ext_cal overflow!\n");
- dvfs->dfs_det_max = 0;
- nvkm_debug(subdev, "%s uv: %d coeff: %x, ext_cal: %d, det_max: %d\n",
- __func__, uv, dvfs->dfs_coeff, dvfs->dfs_ext_cal,
- dvfs->dfs_det_max);
- }
- /*
- * Solve equation for integer and fractional part of the effective NDIV:
- *
- * n_eff = n_int + 1/2 + (SDM_DIN / 2^(SDM_DIN_RANGE + 1)) +
- * (DVFS_COEFF * DVFS_DET_DELTA) / 2^DFS_DET_RANGE
- *
- * The SDM_DIN LSB is finally shifted out, since it is not accessible by sw.
- */
- static void
- gm20b_dvfs_calc_ndiv(struct gm20b_clk *clk, u32 n_eff, u32 *n_int, u32 *sdm_din)
- {
- struct nvkm_subdev *subdev = &clk->base.base.subdev;
- const struct gk20a_clk_pllg_params *p = clk->base.params;
- u32 n;
- s32 det_delta;
- u32 rem, rem_range;
- /* calculate current ext_cal and subtract previous one */
- det_delta = DIV_ROUND_CLOSEST(((s32)clk->uv) - clk->uvdet_offs,
- clk->uvdet_slope);
- det_delta -= clk->dvfs.dfs_ext_cal;
- det_delta = min(det_delta, clk->dvfs.dfs_det_max);
- det_delta *= clk->dvfs.dfs_coeff;
- /* integer part of n */
- n = (n_eff << DFS_DET_RANGE) - det_delta;
- /* should never happen! */
- if (n <= 0) {
- nvkm_error(subdev, "ndiv <= 0 - setting to 1...\n");
- n = 1 << DFS_DET_RANGE;
- }
- if (n >> DFS_DET_RANGE > p->max_n) {
- nvkm_error(subdev, "ndiv > max_n - setting to max_n...\n");
- n = p->max_n << DFS_DET_RANGE;
- }
- *n_int = n >> DFS_DET_RANGE;
- /* fractional part of n */
- rem = ((u32)n) & MASK(DFS_DET_RANGE);
- rem_range = SDM_DIN_RANGE + 1 - DFS_DET_RANGE;
- /* subtract 2^SDM_DIN_RANGE to account for the 1/2 of the equation */
- rem = (rem << rem_range) - BIT(SDM_DIN_RANGE);
- /* lose 8 LSB and clip - sdm_din only keeps the most significant byte */
- *sdm_din = (rem >> BITS_PER_BYTE) & MASK(GPCPLL_CFG2_SDM_DIN_WIDTH);
- nvkm_debug(subdev, "%s n_eff: %d, n_int: %d, sdm_din: %d\n", __func__,
- n_eff, *n_int, *sdm_din);
- }
- static int
- gm20b_pllg_slide(struct gm20b_clk *clk, u32 n)
- {
- struct nvkm_subdev *subdev = &clk->base.base.subdev;
- struct nvkm_device *device = subdev->device;
- struct gm20b_pll pll;
- u32 n_int, sdm_din;
- int ret = 0;
- /* calculate the new n_int/sdm_din for this n/uv */
- gm20b_dvfs_calc_ndiv(clk, n, &n_int, &sdm_din);
- /* get old coefficients */
- gm20b_pllg_read_mnp(clk, &pll);
- /* do nothing if NDIV is the same */
- if (n_int == pll.base.n && sdm_din == pll.sdm_din)
- return 0;
- /* pll slowdown mode */
- nvkm_mask(device, GPCPLL_NDIV_SLOWDOWN,
- BIT(GPCPLL_NDIV_SLOWDOWN_SLOWDOWN_USING_PLL_SHIFT),
- BIT(GPCPLL_NDIV_SLOWDOWN_SLOWDOWN_USING_PLL_SHIFT));
- /* new ndiv ready for ramp */
- /* in DVFS mode SDM is updated via "new" field */
- nvkm_mask(device, GPCPLL_CFG2, GPCPLL_CFG2_SDM_DIN_NEW_MASK,
- sdm_din << GPCPLL_CFG2_SDM_DIN_NEW_SHIFT);
- pll.base.n = n_int;
- udelay(1);
- gk20a_pllg_write_mnp(&clk->base, &pll.base);
- /* dynamic ramp to new ndiv */
- udelay(1);
- nvkm_mask(device, GPCPLL_NDIV_SLOWDOWN,
- BIT(GPCPLL_NDIV_SLOWDOWN_EN_DYNRAMP_SHIFT),
- BIT(GPCPLL_NDIV_SLOWDOWN_EN_DYNRAMP_SHIFT));
- /* wait for ramping to complete */
- if (nvkm_wait_usec(device, 500, GPC_BCAST_NDIV_SLOWDOWN_DEBUG,
- GPC_BCAST_NDIV_SLOWDOWN_DEBUG_PLL_DYNRAMP_DONE_SYNCED_MASK,
- GPC_BCAST_NDIV_SLOWDOWN_DEBUG_PLL_DYNRAMP_DONE_SYNCED_MASK) < 0)
- ret = -ETIMEDOUT;
- /* in DVFS mode complete SDM update */
- nvkm_mask(device, GPCPLL_CFG2, GPCPLL_CFG2_SDM_DIN_MASK,
- sdm_din << GPCPLL_CFG2_SDM_DIN_SHIFT);
- /* exit slowdown mode */
- nvkm_mask(device, GPCPLL_NDIV_SLOWDOWN,
- BIT(GPCPLL_NDIV_SLOWDOWN_SLOWDOWN_USING_PLL_SHIFT) |
- BIT(GPCPLL_NDIV_SLOWDOWN_EN_DYNRAMP_SHIFT), 0);
- nvkm_rd32(device, GPCPLL_NDIV_SLOWDOWN);
- return ret;
- }
- static int
- gm20b_pllg_enable(struct gm20b_clk *clk)
- {
- struct nvkm_device *device = clk->base.base.subdev.device;
- nvkm_mask(device, GPCPLL_CFG, GPCPLL_CFG_ENABLE, GPCPLL_CFG_ENABLE);
- nvkm_rd32(device, GPCPLL_CFG);
- /* In DVFS mode lock cannot be used - so just delay */
- udelay(40);
- /* set SYNC_MODE for glitchless switch out of bypass */
- nvkm_mask(device, GPCPLL_CFG, GPCPLL_CFG_SYNC_MODE,
- GPCPLL_CFG_SYNC_MODE);
- nvkm_rd32(device, GPCPLL_CFG);
- /* switch to VCO mode */
- nvkm_mask(device, SEL_VCO, BIT(SEL_VCO_GPC2CLK_OUT_SHIFT),
- BIT(SEL_VCO_GPC2CLK_OUT_SHIFT));
- return 0;
- }
- static void
- gm20b_pllg_disable(struct gm20b_clk *clk)
- {
- struct nvkm_device *device = clk->base.base.subdev.device;
- /* put PLL in bypass before disabling it */
- nvkm_mask(device, SEL_VCO, BIT(SEL_VCO_GPC2CLK_OUT_SHIFT), 0);
- /* clear SYNC_MODE before disabling PLL */
- nvkm_mask(device, GPCPLL_CFG, GPCPLL_CFG_SYNC_MODE, 0);
- nvkm_mask(device, GPCPLL_CFG, GPCPLL_CFG_ENABLE, 0);
- nvkm_rd32(device, GPCPLL_CFG);
- }
- static int
- gm20b_pllg_program_mnp(struct gm20b_clk *clk, const struct gk20a_pll *pll)
- {
- struct nvkm_subdev *subdev = &clk->base.base.subdev;
- struct nvkm_device *device = subdev->device;
- struct gm20b_pll cur_pll;
- u32 n_int, sdm_din;
- /* if we only change pdiv, we can do a glitchless transition */
- bool pdiv_only;
- int ret;
- gm20b_dvfs_calc_ndiv(clk, pll->n, &n_int, &sdm_din);
- gm20b_pllg_read_mnp(clk, &cur_pll);
- pdiv_only = cur_pll.base.n == n_int && cur_pll.sdm_din == sdm_din &&
- cur_pll.base.m == pll->m;
- /* need full sequence if clock not enabled yet */
- if (!gk20a_pllg_is_enabled(&clk->base))
- pdiv_only = false;
- /* split VCO-to-bypass jump in half by setting out divider 1:2 */
- nvkm_mask(device, GPC2CLK_OUT, GPC2CLK_OUT_VCODIV_MASK,
- GPC2CLK_OUT_VCODIV2 << GPC2CLK_OUT_VCODIV_SHIFT);
- /* Intentional 2nd write to assure linear divider operation */
- nvkm_mask(device, GPC2CLK_OUT, GPC2CLK_OUT_VCODIV_MASK,
- GPC2CLK_OUT_VCODIV2 << GPC2CLK_OUT_VCODIV_SHIFT);
- nvkm_rd32(device, GPC2CLK_OUT);
- udelay(2);
- if (pdiv_only) {
- u32 old = cur_pll.base.pl;
- u32 new = pll->pl;
- /*
- * we can do a glitchless transition only if the old and new PL
- * parameters share at least one bit set to 1. If this is not
- * the case, calculate and program an interim PL that will allow
- * us to respect that rule.
- */
- if ((old & new) == 0) {
- cur_pll.base.pl = min(old | BIT(ffs(new) - 1),
- new | BIT(ffs(old) - 1));
- gk20a_pllg_write_mnp(&clk->base, &cur_pll.base);
- }
- cur_pll.base.pl = new;
- gk20a_pllg_write_mnp(&clk->base, &cur_pll.base);
- } else {
- /* disable before programming if more than pdiv changes */
- gm20b_pllg_disable(clk);
- cur_pll.base = *pll;
- cur_pll.base.n = n_int;
- cur_pll.sdm_din = sdm_din;
- gm20b_pllg_write_mnp(clk, &cur_pll);
- ret = gm20b_pllg_enable(clk);
- if (ret)
- return ret;
- }
- /* restore out divider 1:1 */
- udelay(2);
- nvkm_mask(device, GPC2CLK_OUT, GPC2CLK_OUT_VCODIV_MASK,
- GPC2CLK_OUT_VCODIV1 << GPC2CLK_OUT_VCODIV_SHIFT);
- /* Intentional 2nd write to assure linear divider operation */
- nvkm_mask(device, GPC2CLK_OUT, GPC2CLK_OUT_VCODIV_MASK,
- GPC2CLK_OUT_VCODIV1 << GPC2CLK_OUT_VCODIV_SHIFT);
- nvkm_rd32(device, GPC2CLK_OUT);
- return 0;
- }
- static int
- gm20b_pllg_program_mnp_slide(struct gm20b_clk *clk, const struct gk20a_pll *pll)
- {
- struct gk20a_pll cur_pll;
- int ret;
- if (gk20a_pllg_is_enabled(&clk->base)) {
- gk20a_pllg_read_mnp(&clk->base, &cur_pll);
- /* just do NDIV slide if there is no change to M and PL */
- if (pll->m == cur_pll.m && pll->pl == cur_pll.pl)
- return gm20b_pllg_slide(clk, pll->n);
- /* slide down to current NDIV_LO */
- cur_pll.n = gk20a_pllg_n_lo(&clk->base, &cur_pll);
- ret = gm20b_pllg_slide(clk, cur_pll.n);
- if (ret)
- return ret;
- }
- /* 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);
- if (ret)
- return ret;
- /* slide up to new NDIV */
- return gm20b_pllg_slide(clk, pll->n);
- }
- static int
- gm20b_clk_calc(struct nvkm_clk *base, struct nvkm_cstate *cstate)
- {
- struct gm20b_clk *clk = gm20b_clk(base);
- struct nvkm_subdev *subdev = &base->subdev;
- struct nvkm_volt *volt = base->subdev.device->volt;
- int ret;
- ret = gk20a_pllg_calc_mnp(&clk->base, cstate->domain[nv_clk_src_gpc] *
- GK20A_CLK_GPC_MDIV, &clk->new_pll);
- if (ret)
- return ret;
- clk->new_uv = volt->vid[cstate->voltage].uv;
- gm20b_dvfs_calc_det_coeff(clk, clk->new_uv, &clk->new_dvfs);
- nvkm_debug(subdev, "%s uv: %d uv\n", __func__, clk->new_uv);
- return 0;
- }
- /*
- * Compute PLL parameters that are always safe for the current voltage
- */
- static void
- gm20b_dvfs_calc_safe_pll(struct gm20b_clk *clk, struct gk20a_pll *pll)
- {
- u32 rate = gk20a_pllg_calc_rate(&clk->base, pll) / KHZ;
- u32 parent_rate = clk->base.parent_rate / KHZ;
- u32 nmin, nsafe;
- /* remove a safe margin of 10% */
- if (rate > clk->safe_fmax_vmin)
- rate = rate * (100 - 10) / 100;
- /* gpc2clk */
- rate *= 2;
- nmin = DIV_ROUND_UP(pll->m * clk->base.params->min_vco, parent_rate);
- nsafe = pll->m * rate / (clk->base.parent_rate);
- if (nsafe < nmin) {
- pll->pl = DIV_ROUND_UP(nmin * parent_rate, pll->m * rate);
- nsafe = nmin;
- }
- pll->n = nsafe;
- }
- static void
- gm20b_dvfs_program_coeff(struct gm20b_clk *clk, u32 coeff)
- {
- struct nvkm_device *device = clk->base.base.subdev.device;
- /* strobe to read external DFS coefficient */
- nvkm_mask(device, GPC_BCAST_GPCPLL_DVFS2,
- GPC_BCAST_GPCPLL_DVFS2_DFS_EXT_STROBE_BIT,
- GPC_BCAST_GPCPLL_DVFS2_DFS_EXT_STROBE_BIT);
- nvkm_mask(device, GPCPLL_DVFS0, GPCPLL_DVFS0_DFS_COEFF_MASK,
- coeff << GPCPLL_DVFS0_DFS_COEFF_SHIFT);
- udelay(1);
- nvkm_mask(device, GPC_BCAST_GPCPLL_DVFS2,
- GPC_BCAST_GPCPLL_DVFS2_DFS_EXT_STROBE_BIT, 0);
- }
- static void
- gm20b_dvfs_program_ext_cal(struct gm20b_clk *clk, u32 dfs_det_cal)
- {
- struct nvkm_device *device = clk->base.base.subdev.device;
- u32 val;
- nvkm_mask(device, GPC_BCAST_GPCPLL_DVFS2, MASK(DFS_DET_RANGE + 1),
- dfs_det_cal);
- udelay(1);
- val = nvkm_rd32(device, GPCPLL_DVFS1);
- if (!(val & BIT(25))) {
- /* Use external value to overwrite calibration value */
- val |= BIT(25) | BIT(16);
- nvkm_wr32(device, GPCPLL_DVFS1, val);
- }
- }
- static void
- gm20b_dvfs_program_dfs_detection(struct gm20b_clk *clk,
- struct gm20b_clk_dvfs *dvfs)
- {
- struct nvkm_device *device = clk->base.base.subdev.device;
- /* strobe to read external DFS coefficient */
- nvkm_mask(device, GPC_BCAST_GPCPLL_DVFS2,
- GPC_BCAST_GPCPLL_DVFS2_DFS_EXT_STROBE_BIT,
- GPC_BCAST_GPCPLL_DVFS2_DFS_EXT_STROBE_BIT);
- nvkm_mask(device, GPCPLL_DVFS0,
- GPCPLL_DVFS0_DFS_COEFF_MASK | GPCPLL_DVFS0_DFS_DET_MAX_MASK,
- dvfs->dfs_coeff << GPCPLL_DVFS0_DFS_COEFF_SHIFT |
- dvfs->dfs_det_max << GPCPLL_DVFS0_DFS_DET_MAX_SHIFT);
- udelay(1);
- nvkm_mask(device, GPC_BCAST_GPCPLL_DVFS2,
- GPC_BCAST_GPCPLL_DVFS2_DFS_EXT_STROBE_BIT, 0);
- gm20b_dvfs_program_ext_cal(clk, dvfs->dfs_ext_cal);
- }
- static int
- gm20b_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 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);
- 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);
- }
- static struct nvkm_pstate
- gm20b_pstates[] = {
- {
- .base = {
- .domain[nv_clk_src_gpc] = 76800,
- .voltage = 0,
- },
- },
- {
- .base = {
- .domain[nv_clk_src_gpc] = 153600,
- .voltage = 1,
- },
- },
- {
- .base = {
- .domain[nv_clk_src_gpc] = 230400,
- .voltage = 2,
- },
- },
- {
- .base = {
- .domain[nv_clk_src_gpc] = 307200,
- .voltage = 3,
- },
- },
- {
- .base = {
- .domain[nv_clk_src_gpc] = 384000,
- .voltage = 4,
- },
- },
- {
- .base = {
- .domain[nv_clk_src_gpc] = 460800,
- .voltage = 5,
- },
- },
- {
- .base = {
- .domain[nv_clk_src_gpc] = 537600,
- .voltage = 6,
- },
- },
- {
- .base = {
- .domain[nv_clk_src_gpc] = 614400,
- .voltage = 7,
- },
- },
- {
- .base = {
- .domain[nv_clk_src_gpc] = 691200,
- .voltage = 8,
- },
- },
- {
- .base = {
- .domain[nv_clk_src_gpc] = 768000,
- .voltage = 9,
- },
- },
- {
- .base = {
- .domain[nv_clk_src_gpc] = 844800,
- .voltage = 10,
- },
- },
- {
- .base = {
- .domain[nv_clk_src_gpc] = 921600,
- .voltage = 11,
- },
- },
- {
- .base = {
- .domain[nv_clk_src_gpc] = 998400,
- .voltage = 12,
- },
- },
- };
- static void
- gm20b_clk_fini(struct nvkm_clk *base)
- {
- struct nvkm_device *device = base->subdev.device;
- struct gm20b_clk *clk = gm20b_clk(base);
- /* slide to VCO min */
- if (gk20a_pllg_is_enabled(&clk->base)) {
- struct gk20a_pll pll;
- u32 n_lo;
- gk20a_pllg_read_mnp(&clk->base, &pll);
- n_lo = gk20a_pllg_n_lo(&clk->base, &pll);
- gm20b_pllg_slide(clk, n_lo);
- }
- gm20b_pllg_disable(clk);
- /* set IDDQ */
- nvkm_mask(device, GPCPLL_CFG, GPCPLL_CFG_IDDQ, 1);
- }
- static int
- gm20b_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);
- /* 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);
- 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 int
- gm20b_clk_init(struct nvkm_clk *base)
- {
- struct gk20a_clk *clk = gk20a_clk(base);
- struct nvkm_subdev *subdev = &clk->base.subdev;
- struct nvkm_device *device = subdev->device;
- int ret;
- u32 data;
- /* get out from IDDQ */
- nvkm_mask(device, GPCPLL_CFG, GPCPLL_CFG_IDDQ, 0);
- nvkm_rd32(device, GPCPLL_CFG);
- udelay(5);
- nvkm_mask(device, GPC2CLK_OUT, GPC2CLK_OUT_INIT_MASK,
- GPC2CLK_OUT_INIT_VAL);
- /* Set the global bypass control to VCO */
- nvkm_mask(device, BYPASSCTRL_SYS,
- MASK(BYPASSCTRL_SYS_GPCPLL_WIDTH) << BYPASSCTRL_SYS_GPCPLL_SHIFT,
- 0);
- ret = gk20a_clk_setup_slide(clk);
- if (ret)
- return ret;
- /* If not fused, set RAM SVOP PDP data 0x2, and enable fuse override */
- data = nvkm_rd32(device, 0x021944);
- if (!(data & 0x3)) {
- data |= 0x2;
- nvkm_wr32(device, 0x021944, data);
- data = nvkm_rd32(device, 0x021948);
- data |= 0x1;
- nvkm_wr32(device, 0x021948, data);
- }
- /* Disable idle slow down */
- nvkm_mask(device, 0x20160, 0x003f0000, 0x0);
- /* speedo >= 1? */
- if (clk->base.func == &gm20b_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_clk_init_dvfs(_clk);
- if (ret)
- return ret;
- }
- /* Start with lowest frequency */
- base->func->calc(base, &base->func->pstates[0].base);
- ret = base->func->prog(base);
- if (ret) {
- nvkm_error(subdev, "cannot initialize clock\n");
- return ret;
- }
- return 0;
- }
- static const struct nvkm_clk_func
- gm20b_clk_speedo0 = {
- .init = gm20b_clk_init,
- .fini = gk20a_clk_fini,
- .read = gk20a_clk_read,
- .calc = gk20a_clk_calc,
- .prog = gk20a_clk_prog,
- .tidy = gk20a_clk_tidy,
- .pstates = gm20b_pstates,
- /* Speedo 0 only supports 12 voltages */
- .nr_pstates = ARRAY_SIZE(gm20b_pstates) - 1,
- .domains = {
- { nv_clk_src_crystal, 0xff },
- { nv_clk_src_gpc, 0xff, 0, "core", GK20A_CLK_GPC_MDIV },
- { nv_clk_src_max },
- },
- };
- static const struct nvkm_clk_func
- gm20b_clk = {
- .init = gm20b_clk_init,
- .fini = gm20b_clk_fini,
- .read = gk20a_clk_read,
- .calc = gm20b_clk_calc,
- .prog = gm20b_clk_prog,
- .tidy = gk20a_clk_tidy,
- .pstates = gm20b_pstates,
- .nr_pstates = ARRAY_SIZE(gm20b_pstates),
- .domains = {
- { nv_clk_src_crystal, 0xff },
- { nv_clk_src_gpc, 0xff, 0, "core", GK20A_CLK_GPC_MDIV },
- { nv_clk_src_max },
- },
- };
- static int
- gm20b_clk_new_speedo0(struct nvkm_device *device, int index,
- struct nvkm_clk **pclk)
- {
- struct gk20a_clk *clk;
- int ret;
- clk = kzalloc(sizeof(*clk), GFP_KERNEL);
- if (!clk)
- return -ENOMEM;
- *pclk = &clk->base;
- ret = gk20a_clk_ctor(device, index, &gm20b_clk_speedo0,
- &gm20b_pllg_params, clk);
- clk->pl_to_div = pl_to_div;
- clk->div_to_pl = div_to_pl;
- return ret;
- }
- /* FUSE register */
- #define FUSE_RESERVED_CALIB0 0x204
- #define FUSE_RESERVED_CALIB0_INTERCEPT_FRAC_SHIFT 0
- #define FUSE_RESERVED_CALIB0_INTERCEPT_FRAC_WIDTH 4
- #define FUSE_RESERVED_CALIB0_INTERCEPT_INT_SHIFT 4
- #define FUSE_RESERVED_CALIB0_INTERCEPT_INT_WIDTH 10
- #define FUSE_RESERVED_CALIB0_SLOPE_FRAC_SHIFT 14
- #define FUSE_RESERVED_CALIB0_SLOPE_FRAC_WIDTH 10
- #define FUSE_RESERVED_CALIB0_SLOPE_INT_SHIFT 24
- #define FUSE_RESERVED_CALIB0_SLOPE_INT_WIDTH 6
- #define FUSE_RESERVED_CALIB0_FUSE_REV_SHIFT 30
- #define FUSE_RESERVED_CALIB0_FUSE_REV_WIDTH 2
- static int
- gm20b_clk_init_fused_params(struct gm20b_clk *clk)
- {
- struct nvkm_subdev *subdev = &clk->base.base.subdev;
- u32 val = 0;
- u32 rev = 0;
- #if IS_ENABLED(CONFIG_ARCH_TEGRA)
- tegra_fuse_readl(FUSE_RESERVED_CALIB0, &val);
- rev = (val >> FUSE_RESERVED_CALIB0_FUSE_REV_SHIFT) &
- MASK(FUSE_RESERVED_CALIB0_FUSE_REV_WIDTH);
- #endif
- /* No fused parameters, we will calibrate later */
- if (rev == 0)
- return -EINVAL;
- /* Integer part in mV + fractional part in uV */
- clk->uvdet_slope = ((val >> FUSE_RESERVED_CALIB0_SLOPE_INT_SHIFT) &
- MASK(FUSE_RESERVED_CALIB0_SLOPE_INT_WIDTH)) * 1000 +
- ((val >> FUSE_RESERVED_CALIB0_SLOPE_FRAC_SHIFT) &
- MASK(FUSE_RESERVED_CALIB0_SLOPE_FRAC_WIDTH));
- /* Integer part in mV + fractional part in 100uV */
- clk->uvdet_offs = ((val >> FUSE_RESERVED_CALIB0_INTERCEPT_INT_SHIFT) &
- MASK(FUSE_RESERVED_CALIB0_INTERCEPT_INT_WIDTH)) * 1000 +
- ((val >> FUSE_RESERVED_CALIB0_INTERCEPT_FRAC_SHIFT) &
- MASK(FUSE_RESERVED_CALIB0_INTERCEPT_FRAC_WIDTH)) * 100;
- nvkm_debug(subdev, "fused calibration data: slope %d, offs %d\n",
- clk->uvdet_slope, clk->uvdet_offs);
- return 0;
- }
- static int
- gm20b_clk_init_safe_fmax(struct gm20b_clk *clk)
- {
- struct nvkm_subdev *subdev = &clk->base.base.subdev;
- struct nvkm_volt *volt = subdev->device->volt;
- struct nvkm_pstate *pstates = clk->base.base.func->pstates;
- int nr_pstates = clk->base.base.func->nr_pstates;
- int vmin, id = 0;
- u32 fmax = 0;
- int i;
- /* find lowest voltage we can use */
- vmin = volt->vid[0].uv;
- for (i = 1; i < volt->vid_nr; i++) {
- if (volt->vid[i].uv <= vmin) {
- vmin = volt->vid[i].uv;
- id = volt->vid[i].vid;
- }
- }
- /* find max frequency at this voltage */
- for (i = 0; i < nr_pstates; i++)
- if (pstates[i].base.voltage == id)
- fmax = max(fmax,
- pstates[i].base.domain[nv_clk_src_gpc]);
- if (!fmax) {
- nvkm_error(subdev, "failed to evaluate safe fmax\n");
- return -EINVAL;
- }
- /* we are safe at 90% of the max frequency */
- clk->safe_fmax_vmin = fmax * (100 - 10) / 100;
- nvkm_debug(subdev, "safe fmax @ vmin = %u Khz\n", clk->safe_fmax_vmin);
- return 0;
- }
- int
- gm20b_clk_new(struct nvkm_device *device, int index, struct nvkm_clk **pclk)
- {
- struct nvkm_device_tegra *tdev = device->func->tegra(device);
- struct gm20b_clk *clk;
- struct nvkm_subdev *subdev;
- struct gk20a_clk_pllg_params *clk_params;
- int ret;
- /* Speedo 0 GPUs cannot use noise-aware PLL */
- if (tdev->gpu_speedo_id == 0)
- return gm20b_clk_new_speedo0(device, index, pclk);
- /* Speedo >= 1, use NAPLL */
- clk = kzalloc(sizeof(*clk) + sizeof(*clk_params), GFP_KERNEL);
- if (!clk)
- return -ENOMEM;
- *pclk = &clk->base.base;
- subdev = &clk->base.base.subdev;
- /* duplicate the clock parameters since we will patch them below */
- clk_params = (void *) (clk + 1);
- *clk_params = gm20b_pllg_params;
- ret = gk20a_clk_ctor(device, index, &gm20b_clk, clk_params,
- &clk->base);
- if (ret)
- return ret;
- /*
- * NAPLL can only work with max_u, clamp the m range so
- * gk20a_pllg_calc_mnp always uses it
- */
- clk_params->max_m = clk_params->min_m = DIV_ROUND_UP(clk_params->max_u,
- (clk->base.parent_rate / KHZ));
- if (clk_params->max_m == 0) {
- nvkm_warn(subdev, "cannot use NAPLL, using legacy clock...\n");
- kfree(clk);
- return gm20b_clk_new_speedo0(device, index, pclk);
- }
- clk->base.pl_to_div = pl_to_div;
- clk->base.div_to_pl = div_to_pl;
- clk->dvfs_params = &gm20b_dvfs_params;
- ret = gm20b_clk_init_fused_params(clk);
- /*
- * we will calibrate during init - should never happen on
- * prod parts
- */
- if (ret)
- nvkm_warn(subdev, "no fused calibration parameters\n");
- ret = gm20b_clk_init_safe_fmax(clk);
- if (ret)
- return ret;
- return 0;
- }
|