Adds Nouveau controlled thermal throttling for Kepler+ GPUs. With this I feel safe enough to add support for Maxwell2 reclocking later on (still hidden behind a switch, but we can be fairly sure to not overheat hardware if a user isn't carefull enough) Contains all patches from my clk update series, but I thought it makes sense to include those in this series as well for completness. Please comment Karol Herbst (13): clk: Rename nvkm_pstate_calc to nvkm_clk_update and export it clk: Remove dstate clk: Make pstate a pointer to nvkm_pstate clk: Hold information about the current cstate status clk: We should pass the pstate id around not the index in the list clk: Set clocks to pre suspend state after suspend clk: Check pm_runtime status before reclocking therm: Don't cancel the timer therm: Move the temp readout into the alarm therm: Trigger reclock in temperature daemon bios: add thermal policies table clk: parse thermal policies for throttling thresholds clk: thermal throttling .../include/nvkm/subdev/bios/thermal_policies.h | 27 +++ drm/nouveau/include/nvkm/subdev/clk.h | 12 +- drm/nouveau/include/nvkm/subdev/therm.h | 1 + drm/nouveau/nouveau_debugfs.c | 6 +- drm/nouveau/nvkm/engine/device/ctrl.c | 5 +- drm/nouveau/nvkm/subdev/bios/Kbuild | 1 + drm/nouveau/nvkm/subdev/bios/thermal_policies.c | 81 +++++++ drm/nouveau/nvkm/subdev/clk/base.c | 248 +++++++++++++++------ drm/nouveau/nvkm/subdev/pmu/gk20a.c | 18 +- drm/nouveau/nvkm/subdev/therm/base.c | 46 ++-- 10 files changed, 331 insertions(+), 114 deletions(-) create mode 100644 drm/nouveau/include/nvkm/subdev/bios/thermal_policies.h create mode 100644 drm/nouveau/nvkm/subdev/bios/thermal_policies.c -- 2.13.2
Karol Herbst
2017-Jul-21 21:55 UTC
[Nouveau] [RFC PATCH 01/13] clk: Rename nvkm_pstate_calc to nvkm_clk_update and export it
This function will be used to update the current clock state.
This will happen for various reasons:
* Temperature changes
* User changes clocking state
* Load changes
v2: remove parameter name
Signed-off-by: Karol Herbst <karolherbst at gmail.com>
---
drm/nouveau/include/nvkm/subdev/clk.h | 1 +
drm/nouveau/nvkm/subdev/clk/base.c | 26 ++++++++++++++++----------
2 files changed, 17 insertions(+), 10 deletions(-)
diff --git a/drm/nouveau/include/nvkm/subdev/clk.h
b/drm/nouveau/include/nvkm/subdev/clk.h
index e5275f74..ce3bbcfe 100644
--- a/drm/nouveau/include/nvkm/subdev/clk.h
+++ b/drm/nouveau/include/nvkm/subdev/clk.h
@@ -123,6 +123,7 @@ int nvkm_clk_ustate(struct nvkm_clk *, int req, int pwr);
int nvkm_clk_astate(struct nvkm_clk *, int req, int rel, bool wait);
int nvkm_clk_dstate(struct nvkm_clk *, int req, int rel);
int nvkm_clk_tstate(struct nvkm_clk *, u8 temperature);
+int nvkm_clk_update(struct nvkm_clk *, bool wait);
int nv04_clk_new(struct nvkm_device *, int, struct nvkm_clk **);
int nv40_clk_new(struct nvkm_device *, int, struct nvkm_clk **);
diff --git a/drm/nouveau/nvkm/subdev/clk/base.c
b/drm/nouveau/nvkm/subdev/clk/base.c
index e4c8d310..ecff3ff3 100644
--- a/drm/nouveau/nvkm/subdev/clk/base.c
+++ b/drm/nouveau/nvkm/subdev/clk/base.c
@@ -296,7 +296,7 @@ nvkm_pstate_prog(struct nvkm_clk *clk, int pstatei)
}
static void
-nvkm_pstate_work(struct work_struct *work)
+nvkm_clk_update_work(struct work_struct *work)
{
struct nvkm_clk *clk = container_of(work, typeof(*clk), work);
struct nvkm_subdev *subdev = &clk->subdev;
@@ -332,9 +332,15 @@ nvkm_pstate_work(struct work_struct *work)
nvkm_notify_get(&clk->pwrsrc_ntfy);
}
-static int
-nvkm_pstate_calc(struct nvkm_clk *clk, bool wait)
+int
+nvkm_clk_update(struct nvkm_clk *clk, bool wait)
{
+ if (!clk)
+ return -EINVAL;
+
+ if (!clk->allow_reclock)
+ return -ENODEV;
+
atomic_set(&clk->waiting, 1);
schedule_work(&clk->work);
if (wait)
@@ -524,7 +530,7 @@ nvkm_clk_ustate(struct nvkm_clk *clk, int req, int pwr)
if (ret >= 0) {
if (ret -= 2, pwr) clk->ustate_ac = ret;
else clk->ustate_dc = ret;
- return nvkm_pstate_calc(clk, true);
+ return nvkm_clk_update(clk, true);
}
return ret;
}
@@ -536,7 +542,7 @@ nvkm_clk_astate(struct nvkm_clk *clk, int req, int rel, bool
wait)
if ( rel) clk->astate += rel;
clk->astate = min(clk->astate, clk->state_nr - 1);
clk->astate = max(clk->astate, 0);
- return nvkm_pstate_calc(clk, wait);
+ return nvkm_clk_update(clk, wait);
}
int
@@ -545,7 +551,7 @@ nvkm_clk_tstate(struct nvkm_clk *clk, u8 temp)
if (clk->temp == temp)
return 0;
clk->temp = temp;
- return nvkm_pstate_calc(clk, false);
+ return nvkm_clk_update(clk, false);
}
int
@@ -555,7 +561,7 @@ nvkm_clk_dstate(struct nvkm_clk *clk, int req, int rel)
if ( rel) clk->dstate += rel;
clk->dstate = min(clk->dstate, clk->state_nr - 1);
clk->dstate = max(clk->dstate, 0);
- return nvkm_pstate_calc(clk, true);
+ return nvkm_clk_update(clk, true);
}
static int
@@ -563,7 +569,7 @@ nvkm_clk_pwrsrc(struct nvkm_notify *notify)
{
struct nvkm_clk *clk container_of(notify, typeof(*clk), pwrsrc_ntfy);
- nvkm_pstate_calc(clk, false);
+ nvkm_clk_update(clk, false);
return NVKM_NOTIFY_DROP;
}
@@ -618,7 +624,7 @@ nvkm_clk_init(struct nvkm_subdev *subdev)
clk->dstate = 0;
clk->pstate = -1;
clk->temp = 90; /* reasonable default value */
- nvkm_pstate_calc(clk, true);
+ nvkm_clk_update(clk, true);
return 0;
}
@@ -675,7 +681,7 @@ nvkm_clk_ctor(const struct nvkm_clk_func *func, struct
nvkm_device *device,
clk->ustate_dc = -1;
clk->allow_reclock = allow_reclock;
- INIT_WORK(&clk->work, nvkm_pstate_work);
+ INIT_WORK(&clk->work, nvkm_clk_update_work);
init_waitqueue_head(&clk->wait);
atomic_set(&clk->waiting, 0);
--
2.13.2
We won't need it now, because we will adjust the clocks depending on engine
loads later on anyway or a static lookup table. It also simplifies the
clocking logic.
This code was nowhere used anyway and just a mock up.
v2: fixed typo in commit message
Signed-off-by: Karol Herbst <karolherbst at gmail.com>
Reviewed-by: Martin Peres <martin.peres at free.fr>
---
drm/nouveau/include/nvkm/subdev/clk.h | 2 --
drm/nouveau/nvkm/subdev/clk/base.c | 16 ++--------------
2 files changed, 2 insertions(+), 16 deletions(-)
diff --git a/drm/nouveau/include/nvkm/subdev/clk.h
b/drm/nouveau/include/nvkm/subdev/clk.h
index ce3bbcfe..1340f5b8 100644
--- a/drm/nouveau/include/nvkm/subdev/clk.h
+++ b/drm/nouveau/include/nvkm/subdev/clk.h
@@ -99,7 +99,6 @@ struct nvkm_clk {
int ustate_ac; /* user-requested (-1 disabled, -2 perfmon) */
int ustate_dc; /* user-requested (-1 disabled, -2 perfmon) */
int astate; /* perfmon adjustment (base) */
- int dstate; /* display adjustment (min+) */
u8 temp;
bool allow_reclock;
@@ -121,7 +120,6 @@ struct nvkm_clk {
int nvkm_clk_read(struct nvkm_clk *, enum nv_clk_src);
int nvkm_clk_ustate(struct nvkm_clk *, int req, int pwr);
int nvkm_clk_astate(struct nvkm_clk *, int req, int rel, bool wait);
-int nvkm_clk_dstate(struct nvkm_clk *, int req, int rel);
int nvkm_clk_tstate(struct nvkm_clk *, u8 temperature);
int nvkm_clk_update(struct nvkm_clk *, bool wait);
diff --git a/drm/nouveau/nvkm/subdev/clk/base.c
b/drm/nouveau/nvkm/subdev/clk/base.c
index ecff3ff3..07d530ed 100644
--- a/drm/nouveau/nvkm/subdev/clk/base.c
+++ b/drm/nouveau/nvkm/subdev/clk/base.c
@@ -306,15 +306,14 @@ nvkm_clk_update_work(struct work_struct *work)
return;
clk->pwrsrc = power_supply_is_system_supplied();
- nvkm_trace(subdev, "P %d PWR %d U(AC) %d U(DC) %d A %d T %d°C D
%d\n",
+ nvkm_trace(subdev, "P %d PWR %d U(AC) %d U(DC) %d A %d T %d°C\n",
clk->pstate, clk->pwrsrc, clk->ustate_ac, clk->ustate_dc,
- clk->astate, clk->temp, clk->dstate);
+ clk->astate, clk->temp);
pstate = clk->pwrsrc ? clk->ustate_ac : clk->ustate_dc;
if (clk->state_nr && pstate != -1) {
pstate = (pstate < 0) ? clk->astate : pstate;
pstate = min(pstate, clk->state_nr - 1);
- pstate = max(pstate, clk->dstate);
} else {
pstate = clk->pstate = -1;
}
@@ -554,16 +553,6 @@ nvkm_clk_tstate(struct nvkm_clk *clk, u8 temp)
return nvkm_clk_update(clk, false);
}
-int
-nvkm_clk_dstate(struct nvkm_clk *clk, int req, int rel)
-{
- 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);
- return nvkm_clk_update(clk, true);
-}
-
static int
nvkm_clk_pwrsrc(struct nvkm_notify *notify)
{
@@ -621,7 +610,6 @@ nvkm_clk_init(struct nvkm_subdev *subdev)
return clk->func->init(clk);
clk->astate = clk->state_nr - 1;
- clk->dstate = 0;
clk->pstate = -1;
clk->temp = 90; /* reasonable default value */
nvkm_clk_update(clk, true);
--
2.13.2
Karol Herbst
2017-Jul-21 21:55 UTC
[Nouveau] [RFC PATCH 03/13] clk: Make pstate a pointer to nvkm_pstate
We will access the current cstate at least every second and this saves us
some CPU cycles looking them up every second.
v2: Rewording commit message.
Signed-off-by: Karol Herbst <karolherbst at gmail.com>
Reviewed-by: Martin Peres <martin.peres at free.fr>
---
drm/nouveau/include/nvkm/subdev/clk.h | 4 +++-
drm/nouveau/nvkm/engine/device/ctrl.c | 5 ++++-
drm/nouveau/nvkm/subdev/clk/base.c | 17 ++++++++++++-----
drm/nouveau/nvkm/subdev/pmu/gk20a.c | 18 +++++++-----------
4 files changed, 26 insertions(+), 18 deletions(-)
diff --git a/drm/nouveau/include/nvkm/subdev/clk.h
b/drm/nouveau/include/nvkm/subdev/clk.h
index 1340f5b8..ec537e08 100644
--- a/drm/nouveau/include/nvkm/subdev/clk.h
+++ b/drm/nouveau/include/nvkm/subdev/clk.h
@@ -10,6 +10,8 @@ struct nvkm_pll_vals;
#define NVKM_CLK_CSTATE_BASE -2 /* pstate base */
#define NVKM_CLK_CSTATE_HIGHEST -3 /* highest possible */
+#define NVKM_CLK_PSTATE_DEFAULT -1
+
enum nv_clk_src {
nv_clk_src_crystal,
nv_clk_src_href,
@@ -95,7 +97,7 @@ struct nvkm_clk {
struct nvkm_notify pwrsrc_ntfy;
int pwrsrc;
- int pstate; /* current */
+ struct nvkm_pstate *pstate; /* current */
int ustate_ac; /* user-requested (-1 disabled, -2 perfmon) */
int ustate_dc; /* user-requested (-1 disabled, -2 perfmon) */
int astate; /* perfmon adjustment (base) */
diff --git a/drm/nouveau/nvkm/engine/device/ctrl.c
b/drm/nouveau/nvkm/engine/device/ctrl.c
index b0ece71a..da70626c 100644
--- a/drm/nouveau/nvkm/engine/device/ctrl.c
+++ b/drm/nouveau/nvkm/engine/device/ctrl.c
@@ -52,7 +52,10 @@ nvkm_control_mthd_pstate_info(struct nvkm_control *ctrl, void
*data, u32 size)
args->v0.ustate_ac = clk->ustate_ac;
args->v0.ustate_dc = clk->ustate_dc;
args->v0.pwrsrc = clk->pwrsrc;
- args->v0.pstate = clk->pstate;
+ if (clk->pstate)
+ args->v0.pstate = clk->pstate->pstate;
+ else
+ args->v0.pstate = NVKM_CLK_PSTATE_DEFAULT;
} else {
args->v0.count = 0;
args->v0.ustate_ac = NVIF_CONTROL_PSTATE_INFO_V0_USTATE_DISABLE;
diff --git a/drm/nouveau/nvkm/subdev/clk/base.c
b/drm/nouveau/nvkm/subdev/clk/base.c
index 07d530ed..0d4d9fdf 100644
--- a/drm/nouveau/nvkm/subdev/clk/base.c
+++ b/drm/nouveau/nvkm/subdev/clk/base.c
@@ -271,13 +271,16 @@ nvkm_pstate_prog(struct nvkm_clk *clk, int pstatei)
struct nvkm_pstate *pstate;
int ret, idx = 0;
+ if (pstatei == NVKM_CLK_PSTATE_DEFAULT)
+ return 0;
+
list_for_each_entry(pstate, &clk->states, head) {
if (idx++ == pstatei)
break;
}
nvkm_debug(subdev, "setting performance state %d\n", pstatei);
- clk->pstate = pstatei;
+ clk->pstate = pstate;
nvkm_pcie_set_link(pci, pstate->pcie_speed, pstate->pcie_width);
@@ -306,8 +309,12 @@ nvkm_clk_update_work(struct work_struct *work)
return;
clk->pwrsrc = power_supply_is_system_supplied();
+ if (clk->pstate)
+ pstate = clk->pstate->pstate;
+ else
+ pstate = NVKM_CLK_PSTATE_DEFAULT;
nvkm_trace(subdev, "P %d PWR %d U(AC) %d U(DC) %d A %d T %d°C\n",
- clk->pstate, clk->pwrsrc, clk->ustate_ac, clk->ustate_dc,
+ pstate, clk->pwrsrc, clk->ustate_ac, clk->ustate_dc,
clk->astate, clk->temp);
pstate = clk->pwrsrc ? clk->ustate_ac : clk->ustate_dc;
@@ -315,11 +322,11 @@ nvkm_clk_update_work(struct work_struct *work)
pstate = (pstate < 0) ? clk->astate : pstate;
pstate = min(pstate, clk->state_nr - 1);
} else {
- pstate = clk->pstate = -1;
+ pstate = NVKM_CLK_PSTATE_DEFAULT;
}
nvkm_trace(subdev, "-> %d\n", pstate);
- if (pstate != clk->pstate) {
+ if (!clk->pstate || pstate != clk->pstate->pstate) {
int ret = nvkm_pstate_prog(clk, pstate);
if (ret) {
nvkm_error(subdev, "error setting pstate %d: %d\n",
@@ -610,7 +617,7 @@ nvkm_clk_init(struct nvkm_subdev *subdev)
return clk->func->init(clk);
clk->astate = clk->state_nr - 1;
- clk->pstate = -1;
+ clk->pstate = NULL;
clk->temp = 90; /* reasonable default value */
nvkm_clk_update(clk, true);
return 0;
diff --git a/drm/nouveau/nvkm/subdev/pmu/gk20a.c
b/drm/nouveau/nvkm/subdev/pmu/gk20a.c
index 978aae3c..3dd550c3 100644
--- a/drm/nouveau/nvkm/subdev/pmu/gk20a.c
+++ b/drm/nouveau/nvkm/subdev/pmu/gk20a.c
@@ -55,24 +55,22 @@ gk20a_pmu_dvfs_target(struct gk20a_pmu *pmu, int *state)
return nvkm_clk_astate(clk, *state, 0, false);
}
-static void
-gk20a_pmu_dvfs_get_cur_state(struct gk20a_pmu *pmu, int *state)
-{
- struct nvkm_clk *clk = pmu->base.subdev.device->clk;
-
- *state = clk->pstate;
-}
-
static int
gk20a_pmu_dvfs_get_target_state(struct gk20a_pmu *pmu,
int *state, int load)
{
struct gk20a_pmu_dvfs_data *data = pmu->data;
struct nvkm_clk *clk = pmu->base.subdev.device->clk;
+ struct nvkm_pstate *pstate = clk->pstate;
int cur_level, level;
+ if (!pstate) {
+ *state = 0;
+ return 1;
+ }
+
/* For GK20A, the performance level is directly mapped to pstate */
- level = cur_level = clk->pstate;
+ level = cur_level = clk->pstate->pstate;
if (load > data->p_load_max) {
level = min(clk->state_nr - 1, level + (clk->state_nr / 3));
@@ -142,8 +140,6 @@ gk20a_pmu_dvfs_work(struct nvkm_alarm *alarm)
nvkm_trace(subdev, "utilization = %d %%, avg_load = %d %%\n",
utilization, data->avg_load);
- gk20a_pmu_dvfs_get_cur_state(pmu, &state);
-
if (gk20a_pmu_dvfs_get_target_state(pmu, &state, data->avg_load)) {
nvkm_trace(subdev, "set new state to %d\n", state);
gk20a_pmu_dvfs_target(pmu, &state);
--
2.13.2
Karol Herbst
2017-Jul-21 21:55 UTC
[Nouveau] [RFC PATCH 04/13] clk: Hold information about the current cstate status
Later we will have situations where the expected and the current state
isn't the same.
Signed-off-by: Karol Herbst <karolherbst at gmail.com>
Reviewed-by: Martin Peres <martin.peres at free.fr>
---
drm/nouveau/include/nvkm/subdev/clk.h | 2 ++
drm/nouveau/nvkm/subdev/clk/base.c | 32 +++++++++++++++++++++++++-------
2 files changed, 27 insertions(+), 7 deletions(-)
diff --git a/drm/nouveau/include/nvkm/subdev/clk.h
b/drm/nouveau/include/nvkm/subdev/clk.h
index ec537e08..f35518c3 100644
--- a/drm/nouveau/include/nvkm/subdev/clk.h
+++ b/drm/nouveau/include/nvkm/subdev/clk.h
@@ -101,6 +101,8 @@ struct nvkm_clk {
int ustate_ac; /* user-requested (-1 disabled, -2 perfmon) */
int ustate_dc; /* user-requested (-1 disabled, -2 perfmon) */
int astate; /* perfmon adjustment (base) */
+ struct nvkm_cstate *cstate;
+ int exp_cstateid;
u8 temp;
bool allow_reclock;
diff --git a/drm/nouveau/nvkm/subdev/clk/base.c
b/drm/nouveau/nvkm/subdev/clk/base.c
index 0d4d9fdf..d37c13b7 100644
--- a/drm/nouveau/nvkm/subdev/clk/base.c
+++ b/drm/nouveau/nvkm/subdev/clk/base.c
@@ -146,9 +146,14 @@ static struct nvkm_cstate *
nvkm_cstate_get(struct nvkm_clk *clk, struct nvkm_pstate *pstate, int cstatei)
{
struct nvkm_cstate *cstate;
- if (cstatei == NVKM_CLK_CSTATE_HIGHEST)
+ switch (cstatei) {
+ case NVKM_CLK_CSTATE_HIGHEST:
return list_last_entry(&pstate->list, typeof(*cstate), head);
- else {
+ case NVKM_CLK_CSTATE_BASE:
+ return &pstate->base;
+ case NVKM_CLK_CSTATE_DEFAULT:
+ return NULL;
+ default:
list_for_each_entry(cstate, &pstate->list, head) {
if (cstate->id == cstatei)
return cstate;
@@ -167,6 +172,9 @@ nvkm_cstate_prog(struct nvkm_clk *clk, struct nvkm_pstate
*pstate, int cstatei)
struct nvkm_cstate *cstate;
int ret;
+ if (cstatei == NVKM_CLK_CSTATE_DEFAULT)
+ return 0;
+
if (!list_empty(&pstate->list)) {
cstate = nvkm_cstate_get(clk, pstate, cstatei);
cstate = nvkm_cstate_find_best(clk, pstate, cstate);
@@ -193,6 +201,7 @@ nvkm_cstate_prog(struct nvkm_clk *clk, struct nvkm_pstate
*pstate, int cstatei)
ret = clk->func->calc(clk, cstate);
if (ret == 0) {
+ clk->cstate = cstate;
ret = clk->func->prog(clk);
clk->func->tidy(clk);
}
@@ -295,7 +304,7 @@ nvkm_pstate_prog(struct nvkm_clk *clk, int pstatei)
ram->func->tidy(ram);
}
- return nvkm_cstate_prog(clk, pstate, NVKM_CLK_CSTATE_HIGHEST);
+ return nvkm_cstate_prog(clk, pstate, clk->exp_cstateid);
}
static void
@@ -313,9 +322,9 @@ nvkm_clk_update_work(struct work_struct *work)
pstate = clk->pstate->pstate;
else
pstate = NVKM_CLK_PSTATE_DEFAULT;
- nvkm_trace(subdev, "P %d PWR %d U(AC) %d U(DC) %d A %d T %d°C\n",
+ nvkm_trace(subdev, "P %d PWR %d U(AC) %d U(DC) %d A %d C %d T
%d°C\n",
pstate, clk->pwrsrc, clk->ustate_ac, clk->ustate_dc,
- clk->astate, clk->temp);
+ clk->astate, clk->exp_cstateid, clk->temp);
pstate = clk->pwrsrc ? clk->ustate_ac : clk->ustate_dc;
if (clk->state_nr && pstate != -1) {
@@ -536,6 +545,7 @@ nvkm_clk_ustate(struct nvkm_clk *clk, int req, int pwr)
if (ret >= 0) {
if (ret -= 2, pwr) clk->ustate_ac = ret;
else clk->ustate_dc = ret;
+ clk->exp_cstateid = NVKM_CLK_CSTATE_HIGHEST;
return nvkm_clk_update(clk, true);
}
return ret;
@@ -548,6 +558,7 @@ nvkm_clk_astate(struct nvkm_clk *clk, int req, int rel, bool
wait)
if ( rel) clk->astate += rel;
clk->astate = min(clk->astate, clk->state_nr - 1);
clk->astate = max(clk->astate, 0);
+ clk->exp_cstateid = NVKM_CLK_CSTATE_BASE;
return nvkm_clk_update(clk, wait);
}
@@ -618,6 +629,8 @@ nvkm_clk_init(struct nvkm_subdev *subdev)
clk->astate = clk->state_nr - 1;
clk->pstate = NULL;
+ clk->exp_cstateid = NVKM_CLK_CSTATE_DEFAULT;
+ clk->cstate = NULL;
clk->temp = 90; /* reasonable default value */
nvkm_clk_update(clk, true);
return 0;
@@ -701,15 +714,20 @@ nvkm_clk_ctor(const struct nvkm_clk_func *func, struct
nvkm_device *device,
if (mode) {
clk->ustate_ac = nvkm_clk_nstate(clk, mode, arglen);
clk->ustate_dc = nvkm_clk_nstate(clk, mode, arglen);
+ clk->exp_cstateid = NVKM_CLK_CSTATE_HIGHEST;
}
mode = nvkm_stropt(device->cfgopt, "NvClkModeAC", &arglen);
- if (mode)
+ if (mode) {
clk->ustate_ac = nvkm_clk_nstate(clk, mode, arglen);
+ clk->exp_cstateid = NVKM_CLK_CSTATE_HIGHEST;
+ }
mode = nvkm_stropt(device->cfgopt, "NvClkModeDC", &arglen);
- if (mode)
+ if (mode) {
clk->ustate_dc = nvkm_clk_nstate(clk, mode, arglen);
+ clk->exp_cstateid = NVKM_CLK_CSTATE_HIGHEST;
+ }
clk->boost_mode = nvkm_longopt(device->cfgopt, "NvBoost",
NVKM_CLK_BOOST_NONE);
--
2.13.2
Karol Herbst
2017-Jul-21 21:55 UTC
[Nouveau] [RFC PATCH 05/13] clk: We should pass the pstate id around not the index in the list
This makes the code easier, because we can compare the id with
pstate->pstate and saves us from the trouble of iterating over the pstates
to match the index.
v2: reword commit message
Signed-off-by: Karol Herbst <karolherbst at gmail.com>
Reviewed-by: Martin Peres <martin.peres at free.fr>
---
drm/nouveau/nouveau_debugfs.c | 6 +--
drm/nouveau/nvkm/subdev/clk/base.c | 78 +++++++++++++++++++-------------------
2 files changed, 41 insertions(+), 43 deletions(-)
diff --git a/drm/nouveau/nouveau_debugfs.c b/drm/nouveau/nouveau_debugfs.c
index 963a4dba..27281c4e 100644
--- a/drm/nouveau/nouveau_debugfs.c
+++ b/drm/nouveau/nouveau_debugfs.c
@@ -96,11 +96,11 @@ nouveau_debugfs_pstate_get(struct seq_file *m, void *data)
} while (attr.index);
if (state >= 0) {
- if (info.ustate_ac == state)
+ if (info.ustate_ac == attr.state)
seq_printf(m, " AC");
- if (info.ustate_dc == state)
+ if (info.ustate_dc == attr.state)
seq_printf(m, " DC");
- if (info.pstate == state)
+ if (info.pstate == attr.state)
seq_printf(m, " *");
} else {
if (info.ustate_ac < -1)
diff --git a/drm/nouveau/nvkm/subdev/clk/base.c
b/drm/nouveau/nvkm/subdev/clk/base.c
index d37c13b7..1d71bf09 100644
--- a/drm/nouveau/nvkm/subdev/clk/base.c
+++ b/drm/nouveau/nvkm/subdev/clk/base.c
@@ -272,23 +272,26 @@ nvkm_cstate_new(struct nvkm_clk *clk, int idx, struct
nvkm_pstate *pstate)
* P-States
*****************************************************************************/
static int
-nvkm_pstate_prog(struct nvkm_clk *clk, int pstatei)
+nvkm_pstate_prog(struct nvkm_clk *clk, int pstateid)
{
struct nvkm_subdev *subdev = &clk->subdev;
struct nvkm_fb *fb = subdev->device->fb;
struct nvkm_pci *pci = subdev->device->pci;
struct nvkm_pstate *pstate;
- int ret, idx = 0;
+ int ret;
- if (pstatei == NVKM_CLK_PSTATE_DEFAULT)
+ if (pstateid == NVKM_CLK_PSTATE_DEFAULT)
return 0;
list_for_each_entry(pstate, &clk->states, head) {
- if (idx++ == pstatei)
+ if (pstate->pstate == pstateid)
break;
}
- nvkm_debug(subdev, "setting performance state %d\n", pstatei);
+ if (!pstate)
+ return -EINVAL;
+
+ nvkm_debug(subdev, "setting performance state %x\n", pstateid);
clk->pstate = pstate;
nvkm_pcie_set_link(pci, pstate->pcie_speed, pstate->pcie_width);
@@ -329,7 +332,6 @@ nvkm_clk_update_work(struct work_struct *work)
pstate = clk->pwrsrc ? clk->ustate_ac : clk->ustate_dc;
if (clk->state_nr && pstate != -1) {
pstate = (pstate < 0) ? clk->astate : pstate;
- pstate = min(pstate, clk->state_nr - 1);
} else {
pstate = NVKM_CLK_PSTATE_DEFAULT;
}
@@ -491,32 +493,9 @@ nvkm_pstate_new(struct nvkm_clk *clk, int idx)
* Adjustment triggers
*****************************************************************************/
static int
-nvkm_clk_ustate_update(struct nvkm_clk *clk, int req)
-{
- struct nvkm_pstate *pstate;
- int i = 0;
-
- if (!clk->allow_reclock)
- return -ENOSYS;
-
- 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;
- }
-
- return req + 2;
-}
-
-static int
nvkm_clk_nstate(struct nvkm_clk *clk, const char *mode, int arglen)
{
+ struct nvkm_pstate *pstate;
int ret = 1;
if (clk->allow_reclock && !strncasecmpz(mode, "auto",
arglen))
@@ -528,27 +507,46 @@ nvkm_clk_nstate(struct nvkm_clk *clk, const char *mode,
int arglen)
((char *)mode)[arglen] = '\0';
if (!kstrtol(mode, 0, &v)) {
- ret = nvkm_clk_ustate_update(clk, v);
+ ret = v;
if (ret < 0)
ret = 1;
}
((char *)mode)[arglen] = save;
}
- return ret - 2;
+ if (ret < 0)
+ return ret;
+
+ list_for_each_entry(pstate, &clk->states, head) {
+ if (pstate->pstate == ret)
+ return ret;
+ }
+ return -EINVAL;
}
int
nvkm_clk_ustate(struct nvkm_clk *clk, int req, int pwr)
{
- int ret = nvkm_clk_ustate_update(clk, req);
- if (ret >= 0) {
- if (ret -= 2, pwr) clk->ustate_ac = ret;
- else clk->ustate_dc = ret;
- clk->exp_cstateid = NVKM_CLK_CSTATE_HIGHEST;
- return nvkm_clk_update(clk, true);
+ struct nvkm_pstate *pstate;
+ bool valid = false;
+
+ list_for_each_entry(pstate, &clk->states, head) {
+ if (pstate->pstate == req) {
+ valid = true;
+ break;
+ }
}
- return ret;
+
+ if (!valid)
+ return -EINVAL;
+
+ if (pwr)
+ clk->ustate_ac = req;
+ else
+ clk->ustate_dc = req;
+
+ clk->exp_cstateid = NVKM_CLK_CSTATE_HIGHEST;
+ return nvkm_clk_update(clk, true);
}
int
@@ -627,7 +625,7 @@ nvkm_clk_init(struct nvkm_subdev *subdev)
if (clk->func->init)
return clk->func->init(clk);
- clk->astate = clk->state_nr - 1;
+ clk->astate = NVKM_CLK_PSTATE_DEFAULT;
clk->pstate = NULL;
clk->exp_cstateid = NVKM_CLK_CSTATE_DEFAULT;
clk->cstate = NULL;
--
2.13.2
Karol Herbst
2017-Jul-21 21:55 UTC
[Nouveau] [RFC PATCH 06/13] clk: Set clocks to pre suspend state after suspend
The idea is to clear out the saved state, because after a resume we can't know what the GPU is clocked to. The reclock is triggered by the call to nvkm_clk_update later in nvkm_clk_init. v2: convert to C style comments Signed-off-by: Karol Herbst <karolherbst at gmail.com> Reviewed-by: Martin Peres <martin.peres at free.fr> --- drm/nouveau/nvkm/subdev/clk/base.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/drm/nouveau/nvkm/subdev/clk/base.c b/drm/nouveau/nvkm/subdev/clk/base.c index 1d71bf09..54188d2b 100644 --- a/drm/nouveau/nvkm/subdev/clk/base.c +++ b/drm/nouveau/nvkm/subdev/clk/base.c @@ -625,11 +625,10 @@ nvkm_clk_init(struct nvkm_subdev *subdev) if (clk->func->init) return clk->func->init(clk); - clk->astate = NVKM_CLK_PSTATE_DEFAULT; + /* after a resume we have no idea what clocks are set, reset the state + */ clk->pstate = NULL; - clk->exp_cstateid = NVKM_CLK_CSTATE_DEFAULT; clk->cstate = NULL; - clk->temp = 90; /* reasonable default value */ nvkm_clk_update(clk, true); return 0; } @@ -683,8 +682,13 @@ nvkm_clk_ctor(const struct nvkm_clk_func *func, struct nvkm_device *device, clk->func = func; INIT_LIST_HEAD(&clk->states); clk->domains = func->domains; + + clk->astate = NVKM_CLK_PSTATE_DEFAULT; clk->ustate_ac = -1; clk->ustate_dc = -1; + clk->exp_cstateid = NVKM_CLK_CSTATE_DEFAULT; + clk->temp = 90; /* reasonable default value */ + clk->allow_reclock = allow_reclock; INIT_WORK(&clk->work, nvkm_clk_update_work); -- 2.13.2
Karol Herbst
2017-Jul-21 21:55 UTC
[Nouveau] [RFC PATCH 07/13] clk: Check pm_runtime status before reclocking
We don't want to change anything on the GPU if it's suspended. Also we
need to increase the refcount on the pm_runtime counter so that the GPU
won't be suspended while reclocking.
v2: convert to C style comments
Signed-off-by: Karol Herbst <karolherbst at gmail.com>
---
drm/nouveau/nvkm/subdev/clk/base.c | 14 ++++++++++++--
1 file changed, 12 insertions(+), 2 deletions(-)
diff --git a/drm/nouveau/nvkm/subdev/clk/base.c
b/drm/nouveau/nvkm/subdev/clk/base.c
index 54188d2b..81093e13 100644
--- a/drm/nouveau/nvkm/subdev/clk/base.c
+++ b/drm/nouveau/nvkm/subdev/clk/base.c
@@ -315,6 +315,7 @@ nvkm_clk_update_work(struct work_struct *work)
{
struct nvkm_clk *clk = container_of(work, typeof(*clk), work);
struct nvkm_subdev *subdev = &clk->subdev;
+ struct device *dev = subdev->device->dev;
int pstate;
if (!atomic_xchg(&clk->waiting, 0))
@@ -337,8 +338,17 @@ nvkm_clk_update_work(struct work_struct *work)
}
nvkm_trace(subdev, "-> %d\n", pstate);
- if (!clk->pstate || pstate != clk->pstate->pstate) {
- int ret = nvkm_pstate_prog(clk, pstate);
+
+ /* only call into the code if the GPU is powered on */
+ if ((!clk->pstate || pstate != clk->pstate->pstate)
+ && !pm_runtime_suspended(dev)) {
+ int ret;
+ /* it would be a shame if the GPU goes into suspend while doing
+ * the reclock
+ */
+ pm_runtime_get_sync(dev);
+ ret = nvkm_pstate_prog(clk, pstate);
+ pm_runtime_put(dev);
if (ret) {
nvkm_error(subdev, "error setting pstate %d: %d\n",
pstate, ret);
--
2.13.2
Karol Herbst
2017-Jul-21 21:55 UTC
[Nouveau] [RFC PATCH 08/13] therm: Don't cancel the timer
We will need a always running therm daemon to adjust the voltage/clocks on
the fly.
Signed-off-by: Karol Herbst <karolherbst at gmail.com>
Reviewed-by: Martin Peres <martin.peres at free.fr>
---
drm/nouveau/nvkm/subdev/therm/base.c | 9 ++-------
1 file changed, 2 insertions(+), 7 deletions(-)
diff --git a/drm/nouveau/nvkm/subdev/therm/base.c
b/drm/nouveau/nvkm/subdev/therm/base.c
index 952a7cb0..2c9b2730 100644
--- a/drm/nouveau/nvkm/subdev/therm/base.c
+++ b/drm/nouveau/nvkm/subdev/therm/base.c
@@ -106,7 +106,6 @@ nvkm_therm_update(struct nvkm_therm *therm, int mode)
struct nvkm_timer *tmr = subdev->device->timer;
unsigned long flags;
bool immd = true;
- bool poll = true;
int duty = -1;
spin_lock_irqsave(&therm->lock, flags);
@@ -116,11 +115,9 @@ nvkm_therm_update(struct nvkm_therm *therm, int mode)
switch (mode) {
case NVKM_THERM_CTRL_MANUAL:
- nvkm_timer_alarm(tmr, 0, &therm->alarm);
duty = nvkm_therm_fan_get(therm);
if (duty < 0)
duty = 100;
- poll = false;
break;
case NVKM_THERM_CTRL_AUTO:
switch(therm->fan->bios.fan_mode) {
@@ -135,18 +132,16 @@ nvkm_therm_update(struct nvkm_therm *therm, int mode)
duty = therm->cstate;
else
duty = nvkm_therm_update_linear_fallback(therm);
- poll = false;
break;
}
immd = false;
break;
case NVKM_THERM_CTRL_NONE:
default:
- nvkm_timer_alarm(tmr, 0, &therm->alarm);
- poll = false;
+ break;
}
- if (poll)
+ if (list_empty(&therm->alarm.head))
nvkm_timer_alarm(tmr, 1000000000ULL, &therm->alarm);
spin_unlock_irqrestore(&therm->lock, flags);
--
2.13.2
Karol Herbst
2017-Jul-21 21:55 UTC
[Nouveau] [RFC PATCH 09/13] therm: Move the temp readout into the alarm
It makes more sense to read out the temperature in the alarm, because we
want to do various things with it:
1. adjust the fans
2. notify the clk subdev about the changed temperature
Signed-off-by: Karol Herbst <karolherbst at gmail.com>
---
drm/nouveau/include/nvkm/subdev/therm.h | 1 +
drm/nouveau/nvkm/subdev/therm/base.c | 30 +++++++++++++++---------------
2 files changed, 16 insertions(+), 15 deletions(-)
diff --git a/drm/nouveau/include/nvkm/subdev/therm.h
b/drm/nouveau/include/nvkm/subdev/therm.h
index 1bfd93b8..d1acdcc4 100644
--- a/drm/nouveau/include/nvkm/subdev/therm.h
+++ b/drm/nouveau/include/nvkm/subdev/therm.h
@@ -56,6 +56,7 @@ struct nvkm_therm {
int mode;
int cstate;
int suspend;
+ u8 last_temp;
/* bios */
struct nvbios_therm_sensor bios_sensor;
diff --git a/drm/nouveau/nvkm/subdev/therm/base.c
b/drm/nouveau/nvkm/subdev/therm/base.c
index 2c9b2730..1a49759c 100644
--- a/drm/nouveau/nvkm/subdev/therm/base.c
+++ b/drm/nouveau/nvkm/subdev/therm/base.c
@@ -32,12 +32,11 @@ nvkm_therm_temp_get(struct nvkm_therm *therm)
}
static int
-nvkm_therm_update_trip(struct nvkm_therm *therm)
+nvkm_therm_update_trip(struct nvkm_therm *therm, u8 temp)
{
struct nvbios_therm_trip_point *trip = therm->fan->bios.trip,
*cur_trip = NULL,
*last_trip = therm->last_trip;
- u8 temp = therm->func->temp_get(therm);
u16 duty, i;
/* look for the trip point corresponding to the current temperature */
@@ -65,9 +64,8 @@ nvkm_therm_update_trip(struct nvkm_therm *therm)
static int
nvkm_therm_compute_linear_duty(struct nvkm_therm *therm, u8 linear_min_temp,
- u8 linear_max_temp)
+ u8 linear_max_temp, u8 temp)
{
- u8 temp = therm->func->temp_get(therm);
u16 duty;
/* handle the non-linear part first */
@@ -85,22 +83,22 @@ nvkm_therm_compute_linear_duty(struct nvkm_therm *therm, u8
linear_min_temp,
}
static int
-nvkm_therm_update_linear(struct nvkm_therm *therm)
+nvkm_therm_update_linear(struct nvkm_therm *therm, u8 temp)
{
u8 min = therm->fan->bios.linear_min_temp;
u8 max = therm->fan->bios.linear_max_temp;
- return nvkm_therm_compute_linear_duty(therm, min, max);
+ return nvkm_therm_compute_linear_duty(therm, min, max, temp);
}
static int
-nvkm_therm_update_linear_fallback(struct nvkm_therm *therm)
+nvkm_therm_update_linear_fallback(struct nvkm_therm *therm, u8 temp)
{
u8 max = therm->bios_sensor.thrs_fan_boost.temp;
- return nvkm_therm_compute_linear_duty(therm, 30, max);
+ return nvkm_therm_compute_linear_duty(therm, 30, max, temp);
}
static void
-nvkm_therm_update(struct nvkm_therm *therm, int mode)
+nvkm_therm_update(struct nvkm_therm *therm, u8 temp, int mode)
{
struct nvkm_subdev *subdev = &therm->subdev;
struct nvkm_timer *tmr = subdev->device->timer;
@@ -122,16 +120,16 @@ nvkm_therm_update(struct nvkm_therm *therm, int mode)
case NVKM_THERM_CTRL_AUTO:
switch(therm->fan->bios.fan_mode) {
case NVBIOS_THERM_FAN_TRIP:
- duty = nvkm_therm_update_trip(therm);
+ duty = nvkm_therm_update_trip(therm, temp);
break;
case NVBIOS_THERM_FAN_LINEAR:
- duty = nvkm_therm_update_linear(therm);
+ duty = nvkm_therm_update_linear(therm, temp);
break;
case NVBIOS_THERM_FAN_OTHER:
if (therm->cstate)
duty = therm->cstate;
else
- duty = nvkm_therm_update_linear_fallback(therm);
+ duty = nvkm_therm_update_linear_fallback(therm, temp);
break;
}
immd = false;
@@ -159,7 +157,7 @@ nvkm_therm_cstate(struct nvkm_therm *therm, int fan, int
dir)
(dir > 0 && fan > therm->cstate)) {
nvkm_debug(subdev, "default fan speed -> %d%%\n", fan);
therm->cstate = fan;
- nvkm_therm_update(therm, -1);
+ nvkm_therm_update(therm, therm->last_temp, -1);
}
return 0;
}
@@ -169,7 +167,8 @@ nvkm_therm_alarm(struct nvkm_alarm *alarm)
{
struct nvkm_therm *therm container_of(alarm, struct nvkm_therm,
alarm);
- nvkm_therm_update(therm, -1);
+ therm->last_temp = nvkm_therm_temp_get(therm);
+ nvkm_therm_update(therm, therm->last_temp, -1);
}
int
@@ -199,7 +198,7 @@ nvkm_therm_fan_mode(struct nvkm_therm *therm, int mode)
return 0;
nvkm_debug(subdev, "fan management: %s\n", name[mode]);
- nvkm_therm_update(therm, mode);
+ nvkm_therm_update(therm, therm->last_temp, mode);
return 0;
}
@@ -335,6 +334,7 @@ static int
nvkm_therm_init(struct nvkm_subdev *subdev)
{
struct nvkm_therm *therm = nvkm_therm(subdev);
+ therm->last_temp = nvkm_therm_temp_get(therm);
therm->func->init(therm);
--
2.13.2
Karol Herbst
2017-Jul-21 21:55 UTC
[Nouveau] [RFC PATCH 10/13] therm: Trigger reclock in temperature daemon
Depending on the temperature, cstates might become unreachable or the maped
voltage of a cstate changes. We want to adjust to that.
Signed-off-by: Karol Herbst <karolherbst at gmail.com>
Reviewed-by: Martin Peres <martin.peres at free.fr>
---
drm/nouveau/nvkm/subdev/therm/base.c | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/drm/nouveau/nvkm/subdev/therm/base.c
b/drm/nouveau/nvkm/subdev/therm/base.c
index 1a49759c..2a83e92a 100644
--- a/drm/nouveau/nvkm/subdev/therm/base.c
+++ b/drm/nouveau/nvkm/subdev/therm/base.c
@@ -23,6 +23,8 @@
*/
#include "priv.h"
+#include <subdev/clk.h>
+
int
nvkm_therm_temp_get(struct nvkm_therm *therm)
{
@@ -167,8 +169,13 @@ nvkm_therm_alarm(struct nvkm_alarm *alarm)
{
struct nvkm_therm *therm container_of(alarm, struct nvkm_therm,
alarm);
+ struct nvkm_clk *clk = therm->subdev.device->clk;
+
therm->last_temp = nvkm_therm_temp_get(therm);
nvkm_therm_update(therm, therm->last_temp, -1);
+
+ if (clk)
+ nvkm_clk_tstate(clk, therm->last_temp);
}
int
--
2.13.2
Karol Herbst
2017-Jul-21 21:55 UTC
[Nouveau] [RFC PATCH 11/13] bios: add thermal policies table
Signed-off-by: Karol Herbst <karolherbst at gmail.com>
---
.../include/nvkm/subdev/bios/thermal_policies.h | 27 ++++++++
drm/nouveau/nvkm/subdev/bios/Kbuild | 1 +
drm/nouveau/nvkm/subdev/bios/thermal_policies.c | 81 ++++++++++++++++++++++
3 files changed, 109 insertions(+)
create mode 100644 drm/nouveau/include/nvkm/subdev/bios/thermal_policies.h
create mode 100644 drm/nouveau/nvkm/subdev/bios/thermal_policies.c
diff --git a/drm/nouveau/include/nvkm/subdev/bios/thermal_policies.h
b/drm/nouveau/include/nvkm/subdev/bios/thermal_policies.h
new file mode 100644
index 00000000..c9215fdd
--- /dev/null
+++ b/drm/nouveau/include/nvkm/subdev/bios/thermal_policies.h
@@ -0,0 +1,27 @@
+#ifndef __NVBIOS_THERMAL_POLICIES_H__
+#define __NVBIOS_THERMAL_POLICIES_H__
+
+struct nvbios_thermal_policies_header {
+ u32 offset;
+
+ u8 version;
+ u8 hlen;
+ u8 ecount;
+ u8 elen;
+};
+struct nvbios_thermal_policies_entry {
+ u8 mode;
+ u16 t0;
+ u16 t1;
+ u16 t2;
+ s16 down_offset;
+ s16 up_offset;
+};
+
+int nvbios_thermal_policies_parse(struct nvkm_bios *,
+ struct nvbios_thermal_policies_header *);
+int nvbios_thermal_policies_entry(struct nvkm_bios *,
+ struct nvbios_thermal_policies_header *,
+ u8 idx,
+ struct nvbios_thermal_policies_entry *);
+#endif
diff --git a/drm/nouveau/nvkm/subdev/bios/Kbuild
b/drm/nouveau/nvkm/subdev/bios/Kbuild
index 6b4f1e06..38f31dd0 100644
--- a/drm/nouveau/nvkm/subdev/bios/Kbuild
+++ b/drm/nouveau/nvkm/subdev/bios/Kbuild
@@ -30,6 +30,7 @@ nvkm-y += nvkm/subdev/bios/shadowramin.o
nvkm-y += nvkm/subdev/bios/shadowrom.o
nvkm-y += nvkm/subdev/bios/timing.o
nvkm-y += nvkm/subdev/bios/therm.o
+nvkm-y += nvkm/subdev/bios/thermal_policies.o
nvkm-y += nvkm/subdev/bios/vmap.o
nvkm-y += nvkm/subdev/bios/volt.o
nvkm-y += nvkm/subdev/bios/vpstate.o
diff --git a/drm/nouveau/nvkm/subdev/bios/thermal_policies.c
b/drm/nouveau/nvkm/subdev/bios/thermal_policies.c
new file mode 100644
index 00000000..5105194e
--- /dev/null
+++ b/drm/nouveau/nvkm/subdev/bios/thermal_policies.c
@@ -0,0 +1,81 @@
+/*
+ * Copyright 2017 Karol Herbst
+ *
+ * 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: Karol Herbst
+ */
+#include <subdev/bios.h>
+#include <subdev/bios/bit.h>
+#include <subdev/bios/thermal_policies.h>
+
+static u32
+nvbios_thermal_policies_offset(struct nvkm_bios *b)
+{
+ struct bit_entry bit_P;
+
+ if (!bit_entry(b, 'P', &bit_P)) {
+ if (bit_P.version == 2 && bit_P.length >= 0x50)
+ return nvbios_rd32(b, bit_P.offset + 0x50);
+ }
+
+ return 0;
+}
+
+int
+nvbios_thermal_policies_parse(struct nvkm_bios *b, struct
nvbios_thermal_policies_header *h)
+{
+ if (!h)
+ return -EINVAL;
+
+ h->offset = nvbios_thermal_policies_offset(b);
+ if (!h->offset)
+ return -ENODEV;
+
+ h->version = nvbios_rd08(b, h->offset);
+ switch (h->version) {
+ case 0x10:
+ h->hlen = nvbios_rd08(b, h->offset + 0x1);
+ h->elen = nvbios_rd08(b, h->offset + 0x2);
+ h->ecount = nvbios_rd08(b, h->offset + 0x3);
+ return 0;
+ default:
+ return -EINVAL;
+ }
+}
+
+int
+nvbios_thermal_policies_entry(struct nvkm_bios *b, struct
nvbios_thermal_policies_header *h,
+ u8 idx, struct nvbios_thermal_policies_entry *e)
+{
+ u32 offset;
+
+ if (!e || !h || idx > h->ecount)
+ return -EINVAL;
+
+ offset = h->offset + h->hlen + idx * h->elen;
+ e->mode = nvbios_rd08(b, offset);
+ e->t0 = nvbios_rd16(b, offset + 0x2);
+ e->t1 = nvbios_rd16(b, offset + 0x4);
+ e->t2 = nvbios_rd16(b, offset + 0x6);
+ e->down_offset = nvbios_rd16(b, offset + 0x12);
+ e->up_offset = nvbios_rd16(b, offset + 0x14);
+
+ return 0;
+}
--
2.13.2
Karol Herbst
2017-Jul-21 21:55 UTC
[Nouveau] [RFC PATCH 12/13] clk: parse thermal policies for throttling thresholds
Signed-off-by: Karol Herbst <karolherbst at gmail.com>
---
drm/nouveau/include/nvkm/subdev/clk.h | 2 ++
drm/nouveau/nvkm/subdev/clk/base.c | 42 +++++++++++++++++++++++++++++++++++
2 files changed, 44 insertions(+)
diff --git a/drm/nouveau/include/nvkm/subdev/clk.h
b/drm/nouveau/include/nvkm/subdev/clk.h
index f35518c3..f5ff1fd9 100644
--- a/drm/nouveau/include/nvkm/subdev/clk.h
+++ b/drm/nouveau/include/nvkm/subdev/clk.h
@@ -104,6 +104,8 @@ struct nvkm_clk {
struct nvkm_cstate *cstate;
int exp_cstateid;
u8 temp;
+ u8 max_temp;
+ u8 relax_temp;
bool allow_reclock;
#define NVKM_CLK_BOOST_NONE 0x0
diff --git a/drm/nouveau/nvkm/subdev/clk/base.c
b/drm/nouveau/nvkm/subdev/clk/base.c
index 81093e13..60edb57b 100644
--- a/drm/nouveau/nvkm/subdev/clk/base.c
+++ b/drm/nouveau/nvkm/subdev/clk/base.c
@@ -27,6 +27,7 @@
#include <subdev/bios/boost.h>
#include <subdev/bios/cstep.h>
#include <subdev/bios/perf.h>
+#include <subdev/bios/thermal_policies.h>
#include <subdev/bios/vpstate.h>
#include <subdev/fb.h>
#include <subdev/therm.h>
@@ -669,6 +670,44 @@ nvkm_clk = {
.fini = nvkm_clk_fini,
};
+static void
+nvkm_clk_parse_max_temp(struct nvkm_clk *clk)
+{
+ struct nvkm_subdev *subdev = &clk->subdev;
+ struct nvkm_bios *bios = subdev->device->bios;
+ struct nvbios_thermal_policies_header header;
+ struct nvbios_thermal_policies_entry entry;
+ u8 i;
+ s16 mt = 0xff;
+ s16 rt = 0xff;
+
+ if (nvbios_thermal_policies_parse(bios, &header))
+ return;
+
+ if (!header.ecount)
+ return;
+
+ for (i = 0; i < header.ecount; i++) {
+ if (nvbios_thermal_policies_entry(bios, &header, i, &entry))
+ return;
+
+ if (entry.mode != 1)
+ continue;
+
+ mt = min(mt, (s16)((entry.t0 + entry.down_offset) / 32));
+ rt = min(rt, (s16)((entry.t0 + entry.up_offset) / 32));
+ }
+
+ if (mt == 0xff || rt == 0xff)
+ return;
+
+ clk->max_temp = mt;
+ clk->relax_temp = rt;
+
+ nvkm_debug(subdev, "setting up sw throttling thresholds
(%u/%u°C)\n",
+ clk->max_temp, clk->relax_temp);
+}
+
int
nvkm_clk_ctor(const struct nvkm_clk_func *func, struct nvkm_device *device,
int index, bool allow_reclock, struct nvkm_clk *clk)
@@ -743,6 +782,9 @@ nvkm_clk_ctor(const struct nvkm_clk_func *func, struct
nvkm_device *device,
clk->boost_mode = nvkm_longopt(device->cfgopt, "NvBoost",
NVKM_CLK_BOOST_NONE);
+
+ nvkm_clk_parse_max_temp(clk);
+
return 0;
}
--
2.13.2
Signed-off-by: Karol Herbst <karolherbst at gmail.com>
---
drm/nouveau/include/nvkm/subdev/clk.h | 1 +
drm/nouveau/nvkm/subdev/clk/base.c | 35 +++++++++++++++++++++++++++++++----
2 files changed, 32 insertions(+), 4 deletions(-)
diff --git a/drm/nouveau/include/nvkm/subdev/clk.h
b/drm/nouveau/include/nvkm/subdev/clk.h
index f5ff1fd9..b79bf657 100644
--- a/drm/nouveau/include/nvkm/subdev/clk.h
+++ b/drm/nouveau/include/nvkm/subdev/clk.h
@@ -106,6 +106,7 @@ struct nvkm_clk {
u8 temp;
u8 max_temp;
u8 relax_temp;
+ bool throttled;
bool allow_reclock;
#define NVKM_CLK_BOOST_NONE 0x0
diff --git a/drm/nouveau/nvkm/subdev/clk/base.c
b/drm/nouveau/nvkm/subdev/clk/base.c
index 60edb57b..34642000 100644
--- a/drm/nouveau/nvkm/subdev/clk/base.c
+++ b/drm/nouveau/nvkm/subdev/clk/base.c
@@ -279,7 +279,7 @@ nvkm_pstate_prog(struct nvkm_clk *clk, int pstateid)
struct nvkm_fb *fb = subdev->device->fb;
struct nvkm_pci *pci = subdev->device->pci;
struct nvkm_pstate *pstate;
- int ret;
+ int ret, cstate;
if (pstateid == NVKM_CLK_PSTATE_DEFAULT)
return 0;
@@ -308,7 +308,12 @@ nvkm_pstate_prog(struct nvkm_clk *clk, int pstateid)
ram->func->tidy(ram);
}
- return nvkm_cstate_prog(clk, pstate, clk->exp_cstateid);
+ if (clk->throttled)
+ cstate = list_first_entry(&pstate->list, struct nvkm_cstate,
head)->id;
+ else
+ cstate = clk->exp_cstateid;
+
+ return nvkm_cstate_prog(clk, pstate, cstate);
}
static void
@@ -333,12 +338,17 @@ nvkm_clk_update_work(struct work_struct *work)
pstate = clk->pwrsrc ? clk->ustate_ac : clk->ustate_dc;
if (clk->state_nr && pstate != -1) {
- pstate = (pstate < 0) ? clk->astate : pstate;
+ if (clk->throttled)
+ pstate = list_first_entry(&clk->states, struct nvkm_pstate,
head)->pstate;
+ else
+ pstate = (pstate < 0) ? clk->astate : pstate;
} else {
pstate = NVKM_CLK_PSTATE_DEFAULT;
}
- nvkm_trace(subdev, "-> %d\n", pstate);
+ nvkm_trace(subdev, "P %d PWR %d U(AC) %d U(DC) %d A %d C %d T
%d°C\n",
+ pstate, clk->pwrsrc, clk->ustate_ac, clk->ustate_dc,
+ clk->astate, clk->exp_cstateid, clk->temp);
/* only call into the code if the GPU is powered on */
if ((!clk->pstate || pstate != clk->pstate->pstate)
@@ -574,9 +584,25 @@ nvkm_clk_astate(struct nvkm_clk *clk, int req, int rel,
bool wait)
int
nvkm_clk_tstate(struct nvkm_clk *clk, u8 temp)
{
+ struct nvkm_subdev *subdev = &clk->subdev;
if (clk->temp == temp)
return 0;
clk->temp = temp;
+ if (clk->max_temp && clk->relax_temp) {
+ if (!clk->throttled && temp > clk->max_temp) {
+ nvkm_warn(subdev,
+ "temperature (%d C) hit the 'downclock' "
+ "threshold\n",
+ temp);
+ clk->throttled = true;
+ } else if (clk->throttled && temp < clk->relax_temp) {
+ nvkm_warn(subdev,
+ "temperature (%d C) went below the "
+ "'relax' threshold\n",
+ temp);
+ clk->throttled = false;
+ }
+ }
return nvkm_clk_update(clk, false);
}
@@ -737,6 +763,7 @@ nvkm_clk_ctor(const struct nvkm_clk_func *func, struct
nvkm_device *device,
clk->ustate_dc = -1;
clk->exp_cstateid = NVKM_CLK_CSTATE_DEFAULT;
clk->temp = 90; /* reasonable default value */
+ clk->throttled = false;
clk->allow_reclock = allow_reclock;
--
2.13.2
Ilia Mirkin
2017-Jul-22 00:23 UTC
[Nouveau] [RFC PATCH 12/13] clk: parse thermal policies for throttling thresholds
On Fri, Jul 21, 2017 at 5:55 PM, Karol Herbst <karolherbst at gmail.com> wrote:> Signed-off-by: Karol Herbst <karolherbst at gmail.com> > --- > drm/nouveau/include/nvkm/subdev/clk.h | 2 ++ > drm/nouveau/nvkm/subdev/clk/base.c | 42 +++++++++++++++++++++++++++++++++++ > 2 files changed, 44 insertions(+) > > diff --git a/drm/nouveau/include/nvkm/subdev/clk.h b/drm/nouveau/include/nvkm/subdev/clk.h > index f35518c3..f5ff1fd9 100644 > --- a/drm/nouveau/include/nvkm/subdev/clk.h > +++ b/drm/nouveau/include/nvkm/subdev/clk.h > @@ -104,6 +104,8 @@ struct nvkm_clk { > struct nvkm_cstate *cstate; > int exp_cstateid; > u8 temp; > + u8 max_temp; > + u8 relax_temp; > > bool allow_reclock; > #define NVKM_CLK_BOOST_NONE 0x0 > diff --git a/drm/nouveau/nvkm/subdev/clk/base.c b/drm/nouveau/nvkm/subdev/clk/base.c > index 81093e13..60edb57b 100644 > --- a/drm/nouveau/nvkm/subdev/clk/base.c > +++ b/drm/nouveau/nvkm/subdev/clk/base.c > @@ -27,6 +27,7 @@ > #include <subdev/bios/boost.h> > #include <subdev/bios/cstep.h> > #include <subdev/bios/perf.h> > +#include <subdev/bios/thermal_policies.h> > #include <subdev/bios/vpstate.h> > #include <subdev/fb.h> > #include <subdev/therm.h> > @@ -669,6 +670,44 @@ nvkm_clk = { > .fini = nvkm_clk_fini, > }; > > +static void > +nvkm_clk_parse_max_temp(struct nvkm_clk *clk) > +{ > + struct nvkm_subdev *subdev = &clk->subdev; > + struct nvkm_bios *bios = subdev->device->bios; > + struct nvbios_thermal_policies_header header; > + struct nvbios_thermal_policies_entry entry; > + u8 i; > + s16 mt = 0xff; > + s16 rt = 0xff; > + > + if (nvbios_thermal_policies_parse(bios, &header)) > + return; > + > + if (!header.ecount) > + return; > + > + for (i = 0; i < header.ecount; i++) { > + if (nvbios_thermal_policies_entry(bios, &header, i, &entry)) > + return; > + > + if (entry.mode != 1) > + continue; > + > + mt = min(mt, (s16)((entry.t0 + entry.down_offset) / 32)); > + rt = min(rt, (s16)((entry.t0 + entry.up_offset) / 32));I believe you're looking for min_t(s16, rt, foo)> + } > + > + if (mt == 0xff || rt == 0xff) > + return; > + > + clk->max_temp = mt; > + clk->relax_temp = rt; > + > + nvkm_debug(subdev, "setting up sw throttling thresholds (%u/%u°C)\n", > + clk->max_temp, clk->relax_temp); > +} > + > int > nvkm_clk_ctor(const struct nvkm_clk_func *func, struct nvkm_device *device, > int index, bool allow_reclock, struct nvkm_clk *clk) > @@ -743,6 +782,9 @@ nvkm_clk_ctor(const struct nvkm_clk_func *func, struct nvkm_device *device, > > clk->boost_mode = nvkm_longopt(device->cfgopt, "NvBoost", > NVKM_CLK_BOOST_NONE); > + > + nvkm_clk_parse_max_temp(clk); > + > return 0; > } > > -- > 2.13.2 > > _______________________________________________ > Nouveau mailing list > Nouveau at lists.freedesktop.org > https://lists.freedesktop.org/mailman/listinfo/nouveau
On 2017-07-21 — 23:55, Karol Herbst wrote:> Signed-off-by: Karol Herbst <karolherbst at gmail.com> > --- > drm/nouveau/include/nvkm/subdev/clk.h | 1 + > drm/nouveau/nvkm/subdev/clk/base.c | 35 +++++++++++++++++++++++++++++++---- > 2 files changed, 32 insertions(+), 4 deletions(-) > > diff --git a/drm/nouveau/include/nvkm/subdev/clk.h b/drm/nouveau/include/nvkm/subdev/clk.h > index f5ff1fd9..b79bf657 100644 > --- a/drm/nouveau/include/nvkm/subdev/clk.h > +++ b/drm/nouveau/include/nvkm/subdev/clk.h > @@ -106,6 +106,7 @@ struct nvkm_clk { > u8 temp; > u8 max_temp; > u8 relax_temp; > + bool throttled; > > bool allow_reclock; > #define NVKM_CLK_BOOST_NONE 0x0 > diff --git a/drm/nouveau/nvkm/subdev/clk/base.c b/drm/nouveau/nvkm/subdev/clk/base.c > index 60edb57b..34642000 100644 > --- a/drm/nouveau/nvkm/subdev/clk/base.c > +++ b/drm/nouveau/nvkm/subdev/clk/base.c > @@ -279,7 +279,7 @@ nvkm_pstate_prog(struct nvkm_clk *clk, int pstateid) > struct nvkm_fb *fb = subdev->device->fb; > struct nvkm_pci *pci = subdev->device->pci; > struct nvkm_pstate *pstate; > - int ret; > + int ret, cstate; > > if (pstateid == NVKM_CLK_PSTATE_DEFAULT) > return 0; > @@ -308,7 +308,12 @@ nvkm_pstate_prog(struct nvkm_clk *clk, int pstateid) > ram->func->tidy(ram); > } > > - return nvkm_cstate_prog(clk, pstate, clk->exp_cstateid); > + if (clk->throttled) > + cstate = list_first_entry(&pstate->list, struct nvkm_cstate, head)->id; > + else > + cstate = clk->exp_cstateid; > + > + return nvkm_cstate_prog(clk, pstate, cstate); > } > > static void > @@ -333,12 +338,17 @@ nvkm_clk_update_work(struct work_struct *work) > > pstate = clk->pwrsrc ? clk->ustate_ac : clk->ustate_dc; > if (clk->state_nr && pstate != -1) { > - pstate = (pstate < 0) ? clk->astate : pstate; > + if (clk->throttled) > + pstate = list_first_entry(&clk->states, struct nvkm_pstate, head)->pstate; > + else > + pstate = (pstate < 0) ? clk->astate : pstate; > } else { > pstate = NVKM_CLK_PSTATE_DEFAULT; > } > > - nvkm_trace(subdev, "-> %d\n", pstate); > + nvkm_trace(subdev, "P %d PWR %d U(AC) %d U(DC) %d A %d C %d T %d°C\n", > + pstate, clk->pwrsrc, clk->ustate_ac, clk->ustate_dc, > + clk->astate, clk->exp_cstateid, clk->temp); > > /* only call into the code if the GPU is powered on */ > if ((!clk->pstate || pstate != clk->pstate->pstate) > @@ -574,9 +584,25 @@ nvkm_clk_astate(struct nvkm_clk *clk, int req, int rel, bool wait) > int > nvkm_clk_tstate(struct nvkm_clk *clk, u8 temp) > { > + struct nvkm_subdev *subdev = &clk->subdev; > if (clk->temp == temp) > return 0; > clk->temp = temp; > + if (clk->max_temp && clk->relax_temp) { > + if (!clk->throttled && temp > clk->max_temp) { > + nvkm_warn(subdev, > + "temperature (%d C) hit the 'downclock' " > + "threshold\n", > + temp); > + clk->throttled = true; > + } else if (clk->throttled && temp < clk->relax_temp) { > + nvkm_warn(subdev, > + "temperature (%d C) went below the " > + "'relax' threshold\n", > + temp); > + clk->throttled = false;I don't think this should be a warning. Maybe an `nvkm_info()` instead? Also, should the throttled status be reset/recomputed after suspend/resume cycle?> + } > + } > return nvkm_clk_update(clk, false); > } > > @@ -737,6 +763,7 @@ nvkm_clk_ctor(const struct nvkm_clk_func *func, struct nvkm_device *device, > clk->ustate_dc = -1; > clk->exp_cstateid = NVKM_CLK_CSTATE_DEFAULT; > clk->temp = 90; /* reasonable default value */ > + clk->throttled = false; > > clk->allow_reclock = allow_reclock; > > -- > 2.13.2 > > _______________________________________________ > Nouveau mailing list > Nouveau at lists.freedesktop.org > https://lists.freedesktop.org/mailman/listinfo/nouveau-------------- next part -------------- A non-text attachment was scrubbed... Name: signature.asc Type: application/pgp-signature Size: 833 bytes Desc: not available URL: <https://lists.freedesktop.org/archives/nouveau/attachments/20170722/0757d51f/attachment.sig>
Apparently Analagous Threads
- [RFC PATCH 16/29] clk: parse thermal policies for throttling thresholds
- [RFC PATCH 12/13] clk: parse thermal policies for throttling thresholds
- [RFC PATCH 16/29] clk: parse thermal policies for throttling thresholds
- [RFC PATCH 00/13] Thermal throttling
- [RFC PATCH 13/13] clk: thermal throttling