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
25#include <linux/module.h>
26
27#include "drmP.h"
28
29#include "nouveau_drv.h"
30#include "nouveau_pm.h"
31
32static void
33nouveau_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;
48	sensor->offset_mult = 0;
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
58	/* Set the default range for the pwm fan */
59	pm->fan.min_duty = 30;
60	pm->fan.max_duty = 100;
61
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:
74		case 0x4a:
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;
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;
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++) {
125		s16 value = ROM16(temp[1]);
126
127		switch (temp[0]) {
128		case 0x01:
129			if ((value & 0x8f) == 0)
130				sensor->offset_constant = (value >> 9) & 0x7f;
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;
163		case 0x22:
164			pm->fan.min_duty = value & 0xff;
165			pm->fan.max_duty = (value & 0xff00) >> 8;
166			break;
167		case 0x26:
168			pm->fan.pwm_freq = value;
169			break;
170		}
171		temp += recordlen;
172	}
173
174	nouveau_temp_safety_checks(dev);
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;
183}
184
185static int
186nv40_sensor_setup(struct drm_device *dev)
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;
191	s32 offset = sensor->offset_mult / sensor->offset_div;
192	s32 sensor_calibration;
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 */
210	return nv_rd32(dev, 0x0015b4) & 0x1fff;
211}
212
213int
214nv40_temp_get(struct drm_device *dev)
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;
219	int offset = sensor->offset_mult / sensor->offset_div;
220	int core_temp;
221
222	if (dev_priv->card_type >= NV_50) {
223		core_temp = nv_rd32(dev, 0x20008);
224	} else {
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);
229	}
230
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
237int
238nv84_temp_get(struct drm_device *dev)
239{
240	return nv_rd32(dev, 0x20400);
241}
242
243void
244nouveau_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
266static bool
267probe_monitoring_device(struct nouveau_i2c_chan *i2c,
268			struct i2c_board_info *info)
269{
270	struct i2c_client *client;
271
272	request_module("%s%s", I2C_MODULE_PREFIX, info->type);
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
286static void
287nouveau_temp_probe_i2c(struct drm_device *dev)
288{
289	struct i2c_board_info info[] = {
290		{ I2C_BOARD_INFO("w83l785ts", 0x2d) },
291		{ I2C_BOARD_INFO("w83781d", 0x2d) },
292		{ I2C_BOARD_INFO("adt7473", 0x2e) },
293		{ I2C_BOARD_INFO("f75375", 0x2e) },
294		{ I2C_BOARD_INFO("lm99", 0x4c) },
295		{ }
296	};
297
298	nouveau_i2c_identify(dev, "monitoring device", info,
299			     probe_monitoring_device, NV_I2C_DEFAULT(0));
300}
301
302void
303nouveau_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)
315			temp = ROMPTR(dev, P.data[12]);
316		else if (P.version == 2)
317			temp = ROMPTR(dev, P.data[16]);
318		else
319			NV_WARN(dev, "unknown temp for BIT P %d\n", P.version);
320
321		nouveau_temp_vbios_parse(dev, temp);
322	}
323
324	nouveau_temp_probe_i2c(dev);
325}
326
327void
328nouveau_temp_fini(struct drm_device *dev)
329{
330
331}
332