Skip to content

Commit 8ba926f

Browse files
crawfxrdjackpot51
authored andcommitted
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 PWM via SMFI already existed, but would not work as the value would simply be overwritten by the EC. RPM target is not supported. Signed-off-by: Tim Crawford <[email protected]>
1 parent a038042 commit 8ba926f

File tree

9 files changed

+168
-3
lines changed

9 files changed

+168
-3
lines changed

src/board/system76/common/acpi.c

Lines changed: 27 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,29 @@ 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_MODE_PWM) {
233+
fan1_pwm_target = data;
234+
}
235+
break;
236+
237+
#ifdef FAN2_PWM
238+
case 0xCF:
239+
if (fan_get_mode() == FAN_MODE_PWM) {
240+
fan2_pwm_target = data;
241+
}
242+
break;
243+
#endif
244+
245+
case 0xD4:
246+
switch (data) {
247+
case FAN_MODE_AUTO:
248+
case FAN_MODE_PWM:
249+
fan_set_mode(data);
250+
break;
251+
}
252+
break;
253+
227254
#if HAVE_LED_AIRPLANE_N
228255
// Airplane mode LED
229256
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_MODE_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: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,15 @@ struct Fan {
2121
uint8_t pwm_min;
2222
};
2323

24+
enum FanMode {
25+
// EC control
26+
FAN_MODE_AUTO = 0,
27+
// Host control via target PWM
28+
FAN_MODE_PWM = 1,
29+
// Host control via target RPM
30+
FAN_MODE_RPM = 2,
31+
};
32+
2433
extern bool fan_max;
2534

2635
extern uint8_t fan1_pwm_actual;
@@ -34,4 +43,7 @@ void fan_reset(void);
3443
void fan_update_duty(void);
3544
void fan_update_target(void);
3645

46+
enum FanMode fan_get_mode(void);
47+
void fan_set_mode(enum FanMode);
48+
3749
#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_MODE_AUTO) {
178+
fan_update_target();
179+
}
177180
}
178181

179182
// Idle until next timer interrupt

src/board/system76/common/smfi.c

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,10 @@ static enum Result cmd_fan_get_pwm(void) {
140140
}
141141

