"Fossies" - the Fresh Open Source Software Archive

Member "ethtool-5.1/sfpdiag.c" (4 Oct 2018, 10236 Bytes) of package /linux/misc/ethtool-5.1.tar.xz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "sfpdiag.c" see the Fossies "Dox" file reference documentation and the last Fossies "Diffs" side-by-side code changes report: 4.18_vs_4.19.

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