gf100.c 11.8 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
/*
 * Copyright 2012 Red Hat Inc.
 *
 * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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.
 *
 * Authors: Ben Skeggs
 */
24
#include <subdev/clk.h>
25 26
#include "pll.h"

27 28
#include <subdev/bios.h>
#include <subdev/bios/pll.h>
29
#include <subdev/timer.h>
30

31
struct gf100_clk_info {
32 33 34 35 36 37 38 39
	u32 freq;
	u32 ssel;
	u32 mdiv;
	u32 dsrc;
	u32 ddiv;
	u32 coef;
};

B
Ben Skeggs 已提交
40
struct gf100_clk {
41 42
	struct nvkm_clk base;
	struct gf100_clk_info eng[16];
43 44
};

B
Ben Skeggs 已提交
45
static u32 read_div(struct gf100_clk *, int, u32, u32);
46 47

static u32
B
Ben Skeggs 已提交
48
read_vco(struct gf100_clk *clk, u32 dsrc)
49
{
50 51
	struct nvkm_device *device = clk->base.subdev.device;
	u32 ssrc = nvkm_rd32(device, dsrc);
52
	if (!(ssrc & 0x00000100))
B
Ben Skeggs 已提交
53 54
		return clk->base.read(&clk->base, nv_clk_src_sppll0);
	return clk->base.read(&clk->base, nv_clk_src_sppll1);
55 56 57
}

static u32
B
Ben Skeggs 已提交
58
read_pll(struct gf100_clk *clk, u32 pll)
59
{
60 61 62
	struct nvkm_device *device = clk->base.subdev.device;
	u32 ctrl = nvkm_rd32(device, pll + 0x00);
	u32 coef = nvkm_rd32(device, pll + 0x04);
63 64 65 66 67 68 69 70 71 72 73
	u32 P = (coef & 0x003f0000) >> 16;
	u32 N = (coef & 0x0000ff00) >> 8;
	u32 M = (coef & 0x000000ff) >> 0;
	u32 sclk;

	if (!(ctrl & 0x00000001))
		return 0;

	switch (pll) {
	case 0x00e800:
	case 0x00e820:
74
		sclk = device->crystal;
75 76 77
		P = 1;
		break;
	case 0x132000:
B
Ben Skeggs 已提交
78
		sclk = clk->base.read(&clk->base, nv_clk_src_mpllsrc);
79 80
		break;
	case 0x132020:
B
Ben Skeggs 已提交
81
		sclk = clk->base.read(&clk->base, nv_clk_src_mpllsrcref);
82 83 84 85 86
		break;
	case 0x137000:
	case 0x137020:
	case 0x137040:
	case 0x1370e0:
B
Ben Skeggs 已提交
87
		sclk = read_div(clk, (pll & 0xff) / 0x20, 0x137120, 0x137140);
88 89 90 91 92 93 94 95 96
		break;
	default:
		return 0;
	}

	return sclk * N / M / P;
}

static u32
B
Ben Skeggs 已提交
97
read_div(struct gf100_clk *clk, int doff, u32 dsrc, u32 dctl)
98
{
99 100 101
	struct nvkm_device *device = clk->base.subdev.device;
	u32 ssrc = nvkm_rd32(device, dsrc + (doff * 4));
	u32 sctl = nvkm_rd32(device, dctl + (doff * 4));
102 103 104 105

	switch (ssrc & 0x00000003) {
	case 0:
		if ((ssrc & 0x00030000) != 0x00030000)
106
			return device->crystal;
107 108 109 110 111
		return 108000;
	case 2:
		return 100000;
	case 3:
		if (sctl & 0x80000000) {
B
Ben Skeggs 已提交
112
			u32 sclk = read_vco(clk, dsrc + (doff * 4));
113 114 115 116
			u32 sdiv = (sctl & 0x0000003f) + 2;
			return (sclk * 2) / sdiv;
		}

B
Ben Skeggs 已提交
117
		return read_vco(clk, dsrc + (doff * 4));
118 119 120 121 122 123
	default:
		return 0;
	}
}

