Skip to content

Commit bf18319

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 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 <tcrawford@system76.com>
1 parent 88173de commit bf18319

9 files changed

Lines changed: 170 additions & 3 deletions

File tree

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
@@ -6,6 +6,7 @@ use alloc::{
66
vec,
77
};
88
use core::convert::TryFrom;
9+
use core::fmt;
910

1011
use crate::{
1112
Access,
@@ -39,6 +40,8 @@ enum Cmd {
3940
SetNoInput = 19,
4041
SecurityGet = 20,
4142
SecuritySet = 21,
43+
FanGetMode = 22,
44+
FanSetMode = 23,
4245
}
4346

4447
const CMD_SPI_FLAG_READ: u8 = 1 << 0;
@@ -73,6 +76,42 @@ impl TryFrom<u8> for SecurityState {
7376
}
7477
}
7578

79+
#[derive(Clone, Copy, Default, Debug, Eq, PartialEq)]
80+
#[cfg_attr(feature = "std", derive(clap::ValueEnum))]
81+
#[repr(u8)]
82+
pub enum FanMode {
83+
/// EC control
84+
#[default]
85+
Auto = 0,
86+
/// Host control via target PWM
87+
Pwm = 1,
88+
/// Host control via target RPM
89+
Rpm = 2,
90+
}
91+
92+
impl fmt::Display for FanMode {
93+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
94+
match self {
95+
Self::Auto => write!(f, "auto"),
96+
Self::Pwm => write!(f, "pwm"),
97+
Self::Rpm => write!(f, "rpm"),
98+
}
99+
}
100+
}
101+
102+
impl TryFrom<u8> for FanMode {
103+
type Error = Error;
104+
105+
fn try_from(value: u8) -> Result<Self, Self::Error> {
106+
match value {
107+
0 => Ok(Self::Auto),
108+
1 => Ok(Self::Pwm),
109+
2 => Ok(Self::Rpm),
110+
_ => Err(Error::Verify),
111+
}
112+
}
113+
}
114+
76115
/// Run EC commands using a provided access method
77116
pub struct Ec<A: Access> {
78117
access: A,
@@ -327,6 +366,19 @@ impl<A: Access> Ec<A> {
327366
self.command(Cmd::SecuritySet, &mut data)
328367
}
329368

369+
/// Get fan control mode.
370+
pub unsafe fn fan_get_mode(&mut self) -> Result<FanMode, Error> {
371+
let mut data = [0];
372+
self.command(Cmd::FanGetMode, &mut data)?;
373+
FanMode::try_from(data[0])
374+
}
375+
376+
/// Set fan control mode.
377+
pub unsafe fn fan_set_mode(&mut self, mode: FanMode) -> Result<(), Error> {
378+
let mut data = [mode as u8];
379+
self.command(Cmd::FanSetMode, &mut data)
380+
}
381+
330382
pub fn into_dyn(self) -> Ec<Box<dyn Access>>
331383
where A: 'static {
332384
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: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -277,6 +277,17 @@ 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+
let mode = ec.fan_get_mode()?;
282+
println!("{}", mode);
283+
284+
Ok(())
285+
}
286+
287+
unsafe fn fan_set_mode(ec: &mut Ec<Box<dyn Access>>, mode: ectool::FanMode) -> Result<(), Error> {
288+
ec.fan_set_mode(mode)
289+
}
290+
280291
unsafe fn keymap_get(ec: &mut Ec<Box<dyn Access>>, layer: u8, output: u8, input: u8) -> Result<(), Error> {
281292
let value = ec.keymap_get(layer, output, input)?;
282293
println!("{:04X}", value);
@@ -320,6 +331,9 @@ enum SubCommand {
320331
index: u8,
321332
duty: Option<u8>,
322333
},
334+
FanMode {
335+
mode: Option<ectool::FanMode>,
336+
},
323337
Flash {
324338
path: String,
325339
},
@@ -456,6 +470,24 @@ fn main() {
456470
},
457471
}
458472
},
473+
SubCommand::FanMode { mode } => {
474+
match mode {
475+
Some(mode) => match unsafe { fan_set_mode(&mut ec, mode) } {
476+
Ok(()) => (),
477+
Err(err) => {
478+
eprintln!("failed to set fan mode {}: {:X?}", mode, err);
479+
process::exit(1);
480+
}
481+
},
482+
None => match unsafe { fan_get_mode(&mut ec) } {
483+
Ok(()) => (),
484+
Err(err) => {
485+
eprintln!("failed to get fan mode: {:X?}", err);
486+
process::exit(1);
487+
}
488+
},
489+
}
490+
},
459491
SubCommand::Flash { path } => {
460492
match unsafe { flash(&mut ec, &path, SpiTarget::Main) } {
461493
Ok(()) => (),

0 commit comments

Comments
 (0)