diff options
Diffstat (limited to 'drivers/net/phy/sfp.c')
| -rw-r--r-- | drivers/net/phy/sfp.c | 205 |
1 files changed, 144 insertions, 61 deletions
diff --git a/drivers/net/phy/sfp.c b/drivers/net/phy/sfp.c index a5684ef5884b..0401fa6b24d2 100644 --- a/drivers/net/phy/sfp.c +++ b/drivers/net/phy/sfp.c @@ -17,7 +17,6 @@ #include <linux/workqueue.h> #include "sfp.h" -#include "swphy.h" enum { GPIO_MODDEF0, @@ -221,6 +220,8 @@ static const enum gpiod_flags gpio_flags[] = { */ #define SFP_EEPROM_BLOCK_SIZE 16 +#define SFP_POLL_INTERVAL msecs_to_jiffies(100) + struct sff_data { unsigned int gpios; bool (*module_supported)(const struct sfp_eeprom_id *id); @@ -234,6 +235,7 @@ struct sfp { enum mdio_i2c_proto mdio_protocol; struct phy_device *mod_phy; const struct sff_data *type; + size_t i2c_max_block_size; size_t i2c_block_size; u32 max_power_mW; @@ -298,6 +300,11 @@ struct sfp { #endif }; +static void sfp_schedule_poll(struct sfp *sfp) +{ + mod_delayed_work(system_percpu_wq, &sfp->poll, SFP_POLL_INTERVAL); +} + static bool sff_module_supported(const struct sfp_eeprom_id *id) { return id->base.phys_id == SFF8024_ID_SFF_8472 && @@ -360,6 +367,11 @@ static void sfp_fixup_ignore_tx_fault(struct sfp *sfp) sfp->state_ignore_mask |= SFP_F_TX_FAULT; } +static void sfp_fixup_ignore_hw(struct sfp *sfp, unsigned int mask) +{ + sfp->state_hw_mask &= ~mask; +} + static void sfp_fixup_nokia(struct sfp *sfp) { sfp_fixup_long_startup(sfp); @@ -385,7 +397,7 @@ static void sfp_fixup_rollball(struct sfp *sfp) sfp->phy_t_retry = msecs_to_jiffies(1000); } -static void sfp_fixup_fs_2_5gt(struct sfp *sfp) +static void sfp_fixup_rollball_wait4s(struct sfp *sfp) { sfp_fixup_rollball(sfp); @@ -399,7 +411,7 @@ static void sfp_fixup_fs_2_5gt(struct sfp *sfp) static void sfp_fixup_fs_10gt(struct sfp *sfp) { sfp_fixup_10gbaset_30m(sfp); - sfp_fixup_fs_2_5gt(sfp); + sfp_fixup_rollball_wait4s(sfp); } static void sfp_fixup_halny_gsfp(struct sfp *sfp) @@ -408,7 +420,19 @@ static void sfp_fixup_halny_gsfp(struct sfp *sfp) * these are possibly used for other purposes on this * module, e.g. a serial port. */ - sfp->state_hw_mask &= ~(SFP_F_TX_FAULT | SFP_F_LOS); + sfp_fixup_ignore_hw(sfp, SFP_F_TX_FAULT | SFP_F_LOS); +} + +static void sfp_fixup_potron(struct sfp *sfp) +{ + /* + * The TX_FAULT and LOS pins on this device are used for serial + * communication, so ignore them. Additionally, provide extra + * time for this device to fully start up. + */ + + sfp_fixup_long_startup(sfp); + sfp_fixup_ignore_hw(sfp, SFP_F_TX_FAULT | SFP_F_LOS); } static void sfp_fixup_rollball_cc(struct sfp *sfp) @@ -422,65 +446,69 @@ static void sfp_fixup_rollball_cc(struct sfp *sfp) } static void sfp_quirk_2500basex(const struct sfp_eeprom_id *id, - unsigned long *modes, - unsigned long *interfaces) + struct sfp_module_caps *caps) { - linkmode_set_bit(ETHTOOL_LINK_MODE_2500baseX_Full_BIT, modes); - __set_bit(PHY_INTERFACE_MODE_2500BASEX, interfaces); + linkmode_set_bit(ETHTOOL_LINK_MODE_2500baseX_Full_BIT, + caps->link_modes); + __set_bit(PHY_INTERFACE_MODE_2500BASEX, caps->interfaces); } static void sfp_quirk_disable_autoneg(const struct sfp_eeprom_id *id, - unsigned long *modes, - unsigned long *interfaces) + struct sfp_module_caps *caps) { - linkmode_clear_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, modes); + linkmode_clear_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, caps->link_modes); } static void sfp_quirk_oem_2_5g(const struct sfp_eeprom_id *id, - unsigned long *modes, - unsigned long *interfaces) + struct sfp_module_caps *caps) { /* Copper 2.5G SFP */ - linkmode_set_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT, modes); - __set_bit(PHY_INTERFACE_MODE_2500BASEX, interfaces); - sfp_quirk_disable_autoneg(id, modes, interfaces); + linkmode_set_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT, + caps->link_modes); + __set_bit(PHY_INTERFACE_MODE_2500BASEX, caps->interfaces); + sfp_quirk_disable_autoneg(id, caps); } static void sfp_quirk_ubnt_uf_instant(const struct sfp_eeprom_id *id, - unsigned long *modes, - unsigned long *interfaces) + struct sfp_module_caps *caps) { /* Ubiquiti U-Fiber Instant module claims that support all transceiver * types including 10G Ethernet which is not truth. So clear all claimed * modes and set only one mode which module supports: 1000baseX_Full. */ - linkmode_zero(modes); - linkmode_set_bit(ETHTOOL_LINK_MODE_1000baseX_Full_BIT, modes); + linkmode_zero(caps->link_modes); + linkmode_set_bit(ETHTOOL_LINK_MODE_1000baseX_Full_BIT, + caps->link_modes); } -#define SFP_QUIRK(_v, _p, _m, _f) \ - { .vendor = _v, .part = _p, .modes = _m, .fixup = _f, } -#define SFP_QUIRK_M(_v, _p, _m) SFP_QUIRK(_v, _p, _m, NULL) +#define SFP_QUIRK(_v, _p, _s, _f) \ + { .vendor = _v, .part = _p, .support = _s, .fixup = _f, } +#define SFP_QUIRK_S(_v, _p, _s) SFP_QUIRK(_v, _p, _s, NULL) #define SFP_QUIRK_F(_v, _p, _f) SFP_QUIRK(_v, _p, NULL, _f) static const struct sfp_quirk sfp_quirks[] = { // Alcatel Lucent G-010S-P can operate at 2500base-X, but incorrectly // report 2500MBd NRZ in their EEPROM - SFP_QUIRK_M("ALCATELLUCENT", "G010SP", sfp_quirk_2500basex), + SFP_QUIRK("ALCATELLUCENT", "G010SP", sfp_quirk_2500basex, + sfp_fixup_ignore_tx_fault), // Alcatel Lucent G-010S-A can operate at 2500base-X, but report 3.2GBd // NRZ in their EEPROM SFP_QUIRK("ALCATELLUCENT", "3FE46541AA", sfp_quirk_2500basex, sfp_fixup_nokia), + // FLYPRO SFP-10GT-CS-30M uses Rollball protocol to talk to the PHY. + SFP_QUIRK_F("FLYPRO", "SFP-10GT-CS-30M", sfp_fixup_rollball), + // Fiberstore SFP-10G-T doesn't identify as copper, uses the Rollball // protocol to talk to the PHY and needs 4 sec wait before probing the // PHY. SFP_QUIRK_F("FS", "SFP-10G-T", sfp_fixup_fs_10gt), - // Fiberstore SFP-2.5G-T uses Rollball protocol to talk to the PHY and - // needs 4 sec wait before probing the PHY. - SFP_QUIRK_F("FS", "SFP-2.5G-T", sfp_fixup_fs_2_5gt), + // Fiberstore SFP-2.5G-T and SFP-10GM-T uses Rollball protocol to talk + // to the PHY and needs 4 sec wait before probing the PHY. + SFP_QUIRK_F("FS", "SFP-2.5G-T", sfp_fixup_rollball_wait4s), + SFP_QUIRK_F("FS", "SFP-10GM-T", sfp_fixup_rollball_wait4s), // Fiberstore GPON-ONU-34-20BI can operate at 2500base-X, but report 1.2GBd // NRZ in their EEPROM @@ -491,7 +519,7 @@ static const struct sfp_quirk sfp_quirks[] = { // HG MXPD-483II-F 2.5G supports 2500Base-X, but incorrectly reports // 2600MBd in their EERPOM - SFP_QUIRK_M("HG GENUINE", "MXPD-483II", sfp_quirk_2500basex), + SFP_QUIRK_S("HG GENUINE", "MXPD-483II", sfp_quirk_2500basex), // Huawei MA5671A can operate at 2500base-X, but report 1.2GBd NRZ in // their EEPROM @@ -500,20 +528,24 @@ static const struct sfp_quirk sfp_quirks[] = { // Lantech 8330-262D-E can operate at 2500base-X, but incorrectly report // 2500MBd NRZ in their EEPROM - SFP_QUIRK_M("Lantech", "8330-262D-E", sfp_quirk_2500basex), + SFP_QUIRK_S("Lantech", "8330-262D-E", sfp_quirk_2500basex), - SFP_QUIRK_M("UBNT", "UF-INSTANT", sfp_quirk_ubnt_uf_instant), + SFP_QUIRK_S("UBNT", "UF-INSTANT", sfp_quirk_ubnt_uf_instant), // Walsun HXSX-ATR[CI]-1 don't identify as copper, and use the // Rollball protocol to talk to the PHY. SFP_QUIRK_F("Walsun", "HXSX-ATRC-1", sfp_fixup_fs_10gt), SFP_QUIRK_F("Walsun", "HXSX-ATRI-1", sfp_fixup_fs_10gt), + SFP_QUIRK_F("YV", "SFP+ONU-XGSPON", sfp_fixup_potron), + // OEM SFP-GE-T is a 1000Base-T module with broken TX_FAULT indicator SFP_QUIRK_F("OEM", "SFP-GE-T", sfp_fixup_ignore_tx_fault), SFP_QUIRK_F("OEM", "SFP-10G-T", sfp_fixup_rollball_cc), - SFP_QUIRK_M("OEM", "SFP-2.5G-T", sfp_quirk_oem_2_5g), + SFP_QUIRK_S("OEM", "SFP-2.5G-T", sfp_quirk_oem_2_5g), + SFP_QUIRK_S("OEM", "SFP-2.5G-BX10-D", sfp_quirk_2500basex), + SFP_QUIRK_S("OEM", "SFP-2.5G-BX10-U", sfp_quirk_2500basex), SFP_QUIRK_F("OEM", "RTSFP-10", sfp_fixup_rollball_cc), SFP_QUIRK_F("OEM", "RTSFP-10G", sfp_fixup_rollball_cc), SFP_QUIRK_F("Turris", "RTSFP-2.5G", sfp_fixup_rollball), @@ -561,8 +593,6 @@ static const struct sfp_quirk *sfp_lookup_quirk(const struct sfp_eeprom_id *id) return NULL; } -static unsigned long poll_jiffies; - static unsigned int sfp_gpio_get_state(struct sfp *sfp) { unsigned int i, state, v; @@ -687,14 +717,71 @@ static int sfp_i2c_write(struct sfp *sfp, bool a2, u8 dev_addr, void *buf, return ret == ARRAY_SIZE(msgs) ? len : 0; } -static int sfp_i2c_configure(struct sfp *sfp, struct i2c_adapter *i2c) +static int sfp_smbus_byte_read(struct sfp *sfp, bool a2, u8 dev_addr, + void *buf, size_t len) { - if (!i2c_check_functionality(i2c, I2C_FUNC_I2C)) - return -EINVAL; + union i2c_smbus_data smbus_data; + u8 bus_addr = a2 ? 0x51 : 0x50; + u8 *data = buf; + int ret; + + while (len) { + ret = i2c_smbus_xfer(sfp->i2c, bus_addr, 0, + I2C_SMBUS_READ, dev_addr, + I2C_SMBUS_BYTE_DATA, &smbus_data); + if (ret < 0) + return ret; + + *data = smbus_data.byte; + + len--; + data++; + dev_addr++; + } + return data - (u8 *)buf; +} + +static int sfp_smbus_byte_write(struct sfp *sfp, bool a2, u8 dev_addr, + void *buf, size_t len) +{ + union i2c_smbus_data smbus_data; + u8 bus_addr = a2 ? 0x51 : 0x50; + u8 *data = buf; + int ret; + + while (len) { + smbus_data.byte = *data; + ret = i2c_smbus_xfer(sfp->i2c, bus_addr, 0, + I2C_SMBUS_WRITE, dev_addr, + I2C_SMBUS_BYTE_DATA, &smbus_data); + if (ret) + return ret; + + len--; + data++; + dev_addr++; + } + + return 0; +} + +static int sfp_i2c_configure(struct sfp *sfp, struct i2c_adapter *i2c) +{ sfp->i2c = i2c; - sfp->read = sfp_i2c_read; - sfp->write = sfp_i2c_write; + + if (i2c_check_functionality(i2c, I2C_FUNC_I2C)) { + sfp->read = sfp_i2c_read; + sfp->write = sfp_i2c_write; + sfp->i2c_max_block_size = SFP_EEPROM_BLOCK_SIZE; + } else if (i2c_check_functionality(i2c, I2C_FUNC_SMBUS_BYTE_DATA)) { + sfp->read = sfp_smbus_byte_read; + sfp->write = sfp_smbus_byte_write; + sfp->i2c_max_block_size = 1; + } else { + sfp->i2c = NULL; + return -EINVAL; + } return 0; } @@ -828,7 +915,7 @@ static void sfp_soft_start_poll(struct sfp *sfp) if (sfp->state_soft_mask & (SFP_F_LOS | SFP_F_TX_FAULT) && !sfp->need_poll) - mod_delayed_work(system_wq, &sfp->poll, poll_jiffies); + sfp_schedule_poll(sfp); mutex_unlock(&sfp->st_mutex); } @@ -1590,7 +1677,7 @@ static void sfp_hwmon_probe(struct work_struct *work) */ if (sfp->i2c_block_size < 2) { dev_info(sfp->dev, - "skipping hwmon device registration due to broken EEPROM\n"); + "skipping hwmon device registration\n"); dev_info(sfp->dev, "diagnostic EEPROM area cannot be read atomically to guarantee data coherency\n"); return; @@ -1599,7 +1686,7 @@ static void sfp_hwmon_probe(struct work_struct *work) err = sfp_read(sfp, true, 0, &sfp->diag, sizeof(sfp->diag)); if (err < 0) { if (sfp->hwmon_tries--) { - mod_delayed_work(system_wq, &sfp->hwmon_probe, + mod_delayed_work(system_percpu_wq, &sfp->hwmon_probe, T_PROBE_RETRY_SLOW); } else { dev_warn(sfp->dev, "hwmon probe failed: %pe\n", @@ -1626,7 +1713,7 @@ static void sfp_hwmon_probe(struct work_struct *work) static int sfp_hwmon_insert(struct sfp *sfp) { if (sfp->have_a2 && sfp->id.ext.diagmon & SFP_DIAGMON_DDM) { - mod_delayed_work(system_wq, &sfp->hwmon_probe, 1); + mod_delayed_work(system_percpu_wq, &sfp->hwmon_probe, 1); sfp->hwmon_tries = R_PROBE_RETRY_SLOW; } @@ -2197,7 +2284,7 @@ static int sfp_sm_mod_probe(struct sfp *sfp, bool report) u8 check; int ret; - sfp->i2c_block_size = SFP_EEPROM_BLOCK_SIZE; + sfp->i2c_block_size = sfp->i2c_max_block_size; ret = sfp_read(sfp, false, 0, &id.base, sizeof(id.base)); if (ret < 0) { @@ -2480,7 +2567,7 @@ static void sfp_sm_module(struct sfp *sfp, unsigned int event) /* Force a poll to re-read the hardware signal state after * sfp_sm_mod_probe() changed state_hw_mask. */ - mod_delayed_work(system_wq, &sfp->poll, 1); + mod_delayed_work(system_percpu_wq, &sfp->poll, 1); err = sfp_hwmon_insert(sfp); if (err) @@ -2925,7 +3012,7 @@ static void sfp_poll(struct work_struct *work) // it's unimportant if we race while reading this. if (sfp->state_soft_mask & (SFP_F_LOS | SFP_F_TX_FAULT) || sfp->need_poll) - mod_delayed_work(system_wq, &sfp->poll, poll_jiffies); + sfp_schedule_poll(sfp); } static struct sfp *sfp_alloc(struct device *dev) @@ -2937,7 +3024,6 @@ static struct sfp *sfp_alloc(struct device *dev) return ERR_PTR(-ENOMEM); sfp->dev = dev; - sfp->i2c_block_size = SFP_EEPROM_BLOCK_SIZE; mutex_init(&sfp->sm_mutex); mutex_init(&sfp->st_mutex); @@ -3096,7 +3182,7 @@ static int sfp_probe(struct platform_device *pdev) } if (sfp->need_poll) - mod_delayed_work(system_wq, &sfp->poll, poll_jiffies); + sfp_schedule_poll(sfp); /* We could have an issue in cases no Tx disable pin is available or * wired as modules using a laser as their light source will continue to @@ -3111,6 +3197,15 @@ static int sfp_probe(struct platform_device *pdev) if (!sfp->sfp_bus) return -ENOMEM; + if (sfp->i2c_max_block_size < 2) + dev_warn(sfp->dev, + "Please note:\n" + "This SFP cage is accessed via an SMBus only capable of single byte\n" + "transactions. Some features are disabled, other may be unreliable or\n" + "sporadically fail. Use with caution. There is nothing that the kernel\n" + "or community can do to fix it, the kernel will try best efforts. Please\n" + "verify any problems on hardware that supports multi-byte I2C transactions.\n"); + sfp_debugfs_init(sfp); return 0; @@ -3146,7 +3241,7 @@ static void sfp_shutdown(struct platform_device *pdev) static struct platform_driver sfp_driver = { .probe = sfp_probe, - .remove_new = sfp_remove, + .remove = sfp_remove, .shutdown = sfp_shutdown, .driver = { .name = "sfp", @@ -3154,19 +3249,7 @@ static struct platform_driver sfp_driver = { }, }; -static int sfp_init(void) -{ - poll_jiffies = msecs_to_jiffies(100); - - return platform_driver_register(&sfp_driver); -} -module_init(sfp_init); - -static void sfp_exit(void) -{ - platform_driver_unregister(&sfp_driver); -} -module_exit(sfp_exit); +module_platform_driver(sfp_driver); MODULE_ALIAS("platform:sfp"); MODULE_AUTHOR("Russell King"); |
