gk20a.c 16.9 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
/*
 * Copyright (c) 2014, 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.
 *
 * Shamelessly ripped off from ChromeOS's gk20a/clk_pllg.c
 *
 */
25 26 27 28 29 30
#include <subdev/clk.h>
#include <subdev/timer.h>

#ifdef __KERNEL__
#include <nouveau_platform.h>
#endif
31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110

#define MHZ (1000 * 1000)

#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)
#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 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_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,
};

/* All frequencies in Mhz */
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 const struct gk20a_clk_pllg_params gk20a_pllg_params = {
111
	.min_vco = 1000, .max_vco = 2064,
112 113 114 115 116 117
	.min_u = 12, .max_u = 38,
	.min_m = 1, .max_m = 255,
	.min_n = 8, .max_n = 255,
	.min_pl = 1, .max_pl = 32,
};

B
Ben Skeggs 已提交
118
struct gk20a_clk {
119
	struct nvkm_clk base;
120 121 122 123
	const struct gk20a_clk_pllg_params *params;
	u32 m, n, pl;
	u32 parent_rate;
};
B
Ben Skeggs 已提交
124
#define to_gk20a_clk(base) container_of(base, struct gk20a_clk, base)
125 126

static void
B
Ben Skeggs 已提交
127
gk20a_pllg_read_mnp(struct gk20a_clk *clk)
128
{
129
	struct nvkm_device *device = clk->base.subdev.device;
130 131
	u32 val;

132
	val = nvkm_rd32(device, GPCPLL_COEFF);
B
Ben Skeggs 已提交
133 134 135
	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);
136 137 138
}

static u32
B
Ben Skeggs 已提交
139
gk20a_pllg_calc_rate(struct gk20a_clk *clk)
140 141 142 143
{
	u32 rate;
	u32 divider;

B
Ben Skeggs 已提交
144 145
	rate = clk->parent_rate * clk->n;
	divider = clk->m * pl_to_div[clk->pl];
146 147 148 149 150 151
	do_div(rate, divider);

	return rate / 2;
}

