Alexandre Courbot
2016-Mar-11 14:32 UTC
[Nouveau] [PATCH 00/16] clk/gm20b: add basic driver
This series does some refactoring in the GK20A's volt and clk drivers (fixing a few things while we are at it) to let GM20B benefit from the GK20A's logic with which it is compatible. GM20B is capable of more sophisticated (and power-efficient) reclocking which will follow later. Even after this more fancy reclocking is merged, the present logic will remain used in the lowest speedo of Tegra X1. Ben, it would be super-duper great if you could take these in for 4.6. Not too big a deal if you cannot however. Alexandre Courbot (15): volt/gk20a: split constructor volt: add GM20B driver clk/gk20a: convert parameters to Khz clk/gk20a: reorganize variables in gk20a_pllg_calc_mnp() clk/gk20a: rename enable/disable functions clk/gk20a: fix VCO bit mask clk/gk20a: only compute n_lo if needed clk/gk20a: only restore divider to 1:1 if needed clk/gk20a: emit parent rate as debug message clk/gk20a: put mnp values into their own struct clk/gk20a: abstract pl_to_div clk/gk20a: split gk20a_clk_new() clk/gk20a: set lowest frequency during init() clk/gk20a: share reusable structures/functions clk/gm20b: add basic driver Vince Hsu (1): volt/gk20a: share reusable members & functions drm/nouveau/include/nvkm/subdev/clk.h | 1 + drm/nouveau/include/nvkm/subdev/volt.h | 1 + drm/nouveau/nvkm/engine/device/base.c | 2 + drm/nouveau/nvkm/subdev/clk/Kbuild | 1 + drm/nouveau/nvkm/subdev/clk/gk20a.c | 292 ++++++++++++++++++--------------- drm/nouveau/nvkm/subdev/clk/gk20a.h | 65 ++++++++ drm/nouveau/nvkm/subdev/clk/gm20b.c | 198 ++++++++++++++++++++++ drm/nouveau/nvkm/subdev/volt/Kbuild | 1 + drm/nouveau/nvkm/subdev/volt/gk20a.c | 57 +++---- drm/nouveau/nvkm/subdev/volt/gk20a.h | 49 ++++++ drm/nouveau/nvkm/subdev/volt/gm20b.c | 56 +++++++ 11 files changed, 560 insertions(+), 163 deletions(-) create mode 100644 drm/nouveau/nvkm/subdev/clk/gk20a.h create mode 100644 drm/nouveau/nvkm/subdev/clk/gm20b.c create mode 100644 drm/nouveau/nvkm/subdev/volt/gk20a.h create mode 100644 drm/nouveau/nvkm/subdev/volt/gm20b.c -- 2.7.2
Alexandre Courbot
2016-Mar-11 14:32 UTC
[Nouveau] [PATCH 01/16] volt/gk20a: share reusable members & functions
From: Vince Hsu <vinceh at nvidia.com> The CVB calculation and voltage setting functions can be reused for the future chips. So move the declaration to gk20a.h. Signed-off-by: Vince Hsu <vinceh at nvidia.com> Signed-off-by: Alexandre Courbot <acourbot at nvidia.com> --- drm/nouveau/nvkm/subdev/volt/gk20a.c | 24 +++++-------------- drm/nouveau/nvkm/subdev/volt/gk20a.h | 45 ++++++++++++++++++++++++++++++++++++ 2 files changed, 51 insertions(+), 18 deletions(-) create mode 100644 drm/nouveau/nvkm/subdev/volt/gk20a.h diff --git a/drm/nouveau/nvkm/subdev/volt/gk20a.c b/drm/nouveau/nvkm/subdev/volt/gk20a.c index fd56c64..bdb22e1 100644 --- a/drm/nouveau/nvkm/subdev/volt/gk20a.c +++ b/drm/nouveau/nvkm/subdev/volt/gk20a.c @@ -24,21 +24,9 @@ #include <core/tegra.h> -struct cvb_coef { - int c0; - int c1; - int c2; - int c3; - int c4; - int c5; -}; - -struct gk20a_volt { - struct nvkm_volt base; - struct regulator *vdd; -}; +#include "gk20a.h" -const struct cvb_coef gk20a_cvb_coef[] = { +static const struct cvb_coef gk20a_cvb_coef[] = { /* MHz, c0, c1, c2, c3, c4, c5 */ /* 72 */ { 1209886, -36468, 515, 417, -13123, 203}, /* 108 */ { 1130804, -27659, 296, 298, -10834, 221}, @@ -89,7 +77,7 @@ gk20a_volt_get_cvb_t_voltage(int speedo, int temp, int s_scale, int t_scale, return mv; } -static int +int gk20a_volt_calc_voltage(const struct cvb_coef *coef, int speedo) { int mv; @@ -100,7 +88,7 @@ gk20a_volt_calc_voltage(const struct cvb_coef *coef, int speedo) return mv * 1000; } -static int +int gk20a_volt_vid_get(struct nvkm_volt *base) { struct gk20a_volt *volt = gk20a_volt(base); @@ -115,7 +103,7 @@ gk20a_volt_vid_get(struct nvkm_volt *base) return -EINVAL; } -static int +int gk20a_volt_vid_set(struct nvkm_volt *base, u8 vid) { struct gk20a_volt *volt = gk20a_volt(base); @@ -125,7 +113,7 @@ gk20a_volt_vid_set(struct nvkm_volt *base, u8 vid) return regulator_set_voltage(volt->vdd, volt->base.vid[vid].uv, 1200000); } -static int +int gk20a_volt_set_id(struct nvkm_volt *base, u8 id, int condition) { struct gk20a_volt *volt = gk20a_volt(base); diff --git a/drm/nouveau/nvkm/subdev/volt/gk20a.h b/drm/nouveau/nvkm/subdev/volt/gk20a.h new file mode 100644 index 0000000..e1c62d1 --- /dev/null +++ b/drm/nouveau/nvkm/subdev/volt/gk20a.h @@ -0,0 +1,45 @@ +/* + * 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. + */ + +#ifndef __GK20A_VOLT_H__ +#define __GK20A_VOLT_H__ + +struct cvb_coef { + int c0; + int c1; + int c2; + int c3; + int c4; + int c5; +}; + +struct gk20a_volt { + struct nvkm_volt base; + struct regulator *vdd; +}; + +int gk20a_volt_calc_voltage(const struct cvb_coef *coef, int speedo); +int gk20a_volt_vid_get(struct nvkm_volt *volt); +int gk20a_volt_vid_set(struct nvkm_volt *volt, u8 vid); +int gk20a_volt_set_id(struct nvkm_volt *volt, u8 id, int condition); + +#endif -- 2.7.2
Alexandre Courbot
2016-Mar-11 14:32 UTC
[Nouveau] [PATCH 02/16] volt/gk20a: split constructor
Split the constructor function so we can reuse the same logic in other chips. Signed-off-by: Alexandre Courbot <acourbot at nvidia.com> --- drm/nouveau/nvkm/subdev/volt/gk20a.c | 33 +++++++++++++++++++++------------ drm/nouveau/nvkm/subdev/volt/gk20a.h | 4 ++++ 2 files changed, 25 insertions(+), 12 deletions(-) diff --git a/drm/nouveau/nvkm/subdev/volt/gk20a.c b/drm/nouveau/nvkm/subdev/volt/gk20a.c index bdb22e1..d554455 100644 --- a/drm/nouveau/nvkm/subdev/volt/gk20a.c +++ b/drm/nouveau/nvkm/subdev/volt/gk20a.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, NVIDIA CORPORATION. All rights reserved. + * Copyright (c) 2014-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"), @@ -143,30 +143,25 @@ gk20a_volt = { }; int -gk20a_volt_new(struct nvkm_device *device, int index, struct nvkm_volt **pvolt) +_gk20a_volt_ctor(struct nvkm_device *device, int index, + const struct cvb_coef *coefs, int nb_coefs, + struct gk20a_volt *volt) { struct nvkm_device_tegra *tdev = device->func->tegra(device); - struct gk20a_volt *volt; int i, uv; - if (!(volt = kzalloc(sizeof(*volt), GFP_KERNEL))) - return -ENOMEM; - nvkm_volt_ctor(&gk20a_volt, device, index, &volt->base); - *pvolt = &volt->base; uv = regulator_get_voltage(tdev->vdd); - nvkm_info(&volt->base.subdev, "The default voltage is %duV\n", uv); + nvkm_debug(&volt->base.subdev, "the default voltage is %duV\n", uv); volt->vdd = tdev->vdd; - volt->base.vid_nr = ARRAY_SIZE(gk20a_cvb_coef); - nvkm_debug(&volt->base.subdev, "%s - vid_nr = %d\n", __func__, - volt->base.vid_nr); + volt->base.vid_nr = nb_coefs; for (i = 0; i < volt->base.vid_nr; i++) { volt->base.vid[i].vid = i; volt->base.vid[i].uv - gk20a_volt_calc_voltage(&gk20a_cvb_coef[i], + gk20a_volt_calc_voltage(&coefs[i], tdev->gpu_speedo); nvkm_debug(&volt->base.subdev, "%2d: vid=%d, uv=%d\n", i, volt->base.vid[i].vid, volt->base.vid[i].uv); @@ -174,3 +169,17 @@ gk20a_volt_new(struct nvkm_device *device, int index, struct nvkm_volt **pvolt) return 0; } + +int +gk20a_volt_new(struct nvkm_device *device, int index, struct nvkm_volt **pvolt) +{ + struct gk20a_volt *volt; + + volt = kzalloc(sizeof(*volt), GFP_KERNEL); + if (!volt) + return -ENOMEM; + *pvolt = &volt->base; + + return _gk20a_volt_ctor(device, index, gk20a_cvb_coef, + ARRAY_SIZE(gk20a_cvb_coef), volt); +} diff --git a/drm/nouveau/nvkm/subdev/volt/gk20a.h b/drm/nouveau/nvkm/subdev/volt/gk20a.h index e1c62d1..0fa3b50 100644 --- a/drm/nouveau/nvkm/subdev/volt/gk20a.h +++ b/drm/nouveau/nvkm/subdev/volt/gk20a.h @@ -37,6 +37,10 @@ struct gk20a_volt { struct regulator *vdd; }; +int _gk20a_volt_ctor(struct nvkm_device *device, int index, + const struct cvb_coef *coefs, int nb_coefs, + struct gk20a_volt *volt); + int gk20a_volt_calc_voltage(const struct cvb_coef *coef, int speedo); int gk20a_volt_vid_get(struct nvkm_volt *volt); int gk20a_volt_vid_set(struct nvkm_volt *volt, u8 vid); -- 2.7.2
Add basic GM20B volt driver that reuses the GK20A logic. Signed-off-by: Alexandre Courbot <acourbot at nvidia.com> --- drm/nouveau/include/nvkm/subdev/volt.h | 1 + drm/nouveau/nvkm/engine/device/base.c | 1 + drm/nouveau/nvkm/subdev/volt/Kbuild | 1 + drm/nouveau/nvkm/subdev/volt/gm20b.c | 56 ++++++++++++++++++++++++++++++++++ 4 files changed, 59 insertions(+) create mode 100644 drm/nouveau/nvkm/subdev/volt/gm20b.c diff --git a/drm/nouveau/include/nvkm/subdev/volt.h b/drm/nouveau/include/nvkm/subdev/volt.h index b458d04..feff55c 100644 --- a/drm/nouveau/include/nvkm/subdev/volt.h +++ b/drm/nouveau/include/nvkm/subdev/volt.h @@ -20,4 +20,5 @@ int nvkm_volt_set_id(struct nvkm_volt *, u8 id, int condition); int nv40_volt_new(struct nvkm_device *, int, struct nvkm_volt **); int gk104_volt_new(struct nvkm_device *, int, struct nvkm_volt **); int gk20a_volt_new(struct nvkm_device *, int, struct nvkm_volt **); +int gm20b_volt_new(struct nvkm_device *, int, struct nvkm_volt **); #endif diff --git a/drm/nouveau/nvkm/engine/device/base.c b/drm/nouveau/nvkm/engine/device/base.c index 0a7b077..bfe2f4f 100644 --- a/drm/nouveau/nvkm/engine/device/base.c +++ b/drm/nouveau/nvkm/engine/device/base.c @@ -2093,6 +2093,7 @@ nv12b_chipset = { .secboot = gm20b_secboot_new, .timer = gk20a_timer_new, .ce[2] = gm200_ce_new, + .volt = gm20b_volt_new, .dma = gf119_dma_new, .fifo = gm20b_fifo_new, .gr = gm20b_gr_new, diff --git a/drm/nouveau/nvkm/subdev/volt/Kbuild b/drm/nouveau/nvkm/subdev/volt/Kbuild index b035c6e..c340762 100644 --- a/drm/nouveau/nvkm/subdev/volt/Kbuild +++ b/drm/nouveau/nvkm/subdev/volt/Kbuild @@ -3,3 +3,4 @@ nvkm-y += nvkm/subdev/volt/gpio.o nvkm-y += nvkm/subdev/volt/nv40.o nvkm-y += nvkm/subdev/volt/gk104.o nvkm-y += nvkm/subdev/volt/gk20a.o +nvkm-y += nvkm/subdev/volt/gm20b.o diff --git a/drm/nouveau/nvkm/subdev/volt/gm20b.c b/drm/nouveau/nvkm/subdev/volt/gm20b.c new file mode 100644 index 0000000..49b5ecb --- /dev/null +++ b/drm/nouveau/nvkm/subdev/volt/gm20b.c @@ -0,0 +1,56 @@ +/* + * 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 "priv.h" +#include "gk20a.h" + +#include <core/tegra.h> + +const struct cvb_coef gm20b_cvb_coef[] = { + /* KHz, c0, c1, c2 */ + /* 76800 */ { 1786666, -85625, 1632 }, + /* 153600 */ { 1846729, -87525, 1632 }, + /* 230400 */ { 1910480, -89425, 1632 }, + /* 307200 */ { 1977920, -91325, 1632 }, + /* 384000 */ { 2049049, -93215, 1632 }, + /* 460800 */ { 2122872, -95095, 1632 }, + /* 537600 */ { 2201331, -96985, 1632 }, + /* 614400 */ { 2283479, -98885, 1632 }, + /* 691200 */ { 2369315, -100785, 1632 }, + /* 768000 */ { 2458841, -102685, 1632 }, + /* 844800 */ { 2550821, -104555, 1632 }, + /* 921600 */ { 2647676, -106455, 1632 }, +}; + +int +gm20b_volt_new(struct nvkm_device *device, int index, struct nvkm_volt **pvolt) +{ + struct gk20a_volt *volt; + + volt = kzalloc(sizeof(*volt), GFP_KERNEL); + if (!volt) + return -ENOMEM; + *pvolt = &volt->base; + + return _gk20a_volt_ctor(device, index, gm20b_cvb_coef, + ARRAY_SIZE(gm20b_cvb_coef), volt); +} -- 2.7.2
Alexandre Courbot
2016-Mar-11 14:32 UTC
[Nouveau] [PATCH 04/16] clk/gk20a: convert parameters to Khz
Perform computations in Khz instead of Mhz for better precision. Signed-off-by: Alexandre Courbot <acourbot at nvidia.com> --- drm/nouveau/nvkm/subdev/clk/gk20a.c | 32 +++++++++++++++++--------------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/drm/nouveau/nvkm/subdev/clk/gk20a.c b/drm/nouveau/nvkm/subdev/clk/gk20a.c index 5da2aa8..eaf7939 100644 --- a/drm/nouveau/nvkm/subdev/clk/gk20a.c +++ b/drm/nouveau/nvkm/subdev/clk/gk20a.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, NVIDIA CORPORATION. All rights reserved. + * Copyright (c) 2014-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"), @@ -28,7 +28,8 @@ #include <core/tegra.h> #include <subdev/timer.h> -#define MHZ (1000 * 1000) +#define KHZ (1000) +#define MHZ (KHZ * 1000) #define MASK(w) ((1 << w) - 1) @@ -97,7 +98,7 @@ static const u8 pl_to_div[] = { /* p: */ 1, 2, 3, 4, 5, 6, 8, 10, 12, 16, 12, 16, 20, 24, 32, }; -/* All frequencies in Mhz */ +/* All frequencies in Khz */ struct gk20a_clk_pllg_params { u32 min_vco, max_vco; u32 min_u, max_u; @@ -107,8 +108,8 @@ struct gk20a_clk_pllg_params { }; static const struct gk20a_clk_pllg_params gk20a_pllg_params = { - .min_vco = 1000, .max_vco = 2064, - .min_u = 12, .max_u = 38, + .min_vco = 1000000, .max_vco = 2064000, + .min_u = 12000, .max_u = 38000, .min_m = 1, .max_m = 255, .min_n = 8, .max_n = 255, .min_pl = 1, .max_pl = 32, @@ -159,8 +160,8 @@ gk20a_pllg_calc_mnp(struct gk20a_clk *clk, unsigned long rate) u32 delta, lwv, best_delta = ~0; u32 pl; - target_clk_f = rate * 2 / MHZ; - ref_clk_f = clk->parent_rate / MHZ; + target_clk_f = rate * 2 / KHZ; + ref_clk_f = clk->parent_rate / KHZ; max_vco_f = clk->params->max_vco; min_vco_f = clk->params->min_vco; @@ -249,17 +250,18 @@ found_match: if (best_delta != 0) nvkm_debug(subdev, "no best match for target @ %dMHz on gpc_pll", - target_clk_f); + target_clk_f / KHZ); clk->m = best_m; clk->n = best_n; clk->pl = best_pl; - target_freq = gk20a_pllg_calc_rate(clk) / MHZ; + target_freq = gk20a_pllg_calc_rate(clk); nvkm_debug(subdev, "actual target freq %d MHz, M %d, N %d, PL %d(div%d)\n", - target_freq, clk->m, clk->n, clk->pl, pl_to_div[clk->pl]); + target_freq / MHZ, clk->m, clk->n, clk->pl, + pl_to_div[clk->pl]); return 0; } @@ -360,7 +362,7 @@ _gk20a_pllg_program_mnp(struct gk20a_clk *clk, bool allow_slide) /* slide down to NDIV_LO */ n_lo = DIV_ROUND_UP(m_old * clk->params->min_vco, - clk->parent_rate / MHZ); + clk->parent_rate / KHZ); if (allow_slide && (cfg & GPCPLL_CFG_ENABLE)) { int ret = gk20a_pllg_slide(clk, n_lo); @@ -393,7 +395,7 @@ _gk20a_pllg_program_mnp(struct gk20a_clk *clk, bool allow_slide) clk->m, clk->n, clk->pl); n_lo = DIV_ROUND_UP(clk->m * clk->params->min_vco, - clk->parent_rate / MHZ); + clk->parent_rate / KHZ); val = clk->m << GPCPLL_COEFF_M_SHIFT; val |= (allow_slide ? n_lo : clk->n) << GPCPLL_COEFF_N_SHIFT; val |= clk->pl << GPCPLL_COEFF_P_SHIFT; @@ -452,7 +454,7 @@ gk20a_pllg_disable(struct gk20a_clk *clk) coeff = nvkm_rd32(device, GPCPLL_COEFF); m = (coeff >> GPCPLL_COEFF_M_SHIFT) & MASK(GPCPLL_COEFF_M_WIDTH); n_lo = DIV_ROUND_UP(m * clk->params->min_vco, - clk->parent_rate / MHZ); + clk->parent_rate / KHZ); gk20a_pllg_slide(clk, n_lo); } @@ -663,7 +665,7 @@ gk20a_clk_new(struct nvkm_device *device, int index, struct nvkm_clk **pclk) clk->parent_rate = clk_get_rate(tdev->clk); ret = nvkm_clk_ctor(&gk20a_clk, device, index, true, &clk->base); - nvkm_info(&clk->base.subdev, "parent clock rate: %d Mhz\n", - clk->parent_rate / MHZ); + nvkm_info(&clk->base.subdev, "parent clock rate: %d Khz\n", + clk->parent_rate / KHZ); return ret; } -- 2.7.2
Alexandre Courbot
2016-Mar-11 14:32 UTC
[Nouveau] [PATCH 05/16] clk/gk20a: reorganize variables in gk20a_pllg_calc_mnp()
Move some variables declarations to the scope where they are actually used to make the code easier to follow. Signed-off-by: Alexandre Courbot <acourbot at nvidia.com> --- drm/nouveau/nvkm/subdev/clk/gk20a.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/drm/nouveau/nvkm/subdev/clk/gk20a.c b/drm/nouveau/nvkm/subdev/clk/gk20a.c index eaf7939..0c2b078 100644 --- a/drm/nouveau/nvkm/subdev/clk/gk20a.c +++ b/drm/nouveau/nvkm/subdev/clk/gk20a.c @@ -153,11 +153,9 @@ gk20a_pllg_calc_mnp(struct gk20a_clk *clk, unsigned long rate) u32 target_clk_f, ref_clk_f, target_freq; u32 min_vco_f, max_vco_f; u32 low_pl, high_pl, best_pl; - u32 target_vco_f, vco_f; + u32 target_vco_f; u32 best_m, best_n; - u32 u_f; - u32 m, n, n2; - u32 delta, lwv, best_delta = ~0; + u32 best_delta = ~0; u32 pl; target_clk_f = rate * 2 / KHZ; @@ -202,8 +200,12 @@ gk20a_pllg_calc_mnp(struct gk20a_clk *clk, unsigned long rate) /* Select lowest possible VCO */ for (pl = low_pl; pl <= high_pl; pl++) { + u32 m, n, n2; + target_vco_f = target_clk_f * pl_to_div[pl]; for (m = clk->params->min_m; m <= clk->params->max_m; m++) { + u32 u_f, vco_f; + u_f = ref_clk_f / m; if (u_f < clk->params->min_u) @@ -226,6 +228,8 @@ gk20a_pllg_calc_mnp(struct gk20a_clk *clk, unsigned long rate) vco_f = ref_clk_f * n / m; if (vco_f >= min_vco_f && vco_f <= max_vco_f) { + u32 delta, lwv; + lwv = (vco_f + (pl_to_div[pl] / 2)) / pl_to_div[pl]; delta = abs(lwv - target_clk_f); -- 2.7.2
Alexandre Courbot
2016-Mar-11 14:32 UTC
[Nouveau] [PATCH 06/16] clk/gk20a: rename enable/disable functions
gk20a_pllg_disable() is only used in the context of gk20a_clk_fini(). Move its body there and rename _gk20a_pllg_enable() and _gk20a_pllg_disable() to non-underscored versions. Signed-off-by: Alexandre Courbot <acourbot at nvidia.com> --- drm/nouveau/nvkm/subdev/clk/gk20a.c | 52 +++++++++++++++++-------------------- 1 file changed, 24 insertions(+), 28 deletions(-) diff --git a/drm/nouveau/nvkm/subdev/clk/gk20a.c b/drm/nouveau/nvkm/subdev/clk/gk20a.c index 0c2b078..b5a92f7 100644 --- a/drm/nouveau/nvkm/subdev/clk/gk20a.c +++ b/drm/nouveau/nvkm/subdev/clk/gk20a.c @@ -329,17 +329,19 @@ gk20a_pllg_slide(struct gk20a_clk *clk, u32 n) } static void -_gk20a_pllg_enable(struct gk20a_clk *clk) +gk20a_pllg_enable(struct gk20a_clk *clk) { struct nvkm_device *device = clk->base.subdev.device; + nvkm_mask(device, GPCPLL_CFG, GPCPLL_CFG_ENABLE, GPCPLL_CFG_ENABLE); nvkm_rd32(device, GPCPLL_CFG); } static void -_gk20a_pllg_disable(struct gk20a_clk *clk) +gk20a_pllg_disable(struct gk20a_clk *clk) { struct nvkm_device *device = clk->base.subdev.device; + nvkm_mask(device, GPCPLL_CFG, GPCPLL_CFG_ENABLE, 0); nvkm_rd32(device, GPCPLL_CFG); } @@ -393,7 +395,7 @@ _gk20a_pllg_program_mnp(struct gk20a_clk *clk, bool allow_slide) udelay(2); } - _gk20a_pllg_disable(clk); + gk20a_pllg_disable(clk); nvkm_debug(subdev, "%s: m=%d n=%d pl=%d\n", __func__, clk->m, clk->n, clk->pl); @@ -405,7 +407,7 @@ _gk20a_pllg_program_mnp(struct gk20a_clk *clk, bool allow_slide) val |= clk->pl << GPCPLL_COEFF_P_SHIFT; nvkm_wr32(device, GPCPLL_COEFF, val); - _gk20a_pllg_enable(clk); + gk20a_pllg_enable(clk); val = nvkm_rd32(device, GPCPLL_CFG); if (val & GPCPLL_CFG_LOCK_DET_OFF) { @@ -444,30 +446,6 @@ gk20a_pllg_program_mnp(struct gk20a_clk *clk) return err; } -static void -gk20a_pllg_disable(struct gk20a_clk *clk) -{ - struct nvkm_device *device = clk->base.subdev.device; - u32 val; - - /* slide to VCO min */ - val = nvkm_rd32(device, GPCPLL_CFG); - if (val & GPCPLL_CFG_ENABLE) { - u32 coeff, m, n_lo; - - coeff = nvkm_rd32(device, GPCPLL_COEFF); - m = (coeff >> GPCPLL_COEFF_M_SHIFT) & MASK(GPCPLL_COEFF_M_WIDTH); - n_lo = DIV_ROUND_UP(m * clk->params->min_vco, - clk->parent_rate / KHZ); - gk20a_pllg_slide(clk, n_lo); - } - - /* put PLL in bypass before disabling it */ - nvkm_mask(device, SEL_VCO, BIT(SEL_VCO_GPC2CLK_OUT_SHIFT), 0); - - _gk20a_pllg_disable(clk); -} - #define GK20A_CLK_GPC_MDIV 1000 static struct nvkm_pstate @@ -608,7 +586,25 @@ gk20a_clk_tidy(struct nvkm_clk *base) static void gk20a_clk_fini(struct nvkm_clk *base) { + struct nvkm_device *device = base->subdev.device; struct gk20a_clk *clk = gk20a_clk(base); + u32 val; + + /* slide to VCO min */ + val = nvkm_rd32(device, GPCPLL_CFG); + if (val & GPCPLL_CFG_ENABLE) { + u32 coef, m, n_lo; + + coef = nvkm_rd32(device, GPCPLL_COEFF); + m = (coef >> GPCPLL_COEFF_M_SHIFT) & MASK(GPCPLL_COEFF_M_WIDTH); + n_lo = DIV_ROUND_UP(m * clk->params->min_vco, + clk->parent_rate / KHZ); + gk20a_pllg_slide(clk, n_lo); + } + + /* put PLL in bypass before disabling it */ + nvkm_mask(device, SEL_VCO, BIT(SEL_VCO_GPC2CLK_OUT_SHIFT), 0); + gk20a_pllg_disable(clk); } -- 2.7.2
Alexandre Courbot
2016-Mar-11 14:32 UTC
[Nouveau] [PATCH 07/16] clk/gk20a: fix VCO bit mask
Fix the mask specified to switch to VCO mode was given as an (incorrect) immediate value. Although the side-effect happens to be the same, this is clearly incorrect. Signed-off-by: Alexandre Courbot <acourbot at nvidia.com> --- drm/nouveau/nvkm/subdev/clk/gk20a.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drm/nouveau/nvkm/subdev/clk/gk20a.c b/drm/nouveau/nvkm/subdev/clk/gk20a.c index b5a92f7..96cb72f 100644 --- a/drm/nouveau/nvkm/subdev/clk/gk20a.c +++ b/drm/nouveau/nvkm/subdev/clk/gk20a.c @@ -422,7 +422,8 @@ _gk20a_pllg_program_mnp(struct gk20a_clk *clk, bool allow_slide) return -ETIMEDOUT; /* switch to VCO mode */ - nvkm_mask(device, SEL_VCO, 0, BIT(SEL_VCO_GPC2CLK_OUT_SHIFT)); + nvkm_mask(device, SEL_VCO, BIT(SEL_VCO_GPC2CLK_OUT_SHIFT), + BIT(SEL_VCO_GPC2CLK_OUT_SHIFT)); /* restore out divider 1:1 */ val = nvkm_rd32(device, GPC2CLK_OUT); -- 2.7.2
Alexandre Courbot
2016-Mar-11 14:32 UTC
[Nouveau] [PATCH 08/16] clk/gk20a: only compute n_lo if needed
n_lo is used if we are going to slide. Compute it only if that condition succeeds to avoid confusion about future usage of this computation. Signed-off-by: Alexandre Courbot <acourbot at nvidia.com> --- drm/nouveau/nvkm/subdev/clk/gk20a.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/drm/nouveau/nvkm/subdev/clk/gk20a.c b/drm/nouveau/nvkm/subdev/clk/gk20a.c index 96cb72f..fb5852a 100644 --- a/drm/nouveau/nvkm/subdev/clk/gk20a.c +++ b/drm/nouveau/nvkm/subdev/clk/gk20a.c @@ -367,10 +367,12 @@ _gk20a_pllg_program_mnp(struct gk20a_clk *clk, bool allow_slide) } /* slide down to NDIV_LO */ - n_lo = DIV_ROUND_UP(m_old * clk->params->min_vco, - clk->parent_rate / KHZ); if (allow_slide && (cfg & GPCPLL_CFG_ENABLE)) { - int ret = gk20a_pllg_slide(clk, n_lo); + int ret; + + n_lo = DIV_ROUND_UP(old_pll.m * clk->params->min_vco, + clk->parent_rate / KHZ); + ret = gk20a_pllg_slide(clk, n_lo); if (ret) return ret; -- 2.7.2
Alexandre Courbot
2016-Mar-11 14:32 UTC
[Nouveau] [PATCH 09/16] clk/gk20a: only restore divider to 1:1 if needed
Only restore the 1:1 divider if it is not set already. Also use the proper masks for this operation and add a second write as done in the Android code. Signed-off-by: Alexandre Courbot <acourbot at nvidia.com> --- drm/nouveau/nvkm/subdev/clk/gk20a.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/drm/nouveau/nvkm/subdev/clk/gk20a.c b/drm/nouveau/nvkm/subdev/clk/gk20a.c index fb5852a..939d837 100644 --- a/drm/nouveau/nvkm/subdev/clk/gk20a.c +++ b/drm/nouveau/nvkm/subdev/clk/gk20a.c @@ -429,9 +429,16 @@ _gk20a_pllg_program_mnp(struct gk20a_clk *clk, bool allow_slide) /* restore out divider 1:1 */ val = nvkm_rd32(device, GPC2CLK_OUT); - val &= ~GPC2CLK_OUT_VCODIV_MASK; - udelay(2); - nvkm_wr32(device, GPC2CLK_OUT, val); + if ((val & GPC2CLK_OUT_VCODIV_MASK) !+ (GPC2CLK_OUT_VCODIV1 << GPC2CLK_OUT_VCODIV_SHIFT)) { + val &= ~GPC2CLK_OUT_VCODIV_MASK; + val |= GPC2CLK_OUT_VCODIV1 << GPC2CLK_OUT_VCODIV_SHIFT; + udelay(2); + nvkm_wr32(device, GPC2CLK_OUT, val); + /* Intentional 2nd write to assure linear divider operation */ + nvkm_wr32(device, GPC2CLK_OUT, val); + nvkm_rd32(device, GPC2CLK_OUT); + } /* slide up to new NDIV */ return allow_slide ? gk20a_pllg_slide(clk, clk->n) : 0; -- 2.7.2
Alexandre Courbot
2016-Mar-11 14:32 UTC
[Nouveau] [PATCH 10/16] clk/gk20a: emit parent rate as debug message
Most users are probably not interested in this information. Signed-off-by: Alexandre Courbot <acourbot at nvidia.com> --- drm/nouveau/nvkm/subdev/clk/gk20a.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drm/nouveau/nvkm/subdev/clk/gk20a.c b/drm/nouveau/nvkm/subdev/clk/gk20a.c index 939d837..04b9a20 100644 --- a/drm/nouveau/nvkm/subdev/clk/gk20a.c +++ b/drm/nouveau/nvkm/subdev/clk/gk20a.c @@ -675,7 +675,7 @@ gk20a_clk_new(struct nvkm_device *device, int index, struct nvkm_clk **pclk) clk->parent_rate = clk_get_rate(tdev->clk); ret = nvkm_clk_ctor(&gk20a_clk, device, index, true, &clk->base); - nvkm_info(&clk->base.subdev, "parent clock rate: %d Khz\n", - clk->parent_rate / KHZ); + nvkm_debug(&clk->base.subdev, "parent clock rate: %d Khz\n", + clk->parent_rate / KHZ); return ret; } -- 2.7.2
Alexandre Courbot
2016-Mar-11 14:32 UTC
[Nouveau] [PATCH 11/16] clk/gk20a: put mnp values into their own struct
This allows us to read them using one single function and will be handy to the GM20B driver. Signed-off-by: Alexandre Courbot <acourbot at nvidia.com> --- drm/nouveau/nvkm/subdev/clk/gk20a.c | 65 ++++++++++++++++++++----------------- 1 file changed, 35 insertions(+), 30 deletions(-) diff --git a/drm/nouveau/nvkm/subdev/clk/gk20a.c b/drm/nouveau/nvkm/subdev/clk/gk20a.c index 04b9a20..b7a1b3c 100644 --- a/drm/nouveau/nvkm/subdev/clk/gk20a.c +++ b/drm/nouveau/nvkm/subdev/clk/gk20a.c @@ -115,23 +115,29 @@ static const struct gk20a_clk_pllg_params gk20a_pllg_params = { .min_pl = 1, .max_pl = 32, }; +struct gk20a_pll { + u32 m; + u32 n; + u32 pl; +}; + struct gk20a_clk { struct nvkm_clk base; const struct gk20a_clk_pllg_params *params; - u32 m, n, pl; + struct gk20a_pll pll; u32 parent_rate; }; static void -gk20a_pllg_read_mnp(struct gk20a_clk *clk) +gk20a_pllg_read_mnp(struct gk20a_clk *clk, struct gk20a_pll *pll) { struct nvkm_device *device = clk->base.subdev.device; u32 val; val = nvkm_rd32(device, GPCPLL_COEFF); - clk->m = (val >> GPCPLL_COEFF_M_SHIFT) & MASK(GPCPLL_COEFF_M_WIDTH); - clk->n = (val >> GPCPLL_COEFF_N_SHIFT) & MASK(GPCPLL_COEFF_N_WIDTH); - clk->pl = (val >> GPCPLL_COEFF_P_SHIFT) & MASK(GPCPLL_COEFF_P_WIDTH); + pll->m = (val >> GPCPLL_COEFF_M_SHIFT) & MASK(GPCPLL_COEFF_M_WIDTH); + pll->n = (val >> GPCPLL_COEFF_N_SHIFT) & MASK(GPCPLL_COEFF_N_WIDTH); + pll->pl = (val >> GPCPLL_COEFF_P_SHIFT) & MASK(GPCPLL_COEFF_P_WIDTH); } static u32 @@ -140,8 +146,8 @@ gk20a_pllg_calc_rate(struct gk20a_clk *clk) u32 rate; u32 divider; - rate = clk->parent_rate * clk->n; - divider = clk->m * pl_to_div[clk->pl]; + rate = clk->parent_rate * clk->pll.n; + divider = clk->pll.m * pl_to_div[clk->pll.pl]; return rate / divider / 2; } @@ -256,16 +262,16 @@ found_match: "no best match for target @ %dMHz on gpc_pll", target_clk_f / KHZ); - clk->m = best_m; - clk->n = best_n; - clk->pl = best_pl; + clk->pll.m = best_m; + clk->pll.n = best_n; + clk->pll.pl = best_pl; target_freq = gk20a_pllg_calc_rate(clk); nvkm_debug(subdev, "actual target freq %d MHz, M %d, N %d, PL %d(div%d)\n", - target_freq / MHZ, clk->m, clk->n, clk->pl, - pl_to_div[clk->pl]); + target_freq / MHZ, clk->pll.m, clk->pll.n, clk->pll.pl, + pl_to_div[clk->pll.pl]); return 0; } @@ -352,18 +358,17 @@ _gk20a_pllg_program_mnp(struct gk20a_clk *clk, bool allow_slide) struct nvkm_subdev *subdev = &clk->base.subdev; struct nvkm_device *device = subdev->device; u32 val, cfg; - u32 m_old, pl_old, n_lo; + struct gk20a_pll old_pll; + u32 n_lo; /* get old coefficients */ - val = nvkm_rd32(device, GPCPLL_COEFF); - m_old = (val >> GPCPLL_COEFF_M_SHIFT) & MASK(GPCPLL_COEFF_M_WIDTH); - pl_old = (val >> GPCPLL_COEFF_P_SHIFT) & MASK(GPCPLL_COEFF_P_WIDTH); + gk20a_pllg_read_mnp(clk, &old_pll); /* do NDIV slide if there is no change in M and PL */ cfg = nvkm_rd32(device, GPCPLL_CFG); - if (allow_slide && clk->m == m_old && clk->pl == pl_old && - (cfg & GPCPLL_CFG_ENABLE)) { - return gk20a_pllg_slide(clk, clk->n); + if (allow_slide && clk->pll.m == old_pll.m && + clk->pll.pl == old_pll.pl && (cfg & GPCPLL_CFG_ENABLE)) { + return gk20a_pllg_slide(clk, clk->pll.n); } /* slide down to NDIV_LO */ @@ -400,13 +405,13 @@ _gk20a_pllg_program_mnp(struct gk20a_clk *clk, bool allow_slide) gk20a_pllg_disable(clk); nvkm_debug(subdev, "%s: m=%d n=%d pl=%d\n", __func__, - clk->m, clk->n, clk->pl); + clk->pll.m, clk->pll.n, clk->pll.pl); - n_lo = DIV_ROUND_UP(clk->m * clk->params->min_vco, + n_lo = DIV_ROUND_UP(clk->pll.m * clk->params->min_vco, clk->parent_rate / KHZ); - val = clk->m << GPCPLL_COEFF_M_SHIFT; - val |= (allow_slide ? n_lo : clk->n) << GPCPLL_COEFF_N_SHIFT; - val |= clk->pl << GPCPLL_COEFF_P_SHIFT; + val = clk->pll.m << GPCPLL_COEFF_M_SHIFT; + val |= (allow_slide ? n_lo : clk->pll.n) << GPCPLL_COEFF_N_SHIFT; + val |= clk->pll.pl << GPCPLL_COEFF_P_SHIFT; nvkm_wr32(device, GPCPLL_COEFF, val); gk20a_pllg_enable(clk); @@ -441,7 +446,7 @@ _gk20a_pllg_program_mnp(struct gk20a_clk *clk, bool allow_slide) } /* slide up to new NDIV */ - return allow_slide ? gk20a_pllg_slide(clk, clk->n) : 0; + return allow_slide ? gk20a_pllg_slide(clk, clk->pll.n) : 0; } static int @@ -563,7 +568,7 @@ gk20a_clk_read(struct nvkm_clk *base, enum nv_clk_src src) case nv_clk_src_crystal: return device->crystal; case nv_clk_src_gpc: - gk20a_pllg_read_mnp(clk); + gk20a_pllg_read_mnp(clk, &clk->pll); return gk20a_pllg_calc_rate(clk) / GK20A_CLK_GPC_MDIV; default: nvkm_error(subdev, "invalid clock source %d\n", src); @@ -603,11 +608,11 @@ gk20a_clk_fini(struct nvkm_clk *base) /* slide to VCO min */ val = nvkm_rd32(device, GPCPLL_CFG); if (val & GPCPLL_CFG_ENABLE) { - u32 coef, m, n_lo; + struct gk20a_pll pll; + u32 n_lo; - coef = nvkm_rd32(device, GPCPLL_COEFF); - m = (coef >> GPCPLL_COEFF_M_SHIFT) & MASK(GPCPLL_COEFF_M_WIDTH); - n_lo = DIV_ROUND_UP(m * clk->params->min_vco, + gk20a_pllg_read_mnp(clk, &pll); + n_lo = DIV_ROUND_UP(pll.m * clk->params->min_vco, clk->parent_rate / KHZ); gk20a_pllg_slide(clk, n_lo); } -- 2.7.2
Alexandre Courbot
2016-Mar-11 14:32 UTC
[Nouveau] [PATCH 12/16] clk/gk20a: abstract pl_to_div
pl_to_div may be done differently depending on the chip. Abstract this operation so the same logic can be reused for them as well. Signed-off-by: Alexandre Courbot <acourbot at nvidia.com> --- drm/nouveau/nvkm/subdev/clk/gk20a.c | 57 +++++++++++++++++++++++-------------- 1 file changed, 36 insertions(+), 21 deletions(-) diff --git a/drm/nouveau/nvkm/subdev/clk/gk20a.c b/drm/nouveau/nvkm/subdev/clk/gk20a.c index b7a1b3c..311cf49 100644 --- a/drm/nouveau/nvkm/subdev/clk/gk20a.c +++ b/drm/nouveau/nvkm/subdev/clk/gk20a.c @@ -93,7 +93,7 @@ #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[] = { +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, }; @@ -106,6 +106,25 @@ struct gk20a_clk_pllg_params { u32 min_n, max_n; u32 min_pl, max_pl; }; +static u32 pl_to_div(u32 pl) +{ + if (pl >= ARRAY_SIZE(_pl_to_div)) + return 1; + + return _pl_to_div[pl]; +} + +static u32 div_to_pl(u32 div) +{ + u32 pl; + + for (pl = 0; pl < ARRAY_SIZE(_pl_to_div) - 1; pl++) { + if (_pl_to_div[pl] >= div) + return pl; + } + + return ARRAY_SIZE(_pl_to_div) - 1; +} static const struct gk20a_clk_pllg_params gk20a_pllg_params = { .min_vco = 1000000, .max_vco = 2064000, @@ -126,6 +145,9 @@ struct gk20a_clk { const struct gk20a_clk_pllg_params *params; struct gk20a_pll pll; u32 parent_rate; + + u32 (*div_to_pl)(u32); + u32 (*pl_to_div)(u32); }; static void @@ -147,7 +169,7 @@ gk20a_pllg_calc_rate(struct gk20a_clk *clk) u32 divider; rate = clk->parent_rate * clk->pll.n; - divider = clk->pll.m * pl_to_div[clk->pll.pl]; + divider = clk->pll.m * clk->pl_to_div(clk->pll.pl); return rate / divider / 2; } @@ -181,34 +203,23 @@ gk20a_pllg_calc_mnp(struct gk20a_clk *clk, unsigned long rate) high_pl = (max_vco_f + target_vco_f - 1) / target_vco_f; high_pl = min(high_pl, clk->params->max_pl); high_pl = max(high_pl, clk->params->min_pl); + high_pl = clk->div_to_pl(high_pl); /* min_pl <= low_pl <= max_pl */ low_pl = min_vco_f / target_vco_f; low_pl = min(low_pl, clk->params->max_pl); low_pl = max(low_pl, clk->params->min_pl); - - /* Find Indices of high_pl and low_pl */ - for (pl = 0; pl < ARRAY_SIZE(pl_to_div) - 1; pl++) { - if (pl_to_div[pl] >= low_pl) { - low_pl = pl; - break; - } - } - for (pl = 0; pl < ARRAY_SIZE(pl_to_div) - 1; pl++) { - if (pl_to_div[pl] >= high_pl) { - high_pl = pl; - break; - } - } + low_pl = clk->div_to_pl(low_pl); nvkm_debug(subdev, "low_PL %d(div%d), high_PL %d(div%d)", low_pl, - pl_to_div[low_pl], high_pl, pl_to_div[high_pl]); + clk->pl_to_div(low_pl), high_pl, clk->pl_to_div(high_pl)); /* Select lowest possible VCO */ for (pl = low_pl; pl <= high_pl; pl++) { u32 m, n, n2; - target_vco_f = target_clk_f * pl_to_div[pl]; + target_vco_f = target_clk_f * clk->pl_to_div(pl); + for (m = clk->params->min_m; m <= clk->params->max_m; m++) { u32 u_f, vco_f; @@ -236,8 +247,8 @@ gk20a_pllg_calc_mnp(struct gk20a_clk *clk, unsigned long rate) if (vco_f >= min_vco_f && vco_f <= max_vco_f) { u32 delta, lwv; - lwv = (vco_f + (pl_to_div[pl] / 2)) - / pl_to_div[pl]; + lwv = (vco_f + (clk->pl_to_div(pl) / 2)) + / clk->pl_to_div(pl); delta = abs(lwv - target_clk_f); if (delta < best_delta) { @@ -271,7 +282,7 @@ found_match: nvkm_debug(subdev, "actual target freq %d MHz, M %d, N %d, PL %d(div%d)\n", target_freq / MHZ, clk->pll.m, clk->pll.n, clk->pll.pl, - pl_to_div[clk->pll.pl]); + clk->pl_to_div(clk->pll.pl)); return 0; } @@ -682,5 +693,9 @@ gk20a_clk_new(struct nvkm_device *device, int index, struct nvkm_clk **pclk) ret = nvkm_clk_ctor(&gk20a_clk, device, index, true, &clk->base); nvkm_debug(&clk->base.subdev, "parent clock rate: %d Khz\n", clk->parent_rate / KHZ); + + clk->pl_to_div = pl_to_div; + clk->div_to_pl = div_to_pl; + return ret; } -- 2.7.2
Alexandre Courbot
2016-Mar-11 14:32 UTC
[Nouveau] [PATCH 13/16] clk/gk20a: split gk20a_clk_new()
This allows to instanciate drivers that use the same logic as gk20a with different parameters. Signed-off-by: Alexandre Courbot <acourbot at nvidia.com> Add a constructor function to allow other chips that inherit from this clock to easily initialize its members --- drm/nouveau/nvkm/subdev/clk/gk20a.c | 43 ++++++++++++++++++++++++++----------- 1 file changed, 31 insertions(+), 12 deletions(-) diff --git a/drm/nouveau/nvkm/subdev/clk/gk20a.c b/drm/nouveau/nvkm/subdev/clk/gk20a.c index 311cf49..4e92186 100644 --- a/drm/nouveau/nvkm/subdev/clk/gk20a.c +++ b/drm/nouveau/nvkm/subdev/clk/gk20a.c @@ -671,29 +671,48 @@ gk20a_clk = { }; int -gk20a_clk_new(struct nvkm_device *device, int index, struct nvkm_clk **pclk) +_gk20a_clk_ctor(struct nvkm_device *device, int index, + const struct nvkm_clk_func *func, + const struct gk20a_clk_pllg_params *params, + struct gk20a_clk *clk) { struct nvkm_device_tegra *tdev = device->func->tegra(device); - struct gk20a_clk *clk; - int ret, i; - - if (!(clk = kzalloc(sizeof(*clk), GFP_KERNEL))) - return -ENOMEM; - *pclk = &clk->base; + int ret; + int i; /* Finish initializing the pstates */ - for (i = 0; i < ARRAY_SIZE(gk20a_pstates); i++) { - INIT_LIST_HEAD(&gk20a_pstates[i].list); - gk20a_pstates[i].pstate = i + 1; + for (i = 0; i < func->nr_pstates; i++) { + INIT_LIST_HEAD(&func->pstates[i].list); + func->pstates[i].pstate = i + 1; } - clk->params = &gk20a_pllg_params; + clk->params = params; clk->parent_rate = clk_get_rate(tdev->clk); - ret = nvkm_clk_ctor(&gk20a_clk, device, index, true, &clk->base); + ret = nvkm_clk_ctor(func, device, index, true, &clk->base); + if (ret) + return ret; + nvkm_debug(&clk->base.subdev, "parent clock rate: %d Khz\n", clk->parent_rate / KHZ); + return 0; +} + +int +gk20a_clk_new(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, &gk20a_clk, &gk20a_pllg_params, + clk); + clk->pl_to_div = pl_to_div; clk->div_to_pl = div_to_pl; -- 2.7.2
Alexandre Courbot
2016-Mar-11 14:32 UTC
[Nouveau] [PATCH 14/16] clk/gk20a: set lowest frequency during init()
Err on the safe side by setting the lowest frequency (and thus voltage) during device init. Signed-off-by: Alexandre Courbot <acourbot at nvidia.com> --- drm/nouveau/nvkm/subdev/clk/gk20a.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/drm/nouveau/nvkm/subdev/clk/gk20a.c b/drm/nouveau/nvkm/subdev/clk/gk20a.c index 4e92186..4bc72b1 100644 --- a/drm/nouveau/nvkm/subdev/clk/gk20a.c +++ b/drm/nouveau/nvkm/subdev/clk/gk20a.c @@ -642,9 +642,12 @@ gk20a_clk_init(struct nvkm_clk *base) struct nvkm_device *device = subdev->device; int ret; - nvkm_mask(device, GPC2CLK_OUT, GPC2CLK_OUT_INIT_MASK, GPC2CLK_OUT_INIT_VAL); + nvkm_mask(device, GPC2CLK_OUT, GPC2CLK_OUT_INIT_MASK, + GPC2CLK_OUT_INIT_VAL); - ret = gk20a_clk_prog(&clk->base); + /* Start with lowest frequency */ + base->func->calc(base, &base->func->pstates[0].base); + ret = base->func->prog(&clk->base); if (ret) { nvkm_error(subdev, "cannot initialize clock\n"); return ret; -- 2.7.2
Alexandre Courbot
2016-Mar-11 14:32 UTC
[Nouveau] [PATCH 15/16] clk/gk20a: share reusable structures/functions
Make functions/structures that the GM20B driver will reuse public. Signed-off-by: Alexandre Courbot <acourbot at nvidia.com> --- drm/nouveau/nvkm/subdev/clk/gk20a.c | 44 +++++-------------------- drm/nouveau/nvkm/subdev/clk/gk20a.h | 65 +++++++++++++++++++++++++++++++++++++ 2 files changed, 73 insertions(+), 36 deletions(-) create mode 100644 drm/nouveau/nvkm/subdev/clk/gk20a.h diff --git a/drm/nouveau/nvkm/subdev/clk/gk20a.c b/drm/nouveau/nvkm/subdev/clk/gk20a.c index 4bc72b1..5f0ee24 100644 --- a/drm/nouveau/nvkm/subdev/clk/gk20a.c +++ b/drm/nouveau/nvkm/subdev/clk/gk20a.c @@ -22,8 +22,8 @@ * Shamelessly ripped off from ChromeOS's gk20a/clk_pllg.c * */ -#define gk20a_clk(p) container_of((p), struct gk20a_clk, base) #include "priv.h" +#include "gk20a.h" #include <core/tegra.h> #include <subdev/timer.h> @@ -33,9 +33,6 @@ #define MASK(w) ((1 << w) - 1) -#define SYS_GPCPLL_CFG_BASE 0x00137000 -#define GPC_BCASE_GPCPLL_CFG_BASE 0x00132800 - #define GPCPLL_CFG (SYS_GPCPLL_CFG_BASE + 0) #define GPCPLL_CFG_ENABLE BIT(0) #define GPCPLL_CFG_IDDQ BIT(1) @@ -57,6 +54,7 @@ #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 @@ -76,7 +74,7 @@ #define GPC2CLK_OUT_VCODIV1 0 #define GPC2CLK_OUT_VCODIV_MASK (MASK(GPC2CLK_OUT_VCODIV_WIDTH) << \ GPC2CLK_OUT_VCODIV_SHIFT) -#define GPC2CLK_OUT_BYPDIV_WIDTH 6 +#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) << \ @@ -98,14 +96,6 @@ static const u8 _pl_to_div[] = { /* p: */ 1, 2, 3, 4, 5, 6, 8, 10, 12, 16, 12, 16, 20, 24, 32, }; -/* All frequencies in Khz */ -struct gk20a_clk_pllg_params { - u32 min_vco, max_vco; - u32 min_u, max_u; - u32 min_m, max_m; - u32 min_n, max_n; - u32 min_pl, max_pl; -}; static u32 pl_to_div(u32 pl) { if (pl >= ARRAY_SIZE(_pl_to_div)) @@ -134,22 +124,6 @@ static const struct gk20a_clk_pllg_params gk20a_pllg_params = { .min_pl = 1, .max_pl = 32, }; -struct gk20a_pll { - u32 m; - u32 n; - u32 pl; -}; - -struct gk20a_clk { - struct nvkm_clk base; - const struct gk20a_clk_pllg_params *params; - struct gk20a_pll pll; - u32 parent_rate; - - u32 (*div_to_pl)(u32); - u32 (*pl_to_div)(u32); -}; - static void gk20a_pllg_read_mnp(struct gk20a_clk *clk, struct gk20a_pll *pll) { @@ -472,8 +446,6 @@ gk20a_pllg_program_mnp(struct gk20a_clk *clk) return err; } -#define GK20A_CLK_GPC_MDIV 1000 - static struct nvkm_pstate gk20a_pstates[] = { { @@ -568,7 +540,7 @@ gk20a_pstates[] = { }, }; -static int +int gk20a_clk_read(struct nvkm_clk *base, enum nv_clk_src src) { struct gk20a_clk *clk = gk20a_clk(base); @@ -587,7 +559,7 @@ gk20a_clk_read(struct nvkm_clk *base, enum nv_clk_src src) } } -static int +int gk20a_clk_calc(struct nvkm_clk *base, struct nvkm_cstate *cstate) { struct gk20a_clk *clk = gk20a_clk(base); @@ -596,7 +568,7 @@ gk20a_clk_calc(struct nvkm_clk *base, struct nvkm_cstate *cstate) GK20A_CLK_GPC_MDIV); } -static int +int gk20a_clk_prog(struct nvkm_clk *base) { struct gk20a_clk *clk = gk20a_clk(base); @@ -604,12 +576,12 @@ gk20a_clk_prog(struct nvkm_clk *base) return gk20a_pllg_program_mnp(clk); } -static void +void gk20a_clk_tidy(struct nvkm_clk *base) { } -static void +void gk20a_clk_fini(struct nvkm_clk *base) { struct nvkm_device *device = base->subdev.device; diff --git a/drm/nouveau/nvkm/subdev/clk/gk20a.h b/drm/nouveau/nvkm/subdev/clk/gk20a.h new file mode 100644 index 0000000..13c4674 --- /dev/null +++ b/drm/nouveau/nvkm/subdev/clk/gk20a.h @@ -0,0 +1,65 @@ +/* + * 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. + * + */ + +#ifndef __NVKM_CLK_GK20A_H__ +#define __NVKM_CLK_GK20A_H__ + +#define GK20A_CLK_GPC_MDIV 1000 + +#define SYS_GPCPLL_CFG_BASE 0x00137000 + +/* All frequencies in Khz */ +struct gk20a_clk_pllg_params { + u32 min_vco, max_vco; + u32 min_u, max_u; + u32 min_m, max_m; + u32 min_n, max_n; + u32 min_pl, max_pl; +}; + +struct gk20a_pll { + u32 m; + u32 n; + u32 pl; +}; + +struct gk20a_clk { + struct nvkm_clk base; + const struct gk20a_clk_pllg_params *params; + struct gk20a_pll pll; + u32 parent_rate; + + u32 (*div_to_pl)(u32); + u32 (*pl_to_div)(u32); +}; +#define gk20a_clk(p) container_of((p), struct gk20a_clk, base) + +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 *); +int gk20a_clk_read(struct nvkm_clk *, enum nv_clk_src); +int gk20a_clk_calc(struct nvkm_clk *, struct nvkm_cstate *); +int gk20a_clk_prog(struct nvkm_clk *); +void gk20a_clk_tidy(struct nvkm_clk *); + +#endif -- 2.7.2
Alexandre Courbot
2016-Mar-11 14:32 UTC
[Nouveau] [PATCH 16/16] clk/gm20b: add basic driver
Add a basic clock driver that reuses the GK20A logic. Signed-off-by: Alexandre Courbot <acourbot at nvidia.com> --- drm/nouveau/include/nvkm/subdev/clk.h | 1 + drm/nouveau/nvkm/engine/device/base.c | 1 + drm/nouveau/nvkm/subdev/clk/Kbuild | 1 + drm/nouveau/nvkm/subdev/clk/gm20b.c | 198 ++++++++++++++++++++++++++++++++++ 4 files changed, 201 insertions(+) create mode 100644 drm/nouveau/nvkm/subdev/clk/gm20b.c diff --git a/drm/nouveau/include/nvkm/subdev/clk.h b/drm/nouveau/include/nvkm/subdev/clk.h index 6b33bc0..fb54417 100644 --- a/drm/nouveau/include/nvkm/subdev/clk.h +++ b/drm/nouveau/include/nvkm/subdev/clk.h @@ -121,4 +121,5 @@ int gt215_clk_new(struct nvkm_device *, int, struct nvkm_clk **); int gf100_clk_new(struct nvkm_device *, int, struct nvkm_clk **); int gk104_clk_new(struct nvkm_device *, int, struct nvkm_clk **); int gk20a_clk_new(struct nvkm_device *, int, struct nvkm_clk **); +int gm20b_clk_new(struct nvkm_device *, int, struct nvkm_clk **); #endif diff --git a/drm/nouveau/nvkm/engine/device/base.c b/drm/nouveau/nvkm/engine/device/base.c index bfe2f4f..9f32c87 100644 --- a/drm/nouveau/nvkm/engine/device/base.c +++ b/drm/nouveau/nvkm/engine/device/base.c @@ -2083,6 +2083,7 @@ nv12b_chipset = { .name = "GM20B", .bar = gk20a_bar_new, .bus = gf100_bus_new, + .clk = gm20b_clk_new, .fb = gk20a_fb_new, .fuse = gm107_fuse_new, .ibus = gk20a_ibus_new, diff --git a/drm/nouveau/nvkm/subdev/clk/Kbuild b/drm/nouveau/nvkm/subdev/clk/Kbuild index ed7717b..87d9488 100644 --- a/drm/nouveau/nvkm/subdev/clk/Kbuild +++ b/drm/nouveau/nvkm/subdev/clk/Kbuild @@ -8,6 +8,7 @@ nvkm-y += nvkm/subdev/clk/mcp77.o nvkm-y += nvkm/subdev/clk/gf100.o nvkm-y += nvkm/subdev/clk/gk104.o nvkm-y += nvkm/subdev/clk/gk20a.o +nvkm-y += nvkm/subdev/clk/gm20b.o nvkm-y += nvkm/subdev/clk/pllnv04.o nvkm-y += nvkm/subdev/clk/pllgt215.o diff --git a/drm/nouveau/nvkm/subdev/clk/gm20b.c b/drm/nouveau/nvkm/subdev/clk/gm20b.c new file mode 100644 index 0000000..71b2bbb --- /dev/null +++ b/drm/nouveau/nvkm/subdev/clk/gm20b.c @@ -0,0 +1,198 @@ +/* + * 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 <core/device.h> + +#include "priv.h" +#include "gk20a.h" + +#define KHZ (1000) +#define MHZ (KHZ * 1000) + +#define MASK(w) ((1 << w) - 1) + +#define BYPASSCTRL_SYS (SYS_GPCPLL_CFG_BASE + 0x340) +#define BYPASSCTRL_SYS_GPCPLL_SHIFT 0 +#define BYPASSCTRL_SYS_GPCPLL_WIDTH 1 + +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 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 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; + + /* Set the global bypass control to VCO */ + nvkm_mask(device, BYPASSCTRL_SYS, + MASK(BYPASSCTRL_SYS_GPCPLL_WIDTH) << BYPASSCTRL_SYS_GPCPLL_SHIFT, + 0); + + /* Start with lowest frequency */ + base->func->calc(base, &base->func->pstates[0].base); + ret = base->func->prog(&clk->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, + .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 }, + }, +}; + +int +gm20b_clk_new(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; +} -- 2.7.2