Skip to content

Commit 935fdd0

Browse files
committed
Add manual fan control
Allow fan target duties to be set via ACPI or SMFI command. This will allow system firmware or OS to set fan duty, which can be used for testing or implementing user-defined fan tables. Setting via SMFI already existed, but would not work as the value would simply be overwritten by the EC. It keeps the behavior of using raw PWM duty values. Setting via ACPI takes a duty *percent* rather than a raw value, so that EC can calculate the value based on CTR config and not have to leak such details to the host firmware/OS. RPM target is still not supported. Signed-off-by: Tim Crawford <[email protected]>
1 parent 88173de commit 935fdd0

File tree

9 files changed

+126
-5
lines changed

9 files changed

+126
-5
lines changed

src/board/system76/common/acpi.c

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,10 @@ uint8_t acpi_read(uint8_t addr) {
177177
ACPI_16(0xD2, fan2_rpm);
178178
#endif // FAN2_PWM
179179

180+
case 0xD4:
181+
data = fan_get_mode();
182+
break;
183+
180184
#if HAVE_LED_AIRPLANE_N
181185
// Airplane mode LED
182186
case 0xD9:
@@ -224,6 +228,33 @@ void acpi_write(uint8_t addr, uint8_t data) {
224228
(void)battery_save_thresholds();
225229
break;
226230

231+
case 0xCE:
232+
if (fan_get_mode() == FAN_MANUAL) {
233+
if (data <= 100) {
234+
fan1_pwm_target = PWM_DUTY(data);
235+
}
236+
}
237+
break;
238+
239+
#ifdef FAN2_PWM
240+
case 0xCF:
241+
if (fan_get_mode() == FAN_MANUAL) {
242+
if (data <= 100) {
243+
fan2_pwm_target = PWM_DUTY(data);
244+
}
245+
}
246+
break;
247+
#endif
248+
249+
case 0xD4:
250+
switch (data) {
251+
case FAN_AUTO:
252+
case FAN_MANUAL:
253+
fan_set_mode(data);
254+
break;
255+
}
256+
break;
257+
227258
#if HAVE_LED_AIRPLANE_N
228259
// Airplane mode LED
229260
case 0xD9:

src/board/system76/common/fan.c

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
#endif
1313

1414
bool fan_max = false;
15+
static enum FanMode fan_mode = FAN_AUTO;
1516

1617
uint8_t fan1_pwm_actual = 0;
1718
uint8_t fan1_pwm_target = 0;
@@ -261,3 +262,11 @@ void fan_update_duty(void) {
261262
fan2_rpm = fan_get_tach1_rpm();
262263
#endif
263264
}
265+
266+
enum FanMode fan_get_mode(void) {
267+
return fan_mode;
268+
}
269+
270+
void fan_set_mode(enum FanMode mode) {
271+
fan_mode = mode;
272+
}

src/board/system76/common/include/board/fan.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,11 @@ struct Fan {
2121
uint8_t pwm_min;
2222
};
2323

24+
enum FanMode {
25+
FAN_AUTO = 0,
26+
FAN_MANUAL = 1,
27+
};
28+
2429
extern bool fan_max;
2530

2631
extern uint8_t fan1_pwm_actual;
@@ -34,4 +39,7 @@ void fan_reset(void);
3439
void fan_update_duty(void);
3540
void fan_update_target(void);
3641

42+
enum FanMode fan_get_mode(void);
43+
void fan_set_mode(enum FanMode);
44+
3745
#endif // _BOARD_FAN_H

src/board/system76/common/main.c

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -173,7 +173,10 @@ void main(void) {
173173
last_time_1sec = time;
174174

175175
battery_event();
176-
fan_update_target();
176+
177+
if (fan_get_mode() == FAN_AUTO) {
178+
fan_update_target();
179+
}
177180
}
178181

179182
// Idle until next timer interrupt

src/board/system76/common/smfi.c

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,24 @@ static enum Result cmd_fan_set_pwm(void) {
157157
return RES_ERR;
158158
}
159159

160+
static enum Result cmd_fan_get_mode(void) {
161+
smfi_cmd[SMFI_CMD_DATA] = fan_get_mode();
162+
return RES_OK;
163+
}
164+
165+
static enum Result cmd_fan_set_mode(void) {
166+
enum FanMode mode = smfi_cmd[SMFI_CMD_DATA];
167+
168+
switch (mode) {
169+
case FAN_AUTO:
170+
case FAN_MANUAL:
171+
fan_set_mode(mode);
172+
return RES_OK;
173+
}
174+
175+
return RES_ERR;
176+
}
177+
160178
static enum Result cmd_keymap_get(void) {
161179
int16_t layer = smfi_cmd[SMFI_CMD_DATA];
162180
int16_t output = smfi_cmd[SMFI_CMD_DATA + 1];
@@ -421,6 +439,13 @@ void smfi_event(void) {
421439
break;
422440
#endif // CONFIG_SECURITY
423441

442+
case CMD_FAN_GET_MODE:
443+
smfi_cmd[SMFI_CMD_RES] = cmd_fan_get_mode();
444+
break;
445+
case CMD_FAN_SET_MODE:
446+
smfi_cmd[SMFI_CMD_RES] = cmd_fan_set_mode();
447+
break;
448+
424449
#endif // !defined(__SCRATCH__)
425450
case CMD_SPI:
426451
smfi_cmd[SMFI_CMD_RES] = cmd_spi();

src/common/include/common/command.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,10 @@ enum Command {
5050
CMD_SECURITY_GET = 20,
5151
// Set security state
5252
CMD_SECURITY_SET = 21,
53-
//TODO
53+
// Get fan control mode
54+
CMD_FAN_GET_MODE = 22,
55+
// Set fan control mode
56+
CMD_FAN_SET_MODE = 23,
5457
};
5558

5659
enum Result {

tool/src/ec.rs

Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,8 @@ enum Cmd {
3939
SetNoInput = 19,
4040
SecurityGet = 20,
4141
SecuritySet = 21,
42+
FanGetMode = 22,
43+
FanSetMode = 23,
4244
}
4345

4446
const CMD_SPI_FLAG_READ: u8 = 1 << 0;
@@ -73,6 +75,25 @@ impl TryFrom<u8> for SecurityState {
7375
}
7476
}
7577

78+
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
79+
#[repr(u8)]
80+
pub enum FanMode {
81+
Auto = 0,
82+
Manual = 1,
83+
}
84+
85+
impl TryFrom<u8> for FanMode {
86+
type Error = Error;
87+
88+
fn try_from(value: u8) -> Result<Self, Self::Error> {
89+
match value {
90+
0 => Ok(Self::Auto),
91+
1 => Ok(Self::Manual),
92+
_ => Err(Error::Verify),
93+
}
94+
}
95+
}
96+
7697
/// Run EC commands using a provided access method
7798
pub struct Ec<A: Access> {
7899
access: A,
@@ -186,7 +207,7 @@ impl<A: Access> Ec<A> {
186207
self.command(Cmd::Reset, &mut [])
187208
}
188209

189-
/// Read fan duty cycle by fan index
210+
/// Read raw fan duty cycle by fan index
190211
pub unsafe fn fan_get_pwm(&mut self, index: u8) -> Result<u8, Error> {
191212
let mut data = [
192213
index,
@@ -196,7 +217,7 @@ impl<A: Access> Ec<A> {
196217
Ok(data[1])
197218
}
198219

199-
/// Set fan duty cycle by fan index
220+
/// Set raw fan duty cycle by fan index
200221
pub unsafe fn fan_set_pwm(&mut self, index: u8, duty: u8) -> Result<(), Error> {
201222
let mut data = [
202223
index,
@@ -327,6 +348,19 @@ impl<A: Access> Ec<A> {
327348
self.command(Cmd::SecuritySet, &mut data)
328349
}
329350

351+
/// Get fan control mode.
352+
pub unsafe fn fan_get_mode(&mut self) -> Result<FanMode, Error> {
353+
let mut data = [0];
354+
self.command(Cmd::FanGetMode, &mut data)?;
355+
FanMode::try_from(data[0])
356+
}
357+
358+
/// Set fan control mode.
359+
pub unsafe fn fan_set_mode(&mut self, mode: FanMode) -> Result<(), Error> {
360+
let mut data = [mode as u8];
361+
self.command(Cmd::FanSetMode, &mut data)
362+
}
363+
330364
pub fn into_dyn(self) -> Ec<Box<dyn Access>>
331365
where A: 'static {
332366
Ec {

tool/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ extern crate alloc;
2525
pub use self::access::*;
2626
mod access;
2727

28-
pub use self::ec::{Ec, SecurityState};
28+
pub use self::ec::{Ec, FanMode, SecurityState};
2929
mod ec;
3030

3131
pub use self::error::Error;

tool/src/main.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -277,6 +277,14 @@ unsafe fn fan_set_pwm(ec: &mut Ec<Box<dyn Access>>, index: u8, duty: u8) -> Resu
277277
ec.fan_set_pwm(index, duty)
278278
}
279279

280+
unsafe fn fan_get_mode(ec: &mut Ec<Box<dyn Access>>) -> Result<(), Error> {
281+
todo!()
282+
}
283+
284+
unsafe fn fan_set_mode(ec: &mut Ec<Box<dyn Access>>, mode: ectool::FanMode) -> Result<(), Error> {
285+
todo!()
286+
}
287+
280288
unsafe fn keymap_get(ec: &mut Ec<Box<dyn Access>>, layer: u8, output: u8, input: u8) -> Result<(), Error> {
281289
let value = ec.keymap_get(layer, output, input)?;
282290
println!("{:04X}", value);

0 commit comments

Comments
 (0)