152829d4fSAlexandre Courbot /*
252829d4fSAlexandre Courbot * Copyright (c) 2016, NVIDIA CORPORATION. All rights reserved.
352829d4fSAlexandre Courbot *
452829d4fSAlexandre Courbot * Permission is hereby granted, free of charge, to any person obtaining a
552829d4fSAlexandre Courbot * copy of this software and associated documentation files (the "Software"),
652829d4fSAlexandre Courbot * to deal in the Software without restriction, including without limitation
752829d4fSAlexandre Courbot * the rights to use, copy, modify, merge, publish, distribute, sublicense,
852829d4fSAlexandre Courbot * and/or sell copies of the Software, and to permit persons to whom the
952829d4fSAlexandre Courbot * Software is furnished to do so, subject to the following conditions:
1052829d4fSAlexandre Courbot *
1152829d4fSAlexandre Courbot * The above copyright notice and this permission notice shall be included in
1252829d4fSAlexandre Courbot * all copies or substantial portions of the Software.
1352829d4fSAlexandre Courbot *
1452829d4fSAlexandre Courbot * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
1552829d4fSAlexandre Courbot * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
1652829d4fSAlexandre Courbot * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
1752829d4fSAlexandre Courbot * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
1852829d4fSAlexandre Courbot * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
1952829d4fSAlexandre Courbot * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
2052829d4fSAlexandre Courbot * DEALINGS IN THE SOFTWARE.
2152829d4fSAlexandre Courbot */
2252829d4fSAlexandre Courbot
2352829d4fSAlexandre Courbot #include <subdev/clk.h>
2422b6c9e8SAlexandre Courbot #include <subdev/volt.h>
2522b6c9e8SAlexandre Courbot #include <subdev/timer.h>
2652829d4fSAlexandre Courbot #include <core/device.h>
2722b6c9e8SAlexandre Courbot #include <core/tegra.h>
2852829d4fSAlexandre Courbot
2952829d4fSAlexandre Courbot #include "priv.h"
3052829d4fSAlexandre Courbot #include "gk20a.h"
3152829d4fSAlexandre Courbot
3222b6c9e8SAlexandre Courbot #define GPCPLL_CFG_SYNC_MODE BIT(2)
3352829d4fSAlexandre Courbot
3452829d4fSAlexandre Courbot #define BYPASSCTRL_SYS (SYS_GPCPLL_CFG_BASE + 0x340)
3552829d4fSAlexandre Courbot #define BYPASSCTRL_SYS_GPCPLL_SHIFT 0
3652829d4fSAlexandre Courbot #define BYPASSCTRL_SYS_GPCPLL_WIDTH 1
3752829d4fSAlexandre Courbot
3822b6c9e8SAlexandre Courbot #define GPCPLL_CFG2_SDM_DIN_SHIFT 0
3922b6c9e8SAlexandre Courbot #define GPCPLL_CFG2_SDM_DIN_WIDTH 8
4022b6c9e8SAlexandre Courbot #define GPCPLL_CFG2_SDM_DIN_MASK \
4122b6c9e8SAlexandre Courbot (MASK(GPCPLL_CFG2_SDM_DIN_WIDTH) << GPCPLL_CFG2_SDM_DIN_SHIFT)
4222b6c9e8SAlexandre Courbot #define GPCPLL_CFG2_SDM_DIN_NEW_SHIFT 8
4322b6c9e8SAlexandre Courbot #define GPCPLL_CFG2_SDM_DIN_NEW_WIDTH 15
4422b6c9e8SAlexandre Courbot #define GPCPLL_CFG2_SDM_DIN_NEW_MASK \
4522b6c9e8SAlexandre Courbot (MASK(GPCPLL_CFG2_SDM_DIN_NEW_WIDTH) << GPCPLL_CFG2_SDM_DIN_NEW_SHIFT)
4622b6c9e8SAlexandre Courbot #define GPCPLL_CFG2_SETUP2_SHIFT 16
4722b6c9e8SAlexandre Courbot #define GPCPLL_CFG2_PLL_STEPA_SHIFT 24
4822b6c9e8SAlexandre Courbot
4922b6c9e8SAlexandre Courbot #define GPCPLL_DVFS0 (SYS_GPCPLL_CFG_BASE + 0x10)
5022b6c9e8SAlexandre Courbot #define GPCPLL_DVFS0_DFS_COEFF_SHIFT 0
5122b6c9e8SAlexandre Courbot #define GPCPLL_DVFS0_DFS_COEFF_WIDTH 7
5222b6c9e8SAlexandre Courbot #define GPCPLL_DVFS0_DFS_COEFF_MASK \
5322b6c9e8SAlexandre Courbot (MASK(GPCPLL_DVFS0_DFS_COEFF_WIDTH) << GPCPLL_DVFS0_DFS_COEFF_SHIFT)
5422b6c9e8SAlexandre Courbot #define GPCPLL_DVFS0_DFS_DET_MAX_SHIFT 8
5522b6c9e8SAlexandre Courbot #define GPCPLL_DVFS0_DFS_DET_MAX_WIDTH 7
5622b6c9e8SAlexandre Courbot #define GPCPLL_DVFS0_DFS_DET_MAX_MASK \
5722b6c9e8SAlexandre Courbot (MASK(GPCPLL_DVFS0_DFS_DET_MAX_WIDTH) << GPCPLL_DVFS0_DFS_DET_MAX_SHIFT)
5822b6c9e8SAlexandre Courbot
5922b6c9e8SAlexandre Courbot #define GPCPLL_DVFS1 (SYS_GPCPLL_CFG_BASE + 0x14)
6022b6c9e8SAlexandre Courbot #define GPCPLL_DVFS1_DFS_EXT_DET_SHIFT 0
6122b6c9e8SAlexandre Courbot #define GPCPLL_DVFS1_DFS_EXT_DET_WIDTH 7
6222b6c9e8SAlexandre Courbot #define GPCPLL_DVFS1_DFS_EXT_STRB_SHIFT 7
6322b6c9e8SAlexandre Courbot #define GPCPLL_DVFS1_DFS_EXT_STRB_WIDTH 1
6422b6c9e8SAlexandre Courbot #define GPCPLL_DVFS1_DFS_EXT_CAL_SHIFT 8
6522b6c9e8SAlexandre Courbot #define GPCPLL_DVFS1_DFS_EXT_CAL_WIDTH 7
6622b6c9e8SAlexandre Courbot #define GPCPLL_DVFS1_DFS_EXT_SEL_SHIFT 15
6722b6c9e8SAlexandre Courbot #define GPCPLL_DVFS1_DFS_EXT_SEL_WIDTH 1
6822b6c9e8SAlexandre Courbot #define GPCPLL_DVFS1_DFS_CTRL_SHIFT 16
6922b6c9e8SAlexandre Courbot #define GPCPLL_DVFS1_DFS_CTRL_WIDTH 12
7022b6c9e8SAlexandre Courbot #define GPCPLL_DVFS1_EN_SDM_SHIFT 28
7122b6c9e8SAlexandre Courbot #define GPCPLL_DVFS1_EN_SDM_WIDTH 1
7222b6c9e8SAlexandre Courbot #define GPCPLL_DVFS1_EN_SDM_BIT BIT(28)
7322b6c9e8SAlexandre Courbot #define GPCPLL_DVFS1_EN_DFS_SHIFT 29
7422b6c9e8SAlexandre Courbot #define GPCPLL_DVFS1_EN_DFS_WIDTH 1
7522b6c9e8SAlexandre Courbot #define GPCPLL_DVFS1_EN_DFS_BIT BIT(29)
7622b6c9e8SAlexandre Courbot #define GPCPLL_DVFS1_EN_DFS_CAL_SHIFT 30
7722b6c9e8SAlexandre Courbot #define GPCPLL_DVFS1_EN_DFS_CAL_WIDTH 1
7822b6c9e8SAlexandre Courbot #define GPCPLL_DVFS1_EN_DFS_CAL_BIT BIT(30)
7922b6c9e8SAlexandre Courbot #define GPCPLL_DVFS1_DFS_CAL_DONE_SHIFT 31
8022b6c9e8SAlexandre Courbot #define GPCPLL_DVFS1_DFS_CAL_DONE_WIDTH 1
8122b6c9e8SAlexandre Courbot #define GPCPLL_DVFS1_DFS_CAL_DONE_BIT BIT(31)
8222b6c9e8SAlexandre Courbot
8322b6c9e8SAlexandre Courbot #define GPC_BCAST_GPCPLL_DVFS2 (GPC_BCAST_GPCPLL_CFG_BASE + 0x20)
8422b6c9e8SAlexandre Courbot #define GPC_BCAST_GPCPLL_DVFS2_DFS_EXT_STROBE_BIT BIT(16)
8522b6c9e8SAlexandre Courbot
8622b6c9e8SAlexandre Courbot #define GPCPLL_CFG3_PLL_DFS_TESTOUT_SHIFT 24
8722b6c9e8SAlexandre Courbot #define GPCPLL_CFG3_PLL_DFS_TESTOUT_WIDTH 7
8822b6c9e8SAlexandre Courbot
8922b6c9e8SAlexandre Courbot #define DFS_DET_RANGE 6 /* -2^6 ... 2^6-1 */
9022b6c9e8SAlexandre Courbot #define SDM_DIN_RANGE 12 /* -2^12 ... 2^12-1 */
9122b6c9e8SAlexandre Courbot
9222b6c9e8SAlexandre Courbot struct gm20b_clk_dvfs_params {
9322b6c9e8SAlexandre Courbot s32 coeff_slope;
9422b6c9e8SAlexandre Courbot s32 coeff_offs;
9522b6c9e8SAlexandre Courbot u32 vco_ctrl;
9622b6c9e8SAlexandre Courbot };
9722b6c9e8SAlexandre Courbot
9822b6c9e8SAlexandre Courbot static const struct gm20b_clk_dvfs_params gm20b_dvfs_params = {
9922b6c9e8SAlexandre Courbot .coeff_slope = -165230,
10022b6c9e8SAlexandre Courbot .coeff_offs = 214007,
10122b6c9e8SAlexandre Courbot .vco_ctrl = 0x7 << 3,
10222b6c9e8SAlexandre Courbot };
10322b6c9e8SAlexandre Courbot
10422b6c9e8SAlexandre Courbot /*
10522b6c9e8SAlexandre Courbot * base.n is now the *integer* part of the N factor.
10622b6c9e8SAlexandre Courbot * sdm_din contains n's decimal part.
10722b6c9e8SAlexandre Courbot */
10822b6c9e8SAlexandre Courbot struct gm20b_pll {
10922b6c9e8SAlexandre Courbot struct gk20a_pll base;
11022b6c9e8SAlexandre Courbot u32 sdm_din;
11122b6c9e8SAlexandre Courbot };
11222b6c9e8SAlexandre Courbot
11322b6c9e8SAlexandre Courbot struct gm20b_clk_dvfs {
11422b6c9e8SAlexandre Courbot u32 dfs_coeff;
11522b6c9e8SAlexandre Courbot s32 dfs_det_max;
11622b6c9e8SAlexandre Courbot s32 dfs_ext_cal;
11722b6c9e8SAlexandre Courbot };
11822b6c9e8SAlexandre Courbot
11922b6c9e8SAlexandre Courbot struct gm20b_clk {
12022b6c9e8SAlexandre Courbot /* currently applied parameters */
12122b6c9e8SAlexandre Courbot struct gk20a_clk base;
12222b6c9e8SAlexandre Courbot struct gm20b_clk_dvfs dvfs;
12322b6c9e8SAlexandre Courbot u32 uv;
12422b6c9e8SAlexandre Courbot
12522b6c9e8SAlexandre Courbot /* new parameters to apply */
12622b6c9e8SAlexandre Courbot struct gk20a_pll new_pll;
12722b6c9e8SAlexandre Courbot struct gm20b_clk_dvfs new_dvfs;
12822b6c9e8SAlexandre Courbot u32 new_uv;
12922b6c9e8SAlexandre Courbot
13022b6c9e8SAlexandre Courbot const struct gm20b_clk_dvfs_params *dvfs_params;
13122b6c9e8SAlexandre Courbot
13222b6c9e8SAlexandre Courbot /* fused parameters */
13322b6c9e8SAlexandre Courbot s32 uvdet_slope;
13422b6c9e8SAlexandre Courbot s32 uvdet_offs;
13522b6c9e8SAlexandre Courbot
13622b6c9e8SAlexandre Courbot /* safe frequency we can use at minimum voltage */
13722b6c9e8SAlexandre Courbot u32 safe_fmax_vmin;
13822b6c9e8SAlexandre Courbot };
13922b6c9e8SAlexandre Courbot #define gm20b_clk(p) container_of((gk20a_clk(p)), struct gm20b_clk, base)
14022b6c9e8SAlexandre Courbot
pl_to_div(u32 pl)14152829d4fSAlexandre Courbot static u32 pl_to_div(u32 pl)
14252829d4fSAlexandre Courbot {
14352829d4fSAlexandre Courbot return pl;
14452829d4fSAlexandre Courbot }
14552829d4fSAlexandre Courbot
div_to_pl(u32 div)14652829d4fSAlexandre Courbot static u32 div_to_pl(u32 div)
14752829d4fSAlexandre Courbot {
14852829d4fSAlexandre Courbot return div;
14952829d4fSAlexandre Courbot }
15052829d4fSAlexandre Courbot
15152829d4fSAlexandre Courbot static const struct gk20a_clk_pllg_params gm20b_pllg_params = {
15252829d4fSAlexandre Courbot .min_vco = 1300000, .max_vco = 2600000,
15352829d4fSAlexandre Courbot .min_u = 12000, .max_u = 38400,
15452829d4fSAlexandre Courbot .min_m = 1, .max_m = 255,
15552829d4fSAlexandre Courbot .min_n = 8, .max_n = 255,
15652829d4fSAlexandre Courbot .min_pl = 1, .max_pl = 31,
15752829d4fSAlexandre Courbot };
15852829d4fSAlexandre Courbot
15922b6c9e8SAlexandre Courbot static void
gm20b_pllg_read_mnp(struct gm20b_clk * clk,struct gm20b_pll * pll)16022b6c9e8SAlexandre Courbot gm20b_pllg_read_mnp(struct gm20b_clk *clk, struct gm20b_pll *pll)
16122b6c9e8SAlexandre Courbot {
16222b6c9e8SAlexandre Courbot struct nvkm_subdev *subdev = &clk->base.base.subdev;
16322b6c9e8SAlexandre Courbot struct nvkm_device *device = subdev->device;
16422b6c9e8SAlexandre Courbot u32 val;
16522b6c9e8SAlexandre Courbot
16622b6c9e8SAlexandre Courbot gk20a_pllg_read_mnp(&clk->base, &pll->base);
16722b6c9e8SAlexandre Courbot val = nvkm_rd32(device, GPCPLL_CFG2);
16822b6c9e8SAlexandre Courbot pll->sdm_din = (val >> GPCPLL_CFG2_SDM_DIN_SHIFT) &
16922b6c9e8SAlexandre Courbot MASK(GPCPLL_CFG2_SDM_DIN_WIDTH);
17022b6c9e8SAlexandre Courbot }
17122b6c9e8SAlexandre Courbot
17222b6c9e8SAlexandre Courbot static void
gm20b_pllg_write_mnp(struct gm20b_clk * clk,const struct gm20b_pll * pll)17322b6c9e8SAlexandre Courbot gm20b_pllg_write_mnp(struct gm20b_clk *clk, const struct gm20b_pll *pll)
17422b6c9e8SAlexandre Courbot {
17522b6c9e8SAlexandre Courbot struct nvkm_device *device = clk->base.base.subdev.device;
17622b6c9e8SAlexandre Courbot
17722b6c9e8SAlexandre Courbot nvkm_mask(device, GPCPLL_CFG2, GPCPLL_CFG2_SDM_DIN_MASK,
17822b6c9e8SAlexandre Courbot pll->sdm_din << GPCPLL_CFG2_SDM_DIN_SHIFT);
17922b6c9e8SAlexandre Courbot gk20a_pllg_write_mnp(&clk->base, &pll->base);
18022b6c9e8SAlexandre Courbot }
18122b6c9e8SAlexandre Courbot
18222b6c9e8SAlexandre Courbot /*
18322b6c9e8SAlexandre Courbot * Determine DFS_COEFF for the requested voltage. Always select external
18422b6c9e8SAlexandre Courbot * calibration override equal to the voltage, and set maximum detection
18522b6c9e8SAlexandre Courbot * limit "0" (to make sure that PLL output remains under F/V curve when
18622b6c9e8SAlexandre Courbot * voltage increases).
18722b6c9e8SAlexandre Courbot */
18822b6c9e8SAlexandre Courbot static void
gm20b_dvfs_calc_det_coeff(struct gm20b_clk * clk,s32 uv,struct gm20b_clk_dvfs * dvfs)18922b6c9e8SAlexandre Courbot gm20b_dvfs_calc_det_coeff(struct gm20b_clk *clk, s32 uv,
19022b6c9e8SAlexandre Courbot struct gm20b_clk_dvfs *dvfs)
19122b6c9e8SAlexandre Courbot {
19222b6c9e8SAlexandre Courbot struct nvkm_subdev *subdev = &clk->base.base.subdev;
19322b6c9e8SAlexandre Courbot const struct gm20b_clk_dvfs_params *p = clk->dvfs_params;
19422b6c9e8SAlexandre Courbot u32 coeff;
19522b6c9e8SAlexandre Courbot /* Work with mv as uv would likely trigger an overflow */
19622b6c9e8SAlexandre Courbot s32 mv = DIV_ROUND_CLOSEST(uv, 1000);
19722b6c9e8SAlexandre Courbot
19822b6c9e8SAlexandre Courbot /* coeff = slope * voltage + offset */
19922b6c9e8SAlexandre Courbot coeff = DIV_ROUND_CLOSEST(mv * p->coeff_slope, 1000) + p->coeff_offs;
20022b6c9e8SAlexandre Courbot coeff = DIV_ROUND_CLOSEST(coeff, 1000);
20122b6c9e8SAlexandre Courbot dvfs->dfs_coeff = min_t(u32, coeff, MASK(GPCPLL_DVFS0_DFS_COEFF_WIDTH));
20222b6c9e8SAlexandre Courbot
20322b6c9e8SAlexandre Courbot dvfs->dfs_ext_cal = DIV_ROUND_CLOSEST(uv - clk->uvdet_offs,
20422b6c9e8SAlexandre Courbot clk->uvdet_slope);
20522b6c9e8SAlexandre Courbot /* should never happen */
20622b6c9e8SAlexandre Courbot if (abs(dvfs->dfs_ext_cal) >= BIT(DFS_DET_RANGE))
20722b6c9e8SAlexandre Courbot nvkm_error(subdev, "dfs_ext_cal overflow!\n");
20822b6c9e8SAlexandre Courbot
20922b6c9e8SAlexandre Courbot dvfs->dfs_det_max = 0;
21022b6c9e8SAlexandre Courbot
21122b6c9e8SAlexandre Courbot nvkm_debug(subdev, "%s uv: %d coeff: %x, ext_cal: %d, det_max: %d\n",
21222b6c9e8SAlexandre Courbot __func__, uv, dvfs->dfs_coeff, dvfs->dfs_ext_cal,
21322b6c9e8SAlexandre Courbot dvfs->dfs_det_max);
21422b6c9e8SAlexandre Courbot }
21522b6c9e8SAlexandre Courbot
21622b6c9e8SAlexandre Courbot /*
21722b6c9e8SAlexandre Courbot * Solve equation for integer and fractional part of the effective NDIV:
21822b6c9e8SAlexandre Courbot *
21922b6c9e8SAlexandre Courbot * n_eff = n_int + 1/2 + (SDM_DIN / 2^(SDM_DIN_RANGE + 1)) +
22022b6c9e8SAlexandre Courbot * (DVFS_COEFF * DVFS_DET_DELTA) / 2^DFS_DET_RANGE
22122b6c9e8SAlexandre Courbot *
22222b6c9e8SAlexandre Courbot * The SDM_DIN LSB is finally shifted out, since it is not accessible by sw.
22322b6c9e8SAlexandre Courbot */
22422b6c9e8SAlexandre Courbot static void
gm20b_dvfs_calc_ndiv(struct gm20b_clk * clk,u32 n_eff,u32 * n_int,u32 * sdm_din)22522b6c9e8SAlexandre Courbot gm20b_dvfs_calc_ndiv(struct gm20b_clk *clk, u32 n_eff, u32 *n_int, u32 *sdm_din)
22622b6c9e8SAlexandre Courbot {
22722b6c9e8SAlexandre Courbot struct nvkm_subdev *subdev = &clk->base.base.subdev;
22822b6c9e8SAlexandre Courbot const struct gk20a_clk_pllg_params *p = clk->base.params;
22922b6c9e8SAlexandre Courbot u32 n;
23022b6c9e8SAlexandre Courbot s32 det_delta;
23122b6c9e8SAlexandre Courbot u32 rem, rem_range;
23222b6c9e8SAlexandre Courbot
23322b6c9e8SAlexandre Courbot /* calculate current ext_cal and subtract previous one */
23422b6c9e8SAlexandre Courbot det_delta = DIV_ROUND_CLOSEST(((s32)clk->uv) - clk->uvdet_offs,
23522b6c9e8SAlexandre Courbot clk->uvdet_slope);
23622b6c9e8SAlexandre Courbot det_delta -= clk->dvfs.dfs_ext_cal;
23722b6c9e8SAlexandre Courbot det_delta = min(det_delta, clk->dvfs.dfs_det_max);
23822b6c9e8SAlexandre Courbot det_delta *= clk->dvfs.dfs_coeff;
23922b6c9e8SAlexandre Courbot
24022b6c9e8SAlexandre Courbot /* integer part of n */
24122b6c9e8SAlexandre Courbot n = (n_eff << DFS_DET_RANGE) - det_delta;
24222b6c9e8SAlexandre Courbot /* should never happen! */
24322b6c9e8SAlexandre Courbot if (n <= 0) {
24422b6c9e8SAlexandre Courbot nvkm_error(subdev, "ndiv <= 0 - setting to 1...\n");
24522b6c9e8SAlexandre Courbot n = 1 << DFS_DET_RANGE;
24622b6c9e8SAlexandre Courbot }
24722b6c9e8SAlexandre Courbot if (n >> DFS_DET_RANGE > p->max_n) {
24822b6c9e8SAlexandre Courbot nvkm_error(subdev, "ndiv > max_n - setting to max_n...\n");
24922b6c9e8SAlexandre Courbot n = p->max_n << DFS_DET_RANGE;
25022b6c9e8SAlexandre Courbot }
25122b6c9e8SAlexandre Courbot *n_int = n >> DFS_DET_RANGE;
25222b6c9e8SAlexandre Courbot
25322b6c9e8SAlexandre Courbot /* fractional part of n */
25422b6c9e8SAlexandre Courbot rem = ((u32)n) & MASK(DFS_DET_RANGE);
25522b6c9e8SAlexandre Courbot rem_range = SDM_DIN_RANGE + 1 - DFS_DET_RANGE;
25622b6c9e8SAlexandre Courbot /* subtract 2^SDM_DIN_RANGE to account for the 1/2 of the equation */
25722b6c9e8SAlexandre Courbot rem = (rem << rem_range) - BIT(SDM_DIN_RANGE);
25822b6c9e8SAlexandre Courbot /* lose 8 LSB and clip - sdm_din only keeps the most significant byte */
25922b6c9e8SAlexandre Courbot *sdm_din = (rem >> BITS_PER_BYTE) & MASK(GPCPLL_CFG2_SDM_DIN_WIDTH);
26022b6c9e8SAlexandre Courbot
26122b6c9e8SAlexandre Courbot nvkm_debug(subdev, "%s n_eff: %d, n_int: %d, sdm_din: %d\n", __func__,
26222b6c9e8SAlexandre Courbot n_eff, *n_int, *sdm_din);
26322b6c9e8SAlexandre Courbot }
26422b6c9e8SAlexandre Courbot
26522b6c9e8SAlexandre Courbot static int
gm20b_pllg_slide(struct gm20b_clk * clk,u32 n)26622b6c9e8SAlexandre Courbot gm20b_pllg_slide(struct gm20b_clk *clk, u32 n)
26722b6c9e8SAlexandre Courbot {
26822b6c9e8SAlexandre Courbot struct nvkm_subdev *subdev = &clk->base.base.subdev;
26922b6c9e8SAlexandre Courbot struct nvkm_device *device = subdev->device;
27022b6c9e8SAlexandre Courbot struct gm20b_pll pll;
27122b6c9e8SAlexandre Courbot u32 n_int, sdm_din;
27222b6c9e8SAlexandre Courbot int ret = 0;
27322b6c9e8SAlexandre Courbot
27422b6c9e8SAlexandre Courbot /* calculate the new n_int/sdm_din for this n/uv */
27522b6c9e8SAlexandre Courbot gm20b_dvfs_calc_ndiv(clk, n, &n_int, &sdm_din);
27622b6c9e8SAlexandre Courbot
27722b6c9e8SAlexandre Courbot /* get old coefficients */
27822b6c9e8SAlexandre Courbot gm20b_pllg_read_mnp(clk, &pll);
27922b6c9e8SAlexandre Courbot /* do nothing if NDIV is the same */
28022b6c9e8SAlexandre Courbot if (n_int == pll.base.n && sdm_din == pll.sdm_din)
28122b6c9e8SAlexandre Courbot return 0;
28222b6c9e8SAlexandre Courbot
28322b6c9e8SAlexandre Courbot /* pll slowdown mode */
28422b6c9e8SAlexandre Courbot nvkm_mask(device, GPCPLL_NDIV_SLOWDOWN,
28522b6c9e8SAlexandre Courbot BIT(GPCPLL_NDIV_SLOWDOWN_SLOWDOWN_USING_PLL_SHIFT),
28622b6c9e8SAlexandre Courbot BIT(GPCPLL_NDIV_SLOWDOWN_SLOWDOWN_USING_PLL_SHIFT));
28722b6c9e8SAlexandre Courbot
28822b6c9e8SAlexandre Courbot /* new ndiv ready for ramp */
28922b6c9e8SAlexandre Courbot /* in DVFS mode SDM is updated via "new" field */
29022b6c9e8SAlexandre Courbot nvkm_mask(device, GPCPLL_CFG2, GPCPLL_CFG2_SDM_DIN_NEW_MASK,
29122b6c9e8SAlexandre Courbot sdm_din << GPCPLL_CFG2_SDM_DIN_NEW_SHIFT);
29222b6c9e8SAlexandre Courbot pll.base.n = n_int;
29322b6c9e8SAlexandre Courbot udelay(1);
29422b6c9e8SAlexandre Courbot gk20a_pllg_write_mnp(&clk->base, &pll.base);
29522b6c9e8SAlexandre Courbot
29622b6c9e8SAlexandre Courbot /* dynamic ramp to new ndiv */
29722b6c9e8SAlexandre Courbot udelay(1);
29822b6c9e8SAlexandre Courbot nvkm_mask(device, GPCPLL_NDIV_SLOWDOWN,
29922b6c9e8SAlexandre Courbot BIT(GPCPLL_NDIV_SLOWDOWN_EN_DYNRAMP_SHIFT),
30022b6c9e8SAlexandre Courbot BIT(GPCPLL_NDIV_SLOWDOWN_EN_DYNRAMP_SHIFT));
30122b6c9e8SAlexandre Courbot
30222b6c9e8SAlexandre Courbot /* wait for ramping to complete */
30322b6c9e8SAlexandre Courbot if (nvkm_wait_usec(device, 500, GPC_BCAST_NDIV_SLOWDOWN_DEBUG,
30422b6c9e8SAlexandre Courbot GPC_BCAST_NDIV_SLOWDOWN_DEBUG_PLL_DYNRAMP_DONE_SYNCED_MASK,
30522b6c9e8SAlexandre Courbot GPC_BCAST_NDIV_SLOWDOWN_DEBUG_PLL_DYNRAMP_DONE_SYNCED_MASK) < 0)
30622b6c9e8SAlexandre Courbot ret = -ETIMEDOUT;
30722b6c9e8SAlexandre Courbot
30822b6c9e8SAlexandre Courbot /* in DVFS mode complete SDM update */
30922b6c9e8SAlexandre Courbot nvkm_mask(device, GPCPLL_CFG2, GPCPLL_CFG2_SDM_DIN_MASK,
31022b6c9e8SAlexandre Courbot sdm_din << GPCPLL_CFG2_SDM_DIN_SHIFT);
31122b6c9e8SAlexandre Courbot
31222b6c9e8SAlexandre Courbot /* exit slowdown mode */
31322b6c9e8SAlexandre Courbot nvkm_mask(device, GPCPLL_NDIV_SLOWDOWN,
31422b6c9e8SAlexandre Courbot BIT(GPCPLL_NDIV_SLOWDOWN_SLOWDOWN_USING_PLL_SHIFT) |
31522b6c9e8SAlexandre Courbot BIT(GPCPLL_NDIV_SLOWDOWN_EN_DYNRAMP_SHIFT), 0);
31622b6c9e8SAlexandre Courbot nvkm_rd32(device, GPCPLL_NDIV_SLOWDOWN);
31722b6c9e8SAlexandre Courbot
31822b6c9e8SAlexandre Courbot return ret;
31922b6c9e8SAlexandre Courbot }
32022b6c9e8SAlexandre Courbot
32122b6c9e8SAlexandre Courbot static int
gm20b_pllg_enable(struct gm20b_clk * clk)32222b6c9e8SAlexandre Courbot gm20b_pllg_enable(struct gm20b_clk *clk)
32322b6c9e8SAlexandre Courbot {
32422b6c9e8SAlexandre Courbot struct nvkm_device *device = clk->base.base.subdev.device;
32522b6c9e8SAlexandre Courbot
32622b6c9e8SAlexandre Courbot nvkm_mask(device, GPCPLL_CFG, GPCPLL_CFG_ENABLE, GPCPLL_CFG_ENABLE);
32722b6c9e8SAlexandre Courbot nvkm_rd32(device, GPCPLL_CFG);
32822b6c9e8SAlexandre Courbot
32922b6c9e8SAlexandre Courbot /* In DVFS mode lock cannot be used - so just delay */
33022b6c9e8SAlexandre Courbot udelay(40);
33122b6c9e8SAlexandre Courbot
33222b6c9e8SAlexandre Courbot /* set SYNC_MODE for glitchless switch out of bypass */
33322b6c9e8SAlexandre Courbot nvkm_mask(device, GPCPLL_CFG, GPCPLL_CFG_SYNC_MODE,
33422b6c9e8SAlexandre Courbot GPCPLL_CFG_SYNC_MODE);
33522b6c9e8SAlexandre Courbot nvkm_rd32(device, GPCPLL_CFG);
33622b6c9e8SAlexandre Courbot
33722b6c9e8SAlexandre Courbot /* switch to VCO mode */
33822b6c9e8SAlexandre Courbot nvkm_mask(device, SEL_VCO, BIT(SEL_VCO_GPC2CLK_OUT_SHIFT),
33922b6c9e8SAlexandre Courbot BIT(SEL_VCO_GPC2CLK_OUT_SHIFT));
34022b6c9e8SAlexandre Courbot
34122b6c9e8SAlexandre Courbot return 0;
34222b6c9e8SAlexandre Courbot }
34322b6c9e8SAlexandre Courbot
34422b6c9e8SAlexandre Courbot static void
gm20b_pllg_disable(struct gm20b_clk * clk)34522b6c9e8SAlexandre Courbot gm20b_pllg_disable(struct gm20b_clk *clk)
34622b6c9e8SAlexandre Courbot {
34722b6c9e8SAlexandre Courbot struct nvkm_device *device = clk->base.base.subdev.device;
34822b6c9e8SAlexandre Courbot
34922b6c9e8SAlexandre Courbot /* put PLL in bypass before disabling it */
35022b6c9e8SAlexandre Courbot nvkm_mask(device, SEL_VCO, BIT(SEL_VCO_GPC2CLK_OUT_SHIFT), 0);
35122b6c9e8SAlexandre Courbot
35222b6c9e8SAlexandre Courbot /* clear SYNC_MODE before disabling PLL */
35322b6c9e8SAlexandre Courbot nvkm_mask(device, GPCPLL_CFG, GPCPLL_CFG_SYNC_MODE, 0);
35422b6c9e8SAlexandre Courbot
35522b6c9e8SAlexandre Courbot nvkm_mask(device, GPCPLL_CFG, GPCPLL_CFG_ENABLE, 0);
35622b6c9e8SAlexandre Courbot nvkm_rd32(device, GPCPLL_CFG);
35722b6c9e8SAlexandre Courbot }
35822b6c9e8SAlexandre Courbot
35922b6c9e8SAlexandre Courbot static int
gm20b_pllg_program_mnp(struct gm20b_clk * clk,const struct gk20a_pll * pll)36022b6c9e8SAlexandre Courbot gm20b_pllg_program_mnp(struct gm20b_clk *clk, const struct gk20a_pll *pll)
36122b6c9e8SAlexandre Courbot {
36222b6c9e8SAlexandre Courbot struct nvkm_subdev *subdev = &clk->base.base.subdev;
36322b6c9e8SAlexandre Courbot struct nvkm_device *device = subdev->device;
36422b6c9e8SAlexandre Courbot struct gm20b_pll cur_pll;
36522b6c9e8SAlexandre Courbot u32 n_int, sdm_din;
36622b6c9e8SAlexandre Courbot /* if we only change pdiv, we can do a glitchless transition */
36722b6c9e8SAlexandre Courbot bool pdiv_only;
36822b6c9e8SAlexandre Courbot int ret;
36922b6c9e8SAlexandre Courbot
37022b6c9e8SAlexandre Courbot gm20b_dvfs_calc_ndiv(clk, pll->n, &n_int, &sdm_din);
37122b6c9e8SAlexandre Courbot gm20b_pllg_read_mnp(clk, &cur_pll);
37222b6c9e8SAlexandre Courbot pdiv_only = cur_pll.base.n == n_int && cur_pll.sdm_din == sdm_din &&
37322b6c9e8SAlexandre Courbot cur_pll.base.m == pll->m;
37422b6c9e8SAlexandre Courbot
37522b6c9e8SAlexandre Courbot /* need full sequence if clock not enabled yet */
37622b6c9e8SAlexandre Courbot if (!gk20a_pllg_is_enabled(&clk->base))
37722b6c9e8SAlexandre Courbot pdiv_only = false;
37822b6c9e8SAlexandre Courbot
37922b6c9e8SAlexandre Courbot /* split VCO-to-bypass jump in half by setting out divider 1:2 */
38022b6c9e8SAlexandre Courbot nvkm_mask(device, GPC2CLK_OUT, GPC2CLK_OUT_VCODIV_MASK,
38122b6c9e8SAlexandre Courbot GPC2CLK_OUT_VCODIV2 << GPC2CLK_OUT_VCODIV_SHIFT);
38222b6c9e8SAlexandre Courbot /* Intentional 2nd write to assure linear divider operation */
38322b6c9e8SAlexandre Courbot nvkm_mask(device, GPC2CLK_OUT, GPC2CLK_OUT_VCODIV_MASK,
38422b6c9e8SAlexandre Courbot GPC2CLK_OUT_VCODIV2 << GPC2CLK_OUT_VCODIV_SHIFT);
38522b6c9e8SAlexandre Courbot nvkm_rd32(device, GPC2CLK_OUT);
38622b6c9e8SAlexandre Courbot udelay(2);
38722b6c9e8SAlexandre Courbot
38822b6c9e8SAlexandre Courbot if (pdiv_only) {
38922b6c9e8SAlexandre Courbot u32 old = cur_pll.base.pl;
39022b6c9e8SAlexandre Courbot u32 new = pll->pl;
39122b6c9e8SAlexandre Courbot
39222b6c9e8SAlexandre Courbot /*
39322b6c9e8SAlexandre Courbot * we can do a glitchless transition only if the old and new PL
39422b6c9e8SAlexandre Courbot * parameters share at least one bit set to 1. If this is not
39522b6c9e8SAlexandre Courbot * the case, calculate and program an interim PL that will allow
39622b6c9e8SAlexandre Courbot * us to respect that rule.
39722b6c9e8SAlexandre Courbot */
39822b6c9e8SAlexandre Courbot if ((old & new) == 0) {
39922b6c9e8SAlexandre Courbot cur_pll.base.pl = min(old | BIT(ffs(new) - 1),
40022b6c9e8SAlexandre Courbot new | BIT(ffs(old) - 1));
40122b6c9e8SAlexandre Courbot gk20a_pllg_write_mnp(&clk->base, &cur_pll.base);
40222b6c9e8SAlexandre Courbot }
40322b6c9e8SAlexandre Courbot
40422b6c9e8SAlexandre Courbot cur_pll.base.pl = new;
40522b6c9e8SAlexandre Courbot gk20a_pllg_write_mnp(&clk->base, &cur_pll.base);
40622b6c9e8SAlexandre Courbot } else {
40722b6c9e8SAlexandre Courbot /* disable before programming if more than pdiv changes */
40822b6c9e8SAlexandre Courbot gm20b_pllg_disable(clk);
40922b6c9e8SAlexandre Courbot
41022b6c9e8SAlexandre Courbot cur_pll.base = *pll;
41122b6c9e8SAlexandre Courbot cur_pll.base.n = n_int;
41222b6c9e8SAlexandre Courbot cur_pll.sdm_din = sdm_din;
41322b6c9e8SAlexandre Courbot gm20b_pllg_write_mnp(clk, &cur_pll);
41422b6c9e8SAlexandre Courbot
41522b6c9e8SAlexandre Courbot ret = gm20b_pllg_enable(clk);
41622b6c9e8SAlexandre Courbot if (ret)
41722b6c9e8SAlexandre Courbot return ret;
41822b6c9e8SAlexandre Courbot }
41922b6c9e8SAlexandre Courbot
42022b6c9e8SAlexandre Courbot /* restore out divider 1:1 */
42122b6c9e8SAlexandre Courbot udelay(2);
42222b6c9e8SAlexandre Courbot nvkm_mask(device, GPC2CLK_OUT, GPC2CLK_OUT_VCODIV_MASK,
42322b6c9e8SAlexandre Courbot GPC2CLK_OUT_VCODIV1 << GPC2CLK_OUT_VCODIV_SHIFT);
42422b6c9e8SAlexandre Courbot /* Intentional 2nd write to assure linear divider operation */
42522b6c9e8SAlexandre Courbot nvkm_mask(device, GPC2CLK_OUT, GPC2CLK_OUT_VCODIV_MASK,
42622b6c9e8SAlexandre Courbot GPC2CLK_OUT_VCODIV1 << GPC2CLK_OUT_VCODIV_SHIFT);
42722b6c9e8SAlexandre Courbot nvkm_rd32(device, GPC2CLK_OUT);
42822b6c9e8SAlexandre Courbot
42922b6c9e8SAlexandre Courbot return 0;
43022b6c9e8SAlexandre Courbot }
43122b6c9e8SAlexandre Courbot
43222b6c9e8SAlexandre Courbot static int
gm20b_pllg_program_mnp_slide(struct gm20b_clk * clk,const struct gk20a_pll * pll)43322b6c9e8SAlexandre Courbot gm20b_pllg_program_mnp_slide(struct gm20b_clk *clk, const struct gk20a_pll *pll)
43422b6c9e8SAlexandre Courbot {
43522b6c9e8SAlexandre Courbot struct gk20a_pll cur_pll;
43622b6c9e8SAlexandre Courbot int ret;
43722b6c9e8SAlexandre Courbot
43822b6c9e8SAlexandre Courbot if (gk20a_pllg_is_enabled(&clk->base)) {
43922b6c9e8SAlexandre Courbot gk20a_pllg_read_mnp(&clk->base, &cur_pll);
44022b6c9e8SAlexandre Courbot
44122b6c9e8SAlexandre Courbot /* just do NDIV slide if there is no change to M and PL */
44222b6c9e8SAlexandre Courbot if (pll->m == cur_pll.m && pll->pl == cur_pll.pl)
44322b6c9e8SAlexandre Courbot return gm20b_pllg_slide(clk, pll->n);
44422b6c9e8SAlexandre Courbot
44522b6c9e8SAlexandre Courbot /* slide down to current NDIV_LO */
44622b6c9e8SAlexandre Courbot cur_pll.n = gk20a_pllg_n_lo(&clk->base, &cur_pll);
44722b6c9e8SAlexandre Courbot ret = gm20b_pllg_slide(clk, cur_pll.n);
44822b6c9e8SAlexandre Courbot if (ret)
44922b6c9e8SAlexandre Courbot return ret;
45022b6c9e8SAlexandre Courbot }
45122b6c9e8SAlexandre Courbot
45222b6c9e8SAlexandre Courbot /* program MNP with the new clock parameters and new NDIV_LO */
45322b6c9e8SAlexandre Courbot cur_pll = *pll;
45422b6c9e8SAlexandre Courbot cur_pll.n = gk20a_pllg_n_lo(&clk->base, &cur_pll);
45522b6c9e8SAlexandre Courbot ret = gm20b_pllg_program_mnp(clk, &cur_pll);
45622b6c9e8SAlexandre Courbot if (ret)
45722b6c9e8SAlexandre Courbot return ret;
45822b6c9e8SAlexandre Courbot
45922b6c9e8SAlexandre Courbot /* slide up to new NDIV */
46022b6c9e8SAlexandre Courbot return gm20b_pllg_slide(clk, pll->n);
46122b6c9e8SAlexandre Courbot }
46222b6c9e8SAlexandre Courbot
46322b6c9e8SAlexandre Courbot static int
gm20b_clk_calc(struct nvkm_clk * base,struct nvkm_cstate * cstate)46422b6c9e8SAlexandre Courbot gm20b_clk_calc(struct nvkm_clk *base, struct nvkm_cstate *cstate)
46522b6c9e8SAlexandre Courbot {
46622b6c9e8SAlexandre Courbot struct gm20b_clk *clk = gm20b_clk(base);
46722b6c9e8SAlexandre Courbot struct nvkm_subdev *subdev = &base->subdev;
46822b6c9e8SAlexandre Courbot struct nvkm_volt *volt = base->subdev.device->volt;
46922b6c9e8SAlexandre Courbot int ret;
47022b6c9e8SAlexandre Courbot
47122b6c9e8SAlexandre Courbot ret = gk20a_pllg_calc_mnp(&clk->base, cstate->domain[nv_clk_src_gpc] *
47222b6c9e8SAlexandre Courbot GK20A_CLK_GPC_MDIV, &clk->new_pll);
47322b6c9e8SAlexandre Courbot if (ret)
47422b6c9e8SAlexandre Courbot return ret;
47522b6c9e8SAlexandre Courbot
47622b6c9e8SAlexandre Courbot clk->new_uv = volt->vid[cstate->voltage].uv;
47722b6c9e8SAlexandre Courbot gm20b_dvfs_calc_det_coeff(clk, clk->new_uv, &clk->new_dvfs);
47822b6c9e8SAlexandre Courbot
47922b6c9e8SAlexandre Courbot nvkm_debug(subdev, "%s uv: %d uv\n", __func__, clk->new_uv);
48022b6c9e8SAlexandre Courbot
48122b6c9e8SAlexandre Courbot return 0;
48222b6c9e8SAlexandre Courbot }
48322b6c9e8SAlexandre Courbot
48422b6c9e8SAlexandre Courbot /*
48522b6c9e8SAlexandre Courbot * Compute PLL parameters that are always safe for the current voltage
48622b6c9e8SAlexandre Courbot */
48722b6c9e8SAlexandre Courbot static void
gm20b_dvfs_calc_safe_pll(struct gm20b_clk * clk,struct gk20a_pll * pll)48822b6c9e8SAlexandre Courbot gm20b_dvfs_calc_safe_pll(struct gm20b_clk *clk, struct gk20a_pll *pll)
48922b6c9e8SAlexandre Courbot {
49022b6c9e8SAlexandre Courbot u32 rate = gk20a_pllg_calc_rate(&clk->base, pll) / KHZ;
49122b6c9e8SAlexandre Courbot u32 parent_rate = clk->base.parent_rate / KHZ;
49222b6c9e8SAlexandre Courbot u32 nmin, nsafe;
49322b6c9e8SAlexandre Courbot
49422b6c9e8SAlexandre Courbot /* remove a safe margin of 10% */
49522b6c9e8SAlexandre Courbot if (rate > clk->safe_fmax_vmin)
49622b6c9e8SAlexandre Courbot rate = rate * (100 - 10) / 100;
49722b6c9e8SAlexandre Courbot
49822b6c9e8SAlexandre Courbot /* gpc2clk */
49922b6c9e8SAlexandre Courbot rate *= 2;
50022b6c9e8SAlexandre Courbot
50122b6c9e8SAlexandre Courbot nmin = DIV_ROUND_UP(pll->m * clk->base.params->min_vco, parent_rate);
50222b6c9e8SAlexandre Courbot nsafe = pll->m * rate / (clk->base.parent_rate);
50322b6c9e8SAlexandre Courbot
50422b6c9e8SAlexandre Courbot if (nsafe < nmin) {
50522b6c9e8SAlexandre Courbot pll->pl = DIV_ROUND_UP(nmin * parent_rate, pll->m * rate);
50622b6c9e8SAlexandre Courbot nsafe = nmin;
50722b6c9e8SAlexandre Courbot }
50822b6c9e8SAlexandre Courbot
50922b6c9e8SAlexandre Courbot pll->n = nsafe;
51022b6c9e8SAlexandre Courbot }
51122b6c9e8SAlexandre Courbot
51222b6c9e8SAlexandre Courbot static void
gm20b_dvfs_program_coeff(struct gm20b_clk * clk,u32 coeff)51322b6c9e8SAlexandre Courbot gm20b_dvfs_program_coeff(struct gm20b_clk *clk, u32 coeff)
51422b6c9e8SAlexandre Courbot {
51522b6c9e8SAlexandre Courbot struct nvkm_device *device = clk->base.base.subdev.device;
51622b6c9e8SAlexandre Courbot
51722b6c9e8SAlexandre Courbot /* strobe to read external DFS coefficient */
51822b6c9e8SAlexandre Courbot nvkm_mask(device, GPC_BCAST_GPCPLL_DVFS2,
51922b6c9e8SAlexandre Courbot GPC_BCAST_GPCPLL_DVFS2_DFS_EXT_STROBE_BIT,
52022b6c9e8SAlexandre Courbot GPC_BCAST_GPCPLL_DVFS2_DFS_EXT_STROBE_BIT);
52122b6c9e8SAlexandre Courbot
52222b6c9e8SAlexandre Courbot nvkm_mask(device, GPCPLL_DVFS0, GPCPLL_DVFS0_DFS_COEFF_MASK,
52322b6c9e8SAlexandre Courbot coeff << GPCPLL_DVFS0_DFS_COEFF_SHIFT);
52422b6c9e8SAlexandre Courbot
52522b6c9e8SAlexandre Courbot udelay(1);
52622b6c9e8SAlexandre Courbot nvkm_mask(device, GPC_BCAST_GPCPLL_DVFS2,
52722b6c9e8SAlexandre Courbot GPC_BCAST_GPCPLL_DVFS2_DFS_EXT_STROBE_BIT, 0);
52822b6c9e8SAlexandre Courbot }
52922b6c9e8SAlexandre Courbot
53022b6c9e8SAlexandre Courbot static void
gm20b_dvfs_program_ext_cal(struct gm20b_clk * clk,u32 dfs_det_cal)53122b6c9e8SAlexandre Courbot gm20b_dvfs_program_ext_cal(struct gm20b_clk *clk, u32 dfs_det_cal)
53222b6c9e8SAlexandre Courbot {
53322b6c9e8SAlexandre Courbot struct nvkm_device *device = clk->base.base.subdev.device;
53422b6c9e8SAlexandre Courbot u32 val;
53522b6c9e8SAlexandre Courbot
53622b6c9e8SAlexandre Courbot nvkm_mask(device, GPC_BCAST_GPCPLL_DVFS2, MASK(DFS_DET_RANGE + 1),
53722b6c9e8SAlexandre Courbot dfs_det_cal);
53822b6c9e8SAlexandre Courbot udelay(1);
53922b6c9e8SAlexandre Courbot
54022b6c9e8SAlexandre Courbot val = nvkm_rd32(device, GPCPLL_DVFS1);
54122b6c9e8SAlexandre Courbot if (!(val & BIT(25))) {
54222b6c9e8SAlexandre Courbot /* Use external value to overwrite calibration value */
54322b6c9e8SAlexandre Courbot val |= BIT(25) | BIT(16);
54422b6c9e8SAlexandre Courbot nvkm_wr32(device, GPCPLL_DVFS1, val);
54522b6c9e8SAlexandre Courbot }
54622b6c9e8SAlexandre Courbot }
54722b6c9e8SAlexandre Courbot
54822b6c9e8SAlexandre Courbot static void
gm20b_dvfs_program_dfs_detection(struct gm20b_clk * clk,struct gm20b_clk_dvfs * dvfs)54922b6c9e8SAlexandre Courbot gm20b_dvfs_program_dfs_detection(struct gm20b_clk *clk,
55022b6c9e8SAlexandre Courbot struct gm20b_clk_dvfs *dvfs)
55122b6c9e8SAlexandre Courbot {
55222b6c9e8SAlexandre Courbot struct nvkm_device *device = clk->base.base.subdev.device;
55322b6c9e8SAlexandre Courbot
55422b6c9e8SAlexandre Courbot /* strobe to read external DFS coefficient */
55522b6c9e8SAlexandre Courbot nvkm_mask(device, GPC_BCAST_GPCPLL_DVFS2,
55622b6c9e8SAlexandre Courbot GPC_BCAST_GPCPLL_DVFS2_DFS_EXT_STROBE_BIT,
55722b6c9e8SAlexandre Courbot GPC_BCAST_GPCPLL_DVFS2_DFS_EXT_STROBE_BIT);
55822b6c9e8SAlexandre Courbot
55922b6c9e8SAlexandre Courbot nvkm_mask(device, GPCPLL_DVFS0,
56022b6c9e8SAlexandre Courbot GPCPLL_DVFS0_DFS_COEFF_MASK | GPCPLL_DVFS0_DFS_DET_MAX_MASK,
56122b6c9e8SAlexandre Courbot dvfs->dfs_coeff << GPCPLL_DVFS0_DFS_COEFF_SHIFT |
56222b6c9e8SAlexandre Courbot dvfs->dfs_det_max << GPCPLL_DVFS0_DFS_DET_MAX_SHIFT);
56322b6c9e8SAlexandre Courbot
56422b6c9e8SAlexandre Courbot udelay(1);
56522b6c9e8SAlexandre Courbot nvkm_mask(device, GPC_BCAST_GPCPLL_DVFS2,
56622b6c9e8SAlexandre Courbot GPC_BCAST_GPCPLL_DVFS2_DFS_EXT_STROBE_BIT, 0);
56722b6c9e8SAlexandre Courbot
56822b6c9e8SAlexandre Courbot gm20b_dvfs_program_ext_cal(clk, dvfs->dfs_ext_cal);
56922b6c9e8SAlexandre Courbot }
57022b6c9e8SAlexandre Courbot
57122b6c9e8SAlexandre Courbot static int
gm20b_clk_prog(struct nvkm_clk * base)57222b6c9e8SAlexandre Courbot gm20b_clk_prog(struct nvkm_clk *base)
57322b6c9e8SAlexandre Courbot {
57422b6c9e8SAlexandre Courbot struct gm20b_clk *clk = gm20b_clk(base);
57522b6c9e8SAlexandre Courbot u32 cur_freq;
57622b6c9e8SAlexandre Courbot int ret;
57722b6c9e8SAlexandre Courbot
57822b6c9e8SAlexandre Courbot /* No change in DVFS settings? */
57922b6c9e8SAlexandre Courbot if (clk->uv == clk->new_uv)
58022b6c9e8SAlexandre Courbot goto prog;
58122b6c9e8SAlexandre Courbot
58222b6c9e8SAlexandre Courbot /*
58322b6c9e8SAlexandre Courbot * Interim step for changing DVFS detection settings: low enough
584*83ebec7eSwangjianli * frequency to be safe at DVFS coeff = 0.
58522b6c9e8SAlexandre Courbot *
58622b6c9e8SAlexandre Courbot * 1. If voltage is increasing:
58722b6c9e8SAlexandre Courbot * - safe frequency target matches the lowest - old - frequency
58822b6c9e8SAlexandre Courbot * - DVFS settings are still old
58922b6c9e8SAlexandre Courbot * - Voltage already increased to new level by volt, but maximum
59022b6c9e8SAlexandre Courbot * detection limit assures PLL output remains under F/V curve
59122b6c9e8SAlexandre Courbot *
59222b6c9e8SAlexandre Courbot * 2. If voltage is decreasing:
59322b6c9e8SAlexandre Courbot * - safe frequency target matches the lowest - new - frequency
59422b6c9e8SAlexandre Courbot * - DVFS settings are still old
59522b6c9e8SAlexandre Courbot * - Voltage is also old, it will be lowered by volt afterwards
59622b6c9e8SAlexandre Courbot *
59722b6c9e8SAlexandre Courbot * Interim step can be skipped if old frequency is below safe minimum,
59822b6c9e8SAlexandre Courbot * i.e., it is low enough to be safe at any voltage in operating range
59922b6c9e8SAlexandre Courbot * with zero DVFS coefficient.
60022b6c9e8SAlexandre Courbot */
60122b6c9e8SAlexandre Courbot cur_freq = nvkm_clk_read(&clk->base.base, nv_clk_src_gpc);
60222b6c9e8SAlexandre Courbot if (cur_freq > clk->safe_fmax_vmin) {
60322b6c9e8SAlexandre Courbot struct gk20a_pll pll_safe;
60422b6c9e8SAlexandre Courbot
60522b6c9e8SAlexandre Courbot if (clk->uv < clk->new_uv)
60622b6c9e8SAlexandre Courbot /* voltage will raise: safe frequency is current one */
60722b6c9e8SAlexandre Courbot pll_safe = clk->base.pll;
60822b6c9e8SAlexandre Courbot else
60922b6c9e8SAlexandre Courbot /* voltage will drop: safe frequency is new one */
61022b6c9e8SAlexandre Courbot pll_safe = clk->new_pll;
61122b6c9e8SAlexandre Courbot
61222b6c9e8SAlexandre Courbot gm20b_dvfs_calc_safe_pll(clk, &pll_safe);
61322b6c9e8SAlexandre Courbot ret = gm20b_pllg_program_mnp_slide(clk, &pll_safe);
61422b6c9e8SAlexandre Courbot if (ret)
61522b6c9e8SAlexandre Courbot return ret;
61622b6c9e8SAlexandre Courbot }
61722b6c9e8SAlexandre Courbot
61822b6c9e8SAlexandre Courbot /*
61922b6c9e8SAlexandre Courbot * DVFS detection settings transition:
62022b6c9e8SAlexandre Courbot * - Set DVFS coefficient zero
62122b6c9e8SAlexandre Courbot * - Set calibration level to new voltage
62222b6c9e8SAlexandre Courbot * - Set DVFS coefficient to match new voltage
62322b6c9e8SAlexandre Courbot */
62422b6c9e8SAlexandre Courbot gm20b_dvfs_program_coeff(clk, 0);
62522b6c9e8SAlexandre Courbot gm20b_dvfs_program_ext_cal(clk, clk->new_dvfs.dfs_ext_cal);
62622b6c9e8SAlexandre Courbot gm20b_dvfs_program_coeff(clk, clk->new_dvfs.dfs_coeff);
62722b6c9e8SAlexandre Courbot gm20b_dvfs_program_dfs_detection(clk, &clk->new_dvfs);
62822b6c9e8SAlexandre Courbot
62922b6c9e8SAlexandre Courbot prog:
63022b6c9e8SAlexandre Courbot clk->uv = clk->new_uv;
63122b6c9e8SAlexandre Courbot clk->dvfs = clk->new_dvfs;
63222b6c9e8SAlexandre Courbot clk->base.pll = clk->new_pll;
63322b6c9e8SAlexandre Courbot
63422b6c9e8SAlexandre Courbot return gm20b_pllg_program_mnp_slide(clk, &clk->base.pll);
63522b6c9e8SAlexandre Courbot }
63622b6c9e8SAlexandre Courbot
63752829d4fSAlexandre Courbot static struct nvkm_pstate
63852829d4fSAlexandre Courbot gm20b_pstates[] = {
63952829d4fSAlexandre Courbot {
64052829d4fSAlexandre Courbot .base = {
64152829d4fSAlexandre Courbot .domain[nv_clk_src_gpc] = 76800,
64252829d4fSAlexandre Courbot .voltage = 0,
64352829d4fSAlexandre Courbot },
64452829d4fSAlexandre Courbot },
64552829d4fSAlexandre Courbot {
64652829d4fSAlexandre Courbot .base = {
64752829d4fSAlexandre Courbot .domain[nv_clk_src_gpc] = 153600,
64852829d4fSAlexandre Courbot .voltage = 1,
64952829d4fSAlexandre Courbot },
65052829d4fSAlexandre Courbot },
65152829d4fSAlexandre Courbot {
65252829d4fSAlexandre Courbot .base = {
65352829d4fSAlexandre Courbot .domain[nv_clk_src_gpc] = 230400,
65452829d4fSAlexandre Courbot .voltage = 2,
65552829d4fSAlexandre Courbot },
65652829d4fSAlexandre Courbot },
65752829d4fSAlexandre Courbot {
65852829d4fSAlexandre Courbot .base = {
65952829d4fSAlexandre Courbot .domain[nv_clk_src_gpc] = 307200,
66052829d4fSAlexandre Courbot .voltage = 3,
66152829d4fSAlexandre Courbot },
66252829d4fSAlexandre Courbot },
66352829d4fSAlexandre Courbot {
66452829d4fSAlexandre Courbot .base = {
66552829d4fSAlexandre Courbot .domain[nv_clk_src_gpc] = 384000,
66652829d4fSAlexandre Courbot .voltage = 4,
66752829d4fSAlexandre Courbot },
66852829d4fSAlexandre Courbot },
66952829d4fSAlexandre Courbot {
67052829d4fSAlexandre Courbot .base = {
67152829d4fSAlexandre Courbot .domain[nv_clk_src_gpc] = 460800,
67252829d4fSAlexandre Courbot .voltage = 5,
67352829d4fSAlexandre Courbot },
67452829d4fSAlexandre Courbot },
67552829d4fSAlexandre Courbot {
67652829d4fSAlexandre Courbot .base = {
67752829d4fSAlexandre Courbot .domain[nv_clk_src_gpc] = 537600,
67852829d4fSAlexandre Courbot .voltage = 6,
67952829d4fSAlexandre Courbot },
68052829d4fSAlexandre Courbot },
68152829d4fSAlexandre Courbot {
68252829d4fSAlexandre Courbot .base = {
68352829d4fSAlexandre Courbot .domain[nv_clk_src_gpc] = 614400,
68452829d4fSAlexandre Courbot .voltage = 7,
68552829d4fSAlexandre Courbot },
68652829d4fSAlexandre Courbot },
68752829d4fSAlexandre Courbot {
68852829d4fSAlexandre Courbot .base = {
68952829d4fSAlexandre Courbot .domain[nv_clk_src_gpc] = 691200,
69052829d4fSAlexandre Courbot .voltage = 8,
69152829d4fSAlexandre Courbot },
69252829d4fSAlexandre Courbot },
69352829d4fSAlexandre Courbot {
69452829d4fSAlexandre Courbot .base = {
69552829d4fSAlexandre Courbot .domain[nv_clk_src_gpc] = 768000,
69652829d4fSAlexandre Courbot .voltage = 9,
69752829d4fSAlexandre Courbot },
69852829d4fSAlexandre Courbot },
69952829d4fSAlexandre Courbot {
70052829d4fSAlexandre Courbot .base = {
70152829d4fSAlexandre Courbot .domain[nv_clk_src_gpc] = 844800,
70252829d4fSAlexandre Courbot .voltage = 10,
70352829d4fSAlexandre Courbot },
70452829d4fSAlexandre Courbot },
70552829d4fSAlexandre Courbot {
70652829d4fSAlexandre Courbot .base = {
70752829d4fSAlexandre Courbot .domain[nv_clk_src_gpc] = 921600,
70852829d4fSAlexandre Courbot .voltage = 11,
70952829d4fSAlexandre Courbot },
71052829d4fSAlexandre Courbot },
71152829d4fSAlexandre Courbot {
71252829d4fSAlexandre Courbot .base = {
71352829d4fSAlexandre Courbot .domain[nv_clk_src_gpc] = 998400,
71452829d4fSAlexandre Courbot .voltage = 12,
71552829d4fSAlexandre Courbot },
71652829d4fSAlexandre Courbot },
71752829d4fSAlexandre Courbot };
71852829d4fSAlexandre Courbot
71922b6c9e8SAlexandre Courbot static void
gm20b_clk_fini(struct nvkm_clk * base)72022b6c9e8SAlexandre Courbot gm20b_clk_fini(struct nvkm_clk *base)
72122b6c9e8SAlexandre Courbot {
72222b6c9e8SAlexandre Courbot struct nvkm_device *device = base->subdev.device;
72322b6c9e8SAlexandre Courbot struct gm20b_clk *clk = gm20b_clk(base);
72422b6c9e8SAlexandre Courbot
72522b6c9e8SAlexandre Courbot /* slide to VCO min */
72622b6c9e8SAlexandre Courbot if (gk20a_pllg_is_enabled(&clk->base)) {
72722b6c9e8SAlexandre Courbot struct gk20a_pll pll;
72822b6c9e8SAlexandre Courbot u32 n_lo;
72922b6c9e8SAlexandre Courbot
73022b6c9e8SAlexandre Courbot gk20a_pllg_read_mnp(&clk->base, &pll);
73122b6c9e8SAlexandre Courbot n_lo = gk20a_pllg_n_lo(&clk->base, &pll);
73222b6c9e8SAlexandre Courbot gm20b_pllg_slide(clk, n_lo);
73322b6c9e8SAlexandre Courbot }
73422b6c9e8SAlexandre Courbot
73522b6c9e8SAlexandre Courbot gm20b_pllg_disable(clk);
73622b6c9e8SAlexandre Courbot
73722b6c9e8SAlexandre Courbot /* set IDDQ */
73822b6c9e8SAlexandre Courbot nvkm_mask(device, GPCPLL_CFG, GPCPLL_CFG_IDDQ, 1);
73922b6c9e8SAlexandre Courbot }
74022b6c9e8SAlexandre Courbot
74122b6c9e8SAlexandre Courbot static int
gm20b_clk_init_dvfs(struct gm20b_clk * clk)74222b6c9e8SAlexandre Courbot gm20b_clk_init_dvfs(struct gm20b_clk *clk)
74322b6c9e8SAlexandre Courbot {
74422b6c9e8SAlexandre Courbot struct nvkm_subdev *subdev = &clk->base.base.subdev;
74522b6c9e8SAlexandre Courbot struct nvkm_device *device = subdev->device;
74622b6c9e8SAlexandre Courbot bool fused = clk->uvdet_offs && clk->uvdet_slope;
74722b6c9e8SAlexandre Courbot static const s32 ADC_SLOPE_UV = 10000; /* default ADC detection slope */
74822b6c9e8SAlexandre Courbot u32 data;
74922b6c9e8SAlexandre Courbot int ret;
75022b6c9e8SAlexandre Courbot
75122b6c9e8SAlexandre Courbot /* Enable NA DVFS */
75222b6c9e8SAlexandre Courbot nvkm_mask(device, GPCPLL_DVFS1, GPCPLL_DVFS1_EN_DFS_BIT,
75322b6c9e8SAlexandre Courbot GPCPLL_DVFS1_EN_DFS_BIT);
75422b6c9e8SAlexandre Courbot
75522b6c9e8SAlexandre Courbot /* Set VCO_CTRL */
75622b6c9e8SAlexandre Courbot if (clk->dvfs_params->vco_ctrl)
75722b6c9e8SAlexandre Courbot nvkm_mask(device, GPCPLL_CFG3, GPCPLL_CFG3_VCO_CTRL_MASK,
75822b6c9e8SAlexandre Courbot clk->dvfs_params->vco_ctrl << GPCPLL_CFG3_VCO_CTRL_SHIFT);
75922b6c9e8SAlexandre Courbot
76022b6c9e8SAlexandre Courbot if (fused) {
76122b6c9e8SAlexandre Courbot /* Start internal calibration, but ignore results */
76222b6c9e8SAlexandre Courbot nvkm_mask(device, GPCPLL_DVFS1, GPCPLL_DVFS1_EN_DFS_CAL_BIT,
76322b6c9e8SAlexandre Courbot GPCPLL_DVFS1_EN_DFS_CAL_BIT);
76422b6c9e8SAlexandre Courbot
76522b6c9e8SAlexandre Courbot /* got uvdev parameters from fuse, skip calibration */
76622b6c9e8SAlexandre Courbot goto calibrated;
76722b6c9e8SAlexandre Courbot }
76822b6c9e8SAlexandre Courbot
76922b6c9e8SAlexandre Courbot /*
77022b6c9e8SAlexandre Courbot * If calibration parameters are not fused, start internal calibration,
77122b6c9e8SAlexandre Courbot * wait for completion, and use results along with default slope to
77222b6c9e8SAlexandre Courbot * calculate ADC offset during boot.
77322b6c9e8SAlexandre Courbot */
77422b6c9e8SAlexandre Courbot nvkm_mask(device, GPCPLL_DVFS1, GPCPLL_DVFS1_EN_DFS_CAL_BIT,
77522b6c9e8SAlexandre Courbot GPCPLL_DVFS1_EN_DFS_CAL_BIT);
77622b6c9e8SAlexandre Courbot
77722b6c9e8SAlexandre Courbot /* Wait for internal calibration done (spec < 2us). */
77822b6c9e8SAlexandre Courbot ret = nvkm_wait_usec(device, 10, GPCPLL_DVFS1,
77922b6c9e8SAlexandre Courbot GPCPLL_DVFS1_DFS_CAL_DONE_BIT,
78022b6c9e8SAlexandre Courbot GPCPLL_DVFS1_DFS_CAL_DONE_BIT);
78122b6c9e8SAlexandre Courbot if (ret < 0) {
78222b6c9e8SAlexandre Courbot nvkm_error(subdev, "GPCPLL calibration timeout\n");
78322b6c9e8SAlexandre Courbot return -ETIMEDOUT;
78422b6c9e8SAlexandre Courbot }
78522b6c9e8SAlexandre Courbot
78622b6c9e8SAlexandre Courbot data = nvkm_rd32(device, GPCPLL_CFG3) >>
78722b6c9e8SAlexandre Courbot GPCPLL_CFG3_PLL_DFS_TESTOUT_SHIFT;
78822b6c9e8SAlexandre Courbot data &= MASK(GPCPLL_CFG3_PLL_DFS_TESTOUT_WIDTH);
78922b6c9e8SAlexandre Courbot
79022b6c9e8SAlexandre Courbot clk->uvdet_slope = ADC_SLOPE_UV;
79122b6c9e8SAlexandre Courbot clk->uvdet_offs = ((s32)clk->uv) - data * ADC_SLOPE_UV;
79222b6c9e8SAlexandre Courbot
79322b6c9e8SAlexandre Courbot nvkm_debug(subdev, "calibrated DVFS parameters: offs %d, slope %d\n",
79422b6c9e8SAlexandre Courbot clk->uvdet_offs, clk->uvdet_slope);
79522b6c9e8SAlexandre Courbot
79622b6c9e8SAlexandre Courbot calibrated:
79722b6c9e8SAlexandre Courbot /* Compute and apply initial DVFS parameters */
79822b6c9e8SAlexandre Courbot gm20b_dvfs_calc_det_coeff(clk, clk->uv, &clk->dvfs);
79922b6c9e8SAlexandre Courbot gm20b_dvfs_program_coeff(clk, 0);
80022b6c9e8SAlexandre Courbot gm20b_dvfs_program_ext_cal(clk, clk->dvfs.dfs_ext_cal);
80122b6c9e8SAlexandre Courbot gm20b_dvfs_program_coeff(clk, clk->dvfs.dfs_coeff);
80222b6c9e8SAlexandre Courbot gm20b_dvfs_program_dfs_detection(clk, &clk->new_dvfs);
80322b6c9e8SAlexandre Courbot
80422b6c9e8SAlexandre Courbot return 0;
80522b6c9e8SAlexandre Courbot }
80622b6c9e8SAlexandre Courbot
80722b6c9e8SAlexandre Courbot /* Forward declaration to detect speedo >=1 in gm20b_clk_init() */
80822b6c9e8SAlexandre Courbot static const struct nvkm_clk_func gm20b_clk;
80922b6c9e8SAlexandre Courbot
81052829d4fSAlexandre Courbot static int
gm20b_clk_init(struct nvkm_clk * base)81152829d4fSAlexandre Courbot gm20b_clk_init(struct nvkm_clk *base)
81252829d4fSAlexandre Courbot {
81352829d4fSAlexandre Courbot struct gk20a_clk *clk = gk20a_clk(base);
81452829d4fSAlexandre Courbot struct nvkm_subdev *subdev = &clk->base.subdev;
81552829d4fSAlexandre Courbot struct nvkm_device *device = subdev->device;
81652829d4fSAlexandre Courbot int ret;
81722b6c9e8SAlexandre Courbot u32 data;
81852829d4fSAlexandre Courbot
81922b6c9e8SAlexandre Courbot /* get out from IDDQ */
82022b6c9e8SAlexandre Courbot nvkm_mask(device, GPCPLL_CFG, GPCPLL_CFG_IDDQ, 0);
82122b6c9e8SAlexandre Courbot nvkm_rd32(device, GPCPLL_CFG);
82222b6c9e8SAlexandre Courbot udelay(5);
82322b6c9e8SAlexandre Courbot
82422b6c9e8SAlexandre Courbot nvkm_mask(device, GPC2CLK_OUT, GPC2CLK_OUT_INIT_MASK,
82522b6c9e8SAlexandre Courbot GPC2CLK_OUT_INIT_VAL);
826f5f1b06eSAlexandre Courbot
82752829d4fSAlexandre Courbot /* Set the global bypass control to VCO */
82852829d4fSAlexandre Courbot nvkm_mask(device, BYPASSCTRL_SYS,
82952829d4fSAlexandre Courbot MASK(BYPASSCTRL_SYS_GPCPLL_WIDTH) << BYPASSCTRL_SYS_GPCPLL_SHIFT,
83052829d4fSAlexandre Courbot 0);
83152829d4fSAlexandre Courbot
83222b6c9e8SAlexandre Courbot ret = gk20a_clk_setup_slide(clk);
83322b6c9e8SAlexandre Courbot if (ret)
83422b6c9e8SAlexandre Courbot return ret;
83522b6c9e8SAlexandre Courbot
83622b6c9e8SAlexandre Courbot /* If not fused, set RAM SVOP PDP data 0x2, and enable fuse override */
83722b6c9e8SAlexandre Courbot data = nvkm_rd32(device, 0x021944);
83822b6c9e8SAlexandre Courbot if (!(data & 0x3)) {
83922b6c9e8SAlexandre Courbot data |= 0x2;
84022b6c9e8SAlexandre Courbot nvkm_wr32(device, 0x021944, data);
84122b6c9e8SAlexandre Courbot
84222b6c9e8SAlexandre Courbot data = nvkm_rd32(device, 0x021948);
84322b6c9e8SAlexandre Courbot data |= 0x1;
84422b6c9e8SAlexandre Courbot nvkm_wr32(device, 0x021948, data);
84522b6c9e8SAlexandre Courbot }
84622b6c9e8SAlexandre Courbot
84722b6c9e8SAlexandre Courbot /* Disable idle slow down */
84822b6c9e8SAlexandre Courbot nvkm_mask(device, 0x20160, 0x003f0000, 0x0);
84922b6c9e8SAlexandre Courbot
85022b6c9e8SAlexandre Courbot /* speedo >= 1? */
85122b6c9e8SAlexandre Courbot if (clk->base.func == &gm20b_clk) {
85222b6c9e8SAlexandre Courbot struct gm20b_clk *_clk = gm20b_clk(base);
85322b6c9e8SAlexandre Courbot struct nvkm_volt *volt = device->volt;
85422b6c9e8SAlexandre Courbot
85522b6c9e8SAlexandre Courbot /* Get current voltage */
85622b6c9e8SAlexandre Courbot _clk->uv = nvkm_volt_get(volt);
85722b6c9e8SAlexandre Courbot
85822b6c9e8SAlexandre Courbot /* Initialize DVFS */
85922b6c9e8SAlexandre Courbot ret = gm20b_clk_init_dvfs(_clk);
86022b6c9e8SAlexandre Courbot if (ret)
86122b6c9e8SAlexandre Courbot return ret;
86222b6c9e8SAlexandre Courbot }
86322b6c9e8SAlexandre Courbot
86452829d4fSAlexandre Courbot /* Start with lowest frequency */
86552829d4fSAlexandre Courbot base->func->calc(base, &base->func->pstates[0].base);
86622b6c9e8SAlexandre Courbot ret = base->func->prog(base);
86752829d4fSAlexandre Courbot if (ret) {
86852829d4fSAlexandre Courbot nvkm_error(subdev, "cannot initialize clock\n");
86952829d4fSAlexandre Courbot return ret;
87052829d4fSAlexandre Courbot }
87152829d4fSAlexandre Courbot
87252829d4fSAlexandre Courbot return 0;
87352829d4fSAlexandre Courbot }
87452829d4fSAlexandre Courbot
87552829d4fSAlexandre Courbot static const struct nvkm_clk_func
87652829d4fSAlexandre Courbot gm20b_clk_speedo0 = {
87752829d4fSAlexandre Courbot .init = gm20b_clk_init,
87852829d4fSAlexandre Courbot .fini = gk20a_clk_fini,
87952829d4fSAlexandre Courbot .read = gk20a_clk_read,
88052829d4fSAlexandre Courbot .calc = gk20a_clk_calc,
88152829d4fSAlexandre Courbot .prog = gk20a_clk_prog,
88252829d4fSAlexandre Courbot .tidy = gk20a_clk_tidy,
88352829d4fSAlexandre Courbot .pstates = gm20b_pstates,
88422b6c9e8SAlexandre Courbot /* Speedo 0 only supports 12 voltages */
88552829d4fSAlexandre Courbot .nr_pstates = ARRAY_SIZE(gm20b_pstates) - 1,
88652829d4fSAlexandre Courbot .domains = {
88752829d4fSAlexandre Courbot { nv_clk_src_crystal, 0xff },
88852829d4fSAlexandre Courbot { nv_clk_src_gpc, 0xff, 0, "core", GK20A_CLK_GPC_MDIV },
88952829d4fSAlexandre Courbot { nv_clk_src_max },
89052829d4fSAlexandre Courbot },
89152829d4fSAlexandre Courbot };
89252829d4fSAlexandre Courbot
89322b6c9e8SAlexandre Courbot static const struct nvkm_clk_func
89422b6c9e8SAlexandre Courbot gm20b_clk = {
89522b6c9e8SAlexandre Courbot .init = gm20b_clk_init,
89622b6c9e8SAlexandre Courbot .fini = gm20b_clk_fini,
89722b6c9e8SAlexandre Courbot .read = gk20a_clk_read,
89822b6c9e8SAlexandre Courbot .calc = gm20b_clk_calc,
89922b6c9e8SAlexandre Courbot .prog = gm20b_clk_prog,
90022b6c9e8SAlexandre Courbot .tidy = gk20a_clk_tidy,
90122b6c9e8SAlexandre Courbot .pstates = gm20b_pstates,
90222b6c9e8SAlexandre Courbot .nr_pstates = ARRAY_SIZE(gm20b_pstates),
90322b6c9e8SAlexandre Courbot .domains = {
90422b6c9e8SAlexandre Courbot { nv_clk_src_crystal, 0xff },
90522b6c9e8SAlexandre Courbot { nv_clk_src_gpc, 0xff, 0, "core", GK20A_CLK_GPC_MDIV },
90622b6c9e8SAlexandre Courbot { nv_clk_src_max },
90722b6c9e8SAlexandre Courbot },
90822b6c9e8SAlexandre Courbot };
90922b6c9e8SAlexandre Courbot
91022b6c9e8SAlexandre Courbot static int
gm20b_clk_new_speedo0(struct nvkm_device * device,enum nvkm_subdev_type type,int inst,struct nvkm_clk ** pclk)91198fd7f83SBen Skeggs gm20b_clk_new_speedo0(struct nvkm_device *device, enum nvkm_subdev_type type, int inst,
91222b6c9e8SAlexandre Courbot struct nvkm_clk **pclk)
91352829d4fSAlexandre Courbot {
91452829d4fSAlexandre Courbot struct gk20a_clk *clk;
91552829d4fSAlexandre Courbot int ret;
91652829d4fSAlexandre Courbot
91752829d4fSAlexandre Courbot clk = kzalloc(sizeof(*clk), GFP_KERNEL);
91852829d4fSAlexandre Courbot if (!clk)
91952829d4fSAlexandre Courbot return -ENOMEM;
92052829d4fSAlexandre Courbot *pclk = &clk->base;
92152829d4fSAlexandre Courbot
92298fd7f83SBen Skeggs ret = gk20a_clk_ctor(device, type, inst, &gm20b_clk_speedo0, &gm20b_pllg_params, clk);
92352829d4fSAlexandre Courbot clk->pl_to_div = pl_to_div;
92452829d4fSAlexandre Courbot clk->div_to_pl = div_to_pl;
92552829d4fSAlexandre Courbot return ret;
92652829d4fSAlexandre Courbot }
92722b6c9e8SAlexandre Courbot
92822b6c9e8SAlexandre Courbot /* FUSE register */
92922b6c9e8SAlexandre Courbot #define FUSE_RESERVED_CALIB0 0x204
93022b6c9e8SAlexandre Courbot #define FUSE_RESERVED_CALIB0_INTERCEPT_FRAC_SHIFT 0
93122b6c9e8SAlexandre Courbot #define FUSE_RESERVED_CALIB0_INTERCEPT_FRAC_WIDTH 4
93222b6c9e8SAlexandre Courbot #define FUSE_RESERVED_CALIB0_INTERCEPT_INT_SHIFT 4
93322b6c9e8SAlexandre Courbot #define FUSE_RESERVED_CALIB0_INTERCEPT_INT_WIDTH 10
93422b6c9e8SAlexandre Courbot #define FUSE_RESERVED_CALIB0_SLOPE_FRAC_SHIFT 14
93522b6c9e8SAlexandre Courbot #define FUSE_RESERVED_CALIB0_SLOPE_FRAC_WIDTH 10
93622b6c9e8SAlexandre Courbot #define FUSE_RESERVED_CALIB0_SLOPE_INT_SHIFT 24
93722b6c9e8SAlexandre Courbot #define FUSE_RESERVED_CALIB0_SLOPE_INT_WIDTH 6
93822b6c9e8SAlexandre Courbot #define FUSE_RESERVED_CALIB0_FUSE_REV_SHIFT 30
93922b6c9e8SAlexandre Courbot #define FUSE_RESERVED_CALIB0_FUSE_REV_WIDTH 2
94022b6c9e8SAlexandre Courbot
94122b6c9e8SAlexandre Courbot static int
gm20b_clk_init_fused_params(struct gm20b_clk * clk)94222b6c9e8SAlexandre Courbot gm20b_clk_init_fused_params(struct gm20b_clk *clk)
94322b6c9e8SAlexandre Courbot {
94422b6c9e8SAlexandre Courbot struct nvkm_subdev *subdev = &clk->base.base.subdev;
94522b6c9e8SAlexandre Courbot u32 val = 0;
94622b6c9e8SAlexandre Courbot u32 rev = 0;
94722b6c9e8SAlexandre Courbot
94822b6c9e8SAlexandre Courbot #if IS_ENABLED(CONFIG_ARCH_TEGRA)
94922b6c9e8SAlexandre Courbot tegra_fuse_readl(FUSE_RESERVED_CALIB0, &val);
95022b6c9e8SAlexandre Courbot rev = (val >> FUSE_RESERVED_CALIB0_FUSE_REV_SHIFT) &
95122b6c9e8SAlexandre Courbot MASK(FUSE_RESERVED_CALIB0_FUSE_REV_WIDTH);
95222b6c9e8SAlexandre Courbot #endif
95322b6c9e8SAlexandre Courbot
95422b6c9e8SAlexandre Courbot /* No fused parameters, we will calibrate later */
95522b6c9e8SAlexandre Courbot if (rev == 0)
95622b6c9e8SAlexandre Courbot return -EINVAL;
95722b6c9e8SAlexandre Courbot
95822b6c9e8SAlexandre Courbot /* Integer part in mV + fractional part in uV */
95922b6c9e8SAlexandre Courbot clk->uvdet_slope = ((val >> FUSE_RESERVED_CALIB0_SLOPE_INT_SHIFT) &
96022b6c9e8SAlexandre Courbot MASK(FUSE_RESERVED_CALIB0_SLOPE_INT_WIDTH)) * 1000 +
96122b6c9e8SAlexandre Courbot ((val >> FUSE_RESERVED_CALIB0_SLOPE_FRAC_SHIFT) &
96222b6c9e8SAlexandre Courbot MASK(FUSE_RESERVED_CALIB0_SLOPE_FRAC_WIDTH));
96322b6c9e8SAlexandre Courbot
96422b6c9e8SAlexandre Courbot /* Integer part in mV + fractional part in 100uV */
96522b6c9e8SAlexandre Courbot clk->uvdet_offs = ((val >> FUSE_RESERVED_CALIB0_INTERCEPT_INT_SHIFT) &
96622b6c9e8SAlexandre Courbot MASK(FUSE_RESERVED_CALIB0_INTERCEPT_INT_WIDTH)) * 1000 +
96722b6c9e8SAlexandre Courbot ((val >> FUSE_RESERVED_CALIB0_INTERCEPT_FRAC_SHIFT) &
96822b6c9e8SAlexandre Courbot MASK(FUSE_RESERVED_CALIB0_INTERCEPT_FRAC_WIDTH)) * 100;
96922b6c9e8SAlexandre Courbot
97022b6c9e8SAlexandre Courbot nvkm_debug(subdev, "fused calibration data: slope %d, offs %d\n",
97122b6c9e8SAlexandre Courbot clk->uvdet_slope, clk->uvdet_offs);
97222b6c9e8SAlexandre Courbot return 0;
97322b6c9e8SAlexandre Courbot }
97422b6c9e8SAlexandre Courbot
97522b6c9e8SAlexandre Courbot static int
gm20b_clk_init_safe_fmax(struct gm20b_clk * clk)97622b6c9e8SAlexandre Courbot gm20b_clk_init_safe_fmax(struct gm20b_clk *clk)
97722b6c9e8SAlexandre Courbot {
97822b6c9e8SAlexandre Courbot struct nvkm_subdev *subdev = &clk->base.base.subdev;
97922b6c9e8SAlexandre Courbot struct nvkm_volt *volt = subdev->device->volt;
98022b6c9e8SAlexandre Courbot struct nvkm_pstate *pstates = clk->base.base.func->pstates;
98122b6c9e8SAlexandre Courbot int nr_pstates = clk->base.base.func->nr_pstates;
98222b6c9e8SAlexandre Courbot int vmin, id = 0;
98322b6c9e8SAlexandre Courbot u32 fmax = 0;
98422b6c9e8SAlexandre Courbot int i;
98522b6c9e8SAlexandre Courbot
98622b6c9e8SAlexandre Courbot /* find lowest voltage we can use */
98722b6c9e8SAlexandre Courbot vmin = volt->vid[0].uv;
98822b6c9e8SAlexandre Courbot for (i = 1; i < volt->vid_nr; i++) {
98922b6c9e8SAlexandre Courbot if (volt->vid[i].uv <= vmin) {
99022b6c9e8SAlexandre Courbot vmin = volt->vid[i].uv;
99122b6c9e8SAlexandre Courbot id = volt->vid[i].vid;
99222b6c9e8SAlexandre Courbot }
99322b6c9e8SAlexandre Courbot }
99422b6c9e8SAlexandre Courbot
99522b6c9e8SAlexandre Courbot /* find max frequency at this voltage */
99622b6c9e8SAlexandre Courbot for (i = 0; i < nr_pstates; i++)
99722b6c9e8SAlexandre Courbot if (pstates[i].base.voltage == id)
99822b6c9e8SAlexandre Courbot fmax = max(fmax,
99922b6c9e8SAlexandre Courbot pstates[i].base.domain[nv_clk_src_gpc]);
100022b6c9e8SAlexandre Courbot
100122b6c9e8SAlexandre Courbot if (!fmax) {
100222b6c9e8SAlexandre Courbot nvkm_error(subdev, "failed to evaluate safe fmax\n");
100322b6c9e8SAlexandre Courbot return -EINVAL;
100422b6c9e8SAlexandre Courbot }
100522b6c9e8SAlexandre Courbot
100622b6c9e8SAlexandre Courbot /* we are safe at 90% of the max frequency */
100722b6c9e8SAlexandre Courbot clk->safe_fmax_vmin = fmax * (100 - 10) / 100;
100822b6c9e8SAlexandre Courbot nvkm_debug(subdev, "safe fmax @ vmin = %u Khz\n", clk->safe_fmax_vmin);
100922b6c9e8SAlexandre Courbot
101022b6c9e8SAlexandre Courbot return 0;
101122b6c9e8SAlexandre Courbot }
101222b6c9e8SAlexandre Courbot
101322b6c9e8SAlexandre Courbot int
gm20b_clk_new(struct nvkm_device * device,enum nvkm_subdev_type type,int inst,struct nvkm_clk ** pclk)101498fd7f83SBen Skeggs gm20b_clk_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst,
101598fd7f83SBen Skeggs struct nvkm_clk **pclk)
101622b6c9e8SAlexandre Courbot {
101722b6c9e8SAlexandre Courbot struct nvkm_device_tegra *tdev = device->func->tegra(device);
101822b6c9e8SAlexandre Courbot struct gm20b_clk *clk;
101922b6c9e8SAlexandre Courbot struct nvkm_subdev *subdev;
102022b6c9e8SAlexandre Courbot struct gk20a_clk_pllg_params *clk_params;
102122b6c9e8SAlexandre Courbot int ret;
102222b6c9e8SAlexandre Courbot
102322b6c9e8SAlexandre Courbot /* Speedo 0 GPUs cannot use noise-aware PLL */
102422b6c9e8SAlexandre Courbot if (tdev->gpu_speedo_id == 0)
102598fd7f83SBen Skeggs return gm20b_clk_new_speedo0(device, type, inst, pclk);
102622b6c9e8SAlexandre Courbot
102722b6c9e8SAlexandre Courbot /* Speedo >= 1, use NAPLL */
102822b6c9e8SAlexandre Courbot clk = kzalloc(sizeof(*clk) + sizeof(*clk_params), GFP_KERNEL);
102922b6c9e8SAlexandre Courbot if (!clk)
103022b6c9e8SAlexandre Courbot return -ENOMEM;
103122b6c9e8SAlexandre Courbot *pclk = &clk->base.base;
103222b6c9e8SAlexandre Courbot subdev = &clk->base.base.subdev;
103322b6c9e8SAlexandre Courbot
103422b6c9e8SAlexandre Courbot /* duplicate the clock parameters since we will patch them below */
103522b6c9e8SAlexandre Courbot clk_params = (void *) (clk + 1);
103622b6c9e8SAlexandre Courbot *clk_params = gm20b_pllg_params;
103798fd7f83SBen Skeggs ret = gk20a_clk_ctor(device, type, inst, &gm20b_clk, clk_params, &clk->base);
103822b6c9e8SAlexandre Courbot if (ret)
103922b6c9e8SAlexandre Courbot return ret;
104022b6c9e8SAlexandre Courbot
104122b6c9e8SAlexandre Courbot /*
104222b6c9e8SAlexandre Courbot * NAPLL can only work with max_u, clamp the m range so
104322b6c9e8SAlexandre Courbot * gk20a_pllg_calc_mnp always uses it
104422b6c9e8SAlexandre Courbot */
104522b6c9e8SAlexandre Courbot clk_params->max_m = clk_params->min_m = DIV_ROUND_UP(clk_params->max_u,
104622b6c9e8SAlexandre Courbot (clk->base.parent_rate / KHZ));
104722b6c9e8SAlexandre Courbot if (clk_params->max_m == 0) {
104822b6c9e8SAlexandre Courbot nvkm_warn(subdev, "cannot use NAPLL, using legacy clock...\n");
104922b6c9e8SAlexandre Courbot kfree(clk);
105098fd7f83SBen Skeggs return gm20b_clk_new_speedo0(device, type, inst, pclk);
105122b6c9e8SAlexandre Courbot }
105222b6c9e8SAlexandre Courbot
105322b6c9e8SAlexandre Courbot clk->base.pl_to_div = pl_to_div;
105422b6c9e8SAlexandre Courbot clk->base.div_to_pl = div_to_pl;
105522b6c9e8SAlexandre Courbot
105622b6c9e8SAlexandre Courbot clk->dvfs_params = &gm20b_dvfs_params;
105722b6c9e8SAlexandre Courbot
105822b6c9e8SAlexandre Courbot ret = gm20b_clk_init_fused_params(clk);
105922b6c9e8SAlexandre Courbot /*
106022b6c9e8SAlexandre Courbot * we will calibrate during init - should never happen on
106122b6c9e8SAlexandre Courbot * prod parts
106222b6c9e8SAlexandre Courbot */
106322b6c9e8SAlexandre Courbot if (ret)
106422b6c9e8SAlexandre Courbot nvkm_warn(subdev, "no fused calibration parameters\n");
106522b6c9e8SAlexandre Courbot
106622b6c9e8SAlexandre Courbot ret = gm20b_clk_init_safe_fmax(clk);
106722b6c9e8SAlexandre Courbot if (ret)
106822b6c9e8SAlexandre Courbot return ret;
106922b6c9e8SAlexandre Courbot
107022b6c9e8SAlexandre Courbot return 0;
107122b6c9e8SAlexandre Courbot }
1072