base.c 15.2 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 2013 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 25
#include "priv.h"

26 27 28 29
#include <subdev/bios.h>
#include <subdev/bios/boost.h>
#include <subdev/bios/cstep.h>
#include <subdev/bios/perf.h>
30 31 32 33 34
#include <subdev/fb.h>
#include <subdev/therm.h>
#include <subdev/volt.h>

#include <core/option.h>
35 36 37 38 39

/******************************************************************************
 * misc
 *****************************************************************************/
static u32
40 41
nvkm_clk_adjust(struct nvkm_clk *clk, bool adjust,
		u8 pstate, u8 domain, u32 input)
42
{
43
	struct nvkm_bios *bios = clk->subdev.device->bios;
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
	struct nvbios_boostE boostE;
	u8  ver, hdr, cnt, len;
	u16 data;

	data = nvbios_boostEm(bios, pstate, &ver, &hdr, &cnt, &len, &boostE);
	if (data) {
		struct nvbios_boostS boostS;
		u8  idx = 0, sver, shdr;
		u16 subd;

		input = max(boostE.min, input);
		input = min(boostE.max, input);
		do {
			sver = ver;
			shdr = hdr;
			subd = nvbios_boostSp(bios, idx++, data, &sver, &shdr,
					      cnt, len, &boostS);
			if (subd && boostS.domain == domain) {
				if (adjust)
					input = input * boostS.percent / 100;
				input = max(boostS.min, input);
				input = min(boostS.max, input);
				break;
			}
		} while (subd);
	}

	return input;
}

/******************************************************************************
 * C-States
 *****************************************************************************/
static int
78
nvkm_cstate_prog(struct nvkm_clk *clk, struct nvkm_pstate *pstate, int cstatei)
79
{
80 81 82 83
	struct nvkm_subdev *subdev = &clk->subdev;
	struct nvkm_device *device = subdev->device;
	struct nvkm_therm *therm = device->therm;
	struct nvkm_volt *volt = device->volt;
84
	struct nvkm_cstate *cstate;
85 86 87 88 89 90 91 92
	int ret;

	if (!list_empty(&pstate->list)) {
		cstate = list_entry(pstate->list.prev, typeof(*cstate), head);
	} else {
		cstate = &pstate->base;
	}

B
Ben Skeggs 已提交
93 94
	if (therm) {
		ret = nvkm_therm_cstate(therm, pstate->fanspeed, +1);
95
		if (ret && ret != -ENODEV) {
96
			nvkm_error(subdev, "failed to raise fan speed: %d\n", ret);
97 98
			return ret;
		}
99 100
	}

101
	if (volt) {
102
		ret = nvkm_volt_set_id(volt, cstate->voltage, +1);
103
		if (ret && ret != -ENODEV) {
104
			nvkm_error(subdev, "failed to raise voltage: %d\n", ret);
105 106
			return ret;
		}
107 108
	}

109
	ret = clk->func->calc(clk, cstate);
110
	if (ret == 0) {
111 112
		ret = clk->func->prog(clk);
		clk->func->tidy(clk);
113 114
	}

115
	if (volt) {
116
		ret = nvkm_volt_set_id(volt, cstate->voltage, -1);
117
		if (ret && ret != -ENODEV)
118
			nvkm_error(subdev, "failed to lower voltage: %d\n", ret);
119
	}
120

B
Ben Skeggs 已提交
121 122
	if (therm) {
		ret = nvkm_therm_cstate(therm, pstate->fanspeed, -1);
123
		if (ret && ret != -ENODEV)
124
			nvkm_error(subdev, "failed to lower fan speed: %d\n", ret);
125
	}
126

B
Ben Skeggs 已提交
127
	return ret;
128 129 130
}

static void
131
nvkm_cstate_del(struct nvkm_cstate *cstate)
132 133 134 135 136 137
{
	list_del(&cstate->head);
	kfree(cstate);
}

