12#if PC_ENABLE_HW_HEALTH
16void pc_hwhealth_rail_init(HwRailMonitor *m, uint32_t nominal_mv, uint32_t warn_mv, uint32_t crit_mv)
22 m->nominal_mv = nominal_mv;
25 m->min_mv = nominal_mv;
27 m->brownout_events = 0;
30HwRailVerdict pc_hwhealth_rail_sample(HwRailMonitor *m, uint32_t mv)
34 return HwRailVerdict::HW_RAIL_OK;
43 return HwRailVerdict::HW_RAIL_BROWNOUT;
48 return HwRailVerdict::HW_RAIL_SAG;
50 return HwRailVerdict::HW_RAIL_OK;
53size_t pc_hwhealth_rail_json(
const HwRailMonitor *m,
char *out,
size_t cap)
55 if (!m || !out || cap == 0)
59 pc_sb b = {out, cap, 0,
true};
77void pc_hwhealth_spi_init(HwSpiBackoff *s, uint32_t start_hz, uint32_t min_hz, uint32_t max_hz, uint16_t fail_trip,
86 if (start_hz < min_hz)
90 else if (start_hz > max_hz)
100 s->fail_trip = fail_trip ? fail_trip : 1;
101 s->ok_trip = ok_trip ? ok_trip : 1;
104uint32_t pc_hwhealth_spi_result(HwSpiBackoff *s,
bool crc_ok)
113 if (++s->ok_streak >= s->ok_trip)
116 uint32_t up = s->hz << 1;
117 if (up < s->hz || up > s->max_hz)
127 if (++s->fail_streak >= s->fail_trip)
130 uint32_t down = s->hz >> 1;
131 if (down < s->min_hz)
141HwGpioVerdict pc_hwhealth_gpio_short(
bool driven_high,
bool read_high)
143 if (driven_high && !read_high)
145 return HwGpioVerdict::HW_GPIO_SHORT_GND;
147 if (!driven_high && read_high)
149 return HwGpioVerdict::HW_GPIO_SHORT_VCC;
151 return HwGpioVerdict::HW_GPIO_OK;
154HwCapVerdict pc_hwhealth_cap_leak(uint32_t measured_ms, uint32_t expected_ms, uint8_t tol_pct)
156 if (expected_ms == 0)
158 return HwCapVerdict::HW_CAP_OK;
161 uint64_t band = (uint64_t)expected_ms * tol_pct / 100;
162 uint64_t lo = (uint64_t)expected_ms > band ? (uint64_t)expected_ms - band : 0;
163 uint64_t hi = (uint64_t)expected_ms + band;
164 if (measured_ms < lo)
166 return HwCapVerdict::HW_CAP_LEAK;
168 if (measured_ms > hi)
170 return HwCapVerdict::HW_CAP_HIGH_ESR;
172 return HwCapVerdict::HW_CAP_OK;
Hardware-health diagnostics: rail droop, SPI CRC backoff, GPIO short, cap leakage (PC_ENABLE_HW_HEALT...
Bounded no-heap string builder that fails closed on overflow (one shared copy).
void pc_sb_put(pc_sb *b, const char *s)
Append NUL-terminated s; leaves the buffer untouched and clears ok if it would not fit.
void pc_sb_u32(pc_sb *b, uint32_t v)
Append v as decimal (no leading zeros; "0" for zero).
Bump-append target; ok latches false once an append would overflow cap.