]>
Commit | Line | Data |
---|---|---|
34e9d85a MP |
1 | /* |
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 | ||
e0cd3608 PG |
25 | #include <linux/module.h> |
26 | ||
760285e7 | 27 | #include <drm/drmP.h> |
34e9d85a MP |
28 | |
29 | #include "nouveau_drv.h" | |
30 | #include "nouveau_pm.h" | |
31 | ||
5c4abd09 | 32 | static void |
34e9d85a MP |
33 | nouveau_temp_vbios_parse(struct drm_device *dev, u8 *temp) |
34 | { | |
35 | struct drm_nouveau_private *dev_priv = dev->dev_private; | |
36 | struct nouveau_pm_engine *pm = &dev_priv->engine.pm; | |
37 | struct nouveau_pm_temp_sensor_constants *sensor = &pm->sensor_constants; | |
38 | struct nouveau_pm_threshold_temp *temps = &pm->threshold_temp; | |
39 | int i, headerlen, recordlen, entries; | |
40 | ||
41 | if (!temp) { | |
42 | NV_DEBUG(dev, "temperature table pointer invalid\n"); | |
43 | return; | |
44 | } | |
45 | ||
46 | /* Set the default sensor's contants */ | |
47 | sensor->offset_constant = 0; | |
40ce4279 | 48 | sensor->offset_mult = 0; |
34e9d85a MP |
49 | sensor->offset_div = 1; |
50 | sensor->slope_mult = 1; | |
51 | sensor->slope_div = 1; | |
52 | ||
53 | /* Set the default temperature thresholds */ | |
54 | temps->critical = 110; | |
55 | temps->down_clock = 100; | |
56 | temps->fan_boost = 90; | |
57 | ||
11b7d895 MP |
58 | /* Set the default range for the pwm fan */ |
59 | pm->fan.min_duty = 30; | |
60 | pm->fan.max_duty = 100; | |
61 | ||
34e9d85a MP |
62 | /* Set the known default values to setup the temperature sensor */ |
63 | if (dev_priv->card_type >= NV_40) { | |
64 | switch (dev_priv->chipset) { | |
65 | case 0x43: | |
66 | sensor->offset_mult = 32060; | |
67 | sensor->offset_div = 1000; | |
68 | sensor->slope_mult = 792; | |
69 | sensor->slope_div = 1000; | |
70 | break; | |
71 | ||
72 | case 0x44: | |
73 | case 0x47: | |
d34ec507 | 74 | case 0x4a: |
34e9d85a MP |
75 | sensor->offset_mult = 27839; |
76 | sensor->offset_div = 1000; | |
77 | sensor->slope_mult = 780; | |
78 | sensor->slope_div = 1000; | |
79 | break; | |
80 | ||
81 | case 0x46: | |
82 | sensor->offset_mult = -24775; | |
83 | sensor->offset_div = 100; | |
84 | sensor->slope_mult = 467; | |
85 | sensor->slope_div = 10000; | |
86 | break; | |
87 | ||
88 | case 0x49: | |
89 | sensor->offset_mult = -25051; | |
90 | sensor->offset_div = 100; | |
91 | sensor->slope_mult = 458; | |
92 | sensor->slope_div = 10000; | |
93 | break; | |
94 | ||
95 | case 0x4b: | |
96 | sensor->offset_mult = -24088; | |
97 | sensor->offset_div = 100; | |
98 | sensor->slope_mult = 442; | |
99 | sensor->slope_div = 10000; | |
100 | break; | |
101 | ||
102 | case 0x50: | |
103 | sensor->offset_mult = -22749; | |
104 | sensor->offset_div = 100; | |
105 | sensor->slope_mult = 431; | |
106 | sensor->slope_div = 10000; | |
107 | break; | |
6d13e9c1 EV |
108 | |
109 | case 0x67: | |
110 | sensor->offset_mult = -26149; | |
111 | sensor->offset_div = 100; | |
112 | sensor->slope_mult = 484; | |
113 | sensor->slope_div = 10000; | |
114 | break; | |
34e9d85a MP |
115 | } |
116 | } | |
117 | ||
118 | headerlen = temp[1]; | |
119 | recordlen = temp[2]; | |
120 | entries = temp[3]; | |
121 | temp = temp + headerlen; | |
122 | ||
123 | /* Read the entries from the table */ | |
124 | for (i = 0; i < entries; i++) { | |
40ce4279 | 125 | s16 value = ROM16(temp[1]); |
34e9d85a MP |
126 | |
127 | switch (temp[0]) { | |
128 | case 0x01: | |
67e1d4fb FJ |
129 | if ((value & 0x8f) == 0) |
130 | sensor->offset_constant = (value >> 9) & 0x7f; | |
34e9d85a MP |
131 | break; |
132 | ||
133 | case 0x04: | |
134 | if ((value & 0xf00f) == 0xa000) /* core */ | |
135 | temps->critical = (value&0x0ff0) >> 4; | |
136 | break; | |
137 | ||
138 | case 0x07: | |
139 | if ((value & 0xf00f) == 0xa000) /* core */ | |
140 | temps->down_clock = (value&0x0ff0) >> 4; | |
141 | break; | |
142 | ||
143 | case 0x08: | |
144 | if ((value & 0xf00f) == 0xa000) /* core */ | |
145 | temps->fan_boost = (value&0x0ff0) >> 4; | |
146 | break; | |
147 | ||
148 | case 0x10: | |
149 | sensor->offset_mult = value; | |
150 | break; | |
151 | ||
152 | case 0x11: | |
153 | sensor->offset_div = value; | |
154 | break; | |
155 | ||
156 | case 0x12: | |
157 | sensor->slope_mult = value; | |
158 | break; | |
159 | ||
160 | case 0x13: | |
161 | sensor->slope_div = value; | |
162 | break; | |
11b7d895 MP |
163 | case 0x22: |
164 | pm->fan.min_duty = value & 0xff; | |
165 | pm->fan.max_duty = (value & 0xff00) >> 8; | |
166 | break; | |
3f8e11e4 BS |
167 | case 0x26: |
168 | pm->fan.pwm_freq = value; | |
169 | break; | |
34e9d85a MP |
170 | } |
171 | temp += recordlen; | |
172 | } | |
173 | ||
174 | nouveau_temp_safety_checks(dev); | |
11b7d895 MP |
175 | |
176 | /* check the fan min/max settings */ | |
177 | if (pm->fan.min_duty < 10) | |
178 | pm->fan.min_duty = 10; | |
179 | if (pm->fan.max_duty > 100) | |
180 | pm->fan.max_duty = 100; | |
181 | if (pm->fan.max_duty < pm->fan.min_duty) | |
182 | pm->fan.max_duty = pm->fan.min_duty; | |
34e9d85a MP |
183 | } |
184 | ||
8155cac4 FJ |
185 | static int |
186 | nv40_sensor_setup(struct drm_device *dev) | |
34e9d85a MP |
187 | { |
188 | struct drm_nouveau_private *dev_priv = dev->dev_private; | |
189 | struct nouveau_pm_engine *pm = &dev_priv->engine.pm; | |
190 | struct nouveau_pm_temp_sensor_constants *sensor = &pm->sensor_constants; | |
40ce4279 EV |
191 | s32 offset = sensor->offset_mult / sensor->offset_div; |
192 | s32 sensor_calibration; | |
34e9d85a MP |
193 | |
194 | /* set up the sensors */ | |
195 | sensor_calibration = 120 - offset - sensor->offset_constant; | |
196 | sensor_calibration = sensor_calibration * sensor->slope_div / | |
197 | sensor->slope_mult; | |
198 | ||
199 | if (dev_priv->chipset >= 0x46) | |
200 | sensor_calibration |= 0x80000000; | |
201 | else | |
202 | sensor_calibration |= 0x10000000; | |
203 | ||
204 | nv_wr32(dev, 0x0015b0, sensor_calibration); | |
205 | ||
206 | /* Wait for the sensor to update */ | |
207 | msleep(5); | |
208 | ||
209 | /* read */ | |
4164743c | 210 | return nv_rd32(dev, 0x0015b4) & 0x1fff; |
34e9d85a MP |
211 | } |
212 | ||
8155cac4 FJ |
213 | int |
214 | nv40_temp_get(struct drm_device *dev) | |
34e9d85a MP |
215 | { |
216 | struct drm_nouveau_private *dev_priv = dev->dev_private; | |
217 | struct nouveau_pm_engine *pm = &dev_priv->engine.pm; | |
218 | struct nouveau_pm_temp_sensor_constants *sensor = &pm->sensor_constants; | |
8155cac4 FJ |
219 | int offset = sensor->offset_mult / sensor->offset_div; |
220 | int core_temp; | |
34e9d85a | 221 | |
c1b60ece | 222 | if (dev_priv->card_type >= NV_50) { |
8155cac4 | 223 | core_temp = nv_rd32(dev, 0x20008); |
34e9d85a | 224 | } else { |
8155cac4 FJ |
225 | core_temp = nv_rd32(dev, 0x0015b4) & 0x1fff; |
226 | /* Setup the sensor if the temperature is 0 */ | |
227 | if (core_temp == 0) | |
228 | core_temp = nv40_sensor_setup(dev); | |
34e9d85a MP |
229 | } |
230 | ||
8155cac4 FJ |
231 | core_temp = core_temp * sensor->slope_mult / sensor->slope_div; |
232 | core_temp = core_temp + offset + sensor->offset_constant; | |
233 | ||
234 | return core_temp; | |
235 | } | |
236 | ||
237 | int | |
238 | nv84_temp_get(struct drm_device *dev) | |
239 | { | |
240 | return nv_rd32(dev, 0x20400); | |
34e9d85a MP |
241 | } |
242 | ||
243 | void | |
244 | nouveau_temp_safety_checks(struct drm_device *dev) | |
245 | { | |
246 | struct drm_nouveau_private *dev_priv = dev->dev_private; | |
247 | struct nouveau_pm_engine *pm = &dev_priv->engine.pm; | |
248 | struct nouveau_pm_threshold_temp *temps = &pm->threshold_temp; | |
249 | ||
250 | if (temps->critical > 120) | |
251 | temps->critical = 120; | |
252 | else if (temps->critical < 80) | |
253 | temps->critical = 80; | |
254 | ||
255 | if (temps->down_clock > 110) | |
256 | temps->down_clock = 110; | |
257 | else if (temps->down_clock < 60) | |
258 | temps->down_clock = 60; | |
259 | ||
260 | if (temps->fan_boost > 100) | |
261 | temps->fan_boost = 100; | |
262 | else if (temps->fan_boost < 40) | |
263 | temps->fan_boost = 40; | |
264 | } | |
265 | ||
66146da0 FJ |
266 | static bool |
267 | probe_monitoring_device(struct nouveau_i2c_chan *i2c, | |
268 | struct i2c_board_info *info) | |
269 | { | |
66146da0 FJ |
270 | struct i2c_client *client; |
271 | ||
f17811df | 272 | request_module("%s%s", I2C_MODULE_PREFIX, info->type); |
66146da0 FJ |
273 | |
274 | client = i2c_new_device(&i2c->adapter, info); | |
275 | if (!client) | |
276 | return false; | |
277 | ||
278 | if (!client->driver || client->driver->detect(client, info)) { | |
279 | i2c_unregister_device(client); | |
280 | return false; | |
281 | } | |
282 | ||
283 | return true; | |
284 | } | |
285 | ||
286 | static void | |
287 | nouveau_temp_probe_i2c(struct drm_device *dev) | |
288 | { | |
66146da0 FJ |
289 | struct i2c_board_info info[] = { |
290 | { I2C_BOARD_INFO("w83l785ts", 0x2d) }, | |
291 | { I2C_BOARD_INFO("w83781d", 0x2d) }, | |
66146da0 | 292 | { I2C_BOARD_INFO("adt7473", 0x2e) }, |
b26e72fb | 293 | { I2C_BOARD_INFO("f75375", 0x2e) }, |
66146da0 FJ |
294 | { I2C_BOARD_INFO("lm99", 0x4c) }, |
295 | { } | |
296 | }; | |
66146da0 FJ |
297 | |
298 | nouveau_i2c_identify(dev, "monitoring device", info, | |
486a45c2 | 299 | probe_monitoring_device, NV_I2C_DEFAULT(0)); |
66146da0 FJ |
300 | } |
301 | ||
34e9d85a MP |
302 | void |
303 | nouveau_temp_init(struct drm_device *dev) | |
304 | { | |
305 | struct drm_nouveau_private *dev_priv = dev->dev_private; | |
306 | struct nvbios *bios = &dev_priv->vbios; | |
307 | struct bit_entry P; | |
308 | u8 *temp = NULL; | |
309 | ||
310 | if (bios->type == NVBIOS_BIT) { | |
311 | if (bit_table(dev, 'P', &P)) | |
312 | return; | |
313 | ||
314 | if (P.version == 1) | |
f9f9f536 | 315 | temp = ROMPTR(dev, P.data[12]); |
34e9d85a | 316 | else if (P.version == 2) |
f9f9f536 | 317 | temp = ROMPTR(dev, P.data[16]); |
34e9d85a MP |
318 | else |
319 | NV_WARN(dev, "unknown temp for BIT P %d\n", P.version); | |
66146da0 FJ |
320 | |
321 | nouveau_temp_vbios_parse(dev, temp); | |
34e9d85a MP |
322 | } |
323 | ||
66146da0 | 324 | nouveau_temp_probe_i2c(dev); |
34e9d85a MP |
325 | } |
326 | ||
327 | void | |
328 | nouveau_temp_fini(struct drm_device *dev) | |
329 | { | |
330 | ||
331 | } |