diff options
author | Martin Peres <martin.peres@labri.fr> | 2012-08-16 11:00:55 +0200 |
---|---|---|
committer | Ben Skeggs <bskeggs@redhat.com> | 2012-10-03 13:13:13 +1000 |
commit | 7d70e9c1c69a5d22588ff5977249ac944d7cdfb0 (patch) | |
tree | 98a34b0cd7568d2a9b0faa0e857bba3e5b5dd84b | |
parent | e36199980b51f081671d76de7b7b8f94fe1e0ae3 (diff) |
drm/nouveau/therm: rework thermal table parsing
As an accident, it should also fix temperature reading on nv4x.
v2: introduce nvbios_therm_entry as advised by darktama
Signed-off-by: Martin Peres <martin.peres@labri.fr>
Signed-off-by: Ben Skeggs <bskeggs@redhat.com>
-rw-r--r-- | drivers/gpu/drm/nouveau/Makefile | 1 | ||||
-rw-r--r-- | drivers/gpu/drm/nouveau/core/include/subdev/bios/therm.h | 46 | ||||
-rw-r--r-- | drivers/gpu/drm/nouveau/core/subdev/bios/therm.c | 177 | ||||
-rw-r--r-- | drivers/gpu/drm/nouveau/nouveau_pm.c | 5 | ||||
-rw-r--r-- | drivers/gpu/drm/nouveau/nouveau_pm.h | 1 | ||||
-rw-r--r-- | drivers/gpu/drm/nouveau/nouveau_temp.c | 257 |
6 files changed, 282 insertions, 205 deletions
diff --git a/drivers/gpu/drm/nouveau/Makefile b/drivers/gpu/drm/nouveau/Makefile index 1c506c5a9a4..b6a6fa7fbcd 100644 --- a/drivers/gpu/drm/nouveau/Makefile +++ b/drivers/gpu/drm/nouveau/Makefile @@ -35,6 +35,7 @@ nouveau-y += core/subdev/bios/i2c.o nouveau-y += core/subdev/bios/init.o nouveau-y += core/subdev/bios/mxm.o nouveau-y += core/subdev/bios/pll.o +nouveau-y += core/subdev/bios/therm.o nouveau-y += core/subdev/clock/nv04.o nouveau-y += core/subdev/clock/nv40.o nouveau-y += core/subdev/clock/nv50.o diff --git a/drivers/gpu/drm/nouveau/core/include/subdev/bios/therm.h b/drivers/gpu/drm/nouveau/core/include/subdev/bios/therm.h new file mode 100644 index 00000000000..a2c4296fc5f --- /dev/null +++ b/drivers/gpu/drm/nouveau/core/include/subdev/bios/therm.h @@ -0,0 +1,46 @@ +#ifndef __NVBIOS_THERM_H__ +#define __NVBIOS_THERM_H__ + +struct nouveau_bios; + +struct nvbios_therm_threshold { + u8 temp; + u8 hysteresis; +}; + +struct nvbios_therm_sensor { + /* diode */ + s16 slope_mult; + s16 slope_div; + s16 offset_num; + s16 offset_den; + s8 offset_constant; + + /* thresholds */ + struct nvbios_therm_threshold thrs_fan_boost; + struct nvbios_therm_threshold thrs_down_clock; + struct nvbios_therm_threshold thrs_critical; + struct nvbios_therm_threshold thrs_shutdown; +}; + +struct nvbios_therm_fan { + u16 pwm_freq; + + u8 min_duty; + u8 max_duty; +}; + +enum nvbios_therm_domain { + NVBIOS_THERM_DOMAIN_CORE, + NVBIOS_THERM_DOMAIN_AMBIENT, +}; + +int +nvbios_therm_sensor_parse(struct nouveau_bios *, enum nvbios_therm_domain, + struct nvbios_therm_sensor *); + +int +nvbios_therm_fan_parse(struct nouveau_bios *, struct nvbios_therm_fan *); + + +#endif diff --git a/drivers/gpu/drm/nouveau/core/subdev/bios/therm.c b/drivers/gpu/drm/nouveau/core/subdev/bios/therm.c new file mode 100644 index 00000000000..862a08a2ae2 --- /dev/null +++ b/drivers/gpu/drm/nouveau/core/subdev/bios/therm.c @@ -0,0 +1,177 @@ +/* + * Copyright 2012 Nouveau Community + * + * 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 <subdev/bios.h> +#include <subdev/bios/bit.h> +#include <subdev/bios/therm.h> + +static u16 +therm_table(struct nouveau_bios *bios, u8 *ver, u8 *hdr, u8 *len, u8 *cnt) +{ + struct bit_entry bit_P; + u16 therm = 0; + + if (!bit_entry(bios, 'P', &bit_P)) { + if (bit_P.version == 1) + therm = nv_ro16(bios, bit_P.offset + 12); + else if (bit_P.version == 2) + therm = nv_ro16(bios, bit_P.offset + 16); + else + nv_error(bios, + "unknown offset for thermal in BIT P %d\n", + bit_P.version); + } + + /* exit now if we haven't found the thermal table */ + if (!therm) + return 0x0000; + + *ver = nv_ro08(bios, therm + 0); + *hdr = nv_ro08(bios, therm + 1); + *len = nv_ro08(bios, therm + 2); + *cnt = nv_ro08(bios, therm + 3); + + return therm + nv_ro08(bios, therm + 1); +} + +u16 +nvbios_therm_entry(struct nouveau_bios *bios, int idx, u8 *ver, u8 *len) +{ + u8 hdr, cnt; + u16 therm = therm_table(bios, ver, &hdr, len, &cnt); + if (therm && idx < cnt) + return therm + idx * *len; + return 0x0000; +} + +int +nvbios_therm_sensor_parse(struct nouveau_bios *bios, + enum nvbios_therm_domain domain, + struct nvbios_therm_sensor *sensor) +{ + s8 thrs_section, sensor_section, offset; + u8 ver, len, i; + u16 entry; + + /* we only support the core domain for now */ + if (domain != NVBIOS_THERM_DOMAIN_CORE) + return -EINVAL; + + /* Read the entries from the table */ + thrs_section = 0; + sensor_section = -1; + i = 0; + while ((entry = nvbios_therm_entry(bios, i++, &ver, &len))) { + s16 value = nv_ro16(bios, entry + 1); + + switch (nv_ro08(bios, entry + 0)) { + case 0x0: + thrs_section = value; + if (value > 0) + return 0; /* we do not try to support ambient */ + break; + case 0x01: + sensor_section++; + if (sensor_section == 0) { + offset = ((s8) nv_ro08(bios, entry + 2)) / 2; + sensor->offset_constant = offset; + } + break; + + case 0x04: + if (thrs_section == 0) { + sensor->thrs_critical.temp = (value & 0xff0) >> 4; + sensor->thrs_critical.hysteresis = value & 0xf; + } + break; + + case 0x07: + if (thrs_section == 0) { + sensor->thrs_down_clock.temp = (value & 0xff0) >> 4; + sensor->thrs_down_clock.hysteresis = value & 0xf; + } + break; + + case 0x08: + if (thrs_section == 0) { + sensor->thrs_fan_boost.temp = (value & 0xff0) >> 4; + sensor->thrs_fan_boost.hysteresis = value & 0xf; + } + break; + + case 0x10: + if (sensor_section == 0) + sensor->offset_num = value; + break; + + case 0x11: + if (sensor_section == 0) + sensor->offset_den = value; + break; + + case 0x12: + if (sensor_section == 0) + sensor->slope_mult = value; + break; + + case 0x13: + if (sensor_section == 0) + sensor->slope_div = value; + break; + case 0x32: + if (thrs_section == 0) { + sensor->thrs_shutdown.temp = (value & 0xff0) >> 4; + sensor->thrs_shutdown.hysteresis = value & 0xf; + } + break; + } + } + + return 0; +} + +int +nvbios_therm_fan_parse(struct nouveau_bios *bios, + struct nvbios_therm_fan *fan) +{ + u8 ver, len, i; + u16 entry; + + i = 0; + while ((entry = nvbios_therm_entry(bios, i++, &ver, &len))) { + s16 value = nv_ro16(bios, entry + 1); + + switch (nv_ro08(bios, entry + 0)) { + case 0x22: + fan->min_duty = value & 0xff; + fan->max_duty = (value & 0xff00) >> 8; + break; + case 0x26: + fan->pwm_freq = value; + break; + } + } + + return 0; +} diff --git a/drivers/gpu/drm/nouveau/nouveau_pm.c b/drivers/gpu/drm/nouveau/nouveau_pm.c index d2f2de67f68..bdd50953488 100644 --- a/drivers/gpu/drm/nouveau/nouveau_pm.c +++ b/drivers/gpu/drm/nouveau/nouveau_pm.c @@ -891,10 +891,7 @@ nouveau_pm_init(struct drm_device *dev) } pm->voltage_get = nouveau_voltage_gpio_get; pm->voltage_set = nouveau_voltage_gpio_set; - if (device->chipset == 0x50) - pm->temp_get = nv40_temp_get; - else - pm->temp_get = nv84_temp_get; + pm->temp_get = nv84_temp_get; pm->pwm_get = nv50_pm_pwm_get; pm->pwm_set = nv50_pm_pwm_set; } else diff --git a/drivers/gpu/drm/nouveau/nouveau_pm.h b/drivers/gpu/drm/nouveau/nouveau_pm.h index e2ec9c0ed56..2e39ccbd5d4 100644 --- a/drivers/gpu/drm/nouveau/nouveau_pm.h +++ b/drivers/gpu/drm/nouveau/nouveau_pm.h @@ -148,7 +148,6 @@ struct nouveau_pm_temp_sensor_constants { struct nouveau_pm_threshold_temp { s16 critical; s16 down_clock; - s16 fan_boost; }; struct nouveau_pm_fan { diff --git a/drivers/gpu/drm/nouveau/nouveau_temp.c b/drivers/gpu/drm/nouveau/nouveau_temp.c index 7ae25e47be9..5465abf87f1 100644 --- a/drivers/gpu/drm/nouveau/nouveau_temp.c +++ b/drivers/gpu/drm/nouveau/nouveau_temp.c @@ -30,187 +30,23 @@ #include "nouveau_pm.h" #include <subdev/i2c.h> - -static void -nouveau_temp_vbios_parse(struct drm_device *dev, u8 *temp) -{ - struct nouveau_drm *drm = nouveau_drm(dev); - struct nouveau_pm *pm = nouveau_pm(dev); - struct nouveau_pm_temp_sensor_constants *sensor = &pm->sensor_constants; - struct nouveau_pm_threshold_temp *temps = &pm->threshold_temp; - int i, headerlen, recordlen, entries; - - if (!temp) { - NV_DEBUG(drm, "temperature table pointer invalid\n"); - return; - } - - /* Set the default sensor's contants */ - sensor->offset_constant = 0; - sensor->offset_mult = 0; - sensor->offset_div = 1; - sensor->slope_mult = 1; - sensor->slope_div = 1; - - /* Set the default temperature thresholds */ - temps->critical = 110; - temps->down_clock = 100; - temps->fan_boost = 90; - - /* Set the default range for the pwm fan */ - pm->fan.min_duty = 30; - pm->fan.max_duty = 100; - - /* Set the known default values to setup the temperature sensor */ - if (nv_device(drm->device)->card_type >= NV_40) { - switch (nv_device(drm->device)->chipset) { - case 0x43: - sensor->offset_mult = 32060; - sensor->offset_div = 1000; - sensor->slope_mult = 792; - sensor->slope_div = 1000; - break; - - case 0x44: - case 0x47: - case 0x4a: - sensor->offset_mult = 27839; - sensor->offset_div = 1000; - sensor->slope_mult = 780; - sensor->slope_div = 1000; - break; - - case 0x46: - sensor->offset_mult = -24775; - sensor->offset_div = 100; - sensor->slope_mult = 467; - sensor->slope_div = 10000; - break; - - case 0x49: - sensor->offset_mult = -25051; - sensor->offset_div = 100; - sensor->slope_mult = 458; - sensor->slope_div = 10000; - break; - - case 0x4b: - sensor->offset_mult = -24088; - sensor->offset_div = 100; - sensor->slope_mult = 442; - sensor->slope_div = 10000; - break; - - case 0x50: - sensor->offset_mult = -22749; - sensor->offset_div = 100; - sensor->slope_mult = 431; - sensor->slope_div = 10000; - break; - - case 0x67: - sensor->offset_mult = -26149; - sensor->offset_div = 100; - sensor->slope_mult = 484; - sensor->slope_div = 10000; - break; - } - } - - headerlen = temp[1]; - recordlen = temp[2]; - entries = temp[3]; - temp = temp + headerlen; - - /* Read the entries from the table */ - for (i = 0; i < entries; i++) { - s16 value = ROM16(temp[1]); - - switch (temp[0]) { - case 0x01: - if ((value & 0x8f) == 0) - sensor->offset_constant = (value >> 9) & 0x7f; - break; - - case 0x04: - if ((value & 0xf00f) == 0xa000) /* core */ - temps->critical = (value&0x0ff0) >> 4; - break; - - case 0x07: - if ((value & 0xf00f) == 0xa000) /* core */ - temps->down_clock = (value&0x0ff0) >> 4; - break; - - case 0x08: - if ((value & 0xf00f) == 0xa000) /* core */ - temps->fan_boost = (value&0x0ff0) >> 4; - break; - - case 0x10: - sensor->offset_mult = value; - break; - - case 0x11: - sensor->offset_div = value; - break; - - case 0x12: - sensor->slope_mult = value; - break; - - case 0x13: - sensor->slope_div = value; - break; - case 0x22: - pm->fan.min_duty = value & 0xff; - pm->fan.max_duty = (value & 0xff00) >> 8; - break; - case 0x26: - pm->fan.pwm_freq = value; - break; - } - temp += recordlen; - } - - nouveau_temp_safety_checks(dev); - - /* check the fan min/max settings */ - if (pm->fan.min_duty < 10) - pm->fan.min_duty = 10; - if (pm->fan.max_duty > 100) - pm->fan.max_duty = 100; - if (pm->fan.max_duty < pm->fan.min_duty) - pm->fan.max_duty = pm->fan.min_duty; -} +#include <subdev/bios/therm.h> static int nv40_sensor_setup(struct drm_device *dev) { struct nouveau_device *device = nouveau_dev(dev); struct nouveau_drm *drm = nouveau_drm(dev); - struct nouveau_pm *pm = nouveau_pm(dev); - struct nouveau_pm_temp_sensor_constants *sensor = &pm->sensor_constants; - s32 offset = sensor->offset_mult / sensor->offset_div; - s32 sensor_calibration; - - /* set up the sensors */ - sensor_calibration = 120 - offset - sensor->offset_constant; - sensor_calibration = sensor_calibration * sensor->slope_div / - sensor->slope_mult; - if (nv_device(drm->device)->chipset >= 0x46) - sensor_calibration |= 0x80000000; - else - sensor_calibration |= 0x10000000; - - nv_wr32(device, 0x0015b0, sensor_calibration); - - /* Wait for the sensor to update */ - msleep(5); - - /* read */ - return nv_rd32(device, 0x0015b4) & 0x1fff; + /* enable ADC readout and disable the ALARM threshold */ + if (nv_device(drm->device)->chipset >= 0x46) { + nv_mask(device, 0x15b8, 0x80000000, 0); + nv_wr32(device, 0x15b0, 0x80003fff); + return nv_rd32(device, 0x15b4) & 0x3fff; + } else { + nv_wr32(device, 0x15b0, 0xff); + return nv_rd32(device, 0x15b4) & 0xff; + } } int @@ -220,20 +56,30 @@ nv40_temp_get(struct drm_device *dev) struct nouveau_drm *drm = nouveau_drm(dev); struct nouveau_pm *pm = nouveau_pm(dev); struct nouveau_pm_temp_sensor_constants *sensor = &pm->sensor_constants; - int offset = sensor->offset_mult / sensor->offset_div; int core_temp; - if (nv_device(drm->device)->card_type >= NV_50) { - core_temp = nv_rd32(device, 0x20008); + if (nv_device(drm->device)->chipset >= 0x46) { + nv_wr32(device, 0x15b0, 0x80003fff); + core_temp = nv_rd32(device, 0x15b4) & 0x3fff; } else { - core_temp = nv_rd32(device, 0x0015b4) & 0x1fff; - /* Setup the sensor if the temperature is 0 */ - if (core_temp == 0) - core_temp = nv40_sensor_setup(dev); + nv_wr32(device, 0x15b0, 0xff); + core_temp = nv_rd32(device, 0x15b4) & 0xff; } + /* Setup the sensor if the temperature is 0 */ + if (core_temp == 0) + core_temp = nv40_sensor_setup(dev); + + if (sensor->slope_div == 0) + sensor->slope_div = 1; + if (sensor->offset_div == 0) + sensor->offset_div = 1; + if (sensor->slope_mult < 1) + sensor->slope_mult = 1; + core_temp = core_temp * sensor->slope_mult / sensor->slope_div; - core_temp = core_temp + offset + sensor->offset_constant; + core_temp = core_temp + sensor->offset_mult / sensor->offset_div; + core_temp = core_temp + sensor->offset_constant - 8; return core_temp; } @@ -260,11 +106,6 @@ nouveau_temp_safety_checks(struct drm_device *dev) temps->down_clock = 110; else if (temps->down_clock < 60) temps->down_clock = 60; - - if (temps->fan_boost > 100) - temps->fan_boost = 100; - else if (temps->fan_boost < 40) - temps->fan_boost = 40; } static bool @@ -309,24 +150,40 @@ void nouveau_temp_init(struct drm_device *dev) { struct nouveau_drm *drm = nouveau_drm(dev); - struct nvbios *bios = &drm->vbios; - struct bit_entry P; - u8 *temp = NULL; + struct nouveau_device *device = nv_device(drm->device); + struct nouveau_bios *bios = nouveau_bios(device); + struct nouveau_pm *pm = nouveau_pm(dev); + struct nouveau_pm_temp_sensor_constants *sensor = &pm->sensor_constants; + struct nouveau_pm_threshold_temp *temps = &pm->threshold_temp; + struct nvbios_therm_sensor bios_sensor; + struct nvbios_therm_fan bios_fan; - if (bios->type == NVBIOS_BIT) { - if (bit_table(dev, 'P', &P)) - return; + /* store some safe defaults */ + sensor->offset_constant = 0; + sensor->offset_mult = 0; + sensor->offset_div = 1; + sensor->slope_mult = 1; + sensor->slope_div = 1; - if (P.version == 1) - temp = ROMPTR(dev, P.data[12]); - else if (P.version == 2) - temp = ROMPTR(dev, P.data[16]); - else - NV_WARN(drm, "unknown temp for BIT P %d\n", P.version); + if (!nvbios_therm_sensor_parse(bios, NVBIOS_THERM_DOMAIN_CORE, + &bios_sensor)) { + sensor->slope_mult = bios_sensor.slope_mult; + sensor->slope_div = bios_sensor.slope_div; + sensor->offset_mult = bios_sensor.offset_num; + sensor->offset_div = bios_sensor.offset_den; + sensor->offset_constant = bios_sensor.offset_constant; - nouveau_temp_vbios_parse(dev, temp); + temps->down_clock = bios_sensor.thrs_down_clock.temp; + temps->critical = bios_sensor.thrs_critical.temp; } + if (nvbios_therm_fan_parse(bios, &bios_fan)) { + pm->fan.min_duty = bios_fan.min_duty; + pm->fan.max_duty = bios_fan.max_duty; + pm->fan.pwm_freq = bios_fan.pwm_freq; + } + + nouveau_temp_safety_checks(dev); nouveau_temp_probe_i2c(dev); } |