Implement single-pair BroadR-Reach modes on bcm5481x PHY by Broadcom.
Create set of functions alternative to IEEE 802.3 to handle configuration
of these modes on compatible Broadcom PHYs.
Signed-off-by: Kamil Horák - 2N <kamilh@axis.com>
---
drivers/net/phy/bcm-phy-lib.c | 125 ++++++++++++
drivers/net/phy/bcm-phy-lib.h | 4 +
drivers/net/phy/broadcom.c | 370 ++++++++++++++++++++++++++++++++--
3 files changed, 482 insertions(+), 17 deletions(-)
diff --git a/drivers/net/phy/bcm-phy-lib.c b/drivers/net/phy/bcm-phy-lib.c
index 876f28fd8256..e4f1766cbc10 100644
--- a/drivers/net/phy/bcm-phy-lib.c
+++ b/drivers/net/phy/bcm-phy-lib.c
@@ -794,6 +794,47 @@ static int _bcm_phy_cable_test_get_status(struct phy_device *phydev,
return ret;
}
+static int bcm_setup_lre_forced(struct phy_device *phydev)
+{
+ u16 ctl = 0;
+
+ phydev->pause = 0;
+ phydev->asym_pause = 0;
+
+ if (phydev->speed == SPEED_100)
+ ctl |= LRECR_SPEED100;
+
+ if (phydev->duplex != DUPLEX_FULL)
+ return -EOPNOTSUPP;
+
+ return phy_modify(phydev, MII_BCM54XX_LRECR, LRECR_SPEED100, ctl);
+}
+
+/**
+ * bcm_linkmode_adv_to_lre_adv_t - translate linkmode advertisement to LDS
+ * @advertising: the linkmode advertisement settings
+ * @return: LDS Auto-Negotiation Advertised Ability register value
+ *
+ * A small helper function that translates linkmode advertisement
+ * settings to phy LDS autonegotiation advertisements for the
+ * MII_BCM54XX_LREANAA register of Broadcom PHYs capable of LDS
+ */
+static u32 bcm_linkmode_adv_to_lre_adv_t(unsigned long *advertising)
+{
+ u32 result = 0;
+
+ if (linkmode_test_bit(ETHTOOL_LINK_MODE_10baseT1BRR_Full_BIT, advertising))
+ result |= LREANAA_10_1PAIR;
+ if (linkmode_test_bit(ETHTOOL_LINK_MODE_100baseT1_Full_BIT, advertising))
+ result |= LREANAA_100_1PAIR;
+ if (linkmode_test_bit(ETHTOOL_LINK_MODE_Pause_BIT, advertising))
+ result |= LRELPA_PAUSE;
+ if (linkmode_test_bit(ETHTOOL_LINK_MODE_Asym_Pause_BIT, advertising))
+ result |= LRELPA_PAUSE_ASYM;
+
+ return result;
+}
+
int bcm_phy_cable_test_start(struct phy_device *phydev)
{
return _bcm_phy_cable_test_start(phydev, false);
@@ -1066,6 +1107,90 @@ int bcm_phy_led_brightness_set(struct phy_device *phydev,
}
EXPORT_SYMBOL_GPL(bcm_phy_led_brightness_set);
+int bcm_setup_master_slave(struct phy_device *phydev)
+{
+ u16 ctl = 0;
+
+ switch (phydev->master_slave_set) {
+ case MASTER_SLAVE_CFG_MASTER_PREFERRED:
+ case MASTER_SLAVE_CFG_MASTER_FORCE:
+ ctl = LRECR_MASTER;
+ break;
+ case MASTER_SLAVE_CFG_SLAVE_PREFERRED:
+ case MASTER_SLAVE_CFG_SLAVE_FORCE:
+ break;
+ case MASTER_SLAVE_CFG_UNKNOWN:
+ case MASTER_SLAVE_CFG_UNSUPPORTED:
+ return 0;
+ default:
+ phydev_warn(phydev, "Unsupported Master/Slave mode\n");
+ return -EOPNOTSUPP;
+ }
+
+ return phy_modify_changed(phydev, MII_BCM54XX_LRECR, LRECR_MASTER, ctl);
+}
+EXPORT_SYMBOL_GPL(bcm_setup_master_slave);
+
+int bcm_config_lre_aneg(struct phy_device *phydev, bool changed)
+{
+ int err;
+
+ if (genphy_config_eee_advert(phydev))
+ changed = true;
+
+ err = bcm_setup_master_slave(phydev);
+ if (err < 0)
+ return err;
+ else if (err)
+ changed = true;
+
+ if (phydev->autoneg != AUTONEG_ENABLE)
+ return bcm_setup_lre_forced(phydev);
+
+ err = bcm_config_lre_advert(phydev);
+ if (err < 0)
+ return err;
+ else if (err)
+ changed = true;
+
+ return genphy_check_and_restart_aneg(phydev, changed);
+}
+EXPORT_SYMBOL_GPL(bcm_config_lre_aneg);
+
+/**
+ * bcm_config_lre_advert - sanitize and advertise Long-Distance Signaling
+ * auto-negotiation parameters
+ * @phydev: target phy_device struct
+ * @return: 0 if the PHY's advertisement hasn't changed, < 0 on error,
+ * > 0 if it has changed
+ *
+ * Description: Writes MII_BCM54XX_LREANAA with the appropriate values,
+ * after sanitizing the values to make sure we only advertise
+ * what is supported.
+ */
+int bcm_config_lre_advert(struct phy_device *phydev)
+{
+ int err;
+ u32 adv;
+
+ /* Only allow advertising what this PHY supports */
+ linkmode_and(phydev->advertising, phydev->advertising,
+ phydev->supported);
+
+ adv = bcm_linkmode_adv_to_lre_adv_t(phydev->advertising);
+
+ /* Setup BroadR-Reach mode advertisement */
+ err = phy_modify_changed(phydev, MII_BCM54XX_LREANAA,
+ LRE_ADVERTISE_ALL | LREANAA_PAUSE |
+ LREANAA_PAUSE_ASYM, adv);
+
+ if (err < 0)
+ return err;
+
+ return err;
+}
+EXPORT_SYMBOL_GPL(bcm_config_lre_advert);
+
MODULE_DESCRIPTION("Broadcom PHY Library");
MODULE_LICENSE("GPL v2");
MODULE_AUTHOR("Broadcom Corporation");
diff --git a/drivers/net/phy/bcm-phy-lib.h b/drivers/net/phy/bcm-phy-lib.h
index b52189e45a84..fecdd66ad736 100644
--- a/drivers/net/phy/bcm-phy-lib.h
+++ b/drivers/net/phy/bcm-phy-lib.h
@@ -121,4 +121,8 @@ irqreturn_t bcm_phy_wol_isr(int irq, void *dev_id);
int bcm_phy_led_brightness_set(struct phy_device *phydev,
u8 index, enum led_brightness value);
+int bcm_setup_master_slave(struct phy_device *phydev);
+int bcm_config_lre_aneg(struct phy_device *phydev, bool changed);
+int bcm_config_lre_advert(struct phy_device *phydev);
+
#endif /* _LINUX_BCM_PHY_LIB_H */
diff --git a/drivers/net/phy/broadcom.c b/drivers/net/phy/broadcom.c
index 370e4ed45098..5e590c8f82c4 100644
--- a/drivers/net/phy/broadcom.c
+++ b/drivers/net/phy/broadcom.c
@@ -5,6 +5,9 @@
* Broadcom BCM5411, BCM5421 and BCM5461 Gigabit Ethernet
* transceivers.
*
+ * Broadcom BCM54810, BCM54811 BroadR-Reach transceivers.
+ *
+ *
* Copyright (c) 2006 Maciej W. Rozycki
*
* Inspired by code written by Amy Fong.
@@ -553,18 +556,97 @@ static int bcm54810_write_mmd(struct phy_device *phydev, int devnum, u16 regnum,
return -EOPNOTSUPP;
}
-static int bcm54811_config_init(struct phy_device *phydev)
+static int bcm5481x_get_brrmode(struct phy_device *phydev, u8 *data)
{
- int err, reg;
+ int reg;
- /* Disable BroadR-Reach function. */
reg = bcm_phy_read_exp(phydev, BCM54810_EXP_BROADREACH_LRE_MISC_CTL);
- reg &= ~BCM54810_EXP_BROADREACH_LRE_MISC_CTL_EN;
- err = bcm_phy_write_exp(phydev, BCM54810_EXP_BROADREACH_LRE_MISC_CTL,
- reg);
- if (err < 0)
+
+ *data = (reg & BCM54810_EXP_BROADREACH_LRE_MISC_CTL_EN) ? 1 : 0;
+
+ return 0;
+}
+
+static int bcm54811_read_abilities(struct phy_device *phydev)
+{
+ static const int modes_array[] = { ETHTOOL_LINK_MODE_100baseT1_Full_BIT,
+ ETHTOOL_LINK_MODE_10baseT1BRR_Full_BIT,
+ ETHTOOL_LINK_MODE_1000baseT_Full_BIT,
+ ETHTOOL_LINK_MODE_1000baseX_Full_BIT,
+ ETHTOOL_LINK_MODE_1000baseT_Half_BIT,
+ ETHTOOL_LINK_MODE_100baseT_Full_BIT,
+ ETHTOOL_LINK_MODE_100baseT_Half_BIT,
+ ETHTOOL_LINK_MODE_10baseT_Full_BIT,
+ ETHTOOL_LINK_MODE_10baseT_Half_BIT };
+ int i, val, err;
+ u8 brr_mode;
+
+ for (i = 0; i < ARRAY_SIZE(modes_array); i++)
+ linkmode_clear_bit(modes_array[i], phydev->supported);
+
+ err = bcm5481x_get_brrmode(phydev, &brr_mode);
+ if (err)
+ return err;
+
+ if (brr_mode) {
+ linkmode_set_bit_array(phy_basic_ports_array,
+ ARRAY_SIZE(phy_basic_ports_array),
+ phydev->supported);
+
+ val = phy_read(phydev, MII_BCM54XX_LRESR);
+ if (val < 0)
+ return val;
+
+ linkmode_mod_bit(ETHTOOL_LINK_MODE_Autoneg_BIT,
+ phydev->supported, 1);
+ linkmode_mod_bit(ETHTOOL_LINK_MODE_100baseT1_Full_BIT,
+ phydev->supported,
+ val & LRESR_100_1PAIR);
+ linkmode_mod_bit(ETHTOOL_LINK_MODE_10baseT1BRR_Full_BIT,
+ phydev->supported,
+ val & LRESR_10_1PAIR);
+ } else {
+ return genphy_read_abilities(phydev);
+ }
+
+ return 0;
+}
+
+static int bcm5481x_set_brrmode(struct phy_device *phydev, bool on)
+{
+ int reg;
+ int err;
+
+ reg = bcm_phy_read_exp(phydev, BCM54810_EXP_BROADREACH_LRE_MISC_CTL);
+
+ if (on)
+ reg |= BCM54810_EXP_BROADREACH_LRE_MISC_CTL_EN;
+ else
+ reg &= ~BCM54810_EXP_BROADREACH_LRE_MISC_CTL_EN;
+
+ err = bcm_phy_write_exp(phydev, BCM54810_EXP_BROADREACH_LRE_MISC_CTL, reg);
+ if (err)
+ return err;
+
+ /* Update the abilities based on the current brr on/off setting */
+ err = bcm54811_read_abilities(phydev);
+ if (err)
return err;
+ /* Ensure LRE or IEEE register set is accessed according to the brr on/off,
+ * thus set the override
+ */
+ return bcm_phy_write_exp(phydev, BCM54811_EXP_BROADREACH_LRE_OVERLAY_CTL,
+ BCM54811_EXP_BROADREACH_LRE_OVERLAY_CTL_EN |
+ (on ? 0 : BCM54811_EXP_BROADREACH_LRE_OVERLAY_CTL_OVERRIDE_VAL));
+}
+
+static int bcm54811_config_init(struct phy_device *phydev)
+{
+ int err, reg;
+ bool brr = false;
+ struct device_node *np = phydev->mdio.dev.of_node;
+
err = bcm54xx_config_init(phydev);
/* Enable CLK125 MUX on LED4 if ref clock is enabled. */
@@ -576,29 +658,79 @@ static int bcm54811_config_init(struct phy_device *phydev)
return err;
}
- return err;
+ /* Configure BroadR-Reach function. */
+ brr = of_property_read_bool(np, "brr-mode");
+
+ /* With BCM54811, BroadR-Reach implies no autoneg */
+ if (brr)
+ phydev->autoneg = 0;
+
+ return bcm5481x_set_brrmode(phydev, brr);
}
-static int bcm5481_config_aneg(struct phy_device *phydev)
+static int bcm5481x_config_delay_swap(struct phy_device *phydev)
{
struct device_node *np = phydev->mdio.dev.of_node;
- int ret;
- /* Aneg firstly. */
- ret = genphy_config_aneg(phydev);
-
- /* Then we can set up the delay. */
+ /* Set up the delay. */
bcm54xx_config_clock_delay(phydev);
if (of_property_read_bool(np, "enet-phy-lane-swap")) {
/* Lane Swap - Undocumented register...magic! */
- ret = bcm_phy_write_exp(phydev, MII_BCM54XX_EXP_SEL_ER + 0x9,
+ int ret = bcm_phy_write_exp(phydev, MII_BCM54XX_EXP_SEL_ER + 0x9,
0x11B);
if (ret < 0)
return ret;
}
- return ret;
+ return 0;
+}
+
+static int bcm5481_config_aneg(struct phy_device *phydev)
+{
+ int ret;
+ u8 brr_mode;
+
+ ret = bcm5481x_get_brrmode(phydev, &brr_mode);
+ if (ret)
+ return ret;
+
+ /* Aneg firstly. */
+ if (brr_mode)
+ ret = bcm_config_lre_aneg(phydev, false);
+ else
+ ret = genphy_config_aneg(phydev);
+
+ if (ret)
+ return ret;
+
+ /* Then we can set up the delay and swap. */
+ return bcm5481x_config_delay_swap(phydev);
+}
+
+static int bcm54811_config_aneg(struct phy_device *phydev)
+{
+ int ret;
+ u8 brr_mode;
+
+ /* Aneg firstly. */
+ ret = bcm5481x_get_brrmode(phydev, &brr_mode);
+ if (ret)
+ return ret;
+
+ if (brr_mode) {
+ /* BCM54811 is only capable of autonegotiation in IEEE mode */
+ phydev->autoneg = 0;
+ ret = bcm_config_lre_aneg(phydev, false);
+ } else {
+ ret = genphy_config_aneg(phydev);
+ }
+
+ if (ret)
+ return ret;
+
+ /* Then we can set up the delay and swap. */
+ return bcm5481x_config_delay_swap(phydev);
}
struct bcm54616s_phy_priv {
@@ -1062,6 +1194,208 @@ static void bcm54xx_link_change_notify(struct phy_device *phydev)
bcm_phy_write_exp(phydev, MII_BCM54XX_EXP_EXP08, ret);
}
+static int bcm_read_master_slave(struct phy_device *phydev)
+{
+ int cfg = MASTER_SLAVE_CFG_UNKNOWN, state;
+ int val;
+
+ /* In BroadR-Reach mode we are always capable of master-slave
+ * and there is no preferred master or slave configuration
+ */
+ phydev->master_slave_get = MASTER_SLAVE_CFG_UNKNOWN;
+ phydev->master_slave_state = MASTER_SLAVE_STATE_UNKNOWN;
+
+ val = phy_read(phydev, MII_BCM54XX_LRECR);
+ if (val < 0)
+ return val;
+
+ if ((val & LRECR_LDSEN) == 0) {
+ if (val & LRECR_MASTER)
+ cfg = MASTER_SLAVE_CFG_MASTER_FORCE;
+ else
+ cfg = MASTER_SLAVE_CFG_SLAVE_FORCE;
+ }
+
+ val = phy_read(phydev, MII_BCM54XX_LRELDSE);
+ if (val < 0)
+ return val;
+
+ if (val & LDSE_MASTER)
+ state = MASTER_SLAVE_STATE_MASTER;
+ else
+ state = MASTER_SLAVE_STATE_SLAVE;
+
+ phydev->master_slave_get = cfg;
+ phydev->master_slave_state = state;
+
+ return 0;
+}
+
+/* Read LDS Link Partner Ability in BroadR-Reach mode */
+static int bcm_read_lpa(struct phy_device *phydev)
+{
+ int i, lrelpa;
+
+ if (phydev->autoneg != AUTONEG_ENABLE) {
+ if (!phydev->autoneg_complete) {
+ /* aneg not yet done, reset all relevant bits */
+ static const int br_bits[] = { ETHTOOL_LINK_MODE_Autoneg_BIT,
+ ETHTOOL_LINK_MODE_Pause_BIT,
+ ETHTOOL_LINK_MODE_Asym_Pause_BIT,
+ ETHTOOL_LINK_MODE_10baseT1BRR_Full_BIT,
+ ETHTOOL_LINK_MODE_100baseT1_Full_BIT };
+ for (i = 0; i < ARRAY_SIZE(br_bits); i++)
+ linkmode_clear_bit(br_bits[i], phydev->lp_advertising);
+
+ return 0;
+ }
+
+ /* Long-Distance Signaling Link Partner Ability */
+ lrelpa = phy_read(phydev, MII_BCM54XX_LRELPA);
+ if (lrelpa < 0)
+ return lrelpa;
+
+ linkmode_mod_bit(ETHTOOL_LINK_MODE_Asym_Pause_BIT,
+ phydev->lp_advertising, lrelpa & LRELPA_PAUSE_ASYM);
+ linkmode_mod_bit(ETHTOOL_LINK_MODE_Pause_BIT,
+ phydev->lp_advertising, lrelpa & LRELPA_PAUSE);
+ linkmode_mod_bit(ETHTOOL_LINK_MODE_100baseT1_Full_BIT,
+ phydev->lp_advertising, lrelpa & LRELPA_100_1PAIR);
+ linkmode_mod_bit(ETHTOOL_LINK_MODE_10baseT1BRR_Full_BIT,
+ phydev->lp_advertising, lrelpa & LRELPA_10_1PAIR);
+ } else {
+ linkmode_zero(phydev->lp_advertising);
+ }
+
+ return 0;
+}
+
+static int bcm_read_status_fixed(struct phy_device *phydev)
+{
+ int lrecr = phy_read(phydev, MII_BCM54XX_LRECR);
+
+ if (lrecr < 0)
+ return lrecr;
+
+ phydev->duplex = DUPLEX_FULL;
+
+ if (lrecr & LRECR_SPEED100)
+ phydev->speed = SPEED_100;
+ else
+ phydev->speed = SPEED_10;
+
+ return 0;
+}
+
+/**
+ * lre_update_link - update link status in @phydev
+ * @phydev: target phy_device struct
+ *
+ * Description: Update the value in phydev->link to reflect the
+ * current link value. In order to do this, we need to read
+ * the status register twice, keeping the second value.
+ * This is a genphy_update_link modified to work on LRE registers
+ * of BroadR-Reach PHY
+ */
+static int lre_update_link(struct phy_device *phydev)
+{
+ int status = 0, lrecr;
+
+ lrecr = phy_read(phydev, MII_BCM54XX_LRECR);
+ if (lrecr < 0)
+ return lrecr;
+
+ /* Autoneg is being started, therefore disregard BMSR value and
+ * report link as down.
+ */
+ if (lrecr & BMCR_ANRESTART)
+ goto done;
+
+ /* The link state is latched low so that momentary link
+ * drops can be detected. Do not double-read the status
+ * in polling mode to detect such short link drops except
+ * the link was already down.
+ */
+ if (!phy_polling_mode(phydev) || !phydev->link) {
+ status = phy_read(phydev, MII_BCM54XX_LRESR);
+ if (status < 0)
+ return status;
+ else if (status & LRESR_LSTATUS)
+ goto done;
+ }
+
+ /* Read link and autonegotiation status */
+ status = phy_read(phydev, MII_BCM54XX_LRESR);
+ if (status < 0)
+ return status;
+done:
+ phydev->link = status & LRESR_LSTATUS ? 1 : 0;
+ phydev->autoneg_complete = status & LRESR_LDSCOMPLETE ? 1 : 0;
+
+ /* Consider the case that autoneg was started and "aneg complete"
+ * bit has been reset, but "link up" bit not yet.
+ */
+ if (phydev->autoneg == AUTONEG_ENABLE && !phydev->autoneg_complete)
+ phydev->link = 0;
+
+ return 0;
+}
+
+static int bcm54811_read_status(struct phy_device *phydev)
+{
+ int err;
+ u8 brr_mode;
+
+ err = bcm5481x_get_brrmode(phydev, &brr_mode);
+
+ if (err)
+ return err;
+
+ if (brr_mode) {
+ /* Get the status in BroadRReach mode just like genphy_read_status
+ * does in normal mode
+ */
+
+ int err, old_link = phydev->link;
+
+ /* Update the link, but return if there was an error */
+
+ err = lre_update_link(phydev);
+ if (err)
+ return err;
+
+ /* why bother the PHY if nothing can have changed */
+ if (phydev->autoneg == AUTONEG_ENABLE && old_link && phydev->link)
+ return 0;
+
+ phydev->speed = SPEED_UNKNOWN;
+ phydev->duplex = DUPLEX_UNKNOWN;
+ phydev->pause = 0;
+ phydev->asym_pause = 0;
+
+ err = bcm_read_master_slave(phydev);
+ if (err < 0)
+ return err;
+
+ /* Read LDS Link Partner Ability */
+ err = bcm_read_lpa(phydev);
+ if (err < 0)
+ return err;
+
+ if (phydev->autoneg == AUTONEG_ENABLE && phydev->autoneg_complete) {
+ phy_resolve_aneg_linkmode(phydev);
+ } else if (phydev->autoneg == AUTONEG_DISABLE) {
+ err = bcm_read_status_fixed(phydev);
+ if (err < 0)
+ return err;
+ }
+ } else {
+ err = genphy_read_status(phydev);
+ }
+
+ return err;
+}
+
static struct phy_driver broadcom_drivers[] = {
{
.phy_id = PHY_ID_BCM5411,
@@ -1212,9 +1546,11 @@ static struct phy_driver broadcom_drivers[] = {
.get_stats = bcm54xx_get_stats,
.probe = bcm54xx_phy_probe,
.config_init = bcm54811_config_init,
- .config_aneg = bcm5481_config_aneg,
+ .config_aneg = bcm54811_config_aneg,
.config_intr = bcm_phy_config_intr,
.handle_interrupt = bcm_phy_handle_interrupt,
+ .read_status = bcm54811_read_status,
+ .get_features = bcm54811_read_abilities,
.suspend = bcm54xx_suspend,
.resume = bcm54xx_resume,
.link_change_notify = bcm54xx_link_change_notify,
--
2.39.2
On Mon, Jun 17, 2024 at 01:38:41PM +0200, Kamil Horák - 2N wrote:
> +int bcm_config_lre_advert(struct phy_device *phydev)
> +{
> + int err;
> + u32 adv;
> +
> + /* Only allow advertising what this PHY supports */
> + linkmode_and(phydev->advertising, phydev->advertising,
> + phydev->supported);
Isn't this already done by phy_ethtool_ksettings_set() ?
linkmode_copy(advertising, cmd->link_modes.advertising);
...
/* We make sure that we don't pass unsupported values in to the PHY */
linkmode_and(advertising, advertising, phydev->supported);
...
linkmode_copy(phydev->advertising, advertising);
> diff --git a/drivers/net/phy/broadcom.c b/drivers/net/phy/broadcom.c
> index 370e4ed45098..5e590c8f82c4 100644
> --- a/drivers/net/phy/broadcom.c
> +++ b/drivers/net/phy/broadcom.c
> @@ -5,6 +5,9 @@
> * Broadcom BCM5411, BCM5421 and BCM5461 Gigabit Ethernet
> * transceivers.
> *
> + * Broadcom BCM54810, BCM54811 BroadR-Reach transceivers.
> + *
> + *
Nit: why two blank lines?
> +static int bcm54811_read_abilities(struct phy_device *phydev)
> +{
> + static const int modes_array[] = { ETHTOOL_LINK_MODE_100baseT1_Full_BIT,
> + ETHTOOL_LINK_MODE_10baseT1BRR_Full_BIT,
> + ETHTOOL_LINK_MODE_1000baseT_Full_BIT,
> + ETHTOOL_LINK_MODE_1000baseX_Full_BIT,
> + ETHTOOL_LINK_MODE_1000baseT_Half_BIT,
> + ETHTOOL_LINK_MODE_100baseT_Full_BIT,
> + ETHTOOL_LINK_MODE_100baseT_Half_BIT,
> + ETHTOOL_LINK_MODE_10baseT_Full_BIT,
> + ETHTOOL_LINK_MODE_10baseT_Half_BIT };
Formatting...
static const int modes_array[] = {
ETHTOOL_LINK_MODE_100baseT1_Full_BIT,
...
ETHTOOL_LINK_MODE_10baseT_Half_BIT
};
please. This avoids wrapping beyond column 80, and is to kernel coding
standards.
> + int i, val, err;
> + u8 brr_mode;
> +
> + for (i = 0; i < ARRAY_SIZE(modes_array); i++)
> + linkmode_clear_bit(modes_array[i], phydev->supported);
> +
> + err = bcm5481x_get_brrmode(phydev, &brr_mode);
> + if (err)
> + return err;
> +
> + if (brr_mode) {
> + linkmode_set_bit_array(phy_basic_ports_array,
> + ARRAY_SIZE(phy_basic_ports_array),
> + phydev->supported);
> +
> + val = phy_read(phydev, MII_BCM54XX_LRESR);
> + if (val < 0)
> + return val;
> +
> + linkmode_mod_bit(ETHTOOL_LINK_MODE_Autoneg_BIT,
> + phydev->supported, 1);
linkmode_set_bit(ETHTOOL_LINK_MODE_Autoneg_BIT,
phydev->supported);
...
> + /* Ensure LRE or IEEE register set is accessed according to the brr on/off,
> + * thus set the override
> + */
> + return bcm_phy_write_exp(phydev, BCM54811_EXP_BROADREACH_LRE_OVERLAY_CTL,
> + BCM54811_EXP_BROADREACH_LRE_OVERLAY_CTL_EN |
> + (on ? 0 : BCM54811_EXP_BROADREACH_LRE_OVERLAY_CTL_OVERRIDE_VAL));
Needless parens, wrong formatting. Consider a local variable:
val = BCM54811_EXP_BROADREACH_LRE_OVERLAY_CTL_EN;
if (!on)
val |= BCM54811_EXP_BROADREACH_LRE_OVERLAY_CTL_OVERRIDE_VAL;
return bcm_phy_write_exp(phydev,
BCM54811_EXP_BROADREACH_LRE_OVERLAY_CTL, val);
would be much nicer to read.
> + if (phydev->autoneg != AUTONEG_ENABLE) {
> + if (!phydev->autoneg_complete) {
> + /* aneg not yet done, reset all relevant bits */
> + static const int br_bits[] = { ETHTOOL_LINK_MODE_Autoneg_BIT,
> + ETHTOOL_LINK_MODE_Pause_BIT,
> + ETHTOOL_LINK_MODE_Asym_Pause_BIT,
> + ETHTOOL_LINK_MODE_10baseT1BRR_Full_BIT,
> + ETHTOOL_LINK_MODE_100baseT1_Full_BIT };
More formatting issues. Maybe consider moving these out of the function?
> + for (i = 0; i < ARRAY_SIZE(br_bits); i++)
> + linkmode_clear_bit(br_bits[i], phydev->lp_advertising);
Formatting issue...
...
> + linkmode_mod_bit(ETHTOOL_LINK_MODE_Asym_Pause_BIT,
> + phydev->lp_advertising, lrelpa & LRELPA_PAUSE_ASYM);
> + linkmode_mod_bit(ETHTOOL_LINK_MODE_Pause_BIT,
> + phydev->lp_advertising, lrelpa & LRELPA_PAUSE);
> + linkmode_mod_bit(ETHTOOL_LINK_MODE_100baseT1_Full_BIT,
> + phydev->lp_advertising, lrelpa & LRELPA_100_1PAIR);
> + linkmode_mod_bit(ETHTOOL_LINK_MODE_10baseT1BRR_Full_BIT,
> + phydev->lp_advertising, lrelpa & LRELPA_10_1PAIR);
More formatting issues.
> +static int bcm54811_read_status(struct phy_device *phydev)
> +{
> + int err;
> + u8 brr_mode;
Reverse Christmas tree please.
--
RMK's Patch system: https://www.armlinux.org.uk/developer/patches/
FTTP is here! 80Mbps down 10Mbps up. Decent connectivity at last!
On 2024-06-17 at 17:08:41, Kamil Horák - 2N (kamilh@axis.com) wrote:
> +
> + if (brr_mode) {
> + linkmode_set_bit_array(phy_basic_ports_array,
> + ARRAY_SIZE(phy_basic_ports_array),
> + phydev->supported);
> +
> + val = phy_read(phydev, MII_BCM54XX_LRESR);
> + if (val < 0)
> + return val;
> +
> + linkmode_mod_bit(ETHTOOL_LINK_MODE_Autoneg_BIT,
> + phydev->supported, 1);
> + linkmode_mod_bit(ETHTOOL_LINK_MODE_100baseT1_Full_BIT,
> + phydev->supported,
> + val & LRESR_100_1PAIR);
> + linkmode_mod_bit(ETHTOOL_LINK_MODE_10baseT1BRR_Full_BIT,
> + phydev->supported,
> + val & LRESR_10_1PAIR);
> + } else {
> + return genphy_read_abilities(phydev);
> + }
> +
> + return 0;
nit: Could you move this return to "if" statement and get rid of else part ?
> +static int bcm5481_config_aneg(struct phy_device *phydev)
> +{
> + int ret;
> + u8 brr_mode;
nit: Reverse xmas-tree.
> +static int bcm54811_config_aneg(struct phy_device *phydev)
> +{
> + int ret;
> + u8 brr_mode;
nit: Please apply reverse xmas-tree comment everywhere applicable.
On Mon, 17 Jun 2024 13:38:41 +0200 Kamil Horák - 2N wrote:
> Implement single-pair BroadR-Reach modes on bcm5481x PHY by Broadcom.
> Create set of functions alternative to IEEE 802.3 to handle configuration
> of these modes on compatible Broadcom PHYs.
Some nit picks below, but please don't repost until next week.
Sorry for the delay but it's vacation season, I think some of
the key folks are currently AFK :(
> + * bcm_config_lre_advert - sanitize and advertise Long-Distance Signaling
> + * auto-negotiation parameters
> + * @phydev: target phy_device struct
> + * @return: 0 if the PHY's advertisement hasn't changed, < 0 on error,
> + * > 0 if it has changed
* Return: 0 if the PHY
no @ and after the description
> + *
> + * Description: Writes MII_BCM54XX_LREANAA with the appropriate values,
Please don't prefix the description with the word "description"..
> + * after sanitizing the values to make sure we only advertise
> + * what is supported.
> + */
> +int bcm_config_lre_advert(struct phy_device *phydev)
> +{
> + int err;
> + u32 adv;
> +
> + /* Only allow advertising what this PHY supports */
> + linkmode_and(phydev->advertising, phydev->advertising,
> + phydev->supported);
> +
> + adv = bcm_linkmode_adv_to_lre_adv_t(phydev->advertising);
> +
> + /* Setup BroadR-Reach mode advertisement */
> + err = phy_modify_changed(phydev, MII_BCM54XX_LREANAA,
> + LRE_ADVERTISE_ALL | LREANAA_PAUSE |
> + LREANAA_PAUSE_ASYM, adv);
> +
> + if (err < 0)
> + return err;
> +
> + return err;
You can return phy_modify_changed(... directly, no need for err
> +}
> +EXPORT_SYMBOL_GPL(bcm_config_lre_advert);
> +
> MODULE_DESCRIPTION("Broadcom PHY Library");
> MODULE_LICENSE("GPL v2");
> MODULE_AUTHOR("Broadcom Corporation");
> diff --git a/drivers/net/phy/bcm-phy-lib.h b/drivers/net/phy/bcm-phy-lib.h
> index b52189e45a84..fecdd66ad736 100644
> --- a/drivers/net/phy/bcm-phy-lib.h
> +++ b/drivers/net/phy/bcm-phy-lib.h
> @@ -121,4 +121,8 @@ irqreturn_t bcm_phy_wol_isr(int irq, void *dev_id);
> int bcm_phy_led_brightness_set(struct phy_device *phydev,
> u8 index, enum led_brightness value);
>
> +int bcm_setup_master_slave(struct phy_device *phydev);
> +int bcm_config_lre_aneg(struct phy_device *phydev, bool changed);
> +int bcm_config_lre_advert(struct phy_device *phydev);
> +
> #endif /* _LINUX_BCM_PHY_LIB_H */
> diff --git a/drivers/net/phy/broadcom.c b/drivers/net/phy/broadcom.c
> index 370e4ed45098..5e590c8f82c4 100644
> --- a/drivers/net/phy/broadcom.c
> +++ b/drivers/net/phy/broadcom.c
> @@ -5,6 +5,9 @@
> * Broadcom BCM5411, BCM5421 and BCM5461 Gigabit Ethernet
> * transceivers.
> *
> + * Broadcom BCM54810, BCM54811 BroadR-Reach transceivers.
> + *
> + *
double new line
> * Copyright (c) 2006 Maciej W. Rozycki
> *
> * Inspired by code written by Amy Fong.
> @@ -553,18 +556,97 @@ static int bcm54810_write_mmd(struct phy_device *phydev, int devnum, u16 regnum,
> return -EOPNOTSUPP;
> }
>
> -static int bcm54811_config_init(struct phy_device *phydev)
> +static int bcm5481x_get_brrmode(struct phy_device *phydev, u8 *data)
> {
> - int err, reg;
> + int reg;
>
> - /* Disable BroadR-Reach function. */
> reg = bcm_phy_read_exp(phydev, BCM54810_EXP_BROADREACH_LRE_MISC_CTL);
> - reg &= ~BCM54810_EXP_BROADREACH_LRE_MISC_CTL_EN;
> - err = bcm_phy_write_exp(phydev, BCM54810_EXP_BROADREACH_LRE_MISC_CTL,
> - reg);
> - if (err < 0)
> +
> + *data = (reg & BCM54810_EXP_BROADREACH_LRE_MISC_CTL_EN) ? 1 : 0;
> +
> + return 0;
> +}
> +
> +static int bcm54811_read_abilities(struct phy_device *phydev)
> +{
> + static const int modes_array[] = { ETHTOOL_LINK_MODE_100baseT1_Full_BIT,
> + ETHTOOL_LINK_MODE_10baseT1BRR_Full_BIT,
> + ETHTOOL_LINK_MODE_1000baseT_Full_BIT,
> + ETHTOOL_LINK_MODE_1000baseX_Full_BIT,
> + ETHTOOL_LINK_MODE_1000baseT_Half_BIT,
> + ETHTOOL_LINK_MODE_100baseT_Full_BIT,
> + ETHTOOL_LINK_MODE_100baseT_Half_BIT,
> + ETHTOOL_LINK_MODE_10baseT_Full_BIT,
> + ETHTOOL_LINK_MODE_10baseT_Half_BIT };
This is more normal formatting for the kernel:
static const int modes_array[] = {
ETHTOOL_LINK_MODE_100baseT1_Full_BIT,
ETHTOOL_LINK_MODE_10baseT1BRR_Full_BIT,
please try to avoid going over 80 characters
> +static int bcm54811_config_init(struct phy_device *phydev)
> +{
> + int err, reg;
> + bool brr = false;
> + struct device_node *np = phydev->mdio.dev.of_node;
order variable declaration lines longest to shortest,
AKA reverse xmas tree
On Mon, Jun 17, 2024 at 01:38:41PM +0200, Kamil Horák - 2N wrote: > Implement single-pair BroadR-Reach modes on bcm5481x PHY by Broadcom. > Create set of functions alternative to IEEE 802.3 to handle configuration > of these modes on compatible Broadcom PHYs. > > Signed-off-by: Kamil Horák - 2N <kamilh@axis.com> ... > +/** > + * lre_update_link - update link status in @phydev > + * @phydev: target phy_device struct > + * > + * Description: Update the value in phydev->link to reflect the > + * current link value. In order to do this, we need to read > + * the status register twice, keeping the second value. > + * This is a genphy_update_link modified to work on LRE registers > + * of BroadR-Reach PHY > + */ Hi Kamil, A minor nit from my side: Please consider adding a "Returns:" section to this kernel doc. Doing so as a follow-up would be fine IMHO. Flagged by kernel-doc -none -Wall > +static int lre_update_link(struct phy_device *phydev) ...
© 2016 - 2026 Red Hat, Inc.