static u32
B
Ben Skeggs 已提交
124
read_clk(struct gf100_clk *clk, int idx)
125
{
126 127 128
	struct nvkm_device *device = clk->base.subdev.device;
	u32 sctl = nvkm_rd32(device, 0x137250 + (idx * 4));
	u32 ssel = nvkm_rd32(device, 0x137100);
129 130
	u32 sclk, sdiv;

B
Ben Skeggs 已提交
131 132 133
	if (ssel & (1 << idx)) {
		if (idx < 7)
			sclk = read_pll(clk, 0x137000 + (idx * 0x20));
134
		else
B
Ben Skeggs 已提交
135
			sclk = read_pll(clk, 0x1370e0);
136 137
		sdiv = ((sctl & 0x00003f00) >> 8) + 2;
	} else {
B
Ben Skeggs 已提交
138
		sclk = read_div(clk, idx, 0x137160, 0x1371d0);
139 140 141 142 143 144 145 146 147 148
		sdiv = ((sctl & 0x0000003f) >> 0) + 2;
	}

	if (sctl & 0x80000000)
		return (sclk * 2) / sdiv;

	return sclk;
}

static int
B
Ben Skeggs 已提交
149
gf100_clk_read(struct nvkm_clk *obj, enum nv_clk_src src)
150
{
B
Ben Skeggs 已提交
151
	struct gf100_clk *clk = container_of(obj, typeof(*clk), base);
152
	struct nvkm_device *device = clk->base.subdev.device;
153 154 155 156 157 158 159

	switch (src) {
	case nv_clk_src_crystal:
		return device->crystal;
	case nv_clk_src_href:
		return 100000;
	case nv_clk_src_sppll0:
B
Ben Skeggs 已提交
160
		return read_pll(clk, 0x00e800);
161
	case nv_clk_src_sppll1:
B
Ben Skeggs 已提交
162
		return read_pll(clk, 0x00e820);
163 164

	case nv_clk_src_mpllsrcref:
B
Ben Skeggs 已提交
165
		return read_div(clk, 0, 0x137320, 0x137330);
166
	case nv_clk_src_mpllsrc:
B
Ben Skeggs 已提交
167
		return read_pll(clk, 0x132020);
168
	case nv_clk_src_mpll:
B
Ben Skeggs 已提交
169
		return read_pll(clk, 0x132000);
170
	case nv_clk_src_mdiv:
B
Ben Skeggs 已提交
171
		return read_div(clk, 0, 0x137300, 0x137310);
172
	case nv_clk_src_mem:
173
		if (nvkm_rd32(device, 0x1373f0) & 0x00000002)
B
Ben Skeggs 已提交
174 175
			return clk->base.read(&clk->base, nv_clk_src_mpll);
		return clk->base.read(&clk->base, nv_clk_src_mdiv);
176 177

	case nv_clk_src_gpc:
B
Ben Skeggs 已提交
178
		return read_clk(clk, 0x00);
179
	case nv_clk_src_rop:
B
Ben Skeggs 已提交
180
		return read_clk(clk, 0x01);
181
	case nv_clk_src_hubk07:
B
Ben Skeggs 已提交
182
		return read_clk(clk, 0x02);
183
	case nv_clk_src_hubk06:
B
Ben Skeggs 已提交
184
		return read_clk(clk, 0x07);
185
	case nv_clk_src_hubk01:
B
Ben Skeggs 已提交
186
		return read_clk(clk, 0x08);
187
	case nv_clk_src_copy:
B
Ben Skeggs 已提交
188
		return read_clk(clk, 0x09);
189
	case nv_clk_src_daemon:
B
Ben Skeggs 已提交
190
		return read_clk(clk, 0x0c);
191
	case nv_clk_src_vdec:
B
Ben Skeggs 已提交
192
		return read_clk(clk, 0x0e);
193 194 195 196 197 198 199
	default:
		nv_error(clk, "invalid clock source %d\n", src);
		return -EINVAL;
	}
}

static u32
B
Ben Skeggs 已提交
200
calc_div(struct gf100_clk *clk, int idx, u32 ref, u32 freq, u32 *ddiv)
201 202 203 204 205 206 207 208 209 210
{
	u32 div = min((ref * 2) / freq, (u32)65);
	if (div < 2)
		div = 2;

	*ddiv = div - 2;
	return (ref * 2) / div;
}

