Commit 7d70e9c1 authored by Martin Peres's avatar Martin Peres Committed by Ben Skeggs

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: default avatarMartin Peres <martin.peres@labri.fr>
Signed-off-by: default avatarBen Skeggs <bskeggs@redhat.com>
parent e3619998
......@@ -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
......
#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
/*
* 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;
}
......@@ -891,9 +891,6 @@ 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->pwm_get = nv50_pm_pwm_get;
pm->pwm_set = nv50_pm_pwm_set;
......
......@@ -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 {
......
......@@ -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;
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 (!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;
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);
temps->down_clock = bios_sensor.thrs_down_clock.temp;
temps->critical = bios_sensor.thrs_critical.temp;
}
nouveau_temp_vbios_parse(dev, 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);
}
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment