Karol Herbst
2016-Apr-18 19:13 UTC
[Nouveau] [PATCH v4 00/37] Volting/Clocking improvements for Fermi and newer
We are slowly getting there! v4 of the series with some realy good improvements, so I am sure this is like 95% done and only needs some proper polishing and proper Reviews! I also added the NvVoltOffsetmV module parameter, so that a user is able to over and !under!-volt the GPU. Overvolting makes sense, when there are still some reclocking issues left, which might be solved by a higher voltage. Undervolting makes sense to decrease the used voltage for each Cstate and to make more C-States available on a handfull cards. This is some sort of really basic Overclocking support, which comes at the cost of stability, but has nearly no impact on the power consumption or heat production. But because this doesn't add new stuff and only directly modifies the voltage calculation, it is all still according to the vbios and doesn't add new clocks or something like that. If anything is unclear how I REed or got the information, please leave a note so that I can provide additional information in the commits. This series can be found on my "stable_reclocking_kepler_v4" branch on github Happy testing and happy reviewing! v3: adjust to temperature and minor fixes in the commits v4: add speedo values add proper coefficients based on speedo refactor some code in clk to not affect pre-fermi chips add NvVoltOffsetmV option Karol Herbst (37): bios/volt: handle voltage table version 0x50 with 0ed header volt: properly detect entry based voltage tables volt: save the voltage range we are able to set volt: add nvkm_volt_map_min function clk: don't create cstates whit voltages higher than what the gpu can do volt: parse the max voltage map entries volt: add min_id parameter to nvkm_volt_set_id clk: export nvkm_volt_map clk: add index field to nvkm_cstate add daemon to compare nouveau with blob voltage volt: add temperature parameter to nvkm_volt_map clk: fixup cstate selection clk: respect voltage limits in nvkm_cstate_prog bios: add parsing of BASE CLOCK table clk: allow boosting only when NvBoost is set volt: don't require perfect fit bios/vmap: unk0 field is the mode volt: add speedo volt: add gf100 subdev with speedo volt: add coefficients clk: save the max clock we can set clk: rename nvkm_pstate_calc to nvkm_clk_update nvif: add boost info and set operations debugfs: add boost interface to change the boost_mode clk: remove dstate and tstate therm: don't cancel the timer 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: seperate the locking from the implementation in nvkm_clk_update clk: split out update code to nv40 clk: only do partial reclocks as required therm: trigger reclock in temperature daemon mc: fix NULL pointer access in libnouveau clk: set clocks to pre suspend state after suspend WIP volt/gk104: readout speedo volt: add NvVoltOffsetmV option bin/nv_cmp_volt.c | 139 +++++++++++ drm/nouveau/include/nvif/if0001.h | 15 ++ drm/nouveau/include/nvkm/subdev/bios/baseclock.h | 24 ++ drm/nouveau/include/nvkm/subdev/bios/vmap.h | 5 +- drm/nouveau/include/nvkm/subdev/bios/volt.h | 5 +- drm/nouveau/include/nvkm/subdev/clk.h | 23 +- drm/nouveau/include/nvkm/subdev/volt.h | 17 +- drm/nouveau/nouveau_debugfs.c | 82 ++++++- drm/nouveau/nvkm/engine/device/base.c | 17 +- drm/nouveau/nvkm/engine/device/ctrl.c | 60 ++++- drm/nouveau/nvkm/subdev/bios/Kbuild | 1 + drm/nouveau/nvkm/subdev/bios/baseclock.c | 82 +++++++ drm/nouveau/nvkm/subdev/bios/vmap.c | 12 +- drm/nouveau/nvkm/subdev/bios/volt.c | 45 ++-- drm/nouveau/nvkm/subdev/clk/base.c | 298 ++++++++++++++++------- drm/nouveau/nvkm/subdev/clk/g84.c | 1 + drm/nouveau/nvkm/subdev/clk/gf100.c | 63 ++++- drm/nouveau/nvkm/subdev/clk/gk104.c | 3 +- drm/nouveau/nvkm/subdev/clk/gk20a.c | 1 + drm/nouveau/nvkm/subdev/clk/gm20b.c | 1 + drm/nouveau/nvkm/subdev/clk/gt215.c | 1 + drm/nouveau/nvkm/subdev/clk/mcp77.c | 1 + drm/nouveau/nvkm/subdev/clk/nv40.c | 18 ++ drm/nouveau/nvkm/subdev/clk/nv50.c | 1 + drm/nouveau/nvkm/subdev/clk/priv.h | 9 + drm/nouveau/nvkm/subdev/mc/base.c | 7 +- drm/nouveau/nvkm/subdev/pmu/gk20a.c | 23 +- drm/nouveau/nvkm/subdev/therm/base.c | 14 +- drm/nouveau/nvkm/subdev/volt/Kbuild | 1 + drm/nouveau/nvkm/subdev/volt/base.c | 159 +++++++++++- drm/nouveau/nvkm/subdev/volt/gf100.c | 59 +++++ drm/nouveau/nvkm/subdev/volt/gk104.c | 19 ++ drm/nouveau/nvkm/subdev/volt/priv.h | 1 + 33 files changed, 1034 insertions(+), 173 deletions(-) create mode 100644 bin/nv_cmp_volt.c create mode 100644 drm/nouveau/include/nvkm/subdev/bios/baseclock.h create mode 100644 drm/nouveau/nvkm/subdev/bios/baseclock.c create mode 100644 drm/nouveau/nvkm/subdev/volt/gf100.c -- 2.8.1
Karol Herbst
2016-Apr-18 19:13 UTC
[Nouveau] [PATCH v4 01/37] bios/volt: handle voltage table version 0x50 with 0ed header
Some Fermi+ gpus have no usefull header in the voltage table, which means nouveau has to read the voltages out of the entries directly. The mask may be bigger than 0x1fffff, but this value is already >2V, so it will be fine for now. This patch fixes volting issues on those cards enabling them to switch cstates. Signed-off-by: Karol Herbst <nouveau at karolherbst.de> Reviewed-by: Martin Peres <martin.peres at free.fr> Tested-by: Pierre Moreau <pierre.morrow at free.fr> --- drm/nouveau/nvkm/subdev/bios/volt.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drm/nouveau/nvkm/subdev/bios/volt.c b/drm/nouveau/nvkm/subdev/bios/volt.c index 6e0a336..81a47b2 100644 --- a/drm/nouveau/nvkm/subdev/bios/volt.c +++ b/drm/nouveau/nvkm/subdev/bios/volt.c @@ -142,7 +142,10 @@ nvbios_volt_entry_parse(struct nvkm_bios *bios, int idx, u8 *ver, u8 *len, info->vid = nvbios_rd08(bios, volt + 0x01) >> 2; break; case 0x40: + break; case 0x50: + info->voltage = nvbios_rd32(bios, volt) & 0x001fffff; + info->vid = (nvbios_rd32(bios, volt) >> 23) & 0xff; break; } return volt; -- 2.8.1
Karol Herbst
2016-Apr-18 19:13 UTC
[Nouveau] [PATCH v4 02/37] volt: properly detect entry based voltage tables
there is a field in the voltage table which tells us if the VIDs are taken from the entries or calculated through the header. v2: don't break older versions Signed-off-by: Karol Herbst <nouveau at karolherbst.de> Reviewed-by: Martin Peres <martin.peres at free.fr> Tested-by: Pierre Moreau <pierre.morrow at free.fr> --- drm/nouveau/include/nvkm/subdev/bios/volt.h | 5 ++-- drm/nouveau/nvkm/subdev/bios/volt.c | 42 ++++++++++++++++------------- drm/nouveau/nvkm/subdev/volt/base.c | 8 ++++-- 3 files changed, 33 insertions(+), 22 deletions(-) diff --git a/drm/nouveau/include/nvkm/subdev/bios/volt.h b/drm/nouveau/include/nvkm/subdev/bios/volt.h index b0df610..0d91c24 100644 --- a/drm/nouveau/include/nvkm/subdev/bios/volt.h +++ b/drm/nouveau/include/nvkm/subdev/bios/volt.h @@ -13,8 +13,9 @@ struct nvbios_volt { u32 base; /* GPIO mode */ - u8 vidmask; - s16 step; + bool entry_based; + u8 vidmask; + s16 step; /* PWM mode */ u32 pwm_freq; diff --git a/drm/nouveau/nvkm/subdev/bios/volt.c b/drm/nouveau/nvkm/subdev/bios/volt.c index 81a47b2..70e1f9d 100644 --- a/drm/nouveau/nvkm/subdev/bios/volt.c +++ b/drm/nouveau/nvkm/subdev/bios/volt.c @@ -73,30 +73,34 @@ nvbios_volt_parse(struct nvkm_bios *bios, u8 *ver, u8 *hdr, u8 *cnt, u8 *len, memset(info, 0x00, sizeof(*info)); switch (!!volt * *ver) { case 0x12: - info->type = NVBIOS_VOLT_GPIO; - info->vidmask = nvbios_rd08(bios, volt + 0x04); + info->type = NVBIOS_VOLT_GPIO; + info->vidmask = nvbios_rd08(bios, volt + 0x04); + info->entry_based = true; break; case 0x20: - info->type = NVBIOS_VOLT_GPIO; - info->vidmask = nvbios_rd08(bios, volt + 0x05); + info->type = NVBIOS_VOLT_GPIO; + info->vidmask = nvbios_rd08(bios, volt + 0x05); + info->entry_based = true; break; case 0x30: - info->type = NVBIOS_VOLT_GPIO; - info->vidmask = nvbios_rd08(bios, volt + 0x04); + info->type = NVBIOS_VOLT_GPIO; + info->vidmask = nvbios_rd08(bios, volt + 0x04); + info->entry_based = true; break; case 0x40: - info->type = NVBIOS_VOLT_GPIO; - info->base = nvbios_rd32(bios, volt + 0x04); - info->step = nvbios_rd16(bios, volt + 0x08); - info->vidmask = nvbios_rd08(bios, volt + 0x0b); + info->type = NVBIOS_VOLT_GPIO; + info->base = nvbios_rd32(bios, volt + 0x04); + info->step = nvbios_rd16(bios, volt + 0x08); + info->vidmask = nvbios_rd08(bios, volt + 0x0b); + info->entry_based = false; /* XXX: find the flag byte */ /*XXX*/ - info->min = 0; - info->max = info->base; + info->min = 0; + info->max = info->base; break; case 0x50: - info->min = nvbios_rd32(bios, volt + 0x0a); - info->max = nvbios_rd32(bios, volt + 0x0e); - info->base = nvbios_rd32(bios, volt + 0x12) & 0x00ffffff; + info->min = nvbios_rd32(bios, volt + 0x0a); + info->max = nvbios_rd32(bios, volt + 0x0e); + info->base = nvbios_rd32(bios, volt + 0x12) & 0x00ffffff; /* offset 4 seems to be a flag byte */ if (nvbios_rd32(bios, volt + 0x4) & 1) { @@ -104,9 +108,11 @@ nvbios_volt_parse(struct nvkm_bios *bios, u8 *ver, u8 *hdr, u8 *cnt, u8 *len, info->pwm_freq = nvbios_rd32(bios, volt + 0x5) / 1000; info->pwm_range = nvbios_rd32(bios, volt + 0x16); } else { - info->type = NVBIOS_VOLT_GPIO; - info->vidmask = nvbios_rd08(bios, volt + 0x06); - info->step = nvbios_rd16(bios, volt + 0x16); + info->type = NVBIOS_VOLT_GPIO; + info->vidmask = nvbios_rd08(bios, volt + 0x06); + info->step = nvbios_rd16(bios, volt + 0x16); + info->entry_based + !(nvbios_rd08(bios, volt + 0x4) & 0x2); } break; } diff --git a/drm/nouveau/nvkm/subdev/volt/base.c b/drm/nouveau/nvkm/subdev/volt/base.c index 6b2d753..915a605 100644 --- a/drm/nouveau/nvkm/subdev/volt/base.c +++ b/drm/nouveau/nvkm/subdev/volt/base.c @@ -112,6 +112,7 @@ nvkm_volt_set_id(struct nvkm_volt *volt, u8 id, int condition) static void nvkm_volt_parse_bios(struct nvkm_bios *bios, struct nvkm_volt *volt) { + struct nvkm_subdev *subdev = &bios->subdev; struct nvbios_volt_entry ivid; struct nvbios_volt info; u8 ver, hdr, cnt, len; @@ -119,7 +120,9 @@ nvkm_volt_parse_bios(struct nvkm_bios *bios, struct nvkm_volt *volt) int i; data = nvbios_volt_parse(bios, &ver, &hdr, &cnt, &len, &info); - if (data && info.vidmask && info.base && info.step) { + if (data && info.vidmask && info.base && info.step + && !info.entry_based) { + nvkm_debug(subdev, "found header based VIDs\n"); for (i = 0; i < info.vidmask + 1; i++) { if (info.base >= info.min && info.base <= info.max) { @@ -130,7 +133,8 @@ nvkm_volt_parse_bios(struct nvkm_bios *bios, struct nvkm_volt *volt) info.base += info.step; } volt->vid_mask = info.vidmask; - } else if (data && info.vidmask) { + } else if (data && info.vidmask && info.entry_based) { + nvkm_debug(subdev, "found entry based VIDs\n"); for (i = 0; i < cnt; i++) { data = nvbios_volt_entry_parse(bios, i, &ver, &hdr, &ivid); -- 2.8.1
Karol Herbst
2016-Apr-18 19:13 UTC
[Nouveau] [PATCH v4 03/37] volt: save the voltage range we are able to set
We shouldn't set voltages below the min or above the max voltage the gpu is able to set, so save the range. Signed-off-by: Karol Herbst <nouveau at karolherbst.de> Reviewed-by: Martin Peres <martin.peres at free.fr> Tested-by: Pierre Moreau <pierre.morrow at free.fr> --- drm/nouveau/include/nvkm/subdev/volt.h | 3 +++ drm/nouveau/nvkm/subdev/volt/base.c | 14 +++++++++++++- 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/drm/nouveau/include/nvkm/subdev/volt.h b/drm/nouveau/include/nvkm/subdev/volt.h index feff55c..b765f4f 100644 --- a/drm/nouveau/include/nvkm/subdev/volt.h +++ b/drm/nouveau/include/nvkm/subdev/volt.h @@ -12,6 +12,9 @@ struct nvkm_volt { u32 uv; u8 vid; } vid[256]; + + u32 max_uv; + u32 min_uv; }; int nvkm_volt_get(struct nvkm_volt *); diff --git a/drm/nouveau/nvkm/subdev/volt/base.c b/drm/nouveau/nvkm/subdev/volt/base.c index 915a605..b7dd3ac 100644 --- a/drm/nouveau/nvkm/subdev/volt/base.c +++ b/drm/nouveau/nvkm/subdev/volt/base.c @@ -123,6 +123,8 @@ nvkm_volt_parse_bios(struct nvkm_bios *bios, struct nvkm_volt *volt) if (data && info.vidmask && info.base && info.step && !info.entry_based) { nvkm_debug(subdev, "found header based VIDs\n"); + volt->min_uv = info.min; + volt->max_uv = info.max; for (i = 0; i < info.vidmask + 1; i++) { if (info.base >= info.min && info.base <= info.max) { @@ -135,6 +137,8 @@ nvkm_volt_parse_bios(struct nvkm_bios *bios, struct nvkm_volt *volt) volt->vid_mask = info.vidmask; } else if (data && info.vidmask && info.entry_based) { nvkm_debug(subdev, "found entry based VIDs\n"); + volt->min_uv = 0xffffffff; + volt->max_uv = 0; for (i = 0; i < cnt; i++) { data = nvbios_volt_entry_parse(bios, i, &ver, &hdr, &ivid); @@ -142,9 +146,14 @@ nvkm_volt_parse_bios(struct nvkm_bios *bios, struct nvkm_volt *volt) volt->vid[volt->vid_nr].uv = ivid.voltage; volt->vid[volt->vid_nr].vid = ivid.vid; volt->vid_nr++; + volt->min_uv = min(volt->min_uv, ivid.voltage); + volt->max_uv = max(volt->max_uv, ivid.voltage); } } volt->vid_mask = info.vidmask; + } else if (data && info.type == NVBIOS_VOLT_PWM) { + volt->min_uv = info.base; + volt->max_uv = info.base + info.pwm_range; } } @@ -185,8 +194,11 @@ nvkm_volt_ctor(const struct nvkm_volt_func *func, struct nvkm_device *device, volt->func = func; /* Assuming the non-bios device should build the voltage table later */ - if (bios) + if (bios) { nvkm_volt_parse_bios(bios, volt); + nvkm_debug(&volt->subdev, "min: %iuv max: %iuv\n", + volt->min_uv, volt->max_uv); + } if (volt->vid_nr) { for (i = 0; i < volt->vid_nr; i++) { -- 2.8.1
Karol Herbst
2016-Apr-18 19:13 UTC
[Nouveau] [PATCH v4 04/37] volt: add nvkm_volt_map_min function
this is a copy of nvkm_volt_map, which always returns the lowest possible voltage for a cstate. nvkm_volt_map will get a temperature parameter there later and also fix the voltage calculation, so that this functions will be completly different later. Signed-off-by: Karol Herbst <nouveau at karolherbst.de> Reviewed-by: Martin Peres <martin.peres at free.fr> Tested-by: Pierre Moreau <pierre.morrow at free.fr> --- drm/nouveau/include/nvkm/subdev/volt.h | 1 + drm/nouveau/nvkm/subdev/volt/base.c | 22 ++++++++++++++++++++++ 2 files changed, 23 insertions(+) diff --git a/drm/nouveau/include/nvkm/subdev/volt.h b/drm/nouveau/include/nvkm/subdev/volt.h index b765f4f..fc68825 100644 --- a/drm/nouveau/include/nvkm/subdev/volt.h +++ b/drm/nouveau/include/nvkm/subdev/volt.h @@ -17,6 +17,7 @@ struct nvkm_volt { u32 min_uv; }; +int nvkm_volt_map_min(struct nvkm_volt *volt, u8 id); int nvkm_volt_get(struct nvkm_volt *); int nvkm_volt_set_id(struct nvkm_volt *, u8 id, int condition); diff --git a/drm/nouveau/nvkm/subdev/volt/base.c b/drm/nouveau/nvkm/subdev/volt/base.c index b7dd3ac..ecf4cb4 100644 --- a/drm/nouveau/nvkm/subdev/volt/base.c +++ b/drm/nouveau/nvkm/subdev/volt/base.c @@ -65,6 +65,28 @@ nvkm_volt_set(struct nvkm_volt *volt, u32 uv) return ret; } +int +nvkm_volt_map_min(struct nvkm_volt *volt, u8 id) +{ + struct nvkm_bios *bios = volt->subdev.device->bios; + struct nvbios_vmap_entry info; + u8 ver, len; + u16 vmap; + + vmap = nvbios_vmap_entry_parse(bios, id, &ver, &len, &info); + if (vmap) { + if (info.link != 0xff) { + int ret = nvkm_volt_map_min(volt, info.link); + if (ret < 0) + return ret; + info.min += ret; + } + return info.min; + } + + return id ? id * 10000 : -ENODEV; +} + static int nvkm_volt_map(struct nvkm_volt *volt, u8 id) { -- 2.8.1
Karol Herbst
2016-Apr-18 19:13 UTC
[Nouveau] [PATCH v4 05/37] clk: don't create cstates whit voltages higher than what the gpu can do
Signed-off-by: Karol Herbst <nouveau at karolherbst.de> Reviewed-by: Martin Peres <martin.peres at free.fr> Tested-by: Pierre Moreau <pierre.morrow at free.fr> --- drm/nouveau/nvkm/subdev/clk/base.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drm/nouveau/nvkm/subdev/clk/base.c b/drm/nouveau/nvkm/subdev/clk/base.c index 7102c25..763e1bf 100644 --- a/drm/nouveau/nvkm/subdev/clk/base.c +++ b/drm/nouveau/nvkm/subdev/clk/base.c @@ -138,6 +138,7 @@ static int nvkm_cstate_new(struct nvkm_clk *clk, int idx, struct nvkm_pstate *pstate) { struct nvkm_bios *bios = clk->subdev.device->bios; + struct nvkm_volt *volt = clk->subdev.device->volt; const struct nvkm_domain *domain = clk->domains; struct nvkm_cstate *cstate = NULL; struct nvbios_cstepX cstepX; @@ -148,6 +149,9 @@ nvkm_cstate_new(struct nvkm_clk *clk, int idx, struct nvkm_pstate *pstate) if (!data) return -ENOENT; + if (volt && nvkm_volt_map_min(volt, cstepX.voltage) > volt->max_uv) + return -EINVAL; + cstate = kzalloc(sizeof(*cstate), GFP_KERNEL); if (!cstate) return -ENOMEM; -- 2.8.1
Karol Herbst
2016-Apr-18 19:13 UTC
[Nouveau] [PATCH v4 06/37] volt: parse the max voltage map entries
There are at least three "max" entries, which specify the max voltage. Because they are actually normal voltage map entries, they can also be affected by the temperature. Nvidia respects those entries and if they get changed, nvidia uses the lower voltage from both. We shouldn't exceed those voltages at any given time. v2: state what those entries do in the source v3: add the third max entry Signed-off-by: Karol Herbst <nouveau at karolherbst.de> --- drm/nouveau/include/nvkm/subdev/bios/vmap.h | 3 +++ drm/nouveau/include/nvkm/subdev/volt.h | 5 +++++ drm/nouveau/nvkm/subdev/bios/vmap.c | 10 ++++++++++ drm/nouveau/nvkm/subdev/volt/base.c | 13 +++++++++++++ 4 files changed, 31 insertions(+) diff --git a/drm/nouveau/include/nvkm/subdev/bios/vmap.h b/drm/nouveau/include/nvkm/subdev/bios/vmap.h index 6633c6d..ae2f27b 100644 --- a/drm/nouveau/include/nvkm/subdev/bios/vmap.h +++ b/drm/nouveau/include/nvkm/subdev/bios/vmap.h @@ -1,6 +1,9 @@ #ifndef __NVBIOS_VMAP_H__ #define __NVBIOS_VMAP_H__ struct nvbios_vmap { + u8 max0; + u8 max1; + u8 max2; }; u16 nvbios_vmap_table(struct nvkm_bios *, u8 *ver, u8 *hdr, u8 *cnt, u8 *len); diff --git a/drm/nouveau/include/nvkm/subdev/volt.h b/drm/nouveau/include/nvkm/subdev/volt.h index fc68825..285c6bf 100644 --- a/drm/nouveau/include/nvkm/subdev/volt.h +++ b/drm/nouveau/include/nvkm/subdev/volt.h @@ -15,6 +15,11 @@ struct nvkm_volt { u32 max_uv; u32 min_uv; + + /* max voltage map entries, might be affected by temperature */ + u8 max0_id; + u8 max1_id; + u8 max2_id; }; int nvkm_volt_map_min(struct nvkm_volt *volt, u8 id); diff --git a/drm/nouveau/nvkm/subdev/bios/vmap.c b/drm/nouveau/nvkm/subdev/bios/vmap.c index 2f13db7..f2295e1 100644 --- a/drm/nouveau/nvkm/subdev/bios/vmap.c +++ b/drm/nouveau/nvkm/subdev/bios/vmap.c @@ -61,7 +61,17 @@ nvbios_vmap_parse(struct nvkm_bios *bios, u8 *ver, u8 *hdr, u8 *cnt, u8 *len, memset(info, 0x00, sizeof(*info)); switch (!!vmap * *ver) { case 0x10: + info->max0 = 0xff; + info->max1 = 0xff; + info->max2 = 0xff; + break; case 0x20: + info->max0 = nvbios_rd08(bios, vmap + 0x7); + info->max1 = nvbios_rd08(bios, vmap + 0x8); + if (*len >= 0xc) + info->max2 = nvbios_rd08(bios, vmap + 0xc); + else + info->max2 = 0xff; break; } return vmap; diff --git a/drm/nouveau/nvkm/subdev/volt/base.c b/drm/nouveau/nvkm/subdev/volt/base.c index ecf4cb4..63fffb8 100644 --- a/drm/nouveau/nvkm/subdev/volt/base.c +++ b/drm/nouveau/nvkm/subdev/volt/base.c @@ -217,9 +217,22 @@ nvkm_volt_ctor(const struct nvkm_volt_func *func, struct nvkm_device *device, /* Assuming the non-bios device should build the voltage table later */ if (bios) { + u8 ver, hdr, cnt, len; + struct nvbios_vmap vmap; + nvkm_volt_parse_bios(bios, volt); nvkm_debug(&volt->subdev, "min: %iuv max: %iuv\n", volt->min_uv, volt->max_uv); + + if (nvbios_vmap_parse(bios, &ver, &hdr, &cnt, &len, &vmap)) { + volt->max0_id = vmap.max0; + volt->max1_id = vmap.max1; + volt->max2_id = vmap.max2; + } else { + volt->max0_id = 0xff; + volt->max1_id = 0xff; + volt->max2_id = 0xff; + } } if (volt->vid_nr) { -- 2.8.1
Karol Herbst
2016-Apr-18 19:13 UTC
[Nouveau] [PATCH v4 07/37] volt: add min_id parameter to nvkm_volt_set_id
Each pstate has its own voltage map entry like each cstate has. The voltages of those entries act as a floor value for the currently selected pstate and nvidia never sets a voltage below them. Signed-off-by: Karol Herbst <nouveau at karolherbst.de> Reviewed-by: Martin Peres <martin.peres at free.fr> --- drm/nouveau/include/nvkm/subdev/volt.h | 2 +- drm/nouveau/nvkm/subdev/clk/base.c | 6 ++++-- drm/nouveau/nvkm/subdev/volt/base.c | 5 ++++- 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/drm/nouveau/include/nvkm/subdev/volt.h b/drm/nouveau/include/nvkm/subdev/volt.h index 285c6bf..ec9d87d 100644 --- a/drm/nouveau/include/nvkm/subdev/volt.h +++ b/drm/nouveau/include/nvkm/subdev/volt.h @@ -24,7 +24,7 @@ struct nvkm_volt { int nvkm_volt_map_min(struct nvkm_volt *volt, u8 id); int nvkm_volt_get(struct nvkm_volt *); -int nvkm_volt_set_id(struct nvkm_volt *, u8 id, int condition); +int nvkm_volt_set_id(struct nvkm_volt *, u8 id, u8 min_id, int condition); int nv40_volt_new(struct nvkm_device *, int, struct nvkm_volt **); int gk104_volt_new(struct nvkm_device *, int, struct nvkm_volt **); diff --git a/drm/nouveau/nvkm/subdev/clk/base.c b/drm/nouveau/nvkm/subdev/clk/base.c index 763e1bf..e2c9fea 100644 --- a/drm/nouveau/nvkm/subdev/clk/base.c +++ b/drm/nouveau/nvkm/subdev/clk/base.c @@ -99,7 +99,8 @@ nvkm_cstate_prog(struct nvkm_clk *clk, struct nvkm_pstate *pstate, int cstatei) } if (volt) { - ret = nvkm_volt_set_id(volt, cstate->voltage, +1); + ret = nvkm_volt_set_id(volt, cstate->voltage, + pstate->base.voltage, +1); if (ret && ret != -ENODEV) { nvkm_error(subdev, "failed to raise voltage: %d\n", ret); return ret; @@ -113,7 +114,8 @@ nvkm_cstate_prog(struct nvkm_clk *clk, struct nvkm_pstate *pstate, int cstatei) } if (volt) { - ret = nvkm_volt_set_id(volt, cstate->voltage, -1); + ret = nvkm_volt_set_id(volt, cstate->voltage, + pstate->base.voltage, -1); if (ret && ret != -ENODEV) nvkm_error(subdev, "failed to lower voltage: %d\n", ret); } diff --git a/drm/nouveau/nvkm/subdev/volt/base.c b/drm/nouveau/nvkm/subdev/volt/base.c index 63fffb8..1690c1c 100644 --- a/drm/nouveau/nvkm/subdev/volt/base.c +++ b/drm/nouveau/nvkm/subdev/volt/base.c @@ -110,7 +110,7 @@ nvkm_volt_map(struct nvkm_volt *volt, u8 id) } int -nvkm_volt_set_id(struct nvkm_volt *volt, u8 id, int condition) +nvkm_volt_set_id(struct nvkm_volt *volt, u8 id, u8 min_id, int condition) { int ret; @@ -123,6 +123,9 @@ nvkm_volt_set_id(struct nvkm_volt *volt, u8 id, int condition) if (!condition || prev < 0 || (condition < 0 && ret < prev) || (condition > 0 && ret > prev)) { + int min = nvkm_volt_map(volt, min_id); + if (min >= 0) + ret = max(min, ret); ret = nvkm_volt_set(volt, ret); } else { ret = 0; -- 2.8.1
before clocking to a cstate, we have to check if the voltage is within the allowed range. Signed-off-by: Karol Herbst <nouveau at karolherbst.de> --- drm/nouveau/include/nvkm/subdev/volt.h | 1 + drm/nouveau/nvkm/subdev/volt/base.c | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/drm/nouveau/include/nvkm/subdev/volt.h b/drm/nouveau/include/nvkm/subdev/volt.h index ec9d87d..870d212 100644 --- a/drm/nouveau/include/nvkm/subdev/volt.h +++ b/drm/nouveau/include/nvkm/subdev/volt.h @@ -22,6 +22,7 @@ struct nvkm_volt { u8 max2_id; }; +int nvkm_volt_map(struct nvkm_volt *volt, u8 id); int nvkm_volt_map_min(struct nvkm_volt *volt, u8 id); int nvkm_volt_get(struct nvkm_volt *); int nvkm_volt_set_id(struct nvkm_volt *, u8 id, u8 min_id, int condition); diff --git a/drm/nouveau/nvkm/subdev/volt/base.c b/drm/nouveau/nvkm/subdev/volt/base.c index 1690c1c..6fb9d2e 100644 --- a/drm/nouveau/nvkm/subdev/volt/base.c +++ b/drm/nouveau/nvkm/subdev/volt/base.c @@ -87,7 +87,7 @@ nvkm_volt_map_min(struct nvkm_volt *volt, u8 id) return id ? id * 10000 : -ENODEV; } -static int +int nvkm_volt_map(struct nvkm_volt *volt, u8 id) { struct nvkm_bios *bios = volt->subdev.device->bios; -- 2.8.1
Karol Herbst
2016-Apr-18 19:13 UTC
[Nouveau] [PATCH v4 09/37] clk: add index field to nvkm_cstate
Signed-off-by: Karol Herbst <nouveau at karolherbst.de> --- drm/nouveau/include/nvkm/subdev/clk.h | 1 + drm/nouveau/nvkm/subdev/clk/base.c | 1 + 2 files changed, 2 insertions(+) diff --git a/drm/nouveau/include/nvkm/subdev/clk.h b/drm/nouveau/include/nvkm/subdev/clk.h index fb54417..6226f0d 100644 --- a/drm/nouveau/include/nvkm/subdev/clk.h +++ b/drm/nouveau/include/nvkm/subdev/clk.h @@ -52,6 +52,7 @@ struct nvkm_cstate { struct list_head head; u8 voltage; u32 domain[nv_clk_src_max]; + u8 cstate; }; struct nvkm_pstate { diff --git a/drm/nouveau/nvkm/subdev/clk/base.c b/drm/nouveau/nvkm/subdev/clk/base.c index e2c9fea..0eb4c16 100644 --- a/drm/nouveau/nvkm/subdev/clk/base.c +++ b/drm/nouveau/nvkm/subdev/clk/base.c @@ -160,6 +160,7 @@ nvkm_cstate_new(struct nvkm_clk *clk, int idx, struct nvkm_pstate *pstate) *cstate = pstate->base; cstate->voltage = cstepX.voltage; + cstate->cstate = idx; while (domain && domain->name != nv_clk_src_max) { if (domain->flags & NVKM_CLK_DOM_FLAG_CORE) { -- 2.8.1
Karol Herbst
2016-Apr-18 19:13 UTC
[Nouveau] [PATCH v4 10/37] add daemon to compare nouveau with blob voltage
this tool can be run alongside the nvidia driver to print information about the current p/cstate, which voltage was set by nvidia and what nouveau would set in the same situation. v4: parse default options Signed-off-by: Karol Herbst <nouveau at karolherbst.de> --- bin/nv_cmp_volt.c | 139 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 139 insertions(+) create mode 100644 bin/nv_cmp_volt.c diff --git a/bin/nv_cmp_volt.c b/bin/nv_cmp_volt.c new file mode 100644 index 0000000..c63d91b --- /dev/null +++ b/bin/nv_cmp_volt.c @@ -0,0 +1,139 @@ +/* + * Copyright 2016 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 <nvif/client.h> +#include <nvif/device.h> +#include <nvif/class.h> + +#include <nvkm/subdev/volt.h> + +#include "util.h" + +int +main(int argc, char **argv) +{ + struct nvif_client if_client; + struct nvif_device if_device; + struct nvkm_clk *clk; + struct nvkm_volt *volt; + struct nvkm_device *device; + int ret, c; + int old_voltage = 0, old_nouveau_voltage = 0, old_pstate = 0; + int old_cstate = 0, old_temp = 0; + + while ((c = getopt(argc, argv, U_GETOPT)) != -1) { + switch (c) { + default: + if (!u_option(c)) + return 1; + break; + } + } + + ret = u_device("lib", argv[0], "error", true, true, + (1ULL << NVKM_SUBDEV_CLK) | +// (1ULL << NVKM_SUBDEV_FUSE) | + (1ULL << NVKM_SUBDEV_GPIO) | +// (1ULL << NVKM_SUBDEV_I2C) | + (1ULL << NVKM_SUBDEV_PCI) | +// (1ULL << NVKM_SUBDEV_THERM) | +// (1ULL << NVKM_SUBDEV_TIMER) | + (1ULL << NVKM_SUBDEV_VBIOS) | + (1ULL << NVKM_SUBDEV_VOLT), + 0x00000000, &if_client, &if_device); + + if (ret < 0) + return ret; + + device = nvxx_device(&if_device); + clk = device->clk; +// therm = device->therm; + volt = device->volt; + + printf("current voltage (µV), expected voltage (µV), abs diff (µV)," + "rel diff nouveau/nvidia (%%), pstate, cstate, temperature" + "(°C)\n"); + while (true) { + int gpc_clock = nvkm_clk_read(clk, nv_clk_src_gpc); + int mem_clock = nvkm_clk_read(clk, nv_clk_src_mem); + struct nvkm_pstate *pstate = NULL, *best_pstate = NULL; + struct nvkm_cstate *cstate = NULL, *best_cstate = NULL; + int mem_err, gpc_err; + int new_voltage, new_nouveau_voltage, new_pstate, new_cstate; + int new_temp; + + list_for_each_entry(pstate, &clk->states, head) { + list_for_each_entry(cstate, &pstate->list, head) { + if (!best_pstate) { + best_pstate = pstate; + best_cstate = cstate; + gpc_err = abs(cstate->domain[nv_clk_src_gpc] - gpc_clock); + mem_err = abs(cstate->domain[nv_clk_src_mem] - mem_clock); + continue; + } + + if (abs(cstate->domain[nv_clk_src_gpc] - gpc_clock) <= gpc_err && + abs(cstate->domain[nv_clk_src_mem] - mem_clock) <= mem_err) { + best_pstate = pstate; + best_cstate = cstate; + gpc_err = abs(cstate->domain[nv_clk_src_gpc] - gpc_clock); + mem_err = abs(cstate->domain[nv_clk_src_mem] - mem_clock); + } + } + + if (!best_pstate) { + best_pstate = pstate; + mem_err = abs(cstate->domain[nv_clk_src_mem] - mem_clock); + continue; + } else if (!best_cstate && abs(pstate->base.domain[nv_clk_src_mem] - mem_clock) <= mem_err) { + best_pstate = pstate; + mem_err = abs(cstate->domain[nv_clk_src_mem] - mem_clock); + } + } + + if (!best_cstate) + best_cstate = &best_pstate->base; + + new_voltage = nvkm_volt_get(volt); + new_temp = nvkm_rd32(device, 0x20400);//nvkm_therm_temp_get(therm); + new_nouveau_voltage = max(nvkm_volt_map(volt, best_cstate->voltage), nvkm_volt_map(volt, best_pstate->base.voltage)); + new_pstate = best_pstate->pstate; + new_cstate = best_cstate->cstate; + + if (new_voltage != old_voltage || new_nouveau_voltage != old_nouveau_voltage || new_pstate != old_pstate || new_cstate != old_cstate || new_temp != old_temp) { + old_voltage = new_voltage; + old_nouveau_voltage = new_nouveau_voltage; + old_pstate = new_pstate; + old_cstate = new_cstate; + old_temp = new_temp; + printf("%i, %i, %i, %f, %i, %i, %i\n", new_voltage, + new_nouveau_voltage, new_nouveau_voltage - new_voltage, + 100 * (double)new_nouveau_voltage / new_voltage, + new_pstate, new_cstate, new_temp); + } + usleep(100000); + } + + return 0; +} -- 2.8.1
Karol Herbst
2016-Apr-18 19:13 UTC
[Nouveau] [PATCH v4 11/37] volt: add temperature parameter to nvkm_volt_map
the voltage entries actually may map to a different voltage depending on the current temperature. v2: only read the temperatue when actually needed Signed-off-by: Karol Herbst <nouveau at karolherbst.de> --- bin/nv_cmp_volt.c | 2 +- drm/nouveau/include/nvkm/subdev/volt.h | 2 +- drm/nouveau/nvkm/subdev/volt/base.c | 14 ++++++++++---- 3 files changed, 12 insertions(+), 6 deletions(-) diff --git a/bin/nv_cmp_volt.c b/bin/nv_cmp_volt.c index c63d91b..34147b9 100644 --- a/bin/nv_cmp_volt.c +++ b/bin/nv_cmp_volt.c @@ -117,7 +117,7 @@ main(int argc, char **argv) new_voltage = nvkm_volt_get(volt); new_temp = nvkm_rd32(device, 0x20400);//nvkm_therm_temp_get(therm); - new_nouveau_voltage = max(nvkm_volt_map(volt, best_cstate->voltage), nvkm_volt_map(volt, best_pstate->base.voltage)); + new_nouveau_voltage = max(nvkm_volt_map(volt, best_cstate->voltage, new_temp), nvkm_volt_map(volt, best_pstate->base.voltage, new_temp)); new_pstate = best_pstate->pstate; new_cstate = best_cstate->cstate; diff --git a/drm/nouveau/include/nvkm/subdev/volt.h b/drm/nouveau/include/nvkm/subdev/volt.h index 870d212..f223577 100644 --- a/drm/nouveau/include/nvkm/subdev/volt.h +++ b/drm/nouveau/include/nvkm/subdev/volt.h @@ -22,7 +22,7 @@ struct nvkm_volt { u8 max2_id; }; -int nvkm_volt_map(struct nvkm_volt *volt, u8 id); +int nvkm_volt_map(struct nvkm_volt *volt, u8 id, u8 temperature); int nvkm_volt_map_min(struct nvkm_volt *volt, u8 id); int nvkm_volt_get(struct nvkm_volt *); int nvkm_volt_set_id(struct nvkm_volt *, u8 id, u8 min_id, int condition); diff --git a/drm/nouveau/nvkm/subdev/volt/base.c b/drm/nouveau/nvkm/subdev/volt/base.c index 6fb9d2e..d72bd4a 100644 --- a/drm/nouveau/nvkm/subdev/volt/base.c +++ b/drm/nouveau/nvkm/subdev/volt/base.c @@ -26,6 +26,7 @@ #include <subdev/bios.h> #include <subdev/bios/vmap.h> #include <subdev/bios/volt.h> +#include <subdev/therm.h> int nvkm_volt_get(struct nvkm_volt *volt) @@ -88,7 +89,7 @@ nvkm_volt_map_min(struct nvkm_volt *volt, u8 id) } int -nvkm_volt_map(struct nvkm_volt *volt, u8 id) +nvkm_volt_map(struct nvkm_volt *volt, u8 id, u8 temp) { struct nvkm_bios *bios = volt->subdev.device->bios; struct nvbios_vmap_entry info; @@ -98,7 +99,7 @@ nvkm_volt_map(struct nvkm_volt *volt, u8 id) vmap = nvbios_vmap_entry_parse(bios, id, &ver, &len, &info); if (vmap) { if (info.link != 0xff) { - int ret = nvkm_volt_map(volt, info.link); + int ret = nvkm_volt_map(volt, info.link, temp); if (ret < 0) return ret; info.min += ret; @@ -112,18 +113,23 @@ nvkm_volt_map(struct nvkm_volt *volt, u8 id) int nvkm_volt_set_id(struct nvkm_volt *volt, u8 id, u8 min_id, int condition) { + struct nvkm_therm *therm = volt->subdev.device->therm; int ret; + int temp = 0; if (volt->func->set_id) return volt->func->set_id(volt, id, condition); - ret = nvkm_volt_map(volt, id); + if (therm) + temp = nvkm_therm_temp_get(therm); + + ret = nvkm_volt_map(volt, id, max(temp, 0)); if (ret >= 0) { int prev = nvkm_volt_get(volt); if (!condition || prev < 0 || (condition < 0 && ret < prev) || (condition > 0 && ret > prev)) { - int min = nvkm_volt_map(volt, min_id); + int min = nvkm_volt_map(volt, min_id, max(temp, 0)); if (min >= 0) ret = max(min, ret); ret = nvkm_volt_set(volt, ret); -- 2.8.1
now the cstatei parameter can be used of the nvkm_cstate_prog function to select a specific cstate. -1 is a magic value, which will always select the highest currently possible cstate Signed-off-by: Karol Herbst <nouveau at karolherbst.de> --- drm/nouveau/nvkm/subdev/clk/base.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/drm/nouveau/nvkm/subdev/clk/base.c b/drm/nouveau/nvkm/subdev/clk/base.c index 0eb4c16..fecf58f 100644 --- a/drm/nouveau/nvkm/subdev/clk/base.c +++ b/drm/nouveau/nvkm/subdev/clk/base.c @@ -85,7 +85,15 @@ nvkm_cstate_prog(struct nvkm_clk *clk, struct nvkm_pstate *pstate, int cstatei) int ret; if (!list_empty(&pstate->list)) { - cstate = list_entry(pstate->list.prev, typeof(*cstate), head); + if (cstatei == -1) + cstate = list_entry(pstate->list.prev, typeof(*cstate), + head); + else { + list_for_each_entry(cstate, &pstate->list, head) { + if (cstate->cstate == cstatei) + break; + } + } } else { cstate = &pstate->base; } @@ -207,7 +215,7 @@ nvkm_pstate_prog(struct nvkm_clk *clk, int pstatei) ram->func->tidy(ram); } - return nvkm_cstate_prog(clk, pstate, 0); + return nvkm_cstate_prog(clk, pstate, -1); } static void -- 2.8.1
Karol Herbst
2016-Apr-18 19:13 UTC
[Nouveau] [PATCH v4 13/37] clk: respect voltage limits in nvkm_cstate_prog
we should never allow to select a cstate which current voltage (depending on the temperature) is higher than 1. the max volt entries in the voltage map table 2. what tha gpu actually can volt to. this resolves all remaining volting errors on fermi and newer. v3: use find_best for all cstates before actually trying add nvkm_cstate_get function to get cstate by index Signed-off-by: Karol Herbst <nouveau at karolherbst.de> --- drm/nouveau/nvkm/subdev/clk/base.c | 83 +++++++++++++++++++++++++++++++++----- 1 file changed, 74 insertions(+), 9 deletions(-) diff --git a/drm/nouveau/nvkm/subdev/clk/base.c b/drm/nouveau/nvkm/subdev/clk/base.c index fecf58f..21f6369 100644 --- a/drm/nouveau/nvkm/subdev/clk/base.c +++ b/drm/nouveau/nvkm/subdev/clk/base.c @@ -74,6 +74,78 @@ nvkm_clk_adjust(struct nvkm_clk *clk, bool adjust, /****************************************************************************** * C-States *****************************************************************************/ +static bool +nvkm_cstate_valid(struct nvkm_clk *clk, struct nvkm_cstate *cstate, u32 max_volt, int temp) +{ + struct nvkm_volt *volt = clk->subdev.device->volt; + int voltage; + + if (!volt) + return true; + + voltage = nvkm_volt_map(volt, cstate->voltage, temp); + if (voltage < 0) + return false; + return voltage <= min(max_volt, volt->max_uv) && + voltage >= volt->min_uv; +} + +static struct nvkm_cstate * +nvkm_cstate_find_best(struct nvkm_clk *clk, struct nvkm_pstate *pstate, + struct nvkm_cstate *start) +{ + struct nvkm_device *device = clk->subdev.device; + struct nvkm_therm *therm = device->therm; + struct nvkm_volt *volt = device->volt; + struct nvkm_cstate *cstate; + int max_volt, temp = 0; + + if (!pstate || !start) + return NULL; + + if (!volt) + return start; + + if (therm) { + /* ignore error code */ + temp = max(0, nvkm_therm_temp_get(therm)); + } + + max_volt = volt->max_uv; + if (volt->max0_id != 0xff) + max_volt = min(max_volt, + nvkm_volt_map(volt, volt->max0_id, temp)); + if (volt->max1_id != 0xff) + max_volt = min(max_volt, + nvkm_volt_map(volt, volt->max1_id, temp)); + if (volt->max2_id != 0xff) + max_volt = min(max_volt, + nvkm_volt_map(volt, volt->max2_id, temp)); + + for (cstate = start; &cstate->head != &pstate->list; + cstate = list_entry(cstate->head.prev, typeof(*cstate), head)) { + if (nvkm_cstate_valid(clk, cstate, max_volt, temp)) + break; + } + + return cstate; +} + +static struct nvkm_cstate * +nvkm_cstate_get(struct nvkm_clk *clk, struct nvkm_pstate *pstate, int cstatei) +{ + struct nvkm_cstate *cstate; + if (cstatei == -1) + return list_entry(pstate->list.prev, typeof(*cstate), head); + else { + list_for_each_entry(cstate, &pstate->list, head) { + if (cstate->cstate == cstatei) + return cstate; + } + } + return NULL; +} + static int nvkm_cstate_prog(struct nvkm_clk *clk, struct nvkm_pstate *pstate, int cstatei) { @@ -85,15 +157,8 @@ nvkm_cstate_prog(struct nvkm_clk *clk, struct nvkm_pstate *pstate, int cstatei) int ret; if (!list_empty(&pstate->list)) { - if (cstatei == -1) - cstate = list_entry(pstate->list.prev, typeof(*cstate), - head); - else { - list_for_each_entry(cstate, &pstate->list, head) { - if (cstate->cstate == cstatei) - break; - } - } + cstate = nvkm_cstate_get(clk, pstate, cstatei); + cstate = nvkm_cstate_find_best(clk, pstate, cstate); } else { cstate = &pstate->base; } -- 2.8.1
Karol Herbst
2016-Apr-18 19:13 UTC
[Nouveau] [PATCH v4 14/37] bios: add parsing of BASE CLOCK table
this table contains three important clocks: base clock: this is the non boosted max clock tdp clock: the clock at wich the vbios guarentees the TDP won't ever be exceeded at max load (seems to be always the same as the base clock, but behaves differently). boost clock: the avg clock the gpu will stay boosted to. It doesn't seem to affect the behaviour of the nvidia driver at all though. v2: make clear that base/boost/tdp fields are ids Signed-off-by: Karol Herbst <nouveau at karolherbst.de> Reviewed-by: Martin Peres <martin.peres at free.fr> --- drm/nouveau/include/nvkm/subdev/bios/baseclock.h | 24 +++++++ drm/nouveau/nvkm/subdev/bios/Kbuild | 1 + drm/nouveau/nvkm/subdev/bios/baseclock.c | 82 ++++++++++++++++++++++++ 3 files changed, 107 insertions(+) create mode 100644 drm/nouveau/include/nvkm/subdev/bios/baseclock.h create mode 100644 drm/nouveau/nvkm/subdev/bios/baseclock.c diff --git a/drm/nouveau/include/nvkm/subdev/bios/baseclock.h b/drm/nouveau/include/nvkm/subdev/bios/baseclock.h new file mode 100644 index 0000000..ed6a3e6 --- /dev/null +++ b/drm/nouveau/include/nvkm/subdev/bios/baseclock.h @@ -0,0 +1,24 @@ +#ifndef __NVBIOS_BASECLOCK_H__ +#define __NVBIOS_BASECLOCK_H__ +struct nvbios_baseclk_header { + u16 offset; + + u8 version; + u8 hlen; + u8 ecount; + u8 elen; + u8 scount; + u8 slen; + + u8 base_id; + u8 boost_id; + u8 tdp_id; +}; +struct nvbios_baseclk_entry { + u8 pstate; + u16 clock_mhz; +}; +int nvbios_baseclock_parse(struct nvkm_bios *, struct nvbios_baseclk_header *); +int nvbios_baseclock_entry(struct nvkm_bios *, struct nvbios_baseclk_header *, + u8 idx, struct nvbios_baseclk_entry *); +#endif diff --git a/drm/nouveau/nvkm/subdev/bios/Kbuild b/drm/nouveau/nvkm/subdev/bios/Kbuild index dbcb0ef..6cb3beb 100644 --- a/drm/nouveau/nvkm/subdev/bios/Kbuild +++ b/drm/nouveau/nvkm/subdev/bios/Kbuild @@ -1,4 +1,5 @@ nvkm-y += nvkm/subdev/bios/base.o +nvkm-y += nvkm/subdev/bios/baseclock.o nvkm-y += nvkm/subdev/bios/bit.o nvkm-y += nvkm/subdev/bios/boost.o nvkm-y += nvkm/subdev/bios/conn.o diff --git a/drm/nouveau/nvkm/subdev/bios/baseclock.c b/drm/nouveau/nvkm/subdev/bios/baseclock.c new file mode 100644 index 0000000..3c961e2 --- /dev/null +++ b/drm/nouveau/nvkm/subdev/bios/baseclock.c @@ -0,0 +1,82 @@ +/* + * Copyright 2016 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/baseclock.h> + +static u16 +nvbios_baseclock_offset(struct nvkm_bios *b) +{ + struct bit_entry bit_P; + + if (!bit_entry(b, 'P', &bit_P)) { + if (bit_P.version == 2) + return nvbios_rd16(b, bit_P.offset + 0x38); + } + + return 0x0000; +} + +int +nvbios_baseclock_parse(struct nvkm_bios *b, struct nvbios_baseclk_header *h) +{ + if (!h) + return -EINVAL; + + h->offset = nvbios_baseclock_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->slen = nvbios_rd08(b, h->offset + 0x3); + h->scount = nvbios_rd08(b, h->offset + 0x4); + h->ecount = nvbios_rd08(b, h->offset + 0x5); + + h->base_id = nvbios_rd08(b, h->offset + 0x0f); + h->boost_id = nvbios_rd08(b, h->offset + 0x10); + h->tdp_id = nvbios_rd08(b, h->offset + 0x11); + return 0; + default: + return -EINVAL; + } +} + +int +nvbios_baseclock_entry(struct nvkm_bios *b, struct nvbios_baseclk_header *h, + u8 idx, struct nvbios_baseclk_entry *e) +{ + u16 offset; + + if (!e || !h || idx > h->ecount) + return -EINVAL; + + offset = h->offset + h->hlen + idx * (h->elen + (h->slen * h->scount)); + e->pstate = nvbios_rd08(b, offset); + e->clock_mhz = nvbios_rd16(b, offset + 0x5); + return 0; +} -- 2.8.1
Karol Herbst
2016-Apr-18 19:13 UTC
[Nouveau] [PATCH v4 15/37] clk: allow boosting only when NvBoost is set
0: base clock from the vbios is max clock 1: boost only to boost clock from the vbios (default) 2: boost to max clock available v2: moved into nvkm_cstate_valid v4: check the existence of the clocks before limiting Signed-off-by: Karol Herbst <nouveau at karolherbst.de> Reviewed-by: Martin Peres <martin.peres at free.fr> --- drm/nouveau/include/nvkm/subdev/clk.h | 9 ++++++++- drm/nouveau/nvkm/subdev/clk/base.c | 33 ++++++++++++++++++++++++++++++++- drm/nouveau/nvkm/subdev/clk/gf100.c | 2 +- drm/nouveau/nvkm/subdev/clk/gk104.c | 2 +- 4 files changed, 42 insertions(+), 4 deletions(-) diff --git a/drm/nouveau/include/nvkm/subdev/clk.h b/drm/nouveau/include/nvkm/subdev/clk.h index 6226f0d..99ee05c 100644 --- a/drm/nouveau/include/nvkm/subdev/clk.h +++ b/drm/nouveau/include/nvkm/subdev/clk.h @@ -68,7 +68,8 @@ struct nvkm_pstate { struct nvkm_domain { enum nv_clk_src name; u8 bios; /* 0xff for none */ -#define NVKM_CLK_DOM_FLAG_CORE 0x01 +#define NVKM_CLK_DOM_FLAG_CORE 0x01 +#define NVKM_CLK_DOM_FLAG_BASECLK 0x02 u8 flags; const char *mname; int mdiv; @@ -98,6 +99,12 @@ struct nvkm_clk { int dstate; /* display adjustment (min+) */ bool allow_reclock; +#define NVKM_CLK_BOOST_NONE 0x0 +#define NVKM_CLK_BOOST_AVG 0x1 +#define NVKM_CLK_BOOST_FULL 0x2 + u8 boost_mode; + u32 base_khz; + u32 boost_khz; /*XXX: die, these are here *only* to support the completely * bat-shit insane what-was-nouveau_hw.c code diff --git a/drm/nouveau/nvkm/subdev/clk/base.c b/drm/nouveau/nvkm/subdev/clk/base.c index 21f6369..a9a3666 100644 --- a/drm/nouveau/nvkm/subdev/clk/base.c +++ b/drm/nouveau/nvkm/subdev/clk/base.c @@ -24,6 +24,7 @@ #include "priv.h" #include <subdev/bios.h> +#include <subdev/bios/baseclock.h> #include <subdev/bios/boost.h> #include <subdev/bios/cstep.h> #include <subdev/bios/perf.h> @@ -77,9 +78,25 @@ nvkm_clk_adjust(struct nvkm_clk *clk, bool adjust, static bool nvkm_cstate_valid(struct nvkm_clk *clk, struct nvkm_cstate *cstate, u32 max_volt, int temp) { + const struct nvkm_domain *domain = clk->domains; struct nvkm_volt *volt = clk->subdev.device->volt; int voltage; + while (domain && domain->name != nv_clk_src_max) { + if (domain->flags & NVKM_CLK_DOM_FLAG_BASECLK) { + u32 freq = cstate->domain[domain->name]; + switch (clk->boost_mode) { + case NVKM_CLK_BOOST_NONE: + if (clk->base_khz && freq > clk->base_khz) + return false; + case NVKM_CLK_BOOST_AVG: + if (clk->boost_khz && freq > clk->boost_khz) + return false; + } + } + domain++; + } + if (!volt) return true; @@ -641,10 +658,24 @@ int nvkm_clk_ctor(const struct nvkm_clk_func *func, struct nvkm_device *device, int index, bool allow_reclock, struct nvkm_clk *clk) { + struct nvkm_subdev *subdev = &clk->subdev; + struct nvkm_bios *bios = device->bios; int ret, idx, arglen; const char *mode; + struct nvbios_baseclk_header h; + + nvkm_subdev_ctor(&nvkm_clk, device, index, subdev); + + clk->boost_mode = nvkm_longopt(device->cfgopt, "NvBoost", + NVKM_CLK_BOOST_AVG); + if (bios && !nvbios_baseclock_parse(bios, &h)) { + struct nvbios_baseclk_entry base, boost; + if (!nvbios_baseclock_entry(bios, &h, h.boost_id, &boost)) + clk->boost_khz = boost.clock_mhz * 1000; + if (!nvbios_baseclock_entry(bios, &h, h.base_id, &base)) + clk->base_khz = base.clock_mhz * 1000; + } - nvkm_subdev_ctor(&nvkm_clk, device, index, &clk->subdev); clk->func = func; INIT_LIST_HEAD(&clk->states); clk->domains = func->domains; diff --git a/drm/nouveau/nvkm/subdev/clk/gf100.c b/drm/nouveau/nvkm/subdev/clk/gf100.c index 78c449b..71b7c9f 100644 --- a/drm/nouveau/nvkm/subdev/clk/gf100.c +++ b/drm/nouveau/nvkm/subdev/clk/gf100.c @@ -443,7 +443,7 @@ gf100_clk = { { nv_clk_src_hubk06 , 0x00 }, { nv_clk_src_hubk01 , 0x01 }, { nv_clk_src_copy , 0x02 }, - { nv_clk_src_gpc , 0x03, 0, "core", 2000 }, + { nv_clk_src_gpc , 0x03, NVKM_CLK_DOM_FLAG_BASECLK, "core", 2000 }, { nv_clk_src_rop , 0x04 }, { nv_clk_src_mem , 0x05, 0, "memory", 1000 }, { nv_clk_src_vdec , 0x06 }, diff --git a/drm/nouveau/nvkm/subdev/clk/gk104.c b/drm/nouveau/nvkm/subdev/clk/gk104.c index 975c401..639234f 100644 --- a/drm/nouveau/nvkm/subdev/clk/gk104.c +++ b/drm/nouveau/nvkm/subdev/clk/gk104.c @@ -485,7 +485,7 @@ gk104_clk = { .domains = { { nv_clk_src_crystal, 0xff }, { nv_clk_src_href , 0xff }, - { nv_clk_src_gpc , 0x00, NVKM_CLK_DOM_FLAG_CORE, "core", 2000 }, + { nv_clk_src_gpc , 0x00, NVKM_CLK_DOM_FLAG_CORE | NVKM_CLK_DOM_FLAG_BASECLK, "core", 2000 }, { nv_clk_src_hubk07 , 0x01, NVKM_CLK_DOM_FLAG_CORE }, { nv_clk_src_rop , 0x02, NVKM_CLK_DOM_FLAG_CORE }, { nv_clk_src_mem , 0x03, 0, "memory", 500 }, -- 2.8.1
Karol Herbst
2016-Apr-18 19:13 UTC
[Nouveau] [PATCH v4 16/37] volt: don't require perfect fit
if we calculate the voltage in the table right, we get all kinds of values, which never fit the hardware steps, so we use the closest higher value the hardware can do. v3: simplify the implementation Signed-off-by: Karol Herbst <nouveau at karolherbst.de> --- drm/nouveau/nvkm/subdev/volt/base.c | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/drm/nouveau/nvkm/subdev/volt/base.c b/drm/nouveau/nvkm/subdev/volt/base.c index d72bd4a..028c6e2 100644 --- a/drm/nouveau/nvkm/subdev/volt/base.c +++ b/drm/nouveau/nvkm/subdev/volt/base.c @@ -51,18 +51,30 @@ static int nvkm_volt_set(struct nvkm_volt *volt, u32 uv) { struct nvkm_subdev *subdev = &volt->subdev; - int i, ret = -EINVAL; + int i, ret = -EINVAL, best_err = uv, best = -1; if (volt->func->volt_set) return volt->func->volt_set(volt, uv); for (i = 0; i < volt->vid_nr; i++) { - if (volt->vid[i].uv == uv) { - ret = volt->func->vid_set(volt, volt->vid[i].vid); - nvkm_debug(subdev, "set %duv: %d\n", uv, ret); + int err = volt->vid[i].uv - uv; + if (err < 0 || err > best_err) + continue; + + best_err = err; + best = i; + if (best_err == 0) break; - } } + + if (best == -1) { + nvkm_error(subdev, "couldn't set %iuv\n", uv); + return ret; + } + + ret = volt->func->vid_set(volt, volt->vid[best].vid); + nvkm_debug(subdev, "set req %duv to %duv: %d\n", uv, + volt->vid[best].uv, ret); return ret; } -- 2.8.1
Karol Herbst
2016-Apr-18 19:13 UTC
[Nouveau] [PATCH v4 17/37] bios/vmap: unk0 field is the mode
this selects which formula is used to calculate the voltage. depending on the value, the entry maps to a different voltage and even enables if the temperature has any effect or not. This is easy to observe with the binary driver. Signed-off-by: Karol Herbst <nouveau at karolherbst.de> --- drm/nouveau/include/nvkm/subdev/bios/vmap.h | 2 +- drm/nouveau/nvkm/subdev/bios/vmap.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drm/nouveau/include/nvkm/subdev/bios/vmap.h b/drm/nouveau/include/nvkm/subdev/bios/vmap.h index ae2f27b..8fa1294 100644 --- a/drm/nouveau/include/nvkm/subdev/bios/vmap.h +++ b/drm/nouveau/include/nvkm/subdev/bios/vmap.h @@ -11,7 +11,7 @@ u16 nvbios_vmap_parse(struct nvkm_bios *, u8 *ver, u8 *hdr, u8 *cnt, u8 *len, struct nvbios_vmap *); struct nvbios_vmap_entry { - u8 unk0; + u8 mode; u8 link; u32 min; u32 max; diff --git a/drm/nouveau/nvkm/subdev/bios/vmap.c b/drm/nouveau/nvkm/subdev/bios/vmap.c index f2295e1..32bd8b1 100644 --- a/drm/nouveau/nvkm/subdev/bios/vmap.c +++ b/drm/nouveau/nvkm/subdev/bios/vmap.c @@ -105,7 +105,7 @@ nvbios_vmap_entry_parse(struct nvkm_bios *bios, int idx, u8 *ver, u8 *len, info->arg[2] = nvbios_rd32(bios, vmap + 0x10); break; case 0x20: - info->unk0 = nvbios_rd08(bios, vmap + 0x00); + info->mode = nvbios_rd08(bios, vmap + 0x00); info->link = nvbios_rd08(bios, vmap + 0x01); info->min = nvbios_rd32(bios, vmap + 0x02); info->max = nvbios_rd32(bios, vmap + 0x06); -- 2.8.1
Signed-off-by: Karol Herbst <nouveau at karolherbst.de> --- bin/nv_cmp_volt.c | 2 +- drm/nouveau/include/nvkm/subdev/volt.h | 2 ++ drm/nouveau/nvkm/subdev/volt/base.c | 12 ++++++++++++ drm/nouveau/nvkm/subdev/volt/priv.h | 1 + 4 files changed, 16 insertions(+), 1 deletion(-) diff --git a/bin/nv_cmp_volt.c b/bin/nv_cmp_volt.c index 34147b9..e61056c 100644 --- a/bin/nv_cmp_volt.c +++ b/bin/nv_cmp_volt.c @@ -53,7 +53,7 @@ main(int argc, char **argv) ret = u_device("lib", argv[0], "error", true, true, (1ULL << NVKM_SUBDEV_CLK) | -// (1ULL << NVKM_SUBDEV_FUSE) | + (1ULL << NVKM_SUBDEV_FUSE) | (1ULL << NVKM_SUBDEV_GPIO) | // (1ULL << NVKM_SUBDEV_I2C) | (1ULL << NVKM_SUBDEV_PCI) | diff --git a/drm/nouveau/include/nvkm/subdev/volt.h b/drm/nouveau/include/nvkm/subdev/volt.h index f223577..4cb0292 100644 --- a/drm/nouveau/include/nvkm/subdev/volt.h +++ b/drm/nouveau/include/nvkm/subdev/volt.h @@ -20,6 +20,8 @@ struct nvkm_volt { u8 max0_id; u8 max1_id; u8 max2_id; + + int speedo; }; int nvkm_volt_map(struct nvkm_volt *volt, u8 id, u8 temperature); diff --git a/drm/nouveau/nvkm/subdev/volt/base.c b/drm/nouveau/nvkm/subdev/volt/base.c index 028c6e2..cecfac6 100644 --- a/drm/nouveau/nvkm/subdev/volt/base.c +++ b/drm/nouveau/nvkm/subdev/volt/base.c @@ -201,6 +201,14 @@ nvkm_volt_parse_bios(struct nvkm_bios *bios, struct nvkm_volt *volt) } static int +nvkm_volt_speedo_read(struct nvkm_volt *volt) +{ + if (volt->func->speedo_read) + return volt->func->speedo_read(volt); + return -EINVAL; +} + +static int nvkm_volt_init(struct nvkm_subdev *subdev) { struct nvkm_volt *volt = nvkm_volt(subdev); @@ -262,6 +270,10 @@ nvkm_volt_ctor(const struct nvkm_volt_func *func, struct nvkm_device *device, volt->vid[i].vid, volt->vid[i].uv); } } + + volt->speedo = nvkm_volt_speedo_read(volt); + if (volt->speedo > 0) + nvkm_debug(&volt->subdev, "speedo %x\n", volt->speedo); } int diff --git a/drm/nouveau/nvkm/subdev/volt/priv.h b/drm/nouveau/nvkm/subdev/volt/priv.h index d5140d9..9b34e9f 100644 --- a/drm/nouveau/nvkm/subdev/volt/priv.h +++ b/drm/nouveau/nvkm/subdev/volt/priv.h @@ -14,6 +14,7 @@ struct nvkm_volt_func { int (*vid_get)(struct nvkm_volt *); int (*vid_set)(struct nvkm_volt *, u8 vid); int (*set_id)(struct nvkm_volt *, u8 id, int condition); + int (*speedo_read)(struct nvkm_volt *); }; int nvkm_voltgpio_init(struct nvkm_volt *); -- 2.8.1
Karol Herbst
2016-Apr-18 19:13 UTC
[Nouveau] [PATCH v4 19/37] volt: add gf100 subdev with speedo
Signed-off-by: Karol Herbst <nouveau at karolherbst.de> --- drm/nouveau/include/nvkm/subdev/volt.h | 1 + drm/nouveau/nvkm/engine/device/base.c | 17 +++++----- drm/nouveau/nvkm/subdev/volt/Kbuild | 1 + drm/nouveau/nvkm/subdev/volt/gf100.c | 59 ++++++++++++++++++++++++++++++++++ 4 files changed, 70 insertions(+), 8 deletions(-) create mode 100644 drm/nouveau/nvkm/subdev/volt/gf100.c diff --git a/drm/nouveau/include/nvkm/subdev/volt.h b/drm/nouveau/include/nvkm/subdev/volt.h index 4cb0292..25588c7 100644 --- a/drm/nouveau/include/nvkm/subdev/volt.h +++ b/drm/nouveau/include/nvkm/subdev/volt.h @@ -30,6 +30,7 @@ int nvkm_volt_get(struct nvkm_volt *); int nvkm_volt_set_id(struct nvkm_volt *, u8 id, u8 min_id, int condition); int nv40_volt_new(struct nvkm_device *, int, struct nvkm_volt **); +int gf100_volt_new(struct nvkm_device *, int, struct nvkm_volt **); int gk104_volt_new(struct nvkm_device *, int, struct nvkm_volt **); int gk20a_volt_new(struct nvkm_device *, int, struct nvkm_volt **); int gm20b_volt_new(struct nvkm_device *, int, struct nvkm_volt **); diff --git a/drm/nouveau/nvkm/engine/device/base.c b/drm/nouveau/nvkm/engine/device/base.c index a364efe..528780c 100644 --- a/drm/nouveau/nvkm/engine/device/base.c +++ b/drm/nouveau/nvkm/engine/device/base.c @@ -1357,7 +1357,7 @@ nvc0_chipset = { .pmu = gf100_pmu_new, .therm = gt215_therm_new, .timer = nv41_timer_new, - .volt = nv40_volt_new, + .volt = gf100_volt_new, .ce[0] = gf100_ce_new, .ce[1] = gf100_ce_new, .disp = gt215_disp_new, @@ -1394,7 +1394,7 @@ nvc1_chipset = { .pmu = gf100_pmu_new, .therm = gt215_therm_new, .timer = nv41_timer_new, - .volt = nv40_volt_new, + .volt = gf100_volt_new, .ce[0] = gf100_ce_new, .disp = gt215_disp_new, .dma = gf100_dma_new, @@ -1430,7 +1430,7 @@ nvc3_chipset = { .pmu = gf100_pmu_new, .therm = gt215_therm_new, .timer = nv41_timer_new, - .volt = nv40_volt_new, + .volt = gf100_volt_new, .ce[0] = gf100_ce_new, .disp = gt215_disp_new, .dma = gf100_dma_new, @@ -1466,7 +1466,7 @@ nvc4_chipset = { .pmu = gf100_pmu_new, .therm = gt215_therm_new, .timer = nv41_timer_new, - .volt = nv40_volt_new, + .volt = gf100_volt_new, .ce[0] = gf100_ce_new, .ce[1] = gf100_ce_new, .disp = gt215_disp_new, @@ -1503,7 +1503,7 @@ nvc8_chipset = { .pmu = gf100_pmu_new, .therm = gt215_therm_new, .timer = nv41_timer_new, - .volt = nv40_volt_new, + .volt = gf100_volt_new, .ce[0] = gf100_ce_new, .ce[1] = gf100_ce_new, .disp = gt215_disp_new, @@ -1540,7 +1540,7 @@ nvce_chipset = { .pmu = gf100_pmu_new, .therm = gt215_therm_new, .timer = nv41_timer_new, - .volt = nv40_volt_new, + .volt = gf100_volt_new, .ce[0] = gf100_ce_new, .ce[1] = gf100_ce_new, .disp = gt215_disp_new, @@ -1577,7 +1577,7 @@ nvcf_chipset = { .pmu = gf100_pmu_new, .therm = gt215_therm_new, .timer = nv41_timer_new, - .volt = nv40_volt_new, + .volt = gf100_volt_new, .ce[0] = gf100_ce_new, .disp = gt215_disp_new, .dma = gf100_dma_new, @@ -1612,6 +1612,7 @@ nvd7_chipset = { .pci = gf106_pci_new, .therm = gf119_therm_new, .timer = nv41_timer_new, + .volt = gf100_volt_new, .ce[0] = gf100_ce_new, .disp = gf119_disp_new, .dma = gf119_dma_new, @@ -1647,7 +1648,7 @@ nvd9_chipset = { .pmu = gf119_pmu_new, .therm = gf119_therm_new, .timer = nv41_timer_new, - .volt = nv40_volt_new, + .volt = gf100_volt_new, .ce[0] = gf100_ce_new, .disp = gf119_disp_new, .dma = gf119_dma_new, diff --git a/drm/nouveau/nvkm/subdev/volt/Kbuild b/drm/nouveau/nvkm/subdev/volt/Kbuild index c340762..bcd179b 100644 --- a/drm/nouveau/nvkm/subdev/volt/Kbuild +++ b/drm/nouveau/nvkm/subdev/volt/Kbuild @@ -1,6 +1,7 @@ nvkm-y += nvkm/subdev/volt/base.o nvkm-y += nvkm/subdev/volt/gpio.o nvkm-y += nvkm/subdev/volt/nv40.o +nvkm-y += nvkm/subdev/volt/gf100.o nvkm-y += nvkm/subdev/volt/gk104.o nvkm-y += nvkm/subdev/volt/gk20a.o nvkm-y += nvkm/subdev/volt/gm20b.o diff --git a/drm/nouveau/nvkm/subdev/volt/gf100.c b/drm/nouveau/nvkm/subdev/volt/gf100.c new file mode 100644 index 0000000..16dc0f1 --- /dev/null +++ b/drm/nouveau/nvkm/subdev/volt/gf100.c @@ -0,0 +1,59 @@ +/* + * Copyright 2016 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 "priv.h" + +#include <subdev/fuse.h> + +static int +gf100_volt_speedo_read(struct nvkm_volt *volt) +{ + struct nvkm_device *device = volt->subdev.device; + struct nvkm_fuse *fuse = device->fuse; + + if (!fuse) + return -EINVAL; + + return nvkm_fuse_read(fuse, 0x1cc); +} + +static const struct nvkm_volt_func +gf100_volt = { + .vid_get = nvkm_voltgpio_get, + .vid_set = nvkm_voltgpio_set, + .speedo_read = gf100_volt_speedo_read, +}; + +int +gf100_volt_new(struct nvkm_device *device, int index, struct nvkm_volt **pvolt) +{ + struct nvkm_volt *volt; + int ret; + + ret = nvkm_volt_new_(&gf100_volt, device, index, &volt); + *pvolt = volt; + if (ret) + return ret; + + return nvkm_voltgpio_init(volt); +} -- 2.8.1
I am sure that those are a bit different, but while testing the biggest error compared to nvidia was -1.5%. Without this change we are most of the time around 10% below nvidias voltage, so this change causes no harm and improves the situation a lot already. These coefficients were REed by modifing the voltage map entries and by calculating the set voltage back until I was able to forecast which voltage nvidia sets for a given voltage map entry. v4: use better coefficients and speedo Signed-off-by: Karol Herbst <nouveau at karolherbst.de> --- drm/nouveau/nvkm/subdev/volt/base.c | 38 +++++++++++++++++++++++++++++++++++-- 1 file changed, 36 insertions(+), 2 deletions(-) diff --git a/drm/nouveau/nvkm/subdev/volt/base.c b/drm/nouveau/nvkm/subdev/volt/base.c index cecfac6..5e35d96 100644 --- a/drm/nouveau/nvkm/subdev/volt/base.c +++ b/drm/nouveau/nvkm/subdev/volt/base.c @@ -110,13 +110,47 @@ nvkm_volt_map(struct nvkm_volt *volt, u8 id, u8 temp) vmap = nvbios_vmap_entry_parse(bios, id, &ver, &len, &info); if (vmap) { + s64 result; + + if (volt->speedo < 0) + return volt->speedo; + + if (ver == 0x10 || (ver == 0x20 && info.mode == 0)) { + result = (s64)info.arg[0] / 10; + result += ((s64)info.arg[1] * volt->speedo) / 10; + result += ((s64)info.arg[2] * volt->speedo * volt->speedo) / 100000; + } else if (ver == 0x20) { + switch (info.mode) { + /* 0x0 handled above! */ + case 0x1: + result = ((s64)info.arg[0] * 15625) >> 18; + result += ((s64)info.arg[1] * volt->speedo * 15625) >> 18; + result += ((s64)info.arg[2] * temp * 15625) >> 10; + result += ((s64)info.arg[3] * volt->speedo * temp * 15625) >> 18; + result += ((s64)info.arg[4] * volt->speedo * volt->speedo * 15625) >> 30; + result += ((s64)info.arg[5] * temp * temp * 15625) >> 18; + break; + case 0x3: + result = (info.min + info.max) / 2; + break; + case 0x2: + default: + result = info.min; + break; + } + } else { + return -ENODEV; + } + + result = min(max(result, (s64)info.min), (s64)info.max); + if (info.link != 0xff) { int ret = nvkm_volt_map(volt, info.link, temp); if (ret < 0) return ret; - info.min += ret; + result += ret; } - return info.min; + return result; } return id ? id * 10000 : -ENODEV; -- 2.8.1
Karol Herbst
2016-Apr-18 19:13 UTC
[Nouveau] [PATCH v4 21/37] clk: save the max clock we can set
Signed-off-by: Karol Herbst <nouveau at karolherbst.de> --- drm/nouveau/include/nvkm/subdev/clk.h | 1 + drm/nouveau/nvkm/subdev/clk/base.c | 2 ++ 2 files changed, 3 insertions(+) diff --git a/drm/nouveau/include/nvkm/subdev/clk.h b/drm/nouveau/include/nvkm/subdev/clk.h index 99ee05c..61d99fd 100644 --- a/drm/nouveau/include/nvkm/subdev/clk.h +++ b/drm/nouveau/include/nvkm/subdev/clk.h @@ -105,6 +105,7 @@ struct nvkm_clk { u8 boost_mode; u32 base_khz; u32 boost_khz; + u32 max_khz; /*XXX: die, these are here *only* to support the completely * bat-shit insane what-was-nouveau_hw.c code diff --git a/drm/nouveau/nvkm/subdev/clk/base.c b/drm/nouveau/nvkm/subdev/clk/base.c index a9a3666..1ca25dd 100644 --- a/drm/nouveau/nvkm/subdev/clk/base.c +++ b/drm/nouveau/nvkm/subdev/clk/base.c @@ -257,6 +257,8 @@ nvkm_cstate_new(struct nvkm_clk *clk, int idx, struct nvkm_pstate *pstate) u32 freq = nvkm_clk_adjust(clk, true, pstate->pstate, domain->bios, cstepX.freq); cstate->domain[domain->name] = freq; + if (domain->flags & NVKM_CLK_DOM_FLAG_BASECLK) + clk->max_khz = max(clk->max_khz, freq); } domain++; } -- 2.8.1
Karol Herbst
2016-Apr-18 19:13 UTC
[Nouveau] [PATCH v4 22/37] clk: rename nvkm_pstate_calc to nvkm_clk_update
this function will be used to update the current clock state. This will happen for various reasons: * temperature changes (may change cstate and/or voltage) * user changes boost mode * load changes v2: add wait parameter Signed-off-by: Karol Herbst <nouveau at karolherbst.de> --- drm/nouveau/include/nvkm/subdev/clk.h | 1 + drm/nouveau/nvkm/subdev/clk/base.c | 46 ++++++++++++++++++++--------------- 2 files changed, 28 insertions(+), 19 deletions(-) diff --git a/drm/nouveau/include/nvkm/subdev/clk.h b/drm/nouveau/include/nvkm/subdev/clk.h index 61d99fd..77d94c1 100644 --- a/drm/nouveau/include/nvkm/subdev/clk.h +++ b/drm/nouveau/include/nvkm/subdev/clk.h @@ -120,6 +120,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 *, int req, int rel); +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 1ca25dd..bfc6a49 100644 --- a/drm/nouveau/nvkm/subdev/clk/base.c +++ b/drm/nouveau/nvkm/subdev/clk/base.c @@ -274,11 +274,14 @@ static int nvkm_pstate_prog(struct nvkm_clk *clk, int pstatei) { struct nvkm_subdev *subdev = &clk->subdev; - struct nvkm_ram *ram = subdev->device->fb->ram; + struct nvkm_fb *fb = subdev->device->fb; struct nvkm_pci *pci = subdev->device->pci; struct nvkm_pstate *pstate; int ret, idx = 0; + if (pstatei == -1) + return 0; + list_for_each_entry(pstate, &clk->states, head) { if (idx++ == pstatei) break; @@ -289,7 +292,8 @@ nvkm_pstate_prog(struct nvkm_clk *clk, int pstatei) nvkm_pcie_set_link(pci, pstate->pcie_speed, pstate->pcie_width); - if (ram && ram->func->calc) { + if (fb && fb->ram && fb->ram->func->calc) { + struct nvkm_ram *ram = fb->ram; int khz = pstate->base.domain[nv_clk_src_mem]; do { ret = ram->func->calc(ram, khz); @@ -303,11 +307,11 @@ 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; - int pstate; + int pstate, ret; if (!atomic_xchg(&clk->waiting, 0)) return; @@ -327,21 +331,25 @@ nvkm_pstate_work(struct work_struct *work) } nvkm_trace(subdev, "-> %d\n", pstate); - if (pstate != clk->pstate) { - int ret = nvkm_pstate_prog(clk, pstate); - if (ret) { - nvkm_error(subdev, "error setting pstate %d: %d\n", - pstate, ret); - } + ret = nvkm_pstate_prog(clk, pstate); + if (ret) { + nvkm_error(subdev, "error setting pstate %d: %d\n", + pstate, ret); } wake_up_all(&clk->wait); 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) @@ -531,7 +539,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; } @@ -543,7 +551,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 @@ -553,7 +561,7 @@ nvkm_clk_tstate(struct nvkm_clk *clk, int req, int rel) if ( rel) clk->tstate += rel; clk->tstate = min(clk->tstate, 0); clk->tstate = max(clk->tstate, -(clk->state_nr - 1)); - return nvkm_pstate_calc(clk, true); + return nvkm_clk_update(clk, true); } int @@ -563,7 +571,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 @@ -571,7 +579,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; } @@ -626,7 +634,7 @@ nvkm_clk_init(struct nvkm_subdev *subdev) clk->tstate = 0; clk->dstate = 0; clk->pstate = -1; - nvkm_pstate_calc(clk, true); + nvkm_clk_update(clk, true); return 0; } @@ -685,7 +693,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.8.1
Karol Herbst
2016-Apr-18 19:13 UTC
[Nouveau] [PATCH v4 23/37] nvif: add boost info and set operations
Signed-off-by: Karol Herbst <nouveau at karolherbst.de> Reviewed-by: Martin Peres <martin.peres at free.fr> --- drm/nouveau/include/nvif/if0001.h | 15 ++++++++++ drm/nouveau/nvkm/engine/device/ctrl.c | 55 +++++++++++++++++++++++++++++++++++ 2 files changed, 70 insertions(+) diff --git a/drm/nouveau/include/nvif/if0001.h b/drm/nouveau/include/nvif/if0001.h index bd5b641..e4acd19 100644 --- a/drm/nouveau/include/nvif/if0001.h +++ b/drm/nouveau/include/nvif/if0001.h @@ -4,6 +4,8 @@ #define NVIF_CONTROL_PSTATE_INFO 0x00 #define NVIF_CONTROL_PSTATE_ATTR 0x01 #define NVIF_CONTROL_PSTATE_USER 0x02 +#define NVIF_CONTROL_BOOST_INFO 0x03 +#define NVIF_CONTROL_BOOST_SET 0x04 struct nvif_control_pstate_info_v0 { __u8 version; @@ -43,4 +45,17 @@ struct nvif_control_pstate_user_v0 { __s8 pwrsrc; /* in: target power source */ __u8 pad03[5]; }; + +struct nvif_control_boost_info_v0 { + __u8 version; + __u8 mode; + __u16 base_mhz; + __u16 boost_mhz; + __u16 max_mhz; +}; + +struct nvif_control_boost_set_v0 { + __u8 version; + __u8 mode; +}; #endif diff --git a/drm/nouveau/nvkm/engine/device/ctrl.c b/drm/nouveau/nvkm/engine/device/ctrl.c index b0ece71..039e8a4 100644 --- a/drm/nouveau/nvkm/engine/device/ctrl.c +++ b/drm/nouveau/nvkm/engine/device/ctrl.c @@ -167,6 +167,57 @@ nvkm_control_mthd_pstate_user(struct nvkm_control *ctrl, void *data, u32 size) } static int +nvkm_control_mthd_boost_info(struct nvkm_control *ctrl, void *data, u32 size) +{ + union { + struct nvif_control_boost_info_v0 v0; + } *args = data; + struct nvkm_clk *clk = ctrl->device->clk; + int ret = -ENOSYS; + + nvif_ioctl(&ctrl->object, "control boost info size %d\n", size); + if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, false))) { + nvif_ioctl(&ctrl->object, "control boost info vers %d\n", + args->v0.version); + } else + return ret; + + if (!clk) + return -ENODEV; + + args->v0.mode = clk->boost_mode; + args->v0.base_mhz = clk->base_khz / 2000; + args->v0.boost_mhz = clk->boost_khz / 2000; + args->v0.max_mhz = clk->max_khz / 2000; + return 0; +} + +static int +nvkm_control_mthd_boost_set(struct nvkm_control *ctrl, void *data, u32 size) +{ + union { + struct nvif_control_boost_set_v0 v0; + } *args = data; + struct nvkm_clk *clk = ctrl->device->clk; + int ret = -ENOSYS; + + nvif_ioctl(&ctrl->object, "control boost set size %d\n", size); + if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, false))) { + nvif_ioctl(&ctrl->object, "control boost set vers %d\n", + args->v0.version); + } else + return ret; + + if (!clk) + return -ENODEV; + + if (args->v0.mode > 2) + return -EINVAL; + clk->boost_mode = args->v0.mode; + return nvkm_clk_update(clk, true); +} + +static int nvkm_control_mthd(struct nvkm_object *object, u32 mthd, void *data, u32 size) { struct nvkm_control *ctrl = nvkm_control(object); @@ -177,6 +228,10 @@ nvkm_control_mthd(struct nvkm_object *object, u32 mthd, void *data, u32 size) return nvkm_control_mthd_pstate_attr(ctrl, data, size); case NVIF_CONTROL_PSTATE_USER: return nvkm_control_mthd_pstate_user(ctrl, data, size); + case NVIF_CONTROL_BOOST_INFO: + return nvkm_control_mthd_boost_info(ctrl, data, size); + case NVIF_CONTROL_BOOST_SET: + return nvkm_control_mthd_boost_set(ctrl, data, size); default: break; } -- 2.8.1
Karol Herbst
2016-Apr-18 19:13 UTC
[Nouveau] [PATCH v4 24/37] debugfs: add boost interface to change the boost_mode
Signed-off-by: Karol Herbst <nouveau at karolherbst.de> Reviewed-by: Martin Peres <martin.peres at free.fr> --- drm/nouveau/nouveau_debugfs.c | 76 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 76 insertions(+) diff --git a/drm/nouveau/nouveau_debugfs.c b/drm/nouveau/nouveau_debugfs.c index 3d0dc19..31b309f 100644 --- a/drm/nouveau/nouveau_debugfs.c +++ b/drm/nouveau/nouveau_debugfs.c @@ -180,6 +180,81 @@ static const struct file_operations nouveau_pstate_fops = { .write = nouveau_debugfs_pstate_set, }; +static void +nouveau_debugfs_boost_get_entry(struct seq_file *m, u8 mode, u8 entry, u16 value) +{ + if (value) { + if (mode == entry) + seq_printf(m, "*%i", entry); + else + seq_printf(m, " %i", entry); + seq_printf(m, ": %u MHz\n", value); + } +} + +static int +nouveau_debugfs_boost_get(struct seq_file *m, void *data) +{ + struct drm_info_node *node = (struct drm_info_node *) m->private; + struct nouveau_debugfs *debugfs = nouveau_debugfs(node->minor->dev); + struct nvif_object *ctrl = &debugfs->ctrl; + struct nvif_control_boost_info_v0 info = {}; + int ret; + + ret = nvif_mthd(ctrl, NVIF_CONTROL_BOOST_INFO, &info, sizeof(info)); + if (ret) + return ret; + + nouveau_debugfs_boost_get_entry(m, info.mode, 0, info.base_mhz); + nouveau_debugfs_boost_get_entry(m, info.mode, 1, info.boost_mhz); + nouveau_debugfs_boost_get_entry(m, info.mode, 2, info.max_mhz); + return 0; +} + +static ssize_t +nouveau_debugfs_boost_set(struct file *file, const char __user *ubuf, + size_t len, loff_t *offp) +{ + struct seq_file *m = file->private_data; + struct drm_info_node *node = (struct drm_info_node *) m->private; + struct nouveau_debugfs *debugfs = nouveau_debugfs(node->minor->dev); + struct nvif_object *ctrl = &debugfs->ctrl; + struct nvif_control_boost_set_v0 args = {}; + char buf[3] = {}; + int ret; + u8 value; + + if (len >= sizeof(buf)) + return -EINVAL; + + if (copy_from_user(buf, ubuf, len)) + return -EFAULT; + + ret = kstrtou8(buf, 10, &value); + if (ret) + return ret; + + args.mode = value; + ret = nvif_mthd(ctrl, NVIF_CONTROL_BOOST_SET, &args, sizeof(args)); + if (ret) + return ret; + + return len; +} + +static int +nouveau_debugfs_boost_open(struct inode *inode, struct file *file) +{ + return single_open(file, nouveau_debugfs_boost_get, inode->i_private); +} + +static const struct file_operations nouveau_boost_fops = { + .owner = THIS_MODULE, + .open = nouveau_debugfs_boost_open, + .read = seq_read, + .write = nouveau_debugfs_boost_set, +}; + static struct drm_info_list nouveau_debugfs_list[] = { { "vbios.rom", nouveau_debugfs_vbios_image, 0, NULL }, }; @@ -189,6 +264,7 @@ static const struct nouveau_debugfs_files { const char *name; const struct file_operations *fops; } nouveau_debugfs_files[] = { + {"boost", &nouveau_boost_fops}, {"pstate", &nouveau_pstate_fops}, }; -- 2.8.1
Karol Herbst
2016-Apr-18 19:13 UTC
[Nouveau] [PATCH v4 25/37] clk: remove dstate and tstate
we won't need them, because we will adjust the clocks depending on engine loads later on anyway. It also simplifies the clocking logic. Signed-off-by: Karol Herbst <nouveau at karolherbst.de> --- drm/nouveau/include/nvkm/subdev/clk.h | 4 ---- drm/nouveau/nvkm/subdev/clk/base.c | 28 ++-------------------------- 2 files changed, 2 insertions(+), 30 deletions(-) diff --git a/drm/nouveau/include/nvkm/subdev/clk.h b/drm/nouveau/include/nvkm/subdev/clk.h index 77d94c1..db52e65 100644 --- a/drm/nouveau/include/nvkm/subdev/clk.h +++ b/drm/nouveau/include/nvkm/subdev/clk.h @@ -95,8 +95,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 tstate; /* thermal adjustment (max-) */ - int dstate; /* display adjustment (min+) */ bool allow_reclock; #define NVKM_CLK_BOOST_NONE 0x0 @@ -118,8 +116,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 *, int req, int rel); int nvkm_clk_update(struct nvkm_clk *, bool wait); int nv04_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 bfc6a49..3867ab7 100644 --- a/drm/nouveau/nvkm/subdev/clk/base.c +++ b/drm/nouveau/nvkm/subdev/clk/base.c @@ -317,15 +317,13 @@ 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 D %d\n", + nvkm_trace(subdev, "P %d PWR %d U(AC) %d U(DC) %d A %d\n", clk->pstate, clk->pwrsrc, clk->ustate_ac, clk->ustate_dc, - clk->astate, clk->tstate, clk->dstate); + clk->astate); 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 + clk->tstate); - pstate = max(pstate, clk->dstate); } else { pstate = clk->pstate = -1; } @@ -554,26 +552,6 @@ nvkm_clk_astate(struct nvkm_clk *clk, int req, int rel, bool wait) return nvkm_clk_update(clk, wait); } -int -nvkm_clk_tstate(struct nvkm_clk *clk, int req, int rel) -{ - if (!rel) clk->tstate = req; - if ( rel) clk->tstate += rel; - clk->tstate = min(clk->tstate, 0); - clk->tstate = max(clk->tstate, -(clk->state_nr - 1)); - return nvkm_clk_update(clk, true); -} - -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) { @@ -631,8 +609,6 @@ nvkm_clk_init(struct nvkm_subdev *subdev) return clk->func->init(clk); clk->astate = clk->state_nr - 1; - clk->tstate = 0; - clk->dstate = 0; clk->pstate = -1; nvkm_clk_update(clk, true); return 0; -- 2.8.1
Karol Herbst
2016-Apr-18 19:14 UTC
[Nouveau] [PATCH v4 26/37] 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 <nouveau at karolherbst.de> --- 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 8894fee..0c0feec 100644 --- a/drm/nouveau/nvkm/subdev/therm/base.c +++ b/drm/nouveau/nvkm/subdev/therm/base.c @@ -92,7 +92,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); @@ -102,11 +101,9 @@ nvkm_therm_update(struct nvkm_therm *therm, int mode) switch (mode) { case NVKM_THERM_CTRL_MANUAL: - nvkm_timer_alarm_cancel(tmr, &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) { @@ -119,18 +116,16 @@ nvkm_therm_update(struct nvkm_therm *therm, int mode) case NVBIOS_THERM_FAN_OTHER: if (therm->cstate) duty = therm->cstate; - poll = false; break; } immd = false; break; case NVKM_THERM_CTRL_NONE: default: - nvkm_timer_alarm_cancel(tmr, &therm->alarm); - poll = false; + break; } - if (list_empty(&therm->alarm.head) && poll) + if (list_empty(&therm->alarm.head)) nvkm_timer_alarm(tmr, 1000000000ULL, &therm->alarm); spin_unlock_irqrestore(&therm->lock, flags); -- 2.8.1
Karol Herbst
2016-Apr-18 19:14 UTC
[Nouveau] [PATCH v4 27/37] clk: make pstate a pointer to nvkm_pstate
we will access the current set cstate at least every second and this safes us some CPU cycles looking them up every second. Signed-off-by: Karol Herbst <nouveau at karolherbst.de> --- drm/nouveau/include/nvkm/subdev/clk.h | 2 +- drm/nouveau/nvkm/engine/device/ctrl.c | 5 ++++- drm/nouveau/nvkm/subdev/clk/base.c | 12 ++++++++---- drm/nouveau/nvkm/subdev/pmu/gk20a.c | 23 +++++++---------------- 4 files changed, 20 insertions(+), 22 deletions(-) diff --git a/drm/nouveau/include/nvkm/subdev/clk.h b/drm/nouveau/include/nvkm/subdev/clk.h index db52e65..4fb2c1b 100644 --- a/drm/nouveau/include/nvkm/subdev/clk.h +++ b/drm/nouveau/include/nvkm/subdev/clk.h @@ -91,7 +91,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 039e8a4..cb85266 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 = -1; } 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 3867ab7..762dfe2 100644 --- a/drm/nouveau/nvkm/subdev/clk/base.c +++ b/drm/nouveau/nvkm/subdev/clk/base.c @@ -288,7 +288,7 @@ nvkm_pstate_prog(struct nvkm_clk *clk, int pstatei) } 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); @@ -317,15 +317,19 @@ 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 = -1; nvkm_trace(subdev, "P %d PWR %d U(AC) %d U(DC) %d A %d\n", - clk->pstate, clk->pwrsrc, clk->ustate_ac, clk->ustate_dc, + pstate, clk->pwrsrc, clk->ustate_ac, clk->ustate_dc, clk->astate); pstate = clk->pwrsrc ? clk->ustate_ac : clk->ustate_dc; if (clk->state_nr && pstate != -1) { pstate = (pstate < 0) ? clk->astate : pstate; } else { - pstate = clk->pstate = -1; + pstate = -1; } nvkm_trace(subdev, "-> %d\n", pstate); @@ -609,7 +613,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; 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 f996d90..6f0d290 100644 --- a/drm/nouveau/nvkm/subdev/pmu/gk20a.c +++ b/drm/nouveau/nvkm/subdev/pmu/gk20a.c @@ -57,24 +57,21 @@ gk20a_pmu_dvfs_target(struct gk20a_pmu *pmu, int *state) } static int -gk20a_pmu_dvfs_get_cur_state(struct gk20a_pmu *pmu, int *state) -{ - struct nvkm_clk *clk = pmu->base.subdev.device->clk; - - *state = clk->pstate; - return 0; -} - -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)); @@ -150,12 +147,6 @@ gk20a_pmu_dvfs_work(struct nvkm_alarm *alarm) nvkm_trace(subdev, "utilization = %d %%, avg_load = %d %%\n", utilization, data->avg_load); - ret = gk20a_pmu_dvfs_get_cur_state(pmu, &state); - if (ret) { - nvkm_warn(subdev, "failed to get current state\n"); - goto resched; - } - 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.8.1
Karol Herbst
2016-Apr-18 19:14 UTC
[Nouveau] [PATCH v4 28/37] clk: hold information about the current cstate status
Signed-off-by: Karol Herbst <nouveau at karolherbst.de> --- drm/nouveau/include/nvkm/subdev/clk.h | 5 +++++ drm/nouveau/nvkm/subdev/clk/base.c | 32 +++++++++++++++++++++++++------- 2 files changed, 30 insertions(+), 7 deletions(-) diff --git a/drm/nouveau/include/nvkm/subdev/clk.h b/drm/nouveau/include/nvkm/subdev/clk.h index 4fb2c1b..6deda96 100644 --- a/drm/nouveau/include/nvkm/subdev/clk.h +++ b/drm/nouveau/include/nvkm/subdev/clk.h @@ -95,6 +95,11 @@ 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 *set_cstate; +#define NVKM_CLK_CSTATE_DEFAULT -1 +#define NVKM_CLK_CSTATE_BASE -2 +#define NVKM_CLK_CSTATE_HIGHEST -3 + int exp_cstate; 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 762dfe2..23f4cfe 100644 --- a/drm/nouveau/nvkm/subdev/clk/base.c +++ b/drm/nouveau/nvkm/subdev/clk/base.c @@ -152,9 +152,14 @@ static struct nvkm_cstate * nvkm_cstate_get(struct nvkm_clk *clk, struct nvkm_pstate *pstate, int cstatei) { struct nvkm_cstate *cstate; - if (cstatei == -1) + switch (cstatei) { + case NVKM_CLK_CSTATE_HIGHEST: return list_entry(pstate->list.prev, 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->cstate == cstatei) return cstate; @@ -173,6 +178,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); @@ -199,6 +207,7 @@ nvkm_cstate_prog(struct nvkm_clk *clk, struct nvkm_pstate *pstate, int cstatei) ret = clk->func->calc(clk, cstate); if (ret == 0) { + clk->set_cstate = cstate; ret = clk->func->prog(clk); clk->func->tidy(clk); } @@ -303,7 +312,7 @@ nvkm_pstate_prog(struct nvkm_clk *clk, int pstatei) ram->func->tidy(ram); } - return nvkm_cstate_prog(clk, pstate, -1); + return nvkm_cstate_prog(clk, pstate, clk->exp_cstate); } static void @@ -321,9 +330,9 @@ nvkm_clk_update_work(struct work_struct *work) pstate = clk->pstate->pstate; else pstate = -1; - nvkm_trace(subdev, "P %d PWR %d U(AC) %d U(DC) %d A %d\n", + nvkm_trace(subdev, "P %d PWR %d U(AC) %d U(DC) %d A %d C %d\n", pstate, clk->pwrsrc, clk->ustate_ac, clk->ustate_dc, - clk->astate); + clk->astate, clk->exp_cstate); pstate = clk->pwrsrc ? clk->ustate_ac : clk->ustate_dc; if (clk->state_nr && pstate != -1) { @@ -541,6 +550,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_cstate = NVKM_CLK_CSTATE_HIGHEST; return nvkm_clk_update(clk, true); } return ret; @@ -553,6 +563,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_cstate = NVKM_CLK_CSTATE_BASE; return nvkm_clk_update(clk, wait); } @@ -614,6 +625,8 @@ nvkm_clk_init(struct nvkm_subdev *subdev) clk->astate = clk->state_nr - 1; clk->pstate = NULL; + clk->exp_cstate = NVKM_CLK_CSTATE_DEFAULT; + clk->set_cstate = NULL; nvkm_clk_update(clk, true); return 0; } @@ -698,15 +711,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_cstate = 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_cstate = 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_cstate = NVKM_CLK_CSTATE_HIGHEST; + } return 0; } -- 2.8.1
Karol Herbst
2016-Apr-18 19:14 UTC
[Nouveau] [PATCH v4 29/37] 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 safe us the trouble iterating over the entire pstate list Signed-off-by: Karol Herbst <nouveau at karolherbst.de> --- drm/nouveau/nouveau_debugfs.c | 6 ++--- drm/nouveau/nvkm/subdev/clk/base.c | 49 +++++++++++--------------------------- 2 files changed, 17 insertions(+), 38 deletions(-) diff --git a/drm/nouveau/nouveau_debugfs.c b/drm/nouveau/nouveau_debugfs.c index 31b309f..334b472 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 23f4cfe..7f86e41 100644 --- a/drm/nouveau/nvkm/subdev/clk/base.c +++ b/drm/nouveau/nvkm/subdev/clk/base.c @@ -280,23 +280,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 == -1) + if (pstateid == -1) 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); @@ -496,30 +499,6 @@ 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) { int ret = 1; @@ -533,23 +512,23 @@ 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; + return ret; } int nvkm_clk_ustate(struct nvkm_clk *clk, int req, int pwr) { - int ret = nvkm_clk_ustate_update(clk, req); + int ret = req; if (ret >= 0) { - if (ret -= 2, pwr) clk->ustate_ac = ret; - else clk->ustate_dc = ret; + if (pwr) clk->ustate_ac = ret; + else clk->ustate_dc = ret; clk->exp_cstate = NVKM_CLK_CSTATE_HIGHEST; return nvkm_clk_update(clk, true); } @@ -623,7 +602,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 = -1; clk->pstate = NULL; clk->exp_cstate = NVKM_CLK_CSTATE_DEFAULT; clk->set_cstate = NULL; -- 2.8.1
Karol Herbst
2016-Apr-18 19:14 UTC
[Nouveau] [PATCH v4 30/37] clk: seperate the locking from the implementation in nvkm_clk_update
Signed-off-by: Karol Herbst <nouveau at karolherbst.de> --- drm/nouveau/nvkm/subdev/clk/base.c | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/drm/nouveau/nvkm/subdev/clk/base.c b/drm/nouveau/nvkm/subdev/clk/base.c index 7f86e41..d6f239f 100644 --- a/drm/nouveau/nvkm/subdev/clk/base.c +++ b/drm/nouveau/nvkm/subdev/clk/base.c @@ -319,14 +319,11 @@ nvkm_pstate_prog(struct nvkm_clk *clk, int pstateid) } static void -nvkm_clk_update_work(struct work_struct *work) +nvkm_clk_update_impl(struct nvkm_clk *clk) { - struct nvkm_clk *clk = container_of(work, typeof(*clk), work); struct nvkm_subdev *subdev = &clk->subdev; int pstate, ret; - if (!atomic_xchg(&clk->waiting, 0)) - return; clk->pwrsrc = power_supply_is_system_supplied(); if (clk->pstate) @@ -350,6 +347,17 @@ nvkm_clk_update_work(struct work_struct *work) nvkm_error(subdev, "error setting pstate %d: %d\n", pstate, ret); } +} + +static void +nvkm_clk_update_work(struct work_struct *work) +{ + struct nvkm_clk *clk = container_of(work, typeof(*clk), work); + + if (!atomic_xchg(&clk->waiting, 0)) + return; + + nvkm_clk_update_impl(clk); wake_up_all(&clk->wait); nvkm_notify_get(&clk->pwrsrc_ntfy); -- 2.8.1
Karol Herbst
2016-Apr-18 19:14 UTC
[Nouveau] [PATCH v4 31/37] clk: split out update code to nv40
this code will change for gf100 and newer Signed-off-by: Karol Herbst <nouveau at karolherbst.de> --- drm/nouveau/nvkm/subdev/clk/base.c | 14 ++++++-------- drm/nouveau/nvkm/subdev/clk/g84.c | 1 + drm/nouveau/nvkm/subdev/clk/gf100.c | 1 + drm/nouveau/nvkm/subdev/clk/gk104.c | 1 + drm/nouveau/nvkm/subdev/clk/gk20a.c | 1 + drm/nouveau/nvkm/subdev/clk/gm20b.c | 1 + drm/nouveau/nvkm/subdev/clk/gt215.c | 1 + drm/nouveau/nvkm/subdev/clk/mcp77.c | 1 + drm/nouveau/nvkm/subdev/clk/nv40.c | 15 +++++++++++++++ drm/nouveau/nvkm/subdev/clk/nv50.c | 1 + drm/nouveau/nvkm/subdev/clk/priv.h | 5 +++++ 11 files changed, 34 insertions(+), 8 deletions(-) diff --git a/drm/nouveau/nvkm/subdev/clk/base.c b/drm/nouveau/nvkm/subdev/clk/base.c index d6f239f..3c40f67 100644 --- a/drm/nouveau/nvkm/subdev/clk/base.c +++ b/drm/nouveau/nvkm/subdev/clk/base.c @@ -279,7 +279,7 @@ nvkm_cstate_new(struct nvkm_clk *clk, int idx, struct nvkm_pstate *pstate) /****************************************************************************** * P-States *****************************************************************************/ -static int +int nvkm_pstate_prog(struct nvkm_clk *clk, int pstateid) { struct nvkm_subdev *subdev = &clk->subdev; @@ -322,7 +322,10 @@ static void nvkm_clk_update_impl(struct nvkm_clk *clk) { struct nvkm_subdev *subdev = &clk->subdev; - int pstate, ret; + int pstate; + + if (!clk->func->update) + return; clk->pwrsrc = power_supply_is_system_supplied(); @@ -341,12 +344,7 @@ nvkm_clk_update_impl(struct nvkm_clk *clk) pstate = -1; } - nvkm_trace(subdev, "-> %d\n", pstate); - ret = nvkm_pstate_prog(clk, pstate); - if (ret) { - nvkm_error(subdev, "error setting pstate %d: %d\n", - pstate, ret); - } + clk->func->update(clk, pstate); } static void diff --git a/drm/nouveau/nvkm/subdev/clk/g84.c b/drm/nouveau/nvkm/subdev/clk/g84.c index f97e3ec..7b9b30d 100644 --- a/drm/nouveau/nvkm/subdev/clk/g84.c +++ b/drm/nouveau/nvkm/subdev/clk/g84.c @@ -29,6 +29,7 @@ g84_clk = { .calc = nv50_clk_calc, .prog = nv50_clk_prog, .tidy = nv50_clk_tidy, + .update = nv40_clk_update, .domains = { { nv_clk_src_crystal, 0xff }, { nv_clk_src_href , 0xff }, diff --git a/drm/nouveau/nvkm/subdev/clk/gf100.c b/drm/nouveau/nvkm/subdev/clk/gf100.c index 71b7c9f..808e1ed 100644 --- a/drm/nouveau/nvkm/subdev/clk/gf100.c +++ b/drm/nouveau/nvkm/subdev/clk/gf100.c @@ -437,6 +437,7 @@ gf100_clk = { .calc = gf100_clk_calc, .prog = gf100_clk_prog, .tidy = gf100_clk_tidy, + .update = nv40_clk_update, .domains = { { nv_clk_src_crystal, 0xff }, { nv_clk_src_href , 0xff }, diff --git a/drm/nouveau/nvkm/subdev/clk/gk104.c b/drm/nouveau/nvkm/subdev/clk/gk104.c index 639234f..8448a88 100644 --- a/drm/nouveau/nvkm/subdev/clk/gk104.c +++ b/drm/nouveau/nvkm/subdev/clk/gk104.c @@ -482,6 +482,7 @@ gk104_clk = { .calc = gk104_clk_calc, .prog = gk104_clk_prog, .tidy = gk104_clk_tidy, + .update = nv40_clk_update, .domains = { { nv_clk_src_crystal, 0xff }, { nv_clk_src_href , 0xff }, diff --git a/drm/nouveau/nvkm/subdev/clk/gk20a.c b/drm/nouveau/nvkm/subdev/clk/gk20a.c index 5f0ee24..8b64cc9 100644 --- a/drm/nouveau/nvkm/subdev/clk/gk20a.c +++ b/drm/nouveau/nvkm/subdev/clk/gk20a.c @@ -636,6 +636,7 @@ gk20a_clk = { .calc = gk20a_clk_calc, .prog = gk20a_clk_prog, .tidy = gk20a_clk_tidy, + .update = nv40_clk_update, .pstates = gk20a_pstates, .nr_pstates = ARRAY_SIZE(gk20a_pstates), .domains = { diff --git a/drm/nouveau/nvkm/subdev/clk/gm20b.c b/drm/nouveau/nvkm/subdev/clk/gm20b.c index 71b2bbb..8c8eb8c 100644 --- a/drm/nouveau/nvkm/subdev/clk/gm20b.c +++ b/drm/nouveau/nvkm/subdev/clk/gm20b.c @@ -168,6 +168,7 @@ gm20b_clk_speedo0 = { .calc = gk20a_clk_calc, .prog = gk20a_clk_prog, .tidy = gk20a_clk_tidy, + .update = nv40_clk_update, .pstates = gm20b_pstates, .nr_pstates = ARRAY_SIZE(gm20b_pstates) - 1, .domains = { diff --git a/drm/nouveau/nvkm/subdev/clk/gt215.c b/drm/nouveau/nvkm/subdev/clk/gt215.c index 056702e..8913afa 100644 --- a/drm/nouveau/nvkm/subdev/clk/gt215.c +++ b/drm/nouveau/nvkm/subdev/clk/gt215.c @@ -520,6 +520,7 @@ gt215_clk = { .calc = gt215_clk_calc, .prog = gt215_clk_prog, .tidy = gt215_clk_tidy, + .update = nv40_clk_update, .domains = { { nv_clk_src_crystal , 0xff }, { nv_clk_src_core , 0x00, 0, "core", 1000 }, diff --git a/drm/nouveau/nvkm/subdev/clk/mcp77.c b/drm/nouveau/nvkm/subdev/clk/mcp77.c index 1c21b8b..e80b68e 100644 --- a/drm/nouveau/nvkm/subdev/clk/mcp77.c +++ b/drm/nouveau/nvkm/subdev/clk/mcp77.c @@ -400,6 +400,7 @@ mcp77_clk = { .calc = mcp77_clk_calc, .prog = mcp77_clk_prog, .tidy = mcp77_clk_tidy, + .update = nv40_clk_update, .domains = { { nv_clk_src_crystal, 0xff }, { nv_clk_src_href , 0xff }, diff --git a/drm/nouveau/nvkm/subdev/clk/nv40.c b/drm/nouveau/nvkm/subdev/clk/nv40.c index 2ab9b9b..a808319 100644 --- a/drm/nouveau/nvkm/subdev/clk/nv40.c +++ b/drm/nouveau/nvkm/subdev/clk/nv40.c @@ -201,12 +201,27 @@ nv40_clk_tidy(struct nvkm_clk *obj) { } +void +nv40_clk_update(struct nvkm_clk *clk, int pstate) +{ + struct nvkm_subdev *subdev = &clk->subdev; + int ret; + + nvkm_trace(subdev, "-> %d\n", pstate); + ret = nvkm_pstate_prog(clk, pstate); + if (ret) { + nvkm_error(subdev, "error setting pstate %d: %d\n", + pstate, ret); + } +} + static const struct nvkm_clk_func nv40_clk = { .read = nv40_clk_read, .calc = nv40_clk_calc, .prog = nv40_clk_prog, .tidy = nv40_clk_tidy, + .update = nv40_clk_update, .domains = { { nv_clk_src_crystal, 0xff }, { nv_clk_src_href , 0xff }, diff --git a/drm/nouveau/nvkm/subdev/clk/nv50.c b/drm/nouveau/nvkm/subdev/clk/nv50.c index 5841f29..b29318e 100644 --- a/drm/nouveau/nvkm/subdev/clk/nv50.c +++ b/drm/nouveau/nvkm/subdev/clk/nv50.c @@ -544,6 +544,7 @@ nv50_clk = { .calc = nv50_clk_calc, .prog = nv50_clk_prog, .tidy = nv50_clk_tidy, + .update = nv40_clk_update, .domains = { { nv_clk_src_crystal, 0xff }, { nv_clk_src_href , 0xff }, diff --git a/drm/nouveau/nvkm/subdev/clk/priv.h b/drm/nouveau/nvkm/subdev/clk/priv.h index 51eafc0..958f5e3 100644 --- a/drm/nouveau/nvkm/subdev/clk/priv.h +++ b/drm/nouveau/nvkm/subdev/clk/priv.h @@ -10,6 +10,7 @@ struct nvkm_clk_func { int (*calc)(struct nvkm_clk *, struct nvkm_cstate *); int (*prog)(struct nvkm_clk *); void (*tidy)(struct nvkm_clk *); + void (*update)(struct nvkm_clk *, int pstate); struct nvkm_pstate *pstates; int nr_pstates; struct nvkm_domain domains[]; @@ -20,7 +21,11 @@ int nvkm_clk_ctor(const struct nvkm_clk_func *, struct nvkm_device *, int, int nvkm_clk_new_(const struct nvkm_clk_func *, struct nvkm_device *, int, bool allow_reclock, struct nvkm_clk **); +int nvkm_pstate_prog(struct nvkm_clk *, int pstateid); + int nv04_clk_pll_calc(struct nvkm_clk *, struct nvbios_pll *, int clk, struct nvkm_pll_vals *); int nv04_clk_pll_prog(struct nvkm_clk *, u32 reg1, struct nvkm_pll_vals *); + +void nv40_clk_update(struct nvkm_clk *, int pstate); #endif -- 2.8.1
Karol Herbst
2016-Apr-18 19:14 UTC
[Nouveau] [PATCH v4 32/37] clk: only do partial reclocks as required
we don't want to reclock to the same pstate or cstate over and over again, so only do things we actually have to do. v4: move into gf100 Signed-off-by: Karol Herbst <nouveau at karolherbst.de> --- drm/nouveau/nvkm/subdev/clk/base.c | 11 +++++-- drm/nouveau/nvkm/subdev/clk/gf100.c | 62 ++++++++++++++++++++++++++++++++++++- drm/nouveau/nvkm/subdev/clk/gk104.c | 2 +- drm/nouveau/nvkm/subdev/clk/nv40.c | 3 ++ drm/nouveau/nvkm/subdev/clk/priv.h | 4 +++ 5 files changed, 77 insertions(+), 5 deletions(-) diff --git a/drm/nouveau/nvkm/subdev/clk/base.c b/drm/nouveau/nvkm/subdev/clk/base.c index 3c40f67..2776d79 100644 --- a/drm/nouveau/nvkm/subdev/clk/base.c +++ b/drm/nouveau/nvkm/subdev/clk/base.c @@ -107,7 +107,7 @@ nvkm_cstate_valid(struct nvkm_clk *clk, struct nvkm_cstate *cstate, u32 max_volt voltage >= volt->min_uv; } -static struct nvkm_cstate * +struct nvkm_cstate * nvkm_cstate_find_best(struct nvkm_clk *clk, struct nvkm_pstate *pstate, struct nvkm_cstate *start) { @@ -148,7 +148,7 @@ nvkm_cstate_find_best(struct nvkm_clk *clk, struct nvkm_pstate *pstate, return cstate; } -static struct nvkm_cstate * +struct nvkm_cstate * nvkm_cstate_get(struct nvkm_clk *clk, struct nvkm_pstate *pstate, int cstatei) { struct nvkm_cstate *cstate; @@ -168,7 +168,7 @@ nvkm_cstate_get(struct nvkm_clk *clk, struct nvkm_pstate *pstate, int cstatei) return NULL; } -static int +int nvkm_cstate_prog(struct nvkm_clk *clk, struct nvkm_pstate *pstate, int cstatei) { struct nvkm_subdev *subdev = &clk->subdev; @@ -188,6 +188,11 @@ nvkm_cstate_prog(struct nvkm_clk *clk, struct nvkm_pstate *pstate, int cstatei) cstate = &pstate->base; } + if (!cstate) { + nvkm_error(subdev, "failed to set cstate %d\n", cstatei); + return -EINVAL; + } + if (therm) { ret = nvkm_therm_cstate(therm, pstate->fanspeed, +1); if (ret && ret != -ENODEV) { diff --git a/drm/nouveau/nvkm/subdev/clk/gf100.c b/drm/nouveau/nvkm/subdev/clk/gf100.c index 808e1ed..5025dcc 100644 --- a/drm/nouveau/nvkm/subdev/clk/gf100.c +++ b/drm/nouveau/nvkm/subdev/clk/gf100.c @@ -28,6 +28,7 @@ #include <subdev/bios.h> #include <subdev/bios/pll.h> #include <subdev/timer.h> +#include <subdev/volt.h> struct gf100_clk_info { u32 freq; @@ -431,13 +432,72 @@ gf100_clk_tidy(struct nvkm_clk *base) memset(clk->eng, 0x00, sizeof(clk->eng)); } +static int +gf100_clk_update_volt(struct nvkm_clk *clk) +{ + struct nvkm_subdev *subdev = &clk->subdev; + struct nvkm_volt *volt = subdev->device->volt; + struct nvkm_therm *therm = subdev->device->therm; + + if (!volt || !therm || !clk->pstate || !clk->set_cstate) + return -EINVAL; + + return nvkm_volt_set_id(volt, clk->set_cstate->voltage, + clk->pstate->base.voltage, 0); +} + +void +gf100_clk_update(struct nvkm_clk *clk, int pstate) +{ + struct nvkm_subdev *subdev = &clk->subdev; + int ret; + + if (!clk->pstate || clk->pstate->pstate != pstate) { + nvkm_trace(subdev, "-> P %d\n", pstate); + ret = nvkm_pstate_prog(clk, pstate); + if (ret) { + nvkm_error(subdev, "error setting pstate %d: %d\n", + pstate, ret); + } + } else if (!clk->set_cstate || + clk->set_cstate->cstate != clk->exp_cstate) { + + struct nvkm_cstate *cstate = nvkm_cstate_get(clk, clk->pstate, clk->exp_cstate); + if (!cstate) { + nvkm_error(subdev, "can't find cstate %i\n", + clk->exp_cstate); + return; + } + + cstate = nvkm_cstate_find_best(clk, clk->pstate, cstate); + if (!cstate) { + nvkm_error(subdev, "can't find best cstate for %i\n", + cstate->cstate); + return; + } + + if (cstate != clk->set_cstate) { + nvkm_trace(subdev, "-> C %d\n", cstate->cstate); + ret = nvkm_cstate_prog(clk, clk->pstate, cstate->cstate); + if (ret) { + nvkm_error(subdev, "error setting cstate %d: %d\n", + cstate->cstate, ret); + } + } else { + gf100_clk_update_volt(clk); + } + } else { + gf100_clk_update_volt(clk); + } +} + static const struct nvkm_clk_func gf100_clk = { .read = gf100_clk_read, .calc = gf100_clk_calc, .prog = gf100_clk_prog, .tidy = gf100_clk_tidy, - .update = nv40_clk_update, + .update = gf100_clk_update, .domains = { { nv_clk_src_crystal, 0xff }, { nv_clk_src_href , 0xff }, diff --git a/drm/nouveau/nvkm/subdev/clk/gk104.c b/drm/nouveau/nvkm/subdev/clk/gk104.c index 8448a88..abf1d76 100644 --- a/drm/nouveau/nvkm/subdev/clk/gk104.c +++ b/drm/nouveau/nvkm/subdev/clk/gk104.c @@ -482,7 +482,7 @@ gk104_clk = { .calc = gk104_clk_calc, .prog = gk104_clk_prog, .tidy = gk104_clk_tidy, - .update = nv40_clk_update, + .update = gf100_clk_update, .domains = { { nv_clk_src_crystal, 0xff }, { nv_clk_src_href , 0xff }, diff --git a/drm/nouveau/nvkm/subdev/clk/nv40.c b/drm/nouveau/nvkm/subdev/clk/nv40.c index a808319..5b10ee2 100644 --- a/drm/nouveau/nvkm/subdev/clk/nv40.c +++ b/drm/nouveau/nvkm/subdev/clk/nv40.c @@ -207,6 +207,9 @@ nv40_clk_update(struct nvkm_clk *clk, int pstate) struct nvkm_subdev *subdev = &clk->subdev; int ret; + if (clk->pstate && clk->pstate->pstate == pstate) + return; + nvkm_trace(subdev, "-> %d\n", pstate); ret = nvkm_pstate_prog(clk, pstate); if (ret) { diff --git a/drm/nouveau/nvkm/subdev/clk/priv.h b/drm/nouveau/nvkm/subdev/clk/priv.h index 958f5e3..e2f15c4 100644 --- a/drm/nouveau/nvkm/subdev/clk/priv.h +++ b/drm/nouveau/nvkm/subdev/clk/priv.h @@ -22,10 +22,14 @@ int nvkm_clk_new_(const struct nvkm_clk_func *, struct nvkm_device *, int, bool allow_reclock, struct nvkm_clk **); int nvkm_pstate_prog(struct nvkm_clk *, int pstateid); +int nvkm_cstate_prog(struct nvkm_clk *, struct nvkm_pstate *, int cstatei); +struct nvkm_cstate * nvkm_cstate_get(struct nvkm_clk *, struct nvkm_pstate *, int cstatei); +struct nvkm_cstate * nvkm_cstate_find_best(struct nvkm_clk *, struct nvkm_pstate *, struct nvkm_cstate *start); int nv04_clk_pll_calc(struct nvkm_clk *, struct nvbios_pll *, int clk, struct nvkm_pll_vals *); int nv04_clk_pll_prog(struct nvkm_clk *, u32 reg1, struct nvkm_pll_vals *); void nv40_clk_update(struct nvkm_clk *, int pstate); +void gf100_clk_update(struct nvkm_clk *, int pstate); #endif -- 2.8.1
Karol Herbst
2016-Apr-18 19:14 UTC
[Nouveau] [PATCH v4 33/37] 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 <nouveau at karolherbst.de> --- drm/nouveau/nvkm/subdev/therm/base.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drm/nouveau/nvkm/subdev/therm/base.c b/drm/nouveau/nvkm/subdev/therm/base.c index 0c0feec..566fe5d 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) { @@ -153,7 +155,10 @@ 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; nvkm_therm_update(therm, -1); + if (clk) + nvkm_clk_update(clk, false); } int -- 2.8.1
Karol Herbst
2016-Apr-18 19:14 UTC
[Nouveau] [PATCH v4 34/37] mc: fix NULL pointer access in libnouveau
Signed-off-by: Karol Herbst <nouveau at karolherbst.de> --- drm/nouveau/nvkm/subdev/mc/base.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drm/nouveau/nvkm/subdev/mc/base.c b/drm/nouveau/nvkm/subdev/mc/base.c index aa394af..88bc1cc 100644 --- a/drm/nouveau/nvkm/subdev/mc/base.c +++ b/drm/nouveau/nvkm/subdev/mc/base.c @@ -90,10 +90,15 @@ nvkm_mc_intr(struct nvkm_mc *mc, bool *handled) void nvkm_mc_reset(struct nvkm_mc *mc, enum nvkm_devidx devidx) { - struct nvkm_device *device = mc->subdev.device; + struct nvkm_device *device; const struct nvkm_mc_map *map; u64 pmc_enable; + if (!mc) + return; + + device = mc->subdev.device; + if (!(pmc_enable = nvkm_top_reset(device->top, devidx))) { for (map = mc->func->reset; map && map->stat; map++) { if (map->unit == devidx) { -- 2.8.1
Karol Herbst
2016-Apr-18 19:14 UTC
[Nouveau] [PATCH v4 35/37] clk: set clocks to pre suspend state after suspend
Signed-off-by: Karol Herbst <nouveau at karolherbst.de> --- drm/nouveau/nvkm/subdev/clk/base.c | 19 +++++++++++-------- drm/nouveau/nvkm/subdev/clk/gf100.c | 4 ++-- drm/nouveau/nvkm/subdev/clk/nv40.c | 2 +- drm/nouveau/nvkm/subdev/clk/priv.h | 6 +++--- 4 files changed, 17 insertions(+), 14 deletions(-) diff --git a/drm/nouveau/nvkm/subdev/clk/base.c b/drm/nouveau/nvkm/subdev/clk/base.c index 2776d79..d5440a9 100644 --- a/drm/nouveau/nvkm/subdev/clk/base.c +++ b/drm/nouveau/nvkm/subdev/clk/base.c @@ -324,7 +324,7 @@ nvkm_pstate_prog(struct nvkm_clk *clk, int pstateid) } static void -nvkm_clk_update_impl(struct nvkm_clk *clk) +nvkm_clk_update_impl(struct nvkm_clk *clk, bool force) { struct nvkm_subdev *subdev = &clk->subdev; int pstate; @@ -349,7 +349,7 @@ nvkm_clk_update_impl(struct nvkm_clk *clk) pstate = -1; } - clk->func->update(clk, pstate); + clk->func->update(clk, pstate, force); } static void @@ -360,7 +360,7 @@ nvkm_clk_update_work(struct work_struct *work) if (!atomic_xchg(&clk->waiting, 0)) return; - nvkm_clk_update_impl(clk); + nvkm_clk_update_impl(clk, false); wake_up_all(&clk->wait); nvkm_notify_get(&clk->pwrsrc_ntfy); @@ -613,11 +613,7 @@ nvkm_clk_init(struct nvkm_subdev *subdev) if (clk->func->init) return clk->func->init(clk); - clk->astate = -1; - clk->pstate = NULL; - clk->exp_cstate = NVKM_CLK_CSTATE_DEFAULT; - clk->set_cstate = NULL; - nvkm_clk_update(clk, true); + nvkm_clk_update_impl(clk, true); return 0; } @@ -672,8 +668,15 @@ 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->pstate = NULL; + clk->astate = -1; clk->ustate_ac = -1; clk->ustate_dc = -1; + + clk->exp_cstate = NVKM_CLK_CSTATE_DEFAULT; + clk->set_cstate = NULL; + clk->allow_reclock = allow_reclock; INIT_WORK(&clk->work, nvkm_clk_update_work); diff --git a/drm/nouveau/nvkm/subdev/clk/gf100.c b/drm/nouveau/nvkm/subdev/clk/gf100.c index 5025dcc..5621daf 100644 --- a/drm/nouveau/nvkm/subdev/clk/gf100.c +++ b/drm/nouveau/nvkm/subdev/clk/gf100.c @@ -447,12 +447,12 @@ gf100_clk_update_volt(struct nvkm_clk *clk) } void -gf100_clk_update(struct nvkm_clk *clk, int pstate) +gf100_clk_update(struct nvkm_clk *clk, int pstate, bool force) { struct nvkm_subdev *subdev = &clk->subdev; int ret; - if (!clk->pstate || clk->pstate->pstate != pstate) { + if (!clk->pstate || clk->pstate->pstate != pstate || force) { nvkm_trace(subdev, "-> P %d\n", pstate); ret = nvkm_pstate_prog(clk, pstate); if (ret) { diff --git a/drm/nouveau/nvkm/subdev/clk/nv40.c b/drm/nouveau/nvkm/subdev/clk/nv40.c index 5b10ee2..055063a 100644 --- a/drm/nouveau/nvkm/subdev/clk/nv40.c +++ b/drm/nouveau/nvkm/subdev/clk/nv40.c @@ -202,7 +202,7 @@ nv40_clk_tidy(struct nvkm_clk *obj) } void -nv40_clk_update(struct nvkm_clk *clk, int pstate) +nv40_clk_update(struct nvkm_clk *clk, int pstate, bool force) { struct nvkm_subdev *subdev = &clk->subdev; int ret; diff --git a/drm/nouveau/nvkm/subdev/clk/priv.h b/drm/nouveau/nvkm/subdev/clk/priv.h index e2f15c4..06a78a2 100644 --- a/drm/nouveau/nvkm/subdev/clk/priv.h +++ b/drm/nouveau/nvkm/subdev/clk/priv.h @@ -10,7 +10,7 @@ struct nvkm_clk_func { int (*calc)(struct nvkm_clk *, struct nvkm_cstate *); int (*prog)(struct nvkm_clk *); void (*tidy)(struct nvkm_clk *); - void (*update)(struct nvkm_clk *, int pstate); + void (*update)(struct nvkm_clk *, int pstate, bool force); struct nvkm_pstate *pstates; int nr_pstates; struct nvkm_domain domains[]; @@ -30,6 +30,6 @@ int nv04_clk_pll_calc(struct nvkm_clk *, struct nvbios_pll *, int clk, struct nvkm_pll_vals *); int nv04_clk_pll_prog(struct nvkm_clk *, u32 reg1, struct nvkm_pll_vals *); -void nv40_clk_update(struct nvkm_clk *, int pstate); -void gf100_clk_update(struct nvkm_clk *, int pstate); +void nv40_clk_update(struct nvkm_clk *, int pstate, bool force); +void gf100_clk_update(struct nvkm_clk *, int pstate, bool force); #endif -- 2.8.1
Karol Herbst
2016-Apr-18 19:14 UTC
[Nouveau] [PATCH v4 36/37] WIP volt/gk104: readout speedo
this gk104 volt implementation has to be reworked a little, because the speedo readout in maxwell doesn't need those strange 0 and 41 writes into 0x122634, but it needs this PWM thing. Maybe Maxwell is PWM only and we could just simplify it there, but without proper knowledge there has some refactoring to be made. Signed-off-by: Karol Herbst <nouveau at karolherbst.de> --- drm/nouveau/nvkm/subdev/volt/gk104.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/drm/nouveau/nvkm/subdev/volt/gk104.c b/drm/nouveau/nvkm/subdev/volt/gk104.c index b735173..81788c2 100644 --- a/drm/nouveau/nvkm/subdev/volt/gk104.c +++ b/drm/nouveau/nvkm/subdev/volt/gk104.c @@ -27,6 +27,7 @@ #include <subdev/gpio.h> #include <subdev/bios.h> #include <subdev/bios/volt.h> +#include <subdev/fuse.h> #define gk104_volt(p) container_of((p), struct gk104_volt, base) struct gk104_volt { @@ -64,13 +65,31 @@ gk104_volt_set(struct nvkm_volt *base, u32 uv) return 0; } +static int +gk104_volt_speedo_read(struct nvkm_volt *volt) +{ + struct nvkm_device *device = volt->subdev.device; + struct nvkm_fuse *fuse = device->fuse; + int ret; + + if (!fuse) + return -EINVAL; + + nvkm_wr32(device, 0x122634, 0x0); + ret = nvkm_fuse_read(fuse, 0x3a8); + nvkm_wr32(device, 0x122634, 0x41); + return ret; +} + static const struct nvkm_volt_func gk104_volt_pwm = { .volt_get = gk104_volt_get, .volt_set = gk104_volt_set, + .speedo_read = gk104_volt_speedo_read, }, gk104_volt_gpio = { .vid_get = nvkm_voltgpio_get, .vid_set = nvkm_voltgpio_set, + .speedo_read = gk104_volt_speedo_read, }; int -- 2.8.1
Karol Herbst
2016-Apr-18 19:14 UTC
[Nouveau] [PATCH v4 37/37] volt: add NvVoltOffsetmV option
This option can be used to adjust the calculated voltage or the cstate voltage calculation Signed-off-by: Karol Herbst <nouveau at karolherbst.de> --- bin/nv_cmp_volt.c | 2 +- drm/nouveau/include/nvkm/subdev/volt.h | 4 +++- drm/nouveau/nvkm/subdev/clk/base.c | 8 ++++---- drm/nouveau/nvkm/subdev/volt/base.c | 25 ++++++++++++++++++++----- 4 files changed, 28 insertions(+), 11 deletions(-) diff --git a/bin/nv_cmp_volt.c b/bin/nv_cmp_volt.c index e61056c..d1a0402 100644 --- a/bin/nv_cmp_volt.c +++ b/bin/nv_cmp_volt.c @@ -117,7 +117,7 @@ main(int argc, char **argv) new_voltage = nvkm_volt_get(volt); new_temp = nvkm_rd32(device, 0x20400);//nvkm_therm_temp_get(therm); - new_nouveau_voltage = max(nvkm_volt_map(volt, best_cstate->voltage, new_temp), nvkm_volt_map(volt, best_pstate->base.voltage, new_temp)); + new_nouveau_voltage = max(nvkm_volt_map(volt, best_cstate->voltage, new_temp, true), nvkm_volt_map(volt, best_pstate->base.voltage, new_temp, false)); new_pstate = best_pstate->pstate; new_cstate = best_cstate->cstate; diff --git a/drm/nouveau/include/nvkm/subdev/volt.h b/drm/nouveau/include/nvkm/subdev/volt.h index 25588c7..f34fd39 100644 --- a/drm/nouveau/include/nvkm/subdev/volt.h +++ b/drm/nouveau/include/nvkm/subdev/volt.h @@ -22,9 +22,11 @@ struct nvkm_volt { u8 max2_id; int speedo; + + int volt_offset_mv; }; -int nvkm_volt_map(struct nvkm_volt *volt, u8 id, u8 temperature); +int nvkm_volt_map(struct nvkm_volt *volt, u8 id, u8 temperature, bool enableOffset); int nvkm_volt_map_min(struct nvkm_volt *volt, u8 id); int nvkm_volt_get(struct nvkm_volt *); int nvkm_volt_set_id(struct nvkm_volt *, u8 id, u8 min_id, int condition); diff --git a/drm/nouveau/nvkm/subdev/clk/base.c b/drm/nouveau/nvkm/subdev/clk/base.c index d5440a9..7937155 100644 --- a/drm/nouveau/nvkm/subdev/clk/base.c +++ b/drm/nouveau/nvkm/subdev/clk/base.c @@ -100,7 +100,7 @@ nvkm_cstate_valid(struct nvkm_clk *clk, struct nvkm_cstate *cstate, u32 max_volt if (!volt) return true; - voltage = nvkm_volt_map(volt, cstate->voltage, temp); + voltage = nvkm_volt_map(volt, cstate->voltage, temp, true); if (voltage < 0) return false; return voltage <= min(max_volt, volt->max_uv) && @@ -131,13 +131,13 @@ nvkm_cstate_find_best(struct nvkm_clk *clk, struct nvkm_pstate *pstate, max_volt = volt->max_uv; if (volt->max0_id != 0xff) max_volt = min(max_volt, - nvkm_volt_map(volt, volt->max0_id, temp)); + nvkm_volt_map(volt, volt->max0_id, temp, false)); if (volt->max1_id != 0xff) max_volt = min(max_volt, - nvkm_volt_map(volt, volt->max1_id, temp)); + nvkm_volt_map(volt, volt->max1_id, temp, false)); if (volt->max2_id != 0xff) max_volt = min(max_volt, - nvkm_volt_map(volt, volt->max2_id, temp)); + nvkm_volt_map(volt, volt->max2_id, temp, false)); for (cstate = start; &cstate->head != &pstate->list; cstate = list_entry(cstate->head.prev, typeof(*cstate), head)) { diff --git a/drm/nouveau/nvkm/subdev/volt/base.c b/drm/nouveau/nvkm/subdev/volt/base.c index 5e35d96..7d5e6c8 100644 --- a/drm/nouveau/nvkm/subdev/volt/base.c +++ b/drm/nouveau/nvkm/subdev/volt/base.c @@ -23,6 +23,8 @@ */ #include "priv.h" +#include <core/option.h> + #include <subdev/bios.h> #include <subdev/bios/vmap.h> #include <subdev/bios/volt.h> @@ -100,8 +102,8 @@ nvkm_volt_map_min(struct nvkm_volt *volt, u8 id) return id ? id * 10000 : -ENODEV; } -int -nvkm_volt_map(struct nvkm_volt *volt, u8 id, u8 temp) +static int +nvkm_volt_map_impl(struct nvkm_volt *volt, u8 id, u8 temp) { struct nvkm_bios *bios = volt->subdev.device->bios; struct nvbios_vmap_entry info; @@ -145,7 +147,7 @@ nvkm_volt_map(struct nvkm_volt *volt, u8 id, u8 temp) result = min(max(result, (s64)info.min), (s64)info.max); if (info.link != 0xff) { - int ret = nvkm_volt_map(volt, info.link, temp); + int ret = nvkm_volt_map_impl(volt, info.link, temp); if (ret < 0) return ret; result += ret; @@ -157,6 +159,15 @@ nvkm_volt_map(struct nvkm_volt *volt, u8 id, u8 temp) } int +nvkm_volt_map(struct nvkm_volt *volt, u8 id, u8 temp, bool enable_offset) +{ + int res = nvkm_volt_map_impl(volt, id, temp); + if (enable_offset) + res += volt->volt_offset_mv * 1000; + return res; +} + +int nvkm_volt_set_id(struct nvkm_volt *volt, u8 id, u8 min_id, int condition) { struct nvkm_therm *therm = volt->subdev.device->therm; @@ -169,13 +180,13 @@ nvkm_volt_set_id(struct nvkm_volt *volt, u8 id, u8 min_id, int condition) if (therm) temp = nvkm_therm_temp_get(therm); - ret = nvkm_volt_map(volt, id, max(temp, 0)); + ret = nvkm_volt_map(volt, id, max(temp, 0), true); if (ret >= 0) { int prev = nvkm_volt_get(volt); if (!condition || prev < 0 || (condition < 0 && ret < prev) || (condition > 0 && ret > prev)) { - int min = nvkm_volt_map(volt, min_id, max(temp, 0)); + int min = nvkm_volt_map(volt, min_id, max(temp, 0), false); if (min >= 0) ret = max(min, ret); ret = nvkm_volt_set(volt, ret); @@ -308,6 +319,10 @@ nvkm_volt_ctor(const struct nvkm_volt_func *func, struct nvkm_device *device, volt->speedo = nvkm_volt_speedo_read(volt); if (volt->speedo > 0) nvkm_debug(&volt->subdev, "speedo %x\n", volt->speedo); + + volt->volt_offset_mv = nvkm_longopt(device->cfgopt, "NvVoltOffsetmV", 0); + if (volt->volt_offset_mv) + nvkm_info(&volt->subdev, "Volt Offset applied: %i\n", volt->volt_offset_mv); } int -- 2.8.1
Martin Peres
2016-Apr-19 19:46 UTC
[Nouveau] [PATCH v4 06/37] volt: parse the max voltage map entries
On 18/04/16 22:13, Karol Herbst wrote:> There are at least three "max" entries, which specify the max voltage. Because > they are actually normal voltage map entries, they can also be affected by the > temperature. > > Nvidia respects those entries and if they get changed, nvidia uses the lower > voltage from both.both ;) I guess it is time to update this to: from the three of them.> > We shouldn't exceed those voltages at any given time. > > v2: state what those entries do in the source > v3: add the third max entry > > Signed-off-by: Karol Herbst <nouveau at karolherbst.de> > --- > drm/nouveau/include/nvkm/subdev/bios/vmap.h | 3 +++ > drm/nouveau/include/nvkm/subdev/volt.h | 5 +++++ > drm/nouveau/nvkm/subdev/bios/vmap.c | 10 ++++++++++ > drm/nouveau/nvkm/subdev/volt/base.c | 13 +++++++++++++ > 4 files changed, 31 insertions(+) > > diff --git a/drm/nouveau/include/nvkm/subdev/bios/vmap.h b/drm/nouveau/include/nvkm/subdev/bios/vmap.h > index 6633c6d..ae2f27b 100644 > --- a/drm/nouveau/include/nvkm/subdev/bios/vmap.h > +++ b/drm/nouveau/include/nvkm/subdev/bios/vmap.h > @@ -1,6 +1,9 @@ > #ifndef __NVBIOS_VMAP_H__ > #define __NVBIOS_VMAP_H__ > struct nvbios_vmap { > + u8 max0; > + u8 max1; > + u8 max2; > }; > > u16 nvbios_vmap_table(struct nvkm_bios *, u8 *ver, u8 *hdr, u8 *cnt, u8 *len); > diff --git a/drm/nouveau/include/nvkm/subdev/volt.h b/drm/nouveau/include/nvkm/subdev/volt.h > index fc68825..285c6bf 100644 > --- a/drm/nouveau/include/nvkm/subdev/volt.h > +++ b/drm/nouveau/include/nvkm/subdev/volt.h > @@ -15,6 +15,11 @@ struct nvkm_volt { > > u32 max_uv; > u32 min_uv; > + > + /* max voltage map entries, might be affected by temperature */might be affected *differently* by temperature.> + u8 max0_id; > + u8 max1_id; > + u8 max2_id; > }; > > int nvkm_volt_map_min(struct nvkm_volt *volt, u8 id); > diff --git a/drm/nouveau/nvkm/subdev/bios/vmap.c b/drm/nouveau/nvkm/subdev/bios/vmap.c > index 2f13db7..f2295e1 100644 > --- a/drm/nouveau/nvkm/subdev/bios/vmap.c > +++ b/drm/nouveau/nvkm/subdev/bios/vmap.c > @@ -61,7 +61,17 @@ nvbios_vmap_parse(struct nvkm_bios *bios, u8 *ver, u8 *hdr, u8 *cnt, u8 *len, > memset(info, 0x00, sizeof(*info)); > switch (!!vmap * *ver) { > case 0x10: > + info->max0 = 0xff; > + info->max1 = 0xff; > + info->max2 = 0xff; > + break; > case 0x20: > + info->max0 = nvbios_rd08(bios, vmap + 0x7); > + info->max1 = nvbios_rd08(bios, vmap + 0x8); > + if (*len >= 0xc) > + info->max2 = nvbios_rd08(bios, vmap + 0xc); > + else > + info->max2 = 0xff; > break; > } > return vmap; > diff --git a/drm/nouveau/nvkm/subdev/volt/base.c b/drm/nouveau/nvkm/subdev/volt/base.c > index ecf4cb4..63fffb8 100644 > --- a/drm/nouveau/nvkm/subdev/volt/base.c > +++ b/drm/nouveau/nvkm/subdev/volt/base.c > @@ -217,9 +217,22 @@ nvkm_volt_ctor(const struct nvkm_volt_func *func, struct nvkm_device *device, > > /* Assuming the non-bios device should build the voltage table later */ > if (bios) { > + u8 ver, hdr, cnt, len; > + struct nvbios_vmap vmap; > + > nvkm_volt_parse_bios(bios, volt); > nvkm_debug(&volt->subdev, "min: %iuv max: %iuv\n", > volt->min_uv, volt->max_uv); > + > + if (nvbios_vmap_parse(bios, &ver, &hdr, &cnt, &len, &vmap)) { > + volt->max0_id = vmap.max0; > + volt->max1_id = vmap.max1; > + volt->max2_id = vmap.max2; > + } else { > + volt->max0_id = 0xff; > + volt->max1_id = 0xff; > + volt->max2_id = 0xff; > + } > } > > if (volt->vid_nr) {With the comment and commit message fixed: Reviewed-by: Martin Peres <martin.peres at free.fr>
On 18/04/16 22:13, Karol Herbst wrote:> before clocking to a cstate, we have to check if the voltage is within the > allowed range. > > Signed-off-by: Karol Herbst <nouveau at karolherbst.de> > --- > drm/nouveau/include/nvkm/subdev/volt.h | 1 + > drm/nouveau/nvkm/subdev/volt/base.c | 2 +- > 2 files changed, 2 insertions(+), 1 deletion(-) > > diff --git a/drm/nouveau/include/nvkm/subdev/volt.h b/drm/nouveau/include/nvkm/subdev/volt.h > index ec9d87d..870d212 100644 > --- a/drm/nouveau/include/nvkm/subdev/volt.h > +++ b/drm/nouveau/include/nvkm/subdev/volt.h > @@ -22,6 +22,7 @@ struct nvkm_volt { > u8 max2_id; > }; > > +int nvkm_volt_map(struct nvkm_volt *volt, u8 id); > int nvkm_volt_map_min(struct nvkm_volt *volt, u8 id); > int nvkm_volt_get(struct nvkm_volt *); > int nvkm_volt_set_id(struct nvkm_volt *, u8 id, u8 min_id, int condition); > diff --git a/drm/nouveau/nvkm/subdev/volt/base.c b/drm/nouveau/nvkm/subdev/volt/base.c > index 1690c1c..6fb9d2e 100644 > --- a/drm/nouveau/nvkm/subdev/volt/base.c > +++ b/drm/nouveau/nvkm/subdev/volt/base.c > @@ -87,7 +87,7 @@ nvkm_volt_map_min(struct nvkm_volt *volt, u8 id) > return id ? id * 10000 : -ENODEV; > } > > -static int > +int > nvkm_volt_map(struct nvkm_volt *volt, u8 id) > { > struct nvkm_bios *bios = volt->subdev.device->bios;Reviewed-by: Martin Peres <martin.peres at free.fr>
Martin Peres
2016-Apr-19 20:06 UTC
[Nouveau] [PATCH v4 11/37] volt: add temperature parameter to nvkm_volt_map
On 18/04/16 22:13, Karol Herbst wrote:> the voltage entries actually may map to a different voltage depending on the > current temperature. > > v2: only read the temperatue when actually neededtemperatue -> temperature> > Signed-off-by: Karol Herbst <nouveau at karolherbst.de> > --- > bin/nv_cmp_volt.c | 2 +- > drm/nouveau/include/nvkm/subdev/volt.h | 2 +- > drm/nouveau/nvkm/subdev/volt/base.c | 14 ++++++++++---- > 3 files changed, 12 insertions(+), 6 deletions(-) > > diff --git a/bin/nv_cmp_volt.c b/bin/nv_cmp_volt.c > index c63d91b..34147b9 100644 > --- a/bin/nv_cmp_volt.c > +++ b/bin/nv_cmp_volt.c > @@ -117,7 +117,7 @@ main(int argc, char **argv) > > new_voltage = nvkm_volt_get(volt); > new_temp = nvkm_rd32(device, 0x20400);//nvkm_therm_temp_get(therm); > - new_nouveau_voltage = max(nvkm_volt_map(volt, best_cstate->voltage), nvkm_volt_map(volt, best_pstate->base.voltage)); > + new_nouveau_voltage = max(nvkm_volt_map(volt, best_cstate->voltage, new_temp), nvkm_volt_map(volt, best_pstate->base.voltage, new_temp)); > new_pstate = best_pstate->pstate; > new_cstate = best_cstate->cstate; > > diff --git a/drm/nouveau/include/nvkm/subdev/volt.h b/drm/nouveau/include/nvkm/subdev/volt.h > index 870d212..f223577 100644 > --- a/drm/nouveau/include/nvkm/subdev/volt.h > +++ b/drm/nouveau/include/nvkm/subdev/volt.h > @@ -22,7 +22,7 @@ struct nvkm_volt { > u8 max2_id; > }; > > -int nvkm_volt_map(struct nvkm_volt *volt, u8 id); > +int nvkm_volt_map(struct nvkm_volt *volt, u8 id, u8 temperature); > int nvkm_volt_map_min(struct nvkm_volt *volt, u8 id); > int nvkm_volt_get(struct nvkm_volt *); > int nvkm_volt_set_id(struct nvkm_volt *, u8 id, u8 min_id, int condition); > diff --git a/drm/nouveau/nvkm/subdev/volt/base.c b/drm/nouveau/nvkm/subdev/volt/base.c > index 6fb9d2e..d72bd4a 100644 > --- a/drm/nouveau/nvkm/subdev/volt/base.c > +++ b/drm/nouveau/nvkm/subdev/volt/base.c > @@ -26,6 +26,7 @@ > #include <subdev/bios.h> > #include <subdev/bios/vmap.h> > #include <subdev/bios/volt.h> > +#include <subdev/therm.h> > > int > nvkm_volt_get(struct nvkm_volt *volt) > @@ -88,7 +89,7 @@ nvkm_volt_map_min(struct nvkm_volt *volt, u8 id) > } > > int > -nvkm_volt_map(struct nvkm_volt *volt, u8 id) > +nvkm_volt_map(struct nvkm_volt *volt, u8 id, u8 temp) > { > struct nvkm_bios *bios = volt->subdev.device->bios; > struct nvbios_vmap_entry info; > @@ -98,7 +99,7 @@ nvkm_volt_map(struct nvkm_volt *volt, u8 id) > vmap = nvbios_vmap_entry_parse(bios, id, &ver, &len, &info); > if (vmap) { > if (info.link != 0xff) { > - int ret = nvkm_volt_map(volt, info.link); > + int ret = nvkm_volt_map(volt, info.link, temp); > if (ret < 0) > return ret; > info.min += ret; > @@ -112,18 +113,23 @@ nvkm_volt_map(struct nvkm_volt *volt, u8 id) > int > nvkm_volt_set_id(struct nvkm_volt *volt, u8 id, u8 min_id, int condition) > { > + struct nvkm_therm *therm = volt->subdev.device->therm; > int ret;/* Set the default temperature to 0°C as it always produces the * highest possible voltage which is the safest from a stability point of * view. This may be overridden later if the temperature can be read. */> + int temp = 0; > > if (volt->func->set_id) > return volt->func->set_id(volt, id, condition); > > - ret = nvkm_volt_map(volt, id); > + if (therm) > + temp = nvkm_therm_temp_get(therm);temp = max(0, nvkm_therm_temp_get(therm));> + > + ret = nvkm_volt_map(volt, id, max(temp, 0));s/max(temp, 0)/temp/g> if (ret >= 0) { > int prev = nvkm_volt_get(volt); > if (!condition || prev < 0 || > (condition < 0 && ret < prev) || > (condition > 0 && ret > prev)) { > - int min = nvkm_volt_map(volt, min_id); > + int min = nvkm_volt_map(volt, min_id, max(temp, 0));s/max(temp, 0)/temp/g> if (min >= 0) > ret = max(min, ret); > ret = nvkm_volt_set(volt, ret);With the commit message and the max() duplication fixed, this is: Reviewed-by: Martin Peres <martin.peres at free.fr>
Martin Peres
2016-Apr-19 20:26 UTC
[Nouveau] [PATCH v4 10/37] add daemon to compare nouveau with blob voltage
On 18/04/16 22:13, Karol Herbst wrote:> this tool can be run alongside the nvidia driver to print information about > the current p/cstate, which voltage was set by nvidia and what nouveau would > set in the same situation. > > v4: parse default options > > Signed-off-by: Karol Herbst <nouveau at karolherbst.de> > --- > bin/nv_cmp_volt.c | 139 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ > 1 file changed, 139 insertions(+) > create mode 100644 bin/nv_cmp_volt.c > > diff --git a/bin/nv_cmp_volt.c b/bin/nv_cmp_volt.c > new file mode 100644 > index 0000000..c63d91b > --- /dev/null > +++ b/bin/nv_cmp_volt.c > @@ -0,0 +1,139 @@ > +/* > + * Copyright 2016 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 <nvif/client.h> > +#include <nvif/device.h> > +#include <nvif/class.h> > + > +#include <nvkm/subdev/volt.h> > + > +#include "util.h" > + > +int > +main(int argc, char **argv) > +{ > + struct nvif_client if_client; > + struct nvif_device if_device; > + struct nvkm_clk *clk; > + struct nvkm_volt *volt; > + struct nvkm_device *device; > + int ret, c; > + int old_voltage = 0, old_nouveau_voltage = 0, old_pstate = 0; > + int old_cstate = 0, old_temp = 0; > + > + while ((c = getopt(argc, argv, U_GETOPT)) != -1) { > + switch (c) { > + default: > + if (!u_option(c)) > + return 1; > + break; > + } > + } > + > + ret = u_device("lib", argv[0], "error", true, true, > + (1ULL << NVKM_SUBDEV_CLK) | > +// (1ULL << NVKM_SUBDEV_FUSE) | > + (1ULL << NVKM_SUBDEV_GPIO) | > +// (1ULL << NVKM_SUBDEV_I2C) | > + (1ULL << NVKM_SUBDEV_PCI) | > +// (1ULL << NVKM_SUBDEV_THERM) | > +// (1ULL << NVKM_SUBDEV_TIMER) |// is not a C thing, it is only in c++. Please use /* */ Now, this is also a good place to say why ptherm is problematic for you and how you will just read the reg instead.> + (1ULL << NVKM_SUBDEV_VBIOS) | > + (1ULL << NVKM_SUBDEV_VOLT), > + 0x00000000, &if_client, &if_device); > + > + if (ret < 0) > + return ret; > + > + device = nvxx_device(&if_device); > + clk = device->clk; > +// therm = device->therm;I would say get rid of this commented code.> + volt = device->volt; > + > + printf("current voltage (µV), expected voltage (µV), abs diff (µV)," > + "rel diff nouveau/nvidia (%%), pstate, cstate, temperature" > + "(°C)\n"); > + while (true) { > + int gpc_clock = nvkm_clk_read(clk, nv_clk_src_gpc); > + int mem_clock = nvkm_clk_read(clk, nv_clk_src_mem); > + struct nvkm_pstate *pstate = NULL, *best_pstate = NULL; > + struct nvkm_cstate *cstate = NULL, *best_cstate = NULL; > + int mem_err, gpc_err; > + int new_voltage, new_nouveau_voltage, new_pstate, new_cstate; > + int new_temp; > +/* try to map the clocks set by NVIDIA to the closest pstate/cstate */> + list_for_each_entry(pstate, &clk->states, head) { > + list_for_each_entry(cstate, &pstate->list, head) { > + if (!best_pstate) { > + best_pstate = pstate; > + best_cstate = cstate; > + gpc_err = abs(cstate->domain[nv_clk_src_gpc] - gpc_clock); > + mem_err = abs(cstate->domain[nv_clk_src_mem] - mem_clock); > + continue; > + } > + > + if (abs(cstate->domain[nv_clk_src_gpc] - gpc_clock) <= gpc_err && > + abs(cstate->domain[nv_clk_src_mem] - mem_clock) <= mem_err) { > + best_pstate = pstate; > + best_cstate = cstate; > + gpc_err = abs(cstate->domain[nv_clk_src_gpc] - gpc_clock); > + mem_err = abs(cstate->domain[nv_clk_src_mem] - mem_clock); > + } > + } > + > + if (!best_pstate) { > + best_pstate = pstate; > + mem_err = abs(cstate->domain[nv_clk_src_mem] - mem_clock); > + continue; > + } else if (!best_cstate && abs(pstate->base.domain[nv_clk_src_mem] - mem_clock) <= mem_err) { > + best_pstate = pstate; > + mem_err = abs(cstate->domain[nv_clk_src_mem] - mem_clock); > + } > + } > + > + if (!best_cstate) > + best_cstate = &best_pstate->base; > + > + new_voltage = nvkm_volt_get(volt); > + new_temp = nvkm_rd32(device, 0x20400);//nvkm_therm_temp_get(therm);Same here, get rid of the comment.> + new_nouveau_voltage = max(nvkm_volt_map(volt, best_cstate->voltage), nvkm_volt_map(volt, best_pstate->base.voltage)); > + new_pstate = best_pstate->pstate; > + new_cstate = best_cstate->cstate; > + > + if (new_voltage != old_voltage || new_nouveau_voltage != old_nouveau_voltage || new_pstate != old_pstate || new_cstate != old_cstate || new_temp != old_temp) { > + old_voltage = new_voltage; > + old_nouveau_voltage = new_nouveau_voltage; > + old_pstate = new_pstate; > + old_cstate = new_cstate; > + old_temp = new_temp; > + printf("%i, %i, %i, %f, %i, %i, %i\n", new_voltage, > + new_nouveau_voltage, new_nouveau_voltage - new_voltage, > + 100 * (double)new_nouveau_voltage / new_voltage, > + new_pstate, new_cstate, new_temp); > + } > + usleep(100000); > + } > + > + return 0; > +}I am surprised by how short this tool is. This is great! With the nitpicks addressed, this is : Reviewed-by: Martin Peres <martin.peres at free.fr>
Martin Peres
2016-Apr-19 21:04 UTC
[Nouveau] [PATCH v4 13/37] clk: respect voltage limits in nvkm_cstate_prog
On 18/04/16 22:13, Karol Herbst wrote:> we should never allow to select a cstate which current voltage (depending on > the temperature) is higher than > > 1. the max volt entries in the voltage map table > 2. what tha gpu actually can volt to. > > this resolves all remaining volting errors on fermi and newer. > > v3: use find_best for all cstates before actually trying > add nvkm_cstate_get function to get cstate by index > > Signed-off-by: Karol Herbst <nouveau at karolherbst.de> > --- > drm/nouveau/nvkm/subdev/clk/base.c | 83 +++++++++++++++++++++++++++++++++----- > 1 file changed, 74 insertions(+), 9 deletions(-) > > diff --git a/drm/nouveau/nvkm/subdev/clk/base.c b/drm/nouveau/nvkm/subdev/clk/base.c > index fecf58f..21f6369 100644 > --- a/drm/nouveau/nvkm/subdev/clk/base.c > +++ b/drm/nouveau/nvkm/subdev/clk/base.c > @@ -74,6 +74,78 @@ nvkm_clk_adjust(struct nvkm_clk *clk, bool adjust, > /****************************************************************************** > * C-States > *****************************************************************************/ > +static bool > +nvkm_cstate_valid(struct nvkm_clk *clk, struct nvkm_cstate *cstate, u32 max_volt, int temp) > +{ > + struct nvkm_volt *volt = clk->subdev.device->volt; > + int voltage; > + > + if (!volt) > + return true; > + > + voltage = nvkm_volt_map(volt, cstate->voltage, temp); > + if (voltage < 0) > + return false; > + return voltage <= min(max_volt, volt->max_uv) && > + voltage >= volt->min_uv; > +} > + > +static struct nvkm_cstate * > +nvkm_cstate_find_best(struct nvkm_clk *clk, struct nvkm_pstate *pstate, > + struct nvkm_cstate *start) > +{ > + struct nvkm_device *device = clk->subdev.device; > + struct nvkm_therm *therm = device->therm; > + struct nvkm_volt *volt = device->volt; > + struct nvkm_cstate *cstate; > + int max_volt, temp = 0; > + > + if (!pstate || !start) > + return NULL; > + > + if (!volt) > + return start; > + > + if (therm) { > + /* ignore error code */ > + temp = max(0, nvkm_therm_temp_get(therm)); > + } > + > + max_volt = volt->max_uv; > + if (volt->max0_id != 0xff) > + max_volt = min(max_volt, > + nvkm_volt_map(volt, volt->max0_id, temp)); > + if (volt->max1_id != 0xff) > + max_volt = min(max_volt, > + nvkm_volt_map(volt, volt->max1_id, temp)); > + if (volt->max2_id != 0xff) > + max_volt = min(max_volt, > + nvkm_volt_map(volt, volt->max2_id, temp)); > + > + for (cstate = start; &cstate->head != &pstate->list; > + cstate = list_entry(cstate->head.prev, typeof(*cstate), head)) { > + if (nvkm_cstate_valid(clk, cstate, max_volt, temp)) > + break; > + } > + > + return cstate; > +} > + > +static struct nvkm_cstate * > +nvkm_cstate_get(struct nvkm_clk *clk, struct nvkm_pstate *pstate, int cstatei) > +{ > + struct nvkm_cstate *cstate; > + if (cstatei == -1) > + return list_entry(pstate->list.prev, typeof(*cstate), head); > + else { > + list_for_each_entry(cstate, &pstate->list, head) { > + if (cstate->cstate == cstatei) > + return cstate; > + } > + } > + return NULL; > +} > + > static int > nvkm_cstate_prog(struct nvkm_clk *clk, struct nvkm_pstate *pstate, int cstatei) > { > @@ -85,15 +157,8 @@ nvkm_cstate_prog(struct nvkm_clk *clk, struct nvkm_pstate *pstate, int cstatei) > int ret; > > if (!list_empty(&pstate->list)) { > - if (cstatei == -1) > - cstate = list_entry(pstate->list.prev, typeof(*cstate), > - head); > - else { > - list_for_each_entry(cstate, &pstate->list, head) { > - if (cstate->cstate == cstatei) > - break; > - } > - } > + cstate = nvkm_cstate_get(clk, pstate, cstatei); > + cstate = nvkm_cstate_find_best(clk, pstate, cstate); > } else { > cstate = &pstate->base; > }Reviewed-by: Martin Peres <martin.peres at free.fr>
Martin Peres
2016-Apr-19 21:06 UTC
[Nouveau] [PATCH v4 15/37] clk: allow boosting only when NvBoost is set
On 18/04/16 22:13, Karol Herbst wrote:> 0: base clock from the vbios is max clock > 1: boost only to boost clock from the vbios (default)As commented upon on IRC, I would prefer us to play it super safe and stick to the base clock until we have power monitoring working, at which point we may make 1 the default. Please change it to keep my R-b :)> 2: boost to max clock available > > v2: moved into nvkm_cstate_valid > v4: check the existence of the clocks before limiting > > Signed-off-by: Karol Herbst <nouveau at karolherbst.de> > Reviewed-by: Martin Peres <martin.peres at free.fr> > --- > drm/nouveau/include/nvkm/subdev/clk.h | 9 ++++++++- > drm/nouveau/nvkm/subdev/clk/base.c | 33 ++++++++++++++++++++++++++++++++- > drm/nouveau/nvkm/subdev/clk/gf100.c | 2 +- > drm/nouveau/nvkm/subdev/clk/gk104.c | 2 +- > 4 files changed, 42 insertions(+), 4 deletions(-) > > diff --git a/drm/nouveau/include/nvkm/subdev/clk.h b/drm/nouveau/include/nvkm/subdev/clk.h > index 6226f0d..99ee05c 100644 > --- a/drm/nouveau/include/nvkm/subdev/clk.h > +++ b/drm/nouveau/include/nvkm/subdev/clk.h > @@ -68,7 +68,8 @@ struct nvkm_pstate { > struct nvkm_domain { > enum nv_clk_src name; > u8 bios; /* 0xff for none */ > -#define NVKM_CLK_DOM_FLAG_CORE 0x01 > +#define NVKM_CLK_DOM_FLAG_CORE 0x01 > +#define NVKM_CLK_DOM_FLAG_BASECLK 0x02 > u8 flags; > const char *mname; > int mdiv; > @@ -98,6 +99,12 @@ struct nvkm_clk { > int dstate; /* display adjustment (min+) */ > > bool allow_reclock; > +#define NVKM_CLK_BOOST_NONE 0x0 > +#define NVKM_CLK_BOOST_AVG 0x1 > +#define NVKM_CLK_BOOST_FULL 0x2 > + u8 boost_mode; > + u32 base_khz; > + u32 boost_khz; > > /*XXX: die, these are here *only* to support the completely > * bat-shit insane what-was-nouveau_hw.c code > diff --git a/drm/nouveau/nvkm/subdev/clk/base.c b/drm/nouveau/nvkm/subdev/clk/base.c > index 21f6369..a9a3666 100644 > --- a/drm/nouveau/nvkm/subdev/clk/base.c > +++ b/drm/nouveau/nvkm/subdev/clk/base.c > @@ -24,6 +24,7 @@ > #include "priv.h" > > #include <subdev/bios.h> > +#include <subdev/bios/baseclock.h> > #include <subdev/bios/boost.h> > #include <subdev/bios/cstep.h> > #include <subdev/bios/perf.h> > @@ -77,9 +78,25 @@ nvkm_clk_adjust(struct nvkm_clk *clk, bool adjust, > static bool > nvkm_cstate_valid(struct nvkm_clk *clk, struct nvkm_cstate *cstate, u32 max_volt, int temp) > { > + const struct nvkm_domain *domain = clk->domains; > struct nvkm_volt *volt = clk->subdev.device->volt; > int voltage; > > + while (domain && domain->name != nv_clk_src_max) { > + if (domain->flags & NVKM_CLK_DOM_FLAG_BASECLK) { > + u32 freq = cstate->domain[domain->name]; > + switch (clk->boost_mode) { > + case NVKM_CLK_BOOST_NONE: > + if (clk->base_khz && freq > clk->base_khz) > + return false; > + case NVKM_CLK_BOOST_AVG: > + if (clk->boost_khz && freq > clk->boost_khz) > + return false; > + } > + } > + domain++; > + } > + > if (!volt) > return true; > > @@ -641,10 +658,24 @@ int > nvkm_clk_ctor(const struct nvkm_clk_func *func, struct nvkm_device *device, > int index, bool allow_reclock, struct nvkm_clk *clk) > { > + struct nvkm_subdev *subdev = &clk->subdev; > + struct nvkm_bios *bios = device->bios; > int ret, idx, arglen; > const char *mode; > + struct nvbios_baseclk_header h; > + > + nvkm_subdev_ctor(&nvkm_clk, device, index, subdev); > + > + clk->boost_mode = nvkm_longopt(device->cfgopt, "NvBoost", > + NVKM_CLK_BOOST_AVG); > + if (bios && !nvbios_baseclock_parse(bios, &h)) { > + struct nvbios_baseclk_entry base, boost; > + if (!nvbios_baseclock_entry(bios, &h, h.boost_id, &boost)) > + clk->boost_khz = boost.clock_mhz * 1000; > + if (!nvbios_baseclock_entry(bios, &h, h.base_id, &base)) > + clk->base_khz = base.clock_mhz * 1000; > + } > > - nvkm_subdev_ctor(&nvkm_clk, device, index, &clk->subdev); > clk->func = func; > INIT_LIST_HEAD(&clk->states); > clk->domains = func->domains; > diff --git a/drm/nouveau/nvkm/subdev/clk/gf100.c b/drm/nouveau/nvkm/subdev/clk/gf100.c > index 78c449b..71b7c9f 100644 > --- a/drm/nouveau/nvkm/subdev/clk/gf100.c > +++ b/drm/nouveau/nvkm/subdev/clk/gf100.c > @@ -443,7 +443,7 @@ gf100_clk = { > { nv_clk_src_hubk06 , 0x00 }, > { nv_clk_src_hubk01 , 0x01 }, > { nv_clk_src_copy , 0x02 }, > - { nv_clk_src_gpc , 0x03, 0, "core", 2000 }, > + { nv_clk_src_gpc , 0x03, NVKM_CLK_DOM_FLAG_BASECLK, "core", 2000 }, > { nv_clk_src_rop , 0x04 }, > { nv_clk_src_mem , 0x05, 0, "memory", 1000 }, > { nv_clk_src_vdec , 0x06 }, > diff --git a/drm/nouveau/nvkm/subdev/clk/gk104.c b/drm/nouveau/nvkm/subdev/clk/gk104.c > index 975c401..639234f 100644 > --- a/drm/nouveau/nvkm/subdev/clk/gk104.c > +++ b/drm/nouveau/nvkm/subdev/clk/gk104.c > @@ -485,7 +485,7 @@ gk104_clk = { > .domains = { > { nv_clk_src_crystal, 0xff }, > { nv_clk_src_href , 0xff }, > - { nv_clk_src_gpc , 0x00, NVKM_CLK_DOM_FLAG_CORE, "core", 2000 }, > + { nv_clk_src_gpc , 0x00, NVKM_CLK_DOM_FLAG_CORE | NVKM_CLK_DOM_FLAG_BASECLK, "core", 2000 }, > { nv_clk_src_hubk07 , 0x01, NVKM_CLK_DOM_FLAG_CORE }, > { nv_clk_src_rop , 0x02, NVKM_CLK_DOM_FLAG_CORE }, > { nv_clk_src_mem , 0x03, 0, "memory", 500 },
Martin Peres
2016-Apr-19 21:10 UTC
[Nouveau] [PATCH v4 16/37] volt: don't require perfect fit
On 18/04/16 22:13, Karol Herbst wrote:> if we calculate the voltage in the table right, we get all kinds of values, > which never fit the hardware steps, so we use the closest higher value the > hardware can do. > > v3: simplify the implementation > > Signed-off-by: Karol Herbst <nouveau at karolherbst.de> > --- > drm/nouveau/nvkm/subdev/volt/base.c | 22 +++++++++++++++++----- > 1 file changed, 17 insertions(+), 5 deletions(-) > > diff --git a/drm/nouveau/nvkm/subdev/volt/base.c b/drm/nouveau/nvkm/subdev/volt/base.c > index d72bd4a..028c6e2 100644 > --- a/drm/nouveau/nvkm/subdev/volt/base.c > +++ b/drm/nouveau/nvkm/subdev/volt/base.c > @@ -51,18 +51,30 @@ static int > nvkm_volt_set(struct nvkm_volt *volt, u32 uv) > { > struct nvkm_subdev *subdev = &volt->subdev; > - int i, ret = -EINVAL; > + int i, ret = -EINVAL, best_err = uv, best = -1;"best_err = uv" is not a safe value. You may set best_err to the maximum voltage, anything under that may fail in theory. With this fixed, this is: Reviewed-by: Martin Peres <martin.peres at free.fr>> > if (volt->func->volt_set) > return volt->func->volt_set(volt, uv); > > for (i = 0; i < volt->vid_nr; i++) { > - if (volt->vid[i].uv == uv) { > - ret = volt->func->vid_set(volt, volt->vid[i].vid); > - nvkm_debug(subdev, "set %duv: %d\n", uv, ret); > + int err = volt->vid[i].uv - uv; > + if (err < 0 || err > best_err) > + continue; > + > + best_err = err; > + best = i; > + if (best_err == 0) > break; > - } > } > + > + if (best == -1) { > + nvkm_error(subdev, "couldn't set %iuv\n", uv); > + return ret; > + } > + > + ret = volt->func->vid_set(volt, volt->vid[best].vid); > + nvkm_debug(subdev, "set req %duv to %duv: %d\n", uv, > + volt->vid[best].uv, ret); > return ret; > } >
Martin Peres
2016-Apr-19 21:11 UTC
[Nouveau] [PATCH v4 17/37] bios/vmap: unk0 field is the mode
On 18/04/16 22:13, Karol Herbst wrote:> this selects which formula is used to calculate the voltage. > > depending on the value, the entry maps to a different voltage and even enables > if the temperature has any effect or not. This is easy to observe with the > binary driver. > > Signed-off-by: Karol Herbst <nouveau at karolherbst.de>Reviewed-by: Martin Peres <martin.peres at free.fr>> --- > drm/nouveau/include/nvkm/subdev/bios/vmap.h | 2 +- > drm/nouveau/nvkm/subdev/bios/vmap.c | 2 +- > 2 files changed, 2 insertions(+), 2 deletions(-) > > diff --git a/drm/nouveau/include/nvkm/subdev/bios/vmap.h b/drm/nouveau/include/nvkm/subdev/bios/vmap.h > index ae2f27b..8fa1294 100644 > --- a/drm/nouveau/include/nvkm/subdev/bios/vmap.h > +++ b/drm/nouveau/include/nvkm/subdev/bios/vmap.h > @@ -11,7 +11,7 @@ u16 nvbios_vmap_parse(struct nvkm_bios *, u8 *ver, u8 *hdr, u8 *cnt, u8 *len, > struct nvbios_vmap *); > > struct nvbios_vmap_entry { > - u8 unk0; > + u8 mode; > u8 link; > u32 min; > u32 max; > diff --git a/drm/nouveau/nvkm/subdev/bios/vmap.c b/drm/nouveau/nvkm/subdev/bios/vmap.c > index f2295e1..32bd8b1 100644 > --- a/drm/nouveau/nvkm/subdev/bios/vmap.c > +++ b/drm/nouveau/nvkm/subdev/bios/vmap.c > @@ -105,7 +105,7 @@ nvbios_vmap_entry_parse(struct nvkm_bios *bios, int idx, u8 *ver, u8 *len, > info->arg[2] = nvbios_rd32(bios, vmap + 0x10); > break; > case 0x20: > - info->unk0 = nvbios_rd08(bios, vmap + 0x00); > + info->mode = nvbios_rd08(bios, vmap + 0x00); > info->link = nvbios_rd08(bios, vmap + 0x01); > info->min = nvbios_rd32(bios, vmap + 0x02); > info->max = nvbios_rd32(bios, vmap + 0x06);
On 18/04/16 22:13, Karol Herbst wrote:> Signed-off-by: Karol Herbst <nouveau at karolherbst.de> > --- > bin/nv_cmp_volt.c | 2 +- > drm/nouveau/include/nvkm/subdev/volt.h | 2 ++ > drm/nouveau/nvkm/subdev/volt/base.c | 12 ++++++++++++ > drm/nouveau/nvkm/subdev/volt/priv.h | 1 + > 4 files changed, 16 insertions(+), 1 deletion(-) > > diff --git a/bin/nv_cmp_volt.c b/bin/nv_cmp_volt.c > index 34147b9..e61056c 100644 > --- a/bin/nv_cmp_volt.c > +++ b/bin/nv_cmp_volt.c > @@ -53,7 +53,7 @@ main(int argc, char **argv) > > ret = u_device("lib", argv[0], "error", true, true, > (1ULL << NVKM_SUBDEV_CLK) | > -// (1ULL << NVKM_SUBDEV_FUSE) | > + (1ULL << NVKM_SUBDEV_FUSE) |This change comes a little early, as fuse would be an implementation detail (coming in the next patch) to read the speedo. Either way, I do not really care, as this won't go in the kernel anyway and it does not hurt to enable this a little early. Reviewed-by: Martin Peres <martin.peres at free.fr>> (1ULL << NVKM_SUBDEV_GPIO) | > // (1ULL << NVKM_SUBDEV_I2C) | > (1ULL << NVKM_SUBDEV_PCI) | > diff --git a/drm/nouveau/include/nvkm/subdev/volt.h b/drm/nouveau/include/nvkm/subdev/volt.h > index f223577..4cb0292 100644 > --- a/drm/nouveau/include/nvkm/subdev/volt.h > +++ b/drm/nouveau/include/nvkm/subdev/volt.h > @@ -20,6 +20,8 @@ struct nvkm_volt { > u8 max0_id; > u8 max1_id; > u8 max2_id; > + > + int speedo; > }; > > int nvkm_volt_map(struct nvkm_volt *volt, u8 id, u8 temperature); > diff --git a/drm/nouveau/nvkm/subdev/volt/base.c b/drm/nouveau/nvkm/subdev/volt/base.c > index 028c6e2..cecfac6 100644 > --- a/drm/nouveau/nvkm/subdev/volt/base.c > +++ b/drm/nouveau/nvkm/subdev/volt/base.c > @@ -201,6 +201,14 @@ nvkm_volt_parse_bios(struct nvkm_bios *bios, struct nvkm_volt *volt) > } > > static int > +nvkm_volt_speedo_read(struct nvkm_volt *volt) > +{ > + if (volt->func->speedo_read) > + return volt->func->speedo_read(volt); > + return -EINVAL; > +} > + > +static int > nvkm_volt_init(struct nvkm_subdev *subdev) > { > struct nvkm_volt *volt = nvkm_volt(subdev); > @@ -262,6 +270,10 @@ nvkm_volt_ctor(const struct nvkm_volt_func *func, struct nvkm_device *device, > volt->vid[i].vid, volt->vid[i].uv); > } > } > + > + volt->speedo = nvkm_volt_speedo_read(volt); > + if (volt->speedo > 0) > + nvkm_debug(&volt->subdev, "speedo %x\n", volt->speedo); > } > > int > diff --git a/drm/nouveau/nvkm/subdev/volt/priv.h b/drm/nouveau/nvkm/subdev/volt/priv.h > index d5140d9..9b34e9f 100644 > --- a/drm/nouveau/nvkm/subdev/volt/priv.h > +++ b/drm/nouveau/nvkm/subdev/volt/priv.h > @@ -14,6 +14,7 @@ struct nvkm_volt_func { > int (*vid_get)(struct nvkm_volt *); > int (*vid_set)(struct nvkm_volt *, u8 vid); > int (*set_id)(struct nvkm_volt *, u8 id, int condition); > + int (*speedo_read)(struct nvkm_volt *); > }; > > int nvkm_voltgpio_init(struct nvkm_volt *);
Martin Peres
2016-Apr-19 21:43 UTC
[Nouveau] [PATCH v4 19/37] volt: add gf100 subdev with speedo
On 18/04/16 22:13, Karol Herbst wrote:> Signed-off-by: Karol Herbst <nouveau at karolherbst.de> > --- > drm/nouveau/include/nvkm/subdev/volt.h | 1 + > drm/nouveau/nvkm/engine/device/base.c | 17 +++++----- > drm/nouveau/nvkm/subdev/volt/Kbuild | 1 + > drm/nouveau/nvkm/subdev/volt/gf100.c | 59 ++++++++++++++++++++++++++++++++++ > 4 files changed, 70 insertions(+), 8 deletions(-) > create mode 100644 drm/nouveau/nvkm/subdev/volt/gf100.c > > diff --git a/drm/nouveau/include/nvkm/subdev/volt.h b/drm/nouveau/include/nvkm/subdev/volt.h > index 4cb0292..25588c7 100644 > --- a/drm/nouveau/include/nvkm/subdev/volt.h > +++ b/drm/nouveau/include/nvkm/subdev/volt.h > @@ -30,6 +30,7 @@ int nvkm_volt_get(struct nvkm_volt *); > int nvkm_volt_set_id(struct nvkm_volt *, u8 id, u8 min_id, int condition); > > int nv40_volt_new(struct nvkm_device *, int, struct nvkm_volt **); > +int gf100_volt_new(struct nvkm_device *, int, struct nvkm_volt **); > int gk104_volt_new(struct nvkm_device *, int, struct nvkm_volt **); > int gk20a_volt_new(struct nvkm_device *, int, struct nvkm_volt **); > int gm20b_volt_new(struct nvkm_device *, int, struct nvkm_volt **); > diff --git a/drm/nouveau/nvkm/engine/device/base.c b/drm/nouveau/nvkm/engine/device/base.c > index a364efe..528780c 100644 > --- a/drm/nouveau/nvkm/engine/device/base.c > +++ b/drm/nouveau/nvkm/engine/device/base.c > @@ -1357,7 +1357,7 @@ nvc0_chipset = { > .pmu = gf100_pmu_new, > .therm = gt215_therm_new, > .timer = nv41_timer_new, > - .volt = nv40_volt_new, > + .volt = gf100_volt_new, > .ce[0] = gf100_ce_new, > .ce[1] = gf100_ce_new, > .disp = gt215_disp_new, > @@ -1394,7 +1394,7 @@ nvc1_chipset = { > .pmu = gf100_pmu_new, > .therm = gt215_therm_new, > .timer = nv41_timer_new, > - .volt = nv40_volt_new, > + .volt = gf100_volt_new, > .ce[0] = gf100_ce_new, > .disp = gt215_disp_new, > .dma = gf100_dma_new, > @@ -1430,7 +1430,7 @@ nvc3_chipset = { > .pmu = gf100_pmu_new, > .therm = gt215_therm_new, > .timer = nv41_timer_new, > - .volt = nv40_volt_new, > + .volt = gf100_volt_new, > .ce[0] = gf100_ce_new, > .disp = gt215_disp_new, > .dma = gf100_dma_new, > @@ -1466,7 +1466,7 @@ nvc4_chipset = { > .pmu = gf100_pmu_new, > .therm = gt215_therm_new, > .timer = nv41_timer_new, > - .volt = nv40_volt_new, > + .volt = gf100_volt_new, > .ce[0] = gf100_ce_new, > .ce[1] = gf100_ce_new, > .disp = gt215_disp_new, > @@ -1503,7 +1503,7 @@ nvc8_chipset = { > .pmu = gf100_pmu_new, > .therm = gt215_therm_new, > .timer = nv41_timer_new, > - .volt = nv40_volt_new, > + .volt = gf100_volt_new, > .ce[0] = gf100_ce_new, > .ce[1] = gf100_ce_new, > .disp = gt215_disp_new, > @@ -1540,7 +1540,7 @@ nvce_chipset = { > .pmu = gf100_pmu_new, > .therm = gt215_therm_new, > .timer = nv41_timer_new, > - .volt = nv40_volt_new, > + .volt = gf100_volt_new, > .ce[0] = gf100_ce_new, > .ce[1] = gf100_ce_new, > .disp = gt215_disp_new, > @@ -1577,7 +1577,7 @@ nvcf_chipset = { > .pmu = gf100_pmu_new, > .therm = gt215_therm_new, > .timer = nv41_timer_new, > - .volt = nv40_volt_new, > + .volt = gf100_volt_new, > .ce[0] = gf100_ce_new, > .disp = gt215_disp_new, > .dma = gf100_dma_new, > @@ -1612,6 +1612,7 @@ nvd7_chipset = { > .pci = gf106_pci_new, > .therm = gf119_therm_new, > .timer = nv41_timer_new, > + .volt = gf100_volt_new, > .ce[0] = gf100_ce_new, > .disp = gf119_disp_new, > .dma = gf119_dma_new, > @@ -1647,7 +1648,7 @@ nvd9_chipset = { > .pmu = gf119_pmu_new, > .therm = gf119_therm_new, > .timer = nv41_timer_new, > - .volt = nv40_volt_new, > + .volt = gf100_volt_new, > .ce[0] = gf100_ce_new, > .disp = gf119_disp_new, > .dma = gf119_dma_new, > diff --git a/drm/nouveau/nvkm/subdev/volt/Kbuild b/drm/nouveau/nvkm/subdev/volt/Kbuild > index c340762..bcd179b 100644 > --- a/drm/nouveau/nvkm/subdev/volt/Kbuild > +++ b/drm/nouveau/nvkm/subdev/volt/Kbuild > @@ -1,6 +1,7 @@ > nvkm-y += nvkm/subdev/volt/base.o > nvkm-y += nvkm/subdev/volt/gpio.o > nvkm-y += nvkm/subdev/volt/nv40.o > +nvkm-y += nvkm/subdev/volt/gf100.o > nvkm-y += nvkm/subdev/volt/gk104.o > nvkm-y += nvkm/subdev/volt/gk20a.o > nvkm-y += nvkm/subdev/volt/gm20b.o > diff --git a/drm/nouveau/nvkm/subdev/volt/gf100.c b/drm/nouveau/nvkm/subdev/volt/gf100.c > new file mode 100644 > index 0000000..16dc0f1 > --- /dev/null > +++ b/drm/nouveau/nvkm/subdev/volt/gf100.c > @@ -0,0 +1,59 @@ > +/* > + * Copyright 2016 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 "priv.h" > + > +#include <subdev/fuse.h> > + > +static int > +gf100_volt_speedo_read(struct nvkm_volt *volt) > +{ > + struct nvkm_device *device = volt->subdev.device; > + struct nvkm_fuse *fuse = device->fuse; > + > + if (!fuse) > + return -EINVAL; > + > + return nvkm_fuse_read(fuse, 0x1cc); > +} > + > +static const struct nvkm_volt_func > +gf100_volt = { > + .vid_get = nvkm_voltgpio_get, > + .vid_set = nvkm_voltgpio_set, > + .speedo_read = gf100_volt_speedo_read, > +}; > + > +int > +gf100_volt_new(struct nvkm_device *device, int index, struct nvkm_volt **pvolt) > +{ > + struct nvkm_volt *volt; > + int ret; > + > + ret = nvkm_volt_new_(&gf100_volt, device, index, &volt); > + *pvolt = volt; > + if (ret) > + return ret; > + > + return nvkm_voltgpio_init(volt); > +}This is OK because fuse is initialized before volt (drm/nouveau/include/nvkm/core/device.h). This is: Reviewed-by: Martin Peres <martin.peres at free.fr>
On 18/04/16 22:13, Karol Herbst wrote:> I am sure that those are a bit different, but while testing the biggest error > compared to nvidia was -1.5%.Is this still true? I thought we were *much* closer now.> > Without this change we are most of the time around 10% below nvidias voltage, > so this change causes no harm and improves the situation a lot already.Yeah, this is definitely not up to date!> > These coefficients were REed by modifing the voltage map entries and by > calculating the set voltage back until I was able to forecast which voltage > nvidia sets for a given voltage map entry. > > v4: use better coefficients and speedo > > Signed-off-by: Karol Herbst <nouveau at karolherbst.de> > --- > drm/nouveau/nvkm/subdev/volt/base.c | 38 +++++++++++++++++++++++++++++++++++-- > 1 file changed, 36 insertions(+), 2 deletions(-) > > diff --git a/drm/nouveau/nvkm/subdev/volt/base.c b/drm/nouveau/nvkm/subdev/volt/base.c > index cecfac6..5e35d96 100644 > --- a/drm/nouveau/nvkm/subdev/volt/base.c > +++ b/drm/nouveau/nvkm/subdev/volt/base.c > @@ -110,13 +110,47 @@ nvkm_volt_map(struct nvkm_volt *volt, u8 id, u8 temp) > > vmap = nvbios_vmap_entry_parse(bios, id, &ver, &len, &info); > if (vmap) { > + s64 result; > + > + if (volt->speedo < 0) > + return volt->speedo;Hmm, so you will refuse reclocking if the speedo cannot be read... Fair-enough, but I would like to see a warning in the kernel logs.> + > + if (ver == 0x10 || (ver == 0x20 && info.mode == 0)) { > + result = (s64)info.arg[0] / 10; > + result += ((s64)info.arg[1] * volt->speedo) / 10; > + result += ((s64)info.arg[2] * volt->speedo * volt->speedo) / 100000; > + } else if (ver == 0x20) { > + switch (info.mode) { > + /* 0x0 handled above! */ > + case 0x1: > + result = ((s64)info.arg[0] * 15625) >> 18; > + result += ((s64)info.arg[1] * volt->speedo * 15625) >> 18; > + result += ((s64)info.arg[2] * temp * 15625) >> 10; > + result += ((s64)info.arg[3] * volt->speedo * temp * 15625) >> 18; > + result += ((s64)info.arg[4] * volt->speedo * volt->speedo * 15625) >> 30; > + result += ((s64)info.arg[5] * temp * temp * 15625) >> 18; > + break;Well, I can only say that these values got us really really close to what nvidia does... On around 10 GPUs... So... until we know better, let's stick to them. Hopefully, we can figure out where this 15625 comes from. This patch is: Reviewed-by: Martin Peres <martin.peres at free.fr>> + case 0x3: > + result = (info.min + info.max) / 2; > + break; > + case 0x2: > + default: > + result = info.min; > + break; > + } > + } else { > + return -ENODEV; > + } > + > + result = min(max(result, (s64)info.min), (s64)info.max); > + > if (info.link != 0xff) { > int ret = nvkm_volt_map(volt, info.link, temp); > if (ret < 0) > return ret; > - info.min += ret; > + result += ret; > } > - return info.min; > + return result; > } > > return id ? id * 10000 : -ENODEV;
Martin Peres
2016-Apr-19 22:07 UTC
[Nouveau] [PATCH v4 21/37] clk: save the max clock we can set
On 18/04/16 22:13, Karol Herbst wrote:> Signed-off-by: Karol Herbst <nouveau at karolherbst.de>Reviewed-by: Martin Peres <martin.peres at free.fr>> --- > drm/nouveau/include/nvkm/subdev/clk.h | 1 + > drm/nouveau/nvkm/subdev/clk/base.c | 2 ++ > 2 files changed, 3 insertions(+) > > diff --git a/drm/nouveau/include/nvkm/subdev/clk.h b/drm/nouveau/include/nvkm/subdev/clk.h > index 99ee05c..61d99fd 100644 > --- a/drm/nouveau/include/nvkm/subdev/clk.h > +++ b/drm/nouveau/include/nvkm/subdev/clk.h > @@ -105,6 +105,7 @@ struct nvkm_clk { > u8 boost_mode; > u32 base_khz; > u32 boost_khz; > + u32 max_khz; > > /*XXX: die, these are here *only* to support the completely > * bat-shit insane what-was-nouveau_hw.c code > diff --git a/drm/nouveau/nvkm/subdev/clk/base.c b/drm/nouveau/nvkm/subdev/clk/base.c > index a9a3666..1ca25dd 100644 > --- a/drm/nouveau/nvkm/subdev/clk/base.c > +++ b/drm/nouveau/nvkm/subdev/clk/base.c > @@ -257,6 +257,8 @@ nvkm_cstate_new(struct nvkm_clk *clk, int idx, struct nvkm_pstate *pstate) > u32 freq = nvkm_clk_adjust(clk, true, pstate->pstate, > domain->bios, cstepX.freq); > cstate->domain[domain->name] = freq; > + if (domain->flags & NVKM_CLK_DOM_FLAG_BASECLK) > + clk->max_khz = max(clk->max_khz, freq); > } > domain++; > }
Martin Peres
2016-Apr-20 20:12 UTC
[Nouveau] [PATCH v4 22/37] clk: rename nvkm_pstate_calc to nvkm_clk_update
On 18/04/16 22:13, Karol Herbst wrote:> this function will be used to update the current clock state. > > This will happen for various reasons: > * temperature changes (may change cstate and/or voltage) > * user changes boost mode > * load changes > > v2: add wait parameter > > Signed-off-by: Karol Herbst <nouveau at karolherbst.de> > --- > drm/nouveau/include/nvkm/subdev/clk.h | 1 + > drm/nouveau/nvkm/subdev/clk/base.c | 46 ++++++++++++++++++++--------------- > 2 files changed, 28 insertions(+), 19 deletions(-) > > diff --git a/drm/nouveau/include/nvkm/subdev/clk.h b/drm/nouveau/include/nvkm/subdev/clk.h > index 61d99fd..77d94c1 100644 > --- a/drm/nouveau/include/nvkm/subdev/clk.h > +++ b/drm/nouveau/include/nvkm/subdev/clk.h > @@ -120,6 +120,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 *, int req, int rel); > +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 1ca25dd..bfc6a49 100644 > --- a/drm/nouveau/nvkm/subdev/clk/base.c > +++ b/drm/nouveau/nvkm/subdev/clk/base.c > @@ -274,11 +274,14 @@ static int > nvkm_pstate_prog(struct nvkm_clk *clk, int pstatei) > { > struct nvkm_subdev *subdev = &clk->subdev; > - struct nvkm_ram *ram = subdev->device->fb->ram; > + struct nvkm_fb *fb = subdev->device->fb; > struct nvkm_pci *pci = subdev->device->pci; > struct nvkm_pstate *pstate; > int ret, idx = 0; > > + if (pstatei == -1) > + return 0; > + > list_for_each_entry(pstate, &clk->states, head) { > if (idx++ == pstatei) > break; > @@ -289,7 +292,8 @@ nvkm_pstate_prog(struct nvkm_clk *clk, int pstatei) > > nvkm_pcie_set_link(pci, pstate->pcie_speed, pstate->pcie_width); > > - if (ram && ram->func->calc) { > + if (fb && fb->ram && fb->ram->func->calc) { > + struct nvkm_ram *ram = fb->ram; > int khz = pstate->base.domain[nv_clk_src_mem]; > do { > ret = ram->func->calc(ram, khz);All the changes to this file do not belong to this patch. What are you doing here anyway?> @@ -303,11 +307,11 @@ 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; > - int pstate; > + int pstate, ret; > > if (!atomic_xchg(&clk->waiting, 0)) > return; > @@ -327,21 +331,25 @@ nvkm_pstate_work(struct work_struct *work) > } > > nvkm_trace(subdev, "-> %d\n", pstate); > - if (pstate != clk->pstate) { > - int ret = nvkm_pstate_prog(clk, pstate); > - if (ret) { > - nvkm_error(subdev, "error setting pstate %d: %d\n", > - pstate, ret); > - } > + ret = nvkm_pstate_prog(clk, pstate); > + if (ret) { > + nvkm_error(subdev, "error setting pstate %d: %d\n", > + pstate, ret); > }Why did you get rid of the if?> > wake_up_all(&clk->wait); > 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;This is also not part of the rename. Why do you meed this suddenly?> + > atomic_set(&clk->waiting, 1); > schedule_work(&clk->work); > if (wait) > @@ -531,7 +539,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; > } > @@ -543,7 +551,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 > @@ -553,7 +561,7 @@ nvkm_clk_tstate(struct nvkm_clk *clk, int req, int rel) > if ( rel) clk->tstate += rel; > clk->tstate = min(clk->tstate, 0); > clk->tstate = max(clk->tstate, -(clk->state_nr - 1)); > - return nvkm_pstate_calc(clk, true); > + return nvkm_clk_update(clk, true); > } > > int > @@ -563,7 +571,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 > @@ -571,7 +579,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; > } > > @@ -626,7 +634,7 @@ nvkm_clk_init(struct nvkm_subdev *subdev) > clk->tstate = 0; > clk->dstate = 0; > clk->pstate = -1; > - nvkm_pstate_calc(clk, true); > + nvkm_clk_update(clk, true); > return 0; > } > > @@ -685,7 +693,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); >
Martin Peres
2016-Apr-20 20:45 UTC
[Nouveau] [PATCH v4 25/37] clk: remove dstate and tstate
On 18/04/16 22:13, Karol Herbst wrote:> we won't need them, because we will adjust the clocks depending on engine loads > later on anyway. It also simplifies the clocking logic.You can also say that the code was just mocked up anyway.> > Signed-off-by: Karol Herbst <nouveau at karolherbst.de> > --- > drm/nouveau/include/nvkm/subdev/clk.h | 4 ---- > drm/nouveau/nvkm/subdev/clk/base.c | 28 ++-------------------------- > 2 files changed, 2 insertions(+), 30 deletions(-) > > diff --git a/drm/nouveau/include/nvkm/subdev/clk.h b/drm/nouveau/include/nvkm/subdev/clk.h > index 77d94c1..db52e65 100644 > --- a/drm/nouveau/include/nvkm/subdev/clk.h > +++ b/drm/nouveau/include/nvkm/subdev/clk.h > @@ -95,8 +95,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 tstate; /* thermal adjustment (max-) */ > - int dstate; /* display adjustment (min+) */ > > bool allow_reclock; > #define NVKM_CLK_BOOST_NONE 0x0 > @@ -118,8 +116,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 *, int req, int rel); > int nvkm_clk_update(struct nvkm_clk *, bool wait); > > int nv04_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 bfc6a49..3867ab7 100644 > --- a/drm/nouveau/nvkm/subdev/clk/base.c > +++ b/drm/nouveau/nvkm/subdev/clk/base.c > @@ -317,15 +317,13 @@ 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 D %d\n", > + nvkm_trace(subdev, "P %d PWR %d U(AC) %d U(DC) %d A %d\n", > clk->pstate, clk->pwrsrc, clk->ustate_ac, clk->ustate_dc, > - clk->astate, clk->tstate, clk->dstate); > + clk->astate); > > 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 + clk->tstate); > - pstate = max(pstate, clk->dstate); > } else { > pstate = clk->pstate = -1; > } > @@ -554,26 +552,6 @@ nvkm_clk_astate(struct nvkm_clk *clk, int req, int rel, bool wait) > return nvkm_clk_update(clk, wait); > } > > -int > -nvkm_clk_tstate(struct nvkm_clk *clk, int req, int rel) > -{ > - if (!rel) clk->tstate = req; > - if ( rel) clk->tstate += rel; > - clk->tstate = min(clk->tstate, 0); > - clk->tstate = max(clk->tstate, -(clk->state_nr - 1)); > - return nvkm_clk_update(clk, true); > -} > - > -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) > { > @@ -631,8 +609,6 @@ nvkm_clk_init(struct nvkm_subdev *subdev) > return clk->func->init(clk); > > clk->astate = clk->state_nr - 1; > - clk->tstate = 0; > - clk->dstate = 0; > clk->pstate = -1; > nvkm_clk_update(clk, true); > return 0;
Martin Peres
2016-Apr-20 20:47 UTC
[Nouveau] [PATCH v4 26/37] therm: don't cancel the timer
On 18/04/16 22:14, Karol Herbst wrote:> we will need a always running therm daemon to adjust the voltage/clocks on the > fly. > > Signed-off-by: Karol Herbst <nouveau at karolherbst.de>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 8894fee..0c0feec 100644 > --- a/drm/nouveau/nvkm/subdev/therm/base.c > +++ b/drm/nouveau/nvkm/subdev/therm/base.c > @@ -92,7 +92,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); > @@ -102,11 +101,9 @@ nvkm_therm_update(struct nvkm_therm *therm, int mode) > > switch (mode) { > case NVKM_THERM_CTRL_MANUAL: > - nvkm_timer_alarm_cancel(tmr, &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) { > @@ -119,18 +116,16 @@ nvkm_therm_update(struct nvkm_therm *therm, int mode) > case NVBIOS_THERM_FAN_OTHER: > if (therm->cstate) > duty = therm->cstate; > - poll = false; > break; > } > immd = false; > break; > case NVKM_THERM_CTRL_NONE: > default: > - nvkm_timer_alarm_cancel(tmr, &therm->alarm); > - poll = false; > + break; > } > > - if (list_empty(&therm->alarm.head) && poll) > + if (list_empty(&therm->alarm.head)) > nvkm_timer_alarm(tmr, 1000000000ULL, &therm->alarm); > spin_unlock_irqrestore(&therm->lock, flags); >
Martin Peres
2016-Apr-20 20:53 UTC
[Nouveau] [PATCH v4 27/37] clk: make pstate a pointer to nvkm_pstate
On 18/04/16 22:14, Karol Herbst wrote:> we will access the current set cstate at least every second and this safes ussaves> some CPU cycles looking them up every second. > > Signed-off-by: Karol Herbst <nouveau at karolherbst.de> > --- > drm/nouveau/include/nvkm/subdev/clk.h | 2 +- > drm/nouveau/nvkm/engine/device/ctrl.c | 5 ++++- > drm/nouveau/nvkm/subdev/clk/base.c | 12 ++++++++---- > drm/nouveau/nvkm/subdev/pmu/gk20a.c | 23 +++++++---------------- > 4 files changed, 20 insertions(+), 22 deletions(-) > > diff --git a/drm/nouveau/include/nvkm/subdev/clk.h b/drm/nouveau/include/nvkm/subdev/clk.h > index db52e65..4fb2c1b 100644 > --- a/drm/nouveau/include/nvkm/subdev/clk.h > +++ b/drm/nouveau/include/nvkm/subdev/clk.h > @@ -91,7 +91,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 039e8a4..cb85266 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 = -1; > } 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 3867ab7..762dfe2 100644 > --- a/drm/nouveau/nvkm/subdev/clk/base.c > +++ b/drm/nouveau/nvkm/subdev/clk/base.c > @@ -288,7 +288,7 @@ nvkm_pstate_prog(struct nvkm_clk *clk, int pstatei) > } > > 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); > > @@ -317,15 +317,19 @@ 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 = -1; > nvkm_trace(subdev, "P %d PWR %d U(AC) %d U(DC) %d A %d\n", > - clk->pstate, clk->pwrsrc, clk->ustate_ac, clk->ustate_dc, > + pstate, clk->pwrsrc, clk->ustate_ac, clk->ustate_dc, > clk->astate); > > pstate = clk->pwrsrc ? clk->ustate_ac : clk->ustate_dc; > if (clk->state_nr && pstate != -1) { > pstate = (pstate < 0) ? clk->astate : pstate; > } else { > - pstate = clk->pstate = -1; > + pstate = -1;Isn't "clk->pstate = NULL;" missing here? Why did you change this code otherwise?> } > > nvkm_trace(subdev, "-> %d\n", pstate); > @@ -609,7 +613,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; > 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 f996d90..6f0d290 100644 > --- a/drm/nouveau/nvkm/subdev/pmu/gk20a.c > +++ b/drm/nouveau/nvkm/subdev/pmu/gk20a.c > @@ -57,24 +57,21 @@ gk20a_pmu_dvfs_target(struct gk20a_pmu *pmu, int *state) > } > > static int > -gk20a_pmu_dvfs_get_cur_state(struct gk20a_pmu *pmu, int *state) > -{ > - struct nvkm_clk *clk = pmu->base.subdev.device->clk; > - > - *state = clk->pstate; > - return 0; > -} > - > -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)); > @@ -150,12 +147,6 @@ gk20a_pmu_dvfs_work(struct nvkm_alarm *alarm) > nvkm_trace(subdev, "utilization = %d %%, avg_load = %d %%\n", > utilization, data->avg_load); > > - ret = gk20a_pmu_dvfs_get_cur_state(pmu, &state); > - if (ret) { > - nvkm_warn(subdev, "failed to get current state\n"); > - goto resched; > - } > - > 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);
Martin Peres
2016-Apr-20 21:38 UTC
[Nouveau] [PATCH v4 28/37] clk: hold information about the current cstate status
On 18/04/16 22:14, Karol Herbst wrote:> Signed-off-by: Karol Herbst <nouveau at karolherbst.de> > --- > drm/nouveau/include/nvkm/subdev/clk.h | 5 +++++ > drm/nouveau/nvkm/subdev/clk/base.c | 32 +++++++++++++++++++++++++------- > 2 files changed, 30 insertions(+), 7 deletions(-) > > diff --git a/drm/nouveau/include/nvkm/subdev/clk.h b/drm/nouveau/include/nvkm/subdev/clk.h > index 4fb2c1b..6deda96 100644 > --- a/drm/nouveau/include/nvkm/subdev/clk.h > +++ b/drm/nouveau/include/nvkm/subdev/clk.h > @@ -95,6 +95,11 @@ 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 *set_cstate; > +#define NVKM_CLK_CSTATE_DEFAULT -1 > +#define NVKM_CLK_CSTATE_BASE -2 > +#define NVKM_CLK_CSTATE_HIGHEST -3 > + int exp_cstate; > > 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 762dfe2..23f4cfe 100644 > --- a/drm/nouveau/nvkm/subdev/clk/base.c > +++ b/drm/nouveau/nvkm/subdev/clk/base.c > @@ -152,9 +152,14 @@ static struct nvkm_cstate * > nvkm_cstate_get(struct nvkm_clk *clk, struct nvkm_pstate *pstate, int cstatei) > { > struct nvkm_cstate *cstate; > - if (cstatei == -1) > + switch (cstatei) { > + case NVKM_CLK_CSTATE_HIGHEST: > return list_entry(pstate->list.prev, 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->cstate == cstatei) > return cstate; > @@ -173,6 +178,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); > @@ -199,6 +207,7 @@ nvkm_cstate_prog(struct nvkm_clk *clk, struct nvkm_pstate *pstate, int cstatei) > > ret = clk->func->calc(clk, cstate); > if (ret == 0) { > + clk->set_cstate = cstate; > ret = clk->func->prog(clk); > clk->func->tidy(clk); > } > @@ -303,7 +312,7 @@ nvkm_pstate_prog(struct nvkm_clk *clk, int pstatei) > ram->func->tidy(ram); > } > > - return nvkm_cstate_prog(clk, pstate, -1); > + return nvkm_cstate_prog(clk, pstate, clk->exp_cstate); > } > > static void > @@ -321,9 +330,9 @@ nvkm_clk_update_work(struct work_struct *work) > pstate = clk->pstate->pstate; > else > pstate = -1; > - nvkm_trace(subdev, "P %d PWR %d U(AC) %d U(DC) %d A %d\n", > + nvkm_trace(subdev, "P %d PWR %d U(AC) %d U(DC) %d A %d C %d\n", > pstate, clk->pwrsrc, clk->ustate_ac, clk->ustate_dc, > - clk->astate); > + clk->astate, clk->exp_cstate); > > pstate = clk->pwrsrc ? clk->ustate_ac : clk->ustate_dc; > if (clk->state_nr && pstate != -1) { > @@ -541,6 +550,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_cstate = NVKM_CLK_CSTATE_HIGHEST;I am really not a fan of this astate. We will revisit this when we are done with manual reclocking! In the mean time, this is: Reviewed-by: Martin Peres <martin.peres at free.fr>> return nvkm_clk_update(clk, true); > } > return ret; > @@ -553,6 +563,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_cstate = NVKM_CLK_CSTATE_BASE; > return nvkm_clk_update(clk, wait); > } > > @@ -614,6 +625,8 @@ nvkm_clk_init(struct nvkm_subdev *subdev) > > clk->astate = clk->state_nr - 1; > clk->pstate = NULL; > + clk->exp_cstate = NVKM_CLK_CSTATE_DEFAULT; > + clk->set_cstate = NULL; > nvkm_clk_update(clk, true); > return 0; > } > @@ -698,15 +711,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_cstate = 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_cstate = 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_cstate = NVKM_CLK_CSTATE_HIGHEST; > + } > > return 0; > }
Martin Peres
2016-Apr-20 21:59 UTC
[Nouveau] [PATCH v4 29/37] clk: we should pass the pstate id around not the index in the list
On 18/04/16 22:14, Karol Herbst wrote:> this makes the code easier, because we can compare the id with pstate->pstate > and safe us the trouble iterating over the entire pstate listsave us. Not the easiest patch to read, but it seems alright and it has been tested, so: Reviewed-by: Martin Peres <martin.peres at free.fr>> > Signed-off-by: Karol Herbst <nouveau at karolherbst.de> > --- > drm/nouveau/nouveau_debugfs.c | 6 ++--- > drm/nouveau/nvkm/subdev/clk/base.c | 49 +++++++++++--------------------------- > 2 files changed, 17 insertions(+), 38 deletions(-) > > diff --git a/drm/nouveau/nouveau_debugfs.c b/drm/nouveau/nouveau_debugfs.c > index 31b309f..334b472 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 23f4cfe..7f86e41 100644 > --- a/drm/nouveau/nvkm/subdev/clk/base.c > +++ b/drm/nouveau/nvkm/subdev/clk/base.c > @@ -280,23 +280,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 == -1) > + if (pstateid == -1) > 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); > @@ -496,30 +499,6 @@ 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) > { > int ret = 1; > @@ -533,23 +512,23 @@ 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; > + return ret; > } > > int > nvkm_clk_ustate(struct nvkm_clk *clk, int req, int pwr) > { > - int ret = nvkm_clk_ustate_update(clk, req); > + int ret = req; > if (ret >= 0) { > - if (ret -= 2, pwr) clk->ustate_ac = ret; > - else clk->ustate_dc = ret; > + if (pwr) clk->ustate_ac = ret; > + else clk->ustate_dc = ret; > clk->exp_cstate = NVKM_CLK_CSTATE_HIGHEST; > return nvkm_clk_update(clk, true); > } > @@ -623,7 +602,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 = -1; > clk->pstate = NULL; > clk->exp_cstate = NVKM_CLK_CSTATE_DEFAULT; > clk->set_cstate = NULL;
Martin Peres
2016-Apr-20 22:00 UTC
[Nouveau] [PATCH v4 30/37] clk: seperate the locking from the implementation in nvkm_clk_update
sepArate On 18/04/16 22:14, Karol Herbst wrote: Would be nice to say what for. With a better commit message and the typo fixed: Reviewed-by: Martin Peres <martin.peres at free.fr>> Signed-off-by: Karol Herbst <nouveau at karolherbst.de> > --- > drm/nouveau/nvkm/subdev/clk/base.c | 16 ++++++++++++---- > 1 file changed, 12 insertions(+), 4 deletions(-) > > diff --git a/drm/nouveau/nvkm/subdev/clk/base.c b/drm/nouveau/nvkm/subdev/clk/base.c > index 7f86e41..d6f239f 100644 > --- a/drm/nouveau/nvkm/subdev/clk/base.c > +++ b/drm/nouveau/nvkm/subdev/clk/base.c > @@ -319,14 +319,11 @@ nvkm_pstate_prog(struct nvkm_clk *clk, int pstateid) > } > > static void > -nvkm_clk_update_work(struct work_struct *work) > +nvkm_clk_update_impl(struct nvkm_clk *clk) > { > - struct nvkm_clk *clk = container_of(work, typeof(*clk), work); > struct nvkm_subdev *subdev = &clk->subdev; > int pstate, ret; > > - if (!atomic_xchg(&clk->waiting, 0)) > - return; > clk->pwrsrc = power_supply_is_system_supplied(); > > if (clk->pstate) > @@ -350,6 +347,17 @@ nvkm_clk_update_work(struct work_struct *work) > nvkm_error(subdev, "error setting pstate %d: %d\n", > pstate, ret); > } > +} > + > +static void > +nvkm_clk_update_work(struct work_struct *work) > +{ > + struct nvkm_clk *clk = container_of(work, typeof(*clk), work); > + > + if (!atomic_xchg(&clk->waiting, 0)) > + return; > + > + nvkm_clk_update_impl(clk); > > wake_up_all(&clk->wait); > nvkm_notify_get(&clk->pwrsrc_ntfy);
Martin Peres
2016-Apr-20 22:08 UTC
[Nouveau] [PATCH v4 31/37] clk: split out update code to nv40
On 18/04/16 22:14, Karol Herbst wrote:> this code will change for gf100 and newerReviewed-by: Martin Peres <martin.peres at free.fr>> > Signed-off-by: Karol Herbst <nouveau at karolherbst.de> > --- > drm/nouveau/nvkm/subdev/clk/base.c | 14 ++++++-------- > drm/nouveau/nvkm/subdev/clk/g84.c | 1 + > drm/nouveau/nvkm/subdev/clk/gf100.c | 1 + > drm/nouveau/nvkm/subdev/clk/gk104.c | 1 + > drm/nouveau/nvkm/subdev/clk/gk20a.c | 1 + > drm/nouveau/nvkm/subdev/clk/gm20b.c | 1 + > drm/nouveau/nvkm/subdev/clk/gt215.c | 1 + > drm/nouveau/nvkm/subdev/clk/mcp77.c | 1 + > drm/nouveau/nvkm/subdev/clk/nv40.c | 15 +++++++++++++++ > drm/nouveau/nvkm/subdev/clk/nv50.c | 1 + > drm/nouveau/nvkm/subdev/clk/priv.h | 5 +++++ > 11 files changed, 34 insertions(+), 8 deletions(-) > > diff --git a/drm/nouveau/nvkm/subdev/clk/base.c b/drm/nouveau/nvkm/subdev/clk/base.c > index d6f239f..3c40f67 100644 > --- a/drm/nouveau/nvkm/subdev/clk/base.c > +++ b/drm/nouveau/nvkm/subdev/clk/base.c > @@ -279,7 +279,7 @@ nvkm_cstate_new(struct nvkm_clk *clk, int idx, struct nvkm_pstate *pstate) > /****************************************************************************** > * P-States > *****************************************************************************/ > -static int > +int > nvkm_pstate_prog(struct nvkm_clk *clk, int pstateid) > { > struct nvkm_subdev *subdev = &clk->subdev; > @@ -322,7 +322,10 @@ static void > nvkm_clk_update_impl(struct nvkm_clk *clk) > { > struct nvkm_subdev *subdev = &clk->subdev; > - int pstate, ret; > + int pstate; > + > + if (!clk->func->update) > + return; > > clk->pwrsrc = power_supply_is_system_supplied(); > > @@ -341,12 +344,7 @@ nvkm_clk_update_impl(struct nvkm_clk *clk) > pstate = -1; > } > > - nvkm_trace(subdev, "-> %d\n", pstate); > - ret = nvkm_pstate_prog(clk, pstate); > - if (ret) { > - nvkm_error(subdev, "error setting pstate %d: %d\n", > - pstate, ret); > - } > + clk->func->update(clk, pstate); > } > > static void > diff --git a/drm/nouveau/nvkm/subdev/clk/g84.c b/drm/nouveau/nvkm/subdev/clk/g84.c > index f97e3ec..7b9b30d 100644 > --- a/drm/nouveau/nvkm/subdev/clk/g84.c > +++ b/drm/nouveau/nvkm/subdev/clk/g84.c > @@ -29,6 +29,7 @@ g84_clk = { > .calc = nv50_clk_calc, > .prog = nv50_clk_prog, > .tidy = nv50_clk_tidy, > + .update = nv40_clk_update, > .domains = { > { nv_clk_src_crystal, 0xff }, > { nv_clk_src_href , 0xff }, > diff --git a/drm/nouveau/nvkm/subdev/clk/gf100.c b/drm/nouveau/nvkm/subdev/clk/gf100.c > index 71b7c9f..808e1ed 100644 > --- a/drm/nouveau/nvkm/subdev/clk/gf100.c > +++ b/drm/nouveau/nvkm/subdev/clk/gf100.c > @@ -437,6 +437,7 @@ gf100_clk = { > .calc = gf100_clk_calc, > .prog = gf100_clk_prog, > .tidy = gf100_clk_tidy, > + .update = nv40_clk_update, > .domains = { > { nv_clk_src_crystal, 0xff }, > { nv_clk_src_href , 0xff }, > diff --git a/drm/nouveau/nvkm/subdev/clk/gk104.c b/drm/nouveau/nvkm/subdev/clk/gk104.c > index 639234f..8448a88 100644 > --- a/drm/nouveau/nvkm/subdev/clk/gk104.c > +++ b/drm/nouveau/nvkm/subdev/clk/gk104.c > @@ -482,6 +482,7 @@ gk104_clk = { > .calc = gk104_clk_calc, > .prog = gk104_clk_prog, > .tidy = gk104_clk_tidy, > + .update = nv40_clk_update, > .domains = { > { nv_clk_src_crystal, 0xff }, > { nv_clk_src_href , 0xff }, > diff --git a/drm/nouveau/nvkm/subdev/clk/gk20a.c b/drm/nouveau/nvkm/subdev/clk/gk20a.c > index 5f0ee24..8b64cc9 100644 > --- a/drm/nouveau/nvkm/subdev/clk/gk20a.c > +++ b/drm/nouveau/nvkm/subdev/clk/gk20a.c > @@ -636,6 +636,7 @@ gk20a_clk = { > .calc = gk20a_clk_calc, > .prog = gk20a_clk_prog, > .tidy = gk20a_clk_tidy, > + .update = nv40_clk_update, > .pstates = gk20a_pstates, > .nr_pstates = ARRAY_SIZE(gk20a_pstates), > .domains = { > diff --git a/drm/nouveau/nvkm/subdev/clk/gm20b.c b/drm/nouveau/nvkm/subdev/clk/gm20b.c > index 71b2bbb..8c8eb8c 100644 > --- a/drm/nouveau/nvkm/subdev/clk/gm20b.c > +++ b/drm/nouveau/nvkm/subdev/clk/gm20b.c > @@ -168,6 +168,7 @@ gm20b_clk_speedo0 = { > .calc = gk20a_clk_calc, > .prog = gk20a_clk_prog, > .tidy = gk20a_clk_tidy, > + .update = nv40_clk_update, > .pstates = gm20b_pstates, > .nr_pstates = ARRAY_SIZE(gm20b_pstates) - 1, > .domains = { > diff --git a/drm/nouveau/nvkm/subdev/clk/gt215.c b/drm/nouveau/nvkm/subdev/clk/gt215.c > index 056702e..8913afa 100644 > --- a/drm/nouveau/nvkm/subdev/clk/gt215.c > +++ b/drm/nouveau/nvkm/subdev/clk/gt215.c > @@ -520,6 +520,7 @@ gt215_clk = { > .calc = gt215_clk_calc, > .prog = gt215_clk_prog, > .tidy = gt215_clk_tidy, > + .update = nv40_clk_update, > .domains = { > { nv_clk_src_crystal , 0xff }, > { nv_clk_src_core , 0x00, 0, "core", 1000 }, > diff --git a/drm/nouveau/nvkm/subdev/clk/mcp77.c b/drm/nouveau/nvkm/subdev/clk/mcp77.c > index 1c21b8b..e80b68e 100644 > --- a/drm/nouveau/nvkm/subdev/clk/mcp77.c > +++ b/drm/nouveau/nvkm/subdev/clk/mcp77.c > @@ -400,6 +400,7 @@ mcp77_clk = { > .calc = mcp77_clk_calc, > .prog = mcp77_clk_prog, > .tidy = mcp77_clk_tidy, > + .update = nv40_clk_update, > .domains = { > { nv_clk_src_crystal, 0xff }, > { nv_clk_src_href , 0xff }, > diff --git a/drm/nouveau/nvkm/subdev/clk/nv40.c b/drm/nouveau/nvkm/subdev/clk/nv40.c > index 2ab9b9b..a808319 100644 > --- a/drm/nouveau/nvkm/subdev/clk/nv40.c > +++ b/drm/nouveau/nvkm/subdev/clk/nv40.c > @@ -201,12 +201,27 @@ nv40_clk_tidy(struct nvkm_clk *obj) > { > } > > +void > +nv40_clk_update(struct nvkm_clk *clk, int pstate) > +{ > + struct nvkm_subdev *subdev = &clk->subdev; > + int ret; > + > + nvkm_trace(subdev, "-> %d\n", pstate); > + ret = nvkm_pstate_prog(clk, pstate); > + if (ret) { > + nvkm_error(subdev, "error setting pstate %d: %d\n", > + pstate, ret); > + } > +} > + > static const struct nvkm_clk_func > nv40_clk = { > .read = nv40_clk_read, > .calc = nv40_clk_calc, > .prog = nv40_clk_prog, > .tidy = nv40_clk_tidy, > + .update = nv40_clk_update, > .domains = { > { nv_clk_src_crystal, 0xff }, > { nv_clk_src_href , 0xff }, > diff --git a/drm/nouveau/nvkm/subdev/clk/nv50.c b/drm/nouveau/nvkm/subdev/clk/nv50.c > index 5841f29..b29318e 100644 > --- a/drm/nouveau/nvkm/subdev/clk/nv50.c > +++ b/drm/nouveau/nvkm/subdev/clk/nv50.c > @@ -544,6 +544,7 @@ nv50_clk = { > .calc = nv50_clk_calc, > .prog = nv50_clk_prog, > .tidy = nv50_clk_tidy, > + .update = nv40_clk_update, > .domains = { > { nv_clk_src_crystal, 0xff }, > { nv_clk_src_href , 0xff }, > diff --git a/drm/nouveau/nvkm/subdev/clk/priv.h b/drm/nouveau/nvkm/subdev/clk/priv.h > index 51eafc0..958f5e3 100644 > --- a/drm/nouveau/nvkm/subdev/clk/priv.h > +++ b/drm/nouveau/nvkm/subdev/clk/priv.h > @@ -10,6 +10,7 @@ struct nvkm_clk_func { > int (*calc)(struct nvkm_clk *, struct nvkm_cstate *); > int (*prog)(struct nvkm_clk *); > void (*tidy)(struct nvkm_clk *); > + void (*update)(struct nvkm_clk *, int pstate); > struct nvkm_pstate *pstates; > int nr_pstates; > struct nvkm_domain domains[]; > @@ -20,7 +21,11 @@ int nvkm_clk_ctor(const struct nvkm_clk_func *, struct nvkm_device *, int, > int nvkm_clk_new_(const struct nvkm_clk_func *, struct nvkm_device *, int, > bool allow_reclock, struct nvkm_clk **); > > +int nvkm_pstate_prog(struct nvkm_clk *, int pstateid); > + > int nv04_clk_pll_calc(struct nvkm_clk *, struct nvbios_pll *, int clk, > struct nvkm_pll_vals *); > int nv04_clk_pll_prog(struct nvkm_clk *, u32 reg1, struct nvkm_pll_vals *); > + > +void nv40_clk_update(struct nvkm_clk *, int pstate); > #endif
Martin Peres
2016-Apr-20 22:17 UTC
[Nouveau] [PATCH v4 32/37] clk: only do partial reclocks as required
On 18/04/16 22:14, Karol Herbst wrote:> we don't want to reclock to the same pstate or cstate over and over again, so > only do things we actually have to do. > > v4: move into gf100Reviewed-by: Martin Peres <martin.peres at free.fr>> > Signed-off-by: Karol Herbst <nouveau at karolherbst.de> > --- > drm/nouveau/nvkm/subdev/clk/base.c | 11 +++++-- > drm/nouveau/nvkm/subdev/clk/gf100.c | 62 ++++++++++++++++++++++++++++++++++++- > drm/nouveau/nvkm/subdev/clk/gk104.c | 2 +- > drm/nouveau/nvkm/subdev/clk/nv40.c | 3 ++ > drm/nouveau/nvkm/subdev/clk/priv.h | 4 +++ > 5 files changed, 77 insertions(+), 5 deletions(-) > > diff --git a/drm/nouveau/nvkm/subdev/clk/base.c b/drm/nouveau/nvkm/subdev/clk/base.c > index 3c40f67..2776d79 100644 > --- a/drm/nouveau/nvkm/subdev/clk/base.c > +++ b/drm/nouveau/nvkm/subdev/clk/base.c > @@ -107,7 +107,7 @@ nvkm_cstate_valid(struct nvkm_clk *clk, struct nvkm_cstate *cstate, u32 max_volt > voltage >= volt->min_uv; > } > > -static struct nvkm_cstate * > +struct nvkm_cstate * > nvkm_cstate_find_best(struct nvkm_clk *clk, struct nvkm_pstate *pstate, > struct nvkm_cstate *start) > { > @@ -148,7 +148,7 @@ nvkm_cstate_find_best(struct nvkm_clk *clk, struct nvkm_pstate *pstate, > return cstate; > } > > -static struct nvkm_cstate * > +struct nvkm_cstate * > nvkm_cstate_get(struct nvkm_clk *clk, struct nvkm_pstate *pstate, int cstatei) > { > struct nvkm_cstate *cstate; > @@ -168,7 +168,7 @@ nvkm_cstate_get(struct nvkm_clk *clk, struct nvkm_pstate *pstate, int cstatei) > return NULL; > } > > -static int > +int > nvkm_cstate_prog(struct nvkm_clk *clk, struct nvkm_pstate *pstate, int cstatei) > { > struct nvkm_subdev *subdev = &clk->subdev; > @@ -188,6 +188,11 @@ nvkm_cstate_prog(struct nvkm_clk *clk, struct nvkm_pstate *pstate, int cstatei) > cstate = &pstate->base; > } > > + if (!cstate) { > + nvkm_error(subdev, "failed to set cstate %d\n", cstatei); > + return -EINVAL; > + } > + > if (therm) { > ret = nvkm_therm_cstate(therm, pstate->fanspeed, +1); > if (ret && ret != -ENODEV) { > diff --git a/drm/nouveau/nvkm/subdev/clk/gf100.c b/drm/nouveau/nvkm/subdev/clk/gf100.c > index 808e1ed..5025dcc 100644 > --- a/drm/nouveau/nvkm/subdev/clk/gf100.c > +++ b/drm/nouveau/nvkm/subdev/clk/gf100.c > @@ -28,6 +28,7 @@ > #include <subdev/bios.h> > #include <subdev/bios/pll.h> > #include <subdev/timer.h> > +#include <subdev/volt.h> > > struct gf100_clk_info { > u32 freq; > @@ -431,13 +432,72 @@ gf100_clk_tidy(struct nvkm_clk *base) > memset(clk->eng, 0x00, sizeof(clk->eng)); > } > > +static int > +gf100_clk_update_volt(struct nvkm_clk *clk) > +{ > + struct nvkm_subdev *subdev = &clk->subdev; > + struct nvkm_volt *volt = subdev->device->volt; > + struct nvkm_therm *therm = subdev->device->therm; > + > + if (!volt || !therm || !clk->pstate || !clk->set_cstate) > + return -EINVAL; > + > + return nvkm_volt_set_id(volt, clk->set_cstate->voltage, > + clk->pstate->base.voltage, 0); > +} > + > +void > +gf100_clk_update(struct nvkm_clk *clk, int pstate) > +{ > + struct nvkm_subdev *subdev = &clk->subdev; > + int ret; > + > + if (!clk->pstate || clk->pstate->pstate != pstate) { > + nvkm_trace(subdev, "-> P %d\n", pstate); > + ret = nvkm_pstate_prog(clk, pstate); > + if (ret) { > + nvkm_error(subdev, "error setting pstate %d: %d\n", > + pstate, ret); > + } > + } else if (!clk->set_cstate || > + clk->set_cstate->cstate != clk->exp_cstate) { > + > + struct nvkm_cstate *cstate = nvkm_cstate_get(clk, clk->pstate, clk->exp_cstate); > + if (!cstate) { > + nvkm_error(subdev, "can't find cstate %i\n", > + clk->exp_cstate); > + return; > + } > + > + cstate = nvkm_cstate_find_best(clk, clk->pstate, cstate); > + if (!cstate) { > + nvkm_error(subdev, "can't find best cstate for %i\n", > + cstate->cstate); > + return; > + } > + > + if (cstate != clk->set_cstate) { > + nvkm_trace(subdev, "-> C %d\n", cstate->cstate); > + ret = nvkm_cstate_prog(clk, clk->pstate, cstate->cstate); > + if (ret) { > + nvkm_error(subdev, "error setting cstate %d: %d\n", > + cstate->cstate, ret); > + } > + } else { > + gf100_clk_update_volt(clk); > + } > + } else { > + gf100_clk_update_volt(clk); > + } > +} > + > static const struct nvkm_clk_func > gf100_clk = { > .read = gf100_clk_read, > .calc = gf100_clk_calc, > .prog = gf100_clk_prog, > .tidy = gf100_clk_tidy, > - .update = nv40_clk_update, > + .update = gf100_clk_update, > .domains = { > { nv_clk_src_crystal, 0xff }, > { nv_clk_src_href , 0xff }, > diff --git a/drm/nouveau/nvkm/subdev/clk/gk104.c b/drm/nouveau/nvkm/subdev/clk/gk104.c > index 8448a88..abf1d76 100644 > --- a/drm/nouveau/nvkm/subdev/clk/gk104.c > +++ b/drm/nouveau/nvkm/subdev/clk/gk104.c > @@ -482,7 +482,7 @@ gk104_clk = { > .calc = gk104_clk_calc, > .prog = gk104_clk_prog, > .tidy = gk104_clk_tidy, > - .update = nv40_clk_update, > + .update = gf100_clk_update, > .domains = { > { nv_clk_src_crystal, 0xff }, > { nv_clk_src_href , 0xff }, > diff --git a/drm/nouveau/nvkm/subdev/clk/nv40.c b/drm/nouveau/nvkm/subdev/clk/nv40.c > index a808319..5b10ee2 100644 > --- a/drm/nouveau/nvkm/subdev/clk/nv40.c > +++ b/drm/nouveau/nvkm/subdev/clk/nv40.c > @@ -207,6 +207,9 @@ nv40_clk_update(struct nvkm_clk *clk, int pstate) > struct nvkm_subdev *subdev = &clk->subdev; > int ret; > > + if (clk->pstate && clk->pstate->pstate == pstate) > + return; > + > nvkm_trace(subdev, "-> %d\n", pstate); > ret = nvkm_pstate_prog(clk, pstate); > if (ret) { > diff --git a/drm/nouveau/nvkm/subdev/clk/priv.h b/drm/nouveau/nvkm/subdev/clk/priv.h > index 958f5e3..e2f15c4 100644 > --- a/drm/nouveau/nvkm/subdev/clk/priv.h > +++ b/drm/nouveau/nvkm/subdev/clk/priv.h > @@ -22,10 +22,14 @@ int nvkm_clk_new_(const struct nvkm_clk_func *, struct nvkm_device *, int, > bool allow_reclock, struct nvkm_clk **); > > int nvkm_pstate_prog(struct nvkm_clk *, int pstateid); > +int nvkm_cstate_prog(struct nvkm_clk *, struct nvkm_pstate *, int cstatei); > +struct nvkm_cstate * nvkm_cstate_get(struct nvkm_clk *, struct nvkm_pstate *, int cstatei); > +struct nvkm_cstate * nvkm_cstate_find_best(struct nvkm_clk *, struct nvkm_pstate *, struct nvkm_cstate *start); > > int nv04_clk_pll_calc(struct nvkm_clk *, struct nvbios_pll *, int clk, > struct nvkm_pll_vals *); > int nv04_clk_pll_prog(struct nvkm_clk *, u32 reg1, struct nvkm_pll_vals *); > > void nv40_clk_update(struct nvkm_clk *, int pstate); > +void gf100_clk_update(struct nvkm_clk *, int pstate); > #endif
Martin Peres
2016-Apr-20 22:18 UTC
[Nouveau] [PATCH v4 33/37] therm: trigger reclock in temperature daemon
On 18/04/16 22:14, Karol Herbst wrote:> depending on the temperature, cstates might become unreachable or the maped > voltage of a cstate changes. We want to adjust to that.Yeah! That was a lot of plumbing to get to this, but it is here! Reviewed-by: Martin Peres <martin.peres at free.fr>> > Signed-off-by: Karol Herbst <nouveau at karolherbst.de> > --- > drm/nouveau/nvkm/subdev/therm/base.c | 5 +++++ > 1 file changed, 5 insertions(+) > > diff --git a/drm/nouveau/nvkm/subdev/therm/base.c b/drm/nouveau/nvkm/subdev/therm/base.c > index 0c0feec..566fe5d 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) > { > @@ -153,7 +155,10 @@ 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; > nvkm_therm_update(therm, -1); > + if (clk) > + nvkm_clk_update(clk, false); > } > > int
Martin Peres
2016-Apr-20 22:21 UTC
[Nouveau] [PATCH v4 34/37] mc: fix NULL pointer access in libnouveau
On 18/04/16 22:14, Karol Herbst wrote:> Signed-off-by: Karol Herbst <nouveau at karolherbst.de> > --- > drm/nouveau/nvkm/subdev/mc/base.c | 7 ++++++- > 1 file changed, 6 insertions(+), 1 deletion(-) > > diff --git a/drm/nouveau/nvkm/subdev/mc/base.c b/drm/nouveau/nvkm/subdev/mc/base.c > index aa394af..88bc1cc 100644 > --- a/drm/nouveau/nvkm/subdev/mc/base.c > +++ b/drm/nouveau/nvkm/subdev/mc/base.c > @@ -90,10 +90,15 @@ nvkm_mc_intr(struct nvkm_mc *mc, bool *handled) > void > nvkm_mc_reset(struct nvkm_mc *mc, enum nvkm_devidx devidx) > { > - struct nvkm_device *device = mc->subdev.device; > + struct nvkm_device *device; > const struct nvkm_mc_map *map; > u64 pmc_enable; > > + if (!mc) > + return;Not sure what is the policy of Ben on this, fixing the caller or the callee. His call!> + > + device = mc->subdev.device; > + > if (!(pmc_enable = nvkm_top_reset(device->top, devidx))) { > for (map = mc->func->reset; map && map->stat; map++) { > if (map->unit == devidx) {
Martin Peres
2016-Apr-20 22:23 UTC
[Nouveau] [PATCH v4 35/37] clk: set clocks to pre suspend state after suspend
On 18/04/16 22:14, Karol Herbst wrote:> Signed-off-by: Karol Herbst <nouveau at karolherbst.de>Reviewed-by: Martin Peres <martin.peres at free.fr>> --- > drm/nouveau/nvkm/subdev/clk/base.c | 19 +++++++++++-------- > drm/nouveau/nvkm/subdev/clk/gf100.c | 4 ++-- > drm/nouveau/nvkm/subdev/clk/nv40.c | 2 +- > drm/nouveau/nvkm/subdev/clk/priv.h | 6 +++--- > 4 files changed, 17 insertions(+), 14 deletions(-) > > diff --git a/drm/nouveau/nvkm/subdev/clk/base.c b/drm/nouveau/nvkm/subdev/clk/base.c > index 2776d79..d5440a9 100644 > --- a/drm/nouveau/nvkm/subdev/clk/base.c > +++ b/drm/nouveau/nvkm/subdev/clk/base.c > @@ -324,7 +324,7 @@ nvkm_pstate_prog(struct nvkm_clk *clk, int pstateid) > } > > static void > -nvkm_clk_update_impl(struct nvkm_clk *clk) > +nvkm_clk_update_impl(struct nvkm_clk *clk, bool force) > { > struct nvkm_subdev *subdev = &clk->subdev; > int pstate; > @@ -349,7 +349,7 @@ nvkm_clk_update_impl(struct nvkm_clk *clk) > pstate = -1; > } > > - clk->func->update(clk, pstate); > + clk->func->update(clk, pstate, force); > } > > static void > @@ -360,7 +360,7 @@ nvkm_clk_update_work(struct work_struct *work) > if (!atomic_xchg(&clk->waiting, 0)) > return; > > - nvkm_clk_update_impl(clk); > + nvkm_clk_update_impl(clk, false); > > wake_up_all(&clk->wait); > nvkm_notify_get(&clk->pwrsrc_ntfy); > @@ -613,11 +613,7 @@ nvkm_clk_init(struct nvkm_subdev *subdev) > if (clk->func->init) > return clk->func->init(clk); > > - clk->astate = -1; > - clk->pstate = NULL; > - clk->exp_cstate = NVKM_CLK_CSTATE_DEFAULT; > - clk->set_cstate = NULL; > - nvkm_clk_update(clk, true); > + nvkm_clk_update_impl(clk, true); > return 0; > } > > @@ -672,8 +668,15 @@ 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->pstate = NULL; > + clk->astate = -1; > clk->ustate_ac = -1; > clk->ustate_dc = -1; > + > + clk->exp_cstate = NVKM_CLK_CSTATE_DEFAULT; > + clk->set_cstate = NULL; > + > clk->allow_reclock = allow_reclock; > > INIT_WORK(&clk->work, nvkm_clk_update_work); > diff --git a/drm/nouveau/nvkm/subdev/clk/gf100.c b/drm/nouveau/nvkm/subdev/clk/gf100.c > index 5025dcc..5621daf 100644 > --- a/drm/nouveau/nvkm/subdev/clk/gf100.c > +++ b/drm/nouveau/nvkm/subdev/clk/gf100.c > @@ -447,12 +447,12 @@ gf100_clk_update_volt(struct nvkm_clk *clk) > } > > void > -gf100_clk_update(struct nvkm_clk *clk, int pstate) > +gf100_clk_update(struct nvkm_clk *clk, int pstate, bool force) > { > struct nvkm_subdev *subdev = &clk->subdev; > int ret; > > - if (!clk->pstate || clk->pstate->pstate != pstate) { > + if (!clk->pstate || clk->pstate->pstate != pstate || force) { > nvkm_trace(subdev, "-> P %d\n", pstate); > ret = nvkm_pstate_prog(clk, pstate); > if (ret) { > diff --git a/drm/nouveau/nvkm/subdev/clk/nv40.c b/drm/nouveau/nvkm/subdev/clk/nv40.c > index 5b10ee2..055063a 100644 > --- a/drm/nouveau/nvkm/subdev/clk/nv40.c > +++ b/drm/nouveau/nvkm/subdev/clk/nv40.c > @@ -202,7 +202,7 @@ nv40_clk_tidy(struct nvkm_clk *obj) > } > > void > -nv40_clk_update(struct nvkm_clk *clk, int pstate) > +nv40_clk_update(struct nvkm_clk *clk, int pstate, bool force) > { > struct nvkm_subdev *subdev = &clk->subdev; > int ret; > diff --git a/drm/nouveau/nvkm/subdev/clk/priv.h b/drm/nouveau/nvkm/subdev/clk/priv.h > index e2f15c4..06a78a2 100644 > --- a/drm/nouveau/nvkm/subdev/clk/priv.h > +++ b/drm/nouveau/nvkm/subdev/clk/priv.h > @@ -10,7 +10,7 @@ struct nvkm_clk_func { > int (*calc)(struct nvkm_clk *, struct nvkm_cstate *); > int (*prog)(struct nvkm_clk *); > void (*tidy)(struct nvkm_clk *); > - void (*update)(struct nvkm_clk *, int pstate); > + void (*update)(struct nvkm_clk *, int pstate, bool force); > struct nvkm_pstate *pstates; > int nr_pstates; > struct nvkm_domain domains[]; > @@ -30,6 +30,6 @@ int nv04_clk_pll_calc(struct nvkm_clk *, struct nvbios_pll *, int clk, > struct nvkm_pll_vals *); > int nv04_clk_pll_prog(struct nvkm_clk *, u32 reg1, struct nvkm_pll_vals *); > > -void nv40_clk_update(struct nvkm_clk *, int pstate); > -void gf100_clk_update(struct nvkm_clk *, int pstate); > +void nv40_clk_update(struct nvkm_clk *, int pstate, bool force); > +void gf100_clk_update(struct nvkm_clk *, int pstate, bool force); > #endif
Martin Peres
2016-Apr-20 22:39 UTC
[Nouveau] [PATCH v4 36/37] WIP volt/gk104: readout speedo
On 18/04/16 22:14, Karol Herbst wrote:> this gk104 volt implementation has to be reworked a little, because the speedo > readout in maxwell doesn't need those strange 0 and 41 writes into 0x122634, > but it needs this PWM thing. > > Maybe Maxwell is PWM only and we could just simplify it there, but without > proper knowledge there has some refactoring to be made.This is not true, my GM206 is GPIO-based.> > Signed-off-by: Karol Herbst <nouveau at karolherbst.de> > --- > drm/nouveau/nvkm/subdev/volt/gk104.c | 19 +++++++++++++++++++ > 1 file changed, 19 insertions(+) > > diff --git a/drm/nouveau/nvkm/subdev/volt/gk104.c b/drm/nouveau/nvkm/subdev/volt/gk104.c > index b735173..81788c2 100644 > --- a/drm/nouveau/nvkm/subdev/volt/gk104.c > +++ b/drm/nouveau/nvkm/subdev/volt/gk104.c > @@ -27,6 +27,7 @@ > #include <subdev/gpio.h> > #include <subdev/bios.h> > #include <subdev/bios/volt.h> > +#include <subdev/fuse.h> > > #define gk104_volt(p) container_of((p), struct gk104_volt, base) > struct gk104_volt { > @@ -64,13 +65,31 @@ gk104_volt_set(struct nvkm_volt *base, u32 uv) > return 0; > } > > +static int > +gk104_volt_speedo_read(struct nvkm_volt *volt) > +{ > + struct nvkm_device *device = volt->subdev.device; > + struct nvkm_fuse *fuse = device->fuse; > + int ret; > + > + if (!fuse) > + return -EINVAL; > + > + nvkm_wr32(device, 0x122634, 0x0);I checked on a mmiotrace, and these writes are not only for this fuse. So they should be put in the gk104_fuse_read :) After this,you can stop calling it a WIP patch and get rid of this confusing talk about the PWM voltage management, because I really do not get how it is relevant. With this addressed: Reviewed-by: Martin Peres <martin.peres at free.fr>> + ret = nvkm_fuse_read(fuse, 0x3a8); > + nvkm_wr32(device, 0x122634, 0x41); > + return ret; > +} > + > static const struct nvkm_volt_func > gk104_volt_pwm = { > .volt_get = gk104_volt_get, > .volt_set = gk104_volt_set, > + .speedo_read = gk104_volt_speedo_read, > }, gk104_volt_gpio = { > .vid_get = nvkm_voltgpio_get, > .vid_set = nvkm_voltgpio_set, > + .speedo_read = gk104_volt_speedo_read, > }; > > int
Martin Peres
2016-Apr-20 22:55 UTC
[Nouveau] [PATCH v4 37/37] volt: add NvVoltOffsetmV option
On 18/04/16 22:14, Karol Herbst wrote:> This option can be used to adjust the calculated voltage or the cstate voltage > calculation > > Signed-off-by: Karol Herbst <nouveau at karolherbst.de> > --- > bin/nv_cmp_volt.c | 2 +- > drm/nouveau/include/nvkm/subdev/volt.h | 4 +++- > drm/nouveau/nvkm/subdev/clk/base.c | 8 ++++---- > drm/nouveau/nvkm/subdev/volt/base.c | 25 ++++++++++++++++++++----- > 4 files changed, 28 insertions(+), 11 deletions(-) > > diff --git a/bin/nv_cmp_volt.c b/bin/nv_cmp_volt.c > index e61056c..d1a0402 100644 > --- a/bin/nv_cmp_volt.c > +++ b/bin/nv_cmp_volt.c > @@ -117,7 +117,7 @@ main(int argc, char **argv) > > new_voltage = nvkm_volt_get(volt); > new_temp = nvkm_rd32(device, 0x20400);//nvkm_therm_temp_get(therm); > - new_nouveau_voltage = max(nvkm_volt_map(volt, best_cstate->voltage, new_temp), nvkm_volt_map(volt, best_pstate->base.voltage, new_temp)); > + new_nouveau_voltage = max(nvkm_volt_map(volt, best_cstate->voltage, new_temp, true), nvkm_volt_map(volt, best_pstate->base.voltage, new_temp, false)); > new_pstate = best_pstate->pstate; > new_cstate = best_cstate->cstate; > > diff --git a/drm/nouveau/include/nvkm/subdev/volt.h b/drm/nouveau/include/nvkm/subdev/volt.h > index 25588c7..f34fd39 100644 > --- a/drm/nouveau/include/nvkm/subdev/volt.h > +++ b/drm/nouveau/include/nvkm/subdev/volt.h > @@ -22,9 +22,11 @@ struct nvkm_volt { > u8 max2_id; > > int speedo; > + > + int volt_offset_mv; > }; > > -int nvkm_volt_map(struct nvkm_volt *volt, u8 id, u8 temperature); > +int nvkm_volt_map(struct nvkm_volt *volt, u8 id, u8 temperature, bool enableOffset); > int nvkm_volt_map_min(struct nvkm_volt *volt, u8 id); > int nvkm_volt_get(struct nvkm_volt *); > int nvkm_volt_set_id(struct nvkm_volt *, u8 id, u8 min_id, int condition); > diff --git a/drm/nouveau/nvkm/subdev/clk/base.c b/drm/nouveau/nvkm/subdev/clk/base.c > index d5440a9..7937155 100644 > --- a/drm/nouveau/nvkm/subdev/clk/base.c > +++ b/drm/nouveau/nvkm/subdev/clk/base.c > @@ -100,7 +100,7 @@ nvkm_cstate_valid(struct nvkm_clk *clk, struct nvkm_cstate *cstate, u32 max_volt > if (!volt) > return true; > > - voltage = nvkm_volt_map(volt, cstate->voltage, temp); > + voltage = nvkm_volt_map(volt, cstate->voltage, temp, true); > if (voltage < 0) > return false; > return voltage <= min(max_volt, volt->max_uv) && > @@ -131,13 +131,13 @@ nvkm_cstate_find_best(struct nvkm_clk *clk, struct nvkm_pstate *pstate, > max_volt = volt->max_uv; > if (volt->max0_id != 0xff) > max_volt = min(max_volt, > - nvkm_volt_map(volt, volt->max0_id, temp)); > + nvkm_volt_map(volt, volt->max0_id, temp, false)); > if (volt->max1_id != 0xff) > max_volt = min(max_volt, > - nvkm_volt_map(volt, volt->max1_id, temp)); > + nvkm_volt_map(volt, volt->max1_id, temp, false)); > if (volt->max2_id != 0xff) > max_volt = min(max_volt, > - nvkm_volt_map(volt, volt->max2_id, temp)); > + nvkm_volt_map(volt, volt->max2_id, temp, false));So, this ugly dance of having true or flase depending on whether we are looking for a pstate or a cstate looks really ugly, but it does make some sense. Given how useful this may be to debug voltage-related issues, I would say it is worth the extra complexity (that we really did not need). Anyway: Reviewed-by: Martin Peres <martin.peres at free.fr>> > for (cstate = start; &cstate->head != &pstate->list; > cstate = list_entry(cstate->head.prev, typeof(*cstate), head)) { > diff --git a/drm/nouveau/nvkm/subdev/volt/base.c b/drm/nouveau/nvkm/subdev/volt/base.c > index 5e35d96..7d5e6c8 100644 > --- a/drm/nouveau/nvkm/subdev/volt/base.c > +++ b/drm/nouveau/nvkm/subdev/volt/base.c > @@ -23,6 +23,8 @@ > */ > #include "priv.h" > > +#include <core/option.h> > + > #include <subdev/bios.h> > #include <subdev/bios/vmap.h> > #include <subdev/bios/volt.h> > @@ -100,8 +102,8 @@ nvkm_volt_map_min(struct nvkm_volt *volt, u8 id) > return id ? id * 10000 : -ENODEV; > } > > -int > -nvkm_volt_map(struct nvkm_volt *volt, u8 id, u8 temp) > +static int > +nvkm_volt_map_impl(struct nvkm_volt *volt, u8 id, u8 temp) > { > struct nvkm_bios *bios = volt->subdev.device->bios; > struct nvbios_vmap_entry info; > @@ -145,7 +147,7 @@ nvkm_volt_map(struct nvkm_volt *volt, u8 id, u8 temp) > result = min(max(result, (s64)info.min), (s64)info.max); > > if (info.link != 0xff) { > - int ret = nvkm_volt_map(volt, info.link, temp); > + int ret = nvkm_volt_map_impl(volt, info.link, temp); > if (ret < 0) > return ret; > result += ret; > @@ -157,6 +159,15 @@ nvkm_volt_map(struct nvkm_volt *volt, u8 id, u8 temp) > } > > int > +nvkm_volt_map(struct nvkm_volt *volt, u8 id, u8 temp, bool enable_offset) > +{ > + int res = nvkm_volt_map_impl(volt, id, temp); > + if (enable_offset) > + res += volt->volt_offset_mv * 1000; > + return res; > +} > + > +int > nvkm_volt_set_id(struct nvkm_volt *volt, u8 id, u8 min_id, int condition) > { > struct nvkm_therm *therm = volt->subdev.device->therm; > @@ -169,13 +180,13 @@ nvkm_volt_set_id(struct nvkm_volt *volt, u8 id, u8 min_id, int condition) > if (therm) > temp = nvkm_therm_temp_get(therm); > > - ret = nvkm_volt_map(volt, id, max(temp, 0)); > + ret = nvkm_volt_map(volt, id, max(temp, 0), true); > if (ret >= 0) { > int prev = nvkm_volt_get(volt); > if (!condition || prev < 0 || > (condition < 0 && ret < prev) || > (condition > 0 && ret > prev)) { > - int min = nvkm_volt_map(volt, min_id, max(temp, 0)); > + int min = nvkm_volt_map(volt, min_id, max(temp, 0), false); > if (min >= 0) > ret = max(min, ret); > ret = nvkm_volt_set(volt, ret); > @@ -308,6 +319,10 @@ nvkm_volt_ctor(const struct nvkm_volt_func *func, struct nvkm_device *device, > volt->speedo = nvkm_volt_speedo_read(volt); > if (volt->speedo > 0) > nvkm_debug(&volt->subdev, "speedo %x\n", volt->speedo); > + > + volt->volt_offset_mv = nvkm_longopt(device->cfgopt, "NvVoltOffsetmV", 0); > + if (volt->volt_offset_mv) > + nvkm_info(&volt->subdev, "Volt Offset applied: %i\n", volt->volt_offset_mv); > } > > int