summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRussell King (Oracle) <rmk+kernel@armlinux.org.uk>2025-04-04 14:58:59 +0100
committerRussell King (Oracle) <rmk+kernel@armlinux.org.uk>2025-04-04 14:58:59 +0100
commita5c6668f2a462368d6e159da86de71fe28c3de49 (patch)
tree1161296712cd0ebb8879dbd8b027747dbc8d607a
parent06178e96650ea9506b5449a51dc7a6e79b3dd83a (diff)
parenta60e18bc96488caa764b80a0526a2707cf421195 (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/mt7530.c71
-rw-r--r--drivers/net/dsa/mv88e6xxx/chip.c56
-rw-r--r--drivers/net/dsa/mv88e6xxx/chip.h10
-rw-r--r--drivers/net/dsa/mv88e6xxx/port.c39
-rw-r--r--drivers/net/dsa/mv88e6xxx/port.h1
-rw-r--r--drivers/net/ethernet/marvell/Kconfig4
-rw-r--r--drivers/net/ethernet/marvell/Makefile1
-rw-r--r--drivers/net/ethernet/marvell/mvgmac.c465
-rw-r--r--drivers/net/ethernet/marvell/mvgmac.h56
-rw-r--r--drivers/net/ethernet/marvell/mvneta.c362
-rw-r--r--drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c11
-rw-r--r--drivers/net/ethernet/marvell/mvpp2/mvpp2_tai.c5
-rw-r--r--drivers/net/ethernet/microchip/lan743x_main.c6
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/common.h14
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/dwmac-dwc-qos-eth.c44
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/dwmac-imx.c29
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/dwmac-intel-plat.c27
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/dwmac-ipq806x.c15
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/dwmac-loongson.c3
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/dwmac-meson.c9
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/dwmac-qcom-ethqos.c29
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/dwmac-rk.c10
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/dwmac-socfpga.c2
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/dwmac-starfive.c26
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/dwmac-sti.c8
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/dwmac-sunxi.c2
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/dwmac-thead.c46
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/dwmac-visconti.c2
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/dwmac1000.h13
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/dwmac1000_core.c30
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/dwmac4.h12
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/dwmac4_core.c96
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/dwmac4_lib.c8
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/dwxgmac2.h9
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/dwxgmac2_core.c49
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/dwxgmac2_dma.c12
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/hwif.h21
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/stmmac.h5
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/stmmac_main.c265
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c2
-rw-r--r--drivers/net/pcs/pcs-xpcs.c153
-rw-r--r--drivers/net/pcs/pcs-xpcs.h54
-rw-r--r--drivers/net/phy/Makefile2
-rw-r--r--drivers/net/phy/aquantia/aquantia_main.c15
-rw-r--r--drivers/net/phy/bcm84881.c4
-rw-r--r--drivers/net/phy/marvell.c49
-rw-r--r--drivers/net/phy/marvell10g.c91
-rw-r--r--drivers/net/phy/mdio_bus.c8
-rw-r--r--drivers/net/phy/phy.c10
-rw-r--r--drivers/net/phy/phy_device.c20
-rw-r--r--drivers/net/phy/phylink.c143
-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/phy/marvell/phy-armada38x-comphy.c147
-rw-r--r--include/linux/mdio.h2
-rw-r--r--include/linux/pcs/pcs-xpcs.h3
-rw-r--r--include/linux/phy.h15
-rw-r--r--include/linux/phylink.h50
-rw-r--r--include/linux/stmmac.h6
-rw-r--r--net/dsa/dsa.c5
-rw-r--r--net/dsa/port.c34
-rw-r--r--net/dsa/user.c25
66 files changed, 2145 insertions, 987 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 1181b13deabc..76d7d91c7264 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/mt7530.c b/drivers/net/dsa/mt7530.c
index 5883eb93efb1..296645e50768 100644
--- a/drivers/net/dsa/mt7530.c
+++ b/drivers/net/dsa/mt7530.c
@@ -2953,28 +2953,64 @@ static void mt753x_phylink_mac_link_up(struct phylink_config *config,
mcr |= PMCR_FORCE_RX_FC_EN;
}
- if (mode == MLO_AN_PHY && phydev && phy_init_eee(phydev, false) >= 0) {
- switch (speed) {
- case SPEED_1000:
- case SPEED_2500:
- mcr |= PMCR_FORCE_EEE1G;
- break;
- case SPEED_100:
- mcr |= PMCR_FORCE_EEE100;
- break;
- }
- }
-
mt7530_set(priv, MT753X_PMCR_P(dp->index), mcr);
}
+static void mt753x_phylink_mac_disable_tx_lpi(struct phylink_config *config,
+ phy_interface_t interface)
+{
+ struct dsa_port *dp = dsa_phylink_to_port(config);
+ struct mt7530_priv *priv = dp->ds->priv;
+
+ mt7530_clear(priv, MT753X_PMCR_P(dp->index),
+ PMCR_FORCE_EEE1G | PMCR_FORCE_EEE100);
+}
+
+static int mt753x_phylink_mac_enable_tx_lpi(struct phylink_config *config,
+ phy_interface_t interface,
+ u32 timer, bool tx_clock_stop)
+{
+ struct dsa_port *dp = dsa_phylink_to_port(config);
+ struct mt7530_priv *priv = dp->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, MT753X_PMEEECR_P(dp->index),
+ LPI_THRESH_MASK | LPI_MODE_EN, val);
+
+ mt7530_set(priv, MT753X_PMCR_P(dp->index),
+ PMCR_FORCE_EEE1G | PMCR_FORCE_EEE100);
+
+ return 0;
+}
+
static void mt753x_phylink_get_caps(struct dsa_switch *ds, int port,
struct phylink_config *config)
{
struct mt7530_priv *priv = ds->priv;
+ u32 eeecr;
config->mac_capabilities = MAC_ASYM_PAUSE | MAC_SYM_PAUSE;
+ config->lpi_capabilities = MAC_100FD | MAC_1000FD | MAC_2500FD;
+ config->lpi_timer_limit_us = FIELD_MAX(LPI_THRESH_MASK);
+
+ eeecr = mt7530_read(priv, MT753X_PMEEECR_P(port));
+ /* tx_lpi_timer should be in microseconds. The time units for
+ * LPI threshold are unspecified.
+ */
+ config->lpi_timer_default = FIELD_GET(LPI_THRESH_MASK, eeecr);
+
priv->info->mac_port_get_caps(ds, port, config);
}
@@ -3084,18 +3120,9 @@ mt753x_setup(struct dsa_switch *ds)
static int mt753x_set_mac_eee(struct dsa_switch *ds, int port,
struct ethtool_keee *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 = LPI_THRESH_SET(e->tx_lpi_timer);
- if (!e->tx_lpi_enabled)
- /* Force LPI Mode without a delay */
- set |= LPI_MODE_EN;
- mt7530_rmw(priv, MT753X_PMEEECR_P(port), mask, set);
-
return 0;
}
@@ -3234,6 +3261,8 @@ static const struct phylink_mac_ops mt753x_phylink_mac_ops = {
.mac_config = mt753x_phylink_mac_config,
.mac_link_down = mt753x_phylink_mac_link_down,
.mac_link_up = mt753x_phylink_mac_link_up,
+ .mac_disable_tx_lpi = mt753x_phylink_mac_disable_tx_lpi,
+ .mac_enable_tx_lpi = mt753x_phylink_mac_enable_tx_lpi,
};
const struct mt753x_info mt753x_table[] = {
diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c
index 5db96ca52505..4424c7225c7f 100644
--- a/drivers/net/dsa/mv88e6xxx/chip.c
+++ b/drivers/net/dsa/mv88e6xxx/chip.c
@@ -679,6 +679,8 @@ 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_enabled_default = true;
/* Port 4 supports automedia if the serdes is associated with it. */
if (port == 4) {
@@ -1024,6 +1026,53 @@ error:
"p%d: failed to configure MAC link up\n", port);
}
+static void mv88e6xxx_mac_disable_tx_lpi(struct phylink_config *config,
+ phy_interface_t interface)
+{
+ 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 int mv88e6xxx_mac_enable_tx_lpi(struct phylink_config *config,
+ phy_interface_t interface,
+ u32 timer, bool tx_clock_stop)
+{
+ 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 -EOPNOTSUPP;
+
+ 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));
+
+ return err;
+}
+
static int mv88e6xxx_stats_snapshot(struct mv88e6xxx_chip *chip, int port)
{
int err;
@@ -4595,6 +4644,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,
@@ -4698,6 +4748,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,
@@ -4974,6 +5025,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,
@@ -5397,6 +5449,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,
@@ -5587,6 +5640,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,
@@ -7112,6 +7166,8 @@ static const struct phylink_mac_ops mv88e6xxx_phylink_mac_ops = {
.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 = {
diff --git a/drivers/net/dsa/mv88e6xxx/chip.h b/drivers/net/dsa/mv88e6xxx/chip.h
index 86bf113c9bfa..ceee8c96c0c3 100644
--- a/drivers/net/dsa/mv88e6xxx/chip.h
+++ b/drivers/net/dsa/mv88e6xxx/chip.h
@@ -526,6 +526,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/port.c b/drivers/net/dsa/mv88e6xxx/port.c
index 66b1b7277281..3b74ec043ef1 100644
--- a/drivers/net/dsa/mv88e6xxx/port.c
+++ b/drivers/net/dsa/mv88e6xxx/port.c
@@ -522,6 +522,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)
{
@@ -631,7 +661,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;
@@ -650,13 +679,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 c1d2f99efb1c..78e15c45ca74 100644
--- a/drivers/net/dsa/mv88e6xxx/port.h
+++ b/drivers/net/dsa/mv88e6xxx/port.h
@@ -571,6 +571,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/ethernet/marvell/Kconfig b/drivers/net/ethernet/marvell/Kconfig
index 837295fecd17..c9c9b99e3e06 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 a399defe25fd..994996990dfb 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..cb06d5c2ede4
--- /dev/null
+++ b/drivers/net/ethernet/marvell/mvgmac.c
@@ -0,0 +1,465 @@
+/*
+ * 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_REQUEST_ENABLE = BIT(0),
+ GMAC_LPI_CTRL1_REQUEST_FORCE = BIT(0),
+ GMAC_LPI_CTRL1_MANUAL_MODE = 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, unsigned int neg_mode,
+ 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_disable_lpi(struct mvgmac *gmac)
+{
+ mvgmac_clear(gmac->base + GMAC_LPI_CTRL1_REG,
+ GMAC_LPI_CTRL1_REQUEST_ENABLE |
+ GMAC_LPI_CTRL1_REQUEST_FORCE |
+ GMAC_LPI_CTRL1_MANUAL_MODE);
+}
+EXPORT_SYMBOL_GPL(mvgmac_disable_lpi);
+
+void mvgmac_enable_lpi(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 resolutions 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 | GMAC_LPI_CTRL1_REQUEST_ENABLE,
+ FIELD_PREP(GMAC_LPI_CTRL1_TW, tw) |
+ GMAC_LPI_CTRL1_REQUEST_ENABLE);
+}
+EXPORT_SYMBOL_GPL(mvgmac_enable_lpi);
+
+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..c3b22031173a
--- /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, unsigned int neg_mode,
+ 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_disable_lpi(struct mvgmac *gmac);
+void mvgmac_enable_lpi(struct mvgmac *gmac, unsigned int timer);
+
+#endif
diff --git a/drivers/net/ethernet/marvell/mvneta.c b/drivers/net/ethernet/marvell/mvneta.c
index 4fe121b9f94b..303f0afa9506 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,16 +248,6 @@
(MVNETA_TXQ_BUCKET_REFILL_BASE_PERIOD_NS * \
MVNETA_TXQ_BUCKET_REFILL_PERIOD))
-#define MVNETA_LPI_CTRL_0 0x2cc0
-#define MVNETA_LPI_CTRL_0_TS (0xff << 8)
-#define MVNETA_LPI_CTRL_1 0x2cc4
-#define MVNETA_LPI_CTRL_1_REQUEST_ENABLE BIT(0)
-#define MVNETA_LPI_CTRL_1_REQUEST_FORCE BIT(1)
-#define MVNETA_LPI_CTRL_1_MANUAL_MODE BIT(2)
-#define MVNETA_LPI_CTRL_1_TW (0xfff << 4)
-#define MVNETA_LPI_CTRL_2 0x2cc8
-#define MVNETA_LPI_STATUS 0x2ccc
-
#define MVNETA_CAUSE_TXQ_SENT_DESC_ALL_MASK 0xff
/* Descriptor ring Macros */
@@ -314,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
@@ -537,9 +492,9 @@ 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;
@@ -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,14 +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_pcs_change(&pp->phylink_pcs,
- !!(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).
@@ -3292,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 */
@@ -3749,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 */
@@ -3955,11 +3873,6 @@ 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)
-{
- return container_of(pcs, struct mvneta_port, phylink_pcs);
-}
-
static unsigned int mvneta_pcs_inband_caps(struct phylink_pcs *pcs,
phy_interface_t interface)
{
@@ -3983,105 +3896,23 @@ static unsigned int mvneta_pcs_inband_caps(struct phylink_pcs *pcs,
return LINK_INBAND_DISABLE;
}
-static void mvneta_pcs_get_state(struct phylink_pcs *pcs, unsigned int neg_mode,
- 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);
-
- 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;
-}
-
static int mvneta_pcs_config(struct phylink_pcs *pcs, unsigned int neg_mode,
phy_interface_t interface,
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;
-
- /* 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);
+ /* We should never see Asym_Pause set */
+ WARN_ON(phylink_test(advertising, Asym_Pause));
- 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_inband_caps = mvneta_pcs_inband_caps,
- .pcs_get_state = mvneta_pcs_get_state,
+ .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,
@@ -4090,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,
@@ -4098,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)) {
@@ -4107,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)
@@ -4132,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,
@@ -4188,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)) {
@@ -4204,11 +3984,8 @@ 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;
}
@@ -4218,16 +3995,9 @@ static void mvneta_mac_link_down(struct phylink_config *config,
{
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);
- }
+ mvgmac_link_down(&pp->gmac, mode);
}
static void mvneta_mac_link_up(struct phylink_config *config,
@@ -4238,92 +4008,30 @@ 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;
-
- if (tx_pause || rx_pause)
- val |= MVNETA_GMAC_CONFIG_FLOW_CTRL;
-
- mvreg_write(pp, MVNETA_GMAC_AUTONEG_CONFIG, val);
- }
+ mvgmac_link_up(&pp->gmac, mode, speed, duplex, tx_pause, rx_pause);
mvneta_port_up(pp);
}
-static void mvneta_mac_disable_tx_lpi(struct phylink_config *config)
+static void mvneta_mac_disable_tx_lpi(struct phylink_config *config,
+ phy_interface_t interface)
{
struct mvneta_port *pp = netdev_priv(to_net_dev(config->dev));
- u32 lpi1;
- lpi1 = mvreg_read(pp, MVNETA_LPI_CTRL_1);
- lpi1 &= ~(MVNETA_LPI_CTRL_1_REQUEST_ENABLE |
- MVNETA_LPI_CTRL_1_REQUEST_FORCE |
- MVNETA_LPI_CTRL_1_MANUAL_MODE);
- mvreg_write(pp, MVNETA_LPI_CTRL_1, lpi1);
+ mvgmac_disable_lpi(&pp->gmac);
}
-static int mvneta_mac_enable_tx_lpi(struct phylink_config *config, u32 timer,
+static int mvneta_mac_enable_tx_lpi(struct phylink_config *config,
+ phy_interface_t interface, u32 timer,
bool tx_clk_stop)
{
struct mvneta_port *pp = netdev_priv(to_net_dev(config->dev));
- u32 ts, tw, lpi0, lpi1, status;
- status = mvreg_read(pp, MVNETA_GMAC_STATUS);
- if (status & MVNETA_GMAC_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;
-
- /* Configure ts */
- lpi0 = mvreg_read(pp, MVNETA_LPI_CTRL_0);
- lpi0 = u32_replace_bits(lpi0, ts, MVNETA_LPI_CTRL_0_TS);
- mvreg_write(pp, MVNETA_LPI_CTRL_0, lpi0);
+ if (interface != PHY_INTERFACE_MODE_SGMII &&
+ interface != PHY_INTERFACE_MODE_QSGMII)
+ return -EOPNOTSUPP;
- /* Configure tw and enable LPI generation */
- lpi1 = mvreg_read(pp, MVNETA_LPI_CTRL_1);
- lpi1 = u32_replace_bits(lpi1, tw, MVNETA_LPI_CTRL_1_TW);
- lpi1 |= MVNETA_LPI_CTRL_1_REQUEST_ENABLE;
- mvreg_write(pp, MVNETA_LPI_CTRL_1, lpi1);
+ mvgmac_enable_lpi(&pp->gmac, timer);
return 0;
}
@@ -5471,7 +5179,7 @@ static int mvneta_port_power_up(struct mvneta_port *pp, int phy_mode)
return -EINVAL;
/* Ensure LPI is disabled */
- mvneta_mac_disable_tx_lpi(&pp->phylink_config);
+ mvneta_mac_disable_tx_lpi(&pp->phylink_config, phy_mode);
return 0;
}
@@ -5556,8 +5264,10 @@ 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;
diff --git a/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c b/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c
index dd76c1b7ed3a..c26e3b662474 100644
--- a/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c
+++ b/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c
@@ -6698,7 +6698,8 @@ 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)
+static void mvpp2_mac_disable_tx_lpi(struct phylink_config *config,
+ phy_interface_t interface)
{
struct mvpp2_port *port = mvpp2_phylink_to_port(config);
@@ -6706,12 +6707,16 @@ static void mvpp2_mac_disable_tx_lpi(struct phylink_config *config)
MVPP2_GMAC_LPI_CTRL1_REQ_EN, 0);
}
-static int mvpp2_mac_enable_tx_lpi(struct phylink_config *config, u32 timer,
+static int mvpp2_mac_enable_tx_lpi(struct phylink_config *config,
+ phy_interface_t interface, u32 timer,
bool tx_clk_stop)
{
struct mvpp2_port *port = mvpp2_phylink_to_port(config);
u32 ts, tw, lpi1, status;
+ if (interface != PHY_INTERFACE_MODE_SGMII)
+ return -EOPNOTSUPP;
+
status = readl(port->base + MVPP2_GMAC_STATUS0);
if (status & MVPP2_GMAC_STATUS0_GMII_SPEED) {
/* At 1G speeds, the timer resolution are 1us, and
@@ -7118,7 +7123,7 @@ static int mvpp2_port_probe(struct platform_device *pdev,
}
port->phylink = phylink;
- mvpp2_mac_disable_tx_lpi(&port->phylink_config);
+ mvpp2_mac_disable_tx_lpi(&port->phylink_config, phy_mode);
} else {
dev_warn(&pdev->dev, "Use link irqs for port#%d. FW update required\n", port->id);
port->phylink = NULL;
diff --git a/drivers/net/ethernet/marvell/mvpp2/mvpp2_tai.c b/drivers/net/ethernet/marvell/mvpp2/mvpp2_tai.c
index 95862aff49f1..6b60beb1f3ed 100644
--- a/drivers/net/ethernet/marvell/mvpp2/mvpp2_tai.c
+++ b/drivers/net/ethernet/marvell/mvpp2/mvpp2_tai.c
@@ -54,6 +54,7 @@
#define TCSR_CAPTURE_0_VALID BIT(0)
struct mvpp2_tai {
+ struct device *dev;
struct ptp_clock_info caps;
struct ptp_clock *ptp_clock;
void __iomem *base;
@@ -303,7 +304,8 @@ static long mvpp22_tai_aux_work(struct ptp_clock_info *ptp)
{
struct mvpp2_tai *tai = ptp_to_tai(ptp);
- mvpp22_tai_gettimex64(ptp, &tai->stamp, NULL);
+ if (mvpp22_tai_gettimex64(ptp, &tai->stamp, NULL) < 0)
+ dev_warn_once(tai->dev, "PTP timestamps are unreliable");
return msecs_to_jiffies(2000);
}
@@ -401,6 +403,7 @@ int mvpp22_tai_probe(struct device *dev, struct mvpp2 *priv)
spin_lock_init(&tai->lock);
+ tai->dev = dev;
tai->base = priv->iface_base;
/* The step size consists of three registers - a 16-bit nanosecond step
diff --git a/drivers/net/ethernet/microchip/lan743x_main.c b/drivers/net/ethernet/microchip/lan743x_main.c
index 23760b613d3e..7cda79a9ebfe 100644
--- a/drivers/net/ethernet/microchip/lan743x_main.c
+++ b/drivers/net/ethernet/microchip/lan743x_main.c
@@ -3073,7 +3073,8 @@ static void lan743x_phylink_mac_link_up(struct phylink_config *config,
netif_tx_wake_all_queues(netdev);
}
-static void lan743x_mac_disable_tx_lpi(struct phylink_config *config)
+static void lan743x_mac_disable_tx_lpi(struct phylink_config *config,
+ phy_interface_t interface)
{
struct net_device *netdev = to_net_dev(config->dev);
struct lan743x_adapter *adapter = netdev_priv(netdev);
@@ -3081,7 +3082,8 @@ static void lan743x_mac_disable_tx_lpi(struct phylink_config *config)
lan743x_mac_eee_enable(adapter, false);
}
-static int lan743x_mac_enable_tx_lpi(struct phylink_config *config, u32 timer,
+static int lan743x_mac_enable_tx_lpi(struct phylink_config *config,
+ phy_interface_t interface, u32 timer,
bool tx_clk_stop)
{
struct net_device *netdev = to_net_dev(config->dev);
diff --git a/drivers/net/ethernet/stmicro/stmmac/common.h b/drivers/net/ethernet/stmicro/stmmac/common.h
index e25db747a81a..55053528e498 100644
--- a/drivers/net/ethernet/stmicro/stmmac/common.h
+++ b/drivers/net/ethernet/stmicro/stmmac/common.h
@@ -530,6 +530,20 @@ struct dma_features {
#define STMMAC_DEFAULT_TWT_LS 0x1E
#define STMMAC_ET_MAX 0xFFFFF
+/* Common LPI register bits */
+#define LPI_CTRL_STATUS_LPITCSE BIT(21) /* LPI Tx Clock Stop Enable, gmac4, xgmac2 only */
+#define LPI_CTRL_STATUS_LPIATE BIT(20) /* LPI Timer Enable, gmac4 only */
+#define LPI_CTRL_STATUS_LPITXA BIT(19) /* Enable LPI TX Automate */
+#define LPI_CTRL_STATUS_PLSEN BIT(18) /* Enable PHY Link Status */
+#define LPI_CTRL_STATUS_PLS BIT(17) /* PHY Link Status */
+#define LPI_CTRL_STATUS_LPIEN BIT(16) /* LPI Enable */
+#define LPI_CTRL_STATUS_RLPIST BIT(9) /* Receive LPI state, gmac1000 only? */
+#define LPI_CTRL_STATUS_TLPIST BIT(8) /* Transmit LPI state, gmac1000 only? */
+#define LPI_CTRL_STATUS_RLPIEX BIT(3) /* Receive LPI Exit */
+#define LPI_CTRL_STATUS_RLPIEN BIT(2) /* Receive LPI Entry */
+#define LPI_CTRL_STATUS_TLPIEX BIT(1) /* Transmit LPI Exit */
+#define LPI_CTRL_STATUS_TLPIEN BIT(0) /* Transmit LPI Entry */
+
#define STMMAC_CHAIN_MODE 0x1
#define STMMAC_RING_MODE 0x2
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-dwc-qos-eth.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-dwc-qos-eth.c
index b5a7e05ab7a7..0d38fbfcfbb5 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwmac-dwc-qos-eth.c
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-dwc-qos-eth.c
@@ -31,7 +31,6 @@ struct tegra_eqos {
struct reset_control *rst;
struct clk *clk_master;
struct clk *clk_slave;
- struct clk *clk_tx;
struct clk *clk_rx;
struct gpio_desc *reset;
@@ -180,11 +179,10 @@ static void dwc_qos_remove(struct platform_device *pdev)
#define AUTO_CAL_STATUS 0x880c
#define AUTO_CAL_STATUS_ACTIVE BIT(31)
-static void tegra_eqos_fix_speed(void *priv, unsigned int speed, unsigned int mode)
+static void tegra_eqos_fix_speed(void *priv, int speed, unsigned int mode)
{
struct tegra_eqos *eqos = priv;
bool needs_calibration = false;
- long rate = 125000000;
u32 value;
int err;
@@ -195,11 +193,10 @@ static void tegra_eqos_fix_speed(void *priv, unsigned int speed, unsigned int mo
fallthrough;
case SPEED_10:
- rate = rgmii_clock(speed);
break;
default:
- dev_err(eqos->dev, "invalid speed %u\n", speed);
+ dev_err(eqos->dev, "invalid speed %d\n", speed);
break;
}
@@ -242,10 +239,6 @@ static void tegra_eqos_fix_speed(void *priv, unsigned int speed, unsigned int mo
value &= ~AUTO_CAL_CONFIG_ENABLE;
writel(value, eqos->regs + AUTO_CAL_CONFIG);
}
-
- err = clk_set_rate(eqos->clk_tx, rate);
- if (err < 0)
- dev_err(eqos->dev, "failed to set TX rate: %d\n", err);
}
static int tegra_eqos_init(struct platform_device *pdev, void *priv)
@@ -263,7 +256,7 @@ static int tegra_eqos_init(struct platform_device *pdev, void *priv)
}
static int tegra_eqos_probe(struct platform_device *pdev,
- struct plat_stmmacenet_data *data,
+ struct plat_stmmacenet_data *plat_dat,
struct stmmac_resources *res)
{
struct device *dev = &pdev->dev;
@@ -296,7 +289,7 @@ static int tegra_eqos_probe(struct platform_device *pdev,
goto disable_master;
}
- data->stmmac_clk = eqos->clk_slave;
+ plat_dat->stmmac_clk = eqos->clk_slave;
err = clk_prepare_enable(eqos->clk_slave);
if (err < 0)
@@ -312,16 +305,18 @@ static int tegra_eqos_probe(struct platform_device *pdev,
if (err < 0)
goto disable_slave;
- eqos->clk_tx = devm_clk_get(&pdev->dev, "tx");
- if (IS_ERR(eqos->clk_tx)) {
- err = PTR_ERR(eqos->clk_tx);
+ plat_dat->clk_tx_i = devm_clk_get(&pdev->dev, "tx");
+ if (IS_ERR(plat_dat->clk_tx_i)) {
+ err = PTR_ERR(plat_dat->clk_tx_i);
goto disable_rx;
}
- err = clk_prepare_enable(eqos->clk_tx);
+ err = clk_prepare_enable(plat_dat->clk_tx_i);
if (err < 0)
goto disable_rx;
+ plat_dat->clk_tx_i = eqos->clk_tx;
+
eqos->reset = devm_gpiod_get(&pdev->dev, "phy-reset", GPIOD_OUT_HIGH);
if (IS_ERR(eqos->reset)) {
err = PTR_ERR(eqos->reset);
@@ -332,7 +327,7 @@ static int tegra_eqos_probe(struct platform_device *pdev,
gpiod_set_value(eqos->reset, 0);
/* MDIO bus was already reset just above */
- data->mdio_bus_data->needs_reset = false;
+ plat_dat->mdio_bus_data->needs_reset = false;
eqos->rst = devm_reset_control_get(&pdev->dev, "eqos");
if (IS_ERR(eqos->rst)) {
@@ -353,10 +348,11 @@ static int tegra_eqos_probe(struct platform_device *pdev,
usleep_range(2000, 4000);
bypass_clk_reset_gpio:
- data->fix_mac_speed = tegra_eqos_fix_speed;
- data->init = tegra_eqos_init;
- data->bsp_priv = eqos;
- data->flags |= STMMAC_FLAG_SPH_DISABLE;
+ plat_dat->fix_mac_speed = tegra_eqos_fix_speed;
+ plat_dat->set_clk_tx_rate = stmmac_set_clk_tx_rate;
+ plat_dat->init = tegra_eqos_init;
+ plat_dat->bsp_priv = eqos;
+ plat_dat->flags |= STMMAC_FLAG_SPH_DISABLE;
err = tegra_eqos_init(pdev, eqos);
if (err < 0)
@@ -368,7 +364,7 @@ reset:
reset_phy:
gpiod_set_value(eqos->reset, 1);
disable_tx:
- clk_disable_unprepare(eqos->clk_tx);
+ clk_disable_unprepare(plat_dat->clk_tx_i);
disable_rx:
clk_disable_unprepare(eqos->clk_rx);
disable_slave:
@@ -382,10 +378,12 @@ error:
static void tegra_eqos_remove(struct platform_device *pdev)
{
struct tegra_eqos *eqos = get_stmmac_bsp_priv(&pdev->dev);
+ struct net_device *ndev = platform_get_drvdata(pdev);
+ struct stmmac_priv *priv = netdev_priv(ndev);
reset_control_assert(eqos->rst);
gpiod_set_value(eqos->reset, 1);
- clk_disable_unprepare(eqos->clk_tx);
+ clk_disable_unprepare(priv->plat->clk_tx_i);
clk_disable_unprepare(eqos->clk_rx);
clk_disable_unprepare(eqos->clk_slave);
clk_disable_unprepare(eqos->clk_master);
@@ -393,7 +391,7 @@ static void tegra_eqos_remove(struct platform_device *pdev)
struct dwc_eth_dwmac_data {
int (*probe)(struct platform_device *pdev,
- struct plat_stmmacenet_data *data,
+ struct plat_stmmacenet_data *plat_dat,
struct stmmac_resources *res);
void (*remove)(struct platform_device *pdev);
};
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-imx.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-imx.c
index 20d3a202bb8d..5d279fa54b3e 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwmac-imx.c
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-imx.c
@@ -51,7 +51,7 @@ struct imx_dwmac_ops {
int (*fix_soc_reset)(void *priv, void __iomem *ioaddr);
int (*set_intf_mode)(struct plat_stmmacenet_data *plat_dat);
- void (*fix_mac_speed)(void *priv, unsigned int speed, unsigned int mode);
+ void (*fix_mac_speed)(void *priv, int speed, unsigned int mode);
};
struct imx_priv_data {
@@ -192,7 +192,20 @@ static void imx_dwmac_exit(struct platform_device *pdev, void *priv)
/* nothing to do now */
}
-static void imx_dwmac_fix_speed(void *priv, unsigned int speed, unsigned int mode)
+static int imx_dwmac_set_clk_tx_rate(void *bsp_priv, struct clk *clk_tx_i,
+ phy_interface_t interface, int speed)
+{
+ struct imx_priv_data *dwmac = bsp_priv;
+
+ interface = dwmac->plat_dat->mac_interface;
+ if (interface == PHY_INTERFACE_MODE_RMII ||
+ interface == PHY_INTERFACE_MODE_MII)
+ return 0;
+
+ return stmmac_set_clk_tx_rate(bsp_priv, clk_tx_i, interface, speed);
+}
+
+static void imx_dwmac_fix_speed(void *priv, int speed, unsigned int mode)
{
struct plat_stmmacenet_data *plat_dat;
struct imx_priv_data *dwmac = priv;
@@ -208,7 +221,7 @@ static void imx_dwmac_fix_speed(void *priv, unsigned int speed, unsigned int mod
rate = rgmii_clock(speed);
if (rate < 0) {
- dev_err(dwmac->dev, "invalid speed %u\n", speed);
+ dev_err(dwmac->dev, "invalid speed %d\n", speed);
return;
}
@@ -217,7 +230,7 @@ static void imx_dwmac_fix_speed(void *priv, unsigned int speed, unsigned int mod
dev_err(dwmac->dev, "failed to set tx rate %lu\n", rate);
}
-static void imx93_dwmac_fix_speed(void *priv, unsigned int speed, unsigned int mode)
+static void imx93_dwmac_fix_speed(void *priv, int speed, unsigned int mode)
{
struct imx_priv_data *dwmac = priv;
unsigned int iface;
@@ -358,7 +371,6 @@ static int imx_dwmac_probe(struct platform_device *pdev)
plat_dat->init = imx_dwmac_init;
plat_dat->exit = imx_dwmac_exit;
plat_dat->clks_config = imx_dwmac_clks_config;
- plat_dat->fix_mac_speed = imx_dwmac_fix_speed;
plat_dat->bsp_priv = dwmac;
dwmac->plat_dat = plat_dat;
dwmac->base_addr = stmmac_res.addr;
@@ -371,8 +383,13 @@ static int imx_dwmac_probe(struct platform_device *pdev)
if (ret)
goto err_dwmac_init;
- if (dwmac->ops->fix_mac_speed)
+ if (dwmac->ops->fix_mac_speed) {
plat_dat->fix_mac_speed = dwmac->ops->fix_mac_speed;
+ } else if (!dwmac->ops->mac_rgmii_txclk_auto_adj) {
+ plat_dat->clk_tx_i = dwmac->clk_tx;
+ plat_dat->set_clk_tx_rate = imx_dwmac_set_clk_tx_rate;
+ }
+
dwmac->plat_dat->fix_soc_reset = dwmac->ops->fix_soc_reset;
ret = stmmac_dvr_probe(&pdev->dev, plat_dat, &stmmac_res);
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-intel-plat.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-intel-plat.c
index ddee6154d40b..ba02844da6e3 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwmac-intel-plat.c
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-intel-plat.c
@@ -22,31 +22,12 @@ struct intel_dwmac {
};
struct intel_dwmac_data {
- void (*fix_mac_speed)(void *priv, unsigned int speed, unsigned int mode);
unsigned long ptp_ref_clk_rate;
unsigned long tx_clk_rate;
bool tx_clk_en;
};
-static void kmb_eth_fix_mac_speed(void *priv, unsigned int speed, unsigned int mode)
-{
- struct intel_dwmac *dwmac = priv;
- long rate;
- int ret;
-
- rate = rgmii_clock(speed);
- if (rate < 0) {
- dev_err(dwmac->dev, "Invalid speed\n");
- return;
- }
-
- ret = clk_set_rate(dwmac->tx_clk, rate);
- if (ret)
- dev_err(dwmac->dev, "Failed to configure tx clock rate\n");
-}
-
static const struct intel_dwmac_data kmb_data = {
- .fix_mac_speed = kmb_eth_fix_mac_speed,
.ptp_ref_clk_rate = 200000000,
.tx_clk_rate = 125000000,
.tx_clk_en = true,
@@ -89,8 +70,6 @@ static int intel_eth_plat_probe(struct platform_device *pdev)
* platform_match().
*/
dwmac->data = device_get_match_data(&pdev->dev);
- if (dwmac->data->fix_mac_speed)
- plat_dat->fix_mac_speed = dwmac->data->fix_mac_speed;
/* Enable TX clock */
if (dwmac->data->tx_clk_en) {
@@ -130,8 +109,14 @@ static int intel_eth_plat_probe(struct platform_device *pdev)
goto err_tx_clk_disable;
}
}
+
+ plat_dat->clk_tx_i = dwmac->tx_clk;
+ plat_dat->set_clk_tx_rate = stmmac_set_clk_tx_rate;
}
+ plat_dat->clk_tx_i = dwmac->tx_clk;
+ plat_dat->set_clk_tx_rate = stmmac_set_clk_tx_rate;
+
plat_dat->bsp_priv = dwmac;
plat_dat->eee_usecs_rate = plat_dat->clk_ptp_rate;
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-ipq806x.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-ipq806x.c
index 61227dcf56dc..0a9c137cc4e6 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwmac-ipq806x.c
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-ipq806x.c
@@ -112,7 +112,7 @@ struct ipq806x_gmac {
phy_interface_t phy_mode;
};
-static int get_clk_div_sgmii(struct ipq806x_gmac *gmac, unsigned int speed)
+static int get_clk_div_sgmii(struct ipq806x_gmac *gmac, int speed)
{
struct device *dev = &gmac->pdev->dev;
int div;
@@ -138,7 +138,7 @@ static int get_clk_div_sgmii(struct ipq806x_gmac *gmac, unsigned int speed)
return div;
}
-static int get_clk_div_rgmii(struct ipq806x_gmac *gmac, unsigned int speed)
+static int get_clk_div_rgmii(struct ipq806x_gmac *gmac, int speed)
{
struct device *dev = &gmac->pdev->dev;
int div;
@@ -164,7 +164,7 @@ static int get_clk_div_rgmii(struct ipq806x_gmac *gmac, unsigned int speed)
return div;
}
-static int ipq806x_gmac_set_speed(struct ipq806x_gmac *gmac, unsigned int speed)
+static int ipq806x_gmac_set_speed(struct ipq806x_gmac *gmac, int speed)
{
uint32_t clk_bits, val;
int div;
@@ -260,11 +260,12 @@ static int ipq806x_gmac_of_parse(struct ipq806x_gmac *gmac)
return PTR_ERR_OR_ZERO(gmac->qsgmii_csr);
}
-static void ipq806x_gmac_fix_mac_speed(void *priv, unsigned int speed, unsigned int mode)
+static int ipq806x_gmac_set_clk_tx_rate(void *bsp_priv, struct clk *clk_tx_i,
+ phy_interface_t interface, int speed)
{
- struct ipq806x_gmac *gmac = priv;
+ struct ipq806x_gmac *gmac = bsp_priv;
- ipq806x_gmac_set_speed(gmac, speed);
+ return ipq806x_gmac_set_speed(gmac, speed);
}
static int
@@ -478,7 +479,7 @@ static int ipq806x_gmac_probe(struct platform_device *pdev)
plat_dat->has_gmac = true;
plat_dat->bsp_priv = gmac;
- plat_dat->fix_mac_speed = ipq806x_gmac_fix_mac_speed;
+ plat_dat->set_clk_tx_rate = ipq806x_gmac_set_clk_tx_rate;
plat_dat->multicast_filter_bins = 0;
plat_dat->tx_fifo_size = 8192;
plat_dat->rx_fifo_size = 8192;
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-loongson.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-loongson.c
index ab7c2750c104..e81aef623640 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwmac-loongson.c
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-loongson.c
@@ -151,8 +151,7 @@ static struct stmmac_pci_info loongson_gmac_pci_info = {
.setup = loongson_gmac_data,
};
-static void loongson_gnet_fix_speed(void *priv, unsigned int speed,
- unsigned int mode)
+static void loongson_gnet_fix_speed(void *priv, int speed, unsigned int mode)
{
struct loongson_data *ld = (struct loongson_data *)priv;
struct net_device *ndev = dev_get_drvdata(ld->dev);
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-meson.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-meson.c
index 5469fa1b429e..07c504d07604 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwmac-meson.c
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-meson.c
@@ -22,9 +22,10 @@ struct meson_dwmac {
void __iomem *reg;
};
-static void meson6_dwmac_fix_mac_speed(void *priv, unsigned int speed, unsigned int mode)
+static int meson6_dwmac_set_clk_tx_rate(void *bsp_priv, struct clk *clk_tx_i,
+ phy_interface_t interface, int speed)
{
- struct meson_dwmac *dwmac = priv;
+ struct meson_dwmac *dwmac = bsp_priv;
unsigned int val;
val = readl(dwmac->reg);
@@ -39,6 +40,8 @@ static void meson6_dwmac_fix_mac_speed(void *priv, unsigned int speed, unsigned
}
writel(val, dwmac->reg);
+
+ return 0;
}
static int meson6_dwmac_probe(struct platform_device *pdev)
@@ -65,7 +68,7 @@ static int meson6_dwmac_probe(struct platform_device *pdev)
return PTR_ERR(dwmac->reg);
plat_dat->bsp_priv = dwmac;
- plat_dat->fix_mac_speed = meson6_dwmac_fix_mac_speed;
+ plat_dat->set_clk_tx_rate = meson6_dwmac_set_clk_tx_rate;
return stmmac_dvr_probe(&pdev->dev, plat_dat, &stmmac_res);
}
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-qcom-ethqos.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-qcom-ethqos.c
index 2a5b38723635..eafe637540b6 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwmac-qcom-ethqos.c
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-qcom-ethqos.c
@@ -111,7 +111,7 @@ struct qcom_ethqos {
unsigned int link_clk_rate;
struct clk *link_clk;
struct phy *serdes_phy;
- unsigned int speed;
+ int speed;
int serdes_speed;
phy_interface_t phy_mode;
@@ -169,30 +169,17 @@ static void rgmii_dump(void *priv)
rgmii_readl(ethqos, EMAC_SYSTEM_LOW_POWER_DEBUG));
}
-/* Clock rates */
-#define RGMII_1000_NOM_CLK_FREQ (250 * 1000 * 1000UL)
-#define RGMII_ID_MODE_100_LOW_SVS_CLK_FREQ (50 * 1000 * 1000UL)
-#define RGMII_ID_MODE_10_LOW_SVS_CLK_FREQ (5 * 1000 * 1000UL)
-
static void
-ethqos_update_link_clk(struct qcom_ethqos *ethqos, unsigned int speed)
+ethqos_update_link_clk(struct qcom_ethqos *ethqos, int speed)
{
+ long rate;
+
if (!phy_interface_mode_is_rgmii(ethqos->phy_mode))
return;
- switch (speed) {
- case SPEED_1000:
- ethqos->link_clk_rate = RGMII_1000_NOM_CLK_FREQ;
- break;
-
- case SPEED_100:
- ethqos->link_clk_rate = RGMII_ID_MODE_100_LOW_SVS_CLK_FREQ;
- break;
-
- case SPEED_10:
- ethqos->link_clk_rate = RGMII_ID_MODE_10_LOW_SVS_CLK_FREQ;
- break;
- }
+ rate = rgmii_clock(speed);
+ if (rate > 0)
+ ethqos->link_clk_rate = rate * 2;
clk_set_rate(ethqos->link_clk, ethqos->link_clk_rate);
}
@@ -699,7 +686,7 @@ static int ethqos_configure(struct qcom_ethqos *ethqos)
return ethqos->configure_func(ethqos);
}
-static void ethqos_fix_mac_speed(void *priv, unsigned int speed, unsigned int mode)
+static void ethqos_fix_mac_speed(void *priv, int speed, unsigned int mode)
{
struct qcom_ethqos *ethqos = priv;
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-rk.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-rk.c
index a4dc89e23a68..003fa5cf42c3 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwmac-rk.c
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-rk.c
@@ -1920,9 +1920,10 @@ static void rk_gmac_powerdown(struct rk_priv_data *gmac)
gmac_clk_enable(gmac, false);
}
-static void rk_fix_speed(void *priv, unsigned int speed, unsigned int mode)
+static int rk_set_clk_tx_rate(void *bsp_priv_, struct clk *clk_tx_i,
+ phy_interface_t interface, int speed)
{
- struct rk_priv_data *bsp_priv = priv;
+ struct rk_priv_data *bsp_priv = bsp_priv_;
struct device *dev = &bsp_priv->pdev->dev;
switch (bsp_priv->phy_iface) {
@@ -1940,6 +1941,8 @@ static void rk_fix_speed(void *priv, unsigned int speed, unsigned int mode)
default:
dev_err(dev, "unsupported interface %d", bsp_priv->phy_iface);
}
+
+ return 0;
}
static int rk_gmac_probe(struct platform_device *pdev)
@@ -1968,7 +1971,8 @@ static int rk_gmac_probe(struct platform_device *pdev)
*/
if (!plat_dat->has_gmac4)
plat_dat->has_gmac = true;
- plat_dat->fix_mac_speed = rk_fix_speed;
+
+ plat_dat->set_clk_tx_rate = rk_set_clk_tx_rate;
plat_dat->bsp_priv = rk_gmac_setup(pdev, plat_dat, data);
if (IS_ERR(plat_dat->bsp_priv))
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-socfpga.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-socfpga.c
index 16020b72dec8..6b78ae730466 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwmac-socfpga.c
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-socfpga.c
@@ -61,7 +61,7 @@ struct socfpga_dwmac {
struct mdio_device *pcs_mdiodev;
};
-static void socfpga_dwmac_fix_mac_speed(void *priv, unsigned int speed, unsigned int mode)
+static void socfpga_dwmac_fix_mac_speed(void *priv, int speed, unsigned int mode)
{
struct socfpga_dwmac *dwmac = (struct socfpga_dwmac *)priv;
void __iomem *splitter_base = dwmac->splitter_base;
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-starfive.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-starfive.c
index 0a0a363d3730..5e31cb3bb4b8 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwmac-starfive.c
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-starfive.c
@@ -27,27 +27,9 @@ struct starfive_dwmac_data {
struct starfive_dwmac {
struct device *dev;
- struct clk *clk_tx;
const struct starfive_dwmac_data *data;
};
-static void starfive_dwmac_fix_mac_speed(void *priv, unsigned int speed, unsigned int mode)
-{
- struct starfive_dwmac *dwmac = priv;
- long rate;
- int err;
-
- rate = rgmii_clock(speed);
- if (rate < 0) {
- dev_err(dwmac->dev, "invalid speed %u\n", speed);
- return;
- }
-
- err = clk_set_rate(dwmac->clk_tx, rate);
- if (err)
- dev_err(dwmac->dev, "failed to set tx rate %lu\n", rate);
-}
-
static int starfive_dwmac_set_mode(struct plat_stmmacenet_data *plat_dat)
{
struct starfive_dwmac *dwmac = plat_dat->bsp_priv;
@@ -122,9 +104,9 @@ static int starfive_dwmac_probe(struct platform_device *pdev)
dwmac->data = device_get_match_data(&pdev->dev);
- dwmac->clk_tx = devm_clk_get_enabled(&pdev->dev, "tx");
- if (IS_ERR(dwmac->clk_tx))
- return dev_err_probe(&pdev->dev, PTR_ERR(dwmac->clk_tx),
+ plat_dat->clk_tx_i = devm_clk_get_enabled(&pdev->dev, "tx");
+ if (IS_ERR(plat_dat->clk_tx_i))
+ return dev_err_probe(&pdev->dev, PTR_ERR(plat_dat->clk_tx_i),
"error getting tx clock\n");
clk_gtx = devm_clk_get_enabled(&pdev->dev, "gtx");
@@ -139,7 +121,7 @@ static int starfive_dwmac_probe(struct platform_device *pdev)
* internally, because rgmii_rxin will be adaptively adjusted.
*/
if (!device_property_read_bool(&pdev->dev, "starfive,tx-use-rgmii-clk"))
- plat_dat->fix_mac_speed = starfive_dwmac_fix_mac_speed;
+ plat_dat->set_clk_tx_rate = stmmac_set_clk_tx_rate;
dwmac->dev = &pdev->dev;
plat_dat->bsp_priv = dwmac;
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-sti.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-sti.c
index f25461c292fe..13b9c2a51fce 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwmac-sti.c
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-sti.c
@@ -99,12 +99,12 @@ struct sti_dwmac {
int clk_sel_reg; /* GMAC ext clk selection register */
struct regmap *regmap;
bool gmac_en;
- u32 speed;
- void (*fix_retime_src)(void *priv, unsigned int speed, unsigned int mode);
+ int speed;
+ void (*fix_retime_src)(void *priv, int speed, unsigned int mode);
};
struct sti_dwmac_of_data {
- void (*fix_retime_src)(void *priv, unsigned int speed, unsigned int mode);
+ void (*fix_retime_src)(void *priv, int speed, unsigned int mode);
};
static u32 phy_intf_sels[] = {
@@ -132,7 +132,7 @@ static u32 stih4xx_tx_retime_val[] = {
| STIH4XX_ETH_SEL_INTERNAL_NOTEXT_PHYCLK,
};
-static void stih4xx_fix_retime_src(void *priv, u32 spd, unsigned int mode)
+static void stih4xx_fix_retime_src(void *priv, int spd, unsigned int mode)
{
struct sti_dwmac *dwmac = priv;
u32 src = dwmac->tx_retime_src;
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-sunxi.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-sunxi.c
index 9ae318436c4a..1b1ce2888b2e 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwmac-sunxi.c
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-sunxi.c
@@ -72,7 +72,7 @@ static void sun7i_gmac_exit(struct platform_device *pdev, void *priv)
regulator_disable(gmac->regulator);
}
-static void sun7i_fix_speed(void *priv, unsigned int speed, unsigned int mode)
+static void sun7i_fix_speed(void *priv, int speed, unsigned int mode)
{
struct sunxi_priv_data *gmac = priv;
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-thead.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-thead.c
index dce84ed184e9..c72ee759aae5 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwmac-thead.c
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-thead.c
@@ -45,9 +45,6 @@
#define TXCLK_DIR_OUTPUT FIELD_PREP(TXCLK_DIR_MASK, 0)
#define TXCLK_DIR_INPUT FIELD_PREP(TXCLK_DIR_MASK, 1)
-#define GMAC_GMII_RGMII_RATE 125000000
-#define GMAC_MII_RATE 25000000
-
struct thead_dwmac {
struct plat_stmmacenet_data *plat;
void __iomem *apb_base;
@@ -104,11 +101,13 @@ static int thead_dwmac_set_txclk_dir(struct plat_stmmacenet_data *plat)
return 0;
}
-static void thead_dwmac_fix_speed(void *priv, unsigned int speed, unsigned int mode)
+static int thead_set_clk_tx_rate(void *bsp_priv, struct clk *clk_tx_i,
+ phy_interface_t interface, int speed)
{
+ struct thead_dwmac *dwmac = bsp_priv;
struct plat_stmmacenet_data *plat;
- struct thead_dwmac *dwmac = priv;
unsigned long rate;
+ long tx_rate;
u32 div, reg;
plat = dwmac->plat;
@@ -116,44 +115,37 @@ static void thead_dwmac_fix_speed(void *priv, unsigned int speed, unsigned int m
switch (plat->mac_interface) {
/* For MII, rxc/txc is provided by phy */
case PHY_INTERFACE_MODE_MII:
- return;
+ return 0;
case PHY_INTERFACE_MODE_RGMII:
case PHY_INTERFACE_MODE_RGMII_ID:
case PHY_INTERFACE_MODE_RGMII_RXID:
case PHY_INTERFACE_MODE_RGMII_TXID:
rate = clk_get_rate(plat->stmmac_clk);
- if (!rate || rate % GMAC_GMII_RGMII_RATE != 0 ||
- rate % GMAC_MII_RATE != 0) {
- dev_err(dwmac->dev, "invalid gmac rate %ld\n", rate);
- return;
- }
writel(0, dwmac->apb_base + GMAC_PLLCLK_DIV);
- switch (speed) {
- case SPEED_1000:
- div = rate / GMAC_GMII_RGMII_RATE;
- break;
- case SPEED_100:
- div = rate / GMAC_MII_RATE;
- break;
- case SPEED_10:
- div = rate * 10 / GMAC_MII_RATE;
- break;
- default:
- dev_err(dwmac->dev, "invalid speed %u\n", speed);
- return;
+ tx_rate = rgmii_clock(speed);
+ if (tx_rate < 0) {
+ dev_err(dwmac->dev, "invalid speed %d\n", speed);
+ return tx_rate;
+ }
+
+ div = rate / tx_rate;
+ if (rate != tx_rate * div) {
+ dev_err(dwmac->dev, "invalid gmac rate %lu\n", rate);
+ return -EINVAL;
}
reg = FIELD_PREP(GMAC_PLLCLK_DIV_EN, 1) |
FIELD_PREP(GMAC_PLLCLK_DIV_NUM, div);
writel(reg, dwmac->apb_base + GMAC_PLLCLK_DIV);
- break;
+ return 0;
+
default:
dev_err(dwmac->dev, "unsupported phy interface %d\n",
plat->mac_interface);
- return;
+ return -EINVAL;
}
}
@@ -245,7 +237,7 @@ static int thead_dwmac_probe(struct platform_device *pdev)
dwmac->plat = plat;
dwmac->apb_base = apb;
plat->bsp_priv = dwmac;
- plat->fix_mac_speed = thead_dwmac_fix_speed;
+ plat->set_clk_tx_rate = thead_set_clk_tx_rate;
plat->init = thead_dwmac_init;
return devm_stmmac_pltfr_probe(pdev, plat, &stmmac_res);
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-visconti.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-visconti.c
index eccf7f537467..33cf99797df5 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwmac-visconti.c
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-visconti.c
@@ -54,7 +54,7 @@ struct visconti_eth {
spinlock_t lock; /* lock to protect register update */
};
-static void visconti_eth_fix_mac_speed(void *priv, unsigned int speed, unsigned int mode)
+static void visconti_eth_fix_mac_speed(void *priv, int speed, unsigned int mode)
{
struct visconti_eth *dwmac = priv;
struct net_device *netdev = dev_get_drvdata(dwmac->dev);
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac1000.h b/drivers/net/ethernet/stmicro/stmmac/dwmac1000.h
index 600fea8f712f..967a16212faf 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwmac1000.h
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac1000.h
@@ -59,22 +59,11 @@ enum power_event {
/* Energy Efficient Ethernet (EEE)
*
* LPI status, timer and control register offset
+ * For LPI control and status bit definitions, see common.h.
*/
#define LPI_CTRL_STATUS 0x0030
#define LPI_TIMER_CTRL 0x0034
-/* LPI control and status defines */
-#define LPI_CTRL_STATUS_LPITXA 0x00080000 /* Enable LPI TX Automate */
-#define LPI_CTRL_STATUS_PLSEN 0x00040000 /* Enable PHY Link Status */
-#define LPI_CTRL_STATUS_PLS 0x00020000 /* PHY Link Status */
-#define LPI_CTRL_STATUS_LPIEN 0x00010000 /* LPI Enable */
-#define LPI_CTRL_STATUS_RLPIST 0x00000200 /* Receive LPI state */
-#define LPI_CTRL_STATUS_TLPIST 0x00000100 /* Transmit LPI state */
-#define LPI_CTRL_STATUS_RLPIEX 0x00000008 /* Receive LPI Exit */
-#define LPI_CTRL_STATUS_RLPIEN 0x00000004 /* Receive LPI Entry */
-#define LPI_CTRL_STATUS_TLPIEX 0x00000002 /* Transmit LPI Exit */
-#define LPI_CTRL_STATUS_TLPIEN 0x00000001 /* Transmit LPI Entry */
-
/* GMAC HW ADDR regs */
#define GMAC_ADDR_HIGH(reg) ((reg > 15) ? 0x00000800 + (reg - 16) * 8 : \
0x00000040 + (reg * 8))
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac1000_core.c b/drivers/net/ethernet/stmicro/stmmac/dwmac1000_core.c
index 96bcda0856ec..7900bf3effa7 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwmac1000_core.c
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac1000_core.c
@@ -342,31 +342,24 @@ static int dwmac1000_irq_status(struct mac_device_info *hw,
return ret;
}
-static void dwmac1000_set_eee_mode(struct mac_device_info *hw,
- bool en_tx_lpi_clockgating)
+static int dwmac1000_set_lpi_mode(struct mac_device_info *hw,
+ enum stmmac_lpi_mode mode,
+ bool en_tx_lpi_clockgating, u32 et)
{
void __iomem *ioaddr = hw->pcsr;
u32 value;
- /*TODO - en_tx_lpi_clockgating treatment */
+ if (mode == STMMAC_LPI_TIMER)
+ return -EOPNOTSUPP;
- /* Enable the link status receive on RGMII, SGMII ore SMII
- * receive path and instruct the transmit to enter in LPI
- * state.
- */
value = readl(ioaddr + LPI_CTRL_STATUS);
- value |= LPI_CTRL_STATUS_LPIEN | LPI_CTRL_STATUS_LPITXA;
+ if (mode == STMMAC_LPI_FORCED)
+ value |= LPI_CTRL_STATUS_LPIEN | LPI_CTRL_STATUS_LPITXA;
+ else
+ value &= ~(LPI_CTRL_STATUS_LPIEN | LPI_CTRL_STATUS_LPITXA);
writel(value, ioaddr + LPI_CTRL_STATUS);
-}
-
-static void dwmac1000_reset_eee_mode(struct mac_device_info *hw)
-{
- void __iomem *ioaddr = hw->pcsr;
- u32 value;
- value = readl(ioaddr + LPI_CTRL_STATUS);
- value &= ~(LPI_CTRL_STATUS_LPIEN | LPI_CTRL_STATUS_LPITXA);
- writel(value, ioaddr + LPI_CTRL_STATUS);
+ return 0;
}
static void dwmac1000_set_eee_pls(struct mac_device_info *hw, int link)
@@ -509,8 +502,7 @@ const struct stmmac_ops dwmac1000_ops = {
.pmt = dwmac1000_pmt,
.set_umac_addr = dwmac1000_set_umac_addr,
.get_umac_addr = dwmac1000_get_umac_addr,
- .set_eee_mode = dwmac1000_set_eee_mode,
- .reset_eee_mode = dwmac1000_reset_eee_mode,
+ .set_lpi_mode = dwmac1000_set_lpi_mode,
.set_eee_timer = dwmac1000_set_eee_timer,
.set_eee_pls = dwmac1000_set_eee_pls,
.debug = dwmac1000_debug,
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac4.h b/drivers/net/ethernet/stmicro/stmmac/dwmac4.h
index 184d41a306af..42fe29a4e300 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwmac4.h
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac4.h
@@ -177,23 +177,13 @@ enum power_event {
/* Energy Efficient Ethernet (EEE) for GMAC4
*
* LPI status, timer and control register offset
+ * For LPI control and status bit definitions, see common.h.
*/
#define GMAC4_LPI_CTRL_STATUS 0xd0
#define GMAC4_LPI_TIMER_CTRL 0xd4
#define GMAC4_LPI_ENTRY_TIMER 0xd8
#define GMAC4_MAC_ONEUS_TIC_COUNTER 0xdc
-/* LPI control and status defines */
-#define GMAC4_LPI_CTRL_STATUS_LPITCSE BIT(21) /* LPI Tx Clock Stop Enable */
-#define GMAC4_LPI_CTRL_STATUS_LPIATE BIT(20) /* LPI Timer Enable */
-#define GMAC4_LPI_CTRL_STATUS_LPITXA BIT(19) /* Enable LPI TX Automate */
-#define GMAC4_LPI_CTRL_STATUS_PLS BIT(17) /* PHY Link Status */
-#define GMAC4_LPI_CTRL_STATUS_LPIEN BIT(16) /* LPI Enable */
-#define GMAC4_LPI_CTRL_STATUS_RLPIEX BIT(3) /* Receive LPI Exit */
-#define GMAC4_LPI_CTRL_STATUS_RLPIEN BIT(2) /* Receive LPI Entry */
-#define GMAC4_LPI_CTRL_STATUS_TLPIEX BIT(1) /* Transmit LPI Exit */
-#define GMAC4_LPI_CTRL_STATUS_TLPIEN BIT(0) /* Transmit LPI Entry */
-
/* MAC Debug bitmap */
#define GMAC_DEBUG_TFCSTS_MASK GENMASK(18, 17)
#define GMAC_DEBUG_TFCSTS_SHIFT 17
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac4_core.c b/drivers/net/ethernet/stmicro/stmmac/dwmac4_core.c
index 9ed8620580a8..cc4ddf608652 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwmac4_core.c
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac4_core.c
@@ -376,33 +376,46 @@ static void dwmac4_get_umac_addr(struct mac_device_info *hw,
GMAC_ADDR_LOW(reg_n));
}
-static void dwmac4_set_eee_mode(struct mac_device_info *hw,
- bool en_tx_lpi_clockgating)
+static int dwmac4_set_lpi_mode(struct mac_device_info *hw,
+ enum stmmac_lpi_mode mode,
+ bool en_tx_lpi_clockgating, u32 et)
{
void __iomem *ioaddr = hw->pcsr;
- u32 value;
+ u32 value, mask;
- /* Enable the link status receive on RGMII, SGMII ore SMII
- * receive path and instruct the transmit to enter in LPI
- * state.
- */
- value = readl(ioaddr + GMAC4_LPI_CTRL_STATUS);
- value |= GMAC4_LPI_CTRL_STATUS_LPIEN | GMAC4_LPI_CTRL_STATUS_LPITXA;
+ if (mode == STMMAC_LPI_DISABLE) {
+ value = 0;
+ } else {
+ value = LPI_CTRL_STATUS_LPIEN | LPI_CTRL_STATUS_LPITXA;
- if (en_tx_lpi_clockgating)
- value |= GMAC4_LPI_CTRL_STATUS_LPITCSE;
+ if (mode == STMMAC_LPI_TIMER) {
+ /* Return ERANGE if the timer is larger than the
+ * register field.
+ */
+ if (et > STMMAC_ET_MAX)
+ return -ERANGE;
- writel(value, ioaddr + GMAC4_LPI_CTRL_STATUS);
-}
+ /* Set the hardware LPI entry timer */
+ writel(et, ioaddr + GMAC4_LPI_ENTRY_TIMER);
-static void dwmac4_reset_eee_mode(struct mac_device_info *hw)
-{
- void __iomem *ioaddr = hw->pcsr;
- u32 value;
+ /* Interpret a zero LPI entry timer to mean
+ * immediate entry into LPI mode.
+ */
+ if (et)
+ value |= LPI_CTRL_STATUS_LPIATE;
+ }
- value = readl(ioaddr + GMAC4_LPI_CTRL_STATUS);
- value &= ~(GMAC4_LPI_CTRL_STATUS_LPIEN | GMAC4_LPI_CTRL_STATUS_LPITXA);
+ if (en_tx_lpi_clockgating)
+ value |= LPI_CTRL_STATUS_LPITCSE;
+ }
+
+ mask = LPI_CTRL_STATUS_LPIATE | LPI_CTRL_STATUS_LPIEN |
+ LPI_CTRL_STATUS_LPITXA | LPI_CTRL_STATUS_LPITCSE;
+
+ value |= readl(ioaddr + GMAC4_LPI_CTRL_STATUS) & ~mask;
writel(value, ioaddr + GMAC4_LPI_CTRL_STATUS);
+
+ return 0;
}
static void dwmac4_set_eee_pls(struct mac_device_info *hw, int link)
@@ -413,34 +426,13 @@ static void dwmac4_set_eee_pls(struct mac_device_info *hw, int link)
value = readl(ioaddr + GMAC4_LPI_CTRL_STATUS);
if (link)
- value |= GMAC4_LPI_CTRL_STATUS_PLS;
+ value |= LPI_CTRL_STATUS_PLS;
else
- value &= ~GMAC4_LPI_CTRL_STATUS_PLS;
+ value &= ~LPI_CTRL_STATUS_PLS;
writel(value, ioaddr + GMAC4_LPI_CTRL_STATUS);
}
-static void dwmac4_set_eee_lpi_entry_timer(struct mac_device_info *hw, u32 et)
-{
- void __iomem *ioaddr = hw->pcsr;
- u32 value = et & STMMAC_ET_MAX;
- int regval;
-
- /* Program LPI entry timer value into register */
- writel(value, ioaddr + GMAC4_LPI_ENTRY_TIMER);
-
- /* Enable/disable LPI entry timer */
- regval = readl(ioaddr + GMAC4_LPI_CTRL_STATUS);
- regval |= GMAC4_LPI_CTRL_STATUS_LPIEN | GMAC4_LPI_CTRL_STATUS_LPITXA;
-
- if (et)
- regval |= GMAC4_LPI_CTRL_STATUS_LPIATE;
- else
- regval &= ~GMAC4_LPI_CTRL_STATUS_LPIATE;
-
- writel(regval, ioaddr + GMAC4_LPI_CTRL_STATUS);
-}
-
static void dwmac4_set_eee_timer(struct mac_device_info *hw, int ls, int tw)
{
void __iomem *ioaddr = hw->pcsr;
@@ -849,17 +841,17 @@ static int dwmac4_irq_status(struct mac_device_info *hw,
/* Clear LPI interrupt by reading MAC_LPI_Control_Status */
u32 status = readl(ioaddr + GMAC4_LPI_CTRL_STATUS);
- if (status & GMAC4_LPI_CTRL_STATUS_TLPIEN) {
+ if (status & LPI_CTRL_STATUS_TLPIEN) {
ret |= CORE_IRQ_TX_PATH_IN_LPI_MODE;
x->irq_tx_path_in_lpi_mode_n++;
}
- if (status & GMAC4_LPI_CTRL_STATUS_TLPIEX) {
+ if (status & LPI_CTRL_STATUS_TLPIEX) {
ret |= CORE_IRQ_TX_PATH_EXIT_LPI_MODE;
x->irq_tx_path_exit_lpi_mode_n++;
}
- if (status & GMAC4_LPI_CTRL_STATUS_RLPIEN)
+ if (status & LPI_CTRL_STATUS_RLPIEN)
x->irq_rx_path_in_lpi_mode_n++;
- if (status & GMAC4_LPI_CTRL_STATUS_RLPIEX)
+ if (status & LPI_CTRL_STATUS_RLPIEX)
x->irq_rx_path_exit_lpi_mode_n++;
}
@@ -1201,9 +1193,7 @@ const struct stmmac_ops dwmac4_ops = {
.pmt = dwmac4_pmt,
.set_umac_addr = dwmac4_set_umac_addr,
.get_umac_addr = dwmac4_get_umac_addr,
- .set_eee_mode = dwmac4_set_eee_mode,
- .reset_eee_mode = dwmac4_reset_eee_mode,
- .set_eee_lpi_entry_timer = dwmac4_set_eee_lpi_entry_timer,
+ .set_lpi_mode = dwmac4_set_lpi_mode,
.set_eee_timer = dwmac4_set_eee_timer,
.set_eee_pls = dwmac4_set_eee_pls,
.pcs_ctrl_ane = dwmac4_ctrl_ane,
@@ -1245,9 +1235,7 @@ const struct stmmac_ops dwmac410_ops = {
.pmt = dwmac4_pmt,
.set_umac_addr = dwmac4_set_umac_addr,
.get_umac_addr = dwmac4_get_umac_addr,
- .set_eee_mode = dwmac4_set_eee_mode,
- .reset_eee_mode = dwmac4_reset_eee_mode,
- .set_eee_lpi_entry_timer = dwmac4_set_eee_lpi_entry_timer,
+ .set_lpi_mode = dwmac4_set_lpi_mode,
.set_eee_timer = dwmac4_set_eee_timer,
.set_eee_pls = dwmac4_set_eee_pls,
.pcs_ctrl_ane = dwmac4_ctrl_ane,
@@ -1291,9 +1279,7 @@ const struct stmmac_ops dwmac510_ops = {
.pmt = dwmac4_pmt,
.set_umac_addr = dwmac4_set_umac_addr,
.get_umac_addr = dwmac4_get_umac_addr,
- .set_eee_mode = dwmac4_set_eee_mode,
- .reset_eee_mode = dwmac4_reset_eee_mode,
- .set_eee_lpi_entry_timer = dwmac4_set_eee_lpi_entry_timer,
+ .set_lpi_mode = dwmac4_set_lpi_mode,
.set_eee_timer = dwmac4_set_eee_timer,
.set_eee_pls = dwmac4_set_eee_pls,
.pcs_ctrl_ane = dwmac4_ctrl_ane,
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac4_lib.c b/drivers/net/ethernet/stmicro/stmmac/dwmac4_lib.c
index 57c03d491774..61584b569be7 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwmac4_lib.c
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac4_lib.c
@@ -50,10 +50,6 @@ void dwmac4_dma_start_tx(struct stmmac_priv *priv, void __iomem *ioaddr,
value |= DMA_CONTROL_ST;
writel(value, ioaddr + DMA_CHAN_TX_CONTROL(dwmac4_addrs, chan));
-
- value = readl(ioaddr + GMAC_CONFIG);
- value |= GMAC_CONFIG_TE;
- writel(value, ioaddr + GMAC_CONFIG);
}
void dwmac4_dma_stop_tx(struct stmmac_priv *priv, void __iomem *ioaddr,
@@ -77,10 +73,6 @@ void dwmac4_dma_start_rx(struct stmmac_priv *priv, void __iomem *ioaddr,
value |= DMA_CONTROL_SR;
writel(value, ioaddr + DMA_CHAN_RX_CONTROL(dwmac4_addrs, chan));
-
- value = readl(ioaddr + GMAC_CONFIG);
- value |= GMAC_CONFIG_RE;
- writel(value, ioaddr + GMAC_CONFIG);
}
void dwmac4_dma_stop_rx(struct stmmac_priv *priv, void __iomem *ioaddr,
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwxgmac2.h b/drivers/net/ethernet/stmicro/stmmac/dwxgmac2.h
index 20027d3c25a7..a03f5d771566 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwxgmac2.h
+++ b/drivers/net/ethernet/stmicro/stmmac/dwxgmac2.h
@@ -112,14 +112,7 @@
#define XGMAC_MGKPKTEN BIT(1)
#define XGMAC_PWRDWN BIT(0)
#define XGMAC_LPI_CTRL 0x000000d0
-#define XGMAC_TXCGE BIT(21)
-#define XGMAC_LPITXA BIT(19)
-#define XGMAC_PLS BIT(17)
-#define XGMAC_LPITXEN BIT(16)
-#define XGMAC_RLPIEX BIT(3)
-#define XGMAC_RLPIEN BIT(2)
-#define XGMAC_TLPIEX BIT(1)
-#define XGMAC_TLPIEN BIT(0)
+/* For definitions, see LPI_CTRL_STATUS_xxx in common.h */
#define XGMAC_LPI_TIMER_CTRL 0x000000d4
#define XGMAC_HW_FEATURE0 0x0000011c
#define XGMAC_HWFEAT_EDMA BIT(31)
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwxgmac2_core.c b/drivers/net/ethernet/stmicro/stmmac/dwxgmac2_core.c
index 9a60a6e8f633..a6d395c6bacd 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwxgmac2_core.c
+++ b/drivers/net/ethernet/stmicro/stmmac/dwxgmac2_core.c
@@ -316,17 +316,17 @@ static int dwxgmac2_host_irq_status(struct mac_device_info *hw,
if (stat & XGMAC_LPIIS) {
u32 lpi = readl(ioaddr + XGMAC_LPI_CTRL);
- if (lpi & XGMAC_TLPIEN) {
+ if (lpi & LPI_CTRL_STATUS_TLPIEN) {
ret |= CORE_IRQ_TX_PATH_IN_LPI_MODE;
x->irq_tx_path_in_lpi_mode_n++;
}
- if (lpi & XGMAC_TLPIEX) {
+ if (lpi & LPI_CTRL_STATUS_TLPIEX) {
ret |= CORE_IRQ_TX_PATH_EXIT_LPI_MODE;
x->irq_tx_path_exit_lpi_mode_n++;
}
- if (lpi & XGMAC_RLPIEN)
+ if (lpi & LPI_CTRL_STATUS_RLPIEN)
x->irq_rx_path_in_lpi_mode_n++;
- if (lpi & XGMAC_RLPIEX)
+ if (lpi & LPI_CTRL_STATUS_RLPIEX)
x->irq_rx_path_exit_lpi_mode_n++;
}
@@ -425,29 +425,28 @@ static void dwxgmac2_get_umac_addr(struct mac_device_info *hw,
addr[5] = (hi_addr >> 8) & 0xff;
}
-static void dwxgmac2_set_eee_mode(struct mac_device_info *hw,
- bool en_tx_lpi_clockgating)
+static int dwxgmac2_set_lpi_mode(struct mac_device_info *hw,
+ enum stmmac_lpi_mode mode,
+ bool en_tx_lpi_clockgating, u32 et)
{
void __iomem *ioaddr = hw->pcsr;
u32 value;
- value = readl(ioaddr + XGMAC_LPI_CTRL);
-
- value |= XGMAC_LPITXEN | XGMAC_LPITXA;
- if (en_tx_lpi_clockgating)
- value |= XGMAC_TXCGE;
-
- writel(value, ioaddr + XGMAC_LPI_CTRL);
-}
-
-static void dwxgmac2_reset_eee_mode(struct mac_device_info *hw)
-{
- void __iomem *ioaddr = hw->pcsr;
- u32 value;
+ if (mode == STMMAC_LPI_TIMER)
+ return -EOPNOTSUPP;
value = readl(ioaddr + XGMAC_LPI_CTRL);
- value &= ~(XGMAC_LPITXEN | XGMAC_LPITXA | XGMAC_TXCGE);
+ if (mode == STMMAC_LPI_FORCED) {
+ value |= LPI_CTRL_STATUS_LPIEN | LPI_CTRL_STATUS_LPITXA;
+ if (en_tx_lpi_clockgating)
+ value |= LPI_CTRL_STATUS_LPITCSE;
+ } else {
+ value &= ~(LPI_CTRL_STATUS_LPIEN | LPI_CTRL_STATUS_LPITXA |
+ LPI_CTRL_STATUS_LPITCSE);
+ }
writel(value, ioaddr + XGMAC_LPI_CTRL);
+
+ return 0;
}
static void dwxgmac2_set_eee_pls(struct mac_device_info *hw, int link)
@@ -457,9 +456,9 @@ static void dwxgmac2_set_eee_pls(struct mac_device_info *hw, int link)
value = readl(ioaddr + XGMAC_LPI_CTRL);
if (link)
- value |= XGMAC_PLS;
+ value |= LPI_CTRL_STATUS_PLS;
else
- value &= ~XGMAC_PLS;
+ value &= ~LPI_CTRL_STATUS_PLS;
writel(value, ioaddr + XGMAC_LPI_CTRL);
}
@@ -1525,8 +1524,7 @@ const struct stmmac_ops dwxgmac210_ops = {
.pmt = dwxgmac2_pmt,
.set_umac_addr = dwxgmac2_set_umac_addr,
.get_umac_addr = dwxgmac2_get_umac_addr,
- .set_eee_mode = dwxgmac2_set_eee_mode,
- .reset_eee_mode = dwxgmac2_reset_eee_mode,
+ .set_lpi_mode = dwxgmac2_set_lpi_mode,
.set_eee_timer = dwxgmac2_set_eee_timer,
.set_eee_pls = dwxgmac2_set_eee_pls,
.debug = NULL,
@@ -1582,8 +1580,7 @@ const struct stmmac_ops dwxlgmac2_ops = {
.pmt = dwxgmac2_pmt,
.set_umac_addr = dwxgmac2_set_umac_addr,
.get_umac_addr = dwxgmac2_get_umac_addr,
- .set_eee_mode = dwxgmac2_set_eee_mode,
- .reset_eee_mode = dwxgmac2_reset_eee_mode,
+ .set_lpi_mode = dwxgmac2_set_lpi_mode,
.set_eee_timer = dwxgmac2_set_eee_timer,
.set_eee_pls = dwxgmac2_set_eee_pls,
.debug = NULL,
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwxgmac2_dma.c b/drivers/net/ethernet/stmicro/stmmac/dwxgmac2_dma.c
index 7840bc403788..cba12edc1477 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwxgmac2_dma.c
+++ b/drivers/net/ethernet/stmicro/stmmac/dwxgmac2_dma.c
@@ -288,10 +288,6 @@ static void dwxgmac2_dma_start_tx(struct stmmac_priv *priv,
value = readl(ioaddr + XGMAC_DMA_CH_TX_CONTROL(chan));
value |= XGMAC_TXST;
writel(value, ioaddr + XGMAC_DMA_CH_TX_CONTROL(chan));
-
- value = readl(ioaddr + XGMAC_TX_CONFIG);
- value |= XGMAC_CONFIG_TE;
- writel(value, ioaddr + XGMAC_TX_CONFIG);
}
static void dwxgmac2_dma_stop_tx(struct stmmac_priv *priv, void __iomem *ioaddr,
@@ -302,10 +298,6 @@ static void dwxgmac2_dma_stop_tx(struct stmmac_priv *priv, void __iomem *ioaddr,
value = readl(ioaddr + XGMAC_DMA_CH_TX_CONTROL(chan));
value &= ~XGMAC_TXST;
writel(value, ioaddr + XGMAC_DMA_CH_TX_CONTROL(chan));
-
- value = readl(ioaddr + XGMAC_TX_CONFIG);
- value &= ~XGMAC_CONFIG_TE;
- writel(value, ioaddr + XGMAC_TX_CONFIG);
}
static void dwxgmac2_dma_start_rx(struct stmmac_priv *priv,
@@ -316,10 +308,6 @@ static void dwxgmac2_dma_start_rx(struct stmmac_priv *priv,
value = readl(ioaddr + XGMAC_DMA_CH_RX_CONTROL(chan));
value |= XGMAC_RXST;
writel(value, ioaddr + XGMAC_DMA_CH_RX_CONTROL(chan));
-
- value = readl(ioaddr + XGMAC_RX_CONFIG);
- value |= XGMAC_CONFIG_RE;
- writel(value, ioaddr + XGMAC_RX_CONFIG);
}
static void dwxgmac2_dma_stop_rx(struct stmmac_priv *priv, void __iomem *ioaddr,
diff --git a/drivers/net/ethernet/stmicro/stmmac/hwif.h b/drivers/net/ethernet/stmicro/stmmac/hwif.h
index 0f200b72c225..27c63a9fc163 100644
--- a/drivers/net/ethernet/stmicro/stmmac/hwif.h
+++ b/drivers/net/ethernet/stmicro/stmmac/hwif.h
@@ -306,6 +306,12 @@ struct stmmac_pps_cfg;
struct stmmac_rss;
struct stmmac_est;
+enum stmmac_lpi_mode {
+ STMMAC_LPI_DISABLE,
+ STMMAC_LPI_FORCED,
+ STMMAC_LPI_TIMER,
+};
+
/* Helpers to program the MAC core */
struct stmmac_ops {
/* MAC core initialization */
@@ -360,10 +366,9 @@ struct stmmac_ops {
unsigned int reg_n);
void (*get_umac_addr)(struct mac_device_info *hw, unsigned char *addr,
unsigned int reg_n);
- void (*set_eee_mode)(struct mac_device_info *hw,
- bool en_tx_lpi_clockgating);
- void (*reset_eee_mode)(struct mac_device_info *hw);
- void (*set_eee_lpi_entry_timer)(struct mac_device_info *hw, u32 et);
+ int (*set_lpi_mode)(struct mac_device_info *hw,
+ enum stmmac_lpi_mode mode,
+ bool en_tx_lpi_clockgating, u32 et);
void (*set_eee_timer)(struct mac_device_info *hw, int ls, int tw);
void (*set_eee_pls)(struct mac_device_info *hw, int link);
void (*debug)(struct stmmac_priv *priv, void __iomem *ioaddr,
@@ -467,12 +472,8 @@ struct stmmac_ops {
stmmac_do_void_callback(__priv, mac, set_umac_addr, __args)
#define stmmac_get_umac_addr(__priv, __args...) \
stmmac_do_void_callback(__priv, mac, get_umac_addr, __args)
-#define stmmac_set_eee_mode(__priv, __args...) \
- stmmac_do_void_callback(__priv, mac, set_eee_mode, __args)
-#define stmmac_reset_eee_mode(__priv, __args...) \
- stmmac_do_void_callback(__priv, mac, reset_eee_mode, __args)
-#define stmmac_set_eee_lpi_timer(__priv, __args...) \
- stmmac_do_void_callback(__priv, mac, set_eee_lpi_entry_timer, __args)
+#define stmmac_set_lpi_mode(__priv, __args...) \
+ stmmac_do_callback(__priv, mac, set_lpi_mode, __args)
#define stmmac_set_eee_timer(__priv, __args...) \
stmmac_do_void_callback(__priv, mac, set_eee_timer, __args)
#define stmmac_set_eee_pls(__priv, __args...) \
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac.h b/drivers/net/ethernet/stmicro/stmmac/stmmac.h
index f05cae103d83..639609f464ef 100644
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac.h
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac.h
@@ -282,8 +282,7 @@ struct stmmac_priv {
struct stmmac_channel channel[STMMAC_CH_MAX];
int speed;
- unsigned int flow_ctrl;
- unsigned int pause;
+ unsigned int pause_time;
struct mii_bus *mii;
struct phylink_config phylink_config;
@@ -407,6 +406,8 @@ int stmmac_dvr_probe(struct device *device,
int stmmac_reinit_queues(struct net_device *dev, u32 rx_cnt, u32 tx_cnt);
int stmmac_reinit_ringparam(struct net_device *dev, u32 rx_size, u32 tx_size);
int stmmac_bus_clks_config(struct stmmac_priv *priv, bool enabled);
+int stmmac_set_clk_tx_rate(void *bsp_priv, struct clk *clk_tx_i,
+ phy_interface_t interface, int speed);
static inline bool stmmac_xdp_is_enabled(struct stmmac_priv *priv)
{
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
index c0ae7db96f46..f867d6f06849 100644
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
@@ -88,13 +88,13 @@ MODULE_PARM_DESC(phyaddr, "Physical device address");
#define STMMAC_XDP_TX BIT(1)
#define STMMAC_XDP_REDIRECT BIT(2)
-static int flow_ctrl = FLOW_AUTO;
+static int flow_ctrl = 0xdead;
module_param(flow_ctrl, int, 0644);
-MODULE_PARM_DESC(flow_ctrl, "Flow control ability [on/off]");
+MODULE_PARM_DESC(flow_ctrl, "Flow control ability [on/off] (obsolete)");
static int pause = PAUSE_TIME;
module_param(pause, int, 0644);
-MODULE_PARM_DESC(pause, "Flow Control Pause Time");
+MODULE_PARM_DESC(pause, "Flow Control Pause Time (units of 512 bit times)");
#define TC_DEFAULT 64
static int tc = TC_DEFAULT;
@@ -178,6 +178,38 @@ int stmmac_bus_clks_config(struct stmmac_priv *priv, bool enabled)
EXPORT_SYMBOL_GPL(stmmac_bus_clks_config);
/**
+ * stmmac_set_clk_tx_rate() - set the clock rate for the MAC transmit clock
+ * @bsp_priv: BSP private data structure (unused)
+ * @clk_tx_i: the transmit clock
+ * @interface: the selected interface mode
+ * @speed: the speed that the MAC will be operating at
+ *
+ * Set the transmit clock rate for the MAC, normally 2.5MHz for 10Mbps,
+ * 25MHz for 100Mbps and 125MHz for 1Gbps. This is suitable for at least
+ * MII, GMII, RGMII and RMII interface modes. Platforms can hook this into
+ * the plat_data->set_clk_tx_rate method directly, call it via their own
+ * implementation, or implement their own method should they have more
+ * complex requirements. It is intended to only be used in this method.
+ *
+ * plat_data->clk_tx_i must be filled in.
+ */
+int stmmac_set_clk_tx_rate(void *bsp_priv, struct clk *clk_tx_i,
+ phy_interface_t interface, int speed)
+{
+ long rate = rgmii_clock(speed);
+
+ /* Silently ignore unsupported speeds as rgmii_clock() only
+ * supports 10, 100 and 1000Mbps. We do not want to spit
+ * errors for 2500 and higher speeds here.
+ */
+ if (rate < 0)
+ return 0;
+
+ return clk_set_rate(clk_tx_i, rate);
+}
+EXPORT_SYMBOL_GPL(stmmac_set_clk_tx_rate);
+
+/**
* stmmac_verify_args - verify the driver parameters.
* Description: it checks the driver parameters and set a default in case of
* errors.
@@ -188,12 +220,11 @@ static void stmmac_verify_args(void)
watchdog = TX_TIMEO;
if (unlikely((buf_sz < DEFAULT_BUFSIZE) || (buf_sz > BUF_SIZE_16KiB)))
buf_sz = DEFAULT_BUFSIZE;
- if (unlikely(flow_ctrl > 1))
- flow_ctrl = FLOW_AUTO;
- else if (likely(flow_ctrl < 0))
- flow_ctrl = FLOW_OFF;
if (unlikely((pause < 0) || (pause > 0xffff)))
pause = PAUSE_TIME;
+
+ if (flow_ctrl != 0xdead)
+ pr_warn("stmmac: module parameter 'flow_ctrl' is obsolete - please remove from your module configuration\n");
}
static void __stmmac_disable_all_queues(struct stmmac_priv *priv)
@@ -390,16 +421,6 @@ static inline u32 stmmac_rx_dirty(struct stmmac_priv *priv, u32 queue)
return dirty;
}
-static void stmmac_disable_hw_lpi_timer(struct stmmac_priv *priv)
-{
- stmmac_set_eee_lpi_timer(priv, priv->hw, 0);
-}
-
-static void stmmac_enable_hw_lpi_timer(struct stmmac_priv *priv)
-{
- stmmac_set_eee_lpi_timer(priv, priv->hw, priv->tx_lpi_timer);
-}
-
static bool stmmac_eee_tx_busy(struct stmmac_priv *priv)
{
u32 tx_cnt = priv->plat->tx_queues_to_use;
@@ -436,8 +457,9 @@ static void stmmac_try_to_start_sw_lpi(struct stmmac_priv *priv)
/* Check and enter in LPI mode */
if (!priv->tx_path_in_lpi_mode)
- stmmac_set_eee_mode(priv, priv->hw,
- priv->plat->flags & STMMAC_FLAG_EN_TX_LPI_CLOCKGATING);
+ stmmac_set_lpi_mode(priv, priv->hw, STMMAC_LPI_FORCED,
+ priv->plat->flags & STMMAC_FLAG_EN_TX_LPI_CLOCKGATING,
+ 0);
}
/**
@@ -447,8 +469,8 @@ static void stmmac_try_to_start_sw_lpi(struct stmmac_priv *priv)
*/
static void stmmac_stop_sw_lpi(struct stmmac_priv *priv)
{
- stmmac_reset_eee_mode(priv, priv->hw);
del_timer_sync(&priv->eee_ctrl_timer);
+ stmmac_set_lpi_mode(priv, priv->hw, STMMAC_LPI_DISABLE, false, 0);
priv->tx_path_in_lpi_mode = false;
}
@@ -466,74 +488,6 @@ static void stmmac_eee_ctrl_timer(struct timer_list *t)
stmmac_try_to_start_sw_lpi(priv);
}
-/**
- * stmmac_eee_init - init EEE
- * @priv: driver private structure
- * @active: indicates whether EEE should be enabled.
- * Description:
- * if the GMAC supports the EEE (from the HW cap reg) and the phy device
- * can also manage EEE, this function enable the LPI state and start related
- * timer.
- */
-static void stmmac_eee_init(struct stmmac_priv *priv, bool active)
-{
- priv->eee_active = active;
-
- /* Check if MAC core supports the EEE feature. */
- if (!priv->dma_cap.eee) {
- priv->eee_enabled = false;
- return;
- }
-
- mutex_lock(&priv->lock);
-
- /* Check if it needs to be deactivated */
- if (!priv->eee_active) {
- if (priv->eee_enabled) {
- netdev_dbg(priv->dev, "disable EEE\n");
- priv->eee_sw_timer_en = false;
- stmmac_disable_hw_lpi_timer(priv);
- del_timer_sync(&priv->eee_ctrl_timer);
- stmmac_set_eee_timer(priv, priv->hw, 0,
- STMMAC_DEFAULT_TWT_LS);
- if (priv->hw->xpcs)
- xpcs_config_eee(priv->hw->xpcs,
- priv->plat->mult_fact_100ns,
- false);
- }
- priv->eee_enabled = false;
- mutex_unlock(&priv->lock);
- return;
- }
-
- if (priv->eee_active && !priv->eee_enabled) {
- stmmac_set_eee_timer(priv, priv->hw, STMMAC_DEFAULT_LIT_LS,
- STMMAC_DEFAULT_TWT_LS);
- if (priv->hw->xpcs)
- xpcs_config_eee(priv->hw->xpcs,
- priv->plat->mult_fact_100ns,
- true);
- }
-
- if (priv->plat->has_gmac4 && priv->tx_lpi_timer <= STMMAC_ET_MAX) {
- /* Use hardware LPI mode */
- del_timer_sync(&priv->eee_ctrl_timer);
- priv->tx_path_in_lpi_mode = false;
- priv->eee_sw_timer_en = false;
- stmmac_enable_hw_lpi_timer(priv);
- } else {
- /* Use software LPI mode */
- priv->eee_sw_timer_en = true;
- stmmac_disable_hw_lpi_timer(priv);
- stmmac_restart_sw_lpi_timer(priv);
- }
-
- priv->eee_enabled = true;
-
- mutex_unlock(&priv->lock);
- netdev_dbg(priv->dev, "Energy-Efficient Ethernet initialized\n");
-}
-
/* stmmac_get_tx_hwtstamp - get HW TX timestamps
* @priv: driver private structure
* @p : descriptor pointer
@@ -935,14 +889,16 @@ static void stmmac_release_ptp(struct stmmac_priv *priv)
* stmmac_mac_flow_ctrl - Configure flow control in all queues
* @priv: driver private structure
* @duplex: duplex passed to the next function
+ * @flow_ctrl: desired flow control modes
* Description: It is used for configuring the flow control in all queues
*/
-static void stmmac_mac_flow_ctrl(struct stmmac_priv *priv, u32 duplex)
+static void stmmac_mac_flow_ctrl(struct stmmac_priv *priv, u32 duplex,
+ unsigned int flow_ctrl)
{
u32 tx_cnt = priv->plat->tx_queues_to_use;
- stmmac_flow_ctrl(priv, priv->hw, duplex, priv->flow_ctrl,
- priv->pause, tx_cnt);
+ stmmac_flow_ctrl(priv, priv->hw, duplex, flow_ctrl, priv->pause_time,
+ tx_cnt);
}
static unsigned long stmmac_mac_get_caps(struct phylink_config *config,
@@ -1002,7 +958,9 @@ static void stmmac_mac_link_up(struct phylink_config *config,
bool tx_pause, bool rx_pause)
{
struct stmmac_priv *priv = netdev_priv(to_net_dev(config->dev));
+ unsigned int flow_ctrl;
u32 old_ctrl, ctrl;
+ int ret;
if ((priv->plat->flags & STMMAC_FLAG_SERDES_UP_AFTER_PHY_LINKUP) &&
priv->plat->serdes_powerup)
@@ -1082,19 +1040,29 @@ static void stmmac_mac_link_up(struct phylink_config *config,
/* Flow Control operation */
if (rx_pause && tx_pause)
- priv->flow_ctrl = FLOW_AUTO;
+ flow_ctrl = FLOW_AUTO;
else if (rx_pause && !tx_pause)
- priv->flow_ctrl = FLOW_RX;
+ flow_ctrl = FLOW_RX;
else if (!rx_pause && tx_pause)
- priv->flow_ctrl = FLOW_TX;
+ flow_ctrl = FLOW_TX;
else
- priv->flow_ctrl = FLOW_OFF;
+ flow_ctrl = FLOW_OFF;
- stmmac_mac_flow_ctrl(priv, duplex);
+ stmmac_mac_flow_ctrl(priv, duplex, flow_ctrl);
if (ctrl != old_ctrl)
writel(ctrl, priv->ioaddr + MAC_CTRL_REG);
+ if (priv->plat->set_clk_tx_rate) {
+ ret = priv->plat->set_clk_tx_rate(priv->plat->bsp_priv,
+ priv->plat->clk_tx_i,
+ interface, speed);
+ if (ret < 0)
+ netdev_err(priv->dev,
+ "failed to configure transmit clock for %dMbps: %pe\n",
+ speed, ERR_PTR(ret));
+ }
+
stmmac_mac_set(priv, priv->ioaddr, true);
if (priv->dma_cap.eee)
stmmac_set_eee_pls(priv, priv->hw, true);
@@ -1106,20 +1074,59 @@ static void stmmac_mac_link_up(struct phylink_config *config,
stmmac_hwtstamp_correct_latency(priv, priv);
}
-static void stmmac_mac_disable_tx_lpi(struct phylink_config *config)
+static void stmmac_mac_disable_tx_lpi(struct phylink_config *config,
+ phy_interface_t interface)
{
struct stmmac_priv *priv = netdev_priv(to_net_dev(config->dev));
- stmmac_eee_init(priv, false);
+ priv->eee_active = false;
+
+ mutex_lock(&priv->lock);
+
+ priv->eee_enabled = false;
+
+ netdev_dbg(priv->dev, "disable EEE\n");
+ priv->eee_sw_timer_en = false;
+ del_timer_sync(&priv->eee_ctrl_timer);
+ stmmac_set_lpi_mode(priv, priv->hw, STMMAC_LPI_DISABLE, false, 0);
+ priv->tx_path_in_lpi_mode = false;
+
+ stmmac_set_eee_timer(priv, priv->hw, 0, STMMAC_DEFAULT_TWT_LS);
+ mutex_unlock(&priv->lock);
}
-static int stmmac_mac_enable_tx_lpi(struct phylink_config *config, u32 timer,
+static int stmmac_mac_enable_tx_lpi(struct phylink_config *config,
+ phy_interface_t interface, u32 timer,
bool tx_clk_stop)
{
struct stmmac_priv *priv = netdev_priv(to_net_dev(config->dev));
+ int ret;
priv->tx_lpi_timer = timer;
- stmmac_eee_init(priv, true);
+ priv->eee_active = true;
+
+ mutex_lock(&priv->lock);
+
+ priv->eee_enabled = true;
+
+ stmmac_set_eee_timer(priv, priv->hw, STMMAC_DEFAULT_LIT_LS,
+ STMMAC_DEFAULT_TWT_LS);
+
+ /* Try to cnfigure the hardware timer. */
+ ret = stmmac_set_lpi_mode(priv, priv->hw, STMMAC_LPI_TIMER,
+ priv->plat->flags & STMMAC_FLAG_EN_TX_LPI_CLOCKGATING,
+ priv->tx_lpi_timer);
+
+ if (ret) {
+ /* Hardware timer mode not supported, or value out of range.
+ * Fall back to using software LPI mode
+ */
+ priv->eee_sw_timer_en = true;
+ stmmac_restart_sw_lpi_timer(priv);
+ }
+
+ mutex_unlock(&priv->lock);
+ netdev_dbg(priv->dev, "Energy-Efficient Ethernet initialized\n");
return 0;
}
@@ -3072,7 +3079,7 @@ static int stmmac_init_dma_engine(struct stmmac_priv *priv)
int ret = 0;
if (!priv->plat->dma_cfg || !priv->plat->dma_cfg->pbl) {
- dev_err(priv->device, "Invalid DMA configuration\n");
+ netdev_err(priv->dev, "Invalid DMA configuration\n");
return -EINVAL;
}
@@ -3081,7 +3088,7 @@ static int stmmac_init_dma_engine(struct stmmac_priv *priv)
ret = stmmac_reset(priv, priv->ioaddr);
if (ret) {
- dev_err(priv->device, "Failed to reset the dma\n");
+ netdev_err(priv->dev, "Failed to reset the dma\n");
return ret;
}
@@ -3488,9 +3495,6 @@ static int stmmac_hw_setup(struct net_device *dev, bool ptp_register)
priv->hw->rx_csum = 0;
}
- /* Enable the MAC Rx/Tx */
- stmmac_mac_set(priv, priv->ioaddr, true);
-
/* Set the HW DMA mode and the COE */
stmmac_dma_operation_mode(priv);
@@ -4102,9 +4106,6 @@ static int stmmac_release(struct net_device *dev)
/* Release and free the Rx/Tx resources */
free_dma_desc_resources(priv, &priv->dma_conf);
- /* Disable the MAC Rx/Tx */
- stmmac_mac_set(priv, priv->ioaddr, false);
-
/* Powerdown Serdes if there is */
if (priv->plat->serdes_powerdown)
priv->plat->serdes_powerdown(dev, priv->plat->bsp_priv);
@@ -6869,6 +6870,8 @@ void stmmac_xdp_release(struct net_device *dev)
/* Ensure tx function is not running */
netif_tx_disable(dev);
+ phylink_stop(priv->phylink);
+
/* Disable NAPI process */
stmmac_disable_all_queues(priv);
@@ -6884,14 +6887,10 @@ void stmmac_xdp_release(struct net_device *dev)
/* Release and free the Rx/Tx resources */
free_dma_desc_resources(priv, &priv->dma_conf);
- /* Disable the MAC Rx/Tx */
- stmmac_mac_set(priv, priv->ioaddr, false);
-
/* set trans_start so we don't get spurious
* watchdogs during reset
*/
netif_trans_update(dev);
- netif_carrier_off(dev);
}
int stmmac_xdp_open(struct net_device *dev)
@@ -6974,25 +6973,25 @@ int stmmac_xdp_open(struct net_device *dev)
tx_q->txtimer.function = stmmac_tx_timer;
}
- /* Enable the MAC Rx/Tx */
- stmmac_mac_set(priv, priv->ioaddr, true);
-
/* Start Rx & Tx DMA Channels */
stmmac_start_all_dma(priv);
+ phylink_start(priv->phylink);
+
ret = stmmac_request_irq(dev);
if (ret)
goto irq_error;
/* Enable NAPI process*/
stmmac_enable_all_queues(priv);
- netif_carrier_on(dev);
netif_tx_start_all_queues(dev);
stmmac_enable_all_dma_irq(priv);
return 0;
irq_error:
+ phylink_stop(priv->phylink);
+
for (chan = 0; chan < priv->plat->tx_queues_to_use; chan++)
hrtimer_cancel(&priv->dma_conf.tx_queue[chan].txtimer);
@@ -7444,7 +7443,7 @@ int stmmac_dvr_probe(struct device *device,
return -ENOMEM;
stmmac_set_ethtool_ops(ndev);
- priv->pause = pause;
+ priv->pause_time = pause;
priv->plat = plat_dat;
priv->ioaddr = res->addr;
priv->dev->base_addr = (unsigned long)res->addr;
@@ -7640,9 +7639,6 @@ int stmmac_dvr_probe(struct device *device,
"%s: warning: maxmtu having invalid value (%d)\n",
__func__, priv->plat->maxmtu);
- if (flow_ctrl)
- priv->flow_ctrl = FLOW_AUTO; /* RX/TX pause on */
-
ndev->priv_flags |= IFF_LIVE_ADDR_CHANGE;
/* Setup channels NAPI */
@@ -7744,8 +7740,6 @@ void stmmac_dvr_remove(struct device *dev)
pm_runtime_get_sync(dev);
- stmmac_stop_all_dma(priv);
- stmmac_mac_set(priv, priv->ioaddr, false);
unregister_netdev(ndev);
#ifdef CONFIG_DEBUG_FS
@@ -7816,13 +7810,11 @@ int stmmac_suspend(struct device *dev)
mutex_unlock(&priv->lock);
rtnl_lock();
- if (device_may_wakeup(priv->device) && priv->plat->pmt) {
- phylink_suspend(priv->phylink, true);
- } else {
- if (device_may_wakeup(priv->device))
- phylink_speed_down(priv->phylink, false);
- phylink_suspend(priv->phylink, false);
- }
+ if (device_may_wakeup(priv->device) && !priv->plat->pmt)
+ phylink_speed_down(priv->phylink, false);
+
+ phylink_suspend(priv->phylink,
+ device_may_wakeup(priv->device) && priv->plat->pmt);
rtnl_unlock();
if (stmmac_fpe_supported(priv))
@@ -7912,16 +7904,8 @@ int stmmac_resume(struct device *dev)
}
rtnl_lock();
- if (device_may_wakeup(priv->device) && priv->plat->pmt) {
- phylink_resume(priv->phylink);
- } else {
- phylink_resume(priv->phylink);
- if (device_may_wakeup(priv->device))
- phylink_speed_up(priv->phylink);
- }
- rtnl_unlock();
+ phylink_prepare_resume(priv->phylink);
- rtnl_lock();
mutex_lock(&priv->lock);
stmmac_reset_queues_param(priv);
@@ -7939,6 +7923,11 @@ int stmmac_resume(struct device *dev)
stmmac_enable_all_dma_irq(priv);
mutex_unlock(&priv->lock);
+
+ phylink_resume(priv->phylink);
+ if (device_may_wakeup(priv->device) && !priv->plat->pmt)
+ phylink_speed_up(priv->phylink);
+
rtnl_unlock();
netif_device_attach(ndev);
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c
index 0c7d81ddd440..7c0a4046bbe3 100644
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c
@@ -524,6 +524,8 @@ int stmmac_pcs_setup(struct net_device *ndev)
if (ret)
return dev_err_probe(priv->device, ret, "No xPCS found\n");
+ xpcs_config_eee_mult_fact(xpcs, priv->plat->mult_fact_100ns);
+
priv->hw->xpcs = xpcs;
return 0;
diff --git a/drivers/net/pcs/pcs-xpcs.c b/drivers/net/pcs/pcs-xpcs.c
index 1faa37f0e7b9..bb16fdf49cab 100644
--- a/drivers/net/pcs/pcs-xpcs.c
+++ b/drivers/net/pcs/pcs-xpcs.c
@@ -602,36 +602,6 @@ static void xpcs_get_interfaces(struct dw_xpcs *xpcs, unsigned long *interfaces)
__set_bit(compat->interface, interfaces);
}
-int xpcs_config_eee(struct dw_xpcs *xpcs, int mult_fact_100ns, int enable)
-{
- u16 mask, val;
- int ret;
-
- mask = DW_VR_MII_EEE_LTX_EN | DW_VR_MII_EEE_LRX_EN |
- DW_VR_MII_EEE_TX_QUIET_EN | DW_VR_MII_EEE_RX_QUIET_EN |
- DW_VR_MII_EEE_TX_EN_CTRL | DW_VR_MII_EEE_RX_EN_CTRL |
- DW_VR_MII_EEE_MULT_FACT_100NS;
-
- if (enable)
- val = DW_VR_MII_EEE_LTX_EN | DW_VR_MII_EEE_LRX_EN |
- DW_VR_MII_EEE_TX_QUIET_EN | DW_VR_MII_EEE_RX_QUIET_EN |
- DW_VR_MII_EEE_TX_EN_CTRL | DW_VR_MII_EEE_RX_EN_CTRL |
- FIELD_PREP(DW_VR_MII_EEE_MULT_FACT_100NS,
- mult_fact_100ns);
- else
- val = 0;
-
- ret = xpcs_modify(xpcs, MDIO_MMD_VEND2, DW_VR_MII_EEE_MCTRL0, mask,
- val);
- if (ret < 0)
- return ret;
-
- return xpcs_modify(xpcs, MDIO_MMD_VEND2, DW_VR_MII_EEE_MCTRL1,
- DW_VR_MII_EEE_TRN_LPI,
- enable ? DW_VR_MII_EEE_TRN_LPI : 0);
-}
-EXPORT_SYMBOL_GPL(xpcs_config_eee);
-
static void xpcs_pre_config(struct phylink_pcs *pcs, phy_interface_t interface)
{
struct dw_xpcs *xpcs = phylink_pcs_to_xpcs(pcs);
@@ -695,15 +665,23 @@ static int xpcs_config_aneg_c37_sgmii(struct dw_xpcs *xpcs,
val = FIELD_PREP(DW_VR_MII_PCS_MODE_MASK,
DW_VR_MII_PCS_MODE_C37_SGMII);
- if (xpcs->info.pma == WX_TXGBE_XPCS_PMA_10G_ID) {
- mask |= DW_VR_MII_AN_CTRL_8BIT;
+ switch (xpcs->sgmii_10_100_8bit) {
+ case DW_XPCS_SGMII_10_100_8BIT:
val |= DW_VR_MII_AN_CTRL_8BIT;
- /* Hardware requires it to be PHY side SGMII */
- tx_conf = DW_VR_MII_TX_CONFIG_PHY_SIDE_SGMII;
- } else {
- tx_conf = DW_VR_MII_TX_CONFIG_MAC_SIDE_SGMII;
+ fallthrough;
+ case DW_XPCS_SGMII_10_100_4BIT:
+ mask |= DW_VR_MII_AN_CTRL_8BIT;
+ fallthrough;
+ case DW_XPCS_SGMII_10_100_UNCHANGED:
+ break;
}
+ if (xpcs->sgmii_mode == DW_XPCS_SGMII_MODE_MAC_AUTO ||
+ xpcs->sgmii_mode == DW_XPCS_SGMII_MODE_MAC_MANUAL)
+ tx_conf = DW_VR_MII_TX_CONFIG_MAC_SIDE_SGMII;
+ else
+ tx_conf = DW_VR_MII_TX_CONFIG_PHY_SIDE_SGMII;
+
val |= FIELD_PREP(DW_VR_MII_TX_CONFIG_MASK, tx_conf);
ret = xpcs_modify(xpcs, MDIO_MMD_VEND2, DW_VR_MII_AN_CTRL, mask, val);
@@ -713,12 +691,19 @@ static int xpcs_config_aneg_c37_sgmii(struct dw_xpcs *xpcs,
val = 0;
mask = DW_VR_MII_DIG_CTRL1_2G5_EN | DW_VR_MII_DIG_CTRL1_MAC_AUTO_SW;
- if (neg_mode == PHYLINK_PCS_NEG_INBAND_ENABLED)
- val = DW_VR_MII_DIG_CTRL1_MAC_AUTO_SW;
+ switch (xpcs->sgmii_mode) {
+ case DW_XPCS_SGMII_MODE_MAC_AUTO:
+ if (neg_mode == PHYLINK_PCS_NEG_INBAND_ENABLED)
+ val = DW_VR_MII_DIG_CTRL1_MAC_AUTO_SW;
+ break;
- if (xpcs->info.pma == WX_TXGBE_XPCS_PMA_10G_ID) {
+ case DW_XPCS_SGMII_MODE_MAC_MANUAL:
+ break;
+
+ case DW_XPCS_SGMII_MODE_PHY_HW:
mask |= DW_VR_MII_DIG_CTRL1_PHY_MODE_CTRL;
val |= DW_VR_MII_DIG_CTRL1_PHY_MODE_CTRL;
+ break;
}
ret = xpcs_modify(xpcs, MDIO_MMD_VEND2, DW_VR_MII_DIG_CTRL1, mask, val);
@@ -759,9 +744,18 @@ static int xpcs_config_aneg_c37_1000basex(struct dw_xpcs *xpcs,
return ret;
}
- mask = DW_VR_MII_PCS_MODE_MASK;
+ /* Older XPCS IP requires PHY_MODE (bit 3) and SGMII_LINK (but 4) to
+ * be set when operating in 1000BASE-X mode. See page 233
+ * https://ww1.microchip.com/downloads/aemDocuments/documents/OTH/ProductDocuments/DataSheets/KSZ9477S-Data-Sheet-DS00002392C.pdf
+ * "5.5.9 SGMII AUTO-NEGOTIATION CONTROL REGISTER"
+ */
+ mask = DW_VR_MII_PCS_MODE_MASK | DW_VR_MII_AN_CTRL_SGMII_LINK |
+ DW_VR_MII_TX_CONFIG_MASK;
val = FIELD_PREP(DW_VR_MII_PCS_MODE_MASK,
- DW_VR_MII_PCS_MODE_C37_1000BASEX);
+ DW_VR_MII_PCS_MODE_C37_1000BASEX) |
+ FIELD_PREP(DW_VR_MII_TX_CONFIG_MASK,
+ DW_VR_MII_TX_CONFIG_PHY_SIDE_SGMII) |
+ DW_VR_MII_AN_CTRL_SGMII_LINK;
if (!xpcs->pcs.poll) {
mask |= DW_VR_MII_AN_INTR_EN;
@@ -1140,7 +1134,9 @@ static void xpcs_link_up_sgmii_1000basex(struct dw_xpcs *xpcs,
{
int ret;
- if (neg_mode == PHYLINK_PCS_NEG_INBAND_ENABLED)
+ if (neg_mode == PHYLINK_PCS_NEG_INBAND_ENABLED &&
+ !(interface == PHY_INTERFACE_MODE_SGMII &&
+ xpcs->sgmii_mode == DW_XPCS_SGMII_MODE_MAC_MANUAL))
return;
if (interface == PHY_INTERFACE_MODE_1000BASEX) {
@@ -1157,10 +1153,11 @@ static void xpcs_link_up_sgmii_1000basex(struct dw_xpcs *xpcs,
__func__);
}
- ret = xpcs_write(xpcs, MDIO_MMD_VEND2, MII_BMCR,
- mii_bmcr_encode_fixed(speed, duplex));
+ ret = xpcs_modify(xpcs, MDIO_MMD_VEND2, MII_BMCR,
+ BMCR_SPEED1000 | BMCR_FULLDPLX | BMCR_SPEED100,
+ mii_bmcr_encode_fixed(speed, duplex));
if (ret)
- dev_err(&xpcs->mdiodev->dev, "%s: xpcs_write returned %pe\n",
+ dev_err(&xpcs->mdiodev->dev, "%s: xpcs_modify returned %pe\n",
__func__, ERR_PTR(ret));
}
@@ -1193,6 +1190,63 @@ static void xpcs_an_restart(struct phylink_pcs *pcs)
BMCR_ANRESTART);
}
+static int xpcs_config_eee(struct dw_xpcs *xpcs, bool enable)
+{
+ u16 mask, val;
+ int ret;
+
+ mask = DW_VR_MII_EEE_LTX_EN | DW_VR_MII_EEE_LRX_EN |
+ DW_VR_MII_EEE_TX_QUIET_EN | DW_VR_MII_EEE_RX_QUIET_EN |
+ DW_VR_MII_EEE_TX_EN_CTRL | DW_VR_MII_EEE_RX_EN_CTRL |
+ DW_VR_MII_EEE_MULT_FACT_100NS;
+
+ if (enable)
+ val = DW_VR_MII_EEE_LTX_EN | DW_VR_MII_EEE_LRX_EN |
+ DW_VR_MII_EEE_TX_QUIET_EN | DW_VR_MII_EEE_RX_QUIET_EN |
+ DW_VR_MII_EEE_TX_EN_CTRL | DW_VR_MII_EEE_RX_EN_CTRL |
+ FIELD_PREP(DW_VR_MII_EEE_MULT_FACT_100NS,
+ xpcs->eee_mult_fact);
+ else
+ val = 0;
+
+ ret = xpcs_modify(xpcs, MDIO_MMD_VEND2, DW_VR_MII_EEE_MCTRL0, mask,
+ val);
+ if (ret < 0)
+ return ret;
+
+ return xpcs_modify(xpcs, MDIO_MMD_VEND2, DW_VR_MII_EEE_MCTRL1,
+ DW_VR_MII_EEE_TRN_LPI,
+ enable ? DW_VR_MII_EEE_TRN_LPI : 0);
+}
+
+static void xpcs_disable_eee(struct phylink_pcs *pcs)
+{
+ struct dw_xpcs *xpcs = phylink_pcs_to_xpcs(pcs);
+
+ xpcs_config_eee(xpcs, false);
+}
+
+static void xpcs_enable_eee(struct phylink_pcs *pcs)
+{
+ struct dw_xpcs *xpcs = phylink_pcs_to_xpcs(pcs);
+
+ xpcs_config_eee(xpcs, true);
+}
+
+/**
+ * xpcs_config_eee_mult_fact() - set the EEE clock multiplying factor
+ * @xpcs: pointer to a &struct dw_xpcs instance
+ * @mult_fact: the multiplying factor
+ *
+ * Configure the EEE clock multiplying factor. This value should be such that
+ * clk_eee_time_period * (mult_fact + 1) is within the range 80 to 120ns.
+ */
+void xpcs_config_eee_mult_fact(struct dw_xpcs *xpcs, u8 mult_fact)
+{
+ xpcs->eee_mult_fact = mult_fact;
+}
+EXPORT_SYMBOL_GPL(xpcs_config_eee_mult_fact);
+
static int xpcs_read_ids(struct dw_xpcs *xpcs)
{
int ret;
@@ -1341,6 +1395,8 @@ static const struct phylink_pcs_ops xpcs_phylink_ops = {
.pcs_get_state = xpcs_get_state,
.pcs_an_restart = xpcs_an_restart,
.pcs_link_up = xpcs_link_up,
+ .pcs_disable_eee = xpcs_disable_eee,
+ .pcs_enable_eee = xpcs_enable_eee,
};
static int xpcs_identify(struct dw_xpcs *xpcs)
@@ -1450,10 +1506,13 @@ static struct dw_xpcs *xpcs_create(struct mdio_device *mdiodev)
xpcs_get_interfaces(xpcs, xpcs->pcs.supported_interfaces);
- if (xpcs->info.pma == WX_TXGBE_XPCS_PMA_10G_ID)
+ if (xpcs->info.pma == WX_TXGBE_XPCS_PMA_10G_ID) {
xpcs->pcs.poll = false;
- else
+ xpcs->sgmii_10_100_8bit = DW_XPCS_SGMII_10_100_8BIT;
+ xpcs->sgmii_mode = DW_XPCS_SGMII_MODE_PHY_HW;
+ } else {
xpcs->need_reset = true;
+ }
return xpcs;
diff --git a/drivers/net/pcs/pcs-xpcs.h b/drivers/net/pcs/pcs-xpcs.h
index adc5a0b3c883..db52dc619b80 100644
--- a/drivers/net/pcs/pcs-xpcs.h
+++ b/drivers/net/pcs/pcs-xpcs.h
@@ -55,24 +55,13 @@
/* Clause 37 Defines */
/* VR MII MMD registers offsets */
#define DW_VR_MII_DIG_CTRL1 0x8000
-#define DW_VR_MII_AN_CTRL 0x8001
-#define DW_VR_MII_AN_INTR_STS 0x8002
-/* EEE Mode Control Register */
-#define DW_VR_MII_EEE_MCTRL0 0x8006
-#define DW_VR_MII_EEE_MCTRL1 0x800b
-#define DW_VR_MII_DIG_CTRL2 0x80e1
-
-/* VR_MII_DIG_CTRL1 */
#define DW_VR_MII_DIG_CTRL1_MAC_AUTO_SW BIT(9)
#define DW_VR_MII_DIG_CTRL1_2G5_EN BIT(2)
#define DW_VR_MII_DIG_CTRL1_PHY_MODE_CTRL BIT(0)
-/* VR_MII_DIG_CTRL2 */
-#define DW_VR_MII_DIG_CTRL2_TX_POL_INV BIT(4)
-#define DW_VR_MII_DIG_CTRL2_RX_POL_INV BIT(0)
-
-/* VR_MII_AN_CTRL */
+#define DW_VR_MII_AN_CTRL 0x8001
#define DW_VR_MII_AN_CTRL_8BIT BIT(8)
+#define DW_VR_MII_AN_CTRL_SGMII_LINK BIT(4)
#define DW_VR_MII_TX_CONFIG_MASK BIT(3)
#define DW_VR_MII_TX_CONFIG_PHY_SIDE_SGMII 0x1
#define DW_VR_MII_TX_CONFIG_MAC_SIDE_SGMII 0x0
@@ -81,7 +70,7 @@
#define DW_VR_MII_PCS_MODE_C37_SGMII 0x2
#define DW_VR_MII_AN_INTR_EN BIT(0)
-/* VR_MII_AN_INTR_STS */
+#define DW_VR_MII_AN_INTR_STS 0x8002
#define DW_VR_MII_AN_STS_C37_ANCMPLT_INTR BIT(0)
#define DW_VR_MII_AN_STS_C37_ANSGM_FD BIT(1)
#define DW_VR_MII_AN_STS_C37_ANSGM_SP GENMASK(3, 2)
@@ -90,19 +79,22 @@
#define DW_VR_MII_C37_ANSGM_SP_1000 0x2
#define DW_VR_MII_C37_ANSGM_SP_LNKSTS BIT(4)
-/* VR MII EEE Control 0 defines */
+#define DW_VR_MII_EEE_MCTRL0 0x8006
#define DW_VR_MII_EEE_LTX_EN BIT(0) /* LPI Tx Enable */
#define DW_VR_MII_EEE_LRX_EN BIT(1) /* LPI Rx Enable */
#define DW_VR_MII_EEE_TX_QUIET_EN BIT(2) /* Tx Quiet Enable */
#define DW_VR_MII_EEE_RX_QUIET_EN BIT(3) /* Rx Quiet Enable */
#define DW_VR_MII_EEE_TX_EN_CTRL BIT(4) /* Tx Control Enable */
#define DW_VR_MII_EEE_RX_EN_CTRL BIT(7) /* Rx Control Enable */
-
#define DW_VR_MII_EEE_MULT_FACT_100NS GENMASK(11, 8)
-/* VR MII EEE Control 1 defines */
+#define DW_VR_MII_EEE_MCTRL1 0x800b
#define DW_VR_MII_EEE_TRN_LPI BIT(0) /* Transparent Mode Enable */
+#define DW_VR_MII_DIG_CTRL2 0x80e1
+#define DW_VR_MII_DIG_CTRL2_TX_POL_INV BIT(4)
+#define DW_VR_MII_DIG_CTRL2_RX_POL_INV BIT(0)
+
#define DW_XPCS_INFO_DECLARE(_name, _pcs, _pma) \
static const struct dw_xpcs_info _name = { .pcs = _pcs, .pma = _pma }
@@ -114,6 +106,30 @@ enum dw_xpcs_clock {
DW_XPCS_NUM_CLKS,
};
+enum dw_xpcs_sgmii_10_100 {
+ DW_XPCS_SGMII_10_100_UNCHANGED,
+ DW_XPCS_SGMII_10_100_4BIT,
+ DW_XPCS_SGMII_10_100_8BIT
+};
+
+/* The SGMII mode:
+ * DW_XPCS_SGMII_MODE_MAC_AUTO: the XPCS acts as a MAC, accepting the
+ * parameters from the PHY end of the SGMII link and acknowledging the
+ * config word. The XPCS autonomously switches speed.
+ *
+ * DW_XPCS_SGMII_MODE_MAC_MANUAL: the XPCS acts as a MAC as above, but
+ * does not autonomously switch speed.
+ *
+ * DW_XPCS_SGMII_MODE_PHY_HW: the XPCS acts as a PHY, deriving the tx_config
+ * bits 15 (link), 12 (duplex) and 11:10 (speed) from hardware inputs to the
+ * XPCS.
+ */
+enum dw_xpcs_sgmii_mode {
+ DW_XPCS_SGMII_MODE_MAC_AUTO, /* XPCS is MAC, auto update */
+ DW_XPCS_SGMII_MODE_MAC_MANUAL, /* XPCS is MAC, manual update */
+ DW_XPCS_SGMII_MODE_PHY_HW, /* XPCS is PHY, tx_config from hw */
+};
+
struct dw_xpcs {
struct dw_xpcs_info info;
const struct dw_xpcs_desc *desc;
@@ -122,6 +138,10 @@ struct dw_xpcs {
struct phylink_pcs pcs;
phy_interface_t interface;
bool need_reset;
+ u8 eee_mult_fact;
+ /* Width of the MII MAC/XPCS interface in 100M and 10M modes */
+ enum dw_xpcs_sgmii_10_100 sgmii_10_100_8bit;
+ enum dw_xpcs_sgmii_mode sgmii_mode;
};
int xpcs_read(struct dw_xpcs *xpcs, int dev, u32 reg);
diff --git a/drivers/net/phy/Makefile b/drivers/net/phy/Makefile
index c8dac6e92278..b15e2ac8d3a2 100644
--- a/drivers/net/phy/Makefile
+++ b/drivers/net/phy/Makefile
@@ -29,7 +29,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 e42ace4e682a..93c7a67cac9c 100644
--- a/drivers/net/phy/aquantia/aquantia_main.c
+++ b/drivers/net/phy/aquantia/aquantia_main.c
@@ -891,6 +891,19 @@ static int aqr107_probe(struct phy_device *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),
@@ -1101,7 +1114,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 d7f7cc44c532..65950e410ea1 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;
}
diff --git a/drivers/net/phy/marvell.c b/drivers/net/phy/marvell.c
index 44e1927de499..4c7ae130d165 100644
--- a/drivers/net/phy/marvell.c
+++ b/drivers/net/phy/marvell.c
@@ -176,7 +176,11 @@
#define MII_M1011_PHY_STATUS_FULLDUPLEX 0x2000
#define MII_M1011_PHY_STATUS_RESOLVED 0x0800
#define MII_M1011_PHY_STATUS_LINK 0x0400
+#define MII_88E151X_PHY_STATUS_TX_PAUSE BIT(9)
+#define MII_88E151X_PHY_STATUS_RX_PAUSE BIT(8)
#define MII_M1011_PHY_STATUS_MDIX BIT(6)
+#define MII_M1111_PHY_STATUS_TX_PAUSE BIT(3)
+#define MII_M1111_PHY_STATUS_RX_PAUSE BIT(2)
#define MII_88E3016_PHY_SPEC_CTRL 0x10
#define MII_88E3016_DISABLE_SCRAMBLER 0x0200
@@ -354,6 +358,8 @@ struct marvell_priv {
u32 step;
s8 pair;
u8 vct_phase;
+ u16 tx_pause_mask;
+ u16 rx_pause_mask;
};
static int marvell_read_page(struct phy_device *phydev)
@@ -1664,6 +1670,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;
@@ -1719,6 +1726,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;
}
@@ -1762,6 +1774,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 (fiber) {
phydev->mdix = ETH_TP_MDI_INVALID;
@@ -3568,14 +3581,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);
@@ -3667,11 +3692,23 @@ static const struct sfp_upstream_ops m88e1510_sfp_ops = {
.disconnect_phy = phy_sfp_disconnect_phy,
};
+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;
@@ -3739,7 +3776,7 @@ static struct phy_driver marvell_drivers[] = {
.name = "Marvell 88E1111",
/* PHY_GBIT_FEATURES */
.flags = PHY_POLL_CABLE_TEST,
- .probe = marvell_probe,
+ .probe = m88e1111_probe,
.inband_caps = m88e1111_inband_caps,
.config_inband = m88e1111_config_inband,
.config_init = m88e1111gbe_config_init,
@@ -3969,7 +4006,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,
@@ -3998,7 +4035,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,
@@ -4065,7 +4102,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 623bdb8466b8..791e0d22ab20 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)
@@ -508,6 +513,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);
@@ -519,6 +561,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;
@@ -526,15 +582,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;
@@ -563,7 +613,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);
}
@@ -589,6 +639,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
@@ -828,7 +891,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;
@@ -875,7 +938,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)
@@ -1092,6 +1155,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)
@@ -1434,6 +1501,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,
@@ -1471,6 +1539,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 7e2f10182c0c..fada095a778e 100644
--- a/drivers/net/phy/mdio_bus.c
+++ b/drivers/net/phy/mdio_bus.c
@@ -1396,8 +1396,16 @@ static int mdio_bus_match(struct device *dev, const 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 d0c1718e2b16..e4e6467e9508 100644
--- a/drivers/net/phy/phy.c
+++ b/drivers/net/phy/phy.c
@@ -1350,6 +1350,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");
@@ -1376,7 +1378,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);
@@ -1643,6 +1644,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);
@@ -1675,6 +1680,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 46713d27412b..4e5cfc93f1b2 100644
--- a/drivers/net/phy/phy_device.c
+++ b/drivers/net/phy/phy_device.c
@@ -609,6 +609,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)
@@ -628,6 +641,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;
@@ -3089,6 +3103,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);
diff --git a/drivers/net/phy/phylink.c b/drivers/net/phy/phylink.c
index b00a315de060..aff3313d4de7 100644
--- a/drivers/net/phy/phylink.c
+++ b/drivers/net/phy/phylink.c
@@ -1073,6 +1073,18 @@ static void phylink_pcs_link_up(struct phylink_pcs *pcs, unsigned int neg_mode,
pcs->ops->pcs_link_up(pcs, neg_mode, interface, speed, duplex);
}
+static void phylink_pcs_disable_eee(struct phylink_pcs *pcs)
+{
+ if (pcs && pcs->ops->pcs_disable_eee)
+ pcs->ops->pcs_disable_eee(pcs);
+}
+
+static void phylink_pcs_enable_eee(struct phylink_pcs *pcs)
+{
+ if (pcs && pcs->ops->pcs_enable_eee)
+ pcs->ops->pcs_enable_eee(pcs);
+}
+
/* Query inband for a specific interface mode, asking the MAC for the
* PCS which will be used to handle the interface mode.
*/
@@ -1600,7 +1612,9 @@ static void phylink_deactivate_lpi(struct phylink *pl)
phylink_dbg(pl, "disabling LPI\n");
- pl->mac_ops->mac_disable_tx_lpi(pl->config);
+ pl->mac_ops->mac_disable_tx_lpi(pl->config, pl->cur_interface);
+
+ phylink_pcs_disable_eee(pl->pcs);
}
}
@@ -1617,13 +1631,23 @@ static void phylink_activate_lpi(struct phylink *pl)
phylink_dbg(pl, "LPI timer %uus, tx clock stop %u\n",
pl->mac_tx_lpi_timer, pl->mac_tx_clk_stop);
- err = pl->mac_ops->mac_enable_tx_lpi(pl->config, pl->mac_tx_lpi_timer,
+ phylink_pcs_enable_eee(pl->pcs);
+
+ err = pl->mac_ops->mac_enable_tx_lpi(pl->config, pl->cur_interface,
+ pl->mac_tx_lpi_timer,
pl->mac_tx_clk_stop);
- if (!err)
+ if (!err) {
pl->mac_enable_tx_lpi = true;
- else
- phylink_err(pl, "%ps() failed: %pe\n",
- pl->mac_ops->mac_enable_tx_lpi, ERR_PTR(err));
+ } else {
+ phylink_pcs_disable_eee(pl->pcs);
+ if (err == -EOPNOTSUPP)
+ phylink_dbg(pl, "%ps doesn't support LPI",
+ pl->mac_ops->mac_enable_tx_lpi);
+ else
+ phylink_err(pl, "%ps() failed: %pe\n",
+ pl->mac_ops->mac_enable_tx_lpi,
+ ERR_PTR(err));
+ }
}
static void phylink_link_up(struct phylink *pl,
@@ -1957,8 +1981,7 @@ struct phylink *phylink_create(struct phylink_config *config,
return ERR_PTR(-EINVAL);
}
- pl->mac_supports_eee_ops = mac_ops->mac_disable_tx_lpi &&
- mac_ops->mac_enable_tx_lpi;
+ pl->mac_supports_eee_ops = phylink_mac_implements_lpi(mac_ops);
pl->mac_supports_eee = pl->mac_supports_eee_ops &&
pl->config->lpi_capabilities &&
!phy_interface_empty(pl->config->lpi_interfaces);
@@ -2266,7 +2289,7 @@ static int phylink_bringup_phy(struct phylink *pl, struct phy_device *phy,
pl->mac_tx_clk_stop = phy_eee_tx_clock_stop_capable(phy) > 0;
if (pl->mac_supports_eee_ops) {
- /* Explicitly configure whether the PHY is allowed to stop it's
+ /* Explicitly configure whether the PHY is allowed to stop its
* receive clock.
*/
ret = phy_eee_rx_clock_stop(phy,
@@ -2639,6 +2662,30 @@ void phylink_suspend(struct phylink *pl, bool mac_wol)
EXPORT_SYMBOL_GPL(phylink_suspend);
/**
+ * phylink_prepare_resume() - prepare to resume a network device
+ * @pl: a pointer to a &struct phylink returned from phylink_create()
+ *
+ * Optional, thus must be called prior to phylink_resume().
+ *
+ * Prepare to resume a network device, preparing the PHY as necessary.
+ */
+void phylink_prepare_resume(struct phylink *pl)
+{
+ ASSERT_RTNL();
+
+ /* If the MAC requires the receive clock, but receive clock
+ * stop was enabled at the PHY, we need to ensure that the
+ * receive clock is running. Disable receive clock stop.
+ * phylink_resume() will re-enable it if necessary.
+ */
+ if (pl->mac_supports_eee_ops && pl->phydev &&
+ pl->config->mac_requires_rxc &&
+ pl->config->eee_rx_clk_stop_enable)
+ phy_eee_rx_clock_stop(pl->phydev, false);
+}
+EXPORT_SYMBOL_GPL(phylink_prepare_resume);
+
+/**
* phylink_resume() - handle a network device resume event
* @pl: a pointer to a &struct phylink returned from phylink_create()
*
@@ -2647,8 +2694,22 @@ EXPORT_SYMBOL_GPL(phylink_suspend);
*/
void phylink_resume(struct phylink *pl)
{
+ int ret;
+
ASSERT_RTNL();
+ if (pl->mac_supports_eee_ops && pl->phydev) {
+ /* Explicitly configure whether the PHY is allowed to stop its
+ * receive clock on resume to ensure that it is correctly
+ * configured.
+ */
+ ret = phy_eee_rx_clock_stop(pl->phydev,
+ pl->config->eee_rx_clk_stop_enable);
+ if (ret == -EOPNOTSUPP)
+ phylink_warn(pl, "failed to set rx clock stop: %pe\n",
+ ERR_PTR(ret));
+ }
+
if (test_bit(PHYLINK_DISABLE_MAC_WOL, &pl->phylink_disable_state)) {
/* Wake-on-Lan enabled, MAC handling */
@@ -3777,7 +3838,10 @@ static void phylink_sfp_link_up(void *upstream)
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;
+ int ret;
if (!phy->drv) {
phylink_err(pl, "PHY %s (id 0x%.8lx) has no driver loaded\n",
@@ -3799,8 +3863,55 @@ static int phylink_sfp_connect_phy(void *upstream, struct phy_device *phy)
phy_interface_and(phy->host_interfaces, phylink_sfp_interfaces,
pl->config->supported_interfaces);
- /* Do the initial configuration */
- return phylink_sfp_config_phy(pl, phy);
+ if (phy_interface_empty(phy->supported_interfaces)) {
+ phylink_dbg(pl, "copper SFP: PHY provides empty supported_interfaces\n");
+
+ /* Do the initial configuration */
+ return phylink_sfp_config_phy(pl, phy);
+ }
+
+ 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));
+
+ ret = phylink_attach_phy(pl, phy, interface);
+ if (ret < 0)
+ return ret;
+
+ ret = phylink_bringup_phy(pl, phy, interface);
+ if (ret) {
+ phy_detach(phy);
+ return ret;
+ }
+
+ if (pl->req_link_an_mode != MLO_AN_INBAND ||
+ pl->link_config.interface != interface) {
+ pl->link_config.interface = interface;
+ pl->req_link_an_mode = MLO_AN_INBAND;
+
+ phylink_info(pl, "switched to %s/%s link mode\n",
+ phylink_an_mode_str(pl->req_link_an_mode),
+ phy_modes(pl->link_config.interface));
+ }
+
+ if (!test_bit(PHYLINK_DISABLE_STOPPED, &pl->phylink_disable_state))
+ phylink_mac_initial_config(pl, false);
+
+ return 0;
}
static void phylink_sfp_disconnect_phy(void *upstream,
@@ -3920,12 +4031,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 7dbcbf0a4ee2..984542b8bd8f 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);
@@ -1675,6 +1678,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)
{
@@ -2176,6 +2287,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 */
@@ -2242,9 +2458,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);
@@ -2305,14 +2521,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)) {
@@ -2346,8 +2557,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;
@@ -2596,10 +2810,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)
@@ -2618,8 +2832,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/phy/marvell/phy-armada38x-comphy.c b/drivers/phy/marvell/phy-armada38x-comphy.c
index 5063361b0120..a4533c924edd 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];
@@ -94,6 +111,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)
{
@@ -111,6 +129,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.
@@ -119,7 +228,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;
@@ -138,23 +246,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,
};
@@ -199,6 +298,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)
@@ -208,8 +308,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");
@@ -236,16 +344,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 3c3deac57894..00d3a8cc6f96 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, const 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/pcs/pcs-xpcs.h b/include/linux/pcs/pcs-xpcs.h
index 733f4ddd2ef1..e40f554ff717 100644
--- a/include/linux/pcs/pcs-xpcs.h
+++ b/include/linux/pcs/pcs-xpcs.h
@@ -50,8 +50,7 @@ struct dw_xpcs;
struct phylink_pcs *xpcs_to_phylink_pcs(struct dw_xpcs *xpcs);
int xpcs_get_an_mode(struct dw_xpcs *xpcs, phy_interface_t interface);
-int xpcs_config_eee(struct dw_xpcs *xpcs, int mult_fact_100ns,
- int enable);
+void xpcs_config_eee_mult_fact(struct dw_xpcs *xpcs, u8 mult_fact);
struct dw_xpcs *xpcs_create_mdiodev(struct mii_bus *bus, int addr);
struct dw_xpcs *xpcs_create_fwnode(struct fwnode_handle *fwnode);
void xpcs_destroy(struct dw_xpcs *xpcs);
diff --git a/include/linux/phy.h b/include/linux/phy.h
index 19f076a71f94..7a5b8db38539 100644
--- a/include/linux/phy.h
+++ b/include/linux/phy.h
@@ -716,6 +716,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);
@@ -735,6 +744,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);
+
#ifdef CONFIG_LED_TRIGGER_PHY
struct phy_led_trigger *phy_led_triggers;
unsigned int phy_num_led_triggers;
@@ -1010,6 +1022,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,
diff --git a/include/linux/phylink.h b/include/linux/phylink.h
index 898b00451bbf..2f8efd3aff10 100644
--- a/include/linux/phylink.h
+++ b/include/linux/phylink.h
@@ -208,8 +208,10 @@ 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);
- int (*mac_enable_tx_lpi)(struct phylink_config *config, u32 timer,
+ void (*mac_disable_tx_lpi)(struct phylink_config *config,
+ phy_interface_t interface);
+ int (*mac_enable_tx_lpi)(struct phylink_config *config,
+ phy_interface_t interface, u32 timer,
bool tx_clk_stop);
};
@@ -409,15 +411,18 @@ void mac_link_up(struct phylink_config *config, struct phy_device *phy,
/**
* mac_disable_tx_lpi() - disable LPI generation at the MAC
* @config: a pointer to a &struct phylink_config.
+ * @interface: current link &typedef phy_interface_t mode
*
* 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);
+void mac_disable_tx_lpi(struct phylink_config *config,
+ phy_interface_t interface);
/**
* mac_enable_tx_lpi() - configure and enable LPI generation at the MAC
* @config: a pointer to a &struct phylink_config.
+ * @interface: current link &typedef phy_interface_t mode
* @timer: LPI timeout in microseconds.
* @tx_clk_stop: allow xMII transmit clock to be stopped during LPI
*
@@ -430,8 +435,8 @@ void mac_disable_tx_lpi(struct phylink_config *config);
*
* Returns: 0 on success. Please consult with rmk before returning an error.
*/
-int mac_enable_tx_lpi(struct phylink_config *config, u32 timer,
- bool tx_clk_stop);
+int mac_enable_tx_lpi(struct phylink_config *config, phy_interface_t interface,
+ u32 timer, bool tx_clk_stop);
#endif
struct phylink_pcs_ops;
@@ -477,6 +482,10 @@ struct phylink_pcs {
* @pcs_an_restart: restart 802.3z BaseX autonegotiation.
* @pcs_link_up: program the PCS for the resolved link configuration
* (where necessary).
+ * @pcs_disable_eee: optional notification to PCS that EEE has been disabled
+ * at the MAC.
+ * @pcs_enable_eee: optional notification to PCS that EEE will be enabled at
+ * the MAC.
* @pcs_pre_init: configure PCS components necessary for MAC hardware
* initialization e.g. RX clock for stmmac.
*/
@@ -500,6 +509,8 @@ struct phylink_pcs_ops {
void (*pcs_an_restart)(struct phylink_pcs *pcs);
void (*pcs_link_up)(struct phylink_pcs *pcs, unsigned int neg_mode,
phy_interface_t interface, int speed, int duplex);
+ void (*pcs_disable_eee)(struct phylink_pcs *pcs);
+ void (*pcs_enable_eee)(struct phylink_pcs *pcs);
int (*pcs_pre_init)(struct phylink_pcs *pcs);
};
@@ -626,6 +637,22 @@ void pcs_link_up(struct phylink_pcs *pcs, unsigned int neg_mode,
phy_interface_t interface, int speed, int duplex);
/**
+ * pcs_disable_eee() - Disable EEE at the PCS
+ * @pcs: a pointer to a &struct phylink_pcs
+ *
+ * Optional method informing the PCS that EEE has been disabled at the MAC.
+ */
+void pcs_disable_eee(struct phylink_pcs *pcs);
+
+/**
+ * pcs_enable_eee() - Enable EEE at the PCS
+ * @pcs: a pointer to a &struct phylink_pcs
+ *
+ * Optional method informing the PCS that EEE is about to be enabled at the MAC.
+ */
+void pcs_enable_eee(struct phylink_pcs *pcs);
+
+/**
* pcs_pre_init() - Configure PCS components necessary for MAC initialization
* @pcs: a pointer to a &struct phylink_pcs.
*
@@ -679,6 +706,7 @@ void phylink_start(struct phylink *);
void phylink_stop(struct phylink *);
void phylink_suspend(struct phylink *pl, bool mac_wol);
+void phylink_prepare_resume(struct phylink *pl);
void phylink_resume(struct phylink *pl);
void phylink_ethtool_get_wol(struct phylink *, struct ethtool_wolinfo *);
@@ -737,6 +765,18 @@ static inline int phylink_get_link_timer_ns(phy_interface_t interface)
}
}
+/**
+ * phylink_mac_implements_lpi() - determine if MAC implements LPI ops
+ * @ops: phylink_mac_ops structure
+ *
+ * Returns true if the phylink MAC operations structure indicates that the
+ * LPI operations have been implemented, false otherwise.
+ */
+static inline bool phylink_mac_implements_lpi(const struct phylink_mac_ops *ops)
+{
+ return ops && ops->mac_disable_tx_lpi && ops->mac_enable_tx_lpi;
+}
+
void phylink_mii_c22_pcs_decode_state(struct phylink_link_state *state,
unsigned int neg_mode, u16 bmsr, u16 lpa);
void phylink_mii_c22_pcs_get_state(struct mdio_device *pcs,
diff --git a/include/linux/stmmac.h b/include/linux/stmmac.h
index c9878a612e53..aed23a188e96 100644
--- a/include/linux/stmmac.h
+++ b/include/linux/stmmac.h
@@ -78,6 +78,7 @@
| DMA_AXI_BLEN_32 | DMA_AXI_BLEN_64 \
| DMA_AXI_BLEN_128 | DMA_AXI_BLEN_256)
+struct clk;
struct stmmac_priv;
/* Platfrom data for platform device structure's platform_data field */
@@ -231,7 +232,9 @@ struct plat_stmmacenet_data {
u8 tx_sched_algorithm;
struct stmmac_rxq_cfg rx_queues_cfg[MTL_MAX_RX_QUEUES];
struct stmmac_txq_cfg tx_queues_cfg[MTL_MAX_TX_QUEUES];
- void (*fix_mac_speed)(void *priv, unsigned int speed, unsigned int mode);
+ int (*set_clk_tx_rate)(void *priv, struct clk *clk_tx_i,
+ phy_interface_t interface, int speed);
+ void (*fix_mac_speed)(void *priv, int speed, unsigned int mode);
int (*fix_soc_reset)(void *priv, void __iomem *ioaddr);
int (*serdes_powerup)(struct net_device *ndev, void *priv);
void (*serdes_powerdown)(struct net_device *ndev, void *priv);
@@ -252,6 +255,7 @@ struct plat_stmmacenet_data {
struct clk *stmmac_clk;
struct clk *pclk;
struct clk *clk_ptp_ref;
+ struct clk *clk_tx_i; /* clk_tx_i to MAC core */
unsigned long clk_ptp_rate;
unsigned long clk_ref_rate;
unsigned int mult_fact_100ns;
diff --git a/net/dsa/dsa.c b/net/dsa/dsa.c
index e827775baf2e..4fc016ec1661 100644
--- a/net/dsa/dsa.c
+++ b/net/dsa/dsa.c
@@ -1505,6 +1505,11 @@ static int dsa_switch_probe(struct dsa_switch *ds)
if (!ds->num_ports)
return -EINVAL;
+ if (!ds->phylink_mac_ops) {
+ dev_err(ds->dev, "DSA switch driver does not provide phylink MAC operations");
+ 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 5c9d1798e830..56ebaa4cd76a 100644
--- a/net/dsa/port.c
+++ b/net/dsa/port.c
@@ -1591,36 +1591,8 @@ bool dsa_supports_eee(struct dsa_switch *ds, int port)
}
EXPORT_SYMBOL_GPL(dsa_supports_eee);
-static void dsa_port_phylink_mac_config(struct phylink_config *config,
- unsigned int mode,
- const struct phylink_link_state *state)
-{
-}
-
-static void dsa_port_phylink_mac_link_down(struct phylink_config *config,
- unsigned int mode,
- phy_interface_t interface)
-{
-}
-
-static void dsa_port_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)
-{
-}
-
-static const struct phylink_mac_ops dsa_port_phylink_mac_ops = {
- .mac_config = dsa_port_phylink_mac_config,
- .mac_link_down = dsa_port_phylink_mac_link_down,
- .mac_link_up = dsa_port_phylink_mac_link_up,
-};
-
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;
@@ -1644,12 +1616,8 @@ int dsa_port_phylink_create(struct dsa_port *dp)
}
}
- 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);
+ ds->phylink_mac_ops);
if (IS_ERR(pl)) {
pr_err("error creating PHYLINK: %ld\n", PTR_ERR(pl));
return PTR_ERR(pl);
diff --git a/net/dsa/user.c b/net/dsa/user.c
index 291ab1b4acc4..2296a4ead020 100644
--- a/net/dsa/user.c
+++ b/net/dsa/user.c
@@ -1243,16 +1243,25 @@ static int dsa_user_set_eee(struct net_device *dev, struct ethtool_keee *e)
if (!ds->ops->support_eee || !ds->ops->support_eee(ds, dp->index))
return -EOPNOTSUPP;
- /* Port's PHY and MAC both need to be EEE capable */
- if (!dev->phydev)
- return -ENODEV;
+ /* If the port is using phylink managed EEE, then an unimplemented
+ * set_mac_eee() is permissible.
+ */
+ if (!phylink_mac_implements_lpi(ds->phylink_mac_ops)) {
+ /* Port's PHY and MAC both need to be EEE capable */
+ if (!dev->phydev)
+ 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;
+ } else if (ds->ops->set_mac_eee) {
+ ret = ds->ops->set_mac_eee(ds, dp->index, e);
+ if (ret)
+ return ret;
+ }
return phylink_ethtool_set_eee(dp->pl, e);
}