Loading drivers/gpu/drm/nouveau/Makefile +1 −1 Original line number Diff line number Diff line Loading @@ -25,7 +25,7 @@ nouveau-y := nouveau_drv.o nouveau_state.o nouveau_channel.o nouveau_mem.o \ nv04_crtc.o nv04_display.o nv04_cursor.o nv04_fbcon.o \ nv10_gpio.o nv50_gpio.o \ nv50_calc.o \ nv04_pm.o nv50_pm.o nv04_pm.o nv50_pm.o nouveau_temp.o nouveau-$(CONFIG_DRM_NOUVEAU_DEBUG) += nouveau_debugfs.o nouveau-$(CONFIG_COMPAT) += nouveau_ioc32.o Loading drivers/gpu/drm/nouveau/nouveau_drv.h +18 −0 Original line number Diff line number Diff line Loading @@ -387,10 +387,26 @@ struct nouveau_pm_level { u8 fanspeed; }; struct nouveau_pm_temp_sensor_constants { u16 offset_constant; s16 offset_mult; u16 offset_div; u16 slope_mult; u16 slope_div; }; struct nouveau_pm_threshold_temp { s16 critical; s16 down_clock; s16 fan_boost; }; struct nouveau_pm_engine { struct nouveau_pm_voltage voltage; struct nouveau_pm_level perflvl[NOUVEAU_PM_MAX_LEVEL]; int nr_perflvl; struct nouveau_pm_temp_sensor_constants sensor_constants; struct nouveau_pm_threshold_temp threshold_temp; struct nouveau_pm_level boot; struct nouveau_pm_level *cur; Loading Loading @@ -663,6 +679,8 @@ struct drm_nouveau_private { struct nouveau_fbdev *nfbdev; struct apertures_struct *apertures; struct device *int_hwmon_dev; }; static inline struct drm_nouveau_private * Loading drivers/gpu/drm/nouveau/nouveau_pm.c +221 −38 Original line number Diff line number Diff line Loading @@ -27,6 +27,9 @@ #include "nouveau_drv.h" #include "nouveau_pm.h" #include <linux/hwmon.h> #include <linux/hwmon-sysfs.h> static int nouveau_pm_clock_set(struct drm_device *dev, u8 id, u32 khz) { Loading Loading @@ -227,43 +230,14 @@ nouveau_pm_set_perflvl(struct device *d, struct device_attribute *a, DEVICE_ATTR(performance_level, S_IRUGO | S_IWUSR, nouveau_pm_get_perflvl, nouveau_pm_set_perflvl); int nouveau_pm_init(struct drm_device *dev) static int nouveau_sysfs_init(struct drm_device *dev) { struct drm_nouveau_private *dev_priv = dev->dev_private; struct nouveau_pm_engine *pm = &dev_priv->engine.pm; struct device *d = &dev->pdev->dev; char info[256]; int ret, i; nouveau_volt_init(dev); nouveau_perf_init(dev); NV_INFO(dev, "%d available performance level(s)\n", pm->nr_perflvl); for (i = 0; i < pm->nr_perflvl; i++) { nouveau_pm_perflvl_info(&pm->perflvl[i], info, sizeof(info)); NV_INFO(dev, "%d: %s", pm->perflvl[i].id, info); } /* determine current ("boot") performance level */ ret = nouveau_pm_perflvl_get(dev, &pm->boot); if (ret == 0) { pm->cur = &pm->boot; nouveau_pm_perflvl_info(&pm->boot, info, sizeof(info)); NV_INFO(dev, "c: %s", info); } /* switch performance levels now if requested */ if (nouveau_perflvl != NULL) { ret = nouveau_pm_profile_set(dev, nouveau_perflvl); if (ret) { NV_ERROR(dev, "error setting perflvl \"%s\": %d\n", nouveau_perflvl, ret); } } /* initialise sysfs */ ret = device_create_file(d, &dev_attr_performance_level); if (ret) return ret; Loading @@ -290,17 +264,14 @@ nouveau_pm_init(struct drm_device *dev) return 0; } void nouveau_pm_fini(struct drm_device *dev) static void nouveau_sysfs_fini(struct drm_device *dev) { struct drm_nouveau_private *dev_priv = dev->dev_private; struct nouveau_pm_engine *pm = &dev_priv->engine.pm; struct device *d = &dev->pdev->dev; int i; if (pm->cur != &pm->boot) nouveau_pm_perflvl_set(dev, &pm->boot); device_remove_file(d, &dev_attr_performance_level); for (i = 0; i < pm->nr_perflvl; i++) { struct nouveau_pm_level *pl = &pm->perflvl[i]; Loading @@ -310,9 +281,221 @@ nouveau_pm_fini(struct drm_device *dev) device_remove_file(d, &pl->dev_attr); } } static ssize_t nouveau_hwmon_show_temp(struct device *d, struct device_attribute *a, char *buf) { struct drm_device *dev = dev_get_drvdata(d); return snprintf(buf, PAGE_SIZE, "%d\n", nouveau_temp_get(dev)*1000); } static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, nouveau_hwmon_show_temp, NULL, 0); static ssize_t nouveau_hwmon_max_temp(struct device *d, struct device_attribute *a, char *buf) { struct drm_device *dev = dev_get_drvdata(d); struct drm_nouveau_private *dev_priv = dev->dev_private; struct nouveau_pm_engine *pm = &dev_priv->engine.pm; struct nouveau_pm_threshold_temp *temp = &pm->threshold_temp; return snprintf(buf, PAGE_SIZE, "%d\n", temp->down_clock*1000); } static ssize_t nouveau_hwmon_set_max_temp(struct device *d, struct device_attribute *a, const char *buf, size_t count) { struct drm_device *dev = dev_get_drvdata(d); struct drm_nouveau_private *dev_priv = dev->dev_private; struct nouveau_pm_engine *pm = &dev_priv->engine.pm; struct nouveau_pm_threshold_temp *temp = &pm->threshold_temp; long value; if (strict_strtoul(buf, 10, &value) == -EINVAL) return count; temp->down_clock = value/1000; nouveau_temp_safety_checks(dev); return count; } static SENSOR_DEVICE_ATTR(temp1_max, S_IRUGO | S_IWUSR, nouveau_hwmon_max_temp, nouveau_hwmon_set_max_temp, 0); static ssize_t nouveau_hwmon_critical_temp(struct device *d, struct device_attribute *a, char *buf) { struct drm_device *dev = dev_get_drvdata(d); struct drm_nouveau_private *dev_priv = dev->dev_private; struct nouveau_pm_engine *pm = &dev_priv->engine.pm; struct nouveau_pm_threshold_temp *temp = &pm->threshold_temp; return snprintf(buf, PAGE_SIZE, "%d\n", temp->critical*1000); } static ssize_t nouveau_hwmon_set_critical_temp(struct device *d, struct device_attribute *a, const char *buf, size_t count) { struct drm_device *dev = dev_get_drvdata(d); struct drm_nouveau_private *dev_priv = dev->dev_private; struct nouveau_pm_engine *pm = &dev_priv->engine.pm; struct nouveau_pm_threshold_temp *temp = &pm->threshold_temp; long value; if (strict_strtoul(buf, 10, &value) == -EINVAL) return count; temp->critical = value/1000; nouveau_temp_safety_checks(dev); return count; } static SENSOR_DEVICE_ATTR(temp1_crit, S_IRUGO | S_IWUSR, nouveau_hwmon_critical_temp, nouveau_hwmon_set_critical_temp, 0); static ssize_t nouveau_hwmon_show_name(struct device *dev, struct device_attribute *attr, char *buf) { return sprintf(buf, "nouveau\n"); } static SENSOR_DEVICE_ATTR(name, S_IRUGO, nouveau_hwmon_show_name, NULL, 0); static ssize_t nouveau_hwmon_show_update_rate(struct device *dev, struct device_attribute *attr, char *buf) { return sprintf(buf, "1000\n"); } static SENSOR_DEVICE_ATTR(update_rate, S_IRUGO, nouveau_hwmon_show_update_rate, NULL, 0); static struct attribute *hwmon_attributes[] = { &sensor_dev_attr_temp1_input.dev_attr.attr, &sensor_dev_attr_temp1_max.dev_attr.attr, &sensor_dev_attr_temp1_crit.dev_attr.attr, &sensor_dev_attr_name.dev_attr.attr, &sensor_dev_attr_update_rate.dev_attr.attr, NULL }; static const struct attribute_group hwmon_attrgroup = { .attrs = hwmon_attributes, }; static int nouveau_hwmon_init(struct drm_device *dev) { struct drm_nouveau_private *dev_priv = dev->dev_private; struct device *hwmon_dev; int ret; dev_priv->int_hwmon_dev = NULL; hwmon_dev = hwmon_device_register(&dev->pdev->dev); if (IS_ERR(hwmon_dev)) { ret = PTR_ERR(hwmon_dev); NV_ERROR(dev, "Unable to register hwmon device: %d\n", ret); return ret; } dev_set_drvdata(hwmon_dev, dev); ret = sysfs_create_group(&hwmon_dev->kobj, &hwmon_attrgroup); if (ret) { NV_ERROR(dev, "Unable to create hwmon sysfs file: %d\n", ret); hwmon_device_unregister(hwmon_dev); return ret; } dev_priv->int_hwmon_dev = hwmon_dev; return 0; } static void nouveau_hwmon_fini(struct drm_device *dev) { struct drm_nouveau_private *dev_priv = dev->dev_private; if (dev_priv->int_hwmon_dev) { sysfs_remove_group(&dev_priv->int_hwmon_dev->kobj, &hwmon_attrgroup); hwmon_device_unregister(dev_priv->int_hwmon_dev); } } int nouveau_pm_init(struct drm_device *dev) { struct drm_nouveau_private *dev_priv = dev->dev_private; struct nouveau_pm_engine *pm = &dev_priv->engine.pm; char info[256]; int ret, i; nouveau_volt_init(dev); nouveau_perf_init(dev); nouveau_temp_init(dev); NV_INFO(dev, "%d available performance level(s)\n", pm->nr_perflvl); for (i = 0; i < pm->nr_perflvl; i++) { nouveau_pm_perflvl_info(&pm->perflvl[i], info, sizeof(info)); NV_INFO(dev, "%d: %s", pm->perflvl[i].id, info); } /* determine current ("boot") performance level */ ret = nouveau_pm_perflvl_get(dev, &pm->boot); if (ret == 0) { pm->cur = &pm->boot; nouveau_pm_perflvl_info(&pm->boot, info, sizeof(info)); NV_INFO(dev, "c: %s", info); } /* switch performance levels now if requested */ if (nouveau_perflvl != NULL) { ret = nouveau_pm_profile_set(dev, nouveau_perflvl); if (ret) { NV_ERROR(dev, "error setting perflvl \"%s\": %d\n", nouveau_perflvl, ret); } } nouveau_sysfs_init(dev); nouveau_hwmon_init(dev); return 0; } void nouveau_pm_fini(struct drm_device *dev) { struct drm_nouveau_private *dev_priv = dev->dev_private; struct nouveau_pm_engine *pm = &dev_priv->engine.pm; if (pm->cur != &pm->boot) nouveau_pm_perflvl_set(dev, &pm->boot); nouveau_perf_fini(dev); nouveau_volt_fini(dev); nouveau_temp_fini(dev); nouveau_hwmon_fini(dev); nouveau_sysfs_fini(dev); } void Loading drivers/gpu/drm/nouveau/nouveau_pm.h +6 −0 Original line number Diff line number Diff line Loading @@ -52,4 +52,10 @@ int nv50_pm_clock_get(struct drm_device *, u32 id); void *nv50_pm_clock_pre(struct drm_device *, u32 id, int khz); void nv50_pm_clock_set(struct drm_device *, void *); /* nouveau_temp.c */ void nouveau_temp_init(struct drm_device *dev); void nouveau_temp_fini(struct drm_device *dev); void nouveau_temp_safety_checks(struct drm_device *dev); int16_t nouveau_temp_get(struct drm_device *dev); #endif drivers/gpu/drm/nouveau/nouveau_temp.c 0 → 100644 +272 −0 Original line number Diff line number Diff line /* * Copyright 2010 PathScale inc. * * 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 "drmP.h" #include "nouveau_drv.h" #include "nouveau_pm.h" void nouveau_temp_vbios_parse(struct drm_device *dev, u8 *temp) { struct drm_nouveau_private *dev_priv = dev->dev_private; struct nouveau_pm_engine *pm = &dev_priv->engine.pm; 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(dev, "temperature table pointer invalid\n"); return; } /* Set the default sensor's contants */ sensor->offset_constant = 0; sensor->offset_mult = 1; 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 known default values to setup the temperature sensor */ if (dev_priv->card_type >= NV_40) { switch (dev_priv->chipset) { case 0x43: sensor->offset_mult = 32060; sensor->offset_div = 1000; sensor->slope_mult = 792; sensor->slope_div = 1000; break; case 0x44: case 0x47: 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; } } headerlen = temp[1]; recordlen = temp[2]; entries = temp[3]; temp = temp + headerlen; /* Read the entries from the table */ for (i = 0; i < entries; i++) { u16 value = ROM16(temp[1]); switch (temp[0]) { case 0x01: value = (value&0x8f) == 0 ? (value >> 9) & 0x7f : 0; sensor->offset_constant = value; 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; } temp += recordlen; } nouveau_temp_safety_checks(dev); } static s16 nouveau_nv40_sensor_setup(struct drm_device *dev) { struct drm_nouveau_private *dev_priv = dev->dev_private; struct nouveau_pm_engine *pm = &dev_priv->engine.pm; struct nouveau_pm_temp_sensor_constants *sensor = &pm->sensor_constants; u32 offset = sensor->offset_mult / sensor->offset_div; u32 sensor_calibration; /* set up the sensors */ sensor_calibration = 120 - offset - sensor->offset_constant; sensor_calibration = sensor_calibration * sensor->slope_div / sensor->slope_mult; if (dev_priv->chipset >= 0x46) sensor_calibration |= 0x80000000; else sensor_calibration |= 0x10000000; nv_wr32(dev, 0x0015b0, sensor_calibration); /* Wait for the sensor to update */ msleep(5); /* read */ return nv_rd32(dev, 0x0015b4); } s16 nouveau_temp_get(struct drm_device *dev) { struct drm_nouveau_private *dev_priv = dev->dev_private; struct nouveau_pm_engine *pm = &dev_priv->engine.pm; struct nouveau_pm_temp_sensor_constants *sensor = &pm->sensor_constants; if (dev_priv->chipset >= 0x84) { return nv_rd32(dev, 0x20400); } else if (dev_priv->chipset >= 0x40) { u32 offset = sensor->offset_mult / sensor->offset_div; u32 core_temp; if (dev_priv->chipset >= 0x50) { core_temp = nv_rd32(dev, 0x20008); } else { core_temp = nv_rd32(dev, 0x0015b4); /* Setup the sensor if the temperature is 0 */ if (core_temp == 0) core_temp = nouveau_nv40_sensor_setup(dev); } core_temp = core_temp * sensor->slope_mult / sensor->slope_div; core_temp = core_temp + offset + sensor->offset_constant; return core_temp; } else { NV_ERROR(dev, "Temperature cannot be retrieved from an nv%x card\n", dev_priv->chipset); return 0; } return 0; } void nouveau_temp_safety_checks(struct drm_device *dev) { struct drm_nouveau_private *dev_priv = dev->dev_private; struct nouveau_pm_engine *pm = &dev_priv->engine.pm; struct nouveau_pm_threshold_temp *temps = &pm->threshold_temp; if (temps->critical > 120) temps->critical = 120; else if (temps->critical < 80) temps->critical = 80; if (temps->down_clock > 110) 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; } void nouveau_temp_init(struct drm_device *dev) { struct drm_nouveau_private *dev_priv = dev->dev_private; struct nvbios *bios = &dev_priv->vbios; struct bit_entry P; u8 *temp = NULL; if (bios->type == NVBIOS_BIT) { if (bit_table(dev, 'P', &P)) return; if (P.version == 1) temp = ROMPTR(bios, P.data[12]); else if (P.version == 2) temp = ROMPTR(bios, P.data[16]); else NV_WARN(dev, "unknown temp for BIT P %d\n", P.version); } else { NV_WARN(dev, "BMP entry unknown for temperature table.\n"); } nouveau_temp_vbios_parse(dev, temp); } void nouveau_temp_fini(struct drm_device *dev) { } Loading
drivers/gpu/drm/nouveau/Makefile +1 −1 Original line number Diff line number Diff line Loading @@ -25,7 +25,7 @@ nouveau-y := nouveau_drv.o nouveau_state.o nouveau_channel.o nouveau_mem.o \ nv04_crtc.o nv04_display.o nv04_cursor.o nv04_fbcon.o \ nv10_gpio.o nv50_gpio.o \ nv50_calc.o \ nv04_pm.o nv50_pm.o nv04_pm.o nv50_pm.o nouveau_temp.o nouveau-$(CONFIG_DRM_NOUVEAU_DEBUG) += nouveau_debugfs.o nouveau-$(CONFIG_COMPAT) += nouveau_ioc32.o Loading
drivers/gpu/drm/nouveau/nouveau_drv.h +18 −0 Original line number Diff line number Diff line Loading @@ -387,10 +387,26 @@ struct nouveau_pm_level { u8 fanspeed; }; struct nouveau_pm_temp_sensor_constants { u16 offset_constant; s16 offset_mult; u16 offset_div; u16 slope_mult; u16 slope_div; }; struct nouveau_pm_threshold_temp { s16 critical; s16 down_clock; s16 fan_boost; }; struct nouveau_pm_engine { struct nouveau_pm_voltage voltage; struct nouveau_pm_level perflvl[NOUVEAU_PM_MAX_LEVEL]; int nr_perflvl; struct nouveau_pm_temp_sensor_constants sensor_constants; struct nouveau_pm_threshold_temp threshold_temp; struct nouveau_pm_level boot; struct nouveau_pm_level *cur; Loading Loading @@ -663,6 +679,8 @@ struct drm_nouveau_private { struct nouveau_fbdev *nfbdev; struct apertures_struct *apertures; struct device *int_hwmon_dev; }; static inline struct drm_nouveau_private * Loading
drivers/gpu/drm/nouveau/nouveau_pm.c +221 −38 Original line number Diff line number Diff line Loading @@ -27,6 +27,9 @@ #include "nouveau_drv.h" #include "nouveau_pm.h" #include <linux/hwmon.h> #include <linux/hwmon-sysfs.h> static int nouveau_pm_clock_set(struct drm_device *dev, u8 id, u32 khz) { Loading Loading @@ -227,43 +230,14 @@ nouveau_pm_set_perflvl(struct device *d, struct device_attribute *a, DEVICE_ATTR(performance_level, S_IRUGO | S_IWUSR, nouveau_pm_get_perflvl, nouveau_pm_set_perflvl); int nouveau_pm_init(struct drm_device *dev) static int nouveau_sysfs_init(struct drm_device *dev) { struct drm_nouveau_private *dev_priv = dev->dev_private; struct nouveau_pm_engine *pm = &dev_priv->engine.pm; struct device *d = &dev->pdev->dev; char info[256]; int ret, i; nouveau_volt_init(dev); nouveau_perf_init(dev); NV_INFO(dev, "%d available performance level(s)\n", pm->nr_perflvl); for (i = 0; i < pm->nr_perflvl; i++) { nouveau_pm_perflvl_info(&pm->perflvl[i], info, sizeof(info)); NV_INFO(dev, "%d: %s", pm->perflvl[i].id, info); } /* determine current ("boot") performance level */ ret = nouveau_pm_perflvl_get(dev, &pm->boot); if (ret == 0) { pm->cur = &pm->boot; nouveau_pm_perflvl_info(&pm->boot, info, sizeof(info)); NV_INFO(dev, "c: %s", info); } /* switch performance levels now if requested */ if (nouveau_perflvl != NULL) { ret = nouveau_pm_profile_set(dev, nouveau_perflvl); if (ret) { NV_ERROR(dev, "error setting perflvl \"%s\": %d\n", nouveau_perflvl, ret); } } /* initialise sysfs */ ret = device_create_file(d, &dev_attr_performance_level); if (ret) return ret; Loading @@ -290,17 +264,14 @@ nouveau_pm_init(struct drm_device *dev) return 0; } void nouveau_pm_fini(struct drm_device *dev) static void nouveau_sysfs_fini(struct drm_device *dev) { struct drm_nouveau_private *dev_priv = dev->dev_private; struct nouveau_pm_engine *pm = &dev_priv->engine.pm; struct device *d = &dev->pdev->dev; int i; if (pm->cur != &pm->boot) nouveau_pm_perflvl_set(dev, &pm->boot); device_remove_file(d, &dev_attr_performance_level); for (i = 0; i < pm->nr_perflvl; i++) { struct nouveau_pm_level *pl = &pm->perflvl[i]; Loading @@ -310,9 +281,221 @@ nouveau_pm_fini(struct drm_device *dev) device_remove_file(d, &pl->dev_attr); } } static ssize_t nouveau_hwmon_show_temp(struct device *d, struct device_attribute *a, char *buf) { struct drm_device *dev = dev_get_drvdata(d); return snprintf(buf, PAGE_SIZE, "%d\n", nouveau_temp_get(dev)*1000); } static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, nouveau_hwmon_show_temp, NULL, 0); static ssize_t nouveau_hwmon_max_temp(struct device *d, struct device_attribute *a, char *buf) { struct drm_device *dev = dev_get_drvdata(d); struct drm_nouveau_private *dev_priv = dev->dev_private; struct nouveau_pm_engine *pm = &dev_priv->engine.pm; struct nouveau_pm_threshold_temp *temp = &pm->threshold_temp; return snprintf(buf, PAGE_SIZE, "%d\n", temp->down_clock*1000); } static ssize_t nouveau_hwmon_set_max_temp(struct device *d, struct device_attribute *a, const char *buf, size_t count) { struct drm_device *dev = dev_get_drvdata(d); struct drm_nouveau_private *dev_priv = dev->dev_private; struct nouveau_pm_engine *pm = &dev_priv->engine.pm; struct nouveau_pm_threshold_temp *temp = &pm->threshold_temp; long value; if (strict_strtoul(buf, 10, &value) == -EINVAL) return count; temp->down_clock = value/1000; nouveau_temp_safety_checks(dev); return count; } static SENSOR_DEVICE_ATTR(temp1_max, S_IRUGO | S_IWUSR, nouveau_hwmon_max_temp, nouveau_hwmon_set_max_temp, 0); static ssize_t nouveau_hwmon_critical_temp(struct device *d, struct device_attribute *a, char *buf) { struct drm_device *dev = dev_get_drvdata(d); struct drm_nouveau_private *dev_priv = dev->dev_private; struct nouveau_pm_engine *pm = &dev_priv->engine.pm; struct nouveau_pm_threshold_temp *temp = &pm->threshold_temp; return snprintf(buf, PAGE_SIZE, "%d\n", temp->critical*1000); } static ssize_t nouveau_hwmon_set_critical_temp(struct device *d, struct device_attribute *a, const char *buf, size_t count) { struct drm_device *dev = dev_get_drvdata(d); struct drm_nouveau_private *dev_priv = dev->dev_private; struct nouveau_pm_engine *pm = &dev_priv->engine.pm; struct nouveau_pm_threshold_temp *temp = &pm->threshold_temp; long value; if (strict_strtoul(buf, 10, &value) == -EINVAL) return count; temp->critical = value/1000; nouveau_temp_safety_checks(dev); return count; } static SENSOR_DEVICE_ATTR(temp1_crit, S_IRUGO | S_IWUSR, nouveau_hwmon_critical_temp, nouveau_hwmon_set_critical_temp, 0); static ssize_t nouveau_hwmon_show_name(struct device *dev, struct device_attribute *attr, char *buf) { return sprintf(buf, "nouveau\n"); } static SENSOR_DEVICE_ATTR(name, S_IRUGO, nouveau_hwmon_show_name, NULL, 0); static ssize_t nouveau_hwmon_show_update_rate(struct device *dev, struct device_attribute *attr, char *buf) { return sprintf(buf, "1000\n"); } static SENSOR_DEVICE_ATTR(update_rate, S_IRUGO, nouveau_hwmon_show_update_rate, NULL, 0); static struct attribute *hwmon_attributes[] = { &sensor_dev_attr_temp1_input.dev_attr.attr, &sensor_dev_attr_temp1_max.dev_attr.attr, &sensor_dev_attr_temp1_crit.dev_attr.attr, &sensor_dev_attr_name.dev_attr.attr, &sensor_dev_attr_update_rate.dev_attr.attr, NULL }; static const struct attribute_group hwmon_attrgroup = { .attrs = hwmon_attributes, }; static int nouveau_hwmon_init(struct drm_device *dev) { struct drm_nouveau_private *dev_priv = dev->dev_private; struct device *hwmon_dev; int ret; dev_priv->int_hwmon_dev = NULL; hwmon_dev = hwmon_device_register(&dev->pdev->dev); if (IS_ERR(hwmon_dev)) { ret = PTR_ERR(hwmon_dev); NV_ERROR(dev, "Unable to register hwmon device: %d\n", ret); return ret; } dev_set_drvdata(hwmon_dev, dev); ret = sysfs_create_group(&hwmon_dev->kobj, &hwmon_attrgroup); if (ret) { NV_ERROR(dev, "Unable to create hwmon sysfs file: %d\n", ret); hwmon_device_unregister(hwmon_dev); return ret; } dev_priv->int_hwmon_dev = hwmon_dev; return 0; } static void nouveau_hwmon_fini(struct drm_device *dev) { struct drm_nouveau_private *dev_priv = dev->dev_private; if (dev_priv->int_hwmon_dev) { sysfs_remove_group(&dev_priv->int_hwmon_dev->kobj, &hwmon_attrgroup); hwmon_device_unregister(dev_priv->int_hwmon_dev); } } int nouveau_pm_init(struct drm_device *dev) { struct drm_nouveau_private *dev_priv = dev->dev_private; struct nouveau_pm_engine *pm = &dev_priv->engine.pm; char info[256]; int ret, i; nouveau_volt_init(dev); nouveau_perf_init(dev); nouveau_temp_init(dev); NV_INFO(dev, "%d available performance level(s)\n", pm->nr_perflvl); for (i = 0; i < pm->nr_perflvl; i++) { nouveau_pm_perflvl_info(&pm->perflvl[i], info, sizeof(info)); NV_INFO(dev, "%d: %s", pm->perflvl[i].id, info); } /* determine current ("boot") performance level */ ret = nouveau_pm_perflvl_get(dev, &pm->boot); if (ret == 0) { pm->cur = &pm->boot; nouveau_pm_perflvl_info(&pm->boot, info, sizeof(info)); NV_INFO(dev, "c: %s", info); } /* switch performance levels now if requested */ if (nouveau_perflvl != NULL) { ret = nouveau_pm_profile_set(dev, nouveau_perflvl); if (ret) { NV_ERROR(dev, "error setting perflvl \"%s\": %d\n", nouveau_perflvl, ret); } } nouveau_sysfs_init(dev); nouveau_hwmon_init(dev); return 0; } void nouveau_pm_fini(struct drm_device *dev) { struct drm_nouveau_private *dev_priv = dev->dev_private; struct nouveau_pm_engine *pm = &dev_priv->engine.pm; if (pm->cur != &pm->boot) nouveau_pm_perflvl_set(dev, &pm->boot); nouveau_perf_fini(dev); nouveau_volt_fini(dev); nouveau_temp_fini(dev); nouveau_hwmon_fini(dev); nouveau_sysfs_fini(dev); } void Loading
drivers/gpu/drm/nouveau/nouveau_pm.h +6 −0 Original line number Diff line number Diff line Loading @@ -52,4 +52,10 @@ int nv50_pm_clock_get(struct drm_device *, u32 id); void *nv50_pm_clock_pre(struct drm_device *, u32 id, int khz); void nv50_pm_clock_set(struct drm_device *, void *); /* nouveau_temp.c */ void nouveau_temp_init(struct drm_device *dev); void nouveau_temp_fini(struct drm_device *dev); void nouveau_temp_safety_checks(struct drm_device *dev); int16_t nouveau_temp_get(struct drm_device *dev); #endif
drivers/gpu/drm/nouveau/nouveau_temp.c 0 → 100644 +272 −0 Original line number Diff line number Diff line /* * Copyright 2010 PathScale inc. * * 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 "drmP.h" #include "nouveau_drv.h" #include "nouveau_pm.h" void nouveau_temp_vbios_parse(struct drm_device *dev, u8 *temp) { struct drm_nouveau_private *dev_priv = dev->dev_private; struct nouveau_pm_engine *pm = &dev_priv->engine.pm; 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(dev, "temperature table pointer invalid\n"); return; } /* Set the default sensor's contants */ sensor->offset_constant = 0; sensor->offset_mult = 1; 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 known default values to setup the temperature sensor */ if (dev_priv->card_type >= NV_40) { switch (dev_priv->chipset) { case 0x43: sensor->offset_mult = 32060; sensor->offset_div = 1000; sensor->slope_mult = 792; sensor->slope_div = 1000; break; case 0x44: case 0x47: 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; } } headerlen = temp[1]; recordlen = temp[2]; entries = temp[3]; temp = temp + headerlen; /* Read the entries from the table */ for (i = 0; i < entries; i++) { u16 value = ROM16(temp[1]); switch (temp[0]) { case 0x01: value = (value&0x8f) == 0 ? (value >> 9) & 0x7f : 0; sensor->offset_constant = value; 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; } temp += recordlen; } nouveau_temp_safety_checks(dev); } static s16 nouveau_nv40_sensor_setup(struct drm_device *dev) { struct drm_nouveau_private *dev_priv = dev->dev_private; struct nouveau_pm_engine *pm = &dev_priv->engine.pm; struct nouveau_pm_temp_sensor_constants *sensor = &pm->sensor_constants; u32 offset = sensor->offset_mult / sensor->offset_div; u32 sensor_calibration; /* set up the sensors */ sensor_calibration = 120 - offset - sensor->offset_constant; sensor_calibration = sensor_calibration * sensor->slope_div / sensor->slope_mult; if (dev_priv->chipset >= 0x46) sensor_calibration |= 0x80000000; else sensor_calibration |= 0x10000000; nv_wr32(dev, 0x0015b0, sensor_calibration); /* Wait for the sensor to update */ msleep(5); /* read */ return nv_rd32(dev, 0x0015b4); } s16 nouveau_temp_get(struct drm_device *dev) { struct drm_nouveau_private *dev_priv = dev->dev_private; struct nouveau_pm_engine *pm = &dev_priv->engine.pm; struct nouveau_pm_temp_sensor_constants *sensor = &pm->sensor_constants; if (dev_priv->chipset >= 0x84) { return nv_rd32(dev, 0x20400); } else if (dev_priv->chipset >= 0x40) { u32 offset = sensor->offset_mult / sensor->offset_div; u32 core_temp; if (dev_priv->chipset >= 0x50) { core_temp = nv_rd32(dev, 0x20008); } else { core_temp = nv_rd32(dev, 0x0015b4); /* Setup the sensor if the temperature is 0 */ if (core_temp == 0) core_temp = nouveau_nv40_sensor_setup(dev); } core_temp = core_temp * sensor->slope_mult / sensor->slope_div; core_temp = core_temp + offset + sensor->offset_constant; return core_temp; } else { NV_ERROR(dev, "Temperature cannot be retrieved from an nv%x card\n", dev_priv->chipset); return 0; } return 0; } void nouveau_temp_safety_checks(struct drm_device *dev) { struct drm_nouveau_private *dev_priv = dev->dev_private; struct nouveau_pm_engine *pm = &dev_priv->engine.pm; struct nouveau_pm_threshold_temp *temps = &pm->threshold_temp; if (temps->critical > 120) temps->critical = 120; else if (temps->critical < 80) temps->critical = 80; if (temps->down_clock > 110) 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; } void nouveau_temp_init(struct drm_device *dev) { struct drm_nouveau_private *dev_priv = dev->dev_private; struct nvbios *bios = &dev_priv->vbios; struct bit_entry P; u8 *temp = NULL; if (bios->type == NVBIOS_BIT) { if (bit_table(dev, 'P', &P)) return; if (P.version == 1) temp = ROMPTR(bios, P.data[12]); else if (P.version == 2) temp = ROMPTR(bios, P.data[16]); else NV_WARN(dev, "unknown temp for BIT P %d\n", P.version); } else { NV_WARN(dev, "BMP entry unknown for temperature table.\n"); } nouveau_temp_vbios_parse(dev, temp); } void nouveau_temp_fini(struct drm_device *dev) { }