diff options
author | Russell King (Oracle) <rmk+kernel@armlinux.org.uk> | 2022-01-18 10:18:55 +0000 |
---|---|---|
committer | Russell King (Oracle) <rmk+kernel@armlinux.org.uk> | 2022-01-18 10:18:55 +0000 |
commit | 51efd61e20a08e1a262c3b8b6219600ebca2006c (patch) | |
tree | 887b7604bffd071d5a07640040cc8c9d7f7b09a6 /drivers/net/phy/sfp.c | |
parent | 956c86b60ce352b3e195c6f0700a07176989d356 (diff) | |
parent | e2365fc3e4a0e134ee7f875ce71f0abaaeb4552d (diff) |
Merge branches 'mvebu-cpuidle' and 'mvneta' into clearfog
Diffstat (limited to 'drivers/net/phy/sfp.c')
-rw-r--r-- | drivers/net/phy/sfp.c | 269 |
1 files changed, 249 insertions, 20 deletions
diff --git a/drivers/net/phy/sfp.c b/drivers/net/phy/sfp.c index ab77a9f439ef..4c900d063b19 100644 --- a/drivers/net/phy/sfp.c +++ b/drivers/net/phy/sfp.c @@ -18,6 +18,7 @@ #include <linux/slab.h> #include <linux/workqueue.h> +#include "sff.h" #include "sfp.h" #include "swphy.h" @@ -168,6 +169,7 @@ static const enum gpiod_flags gpio_flags[] = { #define T_WAIT msecs_to_jiffies(50) #define T_START_UP msecs_to_jiffies(300) #define T_START_UP_BAD_GPON msecs_to_jiffies(60000) +#define T_START_UP_COOLED msecs_to_jiffies(90000) /* t_reset is the time required to assert the TX_DISABLE signal to reset * an indicated TX_FAULT. @@ -303,6 +305,7 @@ static const struct sff_data sfp_data = { static const struct of_device_id sfp_of_match[] = { { .compatible = "sff,sff", .data = &sff_data, }, { .compatible = "sff,sfp", .data = &sfp_data, }, + { .compatible = "sff,sfp+", .data = &sfp_data, }, { }, }; MODULE_DEVICE_TABLE(of, sfp_of_match); @@ -1396,6 +1399,114 @@ static void sfp_hwmon_exit(struct sfp *sfp) } #endif +static const struct sff_bitfield sfp_options[] = { + { + .mask = SFP_OPTIONS_HIGH_POWER_LEVEL, + .val = SFP_OPTIONS_HIGH_POWER_LEVEL, + .str = "hpl", + }, { + .mask = SFP_OPTIONS_PAGING_A2, + .val = SFP_OPTIONS_PAGING_A2, + .str = "paginga2", + }, { + .mask = SFP_OPTIONS_RETIMER, + .val = SFP_OPTIONS_RETIMER, + .str = "retimer", + }, { + .mask = SFP_OPTIONS_COOLED_XCVR, + .val = SFP_OPTIONS_COOLED_XCVR, + .str = "cooled", + }, { + .mask = SFP_OPTIONS_POWER_DECL, + .val = SFP_OPTIONS_POWER_DECL, + .str = "powerdecl", + }, { + .mask = SFP_OPTIONS_RX_LINEAR_OUT, + .val = SFP_OPTIONS_RX_LINEAR_OUT, + .str = "rxlinear", + }, { + .mask = SFP_OPTIONS_RX_DECISION_THRESH, + .val = SFP_OPTIONS_RX_DECISION_THRESH, + .str = "rxthresh", + }, { + .mask = SFP_OPTIONS_TUNABLE_TX, + .val = SFP_OPTIONS_TUNABLE_TX, + .str = "tunabletx", + }, { + .mask = SFP_OPTIONS_RATE_SELECT, + .val = SFP_OPTIONS_RATE_SELECT, + .str = "ratesel", + }, { + .mask = SFP_OPTIONS_TX_DISABLE, + .val = SFP_OPTIONS_TX_DISABLE, + .str = "txdisable", + }, { + .mask = SFP_OPTIONS_TX_FAULT, + .val = SFP_OPTIONS_TX_FAULT, + .str = "txfault", + }, { + .mask = SFP_OPTIONS_LOS_INVERTED, + .val = SFP_OPTIONS_LOS_INVERTED, + .str = "los-", + }, { + .mask = SFP_OPTIONS_LOS_NORMAL, + .val = SFP_OPTIONS_LOS_NORMAL, + .str = "los+", + }, { } +}; + +static const struct sff_bitfield diagmon[] = { + { + .mask = SFP_DIAGMON_DDM, + .val = SFP_DIAGMON_DDM, + .str = "ddm", + }, { + .mask = SFP_DIAGMON_INT_CAL, + .val = SFP_DIAGMON_INT_CAL, + .str = "intcal", + }, { + .mask = SFP_DIAGMON_EXT_CAL, + .val = SFP_DIAGMON_EXT_CAL, + .str = "extcal", + }, { + .mask = SFP_DIAGMON_RXPWR_AVG, + .val = SFP_DIAGMON_RXPWR_AVG, + .str = "rxpwravg", + }, { } +}; + +static const struct sff_bitfield sfp_enhopts[] = { + { + .mask = SFP_ENHOPTS_ALARMWARN, + .val = SFP_ENHOPTS_ALARMWARN, + .str = "alarmwarn", + }, { + .mask = SFP_ENHOPTS_SOFT_TX_DISABLE, + .val = SFP_ENHOPTS_SOFT_TX_DISABLE, + .str = "soft_tx_dis", + }, { + .mask = SFP_ENHOPTS_SOFT_TX_FAULT, + .val = SFP_ENHOPTS_SOFT_TX_FAULT, + .str = "soft_tx_fault", + }, { + .mask = SFP_ENHOPTS_SOFT_RX_LOS, + .val = SFP_ENHOPTS_SOFT_RX_LOS, + .str = "soft_rx_los", + }, { + .mask = SFP_ENHOPTS_SOFT_RATE_SELECT, + .val = SFP_ENHOPTS_SOFT_RATE_SELECT, + .str = "soft_rs", + }, { + .mask = SFP_ENHOPTS_APP_SELECT_SFF8079, + .val = SFP_ENHOPTS_APP_SELECT_SFF8079, + .str = "app_sel", + }, { + .mask = SFP_ENHOPTS_SOFT_RATE_SFF8431, + .val = SFP_ENHOPTS_SOFT_RATE_SFF8431, + .str = "soft_r8431", + }, { } +}; + /* Helpers */ static void sfp_module_tx_disable(struct sfp *sfp) { @@ -1641,17 +1752,20 @@ static int sfp_sm_probe_for_phy(struct sfp *sfp) static int sfp_module_parse_power(struct sfp *sfp) { u32 power_mW = 1000; + bool supports_a2; if (sfp->id.ext.options & cpu_to_be16(SFP_OPTIONS_POWER_DECL)) power_mW = 1500; if (sfp->id.ext.options & cpu_to_be16(SFP_OPTIONS_HIGH_POWER_LEVEL)) power_mW = 2000; + supports_a2 = sfp->id.ext.sff8472_compliance != + SFP_SFF8472_COMPLIANCE_NONE || + sfp->id.ext.diagmon & SFP_DIAGMON_DDM; + if (power_mW > sfp->max_power_mW) { /* Module power specification exceeds the allowed maximum. */ - if (sfp->id.ext.sff8472_compliance == - SFP_SFF8472_COMPLIANCE_NONE && - !(sfp->id.ext.diagmon & SFP_DIAGMON_DDM)) { + if (!supports_a2) { /* The module appears not to implement bus address * 0xa2, so assume that the module powers up in the * indicated mode. @@ -1668,11 +1782,25 @@ static int sfp_module_parse_power(struct sfp *sfp) } } + if (power_mW <= 1000) { + /* Modules below 1W do not require a power change sequence */ + sfp->module_power_mW = power_mW; + return 0; + } + + if (!supports_a2) { + /* The module power level is below the host maximum and the + * module appears not to implement bus address 0xa2, so assume + * that the module powers up in the indicated mode. + */ + return 0; + } + /* If the module requires a higher power mode, but also requires * an address change sequence, warn the user that the module may * not be functional. */ - if (sfp->id.ext.diagmon & SFP_DIAGMON_ADDRMODE && power_mW > 1000) { + if (sfp->id.ext.diagmon & SFP_DIAGMON_ADDRMODE) { dev_warn(sfp->dev, "Address Change Sequence not supported but module requires %u.%uW, module may not be functional\n", power_mW / 1000, (power_mW / 100) % 10); @@ -1779,6 +1907,110 @@ static int sfp_cotsworks_fixup_check(struct sfp *sfp, struct sfp_eeprom_id *id) return 0; } +static void sfp_print_module_info(struct sfp *sfp, const struct sfp_eeprom_id *id, bool cotsworks) +{ + unsigned int br_nom, br_min, br_max; + char date[9]; + char options[80]; + + /* Cotsworks also gets the date code wrong. */ + date[0] = id->ext.datecode[4 - 2 * cotsworks]; + date[1] = id->ext.datecode[5 - 2 * cotsworks]; + date[2] = '-'; + date[3] = id->ext.datecode[2 + 2 * cotsworks]; + date[4] = id->ext.datecode[3 + 2 * cotsworks]; + date[5] = '-'; + date[6] = id->ext.datecode[0]; + date[7] = id->ext.datecode[1]; + date[8] = '\0'; + + if (id->base.br_nominal == 0) { + br_min = br_nom = br_max = 0; + } else if (id->base.br_nominal == 255) { + br_nom = 250 * id->ext.br_max; + br_max = br_nom + br_nom * id->ext.br_min / 100; + br_min = br_nom - br_nom * id->ext.br_min / 100; + } else { + br_nom = id->base.br_nominal * 100; + br_min = br_nom - id->base.br_nominal * id->ext.br_min; + br_max = br_nom + id->base.br_nominal * id->ext.br_max; + } + + dev_info(sfp->dev, "module %.*s %.*s rev %.*s sn %.*s dc %s\n", + (int)sizeof(id->base.vendor_name), id->base.vendor_name, + (int)sizeof(id->base.vendor_pn), id->base.vendor_pn, + (int)sizeof(id->base.vendor_rev), id->base.vendor_rev, + (int)sizeof(id->ext.vendor_sn), id->ext.vendor_sn, date); + dev_info(sfp->dev, " %s connector, encoding %s, bitrate %u.%03u (%u.%03u-%u.%03u) Gbps\n", + sff_connector(id->base.connector), + sff_encoding(id->base.encoding), + br_nom / 1000, br_nom % 1000, + br_min / 1000, br_min % 1000, br_max / 1000, br_max % 1000); + dev_info(sfp->dev, " 1000BaseSX%c 1000BaseLX%c 1000BaseCX%c 1000BaseT%c 100BaseLX%c 100BaseFX%c BaseBX10%c BasePX%c\n", + id->base.e1000_base_sx ? '+' : '-', + id->base.e1000_base_lx ? '+' : '-', + id->base.e1000_base_cx ? '+' : '-', + id->base.e1000_base_t ? '+' : '-', + id->base.e100_base_lx ? '+' : '-', + id->base.e100_base_fx ? '+' : '-', + id->base.e_base_bx10 ? '+' : '-', + id->base.e_base_px ? '+' : '-'); + dev_info(sfp->dev, " 10GBaseSR%c 10GBaseLR%c 10GBaseLRM%c 10GBaseER%c\n", + id->base.e10g_base_sr ? '+' : '-', + id->base.e10g_base_lr ? '+' : '-', + id->base.e10g_base_lrm ? '+' : '-', + id->base.e10g_base_er ? '+' : '-'); + + if (!id->base.sfp_ct_passive && !id->base.sfp_ct_active && + !id->base.e1000_base_t) { + char len_9um[16], len_om[16]; + + dev_info(sfp->dev, " Wavelength %unm, fiber lengths:\n", + be16_to_cpup(&id->base.optical_wavelength)); + + if (id->base.link_len[0] == 255) + strcpy(len_9um, ">254km"); + else if (id->base.link_len[1] && id->base.link_len[1] != 255) + sprintf(len_9um, "%um", + id->base.link_len[1] * 100); + else if (id->base.link_len[0]) + sprintf(len_9um, "%ukm", id->base.link_len[0]); + else if (id->base.link_len[1] == 255) + strcpy(len_9um, ">25.4km"); + else + strcpy(len_9um, "unsupported"); + + dev_info(sfp->dev, " 9µm SM : %s\n", len_9um); + dev_info(sfp->dev, " 62.5µm MM OM1: %s\n", + sff_link_len(len_om, sizeof(len_om), + id->base.link_len[3], 10)); + dev_info(sfp->dev, " 50µm MM OM2: %s\n", + sff_link_len(len_om, sizeof(len_om), + id->base.link_len[2], 10)); + dev_info(sfp->dev, " 50µm MM OM3: %s\n", + sff_link_len(len_om, sizeof(len_om), + id->base.link_len[5], 10)); + dev_info(sfp->dev, " 50µm MM OM4: %s\n", + sff_link_len(len_om, sizeof(len_om), + id->base.link_len[4], 10)); + } else { + char len[16]; + dev_info(sfp->dev, " Copper length: %s\n", + sff_link_len(len, sizeof(len), + id->base.link_len[4], 1)); + } + + dev_info(sfp->dev, " Options: %s\n", + sff_bitfield(options, sizeof(options), sfp_options, + be16_to_cpu(id->ext.options))); + dev_info(sfp->dev, " Diagnostics: %s\n", + sff_bitfield(options, sizeof(options), diagmon, + id->ext.diagmon)); + dev_info(sfp->dev, " EnhOpts: %s\n", + sff_bitfield(options, sizeof(options), sfp_enhopts, + id->ext.enhopts)); +} + static int sfp_sm_mod_probe(struct sfp *sfp, bool report) { /* SFP module inserted - read I2C data */ @@ -1833,9 +2065,9 @@ static int sfp_sm_mod_probe(struct sfp *sfp, bool report) } } - /* Cotsworks do not seem to update the checksums when they - * do the final programming with the final module part number, - * serial number and date code. + /* Cotsworks do not seem to update the checksums when they update the + * module part number, serial number and date code. They also format + * the date code incorrectly. */ cotsworks = !memcmp(id.base.vendor_name, "COTSWORKS ", 16); cotsworks_sfbg = !memcmp(id.base.vendor_pn, "SFBG", 4); @@ -1895,14 +2127,9 @@ static int sfp_sm_mod_probe(struct sfp *sfp, bool report) } } - sfp->id = id; + sfp_print_module_info(sfp, &id, cotsworks); - dev_info(sfp->dev, "module %.*s %.*s rev %.*s sn %.*s dc %.*s\n", - (int)sizeof(id.base.vendor_name), id.base.vendor_name, - (int)sizeof(id.base.vendor_pn), id.base.vendor_pn, - (int)sizeof(id.base.vendor_rev), id.base.vendor_rev, - (int)sizeof(id.ext.vendor_sn), id.ext.vendor_sn, - (int)sizeof(id.ext.datecode), id.ext.datecode); + sfp->id = id; /* Check whether we support this module */ if (!sfp->type->module_supported(&id)) { @@ -1925,6 +2152,8 @@ static int sfp_sm_mod_probe(struct sfp *sfp, bool report) if (!memcmp(id.base.vendor_name, "ALCATELLUCENT ", 16) && !memcmp(id.base.vendor_pn, "3FE46541AA ", 16)) sfp->module_t_start_up = T_START_UP_BAD_GPON; + else if (id.ext.options & cpu_to_be16(SFP_OPTIONS_COOLED_XCVR)) + sfp->module_t_start_up = T_START_UP_COOLED; else sfp->module_t_start_up = T_START_UP; @@ -2131,10 +2360,10 @@ static void sfp_sm_main(struct sfp *sfp, unsigned int event) break; if (sfp->state & SFP_F_TX_FAULT) { - /* Wait up to t_init (SFF-8472) or t_start_up (SFF-8431) - * from the TX_DISABLE deassertion for the module to - * initialise, which is indicated by TX_FAULT - * deasserting. + /* Wait up to t_init (SFF-8472), t_start_up (SFF-8431), + * or t_start_up_cooled (SFF-8431) from the TX_DISABLE + * deassertion for the module to initialise, which is + * indicated by TX_FAULT deasserting. */ timeout = sfp->module_t_start_up; if (timeout > T_WAIT) @@ -2153,8 +2382,8 @@ static void sfp_sm_main(struct sfp *sfp, unsigned int event) case SFP_S_INIT: if (event == SFP_E_TIMEOUT && sfp->state & SFP_F_TX_FAULT) { - /* TX_FAULT is still asserted after t_init - * or t_start_up, so assume there is a fault. + /* TX_FAULT is still asserted after t_init, t_start_up + * or t_start_up_cooled, so assume there is a fault. */ sfp_sm_fault(sfp, SFP_S_INIT_TX_FAULT, sfp->sm_fault_retries == N_FAULT_INIT); |