diff options
25 files changed, 912 insertions, 147 deletions
diff --git a/MAINTAINERS b/MAINTAINERS index e8666f980a21..6918c3f0ff1c 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -16117,6 +16117,13 @@ L: netdev@vger.kernel.org S: Supported F: drivers/net/ethernet/synopsys/ +SYNOPSYS DESIGNWARE ETHERNET XPCS DRIVER +M: Jose Abreu <Jose.Abreu@synopsys.com> +L: netdev@vger.kernel.org +S: Supported +F: drivers/net/phy/mdio-xpcs.c +F: include/linux/mdio-xpcs.h + SYNOPSYS DESIGNWARE I2C DRIVER M: Jarkko Nikula <jarkko.nikula@linux.intel.com> R: Andy Shevchenko <andriy.shevchenko@linux.intel.com> diff --git a/drivers/net/dsa/microchip/ksz_common.c b/drivers/net/dsa/microchip/ksz_common.c index d8fda4a02640..fd1d6676ae4f 100644 --- a/drivers/net/dsa/microchip/ksz_common.c +++ b/drivers/net/dsa/microchip/ksz_common.c @@ -67,7 +67,7 @@ static void port_r_cnt(struct ksz_device *dev, int port) static void ksz_mib_read_work(struct work_struct *work) { struct ksz_device *dev = container_of(work, struct ksz_device, - mib_read); + mib_read.work); struct ksz_port_mib *mib; struct ksz_port *p; int i; @@ -93,32 +93,24 @@ static void ksz_mib_read_work(struct work_struct *work) p->read = false; mutex_unlock(&mib->cnt_mutex); } -} - -static void mib_monitor(struct timer_list *t) -{ - struct ksz_device *dev = from_timer(dev, t, mib_read_timer); - mod_timer(&dev->mib_read_timer, jiffies + dev->mib_read_interval); - schedule_work(&dev->mib_read); + schedule_delayed_work(&dev->mib_read, dev->mib_read_interval); } void ksz_init_mib_timer(struct ksz_device *dev) { int i; + INIT_DELAYED_WORK(&dev->mib_read, ksz_mib_read_work); + /* Read MIB counters every 30 seconds to avoid overflow. */ dev->mib_read_interval = msecs_to_jiffies(30000); - INIT_WORK(&dev->mib_read, ksz_mib_read_work); - timer_setup(&dev->mib_read_timer, mib_monitor, 0); - for (i = 0; i < dev->mib_port_cnt; i++) dev->dev_ops->port_init_cnt(dev, i); /* Start the timer 2 seconds later. */ - dev->mib_read_timer.expires = jiffies + msecs_to_jiffies(2000); - add_timer(&dev->mib_read_timer); + schedule_delayed_work(&dev->mib_read, msecs_to_jiffies(2000)); } EXPORT_SYMBOL_GPL(ksz_init_mib_timer); @@ -152,7 +144,7 @@ void ksz_adjust_link(struct dsa_switch *ds, int port, /* Read all MIB counters when the link is going down. */ if (!phydev->link) { p->read = true; - schedule_work(&dev->mib_read); + schedule_delayed_work(&dev->mib_read, 0); } mutex_lock(&dev->dev_mutex); if (!phydev->link) @@ -477,10 +469,8 @@ EXPORT_SYMBOL(ksz_switch_register); void ksz_switch_remove(struct ksz_device *dev) { /* timer started */ - if (dev->mib_read_timer.expires) { - del_timer_sync(&dev->mib_read_timer); - flush_work(&dev->mib_read); - } + if (dev->mib_read_interval) + cancel_delayed_work_sync(&dev->mib_read); dev->dev_ops->exit(dev); dsa_unregister_switch(dev->ds); diff --git a/drivers/net/dsa/microchip/ksz_common.h b/drivers/net/dsa/microchip/ksz_common.h index a20ebb749377..f2c9bb68fd33 100644 --- a/drivers/net/dsa/microchip/ksz_common.h +++ b/drivers/net/dsa/microchip/ksz_common.h @@ -80,8 +80,7 @@ struct ksz_device { struct vlan_table *vlan_cache; struct ksz_port *ports; - struct timer_list mib_read_timer; - struct work_struct mib_read; + struct delayed_work mib_read; unsigned long mib_read_interval; u16 br_member; u16 member; diff --git a/drivers/net/dsa/mt7530.h b/drivers/net/dsa/mt7530.h index 5e6c778feb23..b7cfb3d52b1c 100644 --- a/drivers/net/dsa/mt7530.h +++ b/drivers/net/dsa/mt7530.h @@ -37,7 +37,7 @@ enum { #define CPU_PORT(x) ((x) << 4) #define CPU_MASK (0xf << 4) #define MIRROR_EN BIT(3) -#define MIRROR_PORT(x) ((x & 0x7)) +#define MIRROR_PORT(x) ((x) & 0x7) #define MIRROR_MASK 0x7 /* Registers for address table access */ diff --git a/drivers/net/ethernet/freescale/enetc/Kconfig b/drivers/net/ethernet/freescale/enetc/Kconfig index f86283411f4d..2b43848e1363 100644 --- a/drivers/net/ethernet/freescale/enetc/Kconfig +++ b/drivers/net/ethernet/freescale/enetc/Kconfig @@ -42,16 +42,6 @@ config FSL_ENETC_PTP_CLOCK If compiled as module (M), the module name is fsl-enetc-ptp. -config FSL_ENETC_HW_TIMESTAMPING - bool "ENETC hardware timestamping support" - depends on FSL_ENETC || FSL_ENETC_VF - help - Enable hardware timestamping support on the Ethernet packets - using the SO_TIMESTAMPING API. Because the RX BD ring dynamic - allocation has not been supported and it is too expensive to use - extended RX BDs if timestamping is not used, this option enables - extended RX BDs in order to support hardware timestamping. - config FSL_ENETC_QOS bool "ENETC hardware Time-sensitive Network support" depends on (FSL_ENETC || FSL_ENETC_VF) && (NET_SCH_TAPRIO || NET_SCH_CBS) diff --git a/drivers/net/ethernet/freescale/enetc/enetc.c b/drivers/net/ethernet/freescale/enetc/enetc.c index 1f79e36116a3..ccf2611f4a20 100644 --- a/drivers/net/ethernet/freescale/enetc/enetc.c +++ b/drivers/net/ethernet/freescale/enetc/enetc.c @@ -451,7 +451,7 @@ static int enetc_refill_rx_ring(struct enetc_bdr *rx_ring, const int buff_cnt) i = rx_ring->next_to_use; rx_swbd = &rx_ring->rx_swbd[i]; - rxbd = ENETC_RXBD(*rx_ring, i); + rxbd = enetc_rxbd(rx_ring, i); for (j = 0; j < buff_cnt; j++) { /* try reuse page */ @@ -468,13 +468,12 @@ static int enetc_refill_rx_ring(struct enetc_bdr *rx_ring, const int buff_cnt) /* clear 'R" as well */ rxbd->r.lstatus = 0; + rxbd = enetc_rxbd_next(rx_ring, rxbd, i); rx_swbd++; - rxbd++; i++; if (unlikely(i == rx_ring->bd_count)) { i = 0; rx_swbd = rx_ring->rx_swbd; - rxbd = ENETC_RXBD(*rx_ring, 0); } } @@ -488,7 +487,7 @@ static int enetc_refill_rx_ring(struct enetc_bdr *rx_ring, const int buff_cnt) return j; } -#ifdef CONFIG_FSL_ENETC_HW_TIMESTAMPING +#ifdef CONFIG_FSL_ENETC_PTP_CLOCK static void enetc_get_rx_tstamp(struct net_device *ndev, union enetc_rx_bd *rxbd, struct sk_buff *skb) @@ -502,7 +501,8 @@ static void enetc_get_rx_tstamp(struct net_device *ndev, if (le16_to_cpu(rxbd->r.flags) & ENETC_RXBD_FLAG_TSTMP) { lo = enetc_rd(hw, ENETC_SICTR0); hi = enetc_rd(hw, ENETC_SICTR1); - tstamp_lo = le32_to_cpu(rxbd->r.tstamp); + rxbd = enetc_rxbd_ext(rxbd); + tstamp_lo = le32_to_cpu(rxbd->ext.tstamp); if (lo <= tstamp_lo) hi -= 1; @@ -516,7 +516,7 @@ static void enetc_get_rx_tstamp(struct net_device *ndev, static void enetc_get_offloads(struct enetc_bdr *rx_ring, union enetc_rx_bd *rxbd, struct sk_buff *skb) { -#ifdef CONFIG_FSL_ENETC_HW_TIMESTAMPING +#ifdef CONFIG_FSL_ENETC_PTP_CLOCK struct enetc_ndev_priv *priv = netdev_priv(rx_ring->ndev); #endif /* TODO: hashing */ @@ -533,7 +533,7 @@ static void enetc_get_offloads(struct enetc_bdr *rx_ring, if (le16_to_cpu(rxbd->r.flags) & ENETC_RXBD_FLAG_VLAN) __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), le16_to_cpu(rxbd->r.vlan_opt)); -#ifdef CONFIG_FSL_ENETC_HW_TIMESTAMPING +#ifdef CONFIG_FSL_ENETC_PTP_CLOCK if (priv->active_offloads & ENETC_F_RX_TSTAMP) enetc_get_rx_tstamp(rx_ring->ndev, rxbd, skb); #endif @@ -655,7 +655,7 @@ static int enetc_clean_rx_ring(struct enetc_bdr *rx_ring, cleaned_cnt -= count; } - rxbd = ENETC_RXBD(*rx_ring, i); + rxbd = enetc_rxbd(rx_ring, i); bd_status = le32_to_cpu(rxbd->r.lstatus); if (!bd_status) break; @@ -670,12 +670,10 @@ static int enetc_clean_rx_ring(struct enetc_bdr *rx_ring, enetc_get_offloads(rx_ring, rxbd, skb); cleaned_cnt++; - rxbd++; - i++; - if (unlikely(i == rx_ring->bd_count)) { + + rxbd = enetc_rxbd_next(rx_ring, rxbd, i); + if (unlikely(++i == rx_ring->bd_count)) i = 0; - rxbd = ENETC_RXBD(*rx_ring, 0); - } if (unlikely(bd_status & ENETC_RXBD_LSTATUS(ENETC_RXBD_ERR_MASK))) { @@ -683,12 +681,10 @@ static int enetc_clean_rx_ring(struct enetc_bdr *rx_ring, while (!(bd_status & ENETC_RXBD_LSTATUS_F)) { dma_rmb(); bd_status = le32_to_cpu(rxbd->r.lstatus); - rxbd++; - i++; - if (unlikely(i == rx_ring->bd_count)) { + + rxbd = enetc_rxbd_next(rx_ring, rxbd, i); + if (unlikely(++i == rx_ring->bd_count)) i = 0; - rxbd = ENETC_RXBD(*rx_ring, 0); - } } rx_ring->ndev->stats.rx_dropped++; @@ -710,12 +706,10 @@ static int enetc_clean_rx_ring(struct enetc_bdr *rx_ring, enetc_add_rx_buff_to_skb(rx_ring, i, size, skb); cleaned_cnt++; - rxbd++; - i++; - if (unlikely(i == rx_ring->bd_count)) { + + rxbd = enetc_rxbd_next(rx_ring, rxbd, i); + if (unlikely(++i == rx_ring->bd_count)) i = 0; - rxbd = ENETC_RXBD(*rx_ring, 0); - } } rx_byte_cnt += skb->len; @@ -845,15 +839,19 @@ static void enetc_free_tx_resources(struct enetc_ndev_priv *priv) enetc_free_txbdr(priv->tx_ring[i]); } -static int enetc_alloc_rxbdr(struct enetc_bdr *rxr) +static int enetc_alloc_rxbdr(struct enetc_bdr *rxr, bool extended) { + size_t size = sizeof(union enetc_rx_bd); int err; rxr->rx_swbd = vzalloc(rxr->bd_count * sizeof(struct enetc_rx_swbd)); if (!rxr->rx_swbd) return -ENOMEM; - err = enetc_dma_alloc_bdr(rxr, sizeof(union enetc_rx_bd)); + if (extended) + size *= 2; + + err = enetc_dma_alloc_bdr(rxr, size); if (err) { vfree(rxr->rx_swbd); return err; @@ -862,6 +860,7 @@ static int enetc_alloc_rxbdr(struct enetc_bdr *rxr) rxr->next_to_clean = 0; rxr->next_to_use = 0; rxr->next_to_alloc = 0; + rxr->ext_en = extended; return 0; } @@ -881,10 +880,11 @@ static void enetc_free_rxbdr(struct enetc_bdr *rxr) static int enetc_alloc_rx_resources(struct enetc_ndev_priv *priv) { + bool extended = !!(priv->active_offloads & ENETC_F_RX_TSTAMP); int i, err; for (i = 0; i < priv->num_rx_rings; i++) { - err = enetc_alloc_rxbdr(priv->rx_ring[i]); + err = enetc_alloc_rxbdr(priv->rx_ring[i], extended); if (err) goto fail; @@ -1174,9 +1174,10 @@ static void enetc_setup_rxbdr(struct enetc_hw *hw, struct enetc_bdr *rx_ring) enetc_rxbdr_wr(hw, idx, ENETC_RBICIR0, ENETC_RBICIR0_ICEN | 0x1); rbmr = ENETC_RBMR_EN; -#ifdef CONFIG_FSL_ENETC_HW_TIMESTAMPING - rbmr |= ENETC_RBMR_BDS; -#endif + + if (rx_ring->ext_en) + rbmr |= ENETC_RBMR_BDS; + if (rx_ring->ndev->features & NETIF_F_HW_VLAN_CTAG_RX) rbmr |= ENETC_RBMR_VTE; @@ -1577,11 +1578,12 @@ int enetc_set_features(struct net_device *ndev, return 0; } -#ifdef CONFIG_FSL_ENETC_HW_TIMESTAMPING +#ifdef CONFIG_FSL_ENETC_PTP_CLOCK static int enetc_hwtstamp_set(struct net_device *ndev, struct ifreq *ifr) { struct enetc_ndev_priv *priv = netdev_priv(ndev); struct hwtstamp_config config; + int ao; if (copy_from_user(&config, ifr->ifr_data, sizeof(config))) return -EFAULT; @@ -1597,6 +1599,7 @@ static int enetc_hwtstamp_set(struct net_device *ndev, struct ifreq *ifr) return -ERANGE; } + ao = priv->active_offloads; switch (config.rx_filter) { case HWTSTAMP_FILTER_NONE: priv->active_offloads &= ~ENETC_F_RX_TSTAMP; @@ -1606,6 +1609,11 @@ static int enetc_hwtstamp_set(struct net_device *ndev, struct ifreq *ifr) config.rx_filter = HWTSTAMP_FILTER_ALL; } + if (netif_running(ndev) && ao != priv->active_offloads) { + enetc_close(ndev); + enetc_open(ndev); + } + return copy_to_user(ifr->ifr_data, &config, sizeof(config)) ? -EFAULT : 0; } @@ -1632,7 +1640,7 @@ static int enetc_hwtstamp_get(struct net_device *ndev, struct ifreq *ifr) int enetc_ioctl(struct net_device *ndev, struct ifreq *rq, int cmd) { -#ifdef CONFIG_FSL_ENETC_HW_TIMESTAMPING +#ifdef CONFIG_FSL_ENETC_PTP_CLOCK if (cmd == SIOCSHWTSTAMP) return enetc_hwtstamp_set(ndev, rq); if (cmd == SIOCGHWTSTAMP) diff --git a/drivers/net/ethernet/freescale/enetc/enetc.h b/drivers/net/ethernet/freescale/enetc/enetc.h index 9938f7a5fc0a..56c43f35b633 100644 --- a/drivers/net/ethernet/freescale/enetc/enetc.h +++ b/drivers/net/ethernet/freescale/enetc/enetc.h @@ -73,6 +73,7 @@ struct enetc_bdr { dma_addr_t bd_dma_base; u8 tsd_enable; /* Time specific departure */ + bool ext_en; /* enable h/w descriptor extensions */ } ____cacheline_aligned_in_smp; static inline void enetc_bdr_idx_inc(struct enetc_bdr *bdr, int *i) @@ -104,7 +105,37 @@ struct enetc_cbdr { }; #define ENETC_TXBD(BDR, i) (&(((union enetc_tx_bd *)((BDR).bd_base))[i])) -#define ENETC_RXBD(BDR, i) (&(((union enetc_rx_bd *)((BDR).bd_base))[i])) + +static inline union enetc_rx_bd *enetc_rxbd(struct enetc_bdr *rx_ring, int i) +{ + int hw_idx = i; + +#ifdef CONFIG_FSL_ENETC_PTP_CLOCK + if (rx_ring->ext_en) + hw_idx = 2 * i; +#endif + return &(((union enetc_rx_bd *)rx_ring->bd_base)[hw_idx]); +} + +static inline union enetc_rx_bd *enetc_rxbd_next(struct enetc_bdr *rx_ring, + union enetc_rx_bd *rxbd, + int i) +{ + rxbd++; +#ifdef CONFIG_FSL_ENETC_PTP_CLOCK + if (rx_ring->ext_en) + rxbd++; +#endif + if (unlikely(++i == rx_ring->bd_count)) + rxbd = rx_ring->bd_base; + + return rxbd; +} + +static inline union enetc_rx_bd *enetc_rxbd_ext(union enetc_rx_bd *rxbd) +{ + return ++rxbd; +} struct enetc_msg_swbd { void *vaddr; diff --git a/drivers/net/ethernet/freescale/enetc/enetc_ethtool.c b/drivers/net/ethernet/freescale/enetc/enetc_ethtool.c index 301ee0dde02d..34bd1f3fb415 100644 --- a/drivers/net/ethernet/freescale/enetc/enetc_ethtool.c +++ b/drivers/net/ethernet/freescale/enetc/enetc_ethtool.c @@ -195,15 +195,21 @@ static const char tx_ring_stats[][ETH_GSTRING_LEN] = { static int enetc_get_sset_count(struct net_device *ndev, int sset) { struct enetc_ndev_priv *priv = netdev_priv(ndev); + int len; + + if (sset != ETH_SS_STATS) + return -EOPNOTSUPP; - if (sset == ETH_SS_STATS) - return ARRAY_SIZE(enetc_si_counters) + - ARRAY_SIZE(tx_ring_stats) * priv->num_tx_rings + - ARRAY_SIZE(rx_ring_stats) * priv->num_rx_rings + - (enetc_si_is_pf(priv->si) ? - ARRAY_SIZE(enetc_port_counters) : 0); + len = ARRAY_SIZE(enetc_si_counters) + + ARRAY_SIZE(tx_ring_stats) * priv->num_tx_rings + + ARRAY_SIZE(rx_ring_stats) * priv->num_rx_rings; - return -EOPNOTSUPP; + if (!enetc_si_is_pf(priv->si)) + return len; + + len += ARRAY_SIZE(enetc_port_counters); + + return len; } static void enetc_get_strings(struct net_device *ndev, u32 stringset, u8 *data) @@ -568,7 +574,7 @@ static int enetc_get_ts_info(struct net_device *ndev, info->phc_index = -1; } -#ifdef CONFIG_FSL_ENETC_HW_TIMESTAMPING +#ifdef CONFIG_FSL_ENETC_PTP_CLOCK info->so_timestamping = SOF_TIMESTAMPING_TX_HARDWARE | SOF_TIMESTAMPING_RX_HARDWARE | SOF_TIMESTAMPING_RAW_HARDWARE; diff --git a/drivers/net/ethernet/freescale/enetc/enetc_hw.h b/drivers/net/ethernet/freescale/enetc/enetc_hw.h index da134e211c1a..2a6523136947 100644 --- a/drivers/net/ethernet/freescale/enetc/enetc_hw.h +++ b/drivers/net/ethernet/freescale/enetc/enetc_hw.h @@ -418,9 +418,6 @@ union enetc_rx_bd { struct { __le64 addr; u8 reserved[8]; -#ifdef CONFIG_FSL_ENETC_HW_TIMESTAMPING - u8 reserved1[16]; -#endif } w; struct { __le16 inet_csum; @@ -435,11 +432,11 @@ union enetc_rx_bd { }; __le32 lstatus; }; -#ifdef CONFIG_FSL_ENETC_HW_TIMESTAMPING + } r; + struct { __le32 tstamp; u8 reserved[12]; -#endif - } r; + } ext; }; #define ENETC_RXBD_LSTATUS_R BIT(30) diff --git a/drivers/net/ethernet/freescale/enetc/enetc_pf.c b/drivers/net/ethernet/freescale/enetc/enetc_pf.c index 545a344bce00..4e4a49179f0b 100644 --- a/drivers/net/ethernet/freescale/enetc/enetc_pf.c +++ b/drivers/net/ethernet/freescale/enetc/enetc_pf.c @@ -797,11 +797,6 @@ static int enetc_of_get_phy(struct enetc_ndev_priv *priv) struct device_node *mdio_np; int err; - if (!np) { - dev_err(priv->dev, "missing ENETC port node\n"); - return -ENODEV; - } - priv->phy_node = of_parse_phandle(np, "phy-handle", 0); if (!priv->phy_node) { if (!of_phy_is_fixed_link(np)) { diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c b/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c index 33d3e70418fb..f285713def77 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c @@ -2879,7 +2879,7 @@ static int parse_tc_nic_actions(struct mlx5e_priv *priv, return -EINVAL; if (!flow_action_hw_stats_types_check(flow_action, extack, - FLOW_ACTION_HW_STATS_TYPE_DELAYED)) + FLOW_ACTION_HW_STATS_TYPE_DELAYED_BIT)) return -EOPNOTSUPP; attr->flow_tag = MLX5_FS_DEFAULT_FLOW_TAG; @@ -3374,7 +3374,7 @@ static int parse_tc_fdb_actions(struct mlx5e_priv *priv, return -EINVAL; if (!flow_action_hw_stats_types_check(flow_action, extack, - FLOW_ACTION_HW_STATS_TYPE_DELAYED)) + FLOW_ACTION_HW_STATS_TYPE_DELAYED_BIT)) return -EOPNOTSUPP; flow_action_for_each(i, act, flow_action) { diff --git a/drivers/net/ethernet/stmicro/stmmac/Kconfig b/drivers/net/ethernet/stmicro/stmmac/Kconfig index 338e25a6374e..9ad927f646e8 100644 --- a/drivers/net/ethernet/stmicro/stmmac/Kconfig +++ b/drivers/net/ethernet/stmicro/stmmac/Kconfig @@ -3,6 +3,7 @@ config STMMAC_ETH tristate "STMicroelectronics Multi-Gigabit Ethernet driver" depends on HAS_IOMEM && HAS_DMA select MII + select MDIO_XPCS select PAGE_POOL select PHYLINK select CRC32 diff --git a/drivers/net/ethernet/stmicro/stmmac/common.h b/drivers/net/ethernet/stmicro/stmmac/common.h index 487099092693..9bdbf589d93f 100644 --- a/drivers/net/ethernet/stmicro/stmmac/common.h +++ b/drivers/net/ethernet/stmicro/stmmac/common.h @@ -15,6 +15,7 @@ #include <linux/netdevice.h> #include <linux/stmmac.h> #include <linux/phy.h> +#include <linux/mdio-xpcs.h> #include <linux/module.h> #if IS_ENABLED(CONFIG_VLAN_8021Q) #define STMMAC_VLAN_TAG_USED @@ -446,6 +447,8 @@ struct mac_device_info { const struct stmmac_hwtimestamp *ptp; const struct stmmac_tc_ops *tc; const struct stmmac_mmc_ops *mmc; + const struct mdio_xpcs_ops *xpcs; + struct mdio_xpcs_args xpcs_args; struct mii_regs mii; /* MII register Addresses */ struct mac_link link; void __iomem *pcsr; /* vpointer to device CSRs */ diff --git a/drivers/net/ethernet/stmicro/stmmac/hwif.h b/drivers/net/ethernet/stmicro/stmmac/hwif.h index df63b0367aff..c71dd99c8abf 100644 --- a/drivers/net/ethernet/stmicro/stmmac/hwif.h +++ b/drivers/net/ethernet/stmicro/stmmac/hwif.h @@ -577,6 +577,18 @@ struct stmmac_mmc_ops { #define stmmac_mmc_read(__priv, __args...) \ stmmac_do_void_callback(__priv, mmc, read, __args) +/* XPCS callbacks */ +#define stmmac_xpcs_validate(__priv, __args...) \ + stmmac_do_callback(__priv, xpcs, validate, __args) +#define stmmac_xpcs_config(__priv, __args...) \ + stmmac_do_callback(__priv, xpcs, config, __args) +#define stmmac_xpcs_get_state(__priv, __args...) \ + stmmac_do_callback(__priv, xpcs, get_state, __args) +#define stmmac_xpcs_link_up(__priv, __args...) \ + stmmac_do_callback(__priv, xpcs, link_up, __args) +#define stmmac_xpcs_probe(__priv, __args...) \ + stmmac_do_callback(__priv, xpcs, probe, __args) + struct stmmac_regs_off { u32 ptp_off; u32 mmc_off; diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c index cb7a5bad4cfe..f26699d9a050 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c @@ -858,33 +858,65 @@ static void stmmac_validate(struct phylink_config *config, phylink_set(mask, 1000baseT_Half); } - bitmap_and(supported, supported, mac_supported, - __ETHTOOL_LINK_MODE_MASK_NBITS); - bitmap_andnot(supported, supported, mask, - __ETHTOOL_LINK_MODE_MASK_NBITS); - bitmap_and(state->advertising, state->advertising, mac_supported, - __ETHTOOL_LINK_MODE_MASK_NBITS); - bitmap_andnot(state->advertising, state->advertising, mask, - __ETHTOOL_LINK_MODE_MASK_NBITS); + linkmode_and(supported, supported, mac_supported); + linkmode_andnot(supported, supported, mask); + + linkmode_and(state->advertising, state->advertising, mac_supported); + linkmode_andnot(state->advertising, state->advertising, mask); + + /* If PCS is supported, check which modes it supports. */ + stmmac_xpcs_validate(priv, &priv->hw->xpcs_args, supported, state); } static void stmmac_mac_pcs_get_state(struct phylink_config *config, struct phylink_link_state *state) { + struct stmmac_priv *priv = netdev_priv(to_net_dev(config->dev)); + state->link = 0; + stmmac_xpcs_get_state(priv, &priv->hw->xpcs_args, state); } static void stmmac_mac_config(struct phylink_config *config, unsigned int mode, const struct phylink_link_state *state) { struct stmmac_priv *priv = netdev_priv(to_net_dev(config->dev)); + + stmmac_xpcs_config(priv, &priv->hw->xpcs_args, state); +} + +static void stmmac_mac_an_restart(struct phylink_config *config) +{ + /* Not Supported */ +} + +static void stmmac_mac_link_down(struct phylink_config *config, + unsigned int mode, phy_interface_t interface) +{ + struct stmmac_priv *priv = netdev_priv(to_net_dev(config->dev)); + + stmmac_mac_set(priv, priv->ioaddr, false); + priv->eee_active = false; + stmmac_eee_init(priv); + stmmac_set_eee_pls(priv, priv->hw, false); +} + +static void stmmac_mac_link_up(struct phylink_config *config, + struct phy_device *phy, + unsigned int mode, phy_interface_t interface, + int speed, int duplex, + bool tx_pause, bool rx_pause) +{ + struct stmmac_priv *priv = netdev_priv(to_net_dev(config->dev)); u32 ctrl; + stmmac_xpcs_link_up(priv, &priv->hw->xpcs_args, speed, interface); + ctrl = readl(priv->ioaddr + MAC_CTRL_REG); ctrl &= ~priv->hw->link.speed_mask; - if (state->interface == PHY_INTERFACE_MODE_USXGMII) { - switch (state->speed) { + if (interface == PHY_INTERFACE_MODE_USXGMII) { + switch (speed) { case SPEED_10000: ctrl |= priv->hw->link.xgmii.speed10000; break; @@ -898,7 +930,7 @@ static void stmmac_mac_config(struct phylink_config *config, unsigned int mode, return; } } else { - switch (state->speed) { + switch (speed) { case SPEED_2500: ctrl |= priv->hw->link.speed2500; break; @@ -916,46 +948,21 @@ static void stmmac_mac_config(struct phylink_config *config, unsigned int mode, } } - priv->speed = state->speed; + priv->speed = speed; if (priv->plat->fix_mac_speed) - priv->plat->fix_mac_speed(priv->plat->bsp_priv, state->speed); + priv->plat->fix_mac_speed(priv->plat->bsp_priv, speed); - if (!state->duplex) + if (!duplex) ctrl &= ~priv->hw->link.duplex; else ctrl |= priv->hw->link.duplex; /* Flow Control operation */ - if (state->pause) - stmmac_mac_flow_ctrl(priv, state->duplex); + if (tx_pause && rx_pause) + stmmac_mac_flow_ctrl(priv, duplex); writel(ctrl, priv->ioaddr + MAC_CTRL_REG); -} - -static void stmmac_mac_an_restart(struct phylink_config *config) -{ - /* Not Supported */ -} - -static void stmmac_mac_link_down(struct phylink_config *config, - unsigned int mode, phy_interface_t interface) -{ - struct stmmac_priv *priv = netdev_priv(to_net_dev(config->dev)); - - stmmac_mac_set(priv, priv->ioaddr, false); - priv->eee_active = false; - stmmac_eee_init(priv); - stmmac_set_eee_pls(priv, priv->hw, false); -} - -static void stmmac_mac_link_up(struct phylink_config *config, - struct phy_device *phy, - unsigned int mode, phy_interface_t interface, - int speed, int duplex, - bool tx_pause, bool rx_pause) -{ - struct stmmac_priv *priv = netdev_priv(to_net_dev(config->dev)); stmmac_mac_set(priv, priv->ioaddr, true); if (phy && priv->dma_cap.eee) { @@ -1045,6 +1052,10 @@ static int stmmac_phy_setup(struct stmmac_priv *priv) priv->phylink_config.dev = &priv->dev->dev; priv->phylink_config.type = PHYLINK_NETDEV; + priv->phylink_config.pcs_poll = true; + + if (!fwnode) + fwnode = dev_fwnode(priv->device); phylink = phylink_create(&priv->phylink_config, fwnode, mode, &stmmac_phylink_mac_ops); @@ -2689,7 +2700,8 @@ static int stmmac_open(struct net_device *dev) int ret; if (priv->hw->pcs != STMMAC_PCS_TBI && - priv->hw->pcs != STMMAC_PCS_RTBI) { + priv->hw->pcs != STMMAC_PCS_RTBI && + priv->hw->xpcs == NULL) { ret = stmmac_init_phy(dev); if (ret) { netdev_err(priv->dev, diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c index cfe5d8b73142..b2a707e2ef43 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c @@ -382,6 +382,14 @@ int stmmac_mdio_register(struct net_device *ndev) max_addr = PHY_MAX_ADDR; } + if (mdio_bus_data->has_xpcs) { + priv->hw->xpcs = mdio_xpcs_get_ops(); + if (!priv->hw->xpcs) { + err = -ENODEV; + goto bus_register_fail; + } + } + if (mdio_bus_data->needs_reset) new_bus->reset = &stmmac_mdio_reset; @@ -433,6 +441,25 @@ int stmmac_mdio_register(struct net_device *ndev) found = 1; } + /* Try to probe the XPCS by scanning all addresses. */ + if (priv->hw->xpcs) { + struct mdio_xpcs_args *xpcs = &priv->hw->xpcs_args; + int ret, mode = priv->plat->phy_interface; + max_addr = PHY_MAX_ADDR; + + xpcs->bus = new_bus; + + for (addr = 0; addr < max_addr; addr++) { + xpcs->addr = addr; + + ret = stmmac_xpcs_probe(priv, xpcs, mode); + if (!ret) { + found = 1; + break; + } + } + } + if (!found && !mdio_node) { dev_warn(dev, "No PHY found\n"); mdiobus_unregister(new_bus); diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_selftests.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_selftests.c index 2aba2673d6c3..586a657be984 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_selftests.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_selftests.c @@ -380,7 +380,7 @@ static int stmmac_test_phy_loopback(struct stmmac_priv *priv) int ret; if (!priv->dev->phydev) - return -EBUSY; + return -EOPNOTSUPP; ret = phy_loopback(priv->dev->phydev, true); if (ret) diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig index d6f197e06134..cc7f1df855da 100644 --- a/drivers/net/phy/Kconfig +++ b/drivers/net/phy/Kconfig @@ -214,6 +214,12 @@ config MDIO_XGENE This module provides a driver for the MDIO busses found in the APM X-Gene SoC's. +config MDIO_XPCS + tristate "Synopsys DesignWare XPCS controller" + help + This module provides helper functions for Synopsys DesignWare XPCS + controllers. + endif endif diff --git a/drivers/net/phy/Makefile b/drivers/net/phy/Makefile index d9b3c0fec8e3..26f8039f300f 100644 --- a/drivers/net/phy/Makefile +++ b/drivers/net/phy/Makefile @@ -44,6 +44,7 @@ obj-$(CONFIG_MDIO_OCTEON) += mdio-octeon.o obj-$(CONFIG_MDIO_SUN4I) += mdio-sun4i.o obj-$(CONFIG_MDIO_THUNDER) += mdio-thunder.o obj-$(CONFIG_MDIO_XGENE) += mdio-xgene.o +obj-$(CONFIG_MDIO_XPCS) += mdio-xpcs.o obj-$(CONFIG_NETWORK_PHY_TIMESTAMPING) += mii_timestamper.o diff --git a/drivers/net/phy/mdio-xpcs.c b/drivers/net/phy/mdio-xpcs.c new file mode 100644 index 000000000000..973f588146f7 --- /dev/null +++ b/drivers/net/phy/mdio-xpcs.c @@ -0,0 +1,612 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2020 Synopsys, Inc. and/or its affiliates. + * Synopsys DesignWare XPCS helpers + * + * Author: Jose Abreu <Jose.Abreu@synopsys.com> + */ + +#include <linux/delay.h> +#include <linux/mdio.h> +#include <linux/mdio-xpcs.h> +#include <linux/phylink.h> +#include <linux/workqueue.h> + +#define SYNOPSYS_XPCS_USXGMII_ID 0x7996ced0 +#define SYNOPSYS_XPCS_10GKR_ID 0x7996ced0 +#define SYNOPSYS_XPCS_MASK 0xffffffff + +/* Vendor regs access */ +#define DW_VENDOR BIT(15) + +/* VR_XS_PCS */ +#define DW_USXGMII_RST BIT(10) +#define DW_USXGMII_EN BIT(9) +#define DW_VR_XS_PCS_DIG_STS 0x0010 +#define DW_RXFIFO_ERR GENMASK(6, 5) + +/* SR_MII */ +#define DW_USXGMII_FULL BIT(8) +#define DW_USXGMII_SS_MASK (BIT(13) | BIT(6) | BIT(5)) +#define DW_USXGMII_10000 (BIT(13) | BIT(6)) +#define DW_USXGMII_5000 (BIT(13) | BIT(5)) +#define DW_USXGMII_2500 (BIT(5)) +#define DW_USXGMII_1000 (BIT(6)) +#define DW_USXGMII_100 (BIT(13)) +#define DW_USXGMII_10 (0) + +/* SR_AN */ +#define DW_SR_AN_ADV1 0x10 +#define DW_SR_AN_ADV2 0x11 +#define DW_SR_AN_ADV3 0x12 +#define DW_SR_AN_LP_ABL1 0x13 +#define DW_SR_AN_LP_ABL2 0x14 +#define DW_SR_AN_LP_ABL3 0x15 + +/* Clause 73 Defines */ +/* AN_LP_ABL1 */ +#define DW_C73_PAUSE BIT(10) +#define DW_C73_ASYM_PAUSE BIT(11) +#define DW_C73_AN_ADV_SF 0x1 +/* AN_LP_ABL2 */ +#define DW_C73_1000KX BIT(5) +#define DW_C73_10000KX4 BIT(6) +#define DW_C73_10000KR BIT(7) +/* AN_LP_ABL3 */ +#define DW_C73_2500KX BIT(0) +#define DW_C73_5000KR BIT(1) + +static const int xpcs_usxgmii_features[] = { + ETHTOOL_LINK_MODE_Pause_BIT, + ETHTOOL_LINK_MODE_Asym_Pause_BIT, + ETHTOOL_LINK_MODE_Autoneg_BIT, + ETHTOOL_LINK_MODE_1000baseKX_Full_BIT, + ETHTOOL_LINK_MODE_10000baseKX4_Full_BIT, + ETHTOOL_LINK_MODE_10000baseKR_Full_BIT, + ETHTOOL_LINK_MODE_2500baseX_Full_BIT, + __ETHTOOL_LINK_MODE_MASK_NBITS, +}; + +static const int xpcs_10gkr_features[] = { + ETHTOOL_LINK_MODE_Pause_BIT, + ETHTOOL_LINK_MODE_Asym_Pause_BIT, + ETHTOOL_LINK_MODE_10000baseKR_Full_BIT, + __ETHTOOL_LINK_MODE_MASK_NBITS, +}; + +static const phy_interface_t xpcs_usxgmii_interfaces[] = { + PHY_INTERFACE_MODE_USXGMII, + PHY_INTERFACE_MODE_MAX, +}; + +static const phy_interface_t xpcs_10gkr_interfaces[] = { + PHY_INTERFACE_MODE_10GKR, + PHY_INTERFACE_MODE_MAX, +}; + +static struct xpcs_id { + u32 id; + u32 mask; + const int *supported; + const phy_interface_t *interface; +} xpcs_id_list[] = { + { + .id = SYNOPSYS_XPCS_USXGMII_ID, + .mask = SYNOPSYS_XPCS_MASK, + .supported = xpcs_usxgmii_features, + .interface = xpcs_usxgmii_interfaces, + }, { + .id = SYNOPSYS_XPCS_10GKR_ID, + .mask = SYNOPSYS_XPCS_MASK, + .supported = xpcs_10gkr_features, + .interface = xpcs_10gkr_interfaces, + }, +}; + +static int xpcs_read(struct mdio_xpcs_args *xpcs, int dev, u32 reg) +{ + u32 reg_addr = MII_ADDR_C45 | dev << 16 | reg; + + return mdiobus_read(xpcs->bus, xpcs->addr, reg_addr); +} + +static int xpcs_write(struct mdio_xpcs_args *xpcs, int dev, u32 reg, u16 val) +{ + u32 reg_addr = MII_ADDR_C45 | dev << 16 | reg; + + return mdiobus_write(xpcs->bus, xpcs->addr, reg_addr, val); +} + +static int xpcs_read_vendor(struct mdio_xpcs_args *xpcs, int dev, u32 reg) +{ + return xpcs_read(xpcs, dev, DW_VENDOR | reg); +} + +static int xpcs_write_vendor(struct mdio_xpcs_args *xpcs, int dev, int reg, + u16 val) +{ + return xpcs_write(xpcs, dev, DW_VENDOR | reg, val); +} + +static int xpcs_read_vpcs(struct mdio_xpcs_args *xpcs, int reg) +{ + return xpcs_read_vendor(xpcs, MDIO_MMD_PCS, reg); +} + +static int xpcs_write_vpcs(struct mdio_xpcs_args *xpcs, int reg, u16 val) +{ + return xpcs_write_vendor(xpcs, MDIO_MMD_PCS, reg, val); +} + +static int xpcs_poll_reset(struct mdio_xpcs_args *xpcs, int dev) +{ + /* Poll until the reset bit clears (50ms per retry == 0.6 sec) */ + unsigned int retries = 12; + int ret; + + do { + msleep(50); + ret = xpcs_read(xpcs, dev, MDIO_CTRL1); + if (ret < 0) + return ret; + } while (ret & MDIO_CTRL1_RESET && --retries); + + return (ret & MDIO_CTRL1_RESET) ? -ETIMEDOUT : 0; +} + +static int xpcs_soft_reset(struct mdio_xpcs_args *xpcs, int dev) +{ + int ret; + + ret = xpcs_write(xpcs, dev, MDIO_CTRL1, MDIO_CTRL1_RESET); + if (ret < 0) + return ret; + + return xpcs_poll_reset(xpcs, dev); +} + +#define xpcs_warn(__xpcs, __state, __args...) \ +({ \ + if ((__state)->link) \ + dev_warn(&(__xpcs)->bus->dev, ##__args); \ +}) + +static int xpcs_read_fault(struct mdio_xpcs_args *xpcs, + struct phylink_link_state *state) +{ + int ret; + + ret = xpcs_read(xpcs, MDIO_MMD_PCS, MDIO_STAT1); + if (ret < 0) + return ret; + + if (ret & MDIO_STAT1_FAULT) { + xpcs_warn(xpcs, state, "Link fault condition detected!\n"); + return -EFAULT; + } + + ret = xpcs_read(xpcs, MDIO_MMD_PCS, MDIO_STAT2); + if (ret < 0) + return ret; + + if (ret & MDIO_STAT2_RXFAULT) + xpcs_warn(xpcs, state, "Receiver fault detected!\n"); + if (ret & MDIO_STAT2_TXFAULT) + xpcs_warn(xpcs, state, "Transmitter fault detected!\n"); + + ret = xpcs_read_vendor(xpcs, MDIO_MMD_PCS, DW_VR_XS_PCS_DIG_STS); + if (ret < 0) + return ret; + + if (ret & DW_RXFIFO_ERR) { + xpcs_warn(xpcs, state, "FIFO fault condition detected!\n"); + return -EFAULT; + } + + ret = xpcs_read(xpcs, MDIO_MMD_PCS, MDIO_PCS_10GBRT_STAT1); + if (ret < 0) + return ret; + + if (!(ret & MDIO_PCS_10GBRT_STAT1_BLKLK)) + xpcs_warn(xpcs, state, "Link is not locked!\n"); + + ret = xpcs_read(xpcs, MDIO_MMD_PCS, MDIO_PCS_10GBRT_STAT2); + if (ret < 0) + return ret; + + if (ret & MDIO_PCS_10GBRT_STAT2_ERR) + xpcs_warn(xpcs, state, "Link has errors!\n"); + + return 0; +} + +static int xpcs_read_link(struct mdio_xpcs_args *xpcs, bool an) +{ + bool link = true; + int ret; + + ret = xpcs_read(xpcs, MDIO_MMD_PCS, MDIO_STAT1); + if (ret < 0) + return ret; + + if (!(ret & MDIO_STAT1_LSTATUS)) + link = false; + + if (an) { + ret = xpcs_read(xpcs, MDIO_MMD_AN, MDIO_STAT1); + if (ret < 0) + return ret; + + if (!(ret & MDIO_STAT1_LSTATUS)) + link = false; + } + + return link; +} + +static int xpcs_get_max_usxgmii_speed(const unsigned long *supported) +{ + int max = SPEED_UNKNOWN; + + if (phylink_test(supported, 1000baseKX_Full)) + max = SPEED_1000; + if (phylink_test(supported, 2500baseX_Full)) + max = SPEED_2500; + if (phylink_test(supported, 10000baseKX4_Full)) + max = SPEED_10000; + if (phylink_test(supported, 10000baseKR_Full)) + max = SPEED_10000; + + return max; +} + +static int xpcs_config_usxgmii(struct mdio_xpcs_args *xpcs, int speed) +{ + int ret, speed_sel; + + switch (speed) { + case SPEED_10: + speed_sel = DW_USXGMII_10; + break; + case SPEED_100: + speed_sel = DW_USXGMII_100; + break; + case SPEED_1000: + speed_sel = DW_USXGMII_1000; + break; + case SPEED_2500: + speed_sel = DW_USXGMII_2500; + break; + case SPEED_5000: + speed_sel = DW_USXGMII_5000; + break; + case SPEED_10000: + speed_sel = DW_USXGMII_10000; + break; + default: + /* Nothing to do here */ + return -EINVAL; + } + + ret = xpcs_read_vpcs(xpcs, MDIO_CTRL1); + if (ret < 0) + return ret; + + ret = xpcs_write_vpcs(xpcs, MDIO_CTRL1, ret | DW_USXGMII_EN); + if (ret < 0) + return ret; + + ret = xpcs_read(xpcs, MDIO_MMD_VEND2, MDIO_CTRL1); + if (ret < 0) + return ret; + + ret &= ~DW_USXGMII_SS_MASK; + ret |= speed_sel | DW_USXGMII_FULL; + + ret = xpcs_write(xpcs, MDIO_MMD_VEND2, MDIO_CTRL1, ret); + if (ret < 0) + return ret; + + ret = xpcs_read_vpcs(xpcs, MDIO_CTRL1); + if (ret < 0) + return ret; + + return xpcs_write_vpcs(xpcs, MDIO_CTRL1, ret | DW_USXGMII_RST); +} + +static int xpcs_config_aneg_c73(struct mdio_xpcs_args *xpcs) +{ + int ret, adv; + + /* By default, in USXGMII mode XPCS operates at 10G baud and + * replicates data to achieve lower speeds. Hereby, in this + * default configuration we need to advertise all supported + * modes and not only the ones we want to use. + */ + + /* SR_AN_ADV3 */ + adv = 0; + if (phylink_test(xpcs->supported, 2500baseX_Full)) + adv |= DW_C73_2500KX; + + /* TODO: 5000baseKR */ + + ret = xpcs_write(xpcs, MDIO_MMD_AN, DW_SR_AN_ADV3, adv); + if (ret < 0) + return ret; + + /* SR_AN_ADV2 */ + adv = 0; + if (phylink_test(xpcs->supported, 1000baseKX_Full)) + adv |= DW_C73_1000KX; + if (phylink_test(xpcs->supported, 10000baseKX4_Full)) + adv |= DW_C73_10000KX4; + if (phylink_test(xpcs->supported, 10000baseKR_Full)) + adv |= DW_C73_10000KR; + + ret = xpcs_write(xpcs, MDIO_MMD_AN, DW_SR_AN_ADV2, adv); + if (ret < 0) + return ret; + + /* SR_AN_ADV1 */ + adv = DW_C73_AN_ADV_SF; + if (phylink_test(xpcs->supported, Pause)) + adv |= DW_C73_PAUSE; + if (phylink_test(xpcs->supported, Asym_Pause)) + adv |= DW_C73_ASYM_PAUSE; + + return xpcs_write(xpcs, MDIO_MMD_AN, DW_SR_AN_ADV1, adv); +} + +static int xpcs_config_aneg(struct mdio_xpcs_args *xpcs) +{ + int ret; + + ret = xpcs_config_aneg_c73(xpcs); + if (ret < 0) + return ret; + + ret = xpcs_read(xpcs, MDIO_MMD_AN, MDIO_CTRL1); + if (ret < 0) + return ret; + + ret |= MDIO_AN_CTRL1_ENABLE | MDIO_AN_CTRL1_RESTART; + + return xpcs_write(xpcs, MDIO_MMD_AN, MDIO_CTRL1, ret); +} + +static int xpcs_aneg_done(struct mdio_xpcs_args *xpcs, + struct phylink_link_state *state) +{ + int ret; + + ret = xpcs_read(xpcs, MDIO_MMD_AN, MDIO_STAT1); + if (ret < 0) + return ret; + + if (ret & MDIO_AN_STAT1_COMPLETE) { + ret = xpcs_read(xpcs, MDIO_MMD_AN, DW_SR_AN_LP_ABL1); + if (ret < 0) + return ret; + + /* Check if Aneg outcome is valid */ + if (!(ret & DW_C73_AN_ADV_SF)) + return 0; + + return 1; + } + + return 0; +} + +static int xpcs_read_lpa(struct mdio_xpcs_args *xpcs, + struct phylink_link_state *state) +{ + int ret; + + ret = xpcs_read(xpcs, MDIO_MMD_AN, MDIO_STAT1); + if (ret < 0) + return ret; + + if (!(ret & MDIO_AN_STAT1_LPABLE)) { + phylink_clear(state->lp_advertising, Autoneg); + return 0; + } + + phylink_set(state->lp_advertising, Autoneg); + + /* Clause 73 outcome */ + ret = xpcs_read(xpcs, MDIO_MMD_AN, DW_SR_AN_LP_ABL3); + if (ret < 0) + return ret; + + if (ret & DW_C73_2500KX) + phylink_set(state->lp_advertising, 2500baseX_Full); + + ret = xpcs_read(xpcs, MDIO_MMD_AN, DW_SR_AN_LP_ABL2); + if (ret < 0) + return ret; + + if (ret & DW_C73_1000KX) + phylink_set(state->lp_advertising, 1000baseKX_Full); + if (ret & DW_C73_10000KX4) + phylink_set(state->lp_advertising, 10000baseKX4_Full); + if (ret & DW_C73_10000KR) + phylink_set(state->lp_advertising, 10000baseKR_Full); + + ret = xpcs_read(xpcs, MDIO_MMD_AN, DW_SR_AN_LP_ABL1); + if (ret < 0) + return ret; + + if (ret & DW_C73_PAUSE) + phylink_set(state->lp_advertising, Pause); + if (ret & DW_C73_ASYM_PAUSE) + phylink_set(state->lp_advertising, Asym_Pause); + + linkmode_and(state->lp_advertising, state->lp_advertising, + state->advertising); + return 0; +} + +static void xpcs_resolve_lpa(struct mdio_xpcs_args *xpcs, + struct phylink_link_state *state) +{ + int max_speed = xpcs_get_max_usxgmii_speed(state->lp_advertising); + + state->pause = MLO_PAUSE_TX | MLO_PAUSE_RX; + state->speed = max_speed; + state->duplex = DUPLEX_FULL; +} + +static void xpcs_resolve_pma(struct mdio_xpcs_args *xpcs, + struct phylink_link_state *state) +{ + state->pause = MLO_PAUSE_TX | MLO_PAUSE_RX; + state->duplex = DUPLEX_FULL; + + switch (state->interface) { + case PHY_INTERFACE_MODE_10GKR: + state->speed = SPEED_10000; + break; + default: + state->speed = SPEED_UNKNOWN; + break; + } +} + +static int xpcs_validate(struct mdio_xpcs_args *xpcs, + unsigned long *supported, + struct phylink_link_state *state) +{ + linkmode_and(supported, supported, xpcs->supported); + linkmode_and(state->advertising, state->advertising, xpcs->supported); + return 0; +} + +static int xpcs_config(struct mdio_xpcs_args *xpcs, + const struct phylink_link_state *state) +{ + int ret; + + if (state->an_enabled) { + ret = xpcs_config_aneg(xpcs); + if (ret) + return ret; + } + + return 0; +} + +static int xpcs_get_state(struct mdio_xpcs_args *xpcs, + struct phylink_link_state *state) +{ + int ret; + + /* Link needs to be read first ... */ + state->link = xpcs_read_link(xpcs, state->an_enabled) > 0 ? 1 : 0; + + /* ... and then we check the faults. */ + ret = xpcs_read_fault(xpcs, state); + if (ret) { + ret = xpcs_soft_reset(xpcs, MDIO_MMD_PCS); + if (ret) + return ret; + + state->link = 0; + + return xpcs_config(xpcs, state); + } + + if (state->link && state->an_enabled && xpcs_aneg_done(xpcs, state)) { + state->an_complete = true; + xpcs_read_lpa(xpcs, state); + xpcs_resolve_lpa(xpcs, state); + } else if (state->link) { + xpcs_resolve_pma(xpcs, state); + } + + return 0; +} + +static int xpcs_link_up(struct mdio_xpcs_args *xpcs, int speed, + phy_interface_t interface) +{ + if (interface == PHY_INTERFACE_MODE_USXGMII) + return xpcs_config_usxgmii(xpcs, speed); + + return 0; +} + +static u32 xpcs_get_id(struct mdio_xpcs_args *xpcs) +{ + int ret; + u32 id; + + ret = xpcs_read(xpcs, MDIO_MMD_PCS, MII_PHYSID1); + if (ret < 0) + return 0xffffffff; + + id = ret << 16; + + ret = xpcs_read(xpcs, MDIO_MMD_PCS, MII_PHYSID2); + if (ret < 0) + return 0xffffffff; + + return id | ret; +} + +static bool xpcs_check_features(struct mdio_xpcs_args *xpcs, + struct xpcs_id *match, + phy_interface_t interface) +{ + int i; + + for (i = 0; match->interface[i] != PHY_INTERFACE_MODE_MAX; i++) { + if (match->interface[i] == interface) + break; + } + + if (match->interface[i] == PHY_INTERFACE_MODE_MAX) + return false; + + for (i = 0; match->supported[i] != __ETHTOOL_LINK_MODE_MASK_NBITS; i++) + set_bit(match->supported[i], xpcs->supported); + + return true; +} + +static int xpcs_probe(struct mdio_xpcs_args *xpcs, phy_interface_t interface) +{ + u32 xpcs_id = xpcs_get_id(xpcs); + struct xpcs_id *match = NULL; + int i; + + for (i = 0; i < ARRAY_SIZE(xpcs_id_list); i++) { + struct xpcs_id *entry = &xpcs_id_list[i]; + + if ((xpcs_id & entry->mask) == entry->id) { + match = entry; + + if (xpcs_check_features(xpcs, match, interface)) + return 0; + } + } + + return -ENODEV; +} + +static struct mdio_xpcs_ops xpcs_ops = { + .validate = xpcs_validate, + .config = xpcs_config, + .get_state = xpcs_get_state, + .link_up = xpcs_link_up, + .probe = xpcs_probe, +}; + +struct mdio_xpcs_ops *mdio_xpcs_get_ops(void) +{ + return &xpcs_ops; +} +EXPORT_SYMBOL_GPL(mdio_xpcs_get_ops); + +MODULE_LICENSE("GPL v2"); diff --git a/drivers/net/phy/phylink.c b/drivers/net/phy/phylink.c index b4367fab7899..19db68d74cb4 100644 --- a/drivers/net/phy/phylink.c +++ b/drivers/net/phy/phylink.c @@ -312,11 +312,13 @@ static int phylink_parse_mode(struct phylink *pl, struct fwnode_handle *fwnode) phylink_set(pl->supported, 1000baseT_Half); phylink_set(pl->supported, 1000baseT_Full); phylink_set(pl->supported, 1000baseX_Full); + phylink_set(pl->supported, 1000baseKX_Full); phylink_set(pl->supported, 2500baseT_Full); phylink_set(pl->supported, 2500baseX_Full); phylink_set(pl->supported, 5000baseT_Full); phylink_set(pl->supported, 10000baseT_Full); phylink_set(pl->supported, 10000baseKR_Full); + phylink_set(pl->supported, 10000baseKX4_Full); phylink_set(pl->supported, 10000baseCR_Full); phylink_set(pl->supported, 10000baseSR_Full); phylink_set(pl->supported, 10000baseLR_Full); @@ -338,6 +340,9 @@ static int phylink_parse_mode(struct phylink *pl, struct fwnode_handle *fwnode) "failed to validate link configuration for in-band status\n"); return -EINVAL; } + + /* Check if MAC/PCS also supports Autoneg. */ + pl->link_config.an_enabled = phylink_test(pl->supported, Autoneg); } return 0; diff --git a/include/linux/mdio-xpcs.h b/include/linux/mdio-xpcs.h new file mode 100644 index 000000000000..9a841aa5982d --- /dev/null +++ b/include/linux/mdio-xpcs.h @@ -0,0 +1,41 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2020 Synopsys, Inc. and/or its affiliates. + * Synopsys DesignWare XPCS helpers + */ + +#ifndef __LINUX_MDIO_XPCS_H +#define __LINUX_MDIO_XPCS_H + +#include <linux/phy.h> +#include <linux/phylink.h> + +struct mdio_xpcs_args { + __ETHTOOL_DECLARE_LINK_MODE_MASK(supported); + struct mii_bus *bus; + int addr; +}; + +struct mdio_xpcs_ops { + int (*validate)(struct mdio_xpcs_args *xpcs, + unsigned long *supported, + struct phylink_link_state *state); + int (*config)(struct mdio_xpcs_args *xpcs, + const struct phylink_link_state *state); + int (*get_state)(struct mdio_xpcs_args *xpcs, + struct phylink_link_state *state); + int (*link_up)(struct mdio_xpcs_args *xpcs, int speed, + phy_interface_t interface); + int (*probe)(struct mdio_xpcs_args *xpcs, phy_interface_t interface); +}; + +#if IS_ENABLED(CONFIG_MDIO_XPCS) +struct mdio_xpcs_ops *mdio_xpcs_get_ops(void); +#else +static inline struct mdio_xpcs_ops *mdio_xpcs_get_ops(void) +{ + return NULL; +} +#endif + +#endif /* __LINUX_MDIO_XPCS_H */ diff --git a/include/linux/stmmac.h b/include/linux/stmmac.h index 19190c609282..fbafb353e9be 100644 --- a/include/linux/stmmac.h +++ b/include/linux/stmmac.h @@ -80,6 +80,7 @@ struct stmmac_mdio_bus_data { unsigned int phy_mask; + unsigned int has_xpcs; int *irqs; int probed_phy_irq; bool needs_reset; diff --git a/include/net/flow_offload.h b/include/net/flow_offload.h index 64807aa03cee..d1b1e4aa310a 100644 --- a/include/net/flow_offload.h +++ b/include/net/flow_offload.h @@ -155,11 +155,21 @@ enum flow_action_mangle_base { FLOW_ACT_MANGLE_HDR_TYPE_UDP, }; -#define FLOW_ACTION_HW_STATS_TYPE_IMMEDIATE BIT(0) -#define FLOW_ACTION_HW_STATS_TYPE_DELAYED BIT(1) -#define FLOW_ACTION_HW_STATS_TYPE_ANY (FLOW_ACTION_HW_STATS_TYPE_IMMEDIATE | \ - FLOW_ACTION_HW_STATS_TYPE_DELAYED) -#define FLOW_ACTION_HW_STATS_TYPE_DISABLED 0 +enum flow_action_hw_stats_type_bit { + FLOW_ACTION_HW_STATS_TYPE_IMMEDIATE_BIT, + FLOW_ACTION_HW_STATS_TYPE_DELAYED_BIT, +}; + +enum flow_action_hw_stats_type { + FLOW_ACTION_HW_STATS_TYPE_DISABLED = 0, + FLOW_ACTION_HW_STATS_TYPE_IMMEDIATE = + BIT(FLOW_ACTION_HW_STATS_TYPE_IMMEDIATE_BIT), + FLOW_ACTION_HW_STATS_TYPE_DELAYED = + BIT(FLOW_ACTION_HW_STATS_TYPE_DELAYED_BIT), + FLOW_ACTION_HW_STATS_TYPE_ANY = + FLOW_ACTION_HW_STATS_TYPE_IMMEDIATE | + FLOW_ACTION_HW_STATS_TYPE_DELAYED, +}; typedef void (*action_destr)(void *priv); @@ -175,7 +185,7 @@ void flow_action_cookie_destroy(struct flow_action_cookie *cookie); struct flow_action_entry { enum flow_action_id id; - u8 hw_stats_type; + enum flow_action_hw_stats_type hw_stats_type; action_destr destructor; void *destructor_priv; union { @@ -256,6 +266,11 @@ static inline bool flow_offload_has_one_action(const struct flow_action *action) return action->num_entries == 1; } +#define flow_action_for_each(__i, __act, __actions) \ + for (__i = 0, __act = &(__actions)->entries[0]; \ + __i < (__actions)->num_entries; \ + __act = &(__actions)->entries[++__i]) + static inline bool flow_action_mixed_hw_stats_types_check(const struct flow_action *action, struct netlink_ext_ack *extack) @@ -267,8 +282,7 @@ flow_action_mixed_hw_stats_types_check(const struct flow_action *action, if (flow_offload_has_one_action(action)) return true; - for (i = 0; i < action->num_entries; i++) { - action_entry = &action->entries[i]; + flow_action_for_each(i, action_entry, action) { if (i && action_entry->hw_stats_type != last_hw_stats_type) { NL_SET_ERR_MSG_MOD(extack, "Mixing HW stats types for actions is not supported"); return false; @@ -286,9 +300,10 @@ flow_action_first_entry_get(const struct flow_action *action) } static inline bool -flow_action_hw_stats_types_check(const struct flow_action *action, - struct netlink_ext_ack *extack, - u8 allowed_hw_stats_type) +__flow_action_hw_stats_types_check(const struct flow_action *action, + struct netlink_ext_ack *extack, + bool check_allow_bit, + enum flow_action_hw_stats_type_bit allow_bit) { const struct flow_action_entry *action_entry; @@ -297,12 +312,12 @@ flow_action_hw_stats_types_check(const struct flow_action *action, if (!flow_action_mixed_hw_stats_types_check(action, extack)) return false; action_entry = flow_action_first_entry_get(action); - if (allowed_hw_stats_type == 0 && + if (!check_allow_bit && action_entry->hw_stats_type != FLOW_ACTION_HW_STATS_TYPE_ANY) { NL_SET_ERR_MSG_MOD(extack, "Driver supports only default HW stats type \"any\""); return false; - } else if (allowed_hw_stats_type != 0 && - action_entry->hw_stats_type != allowed_hw_stats_type) { + } else if (check_allow_bit && + !(action_entry->hw_stats_type & BIT(allow_bit))) { NL_SET_ERR_MSG_MOD(extack, "Driver does not support selected HW stats type"); return false; } @@ -310,15 +325,21 @@ flow_action_hw_stats_types_check(const struct flow_action *action, } static inline bool +flow_action_hw_stats_types_check(const struct flow_action *action, + struct netlink_ext_ack *extack, + enum flow_action_hw_stats_type_bit allow_bit) +{ + return __flow_action_hw_stats_types_check(action, extack, + true, allow_bit); +} + +static inline bool flow_action_basic_hw_stats_types_check(const struct flow_action *action, struct netlink_ext_ack *extack) { - return flow_action_hw_stats_types_check(action, extack, 0); + return __flow_action_hw_stats_types_check(action, extack, false, 0); } -#define flow_action_for_each(__i, __act, __actions) \ - for (__i = 0, __act = &(__actions)->entries[0]; __i < (__actions)->num_entries; __act = &(__actions)->entries[++__i]) - struct flow_rule { struct flow_match match; struct flow_action action; diff --git a/net/core/pktgen.c b/net/core/pktgen.c index acc849df60b5..f2b3d8dd40f4 100644 --- a/net/core/pktgen.c +++ b/net/core/pktgen.c @@ -2003,8 +2003,8 @@ static int pktgen_setup_dev(const struct pktgen_net *pn, return -ENODEV; } - if (odev->type != ARPHRD_ETHER) { - pr_err("not an ethernet device: \"%s\"\n", ifname); + if (odev->type != ARPHRD_ETHER && odev->type != ARPHRD_LOOPBACK) { + pr_err("not an ethernet or loopback device: \"%s\"\n", ifname); err = -EINVAL; } else if (!netif_running(odev)) { pr_err("device is down: \"%s\"\n", ifname); |