summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Documentation/devicetree/bindings/net/marvell,10g.yaml35
-rw-r--r--arch/arm/boot/dts/armada-38x.dtsi5
-rw-r--r--arch/arm64/boot/dts/marvell/armada-8040-mcbin.dts2
-rw-r--r--drivers/cpuidle/cpuidle-mvebu-v7.c6
-rw-r--r--drivers/net/dsa/bcm_sf2.c12
-rw-r--r--drivers/net/dsa/mv88e6xxx/chip.c57
-rw-r--r--drivers/net/dsa/mv88e6xxx/chip.h2
-rw-r--r--drivers/net/ethernet/marvell/Kconfig4
-rw-r--r--drivers/net/ethernet/marvell/Makefile1
-rw-r--r--drivers/net/ethernet/marvell/mvgmac.c444
-rw-r--r--drivers/net/ethernet/marvell/mvgmac.h52
-rw-r--r--drivers/net/ethernet/marvell/mvneta.c401
-rw-r--r--drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c23
-rw-r--r--drivers/net/ethernet/mediatek/mtk_eth_soc.c11
-rw-r--r--drivers/net/ethernet/mediatek/mtk_eth_soc.h3
-rw-r--r--drivers/net/ethernet/mediatek/mtk_sgmii.c37
-rw-r--r--drivers/net/phy/Makefile2
-rw-r--r--drivers/net/phy/at803x.c14
-rw-r--r--drivers/net/phy/bcm84881.c4
-rw-r--r--drivers/net/phy/marvell.c50
-rw-r--r--drivers/net/phy/marvell10g.c240
-rw-r--r--drivers/net/phy/phy.c7
-rw-r--r--drivers/net/phy/phy_device.c22
-rw-r--r--drivers/net/phy/phylink.c137
-rw-r--r--drivers/net/phy/sff.c114
-rw-r--r--drivers/net/phy/sff.h16
-rw-r--r--drivers/net/phy/sfp-bus.c76
-rw-r--r--drivers/net/phy/sfp.c244
-rw-r--r--drivers/phy/marvell/phy-armada38x-comphy.c147
-rw-r--r--include/linux/phy.h64
-rw-r--r--include/linux/phylink.h1
-rw-r--r--include/linux/sfp.h5
-rw-r--r--include/net/dsa.h2
-rw-r--r--net/dsa/slave.c4
34 files changed, 1834 insertions, 410 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..93d2c8d7e759
--- /dev/null
+++ b/Documentation/devicetree/bindings/net/marvell,10g.yaml
@@ -0,0 +1,35 @@
+# 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.
+ allOf:
+ - $ref: /schemas/types.yaml#/definitions/uint16-array
+ - minItems: 1
+ maxItems: 4
+
+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/armada-38x.dtsi b/arch/arm/boot/dts/armada-38x.dtsi
index 9b1a24cc5e91..404943b5cdac 100644
--- a/arch/arm/boot/dts/armada-38x.dtsi
+++ b/arch/arm/boot/dts/armada-38x.dtsi
@@ -342,8 +342,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/cpuidle/cpuidle-mvebu-v7.c b/drivers/cpuidle/cpuidle-mvebu-v7.c
index 01a856971f05..18ded9e7cb34 100644
--- a/drivers/cpuidle/cpuidle-mvebu-v7.c
+++ b/drivers/cpuidle/cpuidle-mvebu-v7.c
@@ -39,8 +39,12 @@ static int mvebu_v7_enter_idle(struct cpuidle_device *dev,
ret = mvebu_v7_cpu_suspend(deepidle);
cpu_pm_exit();
+ /*
+ * If we failed to enter the desired state, indicate that we
+ * slept lightly.
+ */
if (ret)
- return ret;
+ return 0;
return index;
}
diff --git a/drivers/net/dsa/bcm_sf2.c b/drivers/net/dsa/bcm_sf2.c
index ba5d546d06aa..25357cc3e12b 100644
--- a/drivers/net/dsa/bcm_sf2.c
+++ b/drivers/net/dsa/bcm_sf2.c
@@ -601,6 +601,7 @@ static void bcm_sf2_sw_validate(struct dsa_switch *ds, int port,
struct bcm_sf2_priv *priv = bcm_sf2_to_priv(ds);
__ETHTOOL_DECLARE_LINK_MODE_MASK(mask) = { 0, };
+ /* FIXME: Are RGMII_RXID and RGMII_ID actually supported? */
if (!phy_interface_mode_is_rgmii(state->interface) &&
state->interface != PHY_INTERFACE_MODE_MII &&
state->interface != PHY_INTERFACE_MODE_REVMII &&
@@ -618,8 +619,13 @@ static void bcm_sf2_sw_validate(struct dsa_switch *ds, int port,
/* Allow all the expected bits */
phylink_set(mask, Autoneg);
phylink_set_port_modes(mask);
- phylink_set(mask, Pause);
- phylink_set(mask, Asym_Pause);
+ if (state->interface == PHY_INTERFACE_MODE_RGMII ||
+ state->interface == PHY_INTERFACE_MODE_RGMII_TXID ||
+ state->interface == PHY_INTERFACE_MODE_MII ||
+ state->interface == PHY_INTERFACE_MODE_REVMII) {
+ phylink_set(mask, Pause);
+ phylink_set(mask, Asym_Pause);
+ }
/* With the exclusion of MII and Reverse MII, we support Gigabit,
* including Half duplex
@@ -653,6 +659,7 @@ static void bcm_sf2_sw_mac_config(struct dsa_switch *ds, int port,
return;
switch (state->interface) {
+ /* FIXME: Are RGMII_RXID and RGMII_ID actually supported? */
case PHY_INTERFACE_MODE_RGMII:
id_mode_dis = 1;
fallthrough;
@@ -746,6 +753,7 @@ static void bcm_sf2_sw_mac_link_up(struct dsa_switch *ds, int port,
else
offset = CORE_STS_OVERRIDE_GMIIP2_PORT(port);
+ /* FIXME: Are RGMII_RXID and RGMII_ID actually supported? */
if (interface == PHY_INTERFACE_MODE_RGMII ||
interface == PHY_INTERFACE_MODE_RGMII_TXID ||
interface == PHY_INTERFACE_MODE_MII ||
diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c
index 189bf00519a5..143bd356f09f 100644
--- a/drivers/net/dsa/mv88e6xxx/chip.c
+++ b/drivers/net/dsa/mv88e6xxx/chip.c
@@ -660,6 +660,50 @@ static void mv88e6xxx_validate(struct dsa_switch *ds, int port,
phylink_helper_basex_speed(state);
}
+static void mv88e6352_phylink_get_interfaces(struct mv88e6xxx_chip *chip,
+ int port,
+ unsigned long *supported)
+{
+ if (mv88e6xxx_serdes_get_lane(chip, port)) {
+ /* FIXME: does code for 6352 family support changing between
+ * SGMII and 1000base-x?
+ */
+ __set_bit(PHY_INTERFACE_MODE_SGMII, supported);
+ __set_bit(PHY_INTERFACE_MODE_1000BASEX, supported);
+ }
+}
+
+static void mv88e6341_phylink_get_interfaces(struct mv88e6xxx_chip *chip,
+ int port,
+ unsigned long *supported)
+{
+ if (port == 5) {
+ __set_bit(PHY_INTERFACE_MODE_SGMII, supported);
+ __set_bit(PHY_INTERFACE_MODE_1000BASEX, supported);
+ __set_bit(PHY_INTERFACE_MODE_2500BASEX, supported);
+ }
+}
+
+static void mv88e6390_phylink_get_interfaces(struct mv88e6xxx_chip *chip,
+ int port,
+ unsigned long *supported)
+{
+ if (port == 9 || port == 10) {
+ __set_bit(PHY_INTERFACE_MODE_SGMII, supported);
+ __set_bit(PHY_INTERFACE_MODE_1000BASEX, supported);
+ __set_bit(PHY_INTERFACE_MODE_2500BASEX, supported);
+ }
+}
+
+static void mv88e6xxx_get_interfaces(struct dsa_switch *ds, int port,
+ unsigned long *supported)
+{
+ struct mv88e6xxx_chip *chip = ds->priv;
+
+ if (chip->info->ops->phylink_get_interfaces)
+ chip->info->ops->phylink_get_interfaces(chip, port, supported);
+}
+
static void mv88e6xxx_mac_config(struct dsa_switch *ds, int port,
unsigned int mode,
const struct phylink_link_state *state)
@@ -3522,6 +3566,7 @@ static const struct mv88e6xxx_ops mv88e6141_ops = {
.serdes_irq_enable = mv88e6390_serdes_irq_enable,
.serdes_irq_status = mv88e6390_serdes_irq_status,
.gpio_ops = &mv88e6352_gpio_ops,
+ .phylink_get_interfaces = mv88e6341_phylink_get_interfaces,
.phylink_validate = mv88e6341_phylink_validate,
};
@@ -3697,6 +3742,7 @@ static const struct mv88e6xxx_ops mv88e6172_ops = {
.serdes_get_regs_len = mv88e6352_serdes_get_regs_len,
.serdes_get_regs = mv88e6352_serdes_get_regs,
.gpio_ops = &mv88e6352_gpio_ops,
+ .phylink_get_interfaces = mv88e6352_phylink_get_interfaces,
.phylink_validate = mv88e6352_phylink_validate,
};
@@ -3797,6 +3843,7 @@ static const struct mv88e6xxx_ops mv88e6176_ops = {
.serdes_get_regs_len = mv88e6352_serdes_get_regs_len,
.serdes_get_regs = mv88e6352_serdes_get_regs,
.gpio_ops = &mv88e6352_gpio_ops,
+ .phylink_get_interfaces = mv88e6352_phylink_get_interfaces,
.phylink_validate = mv88e6352_phylink_validate,
};
@@ -3898,6 +3945,7 @@ static const struct mv88e6xxx_ops mv88e6190_ops = {
.serdes_get_regs_len = mv88e6390_serdes_get_regs_len,
.serdes_get_regs = mv88e6390_serdes_get_regs,
.gpio_ops = &mv88e6352_gpio_ops,
+ .phylink_get_interfaces = mv88e6390_phylink_get_interfaces,
.phylink_validate = mv88e6390_phylink_validate,
};
@@ -3959,6 +4007,7 @@ static const struct mv88e6xxx_ops mv88e6190x_ops = {
.serdes_get_regs_len = mv88e6390_serdes_get_regs_len,
.serdes_get_regs = mv88e6390_serdes_get_regs,
.gpio_ops = &mv88e6352_gpio_ops,
+ .phylink_get_interfaces = mv88e6390_phylink_get_interfaces,
.phylink_validate = mv88e6390x_phylink_validate,
};
@@ -4019,6 +4068,7 @@ static const struct mv88e6xxx_ops mv88e6191_ops = {
.serdes_get_regs = mv88e6390_serdes_get_regs,
.avb_ops = &mv88e6390_avb_ops,
.ptp_ops = &mv88e6352_ptp_ops,
+ .phylink_get_interfaces = mv88e6390_phylink_get_interfaces,
.phylink_validate = mv88e6390_phylink_validate,
};
@@ -4079,6 +4129,7 @@ static const struct mv88e6xxx_ops mv88e6240_ops = {
.gpio_ops = &mv88e6352_gpio_ops,
.avb_ops = &mv88e6352_avb_ops,
.ptp_ops = &mv88e6352_ptp_ops,
+ .phylink_get_interfaces = mv88e6352_phylink_get_interfaces,
.phylink_validate = mv88e6352_phylink_validate,
};
@@ -4181,6 +4232,7 @@ static const struct mv88e6xxx_ops mv88e6290_ops = {
.gpio_ops = &mv88e6352_gpio_ops,
.avb_ops = &mv88e6390_avb_ops,
.ptp_ops = &mv88e6352_ptp_ops,
+ .phylink_get_interfaces = mv88e6390_phylink_get_interfaces,
.phylink_validate = mv88e6390_phylink_validate,
};
@@ -4324,6 +4376,7 @@ static const struct mv88e6xxx_ops mv88e6341_ops = {
.gpio_ops = &mv88e6352_gpio_ops,
.avb_ops = &mv88e6390_avb_ops,
.ptp_ops = &mv88e6352_ptp_ops,
+ .phylink_get_interfaces = mv88e6341_phylink_get_interfaces,
.phylink_validate = mv88e6341_phylink_validate,
};
@@ -4473,6 +4526,7 @@ static const struct mv88e6xxx_ops mv88e6352_ops = {
.serdes_get_stats = mv88e6352_serdes_get_stats,
.serdes_get_regs_len = mv88e6352_serdes_get_regs_len,
.serdes_get_regs = mv88e6352_serdes_get_regs,
+ .phylink_get_interfaces = mv88e6352_phylink_get_interfaces,
.phylink_validate = mv88e6352_phylink_validate,
};
@@ -4538,6 +4592,7 @@ static const struct mv88e6xxx_ops mv88e6390_ops = {
.serdes_get_stats = mv88e6390_serdes_get_stats,
.serdes_get_regs_len = mv88e6390_serdes_get_regs_len,
.serdes_get_regs = mv88e6390_serdes_get_regs,
+ .phylink_get_interfaces = mv88e6390_phylink_get_interfaces,
.phylink_validate = mv88e6390_phylink_validate,
};
@@ -4602,6 +4657,7 @@ static const struct mv88e6xxx_ops mv88e6390x_ops = {
.gpio_ops = &mv88e6352_gpio_ops,
.avb_ops = &mv88e6390_avb_ops,
.ptp_ops = &mv88e6352_ptp_ops,
+ .phylink_get_interfaces = mv88e6390_phylink_get_interfaces,
.phylink_validate = mv88e6390x_phylink_validate,
};
@@ -5779,6 +5835,7 @@ static const struct dsa_switch_ops mv88e6xxx_switch_ops = {
.get_tag_protocol = mv88e6xxx_get_tag_protocol,
.setup = mv88e6xxx_setup,
.teardown = mv88e6xxx_teardown,
+ .phylink_get_interfaces = mv88e6xxx_get_interfaces,
.phylink_validate = mv88e6xxx_validate,
.phylink_mac_link_state = mv88e6xxx_serdes_pcs_get_state,
.phylink_mac_config = mv88e6xxx_mac_config,
diff --git a/drivers/net/dsa/mv88e6xxx/chip.h b/drivers/net/dsa/mv88e6xxx/chip.h
index ef3b657af1b1..10cdced13691 100644
--- a/drivers/net/dsa/mv88e6xxx/chip.h
+++ b/drivers/net/dsa/mv88e6xxx/chip.h
@@ -575,6 +575,8 @@ struct mv88e6xxx_ops {
const struct mv88e6xxx_ptp_ops *ptp_ops;
/* Phylink */
+ void (*phylink_get_interfaces)(struct mv88e6xxx_chip *chip, int port,
+ unsigned long *supported);
void (*phylink_validate)(struct mv88e6xxx_chip *chip, int port,
unsigned long *mask,
struct phylink_link_state *state);
diff --git a/drivers/net/ethernet/marvell/Kconfig b/drivers/net/ethernet/marvell/Kconfig
index fe0989c0fc25..e9149e6fab10 100644
--- a/drivers/net/ethernet/marvell/Kconfig
+++ b/drivers/net/ethernet/marvell/Kconfig
@@ -59,6 +59,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
@@ -71,6 +72,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 9f88fe822555..b1442ff46abf 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..e776d4e85d3f
--- /dev/null
+++ b/drivers/net/ethernet/marvell/mvgmac.c
@@ -0,0 +1,444 @@
+/*
+ * 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/export.h>
+#include <linux/io.h>
+#include <linux/phylink.h>
+
+#include "mvgmac.h"
+
+enum {
+ /* N = Neta, 21 = PPV2.1, 22 = PPV2.2 */
+ /* N: 0-14 21: 0,2-15 22: 0-14 */
+ GMAC_CTRL0_REG = 0x00,
+ GMAC_CTRL0_PORT_ENABLE = BIT(0),
+ GMAC_CTRL0_PORT_1000BASE_X = BIT(1),
+ GMAC_CTRL0_MAX_RX_SIZE_SHIFT = 2,
+ GMAC_CTRL0_MAX_RX_SIZE_MASK = 0x1fff << GMAC_CTRL0_MAX_RX_SIZE_SHIFT,
+ GMAC_CTRL0_MIB_CNTR_ENABLE = BIT(15),
+
+ /* N: 21: 1,5,6 22: */
+ GMAC_CTRL1_REG = 0x04,
+ GMAC_CTRL1_PERIODIC_XON_ENABLE = BIT(1),
+ GMAC_CTRL1_GMII_LB_ENABLE = BIT(5),
+ GMAC_CTRL1_PCS_LB_ENABLE = BIT(6),
+
+ /* ALL: 0,3,4,6 */
+ GMAC_CTRL2_REG = 0x08,
+ GMAC_CTRL2_INBAND_AN_SGMII = BIT(0),
+ GMAC_CTRL2_PCS_ENABLE = BIT(3),
+ GMAC_CTRL2_PORT_RGMII = BIT(4),
+ GMAC_CTRL2_PORT_RESET = BIT(6),
+
+ /* N:0-9,11-13 21:0,1,5-7,9,12,13 22:0-7,9-15 */
+ /* 22 bit 2 - EN_PCS_AN */
+ GMAC_ANEG_REG = 0x0c,
+ GMAC_ANEG_FORCE_LINK_DOWN = BIT(0),
+ GMAC_ANEG_FORCE_LINK_PASS = BIT(1),
+ GMAC_ANEG_INBAND_AN_ENABLE = BIT(2),
+ GMAC_ANEG_AN_BYPASS_ENABLE = BIT(3),
+ GMAC_ANEG_INBAND_RESTART_AN = BIT(4),
+ GMAC_ANEG_MII_SPEED = BIT(5),
+ GMAC_ANEG_GMII_SPEED = BIT(6),
+ GMAC_ANEG_AN_SPEED_ENABLE = BIT(7),
+ GMAC_ANEG_CONFIG_FLOW_CTRL = BIT(8),
+ GMAC_ANEG_ADVERT_SYM_FLOW_CTRL = BIT(9),
+ GMAC_ANEG_ADVERT_ASYM_FLOW_CTRL = BIT(10),
+ GMAC_ANEG_AN_FLOW_CTRL_ENABLE = BIT(11),
+ GMAC_ANEG_FULL_DUPLEX = BIT(12),
+ GMAC_ANEG_AN_DUPLEX_ENABLE = BIT(13),
+ /* pp22: bit 14 - phy mode */
+ /* pp22: bit 15 - choose sample tx config */
+
+ GMAC_STATUS_REG = 0x10,
+ MVGMAC_LINK_UP = BIT(0),
+ MVGMAC_SPEED_1000 = BIT(1),
+ MVGMAC_SPEED_100 = BIT(2),
+ MVGMAC_FULL_DUPLEX = BIT(3),
+ MVGMAC_RX_FLOW_CTRL_ENABLE = BIT(4),
+ MVGMAC_TX_FLOW_CTRL_ENABLE = BIT(5),
+ MVGMAC_RX_FLOW_CTRL_ACTIVE = BIT(6),
+ MVGMAC_TX_FLOW_CTRL_ACTIVE = BIT(7),
+ MVGMAC_AN_COMPLETE = BIT(11),
+ MVGMAC_SYNC_OK = BIT(14),
+
+ /* N: 21:6-13 22: */
+ GMAC_FIFO_CFG1_REG = 0x1c,
+ GMAC_FIFO_CFG1_TX_MIN_TH_SHIFT = 6,
+ GMAC_FIFO_CFG1_TX_MIN_TH_MASK = 0x7f <<
+ GMAC_FIFO_CFG1_TX_MIN_TH_SHIFT,
+
+ /* N:1 21: 22:0,3-7 */
+ GMAC_CTRL4_REG = 0x90,
+ GMAC_CTRL4_EXT_PIN_GMII_SEL = BIT(0),
+ GMAC_CTRL4_SHORT_PREAMBLE_ENABLE = BIT(1),
+ GMAC_CTRL4_FC_RX_ENABLE = BIT(3),
+ GMAC_CTRL4_FC_TX_ENABLE = BIT(4),
+ GMAC_CTRL4_DP_CLK_SEL = BIT(5),
+ GMAC_CTRL4_SYNC_BYPASS = BIT(6),
+ GMAC_CTRL4_QSGMII_BYPASS = BIT(7),
+
+ GMAC_LPI_CTRL0_REG = 0xc0,
+ GMAC_LPI_CTRL0_TS = 0xff << 8,
+ GMAC_LPI_CTRL1_REG = 0xc4,
+ GMAC_LPI_CTRL1_REQ_EN = BIT(0),
+ GMAC_LPI_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)); \
+})
+
+/* 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;
+ u32 val;
+
+ val = readl_relaxed(gmac->base + GMAC_CTRL0_REG);
+ val = insert(val, GMAC_CTRL0_MAX_RX_SIZE_MASK, size);
+ writel_relaxed(val, gmac->base + GMAC_CTRL0_REG);
+}
+EXPORT_SYMBOL_GPL(mvgmac_set_max_rx_size);
+
+/* Enable the port by setting the port enable bit of the MAC control register */
+void mvgmac_enable(struct mvgmac *gmac)
+{
+ u32 val;
+
+ val = readl_relaxed(gmac->base + GMAC_CTRL0_REG);
+ val |= GMAC_CTRL0_PORT_ENABLE;
+ val |= GMAC_CTRL0_MIB_CNTR_ENABLE;
+ writel_relaxed(val, gmac->base + GMAC_CTRL0_REG);
+}
+EXPORT_SYMBOL_GPL(mvgmac_enable);
+
+/* Disable the port */
+void mvgmac_disable(struct mvgmac *gmac)
+{
+ u32 val;
+
+ val = readl_relaxed(gmac->base + GMAC_CTRL0_REG);
+ val &= ~GMAC_CTRL0_PORT_ENABLE;
+ writel_relaxed(val, gmac->base + GMAC_CTRL0_REG);
+}
+EXPORT_SYMBOL_GPL(mvgmac_disable);
+
+int mvgmac_configure(struct mvgmac *gmac, phy_interface_t phy_mode)
+{
+ bool ext_pin_gmii;
+ u32 val;
+
+ switch (phy_mode) {
+ case PHY_INTERFACE_MODE_QSGMII:
+ case PHY_INTERFACE_MODE_SGMII:
+ ext_pin_gmii = false;
+ break;
+
+ case PHY_INTERFACE_MODE_RGMII:
+ case PHY_INTERFACE_MODE_RGMII_ID:
+ case PHY_INTERFACE_MODE_RGMII_RXID:
+ case PHY_INTERFACE_MODE_RGMII_TXID:
+ ext_pin_gmii = true;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ if (gmac->version == MVGMAC_PP21) {
+ /* Min. TX threshold must be less than minimum packet length */
+ val = readl_relaxed(gmac->base + GMAC_FIFO_CFG1_REG);
+ val = insert(val, GMAC_FIFO_CFG1_TX_MIN_TH_MASK, 64 - 4 - 2);
+ writel_relaxed(val, gmac->base + GMAC_FIFO_CFG1_REG);
+ } else if (gmac->version == MVGMAC_PP22) {
+ val = readl_relaxed(gmac->base + GMAC_CTRL4_REG);
+ val &= ~GMAC_CTRL4_DP_CLK_SEL;
+ val |= GMAC_CTRL4_SYNC_BYPASS;
+ val = insert(val, GMAC_CTRL4_QSGMII_BYPASS,
+ phy_mode != PHY_INTERFACE_MODE_QSGMII);
+ val = insert(val, GMAC_CTRL4_EXT_PIN_GMII_SEL, ext_pin_gmii);
+ writel_relaxed(val, gmac->base + GMAC_CTRL4_REG);
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(mvgmac_configure);
+
+void mvgmac_link_unforce(struct mvgmac *gmac)
+{
+ u32 val;
+
+ val = readl_relaxed(gmac->base + GMAC_ANEG_REG);
+ val &= ~(GMAC_ANEG_FORCE_LINK_PASS | GMAC_ANEG_FORCE_LINK_DOWN);
+ writel_relaxed(val, gmac->base + GMAC_ANEG_REG);
+}
+EXPORT_SYMBOL_GPL(mvgmac_link_unforce);
+
+void mvgmac_link_force_down(struct mvgmac *gmac)
+{
+ u32 val;
+
+ val = readl_relaxed(gmac->base + GMAC_ANEG_REG);
+ val &= ~GMAC_ANEG_FORCE_LINK_PASS;
+ val |= GMAC_ANEG_FORCE_LINK_DOWN;
+ writel_relaxed(val, gmac->base + GMAC_ANEG_REG);
+}
+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 val, ctrl4;
+
+ val = readl_relaxed(gmac->base + GMAC_ANEG_REG);
+ val &= ~GMAC_ANEG_CONFIG_FLOW_CTRL;
+
+ if (!phylink_autoneg_inband(mode)) {
+ val &= ~(GMAC_ANEG_FORCE_LINK_DOWN |
+ GMAC_ANEG_MII_SPEED |
+ GMAC_ANEG_GMII_SPEED |
+ GMAC_ANEG_FULL_DUPLEX);
+ val |= GMAC_ANEG_FORCE_LINK_PASS;
+
+ if (speed == SPEED_1000 || speed == SPEED_2500)
+ val |= GMAC_ANEG_GMII_SPEED;
+ else if (speed == SPEED_100)
+ val |= GMAC_ANEG_MII_SPEED;
+
+ if (duplex == DUPLEX_FULL)
+ val |= GMAC_ANEG_FULL_DUPLEX;
+ }
+
+ switch (gmac->version) {
+ case MVGMAC_NETA:
+ val = insert(val, GMAC_ANEG_CONFIG_FLOW_CTRL,
+ tx_pause || rx_pause);
+ break;
+
+ case 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);
+ break;
+ }
+
+ writel_relaxed(val, gmac->base + GMAC_ANEG_REG);
+}
+EXPORT_SYMBOL_GPL(mvgmac_link_up);
+
+bool mvgmac_link_is_up(struct mvgmac *gmac)
+{
+ u32 gmac_stat = readl_relaxed(gmac->base + GMAC_STATUS_REG);
+
+ return !!(gmac_stat & MVGMAC_LINK_UP);
+}
+EXPORT_SYMBOL_GPL(mvgmac_link_is_up);
+
+void mvgmac_pcs_get_state(struct mvgmac *gmac, struct phylink_link_state *state)
+{
+ 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 mvgmac *gmac)
+{
+ 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 mvgmac *gmac, unsigned int mode,
+ phy_interface_t interface,
+ const unsigned long *advertising,
+ bool permit_pause_to_mac)
+{
+ 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 (phylink_autoneg_inband(mode)) {
+ 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);
+
+int mvgmac_set_lpi_ts(struct mvgmac *gmac, unsigned int ts)
+{
+ u32 val;
+
+ if (!(readl_relaxed(gmac->base + GMAC_STATUS_REG) & MVGMAC_SPEED_1000))
+ ts = DIV_ROUND_UP(ts, 10);
+
+ if (ts > 255)
+ ts = 255;
+
+ val = readl_relaxed(gmac->base + GMAC_LPI_CTRL0_REG);
+ val = insert(val, GMAC_LPI_CTRL0_TS, ts);
+ writel_relaxed(val, gmac->base + GMAC_LPI_CTRL0_REG);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(mvgmac_set_lpi_ts);
+
+void mvgmac_set_eee(struct mvgmac *gmac, bool enable)
+{
+ u32 val;
+
+ val = readl_relaxed(gmac->base + GMAC_LPI_CTRL1_REG);
+ val = insert(val, GMAC_LPI_CTRL1_REQ_EN, enable);
+ writel_relaxed(val, gmac->base + GMAC_LPI_CTRL1_REG);
+}
+EXPORT_SYMBOL_GPL(mvgmac_set_eee);
+
+MODULE_DESCRIPTION("Marvell GMAC driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/net/ethernet/marvell/mvgmac.h b/drivers/net/ethernet/marvell/mvgmac.h
new file mode 100644
index 000000000000..f4111fba7258
--- /dev/null
+++ b/drivers/net/ethernet/marvell/mvgmac.h
@@ -0,0 +1,52 @@
+#ifndef MVGMAC_H
+#define MVGMAC_H
+
+#include <linux/phy.h>
+
+struct phylink_link_state;
+
+/* 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;
+};
+
+void mvgmac_set_max_rx_size(struct mvgmac *gmac, size_t max_rx_size);
+void mvgmac_enable(struct mvgmac *gmac);
+void mvgmac_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);
+bool mvgmac_link_is_up(struct mvgmac *gmac);
+void mvgmac_pcs_get_state(struct mvgmac *gmac,
+ struct phylink_link_state *state);
+void mvgmac_pcs_an_restart(struct mvgmac *gmac);
+int mvgmac_pcs_config(struct mvgmac *gmac, unsigned int 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);
+
+int mvgmac_set_lpi_ts(struct mvgmac *gmac, unsigned int ts);
+void mvgmac_set_eee(struct mvgmac *gmac, bool enable);
+
+#endif
diff --git a/drivers/net/ethernet/marvell/mvneta.c b/drivers/net/ethernet/marvell/mvneta.c
index a635cf84608a..b2225166f288 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>
@@ -193,43 +194,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
@@ -253,12 +218,6 @@
#define MVNETA_TXQ_TOKEN_SIZE_REG(q) (0x3e40 + ((q) << 2))
#define MVNETA_TXQ_TOKEN_SIZE_MAX 0x7fffffff
-#define MVNETA_LPI_CTRL_0 0x2cc0
-#define MVNETA_LPI_CTRL_1 0x2cc4
-#define MVNETA_LPI_REQUEST_ENABLE BIT(0)
-#define MVNETA_LPI_CTRL_2 0x2cc8
-#define MVNETA_LPI_STATUS 0x2ccc
-
#define MVNETA_CAUSE_TXQ_SENT_DESC_ALL_MASK 0xff
/* Descriptor ring Macros */
@@ -280,7 +239,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
@@ -499,8 +458,10 @@ 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;
@@ -509,6 +470,7 @@ struct mvneta_port {
bool eee_enabled;
bool eee_active;
bool tx_lpi_enabled;
+ u32 tx_lpi_timer;
u64 ethtool_stats[ARRAY_SIZE(mvneta_statistics)];
@@ -891,19 +853,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,
@@ -1311,23 +1260,13 @@ static void mvneta_port_down(struct mvneta_port *pp)
/* 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);
+ mvgmac_enable(&pp->gmac);
}
/* 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_disable(&pp->gmac);
udelay(200);
}
@@ -3133,9 +3072,9 @@ static irqreturn_t mvneta_percpu_isr(int irq, void *dev_id)
static void mvneta_link_change(struct mvneta_port *pp)
{
- u32 gmac_stat = mvreg_read(pp, MVNETA_GMAC_STATUS);
+ bool link_is_up = mvgmac_link_is_up(&pp->gmac);
- phylink_mac_change(pp->phylink, !!(gmac_stat & MVNETA_GMAC_LINK_UP));
+ phylink_mac_change(pp->phylink, link_is_up);
}
/* NAPI handler
@@ -3627,7 +3566,7 @@ 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 */
@@ -3829,6 +3768,46 @@ 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 void mvneta_pcs_get_state(struct phylink_pcs *pcs,
+ struct phylink_link_state *state)
+{
+ struct mvneta_port *pp = mvneta_pcs_to_port(pcs);
+
+ mvgmac_pcs_get_state(&pp->gmac, state);
+}
+
+static int mvneta_pcs_config(struct phylink_pcs *pcs,
+ unsigned int mode, phy_interface_t interface,
+ const unsigned long *advertising,
+ bool permit_pause_to_mac)
+{
+ struct mvneta_port *pp = mvneta_pcs_to_port(pcs);
+
+ /* We should never see Asym_Pause set */
+ WARN_ON(phylink_test(advertising, Asym_Pause));
+
+ return mvgmac_pcs_config(&pp->gmac, mode, interface, advertising,
+ permit_pause_to_mac);
+}
+
+static void mvneta_pcs_an_restart(struct phylink_pcs *pcs)
+{
+ struct mvneta_port *pp = mvneta_pcs_to_port(pcs);
+
+ mvgmac_pcs_an_restart(&pp->gmac);
+}
+
+static const struct phylink_pcs_ops mvneta_phylink_pcs_ops = {
+ .pcs_get_state = mvneta_pcs_get_state,
+ .pcs_config = mvneta_pcs_config,
+ .pcs_an_restart = mvneta_pcs_an_restart,
+};
+
static void mvneta_validate(struct phylink_config *config,
unsigned long *supported,
struct phylink_link_state *state)
@@ -3883,167 +3862,70 @@ static void mvneta_validate(struct phylink_config *config,
phylink_helper_basex_speed(state);
}
-static void mvneta_mac_pcs_get_state(struct phylink_config *config,
- struct phylink_link_state *state)
+static int mvneta_mac_prepare(struct phylink_config *config, unsigned int mode,
+ phy_interface_t interface)
{
struct net_device *ndev = to_net_dev(config->dev);
struct mvneta_port *pp = netdev_priv(ndev);
- u32 gmac_stat;
- gmac_stat = mvreg_read(pp, MVNETA_GMAC_STATUS);
+ if (pp->phy_interface != interface ||
+ phylink_autoneg_inband(mode)) {
+ /* Force the link down when changing the interface or if in
+ * in-band mode. According to Armada 370 documentation, we
+ * can only change the port mode and in-band enable when the
+ * link is down.
+ */
+ mvgmac_link_force_down(&pp->gmac);
+ }
+
+ if (pp->phy_interface != interface)
+ WARN_ON(phy_power_off(pp->comphy));
- 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;
+ /* Enable the 1ms clock */
+ if (phylink_autoneg_inband(mode)) {
+ unsigned long rate = clk_get_rate(pp->clk);
- 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);
+ mvreg_write(pp, MVNETA_GMAC_CLOCK_DIVIDER,
+ MVNETA_GMAC_1MS_CLOCK_ENABLE | (rate / 1000));
+ }
- state->pause = 0;
- 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;
+ return 0;
}
-static void mvneta_mac_an_restart(struct phylink_config *config)
+static void mvneta_mac_config(struct phylink_config *config, unsigned int mode,
+ const struct phylink_link_state *state)
{
struct net_device *ndev = to_net_dev(config->dev);
struct mvneta_port *pp = netdev_priv(ndev);
- u32 gmac_an = mvreg_read(pp, MVNETA_GMAC_AUTONEG_CONFIG);
- mvreg_write(pp, MVNETA_GMAC_AUTONEG_CONFIG,
- gmac_an | MVNETA_GMAC_INBAND_RESTART_AN);
- mvreg_write(pp, MVNETA_GMAC_AUTONEG_CONFIG,
- gmac_an & ~MVNETA_GMAC_INBAND_RESTART_AN);
+ mvgmac_config_mac(&pp->gmac, mode, state);
}
-static void mvneta_mac_config(struct phylink_config *config, unsigned int mode,
- const struct phylink_link_state *state)
+static int mvneta_mac_finish(struct phylink_config *config, unsigned int mode,
+ phy_interface_t interface)
{
struct net_device *ndev = to_net_dev(config->dev);
struct mvneta_port *pp = netdev_priv(ndev);
- u32 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);
- u32 new_clk, gmac_clk = mvreg_read(pp, MVNETA_GMAC_CLOCK_DIVIDER);
- u32 new_an, gmac_an = mvreg_read(pp, MVNETA_GMAC_AUTONEG_CONFIG);
-
- 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);
- new_clk = gmac_clk & ~MVNETA_GMAC_1MS_CLOCK_ENABLE;
- new_an = gmac_an & ~(MVNETA_GMAC_INBAND_AN_ENABLE |
- MVNETA_GMAC_INBAND_RESTART_AN |
- MVNETA_GMAC_AN_SPEED_EN |
- MVNETA_GMAC_ADVERT_SYM_FLOW_CTRL |
- MVNETA_GMAC_AN_FLOW_CTRL_EN |
- MVNETA_GMAC_AN_DUPLEX_EN);
-
- /* 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_test(state->advertising, Pause))
- new_an |= MVNETA_GMAC_ADVERT_SYM_FLOW_CTRL;
+ u32 clk;
+ /* Disable 1ms clock if not in in-band mode */
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;
- new_clk |= MVNETA_GMAC_1MS_CLOCK_ENABLE;
- new_an = (new_an & ~(MVNETA_GMAC_FORCE_LINK_DOWN |
- MVNETA_GMAC_FORCE_LINK_PASS |
- MVNETA_GMAC_CONFIG_MII_SPEED |
- MVNETA_GMAC_CONFIG_GMII_SPEED |
- MVNETA_GMAC_CONFIG_FULL_DUPLEX)) |
- MVNETA_GMAC_INBAND_AN_ENABLE |
- MVNETA_GMAC_AN_SPEED_EN |
- MVNETA_GMAC_AN_DUPLEX_EN;
- } else {
- /* 802.3z negotiation - only 1000base-X */
- new_ctrl0 |= MVNETA_GMAC0_PORT_1000BASE_X;
- new_clk |= MVNETA_GMAC_1MS_CLOCK_ENABLE;
- new_an = (new_an & ~(MVNETA_GMAC_FORCE_LINK_DOWN |
- MVNETA_GMAC_FORCE_LINK_PASS |
- MVNETA_GMAC_CONFIG_MII_SPEED)) |
- MVNETA_GMAC_INBAND_AN_ENABLE |
- MVNETA_GMAC_CONFIG_GMII_SPEED |
- /* The MAC only supports FD mode */
- MVNETA_GMAC_CONFIG_FULL_DUPLEX;
-
- if (state->pause & MLO_PAUSE_AN && state->an_enabled)
- new_an |= MVNETA_GMAC_AN_FLOW_CTRL_EN;
- }
-
- /* Armada 370 documentation says we can only change the port mode
- * and in-band enable when the link is down, so force it down
- * while making these changes. We also do this for GMAC_CTRL2 */
- if ((new_ctrl0 ^ gmac_ctrl0) & MVNETA_GMAC0_PORT_1000BASE_X ||
- (new_ctrl2 ^ gmac_ctrl2) & MVNETA_GMAC2_INBAND_AN_ENABLE ||
- (new_an ^ gmac_an) & MVNETA_GMAC_INBAND_AN_ENABLE) {
- mvreg_write(pp, MVNETA_GMAC_AUTONEG_CONFIG,
- (gmac_an & ~MVNETA_GMAC_FORCE_LINK_PASS) |
- MVNETA_GMAC_FORCE_LINK_DOWN);
- }
-
-
- /* 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 (pp->phy_interface != state->interface) {
- if (pp->comphy)
- WARN_ON(phy_power_off(pp->comphy));
- WARN_ON(mvneta_config_interface(pp, state->interface));
- }
-
- 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 (new_clk != gmac_clk)
- mvreg_write(pp, MVNETA_GMAC_CLOCK_DIVIDER, new_clk);
- if (new_an != gmac_an)
- mvreg_write(pp, MVNETA_GMAC_AUTONEG_CONFIG, new_an);
-
- if (gmac_ctrl2 & MVNETA_GMAC2_PORT_RESET) {
- while ((mvreg_read(pp, MVNETA_GMAC_CTRL_2) &
- MVNETA_GMAC2_PORT_RESET) != 0)
- continue;
+ clk = mvreg_read(pp, MVNETA_GMAC_CLOCK_DIVIDER);
+ clk &= ~MVNETA_GMAC_1MS_CLOCK_ENABLE;
+ mvreg_write(pp, MVNETA_GMAC_CLOCK_DIVIDER, clk);
}
-}
-static void mvneta_set_eee(struct mvneta_port *pp, bool enable)
-{
- u32 lpi_ctl1;
+ if (pp->phy_interface != interface)
+ /* Enable the Serdes PHY */
+ WARN_ON(mvneta_config_interface(pp, interface));
- lpi_ctl1 = mvreg_read(pp, MVNETA_LPI_CTRL_1);
- if (enable)
- lpi_ctl1 |= MVNETA_LPI_REQUEST_ENABLE;
- else
- lpi_ctl1 &= ~MVNETA_LPI_REQUEST_ENABLE;
- mvreg_write(pp, MVNETA_LPI_CTRL_1, lpi_ctl1);
+ /* 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))
+ mvgmac_link_unforce(&pp->gmac);
+
+ return 0;
}
static void mvneta_mac_link_down(struct phylink_config *config,
@@ -4051,19 +3933,12 @@ 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);
pp->eee_active = false;
- mvneta_set_eee(pp, false);
+ mvgmac_set_eee(&pp->gmac, false);
}
static void mvneta_mac_link_up(struct phylink_config *config,
@@ -4074,56 +3949,22 @@ 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);
if (phy && pp->eee_enabled) {
pp->eee_active = phy_init_eee(phy, 0) >= 0;
- mvneta_set_eee(pp, pp->eee_active && pp->tx_lpi_enabled);
+ mvgmac_set_lpi_ts(&pp->gmac, pp->tx_lpi_timer);
+ mvgmac_set_eee(&pp->gmac, pp->eee_active && pp->tx_lpi_enabled);
}
}
static const struct phylink_mac_ops mvneta_phylink_ops = {
.validate = mvneta_validate,
- .mac_pcs_get_state = mvneta_mac_pcs_get_state,
- .mac_an_restart = mvneta_mac_an_restart,
+ .mac_prepare = mvneta_mac_prepare,
.mac_config = mvneta_mac_config,
+ .mac_finish = mvneta_mac_finish,
.mac_link_down = mvneta_mac_link_down,
.mac_link_up = mvneta_mac_link_up,
};
@@ -4889,14 +4730,11 @@ static int mvneta_ethtool_get_eee(struct net_device *dev,
struct ethtool_eee *eee)
{
struct mvneta_port *pp = netdev_priv(dev);
- u32 lpi_ctl0;
-
- lpi_ctl0 = mvreg_read(pp, MVNETA_LPI_CTRL_0);
eee->eee_enabled = pp->eee_enabled;
eee->eee_active = pp->eee_active;
eee->tx_lpi_enabled = pp->tx_lpi_enabled;
- eee->tx_lpi_timer = (lpi_ctl0) >> 8; // * scale;
+ eee->tx_lpi_timer = pp->tx_lpi_timer;
return phylink_ethtool_get_eee(pp->phylink, eee);
}
@@ -4905,22 +4743,14 @@ static int mvneta_ethtool_set_eee(struct net_device *dev,
struct ethtool_eee *eee)
{
struct mvneta_port *pp = netdev_priv(dev);
- u32 lpi_ctl0;
-
- /* The Armada 37x documents do not give limits for this other than
- * it being an 8-bit register. */
- if (eee->tx_lpi_enabled && eee->tx_lpi_timer > 255)
- return -EINVAL;
-
- lpi_ctl0 = mvreg_read(pp, MVNETA_LPI_CTRL_0);
- lpi_ctl0 &= ~(0xff << 8);
- lpi_ctl0 |= eee->tx_lpi_timer << 8;
- mvreg_write(pp, MVNETA_LPI_CTRL_0, lpi_ctl0);
pp->eee_enabled = eee->eee_enabled;
pp->tx_lpi_enabled = eee->tx_lpi_enabled;
+ pp->tx_lpi_timer = eee->tx_lpi_timer;
- mvneta_set_eee(pp, eee->tx_lpi_enabled && eee->eee_enabled);
+ mvgmac_set_eee(&pp->gmac, false);
+ mvgmac_set_lpi_ts(&pp->gmac, eee->tx_lpi_timer);
+ mvgmac_set_eee(&pp->gmac, pp->eee_active && pp->tx_lpi_enabled);
return phylink_ethtool_set_eee(pp->phylink, eee);
}
@@ -5178,6 +5008,22 @@ static int mvneta_probe(struct platform_device *pdev)
pp->phylink_config.dev = &dev->dev;
pp->phylink_config.type = PHYLINK_NETDEV;
+ __set_bit(PHY_INTERFACE_MODE_SGMII,
+ pp->phylink_config.supported_interfaces);
+ __set_bit(PHY_INTERFACE_MODE_RGMII,
+ pp->phylink_config.supported_interfaces);
+ __set_bit(PHY_INTERFACE_MODE_RGMII_ID,
+ pp->phylink_config.supported_interfaces);
+ __set_bit(PHY_INTERFACE_MODE_RGMII_RXID,
+ pp->phylink_config.supported_interfaces);
+ __set_bit(PHY_INTERFACE_MODE_RGMII_TXID,
+ pp->phylink_config.supported_interfaces);
+ __set_bit(PHY_INTERFACE_MODE_QSGMII,
+ pp->phylink_config.supported_interfaces);
+ __set_bit(PHY_INTERFACE_MODE_1000BASEX,
+ pp->phylink_config.supported_interfaces);
+ __set_bit(PHY_INTERFACE_MODE_2500BASEX,
+ pp->phylink_config.supported_interfaces);
phylink = phylink_create(&pp->phylink_config, pdev->dev.fwnode,
phy_mode, &mvneta_phylink_ops);
@@ -5186,6 +5032,9 @@ static int mvneta_probe(struct platform_device *pdev)
goto err_free_irq;
}
+ pp->phylink_pcs.ops = &mvneta_phylink_pcs_ops;
+ phylink_set_pcs(phylink, &pp->phylink_pcs);
+
dev->tx_queue_len = MVNETA_MAX_TXD;
dev->watchdog_timeo = 5 * HZ;
dev->netdev_ops = &mvneta_netdev_ops;
@@ -5224,6 +5073,10 @@ static int mvneta_probe(struct platform_device *pdev)
goto err_clk;
}
+ pp->gmac.base = pp->base + MVNETA_GMAC_BASE;
+ pp->gmac.version = MVGMAC_NETA;
+ pp->tx_lpi_timer = 16;
+
/* Alloc per-cpu port structure */
pp->ports = alloc_percpu(struct mvneta_pcpu_port);
if (!pp->ports) {
diff --git a/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c b/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c
index 1767c60056c5..342fd8506001 100644
--- a/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c
+++ b/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c
@@ -6887,6 +6887,29 @@ static int mvpp2_port_probe(struct platform_device *pdev,
port->phylink_config.dev = &dev->dev;
port->phylink_config.type = PHYLINK_NETDEV;
+ if (mvpp2_port_supports_xlg(port)) {
+ __set_bit(PHY_INTERFACE_MODE_10GBASER,
+ port->phylink_config.supported_interfaces);
+ __set_bit(PHY_INTERFACE_MODE_XAUI,
+ port->phylink_config.supported_interfaces);
+ }
+ if (mvpp2_port_supports_rgmii(port)) {
+ __set_bit(PHY_INTERFACE_MODE_RGMII,
+ port->phylink_config.supported_interfaces);
+ __set_bit(PHY_INTERFACE_MODE_RGMII_ID,
+ port->phylink_config.supported_interfaces);
+ __set_bit(PHY_INTERFACE_MODE_RGMII_TXID,
+ port->phylink_config.supported_interfaces);
+ __set_bit(PHY_INTERFACE_MODE_RGMII_RXID,
+ port->phylink_config.supported_interfaces);
+ }
+ __set_bit(PHY_INTERFACE_MODE_SGMII,
+ port->phylink_config.supported_interfaces);
+ __set_bit(PHY_INTERFACE_MODE_1000BASEX,
+ port->phylink_config.supported_interfaces);
+ __set_bit(PHY_INTERFACE_MODE_2500BASEX,
+ port->phylink_config.supported_interfaces);
+
phylink = phylink_create(&port->phylink_config, port_fwnode,
phy_mode, &mvpp2_phylink_ops);
if (IS_ERR(phylink)) {
diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.c b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
index 01d3ee4b5829..b32875512ee8 100644
--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
@@ -335,7 +335,7 @@ static void mtk_mac_config(struct phylink_config *config, unsigned int mode,
/* Setup SGMIISYS with the determined property */
if (state->interface != PHY_INTERFACE_MODE_SGMII)
err = mtk_sgmii_setup_mode_force(eth->sgmii, sid,
- state);
+ state->interface);
else if (phylink_autoneg_inband(mode))
err = mtk_sgmii_setup_mode_an(eth->sgmii, sid);
@@ -432,6 +432,15 @@ static void mtk_mac_link_up(struct phylink_config *config,
phylink_config);
u32 mcr = mtk_r32(mac->hw, MTK_MAC_MCR(mac->id));
+ if (phy_interface_mode_is_8023z(interface)) {
+ struct mtk_eth *eth = mac->hw;
+
+ /* Decide how GMAC and SGMIISYS be mapped */
+ int sid = (MTK_HAS_CAPS(eth->soc->caps, MTK_SHARED_SGMII)) ?
+ 0 : mac->id;
+ mtk_sgmii_link_up(eth->sgmii, sid, speed, duplex);
+ }
+
mcr &= ~(MAC_MCR_SPEED_100 | MAC_MCR_SPEED_1000 |
MAC_MCR_FORCE_DPX | MAC_MCR_FORCE_TX_FC |
MAC_MCR_FORCE_RX_FC);
diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.h b/drivers/net/ethernet/mediatek/mtk_eth_soc.h
index fd3cec8f06ba..646029b17bcd 100644
--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h
+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h
@@ -938,7 +938,8 @@ int mtk_sgmii_init(struct mtk_sgmii *ss, struct device_node *np,
u32 ana_rgc3);
int mtk_sgmii_setup_mode_an(struct mtk_sgmii *ss, int id);
int mtk_sgmii_setup_mode_force(struct mtk_sgmii *ss, int id,
- const struct phylink_link_state *state);
+ phy_interface_t interface);
+void mtk_sgmii_link_up(struct mtk_sgmii *ss, int id, int speed, int duplex);
void mtk_sgmii_restart_an(struct mtk_eth *eth, int mac_id);
int mtk_gmac_sgmii_path_setup(struct mtk_eth *eth, int mac_id);
diff --git a/drivers/net/ethernet/mediatek/mtk_sgmii.c b/drivers/net/ethernet/mediatek/mtk_sgmii.c
index 32d83421226a..372c85c830b5 100644
--- a/drivers/net/ethernet/mediatek/mtk_sgmii.c
+++ b/drivers/net/ethernet/mediatek/mtk_sgmii.c
@@ -60,7 +60,7 @@ int mtk_sgmii_setup_mode_an(struct mtk_sgmii *ss, int id)
}
int mtk_sgmii_setup_mode_force(struct mtk_sgmii *ss, int id,
- const struct phylink_link_state *state)
+ phy_interface_t interface)
{
unsigned int val;
@@ -69,7 +69,7 @@ int mtk_sgmii_setup_mode_force(struct mtk_sgmii *ss, int id,
regmap_read(ss->regmap[id], ss->ana_rgc3, &val);
val &= ~RG_PHY_SPEED_MASK;
- if (state->interface == PHY_INTERFACE_MODE_2500BASEX)
+ if (interface == PHY_INTERFACE_MODE_2500BASEX)
val |= RG_PHY_SPEED_3_125G;
regmap_write(ss->regmap[id], ss->ana_rgc3, val);
@@ -78,11 +78,33 @@ int mtk_sgmii_setup_mode_force(struct mtk_sgmii *ss, int id,
val &= ~SGMII_AN_ENABLE;
regmap_write(ss->regmap[id], SGMSYS_PCS_CONTROL_1, val);
+ if (interface == PHY_INTERFACE_MODE_1000BASEX ||
+ interface == PHY_INTERFACE_MODE_2500BASEX) {
+ /* SGMII force mode setting */
+ regmap_read(ss->regmap[id], SGMSYS_SGMII_MODE, &val);
+ val &= ~SGMII_IF_MODE_MASK;
+ val |= SGMII_SPEED_1000;
+ val |= SGMII_DUPLEX_FULL;
+ regmap_write(ss->regmap[id], SGMSYS_SGMII_MODE, val);
+ }
+
+ /* Release PHYA power down state */
+ regmap_read(ss->regmap[id], SGMSYS_QPHY_PWR_STATE_CTRL, &val);
+ val &= ~SGMII_PHYA_PWD;
+ regmap_write(ss->regmap[id], SGMSYS_QPHY_PWR_STATE_CTRL, val);
+
+ return 0;
+}
+
+void mtk_sgmii_link_up(struct mtk_sgmii *ss, int id, int speed, int duplex)
+{
+ unsigned int val;
+
/* SGMII force mode setting */
regmap_read(ss->regmap[id], SGMSYS_SGMII_MODE, &val);
val &= ~SGMII_IF_MODE_MASK;
- switch (state->speed) {
+ switch (speed) {
case SPEED_10:
val |= SGMII_SPEED_10;
break;
@@ -95,17 +117,10 @@ int mtk_sgmii_setup_mode_force(struct mtk_sgmii *ss, int id,
break;
}
- if (state->duplex == DUPLEX_FULL)
+ if (duplex == DUPLEX_FULL)
val |= SGMII_DUPLEX_FULL;
regmap_write(ss->regmap[id], SGMSYS_SGMII_MODE, val);
-
- /* Release PHYA power down state */
- regmap_read(ss->regmap[id], SGMSYS_QPHY_PWR_STATE_CTRL, &val);
- val &= ~SGMII_PHYA_PWD;
- regmap_write(ss->regmap[id], SGMSYS_QPHY_PWR_STATE_CTRL, val);
-
- return 0;
}
void mtk_sgmii_restart_an(struct mtk_eth *eth, int mac_id)
diff --git a/drivers/net/phy/Makefile b/drivers/net/phy/Makefile
index a13e402074cf..78439b199723 100644
--- a/drivers/net/phy/Makefile
+++ b/drivers/net/phy/Makefile
@@ -26,7 +26,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/at803x.c b/drivers/net/phy/at803x.c
index c2aa4c92edde..51a2540ae101 100644
--- a/drivers/net/phy/at803x.c
+++ b/drivers/net/phy/at803x.c
@@ -409,12 +409,6 @@ static int at8031_register_regulators(struct phy_device *phydev)
return 0;
}
-static bool at803x_match_phy_id(struct phy_device *phydev, u32 phy_id)
-{
- return (phydev->phy_id & phydev->drv->phy_id_mask)
- == (phy_id & phydev->drv->phy_id_mask);
-}
-
static int at803x_parse_dt(struct phy_device *phydev)
{
struct device_node *node = phydev->mdio.dev.of_node;
@@ -479,8 +473,8 @@ static int at803x_parse_dt(struct phy_device *phydev)
* to the AR8030 so there might be a good chance it works on
* the AR8030 too.
*/
- if (at803x_match_phy_id(phydev, ATH8030_PHY_ID) ||
- at803x_match_phy_id(phydev, ATH8035_PHY_ID)) {
+ if (phydev->drv->phy_id == ATH8030_PHY_ID ||
+ phydev->drv->phy_id == ATH8035_PHY_ID) {
priv->clk_25m_reg &= AT8035_CLK_OUT_MASK;
priv->clk_25m_mask &= AT8035_CLK_OUT_MASK;
}
@@ -508,7 +502,7 @@ static int at803x_parse_dt(struct phy_device *phydev)
/* Only supported on AR8031/AR8033, the AR8030/AR8035 use strapping
* options.
*/
- if (at803x_match_phy_id(phydev, ATH8031_PHY_ID)) {
+ if (phydev->drv->phy_id == ATH8031_PHY_ID) {
if (of_property_read_bool(node, "qca,keep-pll-enabled"))
priv->flags |= AT803X_KEEP_PLL_ENABLED;
@@ -644,7 +638,7 @@ static int at803x_config_init(struct phy_device *phydev)
if (ret < 0)
return ret;
- if (at803x_match_phy_id(phydev, ATH8031_PHY_ID)) {
+ if (phydev->drv->phy_id == ATH8031_PHY_ID) {
ret = at8031_pll_config(phydev);
if (ret < 0)
return ret;
diff --git a/drivers/net/phy/bcm84881.c b/drivers/net/phy/bcm84881.c
index 9717a1626f3f..cba3ffc0708f 100644
--- a/drivers/net/phy/bcm84881.c
+++ b/drivers/net/phy/bcm84881.c
@@ -51,6 +51,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 8018ddf7f316..042790a675d2 100644
--- a/drivers/net/phy/marvell.c
+++ b/drivers/net/phy/marvell.c
@@ -158,6 +158,10 @@
#define MII_M1011_PHY_STATUS_FULLDUPLEX 0x2000
#define MII_M1011_PHY_STATUS_RESOLVED 0x0800
#define MII_M1011_PHY_STATUS_LINK 0x0400
+#define MII_M1111_PHY_STATUS_TX_PAUSE 0x0008
+#define MII_M1111_PHY_STATUS_RX_PAUSE 0x0004
+#define MII_88E151X_PHY_STATUS_TX_PAUSE 0x0200
+#define MII_88E151X_PHY_STATUS_RX_PAUSE 0x0100
#define MII_88E3016_PHY_SPEC_CTRL 0x10
#define MII_88E3016_DISABLE_SCRAMBLER 0x0200
@@ -283,6 +287,8 @@ struct marvell_priv {
u32 last;
u32 step;
s8 pair;
+ u16 tx_pause_mask;
+ u16 rx_pause_mask;
};
static int marvell_read_page(struct phy_device *phydev)
@@ -1458,6 +1464,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;
@@ -1513,6 +1520,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;
}
@@ -1556,6 +1568,7 @@ static int marvell_read_status_page(struct phy_device *phydev, int page)
phydev->speed = SPEED_UNKNOWN;
phydev->duplex = DUPLEX_UNKNOWN;
phydev->port = fiber ? PORT_FIBRE : PORT_TP;
+ phydev->resolved_pause_valid = false;
if (phydev->autoneg == AUTONEG_ENABLE)
err = marvell_read_status_page_an(phydev, fiber, status);
@@ -2671,6 +2684,32 @@ static int marvell_probe(struct phy_device *phydev)
phydev->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);
+
+ return 0;
+}
+
+static int marvell_probe_pause(struct phy_device *phydev, u16 tx_pause_mask,
+ u16 rx_pause_mask)
+{
+ struct marvell_priv *priv;
+ int err;
+
+ err = marvell_probe(phydev);
+ if (err)
+ return err;
+
+ priv = phydev->priv;
+ priv->tx_pause_mask = tx_pause_mask;
+ priv->rx_pause_mask = rx_pause_mask;
+
return 0;
}
@@ -2685,11 +2724,18 @@ static int m88e1121_probe(struct phy_device *phydev)
return m88e1121_hwmon_probe(phydev);
}
+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;
@@ -2751,7 +2797,7 @@ static struct phy_driver marvell_drivers[] = {
.phy_id_mask = MARVELL_PHY_ID_MASK,
.name = "Marvell 88E1111",
/* PHY_GBIT_FEATURES */
- .probe = marvell_probe,
+ .probe = m88e1111_probe,
.config_init = m88e1111_config_init,
.config_aneg = m88e1111_config_aneg,
.read_status = marvell_read_status,
diff --git a/drivers/net/phy/marvell10g.c b/drivers/net/phy/marvell10g.c
index b1bb9b8e1e4e..0c17be11a855 100644
--- a/drivers/net/phy/marvell10g.c
+++ b/drivers/net/phy/marvell10g.c
@@ -22,16 +22,20 @@
* If both the fiber and copper ports are connected, the first to gain
* link takes priority and the other port is completely locked out.
*/
+#include <linux/bitfield.h>
#include <linux/ctype.h>
#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>
#define MV_PHY_ALASKA_NBT_QUIRK_MASK 0xfffffffe
#define MV_PHY_ALASKA_NBT_QUIRK_REV (MARVELL_PHY_ID_88X3310 | 0xa)
+#define MV_VERSION(a,b,c,d) ((a) << 24 | (b) << 16 | (c) << 8 | (d))
+
enum {
MV_PMA_FW_VER0 = 0xc011,
MV_PMA_FW_VER1 = 0xc012,
@@ -52,6 +56,15 @@ enum {
MV_PCS_CSCR1_MDIX_MDIX = 0x0020,
MV_PCS_CSCR1_MDIX_AUTO = 0x0060,
+ MV_PCS_DSC1 = 0x8003,
+ MV_PCS_DSC1_ENABLE = BIT(9),
+ MV_PCS_DSC1_10GBT = 0x01c0,
+ MV_PCS_DSC1_1GBR = 0x0038,
+ MV_PCS_DSC1_100BTX = 0x0007,
+ MV_PCS_DSC2 = 0x8004,
+ MV_PCS_DSC2_2P5G = 0xf000,
+ MV_PCS_DSC2_5G = 0x0f00,
+
MV_PCS_CSSR1 = 0x8008,
MV_PCS_CSSR1_SPD1_MASK = 0xc000,
MV_PCS_CSSR1_SPD1_SPD2 = 0xc000,
@@ -60,6 +73,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,
@@ -80,8 +95,8 @@ enum {
MV_V2_PORT_CTRL = 0xf001,
MV_V2_PORT_CTRL_SWRST = BIT(15),
MV_V2_PORT_CTRL_PWRDOWN = BIT(11),
- MV_V2_PORT_MAC_TYPE_MASK = 0x7,
- MV_V2_PORT_MAC_TYPE_RATE_MATCH = 0x6,
+ MV_V2_PORT_CTRL_MACTYPE_MASK = 0x7,
+ MV_V2_PORT_CTRL_MACTYPE_RATE_MATCH = 0x6,
/* Temperature control/read registers (88X3310 only) */
MV_V2_TEMP_CTRL = 0xf08a,
MV_V2_TEMP_CTRL_MASK = 0xc000,
@@ -94,9 +109,12 @@ enum {
struct mv3310_priv {
u32 firmware_ver;
bool rate_match;
+ bool firmware_failed;
struct device *hwmon_dev;
char *hwmon_name;
+ u8 num_leds;
+ u16 led_mode[4];
};
#ifdef CONFIG_HWMON
@@ -286,6 +304,66 @@ static int mv3310_reset(struct phy_device *phydev, u32 unit)
5000, 100000, true);
}
+static int mv3310_get_downshift(struct phy_device *phydev, u8 *ds)
+{
+ struct mv3310_priv *priv = dev_get_drvdata(&phydev->mdio.dev);
+ int val;
+
+ if (priv->firmware_ver < MV_VERSION(0,3,5,0))
+ return -EOPNOTSUPP;
+
+ val = phy_read_mmd(phydev, MDIO_MMD_PCS, MV_PCS_DSC1);
+ if (val < 0)
+ return val;
+
+ if (val & MV_PCS_DSC1_ENABLE)
+ /* assume that all fields are the same */
+ *ds = 1 + FIELD_GET(MV_PCS_DSC1_10GBT, (u16)val);
+ else
+ *ds = DOWNSHIFT_DEV_DISABLE;
+
+ return 0;
+}
+
+static int mv3310_set_downshift(struct phy_device *phydev, u8 ds)
+{
+ struct mv3310_priv *priv = dev_get_drvdata(&phydev->mdio.dev);
+ u16 val;
+ int err;
+
+ /* Fails to downshift with v0.3.5.0 and earlier */
+ if (priv->firmware_ver < MV_VERSION(0,3,5,0))
+ return -EOPNOTSUPP;
+
+ if (ds == DOWNSHIFT_DEV_DISABLE)
+ return phy_clear_bits_mmd(phydev, MDIO_MMD_PCS, MV_PCS_DSC1,
+ MV_PCS_DSC1_ENABLE);
+
+ /* FIXME: The default is disabled, so should we disable? */
+ if (ds == DOWNSHIFT_DEV_DEFAULT_COUNT)
+ ds = 2;
+
+ if (ds > 8)
+ return -E2BIG;
+
+ ds -= 1;
+ val = FIELD_PREP(MV_PCS_DSC2_2P5G, ds);
+ val |= FIELD_PREP(MV_PCS_DSC2_5G, ds);
+ err = phy_modify_mmd(phydev, MDIO_MMD_PCS, MV_PCS_DSC2,
+ MV_PCS_DSC2_2P5G | MV_PCS_DSC2_5G, val);
+ if (err < 0)
+ return err;
+
+ val = MV_PCS_DSC1_ENABLE;
+ val |= FIELD_PREP(MV_PCS_DSC1_10GBT, ds);
+ val |= FIELD_PREP(MV_PCS_DSC1_1GBR, ds);
+ val |= FIELD_PREP(MV_PCS_DSC1_100BTX, ds);
+
+ return phy_modify_mmd(phydev, MDIO_MMD_PCS, MV_PCS_DSC1,
+ MV_PCS_DSC1_ENABLE | MV_PCS_DSC1_10GBT |
+ MV_PCS_DSC1_1GBR | MV_PCS_DSC1_100BTX, val);
+}
+
static int mv3310_get_edpd(struct phy_device *phydev, u16 *edpd)
{
int val;
@@ -343,9 +421,10 @@ static int mv3310_sfp_insert(void *upstream, const struct sfp_eeprom_id *id)
{
struct phy_device *phydev = upstream;
__ETHTOOL_DECLARE_LINK_MODE_MASK(support) = { 0, };
+ DECLARE_PHY_INTERFACE_MASK(interfaces);
phy_interface_t iface;
- sfp_parse_support(phydev->sfp_bus, id, support);
+ sfp_parse_support(phydev->sfp_bus, id, support, interfaces);
iface = sfp_select_interface(phydev->sfp_bus, support);
if (iface != PHY_INTERFACE_MODE_10GBASER) {
@@ -361,6 +440,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)
{
struct mv3310_priv *priv;
@@ -371,6 +487,24 @@ static int mv3310_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);
+
+ 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;
@@ -378,15 +512,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;
@@ -436,6 +564,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
@@ -453,19 +594,44 @@ static bool mv3310_has_pma_ngbaset_quirk(struct phy_device *phydev)
MV_PHY_ALASKA_NBT_QUIRK_MASK) == MV_PHY_ALASKA_NBT_QUIRK_REV;
}
+static int mv3310_select_mode(struct phy_device *phydev,
+ unsigned long *host_interfaces)
+{
+ int mac_type = -1;
+
+ if (test_bit(PHY_INTERFACE_MODE_USXGMII, host_interfaces))
+ mac_type = 7;
+ else if (test_bit(PHY_INTERFACE_MODE_SGMII, host_interfaces) &&
+ test_bit(PHY_INTERFACE_MODE_10GBASER, host_interfaces))
+ mac_type = 4;
+ else if (test_bit(PHY_INTERFACE_MODE_SGMII, host_interfaces) &&
+ test_bit(PHY_INTERFACE_MODE_RXAUI, host_interfaces))
+ mac_type = 0;
+ else if (test_bit(PHY_INTERFACE_MODE_10GBASER, host_interfaces))
+ mac_type = 6;
+ else if (test_bit(PHY_INTERFACE_MODE_RXAUI, host_interfaces))
+ mac_type = 2;
+ else if (test_bit(PHY_INTERFACE_MODE_SGMII, host_interfaces))
+ mac_type = 4;
+
+ return mac_type;
+}
+
static int mv3310_config_init(struct phy_device *phydev)
{
struct mv3310_priv *priv = dev_get_drvdata(&phydev->mdio.dev);
- int err;
- int val;
+ int val, ret, err, mac_type = -1;
/* Check that the PHY interface type is compatible */
- if (phydev->interface != PHY_INTERFACE_MODE_SGMII &&
- phydev->interface != PHY_INTERFACE_MODE_2500BASEX &&
- phydev->interface != PHY_INTERFACE_MODE_XAUI &&
- phydev->interface != PHY_INTERFACE_MODE_RXAUI &&
- phydev->interface != PHY_INTERFACE_MODE_10GBASER)
+ if (!phy_interface_empty(phydev->host_interfaces)) {
+ mac_type = mv3310_select_mode(phydev, phydev->host_interfaces);
+ } else if (phydev->interface != PHY_INTERFACE_MODE_SGMII &&
+ phydev->interface != PHY_INTERFACE_MODE_2500BASEX &&
+ phydev->interface != PHY_INTERFACE_MODE_XAUI &&
+ phydev->interface != PHY_INTERFACE_MODE_RXAUI &&
+ phydev->interface != PHY_INTERFACE_MODE_10GBASER) {
return -ENODEV;
+ }
phydev->mdix_ctrl = ETH_TP_MDI_AUTO;
@@ -474,14 +640,38 @@ static int mv3310_config_init(struct phy_device *phydev)
if (err)
return err;
+ if (mac_type != -1) {
+ ret = phy_modify_mmd_changed(phydev, MDIO_MMD_VEND2,
+ MV_V2_PORT_CTRL,
+ MV_V2_PORT_CTRL_MACTYPE_MASK,
+ mac_type);
+ if (ret > 0)
+ ret = phy_modify_mmd(phydev, MDIO_MMD_VEND2,
+ MV_V2_PORT_CTRL,
+ MV_V2_PORT_CTRL_SWRST,
+ MV_V2_PORT_CTRL_SWRST);
+
+ if (ret < 0)
+ return ret;
+ }
+
val = phy_read_mmd(phydev, MDIO_MMD_VEND2, MV_V2_PORT_CTRL);
if (val < 0)
return val;
- priv->rate_match = ((val & MV_V2_PORT_MAC_TYPE_MASK) ==
- MV_V2_PORT_MAC_TYPE_RATE_MATCH);
+ priv->rate_match = ((val & MV_V2_PORT_CTRL_MACTYPE_MASK) ==
+ MV_V2_PORT_CTRL_MACTYPE_RATE_MATCH);
/* Enable EDPD mode - saving 600mW */
- return mv3310_set_edpd(phydev, ETHTOOL_PHY_EDPD_DFLT_TX_MSECS);
+ err = mv3310_set_edpd(phydev, ETHTOOL_PHY_EDPD_DFLT_TX_MSECS);
+ if (err)
+ return err;
+
+ /* Allow downshift */
+ err = mv3310_set_downshift(phydev, DOWNSHIFT_DEV_DEFAULT_COUNT);
+ if (err && err != -EOPNOTSUPP)
+ return err;
+
+ return mv3310_leds_write(phydev);
}
static int mv3310_get_features(struct phy_device *phydev)
@@ -695,6 +885,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)
@@ -747,6 +941,8 @@ static int mv3310_get_tunable(struct phy_device *phydev,
struct ethtool_tunable *tuna, void *data)
{
switch (tuna->id) {
+ case ETHTOOL_PHY_DOWNSHIFT:
+ return mv3310_get_downshift(phydev, data);
case ETHTOOL_PHY_EDPD:
return mv3310_get_edpd(phydev, data);
default:
@@ -758,6 +954,8 @@ static int mv3310_set_tunable(struct phy_device *phydev,
struct ethtool_tunable *tuna, const void *data)
{
switch (tuna->id) {
+ case ETHTOOL_PHY_DOWNSHIFT:
+ return mv3310_set_downshift(phydev, *(u8 *)data);
case ETHTOOL_PHY_EDPD:
return mv3310_set_edpd(phydev, *(u16 *)data);
default:
@@ -775,6 +973,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,
@@ -789,6 +988,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/phy.c b/drivers/net/phy/phy.c
index fc2e7cb5b2e5..369243a8c399 100644
--- a/drivers/net/phy/phy.c
+++ b/drivers/net/phy/phy.c
@@ -912,7 +912,7 @@ void phy_stop_machine(struct phy_device *phydev)
*/
void phy_error(struct phy_device *phydev)
{
- WARN_ON(1);
+ phydev_err(phydev, "Error detected, halting PHY\n");
mutex_lock(&phydev->lock);
phydev->state = PHY_HALTED;
@@ -1024,6 +1024,8 @@ void phy_stop(struct phy_device *phydev)
sfp_upstream_stop(phydev->sfp_bus);
phydev->state = PHY_HALTED;
+ if (phydev->drv->stop)
+ phydev->drv->stop(phydev);
mutex_unlock(&phydev->lock);
@@ -1057,6 +1059,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 cc38e326405a..95edb2e68c29 100644
--- a/drivers/net/phy/phy_device.c
+++ b/drivers/net/phy/phy_device.c
@@ -381,8 +381,7 @@ int phy_unregister_fixup(const char *bus_id, u32 phy_uid, u32 phy_uid_mask)
fixup = list_entry(pos, struct phy_fixup, list);
if ((!strcmp(fixup->bus_id, bus_id)) &&
- ((fixup->phy_uid & phy_uid_mask) ==
- (phy_uid & phy_uid_mask))) {
+ phy_id_compare(fixup->phy_uid, phy_uid, phy_uid_mask)) {
list_del(&fixup->list);
kfree(fixup);
ret = 0;
@@ -418,8 +417,8 @@ static int phy_needs_fixup(struct phy_device *phydev, struct phy_fixup *fixup)
if (strcmp(fixup->bus_id, PHY_ANY_ID) != 0)
return 0;
- if ((fixup->phy_uid & fixup->phy_uid_mask) !=
- (phydev->phy_id & fixup->phy_uid_mask))
+ if (!phy_id_compare(phydev->phy_id, fixup->phy_uid,
+ fixup->phy_uid_mask))
if (fixup->phy_uid != PHY_ANY_UID)
return 0;
@@ -466,15 +465,14 @@ static int phy_bus_match(struct device *dev, struct device_driver *drv)
if (phydev->c45_ids.device_ids[i] == 0xffffffff)
continue;
- if ((phydrv->phy_id & phydrv->phy_id_mask) ==
- (phydev->c45_ids.device_ids[i] &
- phydrv->phy_id_mask))
+ if (phy_id_compare(phydev->c45_ids.device_ids[i],
+ phydrv->phy_id, phydrv->phy_id_mask))
return 1;
}
return 0;
} else {
- return (phydrv->phy_id & phydrv->phy_id_mask) ==
- (phydev->phy_id & phydrv->phy_id_mask);
+ return phy_id_compare(phydev->phy_id, phydrv->phy_id,
+ phydrv->phy_id_mask);
}
}
@@ -2710,6 +2708,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 dc2800beacc3..2ed9b582558b 100644
--- a/drivers/net/phy/phylink.c
+++ b/drivers/net/phy/phylink.c
@@ -75,6 +75,7 @@ struct phylink {
struct sfp_bus *sfp_bus;
bool sfp_may_have_phy;
+ DECLARE_PHY_INTERFACE_MASK(sfp_interfaces);
__ETHTOOL_DECLARE_LINK_MODE_MASK(sfp_support);
u8 sfp_port;
};
@@ -2021,6 +2022,43 @@ static void phylink_sfp_detach(void *upstream, struct sfp_bus *bus)
pl->netdev->sfp_bus = NULL;
}
+static DECLARE_PHY_INTERFACE_MASK(phylink_sfp_interfaces);
+
+static const phy_interface_t phylink_sfp_interface_preference[] = {
+ PHY_INTERFACE_MODE_USXGMII,
+ PHY_INTERFACE_MODE_10GBASER,
+ PHY_INTERFACE_MODE_10GKR,
+ PHY_INTERFACE_MODE_2500BASEX,
+ PHY_INTERFACE_MODE_SGMII,
+ PHY_INTERFACE_MODE_1000BASEX,
+};
+
+static phy_interface_t phylink_select_interface(struct phylink *pl,
+ const unsigned long *intf,
+ const char *intf_name)
+{
+ DECLARE_PHY_INTERFACE_MASK(u);
+ phy_interface_t interface;
+ size_t i;
+
+ phy_interface_and(u, intf, pl->config->supported_interfaces);
+
+ interface = PHY_INTERFACE_MODE_NA;
+ for (i = 0; i < ARRAY_SIZE(phylink_sfp_interface_preference); i++)
+ if (test_bit(phylink_sfp_interface_preference[i], u)) {
+ interface = phylink_sfp_interface_preference[i];
+ break;
+ }
+
+ phylink_info(pl, "interfaces=[mac=%*pbl %s=%*pbl] selected %d (%s)\n",
+ (int)PHY_INTERFACE_MODE_MAX,
+ pl->config->supported_interfaces,
+ intf_name, (int)PHY_INTERFACE_MODE_MAX, intf,
+ interface, phy_modes(interface));
+
+ return interface;
+}
+
static int phylink_sfp_config(struct phylink *pl, u8 mode,
const unsigned long *supported,
const unsigned long *advertising)
@@ -2103,24 +2141,33 @@ static int phylink_sfp_config(struct phylink *pl, u8 mode,
return ret;
}
+static int phylink_sfp_config_nophy(struct phylink *pl)
+{
+ if (!phy_interface_empty(pl->config->supported_interfaces))
+ phylink_select_interface(pl, pl->sfp_interfaces, "sfp");
+
+ return phylink_sfp_config(pl, MLO_AN_INBAND,
+ pl->sfp_support, pl->sfp_support);
+}
+
static int phylink_sfp_module_insert(void *upstream,
const struct sfp_eeprom_id *id)
{
struct phylink *pl = upstream;
- unsigned long *support = pl->sfp_support;
ASSERT_RTNL();
- linkmode_zero(support);
- sfp_parse_support(pl->sfp_bus, id, support);
- pl->sfp_port = sfp_parse_port(pl->sfp_bus, id, support);
+ linkmode_zero(pl->sfp_support);
+ phy_interface_zero(pl->sfp_interfaces);
+ sfp_parse_support(pl->sfp_bus, id, pl->sfp_support, pl->sfp_interfaces);
+ pl->sfp_port = sfp_parse_port(pl->sfp_bus, id, pl->sfp_support);
/* If this module may have a PHY connecting later, defer until later */
pl->sfp_may_have_phy = sfp_may_have_phy(pl->sfp_bus, id);
if (pl->sfp_may_have_phy)
return 0;
- return phylink_sfp_config(pl, MLO_AN_INBAND, support, support);
+ return phylink_sfp_config_nophy(pl);
}
static int phylink_sfp_module_start(void *upstream)
@@ -2139,8 +2186,7 @@ static int phylink_sfp_module_start(void *upstream)
if (!pl->sfp_may_have_phy)
return 0;
- return phylink_sfp_config(pl, MLO_AN_INBAND,
- pl->sfp_support, pl->sfp_support);
+ return phylink_sfp_config_nophy(pl);
}
static void phylink_sfp_module_stop(void *upstream)
@@ -2176,8 +2222,8 @@ static void phylink_sfp_link_up(void *upstream)
*/
static bool phylink_phy_no_inband(struct phy_device *phy)
{
- return phy->is_c45 &&
- (phy->c45_ids.device_ids[1] & 0xfffffff0) == 0xae025150;
+ return phy->is_c45 && phy_id_compare(phy->c45_ids.device_ids[1],
+ 0xae025150, 0xfffffff0);
}
static int phylink_sfp_connect_phy(void *upstream, struct phy_device *phy)
@@ -2201,19 +2247,58 @@ static int phylink_sfp_connect_phy(void *upstream, struct phy_device *phy)
else
mode = MLO_AN_INBAND;
- /* Do the initial configuration */
- ret = phylink_sfp_config(pl, mode, phy->supported, phy->advertising);
- if (ret < 0)
- return ret;
+ /* Set the PHY's host supported interfaces */
+ phy_interface_and(phy->host_interfaces, phylink_sfp_interfaces,
+ pl->config->supported_interfaces);
- interface = pl->link_config.interface;
- ret = phylink_attach_phy(pl, phy, interface);
- if (ret < 0)
- return ret;
+ if (!phy_interface_empty(phy->supported_interfaces) &&
+ !phy_interface_empty(pl->config->supported_interfaces)) {
+ interface = phylink_select_interface(pl,
+ phy->supported_interfaces,
+ "phy");
+ if (interface == PHY_INTERFACE_MODE_NA) {
+ phylink_err(pl,
+ "selection of interface for PHY failed\n");
+ return -EINVAL;
+ }
- ret = phylink_bringup_phy(pl, phy, interface);
- if (ret)
- phy_detach(phy);
+ if (pl->cur_link_an_mode != mode ||
+ pl->link_config.interface != interface) {
+ pl->link_config.interface = interface;
+ pl->cur_link_an_mode = mode;
+
+ phylink_info(pl, "switched to %s/%s link mode\n",
+ phylink_an_mode_str(mode),
+ 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);
+
+ if (!test_bit(PHYLINK_DISABLE_STOPPED,
+ &pl->phylink_disable_state))
+ phylink_mac_initial_config(pl, false);
+ } else {
+ /* Do the initial configuration */
+ ret = phylink_sfp_config(pl, mode, phy->supported,
+ phy->advertising);
+ if (ret < 0)
+ return ret;
+
+ interface = pl->link_config.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;
}
@@ -2584,4 +2669,16 @@ void phylink_mii_c45_pcs_get_state(struct mdio_device *pcs,
}
EXPORT_SYMBOL_GPL(phylink_mii_c45_pcs_get_state);
+static int __init phylink_init(void)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(phylink_sfp_interface_preference); i++)
+ set_bit(phylink_sfp_interface_preference[i],
+ phylink_sfp_interfaces);
+
+ return 0;
+}
+module_init(phylink_init);
+
MODULE_LICENSE("GPL v2");
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-bus.c b/drivers/net/phy/sfp-bus.c
index 2e11176c6b94..5a04ed6737a1 100644
--- a/drivers/net/phy/sfp-bus.c
+++ b/drivers/net/phy/sfp-bus.c
@@ -13,7 +13,8 @@
struct sfp_quirk {
const char *vendor;
const char *part;
- void (*modes)(const struct sfp_eeprom_id *id, unsigned long *modes);
+ void (*modes)(const struct sfp_eeprom_id *id, unsigned long *modes,
+ unsigned long *interfaces);
};
/**
@@ -39,13 +40,15 @@ struct sfp_bus {
};
static void sfp_quirk_2500basex(const struct sfp_eeprom_id *id,
- unsigned long *modes)
+ unsigned long *modes, unsigned long *interfaces)
{
phylink_set(modes, 2500baseX_Full);
+ __set_bit(PHY_INTERFACE_MODE_2500BASEX, interfaces);
}
static void sfp_quirk_ubnt_uf_instant(const struct sfp_eeprom_id *id,
- unsigned long *modes)
+ unsigned long *modes,
+ unsigned long *interfaces)
{
/* Ubiquiti U-Fiber Instant module claims that support all transceiver
* types including 10G Ethernet which is not truth. So clear all claimed
@@ -226,12 +229,14 @@ EXPORT_SYMBOL_GPL(sfp_may_have_phy);
* @bus: a pointer to the &struct sfp_bus structure for the sfp module
* @id: a pointer to the module's &struct sfp_eeprom_id
* @support: pointer to an array of unsigned long for the ethtool support mask
+ * @interfaces: pointer to an array of unsigned long for phy interface modes
+ * mask
*
* Parse the EEPROM identification information and derive the supported
* ethtool link modes for the module.
*/
void sfp_parse_support(struct sfp_bus *bus, const struct sfp_eeprom_id *id,
- unsigned long *support)
+ unsigned long *support, unsigned long *interfaces)
{
unsigned int br_min, br_nom, br_max;
__ETHTOOL_DECLARE_LINK_MODE_MASK(modes) = { 0, };
@@ -258,27 +263,41 @@ void sfp_parse_support(struct sfp_bus *bus, const struct sfp_eeprom_id *id,
}
/* Set ethtool support from the compliance fields. */
- if (id->base.e10g_base_sr)
+ if (id->base.e10g_base_sr) {
phylink_set(modes, 10000baseSR_Full);
- if (id->base.e10g_base_lr)
+ __set_bit(PHY_INTERFACE_MODE_10GBASER, interfaces);
+ }
+ if (id->base.e10g_base_lr) {
phylink_set(modes, 10000baseLR_Full);
- if (id->base.e10g_base_lrm)
+ __set_bit(PHY_INTERFACE_MODE_10GBASER, interfaces);
+ }
+ if (id->base.e10g_base_lrm) {
phylink_set(modes, 10000baseLRM_Full);
- if (id->base.e10g_base_er)
+ __set_bit(PHY_INTERFACE_MODE_10GBASER, interfaces);
+ }
+ if (id->base.e10g_base_er) {
phylink_set(modes, 10000baseER_Full);
+ __set_bit(PHY_INTERFACE_MODE_10GBASER, interfaces);
+ }
if (id->base.e1000_base_sx ||
id->base.e1000_base_lx ||
- id->base.e1000_base_cx)
+ id->base.e1000_base_cx) {
phylink_set(modes, 1000baseX_Full);
+ __set_bit(PHY_INTERFACE_MODE_1000BASEX, interfaces);
+ }
if (id->base.e1000_base_t) {
phylink_set(modes, 1000baseT_Half);
phylink_set(modes, 1000baseT_Full);
+ __set_bit(PHY_INTERFACE_MODE_1000BASEX, interfaces);
+ __set_bit(PHY_INTERFACE_MODE_SGMII, interfaces);
}
/* 1000Base-PX or 1000Base-BX10 */
if ((id->base.e_base_px || id->base.e_base_bx10) &&
- br_min <= 1300 && br_max >= 1200)
+ br_min <= 1300 && br_max >= 1200) {
phylink_set(modes, 1000baseX_Full);
+ __set_bit(PHY_INTERFACE_MODE_1000BASEX, interfaces);
+ }
/* 100Base-FX, 100Base-LX, 100Base-PX, 100Base-BX10 */
if (id->base.e100_base_fx || id->base.e100_base_lx)
@@ -291,21 +310,30 @@ void sfp_parse_support(struct sfp_bus *bus, const struct sfp_eeprom_id *id,
*/
if ((id->base.sfp_ct_passive || id->base.sfp_ct_active) && br_nom) {
/* This may look odd, but some manufacturers use 12000MBd */
- if (br_min <= 12000 && br_max >= 10300)
+ if (br_min <= 12000 && br_max >= 10300) {
phylink_set(modes, 10000baseCR_Full);
- if (br_min <= 3200 && br_max >= 3100)
+ __set_bit(PHY_INTERFACE_MODE_10GBASER, interfaces);
+ }
+ if (br_min <= 3200 && br_max >= 3100) {
phylink_set(modes, 2500baseX_Full);
- if (br_min <= 1300 && br_max >= 1200)
+ __set_bit(PHY_INTERFACE_MODE_2500BASEX, interfaces);
+ }
+ if (br_min <= 1300 && br_max >= 1200) {
phylink_set(modes, 1000baseX_Full);
+ __set_bit(PHY_INTERFACE_MODE_1000BASEX, interfaces);
+ }
}
if (id->base.sfp_ct_passive) {
- if (id->base.passive.sff8431_app_e)
+ if (id->base.passive.sff8431_app_e) {
phylink_set(modes, 10000baseCR_Full);
+ __set_bit(PHY_INTERFACE_MODE_10GBASER, interfaces);
+ }
}
if (id->base.sfp_ct_active) {
if (id->base.active.sff8431_app_e ||
id->base.active.sff8431_lim) {
phylink_set(modes, 10000baseCR_Full);
+ __set_bit(PHY_INTERFACE_MODE_10GBASER, interfaces);
}
}
@@ -330,12 +358,14 @@ void sfp_parse_support(struct sfp_bus *bus, const struct sfp_eeprom_id *id,
case SFF8024_ECC_10GBASE_T_SFI:
case SFF8024_ECC_10GBASE_T_SR:
phylink_set(modes, 10000baseT_Full);
+ __set_bit(PHY_INTERFACE_MODE_10GBASER, interfaces);
break;
case SFF8024_ECC_5GBASE_T:
phylink_set(modes, 5000baseT_Full);
break;
case SFF8024_ECC_2_5GBASE_T:
phylink_set(modes, 2500baseT_Full);
+ __set_bit(PHY_INTERFACE_MODE_2500BASEX, interfaces);
break;
default:
dev_warn(bus->sfp_dev,
@@ -348,10 +378,14 @@ void sfp_parse_support(struct sfp_bus *bus, const struct sfp_eeprom_id *id,
if (id->base.fc_speed_100 ||
id->base.fc_speed_200 ||
id->base.fc_speed_400) {
- if (id->base.br_nominal >= 31)
+ if (id->base.br_nominal >= 31) {
phylink_set(modes, 2500baseX_Full);
- if (id->base.br_nominal >= 12)
+ __set_bit(PHY_INTERFACE_MODE_2500BASEX, interfaces);
+ }
+ if (id->base.br_nominal >= 12) {
phylink_set(modes, 1000baseX_Full);
+ __set_bit(PHY_INTERFACE_MODE_1000BASEX, interfaces);
+ }
}
/* If we haven't discovered any modes that this module supports, try
@@ -364,14 +398,18 @@ void sfp_parse_support(struct sfp_bus *bus, const struct sfp_eeprom_id *id,
* 2500BASE-X, so we allow some slack here.
*/
if (bitmap_empty(modes, __ETHTOOL_LINK_MODE_MASK_NBITS) && br_nom) {
- if (br_min <= 1300 && br_max >= 1200)
+ if (br_min <= 1300 && br_max >= 1200) {
phylink_set(modes, 1000baseX_Full);
- if (br_min <= 3200 && br_max >= 2500)
+ __set_bit(PHY_INTERFACE_MODE_1000BASEX, interfaces);
+ }
+ if (br_min <= 3200 && br_max >= 2500) {
phylink_set(modes, 2500baseX_Full);
+ __set_bit(PHY_INTERFACE_MODE_2500BASEX, interfaces);
+ }
}
if (bus->sfp_quirk)
- bus->sfp_quirk->modes(id, modes);
+ bus->sfp_quirk->modes(id, modes, interfaces);
bitmap_or(support, support, modes, __ETHTOOL_LINK_MODE_MASK_NBITS);
diff --git a/drivers/net/phy/sfp.c b/drivers/net/phy/sfp.c
index 7998acc689b7..e2ad93bc5467 100644
--- a/drivers/net/phy/sfp.c
+++ b/drivers/net/phy/sfp.c
@@ -18,6 +18,7 @@
#include <linux/slab.h>
#include <linux/workqueue.h>
+#include "sff.h"
#include "sfp.h"
#include "swphy.h"
@@ -168,6 +169,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.
@@ -303,6 +305,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);
@@ -1396,6 +1399,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)
{
@@ -1779,6 +1890,110 @@ 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_sm_mod_probe(struct sfp *sfp, bool report)
{
/* SFP module inserted - read I2C data */
@@ -1833,9 +2048,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);
@@ -1895,14 +2110,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)) {
@@ -1925,6 +2135,8 @@ static int sfp_sm_mod_probe(struct sfp *sfp, bool report)
if (!memcmp(id.base.vendor_name, "ALCATELLUCENT ", 16) &&
!memcmp(id.base.vendor_pn, "3FE46541AA ", 16))
sfp->module_t_start_up = T_START_UP_BAD_GPON;
+ else 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;
@@ -2131,10 +2343,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 > T_WAIT)
@@ -2153,8 +2365,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
- * 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 0fe408964334..0e86f484269c 100644
--- a/drivers/phy/marvell/phy-armada38x-comphy.c
+++ b/drivers/phy/marvell/phy-armada38x-comphy.c
@@ -15,32 +15,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];
@@ -88,6 +105,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)
{
@@ -105,6 +123,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.
@@ -113,7 +222,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;
@@ -132,23 +240,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,
};
@@ -193,6 +292,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)
@@ -202,8 +302,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");
@@ -230,16 +338,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/phy.h b/include/linux/phy.h
index 1a12e4436b5b..2db2fa58e93a 100644
--- a/include/linux/phy.h
+++ b/include/linux/phy.h
@@ -151,6 +151,26 @@ typedef enum {
PHY_INTERFACE_MODE_MAX,
} phy_interface_t;
+/* PHY interface mode bitmap handling */
+#define DECLARE_PHY_INTERFACE_MASK(name) \
+ DECLARE_BITMAP(name, PHY_INTERFACE_MODE_MAX)
+
+static inline void phy_interface_zero(unsigned long *intf)
+{
+ bitmap_zero(intf, PHY_INTERFACE_MODE_MAX);
+}
+
+static inline bool phy_interface_empty(const unsigned long *intf)
+{
+ return bitmap_empty(intf, PHY_INTERFACE_MODE_MAX);
+}
+
+static inline void phy_interface_and(unsigned long *dst, const unsigned long *a,
+ const unsigned long *b)
+{
+ bitmap_and(dst, a, b, PHY_INTERFACE_MODE_MAX);
+}
+
/*
* phy_supported_speeds - return all speeds currently supported by a PHY device
*/
@@ -595,6 +615,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);
@@ -603,6 +632,10 @@ struct phy_device {
/* used with phy_speed_down */
__ETHTOOL_DECLARE_LINK_MODE_MASK(adv_old);
+ /* supported PHY interface types */
+ DECLARE_PHY_INTERFACE_MASK(host_interfaces);
+ DECLARE_PHY_INTERFACE_MASK(supported_interfaces);
+
/* Energy efficient ethernet modes which should be prohibited */
u32 eee_broken_modes;
@@ -744,6 +777,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,
@@ -893,6 +929,34 @@ struct phy_driver {
#define PHY_ID_MATCH_MODEL(id) .phy_id = (id), .phy_id_mask = GENMASK(31, 4)
#define PHY_ID_MATCH_VENDOR(id) .phy_id = (id), .phy_id_mask = GENMASK(31, 10)
+/**
+ * phy_id_compare - compare @id1 with @id2 taking account of @mask
+ * @id1: first PHY ID
+ * @id2: second PHY ID
+ * @mask: the PHY ID mask, set bits are significant in matching
+ *
+ * Return true if the bits from @id1 and @id2 specified by @mask match.
+ * This uses an equivalent test to (@id & @mask) == (@phy_id & @mask).
+ */
+static inline bool phy_id_compare(u32 id1, u32 id2, u32 mask)
+{
+ return !((id1 ^ id2) & mask);
+}
+
+/**
+ * phydev_id_compare - compare @id with the PHY's Clause 22 ID
+ * @phydev: the PHY device
+ * @id: the PHY ID to be matched
+ *
+ * Compare the @phydev clause 22 ID with the provided @id and return true or
+ * false depending whether it matches, using the bound driver mask. The
+ * @phydev must be bound to a driver.
+ */
+static inline bool phydev_id_compare(struct phy_device *phydev, u32 id)
+{
+ return phy_id_compare(id, phydev->phy_id, phydev->drv->phy_id_mask);
+}
+
/* A Structure for boards to register fixups with the PHY Lib */
struct phy_fixup {
struct list_head list;
diff --git a/include/linux/phylink.h b/include/linux/phylink.h
index d81a714cfbbd..d2874f815a47 100644
--- a/include/linux/phylink.h
+++ b/include/linux/phylink.h
@@ -74,6 +74,7 @@ struct phylink_config {
bool poll_fixed_state;
void (*get_fixed_state)(struct phylink_config *config,
struct phylink_link_state *state);
+ DECLARE_PHY_INTERFACE_MASK(supported_interfaces);
};
/**
diff --git a/include/linux/sfp.h b/include/linux/sfp.h
index 38893e4dd0f0..2da1a5181779 100644
--- a/include/linux/sfp.h
+++ b/include/linux/sfp.h
@@ -535,7 +535,7 @@ int sfp_parse_port(struct sfp_bus *bus, const struct sfp_eeprom_id *id,
unsigned long *support);
bool sfp_may_have_phy(struct sfp_bus *bus, const struct sfp_eeprom_id *id);
void sfp_parse_support(struct sfp_bus *bus, const struct sfp_eeprom_id *id,
- unsigned long *support);
+ unsigned long *support, unsigned long *interfaces);
phy_interface_t sfp_select_interface(struct sfp_bus *bus,
unsigned long *link_modes);
@@ -565,7 +565,8 @@ static inline bool sfp_may_have_phy(struct sfp_bus *bus,
static inline void sfp_parse_support(struct sfp_bus *bus,
const struct sfp_eeprom_id *id,
- unsigned long *support)
+ unsigned long *support,
+ unsigned long *interfaces)
{
}
diff --git a/include/net/dsa.h b/include/net/dsa.h
index 83a933e563fe..35a9e072fb87 100644
--- a/include/net/dsa.h
+++ b/include/net/dsa.h
@@ -529,6 +529,8 @@ struct dsa_switch_ops {
/*
* PHYLINK integration
*/
+ void (*phylink_get_interfaces)(struct dsa_switch *ds, int port,
+ unsigned long *supported_interfaces);
void (*phylink_validate)(struct dsa_switch *ds, int port,
unsigned long *supported,
struct phylink_link_state *state);
diff --git a/net/dsa/slave.c b/net/dsa/slave.c
index 992fcab4b552..d2377be80591 100644
--- a/net/dsa/slave.c
+++ b/net/dsa/slave.c
@@ -1746,6 +1746,10 @@ static int dsa_slave_phy_setup(struct net_device *slave_dev)
dp->pl_config.poll_fixed_state = true;
}
+ if (ds->ops->phylink_get_interfaces)
+ ds->ops->phylink_get_interfaces(ds, dp->index,
+ dp->pl_config.supported_interfaces);
+
dp->pl = phylink_create(&dp->pl_config, of_fwnode_handle(port_dn), mode,
&dsa_port_phylink_mac_ops);
if (IS_ERR(dp->pl)) {