static int
138
nvkm_cstate_new(struct nvkm_clk *clk, int idx, struct nvkm_pstate *pstate)
139
{
140
	struct nvkm_bios *bios = clk->subdev.device->bios;
141
	const struct nvkm_domain *domain = clk->domains;
142
	struct nvkm_cstate *cstate = NULL;
143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159
	struct nvbios_cstepX cstepX;
	u8  ver, hdr;
	u16 data;

	data = nvbios_cstepXp(bios, idx, &ver, &hdr, &cstepX);
	if (!data)
		return -ENOENT;

	cstate = kzalloc(sizeof(*cstate), GFP_KERNEL);
	if (!cstate)
		return -ENOMEM;

	*cstate = pstate->base;
	cstate->voltage = cstepX.voltage;

	while (domain && domain->name != nv_clk_src_max) {
		if (domain->flags & NVKM_CLK_DOM_FLAG_CORE) {
160 161
			u32 freq = nvkm_clk_adjust(clk, true, pstate->pstate,
						   domain->bios, cstepX.freq);
162 163 164 165 166 167 168 169 170 171 172 173 174
			cstate->domain[domain->name] = freq;
		}
		domain++;
	}

	list_add(&cstate->head, &pstate->list);
	return 0;
}

/******************************************************************************
 * P-States
 *****************************************************************************/
static int
175
nvkm_pstate_prog(struct nvkm_clk *clk, int pstatei)
176
{
177
	struct nvkm_subdev *subdev = &clk->subdev;
178
	struct nvkm_ram *ram = subdev->device->fb->ram;
179
	struct nvkm_pstate *pstate;
180 181 182 183 184 185 186
	int ret, idx = 0;

	list_for_each_entry(pstate, &clk->states, head) {
		if (idx++ == pstatei)
			break;
	}

187
	nvkm_debug(subdev, "setting performance state %d\n", pstatei);
188 189
	clk->pstate = pstatei;

190
	if (ram && ram->func->calc) {
191 192
		int khz = pstate->base.domain[nv_clk_src_mem];
		do {
193
			ret = ram->func->calc(ram, khz);
194
			if (ret == 0)
195
				ret = ram->func->prog(ram);
196
		} while (ret > 0);
197
		ram->func->tidy(ram);
198 199
	}

200
	return nvkm_cstate_prog(clk, pstate, 0);
201 202
}

203
static void
204
nvkm_pstate_work(struct work_struct *work)
205
{
206
	struct nvkm_clk *clk = container_of(work, typeof(*clk), work);
207
	struct nvkm_subdev *subdev = &clk->subdev;
208 209 210 211
	int pstate;

	if (!atomic_xchg(&clk->waiting, 0))
		return;
212
	clk->pwrsrc = power_supply_is_system_supplied();
213

214 215 216
	nvkm_trace(subdev, "P %d PWR %d U(AC) %d U(DC) %d A %d T %d D %d\n",
		   clk->pstate, clk->pwrsrc, clk->ustate_ac, clk->ustate_dc,
		   clk->astate, clk->tstate, clk->dstate);
217

218 219 220
	pstate = clk->pwrsrc ? clk->ustate_ac : clk->ustate_dc;
	if (clk->state_nr && pstate != -1) {
		pstate = (pstate < 0) ? clk->astate : pstate;
221
		pstate = min(pstate, clk->state_nr - 1 + clk->tstate);
222 223 224 225 226
		pstate = max(pstate, clk->dstate);
	} else {
		pstate = clk->pstate = -1;
	}

227
	nvkm_trace(subdev, "-> %d\n", pstate);
228
	if (pstate != clk->pstate) {
229
		int ret = nvkm_pstate_prog(clk, pstate);
230
		if (ret) {
231 232
			nvkm_error(subdev, "error setting pstate %d: %d\n",
				   pstate, ret);
233 234 235 236
		}
	}

	wake_up_all(&clk->wait);
237
	nvkm_notify_get(&clk->pwrsrc_ntfy);
238 239 240
}

static int
241
nvkm_pstate_calc(struct nvkm_clk *clk, bool wait)
242 243 244 245 246 247
{
	atomic_set(&clk->waiting, 1);
	schedule_work(&clk->work);
	if (wait)
		wait_event(clk->wait, !atomic_read(&clk->waiting));
	return 0;
248 249 250
}

