Commit 22b6c9e8 authored by Alexandre Courbot's avatar Alexandre Courbot Committed by Ben Skeggs

drm/nouveau/clk/gm20b: add glitchless and DFS support

This patch adds support for advanced features supported by the
Noise-Aware PLL of Maxwell. Glitchless switch allows the PL field to be
updated without disabling the PLL first if the SYNC_MODE bit of the CFG
register is set.

More significantly, DFS allows the PLL to monitor the actual input
voltage and to dynamically lower the output frequency accordingly. This
allows the clock to be more tolerant of lower voltages.

These improvements are only supported for Tegra speedos >= 1.

Also add the voltage table that is suitable for GM20B's NAPLL. This
change needs to be done atomically for the right voltages to be used by
the clock driver.

v2. Fix build on non-Tegra platforms
Signed-off-by: default avatarAlexandre Courbot <acourbot@nvidia.com>
Signed-off-by: default avatarBen Skeggs <bskeggs@redhat.com>
parent 3786c415
......@@ -28,70 +28,6 @@
#include <core/tegra.h>
#include <subdev/timer.h>
#define KHZ (1000)
#define MHZ (KHZ * 1000)
#define MASK(w) ((1 << (w)) - 1)
#define GPCPLL_CFG (SYS_GPCPLL_CFG_BASE + 0)
#define GPCPLL_CFG_ENABLE BIT(0)
#define GPCPLL_CFG_IDDQ BIT(1)
#define GPCPLL_CFG_LOCK_DET_OFF BIT(4)
#define GPCPLL_CFG_LOCK BIT(17)
#define GPCPLL_COEFF (SYS_GPCPLL_CFG_BASE + 4)
#define GPCPLL_COEFF_M_SHIFT 0
#define GPCPLL_COEFF_M_WIDTH 8
#define GPCPLL_COEFF_N_SHIFT 8
#define GPCPLL_COEFF_N_WIDTH 8
#define GPCPLL_COEFF_P_SHIFT 16
#define GPCPLL_COEFF_P_WIDTH 6
#define GPCPLL_CFG2 (SYS_GPCPLL_CFG_BASE + 0xc)
#define GPCPLL_CFG2_SETUP2_SHIFT 16
#define GPCPLL_CFG2_PLL_STEPA_SHIFT 24
#define GPCPLL_CFG3 (SYS_GPCPLL_CFG_BASE + 0x18)
#define GPCPLL_CFG3_PLL_STEPB_SHIFT 16
#define GPC_BCASE_GPCPLL_CFG_BASE 0x00132800
#define GPCPLL_NDIV_SLOWDOWN (SYS_GPCPLL_CFG_BASE + 0x1c)
#define GPCPLL_NDIV_SLOWDOWN_NDIV_LO_SHIFT 0
#define GPCPLL_NDIV_SLOWDOWN_NDIV_MID_SHIFT 8
#define GPCPLL_NDIV_SLOWDOWN_STEP_SIZE_LO2MID_SHIFT 16
#define GPCPLL_NDIV_SLOWDOWN_SLOWDOWN_USING_PLL_SHIFT 22
#define GPCPLL_NDIV_SLOWDOWN_EN_DYNRAMP_SHIFT 31
#define SEL_VCO (SYS_GPCPLL_CFG_BASE + 0x100)
#define SEL_VCO_GPC2CLK_OUT_SHIFT 0
#define GPC2CLK_OUT (SYS_GPCPLL_CFG_BASE + 0x250)
#define GPC2CLK_OUT_SDIV14_INDIV4_WIDTH 1
#define GPC2CLK_OUT_SDIV14_INDIV4_SHIFT 31
#define GPC2CLK_OUT_SDIV14_INDIV4_MODE 1
#define GPC2CLK_OUT_VCODIV_WIDTH 6
#define GPC2CLK_OUT_VCODIV_SHIFT 8
#define GPC2CLK_OUT_VCODIV1 0
#define GPC2CLK_OUT_VCODIV2 2
#define GPC2CLK_OUT_VCODIV_MASK (MASK(GPC2CLK_OUT_VCODIV_WIDTH) << \
GPC2CLK_OUT_VCODIV_SHIFT)
#define GPC2CLK_OUT_BYPDIV_WIDTH 6
#define GPC2CLK_OUT_BYPDIV_SHIFT 0
#define GPC2CLK_OUT_BYPDIV31 0x3c
#define GPC2CLK_OUT_INIT_MASK ((MASK(GPC2CLK_OUT_SDIV14_INDIV4_WIDTH) << \
GPC2CLK_OUT_SDIV14_INDIV4_SHIFT)\
| (MASK(GPC2CLK_OUT_VCODIV_WIDTH) << GPC2CLK_OUT_VCODIV_SHIFT)\
| (MASK(GPC2CLK_OUT_BYPDIV_WIDTH) << GPC2CLK_OUT_BYPDIV_SHIFT))
#define GPC2CLK_OUT_INIT_VAL ((GPC2CLK_OUT_SDIV14_INDIV4_MODE << \
GPC2CLK_OUT_SDIV14_INDIV4_SHIFT) \
| (GPC2CLK_OUT_VCODIV1 << GPC2CLK_OUT_VCODIV_SHIFT) \
| (GPC2CLK_OUT_BYPDIV31 << GPC2CLK_OUT_BYPDIV_SHIFT))
#define GPC_BCAST_NDIV_SLOWDOWN_DEBUG (GPC_BCASE_GPCPLL_CFG_BASE + 0xa0)
#define GPC_BCAST_NDIV_SLOWDOWN_DEBUG_PLL_DYNRAMP_DONE_SYNCED_SHIFT 24
#define GPC_BCAST_NDIV_SLOWDOWN_DEBUG_PLL_DYNRAMP_DONE_SYNCED_MASK \
(0x1 << GPC_BCAST_NDIV_SLOWDOWN_DEBUG_PLL_DYNRAMP_DONE_SYNCED_SHIFT)
static const u8 _pl_to_div[] = {
/* PL: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14 */
/* p: */ 1, 2, 3, 4, 5, 6, 8, 10, 12, 16, 12, 16, 20, 24, 32,
......@@ -125,7 +61,7 @@ static const struct gk20a_clk_pllg_params gk20a_pllg_params = {
.min_pl = 1, .max_pl = 32,
};
static void
void
gk20a_pllg_read_mnp(struct gk20a_clk *clk, struct gk20a_pll *pll)
{
struct nvkm_device *device = clk->base.subdev.device;
......@@ -137,7 +73,7 @@ gk20a_pllg_read_mnp(struct gk20a_clk *clk, struct gk20a_pll *pll)
pll->pl = (val >> GPCPLL_COEFF_P_SHIFT) & MASK(GPCPLL_COEFF_P_WIDTH);
}
static void
void
gk20a_pllg_write_mnp(struct gk20a_clk *clk, const struct gk20a_pll *pll)
{
struct nvkm_device *device = clk->base.subdev.device;
......@@ -149,7 +85,7 @@ gk20a_pllg_write_mnp(struct gk20a_clk *clk, const struct gk20a_pll *pll)
nvkm_wr32(device, GPCPLL_COEFF, val);
}
static u32
u32
gk20a_pllg_calc_rate(struct gk20a_clk *clk, struct gk20a_pll *pll)
{
u32 rate;
......@@ -161,14 +97,7 @@ gk20a_pllg_calc_rate(struct gk20a_clk *clk, struct gk20a_pll *pll)
return rate / divider / 2;
}
static u32
gk20a_pllg_n_lo(struct gk20a_clk *clk, struct gk20a_pll *pll)
{
return DIV_ROUND_UP(pll->m * clk->params->min_vco,
clk->parent_rate / KHZ);
}
static int
int
gk20a_pllg_calc_mnp(struct gk20a_clk *clk, unsigned long rate,
struct gk20a_pll *pll)
{
......@@ -323,16 +252,6 @@ gk20a_pllg_slide(struct gk20a_clk *clk, u32 n)
return ret;
}
static bool
gk20a_pllg_is_enabled(struct gk20a_clk *clk)
{
struct nvkm_device *device = clk->base.subdev.device;
u32 val;
val = nvkm_rd32(device, GPCPLL_CFG);
return val & GPCPLL_CFG_ENABLE;
}
static int
gk20a_pllg_enable(struct gk20a_clk *clk)
{
......
......@@ -24,9 +24,79 @@
#ifndef __NVKM_CLK_GK20A_H__
#define __NVKM_CLK_GK20A_H__
#define KHZ (1000)
#define MHZ (KHZ * 1000)
#define MASK(w) ((1 << (w)) - 1)
#define GK20A_CLK_GPC_MDIV 1000
#define SYS_GPCPLL_CFG_BASE 0x00137000
#define GPCPLL_CFG (SYS_GPCPLL_CFG_BASE + 0)
#define GPCPLL_CFG_ENABLE BIT(0)
#define GPCPLL_CFG_IDDQ BIT(1)
#define GPCPLL_CFG_LOCK_DET_OFF BIT(4)
#define GPCPLL_CFG_LOCK BIT(17)
#define GPCPLL_CFG2 (SYS_GPCPLL_CFG_BASE + 0xc)
#define GPCPLL_CFG2_SETUP2_SHIFT 16
#define GPCPLL_CFG2_PLL_STEPA_SHIFT 24
#define GPCPLL_CFG3 (SYS_GPCPLL_CFG_BASE + 0x18)
#define GPCPLL_CFG3_VCO_CTRL_SHIFT 0
#define GPCPLL_CFG3_VCO_CTRL_WIDTH 9
#define GPCPLL_CFG3_VCO_CTRL_MASK \
(MASK(GPCPLL_CFG3_VCO_CTRL_WIDTH) << GPCPLL_CFG3_VCO_CTRL_SHIFT)
#define GPCPLL_CFG3_PLL_STEPB_SHIFT 16
#define GPCPLL_CFG3_PLL_STEPB_WIDTH 8
#define GPCPLL_COEFF (SYS_GPCPLL_CFG_BASE + 4)
#define GPCPLL_COEFF_M_SHIFT 0
#define GPCPLL_COEFF_M_WIDTH 8
#define GPCPLL_COEFF_N_SHIFT 8
#define GPCPLL_COEFF_N_WIDTH 8
#define GPCPLL_COEFF_N_MASK \
(MASK(GPCPLL_COEFF_N_WIDTH) << GPCPLL_COEFF_N_SHIFT)
#define GPCPLL_COEFF_P_SHIFT 16
#define GPCPLL_COEFF_P_WIDTH 6
#define GPCPLL_NDIV_SLOWDOWN (SYS_GPCPLL_CFG_BASE + 0x1c)
#define GPCPLL_NDIV_SLOWDOWN_NDIV_LO_SHIFT 0
#define GPCPLL_NDIV_SLOWDOWN_NDIV_MID_SHIFT 8
#define GPCPLL_NDIV_SLOWDOWN_STEP_SIZE_LO2MID_SHIFT 16
#define GPCPLL_NDIV_SLOWDOWN_SLOWDOWN_USING_PLL_SHIFT 22
#define GPCPLL_NDIV_SLOWDOWN_EN_DYNRAMP_SHIFT 31
#define GPC_BCAST_GPCPLL_CFG_BASE 0x00132800
#define GPC_BCAST_NDIV_SLOWDOWN_DEBUG (GPC_BCAST_GPCPLL_CFG_BASE + 0xa0)
#define GPC_BCAST_NDIV_SLOWDOWN_DEBUG_PLL_DYNRAMP_DONE_SYNCED_SHIFT 24
#define GPC_BCAST_NDIV_SLOWDOWN_DEBUG_PLL_DYNRAMP_DONE_SYNCED_MASK \
(0x1 << GPC_BCAST_NDIV_SLOWDOWN_DEBUG_PLL_DYNRAMP_DONE_SYNCED_SHIFT)
#define SEL_VCO (SYS_GPCPLL_CFG_BASE + 0x100)
#define SEL_VCO_GPC2CLK_OUT_SHIFT 0
#define GPC2CLK_OUT (SYS_GPCPLL_CFG_BASE + 0x250)
#define GPC2CLK_OUT_SDIV14_INDIV4_WIDTH 1
#define GPC2CLK_OUT_SDIV14_INDIV4_SHIFT 31
#define GPC2CLK_OUT_SDIV14_INDIV4_MODE 1
#define GPC2CLK_OUT_VCODIV_WIDTH 6
#define GPC2CLK_OUT_VCODIV_SHIFT 8
#define GPC2CLK_OUT_VCODIV1 0
#define GPC2CLK_OUT_VCODIV2 2
#define GPC2CLK_OUT_VCODIV_MASK (MASK(GPC2CLK_OUT_VCODIV_WIDTH) << \
GPC2CLK_OUT_VCODIV_SHIFT)
#define GPC2CLK_OUT_BYPDIV_WIDTH 6
#define GPC2CLK_OUT_BYPDIV_SHIFT 0
#define GPC2CLK_OUT_BYPDIV31 0x3c
#define GPC2CLK_OUT_INIT_MASK ((MASK(GPC2CLK_OUT_SDIV14_INDIV4_WIDTH) << \
GPC2CLK_OUT_SDIV14_INDIV4_SHIFT)\
| (MASK(GPC2CLK_OUT_VCODIV_WIDTH) << GPC2CLK_OUT_VCODIV_SHIFT)\
| (MASK(GPC2CLK_OUT_BYPDIV_WIDTH) << GPC2CLK_OUT_BYPDIV_SHIFT))
#define GPC2CLK_OUT_INIT_VAL ((GPC2CLK_OUT_SDIV14_INDIV4_MODE << \
GPC2CLK_OUT_SDIV14_INDIV4_SHIFT) \
| (GPC2CLK_OUT_VCODIV1 << GPC2CLK_OUT_VCODIV_SHIFT) \
| (GPC2CLK_OUT_BYPDIV31 << GPC2CLK_OUT_BYPDIV_SHIFT))
/* All frequencies in Khz */
struct gk20a_clk_pllg_params {
......@@ -54,6 +124,28 @@ struct gk20a_clk {
};
#define gk20a_clk(p) container_of((p), struct gk20a_clk, base)
u32 gk20a_pllg_calc_rate(struct gk20a_clk *, struct gk20a_pll *);
int gk20a_pllg_calc_mnp(struct gk20a_clk *, unsigned long, struct gk20a_pll *);
void gk20a_pllg_read_mnp(struct gk20a_clk *, struct gk20a_pll *);
void gk20a_pllg_write_mnp(struct gk20a_clk *, const struct gk20a_pll *);
static inline bool
gk20a_pllg_is_enabled(struct gk20a_clk *clk)
{
struct nvkm_device *device = clk->base.subdev.device;
u32 val;
val = nvkm_rd32(device, GPCPLL_CFG);
return val & GPCPLL_CFG_ENABLE;
}
static inline u32
gk20a_pllg_n_lo(struct gk20a_clk *clk, struct gk20a_pll *pll)
{
return DIV_ROUND_UP(pll->m * clk->params->min_vco,
clk->parent_rate / KHZ);
}
int gk20a_clk_ctor(struct nvkm_device *, int, const struct nvkm_clk_func *,
const struct gk20a_clk_pllg_params *, struct gk20a_clk *);
void gk20a_clk_fini(struct nvkm_clk *);
......
......@@ -41,6 +41,23 @@ const struct cvb_coef gm20b_cvb_coef[] = {
/* 921600 */ { 2647676, -106455, 1632 },
};
static const struct cvb_coef gm20b_na_cvb_coef[] = {
/* KHz, c0, c1, c2, c3, c4, c5 */
/* 76800 */ { 814294, 8144, -940, 808, -21583, 226 },
/* 153600 */ { 856185, 8144, -940, 808, -21583, 226 },
/* 230400 */ { 898077, 8144, -940, 808, -21583, 226 },
/* 307200 */ { 939968, 8144, -940, 808, -21583, 226 },
/* 384000 */ { 981860, 8144, -940, 808, -21583, 226 },
/* 460800 */ { 1023751, 8144, -940, 808, -21583, 226 },
/* 537600 */ { 1065642, 8144, -940, 808, -21583, 226 },
/* 614400 */ { 1107534, 8144, -940, 808, -21583, 226 },
/* 691200 */ { 1149425, 8144, -940, 808, -21583, 226 },
/* 768000 */ { 1191317, 8144, -940, 808, -21583, 226 },
/* 844800 */ { 1233208, 8144, -940, 808, -21583, 226 },
/* 921600 */ { 1275100, 8144, -940, 808, -21583, 226 },
/* 998400 */ { 1316991, 8144, -940, 808, -21583, 226 },
};
const u32 speedo_to_vmin[] = {
/* 0, 1, 2, 3, 4, */
950000, 840000, 818750, 840000, 810000,
......@@ -66,6 +83,10 @@ gm20b_volt_new(struct nvkm_device *device, int index, struct nvkm_volt **pvolt)
vmin = speedo_to_vmin[tdev->gpu_speedo_id];
return gk20a_volt_ctor(device, index, gm20b_cvb_coef,
ARRAY_SIZE(gm20b_cvb_coef), vmin, volt);
if (tdev->gpu_speedo_id >= 1)
return gk20a_volt_ctor(device, index, gm20b_na_cvb_coef,
ARRAY_SIZE(gm20b_na_cvb_coef), vmin, volt);
else
return gk20a_volt_ctor(device, index, gm20b_cvb_coef,
ARRAY_SIZE(gm20b_cvb_coef), vmin, volt);
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment