drm/nouveau/therm: switch to subdev printk macros
[deliverable/linux.git] / drivers / gpu / drm / nouveau / nvkm / subdev / therm / temp.c
1 /*
2 * Copyright 2012 The Nouveau community
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 #include "priv.h"
25
26 static void
27 nvkm_therm_temp_set_defaults(struct nvkm_therm *obj)
28 {
29 struct nvkm_therm_priv *therm = container_of(obj, typeof(*therm), base);
30
31 therm->bios_sensor.offset_constant = 0;
32
33 therm->bios_sensor.thrs_fan_boost.temp = 90;
34 therm->bios_sensor.thrs_fan_boost.hysteresis = 3;
35
36 therm->bios_sensor.thrs_down_clock.temp = 95;
37 therm->bios_sensor.thrs_down_clock.hysteresis = 3;
38
39 therm->bios_sensor.thrs_critical.temp = 105;
40 therm->bios_sensor.thrs_critical.hysteresis = 5;
41
42 therm->bios_sensor.thrs_shutdown.temp = 135;
43 therm->bios_sensor.thrs_shutdown.hysteresis = 5; /*not that it matters */
44 }
45
46
47 static void
48 nvkm_therm_temp_safety_checks(struct nvkm_therm *obj)
49 {
50 struct nvkm_therm_priv *therm = container_of(obj, typeof(*therm), base);
51 struct nvbios_therm_sensor *s = &therm->bios_sensor;
52
53 /* enforce a minimum hysteresis on thresholds */
54 s->thrs_fan_boost.hysteresis = max_t(u8, s->thrs_fan_boost.hysteresis, 2);
55 s->thrs_down_clock.hysteresis = max_t(u8, s->thrs_down_clock.hysteresis, 2);
56 s->thrs_critical.hysteresis = max_t(u8, s->thrs_critical.hysteresis, 2);
57 s->thrs_shutdown.hysteresis = max_t(u8, s->thrs_shutdown.hysteresis, 2);
58 }
59
60 /* must be called with alarm_program_lock taken ! */
61 void
62 nvkm_therm_sensor_set_threshold_state(struct nvkm_therm *obj,
63 enum nvkm_therm_thrs thrs,
64 enum nvkm_therm_thrs_state st)
65 {
66 struct nvkm_therm_priv *therm = container_of(obj, typeof(*therm), base);
67 therm->sensor.alarm_state[thrs] = st;
68 }
69
70 /* must be called with alarm_program_lock taken ! */
71 enum nvkm_therm_thrs_state
72 nvkm_therm_sensor_get_threshold_state(struct nvkm_therm *obj,
73 enum nvkm_therm_thrs thrs)
74 {
75 struct nvkm_therm_priv *therm = container_of(obj, typeof(*therm), base);
76 return therm->sensor.alarm_state[thrs];
77 }
78
79 static void
80 nv_poweroff_work(struct work_struct *work)
81 {
82 orderly_poweroff(true);
83 kfree(work);
84 }
85
86 void
87 nvkm_therm_sensor_event(struct nvkm_therm *obj, enum nvkm_therm_thrs thrs,
88 enum nvkm_therm_thrs_direction dir)
89 {
90 struct nvkm_therm_priv *therm = container_of(obj, typeof(*therm), base);
91 struct nvkm_subdev *subdev = &therm->base.subdev;
92 bool active;
93 const char *thresolds[] = {
94 "fanboost", "downclock", "critical", "shutdown"
95 };
96 int temperature = therm->base.temp_get(&therm->base);
97
98 if (thrs < 0 || thrs > 3)
99 return;
100
101 if (dir == NVKM_THERM_THRS_FALLING)
102 nvkm_info(subdev,
103 "temperature (%i C) went below the '%s' threshold\n",
104 temperature, thresolds[thrs]);
105 else
106 nvkm_info(subdev, "temperature (%i C) hit the '%s' threshold\n",
107 temperature, thresolds[thrs]);
108
109 active = (dir == NVKM_THERM_THRS_RISING);
110 switch (thrs) {
111 case NVKM_THERM_THRS_FANBOOST:
112 if (active) {
113 nvkm_therm_fan_set(&therm->base, true, 100);
114 nvkm_therm_fan_mode(&therm->base, NVKM_THERM_CTRL_AUTO);
115 }
116 break;
117 case NVKM_THERM_THRS_DOWNCLOCK:
118 if (therm->emergency.downclock)
119 therm->emergency.downclock(&therm->base, active);
120 break;
121 case NVKM_THERM_THRS_CRITICAL:
122 if (therm->emergency.pause)
123 therm->emergency.pause(&therm->base, active);
124 break;
125 case NVKM_THERM_THRS_SHUTDOWN:
126 if (active) {
127 struct work_struct *work;
128
129 work = kmalloc(sizeof(*work), GFP_ATOMIC);
130 if (work) {
131 INIT_WORK(work, nv_poweroff_work);
132 schedule_work(work);
133 }
134 }
135 break;
136 case NVKM_THERM_THRS_NR:
137 break;
138 }
139
140 }
141
142 /* must be called with alarm_program_lock taken ! */
143 static void
144 nvkm_therm_threshold_hyst_polling(struct nvkm_therm *therm,
145 const struct nvbios_therm_threshold *thrs,
146 enum nvkm_therm_thrs thrs_name)
147 {
148 enum nvkm_therm_thrs_direction direction;
149 enum nvkm_therm_thrs_state prev_state, new_state;
150 int temp = therm->temp_get(therm);
151
152 prev_state = nvkm_therm_sensor_get_threshold_state(therm, thrs_name);
153
154 if (temp >= thrs->temp && prev_state == NVKM_THERM_THRS_LOWER) {
155 direction = NVKM_THERM_THRS_RISING;
156 new_state = NVKM_THERM_THRS_HIGHER;
157 } else if (temp <= thrs->temp - thrs->hysteresis &&
158 prev_state == NVKM_THERM_THRS_HIGHER) {
159 direction = NVKM_THERM_THRS_FALLING;
160 new_state = NVKM_THERM_THRS_LOWER;
161 } else
162 return; /* nothing to do */
163
164 nvkm_therm_sensor_set_threshold_state(therm, thrs_name, new_state);
165 nvkm_therm_sensor_event(therm, thrs_name, direction);
166 }
167
168 static void
169 alarm_timer_callback(struct nvkm_alarm *alarm)
170 {
171 struct nvkm_therm_priv *therm =
172 container_of(alarm, struct nvkm_therm_priv, sensor.therm_poll_alarm);
173 struct nvbios_therm_sensor *sensor = &therm->bios_sensor;
174 struct nvkm_timer *tmr = nvkm_timer(therm);
175 unsigned long flags;
176
177 spin_lock_irqsave(&therm->sensor.alarm_program_lock, flags);
178
179 nvkm_therm_threshold_hyst_polling(&therm->base, &sensor->thrs_fan_boost,
180 NVKM_THERM_THRS_FANBOOST);
181
182 nvkm_therm_threshold_hyst_polling(&therm->base,
183 &sensor->thrs_down_clock,
184 NVKM_THERM_THRS_DOWNCLOCK);
185
186 nvkm_therm_threshold_hyst_polling(&therm->base, &sensor->thrs_critical,
187 NVKM_THERM_THRS_CRITICAL);
188
189 nvkm_therm_threshold_hyst_polling(&therm->base, &sensor->thrs_shutdown,
190 NVKM_THERM_THRS_SHUTDOWN);
191
192 spin_unlock_irqrestore(&therm->sensor.alarm_program_lock, flags);
193
194 /* schedule the next poll in one second */
195 if (therm->base.temp_get(&therm->base) >= 0 && list_empty(&alarm->head))
196 tmr->alarm(tmr, 1000000000ULL, alarm);
197 }
198
199 void
200 nvkm_therm_program_alarms_polling(struct nvkm_therm *obj)
201 {
202 struct nvkm_therm_priv *therm = container_of(obj, typeof(*therm), base);
203 struct nvbios_therm_sensor *sensor = &therm->bios_sensor;
204
205 nvkm_debug(&therm->base.subdev,
206 "programmed thresholds [ %d(%d), %d(%d), %d(%d), %d(%d) ]\n",
207 sensor->thrs_fan_boost.temp,
208 sensor->thrs_fan_boost.hysteresis,
209 sensor->thrs_down_clock.temp,
210 sensor->thrs_down_clock.hysteresis,
211 sensor->thrs_critical.temp,
212 sensor->thrs_critical.hysteresis,
213 sensor->thrs_shutdown.temp,
214 sensor->thrs_shutdown.hysteresis);
215
216 alarm_timer_callback(&therm->sensor.therm_poll_alarm);
217 }
218
219 int
220 nvkm_therm_sensor_init(struct nvkm_therm *obj)
221 {
222 struct nvkm_therm_priv *therm = container_of(obj, typeof(*therm), base);
223 therm->sensor.program_alarms(&therm->base);
224 return 0;
225 }
226
227 int
228 nvkm_therm_sensor_fini(struct nvkm_therm *obj, bool suspend)
229 {
230 struct nvkm_therm_priv *therm = container_of(obj, typeof(*therm), base);
231 struct nvkm_timer *tmr = nvkm_timer(therm);
232
233 if (suspend)
234 tmr->alarm_cancel(tmr, &therm->sensor.therm_poll_alarm);
235 return 0;
236 }
237
238 void
239 nvkm_therm_sensor_preinit(struct nvkm_therm *therm)
240 {
241 const char *sensor_avail = "yes";
242
243 if (therm->temp_get(therm) < 0)
244 sensor_avail = "no";
245
246 nvkm_debug(&therm->subdev, "internal sensor: %s\n", sensor_avail);
247 }
248
249 int
250 nvkm_therm_sensor_ctor(struct nvkm_therm *obj)
251 {
252 struct nvkm_therm_priv *therm = container_of(obj, typeof(*therm), base);
253 struct nvkm_subdev *subdev = &therm->base.subdev;
254 struct nvkm_bios *bios = nvkm_bios(therm);
255
256 nvkm_alarm_init(&therm->sensor.therm_poll_alarm, alarm_timer_callback);
257
258 nvkm_therm_temp_set_defaults(&therm->base);
259 if (nvbios_therm_sensor_parse(bios, NVBIOS_THERM_DOMAIN_CORE,
260 &therm->bios_sensor))
261 nvkm_error(subdev, "nvbios_therm_sensor_parse failed\n");
262 nvkm_therm_temp_safety_checks(&therm->base);
263
264 return 0;
265 }
This page took 0.039293 seconds and 6 git commands to generate.