static u32
B
Ben Skeggs 已提交
211
calc_src(struct gf100_clk *clk, int idx, u32 freq, u32 *dsrc, u32 *ddiv)
212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232
{
	u32 sclk;

	/* use one of the fixed frequencies if possible */
	*ddiv = 0x00000000;
	switch (freq) {
	case  27000:
	case 108000:
		*dsrc = 0x00000000;
		if (freq == 108000)
			*dsrc |= 0x00030000;
		return freq;
	case 100000:
		*dsrc = 0x00000002;
		return freq;
	default:
		*dsrc = 0x00000003;
		break;
	}

	/* otherwise, calculate the closest divider */
B
Ben Skeggs 已提交
233 234 235
	sclk = read_vco(clk, 0x137160 + (idx * 4));
	if (idx < 7)
		sclk = calc_div(clk, idx, sclk, freq, ddiv);
236 237 238 239
	return sclk;
}

static u32
B
Ben Skeggs 已提交
240
calc_pll(struct gf100_clk *clk, int idx, u32 freq, u32 *coef)
241
{
B
Ben Skeggs 已提交
242
	struct nvkm_bios *bios = nvkm_bios(clk);
243 244 245
	struct nvbios_pll limits;
	int N, M, P, ret;

B
Ben Skeggs 已提交
246
	ret = nvbios_pll_parse(bios, 0x137000 + (idx * 0x20), &limits);
247 248 249
	if (ret)
		return 0;

B
Ben Skeggs 已提交
250
	limits.refclk = read_div(clk, idx, 0x137120, 0x137140);
251 252 253
	if (!limits.refclk)
		return 0;

B
Ben Skeggs 已提交
254
	ret = gt215_pll_calc(nv_subdev(clk), &limits, freq, &N, NULL, &M, &P);
255 256 257 258 259 260 261 262
	if (ret <= 0)
		return 0;

	*coef = (P << 16) | (N << 8) | M;
	return ret;
}

static int
B
Ben Skeggs 已提交
263
calc_clk(struct gf100_clk *clk, struct nvkm_cstate *cstate, int idx, int dom)
264
{
B
Ben Skeggs 已提交
265
	struct gf100_clk_info *info = &clk->eng[idx];
266 267 268 269 270 271 272 273 274
	u32 freq = cstate->domain[dom];
	u32 src0, div0, div1D, div1P = 0;
	u32 clk0, clk1 = 0;

	/* invalid clock domain */
	if (!freq)
		return 0;

	/* first possible path, using only dividers */
B
Ben Skeggs 已提交
275 276
	clk0 = calc_src(clk, idx, freq, &src0, &div0);
	clk0 = calc_div(clk, idx, clk0, freq, &div1D);
277 278

	/* see if we can get any closer using PLLs */
B
Ben Skeggs 已提交
279 280 281
	if (clk0 != freq && (0x00004387 & (1 << idx))) {
		if (idx <= 7)
			clk1 = calc_pll(clk, idx, freq, &info->coef);
282 283
		else
			clk1 = cstate->domain[nv_clk_src_hubk06];
B
Ben Skeggs 已提交
284
		clk1 = calc_div(clk, idx, clk1, freq, &div1P);
285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305
	}

	/* select the method which gets closest to target freq */
	if (abs((int)freq - clk0) <= abs((int)freq - clk1)) {
		info->dsrc = src0;
		if (div0) {
			info->ddiv |= 0x80000000;
			info->ddiv |= div0 << 8;
			info->ddiv |= div0;
		}
		if (div1D) {
			info->mdiv |= 0x80000000;
			info->mdiv |= div1D;
		}
		info->ssel = info->coef = 0;
		info->freq = clk0;
	} else {
		if (div1P) {
			info->mdiv |= 0x80000000;
			info->mdiv |= div1P << 8;
		}
B
Ben Skeggs 已提交
306
		info->ssel = (1 << idx);
307 308 309 310 311 312 313
		info->freq = clk1;
	}

	return 0;
}

