diff options
Diffstat (limited to 'drivers/net/ethernet/marvell')
-rw-r--r-- | drivers/net/ethernet/marvell/Kconfig | 4 | ||||
-rw-r--r-- | drivers/net/ethernet/marvell/Makefile | 1 | ||||
-rw-r--r-- | drivers/net/ethernet/marvell/mvgmac.c | 442 | ||||
-rw-r--r-- | drivers/net/ethernet/marvell/mvgmac.h | 56 | ||||
-rw-r--r-- | drivers/net/ethernet/marvell/mvneta.c | 508 | ||||
-rw-r--r-- | drivers/net/ethernet/marvell/mvpp2/mvpp2.h | 3 | ||||
-rw-r--r-- | drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c | 208 |
7 files changed, 755 insertions, 467 deletions
diff --git a/drivers/net/ethernet/marvell/Kconfig b/drivers/net/ethernet/marvell/Kconfig index fe0989c0fc25..e9149e6fab10 100644 --- a/drivers/net/ethernet/marvell/Kconfig +++ b/drivers/net/ethernet/marvell/Kconfig @@ -59,6 +59,7 @@ config MVNETA_BM_ENABLE config MVNETA tristate "Marvell Armada 370/38x/XP/37xx network interface support" depends on ARCH_MVEBU || COMPILE_TEST + select MVGMAC select MVMDIO select PHYLINK select PAGE_POOL @@ -71,6 +72,9 @@ config MVNETA driver, which should be used for the older Marvell SoCs (Dove, Orion, Discovery, Kirkwood). +config MVGMAC + tristate + config MVNETA_BM tristate depends on !64BIT diff --git a/drivers/net/ethernet/marvell/Makefile b/drivers/net/ethernet/marvell/Makefile index 9f88fe822555..b1442ff46abf 100644 --- a/drivers/net/ethernet/marvell/Makefile +++ b/drivers/net/ethernet/marvell/Makefile @@ -7,6 +7,7 @@ obj-$(CONFIG_MVMDIO) += mvmdio.o obj-$(CONFIG_MV643XX_ETH) += mv643xx_eth.o obj-$(CONFIG_MVNETA_BM) += mvneta_bm.o obj-$(CONFIG_MVNETA) += mvneta.o +obj-$(CONFIG_MVGMAC) += mvgmac.o obj-$(CONFIG_MVPP2) += mvpp2/ obj-$(CONFIG_PXA168_ETH) += pxa168_eth.o obj-$(CONFIG_SKGE) += skge.o diff --git a/drivers/net/ethernet/marvell/mvgmac.c b/drivers/net/ethernet/marvell/mvgmac.c new file mode 100644 index 000000000000..04da6d475ff1 --- /dev/null +++ b/drivers/net/ethernet/marvell/mvgmac.c @@ -0,0 +1,442 @@ +/* + * GMAC driver for Marvell network interfaces on Armada SoCs. + * + * Copyright (C) 2012 Marvell + * + * Rami Rosen <rosenr@marvell.com> + * Thomas Petazzoni <thomas.petazzoni@free-electrons.com> + * + * Split from mvneta and mvpp2 by Russell King. + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ +#include <linux/bitfield.h> +#include <linux/export.h> +#include <linux/io.h> +#include <linux/phylink.h> + +#include "mvgmac.h" + +enum { + /* N = Neta, 21 = PPV2.1, 22 = PPV2.2 */ + /* N: 0-14 21: 0,2-15 22: 0-14 */ + GMAC_CTRL0_REG = 0x00, + GMAC_CTRL0_PORT_ENABLE = BIT(0), + GMAC_CTRL0_PORT_1000BASE_X = BIT(1), + GMAC_CTRL0_MAX_RX_SIZE_SHIFT = 2, + GMAC_CTRL0_MAX_RX_SIZE_MASK = 0x1fff << GMAC_CTRL0_MAX_RX_SIZE_SHIFT, + GMAC_CTRL0_MIB_CNTR_ENABLE = BIT(15), + + /* N: 21: 1,5,6 22: */ + GMAC_CTRL1_REG = 0x04, + GMAC_CTRL1_PERIODIC_XON_ENABLE = BIT(1), + GMAC_CTRL1_GMII_LB_ENABLE = BIT(5), + GMAC_CTRL1_PCS_LB_ENABLE = BIT(6), + + /* ALL: 0,3,4,6 */ + GMAC_CTRL2_REG = 0x08, + GMAC_CTRL2_INBAND_AN_SGMII = BIT(0), + GMAC_CTRL2_PCS_ENABLE = BIT(3), + GMAC_CTRL2_PORT_RGMII = BIT(4), + GMAC_CTRL2_PORT_RESET = BIT(6), + + /* N:0-9,11-13 21:0,1,5-7,9,12,13 22:0-7,9-15 */ + /* 22 bit 2 - EN_PCS_AN */ + GMAC_ANEG_REG = 0x0c, + GMAC_ANEG_FORCE_LINK_DOWN = BIT(0), + GMAC_ANEG_FORCE_LINK_PASS = BIT(1), + GMAC_ANEG_INBAND_AN_ENABLE = BIT(2), + GMAC_ANEG_AN_BYPASS_ENABLE = BIT(3), + GMAC_ANEG_INBAND_RESTART_AN = BIT(4), + GMAC_ANEG_MII_SPEED = BIT(5), + GMAC_ANEG_GMII_SPEED = BIT(6), + GMAC_ANEG_AN_SPEED_ENABLE = BIT(7), + GMAC_ANEG_CONFIG_FLOW_CTRL = BIT(8), + GMAC_ANEG_ADVERT_SYM_FLOW_CTRL = BIT(9), + GMAC_ANEG_ADVERT_ASYM_FLOW_CTRL = BIT(10), + GMAC_ANEG_AN_FLOW_CTRL_ENABLE = BIT(11), + GMAC_ANEG_FULL_DUPLEX = BIT(12), + GMAC_ANEG_AN_DUPLEX_ENABLE = BIT(13), + /* pp22: bit 14 - phy mode */ + /* pp22: bit 15 - choose sample tx config */ + + GMAC_STATUS_REG = 0x10, + MVGMAC_LINK_UP = BIT(0), + MVGMAC_SPEED_1000 = BIT(1), + MVGMAC_SPEED_100 = BIT(2), + MVGMAC_FULL_DUPLEX = BIT(3), + MVGMAC_RX_FLOW_CTRL_ENABLE = BIT(4), + MVGMAC_TX_FLOW_CTRL_ENABLE = BIT(5), + MVGMAC_RX_FLOW_CTRL_ACTIVE = BIT(6), + MVGMAC_TX_FLOW_CTRL_ACTIVE = BIT(7), + MVGMAC_AN_COMPLETE = BIT(11), + MVGMAC_SYNC_OK = BIT(14), + + /* N: 21:6-13 22: */ + GMAC_FIFO_CFG1_REG = 0x1c, + GMAC_FIFO_CFG1_TX_MIN_TH_SHIFT = 6, + GMAC_FIFO_CFG1_TX_MIN_TH_MASK = 0x7f << + GMAC_FIFO_CFG1_TX_MIN_TH_SHIFT, + + /* N:1 21: 22:0,3-7 */ + GMAC_CTRL4_REG = 0x90, + GMAC_CTRL4_EXT_PIN_GMII_SEL = BIT(0), + GMAC_CTRL4_SHORT_PREAMBLE_ENABLE = BIT(1), + GMAC_CTRL4_FC_RX_ENABLE = BIT(3), + GMAC_CTRL4_FC_TX_ENABLE = BIT(4), + GMAC_CTRL4_DP_CLK_SEL = BIT(5), + GMAC_CTRL4_SYNC_BYPASS = BIT(6), + GMAC_CTRL4_QSGMII_BYPASS = BIT(7), + + GMAC_LPI_CTRL0_REG = 0xc0, + GMAC_LPI_CTRL0_TS = 0xff << 8, + GMAC_LPI_CTRL1_REG = 0xc4, + GMAC_LPI_CTRL1_REQ_EN = BIT(0), + GMAC_LPI_CTRL2_REG = 0xc8, + GMAC_LPI_STATUS_REG = 0xcc, + GMAC_LPI_CNTR_REG = 0xd0, +}; + +#define insert(var, mask, val) ({ \ + u32 __mask = mask; \ + ((var) & ~(__mask)) | (((val) << __ffs(__mask)) & (__mask)); \ +}) + +static void mvgmac_modify(void __iomem *reg, u32 mask, u32 val) +{ + u32 v; + + val &= mask; + v = readl_relaxed(reg) & ~mask; + writel_relaxed(v | val, reg); +} + +#define mvgmac_set(reg, val) mvgmac_modify(reg, val, val) +#define mvgmac_clear(reg, val) mvgmac_modify(reg, val, 0) + +/* Change maximum receive size of the port. */ +void mvgmac_set_max_rx_size(struct mvgmac *gmac, size_t max_rx_size) +{ + int size = (max_rx_size - MARVELL_HEADER_SIZE) / 2; + + mvgmac_modify(gmac->base + GMAC_CTRL0_REG, + GMAC_CTRL0_MAX_RX_SIZE_MASK, + FIELD_PREP(GMAC_CTRL0_MAX_RX_SIZE_MASK, size)); +} +EXPORT_SYMBOL_GPL(mvgmac_set_max_rx_size); + +/* Enable the port by setting the port enable bit of the MAC control register */ +void mvgmac_port_enable(struct mvgmac *gmac) +{ + mvgmac_set(gmac->base + GMAC_CTRL0_REG, + GMAC_CTRL0_PORT_ENABLE | GMAC_CTRL0_MIB_CNTR_ENABLE); +} +EXPORT_SYMBOL_GPL(mvgmac_port_enable); + +/* Disable the port */ +void mvgmac_port_disable(struct mvgmac *gmac) +{ + mvgmac_clear(gmac->base + GMAC_CTRL0_REG, GMAC_CTRL0_PORT_ENABLE); +} +EXPORT_SYMBOL_GPL(mvgmac_port_disable); + +int mvgmac_configure(struct mvgmac *gmac, phy_interface_t phy_mode) +{ + u32 ctrl4; + + switch (phy_mode) { + case PHY_INTERFACE_MODE_QSGMII: + ctrl4 = 0; + break; + + case PHY_INTERFACE_MODE_SGMII: + ctrl4 = GMAC_CTRL4_QSGMII_BYPASS; + break; + + case PHY_INTERFACE_MODE_RGMII: + case PHY_INTERFACE_MODE_RGMII_ID: + case PHY_INTERFACE_MODE_RGMII_RXID: + case PHY_INTERFACE_MODE_RGMII_TXID: + ctrl4 = GMAC_CTRL4_QSGMII_BYPASS | + GMAC_CTRL4_EXT_PIN_GMII_SEL; + break; + + default: + return -EINVAL; + } + + if (gmac->version == MVGMAC_PP21) { + /* Min. TX threshold must be less than minimum packet length */ + mvgmac_modify(gmac->base + GMAC_FIFO_CFG1_REG, + GMAC_FIFO_CFG1_TX_MIN_TH_MASK, + FIELD_PREP(GMAC_FIFO_CFG1_TX_MIN_TH_MASK, + 64 - 4 - 2)); + } else if (gmac->version == MVGMAC_PP22) { + mvgmac_modify(gmac->base + GMAC_CTRL4_REG, + GMAC_CTRL4_DP_CLK_SEL | GMAC_CTRL4_SYNC_BYPASS | + GMAC_CTRL4_QSGMII_BYPASS | + GMAC_CTRL4_EXT_PIN_GMII_SEL, + GMAC_CTRL4_SYNC_BYPASS | ctrl4); + } + + return 0; +} +EXPORT_SYMBOL_GPL(mvgmac_configure); + +void mvgmac_link_unforce(struct mvgmac *gmac) +{ + mvgmac_clear(gmac->base + GMAC_ANEG_REG, + GMAC_ANEG_FORCE_LINK_PASS | GMAC_ANEG_FORCE_LINK_DOWN); +} +EXPORT_SYMBOL_GPL(mvgmac_link_unforce); + +void mvgmac_link_force_down(struct mvgmac *gmac) +{ + mvgmac_modify(gmac->base + GMAC_ANEG_REG, + GMAC_ANEG_FORCE_LINK_PASS | GMAC_ANEG_FORCE_LINK_DOWN, + GMAC_ANEG_FORCE_LINK_DOWN); +} +EXPORT_SYMBOL_GPL(mvgmac_link_force_down); + +void mvgmac_link_down(struct mvgmac *gmac, int mode) +{ + if (!phylink_autoneg_inband(mode)) + mvgmac_link_force_down(gmac); +} +EXPORT_SYMBOL_GPL(mvgmac_link_down); + +void mvgmac_link_up(struct mvgmac *gmac, int mode, int speed, int duplex, + bool tx_pause, bool rx_pause) +{ + u32 an_mask, an_val, ctrl4; + + if (gmac->version == MVGMAC_NETA) { + an_mask = GMAC_ANEG_CONFIG_FLOW_CTRL; + an_val = FIELD_PREP(GMAC_ANEG_CONFIG_FLOW_CTRL, + tx_pause || rx_pause); + } else { + an_mask = 0; + an_val = 0; + } + + if (!phylink_autoneg_inband(mode)) { + an_mask |= GMAC_ANEG_FORCE_LINK_DOWN | + GMAC_ANEG_FORCE_LINK_PASS | + GMAC_ANEG_MII_SPEED | GMAC_ANEG_GMII_SPEED | + GMAC_ANEG_FULL_DUPLEX; + an_val |= GMAC_ANEG_FORCE_LINK_PASS; + + if (speed == SPEED_1000 || speed == SPEED_2500) + an_val |= GMAC_ANEG_GMII_SPEED; + else if (speed == SPEED_100) + an_val |= GMAC_ANEG_MII_SPEED; + + if (duplex == DUPLEX_FULL) + an_val |= GMAC_ANEG_FULL_DUPLEX; + } + + if (an_mask) + mvgmac_modify(gmac->base + GMAC_ANEG_REG, an_mask, an_val); + + if (gmac->version == MVGMAC_PP22) { + ctrl4 = readl_relaxed(gmac->base + GMAC_CTRL4_REG); + ctrl4 = insert(ctrl4, GMAC_CTRL4_FC_TX_ENABLE, tx_pause); + ctrl4 = insert(ctrl4, GMAC_CTRL4_FC_RX_ENABLE, rx_pause); + writel_relaxed(ctrl4, gmac->base + GMAC_CTRL4_REG); + } +} +EXPORT_SYMBOL_GPL(mvgmac_link_up); + +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 phylink_pcs *pcs, + struct phylink_link_state *state) +{ + struct mvgmac *gmac = pcs_to_mvgmac(pcs); + u32 gmac_stat = readl_relaxed(gmac->base + GMAC_STATUS_REG); + + if (gmac_stat & MVGMAC_SPEED_1000) + state->speed = + state->interface == PHY_INTERFACE_MODE_2500BASEX ? + SPEED_2500 : SPEED_1000; + else if (gmac_stat & MVGMAC_SPEED_100) + state->speed = SPEED_100; + else + state->speed = SPEED_10; + + state->an_complete = !!(gmac_stat & MVGMAC_AN_COMPLETE); + state->link = !!(gmac_stat & MVGMAC_LINK_UP); + state->duplex = !!(gmac_stat & MVGMAC_FULL_DUPLEX); + + if (gmac_stat & MVGMAC_RX_FLOW_CTRL_ENABLE) + state->pause |= MLO_PAUSE_RX; + if (gmac_stat & MVGMAC_TX_FLOW_CTRL_ENABLE) + state->pause |= MLO_PAUSE_TX; +} +EXPORT_SYMBOL_GPL(mvgmac_pcs_get_state); + +void mvgmac_pcs_an_restart(struct phylink_pcs *pcs) +{ + struct mvgmac *gmac = pcs_to_mvgmac(pcs); + u32 gmac_an = readl_relaxed(gmac->base + GMAC_ANEG_REG); + + writel_relaxed(gmac_an | GMAC_ANEG_INBAND_RESTART_AN, + gmac->base + GMAC_ANEG_REG); + writel_relaxed(gmac_an & ~GMAC_ANEG_INBAND_RESTART_AN, + gmac->base + GMAC_ANEG_REG); +} +EXPORT_SYMBOL_GPL(mvgmac_pcs_an_restart); + +int mvgmac_pcs_config(struct phylink_pcs *pcs, unsigned int mode, + phy_interface_t interface, + const unsigned long *advertising, + bool permit_pause_to_mac) +{ + struct mvgmac *gmac = pcs_to_mvgmac(pcs); + u32 mask, val, an, old_an, changed; + + mask = GMAC_ANEG_INBAND_AN_ENABLE | + GMAC_ANEG_INBAND_RESTART_AN | + GMAC_ANEG_AN_SPEED_ENABLE | + GMAC_ANEG_AN_FLOW_CTRL_ENABLE | + GMAC_ANEG_AN_DUPLEX_ENABLE; + + if (phylink_autoneg_inband(mode)) { + mask |= GMAC_ANEG_MII_SPEED | + GMAC_ANEG_GMII_SPEED | + GMAC_ANEG_FULL_DUPLEX; + val = GMAC_ANEG_INBAND_AN_ENABLE; + + if (interface == PHY_INTERFACE_MODE_SGMII) { + /* SGMII mode receives the speed and duplex from PHY */ + val |= GMAC_ANEG_AN_SPEED_ENABLE | + GMAC_ANEG_AN_DUPLEX_ENABLE; + } else { + /* 802.3z mode has fixed speed and duplex */ + val |= GMAC_ANEG_GMII_SPEED | + GMAC_ANEG_FULL_DUPLEX; + + /* The FLOW_CTRL_ENABLE bit selects either the hardware + * automatically or the GMAC_ANEG_FLOW_CTRL manually + * controls the GMAC pause mode. + */ + if (permit_pause_to_mac) + val |= GMAC_ANEG_AN_FLOW_CTRL_ENABLE; + + /* Update the advertisement bits */ + mask |= GMAC_ANEG_ADVERT_SYM_FLOW_CTRL; + if (phylink_test(advertising, Pause)) + val |= GMAC_ANEG_ADVERT_SYM_FLOW_CTRL; + if (gmac->version == MVGMAC_PP22) { + mask |= GMAC_ANEG_ADVERT_ASYM_FLOW_CTRL; + if (phylink_test(advertising, Asym_Pause)) + val |= GMAC_ANEG_ADVERT_ASYM_FLOW_CTRL; + } + } + } else { + /* Phy or fixed speed - disable in-band AN modes */ + val = 0; + } + + old_an = an = readl_relaxed(gmac->base + GMAC_ANEG_REG); + an = (an & ~mask) | val; + changed = old_an ^ an; + if (changed) + writel_relaxed(an, gmac->base + GMAC_ANEG_REG); + + /* We are only interested in the advertisement bits changing */ + return !!(changed & (GMAC_ANEG_ADVERT_SYM_FLOW_CTRL | + GMAC_ANEG_ADVERT_ASYM_FLOW_CTRL)); +} +EXPORT_SYMBOL_GPL(mvgmac_pcs_config); + +void mvgmac_config_mac(struct mvgmac *gmac, unsigned int mode, + const struct phylink_link_state *state) +{ + u32 new_ctrl0, gmac_ctrl0 = readl_relaxed(gmac->base + GMAC_CTRL0_REG); + u32 new_ctrl2, gmac_ctrl2 = readl_relaxed(gmac->base + GMAC_CTRL2_REG); + u32 new_ctrl4, gmac_ctrl4 = readl_relaxed(gmac->base + GMAC_CTRL4_REG); + + new_ctrl0 = gmac_ctrl0 & ~GMAC_CTRL0_PORT_1000BASE_X; + new_ctrl2 = gmac_ctrl2 & ~(GMAC_CTRL2_INBAND_AN_SGMII | + GMAC_CTRL2_PORT_RESET); + new_ctrl4 = gmac_ctrl4; + + /* Even though it might look weird, when we're configured in + * SGMII or QSGMII mode, the RGMII bit needs to be set. + */ + new_ctrl2 |= GMAC_CTRL2_PORT_RGMII; + + if (state->interface == PHY_INTERFACE_MODE_QSGMII || + state->interface == PHY_INTERFACE_MODE_SGMII || + phy_interface_mode_is_8023z(state->interface)) + new_ctrl2 |= GMAC_CTRL2_PCS_ENABLE; + + if (!phylink_autoneg_inband(mode)) { + /* Phy or fixed speed - nothing to do, leave the + * configured speed, duplex and flow control as-is. + */ + } else if (state->interface == PHY_INTERFACE_MODE_SGMII) { + /* SGMII mode receives the state from the PHY */ + new_ctrl2 |= GMAC_CTRL2_INBAND_AN_SGMII; + } else { + /* 802.3z negotiation - 1000BaseX */ + new_ctrl0 |= GMAC_CTRL0_PORT_1000BASE_X; + } + + if (gmac->version == MVGMAC_NETA) { + /* When at 2.5G, the link partner can send frames with + * shortened preambles. + */ + new_ctrl4 &= ~GMAC_CTRL4_SHORT_PREAMBLE_ENABLE; + if (state->interface == PHY_INTERFACE_MODE_2500BASEX) + new_ctrl4 |= GMAC_CTRL4_SHORT_PREAMBLE_ENABLE; + } + + if (new_ctrl0 != gmac_ctrl0) + writel_relaxed(new_ctrl0, gmac->base + GMAC_CTRL0_REG); + if (new_ctrl2 != gmac_ctrl2) + writel_relaxed(new_ctrl2, gmac->base + GMAC_CTRL2_REG); + if (new_ctrl4 != gmac_ctrl4) + writel_relaxed(new_ctrl4, gmac->base + GMAC_CTRL4_REG); + + if (gmac_ctrl2 & GMAC_CTRL2_PORT_RESET) { + while ((readl_relaxed(gmac->base + GMAC_CTRL2_REG) & + GMAC_CTRL2_PORT_RESET) != 0) + continue; + } +} +EXPORT_SYMBOL_GPL(mvgmac_config_mac); + +void mvgmac_set_lpi_ts(struct mvgmac *gmac, unsigned int ts) +{ + if (!(readl_relaxed(gmac->base + GMAC_STATUS_REG) & MVGMAC_SPEED_1000)) + ts = DIV_ROUND_UP(ts, 10); + + if (ts > 255) + ts = 255; + + mvgmac_modify(gmac->base + GMAC_LPI_CTRL0_REG, + GMAC_LPI_CTRL0_TS, + FIELD_PREP(GMAC_LPI_CTRL0_TS, ts)); +} +EXPORT_SYMBOL_GPL(mvgmac_set_lpi_ts); + +void mvgmac_set_eee(struct mvgmac *gmac, bool enable) +{ + mvgmac_modify(gmac->base + GMAC_LPI_CTRL1_REG, + GMAC_LPI_CTRL1_REQ_EN, + FIELD_PREP(GMAC_LPI_CTRL1_REQ_EN, enable)); +} +EXPORT_SYMBOL_GPL(mvgmac_set_eee); + +MODULE_DESCRIPTION("Marvell GMAC driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/net/ethernet/marvell/mvgmac.h b/drivers/net/ethernet/marvell/mvgmac.h new file mode 100644 index 000000000000..36ed203094be --- /dev/null +++ b/drivers/net/ethernet/marvell/mvgmac.h @@ -0,0 +1,56 @@ +#ifndef MVGMAC_H +#define MVGMAC_H + +#include <linux/phylink.h> + +/* The two bytes Marvell header. Either contains a special value used by + * Marvell switches when a specific hardware mode is enabled (not supported + * by this driver) or is filled automatically by zeroes on the RX side. + * Those two bytes being at the front of the Ethernet header, they allow + * to have the IP header aligned on a 4 bytes boundary automatically: the + * hardware skips those two bytes on its own. + */ +#define MARVELL_HEADER_SIZE 2 + +enum { + /* GMAC version */ + MVGMAC_NETA, + MVGMAC_PP21, + MVGMAC_PP22, +}; + +struct mvgmac { + void __iomem *base; + unsigned int version; + struct phylink_pcs pcs; +}; + +static inline struct mvgmac *pcs_to_mvgmac(struct phylink_pcs *pcs) +{ + return container_of(pcs, struct mvgmac, pcs); +} + +void mvgmac_set_max_rx_size(struct mvgmac *gmac, size_t max_rx_size); +void mvgmac_port_enable(struct mvgmac *gmac); +void mvgmac_port_disable(struct mvgmac *gmac); +int mvgmac_configure(struct mvgmac *gmac, phy_interface_t phy_mode); +void mvgmac_link_unforce(struct mvgmac *gmac); +void mvgmac_link_force_down(struct mvgmac *gmac); +void mvgmac_link_down(struct mvgmac *gmac, int mode); +void mvgmac_link_up(struct mvgmac *gmac, int mode, int speed, int duplex, + bool tx_pause, bool rx_pause); +bool mvgmac_link_is_up(struct mvgmac *gmac); +void mvgmac_pcs_get_state(struct phylink_pcs *pcs, + struct phylink_link_state *state); +void mvgmac_pcs_an_restart(struct phylink_pcs *pcs); +int mvgmac_pcs_config(struct phylink_pcs *pcs, unsigned int mode, + phy_interface_t interface, + const unsigned long *advertising, + bool permit_pause_to_mac); +void mvgmac_config_mac(struct mvgmac *gmac, unsigned int mode, + const struct phylink_link_state *state); + +void mvgmac_set_lpi_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 5a7bdca22a63..0e9c418a2171 100644 --- a/drivers/net/ethernet/marvell/mvneta.c +++ b/drivers/net/ethernet/marvell/mvneta.c @@ -34,6 +34,7 @@ #include <linux/skbuff.h> #include <net/hwbm.h> #include "mvneta_bm.h" +#include "mvgmac.h" #include <net/ip.h> #include <net/ipv6.h> #include <net/tso.h> @@ -193,43 +194,7 @@ #define MVNETA_RXQ_ENABLE_MASK 0x000000ff #define MVETH_TXQ_TOKEN_COUNT_REG(q) (0x2700 + ((q) << 4)) #define MVETH_TXQ_TOKEN_CFG_REG(q) (0x2704 + ((q) << 4)) -#define MVNETA_GMAC_CTRL_0 0x2c00 -#define MVNETA_GMAC_MAX_RX_SIZE_SHIFT 2 -#define MVNETA_GMAC_MAX_RX_SIZE_MASK 0x7ffc -#define MVNETA_GMAC0_PORT_1000BASE_X BIT(1) -#define MVNETA_GMAC0_PORT_ENABLE BIT(0) -#define MVNETA_GMAC_CTRL_2 0x2c08 -#define MVNETA_GMAC2_INBAND_AN_ENABLE BIT(0) -#define MVNETA_GMAC2_PCS_ENABLE BIT(3) -#define MVNETA_GMAC2_PORT_RGMII BIT(4) -#define MVNETA_GMAC2_PORT_RESET BIT(6) -#define MVNETA_GMAC_STATUS 0x2c10 -#define MVNETA_GMAC_LINK_UP BIT(0) -#define MVNETA_GMAC_SPEED_1000 BIT(1) -#define MVNETA_GMAC_SPEED_100 BIT(2) -#define MVNETA_GMAC_FULL_DUPLEX BIT(3) -#define MVNETA_GMAC_RX_FLOW_CTRL_ENABLE BIT(4) -#define MVNETA_GMAC_TX_FLOW_CTRL_ENABLE BIT(5) -#define MVNETA_GMAC_RX_FLOW_CTRL_ACTIVE BIT(6) -#define MVNETA_GMAC_TX_FLOW_CTRL_ACTIVE BIT(7) -#define MVNETA_GMAC_AN_COMPLETE BIT(11) -#define MVNETA_GMAC_SYNC_OK BIT(14) -#define MVNETA_GMAC_AUTONEG_CONFIG 0x2c0c -#define MVNETA_GMAC_FORCE_LINK_DOWN BIT(0) -#define MVNETA_GMAC_FORCE_LINK_PASS BIT(1) -#define MVNETA_GMAC_INBAND_AN_ENABLE BIT(2) -#define MVNETA_GMAC_AN_BYPASS_ENABLE BIT(3) -#define MVNETA_GMAC_INBAND_RESTART_AN BIT(4) -#define MVNETA_GMAC_CONFIG_MII_SPEED BIT(5) -#define MVNETA_GMAC_CONFIG_GMII_SPEED BIT(6) -#define MVNETA_GMAC_AN_SPEED_EN BIT(7) -#define MVNETA_GMAC_CONFIG_FLOW_CTRL BIT(8) -#define MVNETA_GMAC_ADVERT_SYM_FLOW_CTRL BIT(9) -#define MVNETA_GMAC_AN_FLOW_CTRL_EN BIT(11) -#define MVNETA_GMAC_CONFIG_FULL_DUPLEX BIT(12) -#define MVNETA_GMAC_AN_DUPLEX_EN BIT(13) -#define MVNETA_GMAC_CTRL_4 0x2c90 -#define MVNETA_GMAC4_SHORT_PREAMBLE_ENABLE BIT(1) +#define MVNETA_GMAC_BASE 0x2c00 #define MVNETA_MIB_COUNTERS_BASE 0x3000 #define MVNETA_MIB_LATE_COLLISION 0x7c #define MVNETA_DA_FILT_SPEC_MCAST 0x3400 @@ -253,12 +218,6 @@ #define MVNETA_TXQ_TOKEN_SIZE_REG(q) (0x3e40 + ((q) << 2)) #define MVNETA_TXQ_TOKEN_SIZE_MAX 0x7fffffff -#define MVNETA_LPI_CTRL_0 0x2cc0 -#define MVNETA_LPI_CTRL_1 0x2cc4 -#define MVNETA_LPI_REQUEST_ENABLE BIT(0) -#define MVNETA_LPI_CTRL_2 0x2cc8 -#define MVNETA_LPI_STATUS 0x2ccc - #define MVNETA_CAUSE_TXQ_SENT_DESC_ALL_MASK 0xff /* Descriptor ring Macros */ @@ -280,7 +239,7 @@ * boundary automatically: the hardware skips those two bytes on its * own. */ -#define MVNETA_MH_SIZE 2 +#define MVNETA_MH_SIZE MARVELL_HEADER_SIZE #define MVNETA_VLAN_TAG_LEN 4 @@ -501,6 +460,7 @@ struct mvneta_port { struct phylink_config phylink_config; struct phy *comphy; + struct mvgmac gmac; struct mvneta_bm *bm_priv; struct mvneta_bm_pool *pool_long; struct mvneta_bm_pool *pool_short; @@ -509,6 +469,7 @@ struct mvneta_port { bool eee_enabled; bool eee_active; bool tx_lpi_enabled; + u32 tx_lpi_timer; u64 ethtool_stats[ARRAY_SIZE(mvneta_statistics)]; @@ -891,19 +852,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, @@ -1308,26 +1256,10 @@ static void mvneta_port_down(struct mvneta_port *pp) udelay(200); } -/* Enable the port by setting the port enable bit of the MAC control register */ -static void mvneta_port_enable(struct mvneta_port *pp) -{ - u32 val; - - /* Enable port */ - val = mvreg_read(pp, MVNETA_GMAC_CTRL_0); - val |= MVNETA_GMAC0_PORT_ENABLE; - mvreg_write(pp, MVNETA_GMAC_CTRL_0, val); -} - /* Disable the port and wait for about 200 usec before retuning */ static void mvneta_port_disable(struct mvneta_port *pp) { - u32 val; - - /* Reset the Enable bit in the Serial Control Register */ - val = mvreg_read(pp, MVNETA_GMAC_CTRL_0); - val &= ~MVNETA_GMAC0_PORT_ENABLE; - mvreg_write(pp, MVNETA_GMAC_CTRL_0, val); + mvgmac_port_disable(&pp->gmac); udelay(200); } @@ -3123,9 +3055,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 @@ -3617,11 +3549,11 @@ static void mvneta_start_dev(struct mvneta_port *pp) WARN_ON(mvneta_config_interface(pp, pp->phy_interface)); - mvneta_max_rx_size_set(pp, pp->pkt_size); + mvgmac_set_max_rx_size(&pp->gmac, pp->pkt_size); mvneta_txq_max_tx_size_set(pp, pp->pkt_size); /* start the Rx/Tx activity */ - mvneta_port_enable(pp); + mvgmac_port_enable(&pp->gmac); if (!pp->neta_armada3700) { /* Enable polling on the port */ @@ -3819,215 +3751,114 @@ static int mvneta_set_mac_addr(struct net_device *dev, void *addr) return 0; } -static void mvneta_validate(struct phylink_config *config, - unsigned long *supported, - struct phylink_link_state *state) +static int mvneta_pcs_validate(struct phylink_pcs *pcs, unsigned int mode, + unsigned long *supported, + const struct phylink_link_state *state) { - __ETHTOOL_DECLARE_LINK_MODE_MASK(mask) = { 0, }; - /* We only support QSGMII, SGMII, 802.3z and RGMII modes. * When in 802.3z mode, we must have AN enabled: * "Bit 2 Field InBandAnEn In-band Auto-Negotiation enable. ... * When <PortType> = 1 (1000BASE-X) this field must be set to 1." */ if (phy_interface_mode_is_8023z(state->interface) && - !phylink_test(state->advertising, Autoneg)) { - linkmode_zero(supported); - return; - } + !phylink_test(state->advertising, Autoneg)) + return -EINVAL; - /* Allow all the expected bits */ - phylink_set(mask, Autoneg); - phylink_set_port_modes(mask); + return 0; +} - /* Asymmetric pause is unsupported */ - phylink_set(mask, Pause); +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) +{ + /* We should never see Asym_Pause set */ + WARN_ON(phylink_test(advertising, Asym_Pause)); - /* Half-duplex at speeds higher than 100Mbit is unsupported */ - if (state->interface != PHY_INTERFACE_MODE_2500BASEX) { - phylink_set(mask, 1000baseT_Full); - phylink_set(mask, 1000baseX_Full); - } + return mvgmac_pcs_config(pcs, mode, interface, advertising, + permit_pause_to_mac); +} - if (state->interface == PHY_INTERFACE_MODE_2500BASEX) { - phylink_set(mask, 2500baseT_Full); - phylink_set(mask, 2500baseX_Full); - } +static const struct phylink_pcs_ops mvneta_phylink_pcs_ops = { + .pcs_validate = mvneta_pcs_validate, + .pcs_get_state = mvgmac_pcs_get_state, + .pcs_config = mvneta_pcs_config, + .pcs_an_restart = mvgmac_pcs_an_restart, +}; - if (!phy_interface_mode_is_8023z(state->interface)) { - /* 10M and 100M are only supported in non-802.3z mode */ - phylink_set(mask, 10baseT_Half); - phylink_set(mask, 10baseT_Full); - phylink_set(mask, 100baseT_Half); - phylink_set(mask, 100baseT_Full); - } +static struct phylink_pcs *mvneta_mac_select_pcs(struct phylink_config *config, + phy_interface_t interface) +{ + struct net_device *ndev = to_net_dev(config->dev); + struct mvneta_port *pp = netdev_priv(ndev); - linkmode_and(supported, supported, mask); - linkmode_and(state->advertising, state->advertising, mask); + return &pp->gmac.pcs; } -static void mvneta_mac_pcs_get_state(struct phylink_config *config, - struct phylink_link_state *state) +static int mvneta_mac_prepare(struct phylink_config *config, unsigned int mode, + phy_interface_t interface) { struct net_device *ndev = to_net_dev(config->dev); struct mvneta_port *pp = netdev_priv(ndev); - u32 gmac_stat; - gmac_stat = mvreg_read(pp, MVNETA_GMAC_STATUS); + if (pp->phy_interface != interface || + phylink_autoneg_inband(mode)) { + /* Force the link down when changing the interface or if in + * in-band mode. According to Armada 370 documentation, we + * can only change the port mode and in-band enable when the + * link is down. + */ + mvgmac_link_force_down(&pp->gmac); + } + + if (pp->phy_interface != interface) + WARN_ON(phy_power_off(pp->comphy)); - if (gmac_stat & MVNETA_GMAC_SPEED_1000) - state->speed = - state->interface == PHY_INTERFACE_MODE_2500BASEX ? - SPEED_2500 : SPEED_1000; - else if (gmac_stat & MVNETA_GMAC_SPEED_100) - state->speed = SPEED_100; - else - state->speed = SPEED_10; + /* Enable the 1ms clock */ + if (phylink_autoneg_inband(mode)) { + unsigned long rate = clk_get_rate(pp->clk); - state->an_complete = !!(gmac_stat & MVNETA_GMAC_AN_COMPLETE); - state->link = !!(gmac_stat & MVNETA_GMAC_LINK_UP); - state->duplex = !!(gmac_stat & MVNETA_GMAC_FULL_DUPLEX); + mvreg_write(pp, MVNETA_GMAC_CLOCK_DIVIDER, + MVNETA_GMAC_1MS_CLOCK_ENABLE | (rate / 1000)); + } - state->pause = 0; - if (gmac_stat & MVNETA_GMAC_RX_FLOW_CTRL_ENABLE) - state->pause |= MLO_PAUSE_RX; - if (gmac_stat & MVNETA_GMAC_TX_FLOW_CTRL_ENABLE) - state->pause |= MLO_PAUSE_TX; + return 0; } -static void mvneta_mac_an_restart(struct phylink_config *config) +static void mvneta_mac_config(struct phylink_config *config, unsigned int mode, + const struct phylink_link_state *state) { struct net_device *ndev = to_net_dev(config->dev); struct mvneta_port *pp = netdev_priv(ndev); - u32 gmac_an = mvreg_read(pp, MVNETA_GMAC_AUTONEG_CONFIG); - mvreg_write(pp, MVNETA_GMAC_AUTONEG_CONFIG, - gmac_an | MVNETA_GMAC_INBAND_RESTART_AN); - mvreg_write(pp, MVNETA_GMAC_AUTONEG_CONFIG, - gmac_an & ~MVNETA_GMAC_INBAND_RESTART_AN); + mvgmac_config_mac(&pp->gmac, mode, state); } -static void mvneta_mac_config(struct phylink_config *config, unsigned int mode, - const struct phylink_link_state *state) +static int mvneta_mac_finish(struct phylink_config *config, unsigned int mode, + phy_interface_t interface) { struct net_device *ndev = to_net_dev(config->dev); struct mvneta_port *pp = netdev_priv(ndev); - u32 new_ctrl0, gmac_ctrl0 = mvreg_read(pp, MVNETA_GMAC_CTRL_0); - u32 new_ctrl2, gmac_ctrl2 = mvreg_read(pp, MVNETA_GMAC_CTRL_2); - u32 new_ctrl4, gmac_ctrl4 = mvreg_read(pp, MVNETA_GMAC_CTRL_4); - u32 new_clk, gmac_clk = mvreg_read(pp, MVNETA_GMAC_CLOCK_DIVIDER); - u32 new_an, gmac_an = mvreg_read(pp, MVNETA_GMAC_AUTONEG_CONFIG); - - new_ctrl0 = gmac_ctrl0 & ~MVNETA_GMAC0_PORT_1000BASE_X; - new_ctrl2 = gmac_ctrl2 & ~(MVNETA_GMAC2_INBAND_AN_ENABLE | - MVNETA_GMAC2_PORT_RESET); - new_ctrl4 = gmac_ctrl4 & ~(MVNETA_GMAC4_SHORT_PREAMBLE_ENABLE); - new_clk = gmac_clk & ~MVNETA_GMAC_1MS_CLOCK_ENABLE; - new_an = gmac_an & ~(MVNETA_GMAC_INBAND_AN_ENABLE | - MVNETA_GMAC_INBAND_RESTART_AN | - MVNETA_GMAC_AN_SPEED_EN | - MVNETA_GMAC_ADVERT_SYM_FLOW_CTRL | - MVNETA_GMAC_AN_FLOW_CTRL_EN | - MVNETA_GMAC_AN_DUPLEX_EN); - - /* Even though it might look weird, when we're configured in - * SGMII or QSGMII mode, the RGMII bit needs to be set. - */ - new_ctrl2 |= MVNETA_GMAC2_PORT_RGMII; - - if (state->interface == PHY_INTERFACE_MODE_QSGMII || - state->interface == PHY_INTERFACE_MODE_SGMII || - phy_interface_mode_is_8023z(state->interface)) - new_ctrl2 |= MVNETA_GMAC2_PCS_ENABLE; - - if (phylink_test(state->advertising, Pause)) - new_an |= MVNETA_GMAC_ADVERT_SYM_FLOW_CTRL; + u32 clk; + /* Disable 1ms clock if not in in-band mode */ if (!phylink_autoneg_inband(mode)) { - /* Phy or fixed speed - nothing to do, leave the - * configured speed, duplex and flow control as-is. - */ - } else if (state->interface == PHY_INTERFACE_MODE_SGMII) { - /* SGMII mode receives the state from the PHY */ - new_ctrl2 |= MVNETA_GMAC2_INBAND_AN_ENABLE; - new_clk |= MVNETA_GMAC_1MS_CLOCK_ENABLE; - new_an = (new_an & ~(MVNETA_GMAC_FORCE_LINK_DOWN | - MVNETA_GMAC_FORCE_LINK_PASS | - MVNETA_GMAC_CONFIG_MII_SPEED | - MVNETA_GMAC_CONFIG_GMII_SPEED | - MVNETA_GMAC_CONFIG_FULL_DUPLEX)) | - MVNETA_GMAC_INBAND_AN_ENABLE | - MVNETA_GMAC_AN_SPEED_EN | - MVNETA_GMAC_AN_DUPLEX_EN; - } else { - /* 802.3z negotiation - only 1000base-X */ - new_ctrl0 |= MVNETA_GMAC0_PORT_1000BASE_X; - new_clk |= MVNETA_GMAC_1MS_CLOCK_ENABLE; - new_an = (new_an & ~(MVNETA_GMAC_FORCE_LINK_DOWN | - MVNETA_GMAC_FORCE_LINK_PASS | - MVNETA_GMAC_CONFIG_MII_SPEED)) | - MVNETA_GMAC_INBAND_AN_ENABLE | - MVNETA_GMAC_CONFIG_GMII_SPEED | - /* The MAC only supports FD mode */ - MVNETA_GMAC_CONFIG_FULL_DUPLEX; - - if (state->pause & MLO_PAUSE_AN && state->an_enabled) - new_an |= MVNETA_GMAC_AN_FLOW_CTRL_EN; - } - - /* Armada 370 documentation says we can only change the port mode - * and in-band enable when the link is down, so force it down - * while making these changes. We also do this for GMAC_CTRL2 - */ - if ((new_ctrl0 ^ gmac_ctrl0) & MVNETA_GMAC0_PORT_1000BASE_X || - (new_ctrl2 ^ gmac_ctrl2) & MVNETA_GMAC2_INBAND_AN_ENABLE || - (new_an ^ gmac_an) & MVNETA_GMAC_INBAND_AN_ENABLE) { - mvreg_write(pp, MVNETA_GMAC_AUTONEG_CONFIG, - (gmac_an & ~MVNETA_GMAC_FORCE_LINK_PASS) | - MVNETA_GMAC_FORCE_LINK_DOWN); + 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 */ + WARN_ON(mvneta_config_interface(pp, interface)); - /* When at 2.5G, the link partner can send frames with shortened - * preambles. + /* Allow the link to come up if in in-band mode, otherwise the + * link is forced via mac_link_down()/mac_link_up() */ - if (state->interface == PHY_INTERFACE_MODE_2500BASEX) - new_ctrl4 |= MVNETA_GMAC4_SHORT_PREAMBLE_ENABLE; - - if (pp->phy_interface != state->interface) { - if (pp->comphy) - WARN_ON(phy_power_off(pp->comphy)); - WARN_ON(mvneta_config_interface(pp, state->interface)); - } - - if (new_ctrl0 != gmac_ctrl0) - mvreg_write(pp, MVNETA_GMAC_CTRL_0, new_ctrl0); - if (new_ctrl2 != gmac_ctrl2) - mvreg_write(pp, MVNETA_GMAC_CTRL_2, new_ctrl2); - if (new_ctrl4 != gmac_ctrl4) - mvreg_write(pp, MVNETA_GMAC_CTRL_4, new_ctrl4); - if (new_clk != gmac_clk) - mvreg_write(pp, MVNETA_GMAC_CLOCK_DIVIDER, new_clk); - if (new_an != gmac_an) - mvreg_write(pp, MVNETA_GMAC_AUTONEG_CONFIG, new_an); - - if (gmac_ctrl2 & MVNETA_GMAC2_PORT_RESET) { - while ((mvreg_read(pp, MVNETA_GMAC_CTRL_2) & - MVNETA_GMAC2_PORT_RESET) != 0) - continue; - } -} - -static void mvneta_set_eee(struct mvneta_port *pp, bool enable) -{ - u32 lpi_ctl1; + if (phylink_autoneg_inband(mode)) + mvgmac_link_unforce(&pp->gmac); - 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); + return 0; } static void mvneta_mac_link_down(struct phylink_config *config, @@ -4035,19 +3866,12 @@ static void mvneta_mac_link_down(struct phylink_config *config, { struct net_device *ndev = to_net_dev(config->dev); struct mvneta_port *pp = netdev_priv(ndev); - u32 val; mvneta_port_down(pp); - - if (!phylink_autoneg_inband(mode)) { - val = mvreg_read(pp, MVNETA_GMAC_AUTONEG_CONFIG); - val &= ~MVNETA_GMAC_FORCE_LINK_PASS; - val |= MVNETA_GMAC_FORCE_LINK_DOWN; - mvreg_write(pp, MVNETA_GMAC_AUTONEG_CONFIG, val); - } + mvgmac_link_down(&pp->gmac, mode); pp->eee_active = false; - mvneta_set_eee(pp, false); + mvgmac_set_eee(&pp->gmac, false); } static void mvneta_mac_link_up(struct phylink_config *config, @@ -4058,56 +3882,23 @@ static void mvneta_mac_link_up(struct phylink_config *config, { struct net_device *ndev = to_net_dev(config->dev); struct mvneta_port *pp = netdev_priv(ndev); - u32 val; - - if (!phylink_autoneg_inband(mode)) { - val = mvreg_read(pp, MVNETA_GMAC_AUTONEG_CONFIG); - val &= ~(MVNETA_GMAC_FORCE_LINK_DOWN | - MVNETA_GMAC_CONFIG_MII_SPEED | - MVNETA_GMAC_CONFIG_GMII_SPEED | - MVNETA_GMAC_CONFIG_FLOW_CTRL | - MVNETA_GMAC_CONFIG_FULL_DUPLEX); - val |= MVNETA_GMAC_FORCE_LINK_PASS; - - if (speed == SPEED_1000 || speed == SPEED_2500) - val |= MVNETA_GMAC_CONFIG_GMII_SPEED; - else if (speed == SPEED_100) - val |= MVNETA_GMAC_CONFIG_MII_SPEED; - - if (duplex == DUPLEX_FULL) - val |= MVNETA_GMAC_CONFIG_FULL_DUPLEX; - - if (tx_pause || rx_pause) - val |= MVNETA_GMAC_CONFIG_FLOW_CTRL; - - mvreg_write(pp, MVNETA_GMAC_AUTONEG_CONFIG, val); - } else { - /* When inband doesn't cover flow control or flow control is - * disabled, we need to manually configure it. This bit will - * only have effect if MVNETA_GMAC_AN_FLOW_CTRL_EN is unset. - */ - val = mvreg_read(pp, MVNETA_GMAC_AUTONEG_CONFIG); - val &= ~MVNETA_GMAC_CONFIG_FLOW_CTRL; - - if (tx_pause || rx_pause) - val |= MVNETA_GMAC_CONFIG_FLOW_CTRL; - - mvreg_write(pp, MVNETA_GMAC_AUTONEG_CONFIG, val); - } + mvgmac_link_up(&pp->gmac, mode, speed, duplex, tx_pause, rx_pause); mvneta_port_up(pp); if (phy && pp->eee_enabled) { pp->eee_active = phy_init_eee(phy, 0) >= 0; - mvneta_set_eee(pp, pp->eee_active && pp->tx_lpi_enabled); + mvgmac_set_lpi_ts(&pp->gmac, pp->tx_lpi_timer); + mvgmac_set_eee(&pp->gmac, pp->eee_active && pp->tx_lpi_enabled); } } static const struct phylink_mac_ops mvneta_phylink_ops = { - .validate = mvneta_validate, - .mac_pcs_get_state = mvneta_mac_pcs_get_state, - .mac_an_restart = mvneta_mac_an_restart, + .validate = phylink_generic_validate, + .mac_select_pcs = mvneta_mac_select_pcs, + .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, }; @@ -4877,14 +4668,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); } @@ -4893,7 +4681,6 @@ static int mvneta_ethtool_set_eee(struct net_device *dev, struct ethtool_eee *eee) { struct mvneta_port *pp = netdev_priv(dev); - u32 lpi_ctl0; /* The Armada 37x documents do not give limits for this other than * it being an 8-bit register. @@ -4901,15 +4688,13 @@ static int mvneta_ethtool_set_eee(struct net_device *dev, 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); } @@ -5143,29 +4928,72 @@ static int mvneta_probe(struct platform_device *pdev) if (!dev) return -ENOMEM; - dev->irq = irq_of_parse_and_map(dn, 0); - if (dev->irq == 0) - return -EINVAL; + dev->tx_queue_len = MVNETA_MAX_TXD; + dev->watchdog_timeo = 5 * HZ; + dev->netdev_ops = &mvneta_netdev_ops; + dev->ethtool_ops = &mvneta_eth_tool_ops; + + pp = netdev_priv(dev); + spin_lock_init(&pp->lock); + pp->dn = dn; + + pp->rxq_def = rxq_def; + pp->indir[0] = rxq_def; err = of_get_phy_mode(dn, &phy_mode); if (err) { dev_err(&pdev->dev, "incorrect phy-mode\n"); - goto err_free_irq; + return err; } + pp->phy_interface = phy_mode; + comphy = devm_of_phy_get(&pdev->dev, dn, NULL); - if (comphy == ERR_PTR(-EPROBE_DEFER)) { - err = -EPROBE_DEFER; - goto err_free_irq; - } else if (IS_ERR(comphy)) { + if (comphy == ERR_PTR(-EPROBE_DEFER)) + return -EPROBE_DEFER; + + if (IS_ERR(comphy)) comphy = NULL; + + pp->comphy = comphy; + + pp->base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(pp->base)) + return PTR_ERR(pp->base); + + /* Get special SoC configurations */ + if (of_device_is_compatible(dn, "marvell,armada-3700-neta")) + pp->neta_armada3700 = true; + + dev->irq = irq_of_parse_and_map(dn, 0); + if (dev->irq == 0) + return -EINVAL; + + pp->clk = devm_clk_get(&pdev->dev, "core"); + if (IS_ERR(pp->clk)) + pp->clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(pp->clk)) { + err = PTR_ERR(pp->clk); + goto err_free_irq; } - pp = netdev_priv(dev); - spin_lock_init(&pp->lock); + clk_prepare_enable(pp->clk); + + pp->clk_bus = devm_clk_get(&pdev->dev, "bus"); + if (!IS_ERR(pp->clk_bus)) + clk_prepare_enable(pp->clk_bus); + + pp->gmac.base = pp->base + MVNETA_GMAC_BASE; + pp->gmac.version = MVGMAC_NETA; + pp->gmac.pcs.ops = &mvneta_phylink_pcs_ops; + + pp->tx_lpi_timer = 16; pp->phylink_config.dev = &dev->dev; pp->phylink_config.type = PHYLINK_NETDEV; + pp->phylink_config.mac_capabilities = MAC_SYM_PAUSE | MAC_10 | + MAC_100 | MAC_1000FD | MAC_2500FD; + phy_interface_set_rgmii(pp->phylink_config.supported_interfaces); __set_bit(PHY_INTERFACE_MODE_QSGMII, pp->phylink_config.supported_interfaces); @@ -5196,52 +5024,16 @@ static int mvneta_probe(struct platform_device *pdev) phy_mode, &mvneta_phylink_ops); if (IS_ERR(phylink)) { err = PTR_ERR(phylink); - goto err_free_irq; + goto err_clk; } - dev->tx_queue_len = MVNETA_MAX_TXD; - dev->watchdog_timeo = 5 * HZ; - dev->netdev_ops = &mvneta_netdev_ops; - - dev->ethtool_ops = &mvneta_eth_tool_ops; - pp->phylink = phylink; - pp->comphy = comphy; - pp->phy_interface = phy_mode; - pp->dn = dn; - - pp->rxq_def = rxq_def; - pp->indir[0] = rxq_def; - - /* Get special SoC configurations */ - if (of_device_is_compatible(dn, "marvell,armada-3700-neta")) - pp->neta_armada3700 = true; - - pp->clk = devm_clk_get(&pdev->dev, "core"); - if (IS_ERR(pp->clk)) - pp->clk = devm_clk_get(&pdev->dev, NULL); - if (IS_ERR(pp->clk)) { - err = PTR_ERR(pp->clk); - goto err_free_phylink; - } - - clk_prepare_enable(pp->clk); - - pp->clk_bus = devm_clk_get(&pdev->dev, "bus"); - if (!IS_ERR(pp->clk_bus)) - clk_prepare_enable(pp->clk_bus); - - pp->base = devm_platform_ioremap_resource(pdev, 0); - if (IS_ERR(pp->base)) { - err = PTR_ERR(pp->base); - goto err_clk; - } /* Alloc per-cpu port structure */ pp->ports = alloc_percpu(struct mvneta_pcpu_port); if (!pp->ports) { err = -ENOMEM; - goto err_clk; + goto err_free_phylink; } /* Alloc per-cpu stats */ @@ -5385,12 +5177,12 @@ err_netdev: free_percpu(pp->stats); err_free_ports: free_percpu(pp->ports); -err_clk: - clk_disable_unprepare(pp->clk_bus); - clk_disable_unprepare(pp->clk); err_free_phylink: if (pp->phylink) phylink_destroy(pp->phylink); +err_clk: + clk_disable_unprepare(pp->clk_bus); + clk_disable_unprepare(pp->clk); err_free_irq: irq_dispose_mapping(dev->irq); return err; diff --git a/drivers/net/ethernet/marvell/mvpp2/mvpp2.h b/drivers/net/ethernet/marvell/mvpp2/mvpp2.h index cf8acabb90ac..ad73a488fc5f 100644 --- a/drivers/net/ethernet/marvell/mvpp2/mvpp2.h +++ b/drivers/net/ethernet/marvell/mvpp2/mvpp2.h @@ -1239,7 +1239,8 @@ struct mvpp2_port { phy_interface_t phy_interface; struct phylink *phylink; struct phylink_config phylink_config; - struct phylink_pcs phylink_pcs; + struct phylink_pcs pcs_gmac; + struct phylink_pcs pcs_xlg; struct phy *comphy; struct mvpp2_bm_pool *pool_long; diff --git a/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c b/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c index 6da8a595026b..ffba977ec57a 100644 --- a/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c +++ b/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c @@ -1488,6 +1488,7 @@ static bool mvpp2_port_supports_rgmii(struct mvpp2_port *port) static bool mvpp2_is_xlg(phy_interface_t interface) { return interface == PHY_INTERFACE_MODE_10GBASER || + interface == PHY_INTERFACE_MODE_5GBASER || interface == PHY_INTERFACE_MODE_XAUI; } @@ -1627,6 +1628,7 @@ static int mvpp22_gop_init(struct mvpp2_port *port, phy_interface_t interface) case PHY_INTERFACE_MODE_2500BASEX: mvpp22_gop_init_sgmii(port); break; + case PHY_INTERFACE_MODE_5GBASER: case PHY_INTERFACE_MODE_10GBASER: if (!mvpp2_port_supports_xlg(port)) goto invalid_conf; @@ -2186,6 +2188,7 @@ static void mvpp22_pcs_reset_deassert(struct mvpp2_port *port, xpcs = priv->iface_base + MVPP22_XPCS_BASE(port->gop_id); switch (interface) { + case PHY_INTERFACE_MODE_5GBASER: case PHY_INTERFACE_MODE_10GBASER: val = readl(mpcs + MVPP22_MPCS_CLK_RESET); val |= MAC_CLK_RESET_MAC | MAC_CLK_RESET_SD_RX | @@ -6109,18 +6112,26 @@ static struct mvpp2_port *mvpp2_phylink_to_port(struct phylink_config *config) return container_of(config, struct mvpp2_port, phylink_config); } -static struct mvpp2_port *mvpp2_pcs_to_port(struct phylink_pcs *pcs) +static struct mvpp2_port *mvpp2_pcs_xlg_to_port(struct phylink_pcs *pcs) { - return container_of(pcs, struct mvpp2_port, phylink_pcs); + return container_of(pcs, struct mvpp2_port, pcs_xlg); +} + +static struct mvpp2_port *mvpp2_pcs_gmac_to_port(struct phylink_pcs *pcs) +{ + return container_of(pcs, struct mvpp2_port, pcs_gmac); } static void mvpp2_xlg_pcs_get_state(struct phylink_pcs *pcs, struct phylink_link_state *state) { - struct mvpp2_port *port = mvpp2_pcs_to_port(pcs); + struct mvpp2_port *port = mvpp2_pcs_xlg_to_port(pcs); u32 val; - state->speed = SPEED_10000; + if (port->phy_interface == PHY_INTERFACE_MODE_5GBASER) + state->speed = SPEED_5000; + else + state->speed = SPEED_10000; state->duplex = 1; state->an_complete = 1; @@ -6149,10 +6160,25 @@ static const struct phylink_pcs_ops mvpp2_phylink_xlg_pcs_ops = { .pcs_config = mvpp2_xlg_pcs_config, }; +static int mvpp2_gmac_pcs_validate(struct phylink_pcs *pcs, unsigned int mode, + unsigned long *supported, + const struct phylink_link_state *state) +{ + /* When in 802.3z mode, we must have AN enabled: + * Bit 2 Field InBandAnEn In-band Auto-Negotiation enable. ... + * When <PortType> = 1 (1000BASE-X) this field must be set to 1. + */ + if (phy_interface_mode_is_8023z(state->interface) && + !phylink_test(state->advertising, Autoneg)) + return -EINVAL; + + return 0; +} + static void mvpp2_gmac_pcs_get_state(struct phylink_pcs *pcs, struct phylink_link_state *state) { - struct mvpp2_port *port = mvpp2_pcs_to_port(pcs); + struct mvpp2_port *port = mvpp2_pcs_gmac_to_port(pcs); u32 val; val = readl(port->base + MVPP2_GMAC_STATUS0); @@ -6189,7 +6215,7 @@ static int mvpp2_gmac_pcs_config(struct phylink_pcs *pcs, unsigned int mode, const unsigned long *advertising, bool permit_pause_to_mac) { - struct mvpp2_port *port = mvpp2_pcs_to_port(pcs); + struct mvpp2_port *port = mvpp2_pcs_gmac_to_port(pcs); u32 mask, val, an, old_an, changed; mask = MVPP2_GMAC_IN_BAND_AUTONEG_BYPASS | @@ -6243,7 +6269,7 @@ static int mvpp2_gmac_pcs_config(struct phylink_pcs *pcs, unsigned int mode, static void mvpp2_gmac_pcs_an_restart(struct phylink_pcs *pcs) { - struct mvpp2_port *port = mvpp2_pcs_to_port(pcs); + struct mvpp2_port *port = mvpp2_pcs_gmac_to_port(pcs); u32 val = readl(port->base + MVPP2_GMAC_AUTONEG_CONFIG); writel(val | MVPP2_GMAC_IN_BAND_RESTART_AN, @@ -6253,78 +6279,12 @@ static void mvpp2_gmac_pcs_an_restart(struct phylink_pcs *pcs) } static const struct phylink_pcs_ops mvpp2_phylink_gmac_pcs_ops = { + .pcs_validate = mvpp2_gmac_pcs_validate, .pcs_get_state = mvpp2_gmac_pcs_get_state, .pcs_config = mvpp2_gmac_pcs_config, .pcs_an_restart = mvpp2_gmac_pcs_an_restart, }; -static void mvpp2_phylink_validate(struct phylink_config *config, - unsigned long *supported, - struct phylink_link_state *state) -{ - struct mvpp2_port *port = mvpp2_phylink_to_port(config); - __ETHTOOL_DECLARE_LINK_MODE_MASK(mask) = { 0, }; - - /* When in 802.3z mode, we must have AN enabled: - * Bit 2 Field InBandAnEn In-band Auto-Negotiation enable. ... - * When <PortType> = 1 (1000BASE-X) this field must be set to 1. - */ - if (phy_interface_mode_is_8023z(state->interface) && - !phylink_test(state->advertising, Autoneg)) - goto empty_set; - - phylink_set(mask, Autoneg); - phylink_set_port_modes(mask); - - if (port->priv->global_tx_fc) { - phylink_set(mask, Pause); - phylink_set(mask, Asym_Pause); - } - - switch (state->interface) { - case PHY_INTERFACE_MODE_10GBASER: - case PHY_INTERFACE_MODE_XAUI: - if (mvpp2_port_supports_xlg(port)) { - phylink_set_10g_modes(mask); - phylink_set(mask, 10000baseKR_Full); - } - break; - - case PHY_INTERFACE_MODE_RGMII: - case PHY_INTERFACE_MODE_RGMII_ID: - case PHY_INTERFACE_MODE_RGMII_RXID: - case PHY_INTERFACE_MODE_RGMII_TXID: - case PHY_INTERFACE_MODE_SGMII: - phylink_set(mask, 10baseT_Half); - phylink_set(mask, 10baseT_Full); - phylink_set(mask, 100baseT_Half); - phylink_set(mask, 100baseT_Full); - phylink_set(mask, 1000baseT_Full); - phylink_set(mask, 1000baseX_Full); - break; - - case PHY_INTERFACE_MODE_1000BASEX: - phylink_set(mask, 1000baseT_Full); - phylink_set(mask, 1000baseX_Full); - break; - - case PHY_INTERFACE_MODE_2500BASEX: - phylink_set(mask, 2500baseT_Full); - phylink_set(mask, 2500baseX_Full); - break; - - default: - goto empty_set; - } - - linkmode_and(supported, supported, mask); - linkmode_and(state->advertising, state->advertising, mask); - return; - -empty_set: - linkmode_zero(supported); -} - static void mvpp2_xlg_config(struct mvpp2_port *port, unsigned int mode, const struct phylink_link_state *state) { @@ -6404,8 +6364,23 @@ static void mvpp2_gmac_config(struct mvpp2_port *port, unsigned int mode, writel(ctrl4, port->base + MVPP22_GMAC_CTRL_4_REG); } -static int mvpp2__mac_prepare(struct phylink_config *config, unsigned int mode, - phy_interface_t interface) +static struct phylink_pcs *mvpp2_select_pcs(struct phylink_config *config, + phy_interface_t interface) +{ + struct mvpp2_port *port = mvpp2_phylink_to_port(config); + + /* Select the appropriate PCS operations depending on the + * configured interface mode. We will only switch to a mode + * that the validate() checks have already passed. + */ + if (mvpp2_is_xlg(interface)) + return &port->pcs_xlg; + else + return &port->pcs_gmac; +} + +static int mvpp2_mac_prepare(struct phylink_config *config, unsigned int mode, + phy_interface_t interface) { struct mvpp2_port *port = mvpp2_phylink_to_port(config); @@ -6454,31 +6429,9 @@ static int mvpp2__mac_prepare(struct phylink_config *config, unsigned int mode, } } - /* Select the appropriate PCS operations depending on the - * configured interface mode. We will only switch to a mode - * that the validate() checks have already passed. - */ - if (mvpp2_is_xlg(interface)) - port->phylink_pcs.ops = &mvpp2_phylink_xlg_pcs_ops; - else - port->phylink_pcs.ops = &mvpp2_phylink_gmac_pcs_ops; - return 0; } -static int mvpp2_mac_prepare(struct phylink_config *config, unsigned int mode, - phy_interface_t interface) -{ - struct mvpp2_port *port = mvpp2_phylink_to_port(config); - int ret; - - ret = mvpp2__mac_prepare(config, mode, interface); - if (ret == 0) - phylink_set_pcs(port->phylink, &port->phylink_pcs); - - return ret; -} - static void mvpp2_mac_config(struct phylink_config *config, unsigned int mode, const struct phylink_link_state *state) { @@ -6649,7 +6602,8 @@ static void mvpp2_mac_link_down(struct phylink_config *config, } static const struct phylink_mac_ops mvpp2_phylink_ops = { - .validate = mvpp2_phylink_validate, + .validate = phylink_generic_validate, + .mac_select_pcs = mvpp2_select_pcs, .mac_prepare = mvpp2_mac_prepare, .mac_config = mvpp2_mac_config, .mac_finish = mvpp2_mac_finish, @@ -6667,12 +6621,15 @@ static void mvpp2_acpi_start(struct mvpp2_port *port) struct phylink_link_state state = { .interface = port->phy_interface, }; - mvpp2__mac_prepare(&port->phylink_config, MLO_AN_INBAND, - port->phy_interface); + struct phylink_pcs *pcs; + + pcs = mvpp2_select_pcs(&port->phylink_config, port->phy_interface); + + mvpp2_mac_prepare(&port->phylink_config, MLO_AN_INBAND, + port->phy_interface); mvpp2_mac_config(&port->phylink_config, MLO_AN_INBAND, &state); - port->phylink_pcs.ops->pcs_config(&port->phylink_pcs, MLO_AN_INBAND, - port->phy_interface, - state.advertising, false); + pcs->ops->pcs_config(pcs, MLO_AN_INBAND, port->phy_interface, + state.advertising, false); mvpp2_mac_finish(&port->phylink_config, MLO_AN_INBAND, port->phy_interface); mvpp2_mac_link_up(&port->phylink_config, NULL, @@ -6913,12 +6870,44 @@ static int mvpp2_port_probe(struct platform_device *pdev, if (!mvpp2_use_acpi_compat_mode(port_fwnode)) { port->phylink_config.dev = &dev->dev; port->phylink_config.type = PHYLINK_NETDEV; + port->phylink_config.mac_capabilities = + MAC_2500FD | MAC_1000FD | MAC_100 | MAC_10; + + if (port->priv->global_tx_fc) + port->phylink_config.mac_capabilities |= + MAC_SYM_PAUSE | MAC_ASYM_PAUSE; 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 a COMPHY is present, we can support any of + * the serdes modes and switch between them. + */ + if (comphy) { + __set_bit(PHY_INTERFACE_MODE_5GBASER, + port->phylink_config.supported_interfaces); + __set_bit(PHY_INTERFACE_MODE_10GBASER, + port->phylink_config.supported_interfaces); + __set_bit(PHY_INTERFACE_MODE_XAUI, + port->phylink_config.supported_interfaces); + } else if (phy_mode == PHY_INTERFACE_MODE_5GBASER) { + __set_bit(PHY_INTERFACE_MODE_5GBASER, + port->phylink_config.supported_interfaces); + } else if (phy_mode == PHY_INTERFACE_MODE_10GBASER) { + __set_bit(PHY_INTERFACE_MODE_10GBASER, + port->phylink_config.supported_interfaces); + } else if (phy_mode == PHY_INTERFACE_MODE_XAUI) { + __set_bit(PHY_INTERFACE_MODE_XAUI, + port->phylink_config.supported_interfaces); + } + + if (comphy) + port->phylink_config.mac_capabilities |= + MAC_10000FD | MAC_5000FD; + else if (phy_mode == PHY_INTERFACE_MODE_5GBASER) + port->phylink_config.mac_capabilities |= + MAC_5000FD; + else + port->phylink_config.mac_capabilities |= + MAC_10000FD; } if (mvpp2_port_supports_rgmii(port)) @@ -6948,6 +6937,9 @@ static int mvpp2_port_probe(struct platform_device *pdev, port->phylink_config.supported_interfaces); } + port->pcs_gmac.ops = &mvpp2_phylink_gmac_pcs_ops; + port->pcs_xlg.ops = &mvpp2_phylink_xlg_pcs_ops; + phylink = phylink_create(&port->phylink_config, port_fwnode, phy_mode, &mvpp2_phylink_ops); if (IS_ERR(phylink)) { |