static void
251
nvkm_pstate_info(struct nvkm_clk *clk, struct nvkm_pstate *pstate)
252
{
253
	const struct nvkm_domain *clock = clk->domains - 1;
254
	struct nvkm_cstate *cstate;
255
	struct nvkm_subdev *subdev = &clk->subdev;
256 257 258 259 260 261 262 263 264 265 266 267 268
	char info[3][32] = { "", "", "" };
	char name[4] = "--";
	int i = -1;

	if (pstate->pstate != 0xff)
		snprintf(name, sizeof(name), "%02x", pstate->pstate);

	while ((++clock)->name != nv_clk_src_max) {
		u32 lo = pstate->base.domain[clock->name];
		u32 hi = lo;
		if (hi == 0)
			continue;

269
		nvkm_debug(subdev, "%02x: %10d KHz\n", clock->name, lo);
270 271 272 273
		list_for_each_entry(cstate, &pstate->list, head) {
			u32 freq = cstate->domain[clock->name];
			lo = min(lo, freq);
			hi = max(hi, freq);
274
			nvkm_debug(subdev, "%10d KHz\n", freq);
275 276 277 278 279 280 281 282 283 284 285 286 287 288 289
		}

		if (clock->mname && ++i < ARRAY_SIZE(info)) {
			lo /= clock->mdiv;
			hi /= clock->mdiv;
			if (lo == hi) {
				snprintf(info[i], sizeof(info[i]), "%s %d MHz",
					 clock->mname, lo);
			} else {
				snprintf(info[i], sizeof(info[i]),
					 "%s %d-%d MHz", clock->mname, lo, hi);
			}
		}
	}

290
	nvkm_debug(subdev, "%s: %s %s %s\n", name, info[0], info[1], info[2]);
291 292 293
}

static void
294
nvkm_pstate_del(struct nvkm_pstate *pstate)
295
{
296
	struct nvkm_cstate *cstate, *temp;
297 298

	list_for_each_entry_safe(cstate, temp, &pstate->list, head) {
299
		nvkm_cstate_del(cstate);
300 301 302 303 304 305 306
	}

	list_del(&pstate->head);
	kfree(pstate);
}

static int
307
nvkm_pstate_new(struct nvkm_clk *clk, int idx)
308
{
309
	struct nvkm_bios *bios = clk->subdev.device->bios;
310
	const struct nvkm_domain *domain = clk->domains - 1;
311 312
	struct nvkm_pstate *pstate;
	struct nvkm_cstate *cstate;
313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348
	struct nvbios_cstepE cstepE;
	struct nvbios_perfE perfE;
	u8  ver, hdr, cnt, len;
	u16 data;

	data = nvbios_perfEp(bios, idx, &ver, &hdr, &cnt, &len, &perfE);
	if (!data)
		return -EINVAL;
	if (perfE.pstate == 0xff)
		return 0;

	pstate = kzalloc(sizeof(*pstate), GFP_KERNEL);
	cstate = &pstate->base;
	if (!pstate)
		return -ENOMEM;

	INIT_LIST_HEAD(&pstate->list);

	pstate->pstate = perfE.pstate;
	pstate->fanspeed = perfE.fanspeed;
	cstate->voltage = perfE.voltage;
	cstate->domain[nv_clk_src_core] = perfE.core;
	cstate->domain[nv_clk_src_shader] = perfE.shader;
	cstate->domain[nv_clk_src_mem] = perfE.memory;
	cstate->domain[nv_clk_src_vdec] = perfE.vdec;
	cstate->domain[nv_clk_src_dom6] = perfE.disp;

	while (ver >= 0x40 && (++domain)->name != nv_clk_src_max) {
		struct nvbios_perfS perfS;
		u8  sver = ver, shdr = hdr;
		u32 perfSe = nvbios_perfSp(bios, data, domain->bios,
					  &sver, &shdr, cnt, len, &perfS);
		if (perfSe == 0 || sver != 0x40)
			continue;

		if (domain->flags & NVKM_CLK_DOM_FLAG_CORE) {
349 350 351 352
			perfS.v40.freq = nvkm_clk_adjust(clk, false,
							 pstate->pstate,
							 domain->bios,
							 perfS.v40.freq);
353 354 355 356 357 358 359 360 361
		}

		cstate->domain[domain->name] = perfS.v40.freq;
	}

	data = nvbios_cstepEm(bios, pstate->pstate, &ver, &hdr, &cstepE);
	if (data) {
		int idx = cstepE.index;
		do {
362
			nvkm_cstate_new(clk, idx, pstate);
363 364 365
		} while(idx--);
	}

366
	nvkm_pstate_info(clk, pstate);
367 368 369 370 371 372 373 374 375
	list_add_tail(&pstate->head, &clk->states);
	clk->state_nr++;
	return 0;
}

/******************************************************************************
 * Adjustment triggers
 *****************************************************************************/