static int
B
Ben Skeggs 已提交
314
gf100_clk_calc(struct nvkm_clk *obj, struct nvkm_cstate *cstate)
315
{
B
Ben Skeggs 已提交
316
	struct gf100_clk *clk = container_of(obj, typeof(*clk), base);
317 318
	int ret;

B
Ben Skeggs 已提交
319 320 321 322 323 324 325 326
	if ((ret = calc_clk(clk, cstate, 0x00, nv_clk_src_gpc)) ||
	    (ret = calc_clk(clk, cstate, 0x01, nv_clk_src_rop)) ||
	    (ret = calc_clk(clk, cstate, 0x02, nv_clk_src_hubk07)) ||
	    (ret = calc_clk(clk, cstate, 0x07, nv_clk_src_hubk06)) ||
	    (ret = calc_clk(clk, cstate, 0x08, nv_clk_src_hubk01)) ||
	    (ret = calc_clk(clk, cstate, 0x09, nv_clk_src_copy)) ||
	    (ret = calc_clk(clk, cstate, 0x0c, nv_clk_src_daemon)) ||
	    (ret = calc_clk(clk, cstate, 0x0e, nv_clk_src_vdec)))
327 328 329 330 331 332
		return ret;

	return 0;
}

static void
B
Ben Skeggs 已提交
333
gf100_clk_prog_0(struct gf100_clk *clk, int idx)
334
{
B
Ben Skeggs 已提交
335
	struct gf100_clk_info *info = &clk->eng[idx];
336
	struct nvkm_device *device = clk->base.subdev.device;
B
Ben Skeggs 已提交
337
	if (idx < 7 && !info->ssel) {
338 339
		nvkm_mask(device, 0x1371d0 + (idx * 0x04), 0x80003f3f, info->ddiv);
		nvkm_wr32(device, 0x137160 + (idx * 0x04), info->dsrc);
340 341 342 343
	}
}

static void
B
Ben Skeggs 已提交
344
gf100_clk_prog_1(struct gf100_clk *clk, int idx)
345
{
346 347
	struct nvkm_device *device = clk->base.subdev.device;
	nvkm_mask(device, 0x137100, (1 << idx), 0x00000000);
348 349 350 351
	nvkm_msec(device, 2000,
		if (!(nvkm_rd32(device, 0x137100) & (1 << idx)))
			break;
	);
352 353 354
}

static void
B
Ben Skeggs 已提交
355
gf100_clk_prog_2(struct gf100_clk *clk, int idx)
356
{
B
Ben Skeggs 已提交
357
	struct gf100_clk_info *info = &clk->eng[idx];
358
	struct nvkm_device *device = clk->base.subdev.device;
B
Ben Skeggs 已提交
359 360
	const u32 addr = 0x137000 + (idx * 0x20);
	if (idx <= 7) {
361 362
		nvkm_mask(device, addr + 0x00, 0x00000004, 0x00000000);
		nvkm_mask(device, addr + 0x00, 0x00000001, 0x00000000);
363
		if (info->coef) {
364 365
			nvkm_wr32(device, addr + 0x04, info->coef);
			nvkm_mask(device, addr + 0x00, 0x00000001, 0x00000001);
366 367 368 369
			nvkm_msec(device, 2000,
				if (nvkm_rd32(device, addr + 0x00) & 0x00020000)
					break;
			);
370
			nvkm_mask(device, addr + 0x00, 0x00020004, 0x00000004);
371 372 373 374 375
		}
	}
}

static void
B
Ben Skeggs 已提交
376
gf100_clk_prog_3(struct gf100_clk *clk, int idx)
377
{
B
Ben Skeggs 已提交
378
	struct gf100_clk_info *info = &clk->eng[idx];
379
	struct nvkm_device *device = clk->base.subdev.device;
380
	if (info->ssel) {
381
		nvkm_mask(device, 0x137100, (1 << idx), info->ssel);
382 383 384 385 386
		nvkm_msec(device, 2000,
			u32 tmp = nvkm_rd32(device, 0x137100) & (1 << idx);
			if (tmp == info->ssel)
				break;
		);
387 388 389 390
	}
}

