From 08f5a258dd48728cfaae68fcc7fbd56c7359329c Mon Sep 17 00:00:00 2001 From: Bryan Cantrill Date: Mon, 6 Nov 2023 13:18:49 -0800 Subject: [PATCH] trace MAX5970 status/faults/max current/max voltage on V12 rails (#1560) --- drv/i2c-devices/src/max5970.rs | 215 ++++++++++++++++++++------------- task/power/src/main.rs | 72 +++++++++++ 2 files changed, 205 insertions(+), 82 deletions(-) diff --git a/drv/i2c-devices/src/max5970.rs b/drv/i2c-devices/src/max5970.rs index 3fc006e60..7f480e181 100644 --- a/drv/i2c-devices/src/max5970.rs +++ b/drv/i2c-devices/src/max5970.rs @@ -218,6 +218,58 @@ pub enum Register { cubf_ba_chx_i = 0x47, } +/// A newtype for the MON input range setting register +struct MonRange(u8); + +impl MonRange { + fn full_scale_voltage(&self, rail: u8) -> u8 { + let range = if rail == 0 { + self.0 & 0b11 + } else { + (self.0 >> 2) & 0b11 + }; + + match range { + 0b00 => 16, + 0b01 => 8, + 0b10 => 4, + 0b11 => 2, + _ => unreachable!(), + } + } +} + +/// A newtype for the fast-trip threshold maximum range register +struct Status2(u8); + +impl Status2 { + fn max_current_sense_range(&self, rail: u8) -> Option { + // + // The datasheet is enragingly inconsistent about how it refers to the + // channels. For most registers that have different settings for + // channels, it refers to them as Channel 1 and Channel 2 -- except + // for status2, which refers to Channel 0 and Channel 1. + // + let range = if rail == 0 { + self.0 & 0b11 + } else { + (self.0 >> 2) & 0b11 + }; + + // + // Our maximum current-sense range is 25mV, 50mV, or 100mV. (Contrary + // to the implication of the datasheet, there is no fourth maximum + // current-sense range.) + // + match range { + 0b00 => Some(100), + 0b01 => Some(50), + 0b10 => Some(25), + _ => None, + } + } +} + pub struct Max5970 { device: I2cDevice, rail: u8, @@ -240,6 +292,72 @@ impl Max5970 { pub fn i2c_device(&self) -> &I2cDevice { &self.device } + + fn convert_volts(&self, mon_range: MonRange, msb: u8, lsb: u8) -> Volts { + // + // The 10-bit value from the ADC is a fraction of the full-scale + // voltage setting. + // + let divisor = 1024.0 / mon_range.full_scale_voltage(self.rail) as f32; + + Volts(((((msb as u16) << 2) | (lsb as u16)) as f32) / divisor) + } + + fn convert_current( + &self, + status2: Status2, + msb: u8, + lsb: u8, + ) -> Result { + let millivolts = status2 + .max_current_sense_range(self.rail) + .ok_or(ResponseCode::BadDeviceState)?; + + // + // The 10-bit value from the ADC is a fraction of the maximum + // current-sense range. + // + let divisor = 1024.0 / millivolts as f32; + let delta = ((((msb as u16) << 2) | (lsb as u16)) as f32) / divisor; + + // + // We have the voltage drop across the current sense resistor; to + // determine current, we divide voltage by resistance (I = V / R). + // + Ok(Amperes(delta / self.rsense as f32)) + } + + pub fn max_vout(&self) -> Result { + let (msb_reg, lsb_reg) = if self.rail == 0 { + (Register::max_chx_mon_msb_ch1, Register::max_chx_mon_lsb_ch1) + } else { + (Register::max_chx_mon_msb_ch2, Register::max_chx_mon_lsb_ch2) + }; + + Ok(self.convert_volts( + MonRange(self.read_reg(Register::mon_range)?), + self.read_reg(msb_reg)?, + self.read_reg(lsb_reg)?, + )) + } + + pub fn max_iout(&self) -> Result { + let (msb_reg, lsb_reg) = if self.rail == 0 { + (Register::max_chx_cs_msb_ch1, Register::max_chx_cs_lsb_ch1) + } else { + (Register::max_chx_cs_msb_ch2, Register::max_chx_cs_lsb_ch2) + }; + + self.convert_current( + Status2(self.read_reg(Register::status2)?), + self.read_reg(msb_reg)?, + self.read_reg(lsb_reg)?, + ) + } + + pub fn status0(&self) -> Result { + self.read_reg(Register::status0) + } } impl Validate for Max5970 { @@ -252,99 +370,32 @@ impl Validate for Max5970 { impl VoltageSensor for Max5970 { fn read_vout(&self) -> Result { - let (msb, lsb) = if self.rail == 0 { - ( - self.read_reg(Register::adc_chx_mon_msb_ch1)?, - self.read_reg(Register::adc_chx_mon_lsb_ch1)?, - ) - } else { - ( - self.read_reg(Register::adc_chx_mon_msb_ch2)?, - self.read_reg(Register::adc_chx_mon_lsb_ch2)?, - ) - }; - - let mon_range = self.read_reg(Register::mon_range)?; - - let range = if self.rail == 0 { - mon_range & 0b11 + let (msb_reg, lsb_reg) = if self.rail == 0 { + (Register::adc_chx_mon_msb_ch1, Register::adc_chx_mon_lsb_ch1) } else { - (mon_range >> 2) & 0b11 - }; - - let volts = match range { - 0b00 => 16, - 0b01 => 8, - 0b10 => 4, - 0b11 => 2, - _ => unreachable!(), + (Register::adc_chx_mon_msb_ch2, Register::adc_chx_mon_lsb_ch2) }; - // - // The 10-bit value from the ADC is a fraction of the full-scale - // voltage setting. - // - let divisor = 1024.0 / volts as f32; - - Ok(Volts( - ((((msb as u16) << 2) | (lsb as u16)) as f32) / divisor, + Ok(self.convert_volts( + MonRange(self.read_reg(Register::mon_range)?), + self.read_reg(msb_reg)?, + self.read_reg(lsb_reg)?, )) } } impl CurrentSensor for Max5970 { fn read_iout(&self) -> Result { - let (msb, lsb) = if self.rail == 0 { - ( - self.read_reg(Register::adc_chx_cs_msb_ch1)?, - self.read_reg(Register::adc_chx_cs_lsb_ch1)?, - ) - } else { - ( - self.read_reg(Register::adc_chx_cs_msb_ch2)?, - self.read_reg(Register::adc_chx_cs_lsb_ch2)?, - ) - }; - - let status2 = self.read_reg(Register::status2)?; - - // - // The datasheet is enragingly inconsistent about how it refers to the - // channels. For most registers that have different settings for - // channels, it refers to them as Channel 1 and Channel 2 -- except - // for status2, which refers to Channel 0 and Channel 1. - // - let range = if self.rail == 0 { - status2 & 0b11 + let (msb_reg, lsb_reg) = if self.rail == 0 { + (Register::adc_chx_cs_msb_ch1, Register::adc_chx_cs_lsb_ch1) } else { - (status2 >> 2) & 0b11 - }; - - // - // Our maximum current-sense range is 25mV, 50mV, or 100mV. (Contrary - // to the implication of the datasheet, there is no fourth maximum - // current-sense range.) - // - let millivolts = match range { - 0b00 => 100, - 0b01 => 50, - 0b10 => 25, - _ => { - return Err(ResponseCode::BadDeviceState); - } + (Register::adc_chx_cs_msb_ch2, Register::adc_chx_cs_lsb_ch2) }; - // - // The 10-bit value from the ADC is a fraction of the maximum - // current-sense range. - // - let divisor = 1024.0 / millivolts as f32; - let delta = ((((msb as u16) << 2) | (lsb as u16)) as f32) / divisor; - - // - // We have the voltage drop across the current sense resistor; to - // determine current, we divide voltage by resistance (I = V / R). - // - Ok(Amperes(delta / self.rsense as f32)) + self.convert_current( + Status2(self.read_reg(Register::status2)?), + self.read_reg(msb_reg)?, + self.read_reg(lsb_reg)?, + ) } } diff --git a/task/power/src/main.rs b/task/power/src/main.rs index 60e8272d5..3b4aef316 100644 --- a/task/power/src/main.rs +++ b/task/power/src/main.rs @@ -38,6 +38,17 @@ use drv_i2c_devices::{ enum Trace { GotVersion(u32), GotAddr(u32), + Max5970 { + sensor: SensorId, + status0: u8, + status1: u8, + status3: u8, + fault0: u8, + fault1: u8, + fault2: u8, + max_current: f32, + max_voltage: f32, + }, None, } @@ -349,6 +360,54 @@ macro_rules! max5970_controller { }; } +fn trace_max5970(dev: &Max5970, sensor: SensorId) { + if let Ok(Volts(volts)) = dev.max_vout() { + use drv_i2c_devices::max5970::Register; + + // + // We want to *not* trace the 3.3V rails on the MAX5970 on the + // Sharkfin (U8), so anything that has either never powered on or + // never seen a voltage greater than ~4V we will not record. + // + if volts < 4.0 { + return; + } + + ringbuf_entry!(Trace::Max5970 { + sensor, + status0: match dev.read_reg(Register::status0) { + Ok(reg) => reg, + _ => return, + }, + status1: match dev.read_reg(Register::status1) { + Ok(reg) => reg, + _ => return, + }, + status3: match dev.read_reg(Register::status3) { + Ok(reg) => reg, + _ => return, + }, + fault0: match dev.read_reg(Register::fault0) { + Ok(reg) => reg, + _ => return, + }, + fault1: match dev.read_reg(Register::fault1) { + Ok(reg) => reg, + _ => return, + }, + fault2: match dev.read_reg(Register::fault2) { + Ok(reg) => reg, + _ => return, + }, + max_current: match dev.max_iout() { + Ok(Amperes(amps)) => amps, + _ => return, + }, + max_voltage: volts, + }); + } +} + #[allow(unused_macros)] macro_rules! mwocp68_controller { ($which:ident, $rail:ident, $state:ident) => { @@ -412,6 +471,7 @@ fn main() -> ! { i2c_task, sensor: sensor_api::Sensor::from(SENSOR.get_task_id()), devices: claim_devices(i2c_task), + fired: 0, }; let mut buffer = [0; idl::INCOMING_SIZE]; @@ -428,6 +488,7 @@ struct ServerImpl { i2c_task: TaskId, sensor: sensor_api::Sensor, devices: &'static mut [Device; bsp::CONTROLLER_CONFIG_LEN], + fired: u32, } impl ServerImpl { @@ -500,7 +561,18 @@ impl ServerImpl { } } } + + // + // Every 10 seconds, gather max5970 data + // + if self.fired % 10 == 0 { + if let Device::Max5970(dev) = dev { + trace_max5970(dev, c.current); + } + } } + + self.fired += 1; } /// Find the BMR491 and return an `I2cDevice` handle