blob: 6d2b957ca016f6b06854e4fcdb49d4868f8e91e6 [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 Peres32334cc2012-08-18 16:33:53 +020034#include <subdev/bios/extdev.h>
Martin Peres34e9d852010-09-22 20:54:22 +020035
Francisco Jerez8155cac2010-09-23 20:58:38 +020036static int
37nv40_sensor_setup(struct drm_device *dev)
Martin Peres34e9d852010-09-22 20:54:22 +020038{
Ben Skeggs77145f12012-07-31 16:16:21 +100039 struct nouveau_device *device = nouveau_dev(dev);
40 struct nouveau_drm *drm = nouveau_drm(dev);
Martin Peres34e9d852010-09-22 20:54:22 +020041
Martin Peres7d70e9c2012-08-16 11:00:55 +020042 /* enable ADC readout and disable the ALARM threshold */
43 if (nv_device(drm->device)->chipset >= 0x46) {
44 nv_mask(device, 0x15b8, 0x80000000, 0);
45 nv_wr32(device, 0x15b0, 0x80003fff);
46 return nv_rd32(device, 0x15b4) & 0x3fff;
47 } else {
48 nv_wr32(device, 0x15b0, 0xff);
49 return nv_rd32(device, 0x15b4) & 0xff;
50 }
Martin Peres34e9d852010-09-22 20:54:22 +020051}
52
Francisco Jerez8155cac2010-09-23 20:58:38 +020053int
54nv40_temp_get(struct drm_device *dev)
Martin Peres34e9d852010-09-22 20:54:22 +020055{
Ben Skeggs77145f12012-07-31 16:16:21 +100056 struct nouveau_device *device = nouveau_dev(dev);
57 struct nouveau_drm *drm = nouveau_drm(dev);
58 struct nouveau_pm *pm = nouveau_pm(dev);
Martin Peres34e9d852010-09-22 20:54:22 +020059 struct nouveau_pm_temp_sensor_constants *sensor = &pm->sensor_constants;
Francisco Jerez8155cac2010-09-23 20:58:38 +020060 int core_temp;
Martin Peres34e9d852010-09-22 20:54:22 +020061
Martin Peres7d70e9c2012-08-16 11:00:55 +020062 if (nv_device(drm->device)->chipset >= 0x46) {
63 nv_wr32(device, 0x15b0, 0x80003fff);
64 core_temp = nv_rd32(device, 0x15b4) & 0x3fff;
Martin Peres34e9d852010-09-22 20:54:22 +020065 } else {
Martin Peres7d70e9c2012-08-16 11:00:55 +020066 nv_wr32(device, 0x15b0, 0xff);
67 core_temp = nv_rd32(device, 0x15b4) & 0xff;
Martin Peres34e9d852010-09-22 20:54:22 +020068 }
69
Martin Peres7d70e9c2012-08-16 11:00:55 +020070 /* Setup the sensor if the temperature is 0 */
71 if (core_temp == 0)
72 core_temp = nv40_sensor_setup(dev);
73
74 if (sensor->slope_div == 0)
75 sensor->slope_div = 1;
76 if (sensor->offset_div == 0)
77 sensor->offset_div = 1;
78 if (sensor->slope_mult < 1)
79 sensor->slope_mult = 1;
80
Francisco Jerez8155cac2010-09-23 20:58:38 +020081 core_temp = core_temp * sensor->slope_mult / sensor->slope_div;
Martin Peres7d70e9c2012-08-16 11:00:55 +020082 core_temp = core_temp + sensor->offset_mult / sensor->offset_div;
83 core_temp = core_temp + sensor->offset_constant - 8;
Francisco Jerez8155cac2010-09-23 20:58:38 +020084
85 return core_temp;
86}
87
88int
89nv84_temp_get(struct drm_device *dev)
90{
Ben Skeggs77145f12012-07-31 16:16:21 +100091 struct nouveau_device *device = nouveau_dev(dev);
92 return nv_rd32(device, 0x20400);
Martin Peres34e9d852010-09-22 20:54:22 +020093}
94
95void
96nouveau_temp_safety_checks(struct drm_device *dev)
97{
Ben Skeggs77145f12012-07-31 16:16:21 +100098 struct nouveau_pm *pm = nouveau_pm(dev);
Martin Peres34e9d852010-09-22 20:54:22 +020099 struct nouveau_pm_threshold_temp *temps = &pm->threshold_temp;
100
101 if (temps->critical > 120)
102 temps->critical = 120;
103 else if (temps->critical < 80)
104 temps->critical = 80;
105
106 if (temps->down_clock > 110)
107 temps->down_clock = 110;
108 else if (temps->down_clock < 60)
109 temps->down_clock = 60;
Martin Peres34e9d852010-09-22 20:54:22 +0200110}
111
Francisco Jerez66146da2010-09-23 21:00:40 +0200112static bool
Ben Skeggs4196faa2012-07-10 14:36:38 +1000113probe_monitoring_device(struct nouveau_i2c_port *i2c,
Francisco Jerez66146da2010-09-23 21:00:40 +0200114 struct i2c_board_info *info)
115{
Francisco Jerez66146da2010-09-23 21:00:40 +0200116 struct i2c_client *client;
117
Lucas Stachf17811d2011-02-06 22:42:54 +0100118 request_module("%s%s", I2C_MODULE_PREFIX, info->type);
Francisco Jerez66146da2010-09-23 21:00:40 +0200119
Ben Skeggs77145f12012-07-31 16:16:21 +1000120 client = i2c_new_device(&i2c->adapter, info);
Francisco Jerez66146da2010-09-23 21:00:40 +0200121 if (!client)
122 return false;
123
124 if (!client->driver || client->driver->detect(client, info)) {
125 i2c_unregister_device(client);
126 return false;
127 }
128
129 return true;
130}
131
132static void
133nouveau_temp_probe_i2c(struct drm_device *dev)
134{
Ben Skeggs77145f12012-07-31 16:16:21 +1000135 struct nouveau_device *device = nouveau_dev(dev);
Martin Peres32334cc2012-08-18 16:33:53 +0200136 struct nouveau_bios *bios = nouveau_bios(device);
Ben Skeggs77145f12012-07-31 16:16:21 +1000137 struct nouveau_i2c *i2c = nouveau_i2c(device);
Martin Peres32334cc2012-08-18 16:33:53 +0200138 struct nvbios_extdev_func extdev_entry;
Francisco Jerez66146da2010-09-23 21:00:40 +0200139 struct i2c_board_info info[] = {
140 { I2C_BOARD_INFO("w83l785ts", 0x2d) },
141 { I2C_BOARD_INFO("w83781d", 0x2d) },
Francisco Jerez66146da2010-09-23 21:00:40 +0200142 { I2C_BOARD_INFO("adt7473", 0x2e) },
Martin Peres32334cc2012-08-18 16:33:53 +0200143 { I2C_BOARD_INFO("adt7473", 0x2d) },
144 { I2C_BOARD_INFO("adt7473", 0x2c) },
Ben Skeggsb26e72f2011-01-19 15:54:10 +1000145 { I2C_BOARD_INFO("f75375", 0x2e) },
Francisco Jerez66146da2010-09-23 21:00:40 +0200146 { I2C_BOARD_INFO("lm99", 0x4c) },
Martin Peres32334cc2012-08-18 16:33:53 +0200147 { I2C_BOARD_INFO("lm90", 0x4c) },
148 { I2C_BOARD_INFO("lm90", 0x4d) },
149 { I2C_BOARD_INFO("adm1021", 0x18) },
150 { I2C_BOARD_INFO("adm1021", 0x19) },
151 { I2C_BOARD_INFO("adm1021", 0x1a) },
152 { I2C_BOARD_INFO("adm1021", 0x29) },
153 { I2C_BOARD_INFO("adm1021", 0x2a) },
154 { I2C_BOARD_INFO("adm1021", 0x2b) },
155 { I2C_BOARD_INFO("adm1021", 0x4c) },
156 { I2C_BOARD_INFO("adm1021", 0x4d) },
157 { I2C_BOARD_INFO("adm1021", 0x4e) },
158 { I2C_BOARD_INFO("lm63", 0x18) },
159 { I2C_BOARD_INFO("lm63", 0x4e) },
Francisco Jerez66146da2010-09-23 21:00:40 +0200160 { }
161 };
Francisco Jerez66146da2010-09-23 21:00:40 +0200162
Martin Peres32334cc2012-08-18 16:33:53 +0200163 if (!nvbios_extdev_find(bios, NVBIOS_EXTDEV_LM89, &extdev_entry)) {
164 struct i2c_board_info board[] = {
165 { I2C_BOARD_INFO("lm90", extdev_entry.addr >> 1) },
166 { }
167 };
168
169 if (i2c->identify(i2c, NV_I2C_DEFAULT(0), "monitoring device",
170 board, probe_monitoring_device))
171 return;
172 }
173
174 if (!nvbios_extdev_find(bios, NVBIOS_EXTDEV_ADT7473, &extdev_entry)) {
175 struct i2c_board_info board[] = {
176 { I2C_BOARD_INFO("adt7473", extdev_entry.addr >> 1) },
177 { }
178 };
179
180 if (i2c->identify(i2c, NV_I2C_DEFAULT(0), "monitoring device",
181 board, probe_monitoring_device))
182 return;
183 }
184
185 /* The vbios doesn't provide the address of an exisiting monitoring
186 device. Let's try our static list.
187 */
Ben Skeggs77145f12012-07-31 16:16:21 +1000188 i2c->identify(i2c, NV_I2C_DEFAULT(0), "monitoring device", info,
189 probe_monitoring_device);
Francisco Jerez66146da2010-09-23 21:00:40 +0200190}
191
Martin Peres34e9d852010-09-22 20:54:22 +0200192void
193nouveau_temp_init(struct drm_device *dev)
194{
Ben Skeggs77145f12012-07-31 16:16:21 +1000195 struct nouveau_drm *drm = nouveau_drm(dev);
Martin Peres7d70e9c2012-08-16 11:00:55 +0200196 struct nouveau_device *device = nv_device(drm->device);
197 struct nouveau_bios *bios = nouveau_bios(device);
198 struct nouveau_pm *pm = nouveau_pm(dev);
199 struct nouveau_pm_temp_sensor_constants *sensor = &pm->sensor_constants;
200 struct nouveau_pm_threshold_temp *temps = &pm->threshold_temp;
201 struct nvbios_therm_sensor bios_sensor;
202 struct nvbios_therm_fan bios_fan;
Martin Peres34e9d852010-09-22 20:54:22 +0200203
Martin Peres7d70e9c2012-08-16 11:00:55 +0200204 /* store some safe defaults */
205 sensor->offset_constant = 0;
206 sensor->offset_mult = 0;
207 sensor->offset_div = 1;
208 sensor->slope_mult = 1;
209 sensor->slope_div = 1;
Martin Peres34e9d852010-09-22 20:54:22 +0200210
Martin Peres7d70e9c2012-08-16 11:00:55 +0200211 if (!nvbios_therm_sensor_parse(bios, NVBIOS_THERM_DOMAIN_CORE,
212 &bios_sensor)) {
213 sensor->slope_mult = bios_sensor.slope_mult;
214 sensor->slope_div = bios_sensor.slope_div;
215 sensor->offset_mult = bios_sensor.offset_num;
216 sensor->offset_div = bios_sensor.offset_den;
217 sensor->offset_constant = bios_sensor.offset_constant;
Francisco Jerez66146da2010-09-23 21:00:40 +0200218
Martin Peres7d70e9c2012-08-16 11:00:55 +0200219 temps->down_clock = bios_sensor.thrs_down_clock.temp;
220 temps->critical = bios_sensor.thrs_critical.temp;
Martin Peres34e9d852010-09-22 20:54:22 +0200221 }
222
Martin Peres7d70e9c2012-08-16 11:00:55 +0200223 if (nvbios_therm_fan_parse(bios, &bios_fan)) {
224 pm->fan.min_duty = bios_fan.min_duty;
225 pm->fan.max_duty = bios_fan.max_duty;
226 pm->fan.pwm_freq = bios_fan.pwm_freq;
227 }
228
229 nouveau_temp_safety_checks(dev);
Francisco Jerez66146da2010-09-23 21:00:40 +0200230 nouveau_temp_probe_i2c(dev);
Martin Peres34e9d852010-09-22 20:54:22 +0200231}
232
233void
234nouveau_temp_fini(struct drm_device *dev)
235{
236
237}