summaryrefslogtreecommitdiffstats
path: root/system/system76-power/nvme_temp.patch
blob: c0531cd38aa6a4c82f43ed5521bda73426809f71 (plain)
diff --git a/src/fan.rs b/src/fan.rs
index 03fdadd..4b3db3b 100644
--- a/src/fan.rs
+++ b/src/fan.rs
@@ -23,9 +23,11 @@ pub enum FanDaemonError {
 
 pub struct FanDaemon {
     curve:             FanCurve,
+    curve_nvme:        FanCurve,
     amdgpus:           Vec<HwMon>,
     platforms:         Vec<HwMon>,
     cpus:              Vec<HwMon>,
+    nvmes:             Vec<HwMon>,
     nvidia_exists:     bool,
     displayed_warning: Cell<bool>,
 }
@@ -41,9 +43,11 @@ impl FanDaemon {
                 "thelio-massive-b1" => FanCurve::xeon(),
                 _ => FanCurve::standard(),
             },
+            curve_nvme: FanCurve::nvme(),
             amdgpus: Vec::new(),
             platforms: Vec::new(),
             cpus: Vec::new(),
+            nvmes: Vec::new(),
             nvidia_exists,
             displayed_warning: Cell::new(false),
         };
@@ -60,6 +64,7 @@ impl FanDaemon {
         self.amdgpus.clear();
         self.platforms.clear();
         self.cpus.clear();
+        self.nvmes.clear();
 
         for hwmon in HwMon::all().map_err(FanDaemonError::HwmonDevices)? {
             if let Ok(name) = hwmon.name() {
@@ -70,6 +75,7 @@ impl FanDaemon {
                     "system76" => (), // TODO: Support laptops
                     "system76_io" | "system76_thelio_io" => self.platforms.push(hwmon),
                     "coretemp" | "k10temp" => self.cpus.push(hwmon),
+                    "nvme" => self.nvmes.push(hwmon),
                     _ => (),
                 }
             }
@@ -83,6 +89,10 @@ impl FanDaemon {
             return Err(FanDaemonError::CpuHwmonNotFound);
         }
 
+        if self.nvmes.is_empty() {
+            // No error.
+        }
+
         Ok(())
     }
 
@@ -128,6 +138,25 @@ impl FanDaemon {
         temp_opt
     }
 
+    /// Get the maximum measured temperature from any NVME on the system, in thousandths of a
+    /// Celsius. Thousandths celsius is the standard Linux hwmon temperature unit.
+    pub fn get_nvme_temp(&self) -> Option<u32> {
+        self
+            .nvmes
+            .iter()
+            .filter_map(|sensor| sensor.temp(1).ok())
+            .filter_map(|temp| temp.input().ok())
+            .fold(None, |mut temp_opt, input| {
+                // Assume temperatures are always above freezing
+                if temp_opt.map_or(true, |x| input as u32 > x) {
+                    log::debug!("highest hwmon nvme temp: {}", input);
+                    temp_opt = Some(input as u32);
+                }
+
+                temp_opt
+            })
+    }
+
     /// Get the correct duty cycle for a temperature in thousandths Celsius, from 0 to 255
     /// Thousandths celsius is the standard Linux hwmon temperature unit
     /// 0 to 255 is the standard Linux hwmon pwm unit
@@ -137,6 +166,15 @@ impl FanDaemon {
             .map(|duty| (((u32::from(duty)) * 255) / 10_000) as u8)
     }
 
+    /// Get the correct duty cycle for an NVME temperature in thousandths Celsius, from 0 to 255
+    /// Thousandths celsius is the standard Linux hwmon temperature unit
+    /// 0 to 255 is the standard Linux hwmon pwm unit
+    pub fn get_nvme_duty(&self, temp: u32) -> Option<u8> {
+        self.curve_nvme
+            .get_duty((temp / 10) as i16)
+            .map(|duty| (((u32::from(duty)) * 255) / 10_000) as u8)
+    }
+
     /// Set the current duty cycle, from 0 to 255
     /// 0 to 255 is the standard Linux hwmon pwm unit
     pub fn set_duty(&self, duty_opt: Option<u8>) {
@@ -159,7 +197,20 @@ impl FanDaemon {
     /// Calculate the correct duty cycle and apply it to all fans
     pub fn step(&mut self) {
         if let Ok(()) = self.discover() {
-            self.set_duty(self.get_temp().and_then(|temp| self.get_duty(temp)));
+            self.set_duty({
+                let cpu_duty = self.get_temp().and_then(|temp| self.get_duty(temp));
+                log::debug!("cpu duty: {:?}", cpu_duty);
+                if self.nvmes.is_empty() {
+                    cpu_duty
+                } else {
+                    let nvme_duty = self.get_nvme_temp().and_then(|temp| self.get_nvme_duty(temp));
+                    log::debug!("nvme duty: {:?}", nvme_duty);
+                    match(cpu_duty, nvme_duty) {
+                        (Some(cpu_duty), Some(nvme_duty)) => Some(cmp::max(cpu_duty, nvme_duty)),
+                        _ => None,
+                    }
+                }
+            });
         }
     }
 }
@@ -282,6 +333,15 @@ impl FanCurve {
             .append(78_00, 100_00)
     }
 
+    /// Fan curve for NVME drives
+    pub fn nvme() -> Self {
+        Self::default()
+            .append(00_00, 00_00)
+            .append(60_00, 00_00)
+            .append(65_00, 70_00)
+            .append(68_00, 100_00)
+    }
+
     pub fn get_duty(&self, temp: i16) -> Option<u16> {
         // If the temp is less than the first point, return the first point duty
         if let Some(first) = self.points.first() {