142142
static enum Result cmd_fan_set_pwm(void) {
143+
if (fan_get_mode() != FAN_MODE_PWM) {
144+
return RES_ERR;
145+
}
146+
143147
switch (smfi_cmd[SMFI_CMD_DATA]) {
144148
case 1:
145149
// Set duty cycle of FAN1
@@ -157,6 +161,24 @@ static enum Result cmd_fan_set_pwm(void) {
157161
return RES_ERR;
158162
}
159163

164+
static enum Result cmd_fan_get_mode(void) {
165+
smfi_cmd[SMFI_CMD_DATA] = fan_get_mode();
166+
return RES_OK;
167+
}
168+
169+
static enum Result cmd_fan_set_mode(void) {
170+
enum FanMode mode = smfi_cmd[SMFI_CMD_DATA];
171+
172+
switch (mode) {
173+
case FAN_MODE_AUTO:
174+
case FAN_MODE_PWM:
175+
fan_set_mode(mode);
176+
return RES_OK;
177+
}
178+
179+
return RES_ERR;
180+
}
181+
160182
static enum Result cmd_keymap_get(void) {
161183
int16_t layer = smfi_cmd[SMFI_CMD_DATA];
162184
int16_t output = smfi_cmd[SMFI_CMD_DATA + 1];
@@ -421,6 +443,13 @@ void smfi_event(void) {
421443
break;
422444
#endif // CONFIG_SECURITY
423445

446+
case CMD_FAN_GET_MODE:
447+
smfi_cmd[SMFI_CMD_RES] = cmd_fan_get_mode();
448+
break;
449+
case CMD_FAN_SET_MODE:
450+
smfi_cmd[SMFI_CMD_RES] = cmd_fan_set_mode();
451+
break;
452+
424453
#endif // !defined(__SCRATCH__)
425454
case CMD_SPI:
426455
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: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
#[cfg(not(feature = "std"))]
44
use alloc::{boxed::Box, vec};
55
use core::convert::TryFrom;
6+
use core::fmt;
67

78
use crate::{Access, Error, Spi, SpiTarget};
89

@@ -31,6 +32,8 @@ enum Cmd {
3132
SetNoInput = 19,
3233
SecurityGet = 20,
3334
SecuritySet = 21,
35+
FanGetMode = 22,
36+
FanSetMode = 23,
3437
}
3538

3639
const CMD_SPI_FLAG_READ: u8 = 1 << 0;
@@ -65,6 +68,42 @@ impl TryFrom<u8> for SecurityState {
6568
}
6669
}
6770

71+
#[derive(Clone, Copy, Default, Debug, Eq, PartialEq)]
72+
#[cfg_attr(feature = "std", derive(clap::ValueEnum))]
73+
#[repr(u8)]
74+
pub enum FanMode {
75+
/// EC control
76+
#[default]
77+
Auto = 0,
78+
/// Host control via target PWM
79+
Pwm = 1,
80+
/// Host control via target RPM
81+
Rpm = 2,
82+
}
83+
84+
impl fmt::Display for FanMode {
85+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
86+
match self {
87+
Self::Auto => write!(f, "auto"),
88+
Self::Pwm => write!(f, "pwm"),
89+
Self::Rpm => write!(f, "rpm"),
90+
}
91+
}
92+
}
93+
94+
impl TryFrom<u8> for FanMode {
95+
type Error = Error;
96+
97+
fn try_from(value: u8) -> Result<Self, Self::Error> {
98+
match value {
99+
0 => Ok(Self::Auto),
100+
1 => Ok(Self::Pwm),
101+
2 => Ok(Self::Rpm),
102+
_ => Err(Error::Verify),
103+
}
104+
}
105+
}
106+
68107
/// Run EC commands using a provided access method
69108
pub struct Ec<A: Access> {
70109
access: A,
@@ -275,6 +314,19 @@ impl<A: Access> Ec<A> {
275314
self.command(Cmd::SecuritySet, &mut data)
276315
}
277316

317+
/// Get fan control mode.
318+
pub unsafe fn fan_get_mode(&mut self) -> Result<FanMode, Error> {
319+
let mut data = [0];
320+
self.command(Cmd::FanGetMode, &mut data)?;
321+
FanMode::try_from(data[0])
322+
}
323+
324+
/// Set fan control mode.
325+
pub unsafe fn fan_set_mode(&mut self, mode: FanMode) -> Result<(), Error> {
326+
let mut data = [mode as u8];
327+
self.command(Cmd::FanSetMode, &mut data)
328+
}
329+
278330
pub fn into_dyn(self) -> Ec<Box<dyn Access>>
279331
where
280332
A: 'static,

tool/src/lib.rs

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

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

3030
pub use self::error::Error;

tool/src/main.rs

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -281,6 +281,17 @@ unsafe fn fan_set_pwm(ec: &mut Ec<Box<dyn Access>>, index: u8, duty: u8) -> Resu
281281
ec.fan_set_pwm(index, duty)
282282
}
283283

284+
unsafe fn fan_get_mode(ec: &mut Ec<Box<dyn Access>>) -> Result<(), Error> {
285+
let mode = ec.fan_get_mode()?;
286+
println!("{}", mode);
287+
288+
Ok(())
289+
}
290+
291+
unsafe fn fan_set_mode(ec: &mut Ec<Box<dyn Access>>, mode: ectool::FanMode) -> Result<(), Error> {
292+
ec.fan_set_mode(mode)
293+
}
294+
284295
unsafe fn keymap_get(
285296
ec: &mut Ec<Box<dyn Access>>,
286297
layer: u8,
@@ -334,6 +345,9 @@ enum SubCommand {
334345
index: u8,
335346
duty: Option<u8>,
336347
},
348+
FanMode {
349+
mode: Option<ectool::FanMode>,
350+
},
337351
Flash {
338352
path: String,
339353
},
@@ -448,6 +462,22 @@ fn main() {
448462
process::exit(1);
449463
}
450464
},
465+
SubCommand::FanMode { mode } => match mode {
466+
Some(mode) => match unsafe { fan_set_mode(&mut ec, mode) } {
467+
Ok(()) => (),
468+
Err(err) => {
469+
eprintln!("failed to set fan mode {}: {:X?}", mode, err);
470+
process::exit(1);
471+
}
472+
},
473+
None => match unsafe { fan_get_mode(&mut ec) } {
474+
Ok(()) => (),
475+
Err(err) => {
476+
eprintln!("failed to get fan mode: {:X?}", err);
477+
process::exit(1);
478+
}
479+
},
480+
},
451481
SubCommand::FanPwm { index, duty } => match duty {
452482
Some(duty) => match unsafe { fan_set_pwm(&mut ec, index, duty) } {
453483
Ok(()) => (),

0 commit comments

Comments
 (0)