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