Centos-kernel-stream-9/drivers/net/phy/broadcom.c

1619 lines
42 KiB
C
Raw Normal View History

// SPDX-License-Identifier: GPL-2.0+
/*
* drivers/net/phy/broadcom.c
*
* 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.
*/
#include "bcm-phy-lib.h"
net: phy: broadcom: Fix brcm_fet_config_init() JIRA: https://issues.redhat.com/browse/RHEL-2799 commit bf8bfc4336f7a34e48b3bbd19b1542bf085bdc3d Author: Florian Fainelli <f.fainelli@gmail.com> Date: Thu Mar 24 16:24:38 2022 -0700 net: phy: broadcom: Fix brcm_fet_config_init() A Broadcom AC201 PHY (same entry as 5241) would be flagged by the Broadcom UniMAC MDIO controller as not completing the turn around properly since the PHY expects 65 MDC clock cycles to complete a write cycle, and the MDIO controller was only sending 64 MDC clock cycles as determined by looking at a scope shot. This would make the subsequent read fail with the UniMAC MDIO controller command field having MDIO_READ_FAIL set and we would abort the brcm_fet_config_init() function and thus not probe the PHY at all. After issuing a software reset, wait for at least 1ms which is well above the 1us reset delay advertised by the datasheet and issue a dummy read to let the PHY turn around the line properly. This read specifically ignores -EIO which would be returned by MDIO controllers checking for the line being turned around. If we have a genuine reaad failure, the next read of the interrupt status register would pick it up anyway. Fixes: d7a2ed9248a3 ("broadcom: Add AC131 phy support") Signed-off-by: Florian Fainelli <f.fainelli@gmail.com> Link: https://lore.kernel.org/r/20220324232438.1156812-1-f.fainelli@gmail.com Signed-off-by: Jakub Kicinski <kuba@kernel.org> Signed-off-by: Adrien Thierry <athierry@redhat.com>
2023-09-14 14:11:09 +00:00
#include <linux/delay.h>
#include <linux/module.h>
#include <linux/phy.h>
net: phy: broadcom: Add support for Wake-on-LAN JIRA: https://issues.redhat.com/browse/RHEL-2799 commit 8baddaa9d4bac939004b5058f3ade7e2bf0a6e43 Author: Florian Fainelli <f.fainelli@gmail.com> Date: Thu May 11 10:21:09 2023 -0700 net: phy: broadcom: Add support for Wake-on-LAN Add support for WAKE_UCAST, WAKE_MCAST, WAKE_BCAST, WAKE_MAGIC and WAKE_MAGICSECURE. This is only supported with the BCM54210E and compatible Ethernet PHYs. Using the in-band interrupt or an out of band GPIO interrupts are supported. Broadcom PHYs will generate a Wake-on-LAN level low interrupt on LED4 as soon as one of the supported patterns is being matched. That includes generating such an interrupt even if the PHY is operated during normal modes. If WAKE_UCAST is selected, this could lead to the LED4 interrupt firing up for every packet being received which is absolutely undesirable from a performance point of view. Because the Wake-on-LAN configuration can be set long before the system is actually put to sleep, we cannot have an interrupt service routine to clear on read the interrupt status register and ensure that new packet matches will be detected. It is desirable to enable the Wake-on-LAN interrupt as late as possible during the system suspend process such that we limit the number of interrupts to be handled by the system, but also conversely feed into the Linux's system suspend way of dealing with interrupts in and around the points of no return. Reviewed-by: Simon Horman <simon.horman@corigine.com> Signed-off-by: Florian Fainelli <f.fainelli@gmail.com> Signed-off-by: David S. Miller <davem@davemloft.net> Signed-off-by: Adrien Thierry <athierry@redhat.com>
2023-09-14 14:11:57 +00:00
#include <linux/pm_wakeup.h>
#include <linux/brcmphy.h>
#include <linux/of.h>
net: phy: broadcom: Add support for Wake-on-LAN JIRA: https://issues.redhat.com/browse/RHEL-2799 commit 8baddaa9d4bac939004b5058f3ade7e2bf0a6e43 Author: Florian Fainelli <f.fainelli@gmail.com> Date: Thu May 11 10:21:09 2023 -0700 net: phy: broadcom: Add support for Wake-on-LAN Add support for WAKE_UCAST, WAKE_MCAST, WAKE_BCAST, WAKE_MAGIC and WAKE_MAGICSECURE. This is only supported with the BCM54210E and compatible Ethernet PHYs. Using the in-band interrupt or an out of band GPIO interrupts are supported. Broadcom PHYs will generate a Wake-on-LAN level low interrupt on LED4 as soon as one of the supported patterns is being matched. That includes generating such an interrupt even if the PHY is operated during normal modes. If WAKE_UCAST is selected, this could lead to the LED4 interrupt firing up for every packet being received which is absolutely undesirable from a performance point of view. Because the Wake-on-LAN configuration can be set long before the system is actually put to sleep, we cannot have an interrupt service routine to clear on read the interrupt status register and ensure that new packet matches will be detected. It is desirable to enable the Wake-on-LAN interrupt as late as possible during the system suspend process such that we limit the number of interrupts to be handled by the system, but also conversely feed into the Linux's system suspend way of dealing with interrupts in and around the points of no return. Reviewed-by: Simon Horman <simon.horman@corigine.com> Signed-off-by: Florian Fainelli <f.fainelli@gmail.com> Signed-off-by: David S. Miller <davem@davemloft.net> Signed-off-by: Adrien Thierry <athierry@redhat.com>
2023-09-14 14:11:57 +00:00
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/gpio/consumer.h>
#define BRCM_PHY_MODEL(phydev) \
((phydev)->drv->phy_id & (phydev)->drv->phy_id_mask)
#define BRCM_PHY_REV(phydev) \
((phydev)->drv->phy_id & ~((phydev)->drv->phy_id_mask))
MODULE_DESCRIPTION("Broadcom PHY driver");
MODULE_AUTHOR("Maciej W. Rozycki");
MODULE_LICENSE("GPL");
struct bcm54xx_phy_priv {
u64 *stats;
struct bcm_ptp_private *ptp;
net: phy: broadcom: Add support for Wake-on-LAN JIRA: https://issues.redhat.com/browse/RHEL-2799 commit 8baddaa9d4bac939004b5058f3ade7e2bf0a6e43 Author: Florian Fainelli <f.fainelli@gmail.com> Date: Thu May 11 10:21:09 2023 -0700 net: phy: broadcom: Add support for Wake-on-LAN Add support for WAKE_UCAST, WAKE_MCAST, WAKE_BCAST, WAKE_MAGIC and WAKE_MAGICSECURE. This is only supported with the BCM54210E and compatible Ethernet PHYs. Using the in-band interrupt or an out of band GPIO interrupts are supported. Broadcom PHYs will generate a Wake-on-LAN level low interrupt on LED4 as soon as one of the supported patterns is being matched. That includes generating such an interrupt even if the PHY is operated during normal modes. If WAKE_UCAST is selected, this could lead to the LED4 interrupt firing up for every packet being received which is absolutely undesirable from a performance point of view. Because the Wake-on-LAN configuration can be set long before the system is actually put to sleep, we cannot have an interrupt service routine to clear on read the interrupt status register and ensure that new packet matches will be detected. It is desirable to enable the Wake-on-LAN interrupt as late as possible during the system suspend process such that we limit the number of interrupts to be handled by the system, but also conversely feed into the Linux's system suspend way of dealing with interrupts in and around the points of no return. Reviewed-by: Simon Horman <simon.horman@corigine.com> Signed-off-by: Florian Fainelli <f.fainelli@gmail.com> Signed-off-by: David S. Miller <davem@davemloft.net> Signed-off-by: Adrien Thierry <athierry@redhat.com>
2023-09-14 14:11:57 +00:00
int wake_irq;
bool wake_irq_enabled;
bool brr_mode;
};
/* Link modes for BCM58411 PHY */
static const int bcm54811_linkmodes[] = {
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
};
/* Long-Distance Signaling (BroadR-Reach mode aneg) relevant linkmode bits */
static const int lds_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
};
net: phy: broadcom: Add support for Wake-on-LAN JIRA: https://issues.redhat.com/browse/RHEL-2799 commit 8baddaa9d4bac939004b5058f3ade7e2bf0a6e43 Author: Florian Fainelli <f.fainelli@gmail.com> Date: Thu May 11 10:21:09 2023 -0700 net: phy: broadcom: Add support for Wake-on-LAN Add support for WAKE_UCAST, WAKE_MCAST, WAKE_BCAST, WAKE_MAGIC and WAKE_MAGICSECURE. This is only supported with the BCM54210E and compatible Ethernet PHYs. Using the in-band interrupt or an out of band GPIO interrupts are supported. Broadcom PHYs will generate a Wake-on-LAN level low interrupt on LED4 as soon as one of the supported patterns is being matched. That includes generating such an interrupt even if the PHY is operated during normal modes. If WAKE_UCAST is selected, this could lead to the LED4 interrupt firing up for every packet being received which is absolutely undesirable from a performance point of view. Because the Wake-on-LAN configuration can be set long before the system is actually put to sleep, we cannot have an interrupt service routine to clear on read the interrupt status register and ensure that new packet matches will be detected. It is desirable to enable the Wake-on-LAN interrupt as late as possible during the system suspend process such that we limit the number of interrupts to be handled by the system, but also conversely feed into the Linux's system suspend way of dealing with interrupts in and around the points of no return. Reviewed-by: Simon Horman <simon.horman@corigine.com> Signed-off-by: Florian Fainelli <f.fainelli@gmail.com> Signed-off-by: David S. Miller <davem@davemloft.net> Signed-off-by: Adrien Thierry <athierry@redhat.com>
2023-09-14 14:11:57 +00:00
static bool bcm54xx_phy_can_wakeup(struct phy_device *phydev)
{
struct bcm54xx_phy_priv *priv = phydev->priv;
return phy_interrupt_is_valid(phydev) || priv->wake_irq >= 0;
}
static int bcm54xx_config_clock_delay(struct phy_device *phydev)
{
int rc, val;
/* handling PHY's internal RX clock delay */
val = bcm54xx_auxctl_read(phydev, MII_BCM54XX_AUXCTL_SHDWSEL_MISC);
val |= MII_BCM54XX_AUXCTL_MISC_WREN;
if (phydev->interface == PHY_INTERFACE_MODE_RGMII ||
phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID) {
/* Disable RGMII RXC-RXD skew */
val &= ~MII_BCM54XX_AUXCTL_SHDWSEL_MISC_RGMII_SKEW_EN;
}
if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID ||
phydev->interface == PHY_INTERFACE_MODE_RGMII_RXID) {
/* Enable RGMII RXC-RXD skew */
val |= MII_BCM54XX_AUXCTL_SHDWSEL_MISC_RGMII_SKEW_EN;
}
rc = bcm54xx_auxctl_write(phydev, MII_BCM54XX_AUXCTL_SHDWSEL_MISC,
val);
if (rc < 0)
return rc;
/* handling PHY's internal TX clock delay */
val = bcm_phy_read_shadow(phydev, BCM54810_SHD_CLK_CTL);
if (phydev->interface == PHY_INTERFACE_MODE_RGMII ||
phydev->interface == PHY_INTERFACE_MODE_RGMII_RXID) {
/* Disable internal TX clock delay */
val &= ~BCM54810_SHD_CLK_CTL_GTXCLK_EN;
}
if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID ||
phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID) {
/* Enable internal TX clock delay */
val |= BCM54810_SHD_CLK_CTL_GTXCLK_EN;
}
rc = bcm_phy_write_shadow(phydev, BCM54810_SHD_CLK_CTL, val);
if (rc < 0)
return rc;
return 0;
}
static int bcm54210e_config_init(struct phy_device *phydev)
{
int val;
bcm54xx_config_clock_delay(phydev);
if (phydev->dev_flags & PHY_BRCM_EN_MASTER_MODE) {
val = phy_read(phydev, MII_CTRL1000);
val |= CTL1000_AS_MASTER | CTL1000_ENABLE_MASTER;
phy_write(phydev, MII_CTRL1000, val);
}
return 0;
}
static int bcm54612e_config_init(struct phy_device *phydev)
{
int reg;
bcm54xx_config_clock_delay(phydev);
/* Enable CLK125 MUX on LED4 if ref clock is enabled. */
if (!(phydev->dev_flags & PHY_BRCM_RX_REFCLK_UNUSED)) {
int err;
reg = bcm_phy_read_exp(phydev, BCM54612E_EXP_SPARE0);
err = bcm_phy_write_exp(phydev, BCM54612E_EXP_SPARE0,
BCM54612E_LED4_CLK125OUT_EN | reg);
if (err < 0)
return err;
}
return 0;
}
static int bcm54616s_config_init(struct phy_device *phydev)
{
int rc, val;
if (phydev->interface != PHY_INTERFACE_MODE_SGMII &&
phydev->interface != PHY_INTERFACE_MODE_1000BASEX)
return 0;
/* Ensure proper interface mode is selected. */
/* Disable RGMII mode */
val = bcm54xx_auxctl_read(phydev, MII_BCM54XX_AUXCTL_SHDWSEL_MISC);
if (val < 0)
return val;
val &= ~MII_BCM54XX_AUXCTL_SHDWSEL_MISC_RGMII_EN;
val |= MII_BCM54XX_AUXCTL_MISC_WREN;
rc = bcm54xx_auxctl_write(phydev, MII_BCM54XX_AUXCTL_SHDWSEL_MISC,
val);
if (rc < 0)
return rc;
/* Select 1000BASE-X register set (primary SerDes) */
val = bcm_phy_read_shadow(phydev, BCM54XX_SHD_MODE);
if (val < 0)
return val;
val |= BCM54XX_SHD_MODE_1000BX;
rc = bcm_phy_write_shadow(phydev, BCM54XX_SHD_MODE, val);
if (rc < 0)
return rc;
/* Power down SerDes interface */
rc = phy_set_bits(phydev, MII_BMCR, BMCR_PDOWN);
if (rc < 0)
return rc;
/* Select proper interface mode */
val &= ~BCM54XX_SHD_INTF_SEL_MASK;
val |= phydev->interface == PHY_INTERFACE_MODE_SGMII ?
BCM54XX_SHD_INTF_SEL_SGMII :
BCM54XX_SHD_INTF_SEL_GBIC;
rc = bcm_phy_write_shadow(phydev, BCM54XX_SHD_MODE, val);
if (rc < 0)
return rc;
/* Power up SerDes interface */
rc = phy_clear_bits(phydev, MII_BMCR, BMCR_PDOWN);
if (rc < 0)
return rc;
/* Select copper register set */
val &= ~BCM54XX_SHD_MODE_1000BX;
rc = bcm_phy_write_shadow(phydev, BCM54XX_SHD_MODE, val);
if (rc < 0)
return rc;
/* Power up copper interface */
return phy_clear_bits(phydev, MII_BMCR, BMCR_PDOWN);
}
/* Needs SMDSP clock enabled via bcm54xx_phydsp_config() */
static int bcm50610_a0_workaround(struct phy_device *phydev)
{
int err;
err = bcm_phy_write_exp(phydev, MII_BCM54XX_EXP_AADJ1CH0,
MII_BCM54XX_EXP_AADJ1CH0_SWP_ABCD_OEN |
MII_BCM54XX_EXP_AADJ1CH0_SWSEL_THPF);
if (err < 0)
return err;
err = bcm_phy_write_exp(phydev, MII_BCM54XX_EXP_AADJ1CH3,
MII_BCM54XX_EXP_AADJ1CH3_ADCCKADJ);
if (err < 0)
return err;
err = bcm_phy_write_exp(phydev, MII_BCM54XX_EXP_EXP75,
MII_BCM54XX_EXP_EXP75_VDACCTRL);
if (err < 0)
return err;
err = bcm_phy_write_exp(phydev, MII_BCM54XX_EXP_EXP96,
MII_BCM54XX_EXP_EXP96_MYST);
if (err < 0)
return err;
err = bcm_phy_write_exp(phydev, MII_BCM54XX_EXP_EXP97,
MII_BCM54XX_EXP_EXP97_MYST);
return err;
}
static int bcm54xx_phydsp_config(struct phy_device *phydev)
{
int err, err2;
/* Enable the SMDSP clock */
err = bcm54xx_auxctl_write(phydev,
MII_BCM54XX_AUXCTL_SHDWSEL_AUXCTL,
MII_BCM54XX_AUXCTL_ACTL_SMDSP_ENA |
MII_BCM54XX_AUXCTL_ACTL_TX_6DB);
if (err < 0)
return err;
if (BRCM_PHY_MODEL(phydev) == PHY_ID_BCM50610 ||
BRCM_PHY_MODEL(phydev) == PHY_ID_BCM50610M) {
/* Clear bit 9 to fix a phy interop issue. */
err = bcm_phy_write_exp(phydev, MII_BCM54XX_EXP_EXP08,
MII_BCM54XX_EXP_EXP08_RJCT_2MHZ);
if (err < 0)
goto error;
if (phydev->drv->phy_id == PHY_ID_BCM50610) {
err = bcm50610_a0_workaround(phydev);
if (err < 0)
goto error;
}
}
if (BRCM_PHY_MODEL(phydev) == PHY_ID_BCM57780) {
int val;
val = bcm_phy_read_exp(phydev, MII_BCM54XX_EXP_EXP75);
if (val < 0)
goto error;
val |= MII_BCM54XX_EXP_EXP75_CM_OSC;
err = bcm_phy_write_exp(phydev, MII_BCM54XX_EXP_EXP75, val);
}
error:
/* Disable the SMDSP clock */
err2 = bcm54xx_auxctl_write(phydev,
MII_BCM54XX_AUXCTL_SHDWSEL_AUXCTL,
MII_BCM54XX_AUXCTL_ACTL_TX_6DB);
/* Return the first error reported. */
return err ? err : err2;
}
static void bcm54xx_adjust_rxrefclk(struct phy_device *phydev)
{
u32 orig;
int val;
bool clk125en = true;
/* Abort if we are using an untested phy. */
if (BRCM_PHY_MODEL(phydev) != PHY_ID_BCM57780 &&
BRCM_PHY_MODEL(phydev) != PHY_ID_BCM50610 &&
BRCM_PHY_MODEL(phydev) != PHY_ID_BCM50610M &&
BRCM_PHY_MODEL(phydev) != PHY_ID_BCM54210E &&
BRCM_PHY_MODEL(phydev) != PHY_ID_BCM54810 &&
BRCM_PHY_MODEL(phydev) != PHY_ID_BCM54811)
return;
val = bcm_phy_read_shadow(phydev, BCM54XX_SHD_SCR3);
if (val < 0)
return;
orig = val;
if ((BRCM_PHY_MODEL(phydev) == PHY_ID_BCM50610 ||
BRCM_PHY_MODEL(phydev) == PHY_ID_BCM50610M) &&
BRCM_PHY_REV(phydev) >= 0x3) {
/*
* Here, bit 0 _disables_ CLK125 when set.
* This bit is set by default.
*/
clk125en = false;
} else {
if (phydev->dev_flags & PHY_BRCM_RX_REFCLK_UNUSED) {
if (BRCM_PHY_MODEL(phydev) != PHY_ID_BCM54811) {
/* Here, bit 0 _enables_ CLK125 when set */
val &= ~BCM54XX_SHD_SCR3_DEF_CLK125;
}
clk125en = false;
}
}
if (!clk125en || (phydev->dev_flags & PHY_BRCM_AUTO_PWRDWN_ENABLE))
val &= ~BCM54XX_SHD_SCR3_DLLAPD_DIS;
else
val |= BCM54XX_SHD_SCR3_DLLAPD_DIS;
if (phydev->dev_flags & PHY_BRCM_DIS_TXCRXC_NOENRGY) {
if (BRCM_PHY_MODEL(phydev) == PHY_ID_BCM54210E ||
BRCM_PHY_MODEL(phydev) == PHY_ID_BCM54810 ||
BRCM_PHY_MODEL(phydev) == PHY_ID_BCM54811)
val |= BCM54XX_SHD_SCR3_RXCTXC_DIS;
else
val |= BCM54XX_SHD_SCR3_TRDDAPD;
}
if (orig != val)
bcm_phy_write_shadow(phydev, BCM54XX_SHD_SCR3, val);
val = bcm_phy_read_shadow(phydev, BCM54XX_SHD_APD);
if (val < 0)
return;
orig = val;
if (!clk125en || (phydev->dev_flags & PHY_BRCM_AUTO_PWRDWN_ENABLE))
val |= BCM54XX_SHD_APD_EN;
else
val &= ~BCM54XX_SHD_APD_EN;
if (orig != val)
bcm_phy_write_shadow(phydev, BCM54XX_SHD_APD, val);
}
static void bcm54xx_ptp_stop(struct phy_device *phydev)
{
struct bcm54xx_phy_priv *priv = phydev->priv;
if (priv->ptp)
bcm_ptp_stop(priv->ptp);
}
static void bcm54xx_ptp_config_init(struct phy_device *phydev)
{
struct bcm54xx_phy_priv *priv = phydev->priv;
if (priv->ptp)
bcm_ptp_config_init(phydev);
}
static int bcm5481x_set_brrmode(struct phy_device *phydev, bool on)
{
int reg;
int err;
u16 val;
reg = bcm_phy_read_exp(phydev, BCM54810_EXP_BROADREACH_LRE_MISC_CTL);
if (reg < 0)
return reg;
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;
/* Ensure LRE or IEEE register set is accessed according to the brr
* on/off, thus set the override
*/
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);
}
static int bcm54811_config_init(struct phy_device *phydev)
{
struct bcm54xx_phy_priv *priv = phydev->priv;
int err, reg;
/* Enable CLK125 MUX on LED4 if ref clock is enabled. */
if (!(phydev->dev_flags & PHY_BRCM_RX_REFCLK_UNUSED)) {
reg = bcm_phy_read_exp(phydev, BCM54612E_EXP_SPARE0);
if (reg < 0)
return reg;
err = bcm_phy_write_exp(phydev, BCM54612E_EXP_SPARE0,
BCM54612E_LED4_CLK125OUT_EN | reg);
if (err < 0)
return err;
}
/* With BCM54811, BroadR-Reach implies no autoneg */
if (priv->brr_mode)
phydev->autoneg = 0;
return bcm5481x_set_brrmode(phydev, priv->brr_mode);
}
static int bcm54xx_config_init(struct phy_device *phydev)
{
int reg, err, val;
reg = phy_read(phydev, MII_BCM54XX_ECR);
if (reg < 0)
return reg;
/* Mask interrupts globally. */
reg |= MII_BCM54XX_ECR_IM;
err = phy_write(phydev, MII_BCM54XX_ECR, reg);
if (err < 0)
return err;
/* Unmask events we are interested in. */
reg = ~(MII_BCM54XX_INT_DUPLEX |
MII_BCM54XX_INT_SPEED |
MII_BCM54XX_INT_LINK);
err = phy_write(phydev, MII_BCM54XX_IMR, reg);
if (err < 0)
return err;
if ((BRCM_PHY_MODEL(phydev) == PHY_ID_BCM50610 ||
BRCM_PHY_MODEL(phydev) == PHY_ID_BCM50610M) &&
(phydev->dev_flags & PHY_BRCM_CLEAR_RGMII_MODE))
bcm_phy_write_shadow(phydev, BCM54XX_SHD_RGMII_MODE, 0);
bcm54xx_adjust_rxrefclk(phydev);
switch (BRCM_PHY_MODEL(phydev)) {
case PHY_ID_BCM50610:
case PHY_ID_BCM50610M:
err = bcm54xx_config_clock_delay(phydev);
break;
case PHY_ID_BCM54210E:
err = bcm54210e_config_init(phydev);
break;
case PHY_ID_BCM54612E:
err = bcm54612e_config_init(phydev);
break;
case PHY_ID_BCM54616S:
err = bcm54616s_config_init(phydev);
break;
case PHY_ID_BCM54810:
/* For BCM54810, we need to disable BroadR-Reach function */
val = bcm_phy_read_exp(phydev,
BCM54810_EXP_BROADREACH_LRE_MISC_CTL);
val &= ~BCM54810_EXP_BROADREACH_LRE_MISC_CTL_EN;
err = bcm_phy_write_exp(phydev,
BCM54810_EXP_BROADREACH_LRE_MISC_CTL,
val);
break;
case PHY_ID_BCM54811:
err = bcm54811_config_init(phydev);
break;
}
if (err)
return err;
bcm54xx_phydsp_config(phydev);
/* For non-SFP setups, encode link speed into LED1 and LED3 pair
* (green/amber).
* Also flash these two LEDs on activity. This means configuring
* them for MULTICOLOR and encoding link/activity into them.
* Don't do this for devices on an SFP module, since some of these
* use the LED outputs to control the SFP LOS signal, and changing
* these settings will cause LOS to malfunction.
*/
if (!phy_on_sfp(phydev)) {
val = BCM54XX_SHD_LEDS1_LED1(BCM_LED_SRC_MULTICOLOR1) |
BCM54XX_SHD_LEDS1_LED3(BCM_LED_SRC_MULTICOLOR1);
bcm_phy_write_shadow(phydev, BCM54XX_SHD_LEDS1, val);
val = BCM_LED_MULTICOLOR_IN_PHASE |
BCM54XX_SHD_LEDS1_LED1(BCM_LED_MULTICOLOR_LINK_ACT) |
BCM54XX_SHD_LEDS1_LED3(BCM_LED_MULTICOLOR_LINK_ACT);
bcm_phy_write_exp(phydev, BCM_EXP_MULTICOLOR, val);
}
bcm54xx_ptp_config_init(phydev);
net: phy: broadcom: Add support for Wake-on-LAN JIRA: https://issues.redhat.com/browse/RHEL-2799 commit 8baddaa9d4bac939004b5058f3ade7e2bf0a6e43 Author: Florian Fainelli <f.fainelli@gmail.com> Date: Thu May 11 10:21:09 2023 -0700 net: phy: broadcom: Add support for Wake-on-LAN Add support for WAKE_UCAST, WAKE_MCAST, WAKE_BCAST, WAKE_MAGIC and WAKE_MAGICSECURE. This is only supported with the BCM54210E and compatible Ethernet PHYs. Using the in-band interrupt or an out of band GPIO interrupts are supported. Broadcom PHYs will generate a Wake-on-LAN level low interrupt on LED4 as soon as one of the supported patterns is being matched. That includes generating such an interrupt even if the PHY is operated during normal modes. If WAKE_UCAST is selected, this could lead to the LED4 interrupt firing up for every packet being received which is absolutely undesirable from a performance point of view. Because the Wake-on-LAN configuration can be set long before the system is actually put to sleep, we cannot have an interrupt service routine to clear on read the interrupt status register and ensure that new packet matches will be detected. It is desirable to enable the Wake-on-LAN interrupt as late as possible during the system suspend process such that we limit the number of interrupts to be handled by the system, but also conversely feed into the Linux's system suspend way of dealing with interrupts in and around the points of no return. Reviewed-by: Simon Horman <simon.horman@corigine.com> Signed-off-by: Florian Fainelli <f.fainelli@gmail.com> Signed-off-by: David S. Miller <davem@davemloft.net> Signed-off-by: Adrien Thierry <athierry@redhat.com>
2023-09-14 14:11:57 +00:00
/* Acknowledge any left over interrupt and charge the device for
* wake-up.
*/
err = bcm_phy_read_exp(phydev, BCM54XX_WOL_INT_STATUS);
if (err < 0)
return err;
if (err)
pm_wakeup_event(&phydev->mdio.dev, 0);
return 0;
}
static int bcm54xx_iddq_set(struct phy_device *phydev, bool enable)
{
int ret = 0;
if (!(phydev->dev_flags & PHY_BRCM_IDDQ_SUSPEND))
return ret;
ret = bcm_phy_read_exp(phydev, BCM54XX_TOP_MISC_IDDQ_CTRL);
if (ret < 0)
goto out;
if (enable)
ret |= BCM54XX_TOP_MISC_IDDQ_SR | BCM54XX_TOP_MISC_IDDQ_LP;
else
ret &= ~(BCM54XX_TOP_MISC_IDDQ_SR | BCM54XX_TOP_MISC_IDDQ_LP);
ret = bcm_phy_write_exp(phydev, BCM54XX_TOP_MISC_IDDQ_CTRL, ret);
out:
return ret;
}
net: phy: broadcom: Add support for Wake-on-LAN JIRA: https://issues.redhat.com/browse/RHEL-2799 commit 8baddaa9d4bac939004b5058f3ade7e2bf0a6e43 Author: Florian Fainelli <f.fainelli@gmail.com> Date: Thu May 11 10:21:09 2023 -0700 net: phy: broadcom: Add support for Wake-on-LAN Add support for WAKE_UCAST, WAKE_MCAST, WAKE_BCAST, WAKE_MAGIC and WAKE_MAGICSECURE. This is only supported with the BCM54210E and compatible Ethernet PHYs. Using the in-band interrupt or an out of band GPIO interrupts are supported. Broadcom PHYs will generate a Wake-on-LAN level low interrupt on LED4 as soon as one of the supported patterns is being matched. That includes generating such an interrupt even if the PHY is operated during normal modes. If WAKE_UCAST is selected, this could lead to the LED4 interrupt firing up for every packet being received which is absolutely undesirable from a performance point of view. Because the Wake-on-LAN configuration can be set long before the system is actually put to sleep, we cannot have an interrupt service routine to clear on read the interrupt status register and ensure that new packet matches will be detected. It is desirable to enable the Wake-on-LAN interrupt as late as possible during the system suspend process such that we limit the number of interrupts to be handled by the system, but also conversely feed into the Linux's system suspend way of dealing with interrupts in and around the points of no return. Reviewed-by: Simon Horman <simon.horman@corigine.com> Signed-off-by: Florian Fainelli <f.fainelli@gmail.com> Signed-off-by: David S. Miller <davem@davemloft.net> Signed-off-by: Adrien Thierry <athierry@redhat.com>
2023-09-14 14:11:57 +00:00
static int bcm54xx_set_wakeup_irq(struct phy_device *phydev, bool state)
{
struct bcm54xx_phy_priv *priv = phydev->priv;
int ret = 0;
if (!bcm54xx_phy_can_wakeup(phydev))
return ret;
if (priv->wake_irq_enabled != state) {
if (state)
ret = enable_irq_wake(priv->wake_irq);
else
ret = disable_irq_wake(priv->wake_irq);
priv->wake_irq_enabled = state;
}
return ret;
}
static int bcm54xx_suspend(struct phy_device *phydev)
{
net: phy: broadcom: Add support for Wake-on-LAN JIRA: https://issues.redhat.com/browse/RHEL-2799 commit 8baddaa9d4bac939004b5058f3ade7e2bf0a6e43 Author: Florian Fainelli <f.fainelli@gmail.com> Date: Thu May 11 10:21:09 2023 -0700 net: phy: broadcom: Add support for Wake-on-LAN Add support for WAKE_UCAST, WAKE_MCAST, WAKE_BCAST, WAKE_MAGIC and WAKE_MAGICSECURE. This is only supported with the BCM54210E and compatible Ethernet PHYs. Using the in-band interrupt or an out of band GPIO interrupts are supported. Broadcom PHYs will generate a Wake-on-LAN level low interrupt on LED4 as soon as one of the supported patterns is being matched. That includes generating such an interrupt even if the PHY is operated during normal modes. If WAKE_UCAST is selected, this could lead to the LED4 interrupt firing up for every packet being received which is absolutely undesirable from a performance point of view. Because the Wake-on-LAN configuration can be set long before the system is actually put to sleep, we cannot have an interrupt service routine to clear on read the interrupt status register and ensure that new packet matches will be detected. It is desirable to enable the Wake-on-LAN interrupt as late as possible during the system suspend process such that we limit the number of interrupts to be handled by the system, but also conversely feed into the Linux's system suspend way of dealing with interrupts in and around the points of no return. Reviewed-by: Simon Horman <simon.horman@corigine.com> Signed-off-by: Florian Fainelli <f.fainelli@gmail.com> Signed-off-by: David S. Miller <davem@davemloft.net> Signed-off-by: Adrien Thierry <athierry@redhat.com>
2023-09-14 14:11:57 +00:00
int ret = 0;
bcm54xx_ptp_stop(phydev);
net: phy: broadcom: Add support for Wake-on-LAN JIRA: https://issues.redhat.com/browse/RHEL-2799 commit 8baddaa9d4bac939004b5058f3ade7e2bf0a6e43 Author: Florian Fainelli <f.fainelli@gmail.com> Date: Thu May 11 10:21:09 2023 -0700 net: phy: broadcom: Add support for Wake-on-LAN Add support for WAKE_UCAST, WAKE_MCAST, WAKE_BCAST, WAKE_MAGIC and WAKE_MAGICSECURE. This is only supported with the BCM54210E and compatible Ethernet PHYs. Using the in-band interrupt or an out of band GPIO interrupts are supported. Broadcom PHYs will generate a Wake-on-LAN level low interrupt on LED4 as soon as one of the supported patterns is being matched. That includes generating such an interrupt even if the PHY is operated during normal modes. If WAKE_UCAST is selected, this could lead to the LED4 interrupt firing up for every packet being received which is absolutely undesirable from a performance point of view. Because the Wake-on-LAN configuration can be set long before the system is actually put to sleep, we cannot have an interrupt service routine to clear on read the interrupt status register and ensure that new packet matches will be detected. It is desirable to enable the Wake-on-LAN interrupt as late as possible during the system suspend process such that we limit the number of interrupts to be handled by the system, but also conversely feed into the Linux's system suspend way of dealing with interrupts in and around the points of no return. Reviewed-by: Simon Horman <simon.horman@corigine.com> Signed-off-by: Florian Fainelli <f.fainelli@gmail.com> Signed-off-by: David S. Miller <davem@davemloft.net> Signed-off-by: Adrien Thierry <athierry@redhat.com>
2023-09-14 14:11:57 +00:00
/* Acknowledge any Wake-on-LAN interrupt prior to suspend */
ret = bcm_phy_read_exp(phydev, BCM54XX_WOL_INT_STATUS);
if (ret < 0)
return ret;
if (phydev->wol_enabled)
return bcm54xx_set_wakeup_irq(phydev, true);
/* We cannot use a read/modify/write here otherwise the PHY gets into
* a bad state where its LEDs keep flashing, thus defeating the purpose
* of low power mode.
*/
ret = phy_write(phydev, MII_BMCR, BMCR_PDOWN);
if (ret < 0)
return ret;
return bcm54xx_iddq_set(phydev, true);
}
static int bcm54xx_resume(struct phy_device *phydev)
{
net: phy: broadcom: Add support for Wake-on-LAN JIRA: https://issues.redhat.com/browse/RHEL-2799 commit 8baddaa9d4bac939004b5058f3ade7e2bf0a6e43 Author: Florian Fainelli <f.fainelli@gmail.com> Date: Thu May 11 10:21:09 2023 -0700 net: phy: broadcom: Add support for Wake-on-LAN Add support for WAKE_UCAST, WAKE_MCAST, WAKE_BCAST, WAKE_MAGIC and WAKE_MAGICSECURE. This is only supported with the BCM54210E and compatible Ethernet PHYs. Using the in-band interrupt or an out of band GPIO interrupts are supported. Broadcom PHYs will generate a Wake-on-LAN level low interrupt on LED4 as soon as one of the supported patterns is being matched. That includes generating such an interrupt even if the PHY is operated during normal modes. If WAKE_UCAST is selected, this could lead to the LED4 interrupt firing up for every packet being received which is absolutely undesirable from a performance point of view. Because the Wake-on-LAN configuration can be set long before the system is actually put to sleep, we cannot have an interrupt service routine to clear on read the interrupt status register and ensure that new packet matches will be detected. It is desirable to enable the Wake-on-LAN interrupt as late as possible during the system suspend process such that we limit the number of interrupts to be handled by the system, but also conversely feed into the Linux's system suspend way of dealing with interrupts in and around the points of no return. Reviewed-by: Simon Horman <simon.horman@corigine.com> Signed-off-by: Florian Fainelli <f.fainelli@gmail.com> Signed-off-by: David S. Miller <davem@davemloft.net> Signed-off-by: Adrien Thierry <athierry@redhat.com>
2023-09-14 14:11:57 +00:00
int ret = 0;
if (phydev->wol_enabled) {
ret = bcm54xx_set_wakeup_irq(phydev, false);
if (ret)
return ret;
}
ret = bcm54xx_iddq_set(phydev, false);
if (ret < 0)
return ret;
/* Writes to register other than BMCR would be ignored
* unless we clear the PDOWN bit first
*/
ret = genphy_resume(phydev);
if (ret < 0)
return ret;
/* Upon exiting power down, the PHY remains in an internal reset state
* for 40us
*/
fsleep(40);
/* Issue a soft reset after clearing the power down bit
* and before doing any other configuration.
*/
if (phydev->dev_flags & PHY_BRCM_IDDQ_SUSPEND) {
ret = genphy_soft_reset(phydev);
if (ret < 0)
return ret;
}
return bcm54xx_config_init(phydev);
}
static int bcm54810_read_mmd(struct phy_device *phydev, int devnum, u16 regnum)
{
return -EOPNOTSUPP;
}
static int bcm54810_write_mmd(struct phy_device *phydev, int devnum, u16 regnum,
u16 val)
{
return -EOPNOTSUPP;
}
/**
* bcm5481x_read_abilities - read PHY abilities from LRESR or Clause 22
* (BMSR) registers, based on whether the PHY is in BroadR-Reach or IEEE mode
* @phydev: target phy_device struct
*
* Description: Reads the PHY's abilities and populates phydev->supported
* accordingly. The register to read the abilities from is determined by
* the brr mode setting of the PHY as read from the device tree.
* Note that the LRE and IEEE sets of abilities are disjunct, in other words,
* not only the link modes differ, but also the auto-negotiation and
* master-slave setup is controlled differently.
*
* Returns: 0 on success, < 0 on failure
*/
static int bcm5481x_read_abilities(struct phy_device *phydev)
{
struct device_node *np = phydev->mdio.dev.of_node;
struct bcm54xx_phy_priv *priv = phydev->priv;
int i, val, err;
for (i = 0; i < ARRAY_SIZE(bcm54811_linkmodes); i++)
linkmode_clear_bit(bcm54811_linkmodes[i], phydev->supported);
priv->brr_mode = of_property_read_bool(np, "brr-mode");
/* Set BroadR-Reach mode as configured in the DT. */
err = bcm5481x_set_brrmode(phydev, priv->brr_mode);
if (err)
return err;
if (priv->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,
val & LRESR_LDSABILITY);
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);
return 0;
}
return genphy_read_abilities(phydev);
}
static int bcm5481x_config_delay_swap(struct phy_device *phydev)
{
struct device_node *np = phydev->mdio.dev.of_node;
/* Set up the delay. */
bcm54xx_config_clock_delay(phydev);
if (of_property_read_bool(np, "enet-phy-lane-swap")) {
/* Lane Swap - Undocumented register...magic! */
int ret = bcm_phy_write_exp(phydev,
MII_BCM54XX_EXP_SEL_ER + 0x9,
0x11B);
if (ret < 0)
return ret;
}
return 0;
}
static int bcm5481_config_aneg(struct phy_device *phydev)
{
struct bcm54xx_phy_priv *priv = phydev->priv;
int ret;
/* Aneg firstly. */
if (priv->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)
{
struct bcm54xx_phy_priv *priv = phydev->priv;
int ret;
/* Aneg firstly. */
if (priv->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 {
bool mode_1000bx_en;
};
static int bcm54616s_probe(struct phy_device *phydev)
{
struct bcm54616s_phy_priv *priv;
int val;
priv = devm_kzalloc(&phydev->mdio.dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
phydev->priv = priv;
val = bcm_phy_read_shadow(phydev, BCM54XX_SHD_MODE);
if (val < 0)
return val;
/* The PHY is strapped in RGMII-fiber mode when INTERF_SEL[1:0]
* is 01b, and the link between PHY and its link partner can be
* either 1000Base-X or 100Base-FX.
* RGMII-1000Base-X is properly supported, but RGMII-100Base-FX
* support is still missing as of now.
*/
if ((val & BCM54XX_SHD_INTF_SEL_MASK) == BCM54XX_SHD_INTF_SEL_RGMII) {
val = bcm_phy_read_shadow(phydev, BCM54616S_SHD_100FX_CTRL);
if (val < 0)
return val;
/* Bit 0 of the SerDes 100-FX Control register, when set
* to 1, sets the MII/RGMII -> 100BASE-FX configuration.
* When this bit is set to 0, it sets the GMII/RGMII ->
* 1000BASE-X configuration.
*/
if (!(val & BCM54616S_100FX_MODE))
priv->mode_1000bx_en = true;
phydev->port = PORT_FIBRE;
}
return 0;
}
static int bcm54616s_config_aneg(struct phy_device *phydev)
{
struct bcm54616s_phy_priv *priv = phydev->priv;
int ret;
/* Aneg firstly. */
if (priv->mode_1000bx_en)
ret = genphy_c37_config_aneg(phydev);
else
ret = genphy_config_aneg(phydev);
/* Then we can set up the delay. */
bcm54xx_config_clock_delay(phydev);
return ret;
}
static int bcm54616s_read_status(struct phy_device *phydev)
{
struct bcm54616s_phy_priv *priv = phydev->priv;
bool changed;
int err;
if (priv->mode_1000bx_en)
err = genphy_c37_read_status(phydev, &changed);
else
err = genphy_read_status(phydev);
return err;
}
static int brcm_fet_config_init(struct phy_device *phydev)
{
int reg, err, err2, brcmtest;
/* Reset the PHY to bring it to a known state. */
err = phy_write(phydev, MII_BMCR, BMCR_RESET);
if (err < 0)
return err;
net: phy: broadcom: Fix brcm_fet_config_init() JIRA: https://issues.redhat.com/browse/RHEL-2799 commit bf8bfc4336f7a34e48b3bbd19b1542bf085bdc3d Author: Florian Fainelli <f.fainelli@gmail.com> Date: Thu Mar 24 16:24:38 2022 -0700 net: phy: broadcom: Fix brcm_fet_config_init() A Broadcom AC201 PHY (same entry as 5241) would be flagged by the Broadcom UniMAC MDIO controller as not completing the turn around properly since the PHY expects 65 MDC clock cycles to complete a write cycle, and the MDIO controller was only sending 64 MDC clock cycles as determined by looking at a scope shot. This would make the subsequent read fail with the UniMAC MDIO controller command field having MDIO_READ_FAIL set and we would abort the brcm_fet_config_init() function and thus not probe the PHY at all. After issuing a software reset, wait for at least 1ms which is well above the 1us reset delay advertised by the datasheet and issue a dummy read to let the PHY turn around the line properly. This read specifically ignores -EIO which would be returned by MDIO controllers checking for the line being turned around. If we have a genuine reaad failure, the next read of the interrupt status register would pick it up anyway. Fixes: d7a2ed9248a3 ("broadcom: Add AC131 phy support") Signed-off-by: Florian Fainelli <f.fainelli@gmail.com> Link: https://lore.kernel.org/r/20220324232438.1156812-1-f.fainelli@gmail.com Signed-off-by: Jakub Kicinski <kuba@kernel.org> Signed-off-by: Adrien Thierry <athierry@redhat.com>
2023-09-14 14:11:09 +00:00
/* The datasheet indicates the PHY needs up to 1us to complete a reset,
* build some slack here.
*/
usleep_range(1000, 2000);
/* The PHY requires 65 MDC clock cycles to complete a write operation
* and turnaround the line properly.
*
* We ignore -EIO here as the MDIO controller (e.g.: mdio-bcm-unimac)
* may flag the lack of turn-around as a read failure. This is
* particularly true with this combination since the MDIO controller
* only used 64 MDC cycles. This is not a critical failure in this
* specific case and it has no functional impact otherwise, so we let
* that one go through. If there is a genuine bus error, the next read
* of MII_BRCM_FET_INTREG will error out.
*/
err = phy_read(phydev, MII_BMCR);
if (err < 0 && err != -EIO)
return err;
reg = phy_read(phydev, MII_BRCM_FET_INTREG);
if (reg < 0)
return reg;
/* Unmask events we are interested in and mask interrupts globally. */
reg = MII_BRCM_FET_IR_DUPLEX_EN |
MII_BRCM_FET_IR_SPEED_EN |
MII_BRCM_FET_IR_LINK_EN |
MII_BRCM_FET_IR_ENABLE |
MII_BRCM_FET_IR_MASK;
err = phy_write(phydev, MII_BRCM_FET_INTREG, reg);
if (err < 0)
return err;
/* Enable shadow register access */
brcmtest = phy_read(phydev, MII_BRCM_FET_BRCMTEST);
if (brcmtest < 0)
return brcmtest;
reg = brcmtest | MII_BRCM_FET_BT_SRE;
err = phy_write(phydev, MII_BRCM_FET_BRCMTEST, reg);
if (err < 0)
return err;
/* Set the LED mode */
reg = phy_read(phydev, MII_BRCM_FET_SHDW_AUXMODE4);
if (reg < 0) {
err = reg;
goto done;
}
reg &= ~MII_BRCM_FET_SHDW_AM4_LED_MASK;
reg |= MII_BRCM_FET_SHDW_AM4_LED_MODE1;
err = phy_write(phydev, MII_BRCM_FET_SHDW_AUXMODE4, reg);
if (err < 0)
goto done;
/* Enable auto MDIX */
err = phy_set_bits(phydev, MII_BRCM_FET_SHDW_MISCCTRL,
MII_BRCM_FET_SHDW_MC_FAME);
if (err < 0)
goto done;
if (phydev->dev_flags & PHY_BRCM_AUTO_PWRDWN_ENABLE) {
/* Enable auto power down */
err = phy_set_bits(phydev, MII_BRCM_FET_SHDW_AUXSTAT2,
MII_BRCM_FET_SHDW_AS2_APDE);
}
done:
/* Disable shadow register access */
err2 = phy_write(phydev, MII_BRCM_FET_BRCMTEST, brcmtest);
if (!err)
err = err2;
return err;
}
static int brcm_fet_ack_interrupt(struct phy_device *phydev)
{
int reg;
/* Clear pending interrupts. */
reg = phy_read(phydev, MII_BRCM_FET_INTREG);
if (reg < 0)
return reg;
return 0;
}
static int brcm_fet_config_intr(struct phy_device *phydev)
{
int reg, err;
reg = phy_read(phydev, MII_BRCM_FET_INTREG);
if (reg < 0)
return reg;
if (phydev->interrupts == PHY_INTERRUPT_ENABLED) {
err = brcm_fet_ack_interrupt(phydev);
if (err)
return err;
reg &= ~MII_BRCM_FET_IR_MASK;
err = phy_write(phydev, MII_BRCM_FET_INTREG, reg);
} else {
reg |= MII_BRCM_FET_IR_MASK;
err = phy_write(phydev, MII_BRCM_FET_INTREG, reg);
if (err)
return err;
err = brcm_fet_ack_interrupt(phydev);
}
return err;
}
static irqreturn_t brcm_fet_handle_interrupt(struct phy_device *phydev)
{
int irq_status;
irq_status = phy_read(phydev, MII_BRCM_FET_INTREG);
if (irq_status < 0) {
phy_error(phydev);
return IRQ_NONE;
}
if (irq_status == 0)
return IRQ_NONE;
phy_trigger_machine(phydev);
return IRQ_HANDLED;
}
net: phy: broadcom: Add support for Wake-on-LAN JIRA: https://issues.redhat.com/browse/RHEL-2799 commit 8baddaa9d4bac939004b5058f3ade7e2bf0a6e43 Author: Florian Fainelli <f.fainelli@gmail.com> Date: Thu May 11 10:21:09 2023 -0700 net: phy: broadcom: Add support for Wake-on-LAN Add support for WAKE_UCAST, WAKE_MCAST, WAKE_BCAST, WAKE_MAGIC and WAKE_MAGICSECURE. This is only supported with the BCM54210E and compatible Ethernet PHYs. Using the in-band interrupt or an out of band GPIO interrupts are supported. Broadcom PHYs will generate a Wake-on-LAN level low interrupt on LED4 as soon as one of the supported patterns is being matched. That includes generating such an interrupt even if the PHY is operated during normal modes. If WAKE_UCAST is selected, this could lead to the LED4 interrupt firing up for every packet being received which is absolutely undesirable from a performance point of view. Because the Wake-on-LAN configuration can be set long before the system is actually put to sleep, we cannot have an interrupt service routine to clear on read the interrupt status register and ensure that new packet matches will be detected. It is desirable to enable the Wake-on-LAN interrupt as late as possible during the system suspend process such that we limit the number of interrupts to be handled by the system, but also conversely feed into the Linux's system suspend way of dealing with interrupts in and around the points of no return. Reviewed-by: Simon Horman <simon.horman@corigine.com> Signed-off-by: Florian Fainelli <f.fainelli@gmail.com> Signed-off-by: David S. Miller <davem@davemloft.net> Signed-off-by: Adrien Thierry <athierry@redhat.com>
2023-09-14 14:11:57 +00:00
static void bcm54xx_phy_get_wol(struct phy_device *phydev,
struct ethtool_wolinfo *wol)
{
/* We cannot wake-up if we do not have a dedicated PHY interrupt line
* or an out of band GPIO descriptor for wake-up. Zeroing
* wol->supported allows the caller (MAC driver) to play through and
* offer its own Wake-on-LAN scheme if available.
*/
if (!bcm54xx_phy_can_wakeup(phydev)) {
wol->supported = 0;
return;
}
bcm_phy_get_wol(phydev, wol);
}
static int bcm54xx_phy_set_wol(struct phy_device *phydev,
struct ethtool_wolinfo *wol)
{
int ret;
/* We cannot wake-up if we do not have a dedicated PHY interrupt line
* or an out of band GPIO descriptor for wake-up. Returning -EOPNOTSUPP
* allows the caller (MAC driver) to play through and offer its own
* Wake-on-LAN scheme if available.
*/
if (!bcm54xx_phy_can_wakeup(phydev))
return -EOPNOTSUPP;
ret = bcm_phy_set_wol(phydev, wol);
if (ret < 0)
return ret;
return 0;
}
static int bcm54xx_phy_probe(struct phy_device *phydev)
{
struct bcm54xx_phy_priv *priv;
net: phy: broadcom: Add support for Wake-on-LAN JIRA: https://issues.redhat.com/browse/RHEL-2799 commit 8baddaa9d4bac939004b5058f3ade7e2bf0a6e43 Author: Florian Fainelli <f.fainelli@gmail.com> Date: Thu May 11 10:21:09 2023 -0700 net: phy: broadcom: Add support for Wake-on-LAN Add support for WAKE_UCAST, WAKE_MCAST, WAKE_BCAST, WAKE_MAGIC and WAKE_MAGICSECURE. This is only supported with the BCM54210E and compatible Ethernet PHYs. Using the in-band interrupt or an out of band GPIO interrupts are supported. Broadcom PHYs will generate a Wake-on-LAN level low interrupt on LED4 as soon as one of the supported patterns is being matched. That includes generating such an interrupt even if the PHY is operated during normal modes. If WAKE_UCAST is selected, this could lead to the LED4 interrupt firing up for every packet being received which is absolutely undesirable from a performance point of view. Because the Wake-on-LAN configuration can be set long before the system is actually put to sleep, we cannot have an interrupt service routine to clear on read the interrupt status register and ensure that new packet matches will be detected. It is desirable to enable the Wake-on-LAN interrupt as late as possible during the system suspend process such that we limit the number of interrupts to be handled by the system, but also conversely feed into the Linux's system suspend way of dealing with interrupts in and around the points of no return. Reviewed-by: Simon Horman <simon.horman@corigine.com> Signed-off-by: Florian Fainelli <f.fainelli@gmail.com> Signed-off-by: David S. Miller <davem@davemloft.net> Signed-off-by: Adrien Thierry <athierry@redhat.com>
2023-09-14 14:11:57 +00:00
struct gpio_desc *wakeup_gpio;
int ret = 0;
priv = devm_kzalloc(&phydev->mdio.dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
net: phy: broadcom: Add support for Wake-on-LAN JIRA: https://issues.redhat.com/browse/RHEL-2799 commit 8baddaa9d4bac939004b5058f3ade7e2bf0a6e43 Author: Florian Fainelli <f.fainelli@gmail.com> Date: Thu May 11 10:21:09 2023 -0700 net: phy: broadcom: Add support for Wake-on-LAN Add support for WAKE_UCAST, WAKE_MCAST, WAKE_BCAST, WAKE_MAGIC and WAKE_MAGICSECURE. This is only supported with the BCM54210E and compatible Ethernet PHYs. Using the in-band interrupt or an out of band GPIO interrupts are supported. Broadcom PHYs will generate a Wake-on-LAN level low interrupt on LED4 as soon as one of the supported patterns is being matched. That includes generating such an interrupt even if the PHY is operated during normal modes. If WAKE_UCAST is selected, this could lead to the LED4 interrupt firing up for every packet being received which is absolutely undesirable from a performance point of view. Because the Wake-on-LAN configuration can be set long before the system is actually put to sleep, we cannot have an interrupt service routine to clear on read the interrupt status register and ensure that new packet matches will be detected. It is desirable to enable the Wake-on-LAN interrupt as late as possible during the system suspend process such that we limit the number of interrupts to be handled by the system, but also conversely feed into the Linux's system suspend way of dealing with interrupts in and around the points of no return. Reviewed-by: Simon Horman <simon.horman@corigine.com> Signed-off-by: Florian Fainelli <f.fainelli@gmail.com> Signed-off-by: David S. Miller <davem@davemloft.net> Signed-off-by: Adrien Thierry <athierry@redhat.com>
2023-09-14 14:11:57 +00:00
priv->wake_irq = -ENXIO;
phydev->priv = priv;
priv->stats = devm_kcalloc(&phydev->mdio.dev,
bcm_phy_get_sset_count(phydev), sizeof(u64),
GFP_KERNEL);
if (!priv->stats)
return -ENOMEM;
priv->ptp = bcm_ptp_probe(phydev);
if (IS_ERR(priv->ptp))
return PTR_ERR(priv->ptp);
net: phy: broadcom: Add support for Wake-on-LAN JIRA: https://issues.redhat.com/browse/RHEL-2799 commit 8baddaa9d4bac939004b5058f3ade7e2bf0a6e43 Author: Florian Fainelli <f.fainelli@gmail.com> Date: Thu May 11 10:21:09 2023 -0700 net: phy: broadcom: Add support for Wake-on-LAN Add support for WAKE_UCAST, WAKE_MCAST, WAKE_BCAST, WAKE_MAGIC and WAKE_MAGICSECURE. This is only supported with the BCM54210E and compatible Ethernet PHYs. Using the in-band interrupt or an out of band GPIO interrupts are supported. Broadcom PHYs will generate a Wake-on-LAN level low interrupt on LED4 as soon as one of the supported patterns is being matched. That includes generating such an interrupt even if the PHY is operated during normal modes. If WAKE_UCAST is selected, this could lead to the LED4 interrupt firing up for every packet being received which is absolutely undesirable from a performance point of view. Because the Wake-on-LAN configuration can be set long before the system is actually put to sleep, we cannot have an interrupt service routine to clear on read the interrupt status register and ensure that new packet matches will be detected. It is desirable to enable the Wake-on-LAN interrupt as late as possible during the system suspend process such that we limit the number of interrupts to be handled by the system, but also conversely feed into the Linux's system suspend way of dealing with interrupts in and around the points of no return. Reviewed-by: Simon Horman <simon.horman@corigine.com> Signed-off-by: Florian Fainelli <f.fainelli@gmail.com> Signed-off-by: David S. Miller <davem@davemloft.net> Signed-off-by: Adrien Thierry <athierry@redhat.com>
2023-09-14 14:11:57 +00:00
/* We cannot utilize the _optional variant here since we want to know
* whether the GPIO descriptor exists or not to advertise Wake-on-LAN
* support or not.
*/
wakeup_gpio = devm_gpiod_get(&phydev->mdio.dev, "wakeup", GPIOD_IN);
if (PTR_ERR(wakeup_gpio) == -EPROBE_DEFER)
return PTR_ERR(wakeup_gpio);
if (!IS_ERR(wakeup_gpio)) {
priv->wake_irq = gpiod_to_irq(wakeup_gpio);
2023-09-14 14:12:03 +00:00
/* Dummy interrupt handler which is not enabled but is provided
* in order for the interrupt descriptor to be fully set-up.
*/
ret = devm_request_irq(&phydev->mdio.dev, priv->wake_irq,
bcm_phy_wol_isr,
IRQF_TRIGGER_LOW | IRQF_NO_AUTOEN,
dev_name(&phydev->mdio.dev), phydev);
net: phy: broadcom: Add support for Wake-on-LAN JIRA: https://issues.redhat.com/browse/RHEL-2799 commit 8baddaa9d4bac939004b5058f3ade7e2bf0a6e43 Author: Florian Fainelli <f.fainelli@gmail.com> Date: Thu May 11 10:21:09 2023 -0700 net: phy: broadcom: Add support for Wake-on-LAN Add support for WAKE_UCAST, WAKE_MCAST, WAKE_BCAST, WAKE_MAGIC and WAKE_MAGICSECURE. This is only supported with the BCM54210E and compatible Ethernet PHYs. Using the in-band interrupt or an out of band GPIO interrupts are supported. Broadcom PHYs will generate a Wake-on-LAN level low interrupt on LED4 as soon as one of the supported patterns is being matched. That includes generating such an interrupt even if the PHY is operated during normal modes. If WAKE_UCAST is selected, this could lead to the LED4 interrupt firing up for every packet being received which is absolutely undesirable from a performance point of view. Because the Wake-on-LAN configuration can be set long before the system is actually put to sleep, we cannot have an interrupt service routine to clear on read the interrupt status register and ensure that new packet matches will be detected. It is desirable to enable the Wake-on-LAN interrupt as late as possible during the system suspend process such that we limit the number of interrupts to be handled by the system, but also conversely feed into the Linux's system suspend way of dealing with interrupts in and around the points of no return. Reviewed-by: Simon Horman <simon.horman@corigine.com> Signed-off-by: Florian Fainelli <f.fainelli@gmail.com> Signed-off-by: David S. Miller <davem@davemloft.net> Signed-off-by: Adrien Thierry <athierry@redhat.com>
2023-09-14 14:11:57 +00:00
if (ret)
return ret;
}
/* If we do not have a main interrupt or a side-band wake-up interrupt,
* then the device cannot be marked as wake-up capable.
*/
if (!bcm54xx_phy_can_wakeup(phydev))
return 0;
return device_init_wakeup(&phydev->mdio.dev, true);
}
static void bcm54xx_get_stats(struct phy_device *phydev,
struct ethtool_stats *stats, u64 *data)
{
struct bcm54xx_phy_priv *priv = phydev->priv;
bcm_phy_get_stats(phydev, priv->stats, stats, data);
}
static void bcm54xx_link_change_notify(struct phy_device *phydev)
{
u16 mask = MII_BCM54XX_EXP_EXP08_EARLY_DAC_WAKE |
MII_BCM54XX_EXP_EXP08_FORCE_DAC_WAKE;
int ret;
if (phydev->state != PHY_RUNNING)
return;
/* Don't change the DAC wake settings if auto power down
* is not requested.
*/
if (!(phydev->dev_flags & PHY_BRCM_AUTO_PWRDWN_ENABLE))
return;
ret = bcm_phy_read_exp(phydev, MII_BCM54XX_EXP_EXP08);
if (ret < 0)
return;
/* Enable/disable 10BaseT auto and forced early DAC wake depending
* on the negotiated speed, those settings should only be done
* for 10Mbits/sec.
*/
if (phydev->speed == SPEED_10)
ret |= mask;
else
ret &= ~mask;
bcm_phy_write_exp(phydev, MII_BCM54XX_EXP_EXP08, ret);
}
static int lre_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 lre_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 */
for (i = 0; i < ARRAY_SIZE(lds_br_bits); i++)
linkmode_clear_bit(lds_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 lre_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
* Return: 0 on success, < 0 on error
*
* 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;
}
/* Get the status in BroadRReach mode just like genphy_read_status does
* in normal mode
*/
static int bcm54811_lre_read_status(struct phy_device *phydev)
{
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 = lre_read_master_slave(phydev);
if (err < 0)
return err;
/* Read LDS Link Partner Ability */
err = lre_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 = lre_read_status_fixed(phydev);
return err;
}
static int bcm54811_read_status(struct phy_device *phydev)
{
struct bcm54xx_phy_priv *priv = phydev->priv;
if (priv->brr_mode)
return bcm54811_lre_read_status(phydev);
return genphy_read_status(phydev);
}
static struct phy_driver broadcom_drivers[] = {
{
.phy_id = PHY_ID_BCM5411,
.phy_id_mask = 0xfffffff0,
.name = "Broadcom BCM5411",
/* PHY_GBIT_FEATURES */
.get_sset_count = bcm_phy_get_sset_count,
.get_strings = bcm_phy_get_strings,
.get_stats = bcm54xx_get_stats,
.probe = bcm54xx_phy_probe,
.config_init = bcm54xx_config_init,
.config_intr = bcm_phy_config_intr,
.handle_interrupt = bcm_phy_handle_interrupt,
.link_change_notify = bcm54xx_link_change_notify,
}, {
.phy_id = PHY_ID_BCM5421,
.phy_id_mask = 0xfffffff0,
.name = "Broadcom BCM5421",
/* PHY_GBIT_FEATURES */
.get_sset_count = bcm_phy_get_sset_count,
.get_strings = bcm_phy_get_strings,
.get_stats = bcm54xx_get_stats,
.probe = bcm54xx_phy_probe,
.config_init = bcm54xx_config_init,
.config_intr = bcm_phy_config_intr,
.handle_interrupt = bcm_phy_handle_interrupt,
.link_change_notify = bcm54xx_link_change_notify,
}, {
.phy_id = PHY_ID_BCM54210E,
.phy_id_mask = 0xfffffff0,
.name = "Broadcom BCM54210E",
/* PHY_GBIT_FEATURES */
net: phy: broadcom: Add support for Wake-on-LAN JIRA: https://issues.redhat.com/browse/RHEL-2799 commit 8baddaa9d4bac939004b5058f3ade7e2bf0a6e43 Author: Florian Fainelli <f.fainelli@gmail.com> Date: Thu May 11 10:21:09 2023 -0700 net: phy: broadcom: Add support for Wake-on-LAN Add support for WAKE_UCAST, WAKE_MCAST, WAKE_BCAST, WAKE_MAGIC and WAKE_MAGICSECURE. This is only supported with the BCM54210E and compatible Ethernet PHYs. Using the in-band interrupt or an out of band GPIO interrupts are supported. Broadcom PHYs will generate a Wake-on-LAN level low interrupt on LED4 as soon as one of the supported patterns is being matched. That includes generating such an interrupt even if the PHY is operated during normal modes. If WAKE_UCAST is selected, this could lead to the LED4 interrupt firing up for every packet being received which is absolutely undesirable from a performance point of view. Because the Wake-on-LAN configuration can be set long before the system is actually put to sleep, we cannot have an interrupt service routine to clear on read the interrupt status register and ensure that new packet matches will be detected. It is desirable to enable the Wake-on-LAN interrupt as late as possible during the system suspend process such that we limit the number of interrupts to be handled by the system, but also conversely feed into the Linux's system suspend way of dealing with interrupts in and around the points of no return. Reviewed-by: Simon Horman <simon.horman@corigine.com> Signed-off-by: Florian Fainelli <f.fainelli@gmail.com> Signed-off-by: David S. Miller <davem@davemloft.net> Signed-off-by: Adrien Thierry <athierry@redhat.com>
2023-09-14 14:11:57 +00:00
.flags = PHY_ALWAYS_CALL_SUSPEND,
.get_sset_count = bcm_phy_get_sset_count,
.get_strings = bcm_phy_get_strings,
.get_stats = bcm54xx_get_stats,
.probe = bcm54xx_phy_probe,
.config_init = bcm54xx_config_init,
.config_intr = bcm_phy_config_intr,
.handle_interrupt = bcm_phy_handle_interrupt,
.link_change_notify = bcm54xx_link_change_notify,
.suspend = bcm54xx_suspend,
.resume = bcm54xx_resume,
net: phy: broadcom: Add support for Wake-on-LAN JIRA: https://issues.redhat.com/browse/RHEL-2799 commit 8baddaa9d4bac939004b5058f3ade7e2bf0a6e43 Author: Florian Fainelli <f.fainelli@gmail.com> Date: Thu May 11 10:21:09 2023 -0700 net: phy: broadcom: Add support for Wake-on-LAN Add support for WAKE_UCAST, WAKE_MCAST, WAKE_BCAST, WAKE_MAGIC and WAKE_MAGICSECURE. This is only supported with the BCM54210E and compatible Ethernet PHYs. Using the in-band interrupt or an out of band GPIO interrupts are supported. Broadcom PHYs will generate a Wake-on-LAN level low interrupt on LED4 as soon as one of the supported patterns is being matched. That includes generating such an interrupt even if the PHY is operated during normal modes. If WAKE_UCAST is selected, this could lead to the LED4 interrupt firing up for every packet being received which is absolutely undesirable from a performance point of view. Because the Wake-on-LAN configuration can be set long before the system is actually put to sleep, we cannot have an interrupt service routine to clear on read the interrupt status register and ensure that new packet matches will be detected. It is desirable to enable the Wake-on-LAN interrupt as late as possible during the system suspend process such that we limit the number of interrupts to be handled by the system, but also conversely feed into the Linux's system suspend way of dealing with interrupts in and around the points of no return. Reviewed-by: Simon Horman <simon.horman@corigine.com> Signed-off-by: Florian Fainelli <f.fainelli@gmail.com> Signed-off-by: David S. Miller <davem@davemloft.net> Signed-off-by: Adrien Thierry <athierry@redhat.com>
2023-09-14 14:11:57 +00:00
.get_wol = bcm54xx_phy_get_wol,
.set_wol = bcm54xx_phy_set_wol,
.led_brightness_set = bcm_phy_led_brightness_set,
}, {
.phy_id = PHY_ID_BCM5461,
.phy_id_mask = 0xfffffff0,
.name = "Broadcom BCM5461",
/* PHY_GBIT_FEATURES */
.get_sset_count = bcm_phy_get_sset_count,
.get_strings = bcm_phy_get_strings,
.get_stats = bcm54xx_get_stats,
.probe = bcm54xx_phy_probe,
.config_init = bcm54xx_config_init,
.config_intr = bcm_phy_config_intr,
.handle_interrupt = bcm_phy_handle_interrupt,
.link_change_notify = bcm54xx_link_change_notify,
.led_brightness_set = bcm_phy_led_brightness_set,
}, {
.phy_id = PHY_ID_BCM54612E,
.phy_id_mask = 0xfffffff0,
.name = "Broadcom BCM54612E",
/* PHY_GBIT_FEATURES */
.get_sset_count = bcm_phy_get_sset_count,
.get_strings = bcm_phy_get_strings,
.get_stats = bcm54xx_get_stats,
.probe = bcm54xx_phy_probe,
.config_init = bcm54xx_config_init,
.config_intr = bcm_phy_config_intr,
.handle_interrupt = bcm_phy_handle_interrupt,
.link_change_notify = bcm54xx_link_change_notify,
.led_brightness_set = bcm_phy_led_brightness_set,
.suspend = bcm54xx_suspend,
.resume = bcm54xx_resume,
}, {
.phy_id = PHY_ID_BCM54616S,
.phy_id_mask = 0xfffffff0,
.name = "Broadcom BCM54616S",
/* PHY_GBIT_FEATURES */
net: phy: broadcom: hook up soft_reset for BCM54616S JIRA: https://issues.redhat.com/browse/RHEL-2799 commit d15c7e875d44367005370e6a82e8f3a382a04f9b Author: Robert Hancock <robert.hancock@calian.com> Date: Tue Jan 18 15:52:43 2022 -0600 net: phy: broadcom: hook up soft_reset for BCM54616S A problem was encountered with the Bel-Fuse 1GBT-SFP05 SFP module (which is a 1 Gbps copper module operating in SGMII mode with an internal BCM54616S PHY device) using the Xilinx AXI Ethernet MAC core, where the module would work properly on the initial insertion or boot of the device, but after the device was rebooted, the link would either only come up at 100 Mbps speeds or go up and down erratically. I found no meaningful changes in the PHY configuration registers between the working and non-working boots, but the status registers seemed to have a lot of error indications set on the SERDES side of the device on the non-working boot. I suspect the problem is that whatever happens on the SGMII link when the device is rebooted and the FPGA logic gets reloaded ends up putting the module's onboard PHY into a bad state. Since commit 6e2d85ec0559 ("net: phy: Stop with excessive soft reset") the genphy_soft_reset call is not made automatically by the PHY core unless the callback is explicitly specified in the driver structure. For most of these Broadcom devices, there is probably a hardware reset that gets asserted to reset the PHY during boot, however for SFP modules (where the BCM54616S is commonly found) no such reset line exists, so if the board keeps the SFP cage powered up across a reboot, it will end up with no reset occurring during reboots. Hook up the genphy_soft_reset callback for BCM54616S to ensure that a PHY reset is performed before the device is initialized. This appears to fix the issue with erratic operation after a reboot with this SFP module. Fixes: 6e2d85ec0559 ("net: phy: Stop with excessive soft reset") Signed-off-by: Robert Hancock <robert.hancock@calian.com> Reviewed-by: Florian Fainelli <f.fainelli@gmail.com> Signed-off-by: David S. Miller <davem@davemloft.net> Signed-off-by: Adrien Thierry <athierry@redhat.com>
2023-09-14 14:11:05 +00:00
.soft_reset = genphy_soft_reset,
.config_init = bcm54xx_config_init,
.config_aneg = bcm54616s_config_aneg,
.config_intr = bcm_phy_config_intr,
.handle_interrupt = bcm_phy_handle_interrupt,
.read_status = bcm54616s_read_status,
.probe = bcm54616s_probe,
.link_change_notify = bcm54xx_link_change_notify,
.led_brightness_set = bcm_phy_led_brightness_set,
}, {
.phy_id = PHY_ID_BCM5464,
.phy_id_mask = 0xfffffff0,
.name = "Broadcom BCM5464",
/* PHY_GBIT_FEATURES */
.get_sset_count = bcm_phy_get_sset_count,
.get_strings = bcm_phy_get_strings,
.get_stats = bcm54xx_get_stats,
.probe = bcm54xx_phy_probe,
.config_init = bcm54xx_config_init,
.config_intr = bcm_phy_config_intr,
.handle_interrupt = bcm_phy_handle_interrupt,
.suspend = genphy_suspend,
.resume = genphy_resume,
.link_change_notify = bcm54xx_link_change_notify,
.led_brightness_set = bcm_phy_led_brightness_set,
}, {
.phy_id = PHY_ID_BCM5481,
.phy_id_mask = 0xfffffff0,
.name = "Broadcom BCM5481",
/* PHY_GBIT_FEATURES */
.get_sset_count = bcm_phy_get_sset_count,
.get_strings = bcm_phy_get_strings,
.get_stats = bcm54xx_get_stats,
.probe = bcm54xx_phy_probe,
.config_init = bcm54xx_config_init,
.config_aneg = bcm5481_config_aneg,
.config_intr = bcm_phy_config_intr,
.handle_interrupt = bcm_phy_handle_interrupt,
.link_change_notify = bcm54xx_link_change_notify,
.led_brightness_set = bcm_phy_led_brightness_set,
}, {
.phy_id = PHY_ID_BCM54810,
.phy_id_mask = 0xfffffff0,
.name = "Broadcom BCM54810",
/* PHY_GBIT_FEATURES */
.get_sset_count = bcm_phy_get_sset_count,
.get_strings = bcm_phy_get_strings,
.get_stats = bcm54xx_get_stats,
.probe = bcm54xx_phy_probe,
.read_mmd = bcm54810_read_mmd,
.write_mmd = bcm54810_write_mmd,
.config_init = bcm54xx_config_init,
.config_aneg = bcm5481_config_aneg,
.config_intr = bcm_phy_config_intr,
.handle_interrupt = bcm_phy_handle_interrupt,
.suspend = bcm54xx_suspend,
.resume = bcm54xx_resume,
.link_change_notify = bcm54xx_link_change_notify,
.led_brightness_set = bcm_phy_led_brightness_set,
}, {
.phy_id = PHY_ID_BCM54811,
.phy_id_mask = 0xfffffff0,
.name = "Broadcom BCM54811",
/* PHY_GBIT_FEATURES */
.get_sset_count = bcm_phy_get_sset_count,
.get_strings = bcm_phy_get_strings,
.get_stats = bcm54xx_get_stats,
.probe = bcm54xx_phy_probe,
.config_init = bcm54xx_config_init,
.config_aneg = bcm54811_config_aneg,
.config_intr = bcm_phy_config_intr,
.handle_interrupt = bcm_phy_handle_interrupt,
.read_status = bcm54811_read_status,
.get_features = bcm5481x_read_abilities,
.suspend = bcm54xx_suspend,
.resume = bcm54xx_resume,
.link_change_notify = bcm54xx_link_change_notify,
.led_brightness_set = bcm_phy_led_brightness_set,
}, {
.phy_id = PHY_ID_BCM5482,
.phy_id_mask = 0xfffffff0,
.name = "Broadcom BCM5482",
/* PHY_GBIT_FEATURES */
.get_sset_count = bcm_phy_get_sset_count,
.get_strings = bcm_phy_get_strings,
.get_stats = bcm54xx_get_stats,
.probe = bcm54xx_phy_probe,
.config_init = bcm54xx_config_init,
.config_intr = bcm_phy_config_intr,
.handle_interrupt = bcm_phy_handle_interrupt,
.link_change_notify = bcm54xx_link_change_notify,
.led_brightness_set = bcm_phy_led_brightness_set,
}, {
.phy_id = PHY_ID_BCM50610,
.phy_id_mask = 0xfffffff0,
.name = "Broadcom BCM50610",
/* PHY_GBIT_FEATURES */
.get_sset_count = bcm_phy_get_sset_count,
.get_strings = bcm_phy_get_strings,
.get_stats = bcm54xx_get_stats,
.probe = bcm54xx_phy_probe,
.config_init = bcm54xx_config_init,
.config_intr = bcm_phy_config_intr,
.handle_interrupt = bcm_phy_handle_interrupt,
.link_change_notify = bcm54xx_link_change_notify,
.suspend = bcm54xx_suspend,
.resume = bcm54xx_resume,
.led_brightness_set = bcm_phy_led_brightness_set,
}, {
.phy_id = PHY_ID_BCM50610M,
.phy_id_mask = 0xfffffff0,
.name = "Broadcom BCM50610M",
/* PHY_GBIT_FEATURES */
.get_sset_count = bcm_phy_get_sset_count,
.get_strings = bcm_phy_get_strings,
.get_stats = bcm54xx_get_stats,
.probe = bcm54xx_phy_probe,
.config_init = bcm54xx_config_init,
.config_intr = bcm_phy_config_intr,
.handle_interrupt = bcm_phy_handle_interrupt,
.link_change_notify = bcm54xx_link_change_notify,
.suspend = bcm54xx_suspend,
.resume = bcm54xx_resume,
.led_brightness_set = bcm_phy_led_brightness_set,
}, {
.phy_id = PHY_ID_BCM57780,
.phy_id_mask = 0xfffffff0,
.name = "Broadcom BCM57780",
/* PHY_GBIT_FEATURES */
.get_sset_count = bcm_phy_get_sset_count,
.get_strings = bcm_phy_get_strings,
.get_stats = bcm54xx_get_stats,
.probe = bcm54xx_phy_probe,
.config_init = bcm54xx_config_init,
.config_intr = bcm_phy_config_intr,
.handle_interrupt = bcm_phy_handle_interrupt,
.link_change_notify = bcm54xx_link_change_notify,
.led_brightness_set = bcm_phy_led_brightness_set,
}, {
.phy_id = PHY_ID_BCMAC131,
.phy_id_mask = 0xfffffff0,
.name = "Broadcom BCMAC131",
/* PHY_BASIC_FEATURES */
.config_init = brcm_fet_config_init,
.config_intr = brcm_fet_config_intr,
.handle_interrupt = brcm_fet_handle_interrupt,
}, {
.phy_id = PHY_ID_BCM5241,
.phy_id_mask = 0xfffffff0,
.name = "Broadcom BCM5241",
/* PHY_BASIC_FEATURES */
.config_init = brcm_fet_config_init,
.config_intr = brcm_fet_config_intr,
.handle_interrupt = brcm_fet_handle_interrupt,
}, {
.phy_id = PHY_ID_BCM5395,
.phy_id_mask = 0xfffffff0,
.name = "Broadcom BCM5395",
.flags = PHY_IS_INTERNAL,
/* PHY_GBIT_FEATURES */
.get_sset_count = bcm_phy_get_sset_count,
.get_strings = bcm_phy_get_strings,
.get_stats = bcm54xx_get_stats,
.probe = bcm54xx_phy_probe,
.link_change_notify = bcm54xx_link_change_notify,
.led_brightness_set = bcm_phy_led_brightness_set,
}, {
.phy_id = PHY_ID_BCM53125,
.phy_id_mask = 0xfffffff0,
.name = "Broadcom BCM53125",
.flags = PHY_IS_INTERNAL,
/* PHY_GBIT_FEATURES */
.get_sset_count = bcm_phy_get_sset_count,
.get_strings = bcm_phy_get_strings,
.get_stats = bcm54xx_get_stats,
.probe = bcm54xx_phy_probe,
.config_init = bcm54xx_config_init,
.config_intr = bcm_phy_config_intr,
.handle_interrupt = bcm_phy_handle_interrupt,
.link_change_notify = bcm54xx_link_change_notify,
.led_brightness_set = bcm_phy_led_brightness_set,
}, {
.phy_id = PHY_ID_BCM53128,
.phy_id_mask = 0xfffffff0,
.name = "Broadcom BCM53128",
.flags = PHY_IS_INTERNAL,
/* PHY_GBIT_FEATURES */
.get_sset_count = bcm_phy_get_sset_count,
.get_strings = bcm_phy_get_strings,
.get_stats = bcm54xx_get_stats,
.probe = bcm54xx_phy_probe,
.config_init = bcm54xx_config_init,
.config_intr = bcm_phy_config_intr,
.handle_interrupt = bcm_phy_handle_interrupt,
.link_change_notify = bcm54xx_link_change_notify,
.led_brightness_set = bcm_phy_led_brightness_set,
}, {
.phy_id = PHY_ID_BCM89610,
.phy_id_mask = 0xfffffff0,
.name = "Broadcom BCM89610",
/* PHY_GBIT_FEATURES */
.get_sset_count = bcm_phy_get_sset_count,
.get_strings = bcm_phy_get_strings,
.get_stats = bcm54xx_get_stats,
.probe = bcm54xx_phy_probe,
.config_init = bcm54xx_config_init,
.config_intr = bcm_phy_config_intr,
.handle_interrupt = bcm_phy_handle_interrupt,
.link_change_notify = bcm54xx_link_change_notify,
} };
module_phy_driver(broadcom_drivers);
2010-10-03 23:43:32 +00:00
static struct mdio_device_id __maybe_unused broadcom_tbl[] = {
{ PHY_ID_BCM5411, 0xfffffff0 },
{ PHY_ID_BCM5421, 0xfffffff0 },
{ PHY_ID_BCM54210E, 0xfffffff0 },
{ PHY_ID_BCM5461, 0xfffffff0 },
{ PHY_ID_BCM54612E, 0xfffffff0 },
{ PHY_ID_BCM54616S, 0xfffffff0 },
{ PHY_ID_BCM5464, 0xfffffff0 },
{ PHY_ID_BCM5481, 0xfffffff0 },
{ PHY_ID_BCM54810, 0xfffffff0 },
{ PHY_ID_BCM54811, 0xfffffff0 },
{ PHY_ID_BCM5482, 0xfffffff0 },
{ PHY_ID_BCM50610, 0xfffffff0 },
{ PHY_ID_BCM50610M, 0xfffffff0 },
{ PHY_ID_BCM57780, 0xfffffff0 },
{ PHY_ID_BCMAC131, 0xfffffff0 },
{ PHY_ID_BCM5241, 0xfffffff0 },
{ PHY_ID_BCM5395, 0xfffffff0 },
{ PHY_ID_BCM53125, 0xfffffff0 },
{ PHY_ID_BCM53128, 0xfffffff0 },
{ PHY_ID_BCM89610, 0xfffffff0 },
{ }
};
MODULE_DEVICE_TABLE(mdio, broadcom_tbl);