aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMartin Peres <martin.peres@labri.fr>2012-11-04 18:18:49 -0500
committerBen Skeggs <bskeggs@redhat.com>2013-02-20 01:00:23 -0500
commit0083b91dac482c70eeb96745d9ef604f904da3e5 (patch)
tree657ddc161b7e1190fb1bbd6a48a5c9b2b1dfd482
parent9d7175c808793b3e30db455da7529d3c05b00712 (diff)
drm/nouveau/therm: implement support for temperature alarms
For now, we only boost the fan speed to the maximum and auto-mode when hitting the FAN_BOOST threshold and halt the computer when it reaches the shutdown temperature. The downclock and critical thresholds do nothing. On nv43:50 and nva3+, temperature is polled because of the limited hardware. I'll improve the nva3+ situation by implementing alarm management in PDAEMON whenever I can but polling once every second shouldn't be such a problem. v2 (Ben Skeggs): - rebased v3: fixed false-detections and threshold reprogrammation handling on nv50:nvc0 Signed-off-by: Ben Skeggs <bskeggs@redhat.com> Signed-off-by: Martin Peres <martin.peres@labri.fr>
-rw-r--r--drivers/gpu/drm/nouveau/core/os.h1
-rw-r--r--drivers/gpu/drm/nouveau/core/subdev/mc/nv98.c1
-rw-r--r--drivers/gpu/drm/nouveau/core/subdev/therm/base.c11
-rw-r--r--drivers/gpu/drm/nouveau/core/subdev/therm/nv40.c17
-rw-r--r--drivers/gpu/drm/nouveau/core/subdev/therm/nv50.c153
-rw-r--r--drivers/gpu/drm/nouveau/core/subdev/therm/nva3.c1
-rw-r--r--drivers/gpu/drm/nouveau/core/subdev/therm/nvd0.c1
-rw-r--r--drivers/gpu/drm/nouveau/core/subdev/therm/priv.h44
-rw-r--r--drivers/gpu/drm/nouveau/core/subdev/therm/temp.c138
9 files changed, 366 insertions, 1 deletions
diff --git a/drivers/gpu/drm/nouveau/core/os.h b/drivers/gpu/drm/nouveau/core/os.h
index cfe3b9cad156..eb496033b55c 100644
--- a/drivers/gpu/drm/nouveau/core/os.h
+++ b/drivers/gpu/drm/nouveau/core/os.h
@@ -16,6 +16,7 @@
16#include <linux/vmalloc.h> 16#include <linux/vmalloc.h>
17#include <linux/acpi.h> 17#include <linux/acpi.h>
18#include <linux/dmi.h> 18#include <linux/dmi.h>
19#include <linux/reboot.h>
19 20
20#include <asm/unaligned.h> 21#include <asm/unaligned.h>
21 22
diff --git a/drivers/gpu/drm/nouveau/core/subdev/mc/nv98.c b/drivers/gpu/drm/nouveau/core/subdev/mc/nv98.c
index b93fd094c806..3a80b29dce0f 100644
--- a/drivers/gpu/drm/nouveau/core/subdev/mc/nv98.c
+++ b/drivers/gpu/drm/nouveau/core/subdev/mc/nv98.c
@@ -35,6 +35,7 @@ nv98_mc_intr[] = {
35 { 0x00001000, NVDEV_ENGINE_GR }, 35 { 0x00001000, NVDEV_ENGINE_GR },
36 { 0x00004000, NVDEV_ENGINE_CRYPT }, /* NV84:NVA3 */ 36 { 0x00004000, NVDEV_ENGINE_CRYPT }, /* NV84:NVA3 */
37 { 0x00008000, NVDEV_ENGINE_BSP }, 37 { 0x00008000, NVDEV_ENGINE_BSP },
38 { 0x00080000, NVDEV_SUBDEV_THERM }, /* NVA3:NVC0 */
38 { 0x00100000, NVDEV_SUBDEV_TIMER }, 39 { 0x00100000, NVDEV_SUBDEV_TIMER },
39 { 0x00200000, NVDEV_SUBDEV_GPIO }, 40 { 0x00200000, NVDEV_SUBDEV_GPIO },
40 { 0x00400000, NVDEV_ENGINE_COPY0 }, /* NVA3- */ 41 { 0x00400000, NVDEV_ENGINE_COPY0 }, /* NVA3- */
diff --git a/drivers/gpu/drm/nouveau/core/subdev/therm/base.c b/drivers/gpu/drm/nouveau/core/subdev/therm/base.c
index dee764c7b693..f54495a0907a 100644
--- a/drivers/gpu/drm/nouveau/core/subdev/therm/base.c
+++ b/drivers/gpu/drm/nouveau/core/subdev/therm/base.c
@@ -123,7 +123,7 @@ nouveau_therm_alarm(struct nouveau_alarm *alarm)
123 nouveau_therm_update(&priv->base, -1); 123 nouveau_therm_update(&priv->base, -1);
124} 124}
125 125
126static int 126int
127nouveau_therm_mode(struct nouveau_therm *therm, int mode) 127nouveau_therm_mode(struct nouveau_therm *therm, int mode)
128{ 128{
129 struct nouveau_therm_priv *priv = (void *)therm; 129 struct nouveau_therm_priv *priv = (void *)therm;
@@ -212,27 +212,35 @@ nouveau_therm_attr_set(struct nouveau_therm *therm,
212 return nouveau_therm_mode(therm, value); 212 return nouveau_therm_mode(therm, value);
213 case NOUVEAU_THERM_ATTR_THRS_FAN_BOOST: 213 case NOUVEAU_THERM_ATTR_THRS_FAN_BOOST:
214 priv->bios_sensor.thrs_fan_boost.temp = value; 214 priv->bios_sensor.thrs_fan_boost.temp = value;
215 priv->sensor.program_alarms(therm);
215 return 0; 216 return 0;
216 case NOUVEAU_THERM_ATTR_THRS_FAN_BOOST_HYST: 217 case NOUVEAU_THERM_ATTR_THRS_FAN_BOOST_HYST:
217 priv->bios_sensor.thrs_fan_boost.hysteresis = value; 218 priv->bios_sensor.thrs_fan_boost.hysteresis = value;
219 priv->sensor.program_alarms(therm);
218 return 0; 220 return 0;
219 case NOUVEAU_THERM_ATTR_THRS_DOWN_CLK: 221 case NOUVEAU_THERM_ATTR_THRS_DOWN_CLK:
220 priv->bios_sensor.thrs_down_clock.temp = value; 222 priv->bios_sensor.thrs_down_clock.temp = value;
223 priv->sensor.program_alarms(therm);
221 return 0; 224 return 0;
222 case NOUVEAU_THERM_ATTR_THRS_DOWN_CLK_HYST: 225 case NOUVEAU_THERM_ATTR_THRS_DOWN_CLK_HYST:
223 priv->bios_sensor.thrs_down_clock.hysteresis = value; 226 priv->bios_sensor.thrs_down_clock.hysteresis = value;
227 priv->sensor.program_alarms(therm);
224 return 0; 228 return 0;
225 case NOUVEAU_THERM_ATTR_THRS_CRITICAL: 229 case NOUVEAU_THERM_ATTR_THRS_CRITICAL:
226 priv->bios_sensor.thrs_critical.temp = value; 230 priv->bios_sensor.thrs_critical.temp = value;
231 priv->sensor.program_alarms(therm);
227 return 0; 232 return 0;
228 case NOUVEAU_THERM_ATTR_THRS_CRITICAL_HYST: 233 case NOUVEAU_THERM_ATTR_THRS_CRITICAL_HYST:
229 priv->bios_sensor.thrs_critical.hysteresis = value; 234 priv->bios_sensor.thrs_critical.hysteresis = value;
235 priv->sensor.program_alarms(therm);
230 return 0; 236 return 0;
231 case NOUVEAU_THERM_ATTR_THRS_SHUTDOWN: 237 case NOUVEAU_THERM_ATTR_THRS_SHUTDOWN:
232 priv->bios_sensor.thrs_shutdown.temp = value; 238 priv->bios_sensor.thrs_shutdown.temp = value;
239 priv->sensor.program_alarms(therm);
233 return 0; 240 return 0;
234 case NOUVEAU_THERM_ATTR_THRS_SHUTDOWN_HYST: 241 case NOUVEAU_THERM_ATTR_THRS_SHUTDOWN_HYST:
235 priv->bios_sensor.thrs_shutdown.hysteresis = value; 242 priv->bios_sensor.thrs_shutdown.hysteresis = value;
243 priv->sensor.program_alarms(therm);
236 return 0; 244 return 0;
237 } 245 }
238 246
@@ -253,6 +261,7 @@ _nouveau_therm_init(struct nouveau_object *object)
253 if (priv->fan->percent >= 0) 261 if (priv->fan->percent >= 0)
254 therm->fan_set(therm, priv->fan->percent); 262 therm->fan_set(therm, priv->fan->percent);
255 263
264 priv->sensor.program_alarms(therm);
256 return 0; 265 return 0;
257} 266}
258 267
diff --git a/drivers/gpu/drm/nouveau/core/subdev/therm/nv40.c b/drivers/gpu/drm/nouveau/core/subdev/therm/nv40.c
index 3f7c11500598..5c17577553f6 100644
--- a/drivers/gpu/drm/nouveau/core/subdev/therm/nv40.c
+++ b/drivers/gpu/drm/nouveau/core/subdev/therm/nv40.c
@@ -38,6 +38,7 @@ nv40_sensor_setup(struct nouveau_therm *therm)
38 if (device->chipset >= 0x46) { 38 if (device->chipset >= 0x46) {
39 nv_mask(therm, 0x15b8, 0x80000000, 0); 39 nv_mask(therm, 0x15b8, 0x80000000, 0);
40 nv_wr32(therm, 0x15b0, 0x80003fff); 40 nv_wr32(therm, 0x15b0, 0x80003fff);
41 mdelay(10); /* wait for the temperature to stabilize */
41 return nv_rd32(therm, 0x15b4) & 0x3fff; 42 return nv_rd32(therm, 0x15b4) & 0x3fff;
42 } else { 43 } else {
43 nv_wr32(therm, 0x15b0, 0xff); 44 nv_wr32(therm, 0x15b0, 0xff);
@@ -135,6 +136,20 @@ nv40_fan_pwm_set(struct nouveau_therm *therm, int line, u32 divs, u32 duty)
135 return 0; 136 return 0;
136} 137}
137 138
139static void
140nv40_therm_intr(struct nouveau_subdev *subdev)
141{
142 struct nouveau_therm *therm = nouveau_therm(subdev);
143 uint32_t stat = nv_rd32(therm, 0x1100);
144
145 /* traitement */
146
147 /* ack all IRQs */
148 nv_wr32(therm, 0x1100, 0x70000);
149
150 nv_error(therm, "THERM received an IRQ: stat = %x\n", stat);
151}
152
138static int 153static int
139nv40_therm_ctor(struct nouveau_object *parent, 154nv40_therm_ctor(struct nouveau_object *parent,
140 struct nouveau_object *engine, 155 struct nouveau_object *engine,
@@ -153,6 +168,8 @@ nv40_therm_ctor(struct nouveau_object *parent,
153 priv->base.base.pwm_get = nv40_fan_pwm_get; 168 priv->base.base.pwm_get = nv40_fan_pwm_get;
154 priv->base.base.pwm_set = nv40_fan_pwm_set; 169 priv->base.base.pwm_set = nv40_fan_pwm_set;
155 priv->base.base.temp_get = nv40_temp_get; 170 priv->base.base.temp_get = nv40_temp_get;
171 priv->base.sensor.program_alarms = nouveau_therm_program_alarms_polling;
172 nv_subdev(priv)->intr = nv40_therm_intr;
156 return nouveau_therm_preinit(&priv->base.base); 173 return nouveau_therm_preinit(&priv->base.base);
157} 174}
158 175
diff --git a/drivers/gpu/drm/nouveau/core/subdev/therm/nv50.c b/drivers/gpu/drm/nouveau/core/subdev/therm/nv50.c
index 8bb4c2a15efa..4b7fe24f5506 100644
--- a/drivers/gpu/drm/nouveau/core/subdev/therm/nv50.c
+++ b/drivers/gpu/drm/nouveau/core/subdev/therm/nv50.c
@@ -124,6 +124,141 @@ nv50_temp_get(struct nouveau_therm *therm)
124 return nv_rd32(therm, 0x20400); 124 return nv_rd32(therm, 0x20400);
125} 125}
126 126
127static void
128nv50_therm_program_alarms(struct nouveau_therm *therm)
129{
130 struct nouveau_therm_priv *priv = (void *)therm;
131 struct nvbios_therm_sensor *sensor = &priv->bios_sensor;
132 unsigned long flags;
133
134 spin_lock_irqsave(&priv->sensor.alarm_program_lock, flags);
135
136 /* enable RISING and FALLING IRQs for shutdown, THRS 0, 1, 2 and 4 */
137 nv_wr32(therm, 0x20000, 0x000003ff);
138
139 /* shutdown: The computer should be shutdown when reached */
140 nv_wr32(therm, 0x20484, sensor->thrs_shutdown.hysteresis);
141 nv_wr32(therm, 0x20480, sensor->thrs_shutdown.temp);
142
143 /* THRS_1 : fan boost*/
144 nv_wr32(therm, 0x204c4, sensor->thrs_fan_boost.temp);
145
146 /* THRS_2 : critical */
147 nv_wr32(therm, 0x204c0, sensor->thrs_critical.temp);
148
149 /* THRS_4 : down clock */
150 nv_wr32(therm, 0x20414, sensor->thrs_down_clock.temp);
151 spin_unlock_irqrestore(&priv->sensor.alarm_program_lock, flags);
152
153 nv_info(therm,
154 "Programmed thresholds [ %d(%d), %d(%d), %d(%d), %d(%d) ]\n",
155 sensor->thrs_fan_boost.temp, sensor->thrs_fan_boost.hysteresis,
156 sensor->thrs_down_clock.temp,
157 sensor->thrs_down_clock.hysteresis,
158 sensor->thrs_critical.temp, sensor->thrs_critical.hysteresis,
159 sensor->thrs_shutdown.temp, sensor->thrs_shutdown.hysteresis);
160
161}
162
163/* must be called with alarm_program_lock taken ! */
164static void
165nv50_therm_threshold_hyst_emulation(struct nouveau_therm *therm,
166 uint32_t thrs_reg, u8 status_bit,
167 const struct nvbios_therm_threshold *thrs,
168 enum nouveau_therm_thrs thrs_name)
169{
170 enum nouveau_therm_thrs_direction direction;
171 enum nouveau_therm_thrs_state prev_state, new_state;
172 int temp, cur;
173
174 prev_state = nouveau_therm_sensor_get_threshold_state(therm, thrs_name);
175 temp = nv_rd32(therm, thrs_reg);
176
177 /* program the next threshold */
178 if (temp == thrs->temp) {
179 nv_wr32(therm, thrs_reg, thrs->temp - thrs->hysteresis);
180 new_state = NOUVEAU_THERM_THRS_HIGHER;
181 } else {
182 nv_wr32(therm, thrs_reg, thrs->temp);
183 new_state = NOUVEAU_THERM_THRS_LOWER;
184 }
185
186 /* fix the state (in case someone reprogrammed the alarms) */
187 cur = therm->temp_get(therm);
188 if (new_state == NOUVEAU_THERM_THRS_LOWER && cur > thrs->temp)
189 new_state = NOUVEAU_THERM_THRS_HIGHER;
190 else if (new_state == NOUVEAU_THERM_THRS_HIGHER &&
191 cur < thrs->temp - thrs->hysteresis)
192 new_state = NOUVEAU_THERM_THRS_LOWER;
193 nouveau_therm_sensor_set_threshold_state(therm, thrs_name, new_state);
194
195 /* find the direction */
196 if (prev_state < new_state)
197 direction = NOUVEAU_THERM_THRS_RISING;
198 else if (prev_state > new_state)
199 direction = NOUVEAU_THERM_THRS_FALLING;
200 else
201 return;
202
203 /* advertise a change in direction */
204 nouveau_therm_sensor_event(therm, thrs_name, direction);
205}
206
207static void
208nv50_therm_intr(struct nouveau_subdev *subdev)
209{
210 struct nouveau_therm *therm = nouveau_therm(subdev);
211 struct nouveau_therm_priv *priv = (void *)therm;
212 struct nvbios_therm_sensor *sensor = &priv->bios_sensor;
213 unsigned long flags;
214 uint32_t intr;
215
216 spin_lock_irqsave(&priv->sensor.alarm_program_lock, flags);
217
218 intr = nv_rd32(therm, 0x20100);
219
220 /* THRS_4: downclock */
221 if (intr & 0x002) {
222 nv50_therm_threshold_hyst_emulation(therm, 0x20414, 24,
223 &sensor->thrs_down_clock,
224 NOUVEAU_THERM_THRS_DOWNCLOCK);
225 intr &= ~0x002;
226 }
227
228 /* shutdown */
229 if (intr & 0x004) {
230 nv50_therm_threshold_hyst_emulation(therm, 0x20480, 20,
231 &sensor->thrs_shutdown,
232 NOUVEAU_THERM_THRS_SHUTDOWN);
233 intr &= ~0x004;
234 }
235
236 /* THRS_1 : fan boost */
237 if (intr & 0x008) {
238 nv50_therm_threshold_hyst_emulation(therm, 0x204c4, 21,
239 &sensor->thrs_fan_boost,
240 NOUVEAU_THERM_THRS_FANBOOST);
241 intr &= ~0x008;
242 }
243
244 /* THRS_2 : critical */
245 if (intr & 0x010) {
246 nv50_therm_threshold_hyst_emulation(therm, 0x204c0, 22,
247 &sensor->thrs_critical,
248 NOUVEAU_THERM_THRS_CRITICAL);
249 intr &= ~0x010;
250 }
251
252 if (intr)
253 nv_error(therm, "unhandled intr 0x%08x\n", intr);
254
255 /* ACK everything */
256 nv_wr32(therm, 0x20100, 0xffffffff);
257 nv_wr32(therm, 0x1100, 0x10000); /* PBUS */
258
259 spin_unlock_irqrestore(&priv->sensor.alarm_program_lock, flags);
260}
261
127static int 262static int
128nv50_therm_ctor(struct nouveau_object *parent, 263nv50_therm_ctor(struct nouveau_object *parent,
129 struct nouveau_object *engine, 264 struct nouveau_object *engine,
@@ -143,6 +278,24 @@ nv50_therm_ctor(struct nouveau_object *parent,
143 priv->base.base.pwm_set = nv50_fan_pwm_set; 278 priv->base.base.pwm_set = nv50_fan_pwm_set;
144 priv->base.base.pwm_clock = nv50_fan_pwm_clock; 279 priv->base.base.pwm_clock = nv50_fan_pwm_clock;
145 priv->base.base.temp_get = nv50_temp_get; 280 priv->base.base.temp_get = nv50_temp_get;
281 priv->base.sensor.program_alarms = nv50_therm_program_alarms;
282 spin_lock_init(&priv->base.sensor.alarm_program_lock);
283 nv_subdev(priv)->intr = nv50_therm_intr;
284
285 /* init the thresholds */
286 nouveau_therm_sensor_set_threshold_state(&priv->base.base,
287 NOUVEAU_THERM_THRS_SHUTDOWN,
288 NOUVEAU_THERM_THRS_LOWER);
289 nouveau_therm_sensor_set_threshold_state(&priv->base.base,
290 NOUVEAU_THERM_THRS_FANBOOST,
291 NOUVEAU_THERM_THRS_LOWER);
292 nouveau_therm_sensor_set_threshold_state(&priv->base.base,
293 NOUVEAU_THERM_THRS_CRITICAL,
294 NOUVEAU_THERM_THRS_LOWER);
295 nouveau_therm_sensor_set_threshold_state(&priv->base.base,
296 NOUVEAU_THERM_THRS_DOWNCLOCK,
297 NOUVEAU_THERM_THRS_LOWER);
298
146 return nouveau_therm_preinit(&priv->base.base); 299 return nouveau_therm_preinit(&priv->base.base);
147} 300}
148 301
diff --git a/drivers/gpu/drm/nouveau/core/subdev/therm/nva3.c b/drivers/gpu/drm/nouveau/core/subdev/therm/nva3.c
index 8625d219f5ca..2dcc5437116a 100644
--- a/drivers/gpu/drm/nouveau/core/subdev/therm/nva3.c
+++ b/drivers/gpu/drm/nouveau/core/subdev/therm/nva3.c
@@ -83,6 +83,7 @@ nva3_therm_ctor(struct nouveau_object *parent,
83 priv->base.base.pwm_clock = nv50_fan_pwm_clock; 83 priv->base.base.pwm_clock = nv50_fan_pwm_clock;
84 priv->base.base.temp_get = nv50_temp_get; 84 priv->base.base.temp_get = nv50_temp_get;
85 priv->base.base.fan_sense = nva3_therm_fan_sense; 85 priv->base.base.fan_sense = nva3_therm_fan_sense;
86 priv->base.sensor.program_alarms = nouveau_therm_program_alarms_polling;
86 return nouveau_therm_preinit(&priv->base.base); 87 return nouveau_therm_preinit(&priv->base.base);
87} 88}
88 89
diff --git a/drivers/gpu/drm/nouveau/core/subdev/therm/nvd0.c b/drivers/gpu/drm/nouveau/core/subdev/therm/nvd0.c
index e722dcd4e37b..d7d30ee8332e 100644
--- a/drivers/gpu/drm/nouveau/core/subdev/therm/nvd0.c
+++ b/drivers/gpu/drm/nouveau/core/subdev/therm/nvd0.c
@@ -137,6 +137,7 @@ nvd0_therm_ctor(struct nouveau_object *parent,
137 priv->base.base.pwm_clock = nvd0_fan_pwm_clock; 137 priv->base.base.pwm_clock = nvd0_fan_pwm_clock;
138 priv->base.base.temp_get = nv50_temp_get; 138 priv->base.base.temp_get = nv50_temp_get;
139 priv->base.base.fan_sense = nva3_therm_fan_sense; 139 priv->base.base.fan_sense = nva3_therm_fan_sense;
140 priv->base.sensor.program_alarms = nouveau_therm_program_alarms_polling;
140 return nouveau_therm_preinit(&priv->base.base); 141 return nouveau_therm_preinit(&priv->base.base);
141} 142}
142 143
diff --git a/drivers/gpu/drm/nouveau/core/subdev/therm/priv.h b/drivers/gpu/drm/nouveau/core/subdev/therm/priv.h
index 5c628b5832e6..c2413e6aae25 100644
--- a/drivers/gpu/drm/nouveau/core/subdev/therm/priv.h
+++ b/drivers/gpu/drm/nouveau/core/subdev/therm/priv.h
@@ -50,6 +50,24 @@ struct nouveau_fan {
50 struct dcb_gpio_func tach; 50 struct dcb_gpio_func tach;
51}; 51};
52 52
53enum nouveau_therm_thrs_direction {
54 NOUVEAU_THERM_THRS_FALLING = 0,
55 NOUVEAU_THERM_THRS_RISING = 1
56};
57
58enum nouveau_therm_thrs_state {
59 NOUVEAU_THERM_THRS_LOWER = 0,
60 NOUVEAU_THERM_THRS_HIGHER = 1
61};
62
63enum nouveau_therm_thrs {
64 NOUVEAU_THERM_THRS_FANBOOST = 0,
65 NOUVEAU_THERM_THRS_DOWNCLOCK = 1,
66 NOUVEAU_THERM_THRS_CRITICAL = 2,
67 NOUVEAU_THERM_THRS_SHUTDOWN = 3,
68 NOUVEAU_THERM_THRS_NR
69};
70
53struct nouveau_therm_priv { 71struct nouveau_therm_priv {
54 struct nouveau_therm base; 72 struct nouveau_therm base;
55 73
@@ -65,10 +83,25 @@ struct nouveau_therm_priv {
65 /* fan priv */ 83 /* fan priv */
66 struct nouveau_fan *fan; 84 struct nouveau_fan *fan;
67 85
86 /* alarms priv */
87 struct {
88 spinlock_t alarm_program_lock;
89 struct nouveau_alarm therm_poll_alarm;
90 enum nouveau_therm_thrs_state alarm_state[NOUVEAU_THERM_THRS_NR];
91 void (*program_alarms)(struct nouveau_therm *);
92 } sensor;
93
94 /* what should be done if the card overheats */
95 struct {
96 void (*downclock)(struct nouveau_therm *, bool active);
97 void (*pause)(struct nouveau_therm *, bool active);
98 } emergency;
99
68 /* ic */ 100 /* ic */
69 struct i2c_client *ic; 101 struct i2c_client *ic;
70}; 102};
71 103
104int nouveau_therm_mode(struct nouveau_therm *therm, int mode);
72int nouveau_therm_attr_get(struct nouveau_therm *therm, 105int nouveau_therm_attr_get(struct nouveau_therm *therm,
73 enum nouveau_therm_attr_type type); 106 enum nouveau_therm_attr_type type);
74int nouveau_therm_attr_set(struct nouveau_therm *therm, 107int nouveau_therm_attr_set(struct nouveau_therm *therm,
@@ -88,6 +121,17 @@ int nouveau_therm_fan_sense(struct nouveau_therm *therm);
88 121
89int nouveau_therm_preinit(struct nouveau_therm *); 122int nouveau_therm_preinit(struct nouveau_therm *);
90 123
124void nouveau_therm_sensor_set_threshold_state(struct nouveau_therm *therm,
125 enum nouveau_therm_thrs thrs,
126 enum nouveau_therm_thrs_state st);
127enum nouveau_therm_thrs_state
128nouveau_therm_sensor_get_threshold_state(struct nouveau_therm *therm,
129 enum nouveau_therm_thrs thrs);
130void nouveau_therm_sensor_event(struct nouveau_therm *therm,
131 enum nouveau_therm_thrs thrs,
132 enum nouveau_therm_thrs_direction dir);
133void nouveau_therm_program_alarms_polling(struct nouveau_therm *therm);
134
91int nv50_fan_pwm_ctrl(struct nouveau_therm *, int, bool); 135int nv50_fan_pwm_ctrl(struct nouveau_therm *, int, bool);
92int nv50_fan_pwm_get(struct nouveau_therm *, int, u32 *, u32 *); 136int nv50_fan_pwm_get(struct nouveau_therm *, int, u32 *, u32 *);
93int nv50_fan_pwm_set(struct nouveau_therm *, int, u32, u32); 137int nv50_fan_pwm_set(struct nouveau_therm *, int, u32, u32);
diff --git a/drivers/gpu/drm/nouveau/core/subdev/therm/temp.c b/drivers/gpu/drm/nouveau/core/subdev/therm/temp.c
index 204282301fb1..d80c572de04f 100644
--- a/drivers/gpu/drm/nouveau/core/subdev/therm/temp.c
+++ b/drivers/gpu/drm/nouveau/core/subdev/therm/temp.c
@@ -65,12 +65,150 @@ nouveau_therm_temp_safety_checks(struct nouveau_therm *therm)
65 priv->bios_sensor.offset_den = 1; 65 priv->bios_sensor.offset_den = 1;
66} 66}
67 67
68/* must be called with alarm_program_lock taken ! */
69void nouveau_therm_sensor_set_threshold_state(struct nouveau_therm *therm,
70 enum nouveau_therm_thrs thrs,
71 enum nouveau_therm_thrs_state st)
72{
73 struct nouveau_therm_priv *priv = (void *)therm;
74 priv->sensor.alarm_state[thrs] = st;
75}
76
77/* must be called with alarm_program_lock taken ! */
78enum nouveau_therm_thrs_state
79nouveau_therm_sensor_get_threshold_state(struct nouveau_therm *therm,
80 enum nouveau_therm_thrs thrs)
81{
82 struct nouveau_therm_priv *priv = (void *)therm;
83 return priv->sensor.alarm_state[thrs];
84}
85
86void nouveau_therm_sensor_event(struct nouveau_therm *therm,
87 enum nouveau_therm_thrs thrs,
88 enum nouveau_therm_thrs_direction dir)
89{
90 struct nouveau_therm_priv *priv = (void *)therm;
91 bool active;
92 const char *thresolds[] = {
93 "fanboost", "downclock", "critical", "shutdown"
94 };
95 uint8_t temperature = therm->temp_get(therm);
96
97 if (thrs < 0 || thrs > 3)
98 return;
99
100 if (dir == NOUVEAU_THERM_THRS_FALLING)
101 nv_info(therm, "temperature (%u C) went bellow the '%s' threshold\n",
102 temperature, thresolds[thrs]);
103 else
104 nv_info(therm, "temperature (%u C) hit the '%s' threshold\n",
105 temperature, thresolds[thrs]);
106
107 active = (dir == NOUVEAU_THERM_THRS_RISING);
108 switch (thrs) {
109 case NOUVEAU_THERM_THRS_FANBOOST:
110 nouveau_therm_fan_set(therm, true, 100);
111 nouveau_therm_mode(therm, FAN_CONTROL_AUTO);
112 break;
113 case NOUVEAU_THERM_THRS_DOWNCLOCK:
114 if (priv->emergency.downclock)
115 priv->emergency.downclock(therm, active);
116 break;
117 case NOUVEAU_THERM_THRS_CRITICAL:
118 if (priv->emergency.pause)
119 priv->emergency.pause(therm, active);
120 break;
121 case NOUVEAU_THERM_THRS_SHUTDOWN:
122 orderly_poweroff(true);
123 break;
124 case NOUVEAU_THERM_THRS_NR:
125 break;
126 }
127
128}
129
130/* must be called with alarm_program_lock taken ! */
131static void
132nouveau_therm_threshold_hyst_polling(struct nouveau_therm *therm,
133 const struct nvbios_therm_threshold *thrs,
134 enum nouveau_therm_thrs thrs_name)
135{
136 enum nouveau_therm_thrs_direction direction;
137 enum nouveau_therm_thrs_state prev_state, new_state;
138 int temp = therm->temp_get(therm);
139
140 prev_state = nouveau_therm_sensor_get_threshold_state(therm, thrs_name);
141
142 if (temp >= thrs->temp && prev_state == NOUVEAU_THERM_THRS_LOWER) {
143 direction = NOUVEAU_THERM_THRS_RISING;
144 new_state = NOUVEAU_THERM_THRS_HIGHER;
145 } else if (temp <= thrs->temp - thrs->hysteresis &&
146 prev_state == NOUVEAU_THERM_THRS_HIGHER) {
147 direction = NOUVEAU_THERM_THRS_FALLING;
148 new_state = NOUVEAU_THERM_THRS_LOWER;
149 } else
150 return; /* nothing to do */
151
152 nouveau_therm_sensor_set_threshold_state(therm, thrs_name, new_state);
153 nouveau_therm_sensor_event(therm, thrs_name, direction);
154}
155
156static void
157alarm_timer_callback(struct nouveau_alarm *alarm)
158{
159 struct nouveau_therm_priv *priv =
160 container_of(alarm, struct nouveau_therm_priv, sensor.therm_poll_alarm);
161 struct nvbios_therm_sensor *sensor = &priv->bios_sensor;
162 struct nouveau_timer *ptimer = nouveau_timer(priv);
163 struct nouveau_therm *therm = &priv->base;
164 unsigned long flags;
165
166 spin_lock_irqsave(&priv->sensor.alarm_program_lock, flags);
167
168 nouveau_therm_threshold_hyst_polling(therm, &sensor->thrs_fan_boost,
169 NOUVEAU_THERM_THRS_FANBOOST);
170
171 nouveau_therm_threshold_hyst_polling(therm, &sensor->thrs_down_clock,
172 NOUVEAU_THERM_THRS_DOWNCLOCK);
173
174 nouveau_therm_threshold_hyst_polling(therm, &sensor->thrs_critical,
175 NOUVEAU_THERM_THRS_CRITICAL);
176
177 nouveau_therm_threshold_hyst_polling(therm, &sensor->thrs_shutdown,
178 NOUVEAU_THERM_THRS_SHUTDOWN);
179
180 /* schedule the next poll in one second */
181 if (list_empty(&alarm->head))
182 ptimer->alarm(ptimer, 1000 * 1000 * 1000, alarm);
183
184 spin_unlock_irqrestore(&priv->sensor.alarm_program_lock, flags);
185}
186
187void
188nouveau_therm_program_alarms_polling(struct nouveau_therm *therm)
189{
190 struct nouveau_therm_priv *priv = (void *)therm;
191 struct nvbios_therm_sensor *sensor = &priv->bios_sensor;
192
193 nv_info(therm,
194 "programmed thresholds [ %d(%d), %d(%d), %d(%d), %d(%d) ]\n",
195 sensor->thrs_fan_boost.temp, sensor->thrs_fan_boost.hysteresis,
196 sensor->thrs_down_clock.temp,
197 sensor->thrs_down_clock.hysteresis,
198 sensor->thrs_critical.temp, sensor->thrs_critical.hysteresis,
199 sensor->thrs_shutdown.temp, sensor->thrs_shutdown.hysteresis);
200
201 alarm_timer_callback(&priv->sensor.therm_poll_alarm);
202}
203
68int 204int
69nouveau_therm_sensor_ctor(struct nouveau_therm *therm) 205nouveau_therm_sensor_ctor(struct nouveau_therm *therm)
70{ 206{
71 struct nouveau_therm_priv *priv = (void *)therm; 207 struct nouveau_therm_priv *priv = (void *)therm;
72 struct nouveau_bios *bios = nouveau_bios(therm); 208 struct nouveau_bios *bios = nouveau_bios(therm);
73 209
210 nouveau_alarm_init(&priv->sensor.therm_poll_alarm, alarm_timer_callback);
211
74 nouveau_therm_temp_set_defaults(therm); 212 nouveau_therm_temp_set_defaults(therm);
75 if (nvbios_therm_sensor_parse(bios, NVBIOS_THERM_DOMAIN_CORE, 213 if (nvbios_therm_sensor_parse(bios, NVBIOS_THERM_DOMAIN_CORE,
76 &priv->bios_sensor)) 214 &priv->bios_sensor))