Skip to content

Commit 420cd94

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. RPM target is still not supported. Signed-off-by: Tim Crawford <[email protected]>
1 parent 88173de commit 420cd94

File tree

9 files changed

+158
-5
lines changed

9 files changed

+158
-5
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_MANUAL) {
233+
fan1_pwm_target = data;
234+
}
235+
break;
236+
237+
#ifdef FAN2_PWM
238+
case 0xCF:
239+
if (fan_get_mode() == FAN_MANUAL) {
240+
fan2_pwm_target = data;
241+
}
242+
break;
243+
#endif
244+
245+
case 0xD4:
246+
switch (data) {
247+
case FAN_AUTO:
248+
case FAN_MANUAL:
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_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: 48 additions & 2 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,36 @@ 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+
#[default]
84+
Auto = 0,
85+
Manual = 1,
86+
}
87+
88+
impl fmt::Display for FanMode {
89+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
90+
match self {
91+
Self::Auto => write!(f, "auto"),
92+
Self::Manual => write!(f, "manual"),
93+
}
94+
}
95+
}
96+
97+
impl TryFrom<u8> for FanMode {
98+
type Error = Error;
99+
100+
fn try_from(value: u8) -> Result<Self, Self::Error> {
101+
match value {
102+
0 => Ok(Self::Auto),
103+
1 => Ok(Self::Manual),
104+
_ => Err(Error::Verify),
105+
}
106+
}
107+
}
108+
76109
/// Run EC commands using a provided access method
77110
pub struct Ec<A: Access> {
78111
access: A,
@@ -186,7 +219,7 @@ impl<A: Access> Ec<A> {
186219
self.command(Cmd::Reset, &mut [])
187220
}
188221

189-
/// Read fan duty cycle by fan index
222+
/// Read raw fan duty cycle by fan index
190223
pub unsafe fn fan_get_pwm(&mut self, index: u8) -> Result<u8, Error> {
191224
let mut data = [
192225
index,
@@ -196,7 +229,7 @@ impl<A: Access> Ec<A> {
196229
Ok(data[1])
197230
}
198231

199-
/// Set fan duty cycle by fan index
232+
/// Set raw fan duty cycle by fan index
200233
pub unsafe fn fan_set_pwm(&mut self, index: u8, duty: u8) -> Result<(), Error> {
201234
let mut data = [
202235
index,
@@ -327,6 +360,19 @@ impl<A: Access> Ec<A> {
327360
self.command(Cmd::SecuritySet, &mut data)
328361
}
329362

363+
/// Get fan control mode.
364+
pub unsafe fn fan_get_mode(&mut self) -> Result<FanMode, Error> {
365+
let mut data = [0];
366+
self.command(Cmd::FanGetMode, &mut data)?;
367+
FanMode::try_from(data[0])
368+
}
369+
370+
/// Set fan control mode.
371+
pub unsafe fn fan_set_mode(&mut self, mode: FanMode) -> Result<(), Error> {
372+
let mut data = [mode as u8];
373+
self.command(Cmd::FanSetMode, &mut data)
374+
}
375+
330376
pub fn into_dyn(self) -> Ec<Box<dyn Access>>
331377
where A: 'static {
332378
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)