static int
376
nvkm_clk_ustate_update(struct nvkm_clk *clk, int req)
377
{
378
	struct nvkm_pstate *pstate;
379 380
	int i = 0;

381 382
	if (!clk->allow_reclock)
		return -ENOSYS;
383 384 385 386 387 388 389 390 391 392 393 394 395

	if (req != -1 && req != -2) {
		list_for_each_entry(pstate, &clk->states, head) {
			if (pstate->pstate == req)
				break;
			i++;
		}

		if (pstate->pstate != req)
			return -EINVAL;
		req = i;
	}

396 397 398 399
	return req + 2;
}

static int
400
nvkm_clk_nstate(struct nvkm_clk *clk, const char *mode, int arglen)
401 402 403
{
	int ret = 1;

404 405 406
	if (clk->allow_reclock && !strncasecmpz(mode, "auto", arglen))
		return -2;

407 408 409 410 411 412
	if (strncasecmpz(mode, "disabled", arglen)) {
		char save = mode[arglen];
		long v;

		((char *)mode)[arglen] = '\0';
		if (!kstrtol(mode, 0, &v)) {
413
			ret = nvkm_clk_ustate_update(clk, v);
414 415 416 417 418 419 420
			if (ret < 0)
				ret = 1;
		}
		((char *)mode)[arglen] = save;
	}

	return ret - 2;
421 422 423
}

int
424
nvkm_clk_ustate(struct nvkm_clk *clk, int req, int pwr)
425
{
426
	int ret = nvkm_clk_ustate_update(clk, req);
427 428 429
	if (ret >= 0) {
		if (ret -= 2, pwr) clk->ustate_ac = ret;
		else		   clk->ustate_dc = ret;
430
		return nvkm_pstate_calc(clk, true);
431 432
	}
	return ret;
433 434 435
}

int
436
nvkm_clk_astate(struct nvkm_clk *clk, int req, int rel, bool wait)
437 438 439 440 441
{
	if (!rel) clk->astate  = req;
	if ( rel) clk->astate += rel;
	clk->astate = min(clk->astate, clk->state_nr - 1);
	clk->astate = max(clk->astate, 0);
442
	return nvkm_pstate_calc(clk, wait);
443 444 445
}

int
446
nvkm_clk_tstate(struct nvkm_clk *clk, int req, int rel)
447 448 449 450 451
{
	if (!rel) clk->tstate  = req;
	if ( rel) clk->tstate += rel;
	clk->tstate = min(clk->tstate, 0);
	clk->tstate = max(clk->tstate, -(clk->state_nr - 1));
452
	return nvkm_pstate_calc(clk, true);
453 454 455
}

int
456
nvkm_clk_dstate(struct nvkm_clk *clk, int req, int rel)
457 458 459 460 461
{
	if (!rel) clk->dstate  = req;
	if ( rel) clk->dstate += rel;
	clk->dstate = min(clk->dstate, clk->state_nr - 1);
	clk->dstate = max(clk->dstate, 0);
462
	return nvkm_pstate_calc(clk, true);
463 464
}

465
static int
466
nvkm_clk_pwrsrc(struct nvkm_notify *notify)
467
{
468
	struct nvkm_clk *clk =
469
		container_of(notify, typeof(*clk), pwrsrc_ntfy);
470
	nvkm_pstate_calc(clk, false);
471
	return NVKM_NOTIFY_DROP;
472 473
}

474 475 476
/******************************************************************************
 * subdev base class implementation
 *****************************************************************************/
477 478

int
479
nvkm_clk_read(struct nvkm_clk *clk, enum nv_clk_src src)
480
{
481 482 483 484 485 486 487
	return clk->func->read(clk, src);
}

static int
nvkm_clk_fini(struct nvkm_subdev *subdev, bool suspend)
{
	struct nvkm_clk *clk = nvkm_clk(subdev);
488
	nvkm_notify_put(&clk->pwrsrc_ntfy);
489 490 491 492
	flush_work(&clk->work);
	if (clk->func->fini)
		clk->func->fini(clk);
	return 0;
493 494
}

