From 378d1a65698eb8060d0a28c438d718a917afcb56 Mon Sep 17 00:00:00 2001 From: Robert Marko Date: Mon, 25 Oct 2021 18:30:00 +0200 Subject: [PATCH] ipq40xx: qca807x: add SFP improvements Currently, QCA807x doesnt do any kind of validation to see whether it actually supports the inserted module. So lets add checks to allow only 1000BaseX and 100BaseFX based modules. While adding validation, move fiber configuration to insert/remove events instead of always doing it at config time. This allows getting rid of the DT property for fiber enable and now only the upstream sfp phandle is required. Since we are refactoring fiber related code, lets heavily simplify the status polling as the current logic is overcomplicated due to previous wish to support non standard SFP cages that dont have pins properly connected, that is removed now and only proper SFP cages will work. Signed-off-by: Robert Marko --- .../ipq40xx/files/drivers/net/phy/qca807x.c | 140 +++++++++--------- 1 file changed, 74 insertions(+), 66 deletions(-) diff --git a/target/linux/ipq40xx/files/drivers/net/phy/qca807x.c b/target/linux/ipq40xx/files/drivers/net/phy/qca807x.c index 8d523548e52..d56e9f9cda8 100644 --- a/target/linux/ipq40xx/files/drivers/net/phy/qca807x.c +++ b/target/linux/ipq40xx/files/drivers/net/phy/qca807x.c @@ -64,6 +64,8 @@ #define QCA807X_CHIP_CONFIGURATION 0x1f #define QCA807X_BT_BX_REG_SEL BIT(15) +#define QCA807X_BT_BX_REG_SEL_FIBER 0 +#define QCA807X_BT_BX_REG_SEL_COPPER 1 #define QCA807X_CHIP_CONFIGURATION_MODE_CFG_MASK GENMASK(3, 0) #define QCA807X_CHIP_CONFIGURATION_MODE_QSGMII_SGMII 4 #define QCA807X_CHIP_CONFIGURATION_MODE_PSGMII_FIBER 3 @@ -400,19 +402,9 @@ static int qca807x_gpio(struct phy_device *phydev) } #endif -static int qca807x_read_copper_status(struct phy_device *phydev, bool combo_port) +static int qca807x_read_copper_status(struct phy_device *phydev) { - int ss, err, page, old_link = phydev->link; - - /* Only combo port has dual pages */ - if (combo_port) { - /* Check whether copper page is set and set if needed */ - page = phy_read(phydev, QCA807X_CHIP_CONFIGURATION); - if (!(page & QCA807X_BT_BX_REG_SEL)) { - page |= QCA807X_BT_BX_REG_SEL; - phy_write(phydev, QCA807X_CHIP_CONFIGURATION, page); - } - } + int ss, err, old_link = phydev->link; /* Update the link, but return if there was an error */ err = genphy_update_link(phydev); @@ -487,16 +479,9 @@ static int qca807x_read_copper_status(struct phy_device *phydev, bool combo_port return 0; } -static int qca807x_read_fiber_status(struct phy_device *phydev, bool combo_port) +static int qca807x_read_fiber_status(struct phy_device *phydev) { - int ss, err, page, lpa, old_link = phydev->link; - - /* Check whether fiber page is set and set if needed */ - page = phy_read(phydev, QCA807X_CHIP_CONFIGURATION); - if (page & QCA807X_BT_BX_REG_SEL) { - page &= ~QCA807X_BT_BX_REG_SEL; - phy_write(phydev, QCA807X_CHIP_CONFIGURATION, page); - } + int ss, err, lpa, old_link = phydev->link; /* Update the link, but return if there was an error */ err = genphy_update_link(phydev); @@ -559,28 +544,17 @@ static int qca807x_read_fiber_status(struct phy_device *phydev, bool combo_port) static int qca807x_read_status(struct phy_device *phydev) { - int val; - - /* Check for Combo port */ - if (phy_read(phydev, QCA807X_CHIP_CONFIGURATION)) { - /* Check for fiber mode first */ - if (linkmode_test_bit(ETHTOOL_LINK_MODE_FIBRE_BIT, phydev->supported)) { - /* Check for actual detected media */ - val = phy_read(phydev, QCA807X_MEDIA_SELECT_STATUS); - if (val & QCA807X_MEDIA_DETECTED_COPPER) { - qca807x_read_copper_status(phydev, true); - } else if ((val & QCA807X_MEDIA_DETECTED_1000_BASE_X) || - (val & QCA807X_MEDIA_DETECTED_100_BASE_FX)) { - qca807x_read_fiber_status(phydev, true); - } - } else { - qca807x_read_copper_status(phydev, true); + if (linkmode_test_bit(ETHTOOL_LINK_MODE_FIBRE_BIT, phydev->supported)) { + switch (phydev->port) { + case PORT_FIBRE: + return qca807x_read_fiber_status(phydev); + case PORT_TP: + return qca807x_read_copper_status(phydev); + default: + return -EINVAL; } - } else { - qca807x_read_copper_status(phydev, false); - } - - return 0; + } else + return qca807x_read_copper_status(phydev); } static int qca807x_config_intr(struct phy_device *phydev) @@ -683,9 +657,63 @@ static int qca807x_led_config(struct phy_device *phydev) return 0; } +static int qca807x_sfp_insert(void *upstream, const struct sfp_eeprom_id *id) +{ + struct phy_device *phydev = upstream; + __ETHTOOL_DECLARE_LINK_MODE_MASK(support) = { 0, }; + phy_interface_t iface; + int ret; + + sfp_parse_support(phydev->sfp_bus, id, support); + iface = sfp_select_interface(phydev->sfp_bus, support); + + dev_info(&phydev->mdio.dev, "%s SFP module inserted\n", phy_modes(iface)); + + switch (iface) { + case PHY_INTERFACE_MODE_1000BASEX: + case PHY_INTERFACE_MODE_100BASEX: + /* Set PHY mode to PSGMII combo (1/4 copper + combo ports) mode */ + ret = phy_modify(phydev, + QCA807X_CHIP_CONFIGURATION, + QCA807X_CHIP_CONFIGURATION_MODE_CFG_MASK, + QCA807X_CHIP_CONFIGURATION_MODE_PSGMII_FIBER); + /* Enable fiber mode autodection (1000Base-X or 100Base-FX) */ + ret = phy_set_bits_mmd(phydev, + MDIO_MMD_AN, + QCA807X_MMD7_FIBER_MODE_AUTO_DETECTION, + QCA807X_MMD7_FIBER_MODE_AUTO_DETECTION_EN); + /* Select fiber page */ + ret = phy_clear_bits(phydev, + QCA807X_CHIP_CONFIGURATION, + QCA807X_BT_BX_REG_SEL); + + phydev->port = PORT_FIBRE; + break; + default: + dev_err(&phydev->mdio.dev, "Incompatible SFP module inserted\n"); + return -EINVAL; + } + + return ret; +} + +static void qca807x_sfp_remove(void *upstream) +{ + struct phy_device *phydev = upstream; + + /* Select copper page */ + phy_set_bits(phydev, + QCA807X_CHIP_CONFIGURATION, + QCA807X_BT_BX_REG_SEL); + + phydev->port = PORT_TP; +} + static const struct sfp_upstream_ops qca807x_sfp_ops = { .attach = phy_sfp_attach, .detach = phy_sfp_detach, + .module_insert = qca807x_sfp_insert, + .module_remove = qca807x_sfp_remove, }; static int qca807x_config(struct phy_device *phydev) @@ -696,28 +724,7 @@ static int qca807x_config(struct phy_device *phydev) /* Check for Combo port */ if (phy_read(phydev, QCA807X_CHIP_CONFIGURATION)) { - int fiber_mode_autodect; int psgmii_serdes; - int chip_config; - - if (of_property_read_bool(node, "qcom,fiber-enable")) { - /* Enable fiber mode autodection (1000Base-X or 100Base-FX) */ - fiber_mode_autodect = phy_read_mmd(phydev, MDIO_MMD_AN, - QCA807X_MMD7_FIBER_MODE_AUTO_DETECTION); - fiber_mode_autodect |= QCA807X_MMD7_FIBER_MODE_AUTO_DETECTION_EN; - phy_write_mmd(phydev, MDIO_MMD_AN, QCA807X_MMD7_FIBER_MODE_AUTO_DETECTION, - fiber_mode_autodect); - - /* Enable 4 copper + combo port mode */ - chip_config = phy_read(phydev, QCA807X_CHIP_CONFIGURATION); - chip_config &= ~QCA807X_CHIP_CONFIGURATION_MODE_CFG_MASK; - chip_config |= FIELD_PREP(QCA807X_CHIP_CONFIGURATION_MODE_CFG_MASK, - QCA807X_CHIP_CONFIGURATION_MODE_PSGMII_FIBER); - phy_write(phydev, QCA807X_CHIP_CONFIGURATION, chip_config); - - linkmode_set_bit(ETHTOOL_LINK_MODE_FIBRE_BIT, phydev->supported); - linkmode_set_bit(ETHTOOL_LINK_MODE_FIBRE_BIT, phydev->advertising); - } /* Prevent PSGMII going into hibernation via PSGMII self test */ psgmii_serdes = phy_read_mmd(phydev, MDIO_MMD_PCS, PSGMII_MMD3_SERDES_CONTROL); @@ -761,9 +768,10 @@ static int qca807x_probe(struct phy_device *phydev) } /* Attach SFP bus on combo port*/ - if (of_property_read_bool(node, "qcom,fiber-enable")) { - if (phy_read(phydev, QCA807X_CHIP_CONFIGURATION)) - ret = phy_sfp_probe(phydev, &qca807x_sfp_ops); + if (phy_read(phydev, QCA807X_CHIP_CONFIGURATION)) { + ret = phy_sfp_probe(phydev, &qca807x_sfp_ops); + linkmode_set_bit(ETHTOOL_LINK_MODE_FIBRE_BIT, phydev->supported); + linkmode_set_bit(ETHTOOL_LINK_MODE_FIBRE_BIT, phydev->advertising); } return ret; -- 2.30.2