static void
B
Ben Skeggs 已提交
391
gf100_clk_prog_4(struct gf100_clk *clk, int idx)
392
{
B
Ben Skeggs 已提交
393
	struct gf100_clk_info *info = &clk->eng[idx];
394 395
	struct nvkm_device *device = clk->base.subdev.device;
	nvkm_mask(device, 0x137250 + (idx * 0x04), 0x00003f3f, info->mdiv);
396 397 398
}

static int
B
Ben Skeggs 已提交
399
gf100_clk_prog(struct nvkm_clk *obj)
400
{
B
Ben Skeggs 已提交
401
	struct gf100_clk *clk = container_of(obj, typeof(*clk), base);
402
	struct {
B
Ben Skeggs 已提交
403
		void (*exec)(struct gf100_clk *, int);
404
	} stage[] = {
405 406 407 408 409
		{ gf100_clk_prog_0 }, /* div programming */
		{ gf100_clk_prog_1 }, /* select div mode */
		{ gf100_clk_prog_2 }, /* (maybe) program pll */
		{ gf100_clk_prog_3 }, /* (maybe) select pll mode */
		{ gf100_clk_prog_4 }, /* final divider */
410 411 412 413
	};
	int i, j;

	for (i = 0; i < ARRAY_SIZE(stage); i++) {
B
Ben Skeggs 已提交
414 415
		for (j = 0; j < ARRAY_SIZE(clk->eng); j++) {
			if (!clk->eng[j].freq)
416
				continue;
B
Ben Skeggs 已提交
417
			stage[i].exec(clk, j);
418 419 420 421 422 423 424
		}
	}

	return 0;
}

static void
B
Ben Skeggs 已提交
425
gf100_clk_tidy(struct nvkm_clk *obj)
426
{
B
Ben Skeggs 已提交
427 428
	struct gf100_clk *clk = container_of(obj, typeof(*clk), base);
	memset(clk->eng, 0x00, sizeof(clk->eng));
429 430
}

431 432
static struct nvkm_domain
gf100_domain[] = {
433 434 435 436 437 438 439 440 441 442 443 444
	{ nv_clk_src_crystal, 0xff },
	{ nv_clk_src_href   , 0xff },
	{ nv_clk_src_hubk06 , 0x00 },
	{ nv_clk_src_hubk01 , 0x01 },
	{ nv_clk_src_copy   , 0x02 },
	{ nv_clk_src_gpc    , 0x03, 0, "core", 2000 },
	{ nv_clk_src_rop    , 0x04 },
	{ nv_clk_src_mem    , 0x05, 0, "memory", 1000 },
	{ nv_clk_src_vdec   , 0x06 },
	{ nv_clk_src_daemon , 0x0a },
	{ nv_clk_src_hubk07 , 0x0b },
	{ nv_clk_src_max }
445 446 447
};

static int
448 449 450
gf100_clk_ctor(struct nvkm_object *parent, struct nvkm_object *engine,
	       struct nvkm_oclass *oclass, void *data, u32 size,
	       struct nvkm_object **pobject)
451
{
B
Ben Skeggs 已提交
452
	struct gf100_clk *clk;
453 454
	int ret;

455
	ret = nvkm_clk_create(parent, engine, oclass, gf100_domain,
B
Ben Skeggs 已提交
456 457
			      NULL, 0, false, &clk);
	*pobject = nv_object(clk);
458 459 460
	if (ret)
		return ret;

B
Ben Skeggs 已提交
461 462 463 464
	clk->base.read = gf100_clk_read;
	clk->base.calc = gf100_clk_calc;
	clk->base.prog = gf100_clk_prog;
	clk->base.tidy = gf100_clk_tidy;
465 466 467
	return 0;
}

468 469
struct nvkm_oclass
gf100_clk_oclass = {
470
	.handle = NV_SUBDEV(CLK, 0xc0),
471 472 473 474 475
	.ofuncs = &(struct nvkm_ofuncs) {
		.ctor = gf100_clk_ctor,
		.dtor = _nvkm_clk_dtor,
		.init = _nvkm_clk_init,
		.fini = _nvkm_clk_fini,
476 477
	},
};