blob: 5465abf87f18639db1ff445d265dabddf20e8598 [file] [log] [blame]
Martin Peres34e9d852010-09-22 20:54:22 +02001/*
2 * Copyright 2010 PathScale inc.
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 * and/or sell copies of the Software, and to permit persons to whom the
9 * Software is furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice shall be included in
12 * all copies or substantial portions of the Software.
13 *
14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
17 * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
18 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
19 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
20 * OTHER DEALINGS IN THE SOFTWARE.
21 *
22 * Authors: Martin Peres
23 */
24
Paul Gortmakere0cd3602011-08-30 11:04:30 -040025#include <linux/module.h>
26
Martin Peres34e9d852010-09-22 20:54:22 +020027#include "drmP.h"
28
Ben Skeggs77145f12012-07-31 16:16:21 +100029#include "nouveau_drm.h"
Martin Peres34e9d852010-09-22 20:54:22 +020030#include "nouveau_pm.h"
31
Ben Skeggs77145f12012-07-31 16:16:21 +100032#include <subdev/i2c.h>
Martin Peres7d70e9c2012-08-16 11:00:55 +020033#include <subdev/bios/therm.h>
Martin Peres34e9d852010-09-22 20:54:22 +020034
Francisco Jerez8155cac2010-09-23 20:58:38 +020035static int
36nv40_sensor_setup(struct drm_device *dev)
Martin Peres34e9d852010-09-22 20:54:22 +020037{
Ben Skeggs77145f12012-07-31 16:16:21 +100038 struct nouveau_device *device = nouveau_dev(dev);
39 struct nouveau_drm *drm = nouveau_drm(dev);
Martin Peres34e9d852010-09-22 20:54:22 +020040
Martin Peres7d70e9c2012-08-16 11:00:55 +020041 /* enable ADC readout and disable the ALARM threshold */
42 if (nv_device(drm->device)->chipset >= 0x46) {
43 nv_mask(device, 0x15b8, 0x80000000, 0);
44 nv_wr32(device, 0x15b0, 0x80003fff);
45 return nv_rd32(device, 0x15b4) & 0x3fff;
46 } else {
47 nv_wr32(device, 0x15b0, 0xff);
48 return nv_rd32(device, 0x15b4) & 0xff;
49 }
Martin Peres34e9d852010-09-22 20:54:22 +020050}
51
Francisco Jerez8155cac2010-09-23 20:58:38 +020052int
53nv40_temp_get(struct drm_device *dev)
Martin Peres34e9d852010-09-22 20:54:22 +020054{
Ben Skeggs77145f12012-07-31 16:16:21 +100055 struct nouveau_device *device = nouveau_dev(dev);
56 struct nouveau_drm *drm = nouveau_drm(dev);
57 struct nouveau_pm *pm = nouveau_pm(dev);
Martin Peres34e9d852010-09-22 20:54:22 +020058 struct nouveau_pm_temp_sensor_constants *sensor = &pm->sensor_constants;
Francisco Jerez8155cac2010-09-23 20:58:38 +020059 int core_temp;
Martin Peres34e9d852010-09-22 20:54:22 +020060
Martin Peres7d70e9c2012-08-16 11:00:55 +020061 if (nv_device(drm->device)->chipset >= 0x46) {
62 nv_wr32(device, 0x15b0, 0x80003fff);
63 core_temp = nv_rd32(device, 0x15b4) & 0x3fff;
Martin Peres34e9d852010-09-22 20:54:22 +020064 } else {
Martin Peres7d70e9c2012-08-16 11:00:55 +020065 nv_wr32(device, 0x15b0, 0xff);
66 core_temp = nv_rd32(device, 0x15b4) & 0xff;
Martin Peres34e9d852010-09-22 20:54:22 +020067 }
68
Martin Peres7d70e9c2012-08-16 11:00:55 +020069 /* Setup the sensor if the temperature is 0 */
70 if (core_temp == 0)
71 core_temp = nv40_sensor_setup(dev);
72
73 if (sensor->slope_div == 0)
74 sensor->slope_div = 1;
75 if (sensor->offset_div == 0)
76 sensor->offset_div = 1;
77 if (sensor->slope_mult < 1)
78 sensor->slope_mult = 1;
79
Francisco Jerez8155cac2010-09-23 20:58:38 +020080 core_temp = core_temp * sensor->slope_mult / sensor->slope_div;
Martin Peres7d70e9c2012-08-16 11:00:55 +020081 core_temp = core_temp + sensor->offset_mult / sensor->offset_div;
82 core_temp = core_temp + sensor->offset_constant - 8;
Francisco Jerez8155cac2010-09-23 20:58:38 +020083
84 return core_temp;
85}
86
87int
88nv84_temp_get(struct drm_device *dev)
89{
Ben Skeggs77145f12012-07-31 16:16:21 +100090 struct nouveau_device *device = nouveau_dev(dev);
91 return nv_rd32(device, 0x20400);
Martin Peres34e9d852010-09-22 20:54:22 +020092}
93
94void
95nouveau_temp_safety_checks(struct drm_device *dev)
96{
Ben Skeggs77145f12012-07-31 16:16:21 +100097 struct nouveau_pm *pm = nouveau_pm(dev);
Martin Peres34e9d852010-09-22 20:54:22 +020098 struct nouveau_pm_threshold_temp *temps = &pm->threshold_temp;
99
100 if (temps->critical > 120)
101 temps->critical = 120;
102 else if (temps->critical < 80)
103 temps->critical = 80;
104
105 if (temps->down_clock > 110)
106 temps->down_clock = 110;
107 else if (temps->down_clock < 60)
108 temps->down_clock = 60;
Martin Peres34e9d852010-09-22 20:54:22 +0200109}
110
Francisco Jerez66146da2010-09-23 21:00:40 +0200111static bool
Ben Skeggs4196faa2012-07-10 14:36:38 +1000112probe_monitoring_device(struct nouveau_i2c_port *i2c,
Francisco Jerez66146da2010-09-23 21:00:40 +0200113 struct i2c_board_info *info)
114{
Francisco Jerez66146da2010-09-23 21:00:40 +0200115 struct i2c_client *client;
116
Lucas Stachf17811d2011-02-06 22:42:54 +0100117 request_module("%s%s", I2C_MODULE_PREFIX, info->type);
Francisco Jerez66146da2010-09-23 21:00:40 +0200118
Ben Skeggs77145f12012-07-31 16:16:21 +1000119 client = i2c_new_device(&i2c->adapter, info);
Francisco Jerez66146da2010-09-23 21:00:40 +0200120 if (!client)
121 return false;
122
123 if (!client->driver || client->driver->detect(client, info)) {
124 i2c_unregister_device(client);
125 return false;
126 }
127
128 return true;
129}
130
131static void
132nouveau_temp_probe_i2c(struct drm_device *dev)
133{
Ben Skeggs77145f12012-07-31 16:16:21 +1000134 struct nouveau_device *device = nouveau_dev(dev);
135 struct nouveau_i2c *i2c = nouveau_i2c(device);
Francisco Jerez66146da2010-09-23 21:00:40 +0200136 struct i2c_board_info info[] = {
137 { I2C_BOARD_INFO("w83l785ts", 0x2d) },
138 { I2C_BOARD_INFO("w83781d", 0x2d) },
Francisco Jerez66146da2010-09-23 21:00:40 +0200139 { I2C_BOARD_INFO("adt7473", 0x2e) },
Ben Skeggsb26e72f2011-01-19 15:54:10 +1000140 { I2C_BOARD_INFO("f75375", 0x2e) },
Francisco Jerez66146da2010-09-23 21:00:40 +0200141 { I2C_BOARD_INFO("lm99", 0x4c) },
142 { }
143 };
Francisco Jerez66146da2010-09-23 21:00:40 +0200144
Ben Skeggs77145f12012-07-31 16:16:21 +1000145 i2c->identify(i2c, NV_I2C_DEFAULT(0), "monitoring device", info,
146 probe_monitoring_device);
Francisco Jerez66146da2010-09-23 21:00:40 +0200147}
148
Martin Peres34e9d852010-09-22 20:54:22 +0200149void
150nouveau_temp_init(struct drm_device *dev)
151{
Ben Skeggs77145f12012-07-31 16:16:21 +1000152 struct nouveau_drm *drm = nouveau_drm(dev);
Martin Peres7d70e9c2012-08-16 11:00:55 +0200153 struct nouveau_device *device = nv_device(drm->device);
154 struct nouveau_bios *bios = nouveau_bios(device);
155 struct nouveau_pm *pm = nouveau_pm(dev);
156 struct nouveau_pm_temp_sensor_constants *sensor = &pm->sensor_constants;
157 struct nouveau_pm_threshold_temp *temps = &pm->threshold_temp;
158 struct nvbios_therm_sensor bios_sensor;
159 struct nvbios_therm_fan bios_fan;
Martin Peres34e9d852010-09-22 20:54:22 +0200160
Martin Peres7d70e9c2012-08-16 11:00:55 +0200161 /* store some safe defaults */
162 sensor->offset_constant = 0;
163 sensor->offset_mult = 0;
164 sensor->offset_div = 1;
165 sensor->slope_mult = 1;
166 sensor->slope_div = 1;
Martin Peres34e9d852010-09-22 20:54:22 +0200167
Martin Peres7d70e9c2012-08-16 11:00:55 +0200168 if (!nvbios_therm_sensor_parse(bios, NVBIOS_THERM_DOMAIN_CORE,
169 &bios_sensor)) {
170 sensor->slope_mult = bios_sensor.slope_mult;
171 sensor->slope_div = bios_sensor.slope_div;
172 sensor->offset_mult = bios_sensor.offset_num;
173 sensor->offset_div = bios_sensor.offset_den;
174 sensor->offset_constant = bios_sensor.offset_constant;
Francisco Jerez66146da2010-09-23 21:00:40 +0200175
Martin Peres7d70e9c2012-08-16 11:00:55 +0200176 temps->down_clock = bios_sensor.thrs_down_clock.temp;
177 temps->critical = bios_sensor.thrs_critical.temp;
Martin Peres34e9d852010-09-22 20:54:22 +0200178 }
179
Martin Peres7d70e9c2012-08-16 11:00:55 +0200180 if (nvbios_therm_fan_parse(bios, &bios_fan)) {
181 pm->fan.min_duty = bios_fan.min_duty;
182 pm->fan.max_duty = bios_fan.max_duty;
183 pm->fan.pwm_freq = bios_fan.pwm_freq;
184 }
185
186 nouveau_temp_safety_checks(dev);
Francisco Jerez66146da2010-09-23 21:00:40 +0200187 nouveau_temp_probe_i2c(dev);
Martin Peres34e9d852010-09-22 20:54:22 +0200188}
189
190void
191nouveau_temp_fini(struct drm_device *dev)
192{
193
194}