Skip to content

Commit adbd445

Browse files
authored
cosmo_seq: mask STATUS_CML bit 1 to not get annoying pmbus alerts about it (#2520)
It turns out that PMBus devices like to set this bit bascially any time they saw any kind of I2C behavior that makes them feel uncomfortable. We probably don't want to produce an ereport every time an I2C Thing makes a VRM uncomfortable. So this commit masks out PMBus alerts for STATUS_CML's "other communication fault" bit, since it's annoying and not a big deal. This branch does a bit of extra refactoring on top of the change from #2511. Since we are now setting even more `SMBALERT_MASK`s in `initialize_pmbus_alerts`, I thought it would be nice to have some shared utilities for trying to do this.
1 parent 68ebd3f commit adbd445

3 files changed

Lines changed: 110 additions & 40 deletions

File tree

drv/cosmo-seq-server/src/vcore.rs

Lines changed: 57 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ use drv_i2c_api::ResponseCode;
1818
use drv_i2c_devices::raa229620a::{self, Raa229620A};
1919
use ereports::pwr::{PmbusAlert, PmbusStatus};
2020
use fixedstr::FixedStr;
21+
use pmbus::commands::raa229620a::STATUS_CML;
2122
use pmbus::commands::raa229620a::STATUS_IOUT;
2223
use pmbus::commands::raa229620a::STATUS_WORD;
2324
use ringbuf::*;
@@ -43,6 +44,7 @@ pub(crate) enum Rail {
4344
enum PmbusCmd {
4445
LoadLimit,
4546
SetStatusIoutMask,
47+
SetStatusCmlMask,
4648
ClearFaults,
4749
ReadVin,
4850
Status,
@@ -59,6 +61,9 @@ enum Trace {
5961
StatusIoutMaskSet {
6062
all_ok: bool,
6163
},
64+
StatusCmlMaskSet {
65+
all_ok: bool,
66+
},
6267
PmbusAlert {
6368
timestamp: u64,
6469
alerted: Vrms,
@@ -219,15 +224,10 @@ impl VCore {
219224
// if possible.
220225

221226
// Set our warn limit
222-
let mut all_ok = true;
223-
all_ok &= retry_i2c_txn(Rail::VddcrCpu0, PmbusCmd::LoadLimit, || {
224-
self.vddcr_cpu0.set_vin_uv_warn_limit(VCORE_UV_WARN_LIMIT)
225-
})
226-
.is_ok();
227-
all_ok &= retry_i2c_txn(Rail::VddcrCpu1, PmbusCmd::LoadLimit, || {
228-
self.vddcr_cpu1.set_vin_uv_warn_limit(VCORE_UV_WARN_LIMIT)
229-
})
230-
.is_ok();
227+
let all_ok = self
228+
.set_alert_config_on_both_vrms(PmbusCmd::LoadLimit, |vrm| {
229+
vrm.set_vin_uv_warn_limit(VCORE_UV_WARN_LIMIT)
230+
});
231231
ringbuf_entry!(Trace::LimitsLoaded { all_ok });
232232

233233
let iout_mask = {
@@ -250,19 +250,33 @@ impl VCore {
250250
);
251251
mask
252252
};
253-
let mut all_ok = true;
254-
all_ok &=
255-
retry_i2c_txn(Rail::VddcrCpu0, PmbusCmd::SetStatusIoutMask, || {
256-
self.vddcr_cpu0.set_status_iout_smbalert_mask(iout_mask)
257-
})
258-
.is_ok();
259-
all_ok &=
260-
retry_i2c_txn(Rail::VddcrCpu1, PmbusCmd::SetStatusIoutMask, || {
261-
self.vddcr_cpu1.set_status_iout_smbalert_mask(iout_mask)
262-
})
263-
.is_ok();
253+
let all_ok = self.set_alert_config_on_both_vrms(
254+
PmbusCmd::SetStatusIoutMask,
255+
|vrm| vrm.set_status_iout_smbalert_mask(iout_mask),
256+
);
264257
ringbuf_entry!(Trace::StatusIoutMaskSet { all_ok });
265258

259+
let cml_mask = {
260+
let mut mask = STATUS_CML::CommandData(0);
261+
// Mask out SMBus alerts for STATUS_CML bit 1. This bit, "other
262+
// fault", is basically set when the PMBus sees something happen on
263+
// the I2C bus that makes it feel weird. Unfortunately, it turns out
264+
// that "I2C things that make you feel kinda weird" can happen a lot
265+
// in an otherwise healthy system. While we are thankful for the
266+
// RAA229620A for setting the status bit that says it saw something
267+
// weird, we would really rather not get an IRQ about it every time
268+
// there's I2C weather. So let's not get alerts for this one.
269+
mask.set_other_communication_error(
270+
STATUS_CML::OtherCommunicationError::Error,
271+
);
272+
mask
273+
};
274+
let all_ok = self
275+
.set_alert_config_on_both_vrms(PmbusCmd::SetStatusCmlMask, |vrm| {
276+
vrm.set_status_cml_smbalert_mask_on_all_rails(cml_mask)
277+
});
278+
ringbuf_entry!(Trace::StatusCmlMaskSet { all_ok });
279+
266280
// Clear our faults
267281
self.try_to_clear_faults(Vrms {
268282
pwr_cont1: true,
@@ -275,6 +289,29 @@ impl VCore {
275289
ringbuf_entry!(Trace::Initialized);
276290
}
277291

292+
fn set_alert_config_on_both_vrms(
293+
&self,
294+
which: PmbusCmd,
295+
txn: impl Fn(&Raa229620A) -> Result<(), raa229620a::Error>,
296+
) -> bool {
297+
let mut all_ok = true;
298+
all_ok &=
299+
retry_i2c_txn(
300+
Rail::VddcrCpu0,
301+
which,
302+
&mut || txn(&self.vddcr_cpu0),
303+
)
304+
.is_ok();
305+
all_ok &=
306+
retry_i2c_txn(
307+
Rail::VddcrCpu1,
308+
which,
309+
&mut || txn(&self.vddcr_cpu1),
310+
)
311+
.is_ok();
312+
all_ok
313+
}
314+
278315
pub fn can_we_unmask_any_vrm_irqs_again(&mut self) -> Vrms {
279316
self.try_to_clear_faults(self.faulted);
280317
Vrms {

drv/i2c-devices/src/lib.rs

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,42 @@ macro_rules! pmbus_rail_write {
178178
}};
179179
}
180180

181+
/// Write the mask `$mask` to the `SMBALERT_MASK` register for `$reg`, where
182+
/// `$reg` is a status register, and `$mask` is a `CommandData` value for that
183+
/// register.
184+
///
185+
/// Importantly, `$reg` must be a PMBus `STATUS_<whatever>` register. This macro
186+
/// cannot stop you from providing any `CommandCode` as the value of `$reg` and
187+
/// any `CommandData` as the value of `$mask`, but, uh, don't do that. On the
188+
/// other hand, the macro *does* at least ensure that `$mask` is a `CommandData`.
189+
/// for the same register as `$reg`.
190+
macro_rules! pmbus_smbalert_mask_write {
191+
($device:expr, $rail:expr, $reg:ident, $mask:expr) => {{
192+
// This assignment is just a type assertion that `$mask` is a
193+
// `CommandData` for the same register as `$reg`.
194+
let mask: $reg::CommandData = $mask;
195+
let rpayload = [PAGE::CommandData::code(), $rail];
196+
// N.B. that the status register *should* always be a single byte, but
197+
// we'll do this "properly" just in case.
198+
let mut payload = [0u8; $reg::CommandData::len() + 2];
199+
// 0 7 15 23
200+
// +---------------+---------------+---------------+
201+
// | SMBALERT_MASK | register code | mask byte |
202+
// +---------------+---------------+---------------+
203+
payload[0] = CommandCode::SMBALERT_MASK as u8;
204+
payload[1] = $reg::CommandData::code();
205+
mask.to_slice(&mut payload[2..]);
206+
207+
match $device.write_write(&rpayload, &payload) {
208+
Err(code) => Err(Error::BadWrite {
209+
cmd: CommandCode::SMBALERT_MASK as u8,
210+
code,
211+
}),
212+
Ok(_) => Ok(()),
213+
}
214+
}};
215+
}
216+
181217
struct BadValidation {
182218
cmd: u8,
183219
code: ResponseCode,

drv/i2c-devices/src/raa229620a.rs

Lines changed: 17 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -151,26 +151,23 @@ impl Raa229620A {
151151
&self,
152152
mask: STATUS_IOUT::CommandData,
153153
) -> Result<(), Error> {
154-
// Unfortunately, SMBALERT_MASK is kinda hard to use with the
155-
// `pmbus_write!` macro family, due to not having its own `CommandData`
156-
// type, and because it requires writing both the name of the status
157-
// register being masked *and* the value of that register (as the mask).
158-
// It's a bit odd. Probably it deserves its own
159-
// `pmbus_smbalert_mask_write!` macro or something, but for now, we'll
160-
// just do it manually.
161-
self.device
162-
.write_write(
163-
&[PAGE::CommandData::code(), self.rail],
164-
&[
165-
CommandCode::SMBALERT_MASK as u8,
166-
CommandCode::STATUS_IOUT as u8,
167-
mask.0,
168-
],
169-
)
170-
.map_err(|code| Error::BadWrite {
171-
cmd: CommandCode::SMBALERT_MASK as u8,
172-
code,
173-
})
154+
pmbus_smbalert_mask_write!(self.device, self.rail, STATUS_IOUT, mask)
155+
}
156+
157+
/// Set the `SMBALERT_MASK` for the `STATUS_CML` register, sending page
158+
/// 0xFF. Though I couldn't find explicit confirmation of this in the PMBus
159+
/// standard, one must kind of assume that `STATUS_CML` bits, which are not
160+
/// specific to a particular output rail, are probably set on all PMBus
161+
/// pages when a CML event happens, and thus we must mask them out on all
162+
/// pages to stop SMBus alerts from being generated?
163+
///
164+
/// Any bits set in `mask` will be masked, suppressing SMBus alerts when
165+
/// those bits in `STATUS_CML` become set.
166+
pub fn set_status_cml_smbalert_mask_on_all_rails(
167+
&self,
168+
mask: STATUS_CML::CommandData,
169+
) -> Result<(), Error> {
170+
pmbus_smbalert_mask_write!(self.device, 0xff, STATUS_CML, mask)
174171
}
175172

176173
pub fn read_vin(&self) -> Result<Volts, Error> {

0 commit comments

Comments
 (0)