| /** |
| * Description: |
| * |
| * This module adds CMIS support to ethtool. The changes are similar to |
| * the ones already existing in qsfp.c, but customized to use the memory |
| * addresses and logic as defined in the specification's document. |
| * |
| */ |
| |
| #include <stdio.h> |
| #include <math.h> |
| #include <errno.h> |
| #include "internal.h" |
| #include "sff-common.h" |
| #include "cmis.h" |
| #include "netlink/extapi.h" |
| |
| /* The maximum number of supported Banks. Relevant documents: |
| * [1] CMIS Rev. 5, page. 128, section 8.4.4, Table 8-40 |
| */ |
| #define CMIS_MAX_BANKS 4 |
| #define CMIS_CHANNELS_PER_BANK 8 |
| #define CMIS_MAX_CHANNEL_NUM (CMIS_MAX_BANKS * CMIS_CHANNELS_PER_BANK) |
| |
| /* We are not parsing further than Page 11h. */ |
| #define CMIS_MAX_PAGES 18 |
| |
| struct cmis_memory_map { |
| const __u8 *lower_memory; |
| const __u8 *upper_memory[CMIS_MAX_BANKS][CMIS_MAX_PAGES]; |
| #define page_00h upper_memory[0x0][0x0] |
| #define page_01h upper_memory[0x0][0x1] |
| #define page_02h upper_memory[0x0][0x2] |
| }; |
| |
| #define CMIS_PAGE_SIZE 0x80 |
| #define CMIS_I2C_ADDRESS 0x50 |
| |
| static struct { |
| const char *str; |
| int offset; |
| __u8 value; /* Alarm is on if (offset & value) != 0. */ |
| } cmis_aw_mod_flags[] = { |
| { "Module temperature high alarm", |
| CMIS_TEMP_AW_OFFSET, CMIS_TEMP_HALARM_STATUS }, |
| { "Module temperature low alarm", |
| CMIS_TEMP_AW_OFFSET, CMIS_TEMP_LALARM_STATUS }, |
| { "Module temperature high warning", |
| CMIS_TEMP_AW_OFFSET, CMIS_TEMP_HWARN_STATUS }, |
| { "Module temperature low warning", |
| CMIS_TEMP_AW_OFFSET, CMIS_TEMP_LWARN_STATUS }, |
| |
| { "Module voltage high alarm", |
| CMIS_VCC_AW_OFFSET, CMIS_VCC_HALARM_STATUS }, |
| { "Module voltage low alarm", |
| CMIS_VCC_AW_OFFSET, CMIS_VCC_LALARM_STATUS }, |
| { "Module voltage high warning", |
| CMIS_VCC_AW_OFFSET, CMIS_VCC_HWARN_STATUS }, |
| { "Module voltage low warning", |
| CMIS_VCC_AW_OFFSET, CMIS_VCC_LWARN_STATUS }, |
| |
| { NULL, 0, 0 }, |
| }; |
| |
| static struct { |
| const char *fmt_str; |
| int offset; |
| int adver_offset; /* In Page 01h. */ |
| __u8 adver_value; /* Supported if (offset & value) != 0. */ |
| } cmis_aw_chan_flags[] = { |
| { "Laser bias current high alarm (Chan %d)", |
| CMIS_TX_BIAS_AW_HALARM_OFFSET, |
| CMIS_DIAG_CHAN_ADVER_OFFSET, CMIS_TX_BIAS_MON_MASK }, |
| { "Laser bias current low alarm (Chan %d)", |
| CMIS_TX_BIAS_AW_LALARM_OFFSET, |
| CMIS_DIAG_CHAN_ADVER_OFFSET, CMIS_TX_BIAS_MON_MASK }, |
| { "Laser bias current high warning (Chan %d)", |
| CMIS_TX_BIAS_AW_HWARN_OFFSET, |
| CMIS_DIAG_CHAN_ADVER_OFFSET, CMIS_TX_BIAS_MON_MASK }, |
| { "Laser bias current low warning (Chan %d)", |
| CMIS_TX_BIAS_AW_LWARN_OFFSET, |
| CMIS_DIAG_CHAN_ADVER_OFFSET, CMIS_TX_BIAS_MON_MASK }, |
| |
| { "Laser tx power high alarm (Channel %d)", |
| CMIS_TX_PWR_AW_HALARM_OFFSET, |
| CMIS_DIAG_CHAN_ADVER_OFFSET, CMIS_TX_PWR_MON_MASK }, |
| { "Laser tx power low alarm (Channel %d)", |
| CMIS_TX_PWR_AW_LALARM_OFFSET, |
| CMIS_DIAG_CHAN_ADVER_OFFSET, CMIS_TX_PWR_MON_MASK }, |
| { "Laser tx power high warning (Channel %d)", |
| CMIS_TX_PWR_AW_HWARN_OFFSET, |
| CMIS_DIAG_CHAN_ADVER_OFFSET, CMIS_TX_PWR_MON_MASK }, |
| { "Laser tx power low warning (Channel %d)", |
| CMIS_TX_PWR_AW_LWARN_OFFSET, |
| CMIS_DIAG_CHAN_ADVER_OFFSET, CMIS_TX_PWR_MON_MASK }, |
| |
| { "Laser rx power high alarm (Channel %d)", |
| CMIS_RX_PWR_AW_HALARM_OFFSET, |
| CMIS_DIAG_CHAN_ADVER_OFFSET, CMIS_RX_PWR_MON_MASK }, |
| { "Laser rx power low alarm (Channel %d)", |
| CMIS_RX_PWR_AW_LALARM_OFFSET, |
| CMIS_DIAG_CHAN_ADVER_OFFSET, CMIS_RX_PWR_MON_MASK }, |
| { "Laser rx power high warning (Channel %d)", |
| CMIS_RX_PWR_AW_HWARN_OFFSET, |
| CMIS_DIAG_CHAN_ADVER_OFFSET, CMIS_RX_PWR_MON_MASK }, |
| { "Laser rx power low warning (Channel %d)", |
| CMIS_RX_PWR_AW_LWARN_OFFSET, |
| CMIS_DIAG_CHAN_ADVER_OFFSET, CMIS_RX_PWR_MON_MASK }, |
| |
| { NULL, 0, 0, 0 }, |
| }; |
| |
| static void cmis_show_identifier(const struct cmis_memory_map *map) |
| { |
| sff8024_show_identifier(map->lower_memory, CMIS_ID_OFFSET); |
| } |
| |
| static void cmis_show_connector(const struct cmis_memory_map *map) |
| { |
| sff8024_show_connector(map->page_00h, CMIS_CTOR_OFFSET); |
| } |
| |
| static void cmis_show_oui(const struct cmis_memory_map *map) |
| { |
| sff8024_show_oui(map->page_00h, CMIS_VENDOR_OUI_OFFSET); |
| } |
| |
| /** |
| * Print the revision compliance. Relevant documents: |
| * [1] CMIS Rev. 3, pag. 45, section 1.7.2.1, Table 18 |
| * [2] CMIS Rev. 4, pag. 81, section 8.2.1, Table 8-2 |
| */ |
| static void cmis_show_rev_compliance(const struct cmis_memory_map *map) |
| { |
| __u8 rev = map->lower_memory[CMIS_REV_COMPLIANCE_OFFSET]; |
| int major = (rev >> 4) & 0x0F; |
| int minor = rev & 0x0F; |
| |
| printf("\t%-41s : Rev. %d.%d\n", "Revision compliance", major, minor); |
| } |
| |
| static void |
| cmis_show_signals_one(const struct cmis_memory_map *map, const char *name, |
| int off, int ioff, unsigned int imask) |
| { |
| unsigned int v; |
| int i; |
| |
| if (!map->page_01h) |
| return; |
| |
| v = 0; |
| for (i = 0; i < CMIS_MAX_BANKS && map->upper_memory[i][0x11]; i++) |
| v |= map->upper_memory[i][0x11][off] << (i * 8); |
| |
| if (map->page_01h[ioff] & imask) |
| sff_show_lane_status(name, i * 8, "Yes", "No", v); |
| } |
| |
| static void cmis_show_signals(const struct cmis_memory_map *map) |
| { |
| cmis_show_signals_one(map, "Rx loss of signal", CMIS_RX_LOS_OFFSET, |
| CMIS_DIAG_FLAGS_RX_OFFSET, CMIS_DIAG_FL_RX_LOS); |
| cmis_show_signals_one(map, "Tx loss of signal", CMIS_TX_LOS_OFFSET, |
| CMIS_DIAG_FLAGS_TX_OFFSET, CMIS_DIAG_FL_TX_LOS); |
| |
| cmis_show_signals_one(map, "Rx loss of lock", CMIS_RX_LOL_OFFSET, |
| CMIS_DIAG_FLAGS_RX_OFFSET, CMIS_DIAG_FL_RX_LOL); |
| cmis_show_signals_one(map, "Tx loss of lock", CMIS_TX_LOL_OFFSET, |
| CMIS_DIAG_FLAGS_TX_OFFSET, CMIS_DIAG_FL_TX_LOL); |
| |
| cmis_show_signals_one(map, "Tx fault", CMIS_TX_FAIL_OFFSET, |
| CMIS_DIAG_FLAGS_TX_OFFSET, CMIS_DIAG_FL_TX_FAIL); |
| |
| cmis_show_signals_one(map, "Tx adaptive eq fault", |
| CMIS_TX_EQ_FAIL_OFFSET, CMIS_DIAG_FLAGS_TX_OFFSET, |
| CMIS_DIAG_FL_TX_ADAPTIVE_EQ_FAIL); |
| } |
| |
| /** |
| * Print information about the device's power consumption. |
| * Relevant documents: |
| * [1] CMIS Rev. 3, pag. 59, section 1.7.3.9, Table 30 |
| * [2] CMIS Rev. 4, pag. 94, section 8.3.9, Table 8-18 |
| * [3] QSFP-DD Hardware Rev 5.0, pag. 22, section 4.2.1 |
| */ |
| static void cmis_show_power_info(const struct cmis_memory_map *map) |
| { |
| float max_power = 0.0f; |
| __u8 base_power = 0; |
| __u8 power_class; |
| |
| /* Get the power class (first 3 most significat bytes) */ |
| power_class = (map->page_00h[CMIS_PWR_CLASS_OFFSET] >> 5) & 0x07; |
| |
| /* Get the base power in multiples of 0.25W */ |
| base_power = map->page_00h[CMIS_PWR_MAX_POWER_OFFSET]; |
| max_power = base_power * 0.25f; |
| |
| printf("\t%-41s : %d\n", "Power class", power_class + 1); |
| printf("\t%-41s : %.02fW\n", "Max power", max_power); |
| } |
| |
| /** |
| * Print the cable assembly length, for both passive copper and active |
| * optical or electrical cables. The base length (bits 5-0) must be |
| * multiplied with the SMF length multiplier (bits 7-6) to obtain the |
| * correct value. Relevant documents: |
| * [1] CMIS Rev. 3, pag. 59, section 1.7.3.10, Table 31 |
| * [2] CMIS Rev. 4, pag. 94, section 8.3.10, Table 8-19 |
| */ |
| static void cmis_show_cbl_asm_len(const struct cmis_memory_map *map) |
| { |
| static const char *fn = "Cable assembly length"; |
| float mul = 1.0f; |
| float val = 0.0f; |
| |
| /* Check if max length */ |
| if (map->page_00h[CMIS_CBL_ASM_LEN_OFFSET] == CMIS_6300M_MAX_LEN) { |
| printf("\t%-41s : > 6.3km\n", fn); |
| return; |
| } |
| |
| /* Get the multiplier from the first two bits */ |
| switch (map->page_00h[CMIS_CBL_ASM_LEN_OFFSET] & CMIS_LEN_MUL_MASK) { |
| case CMIS_MULTIPLIER_00: |
| mul = 0.1f; |
| break; |
| case CMIS_MULTIPLIER_01: |
| mul = 1.0f; |
| break; |
| case CMIS_MULTIPLIER_10: |
| mul = 10.0f; |
| break; |
| case CMIS_MULTIPLIER_11: |
| mul = 100.0f; |
| break; |
| default: |
| break; |
| } |
| |
| /* Get base value from first 6 bits and multiply by mul */ |
| val = (map->page_00h[CMIS_CBL_ASM_LEN_OFFSET] & CMIS_LEN_VAL_MASK); |
| val = (float)val * mul; |
| printf("\t%-41s : %0.2fm\n", fn, val); |
| } |
| |
| /** |
| * Print the length for SMF fiber. The base length (bits 5-0) must be |
| * multiplied with the SMF length multiplier (bits 7-6) to obtain the |
| * correct value. Relevant documents: |
| * [1] CMIS Rev. 3, pag. 63, section 1.7.4.2, Table 39 |
| * [2] CMIS Rev. 4, pag. 99, section 8.4.2, Table 8-27 |
| */ |
| static void cmis_print_smf_cbl_len(const struct cmis_memory_map *map) |
| { |
| static const char *fn = "Length (SMF)"; |
| float mul = 1.0f; |
| float val = 0.0f; |
| |
| if (!map->page_01h) |
| return; |
| |
| /* Get the multiplier from the first two bits */ |
| switch (map->page_01h[CMIS_SMF_LEN_OFFSET] & CMIS_LEN_MUL_MASK) { |
| case CMIS_MULTIPLIER_00: |
| mul = 0.1f; |
| break; |
| case CMIS_MULTIPLIER_01: |
| mul = 1.0f; |
| break; |
| default: |
| break; |
| } |
| |
| /* Get base value from first 6 bits and multiply by mul */ |
| val = (map->page_01h[CMIS_SMF_LEN_OFFSET] & CMIS_LEN_VAL_MASK); |
| val = (float)val * mul; |
| printf("\t%-41s : %0.2fkm\n", fn, val); |
| } |
| |
| /** |
| * Print relevant signal integrity control properties. Relevant documents: |
| * [1] CMIS Rev. 3, pag. 71, section 1.7.4.10, Table 46 |
| * [2] CMIS Rev. 4, pag. 105, section 8.4.10, Table 8-34 |
| */ |
| static void cmis_show_sig_integrity(const struct cmis_memory_map *map) |
| { |
| if (!map->page_01h) |
| return; |
| |
| /* CDR Bypass control: 2nd bit from each byte */ |
| printf("\t%-41s : ", "Tx CDR bypass control"); |
| printf("%s\n", YESNO(map->page_01h[CMIS_SIG_INTEG_TX_OFFSET] & 0x02)); |
| |
| printf("\t%-41s : ", "Rx CDR bypass control"); |
| printf("%s\n", YESNO(map->page_01h[CMIS_SIG_INTEG_RX_OFFSET] & 0x02)); |
| |
| /* CDR Implementation: 1st bit from each byte */ |
| printf("\t%-41s : ", "Tx CDR"); |
| printf("%s\n", YESNO(map->page_01h[CMIS_SIG_INTEG_TX_OFFSET] & 0x01)); |
| |
| printf("\t%-41s : ", "Rx CDR"); |
| printf("%s\n", YESNO(map->page_01h[CMIS_SIG_INTEG_RX_OFFSET] & 0x01)); |
| } |
| |
| /** |
| * Print relevant media interface technology info. Relevant documents: |
| * [1] CMIS Rev. 3 |
| * --> pag. 61, section 1.7.3.14, Table 36 |
| * --> pag. 64, section 1.7.4.3, 1.7.4.4 |
| * [2] CMIS Rev. 4 |
| * --> pag. 97, section 8.3.14, Table 8-24 |
| * --> pag. 98, section 8.4, Table 8-25 |
| * --> page 100, section 8.4.3, 8.4.4 |
| */ |
| static void cmis_show_mit_compliance(const struct cmis_memory_map *map) |
| { |
| static const char *cc = " (Copper cable,"; |
| |
| printf("\t%-41s : 0x%02x", "Transmitter technology", |
| map->page_00h[CMIS_MEDIA_INTF_TECH_OFFSET]); |
| |
| switch (map->page_00h[CMIS_MEDIA_INTF_TECH_OFFSET]) { |
| case CMIS_850_VCSEL: |
| printf(" (850 nm VCSEL)\n"); |
| break; |
| case CMIS_1310_VCSEL: |
| printf(" (1310 nm VCSEL)\n"); |
| break; |
| case CMIS_1550_VCSEL: |
| printf(" (1550 nm VCSEL)\n"); |
| break; |
| case CMIS_1310_FP: |
| printf(" (1310 nm FP)\n"); |
| break; |
| case CMIS_1310_DFB: |
| printf(" (1310 nm DFB)\n"); |
| break; |
| case CMIS_1550_DFB: |
| printf(" (1550 nm DFB)\n"); |
| break; |
| case CMIS_1310_EML: |
| printf(" (1310 nm EML)\n"); |
| break; |
| case CMIS_1550_EML: |
| printf(" (1550 nm EML)\n"); |
| break; |
| case CMIS_OTHERS: |
| printf(" (Others/Undefined)\n"); |
| break; |
| case CMIS_1490_DFB: |
| printf(" (1490 nm DFB)\n"); |
| break; |
| case CMIS_COPPER_UNEQUAL: |
| printf("%s unequalized)\n", cc); |
| break; |
| case CMIS_COPPER_PASS_EQUAL: |
| printf("%s passive equalized)\n", cc); |
| break; |
| case CMIS_COPPER_NF_EQUAL: |
| printf("%s near and far end limiting active equalizers)\n", cc); |
| break; |
| case CMIS_COPPER_F_EQUAL: |
| printf("%s far end limiting active equalizers)\n", cc); |
| break; |
| case CMIS_COPPER_N_EQUAL: |
| printf("%s near end limiting active equalizers)\n", cc); |
| break; |
| case CMIS_COPPER_LINEAR_EQUAL: |
| printf("%s linear active equalizers)\n", cc); |
| break; |
| } |
| |
| if (map->page_00h[CMIS_MEDIA_INTF_TECH_OFFSET] >= CMIS_COPPER_UNEQUAL) { |
| printf("\t%-41s : %udb\n", "Attenuation at 5GHz", |
| map->page_00h[CMIS_COPPER_ATT_5GHZ]); |
| printf("\t%-41s : %udb\n", "Attenuation at 7GHz", |
| map->page_00h[CMIS_COPPER_ATT_7GHZ]); |
| printf("\t%-41s : %udb\n", "Attenuation at 12.9GHz", |
| map->page_00h[CMIS_COPPER_ATT_12P9GHZ]); |
| printf("\t%-41s : %udb\n", "Attenuation at 25.8GHz", |
| map->page_00h[CMIS_COPPER_ATT_25P8GHZ]); |
| } else if (map->page_01h) { |
| printf("\t%-41s : %.3lfnm\n", "Laser wavelength", |
| (((map->page_01h[CMIS_NOM_WAVELENGTH_MSB] << 8) | |
| map->page_01h[CMIS_NOM_WAVELENGTH_LSB]) * 0.05)); |
| printf("\t%-41s : %.3lfnm\n", "Laser wavelength tolerance", |
| (((map->page_01h[CMIS_WAVELENGTH_TOL_MSB] << 8) | |
| map->page_01h[CMIS_WAVELENGTH_TOL_LSB]) * 0.005)); |
| } |
| } |
| |
| /** |
| * Print relevant info about the maximum supported fiber media length |
| * for each type of fiber media at the maximum module-supported bit rate. |
| * Relevant documents: |
| * [1] CMIS Rev. 3, page 64, section 1.7.4.2, Table 39 |
| * [2] CMIS Rev. 4, page 99, section 8.4.2, Table 8-27 |
| */ |
| static void cmis_show_link_len(const struct cmis_memory_map *map) |
| { |
| cmis_print_smf_cbl_len(map); |
| if (!map->page_01h) |
| return; |
| sff_show_value_with_unit(map->page_01h, CMIS_OM5_LEN_OFFSET, |
| "Length (OM5)", 2, "m"); |
| sff_show_value_with_unit(map->page_01h, CMIS_OM4_LEN_OFFSET, |
| "Length (OM4)", 2, "m"); |
| sff_show_value_with_unit(map->page_01h, CMIS_OM3_LEN_OFFSET, |
| "Length (OM3 50/125um)", 2, "m"); |
| sff_show_value_with_unit(map->page_01h, CMIS_OM2_LEN_OFFSET, |
| "Length (OM2 50/125um)", 1, "m"); |
| } |
| |
| /** |
| * Show relevant information about the vendor. Relevant documents: |
| * [1] CMIS Rev. 3, page 56, section 1.7.3, Table 27 |
| * [2] CMIS Rev. 4, page 91, section 8.2, Table 8-15 |
| */ |
| static void cmis_show_vendor_info(const struct cmis_memory_map *map) |
| { |
| const char *clei; |
| |
| sff_show_ascii(map->page_00h, CMIS_VENDOR_NAME_START_OFFSET, |
| CMIS_VENDOR_NAME_END_OFFSET, "Vendor name"); |
| cmis_show_oui(map); |
| sff_show_ascii(map->page_00h, CMIS_VENDOR_PN_START_OFFSET, |
| CMIS_VENDOR_PN_END_OFFSET, "Vendor PN"); |
| sff_show_ascii(map->page_00h, CMIS_VENDOR_REV_START_OFFSET, |
| CMIS_VENDOR_REV_END_OFFSET, "Vendor rev"); |
| sff_show_ascii(map->page_00h, CMIS_VENDOR_SN_START_OFFSET, |
| CMIS_VENDOR_SN_END_OFFSET, "Vendor SN"); |
| sff_show_ascii(map->page_00h, CMIS_DATE_YEAR_OFFSET, |
| CMIS_DATE_VENDOR_LOT_OFFSET + 1, "Date code"); |
| |
| clei = (const char *)(map->page_00h + CMIS_CLEI_START_OFFSET); |
| if (*clei && strncmp(clei, CMIS_CLEI_BLANK, CMIS_CLEI_LEN)) |
| sff_show_ascii(map->page_00h, CMIS_CLEI_START_OFFSET, |
| CMIS_CLEI_END_OFFSET, "CLEI code"); |
| } |
| |
| /* Print the current Module State. Relevant documents: |
| * [1] CMIS Rev. 5, pag. 57, section 6.3.2.2, Figure 6-3 |
| * [2] CMIS Rev. 5, pag. 60, section 6.3.2.3, Figure 6-4 |
| * [3] CMIS Rev. 5, pag. 107, section 8.2.2, Table 8-6 |
| */ |
| static void cmis_show_mod_state(const struct cmis_memory_map *map) |
| { |
| __u8 mod_state; |
| |
| mod_state = (map->lower_memory[CMIS_MODULE_STATE_OFFSET] & |
| CMIS_MODULE_STATE_MASK) >> 1; |
| printf("\t%-41s : 0x%02x", "Module State", mod_state); |
| switch (mod_state) { |
| case CMIS_MODULE_STATE_MODULE_LOW_PWR: |
| printf(" (ModuleLowPwr)\n"); |
| break; |
| case CMIS_MODULE_STATE_MODULE_PWR_UP: |
| printf(" (ModulePwrUp)\n"); |
| break; |
| case CMIS_MODULE_STATE_MODULE_READY: |
| printf(" (ModuleReady)\n"); |
| break; |
| case CMIS_MODULE_STATE_MODULE_PWR_DN: |
| printf(" (ModulePwrDn)\n"); |
| break; |
| case CMIS_MODULE_STATE_MODULE_FAULT: |
| printf(" (ModuleFault)\n"); |
| break; |
| default: |
| printf(" (reserved or unknown)\n"); |
| break; |
| } |
| } |
| |
| /* Print the Module Fault Information. Relevant documents: |
| * [1] CMIS Rev. 5, pag. 64, section 6.3.2.12 |
| * [2] CMIS Rev. 5, pag. 115, section 8.2.10, Table 8-15 |
| */ |
| static void cmis_show_mod_fault_cause(const struct cmis_memory_map *map) |
| { |
| __u8 mod_state, fault_cause; |
| |
| mod_state = (map->lower_memory[CMIS_MODULE_STATE_OFFSET] & |
| CMIS_MODULE_STATE_MASK) >> 1; |
| if (mod_state != CMIS_MODULE_STATE_MODULE_FAULT) |
| return; |
| |
| fault_cause = map->lower_memory[CMIS_MODULE_FAULT_OFFSET]; |
| printf("\t%-41s : 0x%02x", "Module Fault Cause", fault_cause); |
| switch (fault_cause) { |
| case CMIS_MODULE_FAULT_NO_FAULT: |
| printf(" (No fault detected / not supported)\n"); |
| break; |
| case CMIS_MODULE_FAULT_TEC_RUNAWAY: |
| printf(" (TEC runaway)\n"); |
| break; |
| case CMIS_MODULE_FAULT_DATA_MEM_CORRUPTED: |
| printf(" (Data memory corrupted)\n"); |
| break; |
| case CMIS_MODULE_FAULT_PROG_MEM_CORRUPTED: |
| printf(" (Program memory corrupted)\n"); |
| break; |
| default: |
| printf(" (reserved or unknown)\n"); |
| break; |
| } |
| } |
| |
| /* Print the current Module-Level Controls. Relevant documents: |
| * [1] CMIS Rev. 5, pag. 58, section 6.3.2.2, Table 6-12 |
| * [2] CMIS Rev. 5, pag. 111, section 8.2.6, Table 8-10 |
| */ |
| static void cmis_show_mod_lvl_controls(const struct cmis_memory_map *map) |
| { |
| printf("\t%-41s : ", "LowPwrAllowRequestHW"); |
| printf("%s\n", ONOFF(map->lower_memory[CMIS_MODULE_CONTROL_OFFSET] & |
| CMIS_LOW_PWR_ALLOW_REQUEST_HW_MASK)); |
| printf("\t%-41s : ", "LowPwrRequestSW"); |
| printf("%s\n", ONOFF(map->lower_memory[CMIS_MODULE_CONTROL_OFFSET] & |
| CMIS_LOW_PWR_REQUEST_SW_MASK)); |
| } |
| |
| static void cmis_parse_dom_power_type(const struct cmis_memory_map *map, |
| struct sff_diags *sd) |
| { |
| sd->rx_power_type = map->page_01h[CMIS_DIAG_TYPE_OFFSET] & |
| CMIS_RX_PWR_TYPE_MASK; |
| sd->tx_power_type = map->page_01h[CMIS_DIAG_CHAN_ADVER_OFFSET] & |
| CMIS_TX_PWR_MON_MASK; |
| } |
| |
| static void cmis_parse_dom_mod_lvl_monitors(const struct cmis_memory_map *map, |
| struct sff_diags *sd) |
| { |
| sd->sfp_voltage[MCURR] = OFFSET_TO_U16_PTR(map->lower_memory, |
| CMIS_CURR_VCC_OFFSET); |
| sd->sfp_temp[MCURR] = (__s16)OFFSET_TO_U16_PTR(map->lower_memory, |
| CMIS_CURR_TEMP_OFFSET); |
| } |
| |
| static void cmis_parse_dom_mod_lvl_thresh(const struct cmis_memory_map *map, |
| struct sff_diags *sd) |
| { |
| /* Page is not present in IOCTL path. */ |
| if (!map->page_02h) |
| return; |
| sd->supports_alarms = 1; |
| |
| sd->sfp_voltage[HALRM] = OFFSET_TO_U16_PTR(map->page_02h, |
| CMIS_VCC_HALRM_OFFSET); |
| sd->sfp_voltage[LALRM] = OFFSET_TO_U16_PTR(map->page_02h, |
| CMIS_VCC_LALRM_OFFSET); |
| sd->sfp_voltage[HWARN] = OFFSET_TO_U16_PTR(map->page_02h, |
| CMIS_VCC_HWARN_OFFSET); |
| sd->sfp_voltage[LWARN] = OFFSET_TO_U16_PTR(map->page_02h, |
| CMIS_VCC_LWARN_OFFSET); |
| |
| sd->sfp_temp[HALRM] = (__s16)OFFSET_TO_U16_PTR(map->page_02h, |
| CMIS_TEMP_HALRM_OFFSET); |
| sd->sfp_temp[LALRM] = (__s16)OFFSET_TO_U16_PTR(map->page_02h, |
| CMIS_TEMP_LALRM_OFFSET); |
| sd->sfp_temp[HWARN] = (__s16)OFFSET_TO_U16_PTR(map->page_02h, |
| CMIS_TEMP_HWARN_OFFSET); |
| sd->sfp_temp[LWARN] = (__s16)OFFSET_TO_U16_PTR(map->page_02h, |
| CMIS_TEMP_LWARN_OFFSET); |
| } |
| |
| static __u8 cmis_tx_bias_mul(const struct cmis_memory_map *map) |
| { |
| switch (map->page_01h[CMIS_DIAG_CHAN_ADVER_OFFSET] & |
| CMIS_TX_BIAS_MUL_MASK) { |
| case CMIS_TX_BIAS_MUL_1: |
| return 0; |
| case CMIS_TX_BIAS_MUL_2: |
| return 1; |
| case CMIS_TX_BIAS_MUL_4: |
| return 2; |
| } |
| |
| return 0; |
| } |
| |
| static void |
| cmis_parse_dom_chan_lvl_monitors_bank(const struct cmis_memory_map *map, |
| struct sff_diags *sd, int bank) |
| { |
| const __u8 *page_11h = map->upper_memory[bank][0x11]; |
| int i; |
| |
| if (!page_11h) |
| return; |
| |
| for (i = 0; i < CMIS_CHANNELS_PER_BANK; i++) { |
| __u8 tx_bias_offset, rx_power_offset, tx_power_offset; |
| int chan = bank * CMIS_CHANNELS_PER_BANK + i; |
| __u8 bias_mul = cmis_tx_bias_mul(map); |
| |
| tx_bias_offset = CMIS_TX_BIAS_OFFSET + i * sizeof(__u16); |
| rx_power_offset = CMIS_RX_PWR_OFFSET + i * sizeof(__u16); |
| tx_power_offset = CMIS_TX_PWR_OFFSET + i * sizeof(__u16); |
| |
| sd->scd[chan].bias_cur = OFFSET_TO_U16_PTR(page_11h, |
| tx_bias_offset); |
| sd->scd[chan].bias_cur >>= bias_mul; |
| sd->scd[chan].rx_power = OFFSET_TO_U16_PTR(page_11h, |
| rx_power_offset); |
| sd->scd[chan].tx_power = OFFSET_TO_U16_PTR(page_11h, |
| tx_power_offset); |
| } |
| } |
| |
| static void cmis_parse_dom_chan_lvl_monitors(const struct cmis_memory_map *map, |
| struct sff_diags *sd) |
| { |
| int i; |
| |
| for (i = 0; i < CMIS_MAX_BANKS; i++) |
| cmis_parse_dom_chan_lvl_monitors_bank(map, sd, i); |
| } |
| |
| static void cmis_parse_dom_chan_lvl_thresh(const struct cmis_memory_map *map, |
| struct sff_diags *sd) |
| { |
| __u8 bias_mul = cmis_tx_bias_mul(map); |
| |
| if (!map->page_02h) |
| return; |
| |
| sd->bias_cur[HALRM] = OFFSET_TO_U16_PTR(map->page_02h, |
| CMIS_TX_BIAS_HALRM_OFFSET); |
| sd->bias_cur[HALRM] >>= bias_mul; |
| sd->bias_cur[LALRM] = OFFSET_TO_U16_PTR(map->page_02h, |
| CMIS_TX_BIAS_LALRM_OFFSET); |
| sd->bias_cur[LALRM] >>= bias_mul; |
| sd->bias_cur[HWARN] = OFFSET_TO_U16_PTR(map->page_02h, |
| CMIS_TX_BIAS_HWARN_OFFSET); |
| sd->bias_cur[HWARN] >>= bias_mul; |
| sd->bias_cur[LWARN] = OFFSET_TO_U16_PTR(map->page_02h, |
| CMIS_TX_BIAS_LWARN_OFFSET); |
| sd->bias_cur[LWARN] >>= bias_mul; |
| |
| sd->tx_power[HALRM] = OFFSET_TO_U16_PTR(map->page_02h, |
| CMIS_TX_PWR_HALRM_OFFSET); |
| sd->tx_power[LALRM] = OFFSET_TO_U16_PTR(map->page_02h, |
| CMIS_TX_PWR_LALRM_OFFSET); |
| sd->tx_power[HWARN] = OFFSET_TO_U16_PTR(map->page_02h, |
| CMIS_TX_PWR_HWARN_OFFSET); |
| sd->tx_power[LWARN] = OFFSET_TO_U16_PTR(map->page_02h, |
| CMIS_TX_PWR_LWARN_OFFSET); |
| |
| sd->rx_power[HALRM] = OFFSET_TO_U16_PTR(map->page_02h, |
| CMIS_RX_PWR_HALRM_OFFSET); |
| sd->rx_power[LALRM] = OFFSET_TO_U16_PTR(map->page_02h, |
| CMIS_RX_PWR_LALRM_OFFSET); |
| sd->rx_power[HWARN] = OFFSET_TO_U16_PTR(map->page_02h, |
| CMIS_RX_PWR_HWARN_OFFSET); |
| sd->rx_power[LWARN] = OFFSET_TO_U16_PTR(map->page_02h, |
| CMIS_RX_PWR_LWARN_OFFSET); |
| } |
| |
| static void cmis_parse_dom(const struct cmis_memory_map *map, |
| struct sff_diags *sd) |
| { |
| cmis_parse_dom_power_type(map, sd); |
| cmis_parse_dom_mod_lvl_monitors(map, sd); |
| cmis_parse_dom_mod_lvl_thresh(map, sd); |
| cmis_parse_dom_chan_lvl_monitors(map, sd); |
| cmis_parse_dom_chan_lvl_thresh(map, sd); |
| } |
| |
| /* Print module-level monitoring values. Relevant documents: |
| * [1] CMIS Rev. 5, page 110, section 8.2.5, Table 8-9 |
| */ |
| static void cmis_show_dom_mod_lvl_monitors(const struct sff_diags *sd) |
| { |
| PRINT_TEMP("Module temperature", sd->sfp_temp[MCURR]); |
| PRINT_VCC("Module voltage", sd->sfp_voltage[MCURR]); |
| } |
| |
| /* Print channel Tx laser bias current. Relevant documents: |
| * [1] CMIS Rev. 5, page 165, section 8.9.4, Table 8-79 |
| */ |
| static void |
| cmis_show_dom_chan_lvl_tx_bias_bank(const struct cmis_memory_map *map, |
| const struct sff_diags *sd, int bank) |
| { |
| const __u8 *page_11h = map->upper_memory[bank][0x11]; |
| int i; |
| |
| if (!page_11h) |
| return; |
| |
| for (i = 0; i < CMIS_CHANNELS_PER_BANK; i++) { |
| int chan = bank * CMIS_CHANNELS_PER_BANK + i; |
| char fmt_str[80]; |
| |
| snprintf(fmt_str, 80, "%s (Channel %d)", |
| "Laser tx bias current", chan + 1); |
| PRINT_BIAS(fmt_str, sd->scd[chan].bias_cur); |
| } |
| } |
| |
| static void cmis_show_dom_chan_lvl_tx_bias(const struct cmis_memory_map *map, |
| const struct sff_diags *sd) |
| { |
| int i; |
| |
| if(!(map->page_01h[CMIS_DIAG_CHAN_ADVER_OFFSET] & |
| CMIS_TX_BIAS_MON_MASK)) |
| return; |
| |
| for (i = 0; i < CMIS_MAX_BANKS; i++) |
| cmis_show_dom_chan_lvl_tx_bias_bank(map, sd, i); |
| } |
| |
| /* Print channel Tx average optical power. Relevant documents: |
| * [1] CMIS Rev. 5, page 165, section 8.9.4, Table 8-79 |
| */ |
| static void |
| cmis_show_dom_chan_lvl_tx_power_bank(const struct cmis_memory_map *map, |
| const struct sff_diags *sd, int bank) |
| { |
| const __u8 *page_11h = map->upper_memory[bank][0x11]; |
| int i; |
| |
| if (!page_11h) |
| return; |
| |
| for (i = 0; i < CMIS_CHANNELS_PER_BANK; i++) { |
| int chan = bank * CMIS_CHANNELS_PER_BANK + i; |
| char fmt_str[80]; |
| |
| snprintf(fmt_str, 80, "%s (Channel %d)", |
| "Transmit avg optical power", chan + 1); |
| PRINT_xX_PWR(fmt_str, sd->scd[chan].tx_power); |
| } |
| } |
| |
| static void cmis_show_dom_chan_lvl_tx_power(const struct cmis_memory_map *map, |
| const struct sff_diags *sd) |
| { |
| int i; |
| |
| if (!sd->tx_power_type) |
| return; |
| |
| for (i = 0; i < CMIS_MAX_BANKS; i++) |
| cmis_show_dom_chan_lvl_tx_power_bank(map, sd, i); |
| } |
| |
| /* Print channel Rx input optical power. Relevant documents: |
| * [1] CMIS Rev. 5, page 165, section 8.9.4, Table 8-79 |
| */ |
| static void |
| cmis_show_dom_chan_lvl_rx_power_bank(const struct cmis_memory_map *map, |
| const struct sff_diags *sd, int bank) |
| { |
| const __u8 *page_11h = map->upper_memory[bank][0x11]; |
| int i; |
| |
| if (!page_11h) |
| return; |
| |
| for (i = 0; i < CMIS_CHANNELS_PER_BANK; i++) { |
| int chan = bank * CMIS_CHANNELS_PER_BANK + i; |
| char *rx_power_str; |
| char fmt_str[80]; |
| |
| if (!sd->rx_power_type) |
| rx_power_str = "Receiver signal OMA"; |
| else |
| rx_power_str = "Rcvr signal avg optical power"; |
| |
| snprintf(fmt_str, 80, "%s (Channel %d)", rx_power_str, |
| chan + 1); |
| PRINT_xX_PWR(fmt_str, sd->scd[chan].rx_power); |
| } |
| } |
| |
| static void cmis_show_dom_chan_lvl_rx_power(const struct cmis_memory_map *map, |
| const struct sff_diags *sd) |
| { |
| int i; |
| |
| if(!(map->page_01h[CMIS_DIAG_CHAN_ADVER_OFFSET] & CMIS_RX_PWR_MON_MASK)) |
| return; |
| |
| for (i = 0; i < CMIS_MAX_BANKS; i++) |
| cmis_show_dom_chan_lvl_rx_power_bank(map, sd, i); |
| } |
| |
| static void cmis_show_dom_chan_lvl_monitors(const struct cmis_memory_map *map, |
| const struct sff_diags *sd) |
| { |
| cmis_show_dom_chan_lvl_tx_bias(map, sd); |
| cmis_show_dom_chan_lvl_tx_power(map, sd); |
| cmis_show_dom_chan_lvl_rx_power(map, sd); |
| } |
| |
| /* Print module-level flags. Relevant documents: |
| * [1] CMIS Rev. 5, page 109, section 8.2.4, Table 8-8 |
| */ |
| static void cmis_show_dom_mod_lvl_flags(const struct cmis_memory_map *map) |
| { |
| int i; |
| |
| for (i = 0; cmis_aw_mod_flags[i].str; i++) { |
| printf("\t%-41s : %s\n", cmis_aw_mod_flags[i].str, |
| map->lower_memory[cmis_aw_mod_flags[i].offset] & |
| cmis_aw_mod_flags[i].value ? "On" : "Off"); |
| } |
| } |
| |
| /* Print channel-level flags. Relevant documents: |
| * [1] CMIS Rev. 5, page 162, section 8.9.3, Table 8-77 |
| * [1] CMIS Rev. 5, page 164, section 8.9.3, Table 8-78 |
| */ |
| static void cmis_show_dom_chan_lvl_flags_chan(const struct cmis_memory_map *map, |
| int bank, int chan) |
| { |
| const __u8 *page_11h = map->upper_memory[bank][0x11]; |
| int i; |
| |
| for (i = 0; cmis_aw_chan_flags[i].fmt_str; i++) { |
| char str[80]; |
| |
| if (!(map->page_01h[cmis_aw_chan_flags[i].adver_offset] & |
| cmis_aw_chan_flags[i].adver_value)) |
| continue; |
| |
| snprintf(str, 80, cmis_aw_chan_flags[i].fmt_str, chan + 1); |
| printf("\t%-41s : %s\n", str, |
| page_11h[cmis_aw_chan_flags[i].offset] & chan ? |
| "On" : "Off"); |
| } |
| } |
| |
| static void |
| cmis_show_dom_chan_lvl_flags_bank(const struct cmis_memory_map *map, |
| int bank) |
| { |
| const __u8 *page_11h = map->upper_memory[bank][0x11]; |
| int i; |
| |
| if (!page_11h) |
| return; |
| |
| for (i = 0; i < CMIS_CHANNELS_PER_BANK; i++) { |
| int chan = bank * CMIS_CHANNELS_PER_BANK + i; |
| |
| cmis_show_dom_chan_lvl_flags_chan(map, bank, chan); |
| } |
| } |
| |
| static void cmis_show_dom_chan_lvl_flags(const struct cmis_memory_map *map) |
| { |
| int i; |
| |
| for (i = 0; i < CMIS_MAX_BANKS; i++) |
| cmis_show_dom_chan_lvl_flags_bank(map, i); |
| } |
| |
| |
| static void cmis_show_dom(const struct cmis_memory_map *map) |
| { |
| struct sff_diags sd = {}; |
| |
| /* Diagnostic information is only relevant when the module memory |
| * model is paged and not flat. |
| */ |
| if (map->lower_memory[CMIS_MEMORY_MODEL_OFFSET] & |
| CMIS_MEMORY_MODEL_MASK) |
| return; |
| |
| cmis_parse_dom(map, &sd); |
| |
| cmis_show_dom_mod_lvl_monitors(&sd); |
| cmis_show_dom_chan_lvl_monitors(map, &sd); |
| cmis_show_dom_mod_lvl_flags(map); |
| cmis_show_dom_chan_lvl_flags(map); |
| if (sd.supports_alarms) |
| sff_show_thresholds(sd); |
| } |
| |
| /* Print active and inactive firmware versions. Relevant documents: |
| * [1] CMIS Rev. 5, page 115, section 8.2.9, Table 8-14 |
| * [2] CMIS Rev. 5, page 127, section 8.4.1, Table 8-37 |
| */ |
| static void cmis_show_fw_version_common(const char *name, __u8 major, |
| __u8 minor) |
| { |
| if (major == 0 && minor == 0) { |
| return; |
| } else if (major == 0xFF && minor == 0xFF) { |
| printf("\t%-41s : Invalid\n", name); |
| return; |
| } |
| |
| printf("\t%-41s : %d.%d\n", name, major, minor); |
| } |
| |
| static void cmis_show_fw_active_version(const struct cmis_memory_map *map) |
| { |
| __u8 major = map->lower_memory[CMIS_MODULE_ACTIVE_FW_MAJOR_OFFSET]; |
| __u8 minor = map->lower_memory[CMIS_MODULE_ACTIVE_FW_MINOR_OFFSET]; |
| |
| cmis_show_fw_version_common("Active firmware version", major, minor); |
| } |
| |
| static void cmis_show_fw_inactive_version(const struct cmis_memory_map *map) |
| { |
| __u8 major; |
| __u8 minor; |
| |
| if (!map->page_01h) |
| return; |
| |
| major = map->page_01h[CMIS_MODULE_INACTIVE_FW_MAJOR_OFFSET]; |
| minor = map->page_01h[CMIS_MODULE_INACTIVE_FW_MINOR_OFFSET]; |
| cmis_show_fw_version_common("Inactive firmware version", major, minor); |
| } |
| |
| static void cmis_show_fw_version(const struct cmis_memory_map *map) |
| { |
| cmis_show_fw_active_version(map); |
| cmis_show_fw_inactive_version(map); |
| } |
| |
| static u8 cmis_cdb_instances_get(const struct cmis_memory_map *map) |
| { |
| return (map->page_01h[CMIS_CDB_ADVER_OFFSET] & |
| CMIS_CDB_ADVER_INSTANCES_MASK) >> 6; |
| } |
| |
| static bool cmis_cdb_is_supported(const struct cmis_memory_map *map) |
| { |
| __u8 cdb_instances = cmis_cdb_instances_get(map); |
| |
| /* Up to two CDB instances are supported. */ |
| return cdb_instances == 1 || cdb_instances == 2; |
| } |
| |
| static void cmis_show_cdb_instances(const struct cmis_memory_map *map) |
| { |
| __u8 cdb_instances = cmis_cdb_instances_get(map); |
| |
| printf("\t%-41s : %u\n", "CDB instances", cdb_instances); |
| } |
| |
| static void cmis_show_cdb_mode(const struct cmis_memory_map *map) |
| { |
| __u8 mode = map->page_01h[CMIS_CDB_ADVER_OFFSET] & |
| CMIS_CDB_ADVER_MODE_MASK; |
| |
| printf("\t%-41s : %s\n", "CDB background mode", |
| mode ? "Supported" : "Not supported"); |
| } |
| |
| static void cmis_show_cdb_epl_pages(const struct cmis_memory_map *map) |
| { |
| __u8 epl_pages = map->page_01h[CMIS_CDB_ADVER_OFFSET] & |
| CMIS_CDB_ADVER_EPL_MASK; |
| |
| printf("\t%-41s : %u\n", "CDB EPL pages", epl_pages); |
| } |
| |
| static void cmis_show_cdb_rw_len(const struct cmis_memory_map *map) |
| { |
| __u16 rw_len = map->page_01h[CMIS_CDB_ADVER_RW_LEN_OFFSET]; |
| |
| /* Maximum read / write length for CDB EPL pages and the LPL page in |
| * units of 8 bytes, in addition to the minimum 8 bytes. |
| */ |
| rw_len = (rw_len + 1) * 8; |
| printf("\t%-41s : %u\n", "CDB Maximum EPL RW length", rw_len); |
| printf("\t%-41s : %u\n", "CDB Maximum LPL RW length", |
| rw_len > CMIS_PAGE_SIZE ? CMIS_PAGE_SIZE : rw_len); |
| } |
| |
| static void cmis_show_cdb_trigger(const struct cmis_memory_map *map) |
| { |
| __u8 trigger = map->page_01h[CMIS_CDB_ADVER_TRIGGER_OFFSET] & |
| CMIS_CDB_ADVER_TRIGGER_MASK; |
| |
| /* Whether a CDB command can be triggered in a single write to the LPL |
| * page, or by multiple writes ending with the writing of the CDB |
| * Command Code (CMDID). |
| */ |
| printf("\t%-41s : %s\n", "CDB trigger method", |
| trigger ? "Single write" : "Multiple writes"); |
| } |
| |
| /* Print CDB messaging support advertisement. Relevant documents: |
| * [1] CMIS Rev. 5, page 133, section 8.4.11 |
| */ |
| static void cmis_show_cdb_adver(const struct cmis_memory_map *map) |
| { |
| if (!map->page_01h || !cmis_cdb_is_supported(map)) |
| return; |
| |
| cmis_show_cdb_instances(map); |
| cmis_show_cdb_mode(map); |
| cmis_show_cdb_epl_pages(map); |
| cmis_show_cdb_rw_len(map); |
| cmis_show_cdb_trigger(map); |
| } |
| |
| static void cmis_show_all_common(const struct cmis_memory_map *map) |
| { |
| cmis_show_identifier(map); |
| cmis_show_power_info(map); |
| cmis_show_connector(map); |
| cmis_show_cbl_asm_len(map); |
| cmis_show_sig_integrity(map); |
| cmis_show_mit_compliance(map); |
| cmis_show_link_len(map); |
| cmis_show_vendor_info(map); |
| cmis_show_rev_compliance(map); |
| cmis_show_signals(map); |
| cmis_show_mod_state(map); |
| cmis_show_mod_fault_cause(map); |
| cmis_show_mod_lvl_controls(map); |
| cmis_show_dom(map); |
| cmis_show_fw_version(map); |
| cmis_show_cdb_adver(map); |
| } |
| |
| static void cmis_memory_map_init_buf(struct cmis_memory_map *map, |
| const __u8 *id) |
| { |
| /* Lower Memory and Page 00h are always present. |
| * |
| * Offset into Upper Memory is between page size and twice the page |
| * size. Therefore, set the base address of each page to base address |
| * plus page size multiplied by the page number. |
| */ |
| map->lower_memory = id; |
| map->page_00h = id; |
| |
| /* Page 01h is only present when the module memory model is paged and |
| * not flat. |
| */ |
| if (map->lower_memory[CMIS_MEMORY_MODEL_OFFSET] & |
| CMIS_MEMORY_MODEL_MASK) |
| return; |
| |
| map->page_01h = id + CMIS_PAGE_SIZE; |
| } |
| |
| void cmis_show_all_ioctl(const __u8 *id) |
| { |
| struct cmis_memory_map map = {}; |
| |
| cmis_memory_map_init_buf(&map, id); |
| cmis_show_all_common(&map); |
| } |
| |
| static void cmis_request_init(struct ethtool_module_eeprom *request, u8 bank, |
| u8 page, u32 offset) |
| { |
| request->offset = offset; |
| request->length = CMIS_PAGE_SIZE; |
| request->page = page; |
| request->bank = bank; |
| request->i2c_address = CMIS_I2C_ADDRESS; |
| request->data = NULL; |
| } |
| |
| static int cmis_num_banks_get(const struct cmis_memory_map *map, |
| int *p_num_banks) |
| { |
| switch (map->page_01h[CMIS_PAGES_ADVER_OFFSET] & |
| CMIS_BANKS_SUPPORTED_MASK) { |
| case CMIS_BANK_0_SUPPORTED: |
| *p_num_banks = 1; |
| break; |
| case CMIS_BANK_0_1_SUPPORTED: |
| *p_num_banks = 2; |
| break; |
| case CMIS_BANK_0_3_SUPPORTED: |
| *p_num_banks = 4; |
| break; |
| default: |
| return -EINVAL; |
| } |
| |
| return 0; |
| } |
| |
| static int |
| cmis_memory_map_init_pages(struct cmd_context *ctx, |
| struct cmis_memory_map *map) |
| { |
| struct ethtool_module_eeprom request; |
| int num_banks, i, ret; |
| |
| /* Lower Memory and Page 00h are always present. |
| * |
| * Offset into Upper Memory is between page size and twice the page |
| * size. Therefore, set the base address of each page to its base |
| * address minus page size. |
| */ |
| cmis_request_init(&request, 0, 0x0, 0); |
| ret = nl_get_eeprom_page(ctx, &request); |
| if (ret < 0) |
| return ret; |
| map->lower_memory = request.data; |
| |
| cmis_request_init(&request, 0, 0x0, CMIS_PAGE_SIZE); |
| ret = nl_get_eeprom_page(ctx, &request); |
| if (ret < 0) |
| return ret; |
| map->page_00h = request.data - CMIS_PAGE_SIZE; |
| |
| /* Pages 01h and 02h are only present when the module memory model is |
| * paged and not flat. |
| */ |
| if (map->lower_memory[CMIS_MEMORY_MODEL_OFFSET] & |
| CMIS_MEMORY_MODEL_MASK) |
| return 0; |
| |
| cmis_request_init(&request, 0, 0x1, CMIS_PAGE_SIZE); |
| ret = nl_get_eeprom_page(ctx, &request); |
| if (ret < 0) |
| return ret; |
| map->page_01h = request.data - CMIS_PAGE_SIZE; |
| |
| cmis_request_init(&request, 0, 0x2, CMIS_PAGE_SIZE); |
| ret = nl_get_eeprom_page(ctx, &request); |
| if (ret < 0) |
| return ret; |
| map->page_02h = request.data - CMIS_PAGE_SIZE; |
| |
| /* Bank 0 of Page 11h provides lane-specific registers for the first 8 |
| * lanes, and each additional Banks provides support for an additional |
| * 8 lanes. Only initialize supported Banks. |
| */ |
| ret = cmis_num_banks_get(map, &num_banks); |
| if (ret < 0) |
| return ret; |
| |
| for (i = 0; i < num_banks; i++) { |
| cmis_request_init(&request, i, 0x11, CMIS_PAGE_SIZE); |
| ret = nl_get_eeprom_page(ctx, &request); |
| if (ret < 0) |
| return ret; |
| map->upper_memory[i][0x11] = request.data - CMIS_PAGE_SIZE; |
| } |
| |
| return 0; |
| } |
| |
| int cmis_show_all_nl(struct cmd_context *ctx) |
| { |
| struct cmis_memory_map map = {}; |
| int ret; |
| |
| ret = cmis_memory_map_init_pages(ctx, &map); |
| if (ret < 0) |
| return ret; |
| cmis_show_all_common(&map); |
| |
| return 0; |
| } |