2 * Copyright 2012 Red Hat Inc.
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:
11 * The above copyright notice and this permission notice shall be included in
12 * all copies or substantial portions of the Software.
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.
27 #include <subdev/bios/fan.h>
28 #include <subdev/gpio.h>
29 #include <subdev/timer.h>
32 nvkm_fan_update(struct nvkm_fan
*fan
, bool immediate
, int target
)
34 struct nvkm_therm
*therm
= fan
->parent
;
35 struct nvkm_therm_priv
*priv
= (void *)therm
;
36 struct nvkm_timer
*ptimer
= nvkm_timer(priv
);
41 /* update target fan speed, restricting to allowed range */
42 spin_lock_irqsave(&fan
->lock
, flags
);
44 target
= fan
->percent
;
45 target
= max_t(u8
, target
, fan
->bios
.min_duty
);
46 target
= min_t(u8
, target
, fan
->bios
.max_duty
);
47 if (fan
->percent
!= target
) {
48 nv_debug(therm
, "FAN target: %d\n", target
);
49 fan
->percent
= target
;
52 /* check that we're not already at the target duty cycle */
53 duty
= fan
->get(therm
);
55 spin_unlock_irqrestore(&fan
->lock
, flags
);
59 /* smooth out the fanspeed increase/decrease */
60 if (!immediate
&& duty
>= 0) {
61 /* the constant "3" is a rough approximation taken from
63 * it is meant to bump the fan speed more incrementally
66 duty
= min(duty
+ 3, target
);
67 else if (duty
> target
)
68 duty
= max(duty
- 3, target
);
73 nv_debug(therm
, "FAN update: %d\n", duty
);
74 ret
= fan
->set(therm
, duty
);
76 spin_unlock_irqrestore(&fan
->lock
, flags
);
80 /* fan speed updated, drop the fan lock before grabbing the
81 * alarm-scheduling lock and risking a deadlock
83 spin_unlock_irqrestore(&fan
->lock
, flags
);
85 /* schedule next fan update, if not at target speed already */
86 if (list_empty(&fan
->alarm
.head
) && target
!= duty
) {
87 u16 bump_period
= fan
->bios
.bump_period
;
88 u16 slow_down_period
= fan
->bios
.slow_down_period
;
92 delay
= slow_down_period
;
93 else if (duty
== target
)
94 delay
= min(bump_period
, slow_down_period
) ;
98 ptimer
->alarm(ptimer
, delay
* 1000 * 1000, &fan
->alarm
);
105 nvkm_fan_alarm(struct nvkm_alarm
*alarm
)
107 struct nvkm_fan
*fan
= container_of(alarm
, struct nvkm_fan
, alarm
);
108 nvkm_fan_update(fan
, false, -1);
112 nvkm_therm_fan_get(struct nvkm_therm
*therm
)
114 struct nvkm_therm_priv
*priv
= (void *)therm
;
115 return priv
->fan
->get(therm
);
119 nvkm_therm_fan_set(struct nvkm_therm
*therm
, bool immediate
, int percent
)
121 struct nvkm_therm_priv
*priv
= (void *)therm
;
122 return nvkm_fan_update(priv
->fan
, immediate
, percent
);
126 nvkm_therm_fan_sense(struct nvkm_therm
*therm
)
128 struct nvkm_therm_priv
*priv
= (void *)therm
;
129 struct nvkm_timer
*ptimer
= nvkm_timer(therm
);
130 struct nvkm_gpio
*gpio
= nvkm_gpio(therm
);
131 u32 cycles
, cur
, prev
;
132 u64 start
, end
, tach
;
134 if (priv
->fan
->tach
.func
== DCB_GPIO_UNUSED
)
137 /* Time a complete rotation and extrapolate to RPM:
138 * When the fan spins, it changes the value of GPIO FAN_SENSE.
139 * We get 4 changes (0 -> 1 -> 0 -> 1) per complete rotation.
141 start
= ptimer
->read(ptimer
);
142 prev
= gpio
->get(gpio
, 0, priv
->fan
->tach
.func
, priv
->fan
->tach
.line
);
145 usleep_range(500, 1000); /* supports 0 < rpm < 7500 */
147 cur
= gpio
->get(gpio
, 0, priv
->fan
->tach
.func
, priv
->fan
->tach
.line
);
150 start
= ptimer
->read(ptimer
);
154 } while (cycles
< 5 && ptimer
->read(ptimer
) - start
< 250000000);
155 end
= ptimer
->read(ptimer
);
158 tach
= (u64
)60000000000ULL;
159 do_div(tach
, (end
- start
));
166 nvkm_therm_fan_user_get(struct nvkm_therm
*therm
)
168 return nvkm_therm_fan_get(therm
);
172 nvkm_therm_fan_user_set(struct nvkm_therm
*therm
, int percent
)
174 struct nvkm_therm_priv
*priv
= (void *)therm
;
176 if (priv
->mode
!= NVKM_THERM_CTRL_MANUAL
)
179 return nvkm_therm_fan_set(therm
, true, percent
);
183 nvkm_therm_fan_set_defaults(struct nvkm_therm
*therm
)
185 struct nvkm_therm_priv
*priv
= (void *)therm
;
187 priv
->fan
->bios
.pwm_freq
= 0;
188 priv
->fan
->bios
.min_duty
= 0;
189 priv
->fan
->bios
.max_duty
= 100;
190 priv
->fan
->bios
.bump_period
= 500;
191 priv
->fan
->bios
.slow_down_period
= 2000;
192 priv
->fan
->bios
.linear_min_temp
= 40;
193 priv
->fan
->bios
.linear_max_temp
= 85;
197 nvkm_therm_fan_safety_checks(struct nvkm_therm
*therm
)
199 struct nvkm_therm_priv
*priv
= (void *)therm
;
201 if (priv
->fan
->bios
.min_duty
> 100)
202 priv
->fan
->bios
.min_duty
= 100;
203 if (priv
->fan
->bios
.max_duty
> 100)
204 priv
->fan
->bios
.max_duty
= 100;
206 if (priv
->fan
->bios
.min_duty
> priv
->fan
->bios
.max_duty
)
207 priv
->fan
->bios
.min_duty
= priv
->fan
->bios
.max_duty
;
211 nvkm_therm_fan_init(struct nvkm_therm
*therm
)
217 nvkm_therm_fan_fini(struct nvkm_therm
*therm
, bool suspend
)
219 struct nvkm_therm_priv
*priv
= (void *)therm
;
220 struct nvkm_timer
*ptimer
= nvkm_timer(therm
);
223 ptimer
->alarm_cancel(ptimer
, &priv
->fan
->alarm
);
228 nvkm_therm_fan_ctor(struct nvkm_therm
*therm
)
230 struct nvkm_therm_priv
*priv
= (void *)therm
;
231 struct nvkm_gpio
*gpio
= nvkm_gpio(therm
);
232 struct nvkm_bios
*bios
= nvkm_bios(therm
);
233 struct dcb_gpio_func func
;
236 /* attempt to locate a drivable fan, and determine control method */
237 ret
= gpio
->find(gpio
, 0, DCB_GPIO_FAN
, 0xff, &func
);
239 /* FIXME: is this really the place to perform such checks ? */
240 if (func
.line
!= 16 && func
.log
[0] & DCB_GPIO_LOG_DIR_IN
) {
241 nv_debug(therm
, "GPIO_FAN is in input mode\n");
244 ret
= nvkm_fanpwm_create(therm
, &func
);
246 ret
= nvkm_fantog_create(therm
, &func
);
250 /* no controllable fan found, create a dummy fan module */
252 ret
= nvkm_fannil_create(therm
);
257 nv_info(therm
, "FAN control: %s\n", priv
->fan
->type
);
259 /* read the current speed, it is useful when resuming */
260 priv
->fan
->percent
= nvkm_therm_fan_get(therm
);
262 /* attempt to detect a tachometer connection */
263 ret
= gpio
->find(gpio
, 0, DCB_GPIO_FAN_SENSE
, 0xff, &priv
->fan
->tach
);
265 priv
->fan
->tach
.func
= DCB_GPIO_UNUSED
;
267 /* initialise fan bump/slow update handling */
268 priv
->fan
->parent
= therm
;
269 nvkm_alarm_init(&priv
->fan
->alarm
, nvkm_fan_alarm
);
270 spin_lock_init(&priv
->fan
->lock
);
272 /* other random init... */
273 nvkm_therm_fan_set_defaults(therm
);
274 nvbios_perf_fan_parse(bios
, &priv
->fan
->perf
);
275 if (!nvbios_fan_parse(bios
, &priv
->fan
->bios
)) {
276 nv_debug(therm
, "parsing the fan table failed\n");
277 if (nvbios_therm_fan_parse(bios
, &priv
->fan
->bios
))
278 nv_error(therm
, "parsing both fan tables failed\n");
280 nvkm_therm_fan_safety_checks(therm
);