static int
B
Ben Skeggs 已提交
152
gk20a_pllg_calc_mnp(struct gk20a_clk *clk, unsigned long rate)
153 154 155 156 157 158 159 160 161 162 163 164
{
	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 best_m, best_n;
	u32 u_f;
	u32 m, n, n2;
	u32 delta, lwv, best_delta = ~0;
	u32 pl;

	target_clk_f = rate * 2 / MHZ;
B
Ben Skeggs 已提交
165
	ref_clk_f = clk->parent_rate / MHZ;
166

B
Ben Skeggs 已提交
167 168 169 170 171
	max_vco_f = clk->params->max_vco;
	min_vco_f = clk->params->min_vco;
	best_m = clk->params->max_m;
	best_n = clk->params->min_n;
	best_pl = clk->params->min_pl;
172 173 174 175 176 177 178

	target_vco_f = target_clk_f + target_clk_f / 50;
	if (max_vco_f < target_vco_f)
		max_vco_f = target_vco_f;

	/* min_pl <= high_pl <= max_pl */
	high_pl = (max_vco_f + target_vco_f - 1) / target_vco_f;
B
Ben Skeggs 已提交
179 180
	high_pl = min(high_pl, clk->params->max_pl);
	high_pl = max(high_pl, clk->params->min_pl);
181 182 183

	/* min_pl <= low_pl <= max_pl */
	low_pl = min_vco_f / target_vco_f;
B
Ben Skeggs 已提交
184 185
	low_pl = min(low_pl, clk->params->max_pl);
	low_pl = max(low_pl, clk->params->min_pl);
186 187 188 189 190 191 192 193 194 195 196 197 198 199 200

	/* 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;
		}
	}

B
Ben Skeggs 已提交
201
	nv_debug(clk, "low_PL %d(div%d), high_PL %d(div%d)", low_pl,
202 203 204 205 206
		 pl_to_div[low_pl], high_pl, pl_to_div[high_pl]);

	/* Select lowest possible VCO */
	for (pl = low_pl; pl <= high_pl; pl++) {
		target_vco_f = target_clk_f * pl_to_div[pl];
B
Ben Skeggs 已提交
207
		for (m = clk->params->min_m; m <= clk->params->max_m; m++) {
208 209
			u_f = ref_clk_f / m;

B
Ben Skeggs 已提交
210
			if (u_f < clk->params->min_u)
211
				break;
B
Ben Skeggs 已提交
212
			if (u_f > clk->params->max_u)
213 214 215 216 217
				continue;

			n = (target_vco_f * m) / ref_clk_f;
			n2 = ((target_vco_f * m) + (ref_clk_f - 1)) / ref_clk_f;

B
Ben Skeggs 已提交
218
			if (n > clk->params->max_n)
219 220 221
				break;

			for (; n <= n2; n++) {
B
Ben Skeggs 已提交
222
				if (n < clk->params->min_n)
223
					continue;
B
Ben Skeggs 已提交
224
				if (n > clk->params->max_n)
225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251
					break;

				vco_f = ref_clk_f * n / m;

				if (vco_f >= min_vco_f && vco_f <= max_vco_f) {
					lwv = (vco_f + (pl_to_div[pl] / 2))
						/ pl_to_div[pl];
					delta = abs(lwv - target_clk_f);

					if (delta < best_delta) {
						best_delta = delta;
						best_m = m;
						best_n = n;
						best_pl = pl;

						if (best_delta == 0)
							goto found_match;
					}
				}
			}
		}
	}

found_match:
	WARN_ON(best_delta == ~0);

	if (best_delta != 0)
B
Ben Skeggs 已提交
252
		nv_debug(clk, "no best match for target @ %dMHz on gpc_pll",
253 254
			 target_clk_f);

B
Ben Skeggs 已提交
255 256 257
	clk->m = best_m;
	clk->n = best_n;
	clk->pl = best_pl;
258

B
Ben Skeggs 已提交
259
	target_freq = gk20a_pllg_calc_rate(clk) / MHZ;
260

B
Ben Skeggs 已提交
261 262
	nv_debug(clk, "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]);
263 264 265 266
	return 0;
}

static int
B
Ben Skeggs 已提交
267
gk20a_pllg_slide(struct gk20a_clk *clk, u32 n)
268
{
269
	struct nvkm_device *device = clk->base.subdev.device;
270 271 272 273
	u32 val;
	int ramp_timeout;

	/* get old coefficients */
274
	val = nvkm_rd32(device, GPCPLL_COEFF);
275 276 277 278 279
	/* do nothing if NDIV is the same */
	if (n == ((val >> GPCPLL_COEFF_N_SHIFT) & MASK(GPCPLL_COEFF_N_WIDTH)))
		return 0;

	/* setup */
280
	nvkm_mask(device, GPCPLL_CFG2, 0xff << GPCPLL_CFG2_PLL_STEPA_SHIFT,
281
		0x2b << GPCPLL_CFG2_PLL_STEPA_SHIFT);
282
	nvkm_mask(device, GPCPLL_CFG3, 0xff << GPCPLL_CFG3_PLL_STEPB_SHIFT,
283 284 285
		0xb << GPCPLL_CFG3_PLL_STEPB_SHIFT);

	/* pll slowdown mode */
286
	nvkm_mask(device, GPCPLL_NDIV_SLOWDOWN,
287 288 289 290
		BIT(GPCPLL_NDIV_SLOWDOWN_SLOWDOWN_USING_PLL_SHIFT),
		BIT(GPCPLL_NDIV_SLOWDOWN_SLOWDOWN_USING_PLL_SHIFT));

	/* new ndiv ready for ramp */
291
	val = nvkm_rd32(device, GPCPLL_COEFF);
292 293 294
	val &= ~(MASK(GPCPLL_COEFF_N_WIDTH) << GPCPLL_COEFF_N_SHIFT);
	val |= (n & MASK(GPCPLL_COEFF_N_WIDTH)) << GPCPLL_COEFF_N_SHIFT;
	udelay(1);
295
	nvkm_wr32(device, GPCPLL_COEFF, val);
296 297

	/* dynamic ramp to new ndiv */
298
	val = nvkm_rd32(device, GPCPLL_NDIV_SLOWDOWN);
299 300
	val |= 0x1 << GPCPLL_NDIV_SLOWDOWN_EN_DYNRAMP_SHIFT;
	udelay(1);
301
	nvkm_wr32(device, GPCPLL_NDIV_SLOWDOWN, val);
302 303 304

	for (ramp_timeout = 500; ramp_timeout > 0; ramp_timeout--) {
		udelay(1);
305
		val = nvkm_rd32(device, GPC_BCAST_NDIV_SLOWDOWN_DEBUG);
306 307 308 309 310
		if (val & GPC_BCAST_NDIV_SLOWDOWN_DEBUG_PLL_DYNRAMP_DONE_SYNCED_MASK)
			break;
	}

	/* exit slowdown mode */
311
	nvkm_mask(device, GPCPLL_NDIV_SLOWDOWN,
312 313
		BIT(GPCPLL_NDIV_SLOWDOWN_SLOWDOWN_USING_PLL_SHIFT) |
		BIT(GPCPLL_NDIV_SLOWDOWN_EN_DYNRAMP_SHIFT), 0);
314
	nvkm_rd32(device, GPCPLL_NDIV_SLOWDOWN);
315 316

	if (ramp_timeout <= 0) {
B
Ben Skeggs 已提交
317
		nv_error(clk, "gpcpll dynamic ramp timeout\n");
318 319 320 321 322 323 324
		return -ETIMEDOUT;
	}

	return 0;
}

static void
B
Ben Skeggs 已提交
325
_gk20a_pllg_enable(struct gk20a_clk *clk)
326
{
327 328 329
	struct nvkm_device *device = clk->base.subdev.device;
	nvkm_mask(device, GPCPLL_CFG, GPCPLL_CFG_ENABLE, GPCPLL_CFG_ENABLE);
	nvkm_rd32(device, GPCPLL_CFG);
330 331 332
}

static void
B
Ben Skeggs 已提交
333
_gk20a_pllg_disable(struct gk20a_clk *clk)
334
{
335 336 337
	struct nvkm_device *device = clk->base.subdev.device;
	nvkm_mask(device, GPCPLL_CFG, GPCPLL_CFG_ENABLE, 0);
	nvkm_rd32(device, GPCPLL_CFG);
338 339 340
}

static int
B
Ben Skeggs 已提交
341
_gk20a_pllg_program_mnp(struct gk20a_clk *clk, bool allow_slide)
342
{
343
	struct nvkm_device *device = clk->base.subdev.device;
344 345 346 347
	u32 val, cfg;
	u32 m_old, pl_old, n_lo;

	/* get old coefficients */
348
	val = nvkm_rd32(device, GPCPLL_COEFF);
349 350 351 352
	m_old = (val >> GPCPLL_COEFF_M_SHIFT) & MASK(GPCPLL_COEFF_M_WIDTH);
	pl_old = (val >> GPCPLL_COEFF_P_SHIFT) & MASK(GPCPLL_COEFF_P_WIDTH);

	/* do NDIV slide if there is no change in M and PL */
353
	cfg = nvkm_rd32(device, GPCPLL_CFG);
B
Ben Skeggs 已提交
354
	if (allow_slide && clk->m == m_old && clk->pl == pl_old &&
355
	    (cfg & GPCPLL_CFG_ENABLE)) {
B
Ben Skeggs 已提交
356
		return gk20a_pllg_slide(clk, clk->n);
357 358 359
	}

	/* slide down to NDIV_LO */
B
Ben Skeggs 已提交
360 361
	n_lo = DIV_ROUND_UP(m_old * clk->params->min_vco,
			    clk->parent_rate / MHZ);
362
	if (allow_slide && (cfg & GPCPLL_CFG_ENABLE)) {
B
Ben Skeggs 已提交
363
		int ret = gk20a_pllg_slide(clk, n_lo);
364 365 366 367 368 369

		if (ret)
			return ret;
	}

	/* split FO-to-bypass jump in halfs by setting out divider 1:2 */
370
	nvkm_mask(device, GPC2CLK_OUT, GPC2CLK_OUT_VCODIV_MASK,
371 372 373
		0x2 << GPC2CLK_OUT_VCODIV_SHIFT);

	/* put PLL in bypass before programming it */
374
	val = nvkm_rd32(device, SEL_VCO);
375 376
	val &= ~(BIT(SEL_VCO_GPC2CLK_OUT_SHIFT));
	udelay(2);
377
	nvkm_wr32(device, SEL_VCO, val);
378 379

	/* get out from IDDQ */
380
	val = nvkm_rd32(device, GPCPLL_CFG);
381 382
	if (val & GPCPLL_CFG_IDDQ) {
		val &= ~GPCPLL_CFG_IDDQ;
383 384
		nvkm_wr32(device, GPCPLL_CFG, val);
		nvkm_rd32(device, GPCPLL_CFG);
385 386 387
		udelay(2);
	}

B
Ben Skeggs 已提交
388
	_gk20a_pllg_disable(clk);
389

B
Ben Skeggs 已提交
390 391
	nv_debug(clk, "%s: m=%d n=%d pl=%d\n", __func__, clk->m, clk->n,
		 clk->pl);
392

B
Ben Skeggs 已提交
393 394 395 396 397
	n_lo = DIV_ROUND_UP(clk->m * clk->params->min_vco,
			    clk->parent_rate / MHZ);
	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;
398
	nvkm_wr32(device, GPCPLL_COEFF, val);
399

B
Ben Skeggs 已提交
400
	_gk20a_pllg_enable(clk);
401

402
	val = nvkm_rd32(device, GPCPLL_CFG);
403 404
	if (val & GPCPLL_CFG_LOCK_DET_OFF) {
		val &= ~GPCPLL_CFG_LOCK_DET_OFF;
405
		nvkm_wr32(device, GPCPLL_CFG, val);
406 407
	}

B
Ben Skeggs 已提交
408
	if (!nvkm_timer_wait_eq(clk, 300000, GPCPLL_CFG, GPCPLL_CFG_LOCK,
409
				GPCPLL_CFG_LOCK)) {
B
Ben Skeggs 已提交
410
		nv_error(clk, "%s: timeout waiting for pllg lock\n", __func__);
411 412 413 414
		return -ETIMEDOUT;
	}

	/* switch to VCO mode */
415
	nvkm_mask(device, SEL_VCO, 0, BIT(SEL_VCO_GPC2CLK_OUT_SHIFT));
416 417

	/* restore out divider 1:1 */
418
	val = nvkm_rd32(device, GPC2CLK_OUT);
419 420
	val &= ~GPC2CLK_OUT_VCODIV_MASK;
	udelay(2);
421
	nvkm_wr32(device, GPC2CLK_OUT, val);
422 423

	/* slide up to new NDIV */
B
Ben Skeggs 已提交
424
	return allow_slide ? gk20a_pllg_slide(clk, clk->n) : 0;
425 426 427
}

static int
B
Ben Skeggs 已提交
428
gk20a_pllg_program_mnp(struct gk20a_clk *clk)
429 430 431
{
	int err;

B
Ben Skeggs 已提交
432
	err = _gk20a_pllg_program_mnp(clk, true);
433
	if (err)
B
Ben Skeggs 已提交
434
		err = _gk20a_pllg_program_mnp(clk, false);
435 436 437 438 439

	return err;
}

static void
B
Ben Skeggs 已提交
440
gk20a_pllg_disable(struct gk20a_clk *clk)
441
{
442
	struct nvkm_device *device = clk->base.subdev.device;
443 444 445
	u32 val;

	/* slide to VCO min */
446
	val = nvkm_rd32(device, GPCPLL_CFG);
447 448 449
	if (val & GPCPLL_CFG_ENABLE) {
		u32 coeff, m, n_lo;

450
		coeff = nvkm_rd32(device, GPCPLL_COEFF);
451
		m = (coeff >> GPCPLL_COEFF_M_SHIFT) & MASK(GPCPLL_COEFF_M_WIDTH);
B
Ben Skeggs 已提交
452 453 454
		n_lo = DIV_ROUND_UP(m * clk->params->min_vco,
				    clk->parent_rate / MHZ);
		gk20a_pllg_slide(clk, n_lo);
455 456 457
	}

	/* put PLL in bypass before disabling it */
458
	nvkm_mask(device, SEL_VCO, BIT(SEL_VCO_GPC2CLK_OUT_SHIFT), 0);
459

B
Ben Skeggs 已提交
460
	_gk20a_pllg_disable(clk);
461 462 463 464
}

#define GK20A_CLK_GPC_MDIV 1000

465
static struct nvkm_domain
466 467 468 469 470 471
gk20a_domains[] = {
	{ nv_clk_src_crystal, 0xff },
	{ nv_clk_src_gpc, 0xff, 0, "core", GK20A_CLK_GPC_MDIV },
	{ nv_clk_src_max }
};

472
static struct nvkm_pstate
473 474 475 476
gk20a_pstates[] = {
	{
		.base = {
			.domain[nv_clk_src_gpc] = 72000,
477
			.voltage = 0,
478 479 480 481 482
		},
	},
	{
		.base = {
			.domain[nv_clk_src_gpc] = 108000,
483
			.voltage = 1,
484 485 486 487 488
		},
	},
	{
		.base = {
			.domain[nv_clk_src_gpc] = 180000,
489
			.voltage = 2,
490 491 492 493 494
		},
	},
	{
		.base = {
			.domain[nv_clk_src_gpc] = 252000,
495
			.voltage = 3,
496 497 498 499 500
		},
	},
	{
		.base = {
			.domain[nv_clk_src_gpc] = 324000,
501
			.voltage = 4,
502 503 504 505 506
		},
	},
	{
		.base = {
			.domain[nv_clk_src_gpc] = 396000,
507
			.voltage = 5,
508 509 510 511 512
		},
	},
	{
		.base = {
			.domain[nv_clk_src_gpc] = 468000,
513
			.voltage = 6,
514 515 516 517 518
		},
	},
	{
		.base = {
			.domain[nv_clk_src_gpc] = 540000,
519
			.voltage = 7,
520 521 522 523 524
		},
	},
	{
		.base = {
			.domain[nv_clk_src_gpc] = 612000,
525
			.voltage = 8,
526 527 528 529 530
		},
	},
	{
		.base = {
			.domain[nv_clk_src_gpc] = 648000,
531
			.voltage = 9,
532 533 534 535 536
		},
	},
	{
		.base = {
			.domain[nv_clk_src_gpc] = 684000,
537
			.voltage = 10,
538 539 540 541 542
		},
	},
	{
		.base = {
			.domain[nv_clk_src_gpc] = 708000,
543
			.voltage = 11,
544 545 546 547 548
		},
	},
	{
		.base = {
			.domain[nv_clk_src_gpc] = 756000,
549
			.voltage = 12,
550 551 552 553 554
		},
	},
	{
		.base = {
			.domain[nv_clk_src_gpc] = 804000,
555
			.voltage = 13,
556 557 558 559 560
		},
	},
	{
		.base = {
			.domain[nv_clk_src_gpc] = 852000,
561
			.voltage = 14,
562 563 564 565 566
		},
	},
};

static int
B
Ben Skeggs 已提交
567
gk20a_clk_read(struct nvkm_clk *obj, enum nv_clk_src src)
568
{
B
Ben Skeggs 已提交
569
	struct gk20a_clk *clk = container_of(obj, typeof(*clk), base);
570
	struct nvkm_device *device = clk->base.subdev.device;
571 572 573

	switch (src) {
	case nv_clk_src_crystal:
574
		return device->crystal;
575
	case nv_clk_src_gpc:
B
Ben Skeggs 已提交
576 577
		gk20a_pllg_read_mnp(clk);
		return gk20a_pllg_calc_rate(clk) / GK20A_CLK_GPC_MDIV;
578 579 580 581 582 583 584
	default:
		nv_error(clk, "invalid clock source %d\n", src);
		return -EINVAL;
	}
}

static int
B
Ben Skeggs 已提交
585
gk20a_clk_calc(struct nvkm_clk *obj, struct nvkm_cstate *cstate)
586
{
B
Ben Skeggs 已提交
587
	struct gk20a_clk *clk = container_of(obj, typeof(*clk), base);
588

B
Ben Skeggs 已提交
589
	return gk20a_pllg_calc_mnp(clk, cstate->domain[nv_clk_src_gpc] *
590 591 592 593
					 GK20A_CLK_GPC_MDIV);
}

static int
B
Ben Skeggs 已提交
594
gk20a_clk_prog(struct nvkm_clk *obj)
595
{
B
Ben Skeggs 已提交
596
	struct gk20a_clk *clk = container_of(obj, typeof(*clk), base);
597

B
Ben Skeggs 已提交
598
	return gk20a_pllg_program_mnp(clk);
599 600 601
}

static void
B
Ben Skeggs 已提交
602
gk20a_clk_tidy(struct nvkm_clk *obj)
603 604 605 606
{
}

static int
607
gk20a_clk_fini(struct nvkm_object *object, bool suspend)
608
{
B
Ben Skeggs 已提交
609
	struct gk20a_clk *clk = (void *)object;
610 611
	int ret;

B
Ben Skeggs 已提交
612
	ret = nvkm_clk_fini(&clk->base, false);
613

B
Ben Skeggs 已提交
614
	gk20a_pllg_disable(clk);
615 616 617 618 619

	return ret;
}

static int
620
gk20a_clk_init(struct nvkm_object *object)
621
{
B
Ben Skeggs 已提交
622
	struct gk20a_clk *clk = (void *)object;
623
	struct nvkm_device *device = clk->base.subdev.device;
624 625
	int ret;

626
	nvkm_mask(device, GPC2CLK_OUT, GPC2CLK_OUT_INIT_MASK, GPC2CLK_OUT_INIT_VAL);
627

B
Ben Skeggs 已提交
628
	ret = nvkm_clk_init(&clk->base);
629 630 631
	if (ret)
		return ret;

B
Ben Skeggs 已提交
632
	ret = gk20a_clk_prog(&clk->base);
633
	if (ret) {
B
Ben Skeggs 已提交
634
		nv_error(clk, "cannot initialize clock\n");
635 636 637 638 639 640 641
		return ret;
	}

	return 0;
}

static int
642 643 644
gk20a_clk_ctor(struct nvkm_object *parent, struct nvkm_object *engine,
	       struct nvkm_oclass *oclass, void *data, u32 size,
	       struct nvkm_object **pobject)
645
{
B
Ben Skeggs 已提交
646
	struct gk20a_clk *clk;
647 648 649 650 651 652 653 654 655 656
	struct nouveau_platform_device *plat;
	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;
	}

657 658
	ret = nvkm_clk_create(parent, engine, oclass, gk20a_domains,
			      gk20a_pstates, ARRAY_SIZE(gk20a_pstates),
B
Ben Skeggs 已提交
659 660
			      true, &clk);
	*pobject = nv_object(clk);
661 662 663
	if (ret)
		return ret;

B
Ben Skeggs 已提交
664
	clk->params = &gk20a_pllg_params;
665 666

	plat = nv_device_to_platform(nv_device(parent));
B
Ben Skeggs 已提交
667 668
	clk->parent_rate = clk_get_rate(plat->gpu->clk);
	nv_info(clk, "parent clock rate: %d Mhz\n", clk->parent_rate / MHZ);
669

B
Ben Skeggs 已提交
670 671 672 673
	clk->base.read = gk20a_clk_read;
	clk->base.calc = gk20a_clk_calc;
	clk->base.prog = gk20a_clk_prog;
	clk->base.tidy = gk20a_clk_tidy;
674 675 676
	return 0;
}

677
struct nvkm_oclass
678 679
gk20a_clk_oclass = {
	.handle = NV_SUBDEV(CLK, 0xea),
680
	.ofuncs = &(struct nvkm_ofuncs) {
681
		.ctor = gk20a_clk_ctor,
682
		.dtor = _nvkm_subdev_dtor,
683 684
		.init = gk20a_clk_init,
		.fini = gk20a_clk_fini,
685 686
	},
};