495 496
static int
nvkm_clk_init(struct nvkm_subdev *subdev)
497
{
498 499
	struct nvkm_clk *clk = nvkm_clk(subdev);
	const struct nvkm_domain *clock = clk->domains;
500 501 502 503 504 505 506
	int ret;

	memset(&clk->bstate, 0x00, sizeof(clk->bstate));
	INIT_LIST_HEAD(&clk->bstate.list);
	clk->bstate.pstate = 0xff;

	while (clock->name != nv_clk_src_max) {
507
		ret = nvkm_clk_read(clk, clock->name);
508
		if (ret < 0) {
509
			nvkm_error(subdev, "%02x freq unknown\n", clock->name);
510 511 512 513 514 515
			return ret;
		}
		clk->bstate.base.domain[clock->name] = ret;
		clock++;
	}

516
	nvkm_pstate_info(clk, &clk->bstate);
517

518 519 520
	if (clk->func->init)
		return clk->func->init(clk);

521 522 523 524
	clk->astate = clk->state_nr - 1;
	clk->tstate = 0;
	clk->dstate = 0;
	clk->pstate = -1;
525
	nvkm_pstate_calc(clk, true);
526 527 528
	return 0;
}

529 530
static void *
nvkm_clk_dtor(struct nvkm_subdev *subdev)
531
{
532
	struct nvkm_clk *clk = nvkm_clk(subdev);
533
	struct nvkm_pstate *pstate, *temp;
534

535
	nvkm_notify_fini(&clk->pwrsrc_ntfy);
536

537 538 539 540
	/* Early return if the pstates have been provided statically */
	if (clk->func->pstates)
		return clk;

541
	list_for_each_entry_safe(pstate, temp, &clk->states, head) {
542
		nvkm_pstate_del(pstate);
543 544
	}

545
	return clk;
546 547
}

548 549 550 551 552 553 554
static const struct nvkm_subdev_func
nvkm_clk = {
	.dtor = nvkm_clk_dtor,
	.init = nvkm_clk_init,
	.fini = nvkm_clk_fini,
};

555
int
556 557
nvkm_clk_ctor(const struct nvkm_clk_func *func, struct nvkm_device *device,
	      int index, bool allow_reclock, struct nvkm_clk *clk)
558 559 560 561
{
	int ret, idx, arglen;
	const char *mode;

562 563
	nvkm_subdev_ctor(&nvkm_clk, device, index, 0, &clk->subdev);
	clk->func = func;
564
	INIT_LIST_HEAD(&clk->states);
565
	clk->domains = func->domains;
566 567
	clk->ustate_ac = -1;
	clk->ustate_dc = -1;
568
	clk->allow_reclock = allow_reclock;
569

570
	INIT_WORK(&clk->work, nvkm_pstate_work);
571 572 573
	init_waitqueue_head(&clk->wait);
	atomic_set(&clk->waiting, 0);

574
	/* If no pstates are provided, try and fetch them from the BIOS */
575
	if (!func->pstates) {
576 577
		idx = 0;
		do {
578
			ret = nvkm_pstate_new(clk, idx++);
579 580
		} while (ret == 0);
	} else {
581 582 583
		for (idx = 0; idx < func->nr_pstates; idx++)
			list_add_tail(&func->pstates[idx].head, &clk->states);
		clk->state_nr = func->nr_pstates;
584
	}
585

586
	ret = nvkm_notify_init(NULL, &device->event, nvkm_clk_pwrsrc, true,
587
			       NULL, 0, 0, &clk->pwrsrc_ntfy);
588 589 590
	if (ret)
		return ret;

591
	mode = nvkm_stropt(device->cfgopt, "NvClkMode", &arglen);
592
	if (mode) {
593 594
		clk->ustate_ac = nvkm_clk_nstate(clk, mode, arglen);
		clk->ustate_dc = nvkm_clk_nstate(clk, mode, arglen);
595 596
	}

597
	mode = nvkm_stropt(device->cfgopt, "NvClkModeAC", &arglen);
598
	if (mode)
599
		clk->ustate_ac = nvkm_clk_nstate(clk, mode, arglen);
600

601
	mode = nvkm_stropt(device->cfgopt, "NvClkModeDC", &arglen);
602
	if (mode)
603
		clk->ustate_dc = nvkm_clk_nstate(clk, mode, arglen);
604

605 606
	return 0;
}
607 608 609 610 611 612 613 614 615

int
nvkm_clk_new_(const struct nvkm_clk_func *func, struct nvkm_device *device,
	      int index, bool allow_reclock, struct nvkm_clk **pclk)
{
	if (!(*pclk = kzalloc(sizeof(**pclk), GFP_KERNEL)))
		return -ENOMEM;
	return nvkm_clk_ctor(func, device, index, allow_reclock, *pclk);
}