diff --git a/src/board/system76/common/acpi.c b/src/board/system76/common/acpi.c index 132dd6cdc..039653b5e 100644 --- a/src/board/system76/common/acpi.c +++ b/src/board/system76/common/acpi.c @@ -177,6 +177,10 @@ uint8_t acpi_read(uint8_t addr) { ACPI_16(0xD2, fan2_rpm); #endif // FAN2_PWM + case 0xD4: + data = fan_get_mode(); + break; + #if HAVE_LED_AIRPLANE_N // Airplane mode LED case 0xD9: @@ -224,6 +228,29 @@ void acpi_write(uint8_t addr, uint8_t data) { (void)battery_save_thresholds(); break; + case 0xCE: + if (fan_get_mode() == FAN_MANUAL) { + fan1_pwm_target = data; + } + break; + +#ifdef FAN2_PWM + case 0xCF: + if (fan_get_mode() == FAN_MANUAL) { + fan2_pwm_target = data; + } + break; +#endif + + case 0xD4: + switch (data) { + case FAN_AUTO: + case FAN_MANUAL: + fan_set_mode(data); + break; + } + break; + #if HAVE_LED_AIRPLANE_N // Airplane mode LED case 0xD9: diff --git a/src/board/system76/common/fan.c b/src/board/system76/common/fan.c index 17462c862..f116ff947 100644 --- a/src/board/system76/common/fan.c +++ b/src/board/system76/common/fan.c @@ -12,6 +12,7 @@ #endif bool fan_max = false; +static enum FanMode fan_mode = FAN_AUTO; uint8_t fan1_pwm_actual = 0; uint8_t fan1_pwm_target = 0; @@ -261,3 +262,11 @@ void fan_update_duty(void) { fan2_rpm = fan_get_tach1_rpm(); #endif } + +enum FanMode fan_get_mode(void) { + return fan_mode; +} + +void fan_set_mode(enum FanMode mode) { + fan_mode = mode; +} diff --git a/src/board/system76/common/include/board/fan.h b/src/board/system76/common/include/board/fan.h index d06171528..d984d954f 100644 --- a/src/board/system76/common/include/board/fan.h +++ b/src/board/system76/common/include/board/fan.h @@ -21,6 +21,11 @@ struct Fan { uint8_t pwm_min; }; +enum FanMode { + FAN_AUTO = 0, + FAN_MANUAL = 1, +}; + extern bool fan_max; extern uint8_t fan1_pwm_actual; @@ -34,4 +39,7 @@ void fan_reset(void); void fan_update_duty(void); void fan_update_target(void); +enum FanMode fan_get_mode(void); +void fan_set_mode(enum FanMode); + #endif // _BOARD_FAN_H diff --git a/src/board/system76/common/main.c b/src/board/system76/common/main.c index 5c9240a18..d2f3835b8 100644 --- a/src/board/system76/common/main.c +++ b/src/board/system76/common/main.c @@ -173,7 +173,10 @@ void main(void) { last_time_1sec = time; battery_event(); - fan_update_target(); + + if (fan_get_mode() == FAN_AUTO) { + fan_update_target(); + } } // Idle until next timer interrupt diff --git a/src/board/system76/common/smfi.c b/src/board/system76/common/smfi.c index 938db59b1..486479d20 100644 --- a/src/board/system76/common/smfi.c +++ b/src/board/system76/common/smfi.c @@ -157,6 +157,24 @@ static enum Result cmd_fan_set_pwm(void) { return RES_ERR; } +static enum Result cmd_fan_get_mode(void) { + smfi_cmd[SMFI_CMD_DATA] = fan_get_mode(); + return RES_OK; +} + +static enum Result cmd_fan_set_mode(void) { + enum FanMode mode = smfi_cmd[SMFI_CMD_DATA]; + + switch (mode) { + case FAN_AUTO: + case FAN_MANUAL: + fan_set_mode(mode); + return RES_OK; + } + + return RES_ERR; +} + static enum Result cmd_keymap_get(void) { int16_t layer = smfi_cmd[SMFI_CMD_DATA]; int16_t output = smfi_cmd[SMFI_CMD_DATA + 1]; @@ -421,6 +439,13 @@ void smfi_event(void) { break; #endif // CONFIG_SECURITY + case CMD_FAN_GET_MODE: + smfi_cmd[SMFI_CMD_RES] = cmd_fan_get_mode(); + break; + case CMD_FAN_SET_MODE: + smfi_cmd[SMFI_CMD_RES] = cmd_fan_set_mode(); + break; + #endif // !defined(__SCRATCH__) case CMD_SPI: smfi_cmd[SMFI_CMD_RES] = cmd_spi(); diff --git a/src/common/include/common/command.h b/src/common/include/common/command.h index 13b2e8730..0e17e51f4 100644 --- a/src/common/include/common/command.h +++ b/src/common/include/common/command.h @@ -50,7 +50,10 @@ enum Command { CMD_SECURITY_GET = 20, // Set security state CMD_SECURITY_SET = 21, - //TODO + // Get fan control mode + CMD_FAN_GET_MODE = 22, + // Set fan control mode + CMD_FAN_SET_MODE = 23, }; enum Result { diff --git a/tool/src/ec.rs b/tool/src/ec.rs index b53b0262a..2a411e268 100644 --- a/tool/src/ec.rs +++ b/tool/src/ec.rs @@ -6,6 +6,7 @@ use alloc::{ vec, }; use core::convert::TryFrom; +use core::fmt; use crate::{ Access, @@ -39,6 +40,8 @@ enum Cmd { SetNoInput = 19, SecurityGet = 20, SecuritySet = 21, + FanGetMode = 22, + FanSetMode = 23, } const CMD_SPI_FLAG_READ: u8 = 1 << 0; @@ -73,6 +76,36 @@ impl TryFrom for SecurityState { } } +#[derive(Clone, Copy, Default, Debug, Eq, PartialEq)] +#[cfg_attr(feature = "std", derive(clap::ValueEnum))] +#[repr(u8)] +pub enum FanMode { + #[default] + Auto = 0, + Manual = 1, +} + +impl fmt::Display for FanMode { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + Self::Auto => write!(f, "auto"), + Self::Manual => write!(f, "manual"), + } + } +} + +impl TryFrom for FanMode { + type Error = Error; + + fn try_from(value: u8) -> Result { + match value { + 0 => Ok(Self::Auto), + 1 => Ok(Self::Manual), + _ => Err(Error::Verify), + } + } +} + /// Run EC commands using a provided access method pub struct Ec { access: A, @@ -186,7 +219,7 @@ impl Ec { self.command(Cmd::Reset, &mut []) } - /// Read fan duty cycle by fan index + /// Read raw fan duty cycle by fan index pub unsafe fn fan_get_pwm(&mut self, index: u8) -> Result { let mut data = [ index, @@ -196,7 +229,7 @@ impl Ec { Ok(data[1]) } - /// Set fan duty cycle by fan index + /// Set raw fan duty cycle by fan index pub unsafe fn fan_set_pwm(&mut self, index: u8, duty: u8) -> Result<(), Error> { let mut data = [ index, @@ -327,6 +360,19 @@ impl Ec { self.command(Cmd::SecuritySet, &mut data) } + /// Get fan control mode. + pub unsafe fn fan_get_mode(&mut self) -> Result { + let mut data = [0]; + self.command(Cmd::FanGetMode, &mut data)?; + FanMode::try_from(data[0]) + } + + /// Set fan control mode. + pub unsafe fn fan_set_mode(&mut self, mode: FanMode) -> Result<(), Error> { + let mut data = [mode as u8]; + self.command(Cmd::FanSetMode, &mut data) + } + pub fn into_dyn(self) -> Ec> where A: 'static { Ec { diff --git a/tool/src/lib.rs b/tool/src/lib.rs index 5afcac464..150f3ee76 100644 --- a/tool/src/lib.rs +++ b/tool/src/lib.rs @@ -25,7 +25,7 @@ extern crate alloc; pub use self::access::*; mod access; -pub use self::ec::{Ec, SecurityState}; +pub use self::ec::{Ec, FanMode, SecurityState}; mod ec; pub use self::error::Error; diff --git a/tool/src/main.rs b/tool/src/main.rs index aac131af9..193d87752 100644 --- a/tool/src/main.rs +++ b/tool/src/main.rs @@ -277,6 +277,17 @@ unsafe fn fan_set_pwm(ec: &mut Ec>, index: u8, duty: u8) -> Resu ec.fan_set_pwm(index, duty) } +unsafe fn fan_get_mode(ec: &mut Ec>) -> Result<(), Error> { + let mode = ec.fan_get_mode()?; + println!("{}", mode); + + Ok(()) +} + +unsafe fn fan_set_mode(ec: &mut Ec>, mode: ectool::FanMode) -> Result<(), Error> { + ec.fan_set_mode(mode) +} + unsafe fn keymap_get(ec: &mut Ec>, layer: u8, output: u8, input: u8) -> Result<(), Error> { let value = ec.keymap_get(layer, output, input)?; println!("{:04X}", value); @@ -320,6 +331,9 @@ enum SubCommand { index: u8, duty: Option, }, + FanMode { + mode: Option, + }, Flash { path: String, }, @@ -456,6 +470,24 @@ fn main() { }, } }, + SubCommand::FanMode { mode } => { + match mode { + Some(mode) => match unsafe { fan_set_mode(&mut ec, mode) } { + Ok(()) => (), + Err(err) => { + eprintln!("failed to set fan mode {}: {:X?}", mode, err); + process::exit(1); + } + }, + None => match unsafe { fan_get_mode(&mut ec) } { + Ok(()) => (), + Err(err) => { + eprintln!("failed to get fan mode: {:X?}", err); + process::exit(1); + } + }, + } + }, SubCommand::Flash { path } => { match unsafe { flash(&mut ec, &path, SpiTarget::Main) } { Ok(()) => (),