1*0caf7f37SRobin Zhang /* SPDX-License-Identifier: BSD-3-Clause
2*0caf7f37SRobin Zhang * Copyright(c) 2022 Intel Corporation
3*0caf7f37SRobin Zhang * Implements SFF-8472 optics diagnostics.
4*0caf7f37SRobin Zhang */
5*0caf7f37SRobin Zhang
6*0caf7f37SRobin Zhang #include <stdio.h>
7*0caf7f37SRobin Zhang
8*0caf7f37SRobin Zhang #include "sff_common.h"
9*0caf7f37SRobin Zhang
10*0caf7f37SRobin Zhang /* Offsets in decimal, for direct comparison with the SFF specs */
11*0caf7f37SRobin Zhang
12*0caf7f37SRobin Zhang /* A0-based EEPROM offsets for DOM support checks */
13*0caf7f37SRobin Zhang #define SFF_A0_DOM 92
14*0caf7f37SRobin Zhang #define SFF_A0_OPTIONS 93
15*0caf7f37SRobin Zhang #define SFF_A0_COMP 94
16*0caf7f37SRobin Zhang
17*0caf7f37SRobin Zhang /* EEPROM bit values for various registers */
18*0caf7f37SRobin Zhang #define SFF_A0_DOM_EXTCAL RTE_BIT32(4)
19*0caf7f37SRobin Zhang #define SFF_A0_DOM_INTCAL RTE_BIT32(5)
20*0caf7f37SRobin Zhang #define SFF_A0_DOM_IMPL RTE_BIT32(6)
21*0caf7f37SRobin Zhang #define SFF_A0_DOM_PWRT RTE_BIT32(3)
22*0caf7f37SRobin Zhang
23*0caf7f37SRobin Zhang #define SFF_A0_OPTIONS_AW RTE_BIT32(7)
24*0caf7f37SRobin Zhang
25*0caf7f37SRobin Zhang /*
26*0caf7f37SRobin Zhang * This is the offset at which the A2 page is in the EEPROM
27*0caf7f37SRobin Zhang * blob returned by the kernel.
28*0caf7f37SRobin Zhang */
29*0caf7f37SRobin Zhang #define SFF_A2_BASE 0x100
30*0caf7f37SRobin Zhang
31*0caf7f37SRobin Zhang /* A2-based offsets for DOM */
32*0caf7f37SRobin Zhang #define SFF_A2_TEMP 96
33*0caf7f37SRobin Zhang #define SFF_A2_TEMP_HALRM 0
34*0caf7f37SRobin Zhang #define SFF_A2_TEMP_LALRM 2
35*0caf7f37SRobin Zhang #define SFF_A2_TEMP_HWARN 4
36*0caf7f37SRobin Zhang #define SFF_A2_TEMP_LWARN 6
37*0caf7f37SRobin Zhang
38*0caf7f37SRobin Zhang #define SFF_A2_VCC 98
39*0caf7f37SRobin Zhang #define SFF_A2_VCC_HALRM 8
40*0caf7f37SRobin Zhang #define SFF_A2_VCC_LALRM 10
41*0caf7f37SRobin Zhang #define SFF_A2_VCC_HWARN 12
42*0caf7f37SRobin Zhang #define SFF_A2_VCC_LWARN 14
43*0caf7f37SRobin Zhang
44*0caf7f37SRobin Zhang #define SFF_A2_BIAS 100
45*0caf7f37SRobin Zhang #define SFF_A2_BIAS_HALRM 16
46*0caf7f37SRobin Zhang #define SFF_A2_BIAS_LALRM 18
47*0caf7f37SRobin Zhang #define SFF_A2_BIAS_HWARN 20
48*0caf7f37SRobin Zhang #define SFF_A2_BIAS_LWARN 22
49*0caf7f37SRobin Zhang
50*0caf7f37SRobin Zhang #define SFF_A2_TX_PWR 102
51*0caf7f37SRobin Zhang #define SFF_A2_TX_PWR_HALRM 24
52*0caf7f37SRobin Zhang #define SFF_A2_TX_PWR_LALRM 26
53*0caf7f37SRobin Zhang #define SFF_A2_TX_PWR_HWARN 28
54*0caf7f37SRobin Zhang #define SFF_A2_TX_PWR_LWARN 30
55*0caf7f37SRobin Zhang
56*0caf7f37SRobin Zhang #define SFF_A2_RX_PWR 104
57*0caf7f37SRobin Zhang #define SFF_A2_RX_PWR_HALRM 32
58*0caf7f37SRobin Zhang #define SFF_A2_RX_PWR_LALRM 34
59*0caf7f37SRobin Zhang #define SFF_A2_RX_PWR_HWARN 36
60*0caf7f37SRobin Zhang #define SFF_A2_RX_PWR_LWARN 38
61*0caf7f37SRobin Zhang
62*0caf7f37SRobin Zhang #define SFF_A2_ALRM_FLG 112
63*0caf7f37SRobin Zhang #define SFF_A2_WARN_FLG 116
64*0caf7f37SRobin Zhang
65*0caf7f37SRobin Zhang /* 32-bit little-endian calibration constants */
66*0caf7f37SRobin Zhang #define SFF_A2_CAL_RXPWR4 56
67*0caf7f37SRobin Zhang #define SFF_A2_CAL_RXPWR3 60
68*0caf7f37SRobin Zhang #define SFF_A2_CAL_RXPWR2 64
69*0caf7f37SRobin Zhang #define SFF_A2_CAL_RXPWR1 68
70*0caf7f37SRobin Zhang #define SFF_A2_CAL_RXPWR0 72
71*0caf7f37SRobin Zhang
72*0caf7f37SRobin Zhang /* 16-bit little endian calibration constants */
73*0caf7f37SRobin Zhang #define SFF_A2_CAL_TXI_SLP 76
74*0caf7f37SRobin Zhang #define SFF_A2_CAL_TXI_OFF 78
75*0caf7f37SRobin Zhang #define SFF_A2_CAL_TXPWR_SLP 80
76*0caf7f37SRobin Zhang #define SFF_A2_CAL_TXPWR_OFF 82
77*0caf7f37SRobin Zhang #define SFF_A2_CAL_T_SLP 84
78*0caf7f37SRobin Zhang #define SFF_A2_CAL_T_OFF 86
79*0caf7f37SRobin Zhang #define SFF_A2_CAL_V_SLP 88
80*0caf7f37SRobin Zhang #define SFF_A2_CAL_V_OFF 90
81*0caf7f37SRobin Zhang
82*0caf7f37SRobin Zhang static struct sff_8472_aw_flags {
83*0caf7f37SRobin Zhang const char *str; /* Human-readable string, null at the end */
84*0caf7f37SRobin Zhang int offset; /* A2-relative address offset */
85*0caf7f37SRobin Zhang uint8_t value; /* Alarm is on if (offset & value) != 0. */
86*0caf7f37SRobin Zhang } sff_8472_aw_flags[] = {
87*0caf7f37SRobin Zhang { "Laser bias current high alarm", SFF_A2_ALRM_FLG, RTE_BIT32(3) },
88*0caf7f37SRobin Zhang { "Laser bias current low alarm", SFF_A2_ALRM_FLG, RTE_BIT32(2) },
89*0caf7f37SRobin Zhang { "Laser bias current high warning", SFF_A2_WARN_FLG, RTE_BIT32(3) },
90*0caf7f37SRobin Zhang { "Laser bias current low warning", SFF_A2_WARN_FLG, RTE_BIT32(2) },
91*0caf7f37SRobin Zhang
92*0caf7f37SRobin Zhang { "Laser output power high alarm", SFF_A2_ALRM_FLG, RTE_BIT32(1) },
93*0caf7f37SRobin Zhang { "Laser output power low alarm", SFF_A2_ALRM_FLG, RTE_BIT32(0) },
94*0caf7f37SRobin Zhang { "Laser output power high warning", SFF_A2_WARN_FLG, RTE_BIT32(1) },
95*0caf7f37SRobin Zhang { "Laser output power low warning", SFF_A2_WARN_FLG, RTE_BIT32(0) },
96*0caf7f37SRobin Zhang
97*0caf7f37SRobin Zhang { "Module temperature high alarm", SFF_A2_ALRM_FLG, RTE_BIT32(7) },
98*0caf7f37SRobin Zhang { "Module temperature low alarm", SFF_A2_ALRM_FLG, RTE_BIT32(6) },
99*0caf7f37SRobin Zhang { "Module temperature high warning", SFF_A2_WARN_FLG, RTE_BIT32(7) },
100*0caf7f37SRobin Zhang { "Module temperature low warning", SFF_A2_WARN_FLG, RTE_BIT32(6) },
101*0caf7f37SRobin Zhang
102*0caf7f37SRobin Zhang { "Module voltage high alarm", SFF_A2_ALRM_FLG, RTE_BIT32(5) },
103*0caf7f37SRobin Zhang { "Module voltage low alarm", SFF_A2_ALRM_FLG, RTE_BIT32(4) },
104*0caf7f37SRobin Zhang { "Module voltage high warning", SFF_A2_WARN_FLG, RTE_BIT32(5) },
105*0caf7f37SRobin Zhang { "Module voltage low warning", SFF_A2_WARN_FLG, RTE_BIT32(4) },
106*0caf7f37SRobin Zhang
107*0caf7f37SRobin Zhang { "Laser rx power high alarm", SFF_A2_ALRM_FLG + 1, RTE_BIT32(7) },
108*0caf7f37SRobin Zhang { "Laser rx power low alarm", SFF_A2_ALRM_FLG + 1, RTE_BIT32(6) },
109*0caf7f37SRobin Zhang { "Laser rx power high warning", SFF_A2_WARN_FLG + 1, RTE_BIT32(7) },
110*0caf7f37SRobin Zhang { "Laser rx power low warning", SFF_A2_WARN_FLG + 1, RTE_BIT32(6) },
111*0caf7f37SRobin Zhang
112*0caf7f37SRobin Zhang { NULL, 0, 0 },
113*0caf7f37SRobin Zhang };
114*0caf7f37SRobin Zhang
115*0caf7f37SRobin Zhang /* Most common case: 16-bit unsigned integer in a certain unit */
116*0caf7f37SRobin Zhang #define A2_OFFSET_TO_U16(offset) \
117*0caf7f37SRobin Zhang (data[SFF_A2_BASE + (offset)] << 8 | data[SFF_A2_BASE + (offset) + 1])
118*0caf7f37SRobin Zhang
119*0caf7f37SRobin Zhang /* Calibration slope is a number between 0.0 included and 256.0 excluded. */
120*0caf7f37SRobin Zhang #define A2_OFFSET_TO_SLP(offset) \
121*0caf7f37SRobin Zhang (data[SFF_A2_BASE + (offset)] + data[SFF_A2_BASE + (offset) + 1] / 256.)
122*0caf7f37SRobin Zhang
123*0caf7f37SRobin Zhang /* Calibration offset is an integer from -32768 to 32767 */
124*0caf7f37SRobin Zhang #define A2_OFFSET_TO_OFF(offset) \
125*0caf7f37SRobin Zhang ((int16_t)A2_OFFSET_TO_U16(offset))
126*0caf7f37SRobin Zhang
127*0caf7f37SRobin Zhang /* RXPWR(x) are IEEE-754 floating point numbers in big-endian format */
128*0caf7f37SRobin Zhang #define A2_OFFSET_TO_RXPWRx(offset) \
129*0caf7f37SRobin Zhang (befloattoh((const uint32_t *)(data + SFF_A2_BASE + (offset))))
130*0caf7f37SRobin Zhang
131*0caf7f37SRobin Zhang /*
132*0caf7f37SRobin Zhang * 2-byte internal temperature conversions:
133*0caf7f37SRobin Zhang * First byte is a signed 8-bit integer, which is the temp decimal part
134*0caf7f37SRobin Zhang * Second byte are 1/256th of degree, which are added to the dec part.
135*0caf7f37SRobin Zhang */
136*0caf7f37SRobin Zhang #define A2_OFFSET_TO_TEMP(offset) ((int16_t)A2_OFFSET_TO_U16(offset))
137*0caf7f37SRobin Zhang
sff_8472_dom_parse(const uint8_t * data,struct sff_diags * sd)138*0caf7f37SRobin Zhang static void sff_8472_dom_parse(const uint8_t *data, struct sff_diags *sd)
139*0caf7f37SRobin Zhang {
140*0caf7f37SRobin Zhang sd->bias_cur[SFF_MCURR] = A2_OFFSET_TO_U16(SFF_A2_BIAS);
141*0caf7f37SRobin Zhang sd->bias_cur[SFF_HALRM] = A2_OFFSET_TO_U16(SFF_A2_BIAS_HALRM);
142*0caf7f37SRobin Zhang sd->bias_cur[SFF_LALRM] = A2_OFFSET_TO_U16(SFF_A2_BIAS_LALRM);
143*0caf7f37SRobin Zhang sd->bias_cur[SFF_HWARN] = A2_OFFSET_TO_U16(SFF_A2_BIAS_HWARN);
144*0caf7f37SRobin Zhang sd->bias_cur[SFF_LWARN] = A2_OFFSET_TO_U16(SFF_A2_BIAS_LWARN);
145*0caf7f37SRobin Zhang
146*0caf7f37SRobin Zhang sd->sfp_voltage[SFF_MCURR] = A2_OFFSET_TO_U16(SFF_A2_VCC);
147*0caf7f37SRobin Zhang sd->sfp_voltage[SFF_HALRM] = A2_OFFSET_TO_U16(SFF_A2_VCC_HALRM);
148*0caf7f37SRobin Zhang sd->sfp_voltage[SFF_LALRM] = A2_OFFSET_TO_U16(SFF_A2_VCC_LALRM);
149*0caf7f37SRobin Zhang sd->sfp_voltage[SFF_HWARN] = A2_OFFSET_TO_U16(SFF_A2_VCC_HWARN);
150*0caf7f37SRobin Zhang sd->sfp_voltage[SFF_LWARN] = A2_OFFSET_TO_U16(SFF_A2_VCC_LWARN);
151*0caf7f37SRobin Zhang
152*0caf7f37SRobin Zhang sd->tx_power[SFF_MCURR] = A2_OFFSET_TO_U16(SFF_A2_TX_PWR);
153*0caf7f37SRobin Zhang sd->tx_power[SFF_HALRM] = A2_OFFSET_TO_U16(SFF_A2_TX_PWR_HALRM);
154*0caf7f37SRobin Zhang sd->tx_power[SFF_LALRM] = A2_OFFSET_TO_U16(SFF_A2_TX_PWR_LALRM);
155*0caf7f37SRobin Zhang sd->tx_power[SFF_HWARN] = A2_OFFSET_TO_U16(SFF_A2_TX_PWR_HWARN);
156*0caf7f37SRobin Zhang sd->tx_power[SFF_LWARN] = A2_OFFSET_TO_U16(SFF_A2_TX_PWR_LWARN);
157*0caf7f37SRobin Zhang
158*0caf7f37SRobin Zhang sd->rx_power[SFF_MCURR] = A2_OFFSET_TO_U16(SFF_A2_RX_PWR);
159*0caf7f37SRobin Zhang sd->rx_power[SFF_HALRM] = A2_OFFSET_TO_U16(SFF_A2_RX_PWR_HALRM);
160*0caf7f37SRobin Zhang sd->rx_power[SFF_LALRM] = A2_OFFSET_TO_U16(SFF_A2_RX_PWR_LALRM);
161*0caf7f37SRobin Zhang sd->rx_power[SFF_HWARN] = A2_OFFSET_TO_U16(SFF_A2_RX_PWR_HWARN);
162*0caf7f37SRobin Zhang sd->rx_power[SFF_LWARN] = A2_OFFSET_TO_U16(SFF_A2_RX_PWR_LWARN);
163*0caf7f37SRobin Zhang
164*0caf7f37SRobin Zhang sd->sfp_temp[SFF_MCURR] = A2_OFFSET_TO_TEMP(SFF_A2_TEMP);
165*0caf7f37SRobin Zhang sd->sfp_temp[SFF_HALRM] = A2_OFFSET_TO_TEMP(SFF_A2_TEMP_HALRM);
166*0caf7f37SRobin Zhang sd->sfp_temp[SFF_LALRM] = A2_OFFSET_TO_TEMP(SFF_A2_TEMP_LALRM);
167*0caf7f37SRobin Zhang sd->sfp_temp[SFF_HWARN] = A2_OFFSET_TO_TEMP(SFF_A2_TEMP_HWARN);
168*0caf7f37SRobin Zhang sd->sfp_temp[SFF_LWARN] = A2_OFFSET_TO_TEMP(SFF_A2_TEMP_LWARN);
169*0caf7f37SRobin Zhang }
170*0caf7f37SRobin Zhang
171*0caf7f37SRobin Zhang /* Converts to a float from a big-endian 4-byte source buffer. */
befloattoh(const uint32_t * source)172*0caf7f37SRobin Zhang static float befloattoh(const uint32_t *source)
173*0caf7f37SRobin Zhang {
174*0caf7f37SRobin Zhang union {
175*0caf7f37SRobin Zhang uint32_t src;
176*0caf7f37SRobin Zhang float dst;
177*0caf7f37SRobin Zhang } converter;
178*0caf7f37SRobin Zhang
179*0caf7f37SRobin Zhang converter.src = ntohl(*source);
180*0caf7f37SRobin Zhang return converter.dst;
181*0caf7f37SRobin Zhang }
182*0caf7f37SRobin Zhang
sff_8472_calibration(const uint8_t * data,struct sff_diags * sd)183*0caf7f37SRobin Zhang static void sff_8472_calibration(const uint8_t *data, struct sff_diags *sd)
184*0caf7f37SRobin Zhang {
185*0caf7f37SRobin Zhang unsigned long i;
186*0caf7f37SRobin Zhang uint16_t rx_reading;
187*0caf7f37SRobin Zhang
188*0caf7f37SRobin Zhang /* Calibration should occur for all values (threshold and current) */
189*0caf7f37SRobin Zhang for (i = 0; i < RTE_DIM(sd->bias_cur); ++i) {
190*0caf7f37SRobin Zhang /*
191*0caf7f37SRobin Zhang * Apply calibration formula 1 (Temp., Voltage, Bias, Tx Power)
192*0caf7f37SRobin Zhang */
193*0caf7f37SRobin Zhang sd->bias_cur[i] *= A2_OFFSET_TO_SLP(SFF_A2_CAL_TXI_SLP);
194*0caf7f37SRobin Zhang sd->tx_power[i] *= A2_OFFSET_TO_SLP(SFF_A2_CAL_TXPWR_SLP);
195*0caf7f37SRobin Zhang sd->sfp_voltage[i] *= A2_OFFSET_TO_SLP(SFF_A2_CAL_V_SLP);
196*0caf7f37SRobin Zhang sd->sfp_temp[i] *= A2_OFFSET_TO_SLP(SFF_A2_CAL_T_SLP);
197*0caf7f37SRobin Zhang
198*0caf7f37SRobin Zhang sd->bias_cur[i] += A2_OFFSET_TO_OFF(SFF_A2_CAL_TXI_OFF);
199*0caf7f37SRobin Zhang sd->tx_power[i] += A2_OFFSET_TO_OFF(SFF_A2_CAL_TXPWR_OFF);
200*0caf7f37SRobin Zhang sd->sfp_voltage[i] += A2_OFFSET_TO_OFF(SFF_A2_CAL_V_OFF);
201*0caf7f37SRobin Zhang sd->sfp_temp[i] += A2_OFFSET_TO_OFF(SFF_A2_CAL_T_OFF);
202*0caf7f37SRobin Zhang
203*0caf7f37SRobin Zhang /*
204*0caf7f37SRobin Zhang * Apply calibration formula 2 (Rx Power only)
205*0caf7f37SRobin Zhang */
206*0caf7f37SRobin Zhang rx_reading = sd->rx_power[i];
207*0caf7f37SRobin Zhang sd->rx_power[i] = A2_OFFSET_TO_RXPWRx(SFF_A2_CAL_RXPWR0);
208*0caf7f37SRobin Zhang sd->rx_power[i] += rx_reading *
209*0caf7f37SRobin Zhang A2_OFFSET_TO_RXPWRx(SFF_A2_CAL_RXPWR1);
210*0caf7f37SRobin Zhang sd->rx_power[i] += rx_reading *
211*0caf7f37SRobin Zhang A2_OFFSET_TO_RXPWRx(SFF_A2_CAL_RXPWR2);
212*0caf7f37SRobin Zhang sd->rx_power[i] += rx_reading *
213*0caf7f37SRobin Zhang A2_OFFSET_TO_RXPWRx(SFF_A2_CAL_RXPWR3);
214*0caf7f37SRobin Zhang }
215*0caf7f37SRobin Zhang }
216*0caf7f37SRobin Zhang
sff_8472_parse_eeprom(const uint8_t * data,struct sff_diags * sd)217*0caf7f37SRobin Zhang static void sff_8472_parse_eeprom(const uint8_t *data, struct sff_diags *sd)
218*0caf7f37SRobin Zhang {
219*0caf7f37SRobin Zhang sd->supports_dom = data[SFF_A0_DOM] & SFF_A0_DOM_IMPL;
220*0caf7f37SRobin Zhang sd->supports_alarms = data[SFF_A0_OPTIONS] & SFF_A0_OPTIONS_AW;
221*0caf7f37SRobin Zhang sd->calibrated_ext = data[SFF_A0_DOM] & SFF_A0_DOM_EXTCAL;
222*0caf7f37SRobin Zhang sd->rx_power_type = data[SFF_A0_DOM] & SFF_A0_DOM_PWRT;
223*0caf7f37SRobin Zhang
224*0caf7f37SRobin Zhang sff_8472_dom_parse(data, sd);
225*0caf7f37SRobin Zhang
226*0caf7f37SRobin Zhang /*
227*0caf7f37SRobin Zhang * If the SFP is externally calibrated, we need to read calibration data
228*0caf7f37SRobin Zhang * and compensate the already stored readings.
229*0caf7f37SRobin Zhang */
230*0caf7f37SRobin Zhang if (sd->calibrated_ext)
231*0caf7f37SRobin Zhang sff_8472_calibration(data, sd);
232*0caf7f37SRobin Zhang }
233*0caf7f37SRobin Zhang
sff_8472_show_all(const uint8_t * data,struct rte_tel_data * d)234*0caf7f37SRobin Zhang void sff_8472_show_all(const uint8_t *data, struct rte_tel_data *d)
235*0caf7f37SRobin Zhang {
236*0caf7f37SRobin Zhang struct sff_diags sd = {0};
237*0caf7f37SRobin Zhang const char *rx_power_string = NULL;
238*0caf7f37SRobin Zhang char val_string[SFF_ITEM_VAL_COMPOSE_SIZE];
239*0caf7f37SRobin Zhang int i;
240*0caf7f37SRobin Zhang
241*0caf7f37SRobin Zhang sff_8472_parse_eeprom(data, &sd);
242*0caf7f37SRobin Zhang
243*0caf7f37SRobin Zhang if (!sd.supports_dom) {
244*0caf7f37SRobin Zhang ssf_add_dict_string(d, "Optical diagnostics support", "No");
245*0caf7f37SRobin Zhang return;
246*0caf7f37SRobin Zhang }
247*0caf7f37SRobin Zhang ssf_add_dict_string(d, "Optical diagnostics support", "Yes");
248*0caf7f37SRobin Zhang
249*0caf7f37SRobin Zhang SFF_SPRINT_BIAS(val_string, sd.bias_cur[SFF_MCURR]);
250*0caf7f37SRobin Zhang ssf_add_dict_string(d, "Laser bias current", val_string);
251*0caf7f37SRobin Zhang
252*0caf7f37SRobin Zhang SFF_SPRINT_xX_PWR(val_string, sd.tx_power[SFF_MCURR]);
253*0caf7f37SRobin Zhang ssf_add_dict_string(d, "Laser output power", val_string);
254*0caf7f37SRobin Zhang
255*0caf7f37SRobin Zhang if (!sd.rx_power_type)
256*0caf7f37SRobin Zhang rx_power_string = "Receiver signal OMA";
257*0caf7f37SRobin Zhang else
258*0caf7f37SRobin Zhang rx_power_string = "Receiver signal average optical power";
259*0caf7f37SRobin Zhang
260*0caf7f37SRobin Zhang SFF_SPRINT_xX_PWR(val_string, sd.rx_power[SFF_MCURR]);
261*0caf7f37SRobin Zhang ssf_add_dict_string(d, rx_power_string, val_string);
262*0caf7f37SRobin Zhang
263*0caf7f37SRobin Zhang SFF_SPRINT_TEMP(val_string, sd.sfp_temp[SFF_MCURR]);
264*0caf7f37SRobin Zhang ssf_add_dict_string(d, "Module temperature", val_string);
265*0caf7f37SRobin Zhang
266*0caf7f37SRobin Zhang SFF_SPRINT_VCC(val_string, sd.sfp_voltage[SFF_MCURR]);
267*0caf7f37SRobin Zhang ssf_add_dict_string(d, "Module voltage", val_string);
268*0caf7f37SRobin Zhang
269*0caf7f37SRobin Zhang ssf_add_dict_string(d, "Alarm/warning flags implemented",
270*0caf7f37SRobin Zhang (sd.supports_alarms ? "Yes" : "No"));
271*0caf7f37SRobin Zhang
272*0caf7f37SRobin Zhang if (sd.supports_alarms) {
273*0caf7f37SRobin Zhang for (i = 0; sff_8472_aw_flags[i].str; ++i) {
274*0caf7f37SRobin Zhang ssf_add_dict_string(d, sff_8472_aw_flags[i].str,
275*0caf7f37SRobin Zhang data[SFF_A2_BASE + sff_8472_aw_flags[i].offset]
276*0caf7f37SRobin Zhang & sff_8472_aw_flags[i].value ? "On" : "Off");
277*0caf7f37SRobin Zhang }
278*0caf7f37SRobin Zhang sff_show_thresholds(sd, d);
279*0caf7f37SRobin Zhang }
280*0caf7f37SRobin Zhang }
281