Karol Herbst
2016-Mar-17 23:03 UTC
[Nouveau] [PATCH 00/19] Volting/Clocking improvements for Fermi and newer
This series fixes most of the issues regarding volting on GPUs with any form
of GPU Boost inside their vbios, which is mainly Kepler and newer, but we find
some boosting related tables in Fermi vbios' already
In the end reclocking should work on most Kepler cards without any issues
Karol Herbst (19):
bios/volt: handle voltage table version 0x50 with 0ed header
volt: properly detect entry based voltage tables
bios: add parsing of BASE CLOCK table
clk: print the base clocks
clk: allow boosting only when NvBoost is set
volt: save the voltage range we are able to set
volt: add nvkm_volt_map_min function
clk: don't create cstates which voltage is higher than what the gpu
can do
volt: parse the both max voltage 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
nouveau/subdev/clk: fixup cstate selection
clk: respect voltage limits in nvkm_cstate_prog with cstate = -1
volt: don't require perfect fit
bios/vmap: unk0 field is the mode
volt: add coefficients I found on my gpu
bin/nv_cmp_volt.c | 130 +++++++++++++++++++++
drm/nouveau/include/nvkm/subdev/bios/baseclock.h | 24 ++++
drm/nouveau/include/nvkm/subdev/bios/vmap.h | 4 +-
drm/nouveau/include/nvkm/subdev/bios/volt.h | 5 +-
drm/nouveau/include/nvkm/subdev/clk.h | 10 +-
drm/nouveau/include/nvkm/subdev/volt.h | 9 +-
drm/nouveau/nvkm/subdev/bios/Kbuild | 1 +
drm/nouveau/nvkm/subdev/bios/baseclock.c | 82 +++++++++++++
drm/nouveau/nvkm/subdev/bios/vmap.c | 7 +-
drm/nouveau/nvkm/subdev/bios/volt.c | 45 +++++---
drm/nouveau/nvkm/subdev/clk/base.c | 112 +++++++++++++++++-
drm/nouveau/nvkm/subdev/clk/gf100.c | 2 +-
drm/nouveau/nvkm/subdev/clk/gk104.c | 2 +-
drm/nouveau/nvkm/subdev/volt/base.c | 141 +++++++++++++++++++++--
14 files changed, 532 insertions(+), 42 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
--
2.7.3
Karol Herbst
2016-Mar-17 23:03 UTC
[Nouveau] [PATCH 01/19] 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> --- 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.7.3
Karol Herbst
2016-Mar-17 23:03 UTC
[Nouveau] [PATCH 02/19] 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>
---
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 50b5649..ef653b1 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.7.3
Karol Herbst
2016-Mar-17 23:03 UTC
[Nouveau] [PATCH 03/19] 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.
Signed-off-by: Karol Herbst <nouveau at karolherbst.de>
---
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..eca7b4a
--- /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;
+ u8 boost;
+ u8 tdp;
+};
+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..2f15abb
--- /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 = nvbios_rd08(b, h->offset + 0x0f);
+ h->boost = nvbios_rd08(b, h->offset + 0x10);
+ h->tdp = 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.7.3
Signed-off-by: Karol Herbst <nouveau at karolherbst.de>
---
drm/nouveau/nvkm/subdev/clk/base.c | 17 ++++++++++++++++-
1 file changed, 16 insertions(+), 1 deletion(-)
diff --git a/drm/nouveau/nvkm/subdev/clk/base.c
b/drm/nouveau/nvkm/subdev/clk/base.c
index 889cce2..4928668 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>
@@ -561,10 +562,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, 0, subdev);
+
+ if (bios && !nvbios_baseclock_parse(bios, &h)) {
+ struct nvbios_baseclk_entry base, boost;
+ if (!nvbios_baseclock_entry(bios, &h, h.boost, &boost))
+ nvkm_info(subdev, "boost: %i MHz\n",
+ boost.clock_mhz / 2);
+ if (!nvbios_baseclock_entry(bios, &h, h.base, &base))
+ nvkm_info(subdev, "base: %i MHz\n",
+ base.clock_mhz / 2);
+ }
- nvkm_subdev_ctor(&nvkm_clk, device, index, 0, &clk->subdev);
clk->func = func;
INIT_LIST_HEAD(&clk->states);
clk->domains = func->domains;
--
2.7.3
Karol Herbst
2016-Mar-17 23:03 UTC
[Nouveau] [PATCH 05/19] 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
Signed-off-by: Karol Herbst <nouveau at karolherbst.de>
---
drm/nouveau/include/nvkm/subdev/clk.h | 9 ++++++++-
drm/nouveau/nvkm/subdev/clk/base.c | 26 ++++++++++++++++++++++++--
drm/nouveau/nvkm/subdev/clk/gf100.c | 2 +-
drm/nouveau/nvkm/subdev/clk/gk104.c | 2 +-
4 files changed, 34 insertions(+), 5 deletions(-)
diff --git a/drm/nouveau/include/nvkm/subdev/clk.h
b/drm/nouveau/include/nvkm/subdev/clk.h
index fb54417..a292e5a 100644
--- a/drm/nouveau/include/nvkm/subdev/clk.h
+++ b/drm/nouveau/include/nvkm/subdev/clk.h
@@ -67,7 +67,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;
@@ -97,6 +98,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 4928668..d575412 100644
--- a/drm/nouveau/nvkm/subdev/clk/base.c
+++ b/drm/nouveau/nvkm/subdev/clk/base.c
@@ -160,6 +160,18 @@ nvkm_cstate_new(struct nvkm_clk *clk, int idx, struct
nvkm_pstate *pstate)
if (domain->flags & NVKM_CLK_DOM_FLAG_CORE) {
u32 freq = nvkm_clk_adjust(clk, true, pstate->pstate,
domain->bios, cstepX.freq);
+ if (domain->flags & NVKM_CLK_DOM_FLAG_BASECLK) {
+ switch (clk->boost_mode) {
+ case NVKM_CLK_BOOST_NONE:
+ if (clk->base_khz
+ && freq > clk->base_khz)
+ goto err;
+ case NVKM_CLK_BOOST_AVG:
+ if (clk->boost_khz
+ && freq > clk->boost_khz)
+ goto err;
+ }
+ }
cstate->domain[domain->name] = freq;
}
domain++;
@@ -167,6 +179,9 @@ nvkm_cstate_new(struct nvkm_clk *clk, int idx, struct
nvkm_pstate *pstate)
list_add(&cstate->head, &pstate->list);
return 0;
+err:
+ kfree(cstate);
+ return -EINVAL;
}
/******************************************************************************
@@ -570,14 +585,21 @@ nvkm_clk_ctor(const struct nvkm_clk_func *func, struct
nvkm_device *device,
nvkm_subdev_ctor(&nvkm_clk, device, index, 0, 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, &boost))
+ if (!nvbios_baseclock_entry(bios, &h, h.boost, &boost)) {
+ clk->boost_khz = boost.clock_mhz * 1000;
nvkm_info(subdev, "boost: %i MHz\n",
boost.clock_mhz / 2);
- if (!nvbios_baseclock_entry(bios, &h, h.base, &base))
+ }
+
+ if (!nvbios_baseclock_entry(bios, &h, h.base, &base)) {
+ clk->base_khz = base.clock_mhz * 1000;
nvkm_info(subdev, "base: %i MHz\n",
base.clock_mhz / 2);
+ }
}
clk->func = func;
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.7.3
Karol Herbst
2016-Mar-17 23:03 UTC
[Nouveau] [PATCH 06/19] 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>
---
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 ef653b1..95fe065 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.7.3
Karol Herbst
2016-Mar-17 23:03 UTC
[Nouveau] [PATCH 07/19] 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 in
later commits
Signed-off-by: Karol Herbst <nouveau at karolherbst.de>
---
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 95fe065..71094a9 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.7.3
Karol Herbst
2016-Mar-17 23:03 UTC
[Nouveau] [PATCH 08/19] clk: don't create cstates which voltage is higher than what the gpu can do
Signed-off-by: Karol Herbst <nouveau at karolherbst.de>
---
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 d575412..5b8e1df 100644
--- a/drm/nouveau/nvkm/subdev/clk/base.c
+++ b/drm/nouveau/nvkm/subdev/clk/base.c
@@ -139,6 +139,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;
@@ -149,6 +150,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.7.3
Karol Herbst
2016-Mar-17 23:03 UTC
[Nouveau] [PATCH 09/19] volt: parse the both max voltage entries
these entries specify a maximum voltage nvidia never exceeds, we shouldn't
do
that, too.
Signed-off-by: Karol Herbst <nouveau at karolherbst.de>
---
drm/nouveau/include/nvkm/subdev/bios/vmap.h | 2 ++
drm/nouveau/include/nvkm/subdev/volt.h | 2 ++
drm/nouveau/nvkm/subdev/bios/vmap.c | 5 +++++
drm/nouveau/nvkm/subdev/volt/base.c | 11 +++++++++++
4 files changed, 20 insertions(+)
diff --git a/drm/nouveau/include/nvkm/subdev/bios/vmap.h
b/drm/nouveau/include/nvkm/subdev/bios/vmap.h
index 6633c6d..48fe71d 100644
--- a/drm/nouveau/include/nvkm/subdev/bios/vmap.h
+++ b/drm/nouveau/include/nvkm/subdev/bios/vmap.h
@@ -1,6 +1,8 @@
#ifndef __NVBIOS_VMAP_H__
#define __NVBIOS_VMAP_H__
struct nvbios_vmap {
+ u8 max0;
+ u8 max1;
};
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..3e0f8da 100644
--- a/drm/nouveau/include/nvkm/subdev/volt.h
+++ b/drm/nouveau/include/nvkm/subdev/volt.h
@@ -15,6 +15,8 @@ struct nvkm_volt {
u32 max_uv;
u32 min_uv;
+ u8 max0_vid;
+ u8 max1_vid;
};
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..f5463b1 100644
--- a/drm/nouveau/nvkm/subdev/bios/vmap.c
+++ b/drm/nouveau/nvkm/subdev/bios/vmap.c
@@ -61,7 +61,12 @@ 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;
+ break;
case 0x20:
+ info->max0 = nvbios_rd08(bios, vmap + 0x7);
+ info->max1 = nvbios_rd08(bios, vmap + 0x8);
break;
}
return vmap;
diff --git a/drm/nouveau/nvkm/subdev/volt/base.c
b/drm/nouveau/nvkm/subdev/volt/base.c
index 71094a9..205dfcf 100644
--- a/drm/nouveau/nvkm/subdev/volt/base.c
+++ b/drm/nouveau/nvkm/subdev/volt/base.c
@@ -217,9 +217,20 @@ 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_vid = vmap.max0;
+ volt->max1_vid = vmap.max1;
+ } else {
+ volt->max0_vid = 0xff;
+ volt->max1_vid = 0xff;
+ }
}
if (volt->vid_nr) {
--
2.7.3
Karol Herbst
2016-Mar-17 23:03 UTC
[Nouveau] [PATCH 10/19] volt: add min_id parameter to nvkm_volt_set_id
min_id indicates a volt map entry which acts as a floor value, this will be
used to set the lower voltage limit through pstates
Signed-off-by: Karol Herbst <nouveau at karolherbst.de>
---
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 3e0f8da..e966266 100644
--- a/drm/nouveau/include/nvkm/subdev/volt.h
+++ b/drm/nouveau/include/nvkm/subdev/volt.h
@@ -21,7 +21,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 5b8e1df..00271a9 100644
--- a/drm/nouveau/nvkm/subdev/clk/base.c
+++ b/drm/nouveau/nvkm/subdev/clk/base.c
@@ -100,7 +100,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;
@@ -114,7 +115,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 205dfcf..a7f8c76 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.7.3
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 e966266..e8637b9 100644
--- a/drm/nouveau/include/nvkm/subdev/volt.h
+++ b/drm/nouveau/include/nvkm/subdev/volt.h
@@ -19,6 +19,7 @@ struct nvkm_volt {
u8 max1_vid;
};
+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 a7f8c76..c4fde8f 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.7.3
Karol Herbst
2016-Mar-17 23:03 UTC
[Nouveau] [PATCH 12/19] clk: add index field to nvkm_cstate
From: Karol Herbst <git at karolherbst.de>
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 a292e5a..99ee05c 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 00271a9..b79644d 100644
--- a/drm/nouveau/nvkm/subdev/clk/base.c
+++ b/drm/nouveau/nvkm/subdev/clk/base.c
@@ -161,6 +161,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.7.3
Karol Herbst
2016-Mar-17 23:03 UTC
[Nouveau] [PATCH 13/19] 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.
Signed-off-by: Karol Herbst <nouveau at karolherbst.de>
---
bin/nv_cmp_volt.c | 130 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 130 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..6147a7d
--- /dev/null
+++ b/bin/nv_cmp_volt.c
@@ -0,0 +1,130 @@
+/*
+ * 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;
+ int old_voltage = 0, old_nouveau_voltage = 0, old_pstate = 0;
+ int old_cstate = 0, old_temp = 0;
+
+ 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 && (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.7.3
Karol Herbst
2016-Mar-17 23:03 UTC
[Nouveau] [PATCH 14/19] volt: add temperature parameter to nvkm_volt_map
the voltage entries actually may map to a different voltage depending on the
current 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 6147a7d..16aa7e6 100644
--- a/bin/nv_cmp_volt.c
+++ b/bin/nv_cmp_volt.c
@@ -108,7 +108,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 e8637b9..3b94ab0 100644
--- a/drm/nouveau/include/nvkm/subdev/volt.h
+++ b/drm/nouveau/include/nvkm/subdev/volt.h
@@ -19,7 +19,7 @@ struct nvkm_volt {
u8 max1_vid;
};
-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 c4fde8f..e741383 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 (therm)
+ temp = nvkm_therm_temp_get(therm);
if (volt->func->set_id)
return volt->func->set_id(volt, id, condition);
- ret = nvkm_volt_map(volt, id);
+ 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.7.3
Karol Herbst
2016-Mar-17 23:03 UTC
[Nouveau] [PATCH 15/19] nouveau/subdev/clk: fixup cstate selection
From: Karol Herbst <git at karolherbst.de>
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 b79644d..7998840 100644
--- a/drm/nouveau/nvkm/subdev/clk/base.c
+++ b/drm/nouveau/nvkm/subdev/clk/base.c
@@ -86,7 +86,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;
}
@@ -223,7 +231,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.7.3
Karol Herbst
2016-Mar-17 23:03 UTC
[Nouveau] [PATCH 16/19] clk: respect voltage limits in nvkm_cstate_prog with cstate = -1
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 most of the remaining volting errors on fermi and newer
Signed-off-by: Karol Herbst <nouveau at karolherbst.de>
---
drm/nouveau/nvkm/subdev/clk/base.c | 54 ++++++++++++++++++++++++++++++++++++--
1 file changed, 52 insertions(+), 2 deletions(-)
diff --git a/drm/nouveau/nvkm/subdev/clk/base.c
b/drm/nouveau/nvkm/subdev/clk/base.c
index 7998840..9658f6d 100644
--- a/drm/nouveau/nvkm/subdev/clk/base.c
+++ b/drm/nouveau/nvkm/subdev/clk/base.c
@@ -75,6 +75,57 @@ nvkm_clk_adjust(struct nvkm_clk *clk, bool adjust,
/******************************************************************************
* C-States
*****************************************************************************/
+static bool
+nvkm_cstate_valid(struct nvkm_clk *clk, struct nvkm_cstate *cstate, u32
max_volt, int temp)
+{
+ struct nvkm_volt *volt = clk->subdev.device->volt;
+ int voltage;
+
+ if (!volt)
+ return true;
+
+ voltage = nvkm_volt_map(volt, cstate->voltage, temp);
+ if (voltage < 0)
+ return false;
+ return voltage <= min(max_volt, volt->max_uv) &&
+ voltage >= volt->min_uv;
+}
+
+static struct nvkm_cstate *
+nvkm_cstate_find_best(struct nvkm_clk *clk, struct nvkm_pstate *pstate)
+{
+ struct nvkm_device *device = clk->subdev.device;
+ struct nvkm_therm *therm = device->therm;
+ struct nvkm_volt *volt = device->volt;
+ struct nvkm_cstate *cstate;
+ int temp = 0, max_volt;
+
+ if (!volt)
+ return list_entry(pstate->list.prev, typeof(*cstate), head);
+
+ if (therm) {
+ /* ignore error code */
+ temp = max(0, nvkm_therm_temp_get(therm));
+ }
+
+ max_volt = volt->max_uv;
+ if (volt->max0_vid != 0xff)
+ max_volt = min(max_volt,
+ nvkm_volt_map(volt, volt->max0_vid, temp));
+ if (volt->max1_vid != 0xff)
+ max_volt = min(max_volt,
+ nvkm_volt_map(volt, volt->max1_vid, temp));
+
+ for (cstate = list_entry(pstate->list.prev, typeof(*cstate), head);
+ &cstate->head != &pstate->list;
+ cstate = list_entry(cstate->head.prev, typeof(*cstate), head)) {
+ if (nvkm_cstate_valid(clk, cstate, max_volt, temp))
+ break;
+ }
+
+ return cstate;
+}
+
static int
nvkm_cstate_prog(struct nvkm_clk *clk, struct nvkm_pstate *pstate, int cstatei)
{
@@ -87,8 +138,7 @@ nvkm_cstate_prog(struct nvkm_clk *clk, struct nvkm_pstate
*pstate, int cstatei)
if (!list_empty(&pstate->list)) {
if (cstatei == -1)
- cstate = list_entry(pstate->list.prev, typeof(*cstate),
- head);
+ cstate = nvkm_cstate_find_best(clk, pstate);
else {
list_for_each_entry(cstate, &pstate->list, head) {
if (cstate->cstate == cstatei)
--
2.7.3
Karol Herbst
2016-Mar-17 23:03 UTC
[Nouveau] [PATCH 17/19] volt: don't require perfect fit
when 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
Signed-off-by: Karol Herbst <nouveau at karolherbst.de>
---
drm/nouveau/nvkm/subdev/volt/base.c | 25 ++++++++++++++++++++++---
1 file changed, 22 insertions(+), 3 deletions(-)
diff --git a/drm/nouveau/nvkm/subdev/volt/base.c
b/drm/nouveau/nvkm/subdev/volt/base.c
index e741383..58738e3 100644
--- a/drm/nouveau/nvkm/subdev/volt/base.c
+++ b/drm/nouveau/nvkm/subdev/volt/base.c
@@ -51,18 +51,37 @@ 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, err, 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);
+ if (i == 0) {
+ best = 0;
+ err = volt->vid[i].uv - uv;
+ } else {
+ int new_err = volt->vid[i].uv - uv;
+ if (abs(new_err) < abs(err)
+ || (err < 0 && new_err >= 0)) {
+ best = i;
+ err = new_err;
+ }
+ }
+
+ if (err == 0) {
+ ret = volt->func->vid_set(volt, volt->vid[best].vid);
nvkm_debug(subdev, "set %duv: %d\n", uv, ret);
break;
}
}
+
+ if (best != -1) {
+ 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.7.3
Karol Herbst
2016-Mar-17 23:03 UTC
[Nouveau] [PATCH 18/19] bios/vmap: unk0 field is the mode
this selects which formula is used to calculate the voltage
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 48fe71d..c036315 100644
--- a/drm/nouveau/include/nvkm/subdev/bios/vmap.h
+++ b/drm/nouveau/include/nvkm/subdev/bios/vmap.h
@@ -10,7 +10,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 f5463b1..159c456 100644
--- a/drm/nouveau/nvkm/subdev/bios/vmap.c
+++ b/drm/nouveau/nvkm/subdev/bios/vmap.c
@@ -100,7 +100,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.7.3
Karol Herbst
2016-Mar-17 23:03 UTC
[Nouveau] [PATCH 19/19] volt: add coefficients I found on my gpu
I am sure that those are a bit different on other GPUs, but while testing
the error range compared to nvidia was around 100%+-3%.
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.
The remaining task for this is to figure out which of these constants are
chip specific and from where to get the chip specific factors
Signed-off-by: Karol Herbst <nouveau at karolherbst.de>
---
drm/nouveau/nvkm/subdev/volt/base.c | 54 ++++++++++++++++++++++++++++++++-----
1 file changed, 48 insertions(+), 6 deletions(-)
diff --git a/drm/nouveau/nvkm/subdev/volt/base.c
b/drm/nouveau/nvkm/subdev/volt/base.c
index 58738e3..2b3f460 100644
--- a/drm/nouveau/nvkm/subdev/volt/base.c
+++ b/drm/nouveau/nvkm/subdev/volt/base.c
@@ -117,13 +117,55 @@ nvkm_volt_map(struct nvkm_volt *volt, u8 id, u8 temp)
vmap = nvbios_vmap_entry_parse(bios, id, &ver, &len, &info);
if (vmap) {
- if (info.link != 0xff) {
- int ret = nvkm_volt_map(volt, info.link, temp);
- if (ret < 0)
- return ret;
- info.min += ret;
+ switch (ver) {
+ case 0x10:
+ if (info.link != 0xff) {
+ int ret = nvkm_volt_map(volt, info.link, temp);
+ if (ret < 0)
+ return ret;
+ info.min += ret;
+ }
+ return info.min;
+ case 0x20: {
+ s32 result;
+ u16 temp = 40;
+ switch (info.mode) {
+ case 0x0:
+ result = info.arg[0] / 10;
+ result += info.arg[1] * 168;
+ result += info.arg[2] * 28;
+ break;
+ case 0x1:
+ result = (info.arg[0] / 1675) * 100;
+ result += info.arg[1] * 100;
+ result += (info.arg[2] / 10) * 153 * temp;
+ result += info.arg[3] * 100 * temp;
+ result += info.arg[4] * 41;
+ result += (info.arg[5] * (temp * temp)) / 15;
+ break;
+ case 0x3:
+ result = (info.min + info.max) / 2;
+ break;
+ case 0x2:
+ default:
+ result = info.min;
+ break;
+ }
+ result = min(max(result, (s32)info.min),
+ (s32)info.max);
+
+ if (info.link != 0xff) {
+ int ret = nvkm_volt_map(volt, info.link, temp);
+ if (ret < 0)
+ return ret;
+ result += ret;
+ }
+
+ return result;
+ }
+ default:
+ return -ENODEV;
}
- return info.min;
}
return id ? id * 10000 : -ENODEV;
--
2.7.3
Martin Peres
2016-Mar-20 17:07 UTC
[Nouveau] [PATCH 03/19] bios: add parsing of BASE CLOCK table
On 18/03/16 01:03, Karol Herbst wrote:> 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. > > Signed-off-by: Karol Herbst <nouveau at karolherbst.de> > --- > 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..eca7b4a > --- /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; > + u8 boost; > + u8 tdp;Maybe it would be easier to understand if you said that base, boost and tdp are actual entry ids. Other than this, looks good! Congrats for figuring this table out! Reviewed-by: Martin Peres <martin.peres at free.fr>> +}; > +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..2f15abb > --- /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 = nvbios_rd08(b, h->offset + 0x0f); > + h->boost = nvbios_rd08(b, h->offset + 0x10); > + h->tdp = 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; > +}
On 18/03/16 01:03, Karol Herbst wrote:> Signed-off-by: Karol Herbst <nouveau at karolherbst.de> > --- > drm/nouveau/nvkm/subdev/clk/base.c | 17 ++++++++++++++++- > 1 file changed, 16 insertions(+), 1 deletion(-) > > diff --git a/drm/nouveau/nvkm/subdev/clk/base.c b/drm/nouveau/nvkm/subdev/clk/base.c > index 889cce2..4928668 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> > @@ -561,10 +562,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, 0, subdev); > + > + if (bios && !nvbios_baseclock_parse(bios, &h)) { > + struct nvbios_baseclk_entry base, boost; > + if (!nvbios_baseclock_entry(bios, &h, h.boost, &boost)) > + nvkm_info(subdev, "boost: %i MHz\n", > + boost.clock_mhz / 2); > + if (!nvbios_baseclock_entry(bios, &h, h.base, &base)) > + nvkm_info(subdev, "base: %i MHz\n", > + base.clock_mhz / 2); > + } > > - nvkm_subdev_ctor(&nvkm_clk, device, index, 0, &clk->subdev); > clk->func = func; > INIT_LIST_HEAD(&clk->states); > clk->domains = func->domains; >I would rather have you report this information in debugfs, if you don't mind. This is really out of place, especially since we do not show the clocks anymore apparently.
Martin Peres
2016-Mar-20 20:15 UTC
[Nouveau] [PATCH 05/19] clk: allow boosting only when NvBoost is set
On 18/03/16 01:03, Karol Herbst wrote:> 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 > Signed-off-by: Karol Herbst <nouveau at karolherbst.de> > --- > drm/nouveau/include/nvkm/subdev/clk.h | 9 ++++++++- > drm/nouveau/nvkm/subdev/clk/base.c | 26 ++++++++++++++++++++++++-- > drm/nouveau/nvkm/subdev/clk/gf100.c | 2 +- > drm/nouveau/nvkm/subdev/clk/gk104.c | 2 +- > 4 files changed, 34 insertions(+), 5 deletions(-) > > diff --git a/drm/nouveau/include/nvkm/subdev/clk.h b/drm/nouveau/include/nvkm/subdev/clk.h > index fb54417..a292e5a 100644 > --- a/drm/nouveau/include/nvkm/subdev/clk.h > +++ b/drm/nouveau/include/nvkm/subdev/clk.h > @@ -67,7 +67,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; > @@ -97,6 +98,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 4928668..d575412 100644 > --- a/drm/nouveau/nvkm/subdev/clk/base.c > +++ b/drm/nouveau/nvkm/subdev/clk/base.c > @@ -160,6 +160,18 @@ nvkm_cstate_new(struct nvkm_clk *clk, int idx, struct nvkm_pstate *pstate) > if (domain->flags & NVKM_CLK_DOM_FLAG_CORE) { > u32 freq = nvkm_clk_adjust(clk, true, pstate->pstate, > domain->bios, cstepX.freq); > + if (domain->flags & NVKM_CLK_DOM_FLAG_BASECLK) { > + switch (clk->boost_mode) { > + case NVKM_CLK_BOOST_NONE: > + if (clk->base_khz > + && freq > clk->base_khz) > + goto err; > + case NVKM_CLK_BOOST_AVG: > + if (clk->boost_khz > + && freq > clk->boost_khz) > + goto err; > + } > + } > cstate->domain[domain->name] = freq; > } > domain++; > @@ -167,6 +179,9 @@ nvkm_cstate_new(struct nvkm_clk *clk, int idx, struct nvkm_pstate *pstate) > > list_add(&cstate->head, &pstate->list); > return 0; > +err: > + kfree(cstate); > + return -EINVAL; > } > > /****************************************************************************** > @@ -570,14 +585,21 @@ nvkm_clk_ctor(const struct nvkm_clk_func *func, struct nvkm_device *device, > > nvkm_subdev_ctor(&nvkm_clk, device, index, 0, 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, &boost)) > + if (!nvbios_baseclock_entry(bios, &h, h.boost, &boost)) { > + clk->boost_khz = boost.clock_mhz * 1000; > nvkm_info(subdev, "boost: %i MHz\n", > boost.clock_mhz / 2); > - if (!nvbios_baseclock_entry(bios, &h, h.base, &base)) > + } > + > + if (!nvbios_baseclock_entry(bios, &h, h.base, &base)) { > + clk->base_khz = base.clock_mhz * 1000; > nvkm_info(subdev, "base: %i MHz\n", > base.clock_mhz / 2); > + } > } > > clk->func = func; > 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 },Reviewed-by: Martin Peres <martin.peres at free.fr>
Reasonably Related Threads
- [PATCH v2 00/22] Volting/Clocking improvements for Fermi and newer
- [PATCH 0/9] Groundwork for clocking fixes
- [PATCH v3 00/29] Volting/Clocking improvements for Fermi and newer
- [RFC PATCH 0/5] stabilize kepler reclocking
- [RFC PATCH v2 0/7] stabilize kepler reclocking