Skip to content

Commit

Permalink
trace MAX5970 status/faults/max current/max voltage on V12 rails (#1560)
Browse files Browse the repository at this point in the history
  • Loading branch information
bcantrill committed Nov 6, 2023
1 parent c5c5e44 commit 08f5a25
Show file tree
Hide file tree
Showing 2 changed files with 205 additions and 82 deletions.
215 changes: 133 additions & 82 deletions drv/i2c-devices/src/max5970.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<u8> {
//
// 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,
Expand All @@ -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<Amperes, ResponseCode> {
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<Volts, ResponseCode> {
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<Amperes, ResponseCode> {
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<u8, ResponseCode> {
self.read_reg(Register::status0)
}
}

impl Validate<ResponseCode> for Max5970 {
Expand All @@ -252,99 +370,32 @@ impl Validate<ResponseCode> for Max5970 {

impl VoltageSensor<ResponseCode> for Max5970 {
fn read_vout(&self) -> Result<Volts, ResponseCode> {
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<ResponseCode> for Max5970 {
fn read_iout(&self) -> Result<Amperes, ResponseCode> {
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)?,
)
}
}
72 changes: 72 additions & 0 deletions task/power/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
}

Expand Down Expand Up @@ -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) => {
Expand Down Expand Up @@ -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];

Expand All @@ -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 {
Expand Down Expand Up @@ -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
Expand Down

0 comments on commit 08f5a25

Please sign in to comment.