aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/phy
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net/phy')
-rw-r--r--drivers/net/phy/Kconfig3
-rw-r--r--drivers/net/phy/Makefile2
-rw-r--r--drivers/net/phy/aquantia_main.c1
-rw-r--r--drivers/net/phy/ax88796b.c (renamed from drivers/net/phy/asix.c)0
-rw-r--r--drivers/net/phy/dp83867.c41
-rw-r--r--drivers/net/phy/marvell10g.c13
-rw-r--r--drivers/net/phy/phy-c45.c1
-rw-r--r--drivers/net/phy/phylink.c60
-rw-r--r--drivers/net/phy/sfp-bus.c1
-rw-r--r--drivers/net/phy/sfp.c24
10 files changed, 107 insertions, 39 deletions
diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig
index d6299710d634..1d406c6df790 100644
--- a/drivers/net/phy/Kconfig
+++ b/drivers/net/phy/Kconfig
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0-only
#
# PHY Layer Configuration
#
@@ -253,7 +254,7 @@ config AQUANTIA_PHY
---help---
Currently supports the Aquantia AQ1202, AQ2104, AQR105, AQR405
-config ASIX_PHY
+config AX88796B_PHY
tristate "Asix PHYs"
help
Currently supports the Asix Electronics PHY found in the X-Surf 100
diff --git a/drivers/net/phy/Makefile b/drivers/net/phy/Makefile
index 27d7f9f3b0de..5b5c8669499e 100644
--- a/drivers/net/phy/Makefile
+++ b/drivers/net/phy/Makefile
@@ -52,7 +52,7 @@ ifdef CONFIG_HWMON
aquantia-objs += aquantia_hwmon.o
endif
obj-$(CONFIG_AQUANTIA_PHY) += aquantia.o
-obj-$(CONFIG_ASIX_PHY) += asix.o
+obj-$(CONFIG_AX88796B_PHY) += ax88796b.o
obj-$(CONFIG_AT803X_PHY) += at803x.o
obj-$(CONFIG_BCM63XX_PHY) += bcm63xx.o
obj-$(CONFIG_BCM7XXX_PHY) += bcm7xxx.o
diff --git a/drivers/net/phy/aquantia_main.c b/drivers/net/phy/aquantia_main.c
index eed4fe3d871f..0fedd28fdb6e 100644
--- a/drivers/net/phy/aquantia_main.c
+++ b/drivers/net/phy/aquantia_main.c
@@ -487,6 +487,7 @@ static int aqr107_config_init(struct phy_device *phydev)
/* Check that the PHY interface type is compatible */
if (phydev->interface != PHY_INTERFACE_MODE_SGMII &&
phydev->interface != PHY_INTERFACE_MODE_2500BASEX &&
+ phydev->interface != PHY_INTERFACE_MODE_XGMII &&
phydev->interface != PHY_INTERFACE_MODE_10GKR)
return -ENODEV;
diff --git a/drivers/net/phy/asix.c b/drivers/net/phy/ax88796b.c
index 79bf7ef1fcfd..79bf7ef1fcfd 100644
--- a/drivers/net/phy/asix.c
+++ b/drivers/net/phy/ax88796b.c
diff --git a/drivers/net/phy/dp83867.c b/drivers/net/phy/dp83867.c
index fd35131a0c39..c71c7d0f53f0 100644
--- a/drivers/net/phy/dp83867.c
+++ b/drivers/net/phy/dp83867.c
@@ -26,10 +26,18 @@
/* Extended Registers */
#define DP83867_CFG4 0x0031
+#define DP83867_CFG4_SGMII_ANEG_MASK (BIT(5) | BIT(6))
+#define DP83867_CFG4_SGMII_ANEG_TIMER_11MS (3 << 5)
+#define DP83867_CFG4_SGMII_ANEG_TIMER_800US (2 << 5)
+#define DP83867_CFG4_SGMII_ANEG_TIMER_2US (1 << 5)
+#define DP83867_CFG4_SGMII_ANEG_TIMER_16MS (0 << 5)
+
#define DP83867_RGMIICTL 0x0032
#define DP83867_STRAP_STS1 0x006E
#define DP83867_RGMIIDCTL 0x0086
#define DP83867_IO_MUX_CFG 0x0170
+#define DP83867_10M_SGMII_CFG 0x016F
+#define DP83867_10M_SGMII_RATE_ADAPT_MASK BIT(7)
#define DP83867_SW_RESET BIT(15)
#define DP83867_SW_RESTART BIT(14)
@@ -247,10 +255,8 @@ static int dp83867_config_init(struct phy_device *phydev)
ret = phy_write(phydev, MII_DP83867_PHYCTRL, val);
if (ret)
return ret;
- }
- if ((phydev->interface >= PHY_INTERFACE_MODE_RGMII_ID) &&
- (phydev->interface <= PHY_INTERFACE_MODE_RGMII_RXID)) {
+ /* Set up RGMII delays */
val = phy_read_mmd(phydev, DP83867_DEVADDR, DP83867_RGMIICTL);
if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID)
@@ -277,6 +283,33 @@ static int dp83867_config_init(struct phy_device *phydev)
DP83867_IO_MUX_CFG_IO_IMPEDANCE_CTRL);
}
+ if (phydev->interface == PHY_INTERFACE_MODE_SGMII) {
+ /* For support SPEED_10 in SGMII mode
+ * DP83867_10M_SGMII_RATE_ADAPT bit
+ * has to be cleared by software. That
+ * does not affect SPEED_100 and
+ * SPEED_1000.
+ */
+ ret = phy_modify_mmd(phydev, DP83867_DEVADDR,
+ DP83867_10M_SGMII_CFG,
+ DP83867_10M_SGMII_RATE_ADAPT_MASK,
+ 0);
+ if (ret)
+ return ret;
+
+ /* After reset SGMII Autoneg timer is set to 2us (bits 6 and 5
+ * are 01). That is not enough to finalize autoneg on some
+ * devices. Increase this timer duration to maximum 16ms.
+ */
+ ret = phy_modify_mmd(phydev, DP83867_DEVADDR,
+ DP83867_CFG4,
+ DP83867_CFG4_SGMII_ANEG_MASK,
+ DP83867_CFG4_SGMII_ANEG_TIMER_16MS);
+
+ if (ret)
+ return ret;
+ }
+
/* Enable Interrupt output INT_OE in CFG3 register */
if (phy_interrupt_is_valid(phydev)) {
val = phy_read(phydev, DP83867_CFG3);
@@ -307,7 +340,7 @@ static int dp83867_phy_reset(struct phy_device *phydev)
usleep_range(10, 20);
- return dp83867_config_init(phydev);
+ return 0;
}
static struct phy_driver dp83867_driver[] = {
diff --git a/drivers/net/phy/marvell10g.c b/drivers/net/phy/marvell10g.c
index 238a20e13d6a..3b99882692e3 100644
--- a/drivers/net/phy/marvell10g.c
+++ b/drivers/net/phy/marvell10g.c
@@ -31,6 +31,9 @@
#define MV_PHY_ALASKA_NBT_QUIRK_REV (MARVELL_PHY_ID_88X3310 | 0xa)
enum {
+ MV_PMA_BOOT = 0xc050,
+ MV_PMA_BOOT_FATAL = BIT(0),
+
MV_PCS_BASE_T = 0x0000,
MV_PCS_BASE_R = 0x1000,
MV_PCS_1000BASEX = 0x2000,
@@ -213,6 +216,16 @@ static int mv3310_probe(struct phy_device *phydev)
(phydev->c45_ids.devices_in_package & mmd_mask) != mmd_mask)
return -ENODEV;
+ ret = phy_read_mmd(phydev, MDIO_MMD_PMAPMD, MV_PMA_BOOT);
+ if (ret < 0)
+ return ret;
+
+ if (ret & MV_PMA_BOOT_FATAL) {
+ dev_warn(&phydev->mdio.dev,
+ "PHY failed to boot firmware, status=%04x\n", ret);
+ return -ENODEV;
+ }
+
priv = devm_kzalloc(&phydev->mdio.dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
diff --git a/drivers/net/phy/phy-c45.c b/drivers/net/phy/phy-c45.c
index abe13dfe50ad..b9d4145781ca 100644
--- a/drivers/net/phy/phy-c45.c
+++ b/drivers/net/phy/phy-c45.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Clause 45 PHY support
*/
diff --git a/drivers/net/phy/phylink.c b/drivers/net/phy/phylink.c
index 89750c7dfd6f..4c0616ba314d 100644
--- a/drivers/net/phy/phylink.c
+++ b/drivers/net/phy/phylink.c
@@ -51,6 +51,10 @@ struct phylink {
/* The link configuration settings */
struct phylink_link_state link_config;
+
+ /* The current settings */
+ phy_interface_t cur_interface;
+
struct gpio_desc *link_gpio;
struct timer_list link_poll;
void (*get_fixed_state)(struct net_device *dev,
@@ -422,28 +426,21 @@ static void phylink_resolve(struct work_struct *w)
case MLO_AN_INBAND:
phylink_get_mac_state(pl, &link_state);
- if (pl->phydev) {
- bool changed = false;
-
- link_state.link = link_state.link &&
- pl->phy_state.link;
-
- if (pl->phy_state.interface !=
- link_state.interface) {
- link_state.interface = pl->phy_state.interface;
- changed = true;
- }
-
- /* Propagate the flow control from the PHY
- * to the MAC. Also propagate the interface
- * if changed.
- */
- if (pl->phy_state.link || changed) {
- link_state.pause |= pl->phy_state.pause;
- phylink_resolve_flow(pl, &link_state);
-
- phylink_mac_config(pl, &link_state);
- }
+
+ /* If we have a phy, the "up" state is the union of
+ * both the PHY and the MAC */
+ if (pl->phydev)
+ link_state.link &= pl->phy_state.link;
+
+ /* Only update if the PHY link is up */
+ if (pl->phydev && pl->phy_state.link) {
+ link_state.interface = pl->phy_state.interface;
+
+ /* If we have a PHY, we need to update with
+ * the pause mode bits. */
+ link_state.pause |= pl->phy_state.pause;
+ phylink_resolve_flow(pl, &link_state);
+ phylink_mac_config(pl, &link_state);
}
break;
}
@@ -453,12 +450,12 @@ static void phylink_resolve(struct work_struct *w)
if (!link_state.link) {
netif_carrier_off(ndev);
pl->ops->mac_link_down(ndev, pl->link_an_mode,
- pl->phy_state.interface);
+ pl->cur_interface);
netdev_info(ndev, "Link is Down\n");
} else {
+ pl->cur_interface = link_state.interface;
pl->ops->mac_link_up(ndev, pl->link_an_mode,
- pl->phy_state.interface,
- pl->phydev);
+ pl->cur_interface, pl->phydev);
netif_carrier_on(ndev);
@@ -1076,6 +1073,7 @@ EXPORT_SYMBOL_GPL(phylink_ethtool_ksettings_get);
int phylink_ethtool_ksettings_set(struct phylink *pl,
const struct ethtool_link_ksettings *kset)
{
+ __ETHTOOL_DECLARE_LINK_MODE_MASK(support);
struct ethtool_link_ksettings our_kset;
struct phylink_link_state config;
int ret;
@@ -1086,11 +1084,12 @@ int phylink_ethtool_ksettings_set(struct phylink *pl,
kset->base.autoneg != AUTONEG_ENABLE)
return -EINVAL;
+ linkmode_copy(support, pl->supported);
config = pl->link_config;
/* Mask out unsupported advertisements */
linkmode_and(config.advertising, kset->link_modes.advertising,
- pl->supported);
+ support);
/* FIXME: should we reject autoneg if phy/mac does not support it? */
if (kset->base.autoneg == AUTONEG_DISABLE) {
@@ -1100,7 +1099,7 @@ int phylink_ethtool_ksettings_set(struct phylink *pl,
* duplex.
*/
s = phy_lookup_setting(kset->base.speed, kset->base.duplex,
- pl->supported, false);
+ support, false);
if (!s)
return -EINVAL;
@@ -1129,7 +1128,7 @@ int phylink_ethtool_ksettings_set(struct phylink *pl,
__set_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, config.advertising);
}
- if (phylink_validate(pl, pl->supported, &config))
+ if (phylink_validate(pl, support, &config))
return -EINVAL;
/* If autonegotiation is enabled, we must have an advertisement */
@@ -1579,6 +1578,7 @@ static int phylink_sfp_module_insert(void *upstream,
{
struct phylink *pl = upstream;
__ETHTOOL_DECLARE_LINK_MODE_MASK(support) = { 0, };
+ __ETHTOOL_DECLARE_LINK_MODE_MASK(support1);
struct phylink_link_state config;
phy_interface_t iface;
int ret = 0;
@@ -1606,6 +1606,8 @@ static int phylink_sfp_module_insert(void *upstream,
return ret;
}
+ linkmode_copy(support1, support);
+
iface = sfp_select_interface(pl->sfp_bus, id, config.advertising);
if (iface == PHY_INTERFACE_MODE_NA) {
netdev_err(pl->netdev,
@@ -1615,7 +1617,7 @@ static int phylink_sfp_module_insert(void *upstream,
}
config.interface = iface;
- ret = phylink_validate(pl, support, &config);
+ ret = phylink_validate(pl, support1, &config);
if (ret) {
netdev_err(pl->netdev, "validation of %s/%s with support %*pb failed: %d\n",
phylink_an_mode_str(MLO_AN_INBAND),
diff --git a/drivers/net/phy/sfp-bus.c b/drivers/net/phy/sfp-bus.c
index fef701bfad62..e9c187946cca 100644
--- a/drivers/net/phy/sfp-bus.c
+++ b/drivers/net/phy/sfp-bus.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
#include <linux/export.h>
#include <linux/kref.h>
#include <linux/list.h>
diff --git a/drivers/net/phy/sfp.c b/drivers/net/phy/sfp.c
index d4635c2178d1..71812be0ac64 100644
--- a/drivers/net/phy/sfp.c
+++ b/drivers/net/phy/sfp.c
@@ -281,6 +281,7 @@ static int sfp_i2c_read(struct sfp *sfp, bool a2, u8 dev_addr, void *buf,
{
struct i2c_msg msgs[2];
u8 bus_addr = a2 ? 0x51 : 0x50;
+ size_t this_len;
int ret;
msgs[0].addr = bus_addr;
@@ -292,11 +293,26 @@ static int sfp_i2c_read(struct sfp *sfp, bool a2, u8 dev_addr, void *buf,
msgs[1].len = len;
msgs[1].buf = buf;
- ret = i2c_transfer(sfp->i2c, msgs, ARRAY_SIZE(msgs));
- if (ret < 0)
- return ret;
+ while (len) {
+ this_len = len;
+ if (this_len > 16)
+ this_len = 16;
- return ret == ARRAY_SIZE(msgs) ? len : 0;
+ msgs[1].len = this_len;
+
+ ret = i2c_transfer(sfp->i2c, msgs, ARRAY_SIZE(msgs));
+ if (ret < 0)
+ return ret;
+
+ if (ret != ARRAY_SIZE(msgs))
+ break;
+
+ msgs[1].buf += this_len;
+ dev_addr += this_len;
+ len -= this_len;
+ }
+
+ return msgs[1].buf - (u8 *)buf;
}
static int sfp_i2c_write(struct sfp *sfp, bool a2, u8 dev_addr, void *buf,