[v2,13/22] clk: respect voltage limits in nvkm_cstate_prog with cstate = -1

Submitted by Karol Herbst on March 21, 2016, 4:16 p.m.

Details

Message ID 1458577000-6615-14-git-send-email-nouveau@karolherbst.de
State New
Headers show
Series "Volting/Clocking improvements for Fermi and newer" ( rev: 2 ) in Nouveau

Not browsing as part of any series.

Commit Message

Karol Herbst March 21, 2016, 4:16 p.m.
we should never allow to select a cstate which current voltage (depending on
the temperature) is higher than

1. the max volt entries in the voltage map table
2. what tha gpu actually can volt to

this resolves all remaining volting errors on fermi and newer

Signed-off-by: Karol Herbst <nouveau@karolherbst.de>
---
 drm/nouveau/nvkm/subdev/clk/base.c | 54 ++++++++++++++++++++++++++++++++++++--
 1 file changed, 52 insertions(+), 2 deletions(-)

Patch hide | download patch | download mbox

diff --git a/drm/nouveau/nvkm/subdev/clk/base.c b/drm/nouveau/nvkm/subdev/clk/base.c
index 29478c7..d567f0f 100644
--- a/drm/nouveau/nvkm/subdev/clk/base.c
+++ b/drm/nouveau/nvkm/subdev/clk/base.c
@@ -74,6 +74,57 @@  nvkm_clk_adjust(struct nvkm_clk *clk, bool adjust,
 /******************************************************************************
  * C-States
  *****************************************************************************/
+static bool
+nvkm_cstate_valid(struct nvkm_clk *clk, struct nvkm_cstate *cstate, u32 max_volt, int temp)
+{
+	struct nvkm_volt *volt = clk->subdev.device->volt;
+	int voltage;
+
+	if (!volt)
+		return true;
+
+	voltage = nvkm_volt_map(volt, cstate->voltage, temp);
+	if (voltage < 0)
+		return false;
+	return voltage <= min(max_volt, volt->max_uv) &&
+	       voltage >= volt->min_uv;
+}
+
+static struct nvkm_cstate *
+nvkm_cstate_find_best(struct nvkm_clk *clk, struct nvkm_pstate *pstate)
+{
+	struct nvkm_device *device = clk->subdev.device;
+	struct nvkm_therm *therm = device->therm;
+	struct nvkm_volt *volt = device->volt;
+	struct nvkm_cstate *cstate;
+	int temp = 0, max_volt;
+
+	if (!volt)
+		return list_entry(pstate->list.prev, typeof(*cstate), head);
+
+	if (therm) {
+		/* ignore error code */
+		temp = max(0, nvkm_therm_temp_get(therm));
+	}
+
+	max_volt = volt->max_uv;
+	if (volt->max0_vid != 0xff)
+		max_volt = min(max_volt,
+			       nvkm_volt_map(volt, volt->max0_vid, temp));
+	if (volt->max1_vid != 0xff)
+		max_volt = min(max_volt,
+			       nvkm_volt_map(volt, volt->max1_vid, temp));
+
+	for (cstate = list_entry(pstate->list.prev, typeof(*cstate), head);
+	     &cstate->head != &pstate->list;
+	     cstate = list_entry(cstate->head.prev, typeof(*cstate), head)) {
+		if (nvkm_cstate_valid(clk, cstate, max_volt, temp))
+			break;
+	}
+
+	return cstate;
+}
+
 static int
 nvkm_cstate_prog(struct nvkm_clk *clk, struct nvkm_pstate *pstate, int cstatei)
 {
@@ -86,8 +137,7 @@  nvkm_cstate_prog(struct nvkm_clk *clk, struct nvkm_pstate *pstate, int cstatei)
 
 	if (!list_empty(&pstate->list)) {
 		if (cstatei == -1)
-			cstate = list_entry(pstate->list.prev, typeof(*cstate),
-					    head);
+			cstate = nvkm_cstate_find_best(clk, pstate);
 		else {
 			list_for_each_entry(cstate, &pstate->list, head) {
 				if (cstate->cstate == cstatei)