Martin Peres
2015-Sep-09 01:05 UTC
[Nouveau] [PATCH 1/4] bios/volt: add support for pwm-based volt management
Signed-off-by: Martin Peres <martin.peres at free.fr> --- drm/nouveau/include/nvkm/subdev/bios/volt.h | 15 ++++++++++++++- drm/nouveau/nvkm/subdev/bios/volt.c | 17 +++++++++++++++-- 2 files changed, 29 insertions(+), 3 deletions(-) diff --git a/drm/nouveau/include/nvkm/subdev/bios/volt.h b/drm/nouveau/include/nvkm/subdev/bios/volt.h index eb2de4b..b0df610 100644 --- a/drm/nouveau/include/nvkm/subdev/bios/volt.h +++ b/drm/nouveau/include/nvkm/subdev/bios/volt.h @@ -1,11 +1,24 @@ #ifndef __NVBIOS_VOLT_H__ #define __NVBIOS_VOLT_H__ + +enum nvbios_volt_type { + NVBIOS_VOLT_GPIO = 0, + NVBIOS_VOLT_PWM, +}; + struct nvbios_volt { - u8 vidmask; + enum nvbios_volt_type type; u32 min; u32 max; u32 base; + + /* GPIO mode */ + u8 vidmask; s16 step; + + /* PWM mode */ + u32 pwm_freq; + u32 pwm_range; }; u16 nvbios_volt_table(struct nvkm_bios *, u8 *ver, u8 *hdr, u8 *cnt, u8 *len); diff --git a/drm/nouveau/nvkm/subdev/bios/volt.c b/drm/nouveau/nvkm/subdev/bios/volt.c index 615804c..6e0a336 100644 --- a/drm/nouveau/nvkm/subdev/bios/volt.c +++ b/drm/nouveau/nvkm/subdev/bios/volt.c @@ -73,15 +73,19 @@ 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); break; case 0x20: + info->type = NVBIOS_VOLT_GPIO; info->vidmask = nvbios_rd08(bios, volt + 0x05); break; case 0x30: + info->type = NVBIOS_VOLT_GPIO; info->vidmask = nvbios_rd08(bios, volt + 0x04); 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); @@ -90,11 +94,20 @@ nvbios_volt_parse(struct nvkm_bios *bios, u8 *ver, u8 *hdr, u8 *cnt, u8 *len, info->max = info->base; break; case 0x50: - info->vidmask = nvbios_rd08(bios, volt + 0x06); info->min = nvbios_rd32(bios, volt + 0x0a); info->max = nvbios_rd32(bios, volt + 0x0e); info->base = nvbios_rd32(bios, volt + 0x12) & 0x00ffffff; - info->step = nvbios_rd16(bios, volt + 0x16); + + /* offset 4 seems to be a flag byte */ + if (nvbios_rd32(bios, volt + 0x4) & 1) { + info->type = NVBIOS_VOLT_PWM; + 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); + } break; } return volt; -- 2.5.1
Martin Peres
2015-Sep-09 01:05 UTC
[Nouveau] [PATCH 2/4] volt: add support for non-vid-based voltage controllers
This patch is not ideal but it definitely beats a rewrite of the current interface and is very self-contained. Signed-off-by: Martin Peres <martin.peres at free.fr> --- drm/nouveau/nvkm/subdev/volt/base.c | 11 ++++++++++- drm/nouveau/nvkm/subdev/volt/priv.h | 2 ++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/drm/nouveau/nvkm/subdev/volt/base.c b/drm/nouveau/nvkm/subdev/volt/base.c index 4752dbd..50b5649 100644 --- a/drm/nouveau/nvkm/subdev/volt/base.c +++ b/drm/nouveau/nvkm/subdev/volt/base.c @@ -30,7 +30,12 @@ int nvkm_volt_get(struct nvkm_volt *volt) { - int ret = volt->func->vid_get(volt), i; + int ret, i; + + if (volt->func->volt_get) + return volt->func->volt_get(volt); + + ret = volt->func->vid_get(volt); if (ret >= 0) { for (i = 0; i < volt->vid_nr; i++) { if (volt->vid[i].vid == ret) @@ -46,6 +51,10 @@ nvkm_volt_set(struct nvkm_volt *volt, u32 uv) { struct nvkm_subdev *subdev = &volt->subdev; int i, ret = -EINVAL; + + 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); diff --git a/drm/nouveau/nvkm/subdev/volt/priv.h b/drm/nouveau/nvkm/subdev/volt/priv.h index 394f37c..cdb3d9f 100644 --- a/drm/nouveau/nvkm/subdev/volt/priv.h +++ b/drm/nouveau/nvkm/subdev/volt/priv.h @@ -9,6 +9,8 @@ int nvkm_volt_new_(const struct nvkm_volt_func *, struct nvkm_device *, int index, struct nvkm_volt **); struct nvkm_volt_func { + int (*volt_get)(struct nvkm_volt *); + int (*volt_set)(struct nvkm_volt *, u32 uv); int (*vid_get)(struct nvkm_volt *); int (*vid_set)(struct nvkm_volt *, u8 vid); int (*set_id)(struct nvkm_volt *, u8 id, int condition); -- 2.5.1
Martin Peres
2015-Sep-09 01:05 UTC
[Nouveau] [PATCH 3/4] volt/gk104: add support for pwm and gpio modes
Most Keplers actually use the GPIO-based voltage management instead of the new PWM-based one. Use the GPIO mode as a fallback as it already gracefully handles the case where no GPIOs exist. All the Maxwells seem to use the PWM method though. Signed-off-by: Martin Peres <martin.peres at free.fr> --- drm/nouveau/include/nvkm/subdev/bios/gpio.h | 1 + drm/nouveau/include/nvkm/subdev/volt.h | 1 + drm/nouveau/nvkm/engine/device/base.c | 14 ++-- drm/nouveau/nvkm/subdev/volt/Kbuild | 1 + drm/nouveau/nvkm/subdev/volt/gk104.c | 118 ++++++++++++++++++++++++++++ drm/nouveau/nvkm/subdev/volt/priv.h | 4 + 6 files changed, 132 insertions(+), 7 deletions(-) create mode 100644 drm/nouveau/nvkm/subdev/volt/gk104.c diff --git a/drm/nouveau/include/nvkm/subdev/bios/gpio.h b/drm/nouveau/include/nvkm/subdev/bios/gpio.h index 33be260..a47d46d 100644 --- a/drm/nouveau/include/nvkm/subdev/bios/gpio.h +++ b/drm/nouveau/include/nvkm/subdev/bios/gpio.h @@ -15,6 +15,7 @@ enum dcb_gpio_func_name { DCB_GPIO_VID5 = 0x74, DCB_GPIO_VID6 = 0x75, DCB_GPIO_VID7 = 0x76, + DCB_GPIO_VID_PWM = 0x81, }; #define DCB_GPIO_LOG_DIR 0x02 diff --git a/drm/nouveau/include/nvkm/subdev/volt.h b/drm/nouveau/include/nvkm/subdev/volt.h index 5c8a3f1..b458d04 100644 --- a/drm/nouveau/include/nvkm/subdev/volt.h +++ b/drm/nouveau/include/nvkm/subdev/volt.h @@ -18,5 +18,6 @@ int nvkm_volt_get(struct nvkm_volt *); int nvkm_volt_set_id(struct nvkm_volt *, u8 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 **); int gk20a_volt_new(struct nvkm_device *, int, struct nvkm_volt **); #endif diff --git a/drm/nouveau/nvkm/engine/device/base.c b/drm/nouveau/nvkm/engine/device/base.c index 94a906b..7c35f1f 100644 --- a/drm/nouveau/nvkm/engine/device/base.c +++ b/drm/nouveau/nvkm/engine/device/base.c @@ -1673,7 +1673,7 @@ nve4_chipset = { .pmu = gk104_pmu_new, .therm = gf119_therm_new, .timer = nv41_timer_new, - .volt = nv40_volt_new, + .volt = gk104_volt_new, .ce[0] = gk104_ce_new, .ce[1] = gk104_ce_new, .ce[2] = gk104_ce_new, @@ -1710,7 +1710,7 @@ nve6_chipset = { .pmu = gk104_pmu_new, .therm = gf119_therm_new, .timer = nv41_timer_new, - .volt = nv40_volt_new, + .volt = gk104_volt_new, .ce[0] = gk104_ce_new, .ce[1] = gk104_ce_new, .ce[2] = gk104_ce_new, @@ -1747,7 +1747,7 @@ nve7_chipset = { .pmu = gf119_pmu_new, .therm = gf119_therm_new, .timer = nv41_timer_new, - .volt = nv40_volt_new, + .volt = gk104_volt_new, .ce[0] = gk104_ce_new, .ce[1] = gk104_ce_new, .ce[2] = gk104_ce_new, @@ -1808,7 +1808,7 @@ nvf0_chipset = { .pmu = gk110_pmu_new, .therm = gf119_therm_new, .timer = nv41_timer_new, - .volt = nv40_volt_new, + .volt = gk104_volt_new, .ce[0] = gk104_ce_new, .ce[1] = gk104_ce_new, .ce[2] = gk104_ce_new, @@ -1844,7 +1844,7 @@ nvf1_chipset = { .pmu = gk110_pmu_new, .therm = gf119_therm_new, .timer = nv41_timer_new, - .volt = nv40_volt_new, + .volt = gk104_volt_new, .ce[0] = gk104_ce_new, .ce[1] = gk104_ce_new, .ce[2] = gk104_ce_new, @@ -1880,7 +1880,7 @@ nv106_chipset = { .pmu = gk208_pmu_new, .therm = gf119_therm_new, .timer = nv41_timer_new, - .volt = nv40_volt_new, + .volt = gk104_volt_new, .ce[0] = gk104_ce_new, .ce[1] = gk104_ce_new, .ce[2] = gk104_ce_new, @@ -1916,7 +1916,7 @@ nv108_chipset = { .pmu = gk208_pmu_new, .therm = gf119_therm_new, .timer = nv41_timer_new, - .volt = nv40_volt_new, + .volt = gk104_volt_new, .ce[0] = gk104_ce_new, .ce[1] = gk104_ce_new, .ce[2] = gk104_ce_new, diff --git a/drm/nouveau/nvkm/subdev/volt/Kbuild b/drm/nouveau/nvkm/subdev/volt/Kbuild index 6b46ff4..b035c6e 100644 --- a/drm/nouveau/nvkm/subdev/volt/Kbuild +++ b/drm/nouveau/nvkm/subdev/volt/Kbuild @@ -1,4 +1,5 @@ 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/gk104.o nvkm-y += nvkm/subdev/volt/gk20a.o diff --git a/drm/nouveau/nvkm/subdev/volt/gk104.c b/drm/nouveau/nvkm/subdev/volt/gk104.c new file mode 100644 index 0000000..0d28be6 --- /dev/null +++ b/drm/nouveau/nvkm/subdev/volt/gk104.c @@ -0,0 +1,118 @@ +/* + * Copyright 2015 Martin Peres + * + * 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: Martin Peres + */ +#include "priv.h" + +#include <subdev/volt.h> +#include <subdev/gpio.h> +#include <subdev/bios.h> +#include <subdev/bios/volt.h> + +#define gk104_volt(p) container_of((p), struct gk104_volt, base) +struct gk104_volt { + struct nvkm_volt base; + struct nvbios_volt bios; +}; + +int +gk104_volt_get(struct nvkm_volt *base) +{ + struct nvbios_volt *bios = &gk104_volt(base)->bios; + struct nvkm_device *device = base->subdev.device; + u32 div, duty; + + div = nvkm_rd32(device, 0x20340); + duty = nvkm_rd32(device, 0x20344); + + return bios->base + bios->pwm_range * duty / div; +} + +int +gk104_volt_set(struct nvkm_volt *base, u32 uv) +{ + struct nvbios_volt *bios = &gk104_volt(base)->bios; + struct nvkm_device *device = base->subdev.device; + u32 div; + + /* the blob uses this crystal frequency, let's use it too. */ + div = 27648000 / bios->pwm_freq; + + nvkm_wr32(device, 0x20340, div); + nvkm_wr32(device, 0x20344, (uv - bios->base) * div / bios->pwm_range); + + return 0; +} + +static const struct nvkm_volt_func +gk104_volt_pwm = { + .volt_get = gk104_volt_get, + .volt_set = gk104_volt_set, +}, gk104_volt_gpio = { + .vid_get = nvkm_voltgpio_get, + .vid_set = nvkm_voltgpio_set, +}; + +int +gk104_volt_new(struct nvkm_device *device, int index, struct nvkm_volt **pvolt) +{ + const struct nvkm_volt_func *volt_func = &gk104_volt_gpio; + struct dcb_gpio_func gpio; + struct nvbios_volt bios; + struct gk104_volt *volt; + u8 ver, hdr, cnt, len; + const char *mode; + + if (!nvbios_volt_parse(device->bios, &ver, &hdr, &cnt, &len, &bios)) + return 0; + + if (!nvkm_gpio_find(device->gpio, 0, DCB_GPIO_VID_PWM, 0xff, &gpio) && + bios.type == NVBIOS_VOLT_PWM) { + volt_func = &gk104_volt_pwm; + } + + if (!(volt = kzalloc(sizeof(*volt), GFP_KERNEL))) + return -ENOMEM; + nvkm_volt_ctor(volt_func, device, index, &volt->base); + *pvolt = &volt->base; + volt->bios = bios; + + /* now that we have a subdev, we can show an error if we found through + * the voltage table that we were supposed to use the PWN mode but we + * did not find the right GPIO for it. + */ + if (bios.type == NVBIOS_VOLT_PWM && volt_func != &gk104_volt_pwm) { + nvkm_error(&volt->base.subdev, + "Type mismatch between the voltage table type and " + "the GPIO table. Fallback to GPIO mode.\n"); + } + + if (volt_func == &gk104_volt_gpio) { + nvkm_voltgpio_init(&volt->base); + mode = "GPIO"; + } else + mode = "PWM"; + + nvkm_debug(&volt->base.subdev, "Using %s mode\n", mode); + + return 0; +} diff --git a/drm/nouveau/nvkm/subdev/volt/priv.h b/drm/nouveau/nvkm/subdev/volt/priv.h index cdb3d9f..d5140d9 100644 --- a/drm/nouveau/nvkm/subdev/volt/priv.h +++ b/drm/nouveau/nvkm/subdev/volt/priv.h @@ -19,4 +19,8 @@ struct nvkm_volt_func { int nvkm_voltgpio_init(struct nvkm_volt *); int nvkm_voltgpio_get(struct nvkm_volt *); int nvkm_voltgpio_set(struct nvkm_volt *, u8); + +int nvkm_voltpwm_init(struct nvkm_volt *volt); +int nvkm_voltpwm_get(struct nvkm_volt *volt); +int nvkm_voltpwm_set(struct nvkm_volt *volt, u32 uv); #endif -- 2.5.1
Martin Peres
2015-Sep-09 01:05 UTC
[Nouveau] [PATCH 4/4] gm107: add voltage control using the new gk104 volt class
Let's ignore the other desktop Maxwells until I get my hands on one and confirm that still can change the voltage. Signed-off-by: Martin Peres <martin.peres at free.fr> --- drm/nouveau/nvkm/engine/device/base.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drm/nouveau/nvkm/engine/device/base.c b/drm/nouveau/nvkm/engine/device/base.c index 7c35f1f..b324fd1 100644 --- a/drm/nouveau/nvkm/engine/device/base.c +++ b/drm/nouveau/nvkm/engine/device/base.c @@ -1952,6 +1952,7 @@ nv117_chipset = { .pmu = gm107_pmu_new, .therm = gm107_therm_new, .timer = gk20a_timer_new, + .volt = gk104_volt_new, .ce[0] = gk104_ce_new, .ce[2] = gk104_ce_new, .disp = gm107_disp_new, -- 2.5.1