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>