linux/drivers/gpu/drm/nouveau/nvkm/subdev/clk/gk20a.c
<<
>>
Prefs
   1/*
   2 * Copyright (c) 2014, NVIDIA CORPORATION. All rights reserved.
   3 *
   4 * Permission is hereby granted, free of charge, to any person obtaining a
   5 * copy of this software and associated documentation files (the "Software"),
   6 * to deal in the Software without restriction, including without limitation
   7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
   8 * and/or sell copies of the Software, and to permit persons to whom the
   9 * Software is furnished to do so, subject to the following conditions:
  10 *
  11 * The above copyright notice and this permission notice shall be included in
  12 * all copies or substantial portions of the Software.
  13 *
  14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
  17 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  18 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  19 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
  20 * DEALINGS IN THE SOFTWARE.
  21 *
  22 * Shamelessly ripped off from ChromeOS's gk20a/clk_pllg.c
  23 *
  24 */
  25#include <subdev/clk.h>
  26#include <subdev/timer.h>
  27
  28#include <core/device.h>
  29
  30#ifdef __KERNEL__
  31#include <nouveau_platform.h>
  32#endif
  33
  34#define MHZ (1000 * 1000)
  35
  36#define MASK(w) ((1 << w) - 1)
  37
  38#define SYS_GPCPLL_CFG_BASE                     0x00137000
  39#define GPC_BCASE_GPCPLL_CFG_BASE               0x00132800
  40
  41#define GPCPLL_CFG              (SYS_GPCPLL_CFG_BASE + 0)
  42#define GPCPLL_CFG_ENABLE       BIT(0)
  43#define GPCPLL_CFG_IDDQ         BIT(1)
  44#define GPCPLL_CFG_LOCK_DET_OFF BIT(4)
  45#define GPCPLL_CFG_LOCK         BIT(17)
  46
  47#define GPCPLL_COEFF            (SYS_GPCPLL_CFG_BASE + 4)
  48#define GPCPLL_COEFF_M_SHIFT    0
  49#define GPCPLL_COEFF_M_WIDTH    8
  50#define GPCPLL_COEFF_N_SHIFT    8
  51#define GPCPLL_COEFF_N_WIDTH    8
  52#define GPCPLL_COEFF_P_SHIFT    16
  53#define GPCPLL_COEFF_P_WIDTH    6
  54
  55#define GPCPLL_CFG2                     (SYS_GPCPLL_CFG_BASE + 0xc)
  56#define GPCPLL_CFG2_SETUP2_SHIFT        16
  57#define GPCPLL_CFG2_PLL_STEPA_SHIFT     24
  58
  59#define GPCPLL_CFG3                     (SYS_GPCPLL_CFG_BASE + 0x18)
  60#define GPCPLL_CFG3_PLL_STEPB_SHIFT     16
  61
  62#define GPCPLL_NDIV_SLOWDOWN                    (SYS_GPCPLL_CFG_BASE + 0x1c)
  63#define GPCPLL_NDIV_SLOWDOWN_NDIV_LO_SHIFT      0
  64#define GPCPLL_NDIV_SLOWDOWN_NDIV_MID_SHIFT     8
  65#define GPCPLL_NDIV_SLOWDOWN_STEP_SIZE_LO2MID_SHIFT     16
  66#define GPCPLL_NDIV_SLOWDOWN_SLOWDOWN_USING_PLL_SHIFT   22
  67#define GPCPLL_NDIV_SLOWDOWN_EN_DYNRAMP_SHIFT   31
  68
  69#define SEL_VCO                         (SYS_GPCPLL_CFG_BASE + 0x100)
  70#define SEL_VCO_GPC2CLK_OUT_SHIFT       0
  71
  72#define GPC2CLK_OUT                     (SYS_GPCPLL_CFG_BASE + 0x250)
  73#define GPC2CLK_OUT_SDIV14_INDIV4_WIDTH 1
  74#define GPC2CLK_OUT_SDIV14_INDIV4_SHIFT 31
  75#define GPC2CLK_OUT_SDIV14_INDIV4_MODE  1
  76#define GPC2CLK_OUT_VCODIV_WIDTH        6
  77#define GPC2CLK_OUT_VCODIV_SHIFT        8
  78#define GPC2CLK_OUT_VCODIV1             0
  79#define GPC2CLK_OUT_VCODIV_MASK         (MASK(GPC2CLK_OUT_VCODIV_WIDTH) << \
  80                                        GPC2CLK_OUT_VCODIV_SHIFT)
  81#define GPC2CLK_OUT_BYPDIV_WIDTH        6
  82#define GPC2CLK_OUT_BYPDIV_SHIFT        0
  83#define GPC2CLK_OUT_BYPDIV31            0x3c
  84#define GPC2CLK_OUT_INIT_MASK   ((MASK(GPC2CLK_OUT_SDIV14_INDIV4_WIDTH) << \
  85                GPC2CLK_OUT_SDIV14_INDIV4_SHIFT)\
  86                | (MASK(GPC2CLK_OUT_VCODIV_WIDTH) << GPC2CLK_OUT_VCODIV_SHIFT)\
  87                | (MASK(GPC2CLK_OUT_BYPDIV_WIDTH) << GPC2CLK_OUT_BYPDIV_SHIFT))
  88#define GPC2CLK_OUT_INIT_VAL    ((GPC2CLK_OUT_SDIV14_INDIV4_MODE << \
  89                GPC2CLK_OUT_SDIV14_INDIV4_SHIFT) \
  90                | (GPC2CLK_OUT_VCODIV1 << GPC2CLK_OUT_VCODIV_SHIFT) \
  91                | (GPC2CLK_OUT_BYPDIV31 << GPC2CLK_OUT_BYPDIV_SHIFT))
  92
  93#define GPC_BCAST_NDIV_SLOWDOWN_DEBUG   (GPC_BCASE_GPCPLL_CFG_BASE + 0xa0)
  94#define GPC_BCAST_NDIV_SLOWDOWN_DEBUG_PLL_DYNRAMP_DONE_SYNCED_SHIFT     24
  95#define GPC_BCAST_NDIV_SLOWDOWN_DEBUG_PLL_DYNRAMP_DONE_SYNCED_MASK \
  96            (0x1 << GPC_BCAST_NDIV_SLOWDOWN_DEBUG_PLL_DYNRAMP_DONE_SYNCED_SHIFT)
  97
  98static const u8 pl_to_div[] = {
  99/* PL:   0, 1, 2, 3, 4, 5, 6,  7,  8,  9, 10, 11, 12, 13, 14 */
 100/* p: */ 1, 2, 3, 4, 5, 6, 8, 10, 12, 16, 12, 16, 20, 24, 32,
 101};
 102
 103/* All frequencies in Mhz */
 104struct gk20a_clk_pllg_params {
 105        u32 min_vco, max_vco;
 106        u32 min_u, max_u;
 107        u32 min_m, max_m;
 108        u32 min_n, max_n;
 109        u32 min_pl, max_pl;
 110};
 111
 112static const struct gk20a_clk_pllg_params gk20a_pllg_params = {
 113        .min_vco = 1000, .max_vco = 2064,
 114        .min_u = 12, .max_u = 38,
 115        .min_m = 1, .max_m = 255,
 116        .min_n = 8, .max_n = 255,
 117        .min_pl = 1, .max_pl = 32,
 118};
 119
 120struct gk20a_clk_priv {
 121        struct nvkm_clk base;
 122        const struct gk20a_clk_pllg_params *params;
 123        u32 m, n, pl;
 124        u32 parent_rate;
 125};
 126#define to_gk20a_clk(base) container_of(base, struct gk20a_clk_priv, base)
 127
 128static void
 129gk20a_pllg_read_mnp(struct gk20a_clk_priv *priv)
 130{
 131        u32 val;
 132
 133        val = nv_rd32(priv, GPCPLL_COEFF);
 134        priv->m = (val >> GPCPLL_COEFF_M_SHIFT) & MASK(GPCPLL_COEFF_M_WIDTH);
 135        priv->n = (val >> GPCPLL_COEFF_N_SHIFT) & MASK(GPCPLL_COEFF_N_WIDTH);
 136        priv->pl = (val >> GPCPLL_COEFF_P_SHIFT) & MASK(GPCPLL_COEFF_P_WIDTH);
 137}
 138
 139static u32
 140gk20a_pllg_calc_rate(struct gk20a_clk_priv *priv)
 141{
 142        u32 rate;
 143        u32 divider;
 144
 145        rate = priv->parent_rate * priv->n;
 146        divider = priv->m * pl_to_div[priv->pl];
 147        do_div(rate, divider);
 148
 149        return rate / 2;
 150}
 151
 152static int
 153gk20a_pllg_calc_mnp(struct gk20a_clk_priv *priv, unsigned long rate)
 154{
 155        u32 target_clk_f, ref_clk_f, target_freq;
 156        u32 min_vco_f, max_vco_f;
 157        u32 low_pl, high_pl, best_pl;
 158        u32 target_vco_f, vco_f;
 159        u32 best_m, best_n;
 160        u32 u_f;
 161        u32 m, n, n2;
 162        u32 delta, lwv, best_delta = ~0;
 163        u32 pl;
 164
 165        target_clk_f = rate * 2 / MHZ;
 166        ref_clk_f = priv->parent_rate / MHZ;
 167
 168        max_vco_f = priv->params->max_vco;
 169        min_vco_f = priv->params->min_vco;
 170        best_m = priv->params->max_m;
 171        best_n = priv->params->min_n;
 172        best_pl = priv->params->min_pl;
 173
 174        target_vco_f = target_clk_f + target_clk_f / 50;
 175        if (max_vco_f < target_vco_f)
 176                max_vco_f = target_vco_f;
 177
 178        /* min_pl <= high_pl <= max_pl */
 179        high_pl = (max_vco_f + target_vco_f - 1) / target_vco_f;
 180        high_pl = min(high_pl, priv->params->max_pl);
 181        high_pl = max(high_pl, priv->params->min_pl);
 182
 183        /* min_pl <= low_pl <= max_pl */
 184        low_pl = min_vco_f / target_vco_f;
 185        low_pl = min(low_pl, priv->params->max_pl);
 186        low_pl = max(low_pl, priv->params->min_pl);
 187
 188        /* Find Indices of high_pl and low_pl */
 189        for (pl = 0; pl < ARRAY_SIZE(pl_to_div) - 1; pl++) {
 190                if (pl_to_div[pl] >= low_pl) {
 191                        low_pl = pl;
 192                        break;
 193                }
 194        }
 195        for (pl = 0; pl < ARRAY_SIZE(pl_to_div) - 1; pl++) {
 196                if (pl_to_div[pl] >= high_pl) {
 197                        high_pl = pl;
 198                        break;
 199                }
 200        }
 201
 202        nv_debug(priv, "low_PL %d(div%d), high_PL %d(div%d)", low_pl,
 203                 pl_to_div[low_pl], high_pl, pl_to_div[high_pl]);
 204
 205        /* Select lowest possible VCO */
 206        for (pl = low_pl; pl <= high_pl; pl++) {
 207                target_vco_f = target_clk_f * pl_to_div[pl];
 208                for (m = priv->params->min_m; m <= priv->params->max_m; m++) {
 209                        u_f = ref_clk_f / m;
 210
 211                        if (u_f < priv->params->min_u)
 212                                break;
 213                        if (u_f > priv->params->max_u)
 214                                continue;
 215
 216                        n = (target_vco_f * m) / ref_clk_f;
 217                        n2 = ((target_vco_f * m) + (ref_clk_f - 1)) / ref_clk_f;
 218
 219                        if (n > priv->params->max_n)
 220                                break;
 221
 222                        for (; n <= n2; n++) {
 223                                if (n < priv->params->min_n)
 224                                        continue;
 225                                if (n > priv->params->max_n)
 226                                        break;
 227
 228                                vco_f = ref_clk_f * n / m;
 229
 230                                if (vco_f >= min_vco_f && vco_f <= max_vco_f) {
 231                                        lwv = (vco_f + (pl_to_div[pl] / 2))
 232                                                / pl_to_div[pl];
 233                                        delta = abs(lwv - target_clk_f);
 234
 235                                        if (delta < best_delta) {
 236                                                best_delta = delta;
 237                                                best_m = m;
 238                                                best_n = n;
 239                                                best_pl = pl;
 240
 241                                                if (best_delta == 0)
 242                                                        goto found_match;
 243                                        }
 244                                }
 245                        }
 246                }
 247        }
 248
 249found_match:
 250        WARN_ON(best_delta == ~0);
 251
 252        if (best_delta != 0)
 253                nv_debug(priv, "no best match for target @ %dMHz on gpc_pll",
 254                         target_clk_f);
 255
 256        priv->m = best_m;
 257        priv->n = best_n;
 258        priv->pl = best_pl;
 259
 260        target_freq = gk20a_pllg_calc_rate(priv) / MHZ;
 261
 262        nv_debug(priv, "actual target freq %d MHz, M %d, N %d, PL %d(div%d)\n",
 263                 target_freq, priv->m, priv->n, priv->pl, pl_to_div[priv->pl]);
 264        return 0;
 265}
 266
 267static int
 268gk20a_pllg_slide(struct gk20a_clk_priv *priv, u32 n)
 269{
 270        u32 val;
 271        int ramp_timeout;
 272
 273        /* get old coefficients */
 274        val = nv_rd32(priv, GPCPLL_COEFF);
 275        /* do nothing if NDIV is the same */
 276        if (n == ((val >> GPCPLL_COEFF_N_SHIFT) & MASK(GPCPLL_COEFF_N_WIDTH)))
 277                return 0;
 278
 279        /* setup */
 280        nv_mask(priv, GPCPLL_CFG2, 0xff << GPCPLL_CFG2_PLL_STEPA_SHIFT,
 281                0x2b << GPCPLL_CFG2_PLL_STEPA_SHIFT);
 282        nv_mask(priv, GPCPLL_CFG3, 0xff << GPCPLL_CFG3_PLL_STEPB_SHIFT,
 283                0xb << GPCPLL_CFG3_PLL_STEPB_SHIFT);
 284
 285        /* pll slowdown mode */
 286        nv_mask(priv, GPCPLL_NDIV_SLOWDOWN,
 287                BIT(GPCPLL_NDIV_SLOWDOWN_SLOWDOWN_USING_PLL_SHIFT),
 288                BIT(GPCPLL_NDIV_SLOWDOWN_SLOWDOWN_USING_PLL_SHIFT));
 289
 290        /* new ndiv ready for ramp */
 291        val = nv_rd32(priv, GPCPLL_COEFF);
 292        val &= ~(MASK(GPCPLL_COEFF_N_WIDTH) << GPCPLL_COEFF_N_SHIFT);
 293        val |= (n & MASK(GPCPLL_COEFF_N_WIDTH)) << GPCPLL_COEFF_N_SHIFT;
 294        udelay(1);
 295        nv_wr32(priv, GPCPLL_COEFF, val);
 296
 297        /* dynamic ramp to new ndiv */
 298        val = nv_rd32(priv, GPCPLL_NDIV_SLOWDOWN);
 299        val |= 0x1 << GPCPLL_NDIV_SLOWDOWN_EN_DYNRAMP_SHIFT;
 300        udelay(1);
 301        nv_wr32(priv, GPCPLL_NDIV_SLOWDOWN, val);
 302
 303        for (ramp_timeout = 500; ramp_timeout > 0; ramp_timeout--) {
 304                udelay(1);
 305                val = nv_rd32(priv, GPC_BCAST_NDIV_SLOWDOWN_DEBUG);
 306                if (val & GPC_BCAST_NDIV_SLOWDOWN_DEBUG_PLL_DYNRAMP_DONE_SYNCED_MASK)
 307                        break;
 308        }
 309
 310        /* exit slowdown mode */
 311        nv_mask(priv, GPCPLL_NDIV_SLOWDOWN,
 312                BIT(GPCPLL_NDIV_SLOWDOWN_SLOWDOWN_USING_PLL_SHIFT) |
 313                BIT(GPCPLL_NDIV_SLOWDOWN_EN_DYNRAMP_SHIFT), 0);
 314        nv_rd32(priv, GPCPLL_NDIV_SLOWDOWN);
 315
 316        if (ramp_timeout <= 0) {
 317                nv_error(priv, "gpcpll dynamic ramp timeout\n");
 318                return -ETIMEDOUT;
 319        }
 320
 321        return 0;
 322}
 323
 324static void
 325_gk20a_pllg_enable(struct gk20a_clk_priv *priv)
 326{
 327        nv_mask(priv, GPCPLL_CFG, GPCPLL_CFG_ENABLE, GPCPLL_CFG_ENABLE);
 328        nv_rd32(priv, GPCPLL_CFG);
 329}
 330
 331static void
 332_gk20a_pllg_disable(struct gk20a_clk_priv *priv)
 333{
 334        nv_mask(priv, GPCPLL_CFG, GPCPLL_CFG_ENABLE, 0);
 335        nv_rd32(priv, GPCPLL_CFG);
 336}
 337
 338static int
 339_gk20a_pllg_program_mnp(struct gk20a_clk_priv *priv, bool allow_slide)
 340{
 341        u32 val, cfg;
 342        u32 m_old, pl_old, n_lo;
 343
 344        /* get old coefficients */
 345        val = nv_rd32(priv, GPCPLL_COEFF);
 346        m_old = (val >> GPCPLL_COEFF_M_SHIFT) & MASK(GPCPLL_COEFF_M_WIDTH);
 347        pl_old = (val >> GPCPLL_COEFF_P_SHIFT) & MASK(GPCPLL_COEFF_P_WIDTH);
 348
 349        /* do NDIV slide if there is no change in M and PL */
 350        cfg = nv_rd32(priv, GPCPLL_CFG);
 351        if (allow_slide && priv->m == m_old && priv->pl == pl_old &&
 352            (cfg & GPCPLL_CFG_ENABLE)) {
 353                return gk20a_pllg_slide(priv, priv->n);
 354        }
 355
 356        /* slide down to NDIV_LO */
 357        n_lo = DIV_ROUND_UP(m_old * priv->params->min_vco,
 358                            priv->parent_rate / MHZ);
 359        if (allow_slide && (cfg & GPCPLL_CFG_ENABLE)) {
 360                int ret = gk20a_pllg_slide(priv, n_lo);
 361
 362                if (ret)
 363                        return ret;
 364        }
 365
 366        /* split FO-to-bypass jump in halfs by setting out divider 1:2 */
 367        nv_mask(priv, GPC2CLK_OUT, GPC2CLK_OUT_VCODIV_MASK,
 368                0x2 << GPC2CLK_OUT_VCODIV_SHIFT);
 369
 370        /* put PLL in bypass before programming it */
 371        val = nv_rd32(priv, SEL_VCO);
 372        val &= ~(BIT(SEL_VCO_GPC2CLK_OUT_SHIFT));
 373        udelay(2);
 374        nv_wr32(priv, SEL_VCO, val);
 375
 376        /* get out from IDDQ */
 377        val = nv_rd32(priv, GPCPLL_CFG);
 378        if (val & GPCPLL_CFG_IDDQ) {
 379                val &= ~GPCPLL_CFG_IDDQ;
 380                nv_wr32(priv, GPCPLL_CFG, val);
 381                nv_rd32(priv, GPCPLL_CFG);
 382                udelay(2);
 383        }
 384
 385        _gk20a_pllg_disable(priv);
 386
 387        nv_debug(priv, "%s: m=%d n=%d pl=%d\n", __func__, priv->m, priv->n,
 388                 priv->pl);
 389
 390        n_lo = DIV_ROUND_UP(priv->m * priv->params->min_vco,
 391                            priv->parent_rate / MHZ);
 392        val = priv->m << GPCPLL_COEFF_M_SHIFT;
 393        val |= (allow_slide ? n_lo : priv->n) << GPCPLL_COEFF_N_SHIFT;
 394        val |= priv->pl << GPCPLL_COEFF_P_SHIFT;
 395        nv_wr32(priv, GPCPLL_COEFF, val);
 396
 397        _gk20a_pllg_enable(priv);
 398
 399        val = nv_rd32(priv, GPCPLL_CFG);
 400        if (val & GPCPLL_CFG_LOCK_DET_OFF) {
 401                val &= ~GPCPLL_CFG_LOCK_DET_OFF;
 402                nv_wr32(priv, GPCPLL_CFG, val);
 403        }
 404
 405        if (!nvkm_timer_wait_eq(priv, 300000, GPCPLL_CFG, GPCPLL_CFG_LOCK,
 406                                GPCPLL_CFG_LOCK)) {
 407                nv_error(priv, "%s: timeout waiting for pllg lock\n", __func__);
 408                return -ETIMEDOUT;
 409        }
 410
 411        /* switch to VCO mode */
 412        nv_mask(priv, SEL_VCO, 0, BIT(SEL_VCO_GPC2CLK_OUT_SHIFT));
 413
 414        /* restore out divider 1:1 */
 415        val = nv_rd32(priv, GPC2CLK_OUT);
 416        val &= ~GPC2CLK_OUT_VCODIV_MASK;
 417        udelay(2);
 418        nv_wr32(priv, GPC2CLK_OUT, val);
 419
 420        /* slide up to new NDIV */
 421        return allow_slide ? gk20a_pllg_slide(priv, priv->n) : 0;
 422}
 423
 424static int
 425gk20a_pllg_program_mnp(struct gk20a_clk_priv *priv)
 426{
 427        int err;
 428
 429        err = _gk20a_pllg_program_mnp(priv, true);
 430        if (err)
 431                err = _gk20a_pllg_program_mnp(priv, false);
 432
 433        return err;
 434}
 435
 436static void
 437gk20a_pllg_disable(struct gk20a_clk_priv *priv)
 438{
 439        u32 val;
 440
 441        /* slide to VCO min */
 442        val = nv_rd32(priv, GPCPLL_CFG);
 443        if (val & GPCPLL_CFG_ENABLE) {
 444                u32 coeff, m, n_lo;
 445
 446                coeff = nv_rd32(priv, GPCPLL_COEFF);
 447                m = (coeff >> GPCPLL_COEFF_M_SHIFT) & MASK(GPCPLL_COEFF_M_WIDTH);
 448                n_lo = DIV_ROUND_UP(m * priv->params->min_vco,
 449                                    priv->parent_rate / MHZ);
 450                gk20a_pllg_slide(priv, n_lo);
 451        }
 452
 453        /* put PLL in bypass before disabling it */
 454        nv_mask(priv, SEL_VCO, BIT(SEL_VCO_GPC2CLK_OUT_SHIFT), 0);
 455
 456        _gk20a_pllg_disable(priv);
 457}
 458
 459#define GK20A_CLK_GPC_MDIV 1000
 460
 461static struct nvkm_domain
 462gk20a_domains[] = {
 463        { nv_clk_src_crystal, 0xff },
 464        { nv_clk_src_gpc, 0xff, 0, "core", GK20A_CLK_GPC_MDIV },
 465        { nv_clk_src_max }
 466};
 467
 468static struct nvkm_pstate
 469gk20a_pstates[] = {
 470        {
 471                .base = {
 472                        .domain[nv_clk_src_gpc] = 72000,
 473                        .voltage = 0,
 474                },
 475        },
 476        {
 477                .base = {
 478                        .domain[nv_clk_src_gpc] = 108000,
 479                        .voltage = 1,
 480                },
 481        },
 482        {
 483                .base = {
 484                        .domain[nv_clk_src_gpc] = 180000,
 485                        .voltage = 2,
 486                },
 487        },
 488        {
 489                .base = {
 490                        .domain[nv_clk_src_gpc] = 252000,
 491                        .voltage = 3,
 492                },
 493        },
 494        {
 495                .base = {
 496                        .domain[nv_clk_src_gpc] = 324000,
 497                        .voltage = 4,
 498                },
 499        },
 500        {
 501                .base = {
 502                        .domain[nv_clk_src_gpc] = 396000,
 503                        .voltage = 5,
 504                },
 505        },
 506        {
 507                .base = {
 508                        .domain[nv_clk_src_gpc] = 468000,
 509                        .voltage = 6,
 510                },
 511        },
 512        {
 513                .base = {
 514                        .domain[nv_clk_src_gpc] = 540000,
 515                        .voltage = 7,
 516                },
 517        },
 518        {
 519                .base = {
 520                        .domain[nv_clk_src_gpc] = 612000,
 521                        .voltage = 8,
 522                },
 523        },
 524        {
 525                .base = {
 526                        .domain[nv_clk_src_gpc] = 648000,
 527                        .voltage = 9,
 528                },
 529        },
 530        {
 531                .base = {
 532                        .domain[nv_clk_src_gpc] = 684000,
 533                        .voltage = 10,
 534                },
 535        },
 536        {
 537                .base = {
 538                        .domain[nv_clk_src_gpc] = 708000,
 539                        .voltage = 11,
 540                },
 541        },
 542        {
 543                .base = {
 544                        .domain[nv_clk_src_gpc] = 756000,
 545                        .voltage = 12,
 546                },
 547        },
 548        {
 549                .base = {
 550                        .domain[nv_clk_src_gpc] = 804000,
 551                        .voltage = 13,
 552                },
 553        },
 554        {
 555                .base = {
 556                        .domain[nv_clk_src_gpc] = 852000,
 557                        .voltage = 14,
 558                },
 559        },
 560};
 561
 562static int
 563gk20a_clk_read(struct nvkm_clk *clk, enum nv_clk_src src)
 564{
 565        struct gk20a_clk_priv *priv = (void *)clk;
 566
 567        switch (src) {
 568        case nv_clk_src_crystal:
 569                return nv_device(clk)->crystal;
 570        case nv_clk_src_gpc:
 571                gk20a_pllg_read_mnp(priv);
 572                return gk20a_pllg_calc_rate(priv) / GK20A_CLK_GPC_MDIV;
 573        default:
 574                nv_error(clk, "invalid clock source %d\n", src);
 575                return -EINVAL;
 576        }
 577}
 578
 579static int
 580gk20a_clk_calc(struct nvkm_clk *clk, struct nvkm_cstate *cstate)
 581{
 582        struct gk20a_clk_priv *priv = (void *)clk;
 583
 584        return gk20a_pllg_calc_mnp(priv, cstate->domain[nv_clk_src_gpc] *
 585                                         GK20A_CLK_GPC_MDIV);
 586}
 587
 588static int
 589gk20a_clk_prog(struct nvkm_clk *clk)
 590{
 591        struct gk20a_clk_priv *priv = (void *)clk;
 592
 593        return gk20a_pllg_program_mnp(priv);
 594}
 595
 596static void
 597gk20a_clk_tidy(struct nvkm_clk *clk)
 598{
 599}
 600
 601static int
 602gk20a_clk_fini(struct nvkm_object *object, bool suspend)
 603{
 604        struct gk20a_clk_priv *priv = (void *)object;
 605        int ret;
 606
 607        ret = nvkm_clk_fini(&priv->base, false);
 608
 609        gk20a_pllg_disable(priv);
 610
 611        return ret;
 612}
 613
 614static int
 615gk20a_clk_init(struct nvkm_object *object)
 616{
 617        struct gk20a_clk_priv *priv = (void *)object;
 618        int ret;
 619
 620        nv_mask(priv, GPC2CLK_OUT, GPC2CLK_OUT_INIT_MASK, GPC2CLK_OUT_INIT_VAL);
 621
 622        ret = nvkm_clk_init(&priv->base);
 623        if (ret)
 624                return ret;
 625
 626        ret = gk20a_clk_prog(&priv->base);
 627        if (ret) {
 628                nv_error(priv, "cannot initialize clock\n");
 629                return ret;
 630        }
 631
 632        return 0;
 633}
 634
 635static int
 636gk20a_clk_ctor(struct nvkm_object *parent, struct nvkm_object *engine,
 637               struct nvkm_oclass *oclass, void *data, u32 size,
 638               struct nvkm_object **pobject)
 639{
 640        struct gk20a_clk_priv *priv;
 641        struct nouveau_platform_device *plat;
 642        int ret;
 643        int i;
 644
 645        /* Finish initializing the pstates */
 646        for (i = 0; i < ARRAY_SIZE(gk20a_pstates); i++) {
 647                INIT_LIST_HEAD(&gk20a_pstates[i].list);
 648                gk20a_pstates[i].pstate = i + 1;
 649        }
 650
 651        ret = nvkm_clk_create(parent, engine, oclass, gk20a_domains,
 652                              gk20a_pstates, ARRAY_SIZE(gk20a_pstates),
 653                              true, &priv);
 654        *pobject = nv_object(priv);
 655        if (ret)
 656                return ret;
 657
 658        priv->params = &gk20a_pllg_params;
 659
 660        plat = nv_device_to_platform(nv_device(parent));
 661        priv->parent_rate = clk_get_rate(plat->gpu->clk);
 662        nv_info(priv, "parent clock rate: %d Mhz\n", priv->parent_rate / MHZ);
 663
 664        priv->base.read = gk20a_clk_read;
 665        priv->base.calc = gk20a_clk_calc;
 666        priv->base.prog = gk20a_clk_prog;
 667        priv->base.tidy = gk20a_clk_tidy;
 668        return 0;
 669}
 670
 671struct nvkm_oclass
 672gk20a_clk_oclass = {
 673        .handle = NV_SUBDEV(CLK, 0xea),
 674        .ofuncs = &(struct nvkm_ofuncs) {
 675                .ctor = gk20a_clk_ctor,
 676                .dtor = _nvkm_subdev_dtor,
 677                .init = gk20a_clk_init,
 678                .fini = gk20a_clk_fini,
 679        },
 680};
 681