summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRussell King (Oracle) <rmk+kernel@armlinux.org.uk>2024-04-16 16:52:33 +0100
committerRussell King (Oracle) <rmk+kernel@armlinux.org.uk>2024-04-16 16:52:33 +0100
commit7625b23cfeb16a3dc9227467db6a4e7d3eed0908 (patch)
tree14cf007cdbae318a6a04426e38e2fa88a2e1201f
parent59fa404bcafda5ddaf72c8a7be8181c88a8e3ce3 (diff)
parent12bbe31f56df7fc344f6a14d313396d4675968ee (diff)
Merge branches 'mvebu-cpuidle' and 'mvneta' into clearfog
-rw-r--r--Documentation/devicetree/bindings/net/marvell,10g.yaml36
-rw-r--r--arch/arm/boot/dts/marvell/armada-38x.dtsi5
-rw-r--r--arch/arm64/boot/dts/marvell/armada-8040-mcbin.dts2
-rw-r--r--drivers/net/dsa/b53/b53_common.c14
-rw-r--r--drivers/net/dsa/bcm_sf2.c49
-rw-r--r--drivers/net/dsa/lan9303-core.c31
-rw-r--r--drivers/net/dsa/lantiq_gswip.c39
-rw-r--r--drivers/net/dsa/mt7530.c97
-rw-r--r--drivers/net/dsa/mt7530.h6
-rw-r--r--drivers/net/dsa/mv88e6xxx/chip.c129
-rw-r--r--drivers/net/dsa/mv88e6xxx/chip.h10
-rw-r--r--drivers/net/dsa/mv88e6xxx/pcs-6185.c3
-rw-r--r--drivers/net/dsa/mv88e6xxx/port.c41
-rw-r--r--drivers/net/dsa/mv88e6xxx/port.h1
-rw-r--r--drivers/net/dsa/ocelot/felix.c54
-rw-r--r--drivers/net/dsa/ocelot/felix.h1
-rw-r--r--drivers/net/dsa/ocelot/ocelot_ext.c1
-rw-r--r--drivers/net/dsa/qca/ar9331.c37
-rw-r--r--drivers/net/dsa/qca/qca8k-8xxx.c49
-rw-r--r--drivers/net/dsa/rzn1_a5psw.c47
-rw-r--r--drivers/net/dsa/sja1105/sja1105_main.c38
-rw-r--r--drivers/net/dsa/xrs700x/xrs700x.c25
-rw-r--r--drivers/net/ethernet/broadcom/asp2/bcmasp_ethtool.c4
-rw-r--r--drivers/net/ethernet/broadcom/asp2/bcmasp_intf.c5
-rw-r--r--drivers/net/ethernet/broadcom/genet/bcmgenet.c8
-rw-r--r--drivers/net/ethernet/broadcom/genet/bcmmii.c5
-rw-r--r--drivers/net/ethernet/cadence/macb_main.c2
-rw-r--r--drivers/net/ethernet/freescale/fec_main.c4
-rw-r--r--drivers/net/ethernet/marvell/Kconfig4
-rw-r--r--drivers/net/ethernet/marvell/Makefile1
-rw-r--r--drivers/net/ethernet/marvell/mvgmac.c461
-rw-r--r--drivers/net/ethernet/marvell/mvgmac.h56
-rw-r--r--drivers/net/ethernet/marvell/mvneta.c403
-rw-r--r--drivers/net/ethernet/marvell/mvpp2/mvpp2.h5
-rw-r--r--drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c119
-rw-r--r--drivers/net/ethernet/marvell/prestera/prestera_main.c4
-rw-r--r--drivers/net/ethernet/samsung/sxgbe/sxgbe_common.h1
-rw-r--r--drivers/net/ethernet/samsung/sxgbe/sxgbe_ethtool.c2
-rw-r--r--drivers/net/ethernet/samsung/sxgbe/sxgbe_main.c1
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c2
-rw-r--r--drivers/net/ethernet/wangxun/txgbe/txgbe_phy.c3
-rw-r--r--drivers/net/pcs/pcs-rzn1-miic.c8
-rw-r--r--drivers/net/pcs/pcs-xpcs.c3
-rw-r--r--drivers/net/phy/Makefile2
-rw-r--r--drivers/net/phy/aquantia/aquantia_main.c15
-rw-r--r--drivers/net/phy/bcm84881.c14
-rw-r--r--drivers/net/phy/marvell.c70
-rw-r--r--drivers/net/phy/marvell10g.c91
-rw-r--r--drivers/net/phy/mdio_bus.c8
-rw-r--r--drivers/net/phy/phy.c34
-rw-r--r--drivers/net/phy/phy_device.c26
-rw-r--r--drivers/net/phy/phylink.c506
-rw-r--r--drivers/net/phy/sff.c114
-rw-r--r--drivers/net/phy/sff.h16
-rw-r--r--drivers/net/phy/sfp.c248
-rw-r--r--drivers/net/phy/xilinx_gmii2rgmii.c2
-rw-r--r--drivers/phy/marvell/phy-armada38x-comphy.c147
-rw-r--r--include/linux/mdio.h2
-rw-r--r--include/linux/phy.h45
-rw-r--r--include/linux/phylink.h78
-rw-r--r--include/net/dsa.h14
-rw-r--r--include/net/eee.h38
-rw-r--r--net/dsa/dsa.c11
-rw-r--r--net/dsa/port.c60
-rw-r--r--net/dsa/user.c44
65 files changed, 2616 insertions, 785 deletions
diff --git a/Documentation/devicetree/bindings/net/marvell,10g.yaml b/Documentation/devicetree/bindings/net/marvell,10g.yaml
new file mode 100644
index 000000000000..9ec85333f4a4
--- /dev/null
+++ b/Documentation/devicetree/bindings/net/marvell,10g.yaml
@@ -0,0 +1,36 @@
+# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/net/marvell,10g.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Marvell Alaska X family Ethernet PHYs
+
+maintainers:
+ - Russell King <rmk+kernel@armlinux.org.uk>
+
+allOf:
+ - $ref: ethernet-phy.yaml#
+
+properties:
+ marvell,led-mode:
+ description:
+ An array of one to four 16-bit integers to write to the PHY LED
+ configuration registers.
+ $ref: /schemas/types.yaml#/definitions/uint16-array
+ minItems: 1
+ maxItems: 4
+
+additionalProperties: false
+
+examples:
+ - |
+ mdio {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ ethernet-phy@0 {
+ reg = <0>;
+ compatible = "ethernet-phy-ieee802.3-c45";
+ marvell,led-mode = /bits/ 16 <0x0129 0x095d 0x0855>;
+ };
+ };
diff --git a/arch/arm/boot/dts/marvell/armada-38x.dtsi b/arch/arm/boot/dts/marvell/armada-38x.dtsi
index 446861b6b17b..2d6f9f937279 100644
--- a/arch/arm/boot/dts/marvell/armada-38x.dtsi
+++ b/arch/arm/boot/dts/marvell/armada-38x.dtsi
@@ -356,8 +356,9 @@
comphy: phy@18300 {
compatible = "marvell,armada-380-comphy";
- reg-names = "comphy", "conf";
- reg = <0x18300 0x100>, <0x18460 4>;
+ reg-names = "comphy", "pipe", "conf";
+ reg = <0x18300 0x100>, <0xa0000 0x3000>,
+ <0x18460 4>;
#address-cells = <1>;
#size-cells = <0>;
diff --git a/arch/arm64/boot/dts/marvell/armada-8040-mcbin.dts b/arch/arm64/boot/dts/marvell/armada-8040-mcbin.dts
index 1766cf58101b..68c27f22ff57 100644
--- a/arch/arm64/boot/dts/marvell/armada-8040-mcbin.dts
+++ b/arch/arm64/boot/dts/marvell/armada-8040-mcbin.dts
@@ -21,12 +21,14 @@
compatible = "ethernet-phy-ieee802.3-c45";
reg = <0>;
sfp = <&sfp_eth0>;
+ marvell,led-mode = /bits/ 16 <0x0129 0x095d 0x0855>;
};
phy8: ethernet-phy@8 {
compatible = "ethernet-phy-ieee802.3-c45";
reg = <8>;
sfp = <&sfp_eth1>;
+ marvell,led-mode = /bits/ 16 <0x0129 0x095d 0x0855>;
};
};
diff --git a/drivers/net/dsa/b53/b53_common.c b/drivers/net/dsa/b53/b53_common.c
index 0d628b35fd5c..f0bd9efc24dc 100644
--- a/drivers/net/dsa/b53/b53_common.c
+++ b/drivers/net/dsa/b53/b53_common.c
@@ -1420,10 +1420,10 @@ void b53_phylink_mac_link_down(struct dsa_switch *ds, int port,
{
struct b53_device *dev = ds->priv;
- if (mode == MLO_AN_PHY)
+ if (phylink_mode_phy(mode))
return;
- if (mode == MLO_AN_FIXED) {
+ if (phylink_mode_fixed(mode)) {
b53_force_link(dev, port, false);
return;
}
@@ -1446,10 +1446,10 @@ void b53_phylink_mac_link_up(struct dsa_switch *ds, int port,
if (is63xx(dev) && port >= B53_63XX_RGMII0)
b53_adjust_63xx_rgmii(ds, port, interface);
- if (mode == MLO_AN_PHY)
+ if (phylink_mode_phy(mode))
return;
- if (mode == MLO_AN_FIXED) {
+ if (phylink_mode_fixed(mode)) {
b53_force_port_config(dev, port, speed, duplex,
tx_pause, rx_pause);
b53_force_link(dev, port, true);
@@ -2227,16 +2227,10 @@ EXPORT_SYMBOL(b53_eee_init);
int b53_get_mac_eee(struct dsa_switch *ds, int port, struct ethtool_eee *e)
{
struct b53_device *dev = ds->priv;
- struct ethtool_eee *p = &dev->ports[port].eee;
- u16 reg;
if (is5325(dev) || is5365(dev))
return -EOPNOTSUPP;
- b53_read16(dev, B53_EEE_PAGE, B53_EEE_LPI_INDICATE, &reg);
- e->eee_enabled = p->eee_enabled;
- e->eee_active = !!(reg & BIT(port));
-
return 0;
}
EXPORT_SYMBOL(b53_get_mac_eee);
diff --git a/drivers/net/dsa/bcm_sf2.c b/drivers/net/dsa/bcm_sf2.c
index 4a52ccbe393f..0736b894c926 100644
--- a/drivers/net/dsa/bcm_sf2.c
+++ b/drivers/net/dsa/bcm_sf2.c
@@ -740,16 +740,19 @@ static void bcm_sf2_sw_get_caps(struct dsa_switch *ds, int port,
MAC_10 | MAC_100 | MAC_1000;
}
-static void bcm_sf2_sw_mac_config(struct dsa_switch *ds, int port,
+static void bcm_sf2_sw_mac_config(struct phylink_config *config,
unsigned int mode,
const struct phylink_link_state *state)
{
- struct bcm_sf2_priv *priv = bcm_sf2_to_priv(ds);
+ struct dsa_port *dp = dsa_phylink_to_port(config);
u32 id_mode_dis = 0, port_mode;
+ struct bcm_sf2_priv *priv;
u32 reg_rgmii_ctrl;
u32 reg;
- if (port == core_readl(priv, CORE_IMP0_PRT_ID))
+ priv = bcm_sf2_to_priv(dp->ds);
+
+ if (dp->index == core_readl(priv, CORE_IMP0_PRT_ID))
return;
switch (state->interface) {
@@ -770,7 +773,7 @@ static void bcm_sf2_sw_mac_config(struct dsa_switch *ds, int port,
return;
}
- reg_rgmii_ctrl = bcm_sf2_reg_rgmii_cntrl(priv, port);
+ reg_rgmii_ctrl = bcm_sf2_reg_rgmii_cntrl(priv, dp->index);
/* Clear id_mode_dis bit, and the existing port mode, let
* RGMII_MODE_EN bet set by mac_link_{up,down}
@@ -809,13 +812,16 @@ static void bcm_sf2_sw_mac_link_set(struct dsa_switch *ds, int port,
reg_writel(priv, reg, reg_rgmii_ctrl);
}
-static void bcm_sf2_sw_mac_link_down(struct dsa_switch *ds, int port,
+static void bcm_sf2_sw_mac_link_down(struct phylink_config *config,
unsigned int mode,
phy_interface_t interface)
{
- struct bcm_sf2_priv *priv = bcm_sf2_to_priv(ds);
+ struct dsa_port *dp = dsa_phylink_to_port(config);
+ struct bcm_sf2_priv *priv;
+ int port = dp->index;
u32 reg, offset;
+ priv = bcm_sf2_to_priv(dp->ds);
if (priv->wol_ports_mask & BIT(port))
return;
@@ -824,23 +830,26 @@ static void bcm_sf2_sw_mac_link_down(struct dsa_switch *ds, int port,
reg &= ~LINK_STS;
core_writel(priv, reg, offset);
- bcm_sf2_sw_mac_link_set(ds, port, interface, false);
+ bcm_sf2_sw_mac_link_set(dp->ds, port, interface, false);
}
-static void bcm_sf2_sw_mac_link_up(struct dsa_switch *ds, int port,
+static void bcm_sf2_sw_mac_link_up(struct phylink_config *config,
+ struct phy_device *phydev,
unsigned int mode,
phy_interface_t interface,
- struct phy_device *phydev,
int speed, int duplex,
bool tx_pause, bool rx_pause)
{
- struct bcm_sf2_priv *priv = bcm_sf2_to_priv(ds);
- struct ethtool_eee *p = &priv->dev->ports[port].eee;
+ struct dsa_port *dp = dsa_phylink_to_port(config);
+ struct bcm_sf2_priv *priv;
u32 reg_rgmii_ctrl = 0;
+ struct ethtool_eee *p;
+ int port = dp->index;
u32 reg, offset;
- bcm_sf2_sw_mac_link_set(ds, port, interface, true);
+ bcm_sf2_sw_mac_link_set(dp->ds, port, interface, true);
+ priv = bcm_sf2_to_priv(dp->ds);
offset = bcm_sf2_port_override_offset(priv, port);
if (phy_interface_mode_is_rgmii(interface) ||
@@ -886,8 +895,10 @@ static void bcm_sf2_sw_mac_link_up(struct dsa_switch *ds, int port,
core_writel(priv, reg, offset);
- if (mode == MLO_AN_PHY && phydev)
- p->eee_enabled = b53_eee_init(ds, port, phydev);
+ if (phylink_mode_phy(mode) && phydev) {
+ p = &priv->dev->ports[port].eee;
+ p->eee_enabled = b53_eee_init(dp->ds, port, phydev);
+ }
}
static void bcm_sf2_sw_fixed_state(struct dsa_switch *ds, int port,
@@ -1196,6 +1207,12 @@ static int bcm_sf2_sw_get_sset_count(struct dsa_switch *ds, int port,
return cnt;
}
+static const struct phylink_mac_ops bcm_sf2_phylink_mac_ops = {
+ .mac_config = bcm_sf2_sw_mac_config,
+ .mac_link_down = bcm_sf2_sw_mac_link_down,
+ .mac_link_up = bcm_sf2_sw_mac_link_up,
+};
+
static const struct dsa_switch_ops bcm_sf2_ops = {
.get_tag_protocol = b53_get_tag_protocol,
.setup = bcm_sf2_sw_setup,
@@ -1206,9 +1223,6 @@ static const struct dsa_switch_ops bcm_sf2_ops = {
.get_ethtool_phy_stats = b53_get_ethtool_phy_stats,
.get_phy_flags = bcm_sf2_sw_get_phy_flags,
.phylink_get_caps = bcm_sf2_sw_get_caps,
- .phylink_mac_config = bcm_sf2_sw_mac_config,
- .phylink_mac_link_down = bcm_sf2_sw_mac_link_down,
- .phylink_mac_link_up = bcm_sf2_sw_mac_link_up,
.phylink_fixed_state = bcm_sf2_sw_fixed_state,
.suspend = bcm_sf2_sw_suspend,
.resume = bcm_sf2_sw_resume,
@@ -1399,6 +1413,7 @@ static int bcm_sf2_sw_probe(struct platform_device *pdev)
priv->dev = dev;
ds = dev->ds;
ds->ops = &bcm_sf2_ops;
+ ds->phylink_mac_ops = &bcm_sf2_phylink_mac_ops;
/* Advertise the 8 egress queues */
ds->num_tx_queues = SF2_NUM_EGRESS_QUEUES;
diff --git a/drivers/net/dsa/lan9303-core.c b/drivers/net/dsa/lan9303-core.c
index fcb20eac332a..666b4d766c00 100644
--- a/drivers/net/dsa/lan9303-core.c
+++ b/drivers/net/dsa/lan9303-core.c
@@ -1293,14 +1293,29 @@ static void lan9303_phylink_get_caps(struct dsa_switch *ds, int port,
}
}
-static void lan9303_phylink_mac_link_up(struct dsa_switch *ds, int port,
+static void lan9303_phylink_mac_config(struct phylink_config *config,
+ unsigned int mode,
+ const struct phylink_link_state *state)
+{
+}
+
+static void lan9303_phylink_mac_link_down(struct phylink_config *config,
+ unsigned int mode,
+ phy_interface_t interface)
+{
+}
+
+static void lan9303_phylink_mac_link_up(struct phylink_config *config,
+ struct phy_device *phydev,
unsigned int mode,
phy_interface_t interface,
- struct phy_device *phydev, int speed,
- int duplex, bool tx_pause,
+ int speed, int duplex, bool tx_pause,
bool rx_pause)
{
- struct lan9303 *chip = ds->priv;
+ struct dsa_port *dp = dsa_phylink_to_port(config);
+ struct lan9303 *chip = dp->ds->priv;
+ struct dsa_switch *ds = dp->ds;
+ int port = dp->index;
u32 ctl;
u32 reg;
@@ -1330,6 +1345,12 @@ static void lan9303_phylink_mac_link_up(struct dsa_switch *ds, int port,
regmap_write(chip->regmap, flow_ctl_reg[port], reg);
}
+static const struct phylink_mac_ops lan9303_phylink_mac_ops = {
+ .mac_config = lan9303_phylink_mac_config,
+ .mac_link_down = lan9303_phylink_mac_link_down,
+ .mac_link_up = lan9303_phylink_mac_link_up,
+};
+
static const struct dsa_switch_ops lan9303_switch_ops = {
.get_tag_protocol = lan9303_get_tag_protocol,
.setup = lan9303_setup,
@@ -1337,7 +1358,6 @@ static const struct dsa_switch_ops lan9303_switch_ops = {
.phy_read = lan9303_phy_read,
.phy_write = lan9303_phy_write,
.phylink_get_caps = lan9303_phylink_get_caps,
- .phylink_mac_link_up = lan9303_phylink_mac_link_up,
.get_ethtool_stats = lan9303_get_ethtool_stats,
.get_sset_count = lan9303_get_sset_count,
.port_enable = lan9303_port_enable,
@@ -1365,6 +1385,7 @@ static int lan9303_register_switch(struct lan9303 *chip)
chip->ds->num_ports = LAN9303_NUM_PORTS;
chip->ds->priv = chip;
chip->ds->ops = &lan9303_switch_ops;
+ chip->ds->phylink_mac_ops = &lan9303_phylink_mac_ops;
base = chip->phy_addr_base;
chip->ds->phys_mii_mask = GENMASK(LAN9303_NUM_PORTS - 1 + base, base);
diff --git a/drivers/net/dsa/lantiq_gswip.c b/drivers/net/dsa/lantiq_gswip.c
index de48b194048f..a557049e34f5 100644
--- a/drivers/net/dsa/lantiq_gswip.c
+++ b/drivers/net/dsa/lantiq_gswip.c
@@ -1670,11 +1670,13 @@ static void gswip_port_set_pause(struct gswip_priv *priv, int port,
mdio_phy, GSWIP_MDIO_PHYp(port));
}
-static void gswip_phylink_mac_config(struct dsa_switch *ds, int port,
+static void gswip_phylink_mac_config(struct phylink_config *config,
unsigned int mode,
const struct phylink_link_state *state)
{
- struct gswip_priv *priv = ds->priv;
+ struct dsa_port *dp = dsa_phylink_to_port(config);
+ struct gswip_priv *priv = dp->ds->priv;
+ int port = dp->index;
u32 miicfg = 0;
miicfg |= GSWIP_MII_CFG_LDCLKDIS;
@@ -1700,7 +1702,7 @@ static void gswip_phylink_mac_config(struct dsa_switch *ds, int port,
miicfg |= GSWIP_MII_CFG_MODE_GMII;
break;
default:
- dev_err(ds->dev,
+ dev_err(dp->ds->dev,
"Unsupported interface: %d\n", state->interface);
return;
}
@@ -1726,28 +1728,32 @@ static void gswip_phylink_mac_config(struct dsa_switch *ds, int port,
}
}
-static void gswip_phylink_mac_link_down(struct dsa_switch *ds, int port,
+static void gswip_phylink_mac_link_down(struct phylink_config *config,
unsigned int mode,
phy_interface_t interface)
{
- struct gswip_priv *priv = ds->priv;
+ struct dsa_port *dp = dsa_phylink_to_port(config);
+ struct gswip_priv *priv = dp->ds->priv;
+ int port = dp->index;
gswip_mii_mask_cfg(priv, GSWIP_MII_CFG_EN, 0, port);
- if (!dsa_is_cpu_port(ds, port))
+ if (!dsa_port_is_cpu(dp))
gswip_port_set_link(priv, port, false);
}
-static void gswip_phylink_mac_link_up(struct dsa_switch *ds, int port,
+static void gswip_phylink_mac_link_up(struct phylink_config *config,
+ struct phy_device *phydev,
unsigned int mode,
phy_interface_t interface,
- struct phy_device *phydev,
int speed, int duplex,
bool tx_pause, bool rx_pause)
{
- struct gswip_priv *priv = ds->priv;
+ struct dsa_port *dp = dsa_phylink_to_port(config);
+ struct gswip_priv *priv = dp->ds->priv;
+ int port = dp->index;
- if (!dsa_is_cpu_port(ds, port)) {
+ if (!dsa_port_is_cpu(dp)) {
gswip_port_set_link(priv, port, true);
gswip_port_set_speed(priv, port, speed, interface);
gswip_port_set_duplex(priv, port, duplex);
@@ -1824,6 +1830,12 @@ static int gswip_get_sset_count(struct dsa_switch *ds, int port, int sset)
return ARRAY_SIZE(gswip_rmon_cnt);
}
+static const struct phylink_mac_ops gswip_phylink_mac_ops = {
+ .mac_config = gswip_phylink_mac_config,
+ .mac_link_down = gswip_phylink_mac_link_down,
+ .mac_link_up = gswip_phylink_mac_link_up,
+};
+
static const struct dsa_switch_ops gswip_xrx200_switch_ops = {
.get_tag_protocol = gswip_get_tag_protocol,
.setup = gswip_setup,
@@ -1842,9 +1854,6 @@ static const struct dsa_switch_ops gswip_xrx200_switch_ops = {
.port_change_mtu = gswip_port_change_mtu,
.port_max_mtu = gswip_port_max_mtu,
.phylink_get_caps = gswip_xrx200_phylink_get_caps,
- .phylink_mac_config = gswip_phylink_mac_config,
- .phylink_mac_link_down = gswip_phylink_mac_link_down,
- .phylink_mac_link_up = gswip_phylink_mac_link_up,
.get_strings = gswip_get_strings,
.get_ethtool_stats = gswip_get_ethtool_stats,
.get_sset_count = gswip_get_sset_count,
@@ -1868,9 +1877,6 @@ static const struct dsa_switch_ops gswip_xrx300_switch_ops = {
.port_change_mtu = gswip_port_change_mtu,
.port_max_mtu = gswip_port_max_mtu,
.phylink_get_caps = gswip_xrx300_phylink_get_caps,
- .phylink_mac_config = gswip_phylink_mac_config,
- .phylink_mac_link_down = gswip_phylink_mac_link_down,
- .phylink_mac_link_up = gswip_phylink_mac_link_up,
.get_strings = gswip_get_strings,
.get_ethtool_stats = gswip_get_ethtool_stats,
.get_sset_count = gswip_get_sset_count,
@@ -2136,6 +2142,7 @@ static int gswip_probe(struct platform_device *pdev)
priv->ds->num_ports = priv->hw_info->max_ports;
priv->ds->priv = priv;
priv->ds->ops = priv->hw_info->ops;
+ priv->ds->phylink_mac_ops = &gswip_phylink_mac_ops;
priv->dev = dev;
mutex_init(&priv->pce_table_lock);
version = gswip_switch_r(priv, GSWIP_VERSION);
diff --git a/drivers/net/dsa/mt7530.c b/drivers/net/dsa/mt7530.c
index 3c1f657593a8..6985e1bf94f3 100644
--- a/drivers/net/dsa/mt7530.c
+++ b/drivers/net/dsa/mt7530.c
@@ -1080,7 +1080,8 @@ mt7530_port_enable(struct dsa_switch *ds, int port,
priv->ports[port].enable = true;
mt7530_rmw(priv, MT7530_PCR_P(port), PCR_MATRIX_MASK,
priv->ports[port].pm);
- mt7530_clear(priv, MT7530_PMCR_P(port), PMCR_LINK_SETTINGS_MASK);
+ mt7530_clear(priv, MT7530_PMCR_P(port), PMCR_LINK_SETTINGS_MASK |
+ PMCR_FORCE_EEE1G | PMCR_FORCE_EEE100);
mutex_unlock(&priv->reg_mutex);
@@ -2860,20 +2861,45 @@ static void mt753x_phylink_mac_link_up(struct dsa_switch *ds, int port,
mcr |= PMCR_RX_FC_EN;
}
- if (mode == MLO_AN_PHY && phydev && phy_init_eee(phydev, false) >= 0) {
- switch (speed) {
- case SPEED_1000:
- mcr |= PMCR_FORCE_EEE1G;
- break;
- case SPEED_100:
- mcr |= PMCR_FORCE_EEE100;
- break;
- }
- }
-
mt7530_set(priv, MT7530_PMCR_P(port), mcr);
}
+static void mt753x_phylink_mac_disable_tx_lpi(struct dsa_switch *ds, int port)
+{
+ struct mt7530_priv *priv = ds->priv;
+
+ mt7530_clear(priv, MT7530_PMCR_P(port),
+ PMCR_FORCE_EEE1G | PMCR_FORCE_EEE100);
+}
+
+static void mt753x_phylink_mac_enable_tx_lpi(struct dsa_switch *ds, int port,
+ u32 timer)
+{
+ struct mt7530_priv *priv = ds->priv;
+ u32 val;
+
+ /* If the timer is zero, then set LPI_MODE_EN, which allows the
+ * system to enter LPI mode immediately rather than waiting for
+ * the LPI threshold.
+ */
+ if (!timer)
+ val = LPI_MODE_EN;
+ else if (FIELD_FIT(LPI_THRESH_MASK, timer))
+ val = FIELD_PREP(LPI_THRESH_MASK, timer);
+ else
+ val = LPI_THRESH_MASK;
+
+ mt7530_rmw(priv, MT7530_PMEEECR_P(port),
+ LPI_THRESH_MASK | LPI_MODE_EN, val);
+
+ /* FIXME: mt7531 docs say that bits 26 and 25 need to be set to
+ * enable EEE forcing. The PMCR_FORCE_EEE* bits just determine
+ * whether we force-enable or force-disable these modes.
+ */
+ mt7530_set(priv, MT7530_PMCR_P(port),
+ PMCR_FORCE_EEE1G | PMCR_FORCE_EEE100);
+}
+
static int
mt7531_cpu_port_config(struct dsa_switch *ds, int port)
{
@@ -2935,15 +2961,26 @@ static void mt753x_phylink_get_caps(struct dsa_switch *ds, int port,
struct phylink_config *config)
{
struct mt7530_priv *priv = ds->priv;
+ u32 eeecr;
/* This switch only supports full-duplex at 1Gbps */
config->mac_capabilities = MAC_ASYM_PAUSE | MAC_SYM_PAUSE |
MAC_10 | MAC_100 | MAC_1000FD;
+ config->lpi_capabilities = MAC_100FD | MAC_1000FD;
+ config->lpi_timer_limit_us = FIELD_GET(LPI_THRESH_MASK,
+ LPI_THRESH_MASK);
+
+ eeecr = mt7530_read(priv, MT7530_PMEEECR_P(port));
+ /* tx_lpi_timer should be in microseconds. The time units for
+ * LPI threshold are unspecified.
+ */
+ config->eee.tx_lpi_timer = FIELD_GET(LPI_THRESH_MASK, eeecr);
+
priv->info->mac_port_get_caps(ds, port, config);
}
-static int mt753x_pcs_validate(struct phylink_pcs *pcs,
+static int mt753x_pcs_validate(struct phylink_pcs *pcs, unsigned int mode,
unsigned long *supported,
const struct phylink_link_state *state)
{
@@ -3044,36 +3081,6 @@ mt753x_setup(struct dsa_switch *ds)
return ret;
}
-static int mt753x_get_mac_eee(struct dsa_switch *ds, int port,
- struct ethtool_eee *e)
-{
- struct mt7530_priv *priv = ds->priv;
- u32 eeecr = mt7530_read(priv, MT7530_PMEEECR_P(port));
-
- e->tx_lpi_enabled = !(eeecr & LPI_MODE_EN);
- e->tx_lpi_timer = GET_LPI_THRESH(eeecr);
-
- return 0;
-}
-
-static int mt753x_set_mac_eee(struct dsa_switch *ds, int port,
- struct ethtool_eee *e)
-{
- struct mt7530_priv *priv = ds->priv;
- u32 set, mask = LPI_THRESH_MASK | LPI_MODE_EN;
-
- if (e->tx_lpi_timer > 0xFFF)
- return -EINVAL;
-
- set = SET_LPI_THRESH(e->tx_lpi_timer);
- if (!e->tx_lpi_enabled)
- /* Force LPI Mode without a delay */
- set |= LPI_MODE_EN;
- mt7530_rmw(priv, MT7530_PMEEECR_P(port), mask, set);
-
- return 0;
-}
-
static int mt7988_pad_setup(struct dsa_switch *ds, phy_interface_t interface)
{
return 0;
@@ -3127,8 +3134,8 @@ const struct dsa_switch_ops mt7530_switch_ops = {
.phylink_mac_config = mt753x_phylink_mac_config,
.phylink_mac_link_down = mt753x_phylink_mac_link_down,
.phylink_mac_link_up = mt753x_phylink_mac_link_up,
- .get_mac_eee = mt753x_get_mac_eee,
- .set_mac_eee = mt753x_set_mac_eee,
+ .phylink_mac_disable_tx_lpi = mt753x_phylink_mac_disable_tx_lpi,
+ .phylink_mac_enable_tx_lpi = mt753x_phylink_mac_enable_tx_lpi,
};
EXPORT_SYMBOL_GPL(mt7530_switch_ops);
diff --git a/drivers/net/dsa/mt7530.h b/drivers/net/dsa/mt7530.h
index 17e42d30fff4..cdab7ea79294 100644
--- a/drivers/net/dsa/mt7530.h
+++ b/drivers/net/dsa/mt7530.h
@@ -309,8 +309,7 @@ enum mt7530_vlan_port_acc_frm {
#define PMCR_LINK_SETTINGS_MASK (PMCR_TX_EN | PMCR_FORCE_SPEED_1000 | \
PMCR_RX_EN | PMCR_FORCE_SPEED_100 | \
PMCR_TX_FC_EN | PMCR_RX_FC_EN | \
- PMCR_FORCE_FDX | PMCR_FORCE_LNK | \
- PMCR_FORCE_EEE1G | PMCR_FORCE_EEE100)
+ PMCR_FORCE_FDX | PMCR_FORCE_LNK)
#define PMCR_CPU_PORT_SETTING(id) (PMCR_FORCE_MODE_ID((id)) | \
PMCR_IFG_XMIT(1) | PMCR_MAC_MODE | \
PMCR_BACKOFF_EN | PMCR_BACKPR_EN | \
@@ -323,9 +322,6 @@ enum mt7530_vlan_port_acc_frm {
#define WAKEUP_TIME_1000(x) (((x) & 0xFF) << 24)
#define WAKEUP_TIME_100(x) (((x) & 0xFF) << 16)
#define LPI_THRESH_MASK GENMASK(15, 4)
-#define LPI_THRESH_SHT 4
-#define SET_LPI_THRESH(x) (((x) << LPI_THRESH_SHT) & LPI_THRESH_MASK)
-#define GET_LPI_THRESH(x) (((x) & LPI_THRESH_MASK) >> LPI_THRESH_SHT)
#define LPI_MODE_EN BIT(0)
#define MT7530_PMSR_P(x) (0x3008 + (x) * 0x100)
diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c
index 614cabb5c1b0..fa60a7b19d12 100644
--- a/drivers/net/dsa/mv88e6xxx/chip.c
+++ b/drivers/net/dsa/mv88e6xxx/chip.c
@@ -630,6 +630,9 @@ static void mv88e6352_phylink_get_caps(struct mv88e6xxx_chip *chip, int port,
config->mac_capabilities = MAC_SYM_PAUSE | MAC_10 | MAC_100 |
MAC_1000FD;
+ config->lpi_capabilities = MAC_1000FD | MAC_100FD;
+ config->eee.eee_enabled = true;
+ config->eee.tx_lpi_enabled = true;
/* Port 4 supports automedia if the serdes is associated with it. */
if (port == 4) {
@@ -790,31 +793,34 @@ static void mv88e6xxx_get_caps(struct dsa_switch *ds, int port,
}
}
-static struct phylink_pcs *mv88e6xxx_mac_select_pcs(struct dsa_switch *ds,
- int port,
- phy_interface_t interface)
+static struct phylink_pcs *
+mv88e6xxx_mac_select_pcs(struct phylink_config *config,
+ phy_interface_t interface)
{
- struct mv88e6xxx_chip *chip = ds->priv;
+ struct dsa_port *dp = dsa_phylink_to_port(config);
+ struct mv88e6xxx_chip *chip = dp->ds->priv;
struct phylink_pcs *pcs = ERR_PTR(-EOPNOTSUPP);
if (chip->info->ops->pcs_ops)
- pcs = chip->info->ops->pcs_ops->pcs_select(chip, port,
+ pcs = chip->info->ops->pcs_ops->pcs_select(chip, dp->index,
interface);
return pcs;
}
-static int mv88e6xxx_mac_prepare(struct dsa_switch *ds, int port,
+static int mv88e6xxx_mac_prepare(struct phylink_config *config,
unsigned int mode, phy_interface_t interface)
{
- struct mv88e6xxx_chip *chip = ds->priv;
+ struct dsa_port *dp = dsa_phylink_to_port(config);
+ struct mv88e6xxx_chip *chip = dp->ds->priv;
+ int port = dp->index;
int err = 0;
/* In inband mode, the link may come up at any time while the link
* is not forced down. Force the link down while we reconfigure the
* interface mode.
*/
- if (mode == MLO_AN_INBAND &&
+ if (phylink_mode_inband(mode) &&
chip->ports[port].interface != interface &&
chip->info->ops->port_set_link) {
mv88e6xxx_reg_lock(chip);
@@ -826,16 +832,18 @@ static int mv88e6xxx_mac_prepare(struct dsa_switch *ds, int port,
return err;
}
-static void mv88e6xxx_mac_config(struct dsa_switch *ds, int port,
+static void mv88e6xxx_mac_config(struct phylink_config *config,
unsigned int mode,
const struct phylink_link_state *state)
{
- struct mv88e6xxx_chip *chip = ds->priv;
+ struct dsa_port *dp = dsa_phylink_to_port(config);
+ struct mv88e6xxx_chip *chip = dp->ds->priv;
+ int port = dp->index;
int err = 0;
mv88e6xxx_reg_lock(chip);
- if (mode != MLO_AN_PHY || !mv88e6xxx_phy_is_internal(chip, port)) {
+ if (!phylink_mode_phy(mode) || !mv88e6xxx_phy_is_internal(chip, port)) {
err = mv88e6xxx_port_config_interface(chip, port,
state->interface);
if (err && err != -EOPNOTSUPP)
@@ -846,13 +854,15 @@ err_unlock:
mv88e6xxx_reg_unlock(chip);
if (err && err != -EOPNOTSUPP)
- dev_err(ds->dev, "p%d: failed to configure MAC/PCS\n", port);
+ dev_err(chip->dev, "p%d: failed to configure MAC/PCS\n", port);
}
-static int mv88e6xxx_mac_finish(struct dsa_switch *ds, int port,
+static int mv88e6xxx_mac_finish(struct phylink_config *config,
unsigned int mode, phy_interface_t interface)
{
- struct mv88e6xxx_chip *chip = ds->priv;
+ struct dsa_port *dp = dsa_phylink_to_port(config);
+ struct mv88e6xxx_chip *chip = dp->ds->priv;
+ int port = dp->index;
int err = 0;
/* Undo the forced down state above after completing configuration
@@ -864,9 +874,10 @@ static int mv88e6xxx_mac_finish(struct dsa_switch *ds, int port,
mv88e6xxx_reg_lock(chip);
if (chip->info->ops->port_set_link &&
- ((mode == MLO_AN_INBAND &&
+ ((phylink_mode_inband(mode) &&
chip->ports[port].interface != interface) ||
- (mode == MLO_AN_PHY && mv88e6xxx_port_ppu_updates(chip, port))))
+ (phylink_mode_phy(mode) &&
+ mv88e6xxx_port_ppu_updates(chip, port))))
err = chip->info->ops->port_set_link(chip, port, LINK_UNFORCED);
mv88e6xxx_reg_unlock(chip);
@@ -876,12 +887,14 @@ static int mv88e6xxx_mac_finish(struct dsa_switch *ds, int port,
return err;
}
-static void mv88e6xxx_mac_link_down(struct dsa_switch *ds, int port,
+static void mv88e6xxx_mac_link_down(struct phylink_config *config,
unsigned int mode,
phy_interface_t interface)
{
- struct mv88e6xxx_chip *chip = ds->priv;
+ struct dsa_port *dp = dsa_phylink_to_port(config);
+ struct mv88e6xxx_chip *chip = dp->ds->priv;
const struct mv88e6xxx_ops *ops;
+ int port = dp->index;
int err = 0;
ops = chip->info->ops;
@@ -891,7 +904,7 @@ static void mv88e6xxx_mac_link_down(struct dsa_switch *ds, int port,
* updated by the switch or if we are using fixed-link mode.
*/
if ((!mv88e6xxx_port_ppu_updates(chip, port) ||
- mode == MLO_AN_FIXED) && ops->port_sync_link)
+ phylink_mode_fixed(mode)) && ops->port_sync_link)
err = ops->port_sync_link(chip, port, mode, false);
if (!err && ops->port_set_speed_duplex)
@@ -904,14 +917,16 @@ static void mv88e6xxx_mac_link_down(struct dsa_switch *ds, int port,
"p%d: failed to force MAC link down\n", port);
}
-static void mv88e6xxx_mac_link_up(struct dsa_switch *ds, int port,
- unsigned int mode, phy_interface_t interface,
+static void mv88e6xxx_mac_link_up(struct phylink_config *config,
struct phy_device *phydev,
+ unsigned int mode, phy_interface_t interface,
int speed, int duplex,
bool tx_pause, bool rx_pause)
{
- struct mv88e6xxx_chip *chip = ds->priv;
+ struct dsa_port *dp = dsa_phylink_to_port(config);
+ struct mv88e6xxx_chip *chip = dp->ds->priv;
const struct mv88e6xxx_ops *ops;
+ int port = dp->index;
int err = 0;
ops = chip->info->ops;
@@ -922,7 +937,7 @@ static void mv88e6xxx_mac_link_up(struct dsa_switch *ds, int port,
* mode.
*/
if (!mv88e6xxx_port_ppu_updates(chip, port) ||
- mode == MLO_AN_FIXED) {
+ phylink_mode_fixed(mode)) {
if (ops->port_set_speed_duplex) {
err = ops->port_set_speed_duplex(chip, port,
speed, duplex);
@@ -937,10 +952,53 @@ error:
mv88e6xxx_reg_unlock(chip);
if (err && err != -EOPNOTSUPP)
- dev_err(ds->dev,
+ dev_err(chip->dev,
"p%d: failed to configure MAC link up\n", port);
}
+static void mv88e6xxx_mac_disable_tx_lpi(struct phylink_config *config)
+{
+ struct dsa_port *dp = dsa_phylink_to_port(config);
+ struct mv88e6xxx_chip *chip = dp->ds->priv;
+ int port = dp->index;
+ int err;
+
+ if (!chip->info->ops->port_set_eee)
+ return;
+
+ mv88e6xxx_reg_lock(chip);
+ err = chip->info->ops->port_set_eee(chip, port, EEE_FORCE_DISABLE);
+ mv88e6xxx_reg_unlock(chip);
+
+ if (err)
+ dev_err(chip->dev, "p%d: failed to set EEE mode: %pe\n", port,
+ ERR_PTR(err));
+}
+
+static void mv88e6xxx_mac_enable_tx_lpi(struct phylink_config *config,
+ u32 timer)
+{
+ struct dsa_port *dp = dsa_phylink_to_port(config);
+ struct mv88e6xxx_chip *chip = dp->ds->priv;
+ int port = dp->index;
+ int eee, err;
+
+ if (!chip->info->ops->port_set_eee)
+ return;
+
+ mv88e6xxx_reg_lock(chip);
+ if (mv88e6xxx_port_ppu_updates(chip, port))
+ eee = EEE_UNFORCED;
+ else
+ eee = EEE_FORCE_DISABLE;
+ err = chip->info->ops->port_set_eee(chip, port, eee);
+ mv88e6xxx_reg_unlock(chip);
+
+ if (err)
+ dev_err(chip->dev, "p%d: failed to set EEE mode: %pe\n", port,
+ ERR_PTR(err));
+}
+
static int mv88e6xxx_stats_snapshot(struct mv88e6xxx_chip *chip, int port)
{
int err;
@@ -4484,6 +4542,7 @@ static const struct mv88e6xxx_ops mv88e6172_ops = {
.phy_read_c45 = mv88e6xxx_g2_smi_phy_read_c45,
.phy_write_c45 = mv88e6xxx_g2_smi_phy_write_c45,
.port_set_link = mv88e6xxx_port_set_link,
+ .port_set_eee = mv88e6xxx_port_set_eee,
.port_sync_link = mv88e6xxx_port_sync_link,
.port_set_rgmii_delay = mv88e6352_port_set_rgmii_delay,
.port_set_speed_duplex = mv88e6352_port_set_speed_duplex,
@@ -4584,6 +4643,7 @@ static const struct mv88e6xxx_ops mv88e6176_ops = {
.phy_read_c45 = mv88e6xxx_g2_smi_phy_read_c45,
.phy_write_c45 = mv88e6xxx_g2_smi_phy_write_c45,
.port_set_link = mv88e6xxx_port_set_link,
+ .port_set_eee = mv88e6xxx_port_set_eee,
.port_sync_link = mv88e6xxx_port_sync_link,
.port_set_rgmii_delay = mv88e6352_port_set_rgmii_delay,
.port_set_speed_duplex = mv88e6352_port_set_speed_duplex,
@@ -4851,6 +4911,7 @@ static const struct mv88e6xxx_ops mv88e6240_ops = {
.phy_read_c45 = mv88e6xxx_g2_smi_phy_read_c45,
.phy_write_c45 = mv88e6xxx_g2_smi_phy_write_c45,
.port_set_link = mv88e6xxx_port_set_link,
+ .port_set_eee = mv88e6xxx_port_set_eee,
.port_sync_link = mv88e6xxx_port_sync_link,
.port_set_rgmii_delay = mv88e6352_port_set_rgmii_delay,
.port_set_speed_duplex = mv88e6352_port_set_speed_duplex,
@@ -5261,6 +5322,7 @@ static const struct mv88e6xxx_ops mv88e6352_ops = {
.phy_read_c45 = mv88e6xxx_g2_smi_phy_read_c45,
.phy_write_c45 = mv88e6xxx_g2_smi_phy_write_c45,
.port_set_link = mv88e6xxx_port_set_link,
+ .port_set_eee = mv88e6xxx_port_set_eee,
.port_sync_link = mv88e6xxx_port_sync_link,
.port_set_rgmii_delay = mv88e6352_port_set_rgmii_delay,
.port_set_speed_duplex = mv88e6352_port_set_speed_duplex,
@@ -5444,6 +5506,7 @@ static const struct mv88e6xxx_ops mv88e6393x_ops = {
.phy_read_c45 = mv88e6xxx_g2_smi_phy_read_c45,
.phy_write_c45 = mv88e6xxx_g2_smi_phy_write_c45,
.port_set_link = mv88e6xxx_port_set_link,
+ /* no port_set_eee due to mv88e6393x errata 4.5 */
.port_sync_link = mv88e6xxx_port_sync_link,
.port_set_rgmii_delay = mv88e6390_port_set_rgmii_delay,
.port_set_speed_duplex = mv88e6393x_port_set_speed_duplex,
@@ -6915,6 +6978,17 @@ static int mv88e6xxx_crosschip_lag_leave(struct dsa_switch *ds, int sw_index,
return err_sync ? : err_pvt;
}
+static const struct phylink_mac_ops mv88e6xxx_phylink_mac_ops = {
+ .mac_select_pcs = mv88e6xxx_mac_select_pcs,
+ .mac_prepare = mv88e6xxx_mac_prepare,
+ .mac_config = mv88e6xxx_mac_config,
+ .mac_finish = mv88e6xxx_mac_finish,
+ .mac_link_down = mv88e6xxx_mac_link_down,
+ .mac_link_up = mv88e6xxx_mac_link_up,
+ .mac_disable_tx_lpi = mv88e6xxx_mac_disable_tx_lpi,
+ .mac_enable_tx_lpi = mv88e6xxx_mac_enable_tx_lpi,
+};
+
static const struct dsa_switch_ops mv88e6xxx_switch_ops = {
.get_tag_protocol = mv88e6xxx_get_tag_protocol,
.change_tag_protocol = mv88e6xxx_change_tag_protocol,
@@ -6923,12 +6997,6 @@ static const struct dsa_switch_ops mv88e6xxx_switch_ops = {
.port_setup = mv88e6xxx_port_setup,
.port_teardown = mv88e6xxx_port_teardown,
.phylink_get_caps = mv88e6xxx_get_caps,
- .phylink_mac_select_pcs = mv88e6xxx_mac_select_pcs,
- .phylink_mac_prepare = mv88e6xxx_mac_prepare,
- .phylink_mac_config = mv88e6xxx_mac_config,
- .phylink_mac_finish = mv88e6xxx_mac_finish,
- .phylink_mac_link_down = mv88e6xxx_mac_link_down,
- .phylink_mac_link_up = mv88e6xxx_mac_link_up,
.get_strings = mv88e6xxx_get_strings,
.get_ethtool_stats = mv88e6xxx_get_ethtool_stats,
.get_eth_mac_stats = mv88e6xxx_get_eth_mac_stats,
@@ -6997,6 +7065,7 @@ static int mv88e6xxx_register_switch(struct mv88e6xxx_chip *chip)
ds->priv = chip;
ds->dev = dev;
ds->ops = &mv88e6xxx_switch_ops;
+ ds->phylink_mac_ops = &mv88e6xxx_phylink_mac_ops;
ds->ageing_time_min = chip->info->age_time_coeff;
ds->ageing_time_max = chip->info->age_time_coeff * U8_MAX;
diff --git a/drivers/net/dsa/mv88e6xxx/chip.h b/drivers/net/dsa/mv88e6xxx/chip.h
index 85eb293381a7..549592a9764d 100644
--- a/drivers/net/dsa/mv88e6xxx/chip.h
+++ b/drivers/net/dsa/mv88e6xxx/chip.h
@@ -506,6 +506,16 @@ struct mv88e6xxx_ops {
*/
int (*port_set_link)(struct mv88e6xxx_chip *chip, int port, int link);
+#define EEE_FORCE_DISABLE 0
+#define EEE_FORCE_ENABLE 1
+#define EEE_UNFORCED -1
+
+ /* Port's EEE state
+ * Use EEE_FORCE_ENABLE or EEE_FORCE_DISABLE to force EEE to be enabled
+ * or disabled, or EEE_UNFORCED for normal EEE.
+ */
+ int (*port_set_eee)(struct mv88e6xxx_chip *chip, int port, int eee);
+
/* Synchronise the port link state with that of the SERDES
*/
int (*port_sync_link)(struct mv88e6xxx_chip *chip, int port, unsigned int mode, bool isup);
diff --git a/drivers/net/dsa/mv88e6xxx/pcs-6185.c b/drivers/net/dsa/mv88e6xxx/pcs-6185.c
index 4d677f836807..5a27d047a38e 100644
--- a/drivers/net/dsa/mv88e6xxx/pcs-6185.c
+++ b/drivers/net/dsa/mv88e6xxx/pcs-6185.c
@@ -95,7 +95,7 @@ static void mv88e6185_pcs_get_state(struct phylink_pcs *pcs,
}
}
-static int mv88e6185_pcs_config(struct phylink_pcs *pcs, unsigned int mode,
+static int mv88e6185_pcs_config(struct phylink_pcs *pcs, unsigned int neg_mode,
phy_interface_t interface,
const unsigned long *advertising,
bool permit_pause_to_mac)
@@ -137,6 +137,7 @@ static int mv88e6185_pcs_init(struct mv88e6xxx_chip *chip, int port)
mpcs->chip = chip;
mpcs->port = port;
mpcs->phylink_pcs.ops = &mv88e6185_phylink_pcs_ops;
+ mpcs->phylink_pcs.neg_mode = true;
irq = mv88e6xxx_serdes_irq_mapping(chip, port);
if (irq) {
diff --git a/drivers/net/dsa/mv88e6xxx/port.c b/drivers/net/dsa/mv88e6xxx/port.c
index 5394a8cf7bf1..ef9bb3974f33 100644
--- a/drivers/net/dsa/mv88e6xxx/port.c
+++ b/drivers/net/dsa/mv88e6xxx/port.c
@@ -203,7 +203,7 @@ int mv88e6185_port_sync_link(struct mv88e6xxx_chip *chip, int port, unsigned int
int err = 0;
int link;
- if (mode == MLO_AN_INBAND)
+ if (phylink_mode_inband(mode))
link = LINK_UNFORCED;
else if (isup)
link = LINK_FORCED_UP;
@@ -520,6 +520,36 @@ phy_interface_t mv88e6393x_port_max_speed_mode(struct mv88e6xxx_chip *chip,
return PHY_INTERFACE_MODE_10GBASER;
}
+int mv88e6xxx_port_set_eee(struct mv88e6xxx_chip *chip, int port, int eee)
+{
+ u16 reg, val = 0;
+ int err;
+
+ switch (eee) {
+ case EEE_FORCE_ENABLE:
+ val = MV88E6XXX_PORT_MAC_CTL_EEE;
+ fallthrough;
+ case EEE_FORCE_DISABLE:
+ val |= MV88E6XXX_PORT_MAC_CTL_FORCE_EEE;
+ break;
+ default:
+ break;
+ }
+
+ dev_dbg(chip->dev, "p%d: %s eee %sable\n", port,
+ val & MV88E6XXX_PORT_MAC_CTL_FORCE_EEE ? "Force" : "Unforce",
+ val & MV88E6XXX_PORT_MAC_CTL_EEE ? "en" : "dis");
+
+ err = mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_MAC_CTL, &reg);
+ if (err < 0)
+ return err;
+
+ reg &= ~(MV88E6XXX_PORT_MAC_CTL_EEE | MV88E6XXX_PORT_MAC_CTL_FORCE_EEE);
+ reg |= val;
+
+ return mv88e6xxx_port_write(chip, port, MV88E6XXX_PORT_MAC_CTL, reg);
+}
+
static int mv88e6xxx_port_set_cmode(struct mv88e6xxx_chip *chip, int port,
phy_interface_t mode, bool force)
{
@@ -629,7 +659,6 @@ int mv88e6393x_port_set_cmode(struct mv88e6xxx_chip *chip, int port,
phy_interface_t mode)
{
int err;
- u16 reg;
if (port != 0 && port != 9 && port != 10)
return -EOPNOTSUPP;
@@ -648,13 +677,7 @@ int mv88e6393x_port_set_cmode(struct mv88e6xxx_chip *chip, int port,
}
/* mv88e6393x errata 4.5: EEE should be disabled on SERDES ports */
- err = mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_MAC_CTL, &reg);
- if (err)
- return err;
-
- reg &= ~MV88E6XXX_PORT_MAC_CTL_EEE;
- reg |= MV88E6XXX_PORT_MAC_CTL_FORCE_EEE;
- err = mv88e6xxx_port_write(chip, port, MV88E6XXX_PORT_MAC_CTL, reg);
+ err = mv88e6xxx_port_set_eee(chip, port, EEE_FORCE_DISABLE);
if (err)
return err;
diff --git a/drivers/net/dsa/mv88e6xxx/port.h b/drivers/net/dsa/mv88e6xxx/port.h
index 86deeb347cbc..1410677c415a 100644
--- a/drivers/net/dsa/mv88e6xxx/port.h
+++ b/drivers/net/dsa/mv88e6xxx/port.h
@@ -432,6 +432,7 @@ int mv88e6097_port_pause_limit(struct mv88e6xxx_chip *chip, int port, u8 in,
u8 out);
int mv88e6390_port_pause_limit(struct mv88e6xxx_chip *chip, int port, u8 in,
u8 out);
+int mv88e6xxx_port_set_eee(struct mv88e6xxx_chip *chip, int port, int eee);
int mv88e6341_port_set_cmode(struct mv88e6xxx_chip *chip, int port,
phy_interface_t mode);
int mv88e6390_port_set_cmode(struct mv88e6xxx_chip *chip, int port,
diff --git a/drivers/net/dsa/ocelot/felix.c b/drivers/net/dsa/ocelot/felix.c
index 61e95487732d..3aa66bf9eafc 100644
--- a/drivers/net/dsa/ocelot/felix.c
+++ b/drivers/net/dsa/ocelot/felix.c
@@ -1050,24 +1050,32 @@ static void felix_phylink_get_caps(struct dsa_switch *ds, int port,
config->supported_interfaces);
}
-static void felix_phylink_mac_config(struct dsa_switch *ds, int port,
+static void felix_phylink_mac_config(struct phylink_config *config,
unsigned int mode,
const struct phylink_link_state *state)
{
- struct ocelot *ocelot = ds->priv;
- struct felix *felix = ocelot_to_felix(ocelot);
+ struct dsa_port *dp = dsa_phylink_to_port(config);
+ struct ocelot *ocelot = dp->ds->priv;
+ int port = dp->index;
+ struct felix *felix;
+
+ felix = ocelot_to_felix(ocelot);
if (felix->info->phylink_mac_config)
felix->info->phylink_mac_config(ocelot, port, mode, state);
}
-static struct phylink_pcs *felix_phylink_mac_select_pcs(struct dsa_switch *ds,
- int port,
- phy_interface_t iface)
+static struct phylink_pcs *
+felix_phylink_mac_select_pcs(struct phylink_config *config,
+ phy_interface_t iface)
{
- struct ocelot *ocelot = ds->priv;
- struct felix *felix = ocelot_to_felix(ocelot);
+ struct dsa_port *dp = dsa_phylink_to_port(config);
+ struct ocelot *ocelot = dp->ds->priv;
struct phylink_pcs *pcs = NULL;
+ int port = dp->index;
+ struct felix *felix;
+
+ felix = ocelot_to_felix(ocelot);
if (felix->pcs && felix->pcs[port])
pcs = felix->pcs[port];
@@ -1075,11 +1083,13 @@ static struct phylink_pcs *felix_phylink_mac_select_pcs(struct dsa_switch *ds,
return pcs;
}
-static void felix_phylink_mac_link_down(struct dsa_switch *ds, int port,
+static void felix_phylink_mac_link_down(struct phylink_config *config,
unsigned int link_an_mode,
phy_interface_t interface)
{
- struct ocelot *ocelot = ds->priv;
+ struct dsa_port *dp = dsa_phylink_to_port(config);
+ struct ocelot *ocelot = dp->ds->priv;
+ int port = dp->index;
struct felix *felix;
felix = ocelot_to_felix(ocelot);
@@ -1088,15 +1098,19 @@ static void felix_phylink_mac_link_down(struct dsa_switch *ds, int port,
felix->info->quirks);
}
-static void felix_phylink_mac_link_up(struct dsa_switch *ds, int port,
+static void felix_phylink_mac_link_up(struct phylink_config *config,
+ struct phy_device *phydev,
unsigned int link_an_mode,
phy_interface_t interface,
- struct phy_device *phydev,
int speed, int duplex,
bool tx_pause, bool rx_pause)
{
- struct ocelot *ocelot = ds->priv;
- struct felix *felix = ocelot_to_felix(ocelot);
+ struct dsa_port *dp = dsa_phylink_to_port(config);
+ struct ocelot *ocelot = dp->ds->priv;
+ int port = dp->index;
+ struct felix *felix;
+
+ felix = ocelot_to_felix(ocelot);
ocelot_phylink_mac_link_up(ocelot, port, phydev, link_an_mode,
interface, speed, duplex, tx_pause, rx_pause,
@@ -2083,6 +2097,14 @@ static void felix_get_mm_stats(struct dsa_switch *ds, int port,
ocelot_port_get_mm_stats(ocelot, port, stats);
}
+const struct phylink_mac_ops felix_phylink_mac_ops = {
+ .mac_select_pcs = felix_phylink_mac_select_pcs,
+ .mac_config = felix_phylink_mac_config,
+ .mac_link_down = felix_phylink_mac_link_down,
+ .mac_link_up = felix_phylink_mac_link_up,
+};
+EXPORT_SYMBOL_GPL(felix_phylink_mac_ops);
+
const struct dsa_switch_ops felix_switch_ops = {
.get_tag_protocol = felix_get_tag_protocol,
.change_tag_protocol = felix_change_tag_protocol,
@@ -2104,10 +2126,6 @@ const struct dsa_switch_ops felix_switch_ops = {
.get_sset_count = felix_get_sset_count,
.get_ts_info = felix_get_ts_info,
.phylink_get_caps = felix_phylink_get_caps,
- .phylink_mac_config = felix_phylink_mac_config,
- .phylink_mac_select_pcs = felix_phylink_mac_select_pcs,
- .phylink_mac_link_down = felix_phylink_mac_link_down,
- .phylink_mac_link_up = felix_phylink_mac_link_up,
.port_enable = felix_port_enable,
.port_fast_age = felix_port_fast_age,
.port_fdb_dump = felix_fdb_dump,
diff --git a/drivers/net/dsa/ocelot/felix.h b/drivers/net/dsa/ocelot/felix.h
index dbf5872fe367..4d3489aaa659 100644
--- a/drivers/net/dsa/ocelot/felix.h
+++ b/drivers/net/dsa/ocelot/felix.h
@@ -82,6 +82,7 @@ struct felix_tag_proto_ops {
struct netlink_ext_ack *extack);
};
+extern const struct phylink_mac_ops felix_phylink_mac_ops;
extern const struct dsa_switch_ops felix_switch_ops;
/* DSA glue / front-end for struct ocelot */
diff --git a/drivers/net/dsa/ocelot/ocelot_ext.c b/drivers/net/dsa/ocelot/ocelot_ext.c
index 22187d831c4b..a8927dc7aca4 100644
--- a/drivers/net/dsa/ocelot/ocelot_ext.c
+++ b/drivers/net/dsa/ocelot/ocelot_ext.c
@@ -96,6 +96,7 @@ static int ocelot_ext_probe(struct platform_device *pdev)
ds->num_tx_queues = felix->info->num_tx_queues;
ds->ops = &felix_switch_ops;
+ ds->phylink_mac_ops = &felix_phylink_mac_ops;
ds->priv = ocelot;
felix->ds = ds;
felix->tag_proto = DSA_TAG_PROTO_OCELOT;
diff --git a/drivers/net/dsa/qca/ar9331.c b/drivers/net/dsa/qca/ar9331.c
index 8d9d271ac3af..968cb81088bf 100644
--- a/drivers/net/dsa/qca/ar9331.c
+++ b/drivers/net/dsa/qca/ar9331.c
@@ -523,28 +523,30 @@ static void ar9331_sw_phylink_get_caps(struct dsa_switch *ds, int port,
}
}
-static void ar9331_sw_phylink_mac_config(struct dsa_switch *ds, int port,
+static void ar9331_sw_phylink_mac_config(struct phylink_config *config,
unsigned int mode,
const struct phylink_link_state *state)
{
- struct ar9331_sw_priv *priv = ds->priv;
+ struct dsa_port *dp = dsa_phylink_to_port(config);
+ struct ar9331_sw_priv *priv = dp->ds->priv;
struct regmap *regmap = priv->regmap;
int ret;
- ret = regmap_update_bits(regmap, AR9331_SW_REG_PORT_STATUS(port),
+ ret = regmap_update_bits(regmap, AR9331_SW_REG_PORT_STATUS(dp->index),
AR9331_SW_PORT_STATUS_LINK_EN |
AR9331_SW_PORT_STATUS_FLOW_LINK_EN, 0);
if (ret)
dev_err_ratelimited(priv->dev, "%s: %i\n", __func__, ret);
}
-static void ar9331_sw_phylink_mac_link_down(struct dsa_switch *ds, int port,
+static void ar9331_sw_phylink_mac_link_down(struct phylink_config *config,
unsigned int mode,
phy_interface_t interface)
{
- struct ar9331_sw_priv *priv = ds->priv;
- struct ar9331_sw_port *p = &priv->port[port];
+ struct dsa_port *dp = dsa_phylink_to_port(config);
+ struct ar9331_sw_priv *priv = dp->ds->priv;
struct regmap *regmap = priv->regmap;
+ int port = dp->index;
int ret;
ret = regmap_update_bits(regmap, AR9331_SW_REG_PORT_STATUS(port),
@@ -552,23 +554,24 @@ static void ar9331_sw_phylink_mac_link_down(struct dsa_switch *ds, int port,
if (ret)
dev_err_ratelimited(priv->dev, "%s: %i\n", __func__, ret);
- cancel_delayed_work_sync(&p->mib_read);
+ cancel_delayed_work_sync(&priv->port[port].mib_read);
}
-static void ar9331_sw_phylink_mac_link_up(struct dsa_switch *ds, int port,
+static void ar9331_sw_phylink_mac_link_up(struct phylink_config *config,
+ struct phy_device *phydev,
unsigned int mode,
phy_interface_t interface,
- struct phy_device *phydev,
int speed, int duplex,
bool tx_pause, bool rx_pause)
{
- struct ar9331_sw_priv *priv = ds->priv;
- struct ar9331_sw_port *p = &priv->port[port];
+ struct dsa_port *dp = dsa_phylink_to_port(config);
+ struct ar9331_sw_priv *priv = dp->ds->priv;
struct regmap *regmap = priv->regmap;
+ int port = dp->index;
u32 val;
int ret;
- schedule_delayed_work(&p->mib_read, 0);
+ schedule_delayed_work(&priv->port[port].mib_read, 0);
val = AR9331_SW_PORT_STATUS_MAC_MASK;
switch (speed) {
@@ -684,14 +687,17 @@ static void ar9331_get_pause_stats(struct dsa_switch *ds, int port,
spin_unlock(&p->stats_lock);
}
+static const struct phylink_mac_ops ar9331_phylink_mac_ops = {
+ .mac_config = ar9331_sw_phylink_mac_config,
+ .mac_link_down = ar9331_sw_phylink_mac_link_down,
+ .mac_link_up = ar9331_sw_phylink_mac_link_up,
+};
+
static const struct dsa_switch_ops ar9331_sw_ops = {
.get_tag_protocol = ar9331_sw_get_tag_protocol,
.setup = ar9331_sw_setup,
.port_disable = ar9331_sw_port_disable,
.phylink_get_caps = ar9331_sw_phylink_get_caps,
- .phylink_mac_config = ar9331_sw_phylink_mac_config,
- .phylink_mac_link_down = ar9331_sw_phylink_mac_link_down,
- .phylink_mac_link_up = ar9331_sw_phylink_mac_link_up,
.get_stats64 = ar9331_get_stats64,
.get_pause_stats = ar9331_get_pause_stats,
};
@@ -1059,6 +1065,7 @@ static int ar9331_sw_probe(struct mdio_device *mdiodev)
ds->priv = priv;
priv->ops = ar9331_sw_ops;
ds->ops = &priv->ops;
+ ds->phylink_mac_ops = &ar9331_phylink_mac_ops;
dev_set_drvdata(&mdiodev->dev, priv);
for (i = 0; i < ARRAY_SIZE(priv->port); i++) {
diff --git a/drivers/net/dsa/qca/qca8k-8xxx.c b/drivers/net/dsa/qca/qca8k-8xxx.c
index 7a864329cb72..e82b84f6bcd3 100644
--- a/drivers/net/dsa/qca/qca8k-8xxx.c
+++ b/drivers/net/dsa/qca/qca8k-8xxx.c
@@ -1284,11 +1284,13 @@ qca8k_mac_config_setup_internal_delay(struct qca8k_priv *priv, int cpu_port_inde
}
static struct phylink_pcs *
-qca8k_phylink_mac_select_pcs(struct dsa_switch *ds, int port,
+qca8k_phylink_mac_select_pcs(struct phylink_config *config,
phy_interface_t interface)
{
- struct qca8k_priv *priv = ds->priv;
+ struct dsa_port *dp = dsa_phylink_to_port(config);
+ struct qca8k_priv *priv = dp->ds->priv;
struct phylink_pcs *pcs = NULL;
+ int port = dp->index;
switch (interface) {
case PHY_INTERFACE_MODE_SGMII:
@@ -1312,13 +1314,18 @@ qca8k_phylink_mac_select_pcs(struct dsa_switch *ds, int port,
}
static void
-qca8k_phylink_mac_config(struct dsa_switch *ds, int port, unsigned int mode,
+qca8k_phylink_mac_config(struct phylink_config *config, unsigned int mode,
const struct phylink_link_state *state)
{
- struct qca8k_priv *priv = ds->priv;
+ struct dsa_port *dp = dsa_phylink_to_port(config);
+ struct dsa_switch *ds = dp->ds;
+ struct qca8k_priv *priv;
+ int port = dp->index;
int cpu_port_index;
u32 reg;
+ priv = ds->priv;
+
switch (port) {
case 0: /* 1st CPU port */
if (state->interface != PHY_INTERFACE_MODE_RGMII &&
@@ -1427,20 +1434,24 @@ static void qca8k_phylink_get_caps(struct dsa_switch *ds, int port,
}
static void
-qca8k_phylink_mac_link_down(struct dsa_switch *ds, int port, unsigned int mode,
+qca8k_phylink_mac_link_down(struct phylink_config *config, unsigned int mode,
phy_interface_t interface)
{
- struct qca8k_priv *priv = ds->priv;
+ struct dsa_port *dp = dsa_phylink_to_port(config);
+ struct qca8k_priv *priv = dp->ds->priv;
- qca8k_port_set_status(priv, port, 0);
+ qca8k_port_set_status(priv, dp->index, 0);
}
static void
-qca8k_phylink_mac_link_up(struct dsa_switch *ds, int port, unsigned int mode,
- phy_interface_t interface, struct phy_device *phydev,
- int speed, int duplex, bool tx_pause, bool rx_pause)
+qca8k_phylink_mac_link_up(struct phylink_config *config,
+ struct phy_device *phydev, unsigned int mode,
+ phy_interface_t interface, int speed, int duplex,
+ bool tx_pause, bool rx_pause)
{
- struct qca8k_priv *priv = ds->priv;
+ struct dsa_port *dp = dsa_phylink_to_port(config);
+ struct qca8k_priv *priv = dp->ds->priv;
+ int port = dp->index;
u32 reg;
if (phylink_autoneg_inband(mode)) {
@@ -1464,10 +1475,10 @@ qca8k_phylink_mac_link_up(struct dsa_switch *ds, int port, unsigned int mode,
if (duplex == DUPLEX_FULL)
reg |= QCA8K_PORT_STATUS_DUPLEX;
- if (rx_pause || dsa_is_cpu_port(ds, port))
+ if (rx_pause || dsa_port_is_cpu(dp))
reg |= QCA8K_PORT_STATUS_RXFLOW;
- if (tx_pause || dsa_is_cpu_port(ds, port))
+ if (tx_pause || dsa_port_is_cpu(dp))
reg |= QCA8K_PORT_STATUS_TXFLOW;
}
@@ -1992,6 +2003,13 @@ qca8k_setup(struct dsa_switch *ds)
return 0;
}
+static const struct phylink_mac_ops qca8k_phylink_mac_ops = {
+ .mac_select_pcs = qca8k_phylink_mac_select_pcs,
+ .mac_config = qca8k_phylink_mac_config,
+ .mac_link_down = qca8k_phylink_mac_link_down,
+ .mac_link_up = qca8k_phylink_mac_link_up,
+};
+
static const struct dsa_switch_ops qca8k_switch_ops = {
.get_tag_protocol = qca8k_get_tag_protocol,
.setup = qca8k_setup,
@@ -2022,10 +2040,6 @@ static const struct dsa_switch_ops qca8k_switch_ops = {
.port_vlan_add = qca8k_port_vlan_add,
.port_vlan_del = qca8k_port_vlan_del,
.phylink_get_caps = qca8k_phylink_get_caps,
- .phylink_mac_select_pcs = qca8k_phylink_mac_select_pcs,
- .phylink_mac_config = qca8k_phylink_mac_config,
- .phylink_mac_link_down = qca8k_phylink_mac_link_down,
- .phylink_mac_link_up = qca8k_phylink_mac_link_up,
.get_phy_flags = qca8k_get_phy_flags,
.port_lag_join = qca8k_port_lag_join,
.port_lag_leave = qca8k_port_lag_leave,
@@ -2092,6 +2106,7 @@ qca8k_sw_probe(struct mdio_device *mdiodev)
priv->ds->num_ports = QCA8K_NUM_PORTS;
priv->ds->priv = priv;
priv->ds->ops = &qca8k_switch_ops;
+ priv->ds->phylink_mac_ops = &qca8k_phylink_mac_ops;
mutex_init(&priv->reg_mutex);
dev_set_drvdata(&mdiodev->dev, priv);
diff --git a/drivers/net/dsa/rzn1_a5psw.c b/drivers/net/dsa/rzn1_a5psw.c
index 10092ea85e46..92e032972b34 100644
--- a/drivers/net/dsa/rzn1_a5psw.c
+++ b/drivers/net/dsa/rzn1_a5psw.c
@@ -239,23 +239,31 @@ static void a5psw_phylink_get_caps(struct dsa_switch *ds, int port,
}
static struct phylink_pcs *
-a5psw_phylink_mac_select_pcs(struct dsa_switch *ds, int port,
+a5psw_phylink_mac_select_pcs(struct phylink_config *config,
phy_interface_t interface)
{
- struct dsa_port *dp = dsa_to_port(ds, port);
- struct a5psw *a5psw = ds->priv;
+ struct dsa_port *dp = dsa_phylink_to_port(config);
+ struct a5psw *a5psw = dp->ds->priv;
- if (!dsa_port_is_cpu(dp) && a5psw->pcs[port])
- return a5psw->pcs[port];
+ if (dsa_port_is_cpu(dp))
+ return NULL;
- return NULL;
+ return a5psw->pcs[dp->index];
+}
+
+static void a5psw_phylink_mac_config(struct phylink_config *config,
+ unsigned int mode,
+ const struct phylink_link_state *state)
+{
}
-static void a5psw_phylink_mac_link_down(struct dsa_switch *ds, int port,
+static void a5psw_phylink_mac_link_down(struct phylink_config *config,
unsigned int mode,
phy_interface_t interface)
{
- struct a5psw *a5psw = ds->priv;
+ struct dsa_port *dp = dsa_phylink_to_port(config);
+ struct a5psw *a5psw = dp->ds->priv;
+ int port = dp->index;
u32 cmd_cfg;
cmd_cfg = a5psw_reg_readl(a5psw, A5PSW_CMD_CFG(port));
@@ -263,15 +271,17 @@ static void a5psw_phylink_mac_link_down(struct dsa_switch *ds, int port,
a5psw_reg_writel(a5psw, A5PSW_CMD_CFG(port), cmd_cfg);
}
-static void a5psw_phylink_mac_link_up(struct dsa_switch *ds, int port,
+static void a5psw_phylink_mac_link_up(struct phylink_config *config,
+ struct phy_device *phydev,
unsigned int mode,
phy_interface_t interface,
- struct phy_device *phydev, int speed,
- int duplex, bool tx_pause, bool rx_pause)
+ int speed, int duplex, bool tx_pause,
+ bool rx_pause)
{
u32 cmd_cfg = A5PSW_CMD_CFG_RX_ENA | A5PSW_CMD_CFG_TX_ENA |
A5PSW_CMD_CFG_TX_CRC_APPEND;
- struct a5psw *a5psw = ds->priv;
+ struct dsa_port *dp = dsa_phylink_to_port(config);
+ struct a5psw *a5psw = dp->ds->priv;
if (speed == SPEED_1000)
cmd_cfg |= A5PSW_CMD_CFG_ETH_SPEED;
@@ -284,7 +294,7 @@ static void a5psw_phylink_mac_link_up(struct dsa_switch *ds, int port,
if (!rx_pause)
cmd_cfg &= ~A5PSW_CMD_CFG_PAUSE_IGNORE;
- a5psw_reg_writel(a5psw, A5PSW_CMD_CFG(port), cmd_cfg);
+ a5psw_reg_writel(a5psw, A5PSW_CMD_CFG(dp->index), cmd_cfg);
}
static int a5psw_set_ageing_time(struct dsa_switch *ds, unsigned int msecs)
@@ -992,15 +1002,19 @@ static int a5psw_setup(struct dsa_switch *ds)
return 0;
}
+static const struct phylink_mac_ops a5psw_phylink_mac_ops = {
+ .mac_select_pcs = a5psw_phylink_mac_select_pcs,
+ .mac_config = a5psw_phylink_mac_config,
+ .mac_link_down = a5psw_phylink_mac_link_down,
+ .mac_link_up = a5psw_phylink_mac_link_up,
+};
+
static const struct dsa_switch_ops a5psw_switch_ops = {
.get_tag_protocol = a5psw_get_tag_protocol,
.setup = a5psw_setup,
.port_disable = a5psw_port_disable,
.port_enable = a5psw_port_enable,
.phylink_get_caps = a5psw_phylink_get_caps,
- .phylink_mac_select_pcs = a5psw_phylink_mac_select_pcs,
- .phylink_mac_link_down = a5psw_phylink_mac_link_down,
- .phylink_mac_link_up = a5psw_phylink_mac_link_up,
.port_change_mtu = a5psw_port_change_mtu,
.port_max_mtu = a5psw_port_max_mtu,
.get_sset_count = a5psw_get_sset_count,
@@ -1252,6 +1266,7 @@ static int a5psw_probe(struct platform_device *pdev)
ds->dev = dev;
ds->num_ports = A5PSW_PORTS_NUM;
ds->ops = &a5psw_switch_ops;
+ ds->phylink_mac_ops = &a5psw_phylink_mac_ops;
ds->priv = a5psw;
ret = dsa_register_switch(ds);
diff --git a/drivers/net/dsa/sja1105/sja1105_main.c b/drivers/net/dsa/sja1105/sja1105_main.c
index 6646f7fb0f90..9591dab7e991 100644
--- a/drivers/net/dsa/sja1105/sja1105_main.c
+++ b/drivers/net/dsa/sja1105/sja1105_main.c
@@ -1358,10 +1358,11 @@ static int sja1105_adjust_port_config(struct sja1105_private *priv, int port,
}
static struct phylink_pcs *
-sja1105_mac_select_pcs(struct dsa_switch *ds, int port, phy_interface_t iface)
+sja1105_mac_select_pcs(struct phylink_config *config, phy_interface_t iface)
{
- struct sja1105_private *priv = ds->priv;
- struct dw_xpcs *xpcs = priv->xpcs[port];
+ struct dsa_port *dp = dsa_phylink_to_port(config);
+ struct sja1105_private *priv = dp->ds->priv;
+ struct dw_xpcs *xpcs = priv->xpcs[dp->index];
if (xpcs)
return &xpcs->pcs;
@@ -1369,21 +1370,31 @@ sja1105_mac_select_pcs(struct dsa_switch *ds, int port, phy_interface_t iface)
return NULL;
}
-static void sja1105_mac_link_down(struct dsa_switch *ds, int port,
+static void sja1105_mac_config(struct phylink_config *config,
+ unsigned int mode,
+ const struct phylink_link_state *state)
+{
+}
+
+static void sja1105_mac_link_down(struct phylink_config *config,
unsigned int mode,
phy_interface_t interface)
{
- sja1105_inhibit_tx(ds->priv, BIT(port), true);
+ struct dsa_port *dp = dsa_phylink_to_port(config);
+
+ sja1105_inhibit_tx(dp->ds->priv, BIT(dp->index), true);
}
-static void sja1105_mac_link_up(struct dsa_switch *ds, int port,
+static void sja1105_mac_link_up(struct phylink_config *config,
+ struct phy_device *phydev,
unsigned int mode,
phy_interface_t interface,
- struct phy_device *phydev,
int speed, int duplex,
bool tx_pause, bool rx_pause)
{
- struct sja1105_private *priv = ds->priv;
+ struct dsa_port *dp = dsa_phylink_to_port(config);
+ struct sja1105_private *priv = dp->ds->priv;
+ int port = dp->index;
sja1105_adjust_port_config(priv, port, speed);
@@ -3198,6 +3209,13 @@ static void sja1105_teardown(struct dsa_switch *ds)
sja1105_static_config_free(&priv->static_config);
}
+static const struct phylink_mac_ops sja1105_phylink_mac_ops = {
+ .mac_select_pcs = sja1105_mac_select_pcs,
+ .mac_config = sja1105_mac_config,
+ .mac_link_up = sja1105_mac_link_up,
+ .mac_link_down = sja1105_mac_link_down,
+};
+
static const struct dsa_switch_ops sja1105_switch_ops = {
.get_tag_protocol = sja1105_get_tag_protocol,
.connect_tag_protocol = sja1105_connect_tag_protocol,
@@ -3207,9 +3225,6 @@ static const struct dsa_switch_ops sja1105_switch_ops = {
.port_change_mtu = sja1105_change_mtu,
.port_max_mtu = sja1105_get_max_mtu,
.phylink_get_caps = sja1105_phylink_get_caps,
- .phylink_mac_select_pcs = sja1105_mac_select_pcs,
- .phylink_mac_link_up = sja1105_mac_link_up,
- .phylink_mac_link_down = sja1105_mac_link_down,
.get_strings = sja1105_get_strings,
.get_ethtool_stats = sja1105_get_ethtool_stats,
.get_sset_count = sja1105_get_sset_count,
@@ -3375,6 +3390,7 @@ static int sja1105_probe(struct spi_device *spi)
ds->dev = dev;
ds->num_ports = priv->info->num_ports;
ds->ops = &sja1105_switch_ops;
+ ds->phylink_mac_ops = &sja1105_phylink_mac_ops;
ds->priv = priv;
priv->ds = ds;
diff --git a/drivers/net/dsa/xrs700x/xrs700x.c b/drivers/net/dsa/xrs700x/xrs700x.c
index 96db032b478f..6605fa44bcf0 100644
--- a/drivers/net/dsa/xrs700x/xrs700x.c
+++ b/drivers/net/dsa/xrs700x/xrs700x.c
@@ -466,13 +466,25 @@ static void xrs700x_phylink_get_caps(struct dsa_switch *ds, int port,
}
}
-static void xrs700x_mac_link_up(struct dsa_switch *ds, int port,
- unsigned int mode, phy_interface_t interface,
+static void xrs700x_mac_config(struct phylink_config *config, unsigned int mode,
+ const struct phylink_link_state *state)
+{
+}
+
+static void xrs700x_mac_link_down(struct phylink_config *config,
+ unsigned int mode, phy_interface_t interface)
+{
+}
+
+static void xrs700x_mac_link_up(struct phylink_config *config,
struct phy_device *phydev,
+ unsigned int mode, phy_interface_t interface,
int speed, int duplex,
bool tx_pause, bool rx_pause)
{
- struct xrs700x *priv = ds->priv;
+ struct dsa_port *dp = dsa_phylink_to_port(config);
+ struct xrs700x *priv = dp->ds->priv;
+ int port = dp->index;
unsigned int val;
switch (speed) {
@@ -699,13 +711,18 @@ static int xrs700x_hsr_leave(struct dsa_switch *ds, int port,
return 0;
}
+static const struct phylink_mac_ops xrs700x_phylink_mac_ops = {
+ .mac_config = xrs700x_mac_config,
+ .mac_link_down = xrs700x_mac_link_down,
+ .mac_link_up = xrs700x_mac_link_up,
+};
+
static const struct dsa_switch_ops xrs700x_ops = {
.get_tag_protocol = xrs700x_get_tag_protocol,
.setup = xrs700x_setup,
.teardown = xrs700x_teardown,
.port_stp_state_set = xrs700x_port_stp_state_set,
.phylink_get_caps = xrs700x_phylink_get_caps,
- .phylink_mac_link_up = xrs700x_mac_link_up,
.get_strings = xrs700x_get_strings,
.get_sset_count = xrs700x_get_sset_count,
.get_ethtool_stats = xrs700x_get_ethtool_stats,
diff --git a/drivers/net/ethernet/broadcom/asp2/bcmasp_ethtool.c b/drivers/net/ethernet/broadcom/asp2/bcmasp_ethtool.c
index ce6a3d56fb23..3188ece72092 100644
--- a/drivers/net/ethernet/broadcom/asp2/bcmasp_ethtool.c
+++ b/drivers/net/ethernet/broadcom/asp2/bcmasp_ethtool.c
@@ -360,7 +360,6 @@ void bcmasp_eee_enable_set(struct bcmasp_intf *intf, bool enable)
umac_wl(intf, reg, UMC_EEE_CTRL);
intf->eee.eee_enabled = enable;
- intf->eee.eee_active = enable;
}
static int bcmasp_get_eee(struct net_device *dev, struct ethtool_eee *e)
@@ -371,8 +370,6 @@ static int bcmasp_get_eee(struct net_device *dev, struct ethtool_eee *e)
if (!dev->phydev)
return -ENODEV;
- e->eee_enabled = p->eee_enabled;
- e->eee_active = p->eee_active;
e->tx_lpi_enabled = p->tx_lpi_enabled;
e->tx_lpi_timer = umac_rl(intf, UMC_EEE_LPI_TIMER);
@@ -399,7 +396,6 @@ static int bcmasp_set_eee(struct net_device *dev, struct ethtool_eee *e)
}
umac_wl(intf, e->tx_lpi_timer, UMC_EEE_LPI_TIMER);
- intf->eee.eee_active = ret >= 0;
intf->eee.tx_lpi_enabled = e->tx_lpi_enabled;
bcmasp_eee_enable_set(intf, true);
}
diff --git a/drivers/net/ethernet/broadcom/asp2/bcmasp_intf.c b/drivers/net/ethernet/broadcom/asp2/bcmasp_intf.c
index 6ad1366270f7..e429876c7291 100644
--- a/drivers/net/ethernet/broadcom/asp2/bcmasp_intf.c
+++ b/drivers/net/ethernet/broadcom/asp2/bcmasp_intf.c
@@ -607,6 +607,7 @@ static void bcmasp_adj_link(struct net_device *dev)
struct phy_device *phydev = dev->phydev;
u32 cmd_bits = 0, reg;
int changed = 0;
+ bool active;
if (intf->old_link != phydev->link) {
changed = 1;
@@ -658,8 +659,8 @@ static void bcmasp_adj_link(struct net_device *dev)
reg |= cmd_bits;
umac_wl(intf, reg, UMC_CMD);
- intf->eee.eee_active = phy_init_eee(phydev, 0) >= 0;
- bcmasp_eee_enable_set(intf, intf->eee.eee_active);
+ active = phy_init_eee(phydev, 0) >= 0;
+ bcmasp_eee_enable_set(intf, active);
}
reg = rgmii_rl(intf, RGMII_OOB_CNTRL);
diff --git a/drivers/net/ethernet/broadcom/genet/bcmgenet.c b/drivers/net/ethernet/broadcom/genet/bcmgenet.c
index 2d7ae71287b1..fef1d2f53489 100644
--- a/drivers/net/ethernet/broadcom/genet/bcmgenet.c
+++ b/drivers/net/ethernet/broadcom/genet/bcmgenet.c
@@ -1313,7 +1313,6 @@ void bcmgenet_eee_enable_set(struct net_device *dev, bool enable,
}
priv->eee.eee_enabled = enable;
- priv->eee.eee_active = enable;
priv->eee.tx_lpi_enabled = tx_lpi_enabled;
}
@@ -1328,8 +1327,6 @@ static int bcmgenet_get_eee(struct net_device *dev, struct ethtool_eee *e)
if (!dev->phydev)
return -ENODEV;
- e->eee_enabled = p->eee_enabled;
- e->eee_active = p->eee_active;
e->tx_lpi_enabled = p->tx_lpi_enabled;
e->tx_lpi_timer = bcmgenet_umac_readl(priv, UMAC_EEE_LPI_TIMER);
@@ -1340,6 +1337,7 @@ static int bcmgenet_set_eee(struct net_device *dev, struct ethtool_eee *e)
{
struct bcmgenet_priv *priv = netdev_priv(dev);
struct ethtool_eee *p = &priv->eee;
+ bool active;
if (GENET_IS_V1(priv))
return -EOPNOTSUPP;
@@ -1352,9 +1350,9 @@ static int bcmgenet_set_eee(struct net_device *dev, struct ethtool_eee *e)
if (!p->eee_enabled) {
bcmgenet_eee_enable_set(dev, false, false);
} else {
- p->eee_active = phy_init_eee(dev->phydev, false) >= 0;
+ active = phy_init_eee(dev->phydev, false) >= 0;
bcmgenet_umac_writel(priv, e->tx_lpi_timer, UMAC_EEE_LPI_TIMER);
- bcmgenet_eee_enable_set(dev, p->eee_active, e->tx_lpi_enabled);
+ bcmgenet_eee_enable_set(dev, active, e->tx_lpi_enabled);
}
return phy_ethtool_set_eee(dev->phydev, e);
diff --git a/drivers/net/ethernet/broadcom/genet/bcmmii.c b/drivers/net/ethernet/broadcom/genet/bcmmii.c
index 97ea76d443ab..cbbe004621bc 100644
--- a/drivers/net/ethernet/broadcom/genet/bcmmii.c
+++ b/drivers/net/ethernet/broadcom/genet/bcmmii.c
@@ -30,6 +30,7 @@ static void bcmgenet_mac_config(struct net_device *dev)
struct bcmgenet_priv *priv = netdev_priv(dev);
struct phy_device *phydev = dev->phydev;
u32 reg, cmd_bits = 0;
+ bool active;
/* speed */
if (phydev->speed == SPEED_1000)
@@ -88,9 +89,9 @@ static void bcmgenet_mac_config(struct net_device *dev)
}
bcmgenet_umac_writel(priv, reg, UMAC_CMD);
- priv->eee.eee_active = phy_init_eee(phydev, 0) >= 0;
+ active = phy_init_eee(phydev, 0) >= 0;
bcmgenet_eee_enable_set(dev,
- priv->eee.eee_enabled && priv->eee.eee_active,
+ priv->eee.eee_enabled && active,
priv->eee.tx_lpi_enabled);
}
diff --git a/drivers/net/ethernet/cadence/macb_main.c b/drivers/net/ethernet/cadence/macb_main.c
index 898debfd4db3..decfbf91e83d 100644
--- a/drivers/net/ethernet/cadence/macb_main.c
+++ b/drivers/net/ethernet/cadence/macb_main.c
@@ -687,7 +687,7 @@ static void macb_mac_config(struct phylink_config *config, unsigned int mode,
u32 pcsctrl, old_pcsctrl;
old_pcsctrl = gem_readl(bp, PCSCNTRL);
- if (mode == MLO_AN_FIXED)
+ if (phylink_mode_fixed(mode))
pcsctrl = old_pcsctrl & ~GEM_BIT(PCSAUTONEG);
else
pcsctrl = old_pcsctrl | GEM_BIT(PCSAUTONEG);
diff --git a/drivers/net/ethernet/freescale/fec_main.c b/drivers/net/ethernet/freescale/fec_main.c
index 432523b2c789..51828d060c13 100644
--- a/drivers/net/ethernet/freescale/fec_main.c
+++ b/drivers/net/ethernet/freescale/fec_main.c
@@ -3139,8 +3139,6 @@ static int fec_enet_eee_mode_set(struct net_device *ndev, bool enable)
}
p->tx_lpi_enabled = enable;
- p->eee_enabled = enable;
- p->eee_active = enable;
writel(sleep_cycle, fep->hwp + FEC_LPI_SLEEP);
writel(wake_cycle, fep->hwp + FEC_LPI_WAKE);
@@ -3160,8 +3158,6 @@ fec_enet_get_eee(struct net_device *ndev, struct ethtool_eee *edata)
if (!netif_running(ndev))
return -ENETDOWN;
- edata->eee_enabled = p->eee_enabled;
- edata->eee_active = p->eee_active;
edata->tx_lpi_timer = p->tx_lpi_timer;
edata->tx_lpi_enabled = p->tx_lpi_enabled;
diff --git a/drivers/net/ethernet/marvell/Kconfig b/drivers/net/ethernet/marvell/Kconfig
index 884d64114bff..26c6b93fa009 100644
--- a/drivers/net/ethernet/marvell/Kconfig
+++ b/drivers/net/ethernet/marvell/Kconfig
@@ -60,6 +60,7 @@ config MVNETA_BM_ENABLE
config MVNETA
tristate "Marvell Armada 370/38x/XP/37xx network interface support"
depends on ARCH_MVEBU || COMPILE_TEST
+ select MVGMAC
select MVMDIO
select PHYLINK
select PAGE_POOL
@@ -73,6 +74,9 @@ config MVNETA
driver, which should be used for the older Marvell SoCs
(Dove, Orion, Discovery, Kirkwood).
+config MVGMAC
+ tristate
+
config MVNETA_BM
tristate
depends on !64BIT
diff --git a/drivers/net/ethernet/marvell/Makefile b/drivers/net/ethernet/marvell/Makefile
index ceba4aa4f026..d400984f83b7 100644
--- a/drivers/net/ethernet/marvell/Makefile
+++ b/drivers/net/ethernet/marvell/Makefile
@@ -7,6 +7,7 @@ obj-$(CONFIG_MVMDIO) += mvmdio.o
obj-$(CONFIG_MV643XX_ETH) += mv643xx_eth.o
obj-$(CONFIG_MVNETA_BM) += mvneta_bm.o
obj-$(CONFIG_MVNETA) += mvneta.o
+obj-$(CONFIG_MVGMAC) += mvgmac.o
obj-$(CONFIG_MVPP2) += mvpp2/
obj-$(CONFIG_PXA168_ETH) += pxa168_eth.o
obj-$(CONFIG_SKGE) += skge.o
diff --git a/drivers/net/ethernet/marvell/mvgmac.c b/drivers/net/ethernet/marvell/mvgmac.c
new file mode 100644
index 000000000000..b5bf4f783396
--- /dev/null
+++ b/drivers/net/ethernet/marvell/mvgmac.c
@@ -0,0 +1,461 @@
+/*
+ * GMAC driver for Marvell network interfaces on Armada SoCs.
+ *
+ * Copyright (C) 2012 Marvell
+ *
+ * Rami Rosen <rosenr@marvell.com>
+ * Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
+ *
+ * Split from mvneta and mvpp2 by Russell King.
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+#include <linux/bitfield.h>
+#include <linux/export.h>
+#include <linux/io.h>
+#include <linux/phylink.h>
+
+#include "mvgmac.h"
+
+enum {
+ /* N = Neta, 21 = PPV2.1, 22 = PPV2.2 */
+ /* N: 0-14 21: 0,2-15 22: 0-14 */
+ GMAC_CTRL0_REG = 0x00,
+ GMAC_CTRL0_PORT_ENABLE = BIT(0),
+ GMAC_CTRL0_PORT_1000BASE_X = BIT(1),
+ GMAC_CTRL0_MAX_RX_SIZE_SHIFT = 2,
+ GMAC_CTRL0_MAX_RX_SIZE_MASK = 0x1fff << GMAC_CTRL0_MAX_RX_SIZE_SHIFT,
+ GMAC_CTRL0_MIB_CNTR_ENABLE = BIT(15),
+
+ /* N: 21: 1,5,6 22: */
+ GMAC_CTRL1_REG = 0x04,
+ GMAC_CTRL1_PERIODIC_XON_ENABLE = BIT(1),
+ GMAC_CTRL1_GMII_LB_ENABLE = BIT(5),
+ GMAC_CTRL1_PCS_LB_ENABLE = BIT(6),
+
+ /* ALL: 0,3,4,6 */
+ GMAC_CTRL2_REG = 0x08,
+ GMAC_CTRL2_INBAND_AN_SGMII = BIT(0),
+ GMAC_CTRL2_PCS_ENABLE = BIT(3),
+ GMAC_CTRL2_PORT_RGMII = BIT(4),
+ GMAC_CTRL2_PORT_RESET = BIT(6),
+
+ /* N:0-9,11-13 21:0,1,5-7,9,12,13 22:0-7,9-15 */
+ /* 22 bit 2 - EN_PCS_AN */
+ GMAC_ANEG_REG = 0x0c,
+ GMAC_ANEG_FORCE_LINK_DOWN = BIT(0),
+ GMAC_ANEG_FORCE_LINK_PASS = BIT(1),
+ GMAC_ANEG_INBAND_AN_ENABLE = BIT(2),
+ GMAC_ANEG_AN_BYPASS_ENABLE = BIT(3),
+ GMAC_ANEG_INBAND_RESTART_AN = BIT(4),
+ GMAC_ANEG_MII_SPEED = BIT(5),
+ GMAC_ANEG_GMII_SPEED = BIT(6),
+ GMAC_ANEG_AN_SPEED_ENABLE = BIT(7),
+ GMAC_ANEG_CONFIG_FLOW_CTRL = BIT(8),
+ GMAC_ANEG_ADVERT_SYM_FLOW_CTRL = BIT(9),
+ GMAC_ANEG_ADVERT_ASYM_FLOW_CTRL = BIT(10),
+ GMAC_ANEG_AN_FLOW_CTRL_ENABLE = BIT(11),
+ GMAC_ANEG_FULL_DUPLEX = BIT(12),
+ GMAC_ANEG_AN_DUPLEX_ENABLE = BIT(13),
+ /* pp22: bit 14 - phy mode */
+ /* pp22: bit 15 - choose sample tx config */
+
+ GMAC_STATUS_REG = 0x10,
+ MVGMAC_LINK_UP = BIT(0),
+ MVGMAC_SPEED_1000 = BIT(1),
+ MVGMAC_SPEED_100 = BIT(2),
+ MVGMAC_FULL_DUPLEX = BIT(3),
+ MVGMAC_RX_FLOW_CTRL_ENABLE = BIT(4),
+ MVGMAC_TX_FLOW_CTRL_ENABLE = BIT(5),
+ MVGMAC_RX_FLOW_CTRL_ACTIVE = BIT(6),
+ MVGMAC_TX_FLOW_CTRL_ACTIVE = BIT(7),
+ MVGMAC_AN_COMPLETE = BIT(11),
+ MVGMAC_SYNC_OK = BIT(14),
+
+ /* N: 21:6-13 22: */
+ GMAC_FIFO_CFG1_REG = 0x1c,
+ GMAC_FIFO_CFG1_TX_MIN_TH_SHIFT = 6,
+ GMAC_FIFO_CFG1_TX_MIN_TH_MASK = 0x7f <<
+ GMAC_FIFO_CFG1_TX_MIN_TH_SHIFT,
+
+ /* N:1 21: 22:0,3-7 */
+ GMAC_CTRL4_REG = 0x90,
+ GMAC_CTRL4_EXT_PIN_GMII_SEL = BIT(0),
+ GMAC_CTRL4_SHORT_PREAMBLE_ENABLE = BIT(1),
+ GMAC_CTRL4_FC_RX_ENABLE = BIT(3),
+ GMAC_CTRL4_FC_TX_ENABLE = BIT(4),
+ GMAC_CTRL4_DP_CLK_SEL = BIT(5),
+ GMAC_CTRL4_SYNC_BYPASS = BIT(6),
+ GMAC_CTRL4_QSGMII_BYPASS = BIT(7),
+
+ GMAC_LPI_CTRL0_REG = 0xc0,
+ GMAC_LPI_CTRL0_TS = 0xff << 8,
+ GMAC_LPI_CTRL1_REG = 0xc4,
+ GMAC_LPI_CTRL1_REQ_EN = BIT(0),
+ GMAC_LPI_CTRL1_TW = 0xfff << 4,
+ GMAC_LPI_CTRL2_REG = 0xc8,
+ GMAC_LPI_STATUS_REG = 0xcc,
+ GMAC_LPI_CNTR_REG = 0xd0,
+};
+
+#define insert(var, mask, val) ({ \
+ u32 __mask = mask; \
+ ((var) & ~(__mask)) | (((val) << __ffs(__mask)) & (__mask)); \
+})
+
+static void mvgmac_modify(void __iomem *reg, u32 mask, u32 val)
+{
+ u32 v;
+
+ val &= mask;
+ v = readl_relaxed(reg) & ~mask;
+ writel_relaxed(v | val, reg);
+}
+
+#define mvgmac_set(reg, val) mvgmac_modify(reg, val, val)
+#define mvgmac_clear(reg, val) mvgmac_modify(reg, val, 0)
+
+/* Change maximum receive size of the port. */
+void mvgmac_set_max_rx_size(struct mvgmac *gmac, size_t max_rx_size)
+{
+ int size = (max_rx_size - MARVELL_HEADER_SIZE) / 2;
+
+ mvgmac_modify(gmac->base + GMAC_CTRL0_REG,
+ GMAC_CTRL0_MAX_RX_SIZE_MASK,
+ FIELD_PREP(GMAC_CTRL0_MAX_RX_SIZE_MASK, size));
+}
+EXPORT_SYMBOL_GPL(mvgmac_set_max_rx_size);
+
+/* Enable the port by setting the port enable bit of the MAC control register */
+void mvgmac_port_enable(struct mvgmac *gmac)
+{
+ mvgmac_set(gmac->base + GMAC_CTRL0_REG,
+ GMAC_CTRL0_PORT_ENABLE | GMAC_CTRL0_MIB_CNTR_ENABLE);
+}
+EXPORT_SYMBOL_GPL(mvgmac_port_enable);
+
+/* Disable the port */
+void mvgmac_port_disable(struct mvgmac *gmac)
+{
+ mvgmac_clear(gmac->base + GMAC_CTRL0_REG, GMAC_CTRL0_PORT_ENABLE);
+}
+EXPORT_SYMBOL_GPL(mvgmac_port_disable);
+
+int mvgmac_configure(struct mvgmac *gmac, phy_interface_t phy_mode)
+{
+ u32 ctrl4;
+
+ switch (phy_mode) {
+ case PHY_INTERFACE_MODE_QSGMII:
+ ctrl4 = 0;
+ break;
+
+ case PHY_INTERFACE_MODE_SGMII:
+ ctrl4 = GMAC_CTRL4_QSGMII_BYPASS;
+ break;
+
+ case PHY_INTERFACE_MODE_RGMII:
+ case PHY_INTERFACE_MODE_RGMII_ID:
+ case PHY_INTERFACE_MODE_RGMII_RXID:
+ case PHY_INTERFACE_MODE_RGMII_TXID:
+ ctrl4 = GMAC_CTRL4_QSGMII_BYPASS |
+ GMAC_CTRL4_EXT_PIN_GMII_SEL;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ if (gmac->version == MVGMAC_PP21) {
+ /* Min. TX threshold must be less than minimum packet length */
+ mvgmac_modify(gmac->base + GMAC_FIFO_CFG1_REG,
+ GMAC_FIFO_CFG1_TX_MIN_TH_MASK,
+ FIELD_PREP(GMAC_FIFO_CFG1_TX_MIN_TH_MASK,
+ 64 - 4 - 2));
+ } else if (gmac->version == MVGMAC_PP22) {
+ mvgmac_modify(gmac->base + GMAC_CTRL4_REG,
+ GMAC_CTRL4_DP_CLK_SEL | GMAC_CTRL4_SYNC_BYPASS |
+ GMAC_CTRL4_QSGMII_BYPASS |
+ GMAC_CTRL4_EXT_PIN_GMII_SEL,
+ GMAC_CTRL4_SYNC_BYPASS | ctrl4);
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(mvgmac_configure);
+
+void mvgmac_link_unforce(struct mvgmac *gmac)
+{
+ mvgmac_clear(gmac->base + GMAC_ANEG_REG,
+ GMAC_ANEG_FORCE_LINK_PASS | GMAC_ANEG_FORCE_LINK_DOWN);
+}
+EXPORT_SYMBOL_GPL(mvgmac_link_unforce);
+
+void mvgmac_link_force_down(struct mvgmac *gmac)
+{
+ mvgmac_modify(gmac->base + GMAC_ANEG_REG,
+ GMAC_ANEG_FORCE_LINK_PASS | GMAC_ANEG_FORCE_LINK_DOWN,
+ GMAC_ANEG_FORCE_LINK_DOWN);
+}
+EXPORT_SYMBOL_GPL(mvgmac_link_force_down);
+
+void mvgmac_link_down(struct mvgmac *gmac, int mode)
+{
+ if (!phylink_autoneg_inband(mode))
+ mvgmac_link_force_down(gmac);
+}
+EXPORT_SYMBOL_GPL(mvgmac_link_down);
+
+void mvgmac_link_up(struct mvgmac *gmac, int mode, int speed, int duplex,
+ bool tx_pause, bool rx_pause)
+{
+ u32 an_mask, an_val, ctrl4;
+
+ if (gmac->version == MVGMAC_NETA) {
+ an_mask = GMAC_ANEG_CONFIG_FLOW_CTRL;
+ an_val = FIELD_PREP(GMAC_ANEG_CONFIG_FLOW_CTRL,
+ tx_pause || rx_pause);
+ } else {
+ an_mask = 0;
+ an_val = 0;
+ }
+
+ if (!phylink_autoneg_inband(mode)) {
+ an_mask |= GMAC_ANEG_FORCE_LINK_DOWN |
+ GMAC_ANEG_FORCE_LINK_PASS |
+ GMAC_ANEG_MII_SPEED | GMAC_ANEG_GMII_SPEED |
+ GMAC_ANEG_FULL_DUPLEX;
+ an_val |= GMAC_ANEG_FORCE_LINK_PASS;
+
+ if (speed == SPEED_1000 || speed == SPEED_2500)
+ an_val |= GMAC_ANEG_GMII_SPEED;
+ else if (speed == SPEED_100)
+ an_val |= GMAC_ANEG_MII_SPEED;
+
+ if (duplex == DUPLEX_FULL)
+ an_val |= GMAC_ANEG_FULL_DUPLEX;
+ }
+
+ if (an_mask)
+ mvgmac_modify(gmac->base + GMAC_ANEG_REG, an_mask, an_val);
+
+ if (gmac->version == MVGMAC_PP22) {
+ ctrl4 = readl_relaxed(gmac->base + GMAC_CTRL4_REG);
+ ctrl4 = insert(ctrl4, GMAC_CTRL4_FC_TX_ENABLE, tx_pause);
+ ctrl4 = insert(ctrl4, GMAC_CTRL4_FC_RX_ENABLE, rx_pause);
+ writel_relaxed(ctrl4, gmac->base + GMAC_CTRL4_REG);
+ }
+}
+EXPORT_SYMBOL_GPL(mvgmac_link_up);
+
+void mvgmac_link_change(struct mvgmac *gmac)
+{
+ u32 gmac_stat = readl_relaxed(gmac->base + GMAC_STATUS_REG);
+
+ phylink_pcs_change(&gmac->pcs, !!(gmac_stat & MVGMAC_LINK_UP));
+}
+EXPORT_SYMBOL_GPL(mvgmac_link_change);
+
+void mvgmac_pcs_get_state(struct phylink_pcs *pcs,
+ struct phylink_link_state *state)
+{
+ struct mvgmac *gmac = pcs_to_mvgmac(pcs);
+ u32 gmac_stat = readl_relaxed(gmac->base + GMAC_STATUS_REG);
+
+ if (gmac_stat & MVGMAC_SPEED_1000)
+ state->speed =
+ state->interface == PHY_INTERFACE_MODE_2500BASEX ?
+ SPEED_2500 : SPEED_1000;
+ else if (gmac_stat & MVGMAC_SPEED_100)
+ state->speed = SPEED_100;
+ else
+ state->speed = SPEED_10;
+
+ state->an_complete = !!(gmac_stat & MVGMAC_AN_COMPLETE);
+ state->link = !!(gmac_stat & MVGMAC_LINK_UP);
+ state->duplex = !!(gmac_stat & MVGMAC_FULL_DUPLEX);
+
+ if (gmac_stat & MVGMAC_RX_FLOW_CTRL_ENABLE)
+ state->pause |= MLO_PAUSE_RX;
+ if (gmac_stat & MVGMAC_TX_FLOW_CTRL_ENABLE)
+ state->pause |= MLO_PAUSE_TX;
+}
+EXPORT_SYMBOL_GPL(mvgmac_pcs_get_state);
+
+void mvgmac_pcs_an_restart(struct phylink_pcs *pcs)
+{
+ struct mvgmac *gmac = pcs_to_mvgmac(pcs);
+ u32 gmac_an = readl_relaxed(gmac->base + GMAC_ANEG_REG);
+
+ writel_relaxed(gmac_an | GMAC_ANEG_INBAND_RESTART_AN,
+ gmac->base + GMAC_ANEG_REG);
+ writel_relaxed(gmac_an & ~GMAC_ANEG_INBAND_RESTART_AN,
+ gmac->base + GMAC_ANEG_REG);
+}
+EXPORT_SYMBOL_GPL(mvgmac_pcs_an_restart);
+
+int mvgmac_pcs_config(struct phylink_pcs *pcs, unsigned int neg_mode,
+ phy_interface_t interface,
+ const unsigned long *advertising,
+ bool permit_pause_to_mac)
+{
+ struct mvgmac *gmac = pcs_to_mvgmac(pcs);
+ u32 mask, val, an, old_an, changed;
+
+ mask = GMAC_ANEG_INBAND_AN_ENABLE |
+ GMAC_ANEG_INBAND_RESTART_AN |
+ GMAC_ANEG_AN_SPEED_ENABLE |
+ GMAC_ANEG_AN_FLOW_CTRL_ENABLE |
+ GMAC_ANEG_AN_DUPLEX_ENABLE;
+
+ if (neg_mode == PHYLINK_PCS_NEG_INBAND_ENABLED) {
+ mask |= GMAC_ANEG_MII_SPEED |
+ GMAC_ANEG_GMII_SPEED |
+ GMAC_ANEG_FULL_DUPLEX;
+ val = GMAC_ANEG_INBAND_AN_ENABLE;
+
+ if (interface == PHY_INTERFACE_MODE_SGMII) {
+ /* SGMII mode receives the speed and duplex from PHY */
+ val |= GMAC_ANEG_AN_SPEED_ENABLE |
+ GMAC_ANEG_AN_DUPLEX_ENABLE;
+ } else {
+ /* 802.3z mode has fixed speed and duplex */
+ val |= GMAC_ANEG_GMII_SPEED |
+ GMAC_ANEG_FULL_DUPLEX;
+
+ /* The FLOW_CTRL_ENABLE bit selects either the hardware
+ * automatically or the GMAC_ANEG_FLOW_CTRL manually
+ * controls the GMAC pause mode.
+ */
+ if (permit_pause_to_mac)
+ val |= GMAC_ANEG_AN_FLOW_CTRL_ENABLE;
+
+ /* Update the advertisement bits */
+ mask |= GMAC_ANEG_ADVERT_SYM_FLOW_CTRL;
+ if (phylink_test(advertising, Pause))
+ val |= GMAC_ANEG_ADVERT_SYM_FLOW_CTRL;
+ if (gmac->version == MVGMAC_PP22) {
+ mask |= GMAC_ANEG_ADVERT_ASYM_FLOW_CTRL;
+ if (phylink_test(advertising, Asym_Pause))
+ val |= GMAC_ANEG_ADVERT_ASYM_FLOW_CTRL;
+ }
+ }
+ } else {
+ /* Phy or fixed speed - disable in-band AN modes */
+ val = 0;
+ }
+
+ old_an = an = readl_relaxed(gmac->base + GMAC_ANEG_REG);
+ an = (an & ~mask) | val;
+ changed = old_an ^ an;
+ if (changed)
+ writel_relaxed(an, gmac->base + GMAC_ANEG_REG);
+
+ /* We are only interested in the advertisement bits changing */
+ return !!(changed & (GMAC_ANEG_ADVERT_SYM_FLOW_CTRL |
+ GMAC_ANEG_ADVERT_ASYM_FLOW_CTRL));
+}
+EXPORT_SYMBOL_GPL(mvgmac_pcs_config);
+
+void mvgmac_config_mac(struct mvgmac *gmac, unsigned int mode,
+ const struct phylink_link_state *state)
+{
+ u32 new_ctrl0, gmac_ctrl0 = readl_relaxed(gmac->base + GMAC_CTRL0_REG);
+ u32 new_ctrl2, gmac_ctrl2 = readl_relaxed(gmac->base + GMAC_CTRL2_REG);
+ u32 new_ctrl4, gmac_ctrl4 = readl_relaxed(gmac->base + GMAC_CTRL4_REG);
+
+ new_ctrl0 = gmac_ctrl0 & ~GMAC_CTRL0_PORT_1000BASE_X;
+ new_ctrl2 = gmac_ctrl2 & ~(GMAC_CTRL2_INBAND_AN_SGMII |
+ GMAC_CTRL2_PORT_RESET);
+ new_ctrl4 = gmac_ctrl4;
+
+ /* Even though it might look weird, when we're configured in
+ * SGMII or QSGMII mode, the RGMII bit needs to be set.
+ */
+ new_ctrl2 |= GMAC_CTRL2_PORT_RGMII;
+
+ if (state->interface == PHY_INTERFACE_MODE_QSGMII ||
+ state->interface == PHY_INTERFACE_MODE_SGMII ||
+ phy_interface_mode_is_8023z(state->interface))
+ new_ctrl2 |= GMAC_CTRL2_PCS_ENABLE;
+
+ if (!phylink_autoneg_inband(mode)) {
+ /* Phy or fixed speed - nothing to do, leave the
+ * configured speed, duplex and flow control as-is.
+ */
+ } else if (state->interface == PHY_INTERFACE_MODE_SGMII) {
+ /* SGMII mode receives the state from the PHY */
+ new_ctrl2 |= GMAC_CTRL2_INBAND_AN_SGMII;
+ } else {
+ /* 802.3z negotiation - 1000BaseX */
+ new_ctrl0 |= GMAC_CTRL0_PORT_1000BASE_X;
+ }
+
+ if (gmac->version == MVGMAC_NETA) {
+ /* When at 2.5G, the link partner can send frames with
+ * shortened preambles.
+ */
+ new_ctrl4 &= ~GMAC_CTRL4_SHORT_PREAMBLE_ENABLE;
+ if (state->interface == PHY_INTERFACE_MODE_2500BASEX)
+ new_ctrl4 |= GMAC_CTRL4_SHORT_PREAMBLE_ENABLE;
+ }
+
+ if (new_ctrl0 != gmac_ctrl0)
+ writel_relaxed(new_ctrl0, gmac->base + GMAC_CTRL0_REG);
+ if (new_ctrl2 != gmac_ctrl2)
+ writel_relaxed(new_ctrl2, gmac->base + GMAC_CTRL2_REG);
+ if (new_ctrl4 != gmac_ctrl4)
+ writel_relaxed(new_ctrl4, gmac->base + GMAC_CTRL4_REG);
+
+ if (gmac_ctrl2 & GMAC_CTRL2_PORT_RESET) {
+ while ((readl_relaxed(gmac->base + GMAC_CTRL2_REG) &
+ GMAC_CTRL2_PORT_RESET) != 0)
+ continue;
+ }
+}
+EXPORT_SYMBOL_GPL(mvgmac_config_mac);
+
+void mvgmac_set_lpi_timers(struct mvgmac *gmac, unsigned int timer)
+{
+ u32 ts, tw, status;
+
+ status = readl_relaxed(gmac->base + GMAC_STATUS_REG);
+ if (status & MVGMAC_SPEED_1000) {
+ /* At 1G speeds, the timer resolution are 1us, and
+ * 802.3 says tw is 16.5us. Round up to 17us.
+ */
+ tw = 17;
+ ts = timer;
+ } else {
+ /* At 100M speeds, the timer resolutions are 10us, and
+ * 802.3 says tw is 30us.
+ */
+ tw = 3;
+ ts = DIV_ROUND_UP(timer, 10);
+ }
+
+ if (ts > 255)
+ ts = 255;
+
+ mvgmac_modify(gmac->base + GMAC_LPI_CTRL0_REG,
+ GMAC_LPI_CTRL0_TS,
+ FIELD_PREP(GMAC_LPI_CTRL0_TS, ts));
+
+ mvgmac_modify(gmac->base + GMAC_LPI_CTRL1_REG,
+ GMAC_LPI_CTRL1_TW,
+ FIELD_PREP(GMAC_LPI_CTRL1_TW, tw));
+}
+EXPORT_SYMBOL_GPL(mvgmac_set_lpi_timers);
+
+void mvgmac_set_eee(struct mvgmac *gmac, bool enable)
+{
+ mvgmac_modify(gmac->base + GMAC_LPI_CTRL1_REG,
+ GMAC_LPI_CTRL1_REQ_EN,
+ FIELD_PREP(GMAC_LPI_CTRL1_REQ_EN, enable));
+}
+EXPORT_SYMBOL_GPL(mvgmac_set_eee);
+
+MODULE_DESCRIPTION("Marvell GMAC driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/net/ethernet/marvell/mvgmac.h b/drivers/net/ethernet/marvell/mvgmac.h
new file mode 100644
index 000000000000..e423b484f369
--- /dev/null
+++ b/drivers/net/ethernet/marvell/mvgmac.h
@@ -0,0 +1,56 @@
+#ifndef MVGMAC_H
+#define MVGMAC_H
+
+#include <linux/phylink.h>
+
+/* The two bytes Marvell header. Either contains a special value used by
+ * Marvell switches when a specific hardware mode is enabled (not supported
+ * by this driver) or is filled automatically by zeroes on the RX side.
+ * Those two bytes being at the front of the Ethernet header, they allow
+ * to have the IP header aligned on a 4 bytes boundary automatically: the
+ * hardware skips those two bytes on its own.
+ */
+#define MARVELL_HEADER_SIZE 2
+
+enum {
+ /* GMAC version */
+ MVGMAC_NETA,
+ MVGMAC_PP21,
+ MVGMAC_PP22,
+};
+
+struct mvgmac {
+ void __iomem *base;
+ unsigned int version;
+ struct phylink_pcs pcs;
+};
+
+static inline struct mvgmac *pcs_to_mvgmac(struct phylink_pcs *pcs)
+{
+ return container_of(pcs, struct mvgmac, pcs);
+}
+
+void mvgmac_set_max_rx_size(struct mvgmac *gmac, size_t max_rx_size);
+void mvgmac_port_enable(struct mvgmac *gmac);
+void mvgmac_port_disable(struct mvgmac *gmac);
+int mvgmac_configure(struct mvgmac *gmac, phy_interface_t phy_mode);
+void mvgmac_link_unforce(struct mvgmac *gmac);
+void mvgmac_link_force_down(struct mvgmac *gmac);
+void mvgmac_link_down(struct mvgmac *gmac, int mode);
+void mvgmac_link_up(struct mvgmac *gmac, int mode, int speed, int duplex,
+ bool tx_pause, bool rx_pause);
+void mvgmac_link_change(struct mvgmac *gmac);
+void mvgmac_pcs_get_state(struct phylink_pcs *pcs,
+ struct phylink_link_state *state);
+void mvgmac_pcs_an_restart(struct phylink_pcs *pcs);
+int mvgmac_pcs_config(struct phylink_pcs *pcs, unsigned int neg_mode,
+ phy_interface_t interface,
+ const unsigned long *advertising,
+ bool permit_pause_to_mac);
+void mvgmac_config_mac(struct mvgmac *gmac, unsigned int mode,
+ const struct phylink_link_state *state);
+
+void mvgmac_set_lpi_timers(struct mvgmac *gmac, unsigned int timer);
+void mvgmac_set_eee(struct mvgmac *gmac, bool enable);
+
+#endif
diff --git a/drivers/net/ethernet/marvell/mvneta.c b/drivers/net/ethernet/marvell/mvneta.c
index a641b3534ca3..636effe4bdad 100644
--- a/drivers/net/ethernet/marvell/mvneta.c
+++ b/drivers/net/ethernet/marvell/mvneta.c
@@ -34,6 +34,7 @@
#include <linux/skbuff.h>
#include <net/hwbm.h>
#include "mvneta_bm.h"
+#include "mvgmac.h"
#include <net/ip.h>
#include <net/ipv6.h>
#include <net/tso.h>
@@ -196,43 +197,7 @@
#define MVNETA_RXQ_ENABLE_MASK 0x000000ff
#define MVETH_TXQ_TOKEN_COUNT_REG(q) (0x2700 + ((q) << 4))
#define MVETH_TXQ_TOKEN_CFG_REG(q) (0x2704 + ((q) << 4))
-#define MVNETA_GMAC_CTRL_0 0x2c00
-#define MVNETA_GMAC_MAX_RX_SIZE_SHIFT 2
-#define MVNETA_GMAC_MAX_RX_SIZE_MASK 0x7ffc
-#define MVNETA_GMAC0_PORT_1000BASE_X BIT(1)
-#define MVNETA_GMAC0_PORT_ENABLE BIT(0)
-#define MVNETA_GMAC_CTRL_2 0x2c08
-#define MVNETA_GMAC2_INBAND_AN_ENABLE BIT(0)
-#define MVNETA_GMAC2_PCS_ENABLE BIT(3)
-#define MVNETA_GMAC2_PORT_RGMII BIT(4)
-#define MVNETA_GMAC2_PORT_RESET BIT(6)
-#define MVNETA_GMAC_STATUS 0x2c10
-#define MVNETA_GMAC_LINK_UP BIT(0)
-#define MVNETA_GMAC_SPEED_1000 BIT(1)
-#define MVNETA_GMAC_SPEED_100 BIT(2)
-#define MVNETA_GMAC_FULL_DUPLEX BIT(3)
-#define MVNETA_GMAC_RX_FLOW_CTRL_ENABLE BIT(4)
-#define MVNETA_GMAC_TX_FLOW_CTRL_ENABLE BIT(5)
-#define MVNETA_GMAC_RX_FLOW_CTRL_ACTIVE BIT(6)
-#define MVNETA_GMAC_TX_FLOW_CTRL_ACTIVE BIT(7)
-#define MVNETA_GMAC_AN_COMPLETE BIT(11)
-#define MVNETA_GMAC_SYNC_OK BIT(14)
-#define MVNETA_GMAC_AUTONEG_CONFIG 0x2c0c
-#define MVNETA_GMAC_FORCE_LINK_DOWN BIT(0)
-#define MVNETA_GMAC_FORCE_LINK_PASS BIT(1)
-#define MVNETA_GMAC_INBAND_AN_ENABLE BIT(2)
-#define MVNETA_GMAC_AN_BYPASS_ENABLE BIT(3)
-#define MVNETA_GMAC_INBAND_RESTART_AN BIT(4)
-#define MVNETA_GMAC_CONFIG_MII_SPEED BIT(5)
-#define MVNETA_GMAC_CONFIG_GMII_SPEED BIT(6)
-#define MVNETA_GMAC_AN_SPEED_EN BIT(7)
-#define MVNETA_GMAC_CONFIG_FLOW_CTRL BIT(8)
-#define MVNETA_GMAC_ADVERT_SYM_FLOW_CTRL BIT(9)
-#define MVNETA_GMAC_AN_FLOW_CTRL_EN BIT(11)
-#define MVNETA_GMAC_CONFIG_FULL_DUPLEX BIT(12)
-#define MVNETA_GMAC_AN_DUPLEX_EN BIT(13)
-#define MVNETA_GMAC_CTRL_4 0x2c90
-#define MVNETA_GMAC4_SHORT_PREAMBLE_ENABLE BIT(1)
+#define MVNETA_GMAC_BASE 0x2c00
#define MVNETA_MIB_COUNTERS_BASE 0x3000
#define MVNETA_MIB_LATE_COLLISION 0x7c
#define MVNETA_DA_FILT_SPEC_MCAST 0x3400
@@ -283,12 +248,6 @@
(MVNETA_TXQ_BUCKET_REFILL_BASE_PERIOD_NS * \
MVNETA_TXQ_BUCKET_REFILL_PERIOD))
-#define MVNETA_LPI_CTRL_0 0x2cc0
-#define MVNETA_LPI_CTRL_1 0x2cc4
-#define MVNETA_LPI_REQUEST_ENABLE BIT(0)
-#define MVNETA_LPI_CTRL_2 0x2cc8
-#define MVNETA_LPI_STATUS 0x2ccc
-
#define MVNETA_CAUSE_TXQ_SENT_DESC_ALL_MASK 0xff
/* Descriptor ring Macros */
@@ -310,7 +269,7 @@
* boundary automatically: the hardware skips those two bytes on its
* own.
*/
-#define MVNETA_MH_SIZE 2
+#define MVNETA_MH_SIZE MARVELL_HEADER_SIZE
#define MVNETA_VLAN_TAG_LEN 4
@@ -533,18 +492,14 @@ struct mvneta_port {
unsigned int tx_csum_limit;
struct phylink *phylink;
struct phylink_config phylink_config;
- struct phylink_pcs phylink_pcs;
struct phy *comphy;
+ struct mvgmac gmac;
struct mvneta_bm *bm_priv;
struct mvneta_bm_pool *pool_long;
struct mvneta_bm_pool *pool_short;
int bm_win_id;
- bool eee_enabled;
- bool eee_active;
- bool tx_lpi_enabled;
-
u64 ethtool_stats[ARRAY_SIZE(mvneta_statistics)];
u32 indir[MVNETA_RSS_LU_TABLE_SIZE];
@@ -928,19 +883,6 @@ mvneta_rxq_next_desc_get(struct mvneta_rx_queue *rxq)
return rxq->descs + rx_desc;
}
-/* Change maximum receive size of the port. */
-static void mvneta_max_rx_size_set(struct mvneta_port *pp, int max_rx_size)
-{
- u32 val;
-
- val = mvreg_read(pp, MVNETA_GMAC_CTRL_0);
- val &= ~MVNETA_GMAC_MAX_RX_SIZE_MASK;
- val |= ((max_rx_size - MVNETA_MH_SIZE) / 2) <<
- MVNETA_GMAC_MAX_RX_SIZE_SHIFT;
- mvreg_write(pp, MVNETA_GMAC_CTRL_0, val);
-}
-
-
/* Set rx queue offset */
static void mvneta_rxq_offset_set(struct mvneta_port *pp,
struct mvneta_rx_queue *rxq,
@@ -1345,26 +1287,10 @@ static void mvneta_port_down(struct mvneta_port *pp)
udelay(200);
}
-/* Enable the port by setting the port enable bit of the MAC control register */
-static void mvneta_port_enable(struct mvneta_port *pp)
-{
- u32 val;
-
- /* Enable port */
- val = mvreg_read(pp, MVNETA_GMAC_CTRL_0);
- val |= MVNETA_GMAC0_PORT_ENABLE;
- mvreg_write(pp, MVNETA_GMAC_CTRL_0, val);
-}
-
/* Disable the port and wait for about 200 usec before retuning */
static void mvneta_port_disable(struct mvneta_port *pp)
{
- u32 val;
-
- /* Reset the Enable bit in the Serial Control Register */
- val = mvreg_read(pp, MVNETA_GMAC_CTRL_0);
- val &= ~MVNETA_GMAC0_PORT_ENABLE;
- mvreg_write(pp, MVNETA_GMAC_CTRL_0, val);
+ mvgmac_port_disable(&pp->gmac);
udelay(200);
}
@@ -3255,13 +3181,6 @@ static irqreturn_t mvneta_percpu_isr(int irq, void *dev_id)
return IRQ_HANDLED;
}
-static void mvneta_link_change(struct mvneta_port *pp)
-{
- u32 gmac_stat = mvreg_read(pp, MVNETA_GMAC_STATUS);
-
- phylink_mac_change(pp->phylink, !!(gmac_stat & MVNETA_GMAC_LINK_UP));
-}
-
/* NAPI handler
* Bits 0 - 7 of the causeRxTx register indicate that are transmitted
* packets on the corresponding TXQ (Bit 0 is for TX queue 1).
@@ -3291,7 +3210,7 @@ static int mvneta_poll(struct napi_struct *napi, int budget)
if (cause_misc & (MVNETA_CAUSE_PHY_STATUS_CHANGE |
MVNETA_CAUSE_LINK_CHANGE))
- mvneta_link_change(pp);
+ mvgmac_link_change(&pp->gmac);
}
/* Release Tx descriptors */
@@ -3748,11 +3667,11 @@ static void mvneta_start_dev(struct mvneta_port *pp)
WARN_ON(mvneta_config_interface(pp, pp->phy_interface));
- mvneta_max_rx_size_set(pp, pp->pkt_size);
+ mvgmac_set_max_rx_size(&pp->gmac, pp->pkt_size);
mvneta_txq_max_tx_size_set(pp, pp->pkt_size);
/* start the Rx/Tx activity */
- mvneta_port_enable(pp);
+ mvgmac_port_enable(&pp->gmac);
if (!pp->neta_armada3700) {
/* Enable polling on the port */
@@ -3954,52 +3873,27 @@ static int mvneta_set_mac_addr(struct net_device *dev, void *addr)
return 0;
}
-static struct mvneta_port *mvneta_pcs_to_port(struct phylink_pcs *pcs)
+static unsigned int mvneta_pcs_query_inband(struct phylink_pcs *pcs,
+ phy_interface_t interface)
{
- return container_of(pcs, struct mvneta_port, phylink_pcs);
-}
-
-static int mvneta_pcs_validate(struct phylink_pcs *pcs,
- unsigned long *supported,
- const struct phylink_link_state *state)
-{
- /* We only support QSGMII, SGMII, 802.3z and RGMII modes.
- * When in 802.3z mode, we must have AN enabled:
+ /* When operating in an 802.3z mode, we must have AN enabled:
* "Bit 2 Field InBandAnEn In-band Auto-Negotiation enable. ...
* When <PortType> = 1 (1000BASE-X) this field must be set to 1."
+ * Therefore, inband is "required".
*/
- if (phy_interface_mode_is_8023z(state->interface) &&
- !phylink_test(state->advertising, Autoneg))
- return -EINVAL;
+ if (phy_interface_mode_is_8023z(interface))
+ return LINK_INBAND_VALID | LINK_INBAND_REQUIRED;
- return 0;
-}
-
-static void mvneta_pcs_get_state(struct phylink_pcs *pcs,
- struct phylink_link_state *state)
-{
- struct mvneta_port *pp = mvneta_pcs_to_port(pcs);
- u32 gmac_stat;
-
- gmac_stat = mvreg_read(pp, MVNETA_GMAC_STATUS);
-
- if (gmac_stat & MVNETA_GMAC_SPEED_1000)
- state->speed =
- state->interface == PHY_INTERFACE_MODE_2500BASEX ?
- SPEED_2500 : SPEED_1000;
- else if (gmac_stat & MVNETA_GMAC_SPEED_100)
- state->speed = SPEED_100;
- else
- state->speed = SPEED_10;
-
- state->an_complete = !!(gmac_stat & MVNETA_GMAC_AN_COMPLETE);
- state->link = !!(gmac_stat & MVNETA_GMAC_LINK_UP);
- state->duplex = !!(gmac_stat & MVNETA_GMAC_FULL_DUPLEX);
+ /* QSGMII, SGMII and RGMII can be configured to use inband
+ * signalling of the AN result. Indicate these as "possible".
+ */
+ if (interface == PHY_INTERFACE_MODE_SGMII ||
+ interface == PHY_INTERFACE_MODE_QSGMII ||
+ phy_interface_mode_is_rgmii(interface))
+ return LINK_INBAND_VALID | LINK_INBAND_POSSIBLE;
- if (gmac_stat & MVNETA_GMAC_RX_FLOW_CTRL_ENABLE)
- state->pause |= MLO_PAUSE_RX;
- if (gmac_stat & MVNETA_GMAC_TX_FLOW_CTRL_ENABLE)
- state->pause |= MLO_PAUSE_TX;
+ /* For any other modes, indicate that inband is not supported. */
+ return LINK_INBAND_VALID;
}
static int mvneta_pcs_config(struct phylink_pcs *pcs, unsigned int neg_mode,
@@ -4007,73 +3901,18 @@ static int mvneta_pcs_config(struct phylink_pcs *pcs, unsigned int neg_mode,
const unsigned long *advertising,
bool permit_pause_to_mac)
{
- struct mvneta_port *pp = mvneta_pcs_to_port(pcs);
- u32 mask, val, an, old_an, changed;
-
- mask = MVNETA_GMAC_INBAND_AN_ENABLE |
- MVNETA_GMAC_INBAND_RESTART_AN |
- MVNETA_GMAC_AN_SPEED_EN |
- MVNETA_GMAC_AN_FLOW_CTRL_EN |
- MVNETA_GMAC_AN_DUPLEX_EN;
-
- if (neg_mode == PHYLINK_PCS_NEG_INBAND_ENABLED) {
- mask |= MVNETA_GMAC_CONFIG_MII_SPEED |
- MVNETA_GMAC_CONFIG_GMII_SPEED |
- MVNETA_GMAC_CONFIG_FULL_DUPLEX;
- val = MVNETA_GMAC_INBAND_AN_ENABLE;
-
- if (interface == PHY_INTERFACE_MODE_SGMII) {
- /* SGMII mode receives the speed and duplex from PHY */
- val |= MVNETA_GMAC_AN_SPEED_EN |
- MVNETA_GMAC_AN_DUPLEX_EN;
- } else {
- /* 802.3z mode has fixed speed and duplex */
- val |= MVNETA_GMAC_CONFIG_GMII_SPEED |
- MVNETA_GMAC_CONFIG_FULL_DUPLEX;
-
- /* The FLOW_CTRL_EN bit selects either the hardware
- * automatically or the CONFIG_FLOW_CTRL manually
- * controls the GMAC pause mode.
- */
- if (permit_pause_to_mac)
- val |= MVNETA_GMAC_AN_FLOW_CTRL_EN;
+ /* We should never see Asym_Pause set */
+ WARN_ON(phylink_test(advertising, Asym_Pause));
- /* Update the advertisement bits */
- mask |= MVNETA_GMAC_ADVERT_SYM_FLOW_CTRL;
- if (phylink_test(advertising, Pause))
- val |= MVNETA_GMAC_ADVERT_SYM_FLOW_CTRL;
- }
- } else {
- /* Phy or fixed speed - disable in-band AN modes */
- val = 0;
- }
-
- old_an = an = mvreg_read(pp, MVNETA_GMAC_AUTONEG_CONFIG);
- an = (an & ~mask) | val;
- changed = old_an ^ an;
- if (changed)
- mvreg_write(pp, MVNETA_GMAC_AUTONEG_CONFIG, an);
-
- /* We are only interested in the advertisement bits changing */
- return !!(changed & MVNETA_GMAC_ADVERT_SYM_FLOW_CTRL);
-}
-
-static void mvneta_pcs_an_restart(struct phylink_pcs *pcs)
-{
- struct mvneta_port *pp = mvneta_pcs_to_port(pcs);
- u32 gmac_an = mvreg_read(pp, MVNETA_GMAC_AUTONEG_CONFIG);
-
- mvreg_write(pp, MVNETA_GMAC_AUTONEG_CONFIG,
- gmac_an | MVNETA_GMAC_INBAND_RESTART_AN);
- mvreg_write(pp, MVNETA_GMAC_AUTONEG_CONFIG,
- gmac_an & ~MVNETA_GMAC_INBAND_RESTART_AN);
+ return mvgmac_pcs_config(pcs, neg_mode, interface, advertising,
+ permit_pause_to_mac);
}
static const struct phylink_pcs_ops mvneta_phylink_pcs_ops = {
- .pcs_validate = mvneta_pcs_validate,
- .pcs_get_state = mvneta_pcs_get_state,
+ .pcs_query_inband = mvneta_pcs_query_inband,
+ .pcs_get_state = mvgmac_pcs_get_state,
.pcs_config = mvneta_pcs_config,
- .pcs_an_restart = mvneta_pcs_an_restart,
+ .pcs_an_restart = mvgmac_pcs_an_restart,
};
static struct phylink_pcs *mvneta_mac_select_pcs(struct phylink_config *config,
@@ -4082,7 +3921,7 @@ static struct phylink_pcs *mvneta_mac_select_pcs(struct phylink_config *config,
struct net_device *ndev = to_net_dev(config->dev);
struct mvneta_port *pp = netdev_priv(ndev);
- return &pp->phylink_pcs;
+ return &pp->gmac.pcs;
}
static int mvneta_mac_prepare(struct phylink_config *config, unsigned int mode,
@@ -4090,7 +3929,6 @@ static int mvneta_mac_prepare(struct phylink_config *config, unsigned int mode,
{
struct net_device *ndev = to_net_dev(config->dev);
struct mvneta_port *pp = netdev_priv(ndev);
- u32 val;
if (pp->phy_interface != interface ||
phylink_autoneg_inband(mode)) {
@@ -4099,10 +3937,7 @@ static int mvneta_mac_prepare(struct phylink_config *config, unsigned int mode,
* can only change the port mode and in-band enable when the
* link is down.
*/
- val = mvreg_read(pp, MVNETA_GMAC_AUTONEG_CONFIG);
- val &= ~MVNETA_GMAC_FORCE_LINK_PASS;
- val |= MVNETA_GMAC_FORCE_LINK_DOWN;
- mvreg_write(pp, MVNETA_GMAC_AUTONEG_CONFIG, val);
+ mvgmac_link_force_down(&pp->gmac);
}
if (pp->phy_interface != interface)
@@ -4124,55 +3959,8 @@ static void mvneta_mac_config(struct phylink_config *config, unsigned int mode,
{
struct net_device *ndev = to_net_dev(config->dev);
struct mvneta_port *pp = netdev_priv(ndev);
- u32 new_ctrl0, gmac_ctrl0 = mvreg_read(pp, MVNETA_GMAC_CTRL_0);
- u32 new_ctrl2, gmac_ctrl2 = mvreg_read(pp, MVNETA_GMAC_CTRL_2);
- u32 new_ctrl4, gmac_ctrl4 = mvreg_read(pp, MVNETA_GMAC_CTRL_4);
- new_ctrl0 = gmac_ctrl0 & ~MVNETA_GMAC0_PORT_1000BASE_X;
- new_ctrl2 = gmac_ctrl2 & ~(MVNETA_GMAC2_INBAND_AN_ENABLE |
- MVNETA_GMAC2_PORT_RESET);
- new_ctrl4 = gmac_ctrl4 & ~(MVNETA_GMAC4_SHORT_PREAMBLE_ENABLE);
-
- /* Even though it might look weird, when we're configured in
- * SGMII or QSGMII mode, the RGMII bit needs to be set.
- */
- new_ctrl2 |= MVNETA_GMAC2_PORT_RGMII;
-
- if (state->interface == PHY_INTERFACE_MODE_QSGMII ||
- state->interface == PHY_INTERFACE_MODE_SGMII ||
- phy_interface_mode_is_8023z(state->interface))
- new_ctrl2 |= MVNETA_GMAC2_PCS_ENABLE;
-
- if (!phylink_autoneg_inband(mode)) {
- /* Phy or fixed speed - nothing to do, leave the
- * configured speed, duplex and flow control as-is.
- */
- } else if (state->interface == PHY_INTERFACE_MODE_SGMII) {
- /* SGMII mode receives the state from the PHY */
- new_ctrl2 |= MVNETA_GMAC2_INBAND_AN_ENABLE;
- } else {
- /* 802.3z negotiation - only 1000base-X */
- new_ctrl0 |= MVNETA_GMAC0_PORT_1000BASE_X;
- }
-
- /* When at 2.5G, the link partner can send frames with shortened
- * preambles.
- */
- if (state->interface == PHY_INTERFACE_MODE_2500BASEX)
- new_ctrl4 |= MVNETA_GMAC4_SHORT_PREAMBLE_ENABLE;
-
- if (new_ctrl0 != gmac_ctrl0)
- mvreg_write(pp, MVNETA_GMAC_CTRL_0, new_ctrl0);
- if (new_ctrl2 != gmac_ctrl2)
- mvreg_write(pp, MVNETA_GMAC_CTRL_2, new_ctrl2);
- if (new_ctrl4 != gmac_ctrl4)
- mvreg_write(pp, MVNETA_GMAC_CTRL_4, new_ctrl4);
-
- if (gmac_ctrl2 & MVNETA_GMAC2_PORT_RESET) {
- while ((mvreg_read(pp, MVNETA_GMAC_CTRL_2) &
- MVNETA_GMAC2_PORT_RESET) != 0)
- continue;
- }
+ mvgmac_config_mac(&pp->gmac, mode, state);
}
static int mvneta_mac_finish(struct phylink_config *config, unsigned int mode,
@@ -4180,7 +3968,7 @@ static int mvneta_mac_finish(struct phylink_config *config, unsigned int mode,
{
struct net_device *ndev = to_net_dev(config->dev);
struct mvneta_port *pp = netdev_priv(ndev);
- u32 val, clk;
+ u32 clk;
/* Disable 1ms clock if not in in-band mode */
if (!phylink_autoneg_inband(mode)) {
@@ -4196,45 +3984,20 @@ static int mvneta_mac_finish(struct phylink_config *config, unsigned int mode,
/* Allow the link to come up if in in-band mode, otherwise the
* link is forced via mac_link_down()/mac_link_up()
*/
- if (phylink_autoneg_inband(mode)) {
- val = mvreg_read(pp, MVNETA_GMAC_AUTONEG_CONFIG);
- val &= ~MVNETA_GMAC_FORCE_LINK_DOWN;
- mvreg_write(pp, MVNETA_GMAC_AUTONEG_CONFIG, val);
- }
+ if (phylink_autoneg_inband(mode))
+ mvgmac_link_unforce(&pp->gmac);
return 0;
}
-static void mvneta_set_eee(struct mvneta_port *pp, bool enable)
-{
- u32 lpi_ctl1;
-
- lpi_ctl1 = mvreg_read(pp, MVNETA_LPI_CTRL_1);
- if (enable)
- lpi_ctl1 |= MVNETA_LPI_REQUEST_ENABLE;
- else
- lpi_ctl1 &= ~MVNETA_LPI_REQUEST_ENABLE;
- mvreg_write(pp, MVNETA_LPI_CTRL_1, lpi_ctl1);
-}
-
static void mvneta_mac_link_down(struct phylink_config *config,
unsigned int mode, phy_interface_t interface)
{
struct net_device *ndev = to_net_dev(config->dev);
struct mvneta_port *pp = netdev_priv(ndev);
- u32 val;
mvneta_port_down(pp);
-
- if (!phylink_autoneg_inband(mode)) {
- val = mvreg_read(pp, MVNETA_GMAC_AUTONEG_CONFIG);
- val &= ~MVNETA_GMAC_FORCE_LINK_PASS;
- val |= MVNETA_GMAC_FORCE_LINK_DOWN;
- mvreg_write(pp, MVNETA_GMAC_AUTONEG_CONFIG, val);
- }
-
- pp->eee_active = false;
- mvneta_set_eee(pp, false);
+ mvgmac_link_down(&pp->gmac, mode);
}
static void mvneta_mac_link_up(struct phylink_config *config,
@@ -4245,49 +4008,25 @@ static void mvneta_mac_link_up(struct phylink_config *config,
{
struct net_device *ndev = to_net_dev(config->dev);
struct mvneta_port *pp = netdev_priv(ndev);
- u32 val;
- if (!phylink_autoneg_inband(mode)) {
- val = mvreg_read(pp, MVNETA_GMAC_AUTONEG_CONFIG);
- val &= ~(MVNETA_GMAC_FORCE_LINK_DOWN |
- MVNETA_GMAC_CONFIG_MII_SPEED |
- MVNETA_GMAC_CONFIG_GMII_SPEED |
- MVNETA_GMAC_CONFIG_FLOW_CTRL |
- MVNETA_GMAC_CONFIG_FULL_DUPLEX);
- val |= MVNETA_GMAC_FORCE_LINK_PASS;
-
- if (speed == SPEED_1000 || speed == SPEED_2500)
- val |= MVNETA_GMAC_CONFIG_GMII_SPEED;
- else if (speed == SPEED_100)
- val |= MVNETA_GMAC_CONFIG_MII_SPEED;
-
- if (duplex == DUPLEX_FULL)
- val |= MVNETA_GMAC_CONFIG_FULL_DUPLEX;
-
- if (tx_pause || rx_pause)
- val |= MVNETA_GMAC_CONFIG_FLOW_CTRL;
-
- mvreg_write(pp, MVNETA_GMAC_AUTONEG_CONFIG, val);
- } else {
- /* When inband doesn't cover flow control or flow control is
- * disabled, we need to manually configure it. This bit will
- * only have effect if MVNETA_GMAC_AN_FLOW_CTRL_EN is unset.
- */
- val = mvreg_read(pp, MVNETA_GMAC_AUTONEG_CONFIG);
- val &= ~MVNETA_GMAC_CONFIG_FLOW_CTRL;
+ mvgmac_link_up(&pp->gmac, mode, speed, duplex, tx_pause, rx_pause);
+ mvneta_port_up(pp);
+}
- if (tx_pause || rx_pause)
- val |= MVNETA_GMAC_CONFIG_FLOW_CTRL;
+static void mvneta_mac_disable_tx_lpi(struct phylink_config *config)
+{
+ struct mvneta_port *pp = netdev_priv(to_net_dev(config->dev));
- mvreg_write(pp, MVNETA_GMAC_AUTONEG_CONFIG, val);
- }
+ mvgmac_set_eee(&pp->gmac, false);
+}
- mvneta_port_up(pp);
+static void mvneta_mac_enable_tx_lpi(struct phylink_config *config, u32 timer)
+{
+ struct mvneta_port *pp = netdev_priv(to_net_dev(config->dev));
- if (phy && pp->eee_enabled) {
- pp->eee_active = phy_init_eee(phy, false) >= 0;
- mvneta_set_eee(pp, pp->eee_active && pp->tx_lpi_enabled);
- }
+ mvgmac_set_eee(&pp->gmac, false);
+ mvgmac_set_lpi_timers(&pp->gmac, timer);
+ mvgmac_set_eee(&pp->gmac, true);
}
static const struct phylink_mac_ops mvneta_phylink_ops = {
@@ -4297,6 +4036,8 @@ static const struct phylink_mac_ops mvneta_phylink_ops = {
.mac_finish = mvneta_mac_finish,
.mac_link_down = mvneta_mac_link_down,
.mac_link_up = mvneta_mac_link_up,
+ .mac_disable_tx_lpi = mvneta_mac_disable_tx_lpi,
+ .mac_enable_tx_lpi = mvneta_mac_enable_tx_lpi,
};
static int mvneta_mdio_probe(struct mvneta_port *pp)
@@ -5100,14 +4841,6 @@ static int mvneta_ethtool_get_eee(struct net_device *dev,
struct ethtool_eee *eee)
{
struct mvneta_port *pp = netdev_priv(dev);
- u32 lpi_ctl0;
-
- lpi_ctl0 = mvreg_read(pp, MVNETA_LPI_CTRL_0);
-
- eee->eee_enabled = pp->eee_enabled;
- eee->eee_active = pp->eee_active;
- eee->tx_lpi_enabled = pp->tx_lpi_enabled;
- eee->tx_lpi_timer = (lpi_ctl0) >> 8; // * scale;
return phylink_ethtool_get_eee(pp->phylink, eee);
}
@@ -5116,23 +4849,6 @@ static int mvneta_ethtool_set_eee(struct net_device *dev,
struct ethtool_eee *eee)
{
struct mvneta_port *pp = netdev_priv(dev);
- u32 lpi_ctl0;
-
- /* The Armada 37x documents do not give limits for this other than
- * it being an 8-bit register.
- */
- if (eee->tx_lpi_enabled && eee->tx_lpi_timer > 255)
- return -EINVAL;
-
- lpi_ctl0 = mvreg_read(pp, MVNETA_LPI_CTRL_0);
- lpi_ctl0 &= ~(0xff << 8);
- lpi_ctl0 |= eee->tx_lpi_timer << 8;
- mvreg_write(pp, MVNETA_LPI_CTRL_0, lpi_ctl0);
-
- pp->eee_enabled = eee->eee_enabled;
- pp->tx_lpi_enabled = eee->tx_lpi_enabled;
-
- mvneta_set_eee(pp, eee->tx_lpi_enabled && eee->eee_enabled);
return phylink_ethtool_set_eee(pp->phylink, eee);
}
@@ -5530,13 +5246,17 @@ static int mvneta_probe(struct platform_device *pdev)
if (!IS_ERR(pp->clk_bus))
clk_prepare_enable(pp->clk_bus);
- pp->phylink_pcs.ops = &mvneta_phylink_pcs_ops;
- pp->phylink_pcs.neg_mode = true;
+ pp->gmac.base = pp->base + MVNETA_GMAC_BASE;
+ pp->gmac.version = MVGMAC_NETA;
+ pp->gmac.pcs.ops = &mvneta_phylink_pcs_ops;
+ pp->gmac.pcs.neg_mode = true;
pp->phylink_config.dev = &dev->dev;
pp->phylink_config.type = PHYLINK_NETDEV;
pp->phylink_config.mac_capabilities = MAC_SYM_PAUSE | MAC_10 |
MAC_100 | MAC_1000FD | MAC_2500FD;
+ pp->phylink_config.lpi_capabilities = MAC_100FD | MAC_1000FD;
+ pp->phylink_config.lpi_timer_limit_us = 255;
phy_interface_set_rgmii(pp->phylink_config.supported_interfaces);
__set_bit(PHY_INTERFACE_MODE_QSGMII,
@@ -5564,6 +5284,11 @@ static int mvneta_probe(struct platform_device *pdev)
pp->phylink_config.supported_interfaces);
}
+ /* Setup EEE. Choose 250us idle. */
+ pp->phylink_config.eee.eee_enabled = true;
+ pp->phylink_config.eee.tx_lpi_enabled = true;
+ pp->phylink_config.eee.tx_lpi_timer = 250;
+
phylink = phylink_create(&pp->phylink_config, pdev->dev.fwnode,
phy_mode, &mvneta_phylink_ops);
if (IS_ERR(phylink)) {
diff --git a/drivers/net/ethernet/marvell/mvpp2/mvpp2.h b/drivers/net/ethernet/marvell/mvpp2/mvpp2.h
index e809f91c08fb..299b996ac5df 100644
--- a/drivers/net/ethernet/marvell/mvpp2/mvpp2.h
+++ b/drivers/net/ethernet/marvell/mvpp2/mvpp2.h
@@ -481,6 +481,11 @@
#define MVPP22_GMAC_INT_SUM_MASK 0xa4
#define MVPP22_GMAC_INT_SUM_MASK_LINK_STAT BIT(1)
#define MVPP22_GMAC_INT_SUM_MASK_PTP BIT(2)
+#define MVPP2_GMAC_LPI_CTRL0 0xc0
+#define MVPP2_GMAC_LPI_CTRL0_TS_MASK GENMASK(8, 8)
+#define MVPP2_GMAC_LPI_CTRL1 0xc4
+#define MVPP2_GMAC_LPI_CTRL1_REQ_EN BIT(0)
+#define MVPP2_GMAC_LPI_CTRL1_TW_MASK GENMASK(15, 4)
/* Per-port XGMAC registers. PPv2.2 and PPv2.3, only for GOP port 0,
* relative to port->base.
diff --git a/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c b/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c
index 23adf53c2aa1..97e38f61ac65 100644
--- a/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c
+++ b/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c
@@ -3434,12 +3434,13 @@ static void mvpp2_isr_handle_ptp(struct mvpp2_port *port)
mvpp2_isr_handle_ptp_queue(port, 1);
}
-static void mvpp2_isr_handle_link(struct mvpp2_port *port, bool link)
+static void mvpp2_isr_handle_link(struct mvpp2_port *port,
+ struct phylink_pcs *pcs, bool link)
{
struct net_device *dev = port->dev;
if (port->phylink) {
- phylink_mac_change(port->phylink, link);
+ phylink_pcs_change(pcs, link);
return;
}
@@ -3472,7 +3473,7 @@ static void mvpp2_isr_handle_xlg(struct mvpp2_port *port)
if (val & MVPP22_XLG_INT_STAT_LINK) {
val = readl(port->base + MVPP22_XLG_STATUS);
link = (val & MVPP22_XLG_STATUS_LINK_UP);
- mvpp2_isr_handle_link(port, link);
+ mvpp2_isr_handle_link(port, &port->pcs_xlg, link);
}
}
@@ -3488,7 +3489,7 @@ static void mvpp2_isr_handle_gmac_internal(struct mvpp2_port *port)
if (val & MVPP22_GMAC_INT_STAT_LINK) {
val = readl(port->base + MVPP2_GMAC_STATUS0);
link = (val & MVPP2_GMAC_STATUS0_LINK_UP);
- mvpp2_isr_handle_link(port, link);
+ mvpp2_isr_handle_link(port, &port->pcs_gmac, link);
}
}
}
@@ -5726,6 +5727,28 @@ static int mvpp2_ethtool_set_rxfh(struct net_device *dev,
return ret;
}
+static int mvpp2_ethtool_get_eee(struct net_device *dev,
+ struct ethtool_eee *eee)
+{
+ struct mvpp2_port *port = netdev_priv(dev);
+
+ if (!port->phylink)
+ return -EOPNOTSUPP;
+
+ return phylink_ethtool_get_eee(port->phylink, eee);
+}
+
+static int mvpp2_ethtool_set_eee(struct net_device *dev,
+ struct ethtool_eee *eee)
+{
+ struct mvpp2_port *port = netdev_priv(dev);
+
+ if (!port->phylink)
+ return -EOPNOTSUPP;
+
+ return phylink_ethtool_set_eee(port->phylink, eee);
+}
+
/* Device ops */
static const struct net_device_ops mvpp2_netdev_ops = {
@@ -5768,6 +5791,8 @@ static const struct ethtool_ops mvpp2_eth_tool_ops = {
.get_rxfh_indir_size = mvpp2_ethtool_get_rxfh_indir_size,
.get_rxfh = mvpp2_ethtool_get_rxfh,
.set_rxfh = mvpp2_ethtool_set_rxfh,
+ .get_eee = mvpp2_ethtool_get_eee,
+ .set_eee = mvpp2_ethtool_set_eee,
};
/* Used for PPv2.1, or PPv2.2 with the old Device Tree binding that
@@ -6190,19 +6215,26 @@ static const struct phylink_pcs_ops mvpp2_phylink_xlg_pcs_ops = {
.pcs_config = mvpp2_xlg_pcs_config,
};
-static int mvpp2_gmac_pcs_validate(struct phylink_pcs *pcs,
- unsigned long *supported,
- const struct phylink_link_state *state)
+static unsigned int mvpp2_gmac_pcs_query_inband(struct phylink_pcs *pcs,
+ phy_interface_t interface)
{
- /* When in 802.3z mode, we must have AN enabled:
+ /* When operating in an 802.3z mode, we must have AN enabled:
* Bit 2 Field InBandAnEn In-band Auto-Negotiation enable. ...
* When <PortType> = 1 (1000BASE-X) this field must be set to 1.
+ * Therefore, inband is "required".
*/
- if (phy_interface_mode_is_8023z(state->interface) &&
- !phylink_test(state->advertising, Autoneg))
- return -EINVAL;
+ if (phy_interface_mode_is_8023z(interface))
+ return LINK_INBAND_VALID | LINK_INBAND_REQUIRED;
- return 0;
+ /* SGMII and RGMII can be configured to use inband signalling of the
+ * AN result. Indicate these as "possible".
+ */
+ if (interface == PHY_INTERFACE_MODE_SGMII ||
+ phy_interface_mode_is_rgmii(interface))
+ return LINK_INBAND_VALID | LINK_INBAND_POSSIBLE;
+
+ /* For any other modes, indicate that inband is not supported. */
+ return LINK_INBAND_VALID;
}
static void mvpp2_gmac_pcs_get_state(struct phylink_pcs *pcs,
@@ -6309,7 +6341,7 @@ static void mvpp2_gmac_pcs_an_restart(struct phylink_pcs *pcs)
}
static const struct phylink_pcs_ops mvpp2_phylink_gmac_pcs_ops = {
- .pcs_validate = mvpp2_gmac_pcs_validate,
+ .pcs_query_inband = mvpp2_gmac_pcs_query_inband,
.pcs_get_state = mvpp2_gmac_pcs_get_state,
.pcs_config = mvpp2_gmac_pcs_config,
.pcs_an_restart = mvpp2_gmac_pcs_an_restart,
@@ -6631,6 +6663,57 @@ static void mvpp2_mac_link_down(struct phylink_config *config,
mvpp2_port_disable(port);
}
+static void mvpp2_mac_disable_tx_lpi(struct phylink_config *config)
+{
+ struct mvpp2_port *port = mvpp2_phylink_to_port(config);
+
+ mvpp2_modify(port->base + MVPP2_GMAC_LPI_CTRL1,
+ MVPP2_GMAC_LPI_CTRL1_REQ_EN, 0);
+}
+
+static void mvpp2_mac_enable_tx_lpi(struct phylink_config *config, u32 timer)
+{
+ struct mvpp2_port *port = mvpp2_phylink_to_port(config);
+ u32 ts, tw, lpi0, lpi1, status;
+
+ status = readl(port->base + MVPP2_GMAC_STATUS0);
+ if (status & MVPP2_GMAC_STATUS0_GMII_SPEED) {
+ /* At 1G speeds, the timer resolution are 1us, and
+ * 802.3 says tw is 16.5us. Round up to 17us.
+ */
+ tw = 17;
+ ts = timer;
+ } else {
+ /* At 100M speeds, the timer resolutions are 10us, and
+ * 802.3 says tw is 30us.
+ */
+ tw = 3;
+ ts = DIV_ROUND_UP(timer, 10);
+ }
+
+ if (ts > 255)
+ ts = 255;
+
+ /* Ensure LPI generation is disabled */
+ lpi1 = readl(port->base + MVPP2_GMAC_LPI_CTRL1);
+ writel(lpi1 & ~MVPP2_GMAC_LPI_CTRL1_REQ_EN,
+ port->base + MVPP2_GMAC_LPI_CTRL1);
+
+ /* Configure ts */
+ lpi0 = readl(port->base + MVPP2_GMAC_LPI_CTRL0);
+ lpi0 &= ~MVPP2_GMAC_LPI_CTRL0_TS_MASK;
+ lpi0 |= FIELD_PREP(MVPP2_GMAC_LPI_CTRL0_TS_MASK, ts);
+ writel(lpi0, port->base + MVPP2_GMAC_LPI_CTRL0);
+
+ /* Configure tw */
+ lpi1 &= ~MVPP2_GMAC_LPI_CTRL1_TW_MASK;
+ lpi1 |= FIELD_PREP(MVPP2_GMAC_LPI_CTRL1_TW_MASK, tw);
+
+ /* Enable LPI generation */
+ writel(lpi1 | MVPP2_GMAC_LPI_CTRL1_REQ_EN,
+ port->base + MVPP2_GMAC_LPI_CTRL1);
+}
+
static const struct phylink_mac_ops mvpp2_phylink_ops = {
.mac_select_pcs = mvpp2_select_pcs,
.mac_prepare = mvpp2_mac_prepare,
@@ -6638,6 +6721,8 @@ static const struct phylink_mac_ops mvpp2_phylink_ops = {
.mac_finish = mvpp2_mac_finish,
.mac_link_up = mvpp2_mac_link_up,
.mac_link_down = mvpp2_mac_link_down,
+ .mac_enable_tx_lpi = mvpp2_mac_enable_tx_lpi,
+ .mac_disable_tx_lpi = mvpp2_mac_disable_tx_lpi,
};
/* Work-around for ACPI */
@@ -6914,6 +6999,9 @@ static int mvpp2_port_probe(struct platform_device *pdev,
port->phylink_config.type = PHYLINK_NETDEV;
port->phylink_config.mac_capabilities =
MAC_2500FD | MAC_1000FD | MAC_100 | MAC_10;
+ port->phylink_config.lpi_capabilities =
+ MAC_2500FD | MAC_1000FD | MAC_100FD;
+ port->phylink_config.lpi_timer_limit_us = 255;
if (port->priv->global_tx_fc)
port->phylink_config.mac_capabilities |=
@@ -6982,6 +7070,11 @@ static int mvpp2_port_probe(struct platform_device *pdev,
port->phylink_config.supported_interfaces);
}
+ /* Setup EEE. Choose 250us idle. */
+ port->phylink_config.eee.eee_enabled = true;
+ port->phylink_config.eee.tx_lpi_enabled = true;
+ port->phylink_config.eee.tx_lpi_timer = 250;
+
phylink = phylink_create(&port->phylink_config, port_fwnode,
phy_mode, &mvpp2_phylink_ops);
if (IS_ERR(phylink)) {
diff --git a/drivers/net/ethernet/marvell/prestera/prestera_main.c b/drivers/net/ethernet/marvell/prestera/prestera_main.c
index 4fb886c57cd7..ba6d53ac7f55 100644
--- a/drivers/net/ethernet/marvell/prestera/prestera_main.c
+++ b/drivers/net/ethernet/marvell/prestera/prestera_main.c
@@ -821,7 +821,7 @@ static void prestera_port_handle_event(struct prestera_switch *sw,
if (port->state_mac.oper) {
if (port->phy_link)
- phylink_mac_change(port->phy_link, true);
+ phylink_pcs_change(&port->phylink_pcs, true);
else
netif_carrier_on(port->dev);
@@ -829,7 +829,7 @@ static void prestera_port_handle_event(struct prestera_switch *sw,
queue_delayed_work(prestera_wq, caching_dw, 0);
} else {
if (port->phy_link)
- phylink_mac_change(port->phy_link, false);
+ phylink_pcs_change(&port->phylink_pcs, false);
else if (netif_running(port->dev) && netif_carrier_ok(port->dev))
netif_carrier_off(port->dev);
diff --git a/drivers/net/ethernet/samsung/sxgbe/sxgbe_common.h b/drivers/net/ethernet/samsung/sxgbe/sxgbe_common.h
index d14e0cfc3a6b..1458939c3bf5 100644
--- a/drivers/net/ethernet/samsung/sxgbe/sxgbe_common.h
+++ b/drivers/net/ethernet/samsung/sxgbe/sxgbe_common.h
@@ -503,7 +503,6 @@ struct sxgbe_priv_data {
bool tx_path_in_lpi_mode;
int lpi_irq;
int eee_enabled;
- int eee_active;
int tx_lpi_timer;
};
diff --git a/drivers/net/ethernet/samsung/sxgbe/sxgbe_ethtool.c b/drivers/net/ethernet/samsung/sxgbe/sxgbe_ethtool.c
index 8ba017ec9849..52650968bfa5 100644
--- a/drivers/net/ethernet/samsung/sxgbe/sxgbe_ethtool.c
+++ b/drivers/net/ethernet/samsung/sxgbe/sxgbe_ethtool.c
@@ -140,8 +140,6 @@ static int sxgbe_get_eee(struct net_device *dev,
if (!priv->hw_cap.eee)
return -EOPNOTSUPP;
- edata->eee_enabled = priv->eee_enabled;
- edata->eee_active = priv->eee_active;
edata->tx_lpi_timer = priv->tx_lpi_timer;
return phy_ethtool_get_eee(dev->phydev, edata);
diff --git a/drivers/net/ethernet/samsung/sxgbe/sxgbe_main.c b/drivers/net/ethernet/samsung/sxgbe/sxgbe_main.c
index 71439825ea4e..ecbe3994f2b1 100644
--- a/drivers/net/ethernet/samsung/sxgbe/sxgbe_main.c
+++ b/drivers/net/ethernet/samsung/sxgbe/sxgbe_main.c
@@ -130,7 +130,6 @@ bool sxgbe_eee_init(struct sxgbe_priv_data * const priv)
if (phy_init_eee(ndev->phydev, true))
return false;
- priv->eee_active = 1;
timer_setup(&priv->eee_ctrl_timer, sxgbe_eee_ctrl_timer, 0);
priv->eee_ctrl_timer.expires = SXGBE_LPI_TIMER(eee_timer);
add_timer(&priv->eee_ctrl_timer);
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c
index ec44becf0e2d..1471fbdcc6c8 100644
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c
@@ -904,8 +904,6 @@ static int stmmac_ethtool_op_get_eee(struct net_device *dev,
if (!priv->dma_cap.eee)
return -EOPNOTSUPP;
- edata->eee_enabled = priv->eee_enabled;
- edata->eee_active = priv->eee_active;
edata->tx_lpi_timer = priv->tx_lpi_timer;
edata->tx_lpi_enabled = priv->tx_lpi_enabled;
diff --git a/drivers/net/ethernet/wangxun/txgbe/txgbe_phy.c b/drivers/net/ethernet/wangxun/txgbe/txgbe_phy.c
index 1b84d495d14e..b942926e3b69 100644
--- a/drivers/net/ethernet/wangxun/txgbe/txgbe_phy.c
+++ b/drivers/net/ethernet/wangxun/txgbe/txgbe_phy.c
@@ -483,7 +483,8 @@ static void txgbe_irq_handler(struct irq_desc *desc)
TXGBE_PX_MISC_ETH_AN)) {
u32 reg = rd32(wx, TXGBE_CFG_PORT_ST);
- phylink_mac_change(wx->phylink, !!(reg & TXGBE_CFG_PORT_ST_LINK_UP));
+ phylink_pcs_change(&txgbe->xpcs->pcs,
+ !!(reg & TXGBE_CFG_PORT_ST_LINK_UP));
}
/* unmask interrupt */
diff --git a/drivers/net/pcs/pcs-rzn1-miic.c b/drivers/net/pcs/pcs-rzn1-miic.c
index d93f84fbb1fd..f2da79591119 100644
--- a/drivers/net/pcs/pcs-rzn1-miic.c
+++ b/drivers/net/pcs/pcs-rzn1-miic.c
@@ -183,7 +183,7 @@ static void miic_converter_enable(struct miic *miic, int port, int enable)
miic_reg_rmw(miic, MIIC_CONVRST, MIIC_CONVRST_PHYIF_RST(port), val);
}
-static int miic_config(struct phylink_pcs *pcs, unsigned int mode,
+static int miic_config(struct phylink_pcs *pcs, unsigned int neg_mode,
phy_interface_t interface,
const unsigned long *advertising, bool permit)
{
@@ -234,7 +234,7 @@ static int miic_config(struct phylink_pcs *pcs, unsigned int mode,
return 0;
}
-static void miic_link_up(struct phylink_pcs *pcs, unsigned int mode,
+static void miic_link_up(struct phylink_pcs *pcs, unsigned int neg_mode,
phy_interface_t interface, int speed, int duplex)
{
struct miic_port *miic_port = phylink_pcs_to_miic_port(pcs);
@@ -268,7 +268,8 @@ static void miic_link_up(struct phylink_pcs *pcs, unsigned int mode,
(MIIC_CONVCTRL_CONV_SPEED | MIIC_CONVCTRL_FULLD), val);
}
-static int miic_validate(struct phylink_pcs *pcs, unsigned long *supported,
+static int miic_validate(struct phylink_pcs *pcs, unsigned int mode,
+ unsigned long *supported,
const struct phylink_link_state *state)
{
if (phy_interface_mode_is_rgmii(state->interface) ||
@@ -333,6 +334,7 @@ struct phylink_pcs *miic_create(struct device *dev, struct device_node *np)
miic_port->miic = miic;
miic_port->port = port - 1;
miic_port->pcs.ops = &miic_phylink_ops;
+ miic_port->pcs.neg_mode = true;
return &miic_port->pcs;
}
diff --git a/drivers/net/pcs/pcs-xpcs.c b/drivers/net/pcs/pcs-xpcs.c
index 31f0beba638a..f3cccb459ecb 100644
--- a/drivers/net/pcs/pcs-xpcs.c
+++ b/drivers/net/pcs/pcs-xpcs.c
@@ -604,7 +604,8 @@ static void xpcs_resolve_pma(struct dw_xpcs *xpcs,
}
}
-static int xpcs_validate(struct phylink_pcs *pcs, unsigned long *supported,
+static int xpcs_validate(struct phylink_pcs *pcs, unsigned int mode,
+ unsigned long *supported,
const struct phylink_link_state *state)
{
__ETHTOOL_DECLARE_LINK_MODE_MASK(xpcs_supported) = { 0, };
diff --git a/drivers/net/phy/Makefile b/drivers/net/phy/Makefile
index 6097afd44392..dc4c583f7e00 100644
--- a/drivers/net/phy/Makefile
+++ b/drivers/net/phy/Makefile
@@ -28,7 +28,7 @@ obj-$(CONFIG_PHYLIB) += libphy.o
obj-$(CONFIG_NETWORK_PHY_TIMESTAMPING) += mii_timestamper.o
-obj-$(CONFIG_SFP) += sfp.o
+obj-$(CONFIG_SFP) += sff.o sfp.o
sfp-obj-$(CONFIG_SFP) += sfp-bus.o
obj-y += $(sfp-obj-y) $(sfp-obj-m)
diff --git a/drivers/net/phy/aquantia/aquantia_main.c b/drivers/net/phy/aquantia/aquantia_main.c
index 97a2fafa15ca..43886d2995e7 100644
--- a/drivers/net/phy/aquantia/aquantia_main.c
+++ b/drivers/net/phy/aquantia/aquantia_main.c
@@ -746,6 +746,19 @@ static int aqr107_probe(struct phy_device *phydev)
return aqr_hwmon_probe(phydev);
}
+static int aqr113c_probe(struct phy_device *phydev)
+{
+ unsigned long *supported = phydev->supported_interfaces;
+
+ __set_bit(PHY_INTERFACE_MODE_USXGMII, supported);
+ __set_bit(PHY_INTERFACE_MODE_10GBASER, supported);
+ __set_bit(PHY_INTERFACE_MODE_5GBASER, supported);
+ __set_bit(PHY_INTERFACE_MODE_2500BASEX, supported);
+ __set_bit(PHY_INTERFACE_MODE_SGMII, supported);
+
+ return aqr107_probe(phydev);
+}
+
static struct phy_driver aqr_driver[] = {
{
PHY_ID_MATCH_MODEL(PHY_ID_AQ1202),
@@ -866,7 +879,7 @@ static struct phy_driver aqr_driver[] = {
{
PHY_ID_MATCH_MODEL(PHY_ID_AQR113C),
.name = "Aquantia AQR113C",
- .probe = aqr107_probe,
+ .probe = aqr113c_probe,
.get_rate_matching = aqr107_get_rate_matching,
.config_init = aqr113c_config_init,
.config_aneg = aqr_config_aneg,
diff --git a/drivers/net/phy/bcm84881.c b/drivers/net/phy/bcm84881.c
index f1d47c264058..a72b693be846 100644
--- a/drivers/net/phy/bcm84881.c
+++ b/drivers/net/phy/bcm84881.c
@@ -63,6 +63,10 @@ static int bcm84881_probe(struct phy_device *phydev)
(phydev->c45_ids.devices_in_package & mmd_mask) != mmd_mask)
return -ENODEV;
+ __set_bit(PHY_INTERFACE_MODE_SGMII, phydev->supported_interfaces);
+ __set_bit(PHY_INTERFACE_MODE_2500BASEX, phydev->supported_interfaces);
+ __set_bit(PHY_INTERFACE_MODE_10GBASER, phydev->supported_interfaces);
+
return 0;
}
@@ -235,11 +239,21 @@ static int bcm84881_read_status(struct phy_device *phydev)
return genphy_c45_read_mdix(phydev);
}
+/* The Broadcom BCM84881 in the Methode DM7052 is unable to provide a SGMII
+ * or 802.3z control word, so inband will not work.
+ */
+static unsigned int bcm84881_query_inband(struct phy_device *phydev,
+ phy_interface_t interface)
+{
+ return LINK_INBAND_VALID;
+}
+
static struct phy_driver bcm84881_drivers[] = {
{
.phy_id = 0xae025150,
.phy_id_mask = 0xfffffff0,
.name = "Broadcom BCM84881",
+ .query_inband = bcm84881_query_inband,
.config_init = bcm84881_config_init,
.probe = bcm84881_probe,
.get_features = bcm84881_get_features,
diff --git a/drivers/net/phy/marvell.c b/drivers/net/phy/marvell.c
index eba652a4c1d8..6a6bcda8796d 100644
--- a/drivers/net/phy/marvell.c
+++ b/drivers/net/phy/marvell.c
@@ -176,6 +176,10 @@
#define MII_M1011_PHY_STATUS_FULLDUPLEX 0x2000
#define MII_M1011_PHY_STATUS_RESOLVED 0x0800
#define MII_M1011_PHY_STATUS_LINK 0x0400
+#define MII_M1111_PHY_STATUS_TX_PAUSE 0x0008
+#define MII_M1111_PHY_STATUS_RX_PAUSE 0x0004
+#define MII_88E151X_PHY_STATUS_TX_PAUSE 0x0200
+#define MII_88E151X_PHY_STATUS_RX_PAUSE 0x0100
#define MII_88E3016_PHY_SPEC_CTRL 0x10
#define MII_88E3016_DISABLE_SCRAMBLER 0x0200
@@ -310,6 +314,8 @@ struct marvell_priv {
u32 last;
u32 step;
s8 pair;
+ u16 tx_pause_mask;
+ u16 rx_pause_mask;
};
static int marvell_read_page(struct phy_device *phydev)
@@ -673,6 +679,19 @@ static int marvell_config_aneg_fiber(struct phy_device *phydev)
return genphy_check_and_restart_aneg(phydev, changed);
}
+static unsigned int m88e1111_query_inband(struct phy_device *phydev,
+ phy_interface_t interface)
+{
+ /* In 1000base-X and SGMII modes, the inband mode can be changed
+ * through the Fibre page BMCR ANENABLE bit.
+ */
+ if (interface == PHY_INTERFACE_MODE_1000BASEX ||
+ interface == PHY_INTERFACE_MODE_SGMII)
+ return LINK_INBAND_VALID | LINK_INBAND_POSSIBLE;
+
+ return 0;
+}
+
static int m88e1111_config_aneg(struct phy_device *phydev)
{
int extsr = phy_read(phydev, MII_M1111_PHY_EXT_SR);
@@ -919,7 +938,10 @@ static int m88e1111_config_init_1000basex(struct phy_device *phydev)
if (extsr < 0)
return extsr;
- /* If using copper mode, ensure 1000BaseX auto-negotiation is enabled */
+ /* If using copper mode, ensure 1000BaseX auto-negotiation is enabled.
+ * FIXME: this does not actually enable 1000BaseX auto-negotiation if
+ * it was previously disabled in the Fiber BMCR!
+ */
mode = extsr & MII_M1111_HWCFG_MODE_MASK;
if (mode == MII_M1111_HWCFG_MODE_COPPER_1000X_NOAN) {
err = phy_modify(phydev, MII_M1111_PHY_EXT_SR,
@@ -1577,6 +1599,7 @@ static void fiber_lpa_mod_linkmode_lpa_t(unsigned long *advertising, u32 lpa)
static int marvell_read_status_page_an(struct phy_device *phydev,
int fiber, int status)
{
+ struct marvell_priv *priv = phydev->priv;
int lpa;
int err;
@@ -1632,6 +1655,11 @@ static int marvell_read_status_page_an(struct phy_device *phydev,
}
}
+ phydev->resolved_tx_pause = !!(status & priv->tx_pause_mask);
+ phydev->resolved_rx_pause = !!(status & priv->rx_pause_mask);
+ phydev->resolved_pause_valid = !fiber && priv->tx_pause_mask &&
+ priv->rx_pause_mask;
+
return 0;
}
@@ -1675,6 +1703,7 @@ static int marvell_read_status_page(struct phy_device *phydev, int page)
phydev->speed = SPEED_UNKNOWN;
phydev->duplex = DUPLEX_UNKNOWN;
phydev->port = fiber ? PORT_FIBRE : PORT_TP;
+ phydev->resolved_pause_valid = false;
if (phydev->autoneg == AUTONEG_ENABLE)
err = marvell_read_status_page_an(phydev, fiber, status);
@@ -3159,14 +3188,26 @@ static int m88e1318_led_hw_control_get(struct phy_device *phydev, u8 index,
return marvell_get_led_rules(index, rules, mode);
}
-static int marvell_probe(struct phy_device *phydev)
+static int marvell_probe_pause(struct phy_device *phydev, u16 tx_pause_mask,
+ u16 rx_pause_mask)
{
struct marvell_priv *priv;
+ __set_bit(PHY_INTERFACE_MODE_GMII, phydev->supported_interfaces);
+ __set_bit(PHY_INTERFACE_MODE_SGMII, phydev->supported_interfaces);
+ __set_bit(PHY_INTERFACE_MODE_TBI, phydev->supported_interfaces);
+ __set_bit(PHY_INTERFACE_MODE_RGMII, phydev->supported_interfaces);
+ __set_bit(PHY_INTERFACE_MODE_RGMII_ID, phydev->supported_interfaces);
+ __set_bit(PHY_INTERFACE_MODE_RGMII_RXID, phydev->supported_interfaces);
+ __set_bit(PHY_INTERFACE_MODE_RGMII_TXID, phydev->supported_interfaces);
+ __set_bit(PHY_INTERFACE_MODE_RTBI, phydev->supported_interfaces);
+
priv = devm_kzalloc(&phydev->mdio.dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
+ priv->tx_pause_mask = tx_pause_mask;
+ priv->rx_pause_mask = rx_pause_mask;
phydev->priv = priv;
return marvell_hwmon_probe(phydev);
@@ -3256,11 +3297,23 @@ static const struct sfp_upstream_ops m88e1510_sfp_ops = {
.detach = phy_sfp_detach,
};
+static int marvell_probe(struct phy_device *phydev)
+{
+ return marvell_probe_pause(phydev, 0, 0);
+}
+
+static int m88e1111_probe(struct phy_device *phydev)
+{
+ return marvell_probe_pause(phydev, MII_M1111_PHY_STATUS_TX_PAUSE,
+ MII_M1111_PHY_STATUS_RX_PAUSE);
+}
+
static int m88e1510_probe(struct phy_device *phydev)
{
int err;
- err = marvell_probe(phydev);
+ err = marvell_probe_pause(phydev, MII_88E151X_PHY_STATUS_TX_PAUSE,
+ MII_88E151X_PHY_STATUS_RX_PAUSE);
if (err)
return err;
@@ -3292,6 +3345,7 @@ static struct phy_driver marvell_drivers[] = {
.name = "Marvell 88E1112",
/* PHY_GBIT_FEATURES */
.probe = marvell_probe,
+ .query_inband = m88e1111_query_inband,
.config_init = m88e1112_config_init,
.config_aneg = marvell_config_aneg,
.config_intr = marvell_config_intr,
@@ -3311,7 +3365,8 @@ static struct phy_driver marvell_drivers[] = {
.phy_id_mask = MARVELL_PHY_ID_MASK,
.name = "Marvell 88E1111",
/* PHY_GBIT_FEATURES */
- .probe = marvell_probe,
+ .probe = m88e1111_probe,
+ .query_inband = m88e1111_query_inband,
.config_init = m88e1111gbe_config_init,
.config_aneg = m88e1111_config_aneg,
.read_status = marvell_read_status,
@@ -3333,6 +3388,7 @@ static struct phy_driver marvell_drivers[] = {
.name = "Marvell 88E1111 (Finisar)",
/* PHY_GBIT_FEATURES */
.probe = marvell_probe,
+ .query_inband = m88e1111_query_inband,
.config_init = m88e1111gbe_config_init,
.config_aneg = m88e1111_config_aneg,
.read_status = marvell_read_status,
@@ -3532,7 +3588,7 @@ static struct phy_driver marvell_drivers[] = {
.driver_data = DEF_MARVELL_HWMON_OPS(m88e1510_hwmon_ops),
/* PHY_GBIT_FEATURES */
.flags = PHY_POLL_CABLE_TEST,
- .probe = marvell_probe,
+ .probe = m88e1510_probe,
.config_init = marvell_1011gbe_config_init,
.config_aneg = m88e1510_config_aneg,
.read_status = marvell_read_status,
@@ -3561,7 +3617,7 @@ static struct phy_driver marvell_drivers[] = {
.phy_id_mask = MARVELL_PHY_ID_MASK,
.name = "Marvell 88E1545",
.driver_data = DEF_MARVELL_HWMON_OPS(m88e1510_hwmon_ops),
- .probe = marvell_probe,
+ .probe = m88e1510_probe,
/* PHY_GBIT_FEATURES */
.flags = PHY_POLL_CABLE_TEST,
.config_init = marvell_1011gbe_config_init,
@@ -3613,7 +3669,7 @@ static struct phy_driver marvell_drivers[] = {
.driver_data = DEF_MARVELL_HWMON_OPS(m88e1510_hwmon_ops),
/* PHY_GBIT_FEATURES */
.flags = PHY_POLL_CABLE_TEST,
- .probe = marvell_probe,
+ .probe = m88e1510_probe,
.config_init = marvell_1011gbe_config_init,
.config_aneg = m88e6390_config_aneg,
.read_status = marvell_read_status,
diff --git a/drivers/net/phy/marvell10g.c b/drivers/net/phy/marvell10g.c
index ad43e280930c..eb05a578dbd7 100644
--- a/drivers/net/phy/marvell10g.c
+++ b/drivers/net/phy/marvell10g.c
@@ -27,6 +27,7 @@
#include <linux/delay.h>
#include <linux/hwmon.h>
#include <linux/marvell_phy.h>
+#include <linux/of.h>
#include <linux/phy.h>
#include <linux/sfp.h>
#include <linux/netdevice.h>
@@ -82,6 +83,8 @@ enum {
MV_PCS_CSSR1_SPD1_10 = 0x0000,
MV_PCS_CSSR1_DUPLEX_FULL= BIT(13),
MV_PCS_CSSR1_RESOLVED = BIT(11),
+ MV_PCS_CSSR1_TX_PAUSE = BIT(9),
+ MV_PCS_CSSR1_RX_PAUSE = BIT(8),
MV_PCS_CSSR1_MDIX = BIT(6),
MV_PCS_CSSR1_SPD2_MASK = 0x000c,
MV_PCS_CSSR1_SPD2_5000 = 0x0008,
@@ -163,14 +166,16 @@ struct mv3310_chip {
};
struct mv3310_priv {
- DECLARE_BITMAP(supported_interfaces, PHY_INTERFACE_MODE_MAX);
const struct mv3310_mactype *mactype;
u32 firmware_ver;
bool has_downshift;
+ bool firmware_failed;
struct device *hwmon_dev;
char *hwmon_name;
+ u8 num_leds;
+ u16 led_mode[4];
};
static const struct mv3310_chip *to_mv3310_chip(struct phy_device *phydev)
@@ -506,6 +511,43 @@ static const struct sfp_upstream_ops mv3310_sfp_ops = {
.module_insert = mv3310_sfp_insert,
};
+static int mv3310_leds_write(struct phy_device *phydev)
+{
+ struct mv3310_priv *priv = dev_get_drvdata(&phydev->mdio.dev);
+ int i, ret;
+
+ for (i = 0; i < priv->num_leds; i++) {
+ ret = phy_write_mmd(phydev, MDIO_MMD_VEND2, 0xf020 + i,
+ priv->led_mode[i]);
+ if (ret < 0)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int mv3310_fw_config(struct phy_device *phydev)
+{
+ struct mv3310_priv *priv = dev_get_drvdata(&phydev->mdio.dev);
+ struct device_node *node;
+ int ret;
+
+ node = phydev->mdio.dev.of_node;
+ if (!node)
+ return 0;
+
+ ret = of_property_read_variable_u16_array(node, "marvell,led-mode",
+ priv->led_mode, 1, ARRAY_SIZE(priv->led_mode));
+ if (ret == -EINVAL)
+ ret = 0;
+ if (ret < 0)
+ return ret;
+
+ priv->num_leds = ret;
+
+ return 0;
+}
+
static int mv3310_probe(struct phy_device *phydev)
{
const struct mv3310_chip *chip = to_mv3310_chip(phydev);
@@ -517,6 +559,20 @@ static int mv3310_probe(struct phy_device *phydev)
(phydev->c45_ids.devices_in_package & mmd_mask) != mmd_mask)
return -ENODEV;
+ priv = devm_kzalloc(&phydev->mdio.dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ dev_set_drvdata(&phydev->mdio.dev, priv);
+
+ ret = mv3310_fw_config(phydev);
+ if (ret < 0)
+ return ret;
+
+ ret = mv3310_leds_write(phydev);
+ if (ret < 0)
+ return ret;
+
ret = phy_read_mmd(phydev, MDIO_MMD_PMAPMD, MV_PMA_BOOT);
if (ret < 0)
return ret;
@@ -524,15 +580,9 @@ static int mv3310_probe(struct phy_device *phydev)
if (ret & MV_PMA_BOOT_FATAL) {
dev_warn(&phydev->mdio.dev,
"PHY failed to boot firmware, status=%04x\n", ret);
- return -ENODEV;
+ priv->firmware_failed = true;
}
- priv = devm_kzalloc(&phydev->mdio.dev, sizeof(*priv), GFP_KERNEL);
- if (!priv)
- return -ENOMEM;
-
- dev_set_drvdata(&phydev->mdio.dev, priv);
-
ret = phy_read_mmd(phydev, MDIO_MMD_PMAPMD, MV_PMA_FW_VER0);
if (ret < 0)
return ret;
@@ -561,7 +611,7 @@ static int mv3310_probe(struct phy_device *phydev)
if (ret)
return ret;
- chip->init_supported_interfaces(priv->supported_interfaces);
+ chip->init_supported_interfaces(phydev->supported_interfaces);
return phy_sfp_probe(phydev, &mv3310_sfp_ops);
}
@@ -587,6 +637,19 @@ static int mv3310_resume(struct phy_device *phydev)
return mv3310_hwmon_config(phydev, true);
}
+static int mv3310_start(struct phy_device *phydev)
+{
+ struct mv3310_priv *priv = dev_get_drvdata(&phydev->mdio.dev);
+
+ if (priv->firmware_failed) {
+ dev_warn(&phydev->mdio.dev,
+ "PHY firmware failure: PHY not starting");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
/* Some PHYs in the Alaska family such as the 88X3310 and the 88E2010
* don't set bit 14 in PMA Extended Abilities (1.11), although they do
* support 2.5GBASET and 5GBASET. For these models, we can still read their
@@ -826,7 +889,7 @@ static int mv3310_config_init(struct phy_device *phydev)
int err, mactype;
/* Check that the PHY interface type is compatible */
- if (!test_bit(phydev->interface, priv->supported_interfaces))
+ if (!test_bit(phydev->interface, phydev->supported_interfaces))
return -ENODEV;
phydev->mdix_ctrl = ETH_TP_MDI_AUTO;
@@ -873,7 +936,7 @@ static int mv3310_config_init(struct phy_device *phydev)
if (err && err != -EOPNOTSUPP)
return err;
- return 0;
+ return mv3310_leds_write(phydev);
}
static int mv3310_get_features(struct phy_device *phydev)
@@ -1090,6 +1153,10 @@ static int mv3310_read_status_copper(struct phy_device *phydev)
phydev->mdix = cssr1 & MV_PCS_CSSR1_MDIX ?
ETH_TP_MDI_X : ETH_TP_MDI;
+ phydev->resolved_tx_pause = !!(cssr1 & MV_PCS_CSSR1_TX_PAUSE);
+ phydev->resolved_rx_pause = !!(cssr1 & MV_PCS_CSSR1_RX_PAUSE);
+ phydev->resolved_pause_valid = true;
+
if (val & MDIO_AN_STAT1_COMPLETE) {
val = genphy_c45_read_lpa(phydev);
if (val < 0)
@@ -1432,6 +1499,7 @@ static struct phy_driver mv3310_drivers[] = {
.probe = mv3310_probe,
.suspend = mv3310_suspend,
.resume = mv3310_resume,
+ .start = mv3310_start,
.config_aneg = mv3310_config_aneg,
.aneg_done = mv3310_aneg_done,
.read_status = mv3310_read_status,
@@ -1469,6 +1537,7 @@ static struct phy_driver mv3310_drivers[] = {
.probe = mv3310_probe,
.suspend = mv3310_suspend,
.resume = mv3310_resume,
+ .start = mv3310_start,
.config_init = mv3310_config_init,
.config_aneg = mv3310_config_aneg,
.aneg_done = mv3310_aneg_done,
diff --git a/drivers/net/phy/mdio_bus.c b/drivers/net/phy/mdio_bus.c
index afbad1ad8683..836a52d147ef 100644
--- a/drivers/net/phy/mdio_bus.c
+++ b/drivers/net/phy/mdio_bus.c
@@ -1370,8 +1370,16 @@ static int mdio_bus_match(struct device *dev, struct device_driver *drv)
static int mdio_uevent(const struct device *dev, struct kobj_uevent_env *env)
{
+ struct mdio_device *mdio = to_mdio_device(dev);
int rc;
+ /* Use the device-specific uevent if specified */
+ if (mdio->bus_uevent) {
+ rc = mdio->bus_uevent(mdio, env);
+ if (rc != -ENODEV)
+ return rc;
+ }
+
/* Some devices have extra OF data and an OF-style MODALIAS */
rc = of_device_uevent_modalias(dev, env);
if (rc != -ENODEV)
diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c
index 3376e58e2b88..ae1916861986 100644
--- a/drivers/net/phy/phy.c
+++ b/drivers/net/phy/phy.c
@@ -993,6 +993,27 @@ static int phy_check_link_status(struct phy_device *phydev)
}
/**
+ * phy_query_inband - query which in-band signalling modes are supported
+ * @phydev: a pointer to a &struct phy_device
+ * @interface: the interface mode for the PHY
+ *
+ * Returns zero if it is unknown what in-band signalling is supported by the
+ * PHY (e.g. because the PHY driver doesn't implement the method.) Otherwise,
+ * returns a bit mask of the LINK_INBAND_* values from
+ * &enum link_inband_signalling to describe which inband modes are supported
+ * for this interface mode.
+ */
+unsigned int phy_query_inband(struct phy_device *phydev,
+ phy_interface_t interface)
+{
+ if (phydev->drv && phydev->drv->query_inband)
+ return phydev->drv->query_inband(phydev, interface);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(phy_query_inband);
+
+/**
* _phy_start_aneg - start auto-negotiation for this PHY device
* @phydev: the phy_device struct
*
@@ -1239,6 +1260,8 @@ void phy_stop_machine(struct phy_device *phydev)
static void phy_process_error(struct phy_device *phydev)
{
+ phydev_err(phydev, "Error detected, halting PHY\n");
+
/* phydev->lock must be held for the state change to be safe */
if (!mutex_is_locked(&phydev->lock))
phydev_err(phydev, "PHY-device data unsafe context\n");
@@ -1265,7 +1288,6 @@ static void phy_error_precise(struct phy_device *phydev,
*/
void phy_error(struct phy_device *phydev)
{
- WARN_ON(1);
phy_process_error(phydev);
}
EXPORT_SYMBOL(phy_error);
@@ -1290,7 +1312,6 @@ int phy_disable_interrupts(struct phy_device *phydev)
static irqreturn_t phy_interrupt(int irq, void *phy_dat)
{
struct phy_device *phydev = phy_dat;
- struct phy_driver *drv = phydev->drv;
irqreturn_t ret;
/* Wakeup interrupts may occur during a system sleep transition.
@@ -1316,7 +1337,7 @@ static irqreturn_t phy_interrupt(int irq, void *phy_dat)
}
mutex_lock(&phydev->lock);
- ret = drv->handle_interrupt(phydev);
+ ret = phydev->drv->handle_interrupt(phydev);
mutex_unlock(&phydev->lock);
return ret;
@@ -1513,6 +1534,10 @@ void phy_stop(struct phy_device *phydev)
phy_process_state_change(phydev, old_state);
state_work = _phy_state_machine(phydev);
+
+ if (phydev->drv->stop)
+ phydev->drv->stop(phydev);
+
mutex_unlock(&phydev->lock);
_phy_state_machine_post_work(phydev, state_work);
@@ -1545,6 +1570,9 @@ void phy_start(struct phy_device *phydev)
goto out;
}
+ if (phydev->drv->start && phydev->drv->start(phydev))
+ goto out;
+
if (phydev->sfp_bus)
sfp_upstream_start(phydev->sfp_bus);
diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c
index 3611ea64875e..d42b89334116 100644
--- a/drivers/net/phy/phy_device.c
+++ b/drivers/net/phy/phy_device.c
@@ -631,6 +631,19 @@ static int phy_request_driver_module(struct phy_device *dev, u32 phy_id)
return 0;
}
+static int phy_bus_uevent(struct mdio_device *mdiodev,
+ struct kobj_uevent_env *env)
+{
+ struct phy_device *phydev;
+
+ phydev = container_of(mdiodev, struct phy_device, mdio);
+
+ add_uevent_var(env, "MODALIAS=" MDIO_MODULE_PREFIX MDIO_ID_FMT,
+ MDIO_ID_ARGS(phydev->phy_id));
+
+ return 0;
+}
+
struct phy_device *phy_device_create(struct mii_bus *bus, int addr, u32 phy_id,
bool is_c45,
struct phy_c45_device_ids *c45_ids)
@@ -650,6 +663,7 @@ struct phy_device *phy_device_create(struct mii_bus *bus, int addr, u32 phy_id,
mdiodev->dev.type = &mdio_bus_phy_type;
mdiodev->bus = bus;
mdiodev->bus_match = phy_bus_match;
+ mdiodev->bus_uevent = phy_bus_uevent;
mdiodev->addr = addr;
mdiodev->flags = MDIO_DEVICE_FLAG_PHY;
mdiodev->device_free = phy_mdio_device_free;
@@ -1859,7 +1873,7 @@ int phy_suspend(struct phy_device *phydev)
{
struct ethtool_wolinfo wol = { .cmd = ETHTOOL_GWOL };
struct net_device *netdev = phydev->attached_dev;
- struct phy_driver *phydrv = phydev->drv;
+ const struct phy_driver *phydrv = phydev->drv;
int ret;
if (phydev->suspended)
@@ -1884,7 +1898,7 @@ EXPORT_SYMBOL(phy_suspend);
int __phy_resume(struct phy_device *phydev)
{
- struct phy_driver *phydrv = phydev->drv;
+ const struct phy_driver *phydrv = phydev->drv;
int ret;
lockdep_assert_held(&phydev->lock);
@@ -2888,6 +2902,12 @@ void phy_get_pause(struct phy_device *phydev, bool *tx_pause, bool *rx_pause)
return;
}
+ if (phydev->resolved_pause_valid) {
+ *tx_pause = phydev->resolved_tx_pause;
+ *rx_pause = phydev->resolved_rx_pause;
+ return;
+ }
+
return linkmode_resolve_pause(phydev->advertising,
phydev->lp_advertising,
tx_pause, rx_pause);
@@ -2992,7 +3012,7 @@ s32 phy_get_internal_delay(struct phy_device *phydev, struct device *dev,
}
EXPORT_SYMBOL(phy_get_internal_delay);
-static bool phy_drv_supports_irq(struct phy_driver *phydrv)
+static bool phy_drv_supports_irq(const struct phy_driver *phydrv)
{
return phydrv->config_intr && phydrv->handle_interrupt;
}
diff --git a/drivers/net/phy/phylink.c b/drivers/net/phy/phylink.c
index ed0b4ccaa6a6..8a3728d423e7 100644
--- a/drivers/net/phy/phylink.c
+++ b/drivers/net/phy/phylink.c
@@ -85,7 +85,11 @@ struct phylink {
bool sfp_may_have_phy;
DECLARE_PHY_INTERFACE_MASK(sfp_interfaces);
__ETHTOOL_DECLARE_LINK_MODE_MASK(sfp_support);
+ u8 sfp_link_an_mode;
u8 sfp_port;
+
+ struct eee_config eee_cfg;
+ bool eee_active;
};
#define phylink_printk(level, pl, fmt, ...) \
@@ -647,7 +651,7 @@ static void phylink_validate_mask_caps(unsigned long *supported,
linkmode_and(state->advertising, state->advertising, mask);
}
-static int phylink_validate_mac_and_pcs(struct phylink *pl,
+static int phylink_validate_mac_and_pcs(struct phylink *pl, unsigned int mode,
unsigned long *supported,
struct phylink_link_state *state)
{
@@ -678,7 +682,8 @@ static int phylink_validate_mac_and_pcs(struct phylink *pl,
/* Validate the link parameters with the PCS */
if (pcs->ops->pcs_validate) {
- ret = pcs->ops->pcs_validate(pcs, supported, state);
+ ret = pcs->ops->pcs_validate(pcs, mode, supported,
+ state);
if (ret < 0 || phylink_is_empty_linkmode(supported))
return -EINVAL;
@@ -703,6 +708,7 @@ static int phylink_validate_mac_and_pcs(struct phylink *pl,
}
static void phylink_validate_one(struct phylink *pl, struct phy_device *phy,
+ unsigned int mode,
const unsigned long *supported,
const struct phylink_link_state *state,
phy_interface_t interface,
@@ -720,7 +726,8 @@ static void phylink_validate_one(struct phylink *pl, struct phy_device *phy,
if (phy)
tmp_state.rate_matching = phy_get_rate_matching(phy, interface);
- if (!phylink_validate_mac_and_pcs(pl, tmp_supported, &tmp_state)) {
+ if (!phylink_validate_mac_and_pcs(pl, mode, tmp_supported,
+ &tmp_state)) {
phylink_dbg(pl, " interface %u (%s) rate match %s supports %*pbl\n",
interface, phy_modes(interface),
phy_rate_matching_to_str(tmp_state.rate_matching),
@@ -733,6 +740,7 @@ static void phylink_validate_one(struct phylink *pl, struct phy_device *phy,
}
static int phylink_validate_mask(struct phylink *pl, struct phy_device *phy,
+ unsigned int mode,
unsigned long *supported,
struct phylink_link_state *state,
const unsigned long *interfaces)
@@ -742,7 +750,7 @@ static int phylink_validate_mask(struct phylink *pl, struct phy_device *phy,
int interface;
for_each_set_bit(interface, interfaces, PHY_INTERFACE_MODE_MAX)
- phylink_validate_one(pl, phy, supported, state, interface,
+ phylink_validate_one(pl, phy, mode, supported, state, interface,
all_s, all_adv);
linkmode_copy(supported, all_s);
@@ -751,19 +759,20 @@ static int phylink_validate_mask(struct phylink *pl, struct phy_device *phy,
return phylink_is_empty_linkmode(supported) ? -EINVAL : 0;
}
-static int phylink_validate(struct phylink *pl, unsigned long *supported,
+static int phylink_validate(struct phylink *pl, unsigned int mode,
+ unsigned long *supported,
struct phylink_link_state *state)
{
const unsigned long *interfaces = pl->config->supported_interfaces;
if (state->interface == PHY_INTERFACE_MODE_NA)
- return phylink_validate_mask(pl, NULL, supported, state,
+ return phylink_validate_mask(pl, NULL, mode, supported, state,
interfaces);
if (!test_bit(state->interface, interfaces))
return -EINVAL;
- return phylink_validate_mac_and_pcs(pl, supported, state);
+ return phylink_validate_mac_and_pcs(pl, mode, supported, state);
}
static int phylink_parse_fixedlink(struct phylink *pl,
@@ -841,7 +850,7 @@ static int phylink_parse_fixedlink(struct phylink *pl,
linkmode_fill(pl->supported);
linkmode_copy(pl->link_config.advertising, pl->supported);
- phylink_validate(pl, pl->supported, &pl->link_config);
+ phylink_validate(pl, MLO_AN_FIXED, pl->supported, &pl->link_config);
pause = phylink_test(pl->supported, Pause);
asym_pause = phylink_test(pl->supported, Asym_Pause);
@@ -893,7 +902,7 @@ static int phylink_parse_mode(struct phylink *pl,
if ((fwnode_property_read_string(fwnode, "managed", &managed) == 0 &&
strcmp(managed, "in-band-status") == 0) ||
pl->config->ovr_an_inband) {
- if (pl->cfg_link_an_mode == MLO_AN_FIXED) {
+ if (phylink_mode_fixed(pl->cfg_link_an_mode)) {
phylink_err(pl,
"can't use both fixed-link and in-band-status\n");
return -EINVAL;
@@ -939,7 +948,8 @@ static int phylink_parse_mode(struct phylink *pl,
linkmode_copy(pl->link_config.advertising, pl->supported);
- if (phylink_validate(pl, pl->supported, &pl->link_config)) {
+ if (phylink_validate(pl, pl->cfg_link_an_mode, pl->supported,
+ &pl->link_config)) {
phylink_err(pl,
"failed to validate link configuration for in-band status\n");
return -EINVAL;
@@ -977,6 +987,15 @@ static void phylink_resolve_an_pause(struct phylink_link_state *state)
}
}
+static unsigned int phylink_pcs_query_inband(struct phylink_pcs *pcs,
+ phy_interface_t interface)
+{
+ if (pcs && pcs->ops->pcs_query_inband)
+ return pcs->ops->pcs_query_inband(pcs, interface);
+
+ return 0;
+}
+
static void phylink_pcs_pre_config(struct phylink_pcs *pcs,
phy_interface_t interface)
{
@@ -1032,13 +1051,14 @@ static void phylink_pcs_link_up(struct phylink_pcs *pcs, unsigned int neg_mode,
static void phylink_pcs_poll_stop(struct phylink *pl)
{
- if (pl->cfg_link_an_mode == MLO_AN_INBAND)
+ if (phylink_mode_inband(pl->cfg_link_an_mode))
del_timer(&pl->link_poll);
}
static void phylink_pcs_poll_start(struct phylink *pl)
{
- if (pl->pcs && pl->pcs->poll && pl->cfg_link_an_mode == MLO_AN_INBAND)
+ if (pl->pcs && pl->pcs->poll &&
+ phylink_mode_inband(pl->cfg_link_an_mode))
mod_timer(&pl->link_poll, jiffies + HZ);
}
@@ -1076,6 +1096,8 @@ static void phylink_pcs_an_restart(struct phylink *pl)
/**
* phylink_pcs_neg_mode() - helper to determine PCS inband mode
+ * @pl: a pointer to a &struct phylink returned from phylink_create()
+ * @pcs: a pointer to &struct phylink_pcs
* @mode: one of %MLO_AN_FIXED, %MLO_AN_PHY, %MLO_AN_INBAND.
* @interface: interface mode to be used
* @advertising: adertisement ethtool link mode mask
@@ -1093,11 +1115,19 @@ static void phylink_pcs_an_restart(struct phylink *pl)
* Note: this is for cases where the PCS itself is involved in negotiation
* (e.g. Clause 37, SGMII and similar) not Clause 73.
*/
-static unsigned int phylink_pcs_neg_mode(unsigned int mode,
+static unsigned int phylink_pcs_neg_mode(struct phylink *pl,
+ struct phylink_pcs *pcs,
+ unsigned int mode,
phy_interface_t interface,
const unsigned long *advertising)
{
+ unsigned int phy_link_mode = 0;
+ unsigned int pcs_link_mode;
unsigned int neg_mode;
+ enum {
+ INBAND_CISCO_SGMII,
+ INBAND_8023Z,
+ } type;
switch (interface) {
case PHY_INTERFACE_MODE_SGMII:
@@ -1109,10 +1139,7 @@ static unsigned int phylink_pcs_neg_mode(unsigned int mode,
* inband communication. Note: there exist PHYs that run
* with SGMII but do not send the inband data.
*/
- if (!phylink_autoneg_inband(mode))
- neg_mode = PHYLINK_PCS_NEG_OUTBAND;
- else
- neg_mode = PHYLINK_PCS_NEG_INBAND_ENABLED;
+ type = INBAND_CISCO_SGMII;
break;
case PHY_INTERFACE_MODE_1000BASEX:
@@ -1123,20 +1150,92 @@ static unsigned int phylink_pcs_neg_mode(unsigned int mode,
* as well, but drivers may not support this, so may
* need to override this.
*/
- if (!phylink_autoneg_inband(mode))
- neg_mode = PHYLINK_PCS_NEG_OUTBAND;
- else if (linkmode_test_bit(ETHTOOL_LINK_MODE_Autoneg_BIT,
- advertising))
- neg_mode = PHYLINK_PCS_NEG_INBAND_ENABLED;
- else
- neg_mode = PHYLINK_PCS_NEG_INBAND_DISABLED;
+ type = INBAND_8023Z;
break;
default:
- neg_mode = PHYLINK_PCS_NEG_NONE;
- break;
+ return PHYLINK_PCS_NEG_NONE;
+ }
+
+ pcs_link_mode = phylink_pcs_query_inband(pcs, interface);
+ if (pl->phydev)
+ phy_link_mode = phy_query_inband(pl->phydev, interface);
+
+ if (!phylink_autoneg_inband(mode)) {
+ /* If either the PCS or PHY requires inband to be enabled,
+ * this is an invalid configuration. Provide a diagnostic
+ * message for this case, but don't try to force the issue.
+ */
+ if ((pcs_link_mode | phy_link_mode) & LINK_INBAND_REQUIRED) {
+ const char *s1, *s2, *s3, *empty = "";
+
+ s1 = s2 = s3 = empty;
+ if (pcs_link_mode & LINK_INBAND_REQUIRED)
+ s1 = "PCS";
+
+ if (phy_link_mode & LINK_INBAND_REQUIRED)
+ s3 = "PHY";
+
+ if (s1 != empty && s3 != empty)
+ s2 = " and ";
+
+ phylink_warn(pl,
+ "firmware wants %s mode, but %s%s%s requires inband\n",
+ phylink_an_mode_str(mode),
+ s1, s2, s3);
+ }
+ return PHYLINK_PCS_NEG_OUTBAND;
}
+ /* For SGMII modes, which are designed to be used with PHYs, we
+ * try to use inband mode where-ever possible.
+ */
+ if (type == INBAND_CISCO_SGMII) {
+ /* If the PCS or PHY can not provide inband, then use
+ * out of band.
+ */
+ if (pcs_link_mode == LINK_INBAND_VALID ||
+ phy_link_mode == LINK_INBAND_VALID) {
+ /* If either require inband, then we have a conflict. */
+ return PHYLINK_PCS_NEG_OUTBAND;
+ }
+
+ /* Otherwise, use inband as this is what was requested.
+ * This covers the case where the supported inband modes
+ * have not been provided, or the PCS and PHY report that
+ * they support inband.
+ */
+ return PHYLINK_PCS_NEG_INBAND_ENABLED;
+ }
+
+ /* For 802.3z, handle the PHY present vs absent cases separately */
+ if (!pl->phydev) {
+ /* PHY absent: always use inband but inband may be disabled */
+ /* If the PCS doesn't support inband, then inband must be
+ * disabled.
+ */
+ if (pcs_link_mode == LINK_INBAND_VALID)
+ return PHYLINK_PCS_NEG_INBAND_DISABLED;
+
+ /* If the PCS requires inband, then inband must always be
+ * enabled.
+ */
+ if (pcs_link_mode & LINK_INBAND_REQUIRED)
+ return PHYLINK_PCS_NEG_INBAND_ENABLED;
+
+ /* For the possible case, fall through to the "legacy" code. */
+ } else {
+ /* PHY present, inband mode depends on the capabilities
+ * of both.
+ */
+ }
+
+ /* Legacy, so determine inband depending on the advertising bit */
+ if (linkmode_test_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, advertising))
+ neg_mode = PHYLINK_PCS_NEG_INBAND_ENABLED;
+ else
+ neg_mode = PHYLINK_PCS_NEG_INBAND_DISABLED;
+
return neg_mode;
}
@@ -1151,10 +1250,6 @@ static void phylink_major_config(struct phylink *pl, bool restart,
phylink_dbg(pl, "major config %s\n", phy_modes(state->interface));
- pl->pcs_neg_mode = phylink_pcs_neg_mode(pl->cur_link_an_mode,
- state->interface,
- state->advertising);
-
if (pl->using_mac_select_pcs) {
pcs = pl->mac_ops->mac_select_pcs(pl->config, state->interface);
if (IS_ERR(pcs)) {
@@ -1167,6 +1262,11 @@ static void phylink_major_config(struct phylink *pl, bool restart,
pcs_changed = pcs && pl->pcs != pcs;
}
+ pl->pcs_neg_mode = phylink_pcs_neg_mode(pl, pcs,
+ pl->cur_link_an_mode,
+ state->interface,
+ state->advertising);
+
phylink_pcs_poll_stop(pl);
if (pl->mac_ops->mac_prepare) {
@@ -1257,9 +1357,10 @@ static int phylink_change_inband_advert(struct phylink *pl)
pl->link_config.pause);
/* Recompute the PCS neg mode */
- pl->pcs_neg_mode = phylink_pcs_neg_mode(pl->cur_link_an_mode,
- pl->link_config.interface,
- pl->link_config.advertising);
+ pl->pcs_neg_mode = phylink_pcs_neg_mode(pl, pl->pcs,
+ pl->cur_link_an_mode,
+ pl->link_config.interface,
+ pl->link_config.advertising);
neg_mode = pl->cur_link_an_mode;
if (pl->pcs->neg_mode)
@@ -1365,6 +1466,75 @@ static const char *phylink_pause_to_str(int pause)
}
}
+static void phylink_disable_tx_lpi(struct phylink *pl)
+{
+ phylink_dbg(pl, "disabling tx_lpi\n");
+
+ if (pl->mac_ops->mac_disable_tx_lpi)
+ pl->mac_ops->mac_disable_tx_lpi(pl->config);
+}
+
+static void phylink_enable_tx_lpi(struct phylink *pl)
+{
+ phylink_dbg(pl, "enabling tx_lpi, timer %uus\n",
+ pl->eee_cfg.tx_lpi_timer);
+
+ if (pl->mac_ops->mac_enable_tx_lpi)
+ pl->mac_ops->mac_enable_tx_lpi(pl->config,
+ pl->eee_cfg.tx_lpi_timer);
+}
+
+static bool phylink_eee_is_active(struct phylink *pl)
+{
+ return phylink_init_eee(pl, pl->config->eee_clk_stop_enable) >= 0;
+}
+
+static void phylink_deactivate_eee(struct phylink *pl)
+{
+ phylink_dbg(pl, "deactivating EEE, was %sactive\n",
+ pl->eee_active ? "" : "in");
+
+ if (pl->eee_active) {
+ pl->eee_active = false;
+ phylink_disable_tx_lpi(pl);
+ }
+}
+
+static void phylink_activate_eee(struct phylink *pl)
+{
+ pl->eee_active = phylink_eee_is_active(pl);
+
+ phylink_dbg(pl, "can LPI, EEE enabled, %sactive\n",
+ pl->eee_active ? "" : "in");
+
+ if (pl->eee_active)
+ phylink_enable_tx_lpi(pl);
+}
+
+/* Determine whether the MAC has new EEE support. We detect this by checking
+ * for the two new methods being present, but for DSA it will populate these
+ * anyway, so also check that lpi_capabilities is non-zero.
+ */
+static bool phylink_mac_supports_eee(struct phylink *pl)
+{
+ return pl->mac_ops->mac_disable_tx_lpi &&
+ pl->mac_ops->mac_enable_tx_lpi &&
+ pl->config->lpi_capabilities;
+}
+
+static void phylink_phy_restrict_eee(struct phylink *pl, struct phy_device *phy)
+{
+ __ETHTOOL_DECLARE_LINK_MODE_MASK(eee_supported);
+
+ /* Convert the MAC's LPI capabilities to linkmodes */
+ linkmode_zero(eee_supported);
+ phylink_caps_to_linkmodes(eee_supported, pl->config->lpi_capabilities);
+
+ /* Mask out EEE modes that are not supported */
+ linkmode_and(phy->supported_eee, phy->supported_eee, eee_supported);
+ linkmode_and(phy->advertising_eee, phy->advertising_eee, eee_supported);
+}
+
static void phylink_link_up(struct phylink *pl,
struct phylink_link_state link_state)
{
@@ -1411,6 +1581,9 @@ static void phylink_link_up(struct phylink *pl,
pl->cur_interface, speed, duplex,
!!(link_state.pause & MLO_PAUSE_TX), rx_pause);
+ if (eeecfg_mac_can_tx_lpi(&pl->eee_cfg))
+ phylink_activate_eee(pl);
+
if (ndev)
netif_carrier_on(ndev);
@@ -1427,25 +1600,29 @@ static void phylink_link_down(struct phylink *pl)
if (ndev)
netif_carrier_off(ndev);
+
+ phylink_deactivate_eee(pl);
+
pl->mac_ops->mac_link_down(pl->config, pl->cur_link_an_mode,
pl->cur_interface);
phylink_info(pl, "Link is Down\n");
}
+static bool phylink_link_is_up(struct phylink *pl)
+{
+ return pl->netdev ? netif_carrier_ok(pl->netdev) : pl->old_link_state;
+}
+
static void phylink_resolve(struct work_struct *w)
{
struct phylink *pl = container_of(w, struct phylink, resolve);
struct phylink_link_state link_state;
- struct net_device *ndev = pl->netdev;
bool mac_config = false;
bool retrigger = false;
bool cur_link_state;
mutex_lock(&pl->state_mutex);
- if (pl->netdev)
- cur_link_state = netif_carrier_ok(ndev);
- else
- cur_link_state = pl->old_link_state;
+ cur_link_state = phylink_link_is_up(pl);
if (pl->phylink_disable_state) {
pl->mac_link_dropped = false;
@@ -1681,7 +1858,10 @@ struct phylink *phylink_create(struct phylink_config *config,
linkmode_fill(pl->supported);
linkmode_copy(pl->link_config.advertising, pl->supported);
- phylink_validate(pl, pl->supported, &pl->link_config);
+ phylink_validate(pl, MLO_AN_FIXED, pl->supported, &pl->link_config);
+
+ /* Set the default EEE configuration */
+ pl->eee_cfg = pl->config->eee;
ret = phylink_parse_mode(pl, fwnode);
if (ret < 0) {
@@ -1689,7 +1869,7 @@ struct phylink *phylink_create(struct phylink_config *config,
return ERR_PTR(ret);
}
- if (pl->cfg_link_an_mode == MLO_AN_FIXED) {
+ if (phylink_mode_fixed(pl->cfg_link_an_mode)) {
ret = phylink_parse_fixedlink(pl, fwnode);
if (ret < 0) {
kfree(pl);
@@ -1783,6 +1963,9 @@ static int phylink_validate_phy(struct phylink *pl, struct phy_device *phy,
struct phylink_link_state *state)
{
DECLARE_PHY_INTERFACE_MASK(interfaces);
+ unsigned int mode;
+
+ mode = pl->cfg_link_an_mode;
/* If the PHY provides a bitmap of the interfaces it will be using
* depending on the negotiated media speeds, use this to validate
@@ -1819,10 +2002,13 @@ static int phylink_validate_phy(struct phylink *pl, struct phy_device *phy,
phy->possible_interfaces,
(int)PHY_INTERFACE_MODE_MAX, interfaces);
- return phylink_validate_mask(pl, phy, supported, state,
+ return phylink_validate_mask(pl, phy, mode, supported, state,
interfaces);
}
+ phylink_dbg(pl, "PHY %s doesn't supply possible interfaces\n",
+ phydev_name(phy));
+
/* Check whether we would use rate matching for the proposed interface
* mode.
*/
@@ -1845,7 +2031,7 @@ static int phylink_validate_phy(struct phylink *pl, struct phy_device *phy,
state->interface != PHY_INTERFACE_MODE_USXGMII)
state->interface = PHY_INTERFACE_MODE_NA;
- return phylink_validate(pl, supported, state);
+ return phylink_validate(pl, mode, supported, state);
}
static int phylink_bringup_phy(struct phylink *pl, struct phy_device *phy,
@@ -1902,6 +2088,13 @@ static int phylink_bringup_phy(struct phylink *pl, struct phy_device *phy,
/* Restrict the phy advertisement according to the MAC support. */
linkmode_copy(phy->advertising, config.advertising);
+
+ /* If the MAC supports phylink managed EEE, restrict the EEE
+ * advertisement according to the MAC's LPI capabilities.
+ */
+ if (phylink_mac_supports_eee(pl))
+ phylink_phy_restrict_eee(pl, phy);
+
mutex_unlock(&pl->state_mutex);
mutex_unlock(&phy->lock);
@@ -1923,8 +2116,8 @@ static int phylink_bringup_phy(struct phylink *pl, struct phy_device *phy,
static int phylink_attach_phy(struct phylink *pl, struct phy_device *phy,
phy_interface_t interface)
{
- if (WARN_ON(pl->cfg_link_an_mode == MLO_AN_FIXED ||
- (pl->cfg_link_an_mode == MLO_AN_INBAND &&
+ if (WARN_ON(phylink_mode_fixed(pl->cfg_link_an_mode) ||
+ (phylink_mode_inband(pl->cfg_link_an_mode) &&
phy_interface_mode_is_8023z(interface) && !pl->sfp_bus)))
return -EINVAL;
@@ -2010,14 +2203,14 @@ int phylink_fwnode_phy_connect(struct phylink *pl,
int ret;
/* Fixed links and 802.3z are handled without needing a PHY */
- if (pl->cfg_link_an_mode == MLO_AN_FIXED ||
- (pl->cfg_link_an_mode == MLO_AN_INBAND &&
+ if (phylink_mode_fixed(pl->cfg_link_an_mode) ||
+ (phylink_mode_inband(pl->cfg_link_an_mode) &&
phy_interface_mode_is_8023z(pl->link_interface)))
return 0;
phy_fwnode = fwnode_get_phy_node(fwnode);
if (IS_ERR(phy_fwnode)) {
- if (pl->cfg_link_an_mode == MLO_AN_PHY)
+ if (phylink_mode_phy(pl->cfg_link_an_mode))
return -ENODEV;
return 0;
}
@@ -2164,7 +2357,7 @@ void phylink_start(struct phylink *pl)
phylink_enable_and_run_resolve(pl, PHYLINK_DISABLE_STOPPED);
- if (pl->cfg_link_an_mode == MLO_AN_FIXED && pl->link_gpio) {
+ if (phylink_mode_fixed(pl->cfg_link_an_mode) && pl->link_gpio) {
int irq = gpiod_to_irq(pl->link_gpio);
if (irq > 0) {
@@ -2431,6 +2624,39 @@ int phylink_ethtool_ksettings_get(struct phylink *pl,
}
EXPORT_SYMBOL_GPL(phylink_ethtool_ksettings_get);
+static int phylink_query_inband(struct phylink *pl, phy_interface_t interface)
+{
+ struct phylink_pcs *pcs;
+
+ if (!pl->using_mac_select_pcs)
+ return 0;
+
+ pcs = pl->mac_ops->mac_select_pcs(pl->config, interface);
+ if (!pcs)
+ return 0;
+
+ return phylink_pcs_query_inband(pcs, interface);
+}
+
+static bool phylink_validate_pcs_an(struct phylink *pl,
+ phy_interface_t interface, bool an_state)
+{
+ int link_inband = phylink_query_inband(pl, interface);
+
+ /* If the PCS doesn't implement inband support, be permissive. */
+ if (!(link_inband & LINK_INBAND_VALID))
+ return true;
+
+ /* If the PCS can configure the use of inband, allow either state */
+ if (link_inband & LINK_INBAND_POSSIBLE)
+ return true;
+
+ /* Otherwise, require the AN state to be fixed according to the
+ * required flag.
+ */
+ return an_state == !!(link_inband & LINK_INBAND_REQUIRED);
+}
+
/**
* phylink_ethtool_ksettings_set() - set the link settings
* @pl: a pointer to a &struct phylink returned from phylink_create()
@@ -2492,7 +2718,7 @@ int phylink_ethtool_ksettings_set(struct phylink *pl,
/* If we have a fixed link, refuse to change link parameters.
* If the link parameters match, accept them but do nothing.
*/
- if (pl->cur_link_an_mode == MLO_AN_FIXED) {
+ if (phylink_mode_fixed(pl->cur_link_an_mode)) {
if (s->speed != pl->link_config.speed ||
s->duplex != pl->link_config.duplex)
return -EINVAL;
@@ -2508,7 +2734,7 @@ int phylink_ethtool_ksettings_set(struct phylink *pl,
* is our default case) but do not allow the advertisement to
* be changed. If the advertisement matches, simply return.
*/
- if (pl->cur_link_an_mode == MLO_AN_FIXED) {
+ if (phylink_mode_fixed(pl->cur_link_an_mode)) {
if (!linkmode_equal(config.advertising,
pl->link_config.advertising))
return -EINVAL;
@@ -2546,7 +2772,8 @@ int phylink_ethtool_ksettings_set(struct phylink *pl,
/* Revalidate with the selected interface */
linkmode_copy(support, pl->supported);
- if (phylink_validate(pl, support, &config)) {
+ if (phylink_validate(pl, pl->cur_link_an_mode, support,
+ &config)) {
phylink_err(pl, "validation of %s/%s with support %*pb failed\n",
phylink_an_mode_str(pl->cur_link_an_mode),
phy_modes(config.interface),
@@ -2556,7 +2783,8 @@ int phylink_ethtool_ksettings_set(struct phylink *pl,
} else {
/* Validate without changing the current supported mask. */
linkmode_copy(support, pl->supported);
- if (phylink_validate(pl, support, &config))
+ if (phylink_validate(pl, pl->cur_link_an_mode, support,
+ &config))
return -EINVAL;
}
@@ -2566,6 +2794,14 @@ int phylink_ethtool_ksettings_set(struct phylink *pl,
phylink_is_empty_linkmode(config.advertising))
return -EINVAL;
+ /* Validate the autonegotiation state. We don't have a PHY in this
+ * situation, so the PCS is the media-facing entity.
+ */
+ if (!phylink_validate_pcs_an(pl, config.interface,
+ linkmode_test_bit(ETHTOOL_LINK_MODE_Autoneg_BIT,
+ config.advertising)))
+ return -EINVAL;
+
mutex_lock(&pl->state_mutex);
pl->link_config.speed = config.speed;
pl->link_config.duplex = config.duplex;
@@ -2648,7 +2884,7 @@ int phylink_ethtool_set_pauseparam(struct phylink *pl,
ASSERT_RTNL();
- if (pl->cur_link_an_mode == MLO_AN_FIXED)
+ if (phylink_mode_fixed(pl->cur_link_an_mode))
return -EOPNOTSUPP;
if (!phylink_test(pl->supported, Pause) &&
@@ -2756,6 +2992,8 @@ int phylink_init_eee(struct phylink *pl, bool clk_stop_enable)
if (pl->phydev)
ret = phy_init_eee(pl->phydev, clk_stop_enable);
+ else if (pl->sfp_bus && phylink_mac_supports_eee(pl))
+ ret = 0;
return ret;
}
@@ -2772,8 +3010,19 @@ int phylink_ethtool_get_eee(struct phylink *pl, struct ethtool_eee *eee)
ASSERT_RTNL();
- if (pl->phydev)
+ if (pl->phydev) {
ret = phy_ethtool_get_eee(pl->phydev, eee);
+ } else if (pl->sfp_bus && phylink_mac_supports_eee(pl)) {
+ /* For optical SFPs, ensure that eee->supported is nonzero. */
+ eee->supported = SUPPORTED_FIBRE;
+ ret = 0;
+ }
+
+ if (!ret && phylink_mac_supports_eee(pl)) {
+ /* Overwrite phylib's interpretation of configuration */
+ eeecfg_to_eee(&pl->eee_cfg, eee);
+ eee->eee_active = pl->eee_active;
+ }
return ret;
}
@@ -2787,11 +3036,52 @@ EXPORT_SYMBOL_GPL(phylink_ethtool_get_eee);
int phylink_ethtool_set_eee(struct phylink *pl, struct ethtool_eee *eee)
{
int ret = -EOPNOTSUPP;
+ bool mac_eee;
ASSERT_RTNL();
+ mac_eee = phylink_mac_supports_eee(pl);
+
+ phylink_dbg(pl, "mac %s phylink EEE%s, adv 0x%08x, LPI%s timer %uus\n",
+ mac_eee ? "supports" : "does not support",
+ eee->eee_enabled ? ", enabled" : "", eee->advertised,
+ eee->tx_lpi_enabled ? " enabled" : "", eee->tx_lpi_timer);
+
+ /* Clamp the LPI timer maximum value */
+ if (mac_eee && eee->tx_lpi_timer > pl->config->lpi_timer_limit_us) {
+ eee->tx_lpi_timer = pl->config->lpi_timer_limit_us;
+ phylink_dbg(pl, "LPI timer limited to %uus\n",
+ eee->tx_lpi_timer);
+ }
+
if (pl->phydev)
ret = phy_ethtool_set_eee(pl->phydev, eee);
+ else if (pl->sfp_bus && phylink_mac_supports_eee(pl))
+ ret = 0;
+
+ if (!ret && mac_eee) {
+ bool can_lpi, old_can_lpi;
+
+ mutex_lock(&pl->state_mutex);
+ old_can_lpi = eeecfg_mac_can_tx_lpi(&pl->eee_cfg);
+ eee_to_eeecfg(eee, &pl->eee_cfg);
+ can_lpi = eeecfg_mac_can_tx_lpi(&pl->eee_cfg);
+
+ phylink_dbg(pl, "can_lpi %u -> %u\n", old_can_lpi, can_lpi);
+
+ /* If the link is up, and the configuration changes the
+ * LPI permissive state, deal with the change at the MAC.
+ */
+ if (phylink_link_is_up(pl) && old_can_lpi != can_lpi) {
+ phylink_dbg(pl, "link is up, lpi changed\n");
+ if (can_lpi)
+ phylink_activate_eee(pl);
+ else
+ phylink_deactivate_eee(pl);
+ }
+
+ mutex_unlock(&pl->state_mutex);
+ }
return ret;
}
@@ -3144,8 +3434,7 @@ static void phylink_sfp_set_config(struct phylink *pl, u8 mode,
phylink_mac_initial_config(pl, false);
}
-static int phylink_sfp_config_phy(struct phylink *pl, u8 mode,
- struct phy_device *phy)
+static int phylink_sfp_config_phy(struct phylink *pl, struct phy_device *phy)
{
__ETHTOOL_DECLARE_LINK_MODE_MASK(support1);
__ETHTOOL_DECLARE_LINK_MODE_MASK(support);
@@ -3163,7 +3452,7 @@ static int phylink_sfp_config_phy(struct phylink *pl, u8 mode,
config.pause = MLO_PAUSE_AN;
/* Ignore errors if we're expecting a PHY to attach later */
- ret = phylink_validate(pl, support, &config);
+ ret = phylink_validate(pl, pl->sfp_link_an_mode, support, &config);
if (ret) {
phylink_err(pl, "validation with support %*pb failed: %pe\n",
__ETHTOOL_LINK_MODE_MASK_NBITS, support,
@@ -3181,11 +3470,11 @@ static int phylink_sfp_config_phy(struct phylink *pl, u8 mode,
config.interface = iface;
linkmode_copy(support1, support);
- ret = phylink_validate(pl, support1, &config);
+ ret = phylink_validate(pl, pl->sfp_link_an_mode, support1, &config);
if (ret) {
phylink_err(pl,
"validation of %s/%s with support %*pb failed: %pe\n",
- phylink_an_mode_str(mode),
+ phylink_an_mode_str(pl->sfp_link_an_mode),
phy_modes(config.interface),
__ETHTOOL_LINK_MODE_MASK_NBITS, support,
ERR_PTR(ret));
@@ -3194,7 +3483,7 @@ static int phylink_sfp_config_phy(struct phylink *pl, u8 mode,
pl->link_port = pl->sfp_port;
- phylink_sfp_set_config(pl, mode, support, &config);
+ phylink_sfp_set_config(pl, pl->sfp_link_an_mode, support, &config);
return 0;
}
@@ -3233,8 +3522,8 @@ static int phylink_sfp_config_optical(struct phylink *pl)
/* For all the interfaces that are supported, reduce the sfp_support
* mask to only those link modes that can be supported.
*/
- ret = phylink_validate_mask(pl, NULL, pl->sfp_support, &config,
- interfaces);
+ ret = phylink_validate_mask(pl, NULL, MLO_AN_INBAND, pl->sfp_support,
+ &config, interfaces);
if (ret) {
phylink_err(pl, "unsupported SFP module: validation with support %*pb failed\n",
__ETHTOOL_LINK_MODE_MASK_NBITS, support);
@@ -3250,10 +3539,17 @@ static int phylink_sfp_config_optical(struct phylink *pl)
phylink_dbg(pl, "optical SFP: chosen %s interface\n",
phy_modes(interface));
+ if (!phylink_validate_pcs_an(pl, interface,
+ linkmode_test_bit(ETHTOOL_LINK_MODE_Autoneg_BIT,
+ config.advertising))) {
+ phylink_err(pl, "autoneg setting not compatible with PCS");
+ return -EINVAL;
+ }
+
config.interface = interface;
/* Ignore errors if we're expecting a PHY to attach later */
- ret = phylink_validate(pl, support, &config);
+ ret = phylink_validate(pl, MLO_AN_INBAND, support, &config);
if (ret) {
phylink_err(pl, "validation with support %*pb failed: %pe\n",
__ETHTOOL_LINK_MODE_MASK_NBITS, support,
@@ -3279,6 +3575,7 @@ static int phylink_sfp_module_insert(void *upstream,
phy_interface_zero(pl->sfp_interfaces);
sfp_parse_support(pl->sfp_bus, id, pl->sfp_support, pl->sfp_interfaces);
pl->sfp_port = sfp_parse_port(pl->sfp_bus, id, pl->sfp_support);
+ pl->sfp_link_an_mode = MLO_AN_INBAND;
/* If this module may have a PHY connecting later, defer until later */
pl->sfp_may_have_phy = sfp_may_have_phy(pl->sfp_bus, id);
@@ -3334,21 +3631,12 @@ static void phylink_sfp_link_up(void *upstream)
phylink_enable_and_run_resolve(pl, PHYLINK_DISABLE_LINK);
}
-/* The Broadcom BCM84881 in the Methode DM7052 is unable to provide a SGMII
- * or 802.3z control word, so inband will not work.
- */
-static bool phylink_phy_no_inband(struct phy_device *phy)
-{
- return phy->is_c45 && phy_id_compare(phy->c45_ids.device_ids[1],
- 0xae025150, 0xfffffff0);
-}
-
static int phylink_sfp_connect_phy(void *upstream, struct phy_device *phy)
{
+ DECLARE_PHY_INTERFACE_MASK(interfaces);
struct phylink *pl = upstream;
phy_interface_t interface;
- u8 mode;
- int ret;
+ int link_inband, ret;
/*
* This is the new way of dealing with flow control for PHYs,
@@ -3359,21 +3647,63 @@ static int phylink_sfp_connect_phy(void *upstream, struct phy_device *phy)
*/
phy_support_asym_pause(phy);
- if (phylink_phy_no_inband(phy))
- mode = MLO_AN_PHY;
- else
- mode = MLO_AN_INBAND;
-
/* Set the PHY's host supported interfaces */
phy_interface_and(phy->host_interfaces, phylink_sfp_interfaces,
pl->config->supported_interfaces);
- /* Do the initial configuration */
- ret = phylink_sfp_config_phy(pl, mode, phy);
- if (ret < 0)
- return ret;
+ if (phy_interface_empty(phy->supported_interfaces)) {
+ phylink_dbg(pl, "copper SFP: PHY provides empty supported_interfaces\n");
+
+ /* Do the initial configuration */
+ ret = phylink_sfp_config_phy(pl, phy);
+ if (ret < 0)
+ return ret;
+ } else {
+ phylink_dbg(pl, "copper SFP: interfaces=[mac=%*pbl, sfp=%*pbl]\n",
+ (int)PHY_INTERFACE_MODE_MAX,
+ pl->config->supported_interfaces,
+ (int)PHY_INTERFACE_MODE_MAX,
+ phy->supported_interfaces);
+
+ phy_interface_and(interfaces, phy->supported_interfaces,
+ pl->config->supported_interfaces);
+ interface = phylink_choose_sfp_interface(pl, interfaces);
+ if (interface == PHY_INTERFACE_MODE_NA) {
+ phylink_err(pl,
+ "selection of interface for PHY failed\n");
+ return -EINVAL;
+ }
+
+ phylink_dbg(pl, "copper SFP: chosen %s interface\n",
+ phy_modes(interface));
+
+ link_inband = phy_query_inband(phy, interface);
+ phylink_dbg(pl, "copper SFP: PHY link in-band modes 0x%x\n",
+ link_inband);
+
+ /* If the link inband is valid but the PHY doesn't support
+ * inband, then we have to use PHY mode. E.g. BCM84881.
+ */
+ if (link_inband == LINK_INBAND_VALID)
+ pl->sfp_link_an_mode = MLO_AN_PHY;
+
+ if (pl->cur_link_an_mode != pl->sfp_link_an_mode ||
+ pl->link_config.interface != interface) {
+ pl->link_config.interface = interface;
+ pl->cur_link_an_mode = pl->sfp_link_an_mode;
+
+ phylink_info(pl, "switched to %s/%s link mode\n",
+ phylink_an_mode_str(pl->sfp_link_an_mode),
+ phy_modes(interface));
+ }
+
+ if (!test_bit(PHYLINK_DISABLE_STOPPED,
+ &pl->phylink_disable_state))
+ phylink_mac_initial_config(pl, false);
+ }
interface = pl->link_config.interface;
+
ret = phylink_attach_phy(pl, phy, interface);
if (ret < 0)
return ret;
@@ -3501,12 +3831,18 @@ static void phylink_decode_sgmii_word(struct phylink_link_state *state,
* @lpa: a 16 bit value which stores the USXGMII auto-negotiation word
*
* Helper for MAC PCS supporting the USXGMII protocol and the auto-negotiation
- * code word. Decode the USXGMII code word and populate the corresponding fields
- * (speed, duplex) into the phylink_link_state structure.
+ * code word. Decode the USXGMII code word and populate the corresponding fields
+ * (speed, duplex) into the phylink_link_state structure. If the code word
+ * indicates link is down, or we are unable to decode it, set the link down.
*/
void phylink_decode_usxgmii_word(struct phylink_link_state *state,
uint16_t lpa)
{
+ if (!(lpa & MDIO_USXGMII_LINK)) {
+ state->link = false;
+ return;
+ }
+
switch (lpa & MDIO_USXGMII_SPD_MASK) {
case MDIO_USXGMII_10:
state->speed = SPEED_10;
diff --git a/drivers/net/phy/sff.c b/drivers/net/phy/sff.c
new file mode 100644
index 000000000000..a2eb56118dd4
--- /dev/null
+++ b/drivers/net/phy/sff.c
@@ -0,0 +1,114 @@
+#include <linux/kernel.h>
+#include <linux/sfp.h>
+#include "sff.h"
+
+const char *sff_link_len(char *buf, size_t size, unsigned int length,
+ unsigned int multiplier)
+{
+ if (length == 0)
+ return "unsupported/unspecified";
+
+ if (length == 255) {
+ *buf++ = '>';
+ size -= 1;
+ length -= 1;
+ }
+
+ length *= multiplier;
+
+ if (length >= 1000)
+ snprintf(buf, size, "%u.%0*ukm",
+ length / 1000,
+ multiplier > 100 ? 1 :
+ multiplier > 10 ? 2 : 3,
+ length % 1000);
+ else
+ snprintf(buf, size, "%um", length);
+
+ return buf;
+}
+EXPORT_SYMBOL_GPL(sff_link_len);
+
+const char *sff_bitfield(char *buf, size_t size,
+ const struct sff_bitfield *bits, unsigned int val)
+{
+ char *p = buf;
+ int n;
+
+ *p = '\0';
+ while (bits->mask) {
+ if ((val & bits->mask) == bits->val) {
+ n = snprintf(p, size, "%s%s",
+ buf != p ? ", " : "",
+ bits->str);
+ if (n == size)
+ break;
+ p += n;
+ size -= n;
+ }
+ bits++;
+ }
+
+ return buf;
+}
+EXPORT_SYMBOL_GPL(sff_bitfield);
+
+const char *sff_connector(unsigned int connector)
+{
+ switch (connector) {
+ case SFF8024_CONNECTOR_UNSPEC:
+ return "unknown/unspecified";
+ case SFF8024_CONNECTOR_SC:
+ return "SC";
+ case SFF8024_CONNECTOR_FIBERJACK:
+ return "Fiberjack";
+ case SFF8024_CONNECTOR_LC:
+ return "LC";
+ case SFF8024_CONNECTOR_MT_RJ:
+ return "MT-RJ";
+ case SFF8024_CONNECTOR_MU:
+ return "MU";
+ case SFF8024_CONNECTOR_SG:
+ return "SG";
+ case SFF8024_CONNECTOR_OPTICAL_PIGTAIL:
+ return "Optical pigtail";
+ case SFF8024_CONNECTOR_MPO_1X12:
+ return "MPO 1X12";
+ case SFF8024_CONNECTOR_MPO_2X16:
+ return "MPO 2X16";
+ case SFF8024_CONNECTOR_HSSDC_II:
+ return "HSSDC II";
+ case SFF8024_CONNECTOR_COPPER_PIGTAIL:
+ return "Copper pigtail";
+ case SFF8024_CONNECTOR_RJ45:
+ return "RJ45";
+ case SFF8024_CONNECTOR_MXC_2X16:
+ return "MXC 2X16";
+ default:
+ return "unknown";
+ }
+}
+EXPORT_SYMBOL_GPL(sff_connector);
+
+const char *sff_encoding(unsigned int encoding)
+{
+ switch (encoding) {
+ case SFF8024_ENCODING_UNSPEC:
+ return "unspecified";
+ case SFF8024_ENCODING_8472_64B66B:
+ return "64b66b";
+ case SFF8024_ENCODING_8B10B:
+ return "8b10b";
+ case SFF8024_ENCODING_4B5B:
+ return "4b5b";
+ case SFF8024_ENCODING_NRZ:
+ return "NRZ";
+ case SFF8024_ENCODING_8472_MANCHESTER:
+ return "MANCHESTER";
+ default:
+ return "unknown";
+ }
+}
+EXPORT_SYMBOL_GPL(sff_encoding);
+
+MODULE_LICENSE("GPL");
diff --git a/drivers/net/phy/sff.h b/drivers/net/phy/sff.h
new file mode 100644
index 000000000000..cd7bb7c7ae4a
--- /dev/null
+++ b/drivers/net/phy/sff.h
@@ -0,0 +1,16 @@
+#ifndef SFF_H
+#define SFF_H
+
+struct sff_bitfield {
+ unsigned int mask;
+ unsigned int val;
+ const char *str;
+};
+
+const char *sff_link_len(char *buf, size_t size, unsigned int length,
+ unsigned int multiplier);
+const char *sff_bitfield(char *buf, size_t size,
+ const struct sff_bitfield *bits, unsigned int val);
+const char *sff_connector(unsigned int connector);
+const char *sff_encoding(unsigned int encoding);
+#endif
diff --git a/drivers/net/phy/sfp.c b/drivers/net/phy/sfp.c
index f75c9eb3958e..ddf671621796 100644
--- a/drivers/net/phy/sfp.c
+++ b/drivers/net/phy/sfp.c
@@ -16,6 +16,7 @@
#include <linux/slab.h>
#include <linux/workqueue.h>
+#include "sff.h"
#include "sfp.h"
#include "swphy.h"
@@ -172,6 +173,7 @@ static const enum gpiod_flags gpio_flags[] = {
#define T_WAIT msecs_to_jiffies(50)
#define T_START_UP msecs_to_jiffies(300)
#define T_START_UP_BAD_GPON msecs_to_jiffies(60000)
+#define T_START_UP_COOLED msecs_to_jiffies(90000)
/* t_reset is the time required to assert the TX_DISABLE signal to reset
* an indicated TX_FAULT.
@@ -337,6 +339,7 @@ static const struct sff_data sfp_data = {
static const struct of_device_id sfp_of_match[] = {
{ .compatible = "sff,sff", .data = &sff_data, },
{ .compatible = "sff,sfp", .data = &sfp_data, },
+ { .compatible = "sff,sfp+", .data = &sfp_data, },
{ },
};
MODULE_DEVICE_TABLE(of, sfp_of_match);
@@ -1663,6 +1666,114 @@ static void sfp_hwmon_exit(struct sfp *sfp)
}
#endif
+static const struct sff_bitfield sfp_options[] = {
+ {
+ .mask = SFP_OPTIONS_HIGH_POWER_LEVEL,
+ .val = SFP_OPTIONS_HIGH_POWER_LEVEL,
+ .str = "hpl",
+ }, {
+ .mask = SFP_OPTIONS_PAGING_A2,
+ .val = SFP_OPTIONS_PAGING_A2,
+ .str = "paginga2",
+ }, {
+ .mask = SFP_OPTIONS_RETIMER,
+ .val = SFP_OPTIONS_RETIMER,
+ .str = "retimer",
+ }, {
+ .mask = SFP_OPTIONS_COOLED_XCVR,
+ .val = SFP_OPTIONS_COOLED_XCVR,
+ .str = "cooled",
+ }, {
+ .mask = SFP_OPTIONS_POWER_DECL,
+ .val = SFP_OPTIONS_POWER_DECL,
+ .str = "powerdecl",
+ }, {
+ .mask = SFP_OPTIONS_RX_LINEAR_OUT,
+ .val = SFP_OPTIONS_RX_LINEAR_OUT,
+ .str = "rxlinear",
+ }, {
+ .mask = SFP_OPTIONS_RX_DECISION_THRESH,
+ .val = SFP_OPTIONS_RX_DECISION_THRESH,
+ .str = "rxthresh",
+ }, {
+ .mask = SFP_OPTIONS_TUNABLE_TX,
+ .val = SFP_OPTIONS_TUNABLE_TX,
+ .str = "tunabletx",
+ }, {
+ .mask = SFP_OPTIONS_RATE_SELECT,
+ .val = SFP_OPTIONS_RATE_SELECT,
+ .str = "ratesel",
+ }, {
+ .mask = SFP_OPTIONS_TX_DISABLE,
+ .val = SFP_OPTIONS_TX_DISABLE,
+ .str = "txdisable",
+ }, {
+ .mask = SFP_OPTIONS_TX_FAULT,
+ .val = SFP_OPTIONS_TX_FAULT,
+ .str = "txfault",
+ }, {
+ .mask = SFP_OPTIONS_LOS_INVERTED,
+ .val = SFP_OPTIONS_LOS_INVERTED,
+ .str = "los-",
+ }, {
+ .mask = SFP_OPTIONS_LOS_NORMAL,
+ .val = SFP_OPTIONS_LOS_NORMAL,
+ .str = "los+",
+ }, { }
+};
+
+static const struct sff_bitfield diagmon[] = {
+ {
+ .mask = SFP_DIAGMON_DDM,
+ .val = SFP_DIAGMON_DDM,
+ .str = "ddm",
+ }, {
+ .mask = SFP_DIAGMON_INT_CAL,
+ .val = SFP_DIAGMON_INT_CAL,
+ .str = "intcal",
+ }, {
+ .mask = SFP_DIAGMON_EXT_CAL,
+ .val = SFP_DIAGMON_EXT_CAL,
+ .str = "extcal",
+ }, {
+ .mask = SFP_DIAGMON_RXPWR_AVG,
+ .val = SFP_DIAGMON_RXPWR_AVG,
+ .str = "rxpwravg",
+ }, { }
+};
+
+static const struct sff_bitfield sfp_enhopts[] = {
+ {
+ .mask = SFP_ENHOPTS_ALARMWARN,
+ .val = SFP_ENHOPTS_ALARMWARN,
+ .str = "alarmwarn",
+ }, {
+ .mask = SFP_ENHOPTS_SOFT_TX_DISABLE,
+ .val = SFP_ENHOPTS_SOFT_TX_DISABLE,
+ .str = "soft_tx_dis",
+ }, {
+ .mask = SFP_ENHOPTS_SOFT_TX_FAULT,
+ .val = SFP_ENHOPTS_SOFT_TX_FAULT,
+ .str = "soft_tx_fault",
+ }, {
+ .mask = SFP_ENHOPTS_SOFT_RX_LOS,
+ .val = SFP_ENHOPTS_SOFT_RX_LOS,
+ .str = "soft_rx_los",
+ }, {
+ .mask = SFP_ENHOPTS_SOFT_RATE_SELECT,
+ .val = SFP_ENHOPTS_SOFT_RATE_SELECT,
+ .str = "soft_rs",
+ }, {
+ .mask = SFP_ENHOPTS_APP_SELECT_SFF8079,
+ .val = SFP_ENHOPTS_APP_SELECT_SFF8079,
+ .str = "app_sel",
+ }, {
+ .mask = SFP_ENHOPTS_SOFT_RATE_SFF8431,
+ .val = SFP_ENHOPTS_SOFT_RATE_SFF8431,
+ .str = "soft_r8431",
+ }, { }
+};
+
/* Helpers */
static void sfp_module_tx_disable(struct sfp *sfp)
{
@@ -2164,6 +2275,111 @@ static int sfp_cotsworks_fixup_check(struct sfp *sfp, struct sfp_eeprom_id *id)
return 0;
}
+static void sfp_print_module_info(struct sfp *sfp,
+ const struct sfp_eeprom_id *id, bool cotsworks)
+{
+ unsigned int br_nom, br_min, br_max;
+ char date[9];
+ char options[80];
+
+ /* Cotsworks also gets the date code wrong. */
+ date[0] = id->ext.datecode[4 - 2 * cotsworks];
+ date[1] = id->ext.datecode[5 - 2 * cotsworks];
+ date[2] = '-';
+ date[3] = id->ext.datecode[2 + 2 * cotsworks];
+ date[4] = id->ext.datecode[3 + 2 * cotsworks];
+ date[5] = '-';
+ date[6] = id->ext.datecode[0];
+ date[7] = id->ext.datecode[1];
+ date[8] = '\0';
+
+ if (id->base.br_nominal == 0) {
+ br_min = br_nom = br_max = 0;
+ } else if (id->base.br_nominal == 255) {
+ br_nom = 250 * id->ext.br_max;
+ br_max = br_nom + br_nom * id->ext.br_min / 100;
+ br_min = br_nom - br_nom * id->ext.br_min / 100;
+ } else {
+ br_nom = id->base.br_nominal * 100;
+ br_min = br_nom - id->base.br_nominal * id->ext.br_min;
+ br_max = br_nom + id->base.br_nominal * id->ext.br_max;
+ }
+
+ dev_info(sfp->dev, "module %.*s %.*s rev %.*s sn %.*s dc %s\n",
+ (int)sizeof(id->base.vendor_name), id->base.vendor_name,
+ (int)sizeof(id->base.vendor_pn), id->base.vendor_pn,
+ (int)sizeof(id->base.vendor_rev), id->base.vendor_rev,
+ (int)sizeof(id->ext.vendor_sn), id->ext.vendor_sn, date);
+ dev_info(sfp->dev, " %s connector, encoding %s, bitrate %u.%03u (%u.%03u-%u.%03u) Gbps\n",
+ sff_connector(id->base.connector),
+ sff_encoding(id->base.encoding),
+ br_nom / 1000, br_nom % 1000,
+ br_min / 1000, br_min % 1000, br_max / 1000, br_max % 1000);
+ dev_info(sfp->dev, " 1000BaseSX%c 1000BaseLX%c 1000BaseCX%c 1000BaseT%c 100BaseLX%c 100BaseFX%c BaseBX10%c BasePX%c\n",
+ id->base.e1000_base_sx ? '+' : '-',
+ id->base.e1000_base_lx ? '+' : '-',
+ id->base.e1000_base_cx ? '+' : '-',
+ id->base.e1000_base_t ? '+' : '-',
+ id->base.e100_base_lx ? '+' : '-',
+ id->base.e100_base_fx ? '+' : '-',
+ id->base.e_base_bx10 ? '+' : '-',
+ id->base.e_base_px ? '+' : '-');
+ dev_info(sfp->dev, " 10GBaseSR%c 10GBaseLR%c 10GBaseLRM%c 10GBaseER%c\n",
+ id->base.e10g_base_sr ? '+' : '-',
+ id->base.e10g_base_lr ? '+' : '-',
+ id->base.e10g_base_lrm ? '+' : '-',
+ id->base.e10g_base_er ? '+' : '-');
+
+ if (!id->base.sfp_ct_passive && !id->base.sfp_ct_active &&
+ !id->base.e1000_base_t) {
+ char len_9um[16], len_om[16];
+
+ dev_info(sfp->dev, " Wavelength %unm, fiber lengths:\n",
+ be16_to_cpup(&id->base.optical_wavelength));
+
+ if (id->base.link_len[0] == 255)
+ strcpy(len_9um, ">254km");
+ else if (id->base.link_len[1] && id->base.link_len[1] != 255)
+ sprintf(len_9um, "%um",
+ id->base.link_len[1] * 100);
+ else if (id->base.link_len[0])
+ sprintf(len_9um, "%ukm", id->base.link_len[0]);
+ else if (id->base.link_len[1] == 255)
+ strcpy(len_9um, ">25.4km");
+ else
+ strcpy(len_9um, "unsupported");
+
+ dev_info(sfp->dev, " 9µm SM : %s\n", len_9um);
+ dev_info(sfp->dev, " 62.5µm MM OM1: %s\n",
+ sff_link_len(len_om, sizeof(len_om),
+ id->base.link_len[3], 10));
+ dev_info(sfp->dev, " 50µm MM OM2: %s\n",
+ sff_link_len(len_om, sizeof(len_om),
+ id->base.link_len[2], 10));
+ dev_info(sfp->dev, " 50µm MM OM3: %s\n",
+ sff_link_len(len_om, sizeof(len_om),
+ id->base.link_len[5], 10));
+ dev_info(sfp->dev, " 50µm MM OM4: %s\n",
+ sff_link_len(len_om, sizeof(len_om),
+ id->base.link_len[4], 10));
+ } else {
+ char len[16];
+ dev_info(sfp->dev, " Copper length: %s\n",
+ sff_link_len(len, sizeof(len),
+ id->base.link_len[4], 1));
+ }
+
+ dev_info(sfp->dev, " Options: %s\n",
+ sff_bitfield(options, sizeof(options), sfp_options,
+ be16_to_cpu(id->ext.options)));
+ dev_info(sfp->dev, " Diagnostics: %s\n",
+ sff_bitfield(options, sizeof(options), diagmon,
+ id->ext.diagmon));
+ dev_info(sfp->dev, " EnhOpts: %s\n",
+ sff_bitfield(options, sizeof(options), sfp_enhopts,
+ id->ext.enhopts));
+}
+
static int sfp_module_parse_sff8472(struct sfp *sfp)
{
/* If the module requires address swap mode, warn about it */
@@ -2230,9 +2446,9 @@ static int sfp_sm_mod_probe(struct sfp *sfp, bool report)
}
}
- /* Cotsworks do not seem to update the checksums when they
- * do the final programming with the final module part number,
- * serial number and date code.
+ /* Cotsworks do not seem to update the checksums when they update the
+ * module part number, serial number and date code. They also format
+ * the date code incorrectly.
*/
cotsworks = !memcmp(id.base.vendor_name, "COTSWORKS ", 16);
cotsworks_sfbg = !memcmp(id.base.vendor_pn, "SFBG", 4);
@@ -2293,14 +2509,9 @@ static int sfp_sm_mod_probe(struct sfp *sfp, bool report)
}
}
- sfp->id = id;
+ sfp_print_module_info(sfp, &id, cotsworks);
- dev_info(sfp->dev, "module %.*s %.*s rev %.*s sn %.*s dc %.*s\n",
- (int)sizeof(id.base.vendor_name), id.base.vendor_name,
- (int)sizeof(id.base.vendor_pn), id.base.vendor_pn,
- (int)sizeof(id.base.vendor_rev), id.base.vendor_rev,
- (int)sizeof(id.ext.vendor_sn), id.ext.vendor_sn,
- (int)sizeof(id.ext.datecode), id.ext.datecode);
+ sfp->id = id;
/* Check whether we support this module */
if (!sfp->type->module_supported(&id)) {
@@ -2334,8 +2545,11 @@ static int sfp_sm_mod_probe(struct sfp *sfp, bool report)
mask |= SFP_F_RS0;
if (sfp->gpio[GPIO_RS1])
mask |= SFP_F_RS1;
+ if (id.ext.options & cpu_to_be16(SFP_OPTIONS_COOLED_XCVR))
+ sfp->module_t_start_up = T_START_UP_COOLED;
+ else
+ sfp->module_t_start_up = T_START_UP;
- sfp->module_t_start_up = T_START_UP;
sfp->module_t_wait = T_WAIT;
sfp->phy_t_retry = T_PHY_RETRY;
@@ -2585,10 +2799,10 @@ static void sfp_sm_main(struct sfp *sfp, unsigned int event)
break;
if (sfp->state & SFP_F_TX_FAULT) {
- /* Wait up to t_init (SFF-8472) or t_start_up (SFF-8431)
- * from the TX_DISABLE deassertion for the module to
- * initialise, which is indicated by TX_FAULT
- * deasserting.
+ /* Wait up to t_init (SFF-8472), t_start_up (SFF-8431),
+ * or t_start_up_cooled (SFF-8431) from the TX_DISABLE
+ * deassertion for the module to initialise, which is
+ * indicated by TX_FAULT deasserting.
*/
timeout = sfp->module_t_start_up;
if (timeout > sfp->module_t_wait)
@@ -2607,8 +2821,8 @@ static void sfp_sm_main(struct sfp *sfp, unsigned int event)
case SFP_S_INIT:
if (event == SFP_E_TIMEOUT && sfp->state & SFP_F_TX_FAULT) {
- /* TX_FAULT is still asserted after t_init
- * or t_start_up, so assume there is a fault.
+ /* TX_FAULT is still asserted after t_init, t_start_up
+ * or t_start_up_cooled, so assume there is a fault.
*/
sfp_sm_fault(sfp, SFP_S_INIT_TX_FAULT,
sfp->sm_fault_retries == N_FAULT_INIT);
diff --git a/drivers/net/phy/xilinx_gmii2rgmii.c b/drivers/net/phy/xilinx_gmii2rgmii.c
index 7fd9fe6a602b..7b1bc5fcef9b 100644
--- a/drivers/net/phy/xilinx_gmii2rgmii.c
+++ b/drivers/net/phy/xilinx_gmii2rgmii.c
@@ -22,7 +22,7 @@
struct gmii2rgmii {
struct phy_device *phy_dev;
- struct phy_driver *phy_drv;
+ const struct phy_driver *phy_drv;
struct phy_driver conv_phy_drv;
struct mdio_device *mdio;
};
diff --git a/drivers/phy/marvell/phy-armada38x-comphy.c b/drivers/phy/marvell/phy-armada38x-comphy.c
index b7d99861526a..a0066e86bb42 100644
--- a/drivers/phy/marvell/phy-armada38x-comphy.c
+++ b/drivers/phy/marvell/phy-armada38x-comphy.c
@@ -16,32 +16,49 @@
#define MAX_A38X_COMPHY 6
#define MAX_A38X_PORTS 3
+/* Common PHY registers */
+#define COMPHY_REG_SIZE 0x28
#define COMPHY_CFG1 0x00
+#define COMPHY_CFG1_RX_INIT BIT(30)
#define COMPHY_CFG1_GEN_TX(x) ((x) << 26)
#define COMPHY_CFG1_GEN_TX_MSK COMPHY_CFG1_GEN_TX(15)
#define COMPHY_CFG1_GEN_RX(x) ((x) << 22)
#define COMPHY_CFG1_GEN_RX_MSK COMPHY_CFG1_GEN_RX(15)
+#define COMPHY_CFG1_POWER_UP_TX BIT(18)
+#define COMPHY_CFG1_POWER_UP_RX BIT(17)
+#define COMPHY_CFG1_POWER_UP_PLL BIT(16)
#define GEN_SGMII_1_25GBPS 6
#define GEN_SGMII_3_125GBPS 8
#define COMPHY_STAT1 0x18
#define COMPHY_STAT1_PLL_RDY_TX BIT(3)
#define COMPHY_STAT1_PLL_RDY_RX BIT(2)
+#define COMPHY_STAT1_RX_INIT_DONE BIT(0)
#define COMPHY_SELECTOR 0xfc
+/* Common PHY and Pipe Registers */
+#define PIPE_REG_SIZE 0x800
+#define PIPE_POWER_CTRL 0x148
+#define PIPE_POWER_CTRL_SFT_RST_NO_REG BIT(10)
+/* u-boot sets bit 0 during comphy initialisation, but it is undocumented */
+#define PIPE_POWER_CTRL_BIT0 BIT(0)
+
struct a38x_comphy;
struct a38x_comphy_lane {
void __iomem *base;
+ void __iomem *pipe;
struct a38x_comphy *priv;
unsigned int n;
int port;
+ u8 gen;
};
struct a38x_comphy {
void __iomem *base;
+ void __iomem *pipe;
void __iomem *conf;
struct device *dev;
struct a38x_comphy_lane lane[MAX_A38X_COMPHY];
@@ -89,6 +106,7 @@ static void a38x_comphy_set_speed(struct a38x_comphy_lane *lane,
COMPHY_CFG1_GEN_RX(gen_rx));
}
+/* Poll every 1ms for 150ms for a status */
static int a38x_comphy_poll(struct a38x_comphy_lane *lane,
unsigned int offset, u32 mask, u32 value)
{
@@ -106,6 +124,97 @@ static int a38x_comphy_poll(struct a38x_comphy_lane *lane,
return ret;
}
+static int a38x_comphy_power_up(struct a38x_comphy_lane *lane)
+{
+ /* Power up TX, RX and PLL */
+ a38x_comphy_set_reg(lane, COMPHY_CFG1, 0,
+ COMPHY_CFG1_POWER_UP_TX | COMPHY_CFG1_POWER_UP_RX |
+ COMPHY_CFG1_POWER_UP_PLL);
+
+ /* Wait for power up */
+ return a38x_comphy_poll(lane, COMPHY_STAT1,
+ COMPHY_STAT1_PLL_RDY_TX |
+ COMPHY_STAT1_PLL_RDY_RX,
+ COMPHY_STAT1_PLL_RDY_TX |
+ COMPHY_STAT1_PLL_RDY_RX);
+}
+
+static int a38x_comphy_rx_init(struct a38x_comphy_lane *lane)
+{
+ int ret;
+
+ /* Perform RX init */
+ a38x_comphy_set_reg(lane, COMPHY_CFG1, 0, COMPHY_CFG1_RX_INIT);
+
+ /* Wait for RX init done */
+ ret = a38x_comphy_poll(lane, COMPHY_STAT1, COMPHY_STAT1_RX_INIT_DONE,
+ COMPHY_STAT1_RX_INIT_DONE);
+
+ /* Clear RX init */
+ a38x_comphy_set_reg(lane, COMPHY_CFG1, COMPHY_CFG1_RX_INIT, 0);
+
+ return ret;
+}
+
+static int a38x_comphy_power_off(struct phy *phy)
+{
+ struct a38x_comphy_lane *lane = phy_get_drvdata(phy);
+
+ a38x_set_conf(lane, false);
+
+ /* Soft reset */
+ if (lane->pipe) {
+ u32 rst = PIPE_POWER_CTRL_SFT_RST_NO_REG | PIPE_POWER_CTRL_BIT0;
+ u32 val;
+
+ val = readl_relaxed(lane->pipe + PIPE_POWER_CTRL);
+ writel(val | rst, lane->pipe + PIPE_POWER_CTRL);
+ }
+
+ a38x_comphy_set_reg(lane, COMPHY_CFG1,
+ COMPHY_CFG1_POWER_UP_TX | COMPHY_CFG1_POWER_UP_RX |
+ COMPHY_CFG1_POWER_UP_PLL, 0);
+
+ return 0;
+}
+
+static int a38x_comphy_power_on(struct phy *phy)
+{
+ struct a38x_comphy_lane *lane = phy_get_drvdata(phy);
+ int ret;
+
+ /* Program the GEN settings */
+ a38x_comphy_set_speed(lane, lane->gen, lane->gen);
+
+ /* Soft reset */
+ if (lane->pipe) {
+ u32 rst = PIPE_POWER_CTRL_SFT_RST_NO_REG | PIPE_POWER_CTRL_BIT0;
+ u32 val;
+
+ val = readl_relaxed(lane->pipe + PIPE_POWER_CTRL);
+ writel(val | rst, lane->pipe + PIPE_POWER_CTRL);
+ writel(val & ~rst, lane->pipe + PIPE_POWER_CTRL);
+ }
+
+ /* Power up TX, RX and PLL and wait */
+ ret = a38x_comphy_power_up(lane);
+ if (ret)
+ goto fail;
+
+ /* Perform RX init */
+ ret = a38x_comphy_rx_init(lane);
+ if (ret)
+ goto fail;
+
+ a38x_set_conf(lane, true);
+
+ return 0;
+
+fail:
+ a38x_comphy_power_off(phy);
+ return ret;
+}
+
/*
* We only support changing the speed for comphys configured for GBE.
* Since that is all we do, we only poll for PLL ready status.
@@ -114,7 +223,6 @@ static int a38x_comphy_set_mode(struct phy *phy, enum phy_mode mode, int sub)
{
struct a38x_comphy_lane *lane = phy_get_drvdata(phy);
unsigned int gen;
- int ret;
if (mode != PHY_MODE_ETHERNET)
return -EINVAL;
@@ -133,23 +241,14 @@ static int a38x_comphy_set_mode(struct phy *phy, enum phy_mode mode, int sub)
return -EINVAL;
}
- a38x_set_conf(lane, false);
-
- a38x_comphy_set_speed(lane, gen, gen);
-
- ret = a38x_comphy_poll(lane, COMPHY_STAT1,
- COMPHY_STAT1_PLL_RDY_TX |
- COMPHY_STAT1_PLL_RDY_RX,
- COMPHY_STAT1_PLL_RDY_TX |
- COMPHY_STAT1_PLL_RDY_RX);
-
- if (ret == 0)
- a38x_set_conf(lane, true);
+ lane->gen = gen;
- return ret;
+ return 0;
}
static const struct phy_ops a38x_comphy_ops = {
+ .power_on = a38x_comphy_power_on,
+ .power_off = a38x_comphy_power_off,
.set_mode = a38x_comphy_set_mode,
.owner = THIS_MODULE,
};
@@ -194,6 +293,7 @@ static int a38x_comphy_probe(struct platform_device *pdev)
struct a38x_comphy *priv;
struct resource *res;
void __iomem *base;
+ void __iomem *pipe = NULL;
priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
@@ -203,8 +303,16 @@ static int a38x_comphy_probe(struct platform_device *pdev)
if (IS_ERR(base))
return PTR_ERR(base);
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "pipe");
+ if (res) {
+ pipe = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(base))
+ return PTR_ERR(base);
+ }
+
priv->dev = &pdev->dev;
priv->base = base;
+ priv->pipe = pipe;
/* Optional */
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "conf");
@@ -231,16 +339,19 @@ static int a38x_comphy_probe(struct platform_device *pdev)
continue;
}
+ priv->lane[val].base = base + COMPHY_REG_SIZE * val;
+ if (pipe)
+ priv->lane[val].pipe = pipe + PIPE_REG_SIZE * val;
+ priv->lane[val].priv = priv;
+ priv->lane[val].n = val;
+ priv->lane[val].port = -1;
+
phy = devm_phy_create(&pdev->dev, child, &a38x_comphy_ops);
if (IS_ERR(phy)) {
of_node_put(child);
return PTR_ERR(phy);
}
- priv->lane[val].base = base + 0x28 * val;
- priv->lane[val].priv = priv;
- priv->lane[val].n = val;
- priv->lane[val].port = -1;
phy_set_drvdata(phy, &priv->lane[val]);
}
diff --git a/include/linux/mdio.h b/include/linux/mdio.h
index 79ceee3c8673..08d091913808 100644
--- a/include/linux/mdio.h
+++ b/include/linux/mdio.h
@@ -32,6 +32,8 @@ struct mdio_device {
char modalias[MDIO_NAME_SIZE];
int (*bus_match)(struct device *dev, struct device_driver *drv);
+ int (*bus_uevent)(struct mdio_device *mdiodev,
+ struct kobj_uevent_env *env);
void (*device_free)(struct mdio_device *mdiodev);
void (*device_remove)(struct mdio_device *mdiodev);
diff --git a/include/linux/phy.h b/include/linux/phy.h
index 684efaeca07c..ded6ce314710 100644
--- a/include/linux/phy.h
+++ b/include/linux/phy.h
@@ -638,7 +638,7 @@ struct phy_device {
/* Information about the PHY type */
/* And management functions */
- struct phy_driver *drv;
+ const struct phy_driver *drv;
struct device_link *devlink;
@@ -691,6 +691,15 @@ struct phy_device {
u8 master_slave_set;
u8 master_slave_state;
+ /*
+ * private to phylib: the resolved pause state - only valid if
+ * resolved_pause_valid is true. only phy drivers and phylib
+ * should touch this.
+ */
+ bool resolved_pause_valid;
+ bool resolved_tx_pause;
+ bool resolved_rx_pause;
+
/* Union of PHY and Attached devices' supported link modes */
/* See ethtool.h for more info */
__ETHTOOL_DECLARE_LINK_MODE_MASK(supported);
@@ -706,6 +715,9 @@ struct phy_device {
/* Host supported PHY interface types. Should be ignored if empty. */
DECLARE_PHY_INTERFACE_MASK(host_interfaces);
+ /* supported PHY interface types */
+ DECLARE_PHY_INTERFACE_MASK(supported_interfaces);
+
/* Energy efficient ethernet modes which should be prohibited */
u32 eee_broken_modes;
@@ -796,6 +808,24 @@ struct phy_tdr_config {
#define PHY_PAIR_ALL -1
/**
+ * enum link_inband_signalling - inband signalling modes that are supported
+ *
+ * @LINK_INBAND_VALID: inband signalling report is valid
+ * @LINK_INBAND_POSSIBLE: inband signalling can be used
+ * @LINK_INBAND_REQUIRED: inband signalling is required
+ *
+ * The possible and required bits can only be used if the valid bit is set.
+ * If possible is clear, that means inband signalling can not be used.
+ * Required is only valid when possible is set, and means that inband
+ * signalling must be used.
+ */
+enum link_inband_signalling {
+ LINK_INBAND_VALID = BIT(0),
+ LINK_INBAND_POSSIBLE = BIT(1),
+ LINK_INBAND_REQUIRED = BIT(2),
+};
+
+/**
* struct phy_plca_cfg - Configuration of the PLCA (Physical Layer Collision
* Avoidance) Reconciliation Sublayer.
*
@@ -925,6 +955,14 @@ struct phy_driver {
int (*get_features)(struct phy_device *phydev);
/**
+ * @query_inband: query whether inband is supported for the given PHY
+ * interface mode. Returns a bitmask of bits defined by enum
+ * link_inband_signalling.
+ */
+ unsigned int (*query_inband)(struct phy_device *phydev,
+ phy_interface_t interface);
+
+ /**
* @get_rate_matching: Get the supported type of rate matching for a
* particular phy interface. This is used by phy consumers to determine
* whether to advertise lower-speed modes for that interface. It is
@@ -942,6 +980,9 @@ struct phy_driver {
/** @resume: Resume the hardware, restoring state if needed */
int (*resume)(struct phy_device *phydev);
+ int (*start)(struct phy_device *phydev);
+ void (*stop)(struct phy_device *phydev);
+
/**
* @config_aneg: Configures the advertisement and resets
* autonegotiation if phydev->autoneg is on,
@@ -1744,6 +1785,8 @@ int phy_config_aneg(struct phy_device *phydev);
int _phy_start_aneg(struct phy_device *phydev);
int phy_start_aneg(struct phy_device *phydev);
int phy_aneg_done(struct phy_device *phydev);
+unsigned int phy_query_inband(struct phy_device *phydev,
+ phy_interface_t interface);
int phy_speed_down(struct phy_device *phydev, bool sync);
int phy_speed_up(struct phy_device *phydev);
bool phy_check_valid(int speed, int duplex, unsigned long *features);
diff --git a/include/linux/phylink.h b/include/linux/phylink.h
index d589f89c612c..8664b3442a3a 100644
--- a/include/linux/phylink.h
+++ b/include/linux/phylink.h
@@ -5,6 +5,8 @@
#include <linux/spinlock.h>
#include <linux/workqueue.h>
+#include <net/eee.h>
+
struct device_node;
struct ethtool_cmd;
struct fwnode_handle;
@@ -93,11 +95,26 @@ enum {
MAC_400000FD = BIT(18),
};
-static inline bool phylink_autoneg_inband(unsigned int mode)
+static inline bool phylink_mode_inband(unsigned int mode)
{
return mode == MLO_AN_INBAND;
}
+static inline bool phylink_mode_fixed(unsigned int mode)
+{
+ return mode == MLO_AN_FIXED;
+}
+
+static inline bool phylink_mode_phy(unsigned int mode)
+{
+ return mode == MLO_AN_PHY;
+}
+
+static inline bool phylink_autoneg_inband(unsigned int mode)
+{
+ return phylink_mode_inband(mode);
+}
+
/**
* struct phylink_link_state - link state structure
* @advertising: ethtool bitmask containing advertised link modes
@@ -139,11 +156,15 @@ enum phylink_op_type {
* if MAC link is at %MLO_AN_FIXED mode.
* @mac_managed_pm: if true, indicate the MAC driver is responsible for PHY PM.
* @ovr_an_inband: if true, override PCS to MLO_AN_INBAND
+ * @eee_clk_stop_enable: if true, PHY can stop the receive clock during LPI
* @get_fixed_state: callback to execute to determine the fixed link state,
* if MAC link is at %MLO_AN_FIXED mode.
* @supported_interfaces: bitmap describing which PHY_INTERFACE_MODE_xxx
* are supported by the MAC/PCS.
* @mac_capabilities: MAC pause/speed/duplex capabilities.
+ * @lpi_capabilities: MAC speeds which can support LPI signalling
+ * @eee: default EEE configuration.
+ * @lpi_timer_limit_us: Maximum (inclusive) value of the EEE LPI timer.
*/
struct phylink_config {
struct device *dev;
@@ -151,10 +172,14 @@ struct phylink_config {
bool poll_fixed_state;
bool mac_managed_pm;
bool ovr_an_inband;
+ bool eee_clk_stop_enable;
void (*get_fixed_state)(struct phylink_config *config,
struct phylink_link_state *state);
DECLARE_PHY_INTERFACE_MASK(supported_interfaces);
unsigned long mac_capabilities;
+ unsigned long lpi_capabilities;
+ struct eee_config eee;
+ u32 lpi_timer_limit_us;
};
void phylink_limit_mac_speed(struct phylink_config *config, u32 max_speed);
@@ -168,6 +193,8 @@ void phylink_limit_mac_speed(struct phylink_config *config, u32 max_speed);
* @mac_finish: finish a major reconfiguration of the interface.
* @mac_link_down: take the link down.
* @mac_link_up: allow the link to come up.
+ * @mac_disable_tx_lpi: disable LPI.
+ * @mac_enable_tx_lpi: enable and configure LPI.
*
* The individual methods are described more fully below.
*/
@@ -188,6 +215,8 @@ struct phylink_mac_ops {
struct phy_device *phy, unsigned int mode,
phy_interface_t interface, int speed, int duplex,
bool tx_pause, bool rx_pause);
+ void (*mac_disable_tx_lpi)(struct phylink_config *config);
+ void (*mac_enable_tx_lpi)(struct phylink_config *config, u32 timer);
};
#if 0 /* For kernel-doc purposes only. */
@@ -382,6 +411,28 @@ void mac_link_down(struct phylink_config *config, unsigned int mode,
void mac_link_up(struct phylink_config *config, struct phy_device *phy,
unsigned int mode, phy_interface_t interface,
int speed, int duplex, bool tx_pause, bool rx_pause);
+
+/**
+ * mac_disable_tx_lpi() - disable LPI generation at the MAC
+ * @config: a pointer to a &struct phylink_config.
+ *
+ * Disable generation of LPI at the MAC, effectively preventing the MAC
+ * from indicating that it is idle.
+ */
+void mac_disable_tx_lpi(struct phylink_config *config);
+
+/**
+ * mac_enable_tx_lpi() - configure and enable LPI generation at the MAC
+ * @config: a pointer to a &struct phylink_config.
+ * @timer: LPI timeout in microseconds.
+ *
+ * Configure the LPI timeout accordingly. This will only be called when
+ * the link is already up, to cater for situations where the hardware
+ * needs to be programmed according to the link speed.
+ *
+ * Enable LPI generation at the MAC.
+ */
+void mac_enable_tx_lpi(struct phylink_config *config, u32 timer);
#endif
struct phylink_pcs_ops;
@@ -409,6 +460,7 @@ struct phylink_pcs {
/**
* struct phylink_pcs_ops - MAC PCS operations structure.
* @pcs_validate: validate the link configuration.
+ * @pcs_query_inband: query inband support for interface mode.
* @pcs_enable: enable the PCS.
* @pcs_disable: disable the PCS.
* @pcs_pre_config: pre-mac_config method (for errata)
@@ -420,8 +472,11 @@ struct phylink_pcs {
* (where necessary).
*/
struct phylink_pcs_ops {
- int (*pcs_validate)(struct phylink_pcs *pcs, unsigned long *supported,
+ int (*pcs_validate)(struct phylink_pcs *pcs, unsigned int mode,
+ unsigned long *supported,
const struct phylink_link_state *state);
+ unsigned int (*pcs_query_inband)(struct phylink_pcs *pcs,
+ phy_interface_t interface);
int (*pcs_enable)(struct phylink_pcs *pcs);
void (*pcs_disable)(struct phylink_pcs *pcs);
void (*pcs_pre_config)(struct phylink_pcs *pcs,
@@ -443,6 +498,7 @@ struct phylink_pcs_ops {
/**
* pcs_validate() - validate the link configuration.
* @pcs: a pointer to a &struct phylink_pcs.
+ * @mode: link autonegotiation mode
* @supported: ethtool bitmask for supported link modes.
* @state: a const pointer to a &struct phylink_link_state.
*
@@ -454,8 +510,22 @@ struct phylink_pcs_ops {
* Returns -EINVAL if the interface mode/autoneg mode is not supported.
* Returns non-zero positive if the link state can be supported.
*/
-int pcs_validate(struct phylink_pcs *pcs, unsigned long *supported,
- const struct phylink_link_state *state);
+int pcs_validate(struct phylink_pcs *pcs, unsigned int mode,
+ unsigned long *supported, struct phylink_link_state *state);
+
+/**
+ * pcs_query_inband - query inband support for interface mode.
+ * @pcs: a pointer to a &struct phylink_pcs.
+ * @interface: interface mode to be queried
+ *
+ * Returns zero if it is unknown what in-band signalling is supported by the
+ * PHY (e.g. because the PHY driver doesn't implement the method.) Otherwise,
+ * returns a bit mask of the LINK_INBAND_* values from
+ * &enum link_inband_signalling to describe which inband modes are supported
+ * for this interface mode.
+ */
+unsigned int pcs_query_inband(struct phylink_pcs *pcs,
+ phy_interface_t interface);
/**
* pcs_enable() - enable the PCS.
diff --git a/include/net/dsa.h b/include/net/dsa.h
index 82135fbdb1e6..340f04d21cd1 100644
--- a/include/net/dsa.h
+++ b/include/net/dsa.h
@@ -327,6 +327,12 @@ struct dsa_port {
};
};
+static inline struct dsa_port *
+dsa_phylink_to_port(struct phylink_config *config)
+{
+ return container_of(config, struct dsa_port, pl_config);
+}
+
/* TODO: ideally DSA ports would have a single dp->link_dp member,
* and no dst->rtable nor this struct dsa_link would be needed,
* but this would require some more complex tree walking,
@@ -452,6 +458,11 @@ struct dsa_switch {
const struct dsa_switch_ops *ops;
/*
+ * Allow a DSA switch driver to override the phylink MAC ops
+ */
+ const struct phylink_mac_ops *phylink_mac_ops;
+
+ /*
* User mii_bus and devices for the individual ports.
*/
u32 phys_mii_mask;
@@ -891,6 +902,9 @@ struct dsa_switch_ops {
struct phy_device *phydev,
int speed, int duplex,
bool tx_pause, bool rx_pause);
+ void (*phylink_mac_disable_tx_lpi)(struct dsa_switch *ds, int port);
+ void (*phylink_mac_enable_tx_lpi)(struct dsa_switch *ds, int port,
+ u32 timer);
void (*phylink_fixed_state)(struct dsa_switch *ds, int port,
struct phylink_link_state *state);
/*
diff --git a/include/net/eee.h b/include/net/eee.h
new file mode 100644
index 000000000000..d353b79ae79f
--- /dev/null
+++ b/include/net/eee.h
@@ -0,0 +1,38 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+#ifndef _EEE_H
+#define _EEE_H
+
+#include <linux/types.h>
+
+struct eee_config {
+ u32 tx_lpi_timer;
+ bool tx_lpi_enabled;
+ bool eee_enabled;
+};
+
+static inline bool eeecfg_mac_can_tx_lpi(const struct eee_config *eeecfg)
+{
+ /* eee_enabled is the master on/off */
+ if (!eeecfg->eee_enabled || !eeecfg->tx_lpi_enabled)
+ return false;
+
+ return true;
+}
+
+static inline void eeecfg_to_eee(const struct eee_config *eeecfg,
+ struct ethtool_eee *eee)
+{
+ eee->tx_lpi_timer = eeecfg->tx_lpi_timer;
+ eee->tx_lpi_enabled = eeecfg->tx_lpi_enabled;
+ eee->eee_enabled = eeecfg->eee_enabled;
+}
+
+static inline void eee_to_eeecfg(const struct ethtool_eee *eee,
+ struct eee_config *eeecfg)
+{
+ eeecfg->tx_lpi_timer = eee->tx_lpi_timer;
+ eeecfg->tx_lpi_enabled = eee->tx_lpi_enabled;
+ eeecfg->eee_enabled = eee->eee_enabled;
+}
+
+#endif
diff --git a/net/dsa/dsa.c b/net/dsa/dsa.c
index ac7be864e80d..a2479f133005 100644
--- a/net/dsa/dsa.c
+++ b/net/dsa/dsa.c
@@ -1510,6 +1510,17 @@ static int dsa_switch_probe(struct dsa_switch *ds)
if (!ds->num_ports)
return -EINVAL;
+ if (ds->phylink_mac_ops) {
+ if (ds->ops->phylink_mac_select_pcs ||
+ ds->ops->phylink_mac_prepare ||
+ ds->ops->phylink_mac_config ||
+ ds->ops->phylink_mac_finish ||
+ ds->ops->phylink_mac_link_down ||
+ ds->ops->phylink_mac_link_up ||
+ ds->ops->adjust_link)
+ return -EINVAL;
+ }
+
if (np) {
err = dsa_switch_parse_of(ds, np);
if (err)
diff --git a/net/dsa/port.c b/net/dsa/port.c
index c42dac87671b..f8499c50dd40 100644
--- a/net/dsa/port.c
+++ b/net/dsa/port.c
@@ -1558,7 +1558,7 @@ static struct phylink_pcs *
dsa_port_phylink_mac_select_pcs(struct phylink_config *config,
phy_interface_t interface)
{
- struct dsa_port *dp = container_of(config, struct dsa_port, pl_config);
+ struct dsa_port *dp = dsa_phylink_to_port(config);
struct phylink_pcs *pcs = ERR_PTR(-EOPNOTSUPP);
struct dsa_switch *ds = dp->ds;
@@ -1572,7 +1572,7 @@ static int dsa_port_phylink_mac_prepare(struct phylink_config *config,
unsigned int mode,
phy_interface_t interface)
{
- struct dsa_port *dp = container_of(config, struct dsa_port, pl_config);
+ struct dsa_port *dp = dsa_phylink_to_port(config);
struct dsa_switch *ds = dp->ds;
int err = 0;
@@ -1587,7 +1587,7 @@ static void dsa_port_phylink_mac_config(struct phylink_config *config,
unsigned int mode,
const struct phylink_link_state *state)
{
- struct dsa_port *dp = container_of(config, struct dsa_port, pl_config);
+ struct dsa_port *dp = dsa_phylink_to_port(config);
struct dsa_switch *ds = dp->ds;
if (!ds->ops->phylink_mac_config)
@@ -1600,7 +1600,7 @@ static int dsa_port_phylink_mac_finish(struct phylink_config *config,
unsigned int mode,
phy_interface_t interface)
{
- struct dsa_port *dp = container_of(config, struct dsa_port, pl_config);
+ struct dsa_port *dp = dsa_phylink_to_port(config);
struct dsa_switch *ds = dp->ds;
int err = 0;
@@ -1615,7 +1615,7 @@ static void dsa_port_phylink_mac_link_down(struct phylink_config *config,
unsigned int mode,
phy_interface_t interface)
{
- struct dsa_port *dp = container_of(config, struct dsa_port, pl_config);
+ struct dsa_port *dp = dsa_phylink_to_port(config);
struct phy_device *phydev = NULL;
struct dsa_switch *ds = dp->ds;
@@ -1638,7 +1638,7 @@ static void dsa_port_phylink_mac_link_up(struct phylink_config *config,
int speed, int duplex,
bool tx_pause, bool rx_pause)
{
- struct dsa_port *dp = container_of(config, struct dsa_port, pl_config);
+ struct dsa_port *dp = dsa_phylink_to_port(config);
struct dsa_switch *ds = dp->ds;
if (!ds->ops->phylink_mac_link_up) {
@@ -1651,6 +1651,26 @@ static void dsa_port_phylink_mac_link_up(struct phylink_config *config,
speed, duplex, tx_pause, rx_pause);
}
+static void dsa_port_phylink_mac_disable_tx_lpi(struct phylink_config *config)
+{
+ struct dsa_port *dp = dsa_phylink_to_port(config);
+ struct dsa_switch *ds = dp->ds;
+
+ if (ds->ops->phylink_mac_disable_tx_lpi)
+ ds->ops->phylink_mac_disable_tx_lpi(ds, dp->index);
+
+}
+
+static void dsa_port_phylink_mac_enable_tx_lpi(struct phylink_config *config,
+ u32 timer)
+{
+ struct dsa_port *dp = dsa_phylink_to_port(config);
+ struct dsa_switch *ds = dp->ds;
+
+ if (ds->ops->phylink_mac_enable_tx_lpi)
+ ds->ops->phylink_mac_enable_tx_lpi(ds, dp->index, timer);
+}
+
static const struct phylink_mac_ops dsa_port_phylink_mac_ops = {
.mac_select_pcs = dsa_port_phylink_mac_select_pcs,
.mac_prepare = dsa_port_phylink_mac_prepare,
@@ -1658,10 +1678,13 @@ static const struct phylink_mac_ops dsa_port_phylink_mac_ops = {
.mac_finish = dsa_port_phylink_mac_finish,
.mac_link_down = dsa_port_phylink_mac_link_down,
.mac_link_up = dsa_port_phylink_mac_link_up,
+ .mac_disable_tx_lpi = dsa_port_phylink_mac_disable_tx_lpi,
+ .mac_enable_tx_lpi = dsa_port_phylink_mac_enable_tx_lpi,
};
int dsa_port_phylink_create(struct dsa_port *dp)
{
+ const struct phylink_mac_ops *mac_ops;
struct dsa_switch *ds = dp->ds;
phy_interface_t mode;
struct phylink *pl;
@@ -1685,8 +1708,12 @@ int dsa_port_phylink_create(struct dsa_port *dp)
}
}
- pl = phylink_create(&dp->pl_config, of_fwnode_handle(dp->dn),
- mode, &dsa_port_phylink_mac_ops);
+ mac_ops = &dsa_port_phylink_mac_ops;
+ if (ds->phylink_mac_ops)
+ mac_ops = ds->phylink_mac_ops;
+
+ pl = phylink_create(&dp->pl_config, of_fwnode_handle(dp->dn), mode,
+ mac_ops);
if (IS_ERR(pl)) {
pr_err("error creating PHYLINK: %ld\n", PTR_ERR(pl));
return PTR_ERR(pl);
@@ -1952,12 +1979,23 @@ static void dsa_shared_port_validate_of(struct dsa_port *dp,
dn, dsa_port_is_cpu(dp) ? "CPU" : "DSA", dp->index);
}
+static void dsa_shared_port_link_down(struct dsa_port *dp)
+{
+ struct dsa_switch *ds = dp->ds;
+
+ if (ds->phylink_mac_ops && ds->phylink_mac_ops->mac_link_down)
+ ds->phylink_mac_ops->mac_link_down(&dp->pl_config, MLO_AN_FIXED,
+ PHY_INTERFACE_MODE_NA);
+ else if (ds->ops->phylink_mac_link_down)
+ ds->ops->phylink_mac_link_down(ds, dp->index, MLO_AN_FIXED,
+ PHY_INTERFACE_MODE_NA);
+}
+
int dsa_shared_port_link_register_of(struct dsa_port *dp)
{
struct dsa_switch *ds = dp->ds;
bool missing_link_description;
bool missing_phy_mode;
- int port = dp->index;
dsa_shared_port_validate_of(dp, &missing_phy_mode,
&missing_link_description);
@@ -1973,9 +2011,7 @@ int dsa_shared_port_link_register_of(struct dsa_port *dp)
"Skipping phylink registration for %s port %d\n",
dsa_port_is_cpu(dp) ? "CPU" : "DSA", dp->index);
} else {
- if (ds->ops->phylink_mac_link_down)
- ds->ops->phylink_mac_link_down(ds, port,
- MLO_AN_FIXED, PHY_INTERFACE_MODE_NA);
+ dsa_shared_port_link_down(dp);
return dsa_shared_port_phylink_register(dp);
}
diff --git a/net/dsa/user.c b/net/dsa/user.c
index b15e71cc342c..a0ee4d338d1f 100644
--- a/net/dsa/user.c
+++ b/net/dsa/user.c
@@ -1228,16 +1228,21 @@ static int dsa_user_set_eee(struct net_device *dev, struct ethtool_eee *e)
struct dsa_switch *ds = dp->ds;
int ret;
- /* Port's PHY and MAC both need to be EEE capable */
- if (!dev->phydev || !dp->pl)
- return -ENODEV;
+ /* If the port is using phylink managed EEE, then get_mac_eee is
+ * unnecessary.
+ */
+ if (!dp->pl_config.lpi_capabilities) {
+ /* Port's PHY and MAC both need to be EEE capable */
+ if (!dev->phydev || !dp->pl)
+ return -ENODEV;
- if (!ds->ops->set_mac_eee)
- return -EOPNOTSUPP;
+ if (!ds->ops->set_mac_eee)
+ return -EOPNOTSUPP;
- ret = ds->ops->set_mac_eee(ds, dp->index, e);
- if (ret)
- return ret;
+ ret = ds->ops->set_mac_eee(ds, dp->index, e);
+ if (ret)
+ return ret;
+ }
return phylink_ethtool_set_eee(dp->pl, e);
}
@@ -1248,16 +1253,21 @@ static int dsa_user_get_eee(struct net_device *dev, struct ethtool_eee *e)
struct dsa_switch *ds = dp->ds;
int ret;
- /* Port's PHY and MAC both need to be EEE capable */
- if (!dev->phydev || !dp->pl)
- return -ENODEV;
+ /* If the port is using phylink managed EEE, then get_mac_eee is
+ * unnecessary.
+ */
+ if (!dp->pl_config.lpi_capabilities) {
+ /* Port's PHY and MAC both need to be EEE capable */
+ if (!dev->phydev || !dp->pl)
+ return -ENODEV;
- if (!ds->ops->get_mac_eee)
- return -EOPNOTSUPP;
+ if (!ds->ops->get_mac_eee)
+ return -EOPNOTSUPP;
- ret = ds->ops->get_mac_eee(ds, dp->index, e);
- if (ret)
- return ret;
+ ret = ds->ops->get_mac_eee(ds, dp->index, e);
+ if (ret)
+ return ret;
+ }
return phylink_ethtool_get_eee(dp->pl, e);
}
@@ -2445,7 +2455,7 @@ EXPORT_SYMBOL_GPL(dsa_port_phylink_mac_change);
static void dsa_user_phylink_fixed_state(struct phylink_config *config,
struct phylink_link_state *state)
{
- struct dsa_port *dp = container_of(config, struct dsa_port, pl_config);
+ struct dsa_port *dp = dsa_phylink_to_port(config);
struct dsa_switch *ds = dp->ds;
/* No need to check that this operation is valid, the callback would