New upstream version 18.11-rc1
[deb_dpdk.git] / drivers / net / sfc / base / mcdi_mon.c
index 940bd02..b53de0d 100644 (file)
 
 #include "efx.h"
 #include "efx_impl.h"
+#include "mcdi_mon.h"
 
 #if EFSYS_OPT_MON_MCDI
 
 #if EFSYS_OPT_MON_STATS
 
-#define        MCDI_MON_NEXT_PAGE  ((uint16_t)0xfffe)
-#define        MCDI_MON_INVALID_SENSOR ((uint16_t)0xfffd)
-#define        MCDI_MON_PAGE_SIZE 0x20
-
-/* Bitmasks of valid port(s) for each sensor */
-#define        MCDI_MON_PORT_NONE      (0x00)
-#define        MCDI_MON_PORT_P1        (0x01)
-#define        MCDI_MON_PORT_P2        (0x02)
-#define        MCDI_MON_PORT_P3        (0x04)
-#define        MCDI_MON_PORT_P4        (0x08)
-#define        MCDI_MON_PORT_Px        (0xFFFF)
-
 /* Get port mask from one-based MCDI port number */
 #define        MCDI_MON_PORT_MASK(_emip) (1U << ((_emip)->emi_port - 1))
 
-/* Entry for MCDI sensor in sensor map */
-#define        STAT(portmask, stat)    \
-       { (MCDI_MON_PORT_##portmask), (EFX_MON_STAT_##stat) }
-
-/* Entry for sensor next page flag in sensor map */
-#define        STAT_NEXT_PAGE()        \
-       { MCDI_MON_PORT_NONE, MCDI_MON_NEXT_PAGE }
-
-/* Placeholder for gaps in the array */
-#define        STAT_NO_SENSOR()        \
-       { MCDI_MON_PORT_NONE, MCDI_MON_INVALID_SENSOR }
-
-/* Map from MC sensors to monitor statistics */
-static const struct mcdi_sensor_map_s {
-       uint16_t        msm_port_mask;
-       uint16_t        msm_stat;
-} mcdi_sensor_map[] = {
-       /* Sensor page 0                MC_CMD_SENSOR_xxx */
-       STAT(Px, INT_TEMP),             /* 0x00 CONTROLLER_TEMP */
-       STAT(Px, EXT_TEMP),             /* 0x01 PHY_COMMON_TEMP */
-       STAT(Px, INT_COOLING),          /* 0x02 CONTROLLER_COOLING */
-       STAT(P1, EXT_TEMP),             /* 0x03 PHY0_TEMP */
-       STAT(P1, EXT_COOLING),          /* 0x04 PHY0_COOLING */
-       STAT(P2, EXT_TEMP),             /* 0x05 PHY1_TEMP */
-       STAT(P2, EXT_COOLING),          /* 0x06 PHY1_COOLING */
-       STAT(Px, 1V),                   /* 0x07 IN_1V0 */
-       STAT(Px, 1_2V),                 /* 0x08 IN_1V2 */
-       STAT(Px, 1_8V),                 /* 0x09 IN_1V8 */
-       STAT(Px, 2_5V),                 /* 0x0a IN_2V5 */
-       STAT(Px, 3_3V),                 /* 0x0b IN_3V3 */
-       STAT(Px, 12V),                  /* 0x0c IN_12V0 */
-       STAT(Px, 1_2VA),                /* 0x0d IN_1V2A */
-       STAT(Px, VREF),                 /* 0x0e IN_VREF */
-       STAT(Px, VAOE),                 /* 0x0f OUT_VAOE */
-       STAT(Px, AOE_TEMP),             /* 0x10 AOE_TEMP */
-       STAT(Px, PSU_AOE_TEMP),         /* 0x11 PSU_AOE_TEMP */
-       STAT(Px, PSU_TEMP),             /* 0x12 PSU_TEMP */
-       STAT(Px, FAN0),                 /* 0x13 FAN_0 */
-       STAT(Px, FAN1),                 /* 0x14 FAN_1 */
-       STAT(Px, FAN2),                 /* 0x15 FAN_2 */
-       STAT(Px, FAN3),                 /* 0x16 FAN_3 */
-       STAT(Px, FAN4),                 /* 0x17 FAN_4 */
-       STAT(Px, VAOE_IN),              /* 0x18 IN_VAOE */
-       STAT(Px, IAOE),                 /* 0x19 OUT_IAOE */
-       STAT(Px, IAOE_IN),              /* 0x1a IN_IAOE */
-       STAT(Px, NIC_POWER),            /* 0x1b NIC_POWER */
-       STAT(Px, 0_9V),                 /* 0x1c IN_0V9 */
-       STAT(Px, I0_9V),                /* 0x1d IN_I0V9 */
-       STAT(Px, I1_2V),                /* 0x1e IN_I1V2 */
-       STAT_NEXT_PAGE(),               /* 0x1f Next page flag (not a sensor) */
-
-       /* Sensor page 1                MC_CMD_SENSOR_xxx */
-       STAT(Px, 0_9V_ADC),             /* 0x20 IN_0V9_ADC */
-       STAT(Px, INT_TEMP2),            /* 0x21 CONTROLLER_2_TEMP */
-       STAT(Px, VREG_TEMP),            /* 0x22 VREG_INTERNAL_TEMP */
-       STAT(Px, VREG_0_9V_TEMP),       /* 0x23 VREG_0V9_TEMP */
-       STAT(Px, VREG_1_2V_TEMP),       /* 0x24 VREG_1V2_TEMP */
-       STAT(Px, INT_VPTAT),            /* 0x25 CTRLR. VPTAT */
-       STAT(Px, INT_ADC_TEMP),         /* 0x26 CTRLR. INTERNAL_TEMP */
-       STAT(Px, EXT_VPTAT),            /* 0x27 CTRLR. VPTAT_EXTADC */
-       STAT(Px, EXT_ADC_TEMP),         /* 0x28 CTRLR. INTERNAL_TEMP_EXTADC */
-       STAT(Px, AMBIENT_TEMP),         /* 0x29 AMBIENT_TEMP */
-       STAT(Px, AIRFLOW),              /* 0x2a AIRFLOW */
-       STAT(Px, VDD08D_VSS08D_CSR),    /* 0x2b VDD08D_VSS08D_CSR */
-       STAT(Px, VDD08D_VSS08D_CSR_EXTADC), /* 0x2c VDD08D_VSS08D_CSR_EXTADC */
-       STAT(Px, HOTPOINT_TEMP),        /* 0x2d HOTPOINT_TEMP */
-       STAT(P1, PHY_POWER_SWITCH_PORT0),   /* 0x2e PHY_POWER_SWITCH_PORT0 */
-       STAT(P2, PHY_POWER_SWITCH_PORT1),   /* 0x2f PHY_POWER_SWITCH_PORT1 */
-       STAT(Px, MUM_VCC),              /* 0x30 MUM_VCC */
-       STAT(Px, 0V9_A),                /* 0x31 0V9_A */
-       STAT(Px, I0V9_A),               /* 0x32 I0V9_A */
-       STAT(Px, 0V9_A_TEMP),           /* 0x33 0V9_A_TEMP */
-       STAT(Px, 0V9_B),                /* 0x34 0V9_B */
-       STAT(Px, I0V9_B),               /* 0x35 I0V9_B */
-       STAT(Px, 0V9_B_TEMP),           /* 0x36 0V9_B_TEMP */
-       STAT(Px, CCOM_AVREG_1V2_SUPPLY),  /* 0x37 CCOM_AVREG_1V2_SUPPLY */
-       STAT(Px, CCOM_AVREG_1V2_SUPPLY_EXT_ADC),
-                                       /* 0x38 CCOM_AVREG_1V2_SUPPLY_EXT_ADC */
-       STAT(Px, CCOM_AVREG_1V8_SUPPLY),  /* 0x39 CCOM_AVREG_1V8_SUPPLY */
-       STAT(Px, CCOM_AVREG_1V8_SUPPLY_EXT_ADC),
-                                       /* 0x3a CCOM_AVREG_1V8_SUPPLY_EXT_ADC */
-       STAT_NO_SENSOR(),               /* 0x3b (no sensor) */
-       STAT_NO_SENSOR(),               /* 0x3c (no sensor) */
-       STAT_NO_SENSOR(),               /* 0x3d (no sensor) */
-       STAT_NO_SENSOR(),               /* 0x3e (no sensor) */
-       STAT_NEXT_PAGE(),               /* 0x3f Next page flag (not a sensor) */
-
-       /* Sensor page 2                MC_CMD_SENSOR_xxx */
-       STAT(Px, CONTROLLER_MASTER_VPTAT),         /* 0x40 MASTER_VPTAT */
-       STAT(Px, CONTROLLER_MASTER_INTERNAL_TEMP), /* 0x41 MASTER_INT_TEMP */
-       STAT(Px, CONTROLLER_MASTER_VPTAT_EXT_ADC), /* 0x42 MAST_VPTAT_EXT_ADC */
-       STAT(Px, CONTROLLER_MASTER_INTERNAL_TEMP_EXT_ADC),
-                                       /* 0x43 MASTER_INTERNAL_TEMP_EXT_ADC */
-       STAT(Px, CONTROLLER_SLAVE_VPTAT),         /* 0x44 SLAVE_VPTAT */
-       STAT(Px, CONTROLLER_SLAVE_INTERNAL_TEMP), /* 0x45 SLAVE_INTERNAL_TEMP */
-       STAT(Px, CONTROLLER_SLAVE_VPTAT_EXT_ADC), /* 0x46 SLAVE_VPTAT_EXT_ADC */
-       STAT(Px, CONTROLLER_SLAVE_INTERNAL_TEMP_EXT_ADC),
-                                       /* 0x47 SLAVE_INTERNAL_TEMP_EXT_ADC */
-       STAT_NO_SENSOR(),               /* 0x48 (no sensor) */
-       STAT(Px, SODIMM_VOUT),          /* 0x49 SODIMM_VOUT */
-       STAT(Px, SODIMM_0_TEMP),        /* 0x4a SODIMM_0_TEMP */
-       STAT(Px, SODIMM_1_TEMP),        /* 0x4b SODIMM_1_TEMP */
-       STAT(Px, PHY0_VCC),             /* 0x4c PHY0_VCC */
-       STAT(Px, PHY1_VCC),             /* 0x4d PHY1_VCC */
-       STAT(Px, CONTROLLER_TDIODE_TEMP), /* 0x4e CONTROLLER_TDIODE_TEMP */
-       STAT(Px, BOARD_FRONT_TEMP),     /* 0x4f BOARD_FRONT_TEMP */
-       STAT(Px, BOARD_BACK_TEMP),      /* 0x50 BOARD_BACK_TEMP */
-       STAT(Px, I1V8),                 /* 0x51 IN_I1V8 */
-       STAT(Px, I2V5),                 /* 0x52 IN_I2V5 */
-       STAT(Px, I3V3),                 /* 0x53 IN_I3V3 */
-       STAT(Px, I12V0),                /* 0x54 IN_I12V0 */
-       STAT(Px, 1_3V),                 /* 0x55 IN_1V3 */
-       STAT(Px, I1V3),                 /* 0x56 IN_I1V3 */
-};
-
 #define        MCDI_STATIC_SENSOR_ASSERT(_field)                               \
        EFX_STATIC_ASSERT(MC_CMD_SENSOR_STATE_ ## _field                \
                            == EFX_MON_STAT_STATE_ ## _field)
@@ -155,10 +29,10 @@ mcdi_mon_decode_stats(
        __inout_ecount_opt(EFX_MON_NSTATS)      efx_mon_stat_value_t *stat)
 {
        efx_mcdi_iface_t *emip = &(enp->en_mcdi.em_emip);
-       uint16_t port_mask;
+       efx_mon_stat_portmask_t port_mask;
        uint16_t sensor;
        size_t sensor_max;
-       uint32_t stat_mask[(EFX_ARRAY_SIZE(mcdi_sensor_map) + 31) / 32];
+       uint32_t stat_mask[(EFX_MON_NSTATS + 31) / 32];
        uint32_t idx = 0;
        uint32_t page = 0;
 
@@ -169,13 +43,10 @@ mcdi_mon_decode_stats(
        MCDI_STATIC_SENSOR_ASSERT(BROKEN);
        MCDI_STATIC_SENSOR_ASSERT(NO_READING);
 
-       EFX_STATIC_ASSERT(sizeof (stat_mask[0]) * 8 ==
-           EFX_MON_MASK_ELEMENT_SIZE);
-       sensor_max =
-           MIN((8 * sensor_mask_size), EFX_ARRAY_SIZE(mcdi_sensor_map));
+       sensor_max = 8 * sensor_mask_size;
 
        EFSYS_ASSERT(emip->emi_port > 0); /* MCDI port number is one-based */
-       port_mask = MCDI_MON_PORT_MASK(emip);
+       port_mask = (efx_mon_stat_portmask_t)MCDI_MON_PORT_MASK(emip);
 
        memset(stat_mask, 0, sizeof (stat_mask));
 
@@ -190,19 +61,36 @@ mcdi_mon_decode_stats(
         * does not understand.
         */
        for (sensor = 0; sensor < sensor_max; ++sensor) {
-               efx_mon_stat_t id = mcdi_sensor_map[sensor].msm_stat;
+               efx_mon_stat_t id;
+               efx_mon_stat_portmask_t stat_portmask = 0;
+               boolean_t decode_ok;
+               efx_mon_stat_unit_t stat_unit;
 
-               if ((sensor % MCDI_MON_PAGE_SIZE) == MC_CMD_SENSOR_PAGE0_NEXT) {
-                       EFSYS_ASSERT3U(id, ==, MCDI_MON_NEXT_PAGE);
+               if ((sensor % (MC_CMD_SENSOR_PAGE0_NEXT + 1)) ==
+                   MC_CMD_SENSOR_PAGE0_NEXT) {
                        page++;
                        continue;
+                       /* This sensor is one of the page boundary bits. */
                }
+
                if (~(sensor_mask[page]) & (1U << sensor))
                        continue;
+               /* This sensor not in DMA buffer */
+
                idx++;
+               /*
+                * Valid stat in DMA buffer that we need to increment over, even
+                * if we couldn't look up the id
+                */
+
+               decode_ok = efx_mon_mcdi_to_efx_stat(sensor, &id);
+               decode_ok =
+                   decode_ok && efx_mon_get_stat_portmap(id, &stat_portmask);
 
-               if ((port_mask & mcdi_sensor_map[sensor].msm_port_mask) == 0)
+               if (!(decode_ok && (stat_portmask & port_mask)))
                        continue;
+               /* Either bad decode, or don't know what port stat is on */
+
                EFSYS_ASSERT(id < EFX_MON_NSTATS);
 
                /*
@@ -228,6 +116,10 @@ mcdi_mon_decode_stats(
 
                        stat[id].emsv_state = (uint16_t)EFX_DWORD_FIELD(dword,
                            MC_CMD_SENSOR_VALUE_ENTRY_TYPEDEF_STATE);
+
+                       stat[id].emsv_unit =
+                           efx_mon_get_stat_unit(id, &stat_unit) ?
+                           stat_unit : EFX_MON_STAT_UNIT_UNKNOWN;
                }
        }
 
@@ -244,7 +136,7 @@ mcdi_mon_ev(
        __out                           efx_mon_stat_value_t *valuep)
 {
        efx_mcdi_iface_t *emip = &(enp->en_mcdi.em_emip);
-       uint16_t port_mask;
+       efx_mon_stat_portmask_t port_mask, sensor_port_mask;
        uint16_t sensor;
        uint16_t state;
        uint16_t value;
@@ -261,20 +153,22 @@ mcdi_mon_ev(
        /* Hardware must support this MCDI sensor */
        EFSYS_ASSERT3U(sensor, <,
            (8 * enp->en_nic_cfg.enc_mcdi_sensor_mask_size));
-       EFSYS_ASSERT((sensor % MCDI_MON_PAGE_SIZE) != MC_CMD_SENSOR_PAGE0_NEXT);
+       EFSYS_ASSERT((sensor % (MC_CMD_SENSOR_PAGE0_NEXT + 1)) !=
+           MC_CMD_SENSOR_PAGE0_NEXT);
        EFSYS_ASSERT(enp->en_nic_cfg.enc_mcdi_sensor_maskp != NULL);
-       EFSYS_ASSERT(
-           (enp->en_nic_cfg.enc_mcdi_sensor_maskp[sensor/MCDI_MON_PAGE_SIZE] &
-           (1U << (sensor % MCDI_MON_PAGE_SIZE))) != 0);
+       EFSYS_ASSERT((enp->en_nic_cfg.enc_mcdi_sensor_maskp[
+                   sensor / (MC_CMD_SENSOR_PAGE0_NEXT + 1)] &
+               (1U << (sensor % (MC_CMD_SENSOR_PAGE0_NEXT + 1)))) != 0);
 
-       /* But we don't have to understand it */
-       if (sensor >= EFX_ARRAY_SIZE(mcdi_sensor_map)) {
+       /* And we need to understand it, to get port-map */
+       if (!efx_mon_mcdi_to_efx_stat(sensor, &id)) {
                rc = ENOTSUP;
                goto fail1;
        }
-       id = mcdi_sensor_map[sensor].msm_stat;
-       if ((port_mask & mcdi_sensor_map[sensor].msm_port_mask) == 0)
+       if (!(efx_mon_get_stat_portmap(id, &sensor_port_mask) &&
+               (port_mask && sensor_port_mask))) {
                return (ENODEV);
+       }
        EFSYS_ASSERT(id < EFX_MON_NSTATS);
 
        *idp = id;
@@ -297,9 +191,15 @@ efx_mcdi_read_sensors(
        __in            uint32_t size)
 {
        efx_mcdi_req_t req;
-       uint8_t payload[MAX(MC_CMD_READ_SENSORS_EXT_IN_LEN,
-                           MC_CMD_READ_SENSORS_EXT_OUT_LEN)];
+       EFX_MCDI_DECLARE_BUF(payload, MC_CMD_READ_SENSORS_EXT_IN_LEN,
+               MC_CMD_READ_SENSORS_EXT_OUT_LEN);
        uint32_t addr_lo, addr_hi;
+       efx_rc_t rc;
+
+       if (EFSYS_MEM_SIZE(esmp) < size) {
+               rc = EINVAL;
+               goto fail1;
+       }
 
        req.emr_cmd = MC_CMD_READ_SENSORS;
        req.emr_in_buf = payload;
@@ -317,6 +217,11 @@ efx_mcdi_read_sensors(
        efx_mcdi_execute(enp, &req);
 
        return (req.emr_rc);
+
+fail1:
+       EFSYS_PROBE1(fail1, efx_rc_t, rc);
+
+       return (rc);
 }
 
 static __checkReturn   efx_rc_t
@@ -325,8 +230,8 @@ efx_mcdi_sensor_info_npages(
        __out           uint32_t *npagesp)
 {
        efx_mcdi_req_t req;
-       uint8_t payload[MAX(MC_CMD_SENSOR_INFO_EXT_IN_LEN,
-                           MC_CMD_SENSOR_INFO_OUT_LENMAX)];
+       EFX_MCDI_DECLARE_BUF(payload, MC_CMD_SENSOR_INFO_EXT_IN_LEN,
+               MC_CMD_SENSOR_INFO_OUT_LENMAX);
        int page;
        efx_rc_t rc;
 
@@ -369,8 +274,8 @@ efx_mcdi_sensor_info(
        __in                    size_t npages)
 {
        efx_mcdi_req_t req;
-       uint8_t payload[MAX(MC_CMD_SENSOR_INFO_EXT_IN_LEN,
-                           MC_CMD_SENSOR_INFO_OUT_LENMAX)];
+       EFX_MCDI_DECLARE_BUF(payload, MC_CMD_SENSOR_INFO_EXT_IN_LEN,
+               MC_CMD_SENSOR_INFO_OUT_LENMAX);
        uint32_t page;
        efx_rc_t rc;
 
@@ -429,6 +334,86 @@ fail1:
        return (rc);
 }
 
+static __checkReturn           efx_rc_t
+efx_mcdi_sensor_info_page(
+       __in                    efx_nic_t *enp,
+       __in                    uint32_t page,
+       __out                   uint32_t *mask_part,
+       __out_ecount((sizeof (*mask_part) * 8) - 1)
+                               efx_mon_stat_limits_t *limits)
+{
+       efx_mcdi_req_t req;
+       EFX_MCDI_DECLARE_BUF(payload, MC_CMD_SENSOR_INFO_EXT_IN_LEN,
+               MC_CMD_SENSOR_INFO_OUT_LENMAX);
+       efx_rc_t rc;
+       uint32_t mask_copy;
+       efx_dword_t *maskp;
+       efx_qword_t *limit_info;
+
+       EFSYS_ASSERT(mask_part != NULL);
+       EFSYS_ASSERT(limits != NULL);
+
+       memset(limits, 0,
+           ((sizeof (*mask_part) * 8) - 1) * sizeof (efx_mon_stat_limits_t));
+
+       req.emr_cmd = MC_CMD_SENSOR_INFO;
+       req.emr_in_buf = payload;
+       req.emr_in_length = MC_CMD_SENSOR_INFO_EXT_IN_LEN;
+       req.emr_out_buf = payload;
+       req.emr_out_length = MC_CMD_SENSOR_INFO_OUT_LENMAX;
+
+       MCDI_IN_SET_DWORD(req, SENSOR_INFO_EXT_IN_PAGE, page);
+
+       efx_mcdi_execute(enp, &req);
+
+       rc = req.emr_rc;
+
+       if (rc != 0)
+               goto fail1;
+
+       EFSYS_ASSERT(sizeof (*limit_info) ==
+           MC_CMD_SENSOR_INFO_ENTRY_TYPEDEF_LEN);
+       maskp = MCDI_OUT2(req, efx_dword_t, SENSOR_INFO_OUT_MASK);
+       limit_info = (efx_qword_t *)(maskp + 1);
+
+       *mask_part = maskp->ed_u32[0];
+       mask_copy = *mask_part;
+
+       /* Copy an entry for all but the highest bit set. */
+       while (mask_copy) {
+
+               if (mask_copy == (1U << MC_CMD_SENSOR_PAGE0_NEXT)) {
+                       /* Only next page bit set. */
+                       mask_copy = 0;
+               } else {
+                       /* Clear lowest bit */
+                       mask_copy = mask_copy & ~(mask_copy ^ (mask_copy - 1));
+                       /* And copy out limit entry into buffer */
+                       limits->emlv_warning_min = EFX_QWORD_FIELD(*limit_info,
+                           MC_CMD_SENSOR_INFO_ENTRY_TYPEDEF_MIN1);
+
+                       limits->emlv_warning_max = EFX_QWORD_FIELD(*limit_info,
+                           MC_CMD_SENSOR_INFO_ENTRY_TYPEDEF_MAX1);
+
+                       limits->emlv_fatal_min = EFX_QWORD_FIELD(*limit_info,
+                           MC_CMD_SENSOR_INFO_ENTRY_TYPEDEF_MIN2);
+
+                       limits->emlv_fatal_max = EFX_QWORD_FIELD(*limit_info,
+                           MC_CMD_SENSOR_INFO_ENTRY_TYPEDEF_MAX2);
+
+                       limits++;
+                       limit_info++;
+               }
+       }
+
+       return (rc);
+
+fail1:
+       EFSYS_PROBE1(fail1, efx_rc_t, rc);
+
+       return (rc);
+}
+
        __checkReturn                   efx_rc_t
 mcdi_mon_stats_update(
        __in                            efx_nic_t *enp,
@@ -451,6 +436,96 @@ mcdi_mon_stats_update(
 
        return (0);
 
+fail1:
+       EFSYS_PROBE1(fail1, efx_rc_t, rc);
+
+       return (rc);
+}
+
+static         void
+lowest_set_bit(
+       __in    uint32_t input_mask,
+       __out   uint32_t *lowest_bit_mask,
+       __out   uint32_t *lowest_bit_num
+)
+{
+       uint32_t x;
+       uint32_t set_bit, bit_index;
+
+       x = (input_mask ^ (input_mask - 1));
+       set_bit = (x + 1) >> 1;
+       if (!set_bit)
+               set_bit = (1U << 31U);
+
+       bit_index = 0;
+       if (set_bit & 0xFFFF0000)
+               bit_index += 16;
+       if (set_bit & 0xFF00FF00)
+               bit_index += 8;
+       if (set_bit & 0xF0F0F0F0)
+               bit_index += 4;
+       if (set_bit & 0xCCCCCCCC)
+               bit_index += 2;
+       if (set_bit & 0xAAAAAAAA)
+               bit_index += 1;
+
+       *lowest_bit_mask = set_bit;
+       *lowest_bit_num = bit_index;
+}
+
+       __checkReturn                   efx_rc_t
+mcdi_mon_limits_update(
+       __in                            efx_nic_t *enp,
+       __inout_ecount(EFX_MON_NSTATS)  efx_mon_stat_limits_t *values)
+{
+       efx_rc_t rc;
+       uint32_t page;
+       uint32_t page_mask;
+       uint32_t limit_index;
+       efx_mon_stat_limits_t limits[sizeof (page_mask) * 8];
+       efx_mon_stat_t stat;
+
+       page = 0;
+       page--;
+       do {
+               page++;
+
+               rc = efx_mcdi_sensor_info_page(enp, page, &page_mask, limits);
+               if (rc != 0)
+                       goto fail1;
+
+               limit_index = 0;
+               while (page_mask) {
+                       uint32_t set_bit;
+                       uint32_t page_index;
+                       uint32_t mcdi_index;
+
+                       if (page_mask == (1U << MC_CMD_SENSOR_PAGE0_NEXT))
+                               break;
+
+                       lowest_set_bit(page_mask, &set_bit, &page_index);
+                       page_mask = page_mask & ~set_bit;
+
+                       mcdi_index =
+                           page_index + (sizeof (page_mask) * 8 * page);
+
+                       /*
+                        * This can fail if MCDI reports newer stats than the
+                        * drivers understand, or the bit is the next page bit.
+                        *
+                        * Driver needs to be tolerant of this.
+                        */
+                       if (!efx_mon_mcdi_to_efx_stat(mcdi_index, &stat))
+                               continue;
+
+                       values[stat] = limits[limit_index];
+                       limit_index++;
+               }
+
+       } while (page_mask & (1U << MC_CMD_SENSOR_PAGE0_NEXT));
+
+       return (rc);
+
 fail1:
        EFSYS_PROBE1(fail1, efx_rc_t, rc);