From 941b2168603e744b740704b03eae436d50bede02 Mon Sep 17 00:00:00 2001 From: Russell King Date: Tue, 3 Dec 2019 17:45:35 +0000 Subject: net: sfp: add debugfs support Add debugfs support to SFP so that the internal state of the SFP state machines and hardware signal state can be viewed from userspace, rather than having to compile a debug kernel to view state state transitions in the kernel log. The 'state' output looks like: Module state: empty Module probe attempts: 0 0 Device state: up Main state: down Fault recovery remaining retries: 5 PHY probe remaining retries: 12 moddef0: 0 rx_los: 1 tx_fault: 1 tx_disable: 1 Signed-off-by: Russell King --- drivers/net/phy/sfp.c | 55 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/drivers/net/phy/sfp.c b/drivers/net/phy/sfp.c index 91d74c1a920a..374351de2063 100644 --- a/drivers/net/phy/sfp.c +++ b/drivers/net/phy/sfp.c @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-2.0 #include #include +#include #include #include #include @@ -258,6 +259,9 @@ struct sfp { char *hwmon_name; #endif +#if IS_ENABLED(CONFIG_DEBUG_FS) + struct dentry *debugfs_dir; +#endif }; static bool sff_module_supported(const struct sfp_eeprom_id *id) @@ -1390,6 +1394,54 @@ static void sfp_module_tx_enable(struct sfp *sfp) sfp_set_state(sfp, sfp->state); } +#if IS_ENABLED(CONFIG_DEBUG_FS) +static int sfp_debug_state_show(struct seq_file *s, void *data) +{ + struct sfp *sfp = s->private; + + seq_printf(s, "Module state: %s\n", + mod_state_to_str(sfp->sm_mod_state)); + seq_printf(s, "Module probe attempts: %d %d\n", + R_PROBE_RETRY_INIT - sfp->sm_mod_tries_init, + R_PROBE_RETRY_SLOW - sfp->sm_mod_tries); + seq_printf(s, "Device state: %s\n", + dev_state_to_str(sfp->sm_dev_state)); + seq_printf(s, "Main state: %s\n", + sm_state_to_str(sfp->sm_state)); + seq_printf(s, "Fault recovery remaining retries: %d\n", + sfp->sm_fault_retries); + seq_printf(s, "PHY probe remaining retries: %d\n", + sfp->sm_phy_retries); + seq_printf(s, "moddef0: %d\n", !!(sfp->state & SFP_F_PRESENT)); + seq_printf(s, "rx_los: %d\n", !!(sfp->state & SFP_F_LOS)); + seq_printf(s, "tx_fault: %d\n", !!(sfp->state & SFP_F_TX_FAULT)); + seq_printf(s, "tx_disable: %d\n", !!(sfp->state & SFP_F_TX_DISABLE)); + return 0; +} +DEFINE_SHOW_ATTRIBUTE(sfp_debug_state); + +static void sfp_debugfs_init(struct sfp *sfp) +{ + sfp->debugfs_dir = debugfs_create_dir(dev_name(sfp->dev), NULL); + + debugfs_create_file("state", 0600, sfp->debugfs_dir, sfp, + &sfp_debug_state_fops); +} + +static void sfp_debugfs_exit(struct sfp *sfp) +{ + debugfs_remove_recursive(sfp->debugfs_dir); +} +#else +static void sfp_debugfs_init(struct sfp *sfp) +{ +} + +static void sfp_debugfs_exit(struct sfp *sfp) +{ +} +#endif + static void sfp_module_tx_fault_reset(struct sfp *sfp) { unsigned int state = sfp->state; @@ -2483,6 +2535,8 @@ static int sfp_probe(struct platform_device *pdev) if (!sfp->sfp_bus) return -ENOMEM; + sfp_debugfs_init(sfp); + return 0; } @@ -2490,6 +2544,7 @@ static int sfp_remove(struct platform_device *pdev) { struct sfp *sfp = platform_get_drvdata(pdev); + sfp_debugfs_exit(sfp); sfp_unregister_socket(sfp->sfp_bus); rtnl_lock(); -- cgit From 26de4503733b81d3db871c5818c3c566961a3baf Mon Sep 17 00:00:00 2001 From: Russell King Date: Fri, 18 Dec 2020 13:32:43 +0000 Subject: net: sfp: cope with SFPs that set both LOS normal and LOS inverted The SFP MSA defines two option bits in byte 65 to indicate how the Rx_LOS signal on SFP pin 8 behaves: bit 2 - Loss of Signal implemented, signal inverted from standard definition in SFP MSA (often called "Signal Detect"). bit 1 - Loss of Signal implemented, signal as defined in SFP MSA (often called "Rx_LOS"). Clearly, setting both bits results in a meaningless situation: it would mean that LOS is implemented in both the normal sense (1 = signal loss) and inverted sense (0 = signal loss). Unfortunately, there are modules out there which set both bits, which will be initially interpret as "inverted" sense, and then, if the LOS signal changes state, we will toggle between LINK_UP and WAIT_LOS states. Change our LOS handling to give well defined behaviour: only interpret these bits as meaningful if exactly one is set, otherwise treat it as if LOS is not implemented. Signed-off-by: Russell King --- drivers/net/phy/sfp.c | 36 ++++++++++++++++++++++-------------- 1 file changed, 22 insertions(+), 14 deletions(-) diff --git a/drivers/net/phy/sfp.c b/drivers/net/phy/sfp.c index 374351de2063..b2a5ed6915fa 100644 --- a/drivers/net/phy/sfp.c +++ b/drivers/net/phy/sfp.c @@ -1534,15 +1534,19 @@ static void sfp_sm_link_down(struct sfp *sfp) static void sfp_sm_link_check_los(struct sfp *sfp) { - unsigned int los = sfp->state & SFP_F_LOS; + const __be16 los_inverted = cpu_to_be16(SFP_OPTIONS_LOS_INVERTED); + const __be16 los_normal = cpu_to_be16(SFP_OPTIONS_LOS_NORMAL); + __be16 los_options = sfp->id.ext.options & (los_inverted | los_normal); + bool los = false; /* If neither SFP_OPTIONS_LOS_INVERTED nor SFP_OPTIONS_LOS_NORMAL - * are set, we assume that no LOS signal is available. + * are set, we assume that no LOS signal is available. If both are + * set, we assume LOS is not implemented (and is meaningless.) */ - if (sfp->id.ext.options & cpu_to_be16(SFP_OPTIONS_LOS_INVERTED)) - los ^= SFP_F_LOS; - else if (!(sfp->id.ext.options & cpu_to_be16(SFP_OPTIONS_LOS_NORMAL))) - los = 0; + if (los_options == los_inverted) + los = !(sfp->state & SFP_F_LOS); + else if (los_options == los_normal) + los = !!(sfp->state & SFP_F_LOS); if (los) sfp_sm_next(sfp, SFP_S_WAIT_LOS, 0); @@ -1552,18 +1556,22 @@ static void sfp_sm_link_check_los(struct sfp *sfp) static bool sfp_los_event_active(struct sfp *sfp, unsigned int event) { - return (sfp->id.ext.options & cpu_to_be16(SFP_OPTIONS_LOS_INVERTED) && - event == SFP_E_LOS_LOW) || - (sfp->id.ext.options & cpu_to_be16(SFP_OPTIONS_LOS_NORMAL) && - event == SFP_E_LOS_HIGH); + const __be16 los_inverted = cpu_to_be16(SFP_OPTIONS_LOS_INVERTED); + const __be16 los_normal = cpu_to_be16(SFP_OPTIONS_LOS_NORMAL); + __be16 los_options = sfp->id.ext.options & (los_inverted | los_normal); + + return (los_options == los_inverted && event == SFP_E_LOS_LOW) || + (los_options == los_normal && event == SFP_E_LOS_HIGH); } static bool sfp_los_event_inactive(struct sfp *sfp, unsigned int event) { - return (sfp->id.ext.options & cpu_to_be16(SFP_OPTIONS_LOS_INVERTED) && - event == SFP_E_LOS_HIGH) || - (sfp->id.ext.options & cpu_to_be16(SFP_OPTIONS_LOS_NORMAL) && - event == SFP_E_LOS_LOW); + const __be16 los_inverted = cpu_to_be16(SFP_OPTIONS_LOS_INVERTED); + const __be16 los_normal = cpu_to_be16(SFP_OPTIONS_LOS_NORMAL); + __be16 los_options = sfp->id.ext.options & (los_inverted | los_normal); + + return (los_options == los_inverted && event == SFP_E_LOS_HIGH) || + (los_options == los_normal && event == SFP_E_LOS_LOW); } static void sfp_sm_fault(struct sfp *sfp, unsigned int next_state, bool warn) -- cgit From 9b835657827c94be64ed2101717a31ae668aaf5b Mon Sep 17 00:00:00 2001 From: Russell King Date: Mon, 7 Dec 2020 15:23:59 +0000 Subject: net: sfp: extend bitrate-derived mode for 2500BASE-X Extend the bitrate-derived support to include 2500BASE-X for modules that report a bitrate of 2500Mbaud. Signed-off-by: Russell King --- drivers/net/phy/sfp-bus.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/drivers/net/phy/sfp-bus.c b/drivers/net/phy/sfp-bus.c index 20b91f5dfc6e..cdfa0a190962 100644 --- a/drivers/net/phy/sfp-bus.c +++ b/drivers/net/phy/sfp-bus.c @@ -337,11 +337,16 @@ void sfp_parse_support(struct sfp_bus *bus, const struct sfp_eeprom_id *id, * the bitrate to determine supported modes. Some BiDi modules (eg, * 1310nm/1550nm) are not 1000BASE-BX compliant due to the differing * wavelengths, so do not set any transceiver bits. + * + * Do the same for modules supporting 2500BASE-X. Note that some + * modules use 2500Mbaud rather than 3100 or 3200Mbaud for + * 2500BASE-X, so we allow some slack here. */ - if (bitmap_empty(modes, __ETHTOOL_LINK_MODE_MASK_NBITS)) { - /* If the bit rate allows 1000baseX */ - if (br_nom && br_min <= 1300 && br_max >= 1200) + if (bitmap_empty(modes, __ETHTOOL_LINK_MODE_MASK_NBITS) && br_nom) { + if (br_min <= 1300 && br_max >= 1200) phylink_set(modes, 1000baseX_Full); + if (br_min <= 3200 && br_max >= 2500) + phylink_set(modes, 2500baseX_Full); } if (bus->sfp_quirk) -- cgit From b9d3882e6b0339bbf41d73961b3de8f989e29f65 Mon Sep 17 00:00:00 2001 From: Russell King Date: Sun, 10 Jan 2021 14:48:13 +0000 Subject: net: phy: at803x: use phy_modify_mmd() Convert at803x_clk_out_config() to use phy_modify_mmd(). Signed-off-by: Russell King --- drivers/net/phy/at803x.c | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/drivers/net/phy/at803x.c b/drivers/net/phy/at803x.c index d0b36fd6c265..9636edb8d618 100644 --- a/drivers/net/phy/at803x.c +++ b/drivers/net/phy/at803x.c @@ -529,19 +529,12 @@ static void at803x_remove(struct phy_device *phydev) static int at803x_clk_out_config(struct phy_device *phydev) { struct at803x_priv *priv = phydev->priv; - int val; if (!priv->clk_25m_mask) return 0; - val = phy_read_mmd(phydev, MDIO_MMD_AN, AT803X_MMD7_CLK25M); - if (val < 0) - return val; - - val &= ~priv->clk_25m_mask; - val |= priv->clk_25m_reg; - - return phy_write_mmd(phydev, MDIO_MMD_AN, AT803X_MMD7_CLK25M, val); + return phy_modify_mmd(phydev, MDIO_MMD_AN, AT803X_MMD7_CLK25M, + priv->clk_25m_mask, priv->clk_25m_reg); } static int at8031_pll_config(struct phy_device *phydev) -- cgit From 18b2f5fc769ae6c0aa50ee9f3d29d502169e082b Mon Sep 17 00:00:00 2001 From: Russell King Date: Sun, 10 Jan 2021 16:14:45 +0000 Subject: net: phy: ar803x: disable extended next page bit This bit is enabled by default and advertises support for extended next page support. XNP is only needed for 10GBase-T and MultiGig support which is not supported. Additionally, Cisco MultiGig switches will read this bit and attempt 10Gb negotiation even though Next Page support is disabled. This will cause timeouts when the interface is forced to 100Mbps and auto-negotiation will fail. The interfaces are only 1000Base-T and supporting auto-negotiation for this only requires the Next Page bit to be set. Taken from: https://github.com/SolidRun/linux-stable/commit/7406c5244b7ea6bc17a2afe8568277a8c4b126a9 and adapted to mainline kernels by rmk. Signed-off-by: Russell King --- drivers/net/phy/at803x.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/drivers/net/phy/at803x.c b/drivers/net/phy/at803x.c index 9636edb8d618..3909dc9fc94b 100644 --- a/drivers/net/phy/at803x.c +++ b/drivers/net/phy/at803x.c @@ -587,7 +587,13 @@ static int at803x_config_init(struct phy_device *phydev) return ret; } - return 0; + /* Ar803x extended next page bit is enabled by default. Cisco + * multigig switches read this bit and attempt to negotiate 10Gbps + * rates even if the next page bit is disabled. This is incorrect + * behaviour but we still need to accomodate it. XNP is only needed + * for 10Gbps support, so disable XNP. + */ + return phy_modify(phydev, MII_ADVERTISE, MDIO_AN_CTRL1_XNP, 0); } static int at803x_ack_interrupt(struct phy_device *phydev) -- cgit From a0fe82474adc1fd2e92fa9144b65ee1b84bd2aee Mon Sep 17 00:00:00 2001 From: Russell King Date: Thu, 14 Jan 2021 10:12:34 +0000 Subject: dt: ar803x: document SmartEEE properties The SmartEEE feature of Atheros AR803x PHYs can cause the link to bounce. Add DT properties to allow SmartEEE to be disabled, and to allow the Tw parameters for 100M and 1G links to be configured. Signed-off-by: Russell King --- Documentation/devicetree/bindings/net/qca,ar803x.yaml | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/Documentation/devicetree/bindings/net/qca,ar803x.yaml b/Documentation/devicetree/bindings/net/qca,ar803x.yaml index 64b3357ade8a..b3d4013b7ca6 100644 --- a/Documentation/devicetree/bindings/net/qca,ar803x.yaml +++ b/Documentation/devicetree/bindings/net/qca,ar803x.yaml @@ -28,6 +28,10 @@ properties: $ref: /schemas/types.yaml#/definitions/uint32 enum: [0, 1, 2] + qca,disable-smarteee: + description: Disable Atheros SmartEEE feature. + type: boolean + qca,keep-pll-enabled: description: | If set, keep the PLL enabled even if there is no link. Useful if you @@ -36,6 +40,18 @@ properties: Only supported on the AR8031. type: boolean + qca,smarteee-tw-us-100m: + description: EEE Tw parameter for 100M links. + $ref: /schemas/types.yaml#/definitions/uint32 + minimum: 1 + maximum: 255 + + qca,smarteee-tw-us-1g: + description: EEE Tw parameter for gigabit links. + $ref: /schemas/types.yaml#/definitions/uint32 + minimum: 1 + maximum: 255 + vddio-supply: description: | RGMII I/O voltage regulator (see regulator/regulator.yaml). -- cgit From ab4b71454925a1bcdefe2a50b968a127faf5b45d Mon Sep 17 00:00:00 2001 From: Russell King Date: Thu, 14 Jan 2021 10:12:34 +0000 Subject: net: phy: at803x: add support for configuring SmartEEE SmartEEE for the atheros phy was deemed buggy by Freescale and commits were added to disable it for their boards. In initial testing, SolidRun found that the default settings were causing disconnects but by increasing the Tw buffer time we could allow enough time for all parts of the link to come out of a low power state and function properly without causing a disconnect. This allows us to have functional power savings of between 300 and 400mW, rather than disabling the feature altogether. This commit adds support for disabling SmartEEE and configuring the Tw parameters for 1G and 100M speeds. Signed-off-by: Russell King --- drivers/net/phy/at803x.c | 65 +++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 64 insertions(+), 1 deletion(-) diff --git a/drivers/net/phy/at803x.c b/drivers/net/phy/at803x.c index 3909dc9fc94b..104dd7b37c5d 100644 --- a/drivers/net/phy/at803x.c +++ b/drivers/net/phy/at803x.c @@ -132,6 +132,11 @@ #define AT803X_MIN_DOWNSHIFT 2 #define AT803X_MAX_DOWNSHIFT 9 +#define AT803X_MMD3_SMARTEEE_CTL1 0x805b +#define AT803X_MMD3_SMARTEEE_CTL2 0x805c +#define AT803X_MMD3_SMARTEEE_CTL3 0x805d +#define AT803X_MMD3_SMARTEEE_CTL3_LPI_EN BIT(8) + #define ATH9331_PHY_ID 0x004dd041 #define ATH8030_PHY_ID 0x004dd076 #define ATH8031_PHY_ID 0x004dd074 @@ -146,8 +151,11 @@ MODULE_LICENSE("GPL"); struct at803x_priv { int flags; #define AT803X_KEEP_PLL_ENABLED BIT(0) /* don't turn off internal PLL */ +#define AT803X_DISABLE_SMARTEEE BIT(1) u16 clk_25m_reg; u16 clk_25m_mask; + u8 smarteee_lpi_tw_1g; + u8 smarteee_lpi_tw_100m; struct regulator_dev *vddio_rdev; struct regulator_dev *vddh_rdev; struct regulator *vddio; @@ -411,13 +419,32 @@ static int at803x_parse_dt(struct phy_device *phydev) { struct device_node *node = phydev->mdio.dev.of_node; struct at803x_priv *priv = phydev->priv; - u32 freq, strength; + u32 freq, strength, tw; unsigned int sel; int ret; if (!IS_ENABLED(CONFIG_OF_MDIO)) return 0; + if (of_property_read_bool(node, "qca,disable-smarteee")) + priv->flags |= AT803X_DISABLE_SMARTEEE; + + if (!of_property_read_u32(node, "qca,smarteee-tw-us-1g", &tw)) { + if (!tw || tw > 255) { + phydev_err(phydev, "invalid qca,smarteee-tw-us-1g\n"); + return -EINVAL; + } + priv->smarteee_lpi_tw_1g = tw; + } + + if (!of_property_read_u32(node, "qca,smarteee-tw-us-100m", &tw)) { + if (!tw || tw > 255) { + phydev_err(phydev, "invalid qca,smarteee-tw-us-100m\n"); + return -EINVAL; + } + priv->smarteee_lpi_tw_100m = tw; + } + ret = of_property_read_u32(node, "qca,clk-out-frequency", &freq); if (!ret) { switch (freq) { @@ -526,6 +553,38 @@ static void at803x_remove(struct phy_device *phydev) regulator_disable(priv->vddio); } +static int at803x_smarteee_config(struct phy_device *phydev) +{ + struct at803x_priv *priv = phydev->priv; + u16 mask = 0, val = 0; + int ret; + + if (priv->flags & AT803X_DISABLE_SMARTEEE) + return phy_modify_mmd(phydev, MDIO_MMD_PCS, + AT803X_MMD3_SMARTEEE_CTL3, + AT803X_MMD3_SMARTEEE_CTL3_LPI_EN, 0); + + if (priv->smarteee_lpi_tw_1g) { + mask |= 0xff00; + val |= priv->smarteee_lpi_tw_1g << 8; + } + if (priv->smarteee_lpi_tw_100m) { + mask |= 0x00ff; + val |= priv->smarteee_lpi_tw_100m; + } + if (!mask) + return 0; + + ret = phy_modify_mmd(phydev, MDIO_MMD_PCS, AT803X_MMD3_SMARTEEE_CTL1, + mask, val); + if (ret) + return ret; + + return phy_modify_mmd(phydev, MDIO_MMD_PCS, AT803X_MMD3_SMARTEEE_CTL3, + AT803X_MMD3_SMARTEEE_CTL3_LPI_EN, + AT803X_MMD3_SMARTEEE_CTL3_LPI_EN); +} + static int at803x_clk_out_config(struct phy_device *phydev) { struct at803x_priv *priv = phydev->priv; @@ -577,6 +636,10 @@ static int at803x_config_init(struct phy_device *phydev) if (ret < 0) return ret; + ret = at803x_smarteee_config(phydev); + if (ret < 0) + return ret; + ret = at803x_clk_out_config(phydev); if (ret < 0) return ret; -- cgit From 0e9a41219f9918269af92f7d390a4a21f8cbcadd Mon Sep 17 00:00:00 2001 From: Russell King Date: Thu, 14 Jan 2021 10:12:34 +0000 Subject: arm64: dts: lx2160a-cex7: increase at8035 PHY gigabit Tw parameter Increase the SmartEEE Tw parameter for Atheros PHYs to stop gigabit links from sporadically dropping. Testing on this platform shows that a value of 24 results in a stable link, whereas 23 or below has the occasional drop. Tested with a Netgear GS116 unmanaged switch link partner with Cat 5e cabling. Signed-off-by: Russell King --- arch/arm64/boot/dts/freescale/fsl-lx2160a-cex7.dtsi | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/arm64/boot/dts/freescale/fsl-lx2160a-cex7.dtsi b/arch/arm64/boot/dts/freescale/fsl-lx2160a-cex7.dtsi index d87d16460875..6c2eb2583434 100644 --- a/arch/arm64/boot/dts/freescale/fsl-lx2160a-cex7.dtsi +++ b/arch/arm64/boot/dts/freescale/fsl-lx2160a-cex7.dtsi @@ -40,6 +40,7 @@ rgmii_phy1: ethernet-phy@1 { reg = <1>; + qca,smarteee-tw-us-1g = <24>; }; }; -- cgit From c7ee87840ea496a194ddba991ee7871388fe5531 Mon Sep 17 00:00:00 2001 From: Russell King Date: Thu, 14 Jan 2021 10:12:35 +0000 Subject: ARM: dts: imx6qdl-sr-som: increase at8035 PHY gigabit Tw parameter Increase the SmartEEE Tw parameter for Atheros PHYs to stop gigabit links from sporadically dropping. Jon Nettleton found that a value of 23 was the minimum to give a stable link, but testing with the Com Express 7 module shows 24 is the minimum - so 23 may still be on the margins. Use 24 instead for consistency across SolidRun platforms. Signed-off-by: Russell King --- arch/arm/boot/dts/imx6qdl-sr-som.dtsi | 2 ++ 1 file changed, 2 insertions(+) diff --git a/arch/arm/boot/dts/imx6qdl-sr-som.dtsi b/arch/arm/boot/dts/imx6qdl-sr-som.dtsi index 7e4e5fd0143a..0ad8ccde0cf8 100644 --- a/arch/arm/boot/dts/imx6qdl-sr-som.dtsi +++ b/arch/arm/boot/dts/imx6qdl-sr-som.dtsi @@ -69,11 +69,13 @@ ethernet-phy@0 { reg = <0>; qca,clk-out-frequency = <125000000>; + qca,smarteee-tw-us-1g = <24>; }; ethernet-phy@4 { reg = <4>; qca,clk-out-frequency = <125000000>; + qca,smarteee-tw-us-1g = <24>; }; }; }; -- cgit From e12d8795d7d66adc30f22ac83908f6f6f44e2c3a Mon Sep 17 00:00:00 2001 From: Russell King Date: Tue, 3 Mar 2020 11:57:39 +0000 Subject: net: sfp: add interface modes bitmap We currently parse the SFP EEPROM to a bitmap of ethtool link modes, and then attempt to convert the link modes to a PHY interface mode. While this works at present, there are cases where this is sub-optimal. For example, where a module can operate with several different PHY interface modes. To start addressing this, arrange for the SFP EEPROM parsing to also provide a bitmap of the possible PHY interface modes. Signed-off-by: Russell King --- drivers/net/phy/marvell10g.c | 3 +- drivers/net/phy/phylink.c | 4 ++- drivers/net/phy/sfp-bus.c | 73 +++++++++++++++++++++++++++++++++----------- include/linux/sfp.h | 5 +-- 4 files changed, 63 insertions(+), 22 deletions(-) diff --git a/drivers/net/phy/marvell10g.c b/drivers/net/phy/marvell10g.c index 1901ba277413..b5a056bb003a 100644 --- a/drivers/net/phy/marvell10g.c +++ b/drivers/net/phy/marvell10g.c @@ -343,9 +343,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) { diff --git a/drivers/net/phy/phylink.c b/drivers/net/phy/phylink.c index 84f6e197f965..56bb6a00582d 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; }; @@ -2108,7 +2109,8 @@ static int phylink_sfp_module_insert(void *upstream, ASSERT_RTNL(); linkmode_zero(support); - sfp_parse_support(pl->sfp_bus, id, support); + phy_interface_zero(pl->sfp_interfaces); + sfp_parse_support(pl->sfp_bus, id, support, pl->sfp_interfaces); pl->sfp_port = sfp_parse_port(pl->sfp_bus, id, support); /* If this module may have a PHY connecting later, defer until later */ diff --git a/drivers/net/phy/sfp-bus.c b/drivers/net/phy/sfp-bus.c index cdfa0a190962..ef034bf1b995 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,9 +40,10 @@ 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 const struct sfp_quirk sfp_quirks[] = { @@ -211,12 +213,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, }; @@ -243,48 +247,71 @@ 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); + } /* For active or passive cables, select the link modes * based on the bit rates and the cable compliance bytes. */ 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); } } @@ -309,12 +336,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, @@ -327,10 +356,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 @@ -343,14 +376,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/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) { } -- cgit From f0a14a14f64e98f9769dba182b5cd976e900fa9f Mon Sep 17 00:00:00 2001 From: Russell King Date: Tue, 3 Mar 2020 12:12:43 +0000 Subject: net: phylink: add mac PHY interface bitmap Add a PHY interface bitmap for the MAC driver to specify which PHY interfaces are supported to phylink. Signed-off-by: Russell King --- include/linux/phylink.h | 1 + 1 file changed, 1 insertion(+) 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); }; /** -- cgit From d7ea9e27073c710dd69baa6c0c212c436b42510a Mon Sep 17 00:00:00 2001 From: Russell King Date: Tue, 3 Mar 2020 12:12:43 +0000 Subject: net: phylink: use phy interface mode bitmaps for optical modules Where a MAC provides the PHY interface mode capabilities, use the PHY interface mode bitmaps to select the operating interface mode for optical SFP modules, rather than using the linkmode bitmaps. Signed-off-by: Russell King --- drivers/net/phy/phylink.c | 56 +++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 49 insertions(+), 7 deletions(-) diff --git a/drivers/net/phy/phylink.c b/drivers/net/phy/phylink.c index 56bb6a00582d..23e5b9042ae0 100644 --- a/drivers/net/phy/phylink.c +++ b/drivers/net/phy/phylink.c @@ -2018,6 +2018,41 @@ static void phylink_sfp_detach(void *upstream, struct sfp_bus *bus) pl->netdev->sfp_bus = NULL; } +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) @@ -2100,25 +2135,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); + linkmode_zero(pl->sfp_support); phy_interface_zero(pl->sfp_interfaces); - sfp_parse_support(pl->sfp_bus, id, support, pl->sfp_interfaces); - pl->sfp_port = sfp_parse_port(pl->sfp_bus, id, support); + 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) @@ -2137,8 +2180,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) -- cgit From 9e03ae68e17cd60e51dd1e5f01c613febcf8ebc6 Mon Sep 17 00:00:00 2001 From: Russell King Date: Tue, 3 Mar 2020 12:15:38 +0000 Subject: net: mvneta: fill in phy interface mode bitmap Fill in the phy interface mode bitmap for the Marvell mvneta driver, so phylink can know which interfaces are supported by the MAC. Signed-off-by: Russell King --- drivers/net/ethernet/marvell/mvneta.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/drivers/net/ethernet/marvell/mvneta.c b/drivers/net/ethernet/marvell/mvneta.c index bc4d8d144401..3585cd30a95f 100644 --- a/drivers/net/ethernet/marvell/mvneta.c +++ b/drivers/net/ethernet/marvell/mvneta.c @@ -5114,6 +5114,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); -- cgit From 8801d251db192cf7a2d08f379e0f40b98aae7370 Mon Sep 17 00:00:00 2001 From: Russell King Date: Tue, 3 Mar 2020 12:15:38 +0000 Subject: net: mvpp2: fill in phy interface mode bitmap Fill in the phy interface mode bitmap for the Marvell mvpp2 driver, so phylink can know which interfaces are supported by the MAC. Signed-off-by: Russell King --- drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c b/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c index 358119d98358..2171c30a0e90 100644 --- a/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c +++ b/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c @@ -6501,6 +6501,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)) { -- cgit From 59c428e36d2326b535a4b3fc86eb354c596fb50d Mon Sep 17 00:00:00 2001 From: Marek Behún Date: Tue, 20 Oct 2020 19:09:11 +0200 Subject: net: dsa: fill phylink's config supported_interfaces member MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a new DSA switch operation, phylink_get_interfaces, which should fill in which PHY_INTERFACE_MODE_* are supported by given port. Use this before phylink_create to fill phylink's config supported_interfaces member. This allows for phylink to determine which PHY_INTERFACE_MODE to use with SFP modules. Signed-off-by: Marek Behún Signed-off-by: Russell King --- include/net/dsa.h | 2 ++ net/dsa/slave.c | 4 ++++ 2 files changed, 6 insertions(+) diff --git a/include/net/dsa.h b/include/net/dsa.h index 4e60d2610f20..090187ea0114 100644 --- a/include/net/dsa.h +++ b/include/net/dsa.h @@ -456,6 +456,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 4a0498bf6c65..2c8a833467e8 100644 --- a/net/dsa/slave.c +++ b/net/dsa/slave.c @@ -1694,6 +1694,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)) { -- cgit From 28ef56cfa3ce93c75ce57a336d83896037df2784 Mon Sep 17 00:00:00 2001 From: Marek Behún Date: Tue, 20 Oct 2020 19:09:12 +0200 Subject: net: dsa: mv88e6xxx: implement .phylink_get_interfaces operation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Implement the .phylink_get_interfaces method for mv88e6xxx driver. We are currently only interested in SGMII, 1000base-x and 2500base-x modes (for the SFP code). USXGMII and 10gbase-r can be added later for Amethyst. XAUI and RXAUI are irrelevant for SFP (but maybe not for QSFP?). Signed-off-by: Marek Behún Signed-off-by: Russell King --- drivers/net/dsa/mv88e6xxx/chip.c | 57 ++++++++++++++++++++++++++++++++++++++++ drivers/net/dsa/mv88e6xxx/chip.h | 2 ++ 2 files changed, 59 insertions(+) diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c index 54aa942eedaa..c8074443110f 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) @@ -3457,6 +3501,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, }; @@ -3629,6 +3674,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, }; @@ -3727,6 +3773,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, }; @@ -3826,6 +3873,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, }; @@ -3886,6 +3934,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, }; @@ -3945,6 +3994,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, }; @@ -4004,6 +4054,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, }; @@ -4104,6 +4155,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, }; @@ -4244,6 +4296,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, }; @@ -4390,6 +4443,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, }; @@ -4454,6 +4508,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, }; @@ -4517,6 +4572,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, }; @@ -5383,6 +5439,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 3543055bcb51..6f513778ed26 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.h +++ b/drivers/net/dsa/mv88e6xxx/chip.h @@ -571,6 +571,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); -- cgit From 0213e77a4035bfc814251f975cd995aef237723d Mon Sep 17 00:00:00 2001 From: Russell King Date: Fri, 13 Mar 2020 10:48:32 +0000 Subject: net: mtk_eth_soc: use resolved link config for PCS PHY The SGMII PCS PHY needs to be updated with the link configuration in the mac_link_up() call rather than in mac_config(). However, mtk_sgmii_setup_mode_force() programs the SGMII block during mac_config() when using 802.3z interface modes with the link configuration. Split that functionality from mtk_sgmii_setup_mode_force(), moving it to a new mtk_sgmii_link_up() function, and call it from mac_link_up(). This does not look correct to me: 802.3z modes operate at a fixed speed. The contents of mtk_sgmii_link_up() look more appropriate for SGMII mode, but the original code definitely did not call mtk_sgmii_setup_mode_force() for SGMII mode but only 802.3z mode. Signed-off-by: Russell King --- drivers/net/ethernet/mediatek/mtk_eth_soc.c | 11 ++++++++- drivers/net/ethernet/mediatek/mtk_eth_soc.h | 3 ++- drivers/net/ethernet/mediatek/mtk_sgmii.c | 37 ++++++++++++++++++++--------- 3 files changed, 38 insertions(+), 13 deletions(-) diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.c b/drivers/net/ethernet/mediatek/mtk_eth_soc.c index 6d2d60675ffd..ab180f79a82c 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 454cfcd465fd..6f4b99bb7bfb 100644 --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h @@ -932,7 +932,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) -- cgit From 77ec13486d8ea3169ee19d1cf2c17283642b22f0 Mon Sep 17 00:00:00 2001 From: Russell King Date: Sun, 21 Jun 2020 14:30:20 +0100 Subject: net: phy: at803x: simplify custom phy id matching The at803x driver contains a function, at803x_match_phy_id(), which tests whether the PHY ID matches the value passed, comparing phy_id with phydev->phy_id and testing all bits that have a "one" in the mask, phydev->drv->phy_id_mask. This is the same test that is used to match the driver, with phy_id replaced with the driver specified ID, phydev->drv->phy_id. Hence, we already know the value of the bits being tested if we look at phydev->drv->phy_id directly, and we do not require a complicated test to check them. Test directly against phydev->drv->phy_id instead. Signed-off-by: Russell King --- drivers/net/phy/at803x.c | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/drivers/net/phy/at803x.c b/drivers/net/phy/at803x.c index 104dd7b37c5d..c48b9f7630b4 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; -- cgit From 236f4a3a172378f7c8b81c7c35203464c981241e Mon Sep 17 00:00:00 2001 From: Russell King Date: Fri, 29 Nov 2019 00:31:03 +0000 Subject: net: phy: add helpers for comparing phy IDs There are several places which open code comparing PHY IDs. Provide a couple of helpers to assist with this, using a slightly simpler test than the original: - phy_id_compare() compares two arbitary PHY IDs and a mask of the significant bits in the ID. - phydev_id_compare() compares the bound phydev with the specified PHY ID, using the bound driver's mask. Signed-off-by: Russell King --- drivers/net/phy/phy_device.c | 16 +++++++--------- drivers/net/phy/phylink.c | 4 ++-- include/linux/phy.h | 28 ++++++++++++++++++++++++++++ 3 files changed, 37 insertions(+), 11 deletions(-) diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c index 80c2e646c093..3f816d5e2668 100644 --- a/drivers/net/phy/phy_device.c +++ b/drivers/net/phy/phy_device.c @@ -411,8 +411,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; @@ -448,8 +447,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; @@ -496,15 +495,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); } } diff --git a/drivers/net/phy/phylink.c b/drivers/net/phy/phylink.c index 23e5b9042ae0..dc23355d46fb 100644 --- a/drivers/net/phy/phylink.c +++ b/drivers/net/phy/phylink.c @@ -2216,8 +2216,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) diff --git a/include/linux/phy.h b/include/linux/phy.h index 9effb511acde..130e633f9ae3 100644 --- a/include/linux/phy.h +++ b/include/linux/phy.h @@ -878,6 +878,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; -- cgit From 6794d13b27d95e5ac054326381266cb7e1f9208b Mon Sep 17 00:00:00 2001 From: Russell King Date: Fri, 29 Nov 2019 21:41:56 +0000 Subject: net: phy: marvell10g: add downshift tunable support Add support for the downshift tunable for the Marvell 88x3310 PHY. Downshift is only usable with firmware 0.3.5.0 and later. Signed-off-by: Russell King --- drivers/net/phy/marvell10g.c | 87 +++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 86 insertions(+), 1 deletion(-) diff --git a/drivers/net/phy/marvell10g.c b/drivers/net/phy/marvell10g.c index b5a056bb003a..7ddd83b49762 100644 --- a/drivers/net/phy/marvell10g.c +++ b/drivers/net/phy/marvell10g.c @@ -22,6 +22,7 @@ * 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 #include #include #include @@ -32,6 +33,8 @@ #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 +55,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, @@ -286,6 +298,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; @@ -482,7 +554,16 @@ static int mv3310_config_init(struct phy_device *phydev) MV_V2_PORT_MAC_TYPE_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 0; } static int mv3310_get_features(struct phy_device *phydev) @@ -746,6 +827,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: @@ -757,6 +840,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: -- cgit From 23c78a7e90555fe2ead8ccdd65a5f5f15b4c8b27 Mon Sep 17 00:00:00 2001 From: Russell King Date: Tue, 17 Dec 2019 15:21:50 +0000 Subject: dt-bindings: net: add dt bindings for marvell10g driver Add a DT bindings document for the Marvell 10G driver, which will augment the generic ethernet PHY binding by having LED mode configuration. Signed-off-by: Russell King --- .../devicetree/bindings/net/marvell,10g.yaml | 35 ++++++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 Documentation/devicetree/bindings/net/marvell,10g.yaml 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 + +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>; + }; + }; -- cgit From fc664191abc16bcbdc78af6f00a5c683290b61cb Mon Sep 17 00:00:00 2001 From: Russell King Date: Sun, 1 Dec 2019 15:33:39 +0000 Subject: net: phy: marvell10g: add support for configuring LEDs Add support for configuring the LEDs. Macchiatobin has an oddity in that the left LED goes out when the cable is connected, and flashes when there's link activity. This is because the reset default for the LED outputs assume that the LED is connected to supply, not to ground. Add support for configuring the LED modes and polarities. Signed-off-by: Russell King --- drivers/net/phy/marvell10g.c | 62 +++++++++++++++++++++++++++++++++++++++----- 1 file changed, 55 insertions(+), 7 deletions(-) diff --git a/drivers/net/phy/marvell10g.c b/drivers/net/phy/marvell10g.c index 7ddd83b49762..5ccbf2e055d8 100644 --- a/drivers/net/phy/marvell10g.c +++ b/drivers/net/phy/marvell10g.c @@ -27,6 +27,7 @@ #include #include #include +#include #include #include @@ -109,6 +110,8 @@ struct mv3310_priv { struct device *hwmon_dev; char *hwmon_name; + u8 num_leds; + u16 led_mode[4]; }; #ifdef CONFIG_HWMON @@ -434,6 +437,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; @@ -444,6 +484,20 @@ static int mv3310_probe(struct phy_device *phydev) (phydev->c45_ids.devices_in_package & mmd_mask) != mmd_mask) return -ENODEV; + priv = devm_kzalloc(&phydev->mdio.dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + dev_set_drvdata(&phydev->mdio.dev, priv); + + ret = mv3310_fw_config(phydev); + if (ret < 0) + return ret; + + ret = mv3310_leds_write(phydev); + if (ret < 0) + return ret; + ret = phy_read_mmd(phydev, MDIO_MMD_PMAPMD, MV_PMA_BOOT); if (ret < 0) return ret; @@ -454,12 +508,6 @@ static int mv3310_probe(struct phy_device *phydev) return -ENODEV; } - priv = devm_kzalloc(&phydev->mdio.dev, sizeof(*priv), GFP_KERNEL); - if (!priv) - return -ENOMEM; - - dev_set_drvdata(&phydev->mdio.dev, priv); - ret = phy_read_mmd(phydev, MDIO_MMD_PMAPMD, MV_PMA_FW_VER0); if (ret < 0) return ret; @@ -563,7 +611,7 @@ static int mv3310_config_init(struct phy_device *phydev) if (err && err != -EOPNOTSUPP) return err; - return 0; + return mv3310_leds_write(phydev); } static int mv3310_get_features(struct phy_device *phydev) -- cgit From 94d5b10cac49e61ed32de6a33ee40b95aeb516ce Mon Sep 17 00:00:00 2001 From: Russell King Date: Sun, 1 Dec 2019 15:35:08 +0000 Subject: arm64: dts: configure Macchiatobin 10G PHY LED modes Configure the Macchiatobin 10G PHY LED modes to correct their polarity. We keep the existing LED behaviours, but switch their polarity to reflect how they are connected. Tweak the LED modes as well to be: left: off = no link solid green = RJ45 link up (not SFP+ cage) flash green = traffic right: off = no link solid green = 10G solid yellow = 1G flash green = 100M flash yellow = 10M Signed-off-by: Russell King --- arch/arm64/boot/dts/marvell/armada-8040-mcbin.dts | 2 ++ 1 file changed, 2 insertions(+) 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>; }; }; -- cgit From d9f17bd829d7c2dc722b907ca13570dd3717d8e8 Mon Sep 17 00:00:00 2001 From: Russell King Date: Fri, 3 Jan 2020 23:13:28 +0000 Subject: net: phy: add resolved pause support Allow phylib drivers to pass the hardware-resolved pause state to MAC drivers, rather than using the software-based pause resolution code. Signed-off-by: Russell King --- drivers/net/phy/phy_device.c | 6 ++++++ include/linux/phy.h | 9 +++++++++ 2 files changed, 15 insertions(+) diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c index 3f816d5e2668..c839a93ba6a2 100644 --- a/drivers/net/phy/phy_device.c +++ b/drivers/net/phy/phy_device.c @@ -2718,6 +2718,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/include/linux/phy.h b/include/linux/phy.h index 130e633f9ae3..282e95336c35 100644 --- a/include/linux/phy.h +++ b/include/linux/phy.h @@ -583,6 +583,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); -- cgit From cdd74a824c3c5aa90d1674d4b39b1029a5d02e7c Mon Sep 17 00:00:00 2001 From: Russell King Date: Sat, 4 Jan 2020 00:41:35 +0000 Subject: net: phy: marvell*: add support for hw resolved pause modes Support reporting the hardware resolved pause enablement states via phylib, overriding our software implementation. Signed-off-by: Russell King --- drivers/net/phy/marvell.c | 41 +++++++++++++++++++++++++++++++++++++++-- drivers/net/phy/marvell10g.c | 6 ++++++ 2 files changed, 45 insertions(+), 2 deletions(-) diff --git a/drivers/net/phy/marvell.c b/drivers/net/phy/marvell.c index 620052c023a5..0213e07724be 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) @@ -1455,6 +1461,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; @@ -1510,6 +1517,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; } @@ -1552,6 +1564,7 @@ static int marvell_read_status_page(struct phy_device *phydev, int page) phydev->asym_pause = 0; phydev->speed = SPEED_UNKNOWN; phydev->duplex = DUPLEX_UNKNOWN; + phydev->resolved_pause_valid = false; if (phydev->autoneg == AUTONEG_ENABLE) err = marvell_read_status_page_an(phydev, fiber, status); @@ -2670,6 +2683,23 @@ static int marvell_probe(struct phy_device *phydev) 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; +} + static int m88e1121_probe(struct phy_device *phydev) { int err; @@ -2681,11 +2711,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; @@ -2747,7 +2784,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 5ccbf2e055d8..246cb7daeca5 100644 --- a/drivers/net/phy/marvell10g.c +++ b/drivers/net/phy/marvell10g.c @@ -73,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, @@ -823,6 +825,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) -- cgit From a816617f336df08f0333c425148bc53d3def0a9d Mon Sep 17 00:00:00 2001 From: Russell King Date: Mon, 26 Aug 2019 12:19:35 +0100 Subject: net: phy: provide phy driver start/stop hooks Provide phy driver start/stop hooks so that the PHY driver knows when the network driver is starting or stopping. This will be used for the Marvell 10G driver so that we can sanely refuse to start if the PHYs firmware is not present, and also so that we can sanely support SFPs behind the PHY. Signed-off-by: Russell King --- drivers/net/phy/phy.c | 5 +++++ include/linux/phy.h | 3 +++ 2 files changed, 8 insertions(+) diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c index 45f75533c47c..52d92c37bd8a 100644 --- a/drivers/net/phy/phy.c +++ b/drivers/net/phy/phy.c @@ -1022,6 +1022,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); @@ -1055,6 +1057,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/include/linux/phy.h b/include/linux/phy.h index 282e95336c35..dead9f9b6b8d 100644 --- a/include/linux/phy.h +++ b/include/linux/phy.h @@ -738,6 +738,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, -- cgit From 7780d2808ee92a70261c083e4f6f0eecf63a28c1 Mon Sep 17 00:00:00 2001 From: Russell King Date: Wed, 5 Jun 2019 11:44:55 +0100 Subject: net: phy: marvell10g: allow PHY to probe without firmware Allow the PHY to probe when there is no firmware, but do not allow the link to come up by forcing the PHY state to PHY_HALTED in a similar way to phy_error(). Signed-off-by: Russell King --- drivers/net/phy/marvell10g.c | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/drivers/net/phy/marvell10g.c b/drivers/net/phy/marvell10g.c index 246cb7daeca5..f233d09160dc 100644 --- a/drivers/net/phy/marvell10g.c +++ b/drivers/net/phy/marvell10g.c @@ -109,6 +109,7 @@ enum { struct mv3310_priv { u32 firmware_ver; bool rate_match; + bool firmware_failed; struct device *hwmon_dev; char *hwmon_name; @@ -507,7 +508,7 @@ 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; } ret = phy_read_mmd(phydev, MDIO_MMD_PMAPMD, MV_PMA_FW_VER0); @@ -559,6 +560,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 @@ -913,6 +927,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, @@ -927,6 +942,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, -- cgit From 87eaab109be51ba371b622ed1820985d2543a1cd Mon Sep 17 00:00:00 2001 From: Russell King Date: Tue, 5 Nov 2019 10:34:39 +0000 Subject: net: phy: make phy_error() report which PHY has failed phy_error() is called from phy_interrupt() or phy_state_machine(), and uses WARN_ON() to print a backtrace. The backtrace is not useful when reporting a PHY error. However, a system may contain multiple ethernet PHYs, and phy_error() gives no clue which one caused the problem. Replace WARN_ON() with a call to phydev_err() so that we can see which PHY had an error, and also inform the user that we are halting the PHY. Signed-off-by: Russell King --- drivers/net/phy/phy.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c index 52d92c37bd8a..becbd17ffe0a 100644 --- a/drivers/net/phy/phy.c +++ b/drivers/net/phy/phy.c @@ -910,7 +910,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; -- cgit From e89fd43d1f2dfdb285c362326f44bdcaf5677f2a Mon Sep 17 00:00:00 2001 From: Russell King Date: Fri, 18 Oct 2019 10:37:44 +0100 Subject: net: sfp: add support for cooled SFP+ transceivers Cooled SFP+ transceivers need a longer initialisation (startup) time. Select the initialisation time depending on the cooled option bit. Signed-off-by: Russell King --- drivers/net/phy/sfp.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/drivers/net/phy/sfp.c b/drivers/net/phy/sfp.c index b2a5ed6915fa..2c87c8d2dc29 100644 --- a/drivers/net/phy/sfp.c +++ b/drivers/net/phy/sfp.c @@ -168,6 +168,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. @@ -1878,6 +1879,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; @@ -2084,10 +2087,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) @@ -2106,8 +2109,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); -- cgit From c63a1d224803049a084ad0c935243c853eab65fb Mon Sep 17 00:00:00 2001 From: Russell King Date: Fri, 14 Apr 2017 20:17:13 +0100 Subject: net: sfp: add sfp+ compatible [*not for mainline*] Add a compatible for SFP+ cages. SFP+ cages are backwards compatible, but the ethernet device behind them may not support the slower speeds of SFP modules. Signed-off-by: Russell King --- drivers/net/phy/sfp.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/phy/sfp.c b/drivers/net/phy/sfp.c index 2c87c8d2dc29..448a85f71493 100644 --- a/drivers/net/phy/sfp.c +++ b/drivers/net/phy/sfp.c @@ -291,6 +291,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); -- cgit From 5b8a31c943244f38267fe21e387cd6a7857f03f6 Mon Sep 17 00:00:00 2001 From: Russell King Date: Sun, 13 Sep 2015 01:06:31 +0100 Subject: net: sfp: display SFP module information [*not for mainline*] Display SFP module information verbosely, splitting the generic parts into a separate file. Signed-off-by: Russell King --- drivers/net/phy/Makefile | 2 +- drivers/net/phy/sff.c | 114 ++++++++++++++++++++++++ drivers/net/phy/sff.h | 16 ++++ drivers/net/phy/sfp.c | 228 ++++++++++++++++++++++++++++++++++++++++++++--- 4 files changed, 349 insertions(+), 11 deletions(-) create mode 100644 drivers/net/phy/sff.c create mode 100644 drivers/net/phy/sff.h 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/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 +#include +#include "sff.h" + +const char *sff_link_len(char *buf, size_t size, unsigned int length, + unsigned int multiplier) +{ + if (length == 0) + return "unsupported/unspecified"; + + if (length == 255) { + *buf++ = '>'; + size -= 1; + length -= 1; + } + + length *= multiplier; + + if (length >= 1000) + snprintf(buf, size, "%u.%0*ukm", + length / 1000, + multiplier > 100 ? 1 : + multiplier > 10 ? 2 : 3, + length % 1000); + else + snprintf(buf, size, "%um", length); + + return buf; +} +EXPORT_SYMBOL_GPL(sff_link_len); + +const char *sff_bitfield(char *buf, size_t size, + const struct sff_bitfield *bits, unsigned int val) +{ + char *p = buf; + int n; + + *p = '\0'; + while (bits->mask) { + if ((val & bits->mask) == bits->val) { + n = snprintf(p, size, "%s%s", + buf != p ? ", " : "", + bits->str); + if (n == size) + break; + p += n; + size -= n; + } + bits++; + } + + return buf; +} +EXPORT_SYMBOL_GPL(sff_bitfield); + +const char *sff_connector(unsigned int connector) +{ + switch (connector) { + case SFF8024_CONNECTOR_UNSPEC: + return "unknown/unspecified"; + case SFF8024_CONNECTOR_SC: + return "SC"; + case SFF8024_CONNECTOR_FIBERJACK: + return "Fiberjack"; + case SFF8024_CONNECTOR_LC: + return "LC"; + case SFF8024_CONNECTOR_MT_RJ: + return "MT-RJ"; + case SFF8024_CONNECTOR_MU: + return "MU"; + case SFF8024_CONNECTOR_SG: + return "SG"; + case SFF8024_CONNECTOR_OPTICAL_PIGTAIL: + return "Optical pigtail"; + case SFF8024_CONNECTOR_MPO_1X12: + return "MPO 1X12"; + case SFF8024_CONNECTOR_MPO_2X16: + return "MPO 2X16"; + case SFF8024_CONNECTOR_HSSDC_II: + return "HSSDC II"; + case SFF8024_CONNECTOR_COPPER_PIGTAIL: + return "Copper pigtail"; + case SFF8024_CONNECTOR_RJ45: + return "RJ45"; + case SFF8024_CONNECTOR_MXC_2X16: + return "MXC 2X16"; + default: + return "unknown"; + } +} +EXPORT_SYMBOL_GPL(sff_connector); + +const char *sff_encoding(unsigned int encoding) +{ + switch (encoding) { + case SFF8024_ENCODING_UNSPEC: + return "unspecified"; + case SFF8024_ENCODING_8472_64B66B: + return "64b66b"; + case SFF8024_ENCODING_8B10B: + return "8b10b"; + case SFF8024_ENCODING_4B5B: + return "4b5b"; + case SFF8024_ENCODING_NRZ: + return "NRZ"; + case SFF8024_ENCODING_8472_MANCHESTER: + return "MANCHESTER"; + default: + return "unknown"; + } +} +EXPORT_SYMBOL_GPL(sff_encoding); + +MODULE_LICENSE("GPL"); diff --git a/drivers/net/phy/sff.h b/drivers/net/phy/sff.h new file mode 100644 index 000000000000..cd7bb7c7ae4a --- /dev/null +++ b/drivers/net/phy/sff.h @@ -0,0 +1,16 @@ +#ifndef SFF_H +#define SFF_H + +struct sff_bitfield { + unsigned int mask; + unsigned int val; + const char *str; +}; + +const char *sff_link_len(char *buf, size_t size, unsigned int length, + unsigned int multiplier); +const char *sff_bitfield(char *buf, size_t size, + const struct sff_bitfield *bits, unsigned int val); +const char *sff_connector(unsigned int connector); +const char *sff_encoding(unsigned int encoding); +#endif diff --git a/drivers/net/phy/sfp.c b/drivers/net/phy/sfp.c index 448a85f71493..18598f1ca61d 100644 --- a/drivers/net/phy/sfp.c +++ b/drivers/net/phy/sfp.c @@ -18,6 +18,7 @@ #include #include +#include "sff.h" #include "sfp.h" #include "swphy.h" @@ -1379,6 +1380,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) { @@ -1758,6 +1867,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 */ @@ -1785,9 +1998,9 @@ static int sfp_sm_mod_probe(struct sfp *sfp, bool report) return -EAGAIN; } - /* 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); @@ -1850,14 +2063,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)) { -- cgit From 0b705ce3ba635284c200c229e9891beeddbeb005 Mon Sep 17 00:00:00 2001 From: Russell King Date: Sun, 16 Aug 2020 09:32:18 +0100 Subject: net: phy: pass supported PHY interface types to phylib Pass the supported PHY interface types to phylib so that PHY drivers can select an appropriate host configuration mode for their interface according to the host capabilities. This is only done for SFP modules presently. Signed-off-by: Russell King --- drivers/net/phy/phylink.c | 18 ++++++++++++++++++ include/linux/phy.h | 3 +++ 2 files changed, 21 insertions(+) diff --git a/drivers/net/phy/phylink.c b/drivers/net/phy/phylink.c index dc23355d46fb..b0316bfaec24 100644 --- a/drivers/net/phy/phylink.c +++ b/drivers/net/phy/phylink.c @@ -2018,6 +2018,8 @@ 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, @@ -2241,6 +2243,10 @@ static int phylink_sfp_connect_phy(void *upstream, struct phy_device *phy) else mode = MLO_AN_INBAND; + /* Set the PHY's host supported interfaces */ + phy_interface_and(phy->host_interfaces, phylink_sfp_interfaces, + pl->config->supported_interfaces); + /* Do the initial configuration */ ret = phylink_sfp_config(pl, mode, phy->supported, phy->advertising); if (ret < 0) @@ -2624,4 +2630,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/include/linux/phy.h b/include/linux/phy.h index dead9f9b6b8d..de964deadcdc 100644 --- a/include/linux/phy.h +++ b/include/linux/phy.h @@ -600,6 +600,9 @@ 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); + /* Energy efficient ethernet modes which should be prohibited */ u32 eee_broken_modes; -- cgit From 61be0bf5cbaf9a30dc42c807ca118519e4d3bea1 Mon Sep 17 00:00:00 2001 From: Russell King Date: Sun, 16 Aug 2020 09:32:18 +0100 Subject: net: phy: marvell10g: select host interface configuration Select the host interface configuration according to the capabilities of the host; this allows the kernel to support SFP modules using the 88x3310. Signed-off-by: Russell King --- drivers/net/phy/marvell10g.c | 62 ++++++++++++++++++++++++++++++++++++-------- 1 file changed, 51 insertions(+), 11 deletions(-) diff --git a/drivers/net/phy/marvell10g.c b/drivers/net/phy/marvell10g.c index f233d09160dc..f45d1d752a1c 100644 --- a/drivers/net/phy/marvell10g.c +++ b/drivers/net/phy/marvell10g.c @@ -95,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, @@ -590,19 +590,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; @@ -611,11 +636,26 @@ 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 */ err = mv3310_set_edpd(phydev, ETHTOOL_PHY_EDPD_DFLT_TX_MSECS); -- cgit From 75276d337fa88dedf808ef3f7c83e14599b9445c Mon Sep 17 00:00:00 2001 From: Russell King Date: Mon, 23 Dec 2019 23:24:17 +0000 Subject: net: phy: add supported_interfaces to phylib Add a supported_interfaces member to phylib so we know which interfaces a PHY supports. Currently, set any unconverted driver to indicate all interfaces are supported. Signed-off-by: Russell King --- include/linux/phy.h | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/include/linux/phy.h b/include/linux/phy.h index de964deadcdc..b2e385edc3b2 100644 --- a/include/linux/phy.h +++ b/include/linux/phy.h @@ -147,6 +147,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 */ @@ -602,6 +622,7 @@ struct phy_device { /* 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; -- cgit From 60c2bcd009997d7e8021c1ae62f76b5446e7924c Mon Sep 17 00:00:00 2001 From: Russell King Date: Mon, 23 Dec 2019 23:25:35 +0000 Subject: net: phy: add supported_interfaces to bcm84881 Signed-off-by: Russell King --- drivers/net/phy/bcm84881.c | 4 ++++ 1 file changed, 4 insertions(+) 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; } -- cgit From 72dd2ac9530f8bba53aedb988722d8cf3744f880 Mon Sep 17 00:00:00 2001 From: Russell King Date: Mon, 23 Dec 2019 23:25:49 +0000 Subject: net: phy: add supported_interfaces to marvell PHYs Signed-off-by: Russell King --- drivers/net/phy/marvell.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/net/phy/marvell.c b/drivers/net/phy/marvell.c index 0213e07724be..30606d7756c0 100644 --- a/drivers/net/phy/marvell.c +++ b/drivers/net/phy/marvell.c @@ -2680,6 +2680,15 @@ 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; } -- cgit From 01a3e44cc105ca606d7e1cc70bab52479c317da1 Mon Sep 17 00:00:00 2001 From: Russell King Date: Mon, 23 Dec 2019 23:26:04 +0000 Subject: net: phy: add supported_interfaces to marvell10g PHYs Signed-off-by: Russell King --- drivers/net/phy/marvell10g.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/net/phy/marvell10g.c b/drivers/net/phy/marvell10g.c index f45d1d752a1c..f361f6b6682d 100644 --- a/drivers/net/phy/marvell10g.c +++ b/drivers/net/phy/marvell10g.c @@ -487,6 +487,10 @@ 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; -- cgit From 22773927d721efab159f7d4cb9177d7996f456c6 Mon Sep 17 00:00:00 2001 From: Russell King Date: Tue, 3 Mar 2020 12:12:43 +0000 Subject: net: phylink: use phy interface mode bitmaps Use the phy interface mode bitmaps for SFP modules and PHYs to select the operating interface for SFPs and PHYs with SFPs. Signed-off-by: Russell King --- drivers/net/phy/phylink.c | 57 ++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 46 insertions(+), 11 deletions(-) diff --git a/drivers/net/phy/phylink.c b/drivers/net/phy/phylink.c index b0316bfaec24..bd6833148499 100644 --- a/drivers/net/phy/phylink.c +++ b/drivers/net/phy/phylink.c @@ -2247,19 +2247,54 @@ static int phylink_sfp_connect_phy(void *upstream, struct phy_device *phy) phy_interface_and(phy->host_interfaces, phylink_sfp_interfaces, pl->config->supported_interfaces); - /* Do the initial configuration */ - ret = phylink_sfp_config(pl, mode, phy->supported, phy->advertising); - 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; + } - interface = pl->link_config.interface; - ret = phylink_attach_phy(pl, phy, interface); - if (ret < 0) - return ret; + if (pl->cur_link_an_mode != mode || + pl->link_config.interface != interface) { + pl->link_config.interface = interface; + pl->cur_link_an_mode = mode; - ret = phylink_bringup_phy(pl, phy, interface); - if (ret) - phy_detach(phy); + 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; } -- cgit From ba922b7ee3fe5c956d19d97d5732009b3423f1f3 Mon Sep 17 00:00:00 2001 From: Russell King Date: Mon, 22 Jun 2020 01:05:59 +0100 Subject: net: dsa/bcm_sf2: fix pause mode validation The implementation appears not to support pause modes on anything but RGMII, RGMII_TXID, MII and REVMII interface modes. Let phylink know that detail. (This may not be correct; particularly see the FIXMEs in this patch.) Signed-off-by: Russell King --- drivers/net/dsa/bcm_sf2.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/drivers/net/dsa/bcm_sf2.c b/drivers/net/dsa/bcm_sf2.c index 445226720ff2..085fcbba5e39 100644 --- a/drivers/net/dsa/bcm_sf2.c +++ b/drivers/net/dsa/bcm_sf2.c @@ -608,6 +608,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 && @@ -625,8 +626,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 @@ -660,6 +666,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; @@ -751,6 +758,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 || -- cgit From 54839942c20335dd7ff1c591904655fbeaa8a1b7 Mon Sep 17 00:00:00 2001 From: Russell King Date: Mon, 29 Jun 2020 23:16:21 +0100 Subject: phy: armada-38x: further augmentation of setup Further augmentation of the comphy setup. Signed-off-by: Russell King --- arch/arm/boot/dts/armada-38x.dtsi | 5 +- drivers/phy/marvell/phy-armada38x-comphy.c | 147 +++++++++++++++++++++++++---- 2 files changed, 132 insertions(+), 20 deletions(-) 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/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]); } -- cgit From 1bd0ddcaf5fa056d3ab7ee985486f4335aa77b7c Mon Sep 17 00:00:00 2001 From: Russell King Date: Wed, 8 Jul 2020 11:37:25 +0100 Subject: net: mvneta: program 1ms autonegotiation clock divisor Program the 1ms autonegotiation clock divisor according to the clocking rate of neta - without this, the 1ms clock ticks at about 660us on Armada 38x configured for 250MHz. Bring this into correct specification. Signed-off-by: Russell King --- drivers/net/ethernet/marvell/mvneta.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/marvell/mvneta.c b/drivers/net/ethernet/marvell/mvneta.c index 3585cd30a95f..c5facf659d24 100644 --- a/drivers/net/ethernet/marvell/mvneta.c +++ b/drivers/net/ethernet/marvell/mvneta.c @@ -3966,7 +3966,7 @@ static void mvneta_mac_config(struct phylink_config *config, unsigned int mode, } 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_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 | @@ -3978,7 +3978,7 @@ static void mvneta_mac_config(struct phylink_config *config, unsigned int mode, } else { /* 802.3z negotiation - only 1000base-X */ new_ctrl0 |= MVNETA_GMAC0_PORT_1000BASE_X; - new_clk |= MVNETA_GMAC_1MS_CLOCK_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)) | @@ -3991,6 +3991,10 @@ static void mvneta_mac_config(struct phylink_config *config, unsigned int mode, new_an |= MVNETA_GMAC_AN_FLOW_CTRL_EN; } + /* Set the 1ms clock divisor */ + if (new_clk == MVNETA_GMAC_1MS_CLOCK_ENABLE) + new_clk |= clk_get_rate(pp->clk) / 1000; + /* 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 */ -- cgit From 035a2dc6f5ec00fa2f0bf9f62ab6ff737db17d08 Mon Sep 17 00:00:00 2001 From: Russell King Date: Sun, 28 Jun 2020 13:03:47 +0100 Subject: net: mvneta: convert to use mac_prepare()/mac_finish() Convert mvneta to use the mac_prepare() and mac_finish() methods in preparation to converting mvneta to split-PCS support. Signed-off-by: Russell King --- drivers/net/ethernet/marvell/mvneta.c | 77 ++++++++++++++++++++++++----------- 1 file changed, 53 insertions(+), 24 deletions(-) diff --git a/drivers/net/ethernet/marvell/mvneta.c b/drivers/net/ethernet/marvell/mvneta.c index c5facf659d24..39dde9048201 100644 --- a/drivers/net/ethernet/marvell/mvneta.c +++ b/drivers/net/ethernet/marvell/mvneta.c @@ -3923,6 +3923,32 @@ static void mvneta_mac_an_restart(struct phylink_config *config) gmac_an & ~MVNETA_GMAC_INBAND_RESTART_AN); } +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 val; + + 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. + */ + 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); + } + + if (pp->phy_interface != interface) + WARN_ON(phy_power_off(pp->comphy)); + + return 0; +} + static void mvneta_mac_config(struct phylink_config *config, unsigned int mode, const struct phylink_link_state *state) { @@ -3967,9 +3993,7 @@ static void mvneta_mac_config(struct phylink_config *config, unsigned int mode, /* 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 | + new_an = (new_an & ~(MVNETA_GMAC_CONFIG_MII_SPEED | MVNETA_GMAC_CONFIG_GMII_SPEED | MVNETA_GMAC_CONFIG_FULL_DUPLEX)) | MVNETA_GMAC_INBAND_AN_ENABLE | @@ -3979,9 +4003,7 @@ static void mvneta_mac_config(struct phylink_config *config, unsigned int mode, /* 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)) | + new_an = (new_an & ~MVNETA_GMAC_CONFIG_MII_SPEED) | MVNETA_GMAC_INBAND_AN_ENABLE | MVNETA_GMAC_CONFIG_GMII_SPEED | /* The MAC only supports FD mode */ @@ -3995,30 +4017,12 @@ static void mvneta_mac_config(struct phylink_config *config, unsigned int mode, if (new_clk == MVNETA_GMAC_1MS_CLOCK_ENABLE) new_clk |= clk_get_rate(pp->clk) / 1000; - /* 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) @@ -4037,6 +4041,29 @@ static void mvneta_mac_config(struct phylink_config *config, unsigned int mode, } } +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 val; + + if (pp->phy_interface != interface) + /* Enable the Serdes PHY */ + WARN_ON(mvneta_config_interface(pp, interface)); + + /* Allow the link to come up if in in-band mode, otherwise the + * link is forced via mac_link_down()/mac_link_up() + */ + if (phylink_autoneg_inband(mode)) { + val = mvreg_read(pp, MVNETA_GMAC_AUTONEG_CONFIG); + val &= ~MVNETA_GMAC_FORCE_LINK_DOWN; + mvreg_write(pp, MVNETA_GMAC_AUTONEG_CONFIG, val); + } + + return 0; +} + static void mvneta_set_eee(struct mvneta_port *pp, bool enable) { u32 lpi_ctl1; @@ -4126,7 +4153,9 @@ 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, }; -- cgit From d931d6bf1406fa376a7f526c0f09c81de931a88c Mon Sep 17 00:00:00 2001 From: Russell King Date: Wed, 8 Jul 2020 11:44:36 +0100 Subject: net: mvneta: move 1ms clock control into mac_prepare/mac_finish Move the 1ms clock control out of mac_config() into mac_prepare() and mac_finish(), which simplifies the mac_config() code. Signed-off-by: Russell King --- drivers/net/ethernet/marvell/mvneta.c | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/drivers/net/ethernet/marvell/mvneta.c b/drivers/net/ethernet/marvell/mvneta.c index 39dde9048201..a700bccb1c49 100644 --- a/drivers/net/ethernet/marvell/mvneta.c +++ b/drivers/net/ethernet/marvell/mvneta.c @@ -3946,6 +3946,14 @@ static int mvneta_mac_prepare(struct phylink_config *config, unsigned int mode, if (pp->phy_interface != interface) WARN_ON(phy_power_off(pp->comphy)); + /* Enable the 1ms clock */ + if (phylink_autoneg_inband(mode)) { + unsigned long rate = clk_get_rate(pp->clk); + + mvreg_write(pp, MVNETA_GMAC_CLOCK_DIVIDER, + MVNETA_GMAC_1MS_CLOCK_ENABLE | (rate / 1000)); + } + return 0; } @@ -3957,14 +3965,12 @@ static void mvneta_mac_config(struct phylink_config *config, unsigned int mode, 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 | @@ -3992,7 +3998,6 @@ static void mvneta_mac_config(struct phylink_config *config, unsigned int mode, } 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_CONFIG_MII_SPEED | MVNETA_GMAC_CONFIG_GMII_SPEED | MVNETA_GMAC_CONFIG_FULL_DUPLEX)) | @@ -4002,7 +4007,6 @@ static void mvneta_mac_config(struct phylink_config *config, unsigned int mode, } 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_CONFIG_MII_SPEED) | MVNETA_GMAC_INBAND_AN_ENABLE | MVNETA_GMAC_CONFIG_GMII_SPEED | @@ -4013,10 +4017,6 @@ static void mvneta_mac_config(struct phylink_config *config, unsigned int mode, new_an |= MVNETA_GMAC_AN_FLOW_CTRL_EN; } - /* Set the 1ms clock divisor */ - if (new_clk == MVNETA_GMAC_1MS_CLOCK_ENABLE) - new_clk |= clk_get_rate(pp->clk) / 1000; - /* When at 2.5G, the link partner can send frames with shortened * preambles. */ @@ -4029,8 +4029,6 @@ static void mvneta_mac_config(struct phylink_config *config, unsigned int mode, 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); @@ -4046,7 +4044,14 @@ static int mvneta_mac_finish(struct phylink_config *config, unsigned int mode, { struct net_device *ndev = to_net_dev(config->dev); struct mvneta_port *pp = netdev_priv(ndev); - u32 val; + u32 val, clk; + + /* Disable 1ms clock if not in in-band mode */ + if (!phylink_autoneg_inband(mode)) { + clk = mvreg_read(pp, MVNETA_GMAC_CLOCK_DIVIDER); + clk &= ~MVNETA_GMAC_1MS_CLOCK_ENABLE; + mvreg_write(pp, MVNETA_GMAC_CLOCK_DIVIDER, clk); + } if (pp->phy_interface != interface) /* Enable the Serdes PHY */ -- cgit From 225f0673db0c5802654f8ffca564528416aa6ae1 Mon Sep 17 00:00:00 2001 From: Russell King Date: Mon, 22 Jun 2020 17:21:43 +0100 Subject: net: mvneta: convert to phylink pcs operations An initial stab at converting mvneta to PCS operations. There's a few FIXMEs to be solved. Signed-off-by: Russell King --- drivers/net/ethernet/marvell/mvneta.c | 178 +++++++++++++++++++++------------- 1 file changed, 109 insertions(+), 69 deletions(-) diff --git a/drivers/net/ethernet/marvell/mvneta.c b/drivers/net/ethernet/marvell/mvneta.c index a700bccb1c49..1974e1d58dd4 100644 --- a/drivers/net/ethernet/marvell/mvneta.c +++ b/drivers/net/ethernet/marvell/mvneta.c @@ -496,6 +496,7 @@ 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 mvneta_bm *bm_priv; @@ -3828,6 +3829,111 @@ 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); + u32 gmac_stat; + + gmac_stat = mvreg_read(pp, MVNETA_GMAC_STATUS); + + if (gmac_stat & MVNETA_GMAC_SPEED_1000) + state->speed = + state->interface == PHY_INTERFACE_MODE_2500BASEX ? + SPEED_2500 : SPEED_1000; + else if (gmac_stat & MVNETA_GMAC_SPEED_100) + state->speed = SPEED_100; + else + state->speed = SPEED_10; + + state->an_complete = !!(gmac_stat & MVNETA_GMAC_AN_COMPLETE); + state->link = !!(gmac_stat & MVNETA_GMAC_LINK_UP); + state->duplex = !!(gmac_stat & MVNETA_GMAC_FULL_DUPLEX); + + if (gmac_stat & MVNETA_GMAC_RX_FLOW_CTRL_ENABLE) + state->pause |= MLO_PAUSE_RX; + if (gmac_stat & MVNETA_GMAC_TX_FLOW_CTRL_ENABLE) + state->pause |= MLO_PAUSE_TX; +} + +static int mvneta_pcs_config(struct phylink_pcs *pcs, + unsigned int mode, phy_interface_t interface, + const unsigned long *advertising, + bool permit_pause_to_mac) +{ + struct mvneta_port *pp = mvneta_pcs_to_port(pcs); + u32 mask, val, an, old_an, changed; + + mask = MVNETA_GMAC_INBAND_AN_ENABLE | + MVNETA_GMAC_INBAND_RESTART_AN | + MVNETA_GMAC_AN_SPEED_EN | + MVNETA_GMAC_AN_FLOW_CTRL_EN | + MVNETA_GMAC_AN_DUPLEX_EN; + + if (phylink_autoneg_inband(mode)) { + mask |= MVNETA_GMAC_CONFIG_MII_SPEED | + MVNETA_GMAC_CONFIG_GMII_SPEED | + MVNETA_GMAC_CONFIG_FULL_DUPLEX; + val = MVNETA_GMAC_INBAND_AN_ENABLE; + + if (state->interface == PHY_INTERFACE_MODE_SGMII) { + /* SGMII mode receives the speed and duplex from PHY */ + val |= MVNETA_GMAC_AN_SPEED_EN | + MVNETA_GMAC_AN_DUPLEX_EN; + } else { + /* 802.3z mode has fixed speed and duplex */ + val |= MVNETA_GMAC_CONFIG_GMII_SPEED | + MVNETA_GMAC_CONFIG_FULL_DUPLEX; + + /* The FLOW_CTRL_EN bit selects either the hardware + * automatically or the CONFIG_FLOW_CTRL manually + * controls the GMAC pause mode. + */ + if (permit_pause_to_mac) + val |= MVNETA_GMAC_AN_FLOW_CTRL_EN; + + /* Update the advertisement bits */ + mask |= MVNETA_GMAC_ADVERT_SYM_FLOW_CTRL; + if (phylink_test(advertising, Pause)) + val |= MVNETA_GMAC_ADVERT_SYM_FLOW_CTRL; + } + } else { + /* Phy or fixed speed - disable in-band AN modes */ + val = 0; + } + + old_an = an = mvreg_read(pp, MVNETA_GMAC_AUTONEG_CONFIG); + an = (an & ~mask) | val; + changed = old_an ^ an; + if (changed) + mvreg_write(pp, MVNETA_GMAC_AUTONEG_CONFIG, an); + + /* We are only interested in the advertisement bits changing */ + return !!(changed & MVNETA_GMAC_ADVERT_SYM_FLOW_CTRL); +} + +static void mvneta_pcs_an_restart(struct phylink_pcs *pcs) +{ + struct mvneta_port *pp = mvneta_pcs_to_port(pcs); + u32 gmac_an = mvreg_read(pp, MVNETA_GMAC_AUTONEG_CONFIG); + + 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); +} + +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) @@ -3882,47 +3988,6 @@ 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) -{ - 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 (gmac_stat & MVNETA_GMAC_SPEED_1000) - state->speed = - state->interface == PHY_INTERFACE_MODE_2500BASEX ? - SPEED_2500 : SPEED_1000; - else if (gmac_stat & MVNETA_GMAC_SPEED_100) - state->speed = SPEED_100; - else - state->speed = SPEED_10; - - state->an_complete = !!(gmac_stat & MVNETA_GMAC_AN_COMPLETE); - state->link = !!(gmac_stat & MVNETA_GMAC_LINK_UP); - state->duplex = !!(gmac_stat & MVNETA_GMAC_FULL_DUPLEX); - - 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; -} - -static void mvneta_mac_an_restart(struct phylink_config *config) -{ - 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); -} - static int mvneta_mac_prepare(struct phylink_config *config, unsigned int mode, phy_interface_t interface) { @@ -3965,18 +4030,11 @@ static void mvneta_mac_config(struct phylink_config *config, unsigned int mode, 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_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_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. @@ -3988,9 +4046,6 @@ static void mvneta_mac_config(struct phylink_config *config, unsigned int mode, 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; - if (!phylink_autoneg_inband(mode)) { /* Phy or fixed speed - nothing to do, leave the * configured speed, duplex and flow control as-is. @@ -3998,23 +4053,9 @@ static void mvneta_mac_config(struct phylink_config *config, unsigned int mode, } else if (state->interface == PHY_INTERFACE_MODE_SGMII) { /* SGMII mode receives the state from the PHY */ new_ctrl2 |= MVNETA_GMAC2_INBAND_AN_ENABLE; - new_an = (new_an & ~(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_an = (new_an & ~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; } /* When at 2.5G, the link partner can send frames with shortened @@ -4029,8 +4070,6 @@ static void mvneta_mac_config(struct phylink_config *config, unsigned int mode, 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_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) & @@ -4156,8 +4195,6 @@ static void mvneta_mac_link_up(struct phylink_config *config, 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, @@ -5176,6 +5213,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; -- cgit From 3376692b225e542453c2fefc8e51755142870b99 Mon Sep 17 00:00:00 2001 From: Russell King Date: Thu, 22 Dec 2016 16:03:15 +0000 Subject: net: mvneta: split out GMAC Split out the code handling the GMAC from the rest of the driver. This block appears to be shared amongst several revisions of the IP. Signed-off-by: Russell King --- drivers/net/ethernet/marvell/Kconfig | 4 + drivers/net/ethernet/marvell/Makefile | 1 + drivers/net/ethernet/marvell/mvgmac.c | 356 ++++++++++++++++++++++++++++++++++ drivers/net/ethernet/marvell/mvgmac.h | 43 ++++ drivers/net/ethernet/marvell/mvneta.c | 310 ++++------------------------- 5 files changed, 438 insertions(+), 276 deletions(-) create mode 100644 drivers/net/ethernet/marvell/mvgmac.c create mode 100644 drivers/net/ethernet/marvell/mvgmac.h diff --git a/drivers/net/ethernet/marvell/Kconfig b/drivers/net/ethernet/marvell/Kconfig index 41815b609569..32675de95ed2 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..6c0b7c5c0b24 --- /dev/null +++ b/drivers/net/ethernet/marvell/mvgmac.c @@ -0,0 +1,356 @@ +/* + * GMAC driver for Marvell network interfaces on Armada SoCs. + * + * Copyright (C) 2012 Marvell + * + * Rami Rosen + * Thomas Petazzoni + * + * 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 +#include +#include + +#include "mvgmac.h" + +enum { + 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), + + 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), + + 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_AN_FLOW_CTRL_ENABLE = BIT(11), + GMAC_ANEG_FULL_DUPLEX = BIT(12), + GMAC_ANEG_AN_DUPLEX_ENABLE = BIT(13), + + 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), + + GMAC_CTRL4_REG = 0x90, + GMAC_CTRL4_SHORT_PREAMBLE_ENABLE = BIT(1), + + 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); + +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; + + 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; + } + + if (tx_pause || rx_pause) + val |= GMAC_ANEG_CONFIG_FLOW_CTRL; + + 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; + } + } 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); +} +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 & ~GMAC_CTRL4_SHORT_PREAMBLE_ENABLE; + + /* Even though it might look weird, when we're configured in + * SGMII or QSGMII mode, the RGMII bit needs to be set. + */ + new_ctrl2 |= 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; + } + + /* When at 2.5G, the link partner can send frames with shortened + * preambles. + */ + 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..ceccc6777237 --- /dev/null +++ b/drivers/net/ethernet/marvell/mvgmac.h @@ -0,0 +1,43 @@ +#ifndef MVGMAC_H +#define MVGMAC_H + +#include + +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 + +struct mvgmac { + void __iomem *base; +}; + +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); +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 1974e1d58dd4..e149806717f3 100644 --- a/drivers/net/ethernet/marvell/mvneta.c +++ b/drivers/net/ethernet/marvell/mvneta.c @@ -34,6 +34,7 @@ #include #include #include "mvneta_bm.h" +#include "mvgmac.h" #include #include #include @@ -191,43 +192,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 @@ -251,12 +216,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 */ @@ -278,7 +237,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,6 +458,7 @@ struct mvneta_port { 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; @@ -507,6 +467,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)]; @@ -889,19 +850,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, @@ -1309,23 +1257,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); } @@ -3135,9 +3073,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 +3565,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 */ @@ -3838,27 +3776,8 @@ static void mvneta_pcs_get_state(struct phylink_pcs *pcs, struct phylink_link_state *state) { struct mvneta_port *pp = mvneta_pcs_to_port(pcs); - u32 gmac_stat; - - gmac_stat = mvreg_read(pp, MVNETA_GMAC_STATUS); - if (gmac_stat & MVNETA_GMAC_SPEED_1000) - state->speed = - state->interface == PHY_INTERFACE_MODE_2500BASEX ? - SPEED_2500 : SPEED_1000; - else if (gmac_stat & MVNETA_GMAC_SPEED_100) - state->speed = SPEED_100; - else - state->speed = SPEED_10; - - state->an_complete = !!(gmac_stat & MVNETA_GMAC_AN_COMPLETE); - state->link = !!(gmac_stat & MVNETA_GMAC_LINK_UP); - state->duplex = !!(gmac_stat & MVNETA_GMAC_FULL_DUPLEX); - - 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; + mvgmac_pcs_get_state(&pp->gmac, state); } static int mvneta_pcs_config(struct phylink_pcs *pcs, @@ -3867,65 +3786,19 @@ static int mvneta_pcs_config(struct phylink_pcs *pcs, bool permit_pause_to_mac) { struct mvneta_port *pp = mvneta_pcs_to_port(pcs); - u32 mask, val, an, old_an, changed; - - mask = MVNETA_GMAC_INBAND_AN_ENABLE | - MVNETA_GMAC_INBAND_RESTART_AN | - MVNETA_GMAC_AN_SPEED_EN | - MVNETA_GMAC_AN_FLOW_CTRL_EN | - MVNETA_GMAC_AN_DUPLEX_EN; - if (phylink_autoneg_inband(mode)) { - mask |= MVNETA_GMAC_CONFIG_MII_SPEED | - MVNETA_GMAC_CONFIG_GMII_SPEED | - MVNETA_GMAC_CONFIG_FULL_DUPLEX; - val = MVNETA_GMAC_INBAND_AN_ENABLE; - - if (state->interface == PHY_INTERFACE_MODE_SGMII) { - /* SGMII mode receives the speed and duplex from PHY */ - val |= MVNETA_GMAC_AN_SPEED_EN | - MVNETA_GMAC_AN_DUPLEX_EN; - } else { - /* 802.3z mode has fixed speed and duplex */ - val |= MVNETA_GMAC_CONFIG_GMII_SPEED | - MVNETA_GMAC_CONFIG_FULL_DUPLEX; - - /* The FLOW_CTRL_EN bit selects either the hardware - * automatically or the CONFIG_FLOW_CTRL manually - * controls the GMAC pause mode. - */ - if (permit_pause_to_mac) - val |= MVNETA_GMAC_AN_FLOW_CTRL_EN; - - /* Update the advertisement bits */ - mask |= MVNETA_GMAC_ADVERT_SYM_FLOW_CTRL; - if (phylink_test(advertising, Pause)) - val |= MVNETA_GMAC_ADVERT_SYM_FLOW_CTRL; - } - } else { - /* Phy or fixed speed - disable in-band AN modes */ - val = 0; - } - - old_an = an = mvreg_read(pp, MVNETA_GMAC_AUTONEG_CONFIG); - an = (an & ~mask) | val; - changed = old_an ^ an; - if (changed) - mvreg_write(pp, MVNETA_GMAC_AUTONEG_CONFIG, an); + /* We should never see Asym_Pause set */ + WARN_ON(phylink_test(advertising, Asym_Pause)); - /* We are only interested in the advertisement bits changing */ - return !!(changed & MVNETA_GMAC_ADVERT_SYM_FLOW_CTRL); + 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); - 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_pcs_an_restart(&pp->gmac); } static const struct phylink_pcs_ops mvneta_phylink_pcs_ops = { @@ -3993,7 +3866,6 @@ static int mvneta_mac_prepare(struct phylink_config *config, unsigned int mode, { struct net_device *ndev = to_net_dev(config->dev); struct mvneta_port *pp = netdev_priv(ndev); - u32 val; if (pp->phy_interface != interface || phylink_autoneg_inband(mode)) { @@ -4002,10 +3874,7 @@ static int mvneta_mac_prepare(struct phylink_config *config, unsigned int mode, * can only change the port mode and in-band enable when the * link is down. */ - val = mvreg_read(pp, MVNETA_GMAC_AUTONEG_CONFIG); - val &= ~MVNETA_GMAC_FORCE_LINK_PASS; - val |= MVNETA_GMAC_FORCE_LINK_DOWN; - mvreg_write(pp, MVNETA_GMAC_AUTONEG_CONFIG, val); + mvgmac_link_force_down(&pp->gmac); } if (pp->phy_interface != interface) @@ -4027,55 +3896,8 @@ static void mvneta_mac_config(struct phylink_config *config, unsigned int mode, { struct net_device *ndev = to_net_dev(config->dev); struct mvneta_port *pp = netdev_priv(ndev); - u32 new_ctrl0, gmac_ctrl0 = mvreg_read(pp, MVNETA_GMAC_CTRL_0); - u32 new_ctrl2, gmac_ctrl2 = mvreg_read(pp, MVNETA_GMAC_CTRL_2); - u32 new_ctrl4, gmac_ctrl4 = mvreg_read(pp, MVNETA_GMAC_CTRL_4); - - new_ctrl0 = gmac_ctrl0 & ~MVNETA_GMAC0_PORT_1000BASE_X; - new_ctrl2 = gmac_ctrl2 & ~(MVNETA_GMAC2_INBAND_AN_ENABLE | - MVNETA_GMAC2_PORT_RESET); - new_ctrl4 = gmac_ctrl4 & ~(MVNETA_GMAC4_SHORT_PREAMBLE_ENABLE); - /* Even though it might look weird, when we're configured in - * SGMII or QSGMII mode, the RGMII bit needs to be set. - */ - new_ctrl2 |= MVNETA_GMAC2_PORT_RGMII; - - if (state->interface == PHY_INTERFACE_MODE_QSGMII || - state->interface == PHY_INTERFACE_MODE_SGMII || - phy_interface_mode_is_8023z(state->interface)) - new_ctrl2 |= MVNETA_GMAC2_PCS_ENABLE; - - if (!phylink_autoneg_inband(mode)) { - /* Phy or fixed speed - nothing to do, leave the - * configured speed, duplex and flow control as-is. - */ - } else if (state->interface == PHY_INTERFACE_MODE_SGMII) { - /* SGMII mode receives the state from the PHY */ - new_ctrl2 |= MVNETA_GMAC2_INBAND_AN_ENABLE; - } else { - /* 802.3z negotiation - only 1000base-X */ - new_ctrl0 |= MVNETA_GMAC0_PORT_1000BASE_X; - } - - /* When at 2.5G, the link partner can send frames with shortened - * preambles. - */ - if (state->interface == PHY_INTERFACE_MODE_2500BASEX) - new_ctrl4 |= MVNETA_GMAC4_SHORT_PREAMBLE_ENABLE; - - if (new_ctrl0 != gmac_ctrl0) - mvreg_write(pp, MVNETA_GMAC_CTRL_0, new_ctrl0); - if (new_ctrl2 != gmac_ctrl2) - mvreg_write(pp, MVNETA_GMAC_CTRL_2, new_ctrl2); - if (new_ctrl4 != gmac_ctrl4) - mvreg_write(pp, MVNETA_GMAC_CTRL_4, new_ctrl4); - - if (gmac_ctrl2 & MVNETA_GMAC2_PORT_RESET) { - while ((mvreg_read(pp, MVNETA_GMAC_CTRL_2) & - MVNETA_GMAC2_PORT_RESET) != 0) - continue; - } + mvgmac_config_mac(&pp->gmac, mode, state); } static int mvneta_mac_finish(struct phylink_config *config, unsigned int mode, @@ -4083,7 +3905,7 @@ static int mvneta_mac_finish(struct phylink_config *config, unsigned int mode, { struct net_device *ndev = to_net_dev(config->dev); struct mvneta_port *pp = netdev_priv(ndev); - u32 val, clk; + u32 clk; /* Disable 1ms clock if not in in-band mode */ if (!phylink_autoneg_inband(mode)) { @@ -4099,45 +3921,23 @@ static int mvneta_mac_finish(struct phylink_config *config, unsigned int mode, /* Allow the link to come up if in in-band mode, otherwise the * link is forced via mac_link_down()/mac_link_up() */ - if (phylink_autoneg_inband(mode)) { - val = mvreg_read(pp, MVNETA_GMAC_AUTONEG_CONFIG); - val &= ~MVNETA_GMAC_FORCE_LINK_DOWN; - mvreg_write(pp, MVNETA_GMAC_AUTONEG_CONFIG, val); - } + if (phylink_autoneg_inband(mode)) + mvgmac_link_unforce(&pp->gmac); return 0; } -static void mvneta_set_eee(struct mvneta_port *pp, bool enable) -{ - u32 lpi_ctl1; - - lpi_ctl1 = mvreg_read(pp, MVNETA_LPI_CTRL_1); - if (enable) - lpi_ctl1 |= MVNETA_LPI_REQUEST_ENABLE; - else - lpi_ctl1 &= ~MVNETA_LPI_REQUEST_ENABLE; - mvreg_write(pp, MVNETA_LPI_CTRL_1, lpi_ctl1); -} - static void mvneta_mac_link_down(struct phylink_config *config, unsigned int mode, phy_interface_t interface) { struct net_device *ndev = to_net_dev(config->dev); struct mvneta_port *pp = netdev_priv(ndev); - u32 val; mvneta_port_down(pp); - - if (!phylink_autoneg_inband(mode)) { - val = mvreg_read(pp, MVNETA_GMAC_AUTONEG_CONFIG); - val &= ~MVNETA_GMAC_FORCE_LINK_PASS; - val |= MVNETA_GMAC_FORCE_LINK_DOWN; - mvreg_write(pp, MVNETA_GMAC_AUTONEG_CONFIG, val); - } + 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, @@ -4148,48 +3948,14 @@ 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); } } @@ -4958,14 +4724,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); } @@ -4974,22 +4737,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); } @@ -5254,6 +5009,9 @@ static int mvneta_probe(struct platform_device *pdev) goto err_clk; } + pp->gmac.base = pp->base + MVNETA_GMAC_BASE; + pp->tx_lpi_timer = 16; + /* Alloc per-cpu port structure */ pp->ports = alloc_percpu(struct mvneta_pcpu_port); if (!pp->ports) { -- cgit From 628ba9253114654f739c5280c7b573903e31adf6 Mon Sep 17 00:00:00 2001 From: Russell King Date: Fri, 23 Dec 2016 01:09:44 +0000 Subject: net: mvgmac: support different hw versions Signed-off-by: Russell King --- drivers/net/ethernet/marvell/mvgmac.c | 108 ++++++++++++++++++++++++++++++---- drivers/net/ethernet/marvell/mvgmac.h | 9 +++ drivers/net/ethernet/marvell/mvneta.c | 1 + 3 files changed, 108 insertions(+), 10 deletions(-) diff --git a/drivers/net/ethernet/marvell/mvgmac.c b/drivers/net/ethernet/marvell/mvgmac.c index 6c0b7c5c0b24..e776d4e85d3f 100644 --- a/drivers/net/ethernet/marvell/mvgmac.c +++ b/drivers/net/ethernet/marvell/mvgmac.c @@ -19,6 +19,8 @@ #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), @@ -26,12 +28,21 @@ enum { 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), @@ -43,9 +54,12 @@ enum { 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), @@ -59,8 +73,21 @@ enum { 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, @@ -111,6 +138,47 @@ void mvgmac_disable(struct mvgmac *gmac) } 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; @@ -142,7 +210,7 @@ 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; + u32 val, ctrl4; val = readl_relaxed(gmac->base + GMAC_ANEG_REG); val &= ~GMAC_ANEG_CONFIG_FLOW_CTRL; @@ -163,8 +231,19 @@ void mvgmac_link_up(struct mvgmac *gmac, int mode, int speed, int duplex, val |= GMAC_ANEG_FULL_DUPLEX; } - if (tx_pause || rx_pause) - val |= GMAC_ANEG_CONFIG_FLOW_CTRL; + 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); } @@ -252,6 +331,11 @@ int mvgmac_pcs_config(struct mvgmac *gmac, unsigned int mode, 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 */ @@ -265,7 +349,8 @@ int mvgmac_pcs_config(struct mvgmac *gmac, unsigned int mode, 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); + return !!(changed & (GMAC_ANEG_ADVERT_SYM_FLOW_CTRL | + GMAC_ANEG_ADVERT_ASYM_FLOW_CTRL)); } EXPORT_SYMBOL_GPL(mvgmac_pcs_config); @@ -279,7 +364,7 @@ void mvgmac_config_mac(struct mvgmac *gmac, unsigned int mode, 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 & ~GMAC_CTRL4_SHORT_PREAMBLE_ENABLE; + 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. @@ -303,11 +388,14 @@ void mvgmac_config_mac(struct mvgmac *gmac, unsigned int mode, new_ctrl0 |= GMAC_CTRL0_PORT_1000BASE_X; } - /* When at 2.5G, the link partner can send frames with shortened - * preambles. - */ - if (state->interface == PHY_INTERFACE_MODE_2500BASEX) - new_ctrl4 |= GMAC_CTRL4_SHORT_PREAMBLE_ENABLE; + 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); diff --git a/drivers/net/ethernet/marvell/mvgmac.h b/drivers/net/ethernet/marvell/mvgmac.h index ceccc6777237..f4111fba7258 100644 --- a/drivers/net/ethernet/marvell/mvgmac.h +++ b/drivers/net/ethernet/marvell/mvgmac.h @@ -14,13 +14,22 @@ struct phylink_link_state; */ #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); diff --git a/drivers/net/ethernet/marvell/mvneta.c b/drivers/net/ethernet/marvell/mvneta.c index e149806717f3..5bfbdaff1430 100644 --- a/drivers/net/ethernet/marvell/mvneta.c +++ b/drivers/net/ethernet/marvell/mvneta.c @@ -5010,6 +5010,7 @@ static int mvneta_probe(struct platform_device *pdev) } pp->gmac.base = pp->base + MVNETA_GMAC_BASE; + pp->gmac.version = MVGMAC_NETA; pp->tx_lpi_timer = 16; /* Alloc per-cpu port structure */ -- cgit From 4dfe915eabecd08e788550556eace6cd5c2bb0e7 Mon Sep 17 00:00:00 2001 From: Russell King Date: Sat, 3 Oct 2015 09:13:05 +0100 Subject: cpuidle: mvebu: indicate failure to enter deeper sleep states The cpuidle ->enter method expects the return value to be the sleep state we entered. Returning negative numbers or other codes is not permissible since coupled CPU idle was merged. At least some of the mvebu_v7_cpu_suspend() implementations return the value from cpu_suspend(), which returns zero if the CPU vectors back into the kernel via cpu_resume() (the success case), or the non-zero return value of the suspend actor, or one (failure cases). We do not want to be returning the failure case value back to CPU idle as that indicates that we successfully entered one of the deeper idle states. Always return zero instead, indicating that we slept for the shortest amount of time. Signed-off-by: Russell King --- drivers/cpuidle/cpuidle-mvebu-v7.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) 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; } -- cgit